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