12 #include <QApplication>
13 #include <QCoreApplication>
18 #include <QTemporaryFile>
23 #include "libmythbase/mythconfig.h"
40 #define LOC QString("Preview: ")
77 m_programInfo(*pginfo), m_mode(mode),
78 m_pathname(pginfo->GetPathname()),
79 m_token(
std::move(token))
84 moveToThread(QApplication::instance()->thread());
95 if (fileName.isEmpty())
97 QFileInfo fileinfo = QFileInfo(fileName);
112 QObject::deleteLater();
127 QTime tm = QTime::currentTime();
128 QElapsedTimer te; te.start();
134 LOG(VB_GENERAL, LOG_ERR,
LOC +
135 QString(
"RunReal() file not local: '%1'")
140 LOG(VB_GENERAL, LOG_ERR,
LOC +
141 QString(
"RunReal() Preview of '%1' failed "
142 "because mode was invalid 0x%2")
148 msg = QString(
"Generated on %1 in %2 seconds, starting at %3")
150 .arg(te.elapsed()*0.001)
157 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to save preview."
158 "\n\t\t\tYou may need to check user and group ownership on"
159 "\n\t\t\tyour frontend and backend for quicker previews.\n"
160 "\n\t\t\tAttempting to regenerate preview on backend.\n");
165 msg = QString(
"Generated remotely in %1 seconds, starting at %2")
166 .arg(te.elapsed()*0.001)
171 msg =
"Remote preview failed";
176 msg =
"Could not access recording";
190 QFileInfo fi(output_fn);
192 dt = fi.lastModified();
195 QString message = (ok) ?
"PREVIEW_SUCCESS" :
"PREVIEW_FAILED";
198 list.push_back(output_fn);
200 list.push_back(dt.isValid()?dt.toUTC().toString(
Qt::ISODate):
"");
211 QTime tm = QTime::currentTime();
212 QElapsedTimer te; te.start();
217 QFileInfo(command).isExecutable());
226 QString(
"Generated remotely in %1 seconds, starting at %2")
227 .arg(te.elapsed()*0.001)
233 LOG(VB_GENERAL, LOG_ERR,
LOC +
234 QString(
"Run() cannot generate preview locally for: '%1'")
236 msg =
"Failed, local preview requested for remote file.";
247 cmdargs <<
"--seconds" << QString::number(
m_captureTime.count());
250 cmdargs <<
"--chanid"
269 uint ret = ms->Wait();
274 LOG(VB_GENERAL, LOG_ERR,
LOC +
275 QString(
"Encountered problems running '%1 %2' - (%3)")
276 .arg(command, cmdargs.join(
" "), QString::number(ret)));
280 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process returned 0.");
284 QString lpath = QFileInfo(outname).fileName();
285 if (lpath == outname)
288 QString tmpFile = sgroup.
FindFile(lpath);
289 outname = (tmpFile.isEmpty()) ? outname : tmpFile;
292 QFileInfo fi(outname);
293 ok = (fi.exists() && fi.isReadable() && (fi.size() != 0));
296 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process ran ok.");
297 msg = QString(
"Generated on %1 in %2 seconds, starting at %3")
299 .arg(te.elapsed()*0.001)
304 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview process not ok." +
305 QString(
"\n\t\t\tfileinfo(%1)").arg(outname) +
306 QString(
" exists: %1").arg(fi.exists()) +
307 QString(
" readable: %1").arg(fi.isReadable()) +
308 QString(
" size: %1").arg(fi.size()));
309 LOG(VB_GENERAL, LOG_ERR,
LOC +
310 QString(
"Despite command '%1' returning success")
312 msg = QString(
"Failed to read preview image despite "
313 "preview process returning success.");
328 QFileInfo fi(output_fn);
330 dt = fi.lastModified();
333 QString message = (ok) ?
"PREVIEW_SUCCESS" :
"PREVIEW_FAILED";
338 list.push_back(output_fn);
340 list.push_back(dt.isValid()?dt.toUTC().toString(
Qt::ISODate):
"");
357 QStringList strlist(
"QUERY_GENPIXMAP2" );
367 strlist.push_back(
"s");
372 strlist.push_back(
"f");
377 strlist.push_back(
"<EMPTY>");
382 strlist.push_back(fi.fileName());
384 strlist.push_back(QString::number(
m_outSize.width()));
385 strlist.push_back(QString::number(
m_outSize.height()));
391 if (!ok || strlist.empty() || (strlist[0] !=
"OK"))
395 LOG(VB_GENERAL, LOG_ERR,
LOC +
396 "Remote Preview failed due to communications error.");
398 else if (strlist.size() > 1)
400 LOG(VB_GENERAL, LOG_ERR,
LOC +
401 "Remote Preview failed, reason given: " + strlist[1]);
417 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"RemotePreviewRun() -- no reply..");
427 return QObject::event(e);
431 return QObject::event(e);
432 if (me->Message() !=
"GENERATED_PIXMAP" || me->ExtraDataCount() < 3)
433 return QObject::event(e);
435 bool ok = me->ExtraData(0) ==
"OK";
438 for (; i < (
uint) me->ExtraDataCount() && !ours; i++)
439 ours |= me->ExtraData(i) ==
m_token;
441 return QObject::event(e);
443 const QString& pginfokey = me->ExtraData(1);
450 LOG(VB_GENERAL, LOG_ERR,
LOC + pginfokey +
": " + me->ExtraData(2));
455 if (me->ExtraDataCount() < 5)
463 if (!datetime.isValid())
466 LOG(VB_GENERAL, LOG_ERR,
LOC + pginfokey +
"Got invalid date");
468 return QObject::event(e);
471 size_t length = me->ExtraData(4).toULongLong();
472 quint16 checksum16 = me->ExtraData(5).toUInt();
473 QByteArray data = QByteArray::fromBase64(me->ExtraData(6).toLatin1());
474 if ((
size_t) data.size() < length)
477 LOG(VB_GENERAL, LOG_ERR,
LOC +
478 QString(
"Preview size check failed %1 < %2")
479 .arg(data.size()).arg(length));
484 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
485 quint16 calculated = qChecksum(data.constData(), data.size());
487 quint16 calculated = qChecksum(data);
489 if (checksum16 != calculated)
491 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview checksum failed");
506 QString remotecachedirname =
507 QString(
"%1/cache/remotecache").arg(
GetConfDir());
514 LOG(VB_GENERAL, LOG_ERR,
LOC +
515 "Remote Preview failed because we could not create a "
516 "remote cache directory");
526 bool ok =
file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
529 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to open: '%1'")
534 size_t remaining = data.size();
535 uint failure_cnt = 0;
536 while ((remaining > 0) && (failure_cnt < 5))
538 ssize_t written =
file.write(data.data() + offset, remaining);
548 remaining -= written;
551 if (ok && !remaining)
554 struct utimbuf times {};
555 times.actime = times.modtime = dt.toSecsSinceEpoch();
568 const unsigned char *data,
569 uint width,
uint height,
float aspect,
570 int desired_width,
int desired_height,
571 const QString &format)
573 if (!data || !width || !height)
576 const QImage img((
unsigned char*) data,
577 width, height, QImage::Format_RGB32);
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))
586 desired_size_exactly_specified =
false;
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;
593 if (!desired_size_exactly_specified)
595 if (aspect > ppw / pph)
596 pph = (ppw / aspect);
598 ppw = (pph * aspect);
601 ppw = std::max(1.0F, ppw);
602 pph = std::max(1.0F, pph);;
604 QImage small_img = img.scaled((
int) ppw, (
int) pph,
605 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
607 QTemporaryFile f(QFileInfo(
filename).absoluteFilePath()+
".XXXXXX");
608 f.setAutoRemove(
false);
609 if (f.open() && small_img.save(&f, format.toLocal8Bit().constData()))
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");
624 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Saved preview '%0' %1x%2")
625 .arg(
filename).arg((
int) ppw).arg((
int) pph));
641 long long capframe = -1;
646 LOG(VB_GENERAL, LOG_INFO,
"Preview from time spec");
652 LOG(VB_GENERAL, LOG_INFO,
653 QString(
"Preview from bookmark (frame %1)").arg(capframe));
657 if ((captime <= 0s) && (capframe <= 0))
659 std::chrono::seconds startEarly = 0s;
660 std::chrono::seconds programDuration = 0s;
678 if (programDuration > 0s)
680 captime = programDuration / 3;
683 captime += startEarly;
688 LOG(VB_GENERAL, LOG_INFO,
689 QString(
"Preview at calculated offset (%1 seconds)").arg(captime.count()));
697 sz, width, height, aspect);
706 bool ok =
SavePreview(outname, data, width, height, aspect, dw, dh,
713 struct utimbuf times {};
714 times.actime = times.modtime = dt.toSecsSinceEpoch();
715 utime(outname.toLocal8Bit().constData(), ×);
726 const QString &pathname,
const QString &outFileName)
728 QString outname = pathname +
".png";
730 if (outFileName.isEmpty())
733 outname = outFileName;
734 QFileInfo fi(outname);
735 if (outname == fi.fileName())
738 if (pathname.contains(
':'))
740 QUrl uinfo(pathname);
742 dir = uinfo.toString();
746 dir = QFileInfo(pathname).path();
748 outname = dir +
"/" + fi.fileName();
749 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"outfile '%1' -> '%2'")
750 .arg(outFileName, outname));
760 if (tmppathname.startsWith(
"dvd:"))
761 tmppathname = tmppathname.section(
":", 1, 1);
763 if (!QFileInfo(tmppathname).isReadable())
767 QString pathdir = QFileInfo(tmppathname).path();
769 if (!QFileInfo(pathdir).isWritable())
771 LOG(VB_GENERAL, LOG_WARNING,
LOC +
772 QString(
"Output path '%1' is not writeable") .arg(pathdir));
797 std::chrono::seconds seektime,
long long seekframe,
799 int &video_width,
int &video_height,
float &video_aspect)
801 char *retbuf =
nullptr;
806 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not connect to DB.");
814 bool invalid = (!info.exists() || !info.isReadable() || (info.isFile() && (info.size() < 8LL*1024)));
817 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Previewer file '%1' is not valid.")
824 if (!buffer || !buffer->
IsOpen())
826 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not open file: " +
834 ctx->SetRingBuffer(buffer);
835 ctx->SetPlayingInfo(&pginfo);
836 ctx->SetPlayer(player);
840 retbuf = player->GetScreenGrab(seektime, bufferlen,
841 video_width, video_height, video_aspect);
845 retbuf = player->GetScreenGrabAtFrame(
static_cast<uint64_t
>(seekframe),
true,
846 bufferlen, video_width, video_height, video_aspect);
850 auto pos_text = (seektime != std::chrono::seconds::max())
851 ? QString::number(seektime.count()) +
"s"
852 : QString::number(seekframe)+
"f";
855 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Grabbed preview '%0' %1x%2@%3")
856 .arg(
filename).arg(video_width).arg(video_height).arg(pos_text));
860 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to grab preview '%0' %1x%2@%3")
861 .arg(
filename).arg(video_width).arg(video_height).arg(pos_text));