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