12 #include <QApplication>
13 #include <QCoreApplication>
18 #include <QTemporaryFile>
22 #include "mythconfig.h"
39 #define LOC QString("Preview: ")
76 m_programInfo(*pginfo), m_mode(mode),
77 m_pathname(pginfo->GetPathname()),
78 m_token(std::move(
token))
83 moveToThread(QApplication::instance()->thread());
94 if (fileName.isEmpty())
96 QFileInfo fileinfo = QFileInfo(fileName);
111 QObject::deleteLater();
126 QTime tm = QTime::currentTime();
127 QElapsedTimer te; te.start();
133 LOG(VB_GENERAL, LOG_ERR,
LOC +
134 QString(
"RunReal() file not local: '%1'")
139 LOG(VB_GENERAL, LOG_ERR,
LOC +
140 QString(
"RunReal() Preview of '%1' failed "
141 "because mode was invalid 0x%2")
147 msg = QString(
"Generated on %1 in %2 seconds, starting at %3")
149 .arg(te.elapsed()*0.001)
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");
164 msg = QString(
"Generated remotely in %1 seconds, starting at %2")
165 .arg(te.elapsed()*0.001)
170 msg =
"Remote preview failed";
175 msg =
"Could not access recording";
189 QFileInfo fi(output_fn);
191 dt = fi.lastModified();
194 QString message = (ok) ?
"PREVIEW_SUCCESS" :
"PREVIEW_FAILED";
197 list.push_back(output_fn);
199 list.push_back(dt.isValid()?dt.toUTC().toString(
Qt::ISODate):
"");
210 QTime tm = QTime::currentTime();
211 QElapsedTimer te; te.start();
216 QFileInfo(command).isExecutable());
225 QString(
"Generated remotely in %1 seconds, starting at %2")
226 .arg(te.elapsed()*0.001)
232 LOG(VB_GENERAL, LOG_ERR,
LOC +
233 QString(
"Run() cannot generate preview locally for: '%1'")
235 msg =
"Failed, local preview requested for remote file.";
248 cmdargs <<
"--seconds";
250 cmdargs <<
"--frame";
253 cmdargs <<
"--chanid"
272 uint ret = ms->Wait();
277 LOG(VB_GENERAL, LOG_ERR,
LOC +
278 QString(
"Encountered problems running '%1 %2' - (%3)")
279 .
arg(command).
arg(cmdargs.join(
" ")).arg(ret));
283 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process returned 0.");
287 QString lpath = QFileInfo(outname).fileName();
288 if (lpath == outname)
291 QString tmpFile = sgroup.
FindFile(lpath);
292 outname = (tmpFile.isEmpty()) ? outname : tmpFile;
295 QFileInfo fi(outname);
296 ok = (fi.exists() && fi.isReadable() && (fi.size() != 0));
299 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process ran ok.");
300 msg = QString(
"Generated on %1 in %2 seconds, starting at %3")
302 .arg(te.elapsed()*0.001)
307 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview process not ok." +
308 QString(
"\n\t\t\tfileinfo(%1)").
arg(outname) +
309 QString(
" exists: %1").
arg(fi.exists()) +
310 QString(
" readable: %1").
arg(fi.isReadable()) +
311 QString(
" size: %1").
arg(fi.size()));
312 LOG(VB_GENERAL, LOG_ERR,
LOC +
313 QString(
"Despite command '%1' returning success")
315 msg = QString(
"Failed to read preview image despite "
316 "preview process returning success.");
331 QFileInfo fi(output_fn);
333 dt = fi.lastModified();
336 QString message = (ok) ?
"PREVIEW_SUCCESS" :
"PREVIEW_FAILED";
341 list.push_back(output_fn);
343 list.push_back(dt.isValid()?dt.toUTC().toString(
Qt::ISODate):
"");
360 QStringList strlist(
"QUERY_GENPIXMAP2" );
372 strlist.push_back(
"<EMPTY>");
377 strlist.push_back(fi.fileName());
379 strlist.push_back(QString::number(
m_outSize.width()));
380 strlist.push_back(QString::number(
m_outSize.height()));
386 if (!ok || strlist.empty() || (strlist[0] !=
"OK"))
390 LOG(VB_GENERAL, LOG_ERR,
LOC +
391 "Remote Preview failed due to communications error.");
393 else if (strlist.size() > 1)
395 LOG(VB_GENERAL, LOG_ERR,
LOC +
396 "Remote Preview failed, reason given: " + strlist[1]);
412 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"RemotePreviewRun() -- no reply..");
422 return QObject::event(
e);
426 return QObject::event(
e);
427 if (me->Message() !=
"GENERATED_PIXMAP" || me->ExtraDataCount() < 3)
428 return QObject::event(
e);
430 bool ok = me->ExtraData(0) ==
"OK";
433 for (; i < (
uint) me->ExtraDataCount() && !ours; i++)
434 ours |= me->ExtraData(i) ==
m_token;
436 return QObject::event(
e);
438 const QString& pginfokey = me->ExtraData(1);
445 LOG(VB_GENERAL, LOG_ERR,
LOC + pginfokey +
": " + me->ExtraData(2));
450 if (me->ExtraDataCount() < 5)
458 if (!datetime.isValid())
461 LOG(VB_GENERAL, LOG_ERR,
LOC + pginfokey +
"Got invalid date");
463 return QObject::event(
e);
466 size_t length = me->ExtraData(4).toULongLong();
467 quint16 checksum16 = me->ExtraData(5).toUInt();
468 QByteArray data = QByteArray::fromBase64(me->ExtraData(6).toLatin1());
469 if ((
size_t) data.size() < length)
472 LOG(VB_GENERAL, LOG_ERR,
LOC +
473 QString(
"Preview size check failed %1 < %2")
474 .
arg(data.size()).arg(length));
479 if (checksum16 != qChecksum(data.constData(), data.size()))
481 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview checksum failed");
496 QString remotecachedirname =
497 QString(
"%1/cache/remotecache").arg(
GetConfDir());
504 LOG(VB_GENERAL, LOG_ERR,
LOC +
505 "Remote Preview failed because we could not create a "
506 "remote cache directory");
516 bool ok =
file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
519 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to open: '%1'")
524 size_t remaining = data.size();
525 uint failure_cnt = 0;
526 while ((remaining > 0) && (failure_cnt < 5))
528 ssize_t written =
file.write(data.data() + offset, remaining);
538 remaining -= written;
541 if (ok && !remaining)
544 struct utimbuf times {};
545 times.actime = times.modtime = dt.toSecsSinceEpoch();
558 const unsigned char *data,
559 uint width,
uint height,
float aspect,
560 int desired_width,
int desired_height,
561 const QString &format)
563 if (!data || !width || !height)
566 const QImage img((
unsigned char*) data,
567 width, height, QImage::Format_RGB32);
569 float ppw = std::max(desired_width, 0);
570 float pph = std::max(desired_height, 0);
571 bool desired_size_exactly_specified =
true;
572 if ((ppw < 1.0F) && (pph < 1.0F))
576 desired_size_exactly_specified =
false;
579 aspect = (aspect <= 0.0F) ? ((
float) width) / height : aspect;
580 pph = (pph < 1.0F) ? (ppw / aspect) : pph;
581 ppw = (ppw < 1.0F) ? (pph * aspect) : ppw;
583 if (!desired_size_exactly_specified)
585 if (aspect > ppw / pph)
586 pph = (ppw / aspect);
588 ppw = (pph * aspect);
591 ppw = std::max(1.0F, ppw);
592 pph = std::max(1.0F, pph);;
594 QImage small_img = img.scaled((
int) ppw, (
int) pph,
595 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
597 QTemporaryFile
f(QFileInfo(
filename).absoluteFilePath()+
".XXXXXX");
598 f.setAutoRemove(
false);
599 if (
f.open() && small_img.save(&
f, format.toLocal8Bit().constData()))
605 LOG(VB_GENERAL, LOG_ERR,
"Unable to change permissions on "
606 "preview image. Backends and frontends "
607 "running under different users will be "
608 "unable to access it");
614 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Saved preview '%0' %1x%2")
636 LOG(VB_GENERAL, LOG_INFO,
"Preview from time spec");
644 LOG(VB_GENERAL, LOG_INFO,
645 QString(
"Preview from bookmark (frame %1)").
arg(captime));
655 int programDuration = 0;
673 if (programDuration > 0)
675 captime = programDuration / 3;
678 captime += startEarly;
683 LOG(VB_GENERAL, LOG_INFO,
684 QString(
"Preview at calculated offset (%1 seconds)").
arg(captime));
692 sz, width, height, aspect);
701 bool ok =
SavePreview(outname, data, width, height, aspect, dw, dh,
708 struct utimbuf times {};
709 times.actime = times.modtime = dt.toSecsSinceEpoch();
710 utime(outname.toLocal8Bit().constData(), ×);
721 const QString &pathname,
const QString &outFileName)
723 QString outname = pathname +
".png";
725 if (outFileName.isEmpty())
728 outname = outFileName;
729 QFileInfo fi(outname);
730 if (outname == fi.fileName())
733 if (pathname.contains(
':'))
735 QUrl uinfo(pathname);
737 dir = uinfo.toString();
741 dir = QFileInfo(pathname).path();
743 outname =
dir +
"/" + fi.fileName();
744 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"outfile '%1' -> '%2'")
745 .
arg(outFileName).
arg(outname));
755 if (tmppathname.startsWith(
"dvd:"))
756 tmppathname = tmppathname.section(
":", 1, 1);
758 if (!QFileInfo(tmppathname).isReadable())
762 QString pathdir = QFileInfo(tmppathname).path();
764 if (!QFileInfo(pathdir).isWritable())
766 LOG(VB_GENERAL, LOG_WARNING,
LOC +
767 QString(
"Output path '%1' is not writeable") .
arg(pathdir));
791 long long seektime,
bool time_in_secs,
793 int &video_width,
int &video_height,
float &video_aspect)
795 char *retbuf =
nullptr;
800 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not connect to DB.");
808 bool invalid = (!info.exists() || !info.isReadable() || (info.isFile() && (info.size() < 8*1024)));
811 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Previewer file '%1' is not valid.")
818 if (!buffer || !buffer->
IsOpen())
820 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not open file: " +
828 ctx->SetRingBuffer(buffer);
829 ctx->SetPlayingInfo(&pginfo);
830 ctx->SetPlayer(player);
834 retbuf = player->GetScreenGrab(
static_cast<int>(seektime), bufferlen,
835 video_width, video_height, video_aspect);
839 retbuf = player->GetScreenGrabAtFrame(
static_cast<uint64_t
>(seektime),
true,
840 bufferlen, video_width, video_height, video_aspect);
846 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Grabbed preview '%0' %1x%2@%3%4")
848 .
arg(seektime).
arg((time_in_secs) ?
"s" :
"f"));
852 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to grab preview '%0' %1x%2@%3%4")
854 .
arg(seektime).
arg((time_in_secs) ?
"s" :
"f"));