MythTV  master
mythtranscode.cpp
Go to the documentation of this file.
1 // C++ headers
2 #include <cerrno>
3 #include <fcntl.h> // for open flags
4 #include <fstream>
5 #include <iostream>
6 
7 // Qt headers
8 #include <QtGlobal>
9 #include <QCoreApplication>
10 #include <QDir>
11 #include <utility>
12 
13 // MythTV headers
14 #include "libmyth/mythcontext.h"
16 #include "libmythbase/exitcodes.h"
17 #include "libmythbase/mythdate.h"
18 #include "libmythbase/mythdb.h"
22 #include "libmythbase/mythversion.h"
24 #include "libmythbase/remotefile.h"
27 #include "libmythtv/jobqueue.h"
29 
30 // MythTranscode
31 #include "mpeg2fix.h"
33 #include "transcode.h"
34 
35 static void CompleteJob(int jobID, ProgramInfo *pginfo, bool useCutlist,
36  frm_dir_map_t *deleteMap, int &exitCode,
37  int resultCode, bool forceDelete);
38 
39 static int glbl_jobID = -1;
40 static QString recorderOptions = "";
41 
42 static void UpdatePositionMap(frm_pos_map_t &posMap, frm_pos_map_t &durMap, const QString& mapfile,
43  ProgramInfo *pginfo)
44 {
45  if (pginfo && mapfile.isEmpty())
46  {
49  pginfo->SavePositionMap(posMap, MARK_GOP_BYFRAME);
50  pginfo->SavePositionMap(durMap, MARK_DURATION_MS);
51  }
52  else if (!mapfile.isEmpty())
53  {
54  MarkTypes keyType = MARK_GOP_BYFRAME;
55  FILE *mapfh = fopen(mapfile.toLocal8Bit().constData(), "w");
56  if (!mapfh)
57  {
58  LOG(VB_GENERAL, LOG_ERR, QString("Could not open map file '%1'")
59  .arg(mapfile) + ENO);
60  return;
61  }
62  frm_pos_map_t::const_iterator it;
63  fprintf (mapfh, "Type: %d\n", keyType);
64  for (it = posMap.cbegin(); it != posMap.cend(); ++it)
65  {
66  QString str = QString("%1 %2\n").arg(it.key()).arg(*it);
67  fprintf(mapfh, "%s", qPrintable(str));
68  }
69  fclose(mapfh);
70  }
71 }
72 
73 static int BuildKeyframeIndex(MPEG2fixup *m2f, const QString &infile,
74  frm_pos_map_t &posMap, frm_pos_map_t &durMap, int jobID)
75 {
76  if (!m2f)
77  return 0;
78 
79  if (jobID < 0 || JobQueue::GetJobCmd(jobID) != JOB_STOP)
80  {
81  if (jobID >= 0)
83  QObject::tr("Generating Keyframe Index"));
84  int err = m2f->BuildKeyframeIndex(infile, posMap, durMap);
85  if (err)
86  return err;
87  if (jobID >= 0)
89  QObject::tr("Transcode Completed"));
90  }
91  return 0;
92 }
93 
94 static void UpdateJobQueue(float percent_done)
95 {
97  QString("%1% ").arg(percent_done, 0, 'f', 1) +
98  QObject::tr("Completed"));
99 }
100 
101 static int CheckJobQueue()
102 {
104  {
105  LOG(VB_GENERAL, LOG_NOTICE, "Transcoding stopped by JobQueue");
106  return 1;
107  }
108  return 0;
109 }
110 
111 static int QueueTranscodeJob(ProgramInfo *pginfo, const QString& profile,
112  const QString& hostname, bool usecutlist)
113 {
114  if (!profile.isEmpty())
115  {
116  RecordingInfo recinfo(*pginfo);
118  }
119 
121  pginfo->GetRecordingStartTime(),
122  hostname, "", "",
123  usecutlist ? JOB_USE_CUTLIST : 0))
124  {
125  LOG(VB_GENERAL, LOG_NOTICE,
126  QString("Queued transcode job for chanid %1 @ %2")
127  .arg(pginfo->GetChanID())
129  return GENERIC_EXIT_OK;
130  }
131 
132  LOG(VB_GENERAL, LOG_ERR, QString("Error queuing job for chanid %1 @ %2")
133  .arg(pginfo->GetChanID())
135  return GENERIC_EXIT_DB_ERROR;
136 }
137 
138 namespace
139 {
140  void cleanup()
141  {
142  delete gContext;
143  gContext = nullptr;
145  }
146 }
147 
148 int main(int argc, char *argv[])
149 {
150  uint chanid = 0;
151  QDateTime starttime;
152  QString infile;
153  QString outfile;
154  QString profilename = QString("autodetect");
155  QString fifodir = nullptr;
156  int jobID = -1;
157  int jobType = JOB_NONE;
158  int otype = REPLEX_MPEG2;
159  bool useCutlist = false;
160  bool keyframesonly = false;
161  bool build_index = false;
162  bool fifosync = false;
163  bool mpeg2 = false;
164  bool fifo_info = false;
165  bool cleanCut = false;
166  frm_dir_map_t deleteMap;
167  frm_pos_map_t posMap;
168  frm_pos_map_t durMap;
169  int AudioTrackNo = -1;
170 
171  bool found_starttime = false;
172  bool found_chanid = false;
173  bool found_infile = false;
174  int update_index = 1;
175  bool isVideo = false;
176  bool passthru = false;
177 
179  if (!cmdline.Parse(argc, argv))
180  {
181  cmdline.PrintHelp();
183  }
184 
185  if (cmdline.toBool("showhelp"))
186  {
187  cmdline.PrintHelp();
188  return GENERIC_EXIT_OK;
189  }
190 
191  if (cmdline.toBool("showversion"))
192  {
194  return GENERIC_EXIT_OK;
195  }
196 
197  QCoreApplication a(argc, argv);
198  QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHTRANSCODE);
199 
200  if (cmdline.toBool("outputfile"))
201  {
202  outfile = cmdline.toString("outputfile");
203  update_index = 0;
204  }
205 
206  bool showprogress = cmdline.toBool("showprogress");
207 
208  QString mask("general");
209  bool quiet = (outfile == "-") || showprogress;
210  int retval = cmdline.ConfigureLogging(mask, quiet);
211  if (retval != GENERIC_EXIT_OK)
212  return retval;
213 
214  if (cmdline.toBool("starttime"))
215  {
216  starttime = cmdline.toDateTime("starttime");
217  found_starttime = true;
218  }
219  if (cmdline.toBool("chanid"))
220  {
221  chanid = cmdline.toUInt("chanid");
222  found_chanid = true;
223  }
224  if (cmdline.toBool("jobid"))
225  jobID = cmdline.toInt("jobid");
226  if (cmdline.toBool("inputfile"))
227  {
228  infile = cmdline.toString("inputfile");
229  found_infile = true;
230  }
231  if (cmdline.toBool("video"))
232  isVideo = true;
233  if (cmdline.toBool("profile"))
234  profilename = cmdline.toString("profile");
235 
236  if (cmdline.toBool("usecutlist"))
237  {
238  useCutlist = true;
239  if (!cmdline.toString("usecutlist").isEmpty())
240  {
241  if (!cmdline.toBool("inputfile") && !cmdline.toBool("hls"))
242  {
243  LOG(VB_GENERAL, LOG_CRIT, "External cutlists are only allowed "
244  "when using the --infile option.");
246  }
247 
248  uint64_t last = 0;
249  QStringList cutlist = cmdline.toStringList("usecutlist", " ");
250  for (const auto & cut : qAsConst(cutlist))
251  {
252 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
253  QStringList startend =
254  cut.split("-", QString::SkipEmptyParts);
255 #else
256  QStringList startend = cut.split("-", Qt::SkipEmptyParts);
257 #endif
258  if (startend.size() == 2)
259  {
260  uint64_t start = startend.first().toULongLong();
261  uint64_t end = startend.last().toULongLong();
262 
263  if (cmdline.toBool("inversecut"))
264  {
265  LOG(VB_GENERAL, LOG_DEBUG,
266  QString("Cutting section %1-%2.")
267  .arg(last).arg(start));
268  deleteMap[start] = MARK_CUT_END;
269  deleteMap[end] = MARK_CUT_START;
270  last = end;
271  }
272  else
273  {
274  LOG(VB_GENERAL, LOG_DEBUG,
275  QString("Cutting section %1-%2.")
276  .arg(start).arg(end));
277  deleteMap[start] = MARK_CUT_START;
278  deleteMap[end] = MARK_CUT_END;
279  }
280  }
281  }
282 
283  if (cmdline.toBool("inversecut"))
284  {
285  if (deleteMap.contains(0) && (deleteMap[0] == MARK_CUT_END))
286  deleteMap.remove(0);
287  else
288  deleteMap[0] = MARK_CUT_START;
289  deleteMap[999999999] = MARK_CUT_END;
290  LOG(VB_GENERAL, LOG_DEBUG,
291  QString("Cutting section %1-999999999.")
292  .arg(last));
293  }
294 
295  // sanitize cutlist
296  if (deleteMap.count() >= 2)
297  {
298  frm_dir_map_t::iterator cur = deleteMap.begin();
299  frm_dir_map_t::iterator prev;
300  prev = cur++;
301  while (cur != deleteMap.end())
302  {
303  if (prev.value() == cur.value())
304  {
305  // two of the same type next to each other
306  QString err("Cut %1points found at %3 and %4, with no "
307  "%2 point in between.");
308  if (prev.value() == MARK_CUT_END)
309  err = err.arg("end", "start");
310  else
311  err = err.arg("start", "end");
312  LOG(VB_GENERAL, LOG_CRIT, "Invalid cutlist defined!");
313  LOG(VB_GENERAL, LOG_CRIT, err.arg(prev.key())
314  .arg(cur.key()));
316  }
317  if ( (prev.value() == MARK_CUT_START) &&
318  ((cur.key() - prev.key()) < 2) )
319  {
320  LOG(VB_GENERAL, LOG_WARNING, QString("Discarding "
321  "insufficiently long cut: %1-%2")
322  .arg(prev.key()).arg(cur.key()));
323  prev = deleteMap.erase(prev);
324  cur = deleteMap.erase(cur);
325 
326  if (cur == deleteMap.end())
327  continue;
328  }
329  prev = cur++;
330  }
331  }
332  }
333  else if (cmdline.toBool("inversecut"))
334  {
335  std::cerr << "Cutlist inversion requires an external cutlist be" << std::endl
336  << "provided using the --honorcutlist option." << std::endl;
338  }
339  }
340 
341  if (cmdline.toBool("cleancut"))
342  cleanCut = true;
343 
344  if (cmdline.toBool("allkeys"))
345  keyframesonly = true;
346  if (cmdline.toBool("reindex"))
347  build_index = true;
348  if (cmdline.toBool("fifodir"))
349  fifodir = cmdline.toString("fifodir");
350  if (cmdline.toBool("fifoinfo"))
351  fifo_info = true;
352  if (cmdline.toBool("fifosync"))
353  fifosync = true;
354  if (cmdline.toBool("recopt"))
355  recorderOptions = cmdline.toString("recopt");
356  if (cmdline.toBool("mpeg2"))
357  mpeg2 = true;
358  if (cmdline.toBool("ostream"))
359  {
360  if (cmdline.toString("ostream") == "dvd")
361  otype = REPLEX_DVD;
362  else if (cmdline.toString("ostream") == "ps")
363  otype = REPLEX_MPEG2;
364  else if (cmdline.toString("ostream") == "ts")
365  otype = REPLEX_TS_SD;
366  else
367  {
368  std::cerr << "Invalid 'ostream' type: "
369  << cmdline.toString("ostream").toLocal8Bit().constData()
370  << std::endl;
372  }
373  }
374  if (cmdline.toBool("audiotrack"))
375  AudioTrackNo = cmdline.toInt("audiotrack");
376  if (cmdline.toBool("passthru"))
377  passthru = true;
378  // Set if we want to delete the original file once conversion succeeded.
379  bool deleteOriginal = cmdline.toBool("delete");
380 
381  CleanupGuard callCleanup(cleanup);
382 
383 #ifndef _WIN32
385 #endif
386 
387  // Load the context
388  gContext = new MythContext(MYTH_BINARY_VERSION);
389  if (!gContext->Init(false))
390  {
391  LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting.");
393  }
394 
395  MythTranslation::load("mythfrontend");
396 
398 
399  if (jobID != -1)
400  {
401  if (JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime))
402  {
403  found_starttime = true;
404  found_chanid = true;
405  }
406  else
407  {
408  std::cerr << "mythtranscode: ERROR: Unable to find DB info for "
409  << "JobQueue ID# " << jobID << std::endl;
411  }
412  }
413 
414  if (((!found_infile && !(found_chanid && found_starttime)) ||
415  (found_infile && (found_chanid || found_starttime))) &&
416  (!cmdline.toBool("hls")))
417  {
418  std::cerr << "Must specify -i OR -c AND -s options!" << std::endl;
420  }
421  if (isVideo && !found_infile && !cmdline.toBool("hls"))
422  {
423  std::cerr << "Must specify --infile to use --video" << std::endl;
425  }
426  if (jobID >= 0 && (found_infile || build_index))
427  {
428  std::cerr << "Can't specify -j with --buildindex, --video or --infile"
429  << std::endl;
431  }
432  if ((jobID >= 0) && build_index)
433  {
434  std::cerr << "Can't specify both -j and --buildindex" << std::endl;
436  }
437  if (keyframesonly && !fifodir.isEmpty())
438  {
439  std::cerr << "Cannot specify both --fifodir and --allkeys" << std::endl;
441  }
442  if (fifosync && fifodir.isEmpty())
443  {
444  std::cerr << "Must specify --fifodir to use --fifosync" << std::endl;
446  }
447  if (fifo_info && !fifodir.isEmpty())
448  {
449  std::cerr << "Cannot specify both --fifodir and --fifoinfo" << std::endl;
451  }
452  if (cleanCut && fifodir.isEmpty() && !fifo_info)
453  {
454  std::cerr << "Clean cutting works only in fifodir mode" << std::endl;
456  }
457  if (cleanCut && !useCutlist)
458  {
459  std::cerr << "--cleancut is pointless without --honorcutlist" << std::endl;
461  }
462 
463  if (fifo_info)
464  {
465  // Setup a dummy fifodir path, so that the "fifodir" code path
466  // is taken. The path wont actually be used.
467  fifodir = "DummyFifoPath";
468  }
469 
471  {
472  LOG(VB_GENERAL, LOG_ERR, "couldn't open db");
473  return GENERIC_EXIT_DB_ERROR;
474  }
475 
476  ProgramInfo *pginfo = nullptr;
477  if (cmdline.toBool("hls"))
478  {
479  if (cmdline.toBool("hlsstreamid"))
480  {
481  HTTPLiveStream hls(cmdline.toInt("hlsstreamid"));
482  pginfo = new ProgramInfo(hls.GetSourceFile());
483  }
484  if (pginfo == nullptr)
485  pginfo = new ProgramInfo();
486  }
487  else if (isVideo)
488  {
489  // We want the absolute file path for the filemarkup table
490  QFileInfo inf(infile);
491  infile = inf.absoluteFilePath();
492  pginfo = new ProgramInfo(infile);
493  }
494  else if (!found_infile)
495  {
496  pginfo = new ProgramInfo(chanid, starttime);
497 
498  if (!pginfo->GetChanID())
499  {
500  LOG(VB_GENERAL, LOG_ERR,
501  QString("Couldn't find recording for chanid %1 @ %2")
502  .arg(chanid).arg(starttime.toString(Qt::ISODate)));
503  delete pginfo;
505  }
506 
507  infile = pginfo->GetPlaybackURL(false, true);
508  }
509  else
510  {
511  pginfo = new ProgramInfo(infile);
512  if (!pginfo->GetChanID())
513  {
514  LOG(VB_GENERAL, LOG_ERR,
515  QString("Couldn't find a recording for filename '%1'")
516  .arg(infile));
517  delete pginfo;
519  }
520  }
521 
522  if (!pginfo)
523  {
524  LOG(VB_GENERAL, LOG_ERR, "No program info found!");
526  }
527 
528  if (cmdline.toBool("queue"))
529  {
530  QString hostname = cmdline.toString("queue");
531  return QueueTranscodeJob(pginfo, profilename, hostname, useCutlist);
532  }
533 
534  if (infile.startsWith("myth://") && (outfile.isEmpty() || outfile != "-") &&
535  fifodir.isEmpty() && !cmdline.toBool("hls") && !cmdline.toBool("avf"))
536  {
537  LOG(VB_GENERAL, LOG_ERR,
538  QString("Attempted to transcode %1. Mythtranscode is currently "
539  "unable to transcode remote files.") .arg(infile));
540  delete pginfo;
542  }
543 
544  if (outfile.isEmpty() && !build_index && fifodir.isEmpty())
545  outfile = infile + ".tmp";
546 
547  if (jobID >= 0)
548  JobQueue::ChangeJobStatus(jobID, JOB_RUNNING);
549 
550  auto *transcode = new Transcode(pginfo);
551 
552  if (!build_index)
553  {
554  if (cmdline.toBool("hlsstreamid"))
555  {
556  LOG(VB_GENERAL, LOG_NOTICE,
557  QString("Transcoding HTTP Live Stream ID %1")
558  .arg(cmdline.toInt("hlsstreamid")));
559  }
560  else if (fifodir.isEmpty())
561  {
562  LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding from %1 to %2")
563  .arg(infile, outfile));
564  }
565  else
566  {
567  LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding from %1 to FIFO")
568  .arg(infile));
569  }
570  }
571 
572  if (cmdline.toBool("avf"))
573  {
574  transcode->SetAVFMode();
575 
576  if (cmdline.toBool("container"))
577  transcode->SetCMDContainer(cmdline.toString("container"));
578  if (cmdline.toBool("acodec"))
579  transcode->SetCMDAudioCodec(cmdline.toString("acodec"));
580  if (cmdline.toBool("vcodec"))
581  transcode->SetCMDVideoCodec(cmdline.toString("vcodec"));
582  }
583  else if (cmdline.toBool("hls"))
584  {
585  transcode->SetHLSMode();
586 
587  if (cmdline.toBool("hlsstreamid"))
588  transcode->SetHLSStreamID(cmdline.toInt("hlsstreamid"));
589  if (cmdline.toBool("maxsegments"))
590  transcode->SetHLSMaxSegments(cmdline.toInt("maxsegments"));
591  if (cmdline.toBool("noaudioonly"))
592  transcode->DisableAudioOnlyHLS();
593  }
594 
595  if (cmdline.toBool("avf") || cmdline.toBool("hls"))
596  {
597  if (cmdline.toBool("width"))
598  transcode->SetCMDWidth(cmdline.toInt("width"));
599  if (cmdline.toBool("height"))
600  transcode->SetCMDHeight(cmdline.toInt("height"));
601  if (cmdline.toBool("bitrate"))
602  transcode->SetCMDBitrate(cmdline.toInt("bitrate") * 1000);
603  if (cmdline.toBool("audiobitrate"))
604  transcode->SetCMDAudioBitrate(cmdline.toInt("audiobitrate") * 1000);
605  }
606 
607  if (showprogress)
608  transcode->ShowProgress(true);
609  if (!recorderOptions.isEmpty())
610  transcode->SetRecorderOptions(recorderOptions);
611  int result = 0;
612  if ((!mpeg2 && !build_index) || cmdline.toBool("hls"))
613  {
614  result = transcode->TranscodeFile(infile, outfile,
615  profilename, useCutlist,
616  (fifosync || keyframesonly), jobID,
617  fifodir, fifo_info, cleanCut, deleteMap,
618  AudioTrackNo, passthru);
619 
620  if ((result == REENCODE_OK) && (jobID >= 0))
621  {
622  JobQueue::ChangeJobArgs(jobID, "RENAME_TO_NUV");
623  RecordingInfo recInfo(pginfo->GetRecordingID());
624  RecordingFile *recFile = recInfo.GetRecordingFile();
625  recFile->m_containerFormat = formatNUV;
626  recFile->Save();
627  }
628  }
629 
630  if (fifo_info)
631  {
632  delete transcode;
633  return GENERIC_EXIT_OK;
634  }
635 
636  int exitcode = GENERIC_EXIT_OK;
637  if ((result == REENCODE_MPEG2TRANS) || mpeg2 || build_index)
638  {
639  void (*update_func)(float) = nullptr;
640  int (*check_func)() = nullptr;
641  if (useCutlist)
642  {
643  LOG(VB_GENERAL, LOG_INFO, "Honoring the cutlist while transcoding");
644  if (deleteMap.isEmpty())
645  pginfo->QueryCutList(deleteMap);
646  }
647  if (jobID >= 0)
648  {
649  glbl_jobID = jobID;
650  update_func = &UpdateJobQueue;
651  check_func = &CheckJobQueue;
652  }
653 
654  auto *m2f = new MPEG2fixup(infile, outfile,
655  &deleteMap, nullptr, false, false, 20,
656  showprogress, otype, update_func,
657  check_func);
658 
659  if (cmdline.toBool("allaudio"))
660  {
661  m2f->SetAllAudio(true);
662  }
663 
664  if (build_index)
665  {
666  int err = BuildKeyframeIndex(m2f, infile, posMap, durMap, jobID);
667  if (err)
668  {
669  delete m2f;
670  m2f = nullptr;
671  return err;
672  }
673  if (update_index)
674  UpdatePositionMap(posMap, durMap, nullptr, pginfo);
675  else
676  UpdatePositionMap(posMap, durMap, outfile + QString(".map"), pginfo);
677  }
678  else
679  {
680  result = m2f->Start();
681  if (result == REENCODE_OK)
682  {
683  result = BuildKeyframeIndex(m2f, outfile, posMap, durMap, jobID);
684  if (result == REENCODE_OK)
685  {
686  if (update_index)
687  UpdatePositionMap(posMap, durMap, nullptr, pginfo);
688  else
689  UpdatePositionMap(posMap, durMap, outfile + QString(".map"),
690  pginfo);
691  }
692  RecordingInfo recInfo(*pginfo);
693  RecordingFile *recFile = recInfo.GetRecordingFile();
694  if (otype == REPLEX_DVD || otype == REPLEX_MPEG2 ||
695  otype == REPLEX_HDTV)
696  {
698  JobQueue::ChangeJobArgs(jobID, "RENAME_TO_MPG");
699  }
700  else
701  {
703  }
704  recFile->Save();
705  }
706  }
707  delete m2f;
708  m2f = nullptr;
709  }
710 
711  if (result == REENCODE_OK)
712  {
713  if (jobID >= 0)
714  JobQueue::ChangeJobStatus(jobID, JOB_STOPPING);
715  LOG(VB_GENERAL, LOG_NOTICE, QString("%1 %2 done")
716  .arg(build_index ? "Building Index for" : "Transcoding", infile));
717  }
718  else if (result == REENCODE_CUTLIST_CHANGE)
719  {
720  if (jobID >= 0)
721  JobQueue::ChangeJobStatus(jobID, JOB_RETRY);
722  LOG(VB_GENERAL, LOG_NOTICE,
723  QString("Transcoding %1 aborted because of cutlist update")
724  .arg(infile));
725  exitcode = GENERIC_EXIT_RESTART;
726  }
727  else if (result == REENCODE_STOPPED)
728  {
729  if (jobID >= 0)
730  JobQueue::ChangeJobStatus(jobID, JOB_ABORTING);
731  LOG(VB_GENERAL, LOG_NOTICE,
732  QString("Transcoding %1 stopped because of stop command")
733  .arg(infile));
734  exitcode = GENERIC_EXIT_KILLED;
735  }
736  else
737  {
738  if (jobID >= 0)
739  JobQueue::ChangeJobStatus(jobID, JOB_ERRORING);
740  LOG(VB_GENERAL, LOG_ERR, QString("Transcoding %1 failed").arg(infile));
741  exitcode = result;
742  }
743 
744  if (deleteOriginal || jobID >= 0)
745  CompleteJob(jobID, pginfo, useCutlist, &deleteMap, exitcode, result, deleteOriginal);
746 
747  transcode->deleteLater();
748 
749  return exitcode;
750 }
751 
752 static int transUnlink(const QString& filename, ProgramInfo *pginfo)
753 {
754  QString hostname = pginfo->GetHostname();
755 
756  if (!pginfo->GetStorageGroup().isEmpty() &&
757  !hostname.isEmpty())
758  {
760  QString basename = filename.section('/', -1);
761  QString uri = MythCoreContext::GenMythURL(hostname, port, basename,
762  pginfo->GetStorageGroup());
763 
764  LOG(VB_GENERAL, LOG_NOTICE, QString("Requesting delete for file '%1'.")
765  .arg(uri));
766  bool ok = RemoteFile::DeleteFile(uri);
767  if (ok)
768  return 0;
769  }
770 
771  LOG(VB_GENERAL, LOG_NOTICE, QString("Deleting file '%1'.").arg(filename));
772  return unlink(filename.toLocal8Bit().constData());
773 }
774 
775 static uint64_t ComputeNewBookmark(uint64_t oldBookmark,
776  frm_dir_map_t *deleteMap)
777 {
778  if (deleteMap == nullptr)
779  return oldBookmark;
780 
781  uint64_t subtraction = 0;
782  uint64_t startOfCutRegion = 0;
783  frm_dir_map_t delMap = *deleteMap;
784  bool withinCut = false;
785  bool firstMark = true;
786  while (!delMap.empty() && delMap.begin().key() <= oldBookmark)
787  {
788  uint64_t key = delMap.begin().key();
789  MarkTypes mark = delMap.begin().value();
790 
791  if (mark == MARK_CUT_START && !withinCut)
792  {
793  withinCut = true;
794  startOfCutRegion = key;
795  }
796  else if (mark == MARK_CUT_END && firstMark)
797  {
798  subtraction += key;
799  }
800  else if (mark == MARK_CUT_END && withinCut)
801  {
802  withinCut = false;
803  subtraction += (key - startOfCutRegion);
804  }
805  delMap.remove(key);
806  firstMark = false;
807  }
808  if (withinCut)
809  subtraction += (oldBookmark - startOfCutRegion);
810  return oldBookmark - subtraction;
811 }
812 
813 static uint64_t ReloadBookmark(ProgramInfo *pginfo)
814 {
815  MSqlQuery query(MSqlQuery::InitCon());
816  uint64_t currentBookmark = 0;
817  query.prepare("SELECT DISTINCT mark FROM recordedmarkup "
818  "WHERE chanid = :CHANID "
819  "AND starttime = :STARTIME "
820  "AND type = :MARKTYPE ;");
821  query.bindValue(":CHANID", pginfo->GetChanID());
822  query.bindValue(":STARTTIME", pginfo->GetRecordingStartTime());
823  query.bindValue(":MARKTYPE", MARK_BOOKMARK);
824  if (query.exec() && query.next())
825  {
826  currentBookmark = query.value(0).toLongLong();
827  }
828  return currentBookmark;
829 }
830 
831 static void WaitToDelete(ProgramInfo *pginfo)
832 {
833  LOG(VB_GENERAL, LOG_NOTICE,
834  "Transcode: delete old file: waiting while program is in use.");
835 
836  bool inUse = true;
837  MSqlQuery query(MSqlQuery::InitCon());
838  while (inUse)
839  {
840  query.prepare("SELECT count(*) FROM inuseprograms "
841  "WHERE chanid = :CHANID "
842  "AND starttime = :STARTTIME "
843  "AND recusage = 'player' ;");
844  query.bindValue(":CHANID", pginfo->GetChanID());
845  query.bindValue(":STARTTIME", pginfo->GetRecordingStartTime());
846  if (!query.exec() || !query.next())
847  {
848  LOG(VB_GENERAL, LOG_ERR,
849  "Transcode: delete old file: in-use query failed;");
850  inUse = false;
851  }
852  else
853  {
854  inUse = (query.value(0).toUInt() != 0);
855  }
856 
857  if (inUse)
858  {
859  const unsigned kSecondsToWait = 10;
860  LOG(VB_GENERAL, LOG_NOTICE,
861  QString("Transcode: program in use, rechecking in %1 seconds.")
862  .arg(kSecondsToWait));
863  sleep(kSecondsToWait);
864  }
865  }
866  LOG(VB_GENERAL, LOG_NOTICE, "Transcode: program is no longer in use.");
867 }
868 
869 static void CompleteJob(int jobID, ProgramInfo *pginfo, bool useCutlist,
870  frm_dir_map_t *deleteMap, int &exitCode, int resultCode, bool forceDelete)
871 {
872  int status = JOB_UNKNOWN;
873  if (jobID >= 0)
874  status = JobQueue::GetJobStatus(jobID);
875 
876  if (!pginfo)
877  {
878  if (jobID >= 0)
879  JobQueue::ChangeJobStatus(jobID, JOB_ERRORED,
880  QObject::tr("Job errored, unable to find Program Info for job"));
881  LOG(VB_GENERAL, LOG_CRIT, "MythTranscode: Cleanup errored, unable to find Program Info");
882  return;
883  }
884 
885  const QString filename = pginfo->GetPlaybackURL(false, true);
886  const QByteArray fname = filename.toLocal8Bit();
887 
888  if (resultCode == REENCODE_OK)
889  {
890  WaitToDelete(pginfo);
891 
892  // Transcoding may take several minutes. Reload the bookmark
893  // in case it changed, then save its translated value back.
894  uint64_t previousBookmark =
895  ComputeNewBookmark(ReloadBookmark(pginfo), deleteMap);
896  pginfo->SaveBookmark(previousBookmark);
897 
898  QString jobArgs;
899  if (jobID >= 0)
900  jobArgs = JobQueue::GetJobArgs(jobID);
901 
902  const QString tmpfile = filename + ".tmp";
903  const QByteArray atmpfile = tmpfile.toLocal8Bit();
904 
905  // To save the original file...
906  const QString oldfile = filename + ".old";
907  const QByteArray aoldfile = oldfile.toLocal8Bit();
908 
909  QFileInfo st(tmpfile);
910  qint64 newSize = 0;
911  if (st.exists())
912  newSize = st.size();
913 
914  QString cnf = filename;
915  if (jobID >= 0)
916  {
917  if (filename.endsWith(".mpg") && jobArgs == "RENAME_TO_NUV")
918  {
919  QString newbase = pginfo->QueryBasename();
920  cnf.replace(".mpg", ".nuv");
921  newbase.replace(".mpg", ".nuv");
922  pginfo->SaveBasename(newbase);
923  }
924  else if (filename.endsWith(".ts") &&
925  (jobArgs == "RENAME_TO_MPG"))
926  {
927  QString newbase = pginfo->QueryBasename();
928  // MPEG-TS to MPEG-PS
929  cnf.replace(".ts", ".mpg");
930  newbase.replace(".ts", ".mpg");
931  pginfo->SaveBasename(newbase);
932  }
933  }
934 
935  const QString newfile = cnf;
936  const QByteArray anewfile = newfile.toLocal8Bit();
937 
938  if (rename(fname.constData(), aoldfile.constData()) == -1)
939  {
940  LOG(VB_GENERAL, LOG_ERR,
941  QString("mythtranscode: Error Renaming '%1' to '%2'")
942  .arg(filename, oldfile) + ENO);
943  }
944 
945  if (rename(atmpfile.constData(), anewfile.constData()) == -1)
946  {
947  LOG(VB_GENERAL, LOG_ERR,
948  QString("mythtranscode: Error Renaming '%1' to '%2'")
949  .arg(tmpfile, newfile) + ENO);
950  }
951 
952  if (!gCoreContext->GetBoolSetting("SaveTranscoding", false) || forceDelete)
953  {
954  bool followLinks =
955  gCoreContext->GetBoolSetting("DeletesFollowLinks", false);
956 
957  LOG(VB_FILE, LOG_INFO,
958  QString("mythtranscode: About to unlink/delete file: %1")
959  .arg(oldfile));
960 
961  QFileInfo finfo(oldfile);
962  if (followLinks && finfo.isSymLink())
963  {
964  QString link = getSymlinkTarget(oldfile);
965  QByteArray alink = link.toLocal8Bit();
966  int err = transUnlink(alink, pginfo);
967  if (err)
968  {
969  LOG(VB_GENERAL, LOG_ERR,
970  QString("mythtranscode: Error deleting '%1' "
971  "pointed to by '%2'")
972  .arg(alink.constData(), aoldfile.constData()) + ENO);
973  }
974 
975  err = unlink(aoldfile.constData());
976  if (err)
977  {
978  LOG(VB_GENERAL, LOG_ERR,
979  QString("mythtranscode: Error deleting '%1', "
980  "a link pointing to '%2'")
981  .arg(aoldfile.constData(), alink.constData()) + ENO);
982  }
983  }
984  else
985  {
986  int err = transUnlink(aoldfile.constData(), pginfo);
987  if (err)
988  {
989  LOG(VB_GENERAL, LOG_ERR,
990  QString("mythtranscode: Error deleting '%1': ")
991  .arg(oldfile) + ENO);
992  }
993  }
994  }
995 
996  // Rename or delete all preview thumbnails.
997  //
998  // TODO: This cleanup should be moved to RecordingInfo, and triggered
999  // when SaveBasename() is called
1000  QFileInfo fInfo(filename);
1001  QStringList nameFilters;
1002  nameFilters.push_back(fInfo.fileName() + "*.png");
1003  nameFilters.push_back(fInfo.fileName() + "*.jpg");
1004 
1005  QDir dir (fInfo.path());
1006  QFileInfoList previewFiles = dir.entryInfoList(nameFilters);
1007 
1008  for (const auto & previewFile : qAsConst(previewFiles))
1009  {
1010  QString oldFileName = previewFile.absoluteFilePath();
1011 
1012  // Delete previews if cutlist was applied. They will be re-created as
1013  // required. This prevents the user from being stuck with a preview
1014  // from a cut area and ensures that the "dimensioned" previews
1015  // correspond to the new timeline
1016  if (useCutlist)
1017  {
1018  // If unlink fails, keeping the old preview is not a problem.
1019  // The RENAME_TO_NUV check below will attempt to rename the
1020  // file, if required.
1021  if (transUnlink(oldFileName.toLocal8Bit().constData(), pginfo) != -1)
1022  continue;
1023  }
1024 
1025  if (jobArgs == "RENAME_TO_NUV" || jobArgs == "RENAME_TO_MPG")
1026  {
1027  QString newExtension = "mpg";
1028  if (jobArgs == "RENAME_TO_NUV")
1029  newExtension = "nuv";
1030 
1031  QString oldSuffix = previewFile.completeSuffix();
1032 
1033  if (!oldSuffix.startsWith(newExtension))
1034  {
1035  QString newSuffix = oldSuffix;
1036  QString oldExtension = oldSuffix.section(".", 0, 0);
1037  newSuffix.replace(oldExtension, newExtension);
1038 
1039  QString newFileName = oldFileName;
1040  newFileName.replace(oldSuffix, newSuffix);
1041 
1042  if (!QFile::rename(oldFileName, newFileName))
1043  {
1044  LOG(VB_GENERAL, LOG_ERR,
1045  QString("mythtranscode: Error renaming %1 to %2")
1046  .arg(oldFileName, newFileName));
1047  }
1048  }
1049  }
1050  }
1051 
1052  MSqlQuery query(MSqlQuery::InitCon());
1053 
1054  if (useCutlist)
1055  {
1056  query.prepare("DELETE FROM recordedmarkup "
1057  "WHERE chanid = :CHANID "
1058  "AND starttime = :STARTTIME "
1059  "AND type != :BOOKMARK ");
1060  query.bindValue(":CHANID", pginfo->GetChanID());
1061  query.bindValue(":STARTTIME", pginfo->GetRecordingStartTime());
1062  query.bindValue(":BOOKMARK", MARK_BOOKMARK);
1063 
1064  if (!query.exec())
1065  MythDB::DBError("Error in mythtranscode", query);
1066 
1067  query.prepare("UPDATE recorded "
1068  "SET cutlist = :CUTLIST "
1069  "WHERE chanid = :CHANID "
1070  "AND starttime = :STARTTIME ;");
1071  query.bindValue(":CUTLIST", "0");
1072  query.bindValue(":CHANID", pginfo->GetChanID());
1073  query.bindValue(":STARTTIME", pginfo->GetRecordingStartTime());
1074 
1075  if (!query.exec())
1076  MythDB::DBError("Error in mythtranscode", query);
1077 
1079  }
1080  else
1081  {
1082  query.prepare("DELETE FROM recordedmarkup "
1083  "WHERE chanid = :CHANID "
1084  "AND starttime = :STARTTIME "
1085  "AND type not in ( :COMM_START, "
1086  " :COMM_END, :BOOKMARK, "
1087  " :CUTLIST_START, :CUTLIST_END) ;");
1088  query.bindValue(":CHANID", pginfo->GetChanID());
1089  query.bindValue(":STARTTIME", pginfo->GetRecordingStartTime());
1090  query.bindValue(":COMM_START", MARK_COMM_START);
1091  query.bindValue(":COMM_END", MARK_COMM_END);
1092  query.bindValue(":BOOKMARK", MARK_BOOKMARK);
1093  query.bindValue(":CUTLIST_START", MARK_CUT_START);
1094  query.bindValue(":CUTLIST_END", MARK_CUT_END);
1095 
1096  if (!query.exec())
1097  MythDB::DBError("Error in mythtranscode", query);
1098  }
1099 
1100  if (newSize)
1101  pginfo->SaveFilesize(newSize);
1102 
1103  if (jobID >= 0)
1104  JobQueue::ChangeJobStatus(jobID, JOB_FINISHED);
1105  }
1106  else
1107  {
1108  // Not a successful run, so remove the files we created
1109  QString filename_tmp = filename + ".tmp";
1110  QByteArray fname_tmp = filename_tmp.toLocal8Bit();
1111  LOG(VB_GENERAL, LOG_NOTICE, QString("Deleting %1").arg(filename_tmp));
1112  transUnlink(fname_tmp.constData(), pginfo);
1113 
1114  QString filename_map = filename + ".tmp.map";
1115  QByteArray fname_map = filename_map.toLocal8Bit();
1116  unlink(fname_map.constData());
1117 
1118  if (jobID >= 0)
1119  {
1120  if (status == JOB_ABORTING) // Stop command was sent
1121  {
1122  JobQueue::ChangeJobStatus(jobID, JOB_ABORTED,
1123  QObject::tr("Job Aborted"));
1124  }
1125  else if (status != JOB_ERRORING) // Recoverable error
1126  {
1127  exitCode = GENERIC_EXIT_RESTART;
1128  }
1129  else // Unrecoverable error
1130  {
1131  JobQueue::ChangeJobStatus(jobID, JOB_ERRORED,
1132  QObject::tr("Unrecoverable error"));
1133  }
1134  }
1135  }
1136 }
1137 /* vim: set expandtab tabstop=4 shiftwidth=4: */
HTTPLiveStream::GetSourceFile
QString GetSourceFile(void) const
Definition: httplivestream.h:42
JobQueue::GetJobCmd
static enum JobCmds GetJobCmd(int jobID)
Definition: jobqueue.cpp:1458
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:811
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
ProgramInfo::SaveFilesize
virtual void SaveFilesize(uint64_t fsize)
Sets recording file size in database, and sets "filesize" field.
Definition: programinfo.cpp:6314
mpeg2fix.h
MARK_KEYFRAME
@ MARK_KEYFRAME
Definition: programtypes.h:62
COMM_FLAG_NOT_FLAGGED
@ COMM_FLAG_NOT_FLAGGED
Definition: programtypes.h:121
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
MARK_COMM_END
@ MARK_COMM_END
Definition: programtypes.h:60
ProgramInfo::SaveBookmark
void SaveBookmark(uint64_t frame)
Clears any existing bookmark in DB and if frame is greater than 0 sets a new bookmark.
Definition: programinfo.cpp:2677
ProgramInfo::GetHostname
QString GetHostname(void) const
Definition: programinfo.h:421
mythdb.h
ProgramInfo::QueryBasename
QString QueryBasename(void) const
Gets the basename, from the DB if necessary.
Definition: programinfo.cpp:2508
JobQueue::QueueJob
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
Definition: jobqueue.cpp:513
cmdline
MythCommFlagCommandLineParser cmdline
Definition: mythcommflag.cpp:72
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:35
MARK_CUT_END
@ MARK_CUT_END
Definition: programtypes.h:55
ProgramInfo::GetRecordingID
uint GetRecordingID(void) const
Definition: programinfo.h:446
MythContext
Startup context for MythTV.
Definition: mythcontext.h:43
REENCODE_MPEG2TRANS
@ REENCODE_MPEG2TRANS
Definition: transcodedefs.h:5
JOB_USE_CUTLIST
@ JOB_USE_CUTLIST
Definition: jobqueue.h:62
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
frm_dir_map_t
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:118
JOB_NONE
@ JOB_NONE
Definition: jobqueue.h:77
getSymlinkTarget
QString getSymlinkTarget(const QString &start_file, QStringList *intermediaries, unsigned maxLinks)
Definition: mythmiscutil.cpp:451
JobQueue::GetJobInfoFromID
static bool GetJobInfoFromID(int jobID, int &jobType, uint &chanid, QDateTime &recstartts)
Definition: jobqueue.cpp:664
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:617
RecordingFile
Holds information on a recording file and it's video and audio streams.
Definition: recordingfile.h:29
ProgramInfo::SaveCommFlagged
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
Definition: programinfo.cpp:3341
quiet
int quiet
Definition: mythcommflag.cpp:68
BuildKeyframeIndex
static int BuildKeyframeIndex(MPEG2fixup *m2f, const QString &infile, frm_pos_map_t &posMap, frm_pos_map_t &durMap, int jobID)
Definition: mythtranscode.cpp:73
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MPEG2fixup::BuildKeyframeIndex
int BuildKeyframeIndex(const QString &file, frm_pos_map_t &posMap, frm_pos_map_t &durMap)
Definition: mpeg2fix.cpp:2872
httplivestream.h
REPLEX_HDTV
#define REPLEX_HDTV
Definition: multiplex.h:45
JOB_STOP
@ JOB_STOP
Definition: jobqueue.h:56
GENERIC_EXIT_OK
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:11
jobID
int jobID
Definition: mythcommflag.cpp:80
mythburn.FILE
int FILE
Definition: mythburn.py:139
ProgramInfo::GetRecordingStartTime
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:404
MYTH_APPNAME_MYTHTRANSCODE
static constexpr const char * MYTH_APPNAME_MYTHTRANSCODE
Definition: mythcorecontext.h:28
isVideo
static bool isVideo(const QString &mimeType)
Definition: newssite.cpp:298
RecordingInfo::ApplyTranscoderProfileChange
void ApplyTranscoderProfileChange(const QString &profile) const
Sets the transcoder profile for a recording.
Definition: recordinginfo.cpp:844
QueueTranscodeJob
static int QueueTranscodeJob(ProgramInfo *pginfo, const QString &profile, const QString &hostname, bool usecutlist)
Definition: mythtranscode.cpp:111
MPEG2fixup
Definition: mpeg2fix.h:122
MythCommandLineParser::Parse
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
Definition: mythcommandlineparser.cpp:1554
mythdate.h
RecordingInfo::GetRecordingFile
RecordingFile * GetRecordingFile() const
Definition: recordinginfo.h:282
programinfo.h
mythlogging.h
ReloadBookmark
static uint64_t ReloadBookmark(ProgramInfo *pginfo)
Definition: mythtranscode.cpp:813
GENERIC_EXIT_NO_MYTHCONTEXT
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:14
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:759
hardwareprofile.scan.profile
profile
Definition: scan.py:99
mythtranscode_commandlineparser.h
remotefile.h
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:74
JobQueue::ChangeJobComment
static bool ChangeJobComment(int jobID, const QString &comment="")
Definition: jobqueue.cpp:1009
MythCoreContext::GetBackendServerPort
int GetBackendServerPort(void)
Returns the locally defined backend control port.
Definition: mythcorecontext.cpp:1063
signalhandling.h
CheckJobQueue
static int CheckJobQueue()
Definition: mythtranscode.cpp:101
MARK_GOP_START
@ MARK_GOP_START
Definition: programtypes.h:61
JobQueue::GetJobArgs
static QString GetJobArgs(int jobID)
Definition: jobqueue.cpp:1479
ComputeNewBookmark
static uint64_t ComputeNewBookmark(uint64_t oldBookmark, frm_dir_map_t *deleteMap)
Definition: mythtranscode.cpp:775
REENCODE_CUTLIST_CHANGE
@ REENCODE_CUTLIST_CHANGE
Definition: transcodedefs.h:6
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:549
CleanupGuard
Definition: cleanupguard.h:6
RecordingFile::Save
bool Save()
Definition: recordingfile.cpp:55
JobQueue::GetJobStatus
static enum JobStatus GetJobStatus(int jobID)
Definition: jobqueue.cpp:1521
mark
Definition: lang.cpp:22
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
MSqlQuery::testDBConnection
static bool testDBConnection()
Checks DB connection + login (login info via Mythcontext)
Definition: mythdbcon.cpp:875
RecordingFile::m_containerFormat
AVContainer m_containerFormat
Definition: recordingfile.h:47
REPLEX_DVD
#define REPLEX_DVD
Definition: multiplex.h:44
ProgramInfo::QueryCutList
bool QueryCutList(frm_dir_map_t &delMap, bool loadAutosave=false) const
Definition: programinfo.cpp:3465
MARK_BOOKMARK
@ MARK_BOOKMARK
Definition: programtypes.h:57
MythCommandLineParser::PrintVersion
static void PrintVersion(void)
Print application version information.
Definition: mythcommandlineparser.cpp:1382
GENERIC_EXIT_NO_RECORDING_DATA
@ GENERIC_EXIT_NO_RECORDING_DATA
No program/recording data.
Definition: exitcodes.h:30
mythtranslation.h
MythCommandLineParser::toUInt
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2251
MythCommandLineParser::PrintHelp
void PrintHelp(void) const
Print command line option help.
Definition: mythcommandlineparser.cpp:1398
jobqueue.h
recorderOptions
static QString recorderOptions
Definition: mythtranscode.cpp:40
REENCODE_OK
@ REENCODE_OK
Definition: transcodedefs.h:7
ProgramInfo::SaveBasename
bool SaveBasename(const QString &basename)
Sets a recording's basename in the database.
Definition: programinfo.cpp:2468
RemoteFile::DeleteFile
static bool DeleteFile(const QString &url)
Definition: remotefile.cpp:411
uint
unsigned int uint
Definition: compat.h:81
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythCommandLineParser::ApplySettingsOverride
void ApplySettingsOverride(void)
Apply all overrides to the global context.
Definition: mythcommandlineparser.cpp:2932
GENERIC_EXIT_REMOTE_FILE
@ GENERIC_EXIT_REMOTE_FILE
Can't transcode a remote file.
Definition: exitcodes.h:31
ProgramInfo::GetStorageGroup
QString GetStorageGroup(void) const
Definition: programinfo.h:422
cleanup
static QString cleanup(const QString &str)
Definition: remoteencoder.cpp:673
frm_pos_map_t
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:45
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:905
CompleteJob
static void CompleteJob(int jobID, ProgramInfo *pginfo, bool useCutlist, frm_dir_map_t *deleteMap, int &exitCode, int resultCode, bool forceDelete)
Definition: mythtranscode.cpp:869
SignalHandler::Init
static void Init(QObject *parent=nullptr)
Definition: signalhandling.cpp:127
MARK_CUT_START
@ MARK_CUT_START
Definition: programtypes.h:56
recordinginfo.h
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:372
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
mythmiscutil.h
MythCommandLineParser::toString
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2359
HTTPLiveStream
Definition: httplivestream.h:22
transcode.h
MythCommandLineParser::toBool
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
Definition: mythcommandlineparser.cpp:2202
REPLEX_MPEG2
#define REPLEX_MPEG2
Definition: multiplex.h:43
MARK_GOP_BYFRAME
@ MARK_GOP_BYFRAME
Definition: programtypes.h:64
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3809
cleanupguard.h
REPLEX_TS_SD
#define REPLEX_TS_SD
Definition: multiplex.h:46
glbl_jobID
static int glbl_jobID
Definition: mythtranscode.cpp:39
transUnlink
static int transUnlink(const QString &filename, ProgramInfo *pginfo)
Definition: mythtranscode.cpp:752
ProgramInfo::GetPlaybackURL
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
Definition: programinfo.cpp:2546
formatNUV
@ formatNUV
Definition: recordingfile.h:15
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:887
MarkTypes
MarkTypes
Definition: programtypes.h:47
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
MythCommandLineParser::toStringList
QStringList toStringList(const QString &key, const QString &sep="") const
Returns stored QVariant as a QStringList, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2390
main
int main(int argc, char *argv[])
Definition: mythtranscode.cpp:148
UpdateJobQueue
static void UpdateJobQueue(float percent_done)
Definition: mythtranscode.cpp:94
GENERIC_EXIT_RESTART
@ GENERIC_EXIT_RESTART
Need to restart transcoding.
Definition: exitcodes.h:32
mythcontext.h
UpdatePositionMap
static void UpdatePositionMap(frm_pos_map_t &posMap, frm_pos_map_t &durMap, const QString &mapfile, ProgramInfo *pginfo)
Definition: mythtranscode.cpp:42
GENERIC_EXIT_KILLED
@ GENERIC_EXIT_KILLED
Process killed or stopped.
Definition: exitcodes.h:24
REENCODE_STOPPED
@ REENCODE_STOPPED
Definition: transcodedefs.h:9
MythCommandLineParser::ConfigureLogging
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
Definition: mythcommandlineparser.cpp:2864
JobQueue::ChangeJobArgs
static bool ChangeJobArgs(int jobID, const QString &args="")
Definition: jobqueue.cpp:1034
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
MythTranscodeCommandLineParser
Definition: mythtranscode_commandlineparser.h:6
MythTranslation::load
static void load(const QString &module_name)
Load a QTranslator for the user's preferred language.
Definition: mythtranslation.cpp:37
exitcodes.h
MythCommandLineParser::toInt
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2224
JobQueue::ChangeJobStatus
static bool ChangeJobStatus(int jobID, int newStatus, const QString &comment="")
Definition: jobqueue.cpp:982
WaitToDelete
static void WaitToDelete(ProgramInfo *pginfo)
Definition: mythtranscode.cpp:831
build_compdb.filename
filename
Definition: build_compdb.py:21
GENERIC_EXIT_INVALID_CMDLINE
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:16
MARK_COMM_START
@ MARK_COMM_START
Definition: programtypes.h:59
JOB_TRANSCODE
@ JOB_TRANSCODE
Definition: jobqueue.h:80
MythCommandLineParser::toDateTime
QDateTime toDateTime(const QString &key) const
Returns stored QVariant as a QDateTime, falling to default if not provided.
Definition: mythcommandlineparser.cpp:2456
gContext
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:64
MythContext::Init
bool Init(bool gui=true, bool promptForBackend=false, bool disableAutoDiscovery=false, bool ignoreDB=false)
Definition: mythcontext.cpp:1603
formatMPEG2_PS
@ formatMPEG2_PS
Definition: recordingfile.h:17
SignalHandler::Done
static void Done(void)
Definition: signalhandling.cpp:134
formatMPEG2_TS
@ formatMPEG2_TS
Definition: recordingfile.h:16
ProgramInfo::SavePositionMap
void SavePositionMap(frm_pos_map_t &posMap, MarkTypes type, int64_t min_frame=-1, int64_t max_frame=-1) const
Definition: programinfo.cpp:3847
Transcode
Definition: transcode.h:17
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:836
GENERIC_EXIT_DB_ERROR
@ GENERIC_EXIT_DB_ERROR
Database error.
Definition: exitcodes.h:18