Go to the documentation of this file.
9 #include <QCoreApplication>
25 #define LOC QString("PreviewQueue: ")
45 uint maxAttempts, std::chrono::seconds minBlockSeconds)
85 uint maxAttempts, std::chrono::seconds minBlockSeconds) :
86 MThread(
"PreviewGeneratorQueue"),
88 m_maxAttempts(maxAttempts), m_minBlockSeconds(minBlockSeconds)
92 int idealThreads = QThread::idealThreadCount();
93 m_maxThreads = (idealThreads >= 1) ? idealThreads * 2 : 2;
111 QMutexLocker locker(&
m_lock);
116 (*it).m_gen->deleteLater();
117 (*it).m_gen =
nullptr;
146 const QSize outputsize,
147 const QString &outputfile,
148 std::chrono::seconds time,
long long frame,
149 const QString&
token)
166 extra += QString::number(outputsize.width());
167 extra += QString::number(outputsize.height());
171 extra += QString::number(time.count());
176 extra += QString::number(frame);
179 auto *
e =
new MythEvent(
"GET_PREVIEW", extra);
180 QCoreApplication::postEvent(
s_pgq,
e);
234 return QObject::event(
e);
238 return QObject::event(
e);
239 if (me->Message() ==
"GET_PREVIEW")
241 const QStringList &list = me->ExtraDataList();
242 QStringList::const_iterator it = list.begin();
247 long long time_or_frame = -1LL;
248 if (it != list.end())
250 if (it != list.end())
251 outputsize.setWidth((*it++).toInt());
252 if (it != list.end())
253 outputsize.setHeight((*it++).toInt());
254 if (it != list.end())
255 outputfile = (*it++);
256 if (it != list.end())
257 time_or_frame = (*it++).toLongLong();
258 if (it != list.end())
260 bool time_fmt_sec = (*it++).toInt() != 0;
264 std::chrono::seconds(time_or_frame), -1,
token);
269 -1s, time_or_frame,
token);
274 if (me->Message() ==
"PREVIEW_SUCCESS" ||
275 me->Message() ==
"PREVIEW_FAILED")
277 uint recordedingID = me->ExtraData(0).toUInt();
278 const QString&
filename = me->ExtraData(1);
279 const QString& msg = me->ExtraData(2);
280 const QString& datetime = me->ExtraData(3);
281 const QString&
token = me->ExtraData(4);
284 QMutexLocker locker(&
m_lock);
288 LOG(VB_GENERAL, LOG_ERR,
LOC +
289 QString(
"Failed to find token %1 in map.").
arg(
token));
295 LOG(VB_GENERAL, LOG_ERR,
LOC +
296 QString(
"Failed to find key %1 in map.").
arg(*kit));
301 (*it).m_gen->deleteLater();
302 (*it).m_gen =
nullptr;
303 (*it).m_genStarted =
false;
304 if (me->Message() ==
"PREVIEW_SUCCESS")
306 (*it).m_attempts = 0;
307 (*it).m_lastBlockTime = 0s;
308 (*it).m_blockRetryUntil = QDateTime();
312 (*it).m_lastBlockTime =
314 (*it).m_blockRetryUntil =
319 list.push_back(QString::number(recordedingID));
322 list.push_back(datetime);
323 for (
const auto & tok : qAsConst((*it).m_tokens))
335 auto *le =
new MythEvent(me->Message(), list);
336 QCoreApplication::postEvent(listener, le);
338 (*it).m_tokens.clear();
348 return QObject::event(
e);
374 const QString &eventname,
375 const QString &
filename,
const QString &
token,
const QString &msg,
383 list.push_back(
token);
385 QMutexLocker locker(&
m_lock);
389 QCoreApplication::postEvent(listener,
e);
431 const QString &outputfile,
432 std::chrono::seconds time,
long long frame,
433 const QString&
token)
435 auto pos_text = (time >= 0s)
436 ? QString::number(time.count()) +
"s"
437 : QString::number(frame)+
"f";
438 QString key = QString(
"%1_%2x%3_%4")
439 .arg(pginfo.
GetBasename()).arg(size.width()).arg(size.height())
445 "Pending Delete", QDateTime());
450 QString
filename = (outputfile.isEmpty()) ?
455 bool is_special = !outputfile.isEmpty() || time >= 0s ||
456 (size.width() != 0) || (size.height() != 0);
458 bool needs_gen =
true;
461 QDateTime previewLastModified;
462 bool streaming = !
filename.startsWith(
"/");
463 bool locally_accessible =
false;
464 bool bookmark_updated =
false;
468 if (bookmark_ts.isValid())
469 cmp_ts = bookmark_ts;
477 ret_file = QString(
"%1/%2")
480 QFileInfo finfo(ret_file);
481 if (finfo.isReadable() && finfo.lastModified() >= cmp_ts)
488 previewLastModified = finfo.lastModified();
492 previewLastModified =
499 if ((locally_accessible = fi.isReadable()))
500 previewLastModified = fi.lastModified();
504 (!previewLastModified.isValid() || (previewLastModified <= cmp_ts));
506 if (bookmark_updated && bookmark_ts.isValid() &&
507 previewLastModified.isValid())
512 bool preview_exists = previewLastModified.isValid();
515 QString alttext = (bookmark_ts.isValid()) ? QString() :
516 QString(
"\n\t\t\tcmp_ts: %1")
518 LOG(VB_GENERAL, LOG_INFO,
519 QString(
"previewLastModified: %1\n\t\t\t"
520 "bookmark_ts: %2%3\n\t\t\t"
521 "pginfo.lastmodified: %4")
526 QString(
"Title: %1\n\t\t\t")
528 QString(
"File '%1' \n\t\t\tCache '%2'")
530 QString(
"\n\t\t\tPreview Exists: %1, Bookmark Updated: %2, "
532 .
arg(preview_exists).
arg(bookmark_updated)
533 .
arg((bookmark_updated || !preview_exists)));
536 needs_gen = bookmark_updated || !preview_exists;
540 if (locally_accessible)
542 else if (preview_exists && QFileInfo(ret_file).isReadable())
552 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
553 QString(
"Requesting preview for '%1'") .
arg(key));
555 if (!outputfile.isEmpty() || time >= 0s ||
556 size.width() || size.height())
558 pg->SetPreviewTime(time, frame);
559 pg->SetOutputFilename(outputfile);
560 pg->SetOutputSize(size);
565 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
566 QString(
"Requested preview for '%1'").
arg(key));
570 LOG(VB_GENERAL, LOG_ERR,
LOC +
571 QString(
"Attempted to generate preview for '%1' "
572 "%2 times; >= max(%3)")
578 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
579 QString(
"Not requesting preview for %1,"
580 "as it is already being generated")
589 QString msg =
"On Disk";
590 QDateTime dt = QFileInfo(ret).lastModified();
595 uint queue_depth = 0;
597 GetInfo(key, queue_depth, token_cnt);
598 QString msg = QString(
"Queue depth %1, our tokens %2")
599 .arg(queue_depth).arg(token_cnt);
600 SendEvent(pginfo,
"PREVIEW_QUEUED", ret,
token, msg, QDateTime());
619 const QString &key,
uint &queue_depth,
uint &token_cnt)
621 QMutexLocker locker(&
m_lock);
624 token_cnt = (pit ==
m_previewMap.end()) ? 0 : (*pit).m_tokens.size();
635 const QString &key,
const QString&
token)
637 QMutexLocker locker(&
m_lock);
644 if ((*pit).m_gen && !(*pit).m_genStarted)
647 if (!
token.isEmpty())
650 (*pit).m_tokens.insert(
token);
660 QMutexLocker locker(&
m_lock);
664 QString fn = q.back();
667 if (it !=
m_previewMap.end() && (*it).m_gen && !(*it).m_genStarted)
670 (*it).m_gen->start();
671 (*it).m_genStarted =
true;
691 QMutexLocker locker(&
m_lock);
696 if (state.
m_gen != g)
733 PreviewMap::const_iterator it;
734 QMutexLocker locker(&
m_lock);
739 if ((*it).m_blockRetryUntil.isValid())
755 QMutexLocker locker(&
m_lock);
770 QMutexLocker locker(&
m_lock);
static Type MythEventMessage
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
This class holds all the state information related to a specific preview generator.
void AttachSignals(QObject *obj)
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
uint IncPreviewGeneratorAttempts(const QString &key)
Increments and returns number of times we have started a PreviewGenerator to create this file.
uint GetRecordingID(void) const
This class creates a preview image of a recording.
This class is used as a container for messages.
static void RemoveListener(QObject *listener)
Stop receiving notifications when a preview event is generated.
arg(title).arg(filename).arg(doDelete))
static void CreatePreviewGeneratorQueue(PreviewGenerator::Mode mode, uint maxAttempts, std::chrono::seconds minBlockSeconds)
Create the singleton queue of preview generators.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void SendEvent(const ProgramInfo &pginfo, const QString &eventname, const QString &filename, const QString &token, const QString &msg, const QDateTime &dt)
Send a message back to all objects that have requested creation of a specific preview.
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
QString GeneratePreviewImage(ProgramInfo &pginfo, QSize size, const QString &outputfile, std::chrono::seconds time, long long frame, const QString &token)
Generate a preview image for the specified program.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
uint m_running
The number of threads currently generating previews.
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
QString GetPathname(void) const
uint m_maxAttempts
How many times total will the code attempt to generate a preview for a specific file,...
PreviewGenerator::Mode m_mode
void ClearPreviewGeneratorAttempts(const QString &key)
Clears the number of times we have started a PreviewGenerator to create this file.
QSet< QString > m_tokens
The full set of tokens for all callers that have requested this preview.
QMutex m_lock
The thread interlock for this data structure.
bool event(QEvent *e) override
The event handler running on the preview generation thread.
std::chrono::seconds m_minBlockSeconds
How long after a failed preview generation attempt will the code ignore subsequent requests.
void IncPreviewGeneratorPriority(const QString &key, const QString &token)
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
static void AddListener(QObject *listener)
Request notifications when a preview event is generated.
PreviewGenerator * m_gen
A pointer to the generator that this state object describes.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int GetNumSetting(const QString &key, int defaultval=0)
uint m_maxThreads
The maximum number of threads that may concurrently generate previews.
QString GetRemoteCacheDir(void)
Returns the directory for all files cached from the backend.
PreviewMap m_previewMap
A mapping from the generated preview name to the state information on the progress of generating the ...
QSet< QObject * > m_listeners
The set of all listeners that want messages when a preview request is queued or finishes.
static void TeardownPreviewGeneratorQueue()
Destroy the singleton queue of preview generators.
Holds information on recordings and videos.
QDateTime GetLastModifiedTime(void) const
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
void GetInfo(const QString &key, uint &queue_depth, uint &token_cnt)
bool IsGeneratingPreview(const QString &key) const
Is a preview currently being generated for this key.
QString GetToken(void) const
static void GetPreviewImage(const ProgramInfo &pginfo, const QString &token)
Submit a request for the generation of a preview image.
bool m_genStarted
The preview generator for this file is currently running.
This is a wrapper around QThread that does several additional things.
QStringList m_queue
The queue of previews to be generated.
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
PreviewGeneratorQueue(PreviewGenerator::Mode mode, uint maxAttempts, std::chrono::seconds minBlockSeconds)
void SetPreviewGenerator(const QString &key, PreviewGenerator *g)
Sets the PreviewGenerator for a specific file.
static PreviewGeneratorQueue * s_pgq
The singleton queue.
This class implements a queue of preview generation requests.
QDateTime RemoteGetPreviewIfModified(const ProgramInfo &pginfo, const QString &cachefile)
Download preview & get timestamp if newer than cachefile's last modified time, otherwise just get the...
QString GetBasename(void) const
AvailableStatusType GetAvailableStatus(void) const
~PreviewGeneratorQueue() override
Destroy the preview generation queue.
QDateTime GetBookmarkUpdate(void) const
void UpdatePreviewGeneratorThreads(void)
As long as there are items in the queue, make sure we're running the maximum allowed number of previe...
QMap< QString, QString > m_tokenToKeyMap
A mapping from requestor tokens to internal keys.