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
22 #include "mythconfig.h"
23 #include "io/mythmediabuffer.h"
24 #include "mythpreviewplayer.h"
25 #include "previewgenerator.h"
26 #include "tv_rec.h"
27 #include "mythsocket.h"
28 #include "remotefile.h"
29 #include "storagegroup.h"
30 #include "mythdate.h"
31 #include "playercontext.h"
32 #include "mythdirs.h"
33 #include "remoteutil.h"
34 #include "mythsystemlegacy.h"
35 #include "exitcodes.h"
36 #include "mythlogging.h"
37 #include "mythmiscutil.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 {
88  TeardownAll();
89  wait();
90 }
91 
92 void 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")
148  .arg(gCoreContext->GetHostName())
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 >= 0)
246  {
247  if (m_timeInSeconds)
248  cmdargs << "--seconds";
249  else
250  cmdargs << "--frame";
251  cmdargs << QString::number(m_captureTime);
252  }
253  cmdargs << "--chanid"
254  << QString::number(m_programInfo.GetChanID())
255  << "--starttime"
257 
258  if (!m_outFileName.isEmpty())
259  cmdargs << "--outfile" << m_outFileName;
260 
261  // Timeout in 30s
262  auto *ms = new MythSystemLegacy(command, cmdargs,
268  ms->SetNice(10);
269  ms->SetIOPrio(7);
270 
271  ms->Run(30);
272  uint ret = ms->Wait();
273  delete ms;
274 
275  if (ret != GENERIC_EXIT_OK)
276  {
277  LOG(VB_GENERAL, LOG_ERR, LOC +
278  QString("Encountered problems running '%1 %2' - (%3)")
279  .arg(command).arg(cmdargs.join(" ")).arg(ret));
280  }
281  else
282  {
283  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process returned 0.");
284  QString outname = (!m_outFileName.isEmpty()) ?
285  m_outFileName : (m_pathname + ".png");
286 
287  QString lpath = QFileInfo(outname).fileName();
288  if (lpath == outname)
289  {
290  StorageGroup sgroup;
291  QString tmpFile = sgroup.FindFile(lpath);
292  outname = (tmpFile.isEmpty()) ? outname : tmpFile;
293  }
294 
295  QFileInfo fi(outname);
296  ok = (fi.exists() && fi.isReadable() && (fi.size() != 0));
297  if (ok)
298  {
299  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process ran ok.");
300  msg = QString("Generated on %1 in %2 seconds, starting at %3")
301  .arg(gCoreContext->GetHostName())
302  .arg(te.elapsed()*0.001)
303  .arg(tm.toString(Qt::ISODate));
304  }
305  else
306  {
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")
314  .arg(command));
315  msg = QString("Failed to read preview image despite "
316  "preview process returning success.");
317  }
318  }
319  }
320 
321  QMutexLocker locker(&m_previewLock);
322 
323  // Backdate file to start of preview time in case a bookmark was made
324  // while we were generating the preview.
325  QString output_fn = m_outFileName.isEmpty() ?
327 
328  QDateTime dt;
329  if (ok)
330  {
331  QFileInfo fi(output_fn);
332  if (fi.exists())
333  dt = fi.lastModified();
334  }
335 
336  QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED";
337  if (m_listener)
338  {
339  QStringList list;
340  list.push_back(QString::number(m_programInfo.GetRecordingID()));
341  list.push_back(output_fn);
342  list.push_back(msg);
343  list.push_back(dt.isValid()?dt.toUTC().toString(Qt::ISODate):"");
344  list.push_back(m_token);
345  QCoreApplication::postEvent(m_listener, new MythEvent(message, list));
346  }
347 
348  return ok;
349 }
350 
352 {
353  RunProlog();
354  Run();
355  RunEpilog();
356 }
357 
359 {
360  QStringList strlist( "QUERY_GENPIXMAP2" );
361  if (m_token.isEmpty())
362  {
363  m_token = QString("%1:%2")
364  .arg(m_programInfo.MakeUniqueKey()).arg(MythRandom());
365  }
366  strlist.push_back(m_token);
367  m_programInfo.ToStringList(strlist);
368  strlist.push_back(m_timeInSeconds ? "s" : "f");
369  strlist.push_back(QString::number(m_captureTime));
370  if (m_outFileName.isEmpty())
371  {
372  strlist.push_back("<EMPTY>");
373  }
374  else
375  {
376  QFileInfo fi(m_outFileName);
377  strlist.push_back(fi.fileName());
378  }
379  strlist.push_back(QString::number(m_outSize.width()));
380  strlist.push_back(QString::number(m_outSize.height()));
381 
382  gCoreContext->addListener(this);
383  m_pixmapOk = false;
384 
385  bool ok = gCoreContext->SendReceiveStringList(strlist);
386  if (!ok || strlist.empty() || (strlist[0] != "OK"))
387  {
388  if (!ok)
389  {
390  LOG(VB_GENERAL, LOG_ERR, LOC +
391  "Remote Preview failed due to communications error.");
392  }
393  else if (strlist.size() > 1)
394  {
395  LOG(VB_GENERAL, LOG_ERR, LOC +
396  "Remote Preview failed, reason given: " + strlist[1]);
397  }
398 
400 
401  return false;
402  }
403 
404  QMutexLocker locker(&m_previewLock);
405 
406  // wait up to 35 seconds for the preview to complete
407  // The backend waits 30s for creation
408  if (!m_gotReply)
409  m_previewWaitCondition.wait(&m_previewLock, 35 * 1000);
410 
411  if (!m_gotReply)
412  LOG(VB_GENERAL, LOG_NOTICE, LOC + "RemotePreviewRun() -- no reply..");
413 
415 
416  return m_pixmapOk;
417 }
418 
420 {
421  if (e->type() != MythEvent::MythEventMessage)
422  return QObject::event(e);
423 
424  auto *me = dynamic_cast<MythEvent*>(e);
425  if (me == nullptr)
426  return false;
427  if (me->Message() != "GENERATED_PIXMAP" || me->ExtraDataCount() < 3)
428  return QObject::event(e);
429 
430  bool ok = me->ExtraData(0) == "OK";
431  bool ours = false;
432  uint i = ok ? 4 : 3;
433  for (; i < (uint) me->ExtraDataCount() && !ours; i++)
434  ours |= me->ExtraData(i) == m_token;
435  if (!ours)
436  return false;
437 
438  const QString& pginfokey = me->ExtraData(1);
439 
440  QMutexLocker locker(&m_previewLock);
441  m_gotReply = true;
442  m_pixmapOk = ok;
443  if (!ok)
444  {
445  LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + ": " + me->ExtraData(2));
446  m_previewWaitCondition.wakeAll();
447  return true;
448  }
449 
450  if (me->ExtraDataCount() < 5)
451  {
452  m_pixmapOk = false;
453  m_previewWaitCondition.wakeAll();
454  return true; // could only happen with very broken client...
455  }
456 
457  QDateTime datetime = MythDate::fromString(me->ExtraData(3));
458  if (!datetime.isValid())
459  {
460  m_pixmapOk = false;
461  LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + "Got invalid date");
462  m_previewWaitCondition.wakeAll();
463  return false;
464  }
465 
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)
470  { // (note data.size() may be up to 3
471  // bytes longer after decoding
472  LOG(VB_GENERAL, LOG_ERR, LOC +
473  QString("Preview size check failed %1 < %2")
474  .arg(data.size()).arg(length));
475  data.clear();
476  }
477  data.resize(length);
478 
479  if (checksum16 != qChecksum(data.constData(), data.size()))
480  {
481  LOG(VB_GENERAL, LOG_ERR, LOC + "Preview checksum failed");
482  data.clear();
483  }
484 
485  m_pixmapOk = (data.isEmpty()) ? false : SaveOutFile(data, datetime);
486 
487  m_previewWaitCondition.wakeAll();
488 
489  return true;
490 }
491 
492 bool PreviewGenerator::SaveOutFile(const QByteArray &data, const QDateTime &dt)
493 {
494  if (m_outFileName.isEmpty())
495  {
496  QString remotecachedirname =
497  QString("%1/cache/remotecache").arg(GetConfDir());
498  QDir remotecachedir(remotecachedirname);
499 
500  if (!remotecachedir.exists())
501  {
502  if (!remotecachedir.mkdir(remotecachedirname))
503  {
504  LOG(VB_GENERAL, LOG_ERR, LOC +
505  "Remote Preview failed because we could not create a "
506  "remote cache directory");
507  return false;
508  }
509  }
510 
511  QString filename = m_programInfo.GetBasename() + ".png";
512  SetOutputFilename(QString("%1/%2").arg(remotecachedirname).arg(filename));
513  }
514 
515  QFile file(m_outFileName);
516  bool ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
517  if (!ok)
518  {
519  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open: '%1'")
520  .arg(m_outFileName));
521  }
522 
523  off_t offset = 0;
524  size_t remaining = data.size();
525  uint failure_cnt = 0;
526  while ((remaining > 0) && (failure_cnt < 5))
527  {
528  ssize_t written = file.write(data.data() + offset, remaining);
529  if (written < 0)
530  {
531  failure_cnt++;
532  usleep(50000);
533  continue;
534  }
535 
536  failure_cnt = 0;
537  offset += written;
538  remaining -= written;
539  }
540 
541  if (ok && !remaining)
542  {
543  file.close();
544  struct utimbuf times {};
545  times.actime = times.modtime = dt.toSecsSinceEpoch();
546  utime(m_outFileName.toLocal8Bit().constData(), &times);
547  LOG(VB_FILE, LOG_INFO, LOC + QString("Saved: '%1'").arg(m_outFileName));
548  }
549  else
550  {
551  file.remove();
552  }
553 
554  return ok;
555 }
556 
558  const unsigned char *data,
559  uint width, uint height, float aspect,
560  int desired_width, int desired_height,
561  const QString &format)
562 {
563  if (!data || !width || !height)
564  return false;
565 
566  const QImage img((unsigned char*) data,
567  width, height, QImage::Format_RGB32);
568 
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))
573  {
574  ppw = img.width();
575  pph = img.height();
576  desired_size_exactly_specified = false;
577  }
578 
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;
582 
583  if (!desired_size_exactly_specified)
584  {
585  if (aspect > ppw / pph)
586  pph = (ppw / aspect);
587  else
588  ppw = (pph * aspect);
589  }
590 
591  ppw = std::max(1.0F, ppw);
592  pph = std::max(1.0F, pph);;
593 
594  QImage small_img = img.scaled((int) ppw, (int) pph,
595  Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
596 
597  QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX");
598  f.setAutoRemove(false);
599  if (f.open() && small_img.save(&f, format.toLocal8Bit().constData()))
600  {
601  // Let anybody update it
602  bool ret = makeFileAccessible(f.fileName().toLocal8Bit().constData());
603  if (!ret)
604  {
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");
609  }
610  QFile of(filename);
611  of.remove();
612  if (f.rename(filename))
613  {
614  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Saved preview '%0' %1x%2")
615  .arg(filename).arg((int) ppw).arg((int) pph));
616  return true;
617  }
618  f.remove();
619  }
620 
621  return false;
622 }
623 
625 {
629 
630  float aspect = 0;
631  long long captime = m_captureTime;
632 
633  QDateTime dt = MythDate::current();
634 
635  if (captime > 0)
636  LOG(VB_GENERAL, LOG_INFO, "Preview from time spec");
637 
638  if (captime < 0)
639  {
640  captime = m_programInfo.QueryBookmark();
641  if (captime > 0)
642  {
643  m_timeInSeconds = false;
644  LOG(VB_GENERAL, LOG_INFO,
645  QString("Preview from bookmark (frame %1)").arg(captime));
646  }
647  else
648  captime = -1;
649  }
650 
651  if (captime <= 0)
652  {
653  m_timeInSeconds = true;
654  int startEarly = 0;
655  int programDuration = 0;
656  int preroll = gCoreContext->GetNumSetting("RecordPreRoll", 0);
657  if (m_programInfo.GetScheduledStartTime().isValid() &&
658  m_programInfo.GetScheduledEndTime().isValid() &&
661  {
662  programDuration = m_programInfo.GetScheduledStartTime()
664  }
665  if (m_programInfo.GetRecordingStartTime().isValid() &&
666  m_programInfo.GetScheduledStartTime().isValid() &&
669  {
670  startEarly = m_programInfo.GetRecordingStartTime()
672  }
673  if (programDuration > 0)
674  {
675  captime = programDuration / 3;
676  if (captime > 600)
677  captime = 600;
678  captime += startEarly;
679  }
680  if (captime < 0)
681  captime = 600;
682  captime += preroll;
683  LOG(VB_GENERAL, LOG_INFO,
684  QString("Preview at calculated offset (%1 seconds)").arg(captime));
685  }
686 
687  int width = 0;
688  int height = 0;
689  int sz = 0;
690  auto *data = (unsigned char*) GetScreenGrab(m_programInfo, m_pathname,
691  captime, m_timeInSeconds,
692  sz, width, height, aspect);
693 
695 
696  QString format = (m_outFormat.isEmpty()) ? "PNG" : m_outFormat;
697 
698  int dw = (m_outSize.width() < 0) ? width : m_outSize.width();
699  int dh = (m_outSize.height() < 0) ? height : m_outSize.height();
700 
701  bool ok = SavePreview(outname, data, width, height, aspect, dw, dh,
702  format);
703 
704  if (ok)
705  {
706  // Backdate file to start of preview time in case a bookmark was made
707  // while we were generating the preview.
708  struct utimbuf times {};
709  times.actime = times.modtime = dt.toSecsSinceEpoch();
710  utime(outname.toLocal8Bit().constData(), &times);
711  }
712 
713  delete[] data;
714 
716 
717  return ok;
718 }
719 
721  const QString &pathname, const QString &outFileName)
722 {
723  QString outname = pathname + ".png";
724 
725  if (outFileName.isEmpty())
726  return outname;
727 
728  outname = outFileName;
729  QFileInfo fi(outname);
730  if (outname == fi.fileName())
731  {
732  QString dir;
733  if (pathname.contains(':'))
734  {
735  QUrl uinfo(pathname);
736  uinfo.setPath("");
737  dir = uinfo.toString();
738  }
739  else
740  {
741  dir = QFileInfo(pathname).path();
742  }
743  outname = dir + "/" + fi.fileName();
744  LOG(VB_FILE, LOG_INFO, LOC + QString("outfile '%1' -> '%2'")
745  .arg(outFileName).arg(outname));
746  }
747 
748  return outname;
749 }
750 
752 {
753  QString tmppathname = m_pathname;
754 
755  if (tmppathname.startsWith("dvd:"))
756  tmppathname = tmppathname.section(":", 1, 1);
757 
758  if (!QFileInfo(tmppathname).isReadable())
759  return false;
760 
761  tmppathname = m_outFileName.isEmpty() ? tmppathname : m_outFileName;
762  QString pathdir = QFileInfo(tmppathname).path();
763 
764  if (!QFileInfo(pathdir).isWritable())
765  {
766  LOG(VB_GENERAL, LOG_WARNING, LOC +
767  QString("Output path '%1' is not writeable") .arg(pathdir));
768  return false;
769  }
770 
771  return true;
772 }
773 
790  const ProgramInfo &pginfo, const QString &filename,
791  long long seektime, bool time_in_secs,
792  int &bufferlen,
793  int &video_width, int &video_height, float &video_aspect)
794 {
795  char *retbuf = nullptr;
796  bufferlen = 0;
797 
799  {
800  LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not connect to DB.");
801  return nullptr;
802  }
803 
804  // pre-test local files for existence and size. 500 ms speed-up...
805  if (filename.startsWith("/"))
806  {
807  QFileInfo info(filename);
808  bool invalid = (!info.exists() || !info.isReadable() || (info.isFile() && (info.size() < 8*1024)));
809  if (invalid)
810  {
811  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Previewer file '%1' is not valid.")
812  .arg(filename));
813  return nullptr;
814  }
815  }
816 
817  MythMediaBuffer* buffer = MythMediaBuffer::Create(filename, false, false, 0);
818  if (!buffer || !buffer->IsOpen())
819  {
820  LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not open file: " +
821  QString("'%1'").arg(filename));
822  delete buffer;
823  return nullptr;
824  }
825 
826  auto *ctx = new PlayerContext(kPreviewGeneratorInUseID);
827  auto * player = new MythPreviewPlayer(ctx, static_cast<PlayerFlags>(kAudioMuted | kVideoIsNull | kNoITV));
828  ctx->SetRingBuffer(buffer);
829  ctx->SetPlayingInfo(&pginfo);
830  ctx->SetPlayer(player);
831 
832  if (time_in_secs)
833  {
834  retbuf = player->GetScreenGrab(static_cast<int>(seektime), bufferlen,
835  video_width, video_height, video_aspect);
836  }
837  else
838  {
839  retbuf = player->GetScreenGrabAtFrame(static_cast<uint64_t>(seektime), true,
840  bufferlen, video_width, video_height, video_aspect);
841  }
842  delete ctx;
843 
844  if (retbuf)
845  {
846  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Grabbed preview '%0' %1x%2@%3%4")
847  .arg(filename).arg(video_width).arg(video_height)
848  .arg(seektime).arg((time_in_secs) ? "s" : "f"));
849  }
850  else
851  {
852  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to grab preview '%0' %1x%2@%3%4")
853  .arg(filename).arg(video_width).arg(video_height)
854  .arg(seektime).arg((time_in_secs) ? "s" : "f"));
855  }
856 
857  return retbuf;
858 }
PreviewGenerator::m_outSize
QSize m_outSize
Definition: previewgenerator.h:119
e
QDomElement e
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1420
ProgramInfo::MakeUniqueKey
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:337
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:73
GENERIC_EXIT_OK
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
MythSystemLegacy
Definition: mythsystemlegacy.h:68
PreviewGenerator::PreviewGenerator
PreviewGenerator(const ProgramInfo *pginfo, QString token, Mode mode=kLocal)
Constructor.
Definition: previewgenerator.cpp:72
MythCoreContext::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
Definition: mythcorecontext.cpp:1376
PreviewGenerator::kRemote
@ kRemote
Definition: previewgenerator.h:43
PreviewGenerator::run
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: previewgenerator.cpp:351
PreviewGenerator::AttachSignals
void AttachSignals(QObject *obj)
Definition: previewgenerator.cpp:114
PreviewGenerator::Run
bool Run(void)
Definition: previewgenerator.cpp:207
PreviewGenerator::m_token
QString m_token
Definition: previewgenerator.h:122
MythMediaBuffer::Create
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, int Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
Definition: mythmediabuffer.cpp:100
PreviewGenerator::m_captureTime
long long m_captureTime
snapshot time in seconds or frame number, depending on timeInSeconds
Definition: previewgenerator.h:117
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:602
ProgramInfo::GetRecordingID
uint GetRecordingID(void) const
Definition: programinfo.h:444
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:17
arg
arg(title).arg(filename).arg(doDelete))
PreviewGenerator::RunReal
bool RunReal(void)
This call creates a preview without starting a new thread.
Definition: previewgenerator.cpp:123
PreviewGenerator::m_pathname
QString m_pathname
Definition: previewgenerator.h:112
MythMediaBuffer
Definition: mythmediabuffer.h:50
ProgramInfo::GetScheduledEndTime
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:395
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:198
PlayerFlags
PlayerFlags
Definition: mythplayer.h:79
PreviewGenerator::SavePreview
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)
Definition: previewgenerator.cpp:557
PreviewGenerator::LocalPreviewRun
bool LocalPreviewRun(void)
Definition: previewgenerator.cpp:624
build_compdb.file
file
Definition: build_compdb.py:55
mythdirs.h
remoteutil.h
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
ProgramInfo::GetRecordingStartTime
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:402
ProgramInfo::GetPathname
QString GetPathname(void) const
Definition: programinfo.h:341
mythsystemlegacy.h
PreviewGenerator::event
bool event(QEvent *e) override
Definition: previewgenerator.cpp:419
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
ProgramInfo::QueryBookmark
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
Definition: programinfo.cpp:2647
PreviewGenerator::TeardownAll
void TeardownAll(void)
Definition: previewgenerator.cpp:101
ProgramInfo::MarkAsInUse
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...
Definition: programinfo.cpp:4893
PreviewGenerator::SetOutputFilename
void SetOutputFilename(const QString &fileName)
Definition: previewgenerator.cpp:92
mythdate.h
MythEvent::ExtraData
const QString & ExtraData(int idx=0) const
Definition: mythevent.h:66
kPreviewGeneratorInUseID
const char * kPreviewGeneratorInUseID
Definition: programtypes.cpp:22
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:388
PreviewGenerator::m_outFormat
QString m_outFormat
Definition: previewgenerator.h:120
mythlogging.h
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
remotefile.h
MythDate::kFilename
@ kFilename
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
MSqlQuery::testDBConnection
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:852
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:637
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:211
PreviewGenerator::~PreviewGenerator
~PreviewGenerator() override
Definition: previewgenerator.cpp:86
storagegroup.h
PreviewGenerator::m_timeInSeconds
bool m_timeInSeconds
tells us whether to use time as seconds or frame number
Definition: previewgenerator.h:115
f
QTextStream t & f
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:603
kNoITV
@ kNoITV
Definition: mythplayer.h:89
kMSPropagateLogs
@ kMSPropagateLogs
add arguments for MythTV log propagation
Definition: mythsystem.h:50
PreviewGenerator::IsLocal
bool IsLocal(void) const
Definition: previewgenerator.cpp:751
uint
unsigned int uint
Definition: compat.h:140
PreviewGenerator::kForceLocal
@ kForceLocal
Definition: previewgenerator.h:45
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:56
token
return token
Definition: musicutils.cpp:74
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:929
PreviewGenerator::m_previewWaitCondition
QWaitCondition m_previewWaitCondition
Definition: previewgenerator.h:106
mythmediabuffer.h
kMSAutoCleanup
@ kMSAutoCleanup
automatically delete if backgrounded
Definition: mythsystem.h:43
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
PreviewGenerator::SaveOutFile
bool SaveOutFile(const QByteArray &data, const QDateTime &dt)
Definition: previewgenerator.cpp:492
off_t
#define off_t
Definition: mythiowrapper.cpp:238
PreviewGenerator::m_programInfo
ProgramInfo m_programInfo
Definition: previewgenerator.h:108
PreviewGenerator::m_outFileName
QString m_outFileName
Definition: previewgenerator.h:118
PreviewGenerator::RemotePreviewRun
bool RemotePreviewRun(void)
Definition: previewgenerator.cpp:358
PreviewGenerator::m_pixmapOk
bool m_pixmapOk
Definition: previewgenerator.h:124
MythRandom
MBASE_PUBLIC uint32_t MythRandom()
Definition: mythmiscutil.h:24
PreviewGenerator::m_gotReply
bool m_gotReply
Definition: previewgenerator.h:123
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:370
kVideoIsNull
@ kVideoIsNull
Definition: mythplayer.h:87
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
mythmiscutil.h
makeFileAccessible
bool makeFileAccessible(const QString &filename)
Definition: mythmiscutil.cpp:437
ProgramInfo::ToStringList
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
Definition: programinfo.cpp:1270
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1174
playercontext.h
PreviewGenerator::CreateAccessibleFilename
static QString CreateAccessibleFilename(const QString &pathname, const QString &outFileName)
Definition: previewgenerator.cpp:720
kMSProcessEvents
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:37
remotecachedir
static QString remotecachedir
Definition: mythdirs.cpp:26
LOC
#define LOC
Definition: previewgenerator.cpp:39
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:14
PreviewGenerator::m_mode
Mode m_mode
Definition: previewgenerator.h:110
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
GetAppBinDir
QString GetAppBinDir(void)
Definition: mythdirs.cpp:221
tv_rec.h
kAudioMuted
@ kAudioMuted
Definition: mythplayer.h:88
StorageGroup
Definition: storagegroup.h:12
PlayerContext
Definition: playercontext.h:49
MythPreviewPlayer
Definition: mythpreviewplayer.h:8
PreviewGenerator::m_listener
QObject * m_listener
Definition: previewgenerator.h:111
PreviewGenerator::GetScreenGrab
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.
Definition: previewgenerator.cpp:789
PreviewGenerator::deleteLater
void deleteLater()
Definition: previewgenerator.cpp:108
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:855
ProgramInfo::SetIgnoreProgStart
void SetIgnoreProgStart(bool ignore)
If "ignore" is true QueryProgStart() will return 0, otherwise QueryProgStart() will return the progst...
Definition: programinfo.h:556
PreviewGenerator::kLocal
@ kLocal
Definition: previewgenerator.h:42
kMSDontDisableDrawing
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:35
MThread::wait
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:305
previewgenerator.h
exitcodes.h
ProgramInfo::GetBasename
QString GetBasename(void) const
Definition: programinfo.h:342
mythsocket.h
mythpreviewplayer.h
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
PreviewGenerator::Mode
Mode
Definition: previewgenerator.h:40
MThread::usleep
static void usleep(unsigned long time)
Definition: mthread.cpp:342
ProgramInfo::SetAllowLastPlayPos
void SetAllowLastPlayPos(bool allow)
If "ignore" is true QueryLastPlayPos() will return 0, otherwise QueryLastPlayPos() will return the la...
Definition: programinfo.h:564
PreviewGenerator::m_previewLock
QMutex m_previewLock
Definition: previewgenerator.h:107
MythMediaBuffer::IsOpen
virtual bool IsOpen(void) const =0