MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
transcode.cpp
Go to the documentation of this file.
1 #include <fcntl.h>
2 #include <math.h>
3 #include <iostream>
4 
5 #include <QStringList>
6 #include <QMap>
7 #include <QRegExp>
8 #include <QList>
9 #include <QWaitCondition>
10 #include <QMutex>
11 #include <QMutexLocker>
12 #include <QtAlgorithms>
13 
14 #include "mythconfig.h"
15 
16 #include "transcode.h"
17 #include "audiooutput.h"
18 #include "recordingprofile.h"
19 #include "mythcorecontext.h"
20 #include "jobqueue.h"
21 #include "exitcodes.h"
22 #include "mthreadpool.h"
23 #include "deletemap.h"
24 #include "tvremoteutil.h"
25 
26 #include "NuppelVideoRecorder.h"
27 #include "mythplayer.h"
28 #include "programinfo.h"
29 #include "mythdbcon.h"
30 #include "avformatwriter.h"
31 #include "HLS/httplivestream.h"
32 
33 #include "videodecodebuffer.h"
34 #include "cutter.h"
35 #include "audioreencodebuffer.h"
36 
37 extern "C" {
38 #include "libavcodec/avcodec.h"
39 #include "libswscale/swscale.h"
40 }
41 
42 using namespace std;
43 
44 #define LOC QString("Transcode: ")
45 
47  m_proginfo(pginfo),
48  keyframedist(30),
49  nvr(NULL),
50  ctx(NULL),
51  outRingBuffer(NULL),
52  fifow(NULL),
53  kfa_table(NULL),
54  showprogress(false),
55  recorderOptions(""),
56  avfMode(false),
57  hlsMode(false), hlsStreamID(-1),
58  hlsDisableAudioOnly(false),
59  hlsMaxSegments(0),
60  cmdContainer("mpegts"), cmdAudioCodec("aac"),
61  cmdVideoCodec("libx264"),
62  cmdWidth(480), cmdHeight(0),
63  cmdBitrate(600000), cmdAudioBitrate(64000)
64 {
65 }
66 
68 {
69  if (nvr)
70  delete nvr;
71  SetPlayerContext(NULL);
72  if (outRingBuffer)
73  delete outRingBuffer;
74  if (fifow)
75  delete fifow;
76  if (kfa_table)
77  delete kfa_table;
78 }
79 void Transcode::ReencoderAddKFA(long curframe, long lastkey, long num_keyframes)
80 {
81  long delta = curframe - lastkey;
82  if (delta != 0 && delta != keyframedist)
83  {
84  struct kfatable_entry kfate;
85  kfate.adjust = keyframedist - delta;
86  kfate.keyframe_number = num_keyframes;
87  kfa_table->push_back(kfate);
88  }
89 }
90 
91 bool Transcode::GetProfile(QString profileName, QString encodingType,
92  int height, int frameRate)
93 {
94  if (profileName.toLower() == "autodetect")
95  {
96  if (height == 1088)
97  height = 1080;
98 
99  QString autoProfileName = QObject::tr("Autodetect from %1").arg(height);
100  if (frameRate == 25 || frameRate == 30)
101  autoProfileName += "i";
102  if (frameRate == 50 || frameRate == 60)
103  autoProfileName += "p";
104 
105  bool result = false;
106  LOG(VB_GENERAL, LOG_NOTICE,
107  QString("Transcode: Looking for autodetect profile: %1")
108  .arg(autoProfileName));
109  result = profile.loadByGroup(autoProfileName, "Transcoders");
110 
111  if (!result && encodingType == "MPEG-2")
112  {
113  result = profile.loadByGroup("MPEG2", "Transcoders");
114  autoProfileName = "MPEG2";
115  }
116  if (!result && (encodingType == "MPEG-4" || encodingType == "RTjpeg"))
117  {
118  result = profile.loadByGroup("RTjpeg/MPEG4",
119  "Transcoders");
120  autoProfileName = "RTjpeg/MPEG4";
121  }
122  if (!result)
123  {
124  LOG(VB_GENERAL, LOG_ERR,
125  QString("Transcode: Couldn't find profile for : %1")
126  .arg(encodingType));
127 
128  return false;
129  }
130 
131  LOG(VB_GENERAL, LOG_NOTICE,
132  QString("Transcode: Using autodetect profile: %1")
133  .arg(autoProfileName));
134  }
135  else
136  {
137  bool isNum;
138  int profileID;
139  profileID = profileName.toInt(&isNum);
140  // If a bad profile is specified, there will be trouble
141  if (isNum && profileID > 0)
142  profile.loadByID(profileID);
143  else if (!profile.loadByGroup(profileName, "Transcoders"))
144  {
145  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find profile #: %1")
146  .arg(profileName));
147  return false;
148  }
149  }
150  return true;
151 }
152 
154 {
155  if (player_ctx == ctx)
156  return;
157 
158  delete ctx;
159  ctx = player_ctx;
160 }
161 
162 static QString get_str_option(RecordingProfile &profile, const QString &name)
163 {
164  const Setting *setting = profile.byName(name);
165  if (setting)
166  return setting->getValue();
167 
168  LOG(VB_GENERAL, LOG_ERR, LOC +
169  QString("get_str_option(...%1): Option not in profile.").arg(name));
170 
171  return QString::null;
172 }
173 
174 static int get_int_option(RecordingProfile &profile, const QString &name)
175 {
176  QString ret_str = get_str_option(profile, name);
177  if (ret_str.isEmpty())
178  return 0;
179 
180  bool ok = false;
181  int ret_int = ret_str.toInt(&ok);
182 
183  if (!ok)
184  {
185  LOG(VB_GENERAL, LOG_ERR, LOC +
186  QString("get_int_option(...%1): Option is not an int.").arg(name));
187  }
188 
189  return ret_int;
190 }
191 
192 static void TranscodeWriteText(void *ptr, unsigned char *buf, int len,
193  int timecode, int pagenr)
194 {
196  nvr->WriteText(buf, len, timecode, pagenr);
197 }
198 
199 int Transcode::TranscodeFile(const QString &inputname,
200  const QString &outputname,
201  const QString &profileName,
202  bool honorCutList, bool framecontrol,
203  int jobID, QString fifodir,
204  bool fifo_info, bool cleanCut,
205  frm_dir_map_t &deleteMap,
206  int AudioTrackNo,
207  bool passthru)
208 {
209  QDateTime curtime = MythDate::current();
210  QDateTime statustime = curtime;
211  int audioFrame = 0;
212  Cutter *cutter = NULL;
213  AVFormatWriter *avfw = NULL;
214  AVFormatWriter *avfw2 = NULL;
215  HTTPLiveStream *hls = NULL;
216  int hlsSegmentSize = 0;
217  int hlsSegmentFrames = 0;
218 
219  if (jobID >= 0)
220  JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed"));
221 
222  if (hlsMode)
223  {
224  avfMode = true;
225 
226  if (hlsStreamID != -1)
227  {
228  hls = new HTTPLiveStream(hlsStreamID);
230  hls->UpdateStatusMessage("Transcoding Starting");
231  cmdWidth = hls->GetWidth();
232  cmdHeight = hls->GetHeight();
233  cmdBitrate = hls->GetBitrate();
235  }
236  }
237 
238  if (!avfMode)
239  {
240  nvr = new NuppelVideoRecorder(NULL, NULL);
241 
242  if (!nvr)
243  {
244  LOG(VB_GENERAL, LOG_ERR,
245  "Transcoding aborted, error creating NuppelVideoRecorder.");
246  return REENCODE_ERROR;
247  }
248  }
249 
250  // Input setup
252  player_ctx->SetPlayingInfo(m_proginfo);
253  player_ctx->SetRingBuffer((hls && (hlsStreamID != -1)) ?
254  RingBuffer::Create(hls->GetSourceFile(), false, false) :
255  RingBuffer::Create(inputname, false, false));
256  player_ctx->SetPlayer(new MythPlayer(kVideoIsNull));
257  SetPlayerContext(player_ctx);
258  GetPlayer()->SetPlayerInfo(NULL, NULL, GetPlayerContext());
259  if (m_proginfo->GetRecordingEndTime() > curtime)
260  {
263  }
264 
265  if (showprogress)
266  {
267  statustime = statustime.addSecs(5);
268  }
269 
270  AudioOutput *audioOutput = new AudioReencodeBuffer(FORMAT_NONE, 0,
271  passthru);
272  AudioReencodeBuffer *arb = ((AudioReencodeBuffer*)audioOutput);
273  GetPlayer()->GetAudio()->SetAudioOutput(audioOutput);
274  GetPlayer()->SetTranscoding(true);
275 
276  if (GetPlayer()->OpenFile() < 0)
277  {
278  LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, error opening file.");
279  SetPlayerContext(NULL);
280  return REENCODE_ERROR;
281  }
282 
283  if (AudioTrackNo > -1)
284  {
285  LOG(VB_GENERAL, LOG_INFO,
286  QString("Set audiotrack number to %1").arg(AudioTrackNo));
287  GetPlayer()->GetDecoder()->SetTrack(kTrackTypeAudio, AudioTrackNo);
288  }
289 
290  long long total_frame_count = GetPlayer()->GetTotalFrameCount();
291  long long new_frame_count = total_frame_count;
292  if (honorCutList && m_proginfo)
293  {
294  LOG(VB_GENERAL, LOG_INFO, "Honoring the cutlist while transcoding");
295 
296  frm_dir_map_t::const_iterator it;
297  QString cutStr;
298  long long lastStart = 0;
299 
300  if (deleteMap.size() == 0)
301  m_proginfo->QueryCutList(deleteMap);
302 
303  for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
304  {
305  if (*it)
306  {
307  if (!cutStr.isEmpty())
308  cutStr += ",";
309  cutStr += QString("%1-").arg((long)it.key());
310  lastStart = it.key();
311  }
312  else
313  {
314  if (cutStr.isEmpty())
315  cutStr += "0-";
316  cutStr += QString("%1").arg((long)it.key());
317  new_frame_count -= (it.key() - lastStart);
318  }
319  }
320  if (cutStr.isEmpty())
321  cutStr = "Is Empty";
322  else if (cutStr.endsWith('-') && (total_frame_count > lastStart))
323  {
324  new_frame_count -= (total_frame_count - lastStart);
325  cutStr += QString("%1").arg(total_frame_count);
326  }
327  LOG(VB_GENERAL, LOG_INFO, QString("Cutlist : %1").arg(cutStr));
328  LOG(VB_GENERAL, LOG_INFO, QString("Original Length: %1 frames")
329  .arg((long)total_frame_count));
330  LOG(VB_GENERAL, LOG_INFO, QString("New Length : %1 frames")
331  .arg((long)new_frame_count));
332 
333  if ((m_proginfo->QueryIsEditing()) ||
335  {
336  LOG(VB_GENERAL, LOG_INFO, "Transcoding aborted, cutlist changed");
337  SetPlayerContext(NULL);
338  return REENCODE_CUTLIST_CHANGE;
339  }
341  curtime = curtime.addSecs(60);
342  }
343 
345  QString encodingType = GetPlayer()->GetEncodingType();
346  bool copyvideo = false, copyaudio = false;
347 
348  QString vidsetting = NULL, audsetting = NULL, vidfilters = NULL;
349 
350  QSize buf_size = GetPlayer()->GetVideoBufferSize();
351  int video_width = buf_size.width();
352  int video_height = buf_size.height();
353 
354  if (video_height == 1088) {
355  LOG(VB_GENERAL, LOG_NOTICE,
356  "Found video height of 1088. This is unusual and "
357  "more than likely the video is actually 1080 so mythtranscode "
358  "will treat it as such.");
359  }
360 
361  DecoderBase* dec = GetPlayer()->GetDecoder();
362  float video_aspect = dec ? dec->GetVideoAspect() : 4.0f / 3.0f;
363  float video_frame_rate = GetPlayer()->GetFrameRate();
364  int newWidth = video_width;
365  int newHeight = video_height;
366  bool halfFramerate = false;
367  bool skippedLastFrame = false;
368 
369  kfa_table = new vector<struct kfatable_entry>;
370 
371  if (avfMode)
372  {
373  newWidth = cmdWidth;
374  newHeight = cmdHeight;
375 
376  // TODO: is this necessary? It got commented out, but may still be
377  // needed.
378  // int actualHeight = (video_height == 1088 ? 1080 : video_height);
379 
380  // If height or width are 0, then we need to calculate them
381  if (newHeight == 0 && newWidth > 0)
382  newHeight = (int)(1.0 * newWidth / video_aspect);
383  else if (newWidth == 0 && newHeight > 0)
384  newWidth = (int)(1.0 * newHeight * video_aspect);
385  else if (newWidth == 0 && newHeight == 0)
386  {
387  newHeight = 480;
388  newWidth = (int)(1.0 * 480 * video_aspect);
389  if (newWidth > 640)
390  {
391  newWidth = 640;
392  newHeight = (int)(1.0 * 640 / video_aspect);
393  }
394  }
395 
396  // make sure dimensions are valid for MPEG codecs
397  newHeight = (newHeight + 15) & ~0xF;
398  newWidth = (newWidth + 15) & ~0xF;
399 
400  avfw = new AVFormatWriter();
401  if (!avfw)
402  {
403  LOG(VB_GENERAL, LOG_ERR,
404  "Transcoding aborted, error creating AVFormatWriter.");
405  SetPlayerContext(NULL);
406  return REENCODE_ERROR;
407  }
408 
410  avfw->SetHeight(newHeight);
411  avfw->SetWidth(newWidth);
412  avfw->SetAspect(video_aspect);
414  avfw->SetAudioChannels(arb->m_channels);
415  avfw->SetAudioBits(16);
417  avfw->SetAudioSampleBytes(2);
418 
419  if (hlsMode)
420  {
421  int segmentSize = 10;
422  int audioOnlyBitrate = 0;
423 
424  if (!hlsDisableAudioOnly)
425  {
426  audioOnlyBitrate = 48000;
427 
428  avfw2 = new AVFormatWriter();
429  if (!avfw2)
430  {
431  LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, error "
432  "creating low-bitrate AVFormatWriter.");
433  SetPlayerContext(NULL);
434  return REENCODE_ERROR;
435  }
436 
437  avfw2->SetContainer("mpegts");
438 
439  if (!gCoreContext->GetSetting("HLSAUDIO").isEmpty())
440  avfw2->SetAudioCodec(gCoreContext->GetSetting("HLSAUDIO"));
441  else
442 #if CONFIG_LIBFAAC_ENCODER
443  avfw2->SetAudioCodec("libfaac");
444 #else
445 # if CONFIG_LIBMP3LAME_ENCODER
446  avfw2->SetAudioCodec("libmp3lame");
447 # else
448  avfw2->SetAudioCodec("aac");
449 # endif
450 #endif
451 
452  avfw2->SetAudioBitrate(audioOnlyBitrate);
453  avfw2->SetAudioChannels(arb->m_channels);
454  avfw2->SetAudioBits(16);
455  avfw2->SetAudioSampleRate(arb->m_eff_audiorate);
456  avfw2->SetAudioSampleBytes(2);
457  }
458 
459  avfw->SetContainer("mpegts");
460  avfw->SetVideoCodec("libx264");
461 
462  if (!gCoreContext->GetSetting("HLSAUDIO").isEmpty())
463  avfw->SetAudioCodec(gCoreContext->GetSetting("HLSAUDIO"));
464  else
465 #if CONFIG_LIBFAAC_ENCODER
466  avfw->SetAudioCodec("libfaac");
467 #else
468 # if CONFIG_LIBMP3LAME_ENCODER
469  avfw->SetAudioCodec("libmp3lame");
470 # else
471  avfw->SetAudioCodec("aac");
472 # endif
473 #endif
474 
475  if (hlsStreamID == -1)
476  {
477  hls = new HTTPLiveStream(inputname, newWidth, newHeight,
478  cmdBitrate,
480  segmentSize, audioOnlyBitrate);
481 
482  hlsStreamID = hls->GetStreamID();
483  if (!hls || hlsStreamID == -1)
484  {
485  LOG(VB_GENERAL, LOG_ERR, "Unable to create new stream");
486  SetPlayerContext(NULL);
487  return REENCODE_ERROR;
488  }
489  }
490 
492  hls->UpdateStatusMessage("Transcoding Starting");
493  hls->UpdateSizeInfo(newWidth, newHeight, video_width, video_height);
494 
495  if (!hls->InitForWrite())
496  {
497  LOG(VB_GENERAL, LOG_ERR, "hls->InitForWrite() failed");
498  SetPlayerContext(NULL);
499  return REENCODE_ERROR;
500  }
501 
502  if (video_frame_rate > 30)
503  {
504  halfFramerate = true;
505  avfw->SetFramerate(video_frame_rate/2);
506 
507  if (avfw2)
508  avfw2->SetFramerate(video_frame_rate/2);
509 
510  hlsSegmentSize = (int)(segmentSize * video_frame_rate / 2);
511  }
512  else
513  {
514  avfw->SetFramerate(video_frame_rate);
515 
516  if (avfw2)
517  avfw2->SetFramerate(video_frame_rate);
518 
519  hlsSegmentSize = (int)(segmentSize * video_frame_rate);
520  }
521 
522  avfw->SetKeyFrameDist(30);
523  if (avfw2)
524  avfw2->SetKeyFrameDist(30);
525 
526  hls->AddSegment();
527  avfw->SetFilename(hls->GetCurrentFilename());
528  if (avfw2)
529  avfw2->SetFilename(hls->GetCurrentFilename(true));
530  }
531  else
532  {
533  avfw->SetContainer(cmdContainer);
536  avfw->SetFilename(outputname);
537  avfw->SetFramerate(video_frame_rate);
538  avfw->SetKeyFrameDist(30);
539  }
540 
541  avfw->SetThreadCount(
542  gCoreContext->GetNumSetting("HTTPLiveStreamThreads", 2));
543 
544  if (avfw2)
545  avfw2->SetThreadCount(1);
546 
547  if (!avfw->Init())
548  {
549  LOG(VB_GENERAL, LOG_ERR, "avfw->Init() failed");
550  SetPlayerContext(NULL);
551  return REENCODE_ERROR;
552  }
553 
554  if (!avfw->OpenFile())
555  {
556  LOG(VB_GENERAL, LOG_ERR, "avfw->OpenFile() failed");
557  SetPlayerContext(NULL);
558  return REENCODE_ERROR;
559  }
560 
561  if (avfw2 && !avfw2->Init())
562  {
563  LOG(VB_GENERAL, LOG_ERR, "avfw2->Init() failed");
564  SetPlayerContext(NULL);
565  return REENCODE_ERROR;
566  }
567 
568  if (avfw2 && !avfw2->OpenFile())
569  {
570  LOG(VB_GENERAL, LOG_ERR, "avfw2->OpenFile() failed");
571  SetPlayerContext(NULL);
572  return REENCODE_ERROR;
573  }
574 
575  arb->m_audioFrameSize = avfw->GetAudioFrameSize() * arb->m_channels * 2;
576 
578  gCoreContext->GetSetting("HTTPLiveStreamFilters"));
579  }
580  else if (fifodir.isEmpty())
581  {
582  if (!GetProfile(profileName, encodingType, video_height,
583  (int)round(video_frame_rate))) {
584  LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, no profile found.");
585  SetPlayerContext(NULL);
586  return REENCODE_ERROR;
587  }
588 
589  // For overriding settings on the command line
590  QMap<QString, QString> recorderOptionsMap;
591  if (!recorderOptions.isEmpty())
592  {
593  QStringList options = recorderOptions
594  .split(",", QString::SkipEmptyParts);
595  int loop = 0;
596  while (loop < options.size())
597  {
598  QStringList tokens = options[loop].split("=");
599  recorderOptionsMap[tokens[0]] = tokens[1];
600 
601  loop++;
602  }
603  }
604 
605  vidsetting = get_str_option(profile, "videocodec");
606  audsetting = get_str_option(profile, "audiocodec");
607  vidfilters = get_str_option(profile, "transcodefilters");
608 
609  if (encodingType == "MPEG-2" &&
610  get_int_option(profile, "transcodelossless"))
611  {
612  LOG(VB_GENERAL, LOG_NOTICE, "Switching to MPEG-2 transcoder.");
613  SetPlayerContext(NULL);
614  return REENCODE_MPEG2TRANS;
615  }
616 
617  // Recorder setup
618  if (get_int_option(profile, "transcodelossless"))
619  {
620  vidsetting = encodingType;
621  audsetting = "MP3";
622  }
623  else if (get_int_option(profile, "transcoderesize"))
624  {
625  int actualHeight = (video_height == 1088 ? 1080 : video_height);
626 
627  GetPlayer()->SetVideoFilters(vidfilters);
628  newWidth = get_int_option(profile, "width");
629  newHeight = get_int_option(profile, "height");
630 
631  // If height or width are 0, then we need to calculate them
632  if (newHeight == 0 && newWidth > 0)
633  newHeight = (int)(1.0 * newWidth * actualHeight / video_width);
634  else if (newWidth == 0 && newHeight > 0)
635  newWidth = (int)(1.0 * newHeight * video_width / actualHeight);
636  else if (newWidth == 0 && newHeight == 0)
637  {
638  newHeight = 480;
639  newWidth = (int)(1.0 * 480 * video_width / actualHeight);
640  if (newWidth > 640)
641  {
642  newWidth = 640;
643  newHeight = (int)(1.0 * 640 * actualHeight / video_width);
644  }
645  }
646 
647  if (encodingType.startsWith("mpeg", Qt::CaseInsensitive))
648  {
649  // make sure dimensions are valid for MPEG codecs
650  newHeight = (newHeight + 15) & ~0xF;
651  newWidth = (newWidth + 15) & ~0xF;
652  }
653 
654  LOG(VB_GENERAL, LOG_INFO, QString("Resizing from %1x%2 to %3x%4")
655  .arg(video_width).arg(video_height)
656  .arg(newWidth).arg(newHeight));
657  }
658  else // lossy and no resize
659  GetPlayer()->SetVideoFilters(vidfilters);
660 
661  // this is ripped from tv_rec SetupRecording. It'd be nice to merge
662  nvr->SetOption("inpixfmt", FMT_YV12);
663 
664  nvr->SetOption("width", newWidth);
665  nvr->SetOption("height", newHeight);
666 
667  nvr->SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
668  nvr->SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
669 
670  nvr->SetFrameRate(video_frame_rate);
671  nvr->SetVideoAspect(video_aspect);
672  nvr->SetTranscoding(true);
673 
674  if ((vidsetting == "MPEG-4") ||
675  (recorderOptionsMap["videocodec"] == "mpeg4"))
676  {
677  nvr->SetOption("videocodec", "mpeg4");
678 
679  nvr->SetIntOption(&profile, "mpeg4bitrate");
680  nvr->SetIntOption(&profile, "scalebitrate");
681  nvr->SetIntOption(&profile, "mpeg4maxquality");
682  nvr->SetIntOption(&profile, "mpeg4minquality");
683  nvr->SetIntOption(&profile, "mpeg4qualdiff");
684  nvr->SetIntOption(&profile, "mpeg4optionvhq");
685  nvr->SetIntOption(&profile, "mpeg4option4mv");
686 #ifdef USING_FFMPEG_THREADS
687  nvr->SetIntOption(profile, "encodingthreadcount");
688 #endif
689  }
690  else if ((vidsetting == "MPEG-2") ||
691  (recorderOptionsMap["videocodec"] == "mpeg2video"))
692  {
693  nvr->SetOption("videocodec", "mpeg2video");
694 
695  nvr->SetIntOption(&profile, "mpeg2bitrate");
696  nvr->SetIntOption(&profile, "scalebitrate");
697 #ifdef USING_FFMPEG_THREADS
698  nvr->SetIntOption(&profile, "encodingthreadcount");
699 #endif
700  }
701  else if ((vidsetting == "RTjpeg") ||
702  (recorderOptionsMap["videocodec"] == "rtjpeg"))
703  {
704  nvr->SetOption("videocodec", "rtjpeg");
705  nvr->SetIntOption(&profile, "rtjpegquality");
706  nvr->SetIntOption(&profile, "rtjpegchromafilter");
707  nvr->SetIntOption(&profile, "rtjpeglumafilter");
708  }
709  else if (vidsetting.isEmpty())
710  {
711  LOG(VB_GENERAL, LOG_ERR, "No video information found!");
712  LOG(VB_GENERAL, LOG_ERR, "Please ensure that recording profiles "
713  "for the transcoder are set");
714  SetPlayerContext(NULL);
715  return REENCODE_ERROR;
716  }
717  else
718  {
719  LOG(VB_GENERAL, LOG_ERR,
720  QString("Unknown video codec: %1").arg(vidsetting));
721  SetPlayerContext(NULL);
722  return REENCODE_ERROR;
723  }
724 
725  nvr->SetOption("samplerate", arb->m_eff_audiorate);
726  if (audsetting == "MP3")
727  {
728  nvr->SetOption("audiocompression", 1);
729  nvr->SetIntOption(&profile, "mp3quality");
730  copyaudio = true;
731  }
732  else if (audsetting == "Uncompressed")
733  {
734  nvr->SetOption("audiocompression", 0);
735  }
736  else
737  {
738  LOG(VB_GENERAL, LOG_ERR,
739  QString("Unknown audio codec: %1").arg(audsetting));
740  }
741 
742  nvr->AudioInit(true);
743 
744  // For overriding settings on the command line
745  if (recorderOptionsMap.size() > 0)
746  {
747  QMap<QString, QString>::Iterator it;
748  QString key, value;
749  for (it = recorderOptionsMap.begin();
750  it != recorderOptionsMap.end(); ++it)
751  {
752  key = it.key();
753  value = *it;
754 
755  LOG(VB_GENERAL, LOG_NOTICE,
756  QString("Forcing Recorder option '%1' to '%2'")
757  .arg(key).arg(value));
758 
759  if (value.contains(QRegExp("[^0-9]")))
760  nvr->SetOption(key, value);
761  else
762  nvr->SetOption(key, value.toInt());
763 
764  if (key == "width")
765  newWidth = (value.toInt() + 15) & ~0xF;
766  else if (key == "height")
767  newHeight = (value.toInt() + 15) & ~0xF;
768  else if (key == "videocodec")
769  {
770  if (value == "mpeg4")
771  vidsetting = "MPEG-4";
772  else if (value == "mpeg2video")
773  vidsetting = "MPEG-2";
774  else if (value == "rtjpeg")
775  vidsetting = "RTjpeg";
776  }
777  }
778  }
779 
780  if ((vidsetting == "MPEG-4") ||
781  (vidsetting == "MPEG-2"))
783  else if (vidsetting == "RTjpeg")
784  nvr->SetupRTjpeg();
785 
786  outRingBuffer = RingBuffer::Create(outputname, true, false);
788  nvr->WriteHeader();
789  nvr->StreamAllocate();
790  }
791 
792  if (vidsetting == encodingType && !framecontrol && !avfMode &&
793  fifodir.isEmpty() && honorCutList &&
794  video_width == newWidth && video_height == newHeight)
795  {
796  copyvideo = true;
797  LOG(VB_GENERAL, LOG_INFO, "Reencoding video in 'raw' mode");
798  }
799 
800  if (honorCutList && deleteMap.size() > 0)
801  {
802  if (cleanCut)
803  {
804  // Have the player seek only part of the way
805  // through a cut, and then use the cutter to
806  // discard the rest
807  cutter = new Cutter();
808  cutter->SetCutList(deleteMap, ctx);
809  GetPlayer()->SetCutList(cutter->AdjustedCutList());
810  }
811  else
812  {
813  // Have the player apply the cut list
814  GetPlayer()->SetCutList(deleteMap);
815  }
816  }
817 
818  GetPlayer()->InitForTranscode(copyaudio, copyvideo);
819  if (GetPlayer()->IsErrored())
820  {
821  LOG(VB_GENERAL, LOG_ERR,
822  "Unable to initialize MythPlayer for Transcode");
823  SetPlayerContext(NULL);
824  return REENCODE_ERROR;
825  }
826 
827  int vidSize = 0;
828 
829  // 1920x1080 video is actually 1920x1088 because of the 16x16 blocks so
830  // we have to fudge the output size here. nuvexport knows how to handle
831  // this and as of right now it is the only app that uses the fifo ability.
832  if (video_height == 1080 && video_width == 1920)
833  vidSize = (1088 * 1920) * 3 / 2;
834  else
835  vidSize = (video_height * video_width) * 3 / 2;
836 
837  VideoFrame frame;
838  frame.codec = FMT_YV12;
839  frame.width = newWidth;
840  frame.height = newHeight;
841  frame.size = newWidth * newHeight * 3 / 2;
842 
843  if (!fifodir.isEmpty())
844  {
845  AudioPlayer *aplayer = GetPlayer()->GetAudio();
846  const char *audio_codec_name;
847 
848  switch(aplayer->GetCodec())
849  {
850  case AV_CODEC_ID_AC3:
851  audio_codec_name = "ac3";
852  break;
853  case AV_CODEC_ID_EAC3:
854  audio_codec_name = "eac3";
855  break;
856  case AV_CODEC_ID_DTS:
857  audio_codec_name = "dts";
858  break;
859  case AV_CODEC_ID_TRUEHD:
860  audio_codec_name = "truehd";
861  break;
862  case AV_CODEC_ID_MP3:
863  audio_codec_name = "mp3";
864  break;
865  case AV_CODEC_ID_MP2:
866  audio_codec_name = "mp2";
867  break;
868  case AV_CODEC_ID_AAC:
869  audio_codec_name = "aac";
870  break;
871  case AV_CODEC_ID_AAC_LATM:
872  audio_codec_name = "aac_latm";
873  break;
874  default:
875  audio_codec_name = "unknown";
876  }
877 
878  if (!arb->m_passthru)
879  audio_codec_name = "raw";
880 
881  // If cutlist is used then get info on first uncut frame
882  if (honorCutList && fifo_info)
883  {
884  bool is_key;
885  int did_ff;
886  frm_dir_map_t::iterator dm_iter;
887  GetPlayer()->TranscodeGetNextFrame(dm_iter, did_ff, is_key, true);
888 
889  QSize buf_size = GetPlayer()->GetVideoBufferSize();
890  video_width = buf_size.width();
891  video_height = buf_size.height();
892  video_aspect = GetPlayer()->GetVideoAspect();
893  video_frame_rate = GetPlayer()->GetFrameRate();
894  }
895 
896  // Display details of the format of the fifo data.
897  LOG(VB_GENERAL, LOG_INFO,
898  QString("FifoVideoWidth %1").arg(video_width));
899  LOG(VB_GENERAL, LOG_INFO,
900  QString("FifoVideoHeight %1").arg(video_height));
901  LOG(VB_GENERAL, LOG_INFO,
902  QString("FifoVideoAspectRatio %1").arg(video_aspect));
903  LOG(VB_GENERAL, LOG_INFO,
904  QString("FifoVideoFrameRate %1").arg(video_frame_rate));
905  LOG(VB_GENERAL, LOG_INFO,
906  QString("FifoAudioFormat %1").arg(audio_codec_name));
907  LOG(VB_GENERAL, LOG_INFO,
908  QString("FifoAudioChannels %1").arg(arb->m_channels));
909  LOG(VB_GENERAL, LOG_INFO,
910  QString("FifoAudioSampleRate %1").arg(arb->m_eff_audiorate));
911 
912  if(fifo_info)
913  {
914  // Request was for just the format of fifo data, not for
915  // the actual transcode, so stop here.
916  unlink(outputname.toLocal8Bit().constData());
917  SetPlayerContext(NULL);
918  return REENCODE_OK;
919  }
920 
921  QString audfifo = fifodir + QString("/audout");
922  QString vidfifo = fifodir + QString("/vidout");
923  int audio_size = arb->m_eff_audiorate * arb->m_bytes_per_frame;
924  // framecontrol is true if we want to enforce fifo sync.
925  if (framecontrol)
926  LOG(VB_GENERAL, LOG_INFO, "Enforcing sync on fifos");
927  fifow = new FIFOWriter(2, framecontrol);
928 
929  if (!fifow->FIFOInit(0, QString("video"), vidfifo, vidSize, 50) ||
930  !fifow->FIFOInit(1, QString("audio"), audfifo, audio_size, 25))
931  {
932  LOG(VB_GENERAL, LOG_ERR,
933  "Error initializing fifo writer. Aborting");
934  unlink(outputname.toLocal8Bit().constData());
935  SetPlayerContext(NULL);
936  return REENCODE_ERROR;
937  }
938  LOG(VB_GENERAL, LOG_INFO,
939  QString("Video %1x%2@%3fps Audio rate: %4")
940  .arg(video_width).arg(video_height)
941  .arg(video_frame_rate)
942  .arg(arb->m_eff_audiorate));
943  LOG(VB_GENERAL, LOG_INFO, "Created fifos. Waiting for connection.");
944  }
945 
946  bool forceKeyFrames = (fifow == NULL) ? framecontrol : false;
947 
948  frm_dir_map_t::iterator dm_iter;
949  bool writekeyframe = true;
950 
951  int num_keyframes = 0;
952 
953  int did_ff = 0;
954 
955  long curFrameNum = 0;
956  frame.frameNumber = 1;
957  long lastKeyFrame = 0;
958  long totalAudio = 0;
959  int dropvideo = 0;
960  long long lasttimecode = 0;
961  long long lastWrittenTime = 0;
962  long long timecodeOffset = 0;
963 
964  float rateTimeConv = arb->m_eff_audiorate / 1000.0f;
965  float vidFrameTime = 1000.0f / video_frame_rate;
966  int wait_recover = 0;
967  VideoOutput *videoOutput = GetPlayer()->GetVideoOutput();
968  bool is_key = 0;
969  bool first_loop = true;
970  unsigned char *newFrame = (unsigned char *)av_malloc(frame.size);
971  frame.buf = newFrame;
972  AVPicture imageIn, imageOut;
973  struct SwsContext *scontext = NULL;
974 
975  if (fifow)
976  LOG(VB_GENERAL, LOG_INFO, "Dumping Video and Audio data to fifos");
977  else if (copyaudio)
978  LOG(VB_GENERAL, LOG_INFO, "Copying Audio while transcoding Video");
979  else if (hlsMode)
980  LOG(VB_GENERAL, LOG_INFO, "Transcoding for HTTP Live Streaming");
981  else if (avfMode)
982  LOG(VB_GENERAL, LOG_INFO, "Transcoding to libavformat container");
983  else
984  LOG(VB_GENERAL, LOG_INFO, "Transcoding Video and Audio");
985 
986  VideoDecodeBuffer *videoBuffer =
987  new VideoDecodeBuffer(GetPlayer(), videoOutput, honorCutList);
988  MThreadPool::globalInstance()->start(videoBuffer, "VideoDecodeBuffer");
989 
990  QTime flagTime;
991  flagTime.start();
992 
993  if (cutter)
994  cutter->Activate(vidFrameTime * rateTimeConv, total_frame_count);
995 
996  bool stopSignalled = false;
997  VideoFrame *lastDecode = NULL;
998 
999  if (hls)
1000  {
1002  hls->UpdateStatusMessage("Transcoding");
1003  }
1004 
1005  while ((!stopSignalled) &&
1006  (lastDecode = videoBuffer->GetFrame(did_ff, is_key)))
1007  {
1008  if (first_loop)
1009  {
1010  copyaudio = GetPlayer()->GetRawAudioState();
1011  first_loop = false;
1012  }
1013 
1014  float new_aspect = lastDecode->aspect;
1015 
1016  if (cutter)
1017  cutter->NewFrame(lastDecode->frameNumber);
1018 
1019  frame.timecode = lastDecode->timecode;
1020 
1021  if (frame.timecode < lasttimecode)
1022  frame.timecode = (long long)(lasttimecode + vidFrameTime);
1023 
1024  if (fifow)
1025  {
1026  frame.buf = lastDecode->buf;
1027  totalAudio += arb->GetSamples(frame.timecode);
1028  int audbufTime = (int)(totalAudio / rateTimeConv);
1029  int auddelta = frame.timecode - audbufTime;
1030  int vidTime = (int)(curFrameNum * vidFrameTime + 0.5);
1031  int viddelta = frame.timecode - vidTime;
1032  int delta = viddelta - auddelta;
1033  int absdelta = delta < 0 ? -delta : delta;
1034  if (absdelta < 500 && absdelta >= vidFrameTime)
1035  {
1036  QString msg = QString("Audio is %1ms %2 video at # %3: "
1037  "auddelta=%4, viddelta=%5")
1038  .arg(absdelta)
1039  .arg(((delta > 0) ? "ahead of" : "behind"))
1040  .arg((int)curFrameNum)
1041  .arg(auddelta)
1042  .arg(viddelta);
1043  LOG(VB_GENERAL, LOG_INFO, msg);
1044  dropvideo = (delta > 0) ? 1 : -1;
1045  wait_recover = 0;
1046  }
1047  else if (delta >= 500 && delta < 10000)
1048  {
1049  if (wait_recover == 0)
1050  {
1051  dropvideo = 5;
1052  wait_recover = 6;
1053  }
1054  else if (wait_recover == 1)
1055  {
1056  // Video is badly lagging. Try to catch up.
1057  int count = 0;
1058  while (delta > vidFrameTime)
1059  {
1060  if (!cutter || !cutter->InhibitDummyFrame())
1061  fifow->FIFOWrite(0, frame.buf, vidSize);
1062 
1063  count++;
1064  delta -= (int)vidFrameTime;
1065  }
1066  QString msg = QString("Added %1 blank video frames")
1067  .arg(count);
1068  LOG(VB_GENERAL, LOG_INFO, msg);
1069  curFrameNum += count;
1070  dropvideo = 0;
1071  wait_recover = 0;
1072  }
1073  else
1074  wait_recover--;
1075  }
1076  else
1077  {
1078  dropvideo = 0;
1079  wait_recover = 0;
1080  }
1081 
1082 #if 0
1083  int buflen = (int)(arb->audiobuffer_len / rateTimeConv);
1084  LOG(VB_GENERAL, LOG_DEBUG,
1085  QString("%1: video time: %2 audio time: %3 "
1086  "buf: %4 exp: %5 delta: %6")
1087  .arg(curFrameNum) .arg(frame.timecode)
1088  .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime)
1089  .arg(delta));
1090 #endif
1091  AudioBuffer *ab = NULL;
1092  while ((ab = arb->GetData(frame.timecode)) != NULL)
1093  {
1094  if (!cutter ||
1095  !cutter->InhibitUseAudioFrames(ab->m_frames, &totalAudio))
1096  fifow->FIFOWrite(1, ab->data(), ab->size());
1097 
1098  delete ab;
1099  }
1100 
1101  if (dropvideo < 0)
1102  {
1103  if (cutter && cutter->InhibitDropFrame())
1104  fifow->FIFOWrite(0, frame.buf, vidSize);
1105 
1106  LOG(VB_GENERAL, LOG_INFO, "Dropping video frame");
1107  dropvideo++;
1108  curFrameNum--;
1109  }
1110  else
1111  {
1112  if (!cutter || !cutter->InhibitUseVideoFrame())
1113  fifow->FIFOWrite(0, frame.buf, vidSize);
1114 
1115  if (dropvideo)
1116  {
1117  if (!cutter || !cutter->InhibitDummyFrame())
1118  fifow->FIFOWrite(0, frame.buf, vidSize);
1119 
1120  curFrameNum++;
1121  dropvideo--;
1122  }
1123  }
1124  videoOutput->DoneDisplayingFrame(lastDecode);
1126  lasttimecode = frame.timecode;
1127  }
1128  else if (copyaudio)
1129  {
1130  // Encoding from NuppelVideo to NuppelVideo with MP3 audio
1131  // So let's not decode/reencode audio
1132  if (!GetPlayer()->GetRawAudioState())
1133  {
1134  // The Raw state changed during decode. This is not good
1135  LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, MythPlayer "
1136  "is not in raw audio mode.");
1137 
1138  unlink(outputname.toLocal8Bit().constData());
1139  av_free(newFrame);
1140  SetPlayerContext(NULL);
1141  if (videoBuffer)
1142  videoBuffer->stop();
1143  return REENCODE_ERROR;
1144  }
1145 
1146  if (forceKeyFrames)
1147  writekeyframe = true;
1148  else
1149  {
1150  writekeyframe = is_key;
1151  if (writekeyframe)
1152  {
1153  // Currently, we don't create new sync frames,
1154  // (though we do create new 'I' frames), so we mark
1155  // the key-frames before deciding whether we need a
1156  // new 'I' frame.
1157 
1158  //need to correct the frame# and timecode here
1159  // Question: Is it necessary to change the timecodes?
1160  long sync_offset =
1161  GetPlayer()->UpdateStoredFrameNum(curFrameNum);
1162  nvr->UpdateSeekTable(num_keyframes, sync_offset);
1163  ReencoderAddKFA(curFrameNum, lastKeyFrame, num_keyframes);
1164  num_keyframes++;
1165  lastKeyFrame = curFrameNum;
1166 
1167  if (did_ff)
1168  did_ff = 0;
1169  }
1170  }
1171 
1172  if (did_ff == 1)
1173  {
1174  timecodeOffset +=
1175  (frame.timecode - lasttimecode - (int)vidFrameTime);
1176  }
1177  lasttimecode = frame.timecode;
1178  frame.timecode -= timecodeOffset;
1179 
1180  if (!GetPlayer()->WriteStoredData(
1181  outRingBuffer, (did_ff == 0), timecodeOffset))
1182  {
1183  if (video_aspect != new_aspect)
1184  {
1185  video_aspect = new_aspect;
1186  nvr->SetNewVideoParams(video_aspect);
1187  }
1188 
1189  QSize buf_size = GetPlayer()->GetVideoBufferSize();
1190 
1191  if (video_width != buf_size.width() ||
1192  video_height != buf_size.height())
1193  {
1194  video_width = buf_size.width();
1195  video_height = buf_size.height();
1196 
1197  LOG(VB_GENERAL, LOG_INFO,
1198  QString("Resizing from %1x%2 to %3x%4")
1199  .arg(video_width).arg(video_height)
1200  .arg(newWidth).arg(newHeight));
1201 
1202  }
1203 
1204  if (did_ff == 1)
1205  {
1206  // Create a new 'I' frame if we just processed a cut.
1207  did_ff = 2;
1208  writekeyframe = true;
1209  }
1210 
1211  if ((video_width == newWidth) && (video_height == newHeight))
1212  {
1213  frame.buf = lastDecode->buf;
1214  }
1215  else
1216  {
1217  frame.buf = newFrame;
1218  avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
1219  video_width, video_height);
1220  avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
1221  newWidth, newHeight);
1222 
1223  int bottomBand = (video_height == 1088) ? 8 : 0;
1224  scontext = sws_getCachedContext(scontext, video_width,
1225  video_height, PIX_FMT_YUV420P, newWidth,
1226  newHeight, PIX_FMT_YUV420P,
1227  SWS_FAST_BILINEAR, NULL, NULL, NULL);
1228 
1229  sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1230  video_height - bottomBand,
1231  imageOut.data, imageOut.linesize);
1232  }
1233 
1234  nvr->WriteVideo(&frame, true, writekeyframe);
1235  lastWrittenTime = frame.timecode;
1236  }
1238  }
1239  else
1240  {
1241  if (did_ff == 1)
1242  {
1243  did_ff = 2;
1244  timecodeOffset +=
1245  (frame.timecode - lasttimecode - (int)vidFrameTime);
1246  }
1247 
1248  if (video_aspect != new_aspect)
1249  {
1250  video_aspect = new_aspect;
1251  if (nvr)
1252  nvr->SetNewVideoParams(video_aspect);
1253  }
1254 
1255 
1256  QSize buf_size = GetPlayer()->GetVideoBufferSize();
1257 
1258  if (video_width != buf_size.width() ||
1259  video_height != buf_size.height())
1260  {
1261  video_width = buf_size.width();
1262  video_height = buf_size.height();
1263 
1264  LOG(VB_GENERAL, LOG_INFO,
1265  QString("Resizing from %1x%2 to %3x%4")
1266  .arg(video_width).arg(video_height)
1267  .arg(newWidth).arg(newHeight));
1268  }
1269 
1270  if ((video_width == newWidth) && (video_height == newHeight))
1271  {
1272  frame.buf = lastDecode->buf;
1273  }
1274  else
1275  {
1276  frame.buf = newFrame;
1277  avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
1278  video_width, video_height);
1279  avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
1280  newWidth, newHeight);
1281 
1282  int bottomBand = (video_height == 1088) ? 8 : 0;
1283  scontext = sws_getCachedContext(scontext, video_width,
1284  video_height, PIX_FMT_YUV420P, newWidth,
1285  newHeight, PIX_FMT_YUV420P,
1286  SWS_FAST_BILINEAR, NULL, NULL, NULL);
1287 
1288  sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1289  video_height - bottomBand,
1290  imageOut.data, imageOut.linesize);
1291  }
1292 
1293  // audio is fully decoded, so we need to reencode it
1294  AudioBuffer *ab = NULL;
1295  while ((ab = arb->GetData(lastWrittenTime)) != NULL)
1296  {
1297  unsigned char *buf = (unsigned char *)ab->data();
1298  if (avfMode)
1299  {
1300  if (did_ff != 1)
1301  {
1302  long long tc = ab->m_time - timecodeOffset;
1303  avfw->WriteAudioFrame(buf, audioFrame, tc);
1304 
1305  if (avfw2)
1306  {
1307  if ((avfw2->GetTimecodeOffset() == -1) &&
1308  (avfw->GetTimecodeOffset() != -1))
1309  {
1310  avfw2->SetTimecodeOffset(
1311  avfw->GetTimecodeOffset());
1312  }
1313 
1314  tc = ab->m_time - timecodeOffset;
1315  avfw2->WriteAudioFrame(buf, audioFrame, tc);
1316  }
1317 
1318  ++audioFrame;
1319  }
1320  }
1321  else
1322  {
1323  nvr->SetOption("audioframesize", ab->size());
1324  nvr->WriteAudio(buf, audioFrame++,
1325  ab->m_time - timecodeOffset);
1326  if (nvr->IsErrored())
1327  {
1328  LOG(VB_GENERAL, LOG_ERR,
1329  "Transcode: Encountered irrecoverable error in "
1330  "NVR::WriteAudio");
1331 
1332  av_free(newFrame);
1333  SetPlayerContext(NULL);
1334  if (videoBuffer)
1335  videoBuffer->stop();
1336  delete ab;
1337  return REENCODE_ERROR;
1338  }
1339  }
1340 
1341  delete ab;
1342  }
1343 
1344  if (!avfMode)
1345  {
1346  GetPlayer()->GetCC608Reader()->
1348  }
1349  lasttimecode = frame.timecode;
1350  frame.timecode -= timecodeOffset;
1351 
1352  if (avfMode)
1353  {
1354  if (halfFramerate && !skippedLastFrame)
1355  {
1356  skippedLastFrame = true;
1357  }
1358  else
1359  {
1360  skippedLastFrame = false;
1361 
1362  if ((hls) &&
1363  (avfw->GetFramesWritten()) &&
1364  (hlsSegmentFrames > hlsSegmentSize) &&
1365  (avfw->NextFrameIsKeyFrame()))
1366  {
1367  hls->AddSegment();
1368  avfw->ReOpen(hls->GetCurrentFilename());
1369 
1370  if (avfw2)
1371  avfw2->ReOpen(hls->GetCurrentFilename(true));
1372 
1373  hlsSegmentFrames = 0;
1374  }
1375 
1376  if (avfw->WriteVideoFrame(&frame) > 0)
1377  {
1378  lastWrittenTime = frame.timecode;
1379  if (hls)
1380  ++hlsSegmentFrames;
1381  }
1382 
1383  }
1384  }
1385  else
1386  {
1387  if (forceKeyFrames)
1388  nvr->WriteVideo(&frame, true, true);
1389  else
1390  nvr->WriteVideo(&frame);
1391  lastWrittenTime = frame.timecode;
1392  }
1393  }
1394  if (MythDate::current() > statustime)
1395  {
1396  if (showprogress)
1397  {
1398  LOG(VB_GENERAL, LOG_INFO,
1399  QString("Processed: %1 of %2 frames(%3 seconds)").
1400  arg((long)curFrameNum).arg((long)total_frame_count).
1401  arg((long)(curFrameNum / video_frame_rate)));
1402  }
1403 
1404  if (hls && hls->CheckStop())
1405  {
1407  stopSignalled = true;
1408  }
1409 
1410  statustime = MythDate::current().addSecs(5);
1411  }
1412  if (MythDate::current() > curtime)
1413  {
1414  if (honorCutList && m_proginfo && !avfMode &&
1416  {
1417  LOG(VB_GENERAL, LOG_NOTICE,
1418  "Transcoding aborted, cutlist updated");
1419 
1420  unlink(outputname.toLocal8Bit().constData());
1421  av_free(newFrame);
1422  SetPlayerContext(NULL);
1423  if (videoBuffer)
1424  videoBuffer->stop();
1425  return REENCODE_CUTLIST_CHANGE;
1426  }
1427 
1428  if ((jobID >= 0) || (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_INFO)))
1429  {
1430  if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
1431  {
1432  LOG(VB_GENERAL, LOG_NOTICE,
1433  "Transcoding STOPped by JobQueue");
1434 
1435  unlink(outputname.toLocal8Bit().constData());
1436  av_free(newFrame);
1437  SetPlayerContext(NULL);
1438  if (videoBuffer)
1439  videoBuffer->stop();
1440  return REENCODE_STOPPED;
1441  }
1442 
1443  float flagFPS = 0.0;
1444  float elapsed = flagTime.elapsed() / 1000.0;
1445  if (elapsed)
1446  flagFPS = curFrameNum / elapsed;
1447 
1448  total_frame_count = GetPlayer()->GetCurrentFrameCount();
1449  int percentage = curFrameNum * 100 / total_frame_count;
1450 
1451  if (hls)
1452  hls->UpdatePercentComplete(percentage);
1453 
1454  if (jobID >= 0)
1456  QObject::tr("%1% Completed @ %2 fps.")
1457  .arg(percentage).arg(flagFPS));
1458  else
1459  LOG(VB_GENERAL, LOG_INFO,
1460  QString("mythtranscode: %1% Completed @ %2 fps.")
1461  .arg(percentage).arg(flagFPS));
1462 
1463  }
1464  curtime = MythDate::current().addSecs(20);
1465  }
1466 
1467  curFrameNum++;
1468  frame.frameNumber = 1 + (curFrameNum << 1);
1469 
1470  GetPlayer()->DiscardVideoFrame(lastDecode);
1471  }
1472 
1473  sws_freeContext(scontext);
1474 
1475  if (!fifow)
1476  {
1477  if (avfw)
1478  avfw->CloseFile();
1479 
1480  if (avfw2)
1481  avfw2->CloseFile();
1482 
1483  if (!avfMode && m_proginfo)
1484  {
1489  }
1490 
1491  if (nvr)
1492  {
1493  nvr->WriteSeekTable();
1494  if (!kfa_table->empty())
1496  }
1497  } else {
1498  fifow->FIFODrain();
1499  }
1500 
1501  if (cutter)
1502  delete cutter;
1503 
1504  if (avfw)
1505  delete avfw;
1506 
1507  if (avfw2)
1508  delete avfw2;
1509 
1510  if (hls)
1511  {
1512  if (!stopSignalled)
1513  {
1515  hls->UpdateStatusMessage("Transcoding Completed");
1516  hls->UpdatePercentComplete(100);
1517  }
1518  else
1519  {
1521  hls->UpdateStatusMessage("Transcoding Stopped");
1522  }
1523  delete hls;
1524  }
1525 
1526  if (videoBuffer)
1527  {
1528  videoBuffer->stop();
1529  }
1530 
1531  av_free(newFrame);
1532  SetPlayerContext(NULL);
1533 
1534  return REENCODE_OK;
1535 }
1536 
1537 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1538