11 #include <QCoreApplication> 12 #include <QTemporaryFile> 18 #include <QApplication> 39 #define LOC QString("Preview: ") 76 m_programInfo(*pginfo), m_mode(mode), m_listener(nullptr),
77 m_pathname(pginfo->GetPathname()),
78 m_timeInSeconds(
true), m_captureTime(-1),
79 m_outSize(0,0), m_outFormat(
"PNG"),
80 m_token(token), m_gotReply(
false), m_pixmapOk(
false)
85 moveToThread(QApplication::instance()->thread());
96 if (fileName.isEmpty())
98 QFileInfo fileinfo = QFileInfo(fileName);
113 QObject::deleteLater();
128 QTime tm = QTime::currentTime();
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(tm.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(tm.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):
"");
212 QTime tm = QTime::currentTime();
217 QFileInfo(command).isExecutable());
226 QString(
"Generated remotely in %1 seconds, starting at %2")
227 .arg(tm.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.";
249 cmdargs <<
"--seconds";
251 cmdargs <<
"--frame";
254 cmdargs <<
"--chanid" 278 LOG(VB_GENERAL, LOG_ERR,
LOC +
279 QString(
"Encountered problems running '%1 %2' - (%3)")
280 .arg(command).arg(cmdargs.join(
" ")).arg(ret));
284 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process returned 0.");
288 QString lpath = QFileInfo(outname).fileName();
289 if (lpath == outname)
292 QString tmpFile = sgroup.
FindFile(lpath);
293 outname = (tmpFile.isEmpty()) ? outname : tmpFile;
296 QFileInfo fi(outname);
297 ok = (fi.exists() && fi.isReadable() && fi.size());
300 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Preview process ran ok.");
301 msg = QString(
"Generated on %1 in %2 seconds, starting at %3")
303 .arg(tm.elapsed()*0.001)
308 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview process not ok." +
309 QString(
"\n\t\t\tfileinfo(%1)").arg(outname) +
310 QString(
" exists: %1").arg(fi.exists()) +
311 QString(
" readable: %1").arg(fi.isReadable()) +
312 QString(
" size: %1").arg(fi.size()));
313 LOG(VB_GENERAL, LOG_ERR,
LOC +
314 QString(
"Despite command '%1' returning success")
316 msg = QString(
"Failed to read preview image despite " 317 "preview process returning success.");
332 QFileInfo fi(output_fn);
334 dt = fi.lastModified();
337 QString message = (ok) ?
"PREVIEW_SUCCESS" :
"PREVIEW_FAILED";
342 list.push_back(output_fn);
344 list.push_back(dt.isValid()?dt.toUTC().toString(
Qt::ISODate):
"");
361 QStringList strlist(
"QUERY_GENPIXMAP2" );
373 strlist.push_back(
"<EMPTY>");
378 strlist.push_back(fi.fileName());
380 strlist.push_back(QString::number(
m_outSize.width()));
381 strlist.push_back(QString::number(
m_outSize.height()));
387 if (!ok || strlist.empty() || (strlist[0] !=
"OK"))
391 LOG(VB_GENERAL, LOG_ERR,
LOC +
392 "Remote Preview failed due to communications error.");
394 else if (strlist.size() > 1)
396 LOG(VB_GENERAL, LOG_ERR,
LOC +
397 "Remote Preview failed, reason given: " + strlist[1]);
413 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"RemotePreviewRun() -- no reply..");
423 return QObject::event(e);
425 MythEvent *me = static_cast<MythEvent*>(e);
427 return QObject::event(e);
457 if (!datetime.isValid())
460 LOG(VB_GENERAL, LOG_ERR,
LOC + pginfokey +
"Got invalid date");
465 size_t length = me->
ExtraData(4).toULongLong();
466 quint16 checksum16 = me->
ExtraData(5).toUInt();
467 QByteArray data = QByteArray::fromBase64(me->
ExtraData(6).toLatin1());
468 if ((
size_t) data.size() < length)
471 LOG(VB_GENERAL, LOG_ERR,
LOC +
472 QString(
"Preview size check failed %1 < %2")
473 .arg(data.size()).arg(length));
478 if (checksum16 != qChecksum(data.constData(), data.size()))
480 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Preview checksum failed");
495 QString remotecachedirname =
496 QString(
"%1/cache/remotecache").arg(
GetConfDir());
503 LOG(VB_GENERAL, LOG_ERR,
LOC +
504 "Remote Preview failed because we could not create a " 505 "remote cache directory");
515 bool ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
518 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to open: '%1'")
523 size_t remaining = data.size();
524 uint failure_cnt = 0;
525 while ((remaining > 0) && (failure_cnt < 5))
527 ssize_t written = file.write(data.data() + offset, remaining);
537 remaining -= written;
540 if (ok && !remaining)
543 struct utimbuf times;
544 #if QT_VERSION < QT_VERSION_CHECK(5,8,0) 545 times.actime = times.modtime = dt.toTime_t();
547 times.actime = times.modtime = dt.toSecsSinceEpoch();
561 const unsigned char *data,
562 uint width,
uint height,
float aspect,
563 int desired_width,
int desired_height,
564 const QString &format)
566 if (!data || !width || !height)
569 const QImage img((
unsigned char*) data,
570 width, height, QImage::Format_RGB32);
572 float ppw = max(desired_width, 0);
573 float pph = max(desired_height, 0);
574 bool desired_size_exactly_specified =
true;
575 if ((ppw < 1.0f) && (pph < 1.0f))
579 desired_size_exactly_specified =
false;
582 aspect = (aspect <= 0.0f) ? ((
float) width) / height : aspect;
583 pph = (pph < 1.0f) ? (ppw / aspect) : pph;
584 ppw = (ppw < 1.0f) ? (pph * aspect) : ppw;
586 if (!desired_size_exactly_specified)
588 if (aspect > ppw / pph)
589 pph = (ppw / aspect);
591 ppw = (pph * aspect);
594 ppw = max(1.0f, ppw);
595 pph = max(1.0f, pph);;
597 QImage small_img = img.scaled((
int) ppw, (
int) pph,
598 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
600 QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+
".XXXXXX");
601 f.setAutoRemove(
false);
602 if (f.open() && small_img.save(&f, format.toLocal8Bit().constData()))
608 LOG(VB_GENERAL, LOG_ERR,
"Unable to change permissions on " 609 "preview image. Backends and frontends " 610 "running under different users will be " 611 "unable to access it");
615 if (f.rename(filename))
617 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Saved preview '%0' %1x%2")
618 .arg(filename).arg((
int) ppw).arg((
int) pph));
634 int width, height, sz;
640 LOG(VB_GENERAL, LOG_INFO,
"Preview from time spec");
648 LOG(VB_GENERAL, LOG_INFO,
649 QString(
"Preview from bookmark (frame %1)").arg(captime));
659 int programDuration = 0;
677 if (programDuration > 0)
679 captime = startEarly + (programDuration / 3);
684 LOG(VB_GENERAL, LOG_INFO,
685 QString(
"Preview at calculated offset (%1 seconds)").arg(captime));
688 width = height = sz = 0;
689 unsigned char *data = (
unsigned char*)
692 sz, width, height, aspect);
701 bool ok =
SavePreview(outname, data, width, height, aspect, dw, dh,
708 struct utimbuf times;
709 #if QT_VERSION < QT_VERSION_CHECK(5,8,0) 710 times.actime = times.modtime = dt.toTime_t();
712 times.actime = times.modtime = dt.toSecsSinceEpoch();
714 utime(outname.toLocal8Bit().constData(), ×);
725 const QString &pathname,
const QString &outFileName)
727 QString outname = pathname +
".png";
729 if (outFileName.isEmpty())
732 outname = outFileName;
733 QFileInfo fi(outname);
734 if (outname == fi.fileName())
737 if (pathname.contains(
':'))
739 QUrl uinfo(pathname);
741 dir = uinfo.toString();
745 dir = QFileInfo(pathname).path();
747 outname = dir +
"/" + fi.fileName();
748 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"outfile '%1' -> '%2'")
749 .arg(outFileName).arg(outname));
759 if (tmppathname.startsWith(
"dvd:"))
760 tmppathname = tmppathname.section(
":", 1, 1);
762 if (!QFileInfo(tmppathname).isReadable())
766 QString pathdir = QFileInfo(tmppathname).path();
768 if (!QFileInfo(pathdir).isWritable())
770 LOG(VB_GENERAL, LOG_WARNING,
LOC +
771 QString(
"Output path '%1' is not writeable") .arg(pathdir));
794 const ProgramInfo &pginfo,
const QString &filename,
795 long long seektime,
bool time_in_secs,
797 int &video_width,
int &video_height,
float &video_aspect)
806 char *retbuf =
nullptr;
811 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not connect to DB.");
816 if (filename.startsWith(
"/"))
818 QFileInfo info(filename);
819 bool invalid = (!info.exists() || !info.isReadable() ||
820 (info.isFile() && (info.size() < 8*1024)));
823 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer file " +
824 QString(
"'%1'").arg(filename) +
" is not valid.");
830 if (!rbuf || !rbuf->
IsOpen())
832 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Previewer could not open file: " +
833 QString(
"'%1'").arg(filename));
846 video_width, video_height, video_aspect);
849 seektime,
true, bufferlen,
850 video_width, video_height, video_aspect);
856 LOG(VB_GENERAL, LOG_INFO,
LOC +
857 QString(
"Grabbed preview '%0' %1x%2@%3%4")
858 .arg(filename).arg(video_width).arg(video_height)
859 .arg(seektime).arg((time_in_secs) ?
"s" :
"f"));
863 LOG(VB_GENERAL, LOG_ERR,
LOC +
864 QString(
"Failed to grab preview '%0' %1x%2@%3%4")
865 .arg(filename).arg(video_width).arg(video_height)
866 .arg(seektime).arg((time_in_secs) ?
"s" :
"f"));
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void Run(time_t timeout=0)
Runs a command inside the /bin/sh shell. Returns immediately.
avoid disabling UI drawing
virtual ~PreviewGenerator()
This is a wrapper around QThread that does several additional things.
automatically delete if backgrounded
void SetAllowLastPlayPos(bool allow)
If "ignore" is true QueryLastPlayPos() will return 0, otherwise QueryLastPlayPos() will return the la...
VERBOSE_PREAMBLE Most true
#define GENERIC_EXIT_OK
Exited with no error.
bool RemotePreviewRun(void)
void AttachSignals(QObject *)
static Type MythEventMessage
void removeListener(QObject *listener)
Remove a listener to the observable.
QWaitCondition m_previewWaitCondition
void SetOutputFilename(const QString &)
void MarkAsInUse(bool inuse, QString usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
static QString remotecachedir
bool m_timeInSeconds
tells us whether to use time as seconds or frame number
static char * GetScreenGrab(const ProgramInfo &pginfo, const QString &filename, long long seektime, bool time_in_secs, int &bufferlen, int &video_width, int &video_height, float &video_aspect)
Returns a AV_PIX_FMT_RGBA32 buffer containg a frame from the video.
Default UTC, "yyyyMMddhhmmss".
void SetPlayer(MythPlayer *new_player)
void SetRingBuffer(RingBuffer *buf)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
void addListener(QObject *listener)
Add a listener to the observable.
const char * kPreviewGeneratorInUseID
ProgramInfo m_programInfo
PreviewGenerator(const ProgramInfo *pginfo, const QString &token, Mode mode=kLocal)
Constructor.
virtual char * GetScreenGrab(int secondsin, int &bufflen, int &vw, int &vh, float &ar)
Returns a one RGB frame grab from a video.
process events while waiting
add arguments for MythTV log propagation
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
static QString CreateAccessibleFilename(const QString &pathname, const QString &outFileName)
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Holds information on recordings and videos.
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
This class is used as a container for messages.
QString GetBasename(void) const
QString GetAppBinDir(void)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
bool makeFileAccessible(QString filename)
Makes a file accessible to all frontends/backends.
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
uint GetRecordingID(void) const
virtual bool IsOpen(void) const =0
Returns true if open for either reading or writing.
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
uint Wait(time_t timeout=0)
void SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
bool event(QEvent *e) override
const QString & ExtraData(int idx=0) const
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)
QString FindFile(const QString &filename)
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
long long m_captureTime
snapshot time in seconds or frame number, depending on timeInSeconds
bool LocalPreviewRun(void)
int ExtraDataCount() const
Implements a file/stream reader/writer.
static long int random(void)
static void usleep(unsigned long time)
void SetIgnoreProgStart(bool ignore)
If "ignore" is true QueryProgStart() will return 0, otherwise QueryProgStart() will return the progst...
bool RunReal(void)
This call creates a preview without starting a new thread.
const QString & Message() const
QString GetHostName(void)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QString GetPathname(void) const
virtual char * GetScreenGrabAtFrame(uint64_t frameNum, bool absolute, int &bufflen, int &vw, int &vh, float &ar)
Returns a one RGB frame grab from a video.
avoid blocking LIRC & Joystick Menu
bool SaveOutFile(const QByteArray &data, const QDateTime &dt)