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