Ticket #7195: 7191-gen2-v2.patch
File 7191-gen2-v2.patch, 77.8 KB (added by , 15 years ago) |
---|
-
libs/libmythtv/libmythtv.pro
153 153 HEADERS += scheduledrecording.h 154 154 HEADERS += signalmonitorvalue.h signalmonitorlistener.h 155 155 HEADERS += livetvchain.h playgroup.h 156 HEADERS += channelsettings.h previewgenerator.h 156 HEADERS += channelsettings.h 157 HEADERS += previewgenerator.h previewgeneratorqueue.h 157 158 HEADERS += transporteditor.h listingsources.h 158 159 HEADERS += myth_imgconvert.h 159 160 HEADERS += channelgroup.h channelgroupsettings.h … … 177 178 SOURCES += scheduledrecording.cpp 178 179 SOURCES += signalmonitorvalue.cpp 179 180 SOURCES += livetvchain.cpp playgroup.cpp 180 SOURCES += channelsettings.cpp previewgenerator.cpp 181 SOURCES += channelsettings.cpp 182 SOURCES += previewgenerator.cpp previewgeneratorqueue.cpp 181 183 SOURCES += transporteditor.cpp 182 184 SOURCES += channelgroup.cpp channelgroupsettings.cpp 183 185 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 80 private: 81 static PreviewGeneratorQueue *s_pgq; 82 QSet<QObject*> m_listeners; 83 84 mutable QMutex m_lock; 85 PreviewGenerator::Mode m_mode; 86 PreviewMap m_previewMap; 87 QMap<QString,QString> m_tokenToKeyMap; 88 QStringList m_queue; 89 uint m_running; 90 uint m_maxThreads; 91 uint m_maxAttempts; 92 uint m_minBlockSeconds; 93 }; 94 95 #endif // _PREVIEW_GENERATOR_QUEUE_H_ -
libs/libmythtv/previewgenerator.cpp
1 1 // C headers 2 2 #include <cmath> 3 #include <cassert> 3 4 4 5 // POSIX headers 5 6 #include <sys/time.h> 6 7 #include <fcntl.h> 7 8 8 9 // Qt headers 10 #include <QCoreApplication> 11 #include <QTemporaryFile> 9 12 #include <QFileInfo> 13 #include <QMetaType> 14 #include <QThread> 10 15 #include <QImage> 11 #include <Q MetaType>16 #include <QDir> 12 17 #include <QUrl> 13 #include <QDir>14 18 15 19 // MythTV headers 16 20 #include "mythconfig.h" … … 27 31 #include "playercontext.h" 28 32 #include "mythdirs.h" 29 33 #include "mythverbose.h" 34 #include "remoteutil.h" 30 35 31 36 #define LOC QString("Preview: ") 32 37 #define LOC_ERR QString("Preview Error: ") … … 37 42 * 38 43 * The usage is simple: First, pass a ProgramInfo whose pathname points 39 44 * to a local or remote recording to the constructor. Then call either 40 * Start(void) or Run(void) to generate the preview.45 * start(void) or Run(void) to generate the preview. 41 46 * 42 * Start(void) will create a thread that processes the request,47 * start(void) will create a thread that processes the request, 43 48 * creating a sockets the the backend if the recording is not local. 44 49 * 45 50 * Run(void) will process the request in the current thread, and it … … 47 52 * is not local. 48 53 * 49 54 * The PreviewGenerator will send Qt signals when the preview is ready 50 * and when the preview thread finishes running if Start(void) was called.55 * and when the preview thread finishes running if start(void) was called. 51 56 */ 52 57 53 58 /** … … 65 70 */ 66 71 PreviewGenerator::PreviewGenerator(const ProgramInfo *pginfo, 67 72 PreviewGenerator::Mode _mode) 68 : programInfo(*pginfo), mode(_mode), isConnected(false),69 createSockets(false), serverSock(NULL),pathname(pginfo->GetPathname()),73 : programInfo(*pginfo), mode(_mode), listener(NULL), 74 pathname(pginfo->GetPathname()), 70 75 timeInSeconds(true), captureTime(-1), outFileName(QString::null), 71 outSize(0,0) 76 outSize(0,0), gotReply(false), pixmapOk(false) 72 77 { 73 78 } 74 79 … … 84 89 85 90 void PreviewGenerator::TeardownAll(void) 86 91 { 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(); 92 previewWaitCondition.wakeAll(); 93 listener = NULL; 106 94 } 107 95 108 96 void PreviewGenerator::deleteLater() … … 114 102 void PreviewGenerator::AttachSignals(QObject *obj) 115 103 { 116 104 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; 105 listener = obj; 125 106 } 126 107 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 108 /** \fn PreviewGenerator::RunReal(void) 149 109 * \brief This call creates a preview without starting a new thread. 150 110 */ 151 111 bool PreviewGenerator::RunReal(void) 152 112 { 113 QString msg; 114 QTime tm = QTime::currentTime(); 153 115 bool ok = false; 154 116 bool is_local = IsLocal(); 155 117 if (is_local && (mode && kLocal) && LocalPreviewRun()) 156 118 { 157 119 ok = true; 120 msg = QString("Generated on %1 in %2 seconds, starting at %3") 121 .arg(gCoreContext->GetHostName()) 122 .arg(tm.elapsed()*0.001) 123 .arg(tm.toString(Qt::ISODate)); 158 124 } 159 125 else if (mode & kRemote) 160 126 { … … 166 132 "\n\t\t\tAttempting to regenerate preview on backend.\n"); 167 133 } 168 134 ok = RemotePreviewRun(); 135 if (ok) 136 { 137 msg = QString("Generated remotely in %1 seconds, starting at %2") 138 .arg(tm.elapsed()*0.001) 139 .arg(tm.toString(Qt::ISODate)); 140 } 169 141 } 170 142 else 171 143 { 172 144 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Run() file not local: '%1'") 173 145 .arg(pathname)); 146 msg = "Could not access recording"; 174 147 } 175 148 149 QMutexLocker locker(&previewLock); 150 if (listener) 151 { 152 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; 153 assert(!token.isEmpty()); 154 QStringList list(token); 155 list.push_back(programInfo.MakeUniqueKey()); 156 list.push_back(outFileName.isEmpty() ? 157 (programInfo.GetPathname()+".png") : outFileName); 158 list.push_back(msg); 159 QCoreApplication::postEvent(listener, new MythEvent(message, list)); 160 } 161 176 162 return ok; 177 163 } 178 164 179 165 bool PreviewGenerator::Run(void) 180 166 { 167 VERBOSE(VB_IMPORTANT, LOC + "Run() -- begin"); 168 169 QString msg; 170 QTime tm = QTime::currentTime(); 181 171 bool ok = false; 182 172 QString command = GetInstallPrefix() + "/bin/mythpreviewgen"; 183 173 bool local_ok = (IsLocal() && (mode & kLocal) && … … 187 177 if (mode & kRemote) 188 178 { 189 179 ok = RemotePreviewRun(); 180 if (ok) 181 { 182 msg = 183 QString("Generated remotely in %1 seconds, starting at %2") 184 .arg(tm.elapsed()*0.001) 185 .arg(tm.toString(Qt::ISODate)); 186 } 190 187 } 191 188 else 192 189 { 193 190 VERBOSE(VB_IMPORTANT, LOC_ERR + 194 191 QString("Run() can not generate preview locally for: '%1'") 195 192 .arg(pathname)); 193 msg = "Failed, local preview requested for remote file."; 196 194 } 197 195 } 198 196 else … … 215 213 if (!outFileName.isEmpty()) 216 214 command += QString("--outfile \"%1\" ").arg(outFileName); 217 215 216 command += " > /dev/null"; 217 218 218 int ret = myth_system(command, MYTH_SYSTEM_DONT_BLOCK_LIRC | 219 219 MYTH_SYSTEM_DONT_BLOCK_JOYSTICK_MENU | 220 220 MYTH_SYSTEM_DONT_BLOCK_PARENT); 221 221 if (ret) 222 222 { 223 VERBOSE(VB_IMPORTANT, LOC_ERR + "Encountered problems running " +224 QString("'%1'").arg(command));223 msg = QString("Encountered problems running '%1'").arg(command); 224 VERBOSE(VB_IMPORTANT, LOC_ERR + msg); 225 225 } 226 226 else 227 227 { … … 240 240 QFileInfo fi(outname); 241 241 ok = (fi.exists() && fi.isReadable() && fi.size()); 242 242 if (ok) 243 { 243 244 VERBOSE(VB_PLAYBACK, LOC + "Preview process ran ok."); 245 msg = QString("Generated on %1 in %2 seconds, starting at %3") 246 .arg(gCoreContext->GetHostName()) 247 .arg(tm.elapsed()*0.001) 248 .arg(tm.toString(Qt::ISODate)); 249 } 244 250 else 245 251 { 246 252 VERBOSE(VB_IMPORTANT, LOC_ERR + "Preview process not ok." + … … 251 257 VERBOSE(VB_IMPORTANT, LOC_ERR + 252 258 QString("Despite command '%1' returning success") 253 259 .arg(command)); 260 msg = QString("Failed to read preview image despite " 261 "preview process returning success."); 254 262 } 255 263 } 256 264 } 257 265 258 if (ok) 266 VERBOSE(VB_IMPORTANT, LOC + "Run() -- waiting on lock"); 267 QMutexLocker locker(&previewLock); 268 VERBOSE(VB_IMPORTANT, LOC + "Run() -- got lock"); 269 270 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; 271 if (listener) 259 272 { 260 QMutexLocker locker(&previewLock); 261 emit previewReady(&programInfo); 273 assert(!token.isEmpty()); 274 QStringList list(token); 275 list.push_back(programInfo.MakeUniqueKey()); 276 list.push_back(outFileName.isEmpty() ? 277 (programInfo.GetPathname()+".png") : outFileName); 278 if (!msg.isEmpty()) 279 list.push_back(msg); 280 QCoreApplication::postEvent(listener, new MythEvent(message, list)); 262 281 } 282 VERBOSE(VB_IMPORTANT, LOC + "Run() -- end"); 263 283 264 284 return ok; 265 285 } 266 286 267 void *PreviewGenerator::PreviewRun(void *param)287 void PreviewGenerator::run(void) 268 288 { 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; 289 VERBOSE(VB_IMPORTANT, LOC + "run() -- begin"); 290 setPriority(QThread::LowPriority); 291 Run(); 292 connect(this, SIGNAL(finished()), 293 this, SLOT(deleteLater())); 294 VERBOSE(VB_IMPORTANT, LOC + "run() -- end"); 277 295 } 278 296 279 bool PreviewGenerator::RemotePreview Setup(void)297 bool PreviewGenerator::RemotePreviewRun(void) 280 298 { 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); 299 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- begin"); 285 300 286 serverSock = gCoreContext->ConnectCommandSocket(server, port, ann);287 return serverSock;288 } 289 290 bool PreviewGenerator::RemotePreviewRun(void) 291 { 292 QStringList strlist( "QUERY_GENPIXMAP");301 QStringList strlist( "QUERY_GENPIXMAP2" ); 302 if (token.isEmpty()) 303 { 304 token = QString("%1:%2") 305 .arg(programInfo.MakeUniqueKey()).arg(rand()); 306 } 307 strlist.push_back(token); 293 308 programInfo.ToStringList(strlist); 294 309 strlist.push_back(timeInSeconds ? "s" : "f"); 295 310 encodeLongLong(strlist, captureTime); … … 305 320 strlist.push_back(QString::number(outSize.width())); 306 321 strlist.push_back(QString::number(outSize.height())); 307 322 308 bool ok = false; 323 gCoreContext->addListener(this); 324 pixmapOk = false; 309 325 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 326 bool ok = gCoreContext->SendReceiveStringList(strlist); 331 327 if (!ok || strlist.empty() || (strlist[0] != "OK")) 332 328 { 333 329 if (!ok) … … 340 336 VERBOSE(VB_IMPORTANT, LOC_ERR + 341 337 "Remote Preview failed, reason given: " <<strlist[1]); 342 338 } 343 else 339 340 gCoreContext->removeListener(this); 341 342 return false; 343 } 344 345 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- waiting on lock"); 346 QMutexLocker locker(&previewLock); 347 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- got lock"); 348 349 // wait up to 30 seconds for the preview to complete 350 if (!gotReply) 351 previewWaitCondition.wait(&previewLock, 30 * 1000); 352 353 if (!gotReply) 354 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- no reply.."); 355 356 gCoreContext->removeListener(this); 357 358 VERBOSE(VB_IMPORTANT, LOC + "RemotePreviewRun() -- end"); 359 360 return pixmapOk; 361 } 362 363 bool PreviewGenerator::event(QEvent *e) 364 { 365 if (e->type() == (QEvent::Type) MythEvent::MythEventMessage) 366 { 367 MythEvent *me = (MythEvent*)e; 368 if (me->Message() == "GENERATED_PIXMAP" && me->ExtraDataCount() >= 2 && 369 me->ExtraData(0) == token) 344 370 { 345 VERBOSE(VB_IMPORTANT, LOC_ERR + 346 "Remote Preview failed due to an unknown error."); 371 VERBOSE(VB_IMPORTANT, LOC + "got GENERATED_PIXMAP event\n\n\n"); 372 QMutexLocker locker(&previewLock); 373 gotReply = true; 374 pixmapOk = me->ExtraData(1) == "OK"; 375 if (pixmapOk) 376 { 377 QByteArray data; 378 if (me->ExtraDataCount() >= 4) 379 { 380 size_t length = me->ExtraData(2).toULongLong(); 381 quint16 checksum16 = me->ExtraData(3).toUInt(); 382 data = QByteArray::fromBase64(me->ExtraData(4).toAscii()); 383 if ((size_t) data.size() < length) 384 { // (note data.size() may be up to 3 385 // bytes longer after decoding 386 VERBOSE(VB_IMPORTANT, LOC_ERR + 387 QString("Preview size check failed %1 < %2") 388 .arg(data.size()).arg(length)); 389 data.clear(); 390 } 391 data.resize(length); 392 393 if (checksum16 != qChecksum(data.constData(), data.size())) 394 { 395 VERBOSE(VB_IMPORTANT, LOC_ERR + 396 "Preview checksum failed"); 397 data.clear(); 398 } 399 } 400 pixmapOk = (data.isEmpty()) ? false : SaveOutFile(data); 401 } 402 403 previewWaitCondition.wakeAll(); 404 405 return true; 347 406 } 348 return false;349 407 } 408 return QObject::event(e); 409 } 350 410 411 bool PreviewGenerator::SaveOutFile(QByteArray &data) 412 { 351 413 if (outFileName.isEmpty()) 352 414 { 353 QString remotecachedirname = QString("%1/remotecache").arg(GetConfDir()); 415 QString remotecachedirname = 416 QString("%1/remotecache").arg(GetConfDir()); 354 417 QDir remotecachedir(remotecachedirname); 355 418 356 419 if (!remotecachedir.exists()) … … 368 431 outFileName = QString("%1/%2").arg(remotecachedirname).arg(filename); 369 432 } 370 433 371 // find file, copy/move to output file name & location... 434 bool ok = true; 435 if (data.isEmpty()) 436 { 437 // find file, copy/move to output file name & location... 438 QString url = QString::null; 439 QString fn = QFileInfo(outFileName).fileName(); 440 ok = false; 372 441 373 QString url = QString::null; 374 QString fn = QFileInfo(outFileName).fileName(); 375 QByteArray data; 376 ok = false; 442 QStringList fileNames; 443 fileNames.push_back( 444 CreateAccessibleFilename(programInfo.GetPathname(), fn)); 445 fileNames.push_back( 446 CreateAccessibleFilename(programInfo.GetPathname(), "")); 377 447 378 QStringList fileNames; 379 fileNames.push_back( 380 CreateAccessibleFilename(programInfo.GetPathname(), fn)); 381 fileNames.push_back( 382 CreateAccessibleFilename(programInfo.GetPathname(), "")); 448 QStringList::const_iterator it = fileNames.begin(); 449 for ( ; it != fileNames.end() && (!ok || data.isEmpty()); ++it) 450 { 451 data.resize(0); 452 url = *it; 453 RemoteFile *rf = new RemoteFile(url, false, false, 0); 454 ok = rf->SaveAs(data); 455 delete rf; 456 } 457 } 383 458 384 QStringList::const_iterator it = fileNames.begin(); 385 for ( ; it != fileNames.end() && (!ok || data.isEmpty()); ++it) 459 if (!ok) 460 return false; 461 462 QFile file(outFileName); 463 ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly); 464 if (!ok) 386 465 { 387 data.resize(0); 388 url = *it; 389 RemoteFile *rf = new RemoteFile(url, false, false, 0); 390 ok = rf->SaveAs(data); 391 delete rf; 466 VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Failed to open: '%1'") 467 .arg(outFileName)); 392 468 } 393 469 394 if (ok && data.size()) 470 off_t offset = 0; 471 size_t remaining = data.size(); 472 uint failure_cnt = 0; 473 while ((remaining > 0) && (failure_cnt < 5)) 395 474 { 396 QFile file(outFileName); 397 ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly); 398 if (!ok) 475 ssize_t written = file.write(data.data() + offset, remaining); 476 if (written < 0) 399 477 { 400 VERBOSE(VB_IMPORTANT, QString("Failed to open: '%1'") 401 .arg(outFileName)); 478 failure_cnt++; 479 usleep(50000); 480 continue; 402 481 } 403 482 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 } 483 failure_cnt = 0; 484 offset += written; 485 remaining -= written; 426 486 } 427 428 return ok && data.size(); 429 } 430 431 void PreviewGenerator::RemotePreviewTeardown(void) 432 { 433 if (serverSock) 487 if (ok && !remaining) 434 488 { 435 serverSock->DownRef(); 436 serverSock = NULL; 489 VERBOSE(VB_PLAYBACK, LOC + QString("Saved: '%1'").arg(outFileName)); 437 490 } 491 else 492 { 493 file.remove(); 494 } 495 496 return ok; 438 497 } 439 498 440 499 bool PreviewGenerator::SavePreview(QString filename, … … 476 535 QImage small_img = img.scaled((int) ppw, (int) pph, 477 536 Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 478 537 479 QByteArray fname = filename.toAscii(); 480 if (small_img.save(fname.constData(), "PNG")) 538 QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX"); 539 f.setAutoRemove(false); 540 if (f.open() && small_img.save(&f, "PNG")) 481 541 { 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; 542 // Let anybody update it 543 makeFileAccessible(f.fileName().toLocal8Bit().constData()); 544 QFile of(filename); 545 of.remove(); 546 if (f.rename(filename)) 547 { 548 VERBOSE(VB_PLAYBACK, LOC + 549 QString("Saved preview '%0' %1x%2") 550 .arg(filename).arg((int) ppw).arg((int) pph)); 551 return true; 552 } 553 f.remove(); 489 554 } 490 555 491 // Save failed; if file exists, try saving to .new and moving over 492 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()); 556 VERBOSE(VB_IMPORTANT, LOC_ERR + 557 QString("Failed to save preview '%0' %1x%2") 558 .arg(filename).arg((int) ppw).arg((int) pph)); 499 559 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 560 return false; 509 561 } 510 562 -
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 #include <cassert> 62 void PreviewGeneratorQueue::GetPreviewImage( 63 const ProgramInfo &pginfo, 64 const QSize &outputsize, 65 const QString &outputfile, 66 long long time, bool in_seconds, 67 QString token) 68 { 69 assert(!token.isEmpty()); 70 71 if (!s_pgq) 72 return; 73 74 if (pginfo.GetPathname().isEmpty() || 75 pginfo.GetBasename() == pginfo.GetPathname()) 76 { 77 return; 78 } 79 80 QStringList extra; 81 pginfo.ToStringList(extra); 82 extra += token; 83 extra += QString::number(outputsize.width()); 84 extra += QString::number(outputsize.height()); 85 extra += outputfile; 86 extra += QString::number(time); 87 extra += (in_seconds ? "1" : "0"); 88 MythEvent *e = new MythEvent("GET_PREVIEW", extra); 89 QCoreApplication::postEvent(s_pgq, e); 90 } 91 92 void PreviewGeneratorQueue::AddListener(QObject *listener) 93 { 94 if (!s_pgq) 95 return; 96 97 QMutexLocker locker(&s_pgq->m_lock); 98 s_pgq->m_listeners.insert(listener); 99 } 100 101 void PreviewGeneratorQueue::RemoveListener(QObject *listener) 102 { 103 if (!s_pgq) 104 return; 105 106 QMutexLocker locker(&s_pgq->m_lock); 107 s_pgq->m_listeners.remove(listener); 108 } 109 110 bool PreviewGeneratorQueue::event(QEvent *e) 111 { 112 if (e->type() != (QEvent::Type) MythEvent::MythEventMessage) 113 return QObject::event(e); 114 115 MythEvent *me = (MythEvent*)e; 116 if (me->Message() == "GET_PREVIEW") 117 { 118 const QStringList list = me->ExtraDataList(); 119 QStringList::const_iterator it = list.begin(); 120 ProgramInfo evinfo(it, list.end()); 121 QString token; 122 QSize outputsize; 123 QString outputfile; 124 long long time; 125 bool time_fmt_sec; 126 if (it != list.end()) 127 token = (*it++); 128 if (it != list.end()) 129 outputsize.setWidth((*it++).toInt()); 130 if (it != list.end()) 131 outputsize.setHeight((*it++).toInt()); 132 if (it != list.end()) 133 outputfile = (*it++); 134 if (it != list.end()) 135 time = (*it++).toLongLong(); 136 QString fn; 137 if (it != list.end()) 138 { 139 time_fmt_sec = (*it++).toInt() != 0; 140 fn = GeneratePreviewImage(evinfo, outputsize, outputfile, 141 time, time_fmt_sec, token); 142 } 143 return true; 144 } 145 else if (me->Message() == "PREVIEW_SUCCESS" || 146 me->Message() == "PREVIEW_FAILED") 147 { 148 QString token = me->ExtraData(0); 149 QString pginfokey = me->ExtraData(1); // pginfo->MakeUniqueKey() 150 QString filename = me->ExtraData(2); // outFileName 151 QString msg; 152 if (me->ExtraDataCount() >= 4) 153 msg = me->ExtraData(3); 154 155 VERBOSE(VB_PLAYBACK, LOC + QString("Preview '%1' processed") 156 .arg(token)); 157 158 { 159 QMutexLocker locker(&m_lock); 160 QMap<QString,QString>::iterator kit = m_tokenToKeyMap.find(token); 161 if (kit == m_tokenToKeyMap.end()) 162 { 163 VERBOSE(VB_IMPORTANT, LOC_ERR + 164 QString("Failed to find token %1 in map.").arg(token)); 165 return true; 166 } 167 PreviewMap::iterator it = m_previewMap.find(*kit); 168 if (it == m_previewMap.end()) 169 { 170 VERBOSE(VB_IMPORTANT, LOC_ERR + 171 QString("Failed to find key %1 in map.").arg(*kit)); 172 return true; 173 } 174 175 (*it).gen = NULL; 176 (*it).genStarted = false; 177 if (me->Message() == "PREVIEW_SUCCESS") 178 { 179 (*it).attempts = 0; 180 (*it).lastBlockTime = 0; 181 (*it).blockRetryUntil = QDateTime(); 182 } 183 else 184 { 185 (*it).lastBlockTime = 186 max(m_minBlockSeconds, (*it).lastBlockTime * 2); 187 (*it).blockRetryUntil = 188 QDateTime::currentDateTime().addSecs((*it).lastBlockTime); 189 } 190 191 QStringList list; 192 list.push_back(pginfokey); 193 list.push_back(filename); 194 list.push_back(""); 195 if (!msg.isEmpty()) 196 list.push_back(msg); 197 198 QSet<QString>::const_iterator tit = (*it).tokens.begin(); 199 for (; tit != (*it).tokens.end(); ++tit) 200 { 201 list[2] = (*tit); 202 QSet<QObject*>::iterator sit = m_listeners.begin(); 203 for (; sit != m_listeners.end(); ++sit) 204 { 205 MythEvent *e = new MythEvent(me->Message(), list); 206 QCoreApplication::postEvent(*sit, e); 207 } 208 } 209 (*it).tokens.clear(); 210 211 m_running--; 212 } 213 214 UpdatePreviewGeneratorThreads(); 215 216 return true; 217 } 218 return false; 219 } 220 221 void PreviewGeneratorQueue::SendEvent( 222 const ProgramInfo &pginfo, 223 const QString &eventname, 224 const QString &fn, const QString &token, const QString &msg) 225 { 226 VERBOSE(VB_IMPORTANT, QString("SendEvent(%1, %2, %3, %4)") 227 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)) 228 .arg(eventname).arg(msg).arg(token)); 229 230 QStringList list; 231 list.push_back(pginfo.MakeUniqueKey()); 232 list.push_back(fn); 233 list.push_back(token); 234 list.push_back(msg); 235 236 QMutexLocker locker(&m_lock); 237 QSet<QObject*>::iterator it = m_listeners.begin(); 238 for (; it != m_listeners.end(); ++it) 239 { 240 MythEvent *e = new MythEvent(eventname, list); 241 QCoreApplication::postEvent(*it, e); 242 } 243 } 244 245 QString PreviewGeneratorQueue::GeneratePreviewImage( 246 ProgramInfo &pginfo, 247 const QSize &size, 248 const QString &outputfile, 249 long long time, bool in_seconds, 250 QString token) 251 { 252 QString key = QString("%1_%2x%3_%4%5") 253 .arg(pginfo.GetBasename()).arg(size.width()).arg(size.height()) 254 .arg(time).arg(in_seconds?"s":"f"); 255 256 if (pginfo.GetAvailableStatus() == asPendingDelete) 257 { 258 SendEvent(pginfo, "PREVIEW_FAILED", key, token, "Pending Delete"); 259 return QString(); 260 } 261 262 QString filename = (outputfile.isEmpty()) ? 263 pginfo.GetPathname() + ".png" : outputfile; 264 QString ret_file = filename; 265 QString ret; 266 267 bool is_special = !outputfile.isEmpty() || time >= 0 || 268 size.width() || size.height(); 269 270 bool needs_gen = true; 271 if (!is_special) 272 { 273 QDateTime previewLastModified; 274 bool streaming = filename.left(1) != "/"; 275 bool locally_accessible = false; 276 bool bookmark_updated = false; 277 278 QDateTime bookmark_ts = pginfo.QueryBookmarkTimeStamp(); 279 QDateTime cmp_ts = bookmark_ts.isValid() ? 280 bookmark_ts : pginfo.GetLastModifiedTime(); 281 282 if (streaming) 283 { 284 ret_file = QString("%1/remotecache/%2") 285 .arg(GetConfDir()).arg(filename.section('/', -1)); 286 287 QFileInfo finfo(ret_file); 288 if (finfo.isReadable() && finfo.lastModified() >= cmp_ts) 289 { 290 // This is just an optimization to avoid 291 // hitting the backend if our cached copy 292 // is newer than the bookmark, or if we have 293 // a preview and do not update it when the 294 // bookmark changes. 295 previewLastModified = finfo.lastModified(); 296 } 297 else if (!IsGeneratingPreview(key)) 298 { 299 previewLastModified = 300 RemoteGetPreviewIfModified(pginfo, ret_file); 301 } 302 } 303 else 304 { 305 QFileInfo fi(filename); 306 if ((locally_accessible = fi.isReadable())) 307 previewLastModified = fi.lastModified(); 308 } 309 310 bookmark_updated = 311 (!previewLastModified.isValid() || (previewLastModified < cmp_ts)); 312 313 if (bookmark_updated && bookmark_ts.isValid() && 314 previewLastModified.isValid()) 315 { 316 ClearPreviewGeneratorAttempts(key); 317 } 318 319 if (0) 320 { 321 VERBOSE(VB_IMPORTANT, QString( 322 "previewLastModified: %1\n\t\t\t" 323 "bookmark_ts: %2\n\t\t\t" 324 "pginfo.lastmodified: %3") 325 .arg(previewLastModified.toString(Qt::ISODate)) 326 .arg(bookmark_ts.toString(Qt::ISODate)) 327 .arg(pginfo.GetLastModifiedTime(ISODate))); 328 } 329 330 bool preview_exists = previewLastModified.isValid(); 331 332 if (0) 333 { 334 VERBOSE(VB_IMPORTANT, 335 QString("Title: %1\n\t\t\t") 336 .arg(pginfo.toString(ProgramInfo::kTitleSubtitle)) + 337 QString("File '%1' \n\t\t\tCache '%2'") 338 .arg(filename).arg(ret_file) + 339 QString("\n\t\t\tPreview Exists: %1, " 340 "Bookmark Updated: %2, " 341 "Need Preview: %3") 342 .arg(preview_exists).arg(bookmark_updated) 343 .arg((bookmark_updated || !preview_exists))); 344 } 345 346 needs_gen = bookmark_updated || !preview_exists; 347 348 if (!needs_gen) 349 { 350 if (locally_accessible) 351 ret = filename; 352 else if (preview_exists && QFileInfo(ret_file).isReadable()) 353 ret = ret_file; 354 } 355 } 356 357 if (needs_gen && !IsGeneratingPreview(key)) 358 { 359 uint attempts = IncPreviewGeneratorAttempts(key); 360 if (attempts < m_maxAttempts) 361 { 362 VERBOSE(VB_PLAYBACK, LOC + 363 QString("Requesting preview for '%1'") 364 .arg(key)); 365 PreviewGenerator *pg = new PreviewGenerator(&pginfo, m_mode); 366 if (!outputfile.isEmpty() || time >= 0 || 367 size.width() || size.height()) 368 { 369 pg->SetPreviewTime(time, in_seconds); 370 pg->SetOutputFilename(outputfile); 371 pg->SetOutputSize(size); 372 } 373 pg->token = token; 374 375 SetPreviewGenerator(key, pg); 376 377 VERBOSE(VB_PLAYBACK, LOC + 378 QString("Requested preview for '%1'").arg(key)); 379 } 380 else if (attempts >= m_maxAttempts) 381 { 382 VERBOSE(VB_IMPORTANT, LOC_ERR + 383 QString("Attempted to generate preview for '%1' " 384 "%2 times; >= max(%3)") 385 .arg(key).arg(attempts).arg(m_maxAttempts)); 386 } 387 } 388 else if (needs_gen) 389 { 390 VERBOSE(VB_PLAYBACK, LOC + 391 "Not requesting preview as it " 392 "is already being generated"); 393 IncPreviewGeneratorPriority(key, token); 394 } 395 396 UpdatePreviewGeneratorThreads(); 397 398 if (!ret.isEmpty()) 399 { 400 QString msg = "On Disk"; 401 SendEvent(pginfo, "PREVIEW_SUCCESS", ret, token, msg); 402 } 403 else 404 { 405 uint queue_depth, token_cnt; 406 GetInfo(key, queue_depth, token_cnt); 407 QString msg = QString("Queue depth %1, our tokens %2") 408 .arg(queue_depth).arg(token_cnt); 409 SendEvent(pginfo, "PREVIEW_QUEUED", ret, token, msg); 410 } 411 412 return ret; 413 } 414 415 void PreviewGeneratorQueue::GetInfo( 416 const QString &key, uint &queue_depth, uint &token_cnt) 417 { 418 QMutexLocker locker(&m_lock); 419 queue_depth = m_queue.size(); 420 PreviewMap::iterator pit = m_previewMap.find(key); 421 token_cnt = (pit == m_previewMap.end()) ? 0 : (*pit).tokens.size(); 422 } 423 424 void PreviewGeneratorQueue::IncPreviewGeneratorPriority( 425 const QString &key, QString token) 426 { 427 QMutexLocker locker(&m_lock); 428 m_queue.removeAll(key); 429 430 PreviewMap::iterator pit = m_previewMap.find(key); 431 if (pit == m_previewMap.end()) 432 return; 433 434 if ((*pit).gen && !(*pit).genStarted) 435 m_queue.push_back(key); 436 437 if (!token.isEmpty()) 438 { 439 m_tokenToKeyMap[token] = key; 440 (*pit).tokens.insert(token); 441 } 442 } 443 444 void PreviewGeneratorQueue::UpdatePreviewGeneratorThreads(void) 445 { 446 QMutexLocker locker(&m_lock); 447 QStringList &q = m_queue; 448 if (!q.empty() && (m_running < m_maxThreads)) 449 { 450 QString fn = q.back(); 451 q.pop_back(); 452 PreviewMap::iterator it = m_previewMap.find(fn); 453 if (it != m_previewMap.end() && (*it).gen && !(*it).genStarted) 454 { 455 m_running++; 456 VERBOSE(VB_IMPORTANT, LOC + "Starting pg thread"); 457 (*it).gen->start(); 458 (*it).genStarted = true; 459 } 460 } 461 } 462 463 /** \brief Sets the PreviewGenerator for a specific file. 464 * \return true iff call succeeded. 465 */ 466 void PreviewGeneratorQueue::SetPreviewGenerator( 467 const QString &key, PreviewGenerator *g) 468 { 469 { 470 QMutexLocker locker(&m_lock); 471 m_tokenToKeyMap[g->token] = key; 472 PreviewGenState &state = m_previewMap[key]; 473 if (state.gen) 474 { 475 if (!g->token.isEmpty()) 476 state.tokens.insert(g->token); 477 g->deleteLater(); 478 } 479 else 480 { 481 g->AttachSignals(this); 482 state.gen = g; 483 state.genStarted = false; 484 if (!g->token.isEmpty()) 485 state.tokens.insert(g->token); 486 } 487 } 488 489 IncPreviewGeneratorPriority(key, ""); 490 } 491 492 /** \brief Returns true if we have already started a 493 * PreviewGenerator to create this file. 494 */ 495 bool PreviewGeneratorQueue::IsGeneratingPreview(const QString &key) const 496 { 497 PreviewMap::const_iterator it; 498 QMutexLocker locker(&m_lock); 499 500 if ((it = m_previewMap.find(key)) == m_previewMap.end()) 501 return false; 502 503 if ((*it).blockRetryUntil.isValid()) 504 return QDateTime::currentDateTime() < (*it).blockRetryUntil; 505 506 return (*it).gen; 507 } 508 509 /** \fn PreviewGeneratorQueue::IncPreviewGeneratorAttempts(const QString&) 510 * \brief Increments and returns number of times we have 511 * started a PreviewGenerator to create this file. 512 */ 513 uint PreviewGeneratorQueue::IncPreviewGeneratorAttempts(const QString &key) 514 { 515 QMutexLocker locker(&m_lock); 516 return m_previewMap[key].attempts++; 517 } 518 519 /** \fn PreviewGeneratorQueue::ClearPreviewGeneratorAttempts(const QString&) 520 * \brief Clears the number of times we have 521 * started a PreviewGenerator to create this file. 522 */ 523 void PreviewGeneratorQueue::ClearPreviewGeneratorAttempts(const QString &key) 524 { 525 QMutexLocker locker(&m_lock); 526 m_previewMap[key].attempts = 0; 527 m_previewMap[key].lastBlockTime = 0; 528 m_previewMap[key].blockRetryUntil = 529 QDateTime::currentDateTime().addSecs(-60); 530 } -
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" … … 1132 1132 if (!killFile) 1133 1133 { 1134 1134 if (curRecording->IsLocal()) 1135 { 1136 (new PreviewGenerator( 1137 curRecording, PreviewGenerator::kLocal))->Start(); 1138 } 1135 PreviewGeneratorQueue::GetPreviewImage(*curRecording, ""); 1139 1136 1140 1137 if (!tvchain) 1141 1138 { … … 4471 4468 if (!oldinfo->IsLocal()) 4472 4469 oldinfo->SetPathname(oldinfo->GetPlaybackURL(false,true)); 4473 4470 if (oldinfo->IsLocal()) 4474 { 4475 (new PreviewGenerator( 4476 oldinfo, PreviewGenerator::kLocal))->Start(); 4477 } 4471 PreviewGeneratorQueue::GetPreviewImage(*oldinfo, ""); 4478 4472 } 4479 4473 delete oldinfo; 4480 4474 } -
libs/libmythtv/previewgenerator.h
4 4 5 5 #include <pthread.h> 6 6 7 #include <QWaitCondition> 7 8 #include <QString> 9 #include <QThread> 8 10 #include <QMutex> 9 11 #include <QSize> 12 #include <QSet> 10 13 11 14 #include "programinfo.h" 12 15 #include "util.h" 13 16 14 17 class MythSocket; 18 class PreviewGenerator; 15 19 16 class MPUBLIC PreviewGenerator : public QObject 20 // TODO Changes to make... 21 // Update docs 22 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, … … 47 56 void SetOutputFilename(const QString&); 48 57 void SetOutputSize(const QSize &size) { outSize = size; } 49 58 50 void Start(void);59 void run(void); // QThread 51 60 bool Run(void); 52 61 53 62 void AttachSignals(QObject*); 54 void disconnectSafe(void);55 63 56 signals:57 void previewThreadDone(const QString&, bool&);58 void previewReady(const ProgramInfo*);59 60 64 public slots: 61 65 void deleteLater(); 62 66 … … 64 68 virtual ~PreviewGenerator(); 65 69 void TeardownAll(void); 66 70 67 bool RemotePreviewSetup(void);68 71 bool RemotePreviewRun(void); 69 void RemotePreviewTeardown(void);70 71 72 bool LocalPreviewRun(void); 72 73 bool IsLocal(void) const; 73 74 74 75 bool RunReal(void); 75 76 76 static void *PreviewRun(void*);77 78 77 static char *GetScreenGrab(const ProgramInfo &pginfo, 79 78 const QString &filename, 80 79 long long seektime, … … 93 92 static QString CreateAccessibleFilename( 94 93 const QString &pathname, const QString &outFileName); 95 94 96 protected: 95 virtual bool event(QEvent *e); // QObject 96 bool SaveOutFile(QByteArray &data); 97 98 // protected: 99 public: 100 QWaitCondition previewWaitCondition; 97 101 QMutex previewLock; 98 102 pthread_t previewThread; 99 103 ProgramInfo programInfo; 100 104 101 105 Mode mode; 102 bool isConnected; 103 bool createSockets; 104 MythSocket *serverSock; 106 QObject *listener; 105 107 QString pathname; 106 108 107 109 /// tells us whether to use time as seconds or frame number … … 110 112 long long captureTime; 111 113 QString outFileName; 112 114 QSize outSize; 115 116 QString token; 117 bool gotReply; 118 bool pixmapOk; 113 119 }; 114 120 115 121 #endif // PREVIEW_GENERATOR_H_ -
libs/libmyth/remoteutil.cpp
357 357 return retdatetime; 358 358 } 359 359 360 size_t length = strlist[1].to LongLong();360 size_t length = strlist[1].toULongLong(); 361 361 quint16 checksum16 = strlist[2].toUInt(); 362 362 QByteArray data = QByteArray::fromBase64(strlist[3].toAscii()); 363 363 if ((size_t) data.size() < length) … … 367 367 .arg(data.size()).arg(length)); 368 368 return QDateTime(); 369 369 } 370 data.resize(length); 370 371 371 372 if (checksum16 != qChecksum(data.constData(), data.size())) 372 373 { -
programs/mythfrontend/playbackbox.cpp
8 8 #include <QTimer> 9 9 #include <QMap> 10 10 11 // libmythdb 12 #include "oldsettings.h" 13 #include "mythdb.h" 14 #include "mythdbcon.h" 15 #include "mythverbose.h" 16 #include "mythdirs.h" 17 18 // libmythtv 19 #include "tv.h" 11 // MythTV 12 #include "previewgeneratorqueue.h" 13 #include "mythuiprogressbar.h" 20 14 #include "mythplayer.h" 15 #include "mythuibuttonlist.h" 16 #include "mythcorecontext.h" 17 #include "mythsystemevent.h" 18 #include "mythuistatetype.h" 19 #include "mythuicheckbox.h" 20 #include "mythuitextedit.h" 21 #include "mythdialogbox.h" 21 22 #include "recordinginfo.h" 22 #include "playgroup.h" 23 #include "mythsystemevent.h" 24 25 // libmyth 26 #include "mythcorecontext.h" 27 #include "util.h" 23 #include "mythuihelper.h" 28 24 #include "storagegroup.h" 25 #include "mythuibutton.h" 26 #include "mythverbose.h" 27 #include "mythuiimage.h" 29 28 #include "programinfo.h" 30 31 // libmythui 32 #include "mythuihelper.h" 29 #include "oldsettings.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: ") … … 447 442 PlaybackBox::~PlaybackBox(void) 448 443 { 449 444 gCoreContext->removeListener(this); 445 PreviewGeneratorQueue::RemoveListener(this); 450 446 451 447 for (uint i = 0; i < sizeof(m_artImage) / sizeof(MythUIImage*); i++) 452 448 { … … 517 513 void PlaybackBox::Load(void) 518 514 { 519 515 m_programInfoCache.WaitForLoadToComplete(); 516 PreviewGeneratorQueue::AddListener(this); 520 517 } 521 518 522 519 void PlaybackBox::Init() … … 797 794 798 795 QString oldimgfile = item->GetImage("preview"); 799 796 if (oldimgfile.isEmpty() || force_preview_reload) 800 m_ helper.GetPreviewImage(*pginfo);797 m_preview_tokens.insert(m_helper.GetPreviewImage(*pginfo)); 801 798 802 799 if ((GetFocusWidget() == m_recordingList) && is_sel) 803 800 { … … 864 861 * gets updated as well. 865 862 */ 866 863 void PlaybackBox::HandlePreviewEvent( 867 const QString &piKey, const QString &previewFile )864 const QString &piKey, const QString &previewFile, const QString &token) 868 865 { 866 QSet<QString>::iterator it = m_preview_tokens.find(token); 867 if (it == m_preview_tokens.end()) 868 return; 869 m_preview_tokens.erase(it); 870 869 871 if (previewFile.isEmpty()) 870 872 return; 871 873 … … 3803 3805 // asPendingDelete, we need to put them back now.. 3804 3806 ScheduleUpdateUIList(); 3805 3807 } 3806 else if (message == "PREVIEW_ READY" && me->ExtraDataCount() == 2)3808 else if (message == "PREVIEW_SUCCESS" && me->ExtraDataCount() >= 3) 3807 3809 { 3808 HandlePreviewEvent(me->ExtraData(0), me->ExtraData(1)); 3810 HandlePreviewEvent( 3811 me->ExtraData(0), me->ExtraData(1), me->ExtraData(2)); 3809 3812 } 3813 else if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 3) 3814 { 3815 QString token = me->ExtraData(2); 3816 QSet<QString>::iterator it = m_preview_tokens.find(token); 3817 if (it == m_preview_tokens.end()) 3818 return; 3819 m_preview_tokens.erase(it); 3820 } 3810 3821 else if (message == "AVAILABILITY" && me->ExtraDataCount() == 8) 3811 3822 { 3812 3823 const uint kMaxUIWaitTime = 100; // ms -
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/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/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" … … 317 318 void UpdateUIListItem(MythUIButtonListItem *item, bool is_sel, 318 319 bool force_preview_reload = false); 319 320 320 void HandlePreviewEvent(const QString &piKey, const QString &previewFile); 321 void HandlePreviewEvent( 322 const QString &piKey, const QString &previewFile, const QString &token); 321 323 void HandleRecordingRemoveEvent(uint chanid, const QDateTime &recstartts); 322 324 void HandleRecordingAddEvent(const ProgramInfo &evinfo); 323 325 void HandleUpdateProgramInfoEvent(const ProgramInfo &evinfo); … … 443 445 QStringList m_player_selected_new_show; 444 446 /// Main helper thread 445 447 PlaybackBoxHelper m_helper; 448 /// Outstanding preview image requests 449 QSet<QString> m_preview_tokens; 446 450 }; 447 451 448 452 class GroupSelector : public MythScreenType -
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/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
26 26 #endif // !__linux__ 27 27 28 28 #include <QCoreApplication> 29 #include <QThreadPool> 29 30 #include <QDateTime> 31 #include <QRunnable> 30 32 #include <QFile> 31 33 #include <QDir> 32 34 #include <QThread> … … 37 39 #include <QTcpServer> 38 40 #include <QTimer> 39 41 42 #include "previewgeneratorqueue.h" 40 43 #include "exitcodes.h" 41 44 #include "mythcontext.h" 42 45 #include "mythverbose.h" … … 53 56 #include "scheduledrecording.h" 54 57 #include "jobqueue.h" 55 58 #include "autoexpire.h" 56 #include "previewgenerator.h"57 59 #include "storagegroup.h" 58 60 #include "compat.h" 59 61 #include "RingBuffer.h" … … 65 67 #include "mythdirs.h" 66 68 #include "mythdownloadmanager.h" 67 69 68 69 70 /** Milliseconds to wait for an existing thread from 70 71 * process request thread pool. 71 72 */ … … 184 185 { 185 186 AutoExpire::Update(true); 186 187 188 PreviewGeneratorQueue::CreatePreviewGeneratorQueue( 189 PreviewGenerator::kLocalAndRemote, ~0, 0); 190 PreviewGeneratorQueue::AddListener(this); 191 187 192 for (int i = 0; i < PRT_STARTUP_THREAD_COUNT; i++) 188 193 { 189 194 ProcessRequestThread *prt = new ProcessRequestThread(this); … … 237 242 238 243 MainServer::~MainServer() 239 244 { 245 PreviewGeneratorQueue::RemoveListener(this); 246 PreviewGeneratorQueue::TeardownPreviewGeneratorQueue(); 247 240 248 if (mythserver) 241 249 { 242 250 mythserver->disconnect(); … … 546 554 else 547 555 HandleFileTransferQuery(listline, tokens, pbs); 548 556 } 549 else if (command == "QUERY_GENPIXMAP ")557 else if (command == "QUERY_GENPIXMAP2") 550 558 { 551 559 HandleGenPreviewPixmap(listline, pbs); 552 560 } … … 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 { 749 bool ok = false; 750 QString filename; 751 QString pginfokey; 752 QString token; 753 if (me->ExtraDataCount() >= 3) 754 { 755 ok = true; 756 pginfokey = me->ExtraData(0); 757 filename = me->ExtraData(1); 758 token = me->ExtraData(2); 759 } 760 761 if (message == "PREVIEW_QUEUED") 762 { 763 VERBOSE(VB_IMPORTANT, QString("Preview Queued: '%1' '%2' '%3'") 764 .arg(pginfokey).arg(filename).arg(token)); 765 return; 766 } 767 768 QFile file(filename); 769 ok = ok && file.open(QIODevice::ReadOnly); 770 771 if (ok) 772 { 773 VERBOSE(VB_IMPORTANT, QString("Preview Success: '%1' '%2' '%3'") 774 .arg(pginfokey).arg(filename).arg(token)); 775 776 QByteArray data = file.readAll(); 777 QStringList extra(token); 778 extra += "OK"; 779 extra += QString::number(data.size()); 780 extra += QString::number( 781 qChecksum(data.constData(), data.size())); 782 extra += QString(data.toBase64()); 783 MythEvent me("GENERATED_PIXMAP", extra); 784 gCoreContext->dispatch(me); 785 return; 786 } 787 else 788 { 789 message = "PREVIEW_FAILED"; 790 error = QString("Failed to read '%1'") 791 .arg(filename); 792 VERBOSE(VB_IMPORTANT, LOC_ERR + error); 793 } 794 } 795 796 if (message == "PREVIEW_FAILED" && me->ExtraDataCount() >= 3) 797 { 798 QString pginfokey = me->ExtraData(0); 799 QString filename = me->ExtraData(1); 800 QString token = me->ExtraData(2); 801 QString msg = me->ExtraDataCount() >= 4 ? me->ExtraData(3) : error; 802 QStringList extra(token); 803 extra.push_back("ERROR"); 804 extra.push_back(msg); 805 806 VERBOSE(VB_IMPORTANT, QString("Preview Failed: '%1' '%2' '%3'" 807 "\n\t\t\tMsg: %4") 808 .arg(pginfokey).arg(filename).arg(token) 809 .arg(msg)); 810 811 MythEvent me("GENERATED_PIXMAP", extra); 812 gCoreContext->dispatch(me); 813 return; 814 } 815 737 816 if (me->Message().left(11) == "AUTO_EXPIRE") 738 817 { 739 818 QStringList tokens = me->Message() … … 4876 4955 { 4877 4956 MythSocket *pbssock = pbs->getSocket(); 4878 4957 4958 if (slist.size() < 3) 4959 { 4960 VERBOSE(VB_IMPORTANT, LOC_ERR + "Too few params in pixmap request"); 4961 QStringList outputlist("ERROR"); 4962 outputlist += "TOO_FEW_PARAMS"; 4963 SendResponse(pbssock, outputlist); 4964 return; 4965 } 4966 4879 4967 bool time_fmt_sec = true; 4880 4968 long long time = -1; 4881 4969 QString outputfile; … … 4883 4971 int height = -1; 4884 4972 bool has_extra_data = false; 4885 4973 4886 QStringList::const_iterator it = slist.begin() + 1; 4974 QString token = slist[1]; 4975 if (token.isEmpty()) 4976 { 4977 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to parse pixmap request. " 4978 "Token absent"); 4979 QStringList outputlist("ERROR"); 4980 outputlist += "TOKEN_ABSENT"; 4981 SendResponse(pbssock, outputlist); 4982 } 4983 4984 QStringList::const_iterator it = slist.begin() + 2; 4887 4985 QStringList::const_iterator end = slist.end(); 4888 4986 ProgramInfo pginfo(it, end); 4889 4987 bool ok = pginfo.HasPathname(); 4890 4988 if (!ok) 4891 4989 { 4892 VERBOSE(VB_IMPORTANT, "MainServer: Failed to parse pixmap request."); 4990 VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to parse pixmap request. " 4991 "ProgramInfo missing pathname"); 4893 4992 QStringList outputlist("BAD"); 4894 outputlist += " ERROR_INVALID_REQUEST";4993 outputlist += "NO_PATHNAME"; 4895 4994 SendResponse(pbssock, outputlist); 4896 4995 } 4897 4996 if (it != slist.end()) … … 4936 5035 if (has_extra_data) 4937 5036 { 4938 5037 outputlist = slave->GenPreviewPixmap( 4939 &pginfo, time_fmt_sec, time, outputfile, outputsize);5038 token, &pginfo, time_fmt_sec, time, outputfile, outputsize); 4940 5039 } 4941 5040 else 4942 5041 { 4943 outputlist = slave->GenPreviewPixmap( &pginfo);5042 outputlist = slave->GenPreviewPixmap(token, &pginfo); 4944 5043 } 4945 5044 4946 5045 slave->DownRef(); … … 4955 5054 4956 5055 if (!pginfo.IsLocal()) 4957 5056 { 4958 VERBOSE(VB_IMPORTANT, "MainServer:HandleGenPreviewPixmap: Unable to "5057 VERBOSE(VB_IMPORTANT, LOC_ERR + "HandleGenPreviewPixmap: Unable to " 4959 5058 "find file locally, unable to make preview image."); 4960 QStringList outputlist( " BAD" );4961 outputlist += " ERROR_NOFILE";5059 QStringList outputlist( "ERROR" ); 5060 outputlist += "FILE_INACCESSIBLE"; 4962 5061 SendResponse(pbssock, outputlist); 4963 5062 return; 4964 5063 } 4965 5064 4966 PreviewGenerator *previewgen = new PreviewGenerator(&pginfo);4967 5065 if (has_extra_data) 4968 5066 { 4969 previewgen->SetOutputSize(outputsize); 4970 previewgen->SetOutputFilename(outputfile); 4971 previewgen->SetPreviewTime(time, time_fmt_sec); 5067 PreviewGeneratorQueue::GetPreviewImage( 5068 pginfo, outputsize, outputfile, time, time_fmt_sec, token); 4972 5069 } 4973 ok = previewgen->Run();4974 previewgen->deleteLater();4975 4976 if (ok)4977 {4978 QStringList outputlist("OK");4979 if (!outputfile.isEmpty())4980 outputlist += outputfile;4981 SendResponse(pbssock, outputlist);4982 }4983 5070 else 4984 5071 { 4985 VERBOSE(VB_IMPORTANT, "MainServer: Failed to make preview image."); 4986 QStringList outputlist( "BAD" ); 4987 outputlist += "ERROR_UNKNOWN"; 4988 SendResponse(pbssock, outputlist); 5072 PreviewGeneratorQueue::GetPreviewImage(pginfo, token); 4989 5073 } 5074 5075 QStringList outputlist("OK"); 5076 if (!outputfile.isEmpty()) 5077 outputlist += outputfile; 5078 SendResponse(pbssock, outputlist); 4990 5079 } 4991 5080 4992 5081 void MainServer::HandlePixmapLastModified(QStringList &slist, PlaybackSock *pbs)