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