Ticket #7195: 7195-gen2-v8.patch
File 7195-gen2-v8.patch, 104.7 KB (added by , 14 years ago) |
---|
-
libs/libmythtv/jobqueue.cpp
2169 2169 if (program_info->IsLocal()) 2170 2170 { 2171 2171 PreviewGenerator *pg = new PreviewGenerator( 2172 program_info, PreviewGenerator::kLocal);2172 program_info, QString(), PreviewGenerator::kLocal); 2173 2173 pg->Run(); 2174 2174 pg->deleteLater(); 2175 2175 } -
libs/libmythtv/libmythtv.pro
156 156 HEADERS += scheduledrecording.h 157 157 HEADERS += signalmonitorvalue.h signalmonitorlistener.h 158 158 HEADERS += livetvchain.h playgroup.h 159 HEADERS += channelsettings.h previewgenerator.h 159 HEADERS += channelsettings.h 160 HEADERS += previewgenerator.h previewgeneratorqueue.h 160 161 HEADERS += transporteditor.h listingsources.h 161 162 HEADERS += myth_imgconvert.h 162 163 HEADERS += channelgroup.h channelgroupsettings.h … … 179 180 SOURCES += scheduledrecording.cpp 180 181 SOURCES += signalmonitorvalue.cpp 181 182 SOURCES += livetvchain.cpp playgroup.cpp 182 SOURCES += channelsettings.cpp previewgenerator.cpp 183 SOURCES += channelsettings.cpp 184 SOURCES += previewgenerator.cpp previewgeneratorqueue.cpp 183 185 SOURCES += transporteditor.cpp 184 186 SOURCES += channelgroup.cpp channelgroupsettings.cpp 185 187 SOURCES += myth_imgconvert.cpp -
libs/libmythtv/previewgeneratorqueue.h
1 // -*- Mode: c++ -*- 2 #ifndef _PREVIEW_GENERATOR_QUEUE_H_ 3 #define _PREVIEW_GENERATOR_QUEUE_H_ 4 5 #include <QStringList> 6 #include <QDateTime> 7 #include <QThread> 8 #include <QMutex> 9 #include <QMap> 10 #include <QSet> 11 12 #include "previewgenerator.h" 13 #include "mythexp.h" 14 15 class ProgramInfo; 16 class QSize; 17 18 class PreviewGenState 19 { 20 public: 21 PreviewGenState() : 22 gen(NULL), genStarted(false), 23 attempts(0), lastBlockTime(0) {} 24 PreviewGenerator *gen; 25 bool genStarted; 26 uint attempts; 27 uint lastBlockTime; 28 QDateTime blockRetryUntil; 29 QSet<QString> tokens; 30 }; 31 typedef QMap<QString,PreviewGenState> PreviewMap; 32 33 class MPUBLIC PreviewGeneratorQueue : public QThread 34 { 35 Q_OBJECT 36 37 public: 38 static void CreatePreviewGeneratorQueue( 39 PreviewGenerator::Mode mode, 40 uint maxAttempts, uint minBlockSeconds); 41 static void TeardownPreviewGeneratorQueue(); 42 43 static void GetPreviewImage(const ProgramInfo &pginfo, QString token) 44 { 45 GetPreviewImage(pginfo, QSize(0,0), "", -1, true, token); 46 } 47 static void GetPreviewImage(const ProgramInfo&, const QSize&, 48 const QString &outputfile, 49 long long time, bool in_seconds, 50 QString token); 51 static void AddListener(QObject*); 52 static void RemoveListener(QObject*); 53 54 private: 55 PreviewGeneratorQueue(PreviewGenerator::Mode mode, 56 uint maxAttempts, uint minBlockSeconds); 57 ~PreviewGeneratorQueue(); 58 59 QString GeneratePreviewImage(ProgramInfo &pginfo, const QSize&, 60 const QString &outputfile, 61 long long time, bool in_seconds, 62 QString token); 63 64 void GetInfo(const QString &key, uint &queue_depth, uint &preview_tokens); 65 void SetPreviewGenerator(const QString &key, PreviewGenerator *g); 66 void IncPreviewGeneratorPriority(const QString &key, QString token); 67 void UpdatePreviewGeneratorThreads(void); 68 bool IsGeneratingPreview(const QString &key) const; 69 uint IncPreviewGeneratorAttempts(const QString &key); 70 void ClearPreviewGeneratorAttempts(const QString &key); 71 72 virtual bool event(QEvent *e); // QObject 73 74 void SendEvent(const ProgramInfo &pginfo, 75 const QString &eventname, 76 const QString &fn, 77 const QString &token, 78 const QString &msg, 79 const QDateTime &dt); 80 81 private: 82 static PreviewGeneratorQueue *s_pgq; 83 QSet<QObject*> m_listeners; 84 85 mutable QMutex m_lock; 86 PreviewGenerator::Mode m_mode; 87 PreviewMap m_previewMap; 88 QMap<QString,QString> m_tokenToKeyMap; 89 QStringList m_queue; 90 uint m_running; 91 uint m_maxThreads; 92 uint m_maxAttempts; 93 uint m_minBlockSeconds; 94 }; 95 96 #endif // _PREVIEW_GENERATOR_QUEUE_H_ -
libs/libmythtv/previewgenerator.cpp
2 2 #include <cmath> 3 3 4 4 // POSIX headers 5 #include <sys/types.h> // for utime 5 6 #include <sys/time.h> 6 7 #include <fcntl.h> 8 #include <utime.h> // for utime 7 9 8 10 // Qt headers 11 #include <QCoreApplication> 12 #include <QTemporaryFile> 9 13 #include <QFileInfo> 14 #include <QMetaType> 15 #include <QThread> 10 16 #include <QImage> 11 #include <Q MetaType>17 #include <QDir> 12 18 #include <QUrl> 13 #include <QDir>14 19 15 20 // MythTV headers 16 21 #include "mythconfig.h" … … 27 32 #include "playercontext.h" 28 33 #include "mythdirs.h" 29 34 #include "mythverbose.h" 35 #include "remoteutil.h" 30 36 31 37 #define LOC QString("Preview: ") 32 38 #define LOC_ERR QString("Preview Error: ") … … 37 43 * 38 44 * The usage is simple: First, pass a ProgramInfo whose pathname points 39 45 * to a local or remote recording to the constructor. Then call either 40 * Start(void) or Run(void) to generate the preview.46 * start(void) or Run(void) to generate the preview. 41 47 * 42 * Start(void) will create a thread that processes the request, 43 * creating a sockets the the backend if the recording is not local. 48 * start(void) will create a thread that processes the request. 44 49 * 45 * Run(void) will process the request in the current thread, and it 46 * uses the MythContext's server and event sockets if the recording 47 * is not local. 50 * Run(void) will block until the preview completes. 48 51 * 49 * The PreviewGenerator will send Qt signals when the preview is ready50 * and when the preview thread finishes running if Start(void) was called.52 * The PreviewGenerator will send a PREVIEW_SUCCESS or a 53 * PREVIEW_FAILED event when the preview completes or fails. 51 54 */ 52 55 53 56 /** … … 64 67 * if the file is local. 65 68 */ 66 69 PreviewGenerator::PreviewGenerator(const ProgramInfo *pginfo, 70 const QString &_token, 67 71 PreviewGenerator::Mode _mode) 68 : programInfo(*pginfo), mode(_mode), isConnected(false),69 createSockets(false), serverSock(NULL),pathname(pginfo->GetPathname()),72 : programInfo(*pginfo), mode(_mode), listener(NULL), 73 pathname(pginfo->GetPathname()), 70 74 timeInSeconds(true), captureTime(-1), outFileName(QString::null), 71 outSize(0,0) 75 outSize(0,0), token(_token), gotReply(false), pixmapOk(false) 72 76 { 73 77 } 74 78 … … 84 88 85 89 void PreviewGenerator::TeardownAll(void) 86 90 { 87 if (!isConnected) 88 return; 89 90 const QString filename = programInfo.GetPathname() + ".png"; 91 92 MythTimer t; 93 t.start(); 94 for (bool done = false; !done;) 95 { 96 previewLock.lock(); 97 if (isConnected) 98 emit previewThreadDone(filename, done); 99 else 100 done = true; 101 previewLock.unlock(); 102 usleep(5000); 103 } 104 VERBOSE(VB_PLAYBACK, LOC + "previewThreadDone took "<<t.elapsed()<<"ms"); 105 disconnectSafe(); 91 previewWaitCondition.wakeAll(); 92 listener = NULL; 106 93 } 107 94 108 95 void PreviewGenerator::deleteLater() … … 114 101 void PreviewGenerator::AttachSignals(QObject *obj) 115 102 { 116 103 QMutexLocker locker(&previewLock); 117 qRegisterMetaType<bool>("bool &"); 118 connect(this, SIGNAL(previewThreadDone(const QString&,bool&)), 119 obj, SLOT( previewThreadDone(const QString&,bool&)), 120 Qt::DirectConnection); 121 connect(this, SIGNAL(previewReady(const ProgramInfo*)), 122 obj, SLOT( previewReady(const ProgramInfo*)), 123 Qt::DirectConnection); 124 isConnected = true; 104 listener = obj; 125 105 } 126 106 127 /** \fn PreviewGenerator::disconnectSafe(void)128 * \brief disconnects signals while holding previewLock, ensuring that129 * no one will receive a signal from this class after this call.130 */131 void PreviewGenerator::disconnectSafe(void)132 {133 QMutexLocker locker(&previewLock);134 QObject::disconnect(this, NULL, NULL, NULL);135 isConnected = false;136 }137 138 /** \fn PreviewGenerator::Start(void)139 * \brief This call starts a thread that will create a preview.140 */141 void PreviewGenerator::Start(void)142 {143 pthread_create(&previewThread, NULL, PreviewRun, this);144 // detach, so we don't have to join thread to free thread local mem.145 pthread_detach(previewThread);146 }147 148 107 /** \fn PreviewGenerator::RunReal(void) 149 108 * \brief This call creates a preview without starting a new thread. 150 109 */ 151 110 bool PreviewGenerator::RunReal(void) 152 111 { 112 QString msg; 113 QTime tm = QTime::currentTime(); 153 114 bool ok = false; 154 115 bool is_local = IsLocal(); 155 if (is_local && (mode && kLocal) && LocalPreviewRun()) 116 117 if (!is_local && !!(mode & kRemote)) 156 118 { 119 VERBOSE(VB_IMPORTANT, LOC_ERR + 120 QString("Run() file not local: '%1'") 121 .arg(pathname)); 122 } 123 else if (!(mode & kLocal) && !(mode & kRemote)) 124 { 125 VERBOSE(VB_IMPORTANT, LOC_ERR + 126 QString("Run() Preview of '%1' failed " 127 "because mode was invalid 0x%2") 128 .arg(pathname).arg((int)mode,0,16)); 129 } 130 else if (is_local && !!(mode & kLocal) && LocalPreviewRun()) 131 { 157 132 ok = true; 133 msg = QString("Generated on %1 in %2 seconds, starting at %3") 134 .arg(gCoreContext->GetHostName()) 135 .arg(tm.elapsed()*0.001) 136 .arg(tm.toString(Qt::ISODate)); 158 137 } 159 else if ( mode & kRemote)138 else if (!!(mode & kRemote)) 160 139 { 161 if (is_local )140 if (is_local && (mode & kLocal)) 162 141 { 163 142 VERBOSE(VB_IMPORTANT, LOC_WARN + "Failed to save preview." 164 143 "\n\t\t\tYou may need to check user and group ownership on" … … 166 145 "\n\t\t\tAttempting to regenerate preview on backend.\n"); 167 146 } 168 147 ok = RemotePreviewRun(); 148 if (ok) 149 { 150 msg = QString("Generated remotely in %1 seconds, starting at %2") 151 .arg(tm.elapsed()*0.001) 152 .arg(tm.toString(Qt::ISODate)); 153 } 154 else 155 { 156 msg = "Remote preview failed"; 157 } 169 158 } 170 159 else 171 160 { 172 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Run() file not local: '%1'") 173 .arg(pathname)); 161 msg = "Could not access recording"; 174 162 } 175 163 164 QMutexLocker locker(&previewLock); 165 if (listener) 166 { 167 QString output_fn = outFileName.isEmpty() ? 168 (programInfo.GetPathname()+".png") : outFileName; 169 170 QDateTime dt; 171 if (ok) 172 { 173 QFileInfo fi(output_fn); 174 if (fi.exists()) 175 dt = fi.lastModified(); 176 } 177 178 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; 179 QStringList list; 180 list.push_back(programInfo.MakeUniqueKey()); 181 list.push_back(output_fn); 182 list.push_back(msg); 183 list.push_back(dt.isValid()?dt.toString(Qt::ISODate):""); 184 list.push_back(token); 185 QCoreApplication::postEvent(listener, new MythEvent(message, list)); 186 } 187 176 188 return ok; 177 189 } 178 190 179 191 bool PreviewGenerator::Run(void) 180 192 { 193 QString msg; 194 QDateTime dtm = QDateTime::currentDateTime(); 195 QTime tm = QTime::currentTime(); 181 196 bool ok = false; 182 197 QString command = GetInstallPrefix() + "/bin/mythpreviewgen"; 183 bool local_ok = (IsLocal() && (mode & kLocal) &&198 bool local_ok = (IsLocal() && !!(mode & kLocal) && 184 199 QFileInfo(command).isExecutable()); 185 200 if (!local_ok) 186 201 { 187 if ( mode & kRemote)202 if (!!(mode & kRemote)) 188 203 { 189 204 ok = RemotePreviewRun(); 205 if (ok) 206 { 207 msg = 208 QString("Generated remotely in %1 seconds, starting at %2") 209 .arg(tm.elapsed()*0.001) 210 .arg(tm.toString(Qt::ISODate)); 211 } 190 212 } 191 213 else 192 214 { 193 215 VERBOSE(VB_IMPORTANT, LOC_ERR + 194 216 QString("Run() can not generate preview locally for: '%1'") 195 217 .arg(pathname)); 218 msg = "Failed, local preview requested for remote file."; 196 219 } 197 220 } 198 221 else … … 215 238 if (!outFileName.isEmpty()) 216 239 command += QString("--outfile \"%1\" ").arg(outFileName); 217 240 241 command += " > /dev/null"; 242 218 243 int ret = myth_system(command, MYTH_SYSTEM_DONT_BLOCK_LIRC | 219 244 MYTH_SYSTEM_DONT_BLOCK_JOYSTICK_MENU | 220 245 MYTH_SYSTEM_DONT_BLOCK_PARENT); 221 246 if (ret) 222 247 { 223 VERBOSE(VB_IMPORTANT, LOC_ERR + "Encountered problems running " +224 QString("'%1'").arg(command));248 msg = QString("Encountered problems running '%1'").arg(command); 249 VERBOSE(VB_IMPORTANT, LOC_ERR + msg); 225 250 } 226 251 else 227 252 { … … 240 265 QFileInfo fi(outname); 241 266 ok = (fi.exists() && fi.isReadable() && fi.size()); 242 267 if (ok) 268 { 243 269 VERBOSE(VB_PLAYBACK, LOC + "Preview process ran ok."); 270 msg = QString("Generated on %1 in %2 seconds, starting at %3") 271 .arg(gCoreContext->GetHostName()) 272 .arg(tm.elapsed()*0.001) 273 .arg(tm.toString(Qt::ISODate)); 274 } 244 275 else 245 276 { 246 277 VERBOSE(VB_IMPORTANT, LOC_ERR + "Preview process not ok." + … … 251 282 VERBOSE(VB_IMPORTANT, LOC_ERR + 252 283 QString("Despite command '%1' returning success") 253 284 .arg(command)); 285 msg = QString("Failed to read preview image despite " 286 "preview process returning success."); 254 287 } 255 288 } 256 289 } 257 290 291 QMutexLocker locker(&previewLock); 292 293 // Backdate file to start of preview time in case a bookmark was made 294 // while we were generating the preview. 295 QString output_fn = outFileName.isEmpty() ? 296 (programInfo.GetPathname()+".png") : outFileName; 297 298 QDateTime dt; 258 299 if (ok) 259 300 { 260 QMutexLocker locker(&previewLock); 261 emit previewReady(&programInfo); 301 QFileInfo fi(output_fn); 302 if (fi.exists()) 303 dt = fi.lastModified(); 262 304 } 263 305 306 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; 307 if (listener) 308 { 309 QStringList list; 310 list.push_back(programInfo.MakeUniqueKey()); 311 list.push_back(outFileName.isEmpty() ? 312 (programInfo.GetPathname()+".png") : outFileName); 313 list.push_back(msg); 314 list.push_back(dt.isValid()?dt.toString(Qt::ISODate):""); 315 list.push_back(token); 316 QCoreApplication::postEvent(listener, new MythEvent(message, list)); 317 } 318 264 319 return ok; 265 320 } 266 321 267 void *PreviewGenerator::PreviewRun(void *param)322 void PreviewGenerator::run(void) 268 323 { 269 // Lower scheduling priority, to avoid problems with recordings. 270 if (setpriority(PRIO_PROCESS, 0, 9)) 271 VERBOSE(VB_IMPORTANT, LOC + "Setting priority failed." + ENO); 272 PreviewGenerator *gen = (PreviewGenerator*) param; 273 gen->createSockets = true; 274 gen->Run(); 275 gen->deleteLater(); 276 return NULL; 324 setPriority(QThread::LowPriority); 325 Run(); 326 connect(this, SIGNAL(finished()), 327 this, SLOT(deleteLater())); 277 328 } 278 329 279 bool PreviewGenerator::RemotePreviewSetup(void)280 {281 QString server = gCoreContext->GetSetting("MasterServerIP", "localhost");282 int port = gCoreContext->GetNumSetting("MasterServerPort", 6543);283 QString ann = QString("ANN Monitor %2 %3")284 .arg(gCoreContext->GetHostName()).arg(false);285 286 serverSock = gCoreContext->ConnectCommandSocket(server, port, ann);287 return serverSock;288 }289 290 330 bool PreviewGenerator::RemotePreviewRun(void) 291 331 { 292 QStringList strlist( "QUERY_GENPIXMAP" ); 332 QStringList strlist( "QUERY_GENPIXMAP2" ); 333 if (token.isEmpty()) 334 { 335 token = QString("%1:%2") 336 .arg(programInfo.MakeUniqueKey()).arg(rand()); 337 } 338 strlist.push_back(token); 293 339 programInfo.ToStringList(strlist); 294 340 strlist.push_back(timeInSeconds ? "s" : "f"); 295 341 encodeLongLong(strlist, captureTime); … … 305 351 strlist.push_back(QString::number(outSize.width())); 306 352 strlist.push_back(QString::number(outSize.height())); 307 353 308 bool ok = false; 354 gCoreContext->addListener(this); 355 pixmapOk = false; 309 356 310 if (createSockets) 311 { 312 if (!RemotePreviewSetup()) 313 { 314 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to open sockets."); 315 return false; 316 } 317 318 if (serverSock) 319 { 320 serverSock->writeStringList(strlist); 321 ok = serverSock->readStringList(strlist, false); 322 } 323 324 RemotePreviewTeardown(); 325 } 326 else 327 { 328 ok = gCoreContext->SendReceiveStringList(strlist); 329 } 330 357 bool ok = gCoreContext->SendReceiveStringList(strlist); 331 358 if (!ok || strlist.empty() || (strlist[0] != "OK")) 332 359 { 333 360 if (!ok) … … 340 367 VERBOSE(VB_IMPORTANT, LOC_ERR + 341 368 "Remote Preview failed, reason given: " <<strlist[1]); 342 369 } 343 else 344 { 345 VERBOSE(VB_IMPORTANT, LOC_ERR + 346 "Remote Preview failed due to an unknown error."); 347 } 370 371 gCoreContext->removeListener(this); 372 348 373 return false; 349 374 } 350 375 376 QMutexLocker locker(&previewLock); 377 378 // wait up to 30 seconds for the preview to complete 379 if (!gotReply) 380 previewWaitCondition.wait(&previewLock, 30 * 1000); 381 382 if (!gotReply) 383 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- no reply.."); 384 385 gCoreContext->removeListener(this); 386 387 return pixmapOk; 388 } 389 390 bool PreviewGenerator::event(QEvent *e) 391 { 392 if (e->type() != (QEvent::Type) MythEvent::MythEventMessage) 393 return QObject::event(e); 394 395 MythEvent *me = (MythEvent*)e; 396 if (me->Message() != "GENERATED_PIXMAP" || me->ExtraDataCount() < 3) 397 return QObject::event(e); 398 399 bool ok = me->ExtraData(0) == "OK"; 400 bool ours = false; 401 uint i = ok ? 4 : 3; 402 for (; i < (uint) me->ExtraDataCount() && !ours; i++) 403 ours |= me->ExtraData(i) == token; 404 if (!ours) 405 return false; 406 407 QString pginfokey = me->ExtraData(1); 408 409 QMutexLocker locker(&previewLock); 410 gotReply = true; 411 pixmapOk = ok; 412 if (!ok) 413 { 414 VERBOSE(VB_IMPORTANT, LOC_ERR + pginfokey + ": " + me->ExtraData(2)); 415 previewWaitCondition.wakeAll(); 416 return true; 417 } 418 419 if (me->ExtraDataCount() < 5) 420 { 421 pixmapOk = false; 422 previewWaitCondition.wakeAll(); 423 return true; // could only happen with very broken client... 424 } 425 426 QDateTime datetime = QDateTime::fromString(me->ExtraData(3), Qt::ISODate); 427 if (!datetime.isValid()) 428 { 429 pixmapOk = false; 430 VERBOSE(VB_IMPORTANT, LOC_ERR + pginfokey + "Got invalid date"); 431 previewWaitCondition.wakeAll(); 432 return false; 433 } 434 435 size_t length = me->ExtraData(4).toULongLong(); 436 quint16 checksum16 = me->ExtraData(5).toUInt(); 437 QByteArray data = QByteArray::fromBase64(me->ExtraData(6).toAscii()); 438 if ((size_t) data.size() < length) 439 { // (note data.size() may be up to 3 440 // bytes longer after decoding 441 VERBOSE(VB_IMPORTANT, LOC_ERR + 442 QString("Preview size check failed %1 < %2") 443 .arg(data.size()).arg(length)); 444 data.clear(); 445 } 446 data.resize(length); 447 448 if (checksum16 != qChecksum(data.constData(), data.size())) 449 { 450 VERBOSE(VB_IMPORTANT, LOC_ERR + "Preview checksum failed"); 451 data.clear(); 452 } 453 454 pixmapOk = (data.isEmpty()) ? false : SaveOutFile(data, datetime); 455 456 previewWaitCondition.wakeAll(); 457 458 return true; 459 } 460 461 bool PreviewGenerator::SaveOutFile(const QByteArray &data, const QDateTime &dt) 462 { 351 463 if (outFileName.isEmpty()) 352 464 { 353 QString remotecachedirname = QString("%1/remotecache").arg(GetConfDir()); 465 QString remotecachedirname = 466 QString("%1/remotecache").arg(GetConfDir()); 354 467 QDir remotecachedir(remotecachedirname); 355 468 356 469 if (!remotecachedir.exists()) … … 368 481 outFileName = QString("%1/%2").arg(remotecachedirname).arg(filename); 369 482 } 370 483 371 // find file, copy/move to output file name & location... 372 373 QString url = QString::null; 374 QString fn = QFileInfo(outFileName).fileName(); 375 QByteArray data; 376 ok = false; 377 378 QStringList fileNames; 379 fileNames.push_back( 380 CreateAccessibleFilename(programInfo.GetPathname(), fn)); 381 fileNames.push_back( 382 CreateAccessibleFilename(programInfo.GetPathname(), "")); 383 384 QStringList::const_iterator it = fileNames.begin(); 385 for ( ; it != fileNames.end() && (!ok || data.isEmpty()); ++it) 484 QFile file(outFileName); 485 bool ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly); 486 if (!ok) 386 487 { 387 data.resize(0); 388 url = *it; 389 RemoteFile *rf = new RemoteFile(url, false, false, 0); 390 ok = rf->SaveAs(data); 391 delete rf; 488 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Failed to open: '%1'") 489 .arg(outFileName)); 392 490 } 393 491 394 if (ok && data.size()) 492 off_t offset = 0; 493 size_t remaining = data.size(); 494 uint failure_cnt = 0; 495 while ((remaining > 0) && (failure_cnt < 5)) 395 496 { 396 QFile file(outFileName); 397 ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly); 398 if (!ok) 497 ssize_t written = file.write(data.data() + offset, remaining); 498 if (written < 0) 399 499 { 400 VERBOSE(VB_IMPORTANT, QString("Failed to open: '%1'") 401 .arg(outFileName)); 500 failure_cnt++; 501 usleep(50000); 502 continue; 402 503 } 403 504 404 off_t offset = 0; 405 size_t remaining = (ok) ? data.size() : 0; 406 uint failure_cnt = 0; 407 while ((remaining > 0) && (failure_cnt < 5)) 408 { 409 ssize_t written = file.write(data.data() + offset, remaining); 410 if (written < 0) 411 { 412 failure_cnt++; 413 usleep(50000); 414 continue; 415 } 416 417 failure_cnt = 0; 418 offset += written; 419 remaining -= written; 420 } 421 if (ok && !remaining) 422 { 423 VERBOSE(VB_PLAYBACK, QString("Saved: '%1'") 424 .arg(outFileName)); 425 } 505 failure_cnt = 0; 506 offset += written; 507 remaining -= written; 426 508 } 427 509 428 return ok && data.size(); 429 } 430 431 void PreviewGenerator::RemotePreviewTeardown(void) 432 { 433 if (serverSock) 510 if (ok && !remaining) 434 511 { 435 serverSock->DownRef(); 436 serverSock = NULL; 512 file.close(); 513 struct utimbuf times; 514 times.actime = times.modtime = dt.toTime_t(); 515 utime(outFileName.toLocal8Bit().constData(), ×); 516 VERBOSE(VB_FILE, LOC + QString("Saved: '%1'").arg(outFileName)); 437 517 } 518 else 519 { 520 file.remove(); 521 } 522 523 return ok; 438 524 } 439 525 440 526 bool PreviewGenerator::SavePreview(QString filename, … … 476 562 QImage small_img = img.scaled((int) ppw, (int) pph, 477 563 Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 478 564 479 QByteArray fname = filename.toAscii(); 480 if (small_img.save(fname.constData(), "PNG")) 565 QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX"); 566 f.setAutoRemove(false); 567 if (f.open() && small_img.save(&f, "PNG")) 481 568 { 482 makeFileAccessible(fname.constData()); // Let anybody update it 483 484 VERBOSE(VB_PLAYBACK, LOC + 485 QString("Saved preview '%0' %1x%2") 486 .arg(filename).arg((int) ppw).arg((int) pph)); 487 488 return true; 569 // Let anybody update it 570 makeFileAccessible(f.fileName().toLocal8Bit().constData()); 571 QFile of(filename); 572 of.remove(); 573 if (f.rename(filename)) 574 { 575 VERBOSE(VB_PLAYBACK, LOC + 576 QString("Saved preview '%0' %1x%2") 577 .arg(filename).arg((int) ppw).arg((int) pph)); 578 return true; 579 } 580 f.remove(); 489 581 } 490 582 491 // Save failed; if file exists, try saving to .new and moving over492 QString newfile = filename + ".new";493 QByteArray newfilea = newfile.toAscii();494 if (QFileInfo(fname.constData()).exists() &&495 small_img.save(newfilea.constData(), "PNG"))496 {497 makeFileAccessible(newfilea.constData());498 rename(newfilea.constData(), fname.constData());499 500 VERBOSE(VB_PLAYBACK, LOC +501 QString("Saved preview '%0' %1x%2")502 .arg(filename).arg((int) ppw).arg((int) pph));503 504 return true;505 }506 507 // Couldn't save, nothing else I can do?508 583 return false; 509 584 } 510 585 … … 515 590 float aspect = 0; 516 591 int width, height, sz; 517 592 long long captime = captureTime; 593 594 QDateTime dt = QDateTime::currentDateTime(); 595 596 if (captime > 0) 597 VERBOSE(VB_IMPORTANT, "Preview from time spec"); 598 518 599 if (captime < 0) 519 600 { 601 captime = programInfo.QueryBookmark(); 602 if (captime > 0) 603 timeInSeconds = false; 604 else 605 captime = -1; 606 } 607 608 if (captime < 0) 609 { 520 610 timeInSeconds = true; 521 611 int startEarly = 0; 522 612 int programDuration = 0; … … 559 649 560 650 bool ok = SavePreview(outname, data, width, height, aspect, dw, dh); 561 651 652 if (ok) 653 { 654 // Backdate file to start of preview time in case a bookmark was made 655 // while we were generating the preview. 656 struct utimbuf times; 657 times.actime = times.modtime = dt.toTime_t(); 658 utime(outname.toLocal8Bit().constData(), ×); 659 } 660 562 661 delete[] data; 563 662 564 663 programInfo.MarkAsInUse(false, kPreviewGeneratorInUseID); … … 604 703 if (tmppathname.left(4) == "dvd:") 605 704 tmppathname = tmppathname.section(":", 1, 1); 606 705 706 if (!QFileInfo(tmppathname).isReadable()) 707 return false; 708 709 tmppathname = outFileName.isEmpty() ? tmppathname : outFileName; 607 710 QString pathdir = QFileInfo(tmppathname).path(); 608 711 609 return (QFileInfo(tmppathname).isReadable() && 610 QFileInfo(pathdir).isWritable()); 712 if (!QFileInfo(pathdir).isWritable()) 713 { 714 VERBOSE(VB_IMPORTANT, LOC_WARN + 715 QString("Output path '%1' is not writeable") 716 .arg(pathdir)); 717 return false; 718 } 719 720 return true; 611 721 } 612 722 613 723 /** … … 640 750 (void) video_height; 641 751 char *retbuf = NULL; 642 752 bufferlen = 0; 643 #ifdef USING_FRONTEND 753 644 754 if (!MSqlQuery::testDBConnection()) 645 755 { 646 756 VERBOSE(VB_IMPORTANT, LOC_ERR + "Previewer could not connect to DB."); … … 686 796 687 797 delete ctx; 688 798 689 #else // USING_FRONTEND690 QString msg = "Backend compiled without USING_FRONTEND !!!!";691 VERBOSE(VB_IMPORTANT, LOC_ERR + msg);692 #endif // USING_FRONTEND693 694 799 if (retbuf) 695 800 { 696 801 VERBOSE(VB_GENERAL, LOC + -
libs/libmythtv/tv_play.cpp
57 57 #include "mythsystemevent.h" 58 58 #include "videometadatautil.h" 59 59 #include "mythdialogbox.h" 60 #include "mythdirs.h" 60 61 61 62 #if ! HAVE_ROUND 62 63 #define round(x) ((int) ((x) + 0.5)) … … 11447 11448 */ 11448 11449 bool TV::ScreenShot(PlayerContext *ctx, long long frameNumber) 11449 11450 { 11451 QDir d; 11452 QString confdir = GetConfDir(); 11453 if (!d.mkpath(confdir)) 11454 { 11455 QString msg = tr("Screen Shot") + " " + tr("Error"); 11456 SetOSDMessage(ctx, msg); 11457 return false; 11458 } 11459 11450 11460 ctx->LockPlayingInfo(__FILE__, __LINE__); 11451 11461 if (!ctx->playingInfo) 11452 11462 { … … 11456 11466 return false; 11457 11467 } 11458 11468 11459 // TODO FIXME .mythtv isn't guaranteed to exist, and may11460 // very well belong to another frontend.11461 11469 QString outFile = 11462 QString("%1/ .mythtv/%2_%3_%4.png")11463 .arg( QDir::homePath()).arg(ctx->playingInfo->GetChanID())11470 QString("%1/%2_%3_%4.png") 11471 .arg(confdir).arg(ctx->playingInfo->GetChanID()) 11464 11472 .arg(ctx->playingInfo->GetRecordingStartTime(MythDate)) 11465 11473 .arg(frameNumber); 11466 11474 11467 11475 PreviewGenerator *previewgen = new PreviewGenerator( 11468 ctx->playingInfo, PreviewGenerator::kLocalAndRemote);11476 ctx->playingInfo, QString(), PreviewGenerator::kLocalAndRemote); 11469 11477 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 11470 11478 11471 11479 previewgen->SetPreviewTimeAsFrameNumber(frameNumber); -
libs/libmythtv/previewgeneratorqueue.cpp
1 #include <QCoreApplication> 2 #include <QFileInfo> 3 #include <QThread> 4 5 #include "previewgeneratorqueue.h" 6 #include "previewgenerator.h" 7 #include "mythcorecontext.h" 8 #include "mythcontext.h" 9 #include "remoteutil.h" 10 #include "mythdirs.h" 11 12 #define LOC QString("PreviewQueue: ") 13 #define LOC_ERR QString("PreviewQueue Error: ") 14 #define LOC_WARN QString("PreviewQueue Warning: ") 15 16 PreviewGeneratorQueue *PreviewGeneratorQueue::s_pgq = NULL; 17 18 void PreviewGeneratorQueue::CreatePreviewGeneratorQueue( 19 PreviewGenerator::Mode mode, 20 uint maxAttempts, uint minBlockSeconds) 21 { 22 s_pgq = new PreviewGeneratorQueue(mode, maxAttempts, minBlockSeconds); 23 } 24 25 void PreviewGeneratorQueue::TeardownPreviewGeneratorQueue() 26 { 27 s_pgq->exit(0); 28 s_pgq->wait(); 29 delete s_pgq; 30 } 31 32 PreviewGeneratorQueue::PreviewGeneratorQueue( 33 PreviewGenerator::Mode mode, 34 uint maxAttempts, uint minBlockSeconds) : 35 m_mode(mode), 36 m_running(0), m_maxThreads(2), 37 m_maxAttempts(maxAttempts), m_minBlockSeconds(minBlockSeconds) 38 { 39 if (PreviewGenerator::kLocal & mode) 40 { 41 int idealThreads = QThread::idealThreadCount(); 42 m_maxThreads = (idealThreads >= 1) ? idealThreads * 2 : 2; 43 } 44 45 moveToThread(this); 46 start(); 47 } 48 49 PreviewGeneratorQueue::~PreviewGeneratorQueue() 50 { 51 // disconnect preview generators 52 QMutexLocker locker(&m_lock); 53 PreviewMap::iterator it = m_previewMap.begin(); 54 for (;it != m_previewMap.end(); ++it) 55 { 56 if ((*it).gen) 57 (*it).gen->deleteLater(); 58 } 59 } 60 61 void PreviewGeneratorQueue::GetPreviewImage( 62 const ProgramInfo &pginfo, 63 const QSize &outputsize, 64 const QString &outputfile, 65 long long time, bool in_seconds, 66 QString token) 67 { 68 if (!s_pgq) 69 return; 70 71 if (pginfo.GetPathname().isEmpty() || 72 pginfo.GetBasename() == pginfo.GetPathname()) 73 { 74 return; 75 } 76 77 QStringList extra; 78 pginfo.ToStringList(extra); 79 extra += token; 80 extra += QString::number(outputsize.width()); 81 extra += QString::number(outputsize.height()); 82 extra += outputfile; 83 extra += QString::number(time); 84 extra += (in_seconds ? "1" : "0"); 85 MythEvent *e = new MythEvent("GET_PREVIEW", extra); 86 QCoreApplication::postEvent(s_pgq, e); 87 } 88 89 void PreviewGeneratorQueue::AddListener(QObject *listener) 90 { 91 if (!s_pgq) 92 return; 93 94 QMutexLocker locker(&s_pgq->m_lock); 95 s_pgq->m_listeners.insert(listener); 96 } 97 98 void PreviewGeneratorQueue::RemoveListener(QObject *listener) 99 { 100 if (!s_pgq) 101 return; 102 103 QMutexLocker locker(&s_pgq->m_lock); 104 s_pgq->m_listeners.remove(listener); 105 } 106 107 bool PreviewGeneratorQueue::event(QEvent *e) 108 { 109 if (e->type() != (QEvent::Type) MythEvent::MythEventMessage) 110 return QObject::event(e); 111 112 MythEvent *me = (MythEvent*)e; 113 if (me->Message() == "GET_PREVIEW") 114 { 115 const QStringList list = me->ExtraDataList(); 116 QStringList::const_iterator it = list.begin(); 117 ProgramInfo evinfo(it, list.end()); 118 QString token; 119 QSize outputsize; 120 QString outputfile; 121 long long time; 122 bool time_fmt_sec; 123 if (it != list.end()) 124 token = (*it++); 125 if (it != list.end()) 126 outputsize.setWidth((*it++).toInt()); 127 if (it != list.end()) 128 outputsize.setHeight((*it++).toInt()); 129 if (it != list.end()) 130 outputfile = (*it++); 131 if (it != list.end()) 132 time = (*it++).toLongLong(); 133 QString fn; 134 if (it != list.end()) 135 { 136 time_fmt_sec = (*it++).toInt() != 0; 137 fn = GeneratePreviewImage(evinfo, outputsize, outputfile, 138 time, time_fmt_sec, token); 139 } 140 return true; 141 } 142 else if (me->Message() == "PREVIEW_SUCCESS" || 143 me->Message() == "PREVIEW_FAILED") 144 { 145 QString pginfokey = me->ExtraData(0); // pginfo->MakeUniqueKey() 146 QString filename = me->ExtraData(1); // outFileName 147 QString msg = me->ExtraData(2); 148 QString datetime = me->ExtraData(3); 149 QString token = me->ExtraData(4); 150 151 { 152 QMutexLocker locker(&m_lock); 153 QMap<QString,QString>::iterator kit = m_tokenToKeyMap.find(token); 154 if (kit == m_tokenToKeyMap.end()) 155 { 156 VERBOSE(VB_IMPORTANT, LOC_ERR + 157 QString("Failed to find token %1 in map.").arg(token)); 158 return true; 159 } 160 PreviewMap::iterator it = m_previewMap.find(*kit); 161 if (it == m_previewMap.end()) 162 { 163 VERBOSE(VB_IMPORTANT, LOC_ERR + 164 QString("Failed to find key %1 in map.").arg(*kit)); 165 return true; 166 } 167 168 (*it).gen = NULL; 169 (*it).genStarted = false; 170 if (me->Message() == "PREVIEW_SUCCESS") 171 { 172 (*it).attempts = 0; 173 (*it).lastBlockTime = 0; 174 (*it).blockRetryUntil = QDateTime(); 175 } 176 else 177 { 178 (*it).lastBlockTime = 179 max(m_minBlockSeconds, (*it).lastBlockTime * 2); 180 (*it).blockRetryUntil = 181 QDateTime::currentDateTime().addSecs((*it).lastBlockTime); 182 } 183 184 QStringList list; 185 list.push_back(pginfokey); 186 list.push_back(filename); 187 list.push_back(msg); 188 list.push_back(datetime); 189 QSet<QString>::const_iterator tit = (*it).tokens.begin(); 190 for (; tit != (*it).tokens.end(); ++tit) 191 { 192 kit = m_tokenToKeyMap.find(*tit); 193 if (kit != m_tokenToKeyMap.end()) 194 m_tokenToKeyMap.erase(kit); 195 list.push_back(*tit); 196 } 197 198 QSet<QObject*>::iterator sit = m_listeners.begin(); 199 for (; sit != m_listeners.end(); ++sit) 200 { 201 MythEvent *e = new MythEvent(me->Message(), list); 202 QCoreApplication::postEvent(*sit, e); 203 } 204 (*it).tokens.clear(); 205 206 m_running--; 207 } 208 209 UpdatePreviewGeneratorThreads(); 210 211 return true; 212 } 213 return false; 214 } 215 216 void PreviewGeneratorQueue::SendEvent( 217 const ProgramInfo &pginfo, 218 const QString &eventname, 219 const QString &fn, const QString &token, const QString &msg, 220 const QDateTime &dt) 221 { 222 QStringList list; 223 list.push_back(pginfo.MakeUniqueKey()); 224 list.push_back(fn); 225 list.push_back(msg); 226 list.push_back(dt.toString(Qt::ISODate)); 227 list.push_back(token); 228 229 QMutexLocker locker(&m_lock); 230 QSet<QObject*>::iterator it = m_listeners.begin(); 231 for (; it != m_listeners.end(); ++it) 232 { 233 MythEvent *e = new MythEvent(eventname, list); 234 QCoreApplication::postEvent(*it, e); 235 } 236 } 237 238 QString PreviewGeneratorQueue::GeneratePreviewImage( 239 ProgramInfo &pginfo, 240 const QSize &size, 241 const QString &outputfile, 242 long long time, bool in_seconds, 243 QString token) 244 { 245 QString key = QString("%1_%2x%3_%4%5") 246 .arg(pginfo.GetBasename()).arg(size.width()).arg(size.height()) 247 .arg(time).arg(in_seconds?"s":"f"); 248 249 if (pginfo.GetAvailableStatus() == asPendingDelete) 250 { 251 SendEvent(pginfo, "PREVIEW_FAILED", key, token, 252 "Pending Delete", QDateTime()); 253 return QString(); 254 } 255 256 QString filename = (outputfile.isEmpty()) ? 257 pginfo.GetPathname() + ".png" : outputfile; 258 QString ret_file = filename; 259 QString ret; 260 261 bool is_special = !outputfile.isEmpty() || time >= 0 || 262 size.width() || size.height(); 263 264 bool needs_gen = true; 265 if (!is_special) 266 { 267 QDateTime previewLastModified; 268 bool streaming = filename.left(1) != "/"; 269 bool locally_accessible = false; 270 bool bookmark_updated = false; 271 272 QDateTime bookmark_ts = pginfo.QueryBookmarkTimeStamp(); 273 QDateTime cmp_ts = bookmark_ts.isValid() ? 274 bookmark_ts : pginfo.GetLastModifiedTime(); 275 276 if (streaming) 277 { 278 ret_file = QString("%1/remotecache/%2") 279 .arg(GetConfDir()).arg(filename.section('/', -1)); 280 281 QFileInfo finfo(ret_file); 282 if (finfo.isReadable() && finfo.lastModified() >= cmp_ts) 283 { 284 // This is just an optimization to avoid 285 // hitting the backend if our cached copy 286 // is newer than the bookmark, or if we have 287 // a preview and do not update it when the 288 // bookmark changes. 289 previewLastModified = finfo.lastModified(); 290 } 291 else if (!IsGeneratingPreview(key)) 292 { 293 previewLastModified = 294 RemoteGetPreviewIfModified(pginfo, ret_file); 295 } 296 } 297 else 298 { 299 QFileInfo fi(filename); 300 if ((locally_accessible = fi.isReadable())) 301 previewLastModified = fi.lastModified(); 302 } 303 304 bookmark_updated = 305 (!previewLastModified.isValid() || (previewLastModified <= cmp_ts)); 306 307 if (bookmark_updated && bookmark_ts.isValid() && 308 previewLastModified.isValid()) 309 { 310 ClearPreviewGeneratorAttempts(key); 311 } 312 313 bool preview_exists = previewLastModified.isValid(); 314 315 if (0) 316 { 317 QString alttext = (bookmark_ts.isValid()) ? QString() : 318 QString("\n\t\t\tcmp_ts: %1") 319 .arg(cmp_ts.toString(Qt::ISODate)); 320 VERBOSE(VB_IMPORTANT, QString( 321 "previewLastModified: %1\n\t\t\t" 322 "bookmark_ts: %2%3\n\t\t\t" 323 "pginfo.lastmodified: %4") 324 .arg(previewLastModified.toString(Qt::ISODate)) 325 .arg(bookmark_ts.toString(Qt::ISODate)) 326 .arg(alttext) 327 .arg(pginfo.GetLastModifiedTime(ISODate)) + 328 QString("Title: %1\n\t\t\t") 329 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)) + 330 QString("File '%1' \n\t\t\tCache '%2'") 331 .arg(filename).arg(ret_file) + 332 QString("\n\t\t\tPreview Exists: %1, " 333 "Bookmark Updated: %2, " 334 "Need Preview: %3") 335 .arg(preview_exists).arg(bookmark_updated) 336 .arg((bookmark_updated || !preview_exists))); 337 } 338 339 needs_gen = bookmark_updated || !preview_exists; 340 341 if (!needs_gen) 342 { 343 if (locally_accessible) 344 ret = filename; 345 else if (preview_exists && QFileInfo(ret_file).isReadable()) 346 ret = ret_file; 347 } 348 } 349 350 if (needs_gen && !IsGeneratingPreview(key)) 351 { 352 uint attempts = IncPreviewGeneratorAttempts(key); 353 if (attempts < m_maxAttempts) 354 { 355 VERBOSE(VB_PLAYBACK, LOC + 356 QString("Requesting preview for '%1'") 357 .arg(key)); 358 PreviewGenerator *pg = new PreviewGenerator( 359 &pginfo, token, m_mode); 360 if (!outputfile.isEmpty() || time >= 0 || 361 size.width() || size.height()) 362 { 363 pg->SetPreviewTime(time, in_seconds); 364 pg->SetOutputFilename(outputfile); 365 pg->SetOutputSize(size); 366 } 367 368 SetPreviewGenerator(key, pg); 369 370 VERBOSE(VB_PLAYBACK, LOC + 371 QString("Requested preview for '%1'").arg(key)); 372 } 373 else if (attempts >= m_maxAttempts) 374 { 375 VERBOSE(VB_IMPORTANT, LOC_ERR + 376 QString("Attempted to generate preview for '%1' " 377 "%2 times; >= max(%3)") 378 .arg(key).arg(attempts).arg(m_maxAttempts)); 379 } 380 } 381 else if (needs_gen) 382 { 383 VERBOSE(VB_PLAYBACK, LOC + 384 "Not requesting preview as it " 385 "is already being generated"); 386 IncPreviewGeneratorPriority(key, token); 387 } 388 389 UpdatePreviewGeneratorThreads(); 390 391 if (!ret.isEmpty()) 392 { 393 QString msg = "On Disk"; 394 QDateTime dt = QFileInfo(ret).lastModified(); 395 SendEvent(pginfo, "PREVIEW_SUCCESS", ret, token, msg, dt); 396 } 397 else 398 { 399 uint queue_depth, token_cnt; 400 GetInfo(key, queue_depth, token_cnt); 401 QString msg = QString("Queue depth %1, our tokens %2") 402 .arg(queue_depth).arg(token_cnt); 403 SendEvent(pginfo, "PREVIEW_QUEUED", ret, token, msg, QDateTime()); 404 } 405 406 return ret; 407 } 408 409 void PreviewGeneratorQueue::GetInfo( 410 const QString &key, uint &queue_depth, uint &token_cnt) 411 { 412 QMutexLocker locker(&m_lock); 413 queue_depth = m_queue.size(); 414 PreviewMap::iterator pit = m_previewMap.find(key); 415 token_cnt = (pit == m_previewMap.end()) ? 0 : (*pit).tokens.size(); 416 } 417 418 void PreviewGeneratorQueue::IncPreviewGeneratorPriority( 419 const QString &key, QString token) 420 { 421 QMutexLocker locker(&m_lock); 422 m_queue.removeAll(key); 423 424 PreviewMap::iterator pit = m_previewMap.find(key); 425 if (pit == m_previewMap.end()) 426 return; 427 428 if ((*pit).gen && !(*pit).genStarted) 429 m_queue.push_back(key); 430 431 if (!token.isEmpty()) 432 { 433 m_tokenToKeyMap[token] = key; 434 (*pit).tokens.insert(token); 435 } 436 } 437 438 void PreviewGeneratorQueue::UpdatePreviewGeneratorThreads(void) 439 { 440 QMutexLocker locker(&m_lock); 441 QStringList &q = m_queue; 442 if (!q.empty() && (m_running < m_maxThreads)) 443 { 444 QString fn = q.back(); 445 q.pop_back(); 446 PreviewMap::iterator it = m_previewMap.find(fn); 447 if (it != m_previewMap.end() && (*it).gen && !(*it).genStarted) 448 { 449 m_running++; 450 (*it).gen->start(); 451 (*it).genStarted = true; 452 } 453 } 454 } 455 456 /** \brief Sets the PreviewGenerator for a specific file. 457 * \return true iff call succeeded. 458 */ 459 void PreviewGeneratorQueue::SetPreviewGenerator( 460 const QString &key, PreviewGenerator *g) 461 { 462 { 463 QMutexLocker locker(&m_lock); 464 m_tokenToKeyMap[g->GetToken()] = key; 465 PreviewGenState &state = m_previewMap[key]; 466 if (state.gen) 467 { 468 if (!g->GetToken().isEmpty()) 469 state.tokens.insert(g->GetToken()); 470 g->deleteLater(); 471 } 472 else 473 { 474 g->AttachSignals(this); 475 state.gen = g; 476 state.genStarted = false; 477 if (!g->GetToken().isEmpty()) 478 state.tokens.insert(g->GetToken()); 479 } 480 } 481 482 IncPreviewGeneratorPriority(key, ""); 483 } 484 485 /** \brief Returns true if we have already started a 486 * PreviewGenerator to create this file. 487 */ 488 bool PreviewGeneratorQueue::IsGeneratingPreview(const QString &key) const 489 { 490 PreviewMap::const_iterator it; 491 QMutexLocker locker(&m_lock); 492 493 if ((it = m_previewMap.find(key)) == m_previewMap.end()) 494 return false; 495 496 if ((*it).blockRetryUntil.isValid()) 497 return QDateTime::currentDateTime() < (*it).blockRetryUntil; 498 499 return (*it).gen; 500 } 501 502 /** \fn PreviewGeneratorQueue::IncPreviewGeneratorAttempts(const QString&) 503 * \brief Increments and returns number of times we have 504 * started a PreviewGenerator to create this file. 505 */ 506 uint PreviewGeneratorQueue::IncPreviewGeneratorAttempts(const QString &key) 507 { 508 QMutexLocker locker(&m_lock); 509 return m_previewMap[key].attempts++; 510 } 511 512 /** \fn PreviewGeneratorQueue::ClearPreviewGeneratorAttempts(const QString&) 513 * \brief Clears the number of times we have 514 * started a PreviewGenerator to create this file. 515 */ 516 void PreviewGeneratorQueue::ClearPreviewGeneratorAttempts(const QString &key) 517 { 518 QMutexLocker locker(&m_lock); 519 m_previewMap[key].attempts = 0; 520 m_previewMap[key].lastBlockTime = 0; 521 m_previewMap[key].blockRetryUntil = 522 QDateTime::currentDateTime().addSecs(-60); 523 } -
libs/libmythtv/tv_rec.cpp
11 11 using namespace std; 12 12 13 13 // MythTV headers 14 #include "previewgeneratorqueue.h" 14 15 #include "mythconfig.h" 15 16 #include "tv_rec.h" 16 17 #include "osd.h" … … 25 26 #include "recordingrule.h" 26 27 #include "eitscanner.h" 27 28 #include "RingBuffer.h" 28 #include "previewgenerator.h"29 29 #include "storagegroup.h" 30 30 #include "remoteutil.h" 31 31 #include "tvremoteutil.h" … … 1143 1143 if (!killFile) 1144 1144 { 1145 1145 if (curRecording->IsLocal()) 1146 { 1147 (new PreviewGenerator( 1148 curRecording, PreviewGenerator::kLocal))->Start(); 1149 } 1146 PreviewGeneratorQueue::GetPreviewImage(*curRecording, ""); 1150 1147 1151 1148 if (!tvchain) 1152 1149 { … … 4528 4525 if (!oldinfo->IsLocal()) 4529 4526 oldinfo->SetPathname(oldinfo->GetPlaybackURL(false,true)); 4530 4527 if (oldinfo->IsLocal()) 4531 { 4532 (new PreviewGenerator( 4533 oldinfo, PreviewGenerator::kLocal))->Start(); 4534 } 4528 PreviewGeneratorQueue::GetPreviewImage(*oldinfo, ""); 4535 4529 } 4536 4530 delete oldinfo; 4537 4531 } -
libs/libmythtv/previewgenerator.h
2 2 #ifndef PREVIEW_GENERATOR_H_ 3 3 #define PREVIEW_GENERATOR_H_ 4 4 5 #include < pthread.h>6 5 #include <QWaitCondition> 6 #include <QDateTime> 7 7 #include <QString> 8 #include <QThread> 8 9 #include <QMutex> 9 10 #include <QSize> 11 #include <QMap> 12 #include <QSet> 10 13 11 14 #include "programinfo.h" 12 15 #include "util.h" 13 16 17 class PreviewGenerator; 18 class QByteArray; 14 19 class MythSocket; 20 class QObject; 21 class QEvent; 15 22 16 class MPUBLIC PreviewGenerator : public QObject 23 typedef QMap<QString,QDateTime> FileTimeStampMap; 24 25 class MPUBLIC PreviewGenerator : public QThread 17 26 { 18 27 friend int preview_helper(const QString &chanid, 19 28 const QString &starttime, … … 36 45 } Mode; 37 46 38 47 public: 39 PreviewGenerator(const ProgramInfo *pginfo, Mode mode = kLocal); 48 PreviewGenerator(const ProgramInfo *pginfo, 49 const QString &token, 50 Mode mode = kLocal); 40 51 41 52 void SetPreviewTime(long long time, bool in_seconds) 42 53 { captureTime = time; timeInSeconds = in_seconds; } … … 47 58 void SetOutputFilename(const QString&); 48 59 void SetOutputSize(const QSize &size) { outSize = size; } 49 60 50 void Start(void); 61 QString GetToken(void) const { return token; } 62 63 void run(void); // QThread 51 64 bool Run(void); 52 65 53 66 void AttachSignals(QObject*); 54 void disconnectSafe(void);55 67 56 signals:57 void previewThreadDone(const QString&, bool&);58 void previewReady(const ProgramInfo*);59 60 68 public slots: 61 69 void deleteLater(); 62 70 … … 64 72 virtual ~PreviewGenerator(); 65 73 void TeardownAll(void); 66 74 67 bool RemotePreviewSetup(void);68 75 bool RemotePreviewRun(void); 69 void RemotePreviewTeardown(void);70 71 76 bool LocalPreviewRun(void); 72 77 bool IsLocal(void) const; 73 78 74 79 bool RunReal(void); 75 80 76 static void *PreviewRun(void*);77 78 81 static char *GetScreenGrab(const ProgramInfo &pginfo, 79 82 const QString &filename, 80 83 long long seektime, … … 93 96 static QString CreateAccessibleFilename( 94 97 const QString &pathname, const QString &outFileName); 95 98 99 virtual bool event(QEvent *e); // QObject 100 bool SaveOutFile(const QByteArray &data, const QDateTime &dt); 101 96 102 protected: 103 QWaitCondition previewWaitCondition; 97 104 QMutex previewLock; 98 pthread_t previewThread;99 105 ProgramInfo programInfo; 100 106 101 107 Mode mode; 102 bool isConnected; 103 bool createSockets; 104 MythSocket *serverSock; 108 QObject *listener; 105 109 QString pathname; 106 110 107 111 /// tells us whether to use time as seconds or frame number … … 110 114 long long captureTime; 111 115 QString outFileName; 112 116 QSize outSize; 117 118 QString token; 119 bool gotReply; 120 bool pixmapOk; 113 121 }; 114 122 115 123 #endif // PREVIEW_GENERATOR_H_ -
libs/libmythui/mythuiimage.cpp
73 73 public: 74 74 ImageLoadThread(MythUIImage *parent, const QString &basefile, 75 75 const QString &filename, int number, 76 QSize forceSize ) :76 QSize forceSize, ImageCacheMode mode) : 77 77 m_parent(parent), m_basefile(basefile), 78 78 m_filename(filename), m_number(number), 79 m_ForceSize(forceSize) 79 m_ForceSize(forceSize), m_cacheMode(mode) 80 80 { 81 81 m_basefile.detach(); 82 82 m_filename.detach(); … … 95 95 96 96 if (imageReader.supportsAnimation()) 97 97 { 98 m_parent->LoadAnimatedImage(imageReader, m_filename, m_ForceSize); 98 m_parent->LoadAnimatedImage( 99 imageReader, m_filename, m_ForceSize, m_cacheMode); 99 100 } 100 101 else 101 102 { 102 MythImage *image = 103 m_parent->LoadImage(imageReader, m_filename, m_ForceSize); 104 103 MythImage *image = m_parent->LoadImage( 104 imageReader, m_filename, m_ForceSize, m_cacheMode); 105 105 ImageLoadEvent *le = new ImageLoadEvent(m_parent, image, m_basefile, 106 106 m_filename, m_number); 107 107 QCoreApplication::postEvent(m_parent, le); … … 114 114 QString m_filename; 115 115 int m_number; 116 116 QSize m_ForceSize; 117 ImageCacheMode m_cacheMode; 117 118 }; 118 119 119 120 ///////////////////////////////////////////////////////////////// … … 542 543 /** 543 544 * \brief Load the image(s), wraps LoadImage() 544 545 */ 545 bool MythUIImage::Load(bool allowLoadInBackground )546 bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat) 546 547 { 547 548 d->m_UpdateLock.lockForRead(); 548 549 … … 621 622 } 622 623 623 624 QString imagelabel; 624 ImageCacheMode cacheMode = kCacheCheckMemoryOnly;625 625 626 626 int j = 0; 627 627 for (int i = m_LowNum; i <= m_HighNum && !m_animatedImage; i++) … … 633 633 634 634 // Only load in the background if allowed and the image is 635 635 // not already in our mem cache 636 ImageCacheMode cacheMode = kCacheCheckMemoryOnly; 636 637 if (m_gradient) 637 638 cacheMode = kCacheIgnoreDisk; 638 else 639 cacheMode = kCacheCheckMemoryOnly; 639 else if (forceStat) 640 cacheMode = (ImageCacheMode) 641 ((int)kCacheCheckMemoryOnly | (int)kCacheForceStat); 642 643 ImageCacheMode cacheMode2 = (!forceStat) ? kCacheNormal : 644 (ImageCacheMode) ((int)kCacheNormal | (int)kCacheForceStat); 640 645 646 641 647 if ((allowLoadInBackground) && 642 648 (!GetMythUI()->LoadCacheImage(filename, imagelabel, cacheMode)) && 643 649 (!getenv("DISABLETHREADEDMYTHUIIMAGE"))) 644 650 { 645 651 VERBOSE(VB_GUI|VB_FILE|VB_EXTRA, LOC + QString( 646 652 "Load(), spawning thread to load '%1'").arg(filename)); 647 ImageLoadThread *bImgThread = 648 new ImageLoadThread(this, bFilename, filename, i, 649 bForceSize); 653 ImageLoadThread *bImgThread = new ImageLoadThread( 654 this, bFilename, filename, i, bForceSize, cacheMode2); 650 655 GetMythUI()->GetImageThreadPool()->start(bImgThread); 651 656 } 652 657 else … … 665 670 666 671 if (imageReader.supportsAnimation()) 667 672 { 668 LoadAnimatedImage(imageReader, filename, bForceSize); 673 LoadAnimatedImage( 674 imageReader, filename, bForceSize, cacheMode2); 669 675 } 670 676 else 671 677 { 672 MythImage *image = LoadImage(imageReader, filename, bForceSize); 678 MythImage *image = LoadImage( 679 imageReader, filename, bForceSize, cacheMode2); 673 680 if (image) 674 681 { 675 682 if (bForceSize.isNull()) … … 701 708 /** 702 709 * \brief Load an image 703 710 */ 704 MythImage *MythUIImage::LoadImage(MythImageReader &imageReader, 705 const QString &imFile, QSize bForceSize) 711 MythImage *MythUIImage::LoadImage( 712 MythImageReader &imageReader, const QString &imFile, 713 QSize bForceSize, int cacheMode) 706 714 { 707 715 QString filename = imFile; 708 716 … … 754 762 imagelabel = GenImageLabel(filename, w, h); 755 763 756 764 if (!imageReader.supportsAnimation()) 757 image = GetMythUI()->LoadCacheImage(filename, imagelabel); 765 { 766 image = GetMythUI()->LoadCacheImage( 767 filename, imagelabel, (ImageCacheMode) cacheMode); 768 } 758 769 759 770 if (image) 760 771 { … … 882 893 /** 883 894 * \brief Load an animated image 884 895 */ 885 bool MythUIImage::LoadAnimatedImage(MythImageReader &imageReader, 886 const QString &imFile, QSize bForceSize) 896 bool MythUIImage::LoadAnimatedImage( 897 MythImageReader &imageReader, const QString &imFile, 898 QSize bForceSize, int cacheMode) 887 899 { 888 900 bool result = false; 889 901 m_loadingImagesLock.lock(); … … 930 942 { 931 943 frameFilename = filename.arg(imageCount); 932 944 imageLabel = GenImageLabel(frameFilename, w, h); 933 MythImage *im = LoadImage(imageReader, frameFilename, bForceSize); 945 MythImage *im = LoadImage( 946 imageReader, frameFilename, 947 bForceSize, (ImageCacheMode) cacheMode); 934 948 935 949 if (!im) 936 950 break; -
libs/libmythui/mythuihelper.h
23 23 typedef enum ImageCacheMode 24 24 { 25 25 kCacheNormal = 0x0, 26 kCacheIgnoreDisk, 27 kCacheCheckMemoryOnly 26 kCacheIgnoreDisk = 0x1, 27 kCacheCheckMemoryOnly = 0x2, 28 kCacheForceStat = 0x4, 28 29 } ImageCacheMode; 29 30 30 31 struct MPUBLIC MythUIMenuCallbacks -
libs/libmythui/mythuihelper.cpp
1395 1395 if (srcfile.isEmpty() || label.isEmpty()) 1396 1396 return NULL; 1397 1397 1398 // Some screens include certain images dozens or even hundreds of 1399 // times. Even if the image is in the cache, there is still a 1400 // stat system call on the original file to see if it has changed. 1401 // This code relaxes the original-file check so that the check 1402 // isn't repeated if it was already done within kImageCacheTimeout 1403 // seconds. 1404 const uint kImageCacheTimeout = 5; 1405 uint now = QDateTime::currentDateTime().toTime_t(); 1406 if (d->imageCache.contains(label) && 1407 d->CacheTrack[label] + kImageCacheTimeout > now) 1398 if (!(kCacheForceStat & cacheMode)) 1408 1399 { 1409 return d->imageCache[label]; 1400 // Some screens include certain images dozens or even hundreds of 1401 // times. Even if the image is in the cache, there is still a 1402 // stat system call on the original file to see if it has changed. 1403 // This code relaxes the original-file check so that the check 1404 // isn't repeated if it was already done within kImageCacheTimeout 1405 // seconds. 1406 const uint kImageCacheTimeout = 5; 1407 uint now = QDateTime::currentDateTime().toTime_t(); 1408 if (d->imageCache.contains(label) && 1409 d->CacheTrack[label] + kImageCacheTimeout > now) 1410 { 1411 return d->imageCache[label]; 1412 } 1410 1413 } 1411 1414 1412 1415 QString cachefilepath = GetThemeCacheDir() + '/' + label; … … 1414 1417 1415 1418 MythImage *ret = NULL; 1416 1419 1417 if ( (cacheMode ==kCacheIgnoreDisk) || fi.exists())1420 if (!!(cacheMode & kCacheIgnoreDisk) || fi.exists()) 1418 1421 { 1419 1422 // Now compare the time on the source versus our cached copy 1420 if ( cacheMode != kCacheIgnoreDisk)1423 if (!(cacheMode & kCacheIgnoreDisk)) 1421 1424 FindThemeFile(srcfile); 1422 1425 1423 1426 QDateTime srcLastModified; … … 1435 1438 else if (original.exists()) 1436 1439 srcLastModified = original.lastModified(); 1437 1440 1438 if ( (cacheMode ==kCacheIgnoreDisk) ||1441 if (!!(cacheMode & kCacheIgnoreDisk) || 1439 1442 (fi.lastModified() > srcLastModified)) 1440 1443 { 1441 1444 // Check Memory Cache 1442 1445 ret = GetImageFromCache(label); 1443 if (!ret && ( cacheMode == kCacheNormal))1446 if (!ret && (!!(kCacheNormal & cacheMode))) 1444 1447 { 1445 1448 // Load file from disk cache to memory cache 1446 1449 ret = GetMythPainter()->GetFormatImage(); -
libs/libmythui/mythuiimage.h
51 51 void SetDelays(QVector<int> delays); 52 52 53 53 void Reset(void); 54 bool Load(bool allowLoadInBackground = true );54 bool Load(bool allowLoadInBackground = true, bool forceStat = false); 55 55 56 56 bool IsGradient(void) const { return m_gradient; } 57 57 … … 66 66 void Init(void); 67 67 void Clear(void); 68 68 MythImage* LoadImage(MythImageReader &imageReader, const QString &imFile, 69 QSize bForceSize );69 QSize bForceSize, int cacheMode); 70 70 bool LoadAnimatedImage(MythImageReader &imageReader, const QString &imFile, 71 QSize bForceSize );71 QSize bForceSize, int mode); 72 72 void customEvent(QEvent *event); 73 73 74 74 virtual bool ParseElement( -
libs/libmyth/remoteutil.cpp
354 354 return retdatetime; 355 355 } 356 356 357 size_t length = strlist[1].to LongLong();357 size_t length = strlist[1].toULongLong(); 358 358 quint16 checksum16 = strlist[2].toUInt(); 359 359 QByteArray data = QByteArray::fromBase64(strlist[3].toAscii()); 360 360 if ((size_t) data.size() < length) … … 364 364 .arg(data.size()).arg(length)); 365 365 return QDateTime(); 366 366 } 367 data.resize(length); 367 368 368 369 if (checksum16 != qChecksum(data.constData(), data.size())) 369 370 { -
libs/libmythdb/mythversion.h
30 30 * mythtv/bindings/python/MythTV/static.py (version number) 31 31 * mythtv/bindings/python/MythTV/mythproto.py (layout) 32 32 */ 33 #define MYTH_PROTO_VERSION " 59"33 #define MYTH_PROTO_VERSION "60" 34 34 35 35 MPUBLIC const char *GetMythSourceVersion(); 36 36 -
programs/mythfrontend/playbackbox.cpp
8 8 #include <QTimer> 9 9 #include <QMap> 10 10 11 // libmythdb12 #include " oldsettings.h"13 #include "myth db.h"14 #include "myth dbcon.h"15 #include "myth verbose.h"16 #include "myth dirs.h"17 18 // libmythtv 19 #include " tv.h"20 #include "myth player.h"11 // MythTV 12 #include "previewgeneratorqueue.h" 13 #include "mythuiprogressbar.h" 14 #include "mythuibuttonlist.h" 15 #include "mythcorecontext.h" 16 #include "mythsystemevent.h" 17 #include "mythuistatetype.h" 18 #include "mythuicheckbox.h" 19 #include "mythuitextedit.h" 20 #include "mythdialogbox.h" 21 21 #include "recordinginfo.h" 22 #include "playgroup.h" 23 #include "mythsystemevent.h" 24 25 // libmyth 26 #include "mythcorecontext.h" 27 #include "util.h" 22 #include "mythuihelper.h" 28 23 #include "storagegroup.h" 24 #include "mythuibutton.h" 25 #include "mythverbose.h" 26 #include "mythuiimage.h" 29 27 #include "programinfo.h" 30 31 // libmythui 32 #include "mythuihelper.h" 28 #include "oldsettings.h" 29 #include "mythplayer.h" 33 30 #include "mythuitext.h" 34 #include "mythuibutton.h"35 #include "mythuibuttonlist.h"36 #include "mythuistatetype.h"37 #include "mythdialogbox.h"38 #include "mythuitextedit.h"39 #include "mythuiimage.h"40 #include "mythuicheckbox.h"41 #include "mythuiprogressbar.h"42 43 31 #include "remoteutil.h" 44 32 45 33 // Mythfrontend 46 34 #include "playbackboxlistitem.h" 47 35 #include "customedit.h" 36 #include "mythdbcon.h" 37 #include "playgroup.h" 38 #include "mythdirs.h" 39 #include "proglist.h" 40 #include "mythdb.h" 41 #include "util.h" 42 #include "tv.h" 48 43 49 44 #define LOC QString("PlaybackBox: ") 50 45 #define LOC_WARN QString("PlaybackBox Warning: ") … … 444 439 PlaybackBox::~PlaybackBox(void) 445 440 { 446 441 gCoreContext->removeListener(this); 442 PreviewGeneratorQueue::RemoveListener(this); 447 443 448 444 for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++) 449 445 { … … 514 510 void PlaybackBox::Load(void) 515 511 { 516 512 m_programInfoCache.WaitForLoadToComplete(); 513 PreviewGeneratorQueue::AddListener(this); 517 514 } 518 515 519 516 void PlaybackBox::Init() … … 676 673 jobState->Reset(); 677 674 678 675 if (m_previewImage) 676 { 677 VERBOSE(VB_IMPORTANT, "m_previewImage->Reset()"); 679 678 m_previewImage->Reset(); 679 } 680 680 681 681 if (m_artImage[kArtworkBanner]) 682 682 m_artImage[kArtworkBanner]->Reset(); … … 735 735 item->DisplayState(disp_flag_stat[i]?"yes":"no", disp_flags[i]); 736 736 } 737 737 738 QHash<MythUIButtonListItem*,QTime> xmap; 739 738 740 void PlaybackBox::UpdateUIListItem( 739 741 MythUIButtonListItem *item, bool is_sel, bool force_preview_reload) 740 742 { … … 746 748 if (!pginfo) 747 749 return; 748 750 751 VERBOSE(VB_IMPORTANT, LOC + 752 QString("UpdateUIListItem(%1, %2%3) -- begin 0x%4") 753 .arg(pginfo->toString(ProgramInfo::kTitleSubtitle)) 754 .arg(is_sel?"sel":"not sel") 755 .arg(force_preview_reload?", force preview":"") 756 .arg((intptr_t)QThread::currentThread(),0,16)); 757 758 QTime &tm = xmap[item]; 759 if (tm.isValid() && tm.addMSecs(1000) > QTime::currentTime()) 760 { 761 tm = QTime::currentTime(); // just something to breakpoint on 762 } 763 tm = QTime::currentTime(); 764 749 765 QString state = extract_main_state(*pginfo, m_player); 750 766 751 767 // Update the text, e.g. Title or subtitle may have been changed on another … … 787 803 788 804 QString oldimgfile = item->GetImage("preview"); 789 805 if (oldimgfile.isEmpty() || force_preview_reload) 790 m_ helper.GetPreviewImage(*pginfo);806 m_preview_tokens.insert(m_helper.GetPreviewImage(*pginfo)); 791 807 792 808 if ((GetFocusWidget() == m_recordingList) && is_sel) 793 809 { … … 810 826 811 827 if (m_previewImage) 812 828 { 829 VERBOSE(VB_IMPORTANT, QString("m_previewImage->SetFilename(%1) 2") 830 .arg(oldimgfile)); 813 831 m_previewImage->SetFilename(oldimgfile); 814 m_previewImage->Load( );832 m_previewImage->Load(true, true); 815 833 } 816 834 817 835 // Handle artwork … … 845 863 846 864 updateIcons(pginfo); 847 865 } 866 867 VERBOSE(VB_IMPORTANT, LOC + QString("UpdateUIListItem(%1, %2%3) -- end") 868 .arg(pginfo->toString(ProgramInfo::kTitleSubtitle)) 869 .arg(is_sel?"sel":"not sel") 870 .arg(force_preview_reload?", force preview":"")); 848 871 } 849 872 850 873 /** \brief Updates the UI properties for a new preview file. … … 853 876 * a preview image UI item in the theme that it's filename property 854 877 * gets updated as well. 855 878 */ 856 void PlaybackBox::HandlePreviewEvent( 857 const QString &piKey, const QString &previewFile) 879 void PlaybackBox::HandlePreviewEvent(const QStringList &list) 858 880 { 881 if (list.size() < 5) 882 { 883 VERBOSE(VB_IMPORTANT, "HandlePreviewEvent() -- too few args"); 884 for (uint i = 0; i < (uint) list.size(); i++) 885 { 886 VERBOSE(VB_IMPORTANT, QString("%1: %2") 887 .arg(i).arg(list[i])); 888 } 889 return; 890 } 891 892 const QString piKey = list[0]; 893 const QString previewFile = list[1]; 894 const QString message = list[2]; 895 896 VERBOSE(VB_IMPORTANT, "HandlePreviewEvent()"); 897 898 bool found = false; 899 for (uint i = 4; i < (uint) list.size(); i++) 900 { 901 QString token = list[i]; 902 QSet<QString>::iterator it = m_preview_tokens.find(token); 903 if (it != m_preview_tokens.end()) 904 { 905 found = true; 906 m_preview_tokens.erase(it); 907 } 908 } 909 910 if (!found) 911 { 912 QString tokens("\n\t\t\ttokens: "); 913 for (uint i = 4; i < (uint) list.size(); i++) 914 tokens += list[i] + ", "; 915 VERBOSE(VB_IMPORTANT, LOC + 916 "Ignoring PREVIEW_SUCCESS, no matcing token" + tokens); 917 return; 918 } 919 859 920 if (previewFile.isEmpty()) 921 { 922 VERBOSE(VB_IMPORTANT, LOC_ERR + 923 "Ignoring PREVIEW_SUCCESS, no preview file."); 860 924 return; 925 } 861 926 862 927 ProgramInfo *info = m_programInfoCache.GetProgramInfo(piKey); 863 928 MythUIButtonListItem *item = NULL; … … 865 930 if (info) 866 931 item = m_recordingList->GetItemByData(qVariantFromValue(info)); 867 932 933 if (!item) 934 { 935 VERBOSE(VB_IMPORTANT, LOC_ERR + 936 "Ignoring PREVIEW_SUCCESS, item no longer on screen."); 937 } 938 868 939 if (item) 869 940 { 941 VERBOSE(VB_GUI, LOC + 942 QString("Loading preview %1,\n\t\t\tmsg %2") 943 .arg(previewFile).arg(message)); 944 870 945 item->SetImage(previewFile, "preview", true); 871 946 872 947 if ((GetFocusWidget() == m_recordingList) && 873 948 (m_recordingList->GetItemCurrent() == item) && 874 949 m_previewImage) 875 950 { 951 VERBOSE(VB_IMPORTANT, QString("m_previewImage->SetFilename(%1) 1") 952 .arg(previewFile)); 876 953 m_previewImage->SetFilename(previewFile); 877 m_previewImage->Load( );954 m_previewImage->Load(true, true); 878 955 } 879 956 } 880 957 } … … 2249 2326 QCoreApplication::postEvent( 2250 2327 this, new MythEvent("PLAY_PLAYLIST")); 2251 2328 } 2329 else 2330 { 2331 // User may have saved or deleted a bookmark, 2332 // requiring update of preview.. 2333 ProgramInfo *pginfo = m_programInfoCache.GetProgramInfo( 2334 tvrec.GetChanID(), tvrec.GetRecordingStartTime()); 2335 if (pginfo) 2336 UpdateUIListItem(pginfo, true); 2337 } 2252 2338 2253 2339 return playCompleted; 2254 2340 } … … 3793 3879 // asPendingDelete, we need to put them back now.. 3794 3880 ScheduleUpdateUIList(); 3795 3881 } 3796 else if (message == "PREVIEW_ READY" && me->ExtraDataCount() == 2)3882 else if (message == "PREVIEW_SUCCESS") 3797 3883 { 3798 HandlePreviewEvent(me->ExtraData (0), me->ExtraData(1));3884 HandlePreviewEvent(me->ExtraDataList()); 3799 3885 } 3886 else if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 5) 3887 { 3888 for (uint i = 4; i < (uint) me->ExtraDataCount(); i++) 3889 { 3890 QString token = me->ExtraData(i); 3891 QSet<QString>::iterator it = m_preview_tokens.find(token); 3892 if (it != m_preview_tokens.end()) 3893 m_preview_tokens.erase(it); 3894 } 3895 } 3800 3896 else if (message == "AVAILABILITY" && me->ExtraDataCount() == 8) 3801 3897 { 3802 3898 const uint kMaxUIWaitTime = 100; // ms … … 4013 4109 { 4014 4110 ProgramInfo *dst = FindProgramInUILists(evinfo); 4015 4111 if (dst) 4016 UpdateUIListItem(dst, false);4112 UpdateUIListItem(dst, true); 4017 4113 return; 4018 4114 } 4019 4115 -
programs/mythfrontend/playbackboxhelper.h
17 17 class QObject; 18 18 class QTimer; 19 19 20 class PreviewGenState21 {22 public:23 PreviewGenState() :24 gen(NULL), genStarted(false), ready(false),25 attempts(0), lastBlockTime(0) {}26 PreviewGenerator *gen;27 bool genStarted;28 bool ready;29 uint attempts;30 uint lastBlockTime;31 QDateTime blockRetryUntil;32 33 static const uint maxAttempts;34 static const uint minBlockSeconds;35 };36 typedef QMap<QString,PreviewGenState> PreviewMap;37 typedef QMap<QString,QDateTime> FileTimeStampMap;38 39 20 typedef enum CheckAvailabilityType { 40 21 kCheckForCache, 41 22 kCheckForMenuAction, … … 72 53 void UndeleteRecording(uint chanid, const QDateTime &recstartts); 73 54 void CheckAvailability(const ProgramInfo&, 74 55 CheckAvailabilityType cat = kCheckForCache); 75 voidGetPreviewImage(const ProgramInfo&);56 QString GetPreviewImage(const ProgramInfo&); 76 57 77 58 QString LocateArtwork(const QString &seriesid, const QString &title, 78 59 ArtworkType, const QString &host, … … 83 64 uint64_t GetFreeSpaceTotalMB(void) const; 84 65 uint64_t GetFreeSpaceUsedMB(void) const; 85 66 86 private slots:87 void previewThreadDone(const QString &fn, bool &success);88 void previewReady(const ProgramInfo *pginfo);89 90 67 private: 91 68 void UpdateFreeSpace(void); 92 69 93 QString GeneratePreviewImage(ProgramInfo &pginfo);94 bool SetPreviewGenerator(const QString &fn, PreviewGenerator *g);95 void IncPreviewGeneratorPriority(const QString &fn);96 void UpdatePreviewGeneratorThreads(void);97 bool IsGeneratingPreview(const QString &fn, bool really = false) const;98 uint IncPreviewGeneratorAttempts(const QString &fn);99 void ClearPreviewGeneratorAttempts(const QString &fn);100 101 70 private: 102 71 QObject *m_listener; 103 72 PBHEventHandler *m_eventHandler; … … 107 76 uint64_t m_freeSpaceTotalMB; 108 77 uint64_t m_freeSpaceUsedMB; 109 78 110 // Preview Pixmap Variables ///////////////////////////////////////////////111 mutable QMutex m_previewGeneratorLock;112 uint m_previewGeneratorMode;113 FileTimeStampMap m_previewFileTS;114 bool m_previewSuspend;115 PreviewMap m_previewGenerator;116 QStringList m_previewGeneratorQueue;117 uint m_previewGeneratorRunning;118 uint m_previewGeneratorMaxThreads;119 120 79 // Artwork Variables ////////////////////////////////////////////////////// 121 80 QHash<QString, QString> m_artworkFilenameCache; 122 81 }; -
programs/mythfrontend/playbackboxlistitem.cpp
9 9 pbbox(parent), needs_update(true) 10 10 { 11 11 } 12 12 /* 13 13 void PlaybackBoxListItem::SetToRealButton( 14 14 MythUIStateType *button, bool selected) 15 15 { … … 20 20 } 21 21 MythUIButtonListItem::SetToRealButton(button, selected); 22 22 } 23 */ -
programs/mythfrontend/playbackboxhelper.cpp
7 7 #include <QFileInfo> 8 8 #include <QDir> 9 9 10 #include "previewgeneratorqueue.h" 10 11 #include "playbackboxhelper.h" 11 #include "previewgenerator.h"12 12 #include "mythcorecontext.h" 13 13 #include "tvremoteutil.h" 14 14 #include "storagegroup.h" … … 277 277 } 278 278 else if (me->Message() == "GET_PREVIEW") 279 279 { 280 ProgramInfo evinfo(me->ExtraDataList()); 280 QString token = me->ExtraData(0); 281 QStringList list = me->ExtraDataList(); 282 QStringList::const_iterator it = list.begin()+1; 283 ProgramInfo evinfo(it, list.end()); 281 284 if (!evinfo.HasPathname()) 282 285 return true; 283 286 284 QStringList list;287 list.clear(); 285 288 evinfo.ToStringList(list); 286 289 list += QString::number(kCheckForCache); 287 290 if (asAvailable != CheckAvailability(list)) 288 291 return true; 289 292 290 QString fn = m_pbh.GeneratePreviewImage(evinfo); 291 if (!fn.isEmpty()) 292 { 293 QStringList list; 294 list.push_back(evinfo.MakeUniqueKey()); 295 list.push_back(fn); 296 MythEvent *e = new MythEvent("PREVIEW_READY", list); 297 QCoreApplication::postEvent(m_pbh.m_listener, e); 298 } 293 // Now we can actually request the preview... 294 PreviewGeneratorQueue::GetPreviewImage(evinfo, token); 299 295 300 296 return true; 301 297 } … … 458 454 459 455 ////////////////////////////////////////////////////////////////////// 460 456 461 const uint PreviewGenState::maxAttempts = 5;462 const uint PreviewGenState::minBlockSeconds = 60;463 464 457 PlaybackBoxHelper::PlaybackBoxHelper(QObject *listener) : 465 458 m_listener(listener), m_eventHandler(NULL), 466 459 // Free Space Tracking Variables 467 m_freeSpaceTotalMB(0ULL), m_freeSpaceUsedMB(0ULL), 468 // Preview Image Variables 469 m_previewGeneratorRunning(0), m_previewGeneratorMaxThreads(2) 460 m_freeSpaceTotalMB(0ULL), m_freeSpaceUsedMB(0ULL) 470 461 { 471 m_previewGeneratorMode = PreviewGenerator::kRemote;472 473 int idealThreads = QThread::idealThreadCount();474 if (idealThreads >= 1)475 m_previewGeneratorMaxThreads = idealThreads * 2;476 477 462 start(); 478 463 } 479 464 … … 482 467 exit(); 483 468 wait(); 484 469 485 // disconnect preview generators486 QMutexLocker locker(&m_previewGeneratorLock);487 PreviewMap::iterator it = m_previewGenerator.begin();488 for (;it != m_previewGenerator.end(); ++it)489 {490 if ((*it).gen)491 (*it).gen->disconnectSafe();492 }493 494 470 // delete the event handler 495 471 delete m_eventHandler; 496 472 m_eventHandler = NULL; … … 620 596 return QString(); 621 597 } 622 598 623 voidPlaybackBoxHelper::GetPreviewImage(const ProgramInfo &pginfo)599 QString PlaybackBoxHelper::GetPreviewImage(const ProgramInfo &pginfo) 624 600 { 625 QStringList extra;626 pginfo.ToStringList(extra);627 MythEvent *e = new MythEvent("GET_PREVIEW", extra);628 QCoreApplication::postEvent(m_eventHandler, e);629 }630 631 QString PlaybackBoxHelper::GeneratePreviewImage(ProgramInfo &pginfo)632 {633 601 if (pginfo.GetAvailableStatus() == asPendingDelete) 634 602 return QString(); 635 603 636 QString filename = pginfo.GetPathname() + ".png"; 604 QString token = QString("%1:%2") 605 .arg(pginfo.MakeUniqueKey()).arg(rand()); 637 606 638 // If someone is asking for this preview it must be on screen639 // and hence higher priority than anything else we may have640 // queued up recently....641 IncPreviewGeneratorPriority(filename);607 QStringList extra(token); 608 pginfo.ToStringList(extra); 609 MythEvent *e = new MythEvent("GET_PREVIEW", extra); 610 QCoreApplication::postEvent(m_eventHandler, e); 642 611 643 QDateTime previewLastModified; 644 QString ret_file = filename; 645 bool streaming = filename.left(1) != "/"; 646 bool locally_accessible = false; 647 bool bookmark_updated = false; 648 649 QDateTime bookmark_ts = pginfo.QueryBookmarkTimeStamp(); 650 QDateTime cmp_ts = bookmark_ts.isValid() ? 651 bookmark_ts : pginfo.GetLastModifiedTime(); 652 653 if (streaming) 654 { 655 ret_file = QString("%1/remotecache/%2") 656 .arg(GetConfDir()).arg(filename.section('/', -1)); 657 658 QFileInfo finfo(ret_file); 659 if (finfo.isReadable() && finfo.lastModified() >= cmp_ts) 660 { 661 // This is just an optimization to avoid 662 // hitting the backend if our cached copy 663 // is newer than the bookmark, or if we have 664 // a preview and do not update it when the 665 // bookmark changes. 666 previewLastModified = finfo.lastModified(); 667 } 668 else if (!IsGeneratingPreview(filename)) 669 { 670 previewLastModified = 671 RemoteGetPreviewIfModified(pginfo, ret_file); 672 } 673 } 674 else 675 { 676 QFileInfo fi(filename); 677 if ((locally_accessible = fi.isReadable())) 678 previewLastModified = fi.lastModified(); 679 } 680 681 bookmark_updated = 682 (!previewLastModified.isValid() || (previewLastModified < cmp_ts)); 683 684 if (bookmark_updated && bookmark_ts.isValid() && 685 previewLastModified.isValid()) 686 { 687 ClearPreviewGeneratorAttempts(filename); 688 } 689 690 if (0) 691 { 692 VERBOSE(VB_IMPORTANT, QString( 693 "previewLastModified: %1\n\t\t\t" 694 "bookmark_ts: %2\n\t\t\t" 695 "pginfo.lastmodified: %3") 696 .arg(previewLastModified.toString(Qt::ISODate)) 697 .arg(bookmark_ts.toString(Qt::ISODate)) 698 .arg(pginfo.GetLastModifiedTime(ISODate))); 699 } 700 701 bool preview_exists = previewLastModified.isValid(); 702 703 if (0) 704 { 705 VERBOSE(VB_IMPORTANT, 706 QString("Title: %1\n\t\t\t") 707 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)) + 708 QString("File '%1' \n\t\t\tCache '%2'") 709 .arg(filename).arg(ret_file) + 710 QString("\n\t\t\tPreview Exists: %1, " 711 "Bookmark Updated: %2, " 712 "Need Preview: %3") 713 .arg(preview_exists).arg(bookmark_updated) 714 .arg((bookmark_updated || !preview_exists))); 715 } 716 717 if ((bookmark_updated || !preview_exists) && 718 !IsGeneratingPreview(filename)) 719 { 720 uint attempts = IncPreviewGeneratorAttempts(filename); 721 if (attempts < PreviewGenState::maxAttempts) 722 { 723 VERBOSE(VB_PLAYBACK, LOC + 724 QString("Requesting preview for '%1'") 725 .arg(filename)); 726 PreviewGenerator::Mode mode = 727 (PreviewGenerator::Mode) m_previewGeneratorMode; 728 PreviewGenerator *pg = new PreviewGenerator(&pginfo, mode); 729 while (!SetPreviewGenerator(filename, pg)) usleep(50000); 730 VERBOSE(VB_PLAYBACK, LOC + 731 QString("Requested preview for '%1'") 732 .arg(filename)); 733 } 734 else if (attempts == PreviewGenState::maxAttempts) 735 { 736 VERBOSE(VB_IMPORTANT, LOC_ERR + 737 QString("Attempted to generate preview for '%1' " 738 "%2 times, giving up.") 739 .arg(filename).arg(PreviewGenState::maxAttempts)); 740 } 741 } 742 else if (bookmark_updated || !preview_exists) 743 { 744 VERBOSE(VB_PLAYBACK, LOC + 745 "Not requesting preview as it " 746 "is already being generated"); 747 } 748 749 UpdatePreviewGeneratorThreads(); 750 751 QString ret = (locally_accessible) ? 752 filename : (previewLastModified.isValid()) ? 753 ret_file : (QFileInfo(ret_file).isReadable()) ? 754 ret_file : QString(); 755 756 //VERBOSE(VB_IMPORTANT, QString("Returning: '%1'").arg(ret)); 757 758 return ret; 612 return token; 759 613 } 760 761 void PlaybackBoxHelper::IncPreviewGeneratorPriority(const QString &xfn)762 {763 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));764 765 QMutexLocker locker(&m_previewGeneratorLock);766 m_previewGeneratorQueue.removeAll(fn);767 768 PreviewMap::iterator pit = m_previewGenerator.find(fn);769 if (pit != m_previewGenerator.end() && (*pit).gen && !(*pit).genStarted)770 m_previewGeneratorQueue.push_back(fn);771 }772 773 void PlaybackBoxHelper::UpdatePreviewGeneratorThreads(void)774 {775 QMutexLocker locker(&m_previewGeneratorLock);776 QStringList &q = m_previewGeneratorQueue;777 if (!q.empty() &&778 (m_previewGeneratorRunning < m_previewGeneratorMaxThreads))779 {780 QString fn = q.back();781 q.pop_back();782 PreviewMap::iterator it = m_previewGenerator.find(fn);783 if (it != m_previewGenerator.end() && (*it).gen && !(*it).genStarted)784 {785 m_previewGeneratorRunning++;786 (*it).gen->Start();787 (*it).genStarted = true;788 }789 }790 }791 792 /** \fn PlaybackBoxHelper::SetPreviewGenerator(const QString&, PreviewGenerator*)793 * \brief Sets the PreviewGenerator for a specific file.794 * \return true iff call succeeded.795 */796 bool PlaybackBoxHelper::SetPreviewGenerator(const QString &xfn, PreviewGenerator *g)797 {798 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));799 800 if (!m_previewGeneratorLock.tryLock())801 return false;802 803 if (!g)804 {805 m_previewGeneratorRunning = max(0, (int)m_previewGeneratorRunning - 1);806 PreviewMap::iterator it = m_previewGenerator.find(fn);807 if (it == m_previewGenerator.end())808 {809 m_previewGeneratorLock.unlock();810 return false;811 }812 813 (*it).gen = NULL;814 (*it).genStarted = false;815 (*it).ready = false;816 (*it).lastBlockTime =817 max(PreviewGenState::minBlockSeconds, (*it).lastBlockTime * 2);818 (*it).blockRetryUntil =819 QDateTime::currentDateTime().addSecs((*it).lastBlockTime);820 821 m_previewGeneratorLock.unlock();822 return true;823 }824 825 g->AttachSignals(this);826 m_previewGenerator[fn].gen = g;827 m_previewGenerator[fn].genStarted = false;828 m_previewGenerator[fn].ready = false;829 830 m_previewGeneratorLock.unlock();831 IncPreviewGeneratorPriority(xfn);832 833 return true;834 }835 836 /** \fn PlaybackBoxHelper::IsGeneratingPreview(const QString&, bool) const837 * \brief Returns true if we have already started a838 * PreviewGenerator to create this file.839 */840 bool PlaybackBoxHelper::IsGeneratingPreview(const QString &xfn, bool really) const841 {842 PreviewMap::const_iterator it;843 QMutexLocker locker(&m_previewGeneratorLock);844 845 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));846 if ((it = m_previewGenerator.find(fn)) == m_previewGenerator.end())847 return false;848 849 if (really)850 return ((*it).gen && !(*it).ready);851 852 if ((*it).blockRetryUntil.isValid())853 return QDateTime::currentDateTime() < (*it).blockRetryUntil;854 855 return (*it).gen;856 }857 858 /** \fn PlaybackBoxHelper::IncPreviewGeneratorAttempts(const QString&)859 * \brief Increments and returns number of times we have860 * started a PreviewGenerator to create this file.861 */862 uint PlaybackBoxHelper::IncPreviewGeneratorAttempts(const QString &xfn)863 {864 QMutexLocker locker(&m_previewGeneratorLock);865 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));866 return m_previewGenerator[fn].attempts++;867 }868 869 /** \fn PlaybackBoxHelper::ClearPreviewGeneratorAttempts(const QString&)870 * \brief Clears the number of times we have871 * started a PreviewGenerator to create this file.872 */873 void PlaybackBoxHelper::ClearPreviewGeneratorAttempts(const QString &xfn)874 {875 QMutexLocker locker(&m_previewGeneratorLock);876 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));877 m_previewGenerator[fn].attempts = 0;878 m_previewGenerator[fn].lastBlockTime = 0;879 m_previewGenerator[fn].blockRetryUntil =880 QDateTime::currentDateTime().addSecs(-60);881 }882 883 void PlaybackBoxHelper::previewThreadDone(const QString &fn, bool &success)884 {885 VERBOSE(VB_PLAYBACK, LOC + QString("Preview for '%1' done").arg(fn));886 success = SetPreviewGenerator(fn, NULL);887 UpdatePreviewGeneratorThreads();888 }889 890 /** \fn PlaybackBoxHelper::previewReady(const ProgramInfo*)891 * \brief Callback used by PreviewGenerator to tell us a m_preview892 * we requested has been returned from the backend.893 * \param pginfo ProgramInfo describing the previewed recording.894 */895 void PlaybackBoxHelper::previewReady(const ProgramInfo *pginfo)896 {897 if (!pginfo)898 return;899 900 QString xfn = pginfo->GetPathname() + ".png";901 QString fn = xfn.mid(max(xfn.lastIndexOf('/') + 1,0));902 903 VERBOSE(VB_PLAYBACK, LOC + QString("Preview for '%1' ready")904 .arg(pginfo->GetPathname()));905 906 m_previewGeneratorLock.lock();907 PreviewMap::iterator it = m_previewGenerator.find(fn);908 if (it != m_previewGenerator.end())909 {910 (*it).ready = true;911 (*it).attempts = 0;912 (*it).lastBlockTime = 0;913 }914 m_previewGeneratorLock.unlock();915 916 if (pginfo)917 {918 QStringList list;919 list.push_back(pginfo->MakeUniqueKey());920 list.push_back(xfn);921 MythEvent *e = new MythEvent("PREVIEW_READY", list);922 QCoreApplication::postEvent(m_listener, e);923 }924 } -
programs/mythfrontend/playbackboxlistitem.h
14 14 public: 15 15 PlaybackBoxListItem(PlaybackBox *parent, MythUIButtonList *lbtype, ProgramInfo *pi); 16 16 17 virtual void SetToRealButton(MythUIStateType *button, bool selected);17 // virtual void SetToRealButton(MythUIStateType *button, bool selected); 18 18 19 19 private: 20 20 PlaybackBox *pbbox; -
programs/mythfrontend/main.cpp
19 19 #include <QApplication> 20 20 #include <QTimer> 21 21 22 #include "previewgeneratorqueue.h" 22 23 #include "mythconfig.h" 23 24 #include "tv.h" 24 25 #include "proglist.h" … … 1458 1459 1459 1460 BackendConnectionManager bcm; 1460 1461 1462 PreviewGeneratorQueue::CreatePreviewGeneratorQueue( 1463 PreviewGenerator::kRemote, 50, 60); 1464 1461 1465 int ret = qApp->exec(); 1462 1466 1467 PreviewGeneratorQueue::TeardownPreviewGeneratorQueue(); 1468 1463 1469 delete sysEventHandler; 1464 1470 1465 1471 pmanager->DestroyAllPlugins(); -
programs/mythfrontend/playbackbox.h
16 16 #include <QObject> 17 17 #include <QMutex> 18 18 #include <QMap> 19 #include <QSet> 19 20 20 21 #include "jobqueue.h" 21 22 #include "tv_play.h" … … 315 316 ProgramInfo *ProgramInfo_pointer_from_FindProgramInUILists, 316 317 bool force_preview_reload); 317 318 void UpdateUIListItem(MythUIButtonListItem *item, bool is_sel, 318 bool force_preview_reload = false);319 bool force_preview_reload = true); 319 320 320 void HandlePreviewEvent(const QString &piKey, const QString &previewFile);321 void HandlePreviewEvent(const QStringList &list); 321 322 void HandleRecordingRemoveEvent(uint chanid, const QDateTime &recstartts); 322 323 void HandleRecordingAddEvent(const ProgramInfo &evinfo); 323 324 void HandleUpdateProgramInfoEvent(const ProgramInfo &evinfo); … … 442 443 QStringList m_player_selected_new_show; 443 444 /// Main helper thread 444 445 PlaybackBoxHelper m_helper; 446 /// Outstanding preview image requests 447 QSet<QString> m_preview_tokens; 445 448 }; 446 449 447 450 class GroupSelector : public MythScreenType -
programs/mythfrontend/networkcontrol.cpp
1403 1403 1404 1404 frameNumber = results[7].toLongLong(); 1405 1405 1406 PreviewGenerator *previewgen = new PreviewGenerator(&pginfo); 1406 PreviewGenerator *previewgen = new PreviewGenerator( 1407 &pginfo, QString(), PreviewGenerator::kLocal); 1407 1408 previewgen->SetPreviewTimeAsFrameNumber(frameNumber); 1408 1409 previewgen->SetOutputFilename(outFile); 1409 1410 previewgen->SetOutputSize(QSize(width, height)); -
programs/mythbackend/mythxml.cpp
1149 1149 return; 1150 1150 1151 1151 PreviewGenerator *previewgen = new PreviewGenerator( 1152 &pginfo, PreviewGenerator::kLocal);1152 &pginfo, QString(), PreviewGenerator::kLocal); 1153 1153 previewgen->SetPreviewTimeAsSeconds(nSecsIn); 1154 1154 previewgen->SetOutputFilename(sPreviewFileName); 1155 1155 bool ok = previewgen->Run(); -
programs/mythbackend/playbacksock.h
70 70 QStringList GetSGFileQuery(QString &host, QString &groupname, 71 71 QString &filename); 72 72 73 QStringList GenPreviewPixmap(const ProgramInfo *pginfo); 74 QStringList GenPreviewPixmap(const ProgramInfo *pginfo, 73 QStringList GenPreviewPixmap(const QString &token, 74 const ProgramInfo *pginfo); 75 QStringList GenPreviewPixmap(const QString &token, 76 const ProgramInfo *pginfo, 75 77 bool time_fmt_sec, 76 78 long long time, 77 79 const QString &outputFile, -
programs/mythbackend/mainserver.h
1 1 #ifndef MAINSERVER_H_ 2 2 #define MAINSERVER_H_ 3 3 4 #include <QMap>5 #include <QMutex>6 4 #include <QReadWriteLock> 7 5 #include <QEvent> 6 #include <QMutex> 7 #include <QHash> 8 #include <QMap> 8 9 9 10 #include <vector> 10 11 using namespace std; … … 249 250 250 251 int m_exitCode; 251 252 253 typedef QHash<QString,QString> RequestedBy; 254 RequestedBy m_previewRequestedBy; 255 252 256 static const uint kMasterServerReconnectTimeout; 253 257 }; 254 258 -
programs/mythbackend/main.cpp
51 51 #include "programinfo.h" 52 52 #include "dbcheck.h" 53 53 #include "jobqueue.h" 54 #include "previewgenerator.h"55 54 #include "mythcommandlineparser.h" 56 55 #include "mythsystemevent.h" 57 56 -
programs/mythbackend/playbacksock.cpp
262 262 return strlist; 263 263 } 264 264 265 QStringList PlaybackSock::GenPreviewPixmap(const ProgramInfo *pginfo) 265 QStringList PlaybackSock::GenPreviewPixmap( 266 const QString &token, const ProgramInfo *pginfo) 266 267 { 267 QStringList strlist( QString("QUERY_GENPIXMAP") ); 268 QStringList strlist( QString("QUERY_GENPIXMAP2") ); 269 strlist += token; 268 270 pginfo->ToStringList(strlist); 269 271 270 272 SendReceiveStringList(strlist); … … 272 274 return strlist; 273 275 } 274 276 275 QStringList PlaybackSock::GenPreviewPixmap(const ProgramInfo *pginfo, 277 QStringList PlaybackSock::GenPreviewPixmap(const QString &token, 278 const ProgramInfo *pginfo, 276 279 bool time_fmt_sec, 277 280 long long time, 278 281 const QString &outputFile, 279 282 const QSize &outputSize) 280 283 { 281 QStringList strlist(QString("QUERY_GENPIXMAP")); 284 QStringList strlist(QString("QUERY_GENPIXMAP2")); 285 strlist += token; 282 286 pginfo->ToStringList(strlist); 283 287 strlist.push_back(time_fmt_sec ? "s" : "f"); 284 288 encodeLongLong(strlist, time); -
programs/mythbackend/mainserver.cpp
37 37 #include <QTcpServer> 38 38 #include <QTimer> 39 39 40 #include "previewgeneratorqueue.h" 40 41 #include "exitcodes.h" 41 42 #include "mythcontext.h" 42 43 #include "mythverbose.h" … … 53 54 #include "scheduledrecording.h" 54 55 #include "jobqueue.h" 55 56 #include "autoexpire.h" 56 #include "previewgenerator.h"57 57 #include "storagegroup.h" 58 58 #include "compat.h" 59 59 #include "RingBuffer.h" … … 184 184 { 185 185 AutoExpire::Update(true); 186 186 187 PreviewGeneratorQueue::CreatePreviewGeneratorQueue( 188 PreviewGenerator::kLocalAndRemote, ~0, 0); 189 PreviewGeneratorQueue::AddListener(this); 190 187 191 for (int i = 0; i < PRT_STARTUP_THREAD_COUNT; i++) 188 192 { 189 193 ProcessRequestThread *prt = new ProcessRequestThread(this); … … 237 241 238 242 MainServer::~MainServer() 239 243 { 244 PreviewGeneratorQueue::RemoveListener(this); 245 PreviewGeneratorQueue::TeardownPreviewGeneratorQueue(); 246 240 247 if (mythserver) 241 248 { 242 249 mythserver->disconnect(); … … 546 553 else 547 554 HandleFileTransferQuery(listline, tokens, pbs); 548 555 } 549 else if (command == "QUERY_GENPIXMAP ")556 else if (command == "QUERY_GENPIXMAP2") 550 557 { 551 558 HandleGenPreviewPixmap(listline, pbs); 552 559 } … … 729 736 void MainServer::customEvent(QEvent *e) 730 737 { 731 738 QStringList broadcast; 739 QSet<QString> receivers; 732 740 733 741 if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage) 734 742 { 735 743 MythEvent *me = (MythEvent *)e; 736 744 745 QString message = me->Message(); 746 QString error; 747 if ((message == "PREVIEW_SUCCESS" || message == "PREVIEW_QUEUED") && 748 me->ExtraDataCount() >= 5) 749 { 750 bool ok = true; 751 QString pginfokey = me->ExtraData(0); // pginfo->MakeUniqueKey() 752 QString filename = me->ExtraData(1); // outFileName 753 QString msg = me->ExtraData(2); 754 QString datetime = me->ExtraData(3); 755 756 if (message == "PREVIEW_QUEUED") 757 { 758 VERBOSE(VB_PLAYBACK, QString("Preview Queued: '%1' '%2'") 759 .arg(pginfokey).arg(filename)); 760 return; 761 } 762 763 QFile file(filename); 764 ok = ok && file.open(QIODevice::ReadOnly); 765 766 if (ok) 767 { 768 QByteArray data = file.readAll(); 769 QStringList extra("OK"); 770 extra.push_back(pginfokey); 771 extra.push_back(msg); 772 extra.push_back(datetime); 773 extra.push_back(QString::number(data.size())); 774 extra.push_back( 775 QString::number(qChecksum(data.constData(), data.size()))); 776 extra.push_back(QString(data.toBase64())); 777 778 for (uint i = 4 ; i < (uint) me->ExtraDataCount(); i++) 779 { 780 QString token = me->ExtraData(i); 781 extra.push_back(token); 782 RequestedBy::iterator it = m_previewRequestedBy.find(token); 783 if (it != m_previewRequestedBy.end()) 784 { 785 receivers.insert(*it); 786 m_previewRequestedBy.erase(it); 787 } 788 } 789 790 if (receivers.empty()) 791 { 792 VERBOSE(VB_IMPORTANT, LOC_ERR + 793 "PREVIEW_SUCCESS but no receivers."); 794 return; 795 } 796 797 broadcast.push_back("BACKEND_MESSAGE"); 798 broadcast.push_back("GENERATED_PIXMAP"); 799 broadcast += extra; 800 } 801 else 802 { 803 message = "PREVIEW_FAILED"; 804 error = QString("Failed to read '%1'").arg(filename); 805 VERBOSE(VB_IMPORTANT, LOC_ERR + error); 806 } 807 } 808 809 if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 5) 810 { 811 QString pginfokey = me->ExtraData(0); // pginfo->MakeUniqueKey() 812 QString msg = me->ExtraData(2); 813 814 QStringList extra("ERROR"); 815 extra.push_back(pginfokey); 816 extra.push_back(msg); 817 for (uint i = 4 ; i < (uint) me->ExtraDataCount(); i++) 818 { 819 QString token = me->ExtraData(i); 820 extra.push_back(token); 821 RequestedBy::iterator it = m_previewRequestedBy.find(token); 822 if (it != m_previewRequestedBy.end()) 823 { 824 receivers.insert(*it); 825 m_previewRequestedBy.erase(it); 826 } 827 } 828 829 if (receivers.empty()) 830 { 831 VERBOSE(VB_IMPORTANT, LOC_ERR + 832 "PREVIEW_FAILED but no receivers."); 833 return; 834 } 835 836 broadcast.push_back("BACKEND_MESSAGE"); 837 broadcast.push_back("GENERATED_PIXMAP"); 838 broadcast += extra; 839 } 840 737 841 if (me->Message().left(11) == "AUTO_EXPIRE") 738 842 { 739 843 QStringList tokens = me->Message() … … 945 1049 me = &mod_me; 946 1050 } 947 1051 948 broadcast = QStringList( "BACKEND_MESSAGE" ); 949 broadcast << me->Message(); 950 broadcast += me->ExtraDataList(); 1052 if (broadcast.empty()) 1053 { 1054 broadcast.push_back("BACKEND_MESSAGE"); 1055 broadcast.push_back(me->Message()); 1056 broadcast += me->ExtraDataList(); 1057 } 951 1058 } 952 1059 953 1060 if (!broadcast.empty()) … … 973 1080 sendGlobal = true; 974 1081 } 975 1082 976 vector<PlaybackSock*> sentSet;1083 QSet<PlaybackSock*> sentSet; 977 1084 978 1085 bool isSystemEvent = broadcast[1].startsWith("SYSTEM_EVENT "); 979 1086 QStringList sentSetSystemEvent(gCoreContext->GetHostName()); … … 983 1090 { 984 1091 PlaybackSock *pbs = *iter; 985 1092 986 vector<PlaybackSock*>::const_iterator it = 987 find(sentSet.begin(), sentSet.end(), pbs); 988 if (it != sentSet.end() || pbs->IsDisconnected()) 1093 if (sentSet.contains(pbs) || pbs->IsDisconnected()) 989 1094 continue; 990 1095 991 sentSet.push_back(pbs); 1096 if (!receivers.empty() && !receivers.contains(pbs->getHostname())) 1097 continue; 992 1098 1099 sentSet.insert(pbs); 1100 993 1101 bool reallysendit = false; 994 1102 995 1103 if (broadcast[1] == "CLEAR_SETTINGS_CACHE") … … 4913 5021 { 4914 5022 MythSocket *pbssock = pbs->getSocket(); 4915 5023 5024 if (slist.size() < 3) 5025 { 5026 VERBOSE(VB_IMPORTANT, LOC_ERR + "Too few params in pixmap request"); 5027 QStringList outputlist("ERROR"); 5028 outputlist += "TOO_FEW_PARAMS"; 5029 SendResponse(pbssock, outputlist); 5030 return; 5031 } 5032 4916 5033 bool time_fmt_sec = true; 4917 5034 long long time = -1; 4918 5035 QString outputfile; … … 4920 5037 int height = -1; 4921 5038 bool has_extra_data = false; 4922 5039 4923 QStringList::const_iterator it = slist.begin() + 1; 5040 QString token = slist[1]; 5041 if (token.isEmpty()) 5042 { 5043 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to parse pixmap request. " 5044 "Token absent"); 5045 QStringList outputlist("ERROR"); 5046 outputlist += "TOKEN_ABSENT"; 5047 SendResponse(pbssock, outputlist); 5048 } 5049 5050 QStringList::const_iterator it = slist.begin() + 2; 4924 5051 QStringList::const_iterator end = slist.end(); 4925 5052 ProgramInfo pginfo(it, end); 4926 5053 bool ok = pginfo.HasPathname(); 4927 5054 if (!ok) 4928 5055 { 4929 VERBOSE(VB_IMPORTANT, "MainServer: Failed to parse pixmap request."); 5056 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to parse pixmap request. " 5057 "ProgramInfo missing pathname"); 4930 5058 QStringList outputlist("BAD"); 4931 outputlist += " ERROR_INVALID_REQUEST";5059 outputlist += "NO_PATHNAME"; 4932 5060 SendResponse(pbssock, outputlist); 4933 5061 } 4934 5062 if (it != slist.end()) … … 4961 5089 4962 5090 pginfo.SetPathname(GetPlaybackURL(&pginfo)); 4963 5091 5092 m_previewRequestedBy[token] = pbs->getHostname(); 5093 4964 5094 if ((ismaster) && 4965 5095 (pginfo.GetHostname() != gCoreContext->GetHostName()) && 4966 5096 (!masterBackendOverride || !pginfo.IsLocal())) … … 4969 5099 4970 5100 if (slave) 4971 5101 { 4972 QStringList outputlist ("OK");5102 QStringList outputlist; 4973 5103 if (has_extra_data) 4974 5104 { 4975 5105 outputlist = slave->GenPreviewPixmap( 4976 &pginfo, time_fmt_sec, time, outputfile, outputsize);5106 token, &pginfo, time_fmt_sec, time, outputfile, outputsize); 4977 5107 } 4978 5108 else 4979 5109 { 4980 outputlist = slave->GenPreviewPixmap( &pginfo);5110 outputlist = slave->GenPreviewPixmap(token, &pginfo); 4981 5111 } 4982 5112 4983 5113 slave->DownRef(); 5114 5115 if (outputlist.empty() || outputlist[0] != "OK") 5116 m_previewRequestedBy.remove(token); 5117 4984 5118 SendResponse(pbssock, outputlist); 4985 5119 return; 4986 5120 } … … 4992 5126 4993 5127 if (!pginfo.IsLocal()) 4994 5128 { 4995 VERBOSE(VB_IMPORTANT, "MainServer:HandleGenPreviewPixmap: Unable to "5129 VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleGenPreviewPixmap: Unable to " 4996 5130 "find file locally, unable to make preview image."); 4997 QStringList outputlist( " BAD" );4998 outputlist += " ERROR_NOFILE";5131 QStringList outputlist( "ERROR" ); 5132 outputlist += "FILE_INACCESSIBLE"; 4999 5133 SendResponse(pbssock, outputlist); 5134 m_previewRequestedBy.remove(token); 5000 5135 return; 5001 5136 } 5002 5137 5003 PreviewGenerator *previewgen = new PreviewGenerator(&pginfo);5004 5138 if (has_extra_data) 5005 5139 { 5006 previewgen->SetOutputSize(outputsize); 5007 previewgen->SetOutputFilename(outputfile); 5008 previewgen->SetPreviewTime(time, time_fmt_sec); 5140 PreviewGeneratorQueue::GetPreviewImage( 5141 pginfo, outputsize, outputfile, time, time_fmt_sec, token); 5009 5142 } 5010 ok = previewgen->Run();5011 previewgen->deleteLater();5012 5013 if (ok)5014 {5015 QStringList outputlist("OK");5016 if (!outputfile.isEmpty())5017 outputlist += outputfile;5018 SendResponse(pbssock, outputlist);5019 }5020 5143 else 5021 5144 { 5022 VERBOSE(VB_IMPORTANT, "MainServer: Failed to make preview image."); 5023 QStringList outputlist( "BAD" ); 5024 outputlist += "ERROR_UNKNOWN"; 5025 SendResponse(pbssock, outputlist); 5145 PreviewGeneratorQueue::GetPreviewImage(pginfo, token); 5026 5146 } 5147 5148 QStringList outputlist("OK"); 5149 if (!outputfile.isEmpty()) 5150 outputlist += outputfile; 5151 SendResponse(pbssock, outputlist); 5027 5152 } 5028 5153 5029 5154 void MainServer::HandlePixmapLastModified(QStringList &slist, PlaybackSock *pbs) -
programs/mythpreviewgen/main.cpp
135 135 } 136 136 137 137 PreviewGenerator *previewgen = new PreviewGenerator( 138 pginfo, PreviewGenerator::kLocal);138 pginfo, QString(), PreviewGenerator::kLocal); 139 139 140 140 if (previewFrameNumber >= 0) 141 141 previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber);