MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Modules 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 #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, PIX_FMT_YUV420P,
1309  video_width, video_height);
1310  avpicture_fill(&imageOut, frame.buf, 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, PIX_FMT_YUV420P, newWidth,
1316  newHeight, 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, PIX_FMT_YUV420P,
1367  video_width, video_height);
1368  avpicture_fill(&imageOut, frame.buf, 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, PIX_FMT_YUV420P, newWidth,
1374  newHeight, 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
long long GetFramesWritten(void) const
frm_dir_map_t AdjustedCutList() const
Definition: cutter.cpp:80
bool InhibitDropFrame(void)
Definition: cutter.cpp:192
uint32_t GetAudioOnlyBitrate(void) const
void SetVideoBitrate(int bitrate)
static void TranscodeWriteText(void *ptr, unsigned char *buf, int len, int timecode, int pagenr)
Definition: transcode.cpp:197
int cmdBitrate
Definition: transcode.h:70
void SetTranscoding(bool value)
int GetAudioFrameSize(void) const
number of audio samples (per channel) in an AVFrame
PlayerContext * GetPlayerContext(void)
Definition: transcode.h:46
void SetAudioFormat(AudioFormat f)
void ReencoderAddKFA(long curframe, long lastkey, long num_keyframes)
Definition: transcode.cpp:84
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
PlayerFlags
Definition: mythplayer.h:86
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:407
bool QueryCutList(frm_dir_map_t &, bool loadAutosave=false) const
void SetCutList(frm_dir_map_t &deleteMap, PlayerContext *ctx)
Definition: cutter.cpp:11
void SetAudioBitrate(int bitrate)
virtual Setting * byName(const QString &settingName)
virtual bool loadByGroup(const QString &name, const QString &group)
void SetOption(const QString &name, int value)
handles the "wait_for_seqstart" option.
void SetFilename(QString fname)
void WriteKeyFrameAdjustTable(const vector< struct kfatable_entry > &kfa_table)
bool QueryMarkupFlag(MarkTypes type) const
Returns true iff the speficied mark type is set on frame 0.
bool InhibitUseAudioFrames(int64_t frames, long *totalAudio)
Definition: cutter.cpp:142
void SetAudioFrameRate(int rate)
void SetFramerate(double rate)
void SetNewVideoParams(double newaspect)
long long GetTimecodeOffset(void) 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)
uint64_t GetCurrentFrameCount(void) const
virtual CC608Reader * GetCC608Reader(uint id=0)
Definition: mythplayer.h:291
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)
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:91
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 GetHeight(void) const
QString GetSourceFile(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:109
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:72
void ClearMarkupFlag(MarkTypes type) const
Definition: programinfo.h:605
float GetVideoAspect(void) const
Definition: mythplayer.h:171
void InitForTranscode(bool copyaudio, bool copyvideo)
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
QSize GetVideoBufferSize(void) const
Definition: mythplayer.h:169
void SetPlayerContext(PlayerContext *)
Definition: transcode.cpp:158
void SetKeyFrameDist(int dist)
void SetHeight(int height)
void SetContainer(QString cont)
AudioPlayer * GetAudio(void)
Definition: mythplayer.h:185
bool UpdateStatusMessage(QString message)
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
uint64_t GetTotalFrameCount(void) const
Definition: mythplayer.h:190
void SetAudioChannels(int channels)
QString GetEncodingType(void) const
uint32_t GetAudioBitrate(void) const
bool UpdatePercentComplete(int percent)
int cmdAudioBitrate
Definition: transcode.h:71
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end. ...
Definition: programinfo.h:395
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
void Activate(float v2a, int64_t total)
Definition: cutter.cpp:85
int GetCodec(void) const
Definition: audioplayer.h:59
MythPlayer * GetPlayer(void)
Definition: transcode.h:47
const char * name
Definition: ParseText.cpp:338
This class is to act as a fake audio output device to store the data for reencoding.
float GetFrameRate(void) const
Definition: mythplayer.h:172
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:1065
bool InitForWrite(void)
NuppelVideoRecorder * nvr
Definition: transcode.h:53
void SetThreadCount(int count)
KFATable * kfa_table
Definition: transcode.h:57
const char * kTranscoderInUseID
bool GetRawAudioState(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:203
void SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
FIFOWriter * fifow
Definition: transcode.h:56
static MThreadPool * globalInstance(void)
void ClearPositionMap(MarkTypes type) const
bool ReOpen(QString filename)
QString GetLastError(void) const
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)
QString cmdVideoCodec
Definition: transcode.h:67
int keyframedist
Definition: transcode.h:52
bool AddSegment(void)
int GetStreamID(void) const
uint16_t GetWidth(void) const
bool avfMode
Definition: transcode.h:60
uint16_t GetSegmentSize(void) const
long UpdateStoredFrameNum(long curFrameNum)
void DiscardVideoFrame(VideoFrame *buffer)
Places frame in the available frames queue.
float GetVideoAspect(void) const
Definition: decoderbase.h:189
void SetTranscoding(bool value)
bool NextFrameIsKeyFrame(void)
bool hlsMode
Definition: transcode.h:61
void SetAspect(float aspect)
bool QueryIsEditing(void) const
Queries "recorded" table for its "editing" field and returns true if it is set to true...
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
void FIFODrain(void)
Definition: fifowriter.cpp:247
Definition: cutter.h:13
char * data(void)
virtual int SetTrack(uint type, int trackNo)
void SetAudioCodec(QString codec)
int adjust
Definition: format.h:131
virtual QString getValue(void) const
Definition: settings.cpp:91
static const QString LOC
void av_free(void *ptr)
Implements a file/stream reader/writer.
void FIFOWrite(int id, void *buf, long size)
Definition: fifowriter.cpp:203
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:312
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
uint32_t GetBitrate(void) const
int cmdHeight
Definition: transcode.h:69
QString GetCurrentFilename(bool audioOnly=false, bool encoded=false) const
DecoderBase * GetDecoder(void)
Returns the stream decoder currently in use.
Definition: mythplayer.h:273
Definition: format.h:129
static QString get_str_option(RecordingProfile *profile, const QString &name)
Definition: transcode.cpp:167
VideoFrame * GetFrame(int &didFF, bool &isKey)
RingBuffer * outRingBuffer
Definition: transcode.h:55
GLenum GLsizei len
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
bool IsErrored(void)
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:47
void SetRingBuffer(RingBuffer *rbuf)
Tells recorder to use an externally created ringbuffer.
VideoFrameType codec
Definition: mythframe.h:33