MythTV master
previewgenerator.cpp
Go to the documentation of this file.
1// C headers
2#include <cmath>
3#include <utility>
4
5// POSIX headers
6#include <sys/types.h> // for utime
7#include <sys/time.h>
8#include <fcntl.h>
9#include <utime.h> // for utime
10
11// Qt headers
12#include <QApplication>
13#include <QCoreApplication>
14#include <QDir>
15#include <QFileInfo>
16#include <QImage>
17#include <QMetaType>
18#include <QTemporaryFile>
19#include <QUrl>
20
21// MythTV headers
23#include "libmythbase/mythconfig.h"
34
35#include "io/mythmediabuffer.h"
36#include "mythpreviewplayer.h"
37#include "playercontext.h"
38#include "previewgenerator.h"
39#include "tv_rec.h"
40
41#define LOC QString("Preview: ")
42
75 QString token,
77 : MThread("PreviewGenerator"),
78 m_programInfo(*pginfo), m_mode(mode),
79 m_pathname(pginfo->GetPathname()),
80 m_token(std::move(token))
81{
82 // Qt requires that a receiver have the same thread affinity as the QThread
83 // sending the event, which is used to dispatch MythEvents sent by
84 // gCoreContext->dispatchNow(me)
85 moveToThread(QApplication::instance()->thread());
86}
87
89{
91 wait();
92}
93
94void PreviewGenerator::SetOutputFilename(const QString &fileName)
95{
96 if (fileName.isEmpty())
97 return;
98 QFileInfo fileinfo = QFileInfo(fileName);
99 m_outFileName = fileName;
100 m_outFormat = fileinfo.suffix().toUpper();
101}
102
104{
105 QMutexLocker locker(&m_previewLock);
106 m_previewWaitCondition.wakeAll();
107 m_listener = nullptr;
108}
109
111{
112 TeardownAll();
113 QObject::deleteLater();
114}
115
117{
118 QMutexLocker locker(&m_previewLock);
119 m_listener = obj;
120}
121
126{
127 QString msg;
128 QTime tm = QTime::currentTime();
129 QElapsedTimer te; te.start();
130 bool ok = false;
131 bool is_local = IsLocal();
132
133 if (!is_local && !!(m_mode & kRemote))
134 {
135 LOG(VB_GENERAL, LOG_ERR, LOC +
136 QString("RunReal() file not local: '%1'")
137 .arg(m_pathname));
138 }
139 else if (!(m_mode & kLocal) && !(m_mode & kRemote))
140 {
141 LOG(VB_GENERAL, LOG_ERR, LOC +
142 QString("RunReal() Preview of '%1' failed "
143 "because mode was invalid 0x%2")
144 .arg(m_pathname).arg((int)m_mode,0,16));
145 }
146 else if (!!(m_mode & kLocal) && LocalPreviewRun())
147 {
148 ok = true;
149 msg = QString("Generated on %1 in %2 seconds, starting at %3")
151 .arg(te.elapsed()*0.001)
152 .arg(tm.toString(Qt::ISODate));
153 }
154 else if (!!(m_mode & kRemote))
155 {
156 if (is_local && (m_mode & kLocal))
157 {
158 LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to save preview."
159 "\n\t\t\tYou may need to check user and group ownership on"
160 "\n\t\t\tyour frontend and backend for quicker previews.\n"
161 "\n\t\t\tAttempting to regenerate preview on backend.\n");
162 }
163 ok = RemotePreviewRun();
164 if (ok)
165 {
166 msg = QString("Generated remotely in %1 seconds, starting at %2")
167 .arg(te.elapsed()*0.001)
168 .arg(tm.toString(Qt::ISODate));
169 }
170 else
171 {
172 msg = "Remote preview failed";
173 }
174 }
175 else
176 {
177 msg = "Could not access recording";
178 }
179
180 QMutexLocker locker(&m_previewLock);
181 if (m_listener)
182 {
183 // keep in sync with default filename in
184 // PreviewGeneratorQueue::GeneratePreviewImage
185 QString output_fn = m_outFileName.isEmpty() ?
187
188 QDateTime dt;
189 if (ok)
190 {
191 QFileInfo fi(output_fn);
192 if (fi.exists())
193 dt = fi.lastModified();
194 }
195
196 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED";
197 QStringList list;
198 list.push_back(QString::number(m_programInfo.GetRecordingID()));
199 list.push_back(output_fn);
200 list.push_back(msg);
201 list.push_back(dt.isValid()?dt.toUTC().toString(Qt::ISODate):"");
202 list.push_back(m_token);
203 QCoreApplication::postEvent(m_listener, new MythEvent(message, list));
204 }
205
206 return ok;
207}
208
210{
211 QString msg;
212 QTime tm = QTime::currentTime();
213 QElapsedTimer te; te.start();
214 bool ok = false;
215 QString command = GetAppBinDir() + "mythpreviewgen";
216 bool local_ok = ((IsLocal() || ((m_mode & kForceLocal) != 0)) &&
217 ((m_mode & kLocal) != 0) &&
218 QFileInfo(command).isExecutable());
219 if (!local_ok)
220 {
221 if (!!(m_mode & kRemote))
222 {
223 ok = RemotePreviewRun();
224 if (ok)
225 {
226 msg =
227 QString("Generated remotely in %1 seconds, starting at %2")
228 .arg(te.elapsed()*0.001)
229 .arg(tm.toString(Qt::ISODate));
230 }
231 }
232 else
233 {
234 LOG(VB_GENERAL, LOG_ERR, LOC +
235 QString("Run() cannot generate preview locally for: '%1'")
236 .arg(m_pathname));
237 msg = "Failed, local preview requested for remote file.";
238 }
239 }
240 else
241 {
242 // This is where we fork and run mythpreviewgen to actually make preview
243 QStringList cmdargs;
244
245 cmdargs << "--size"
246 << QString("%1x%2").arg(m_outSize.width()).arg(m_outSize.height());
247 if (m_captureTime >= 0s)
248 cmdargs << "--seconds" << QString::number(m_captureTime.count());
249 else if (m_captureFrame >= 0)
250 cmdargs << "--frame" << QString::number(m_captureFrame);
251 cmdargs << "--chanid"
252 << QString::number(m_programInfo.GetChanID())
253 << "--starttime"
255
256 if (!m_outFileName.isEmpty())
257 cmdargs << "--outfile" << m_outFileName;
258
259 // Timeout in 30s
260 auto *ms = new MythSystemLegacy(command, cmdargs,
266 ms->SetNice(10);
267 ms->SetIOPrio(7);
268
269 ms->Run(30s);
270 uint ret = ms->Wait();
271 delete ms;
272
273 if (ret != GENERIC_EXIT_OK)
274 {
275 LOG(VB_GENERAL, LOG_ERR, LOC +
276 QString("Encountered problems running '%1 %2' - (%3)")
277 .arg(command, cmdargs.join(" "), QString::number(ret)));
278 }
279 else
280 {
281 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process returned 0.");
282 QString outname = (!m_outFileName.isEmpty()) ?
283 m_outFileName : (m_pathname + ".png");
284
285 QString lpath = QFileInfo(outname).fileName();
286 if (lpath == outname)
287 {
288 StorageGroup sgroup;
289 QString tmpFile = sgroup.FindFile(lpath);
290 outname = (tmpFile.isEmpty()) ? outname : tmpFile;
291 }
292
293 QFileInfo fi(outname);
294 ok = (fi.exists() && fi.isReadable() && (fi.size() != 0));
295 if (ok)
296 {
297 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process ran ok.");
298 msg = QString("Generated on %1 in %2 seconds, starting at %3")
300 .arg(te.elapsed()*0.001)
301 .arg(tm.toString(Qt::ISODate));
302 }
303 else
304 {
305 LOG(VB_GENERAL, LOG_ERR, LOC + "Preview process not ok." +
306 QString("\n\t\t\tfileinfo(%1)").arg(outname) +
307 QString(" exists: %1").arg(fi.exists()) +
308 QString(" readable: %1").arg(fi.isReadable()) +
309 QString(" size: %1").arg(fi.size()));
310 LOG(VB_GENERAL, LOG_ERR, LOC +
311 QString("Despite command '%1' returning success")
312 .arg(command));
313 msg = QString("Failed to read preview image despite "
314 "preview process returning success.");
315 }
316 }
317 }
318
319 QMutexLocker locker(&m_previewLock);
320
321 // Backdate file to start of preview time in case a bookmark was made
322 // while we were generating the preview.
323 QString output_fn = m_outFileName.isEmpty() ?
325
326 QDateTime dt;
327 if (ok)
328 {
329 QFileInfo fi(output_fn);
330 if (fi.exists())
331 dt = fi.lastModified();
332 }
333
334 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED";
335 if (m_listener)
336 {
337 QStringList list;
338 list.push_back(QString::number(m_programInfo.GetRecordingID()));
339 list.push_back(output_fn);
340 list.push_back(msg);
341 list.push_back(dt.isValid()?dt.toUTC().toString(Qt::ISODate):"");
342 list.push_back(m_token);
343 QCoreApplication::postEvent(m_listener, new MythEvent(message, list));
344 }
345
346 return ok;
347}
348
350{
351 RunProlog();
352 Run();
353 RunEpilog();
354}
355
357{
358 QStringList strlist( "QUERY_GENPIXMAP2" );
359 if (m_token.isEmpty())
360 {
361 m_token = QString("%1:%2")
363 }
364 strlist.push_back(m_token);
366 if (m_captureTime >= 0s)
367 {
368 strlist.push_back("s");
369 strlist.push_back(QString::number(m_captureTime.count()));
370 }
371 else
372 {
373 strlist.push_back("f");
374 strlist.push_back(QString::number(m_captureFrame));
375 }
376 if (m_outFileName.isEmpty())
377 {
378 strlist.push_back("<EMPTY>");
379 }
380 else
381 {
382 QFileInfo fi(m_outFileName);
383 strlist.push_back(fi.fileName());
384 }
385 strlist.push_back(QString::number(m_outSize.width()));
386 strlist.push_back(QString::number(m_outSize.height()));
387
389 m_pixmapOk = false;
390
391 bool ok = gCoreContext->SendReceiveStringList(strlist);
392 if (!ok || strlist.empty() || (strlist[0] != "OK"))
393 {
394 if (!ok)
395 {
396 LOG(VB_GENERAL, LOG_ERR, LOC +
397 "Remote Preview failed due to communications error.");
398 }
399 else if (strlist.size() > 1)
400 {
401 LOG(VB_GENERAL, LOG_ERR, LOC +
402 "Remote Preview failed, reason given: " + strlist[1]);
403 }
404
406
407 return false;
408 }
409
410 QMutexLocker locker(&m_previewLock);
411
412 // wait up to 35 seconds for the preview to complete
413 // The backend waits 30s for creation
414 if (!m_gotReply)
415 m_previewWaitCondition.wait(&m_previewLock, 35UL * 1000);
416
417 if (!m_gotReply)
418 LOG(VB_GENERAL, LOG_NOTICE, LOC + "RemotePreviewRun() -- no reply..");
419
421
422 return m_pixmapOk;
423}
424
426{
427 if (e->type() != MythEvent::kMythEventMessage)
428 return QObject::event(e);
429
430 auto *me = dynamic_cast<MythEvent*>(e);
431 if (me == nullptr)
432 return QObject::event(e);
433 if (me->Message() != "GENERATED_PIXMAP" || me->ExtraDataCount() < 3)
434 return QObject::event(e);
435
436 bool ok = me->ExtraData(0) == "OK";
437 bool ours = false;
438 uint i = ok ? 4 : 3;
439 for (; i < (uint) me->ExtraDataCount() && !ours; i++)
440 ours |= me->ExtraData(i) == m_token;
441 if (!ours)
442 return QObject::event(e);
443
444 const QString& pginfokey = me->ExtraData(1);
445
446 QMutexLocker locker(&m_previewLock);
447 m_gotReply = true;
448 m_pixmapOk = ok;
449 if (!ok)
450 {
451 LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + ": " + me->ExtraData(2));
452 m_previewWaitCondition.wakeAll();
453 return true;
454 }
455
456 if (me->ExtraDataCount() < 5)
457 {
458 m_pixmapOk = false;
459 m_previewWaitCondition.wakeAll();
460 return true; // could only happen with very broken client...
461 }
462
463 QDateTime datetime = MythDate::fromString(me->ExtraData(3));
464 if (!datetime.isValid())
465 {
466 m_pixmapOk = false;
467 LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + "Got invalid date");
468 m_previewWaitCondition.wakeAll();
469 return QObject::event(e);
470 }
471
472 size_t length = me->ExtraData(4).toULongLong();
473 quint16 checksum16 = me->ExtraData(5).toUInt();
474 QByteArray data = QByteArray::fromBase64(me->ExtraData(6).toLatin1());
475 if ((size_t) data.size() < length)
476 { // (note data.size() may be up to 3
477 // bytes longer after decoding
478 LOG(VB_GENERAL, LOG_ERR, LOC +
479 QString("Preview size check failed %1 < %2")
480 .arg(data.size()).arg(length));
481 data.clear();
482 }
483 data.resize(length);
484
485#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
486 quint16 calculated = qChecksum(data.constData(), data.size());
487#else
488 quint16 calculated = qChecksum(data);
489#endif
490 if (checksum16 != calculated)
491 {
492 LOG(VB_GENERAL, LOG_ERR, LOC + "Preview checksum failed");
493 data.clear();
494 }
495
496 m_pixmapOk = (data.isEmpty()) ? false : SaveOutFile(data, datetime);
497
498 m_previewWaitCondition.wakeAll();
499
500 return true;
501}
502
503bool PreviewGenerator::SaveOutFile(const QByteArray &data, const QDateTime &dt)
504{
505 if (m_outFileName.isEmpty())
506 {
507 QString remotecachedirname =
508 QString("%1/cache/remotecache").arg(GetConfDir());
509 QDir remotecachedir(remotecachedirname);
510
511 if (!remotecachedir.exists())
512 {
513 if (!remotecachedir.mkdir(remotecachedirname))
514 {
515 LOG(VB_GENERAL, LOG_ERR, LOC +
516 "Remote Preview failed because we could not create a "
517 "remote cache directory");
518 return false;
519 }
520 }
521
522 QString filename = m_programInfo.GetBasename() + ".png";
523 SetOutputFilename(QString("%1/%2").arg(remotecachedirname, filename));
524 }
525
526 QFile file(m_outFileName);
527 bool ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
528 if (!ok)
529 {
530 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open: '%1'")
531 .arg(m_outFileName));
532 }
533
534 off_t offset = 0;
535 size_t remaining = data.size();
536 uint failure_cnt = 0;
537 while ((remaining > 0) && (failure_cnt < 5))
538 {
539 ssize_t written = file.write(data.data() + offset, remaining);
540 if (written < 0)
541 {
542 failure_cnt++;
543 usleep(50ms);
544 continue;
545 }
546
547 failure_cnt = 0;
548 offset += written;
549 remaining -= written;
550 }
551
552 if (ok && !remaining)
553 {
554 file.close();
555 struct utimbuf times {};
556 times.actime = times.modtime = dt.toSecsSinceEpoch();
557 utime(m_outFileName.toLocal8Bit().constData(), &times);
558 LOG(VB_FILE, LOG_INFO, LOC + QString("Saved: '%1'").arg(m_outFileName));
559 }
560 else
561 {
562 file.remove();
563 }
564
565 return ok;
566}
567
569 const unsigned char *data,
570 uint width, uint height, float aspect,
571 int desired_width, int desired_height,
572 const QString &format)
573{
574 if (!data || !width || !height)
575 return false;
576
577 const QImage img(data, width, height, QImage::Format_RGB32);
578
579 float ppw = std::max(desired_width, 0);
580 float pph = std::max(desired_height, 0);
581 bool desired_size_exactly_specified = true;
582 if ((ppw < 1.0F) && (pph < 1.0F))
583 {
584 ppw = img.width();
585 pph = img.height();
586 desired_size_exactly_specified = false;
587 }
588
589 aspect = (aspect <= 0.0F) ? ((float) width) / height : aspect;
590 pph = (pph < 1.0F) ? (ppw / aspect) : pph;
591 ppw = (ppw < 1.0F) ? (pph * aspect) : ppw;
592
593 if (!desired_size_exactly_specified)
594 {
595 if (aspect > ppw / pph)
596 pph = (ppw / aspect);
597 else
598 ppw = (pph * aspect);
599 }
600
601 ppw = std::max(1.0F, ppw);
602 pph = std::max(1.0F, pph);;
603
604 QImage small_img = img.scaled((int) ppw, (int) pph,
605 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
606
607 QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX");
608 f.setAutoRemove(false);
609 if (f.open() && small_img.save(&f, format.toLocal8Bit().constData()))
610 {
611 // Let anybody update it
612 bool ret = makeFileAccessible(f.fileName().toLocal8Bit().constData());
613 if (!ret)
614 {
615 LOG(VB_GENERAL, LOG_ERR, "Unable to change permissions on "
616 "preview image. Backends and frontends "
617 "running under different users will be "
618 "unable to access it");
619 }
620 QFile of(filename);
621 of.remove();
622 if (f.rename(filename))
623 {
624 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Saved preview '%0' %1x%2")
625 .arg(filename).arg((int) ppw).arg((int) pph));
626 return true;
627 }
628 f.remove();
629 }
630
631 return false;
632}
633
635{
638
639 float aspect = 0;
640 std::chrono::seconds captime = m_captureTime;
641 long long capframe = -1;
642
643 QDateTime dt = MythDate::current();
644
645 if (captime > 0s)
646 LOG(VB_GENERAL, LOG_INFO, "Preview from time spec");
647 else
648 {
649 capframe = m_programInfo.QueryStartMark();
650 if (capframe > 0)
651 {
652 LOG(VB_GENERAL, LOG_INFO,
653 QString("Preview from bookmark (frame %1)").arg(capframe));
654 }
655 }
656
657 if ((captime <= 0s) && (capframe <= 0))
658 {
659 std::chrono::seconds startEarly = 0s;
660 std::chrono::seconds programDuration = 0s;
661 auto preroll = gCoreContext->GetDurSetting<std::chrono::seconds>("RecordPreRoll", 0s);
662 if (m_programInfo.GetScheduledStartTime().isValid() &&
666 {
667 programDuration = std::chrono::seconds(m_programInfo.GetScheduledStartTime()
669 }
670 if (m_programInfo.GetRecordingStartTime().isValid() &&
674 {
675 startEarly = std::chrono::seconds(m_programInfo.GetRecordingStartTime()
677 }
678 if (programDuration > 0s)
679 {
680 captime = programDuration / 3;
681 if (captime > 10min)
682 captime = 10min;
683 captime += startEarly;
684 }
685 if (captime < 0s)
686 captime = 10min;
687 captime += preroll;
688 LOG(VB_GENERAL, LOG_INFO,
689 QString("Preview at calculated offset (%1 seconds)").arg(captime.count()));
690 }
691
692 int width = 0;
693 int height = 0;
694 int sz = 0;
695 auto *data = GetScreenGrab(m_programInfo, m_pathname, captime, capframe,
696 sz, width, height, aspect);
697
699
700 QString format = (m_outFormat.isEmpty()) ? "PNG" : m_outFormat;
701
702 int dw = (m_outSize.width() < 0) ? width : m_outSize.width();
703 int dh = (m_outSize.height() < 0) ? height : m_outSize.height();
704
705 bool ok = SavePreview(outname, data, width, height, aspect, dw, dh,
706 format);
707
708 if (ok)
709 {
710 // Backdate file to start of preview time in case a bookmark was made
711 // while we were generating the preview.
712 struct utimbuf times {};
713 times.actime = times.modtime = dt.toSecsSinceEpoch();
714 utime(outname.toLocal8Bit().constData(), &times);
715 }
716
717 delete[] data;
718
720
721 return ok;
722}
723
725 const QString &pathname, const QString &outFileName)
726{
727 QString outname = pathname + ".png";
728
729 if (outFileName.isEmpty())
730 return outname;
731
732 outname = outFileName;
733 QFileInfo fi(outname);
734 if (outname == fi.fileName())
735 {
736 QString dir;
737 if (pathname.contains(':'))
738 {
739 QUrl uinfo(pathname);
740 uinfo.setPath("");
741 dir = uinfo.toString();
742 }
743 else
744 {
745 dir = QFileInfo(pathname).path();
746 }
747 outname = dir + "/" + fi.fileName();
748 LOG(VB_FILE, LOG_INFO, LOC + QString("outfile '%1' -> '%2'")
749 .arg(outFileName, outname));
750 }
751
752 return outname;
753}
754
756{
757 QString tmppathname = m_pathname;
758
759 if (tmppathname.startsWith("dvd:"))
760 tmppathname = tmppathname.section(":", 1, 1);
761
762 if (!QFileInfo(tmppathname).isReadable())
763 return false;
764
765 tmppathname = m_outFileName.isEmpty() ? tmppathname : m_outFileName;
766 QString pathdir = QFileInfo(tmppathname).path();
767
768 if (!QFileInfo(pathdir).isWritable())
769 {
770 LOG(VB_GENERAL, LOG_WARNING, LOC +
771 QString("Output path '%1' is not writeable") .arg(pathdir));
772 return false;
773 }
774
775 return true;
776}
777
795 const ProgramInfo &pginfo, const QString &filename,
796 std::chrono::seconds seektime, long long seekframe,
797 int &bufferlen,
798 int &video_width, int &video_height, float &video_aspect)
799{
800 uint8_t *retbuf = nullptr;
801 bufferlen = 0;
802
804 {
805 LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not connect to DB.");
806 return nullptr;
807 }
808
809 // pre-test local files for existence and size. 500 ms speed-up...
810 if (filename.startsWith("/"))
811 {
812 QFileInfo info(filename);
813 bool invalid = (!info.exists() || !info.isReadable() || (info.isFile() && (info.size() < 8LL*1024)));
814 if (invalid)
815 {
816 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Previewer file '%1' is not valid.")
817 .arg(filename));
818 return nullptr;
819 }
820 }
821
822 MythMediaBuffer* buffer = MythMediaBuffer::Create(filename, false, false, 0ms);
823 if (!buffer || !buffer->IsOpen())
824 {
825 LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not open file: " +
826 QString("'%1'").arg(filename));
827 delete buffer;
828 return nullptr;
829 }
830
832 auto * player = new MythPreviewPlayer(ctx, static_cast<PlayerFlags>(kAudioMuted | kVideoIsNull | kNoITV));
833 ctx->SetRingBuffer(buffer);
834 ctx->SetPlayingInfo(&pginfo);
835 ctx->SetPlayer(player);
836
837 if (seektime >= 0s)
838 {
839 retbuf = player->GetScreenGrab(seektime, bufferlen,
840 video_width, video_height, video_aspect);
841 }
842 else
843 {
844 retbuf = player->GetScreenGrabAtFrame(static_cast<uint64_t>(seekframe), true,
845 bufferlen, video_width, video_height, video_aspect);
846 }
847 delete ctx;
848
849 auto pos_text = (seektime != std::chrono::seconds::max())
850 ? QString::number(seektime.count()) + "s"
851 : QString::number(seekframe)+ "f";
852 if (retbuf)
853 {
854 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Grabbed preview '%0' %1x%2@%3")
855 .arg(filename).arg(video_width).arg(video_height).arg(pos_text));
856 }
857 else
858 {
859 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to grab preview '%0' %1x%2@%3")
860 .arg(filename).arg(video_width).arg(video_height).arg(pos_text));
861 }
862
863 return retbuf;
864}
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:876
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
QString GetHostName(void)
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
This class is used as a container for messages.
Definition: mythevent.h:17
static const Type kMythEventMessage
Definition: mythevent.h:79
virtual bool IsOpen(void) const =0
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
static uint8_t * GetScreenGrab(const ProgramInfo &pginfo, const QString &filename, std::chrono::seconds seektime, long long seekframe, int &bufferlen, int &video_width, int &video_height, float &video_aspect)
Returns a AV_PIX_FMT_RGBA32 buffer containg a frame from the video.
bool RemotePreviewRun(void)
PreviewGenerator(const ProgramInfo *pginfo, QString token, Mode mode=kLocal)
Constructor.
long long m_captureFrame
std::chrono::seconds m_captureTime
snapshot time in seconds or frame number (seconds has priority)
bool LocalPreviewRun(void)
static QString CreateAccessibleFilename(const QString &pathname, const QString &outFileName)
static bool SavePreview(const QString &filename, const unsigned char *data, uint width, uint height, float aspect, int desired_width, int desired_height, const QString &format)
bool SaveOutFile(const QByteArray &data, const QDateTime &dt)
void AttachSignals(QObject *obj)
~PreviewGenerator() override
QWaitCondition m_previewWaitCondition
void SetOutputFilename(const QString &fileName)
bool RunReal(void)
This call creates a preview without starting a new thread.
bool event(QEvent *e) override
ProgramInfo m_programInfo
bool IsLocal(void) const
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Holds information on recordings and videos.
Definition: programinfo.h:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
uint64_t QueryStartMark(void) const
QString GetBasename(void) const
Definition: programinfo.h:345
void SetIgnoreProgStart(bool ignore)
If "ignore" is true QueryProgStart() will return 0, otherwise QueryProgStart() will return the progst...
Definition: programinfo.h:570
uint GetRecordingID(void) const
Definition: programinfo.h:450
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:398
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:391
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:340
QString GetPathname(void) const
Definition: programinfo.h:344
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
QString FindFile(const QString &filename)
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
unsigned int uint
Definition: freesurround.h:24
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetAppBinDir(void)
Definition: mythdirs.cpp:260
static QString remotecachedir
Definition: mythdirs.cpp:28
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#define off_t
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
bool makeFileAccessible(const QString &filename)
PlayerFlags
Definition: mythplayer.h:65
@ kAudioMuted
Definition: mythplayer.h:74
@ kVideoIsNull
Definition: mythplayer.h:73
@ kNoITV
Definition: mythplayer.h:75
Convenience inline random number generator functions.
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:36
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:39
@ kMSPropagateLogs
add arguments for MythTV log propagation
Definition: mythsystem.h:52
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
@ kMSAutoCleanup
automatically delete if backgrounded
Definition: mythsystem.h:45
@ kFilename
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:18
@ ISODate
Default UTC.
Definition: mythdate.h:17
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
dictionary info
Definition: azlyrics.py:7
STL namespace.
#define LOC
const QString kPreviewGeneratorInUseID