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