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