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