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((PlayerFlags)(kVideoIsNull | kNoITV)));
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  delete hls;
993  return REENCODE_OK;
994  }
995 
996  QString audfifo = fifodir + QString("/audout");
997  QString vidfifo = fifodir + QString("/vidout");
998  int audio_size = arb->m_eff_audiorate * arb->m_bytes_per_frame;
999  // framecontrol is true if we want to enforce fifo sync.
1000  if (framecontrol)
1001  LOG(VB_GENERAL, LOG_INFO, "Enforcing sync on fifos");
1002  fifow = new FIFOWriter(2, framecontrol);
1003 
1004  if (!fifow->FIFOInit(0, QString("video"), vidfifo, vidSize, 50) ||
1005  !fifow->FIFOInit(1, QString("audio"), audfifo, audio_size, 25))
1006  {
1007  LOG(VB_GENERAL, LOG_ERR,
1008  "Error initializing fifo writer. Aborting");
1009  unlink(outputname.toLocal8Bit().constData());
1010  SetPlayerContext(NULL);
1011  delete hls;
1012  return REENCODE_ERROR;
1013  }
1014  LOG(VB_GENERAL, LOG_INFO,
1015  QString("Video %1x%2@%3fps Audio rate: %4")
1016  .arg(video_width).arg(video_height)
1017  .arg(video_frame_rate)
1018  .arg(arb->m_eff_audiorate));
1019  LOG(VB_GENERAL, LOG_INFO, "Created fifos. Waiting for connection.");
1020  }
1021 
1022  bool forceKeyFrames = (fifow == NULL) ? framecontrol : false;
1023 
1024  frm_dir_map_t::iterator dm_iter;
1025  bool writekeyframe = true;
1026 
1027  int num_keyframes = 0;
1028 
1029  int did_ff = 0;
1030 
1031  long curFrameNum = 0;
1032  frame.frameNumber = 1;
1033  long lastKeyFrame = 0;
1034  long totalAudio = 0;
1035  int dropvideo = 0;
1036  // timecode of the last read video frame in input time
1037  long long lasttimecode = 0;
1038  // timecode of the last write video frame in input or output time
1039  long long lastWrittenTime = 0;
1040  // delta between the same video frame in input and output due to applying the cut list
1041  long long timecodeOffset = 0;
1042 
1043  float rateTimeConv = arb->m_eff_audiorate / 1000.0f;
1044  float vidFrameTime = 1000.0f / video_frame_rate;
1045  int wait_recover = 0;
1046  VideoOutput *videoOutput = GetPlayer()->GetVideoOutput();
1047  bool is_key = 0;
1048  bool first_loop = true;
1049  unsigned char *newFrame = (unsigned char *)av_malloc(frame.size);
1050  frame.buf = newFrame;
1051  AVPicture imageIn, imageOut;
1052  struct SwsContext *scontext = NULL;
1053 
1054  if (fifow)
1055  LOG(VB_GENERAL, LOG_INFO, "Dumping Video and Audio data to fifos");
1056  else if (copyaudio)
1057  LOG(VB_GENERAL, LOG_INFO, "Copying Audio while transcoding Video");
1058  else if (hlsMode)
1059  LOG(VB_GENERAL, LOG_INFO, "Transcoding for HTTP Live Streaming");
1060  else if (avfMode)
1061  LOG(VB_GENERAL, LOG_INFO, "Transcoding to libavformat container");
1062  else
1063  LOG(VB_GENERAL, LOG_INFO, "Transcoding Video and Audio");
1064 
1065  VideoDecodeBuffer *videoBuffer =
1066  new VideoDecodeBuffer(GetPlayer(), videoOutput, honorCutList);
1067  MThreadPool::globalInstance()->start(videoBuffer, "VideoDecodeBuffer");
1068 
1069  QTime flagTime;
1070  flagTime.start();
1071 
1072  if (cutter)
1073  cutter->Activate(vidFrameTime * rateTimeConv, total_frame_count);
1074 
1075  bool stopSignalled = false;
1076  VideoFrame *lastDecode = NULL;
1077 
1078  if (hls)
1079  {
1081  hls->UpdateStatusMessage("Transcoding");
1082  }
1083 
1084  while ((!stopSignalled) &&
1085  (lastDecode = videoBuffer->GetFrame(did_ff, is_key)))
1086  {
1087  if (first_loop)
1088  {
1089  copyaudio = GetPlayer()->GetRawAudioState();
1090  first_loop = false;
1091  }
1092 
1093  float new_aspect = lastDecode->aspect;
1094 
1095  if (cutter)
1096  cutter->NewFrame(lastDecode->frameNumber);
1097 
1098 // frame timecode is on input time base
1099  frame.timecode = lastDecode->timecode;
1100 
1101  // if the timecode jumps backwards just use the last frame's timecode plus the duration of a frame
1102  if (frame.timecode < lasttimecode)
1103  frame.timecode = (long long)(lasttimecode + vidFrameTime);
1104 
1105  if (fifow)
1106  {
1107  frame.buf = lastDecode->buf;
1108  totalAudio += arb->GetSamples(frame.timecode);
1109  int audbufTime = (int)(totalAudio / rateTimeConv);
1110  int auddelta = frame.timecode - audbufTime;
1111  int vidTime = (int)(curFrameNum * vidFrameTime + 0.5);
1112  int viddelta = frame.timecode - vidTime;
1113  int delta = viddelta - auddelta;
1114  int absdelta = delta < 0 ? -delta : delta;
1115  if (absdelta < 500 && absdelta >= vidFrameTime)
1116  {
1117  QString msg = QString("Audio is %1ms %2 video at # %3: "
1118  "auddelta=%4, viddelta=%5")
1119  .arg(absdelta)
1120  .arg(((delta > 0) ? "ahead of" : "behind"))
1121  .arg((int)curFrameNum)
1122  .arg(auddelta)
1123  .arg(viddelta);
1124  LOG(VB_GENERAL, LOG_INFO, msg);
1125  dropvideo = (delta > 0) ? 1 : -1;
1126  wait_recover = 0;
1127  }
1128  else if (delta >= 500 && delta < 10000)
1129  {
1130  if (wait_recover == 0)
1131  {
1132  dropvideo = 5;
1133  wait_recover = 6;
1134  }
1135  else if (wait_recover == 1)
1136  {
1137  // Video is badly lagging. Try to catch up.
1138  int count = 0;
1139  while (delta > vidFrameTime)
1140  {
1141  if (!cutter || !cutter->InhibitDummyFrame())
1142  fifow->FIFOWrite(0, frame.buf, vidSize);
1143 
1144  count++;
1145  delta -= (int)vidFrameTime;
1146  }
1147  QString msg = QString("Added %1 blank video frames")
1148  .arg(count);
1149  LOG(VB_GENERAL, LOG_INFO, msg);
1150  curFrameNum += count;
1151  dropvideo = 0;
1152  wait_recover = 0;
1153  }
1154  else
1155  wait_recover--;
1156  }
1157  else
1158  {
1159  dropvideo = 0;
1160  wait_recover = 0;
1161  }
1162 
1163 #if 0
1164  int buflen = (int)(arb->audiobuffer_len / rateTimeConv);
1165  LOG(VB_GENERAL, LOG_DEBUG,
1166  QString("%1: video time: %2 audio time: %3 "
1167  "buf: %4 exp: %5 delta: %6")
1168  .arg(curFrameNum) .arg(frame.timecode)
1169  .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime)
1170  .arg(delta));
1171 #endif
1172  AudioBuffer *ab = NULL;
1173  while ((ab = arb->GetData(frame.timecode)) != NULL)
1174  {
1175  if (!cutter ||
1176  !cutter->InhibitUseAudioFrames(ab->m_frames, &totalAudio))
1177  fifow->FIFOWrite(1, ab->data(), ab->size());
1178 
1179  delete ab;
1180  }
1181 
1182  if (dropvideo < 0)
1183  {
1184  if (cutter && cutter->InhibitDropFrame())
1185  fifow->FIFOWrite(0, frame.buf, vidSize);
1186 
1187  LOG(VB_GENERAL, LOG_INFO, "Dropping video frame");
1188  dropvideo++;
1189  curFrameNum--;
1190  }
1191  else
1192  {
1193  if (!cutter || !cutter->InhibitUseVideoFrame())
1194  fifow->FIFOWrite(0, frame.buf, vidSize);
1195 
1196  if (dropvideo)
1197  {
1198  if (!cutter || !cutter->InhibitDummyFrame())
1199  fifow->FIFOWrite(0, frame.buf, vidSize);
1200 
1201  curFrameNum++;
1202  dropvideo--;
1203  }
1204  }
1205  videoOutput->DoneDisplayingFrame(lastDecode);
1207  lasttimecode = frame.timecode;
1208  }
1209  else if (copyaudio)
1210  {
1211  // Encoding from NuppelVideo to NuppelVideo with MP3 audio
1212  // So let's not decode/reencode audio
1213  if (!GetPlayer()->GetRawAudioState())
1214  {
1215  // The Raw state changed during decode. This is not good
1216  LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, MythPlayer "
1217  "is not in raw audio mode.");
1218 
1219  unlink(outputname.toLocal8Bit().constData());
1220  av_free(newFrame);
1221  SetPlayerContext(NULL);
1222  if (videoBuffer)
1223  videoBuffer->stop();
1224  if (hls)
1225  {
1227  hls->UpdateStatusMessage("Transcoding Errored");
1228  delete hls;
1229  }
1230  return REENCODE_ERROR;
1231  }
1232 
1233  if (forceKeyFrames)
1234  writekeyframe = true;
1235  else
1236  {
1237  writekeyframe = is_key;
1238  if (writekeyframe)
1239  {
1240  // Currently, we don't create new sync frames,
1241  // (though we do create new 'I' frames), so we mark
1242  // the key-frames before deciding whether we need a
1243  // new 'I' frame.
1244 
1245  //need to correct the frame# and timecode here
1246  // Question: Is it necessary to change the timecodes?
1247  long sync_offset =
1248  GetPlayer()->UpdateStoredFrameNum(curFrameNum);
1249  nvr->UpdateSeekTable(num_keyframes, sync_offset);
1250  ReencoderAddKFA(curFrameNum, lastKeyFrame, num_keyframes);
1251  num_keyframes++;
1252  lastKeyFrame = curFrameNum;
1253 
1254  if (did_ff)
1255  did_ff = 0;
1256  }
1257  }
1258 
1259  if (did_ff == 1)
1260  {
1261  timecodeOffset +=
1262  (frame.timecode - lasttimecode - (int)vidFrameTime);
1263  }
1264  lasttimecode = frame.timecode;
1265 // from here on the timecode is on the output time base
1266  frame.timecode -= timecodeOffset;
1267 
1268  if (!GetPlayer()->WriteStoredData(
1269  outRingBuffer, (did_ff == 0), timecodeOffset))
1270  {
1271  if (video_aspect != new_aspect)
1272  {
1273  video_aspect = new_aspect;
1274  nvr->SetNewVideoParams(video_aspect);
1275  }
1276 
1277  QSize buf_size = GetPlayer()->GetVideoBufferSize();
1278 
1279  if (video_width != buf_size.width() ||
1280  video_height != buf_size.height())
1281  {
1282  video_width = buf_size.width();
1283  video_height = buf_size.height();
1284 
1285  LOG(VB_GENERAL, LOG_INFO,
1286  QString("Resizing from %1x%2 to %3x%4")
1287  .arg(video_width).arg(video_height)
1288  .arg(newWidth).arg(newHeight));
1289 
1290  }
1291 
1292  if (did_ff == 1)
1293  {
1294  // Create a new 'I' frame if we just processed a cut.
1295  did_ff = 2;
1296  writekeyframe = true;
1297  }
1298 
1299  if ((video_width == newWidth) && (video_height == newHeight))
1300  {
1301  frame.buf = lastDecode->buf;
1302  }
1303  else
1304  {
1305  frame.buf = newFrame;
1306  avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
1307  video_width, video_height);
1308  avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
1309  newWidth, newHeight);
1310 
1311  int bottomBand = (video_height == 1088) ? 8 : 0;
1312  scontext = sws_getCachedContext(scontext, video_width,
1313  video_height, PIX_FMT_YUV420P, newWidth,
1314  newHeight, PIX_FMT_YUV420P,
1315  SWS_FAST_BILINEAR, NULL, NULL, NULL);
1316 
1317  sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1318  video_height - bottomBand,
1319  imageOut.data, imageOut.linesize);
1320  }
1321 
1322  nvr->WriteVideo(&frame, true, writekeyframe);
1323  }
1325  }
1326  else
1327  {
1328  if (did_ff == 1)
1329  {
1330  did_ff = 2;
1331  timecodeOffset +=
1332  (frame.timecode - lasttimecode - (int)vidFrameTime);
1333  }
1334 
1335  if (video_aspect != new_aspect)
1336  {
1337  video_aspect = new_aspect;
1338  if (nvr)
1339  nvr->SetNewVideoParams(video_aspect);
1340  }
1341 
1342 
1343  QSize buf_size = GetPlayer()->GetVideoBufferSize();
1344 
1345  if (video_width != buf_size.width() ||
1346  video_height != buf_size.height())
1347  {
1348  video_width = buf_size.width();
1349  video_height = buf_size.height();
1350 
1351  LOG(VB_GENERAL, LOG_INFO,
1352  QString("Resizing from %1x%2 to %3x%4")
1353  .arg(video_width).arg(video_height)
1354  .arg(newWidth).arg(newHeight));
1355  }
1356 
1357  if ((video_width == newWidth) && (video_height == newHeight))
1358  {
1359  frame.buf = lastDecode->buf;
1360  }
1361  else
1362  {
1363  frame.buf = newFrame;
1364  avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
1365  video_width, video_height);
1366  avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
1367  newWidth, newHeight);
1368 
1369  int bottomBand = (video_height == 1088) ? 8 : 0;
1370  scontext = sws_getCachedContext(scontext, video_width,
1371  video_height, PIX_FMT_YUV420P, newWidth,
1372  newHeight, PIX_FMT_YUV420P,
1373  SWS_FAST_BILINEAR, NULL, NULL, NULL);
1374 
1375  sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1376  video_height - bottomBand,
1377  imageOut.data, imageOut.linesize);
1378  }
1379 
1380  // audio is fully decoded, so we need to reencode it
1381  AudioBuffer *ab = NULL;
1382  while ((ab = arb->GetData(lastWrittenTime)) != NULL)
1383  {
1384  unsigned char *buf = (unsigned char *)ab->data();
1385  if (avfMode)
1386  {
1387  if (did_ff != 1)
1388  {
1389  long long tc = ab->m_time - timecodeOffset;
1390  avfw->WriteAudioFrame(buf, audioFrame, tc);
1391 
1392  if (avfw2)
1393  {
1394  if ((avfw2->GetTimecodeOffset() == -1) &&
1395  (avfw->GetTimecodeOffset() != -1))
1396  {
1397  avfw2->SetTimecodeOffset(
1398  avfw->GetTimecodeOffset());
1399  }
1400 
1401  tc = ab->m_time - timecodeOffset;
1402  avfw2->WriteAudioFrame(buf, audioFrame, tc);
1403  }
1404 
1405  ++audioFrame;
1406  }
1407  }
1408  else
1409  {
1410  nvr->SetOption("audioframesize", ab->size());
1411  nvr->WriteAudio(buf, audioFrame++,
1412  ab->m_time - timecodeOffset);
1413  if (nvr->IsErrored())
1414  {
1415  LOG(VB_GENERAL, LOG_ERR,
1416  "Transcode: Encountered irrecoverable error in "
1417  "NVR::WriteAudio");
1418 
1419  av_free(newFrame);
1420  SetPlayerContext(NULL);
1421  if (videoBuffer)
1422  videoBuffer->stop();
1423  delete ab;
1424  delete hls; // HLS isn't actually going to be running here
1425  return REENCODE_ERROR;
1426  }
1427  }
1428 
1429  delete ab;
1430  }
1431 
1432  if (!avfMode)
1433  {
1434  GetPlayer()->GetCC608Reader()->
1436  }
1437  lasttimecode = frame.timecode;
1438  frame.timecode -= timecodeOffset;
1439 
1440  if (avfMode)
1441  {
1442  if (halfFramerate && !skippedLastFrame)
1443  {
1444  skippedLastFrame = true;
1445  }
1446  else
1447  {
1448  skippedLastFrame = false;
1449 
1450  if ((hls) &&
1451  (avfw->GetFramesWritten()) &&
1452  (hlsSegmentFrames > hlsSegmentSize) &&
1453  (avfw->NextFrameIsKeyFrame()))
1454  {
1455  hls->AddSegment();
1456  avfw->ReOpen(hls->GetCurrentFilename());
1457 
1458  if (avfw2)
1459  avfw2->ReOpen(hls->GetCurrentFilename(true));
1460 
1461  hlsSegmentFrames = 0;
1462  }
1463 
1464  if (avfw->WriteVideoFrame(&frame) > 0)
1465  {
1466  lastWrittenTime = frame.timecode + timecodeOffset;
1467  if (hls)
1468  ++hlsSegmentFrames;
1469  }
1470 
1471  }
1472  }
1473  else
1474  {
1475  if (forceKeyFrames)
1476  nvr->WriteVideo(&frame, true, true);
1477  else
1478  nvr->WriteVideo(&frame);
1479  lastWrittenTime = frame.timecode + timecodeOffset;
1480  }
1481  }
1482  if (MythDate::current() > statustime)
1483  {
1484  if (showprogress)
1485  {
1486  LOG(VB_GENERAL, LOG_INFO,
1487  QString("Processed: %1 of %2 frames(%3 seconds)").
1488  arg((long)curFrameNum).arg((long)total_frame_count).
1489  arg((long)(curFrameNum / video_frame_rate)));
1490  }
1491 
1492  if (hls && hls->CheckStop())
1493  {
1495  stopSignalled = true;
1496  }
1497 
1498  statustime = MythDate::current().addSecs(5);
1499  }
1500  if (MythDate::current() > curtime)
1501  {
1502  if (honorCutList && m_proginfo && !avfMode &&
1504  {
1505  LOG(VB_GENERAL, LOG_NOTICE,
1506  "Transcoding aborted, cutlist updated");
1507 
1508  unlink(outputname.toLocal8Bit().constData());
1509  av_free(newFrame);
1510  SetPlayerContext(NULL);
1511  if (videoBuffer)
1512  videoBuffer->stop();
1513  return REENCODE_CUTLIST_CHANGE;
1514  }
1515 
1516  if ((jobID >= 0) || (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_INFO)))
1517  {
1518  if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
1519  {
1520  LOG(VB_GENERAL, LOG_NOTICE,
1521  "Transcoding STOPped by JobQueue");
1522 
1523  unlink(outputname.toLocal8Bit().constData());
1524  av_free(newFrame);
1525  SetPlayerContext(NULL);
1526  if (videoBuffer)
1527  videoBuffer->stop();
1528  if (hls)
1529  {
1531  hls->UpdateStatusMessage("Transcoding Stopped");
1532  delete hls;
1533  }
1534  return REENCODE_STOPPED;
1535  }
1536 
1537  float flagFPS = 0.0;
1538  float elapsed = flagTime.elapsed() / 1000.0;
1539  if (elapsed)
1540  flagFPS = curFrameNum / elapsed;
1541 
1542  total_frame_count = GetPlayer()->GetCurrentFrameCount();
1543  int percentage = curFrameNum * 100 / total_frame_count;
1544 
1545  if (hls)
1546  hls->UpdatePercentComplete(percentage);
1547 
1548  if (jobID >= 0)
1550  QObject::tr("%1% Completed @ %2 fps.")
1551  .arg(percentage).arg(flagFPS));
1552  else
1553  LOG(VB_GENERAL, LOG_INFO,
1554  QString("mythtranscode: %1% Completed @ %2 fps.")
1555  .arg(percentage).arg(flagFPS));
1556 
1557  }
1558  curtime = MythDate::current().addSecs(20);
1559  }
1560 
1561  curFrameNum++;
1562  frame.frameNumber = 1 + (curFrameNum << 1);
1563 
1564  GetPlayer()->DiscardVideoFrame(lastDecode);
1565  }
1566 
1567  sws_freeContext(scontext);
1568 
1569  if (!fifow)
1570  {
1571  if (avfw)
1572  avfw->CloseFile();
1573 
1574  if (avfw2)
1575  avfw2->CloseFile();
1576 
1577  if (!avfMode && m_proginfo)
1578  {
1583  }
1584 
1585  if (nvr)
1586  {
1587  nvr->WriteSeekTable();
1588  if (!kfa_table->empty())
1590  }
1591  } else {
1592  fifow->FIFODrain();
1593  }
1594 
1595  if (cutter)
1596  delete cutter;
1597 
1598  if (avfw)
1599  delete avfw;
1600 
1601  if (avfw2)
1602  delete avfw2;
1603 
1604  if (hls)
1605  {
1606  if (!stopSignalled)
1607  {
1609  hls->UpdateStatusMessage("Transcoding Completed");
1610  hls->UpdatePercentComplete(100);
1611  }
1612  else
1613  {
1615  hls->UpdateStatusMessage("Transcoding Stopped");
1616  }
1617  delete hls;
1618  }
1619 
1620  if (videoBuffer)
1621  {
1622  videoBuffer->stop();
1623  }
1624 
1625  av_free(newFrame);
1626  SetPlayerContext(NULL);
1627 
1628  return REENCODE_OK;
1629 }
1630 
1631 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1632 
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
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:83
PlayerFlags
Definition: mythplayer.h:83
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:404
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
void SetAudioFrameRate(int rate)
void SetFramerate(double rate)
void SetNewVideoParams(double newaspect)
long long GetTimecodeOffset(void) const
int buflen
Definition: pxsup2dast.c:100
void SetPlayer(MythPlayer *new_player)
static enum JobCmds GetJobCmd(int jobID)
Definition: jobqueue.cpp:1454
int hlsMaxSegments
Definition: transcode.h:64
void SetRingBuffer(RingBuffer *buf)
uint64_t GetCurrentFrameCount(void) const
virtual CC608Reader * GetCC608Reader(uint id=0)
Definition: mythplayer.h:288
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:80
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString ReinitAudio(void)
bool showprogress
Definition: transcode.h:58
void SetRecorder(RemoteEncoder *rec)
int FIFOInit(int id, QString desc, QString name, long size, int num_bufs)
Definition: fifowriter.cpp:91
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
long long timecode
Definition: mythframe.h:44
void WriteVideo(VideoFrame *frame, bool skipsync=false, bool forcekey=false)
void SetVideoAspect(float newAspect)
bool OpenFile(void)
int WriteVideoFrame(VideoFrame *frame)
void FlushTxtBuffers(void)
Definition: cc608reader.cpp:35
void NewFrame(int64_t currentFrame)
Definition: cutter.cpp: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:109
void SetAudioOutput(AudioOutput *ao)
bool hlsDisableAudioOnly
Definition: transcode.h:63
This class serves as the base class for all video output methods.
Definition: videooutbase.h:46
Holds information on recordings and videos.
Definition: programinfo.h:72
void ClearMarkupFlag(MarkTypes type) const
Definition: programinfo.h:588
float GetVideoAspect(void) const
Definition: mythplayer.h:168
void InitForTranscode(bool copyaudio, bool copyvideo)
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
QSize GetVideoBufferSize(void) const
Definition: mythplayer.h:166
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:182
bool UpdateStatusMessage(QString message)
float aspect
Definition: mythframe.h:38
int hlsStreamID
Definition: transcode.h:62
bool CloseFile(void)
void SetFrameRate(double rate)
Sets the video frame rate.
Definition: recorderbase.h:75
int WriteAudioFrame(unsigned char *buf, int fnum, long long &timecode)
int height
Definition: mythframe.h:37
bool InhibitDummyFrame(void)
Definition: cutter.cpp: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:187
void SetAudioChannels(int channels)
QString GetEncodingType(void) const
uint32_t GetAudioBitrate(void) const
bool UpdatePercentComplete(int percent)
int cmdAudioBitrate
Definition: transcode.h:71
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end. ...
Definition: programinfo.h:395
bool CheckStop(void)
long long frameNumber
Definition: mythframe.h:43
static bool ChangeJobComment(int jobID, QString comment="")
Definition: jobqueue.cpp:997
int cmdWidth
Definition: transcode.h:68
typedef long(ZCALLBACK *tell_file_func) OF((voidpf opaque
void Activate(float v2a, int64_t total)
Definition: cutter.cpp: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:169
bool UpdateSizeInfo(uint16_t width, uint16_t height, uint16_t srcwidth, uint16_t srcheight)
bool TranscodeGetNextFrame(frm_dir_map_t::iterator &dm_iter, int &did_ff, bool &is_key, bool honorCutList)
void * av_malloc(unsigned int size)
void UpdateSeekTable(int frame_num, long offset=0)
static bool IsJobRunning(int jobType, uint chanid, const QDateTime &recstartts)
Definition: jobqueue.cpp:1065
bool InitForWrite(void)
NuppelVideoRecorder * nvr
Definition: transcode.h:53
void SetThreadCount(int count)
KFATable * kfa_table
Definition: transcode.h:57
const char * kTranscoderInUseID
bool GetRawAudioState(void) const
void SetEncodingTune(QString tune)
virtual void DoneDisplayingFrame(VideoFrame *frame)
Releases frame returned from GetLastShownFrame() onto the queue of frames ready for decoding onto...
Definition: videooutbase.h:203
void SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
FIFOWriter * fifow
Definition: transcode.h:56
static MThreadPool * globalInstance(void)
void ClearPositionMap(MarkTypes type) const
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
int keyframedist
Definition: transcode.h:52
bool AddSegment(void)
int GetStreamID(void) const
uint16_t GetWidth(void) const
bool avfMode
Definition: transcode.h:60
uint16_t GetSegmentSize(void) const
long UpdateStoredFrameNum(long curFrameNum)
void DiscardVideoFrame(VideoFrame *buffer)
Places frame in the available frames queue.
float GetVideoAspect(void) const
Definition: decoderbase.h:189
void SetTranscoding(bool value)
bool NextFrameIsKeyFrame(void)
bool hlsMode
Definition: transcode.h:61
void SetAspect(float aspect)
bool QueryIsEditing(void) const
Queries "recorded" table for its "editing" field and returns true if it is set to true...
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
bool GetProfile(QString profileName, QString encodingType, int height, int frameRate)
Definition: transcode.cpp: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.
void FIFOWrite(int id, void *buf, long size)
Definition: fifowriter.cpp:203
void SetTimecodeOffset(long long o)
unsigned char * buf
Definition: mythframe.h:34
struct AVPicture AVPicture
void SetWidth(int width)
void SetWatchingRecording(bool mode)
Definition: mythplayer.cpp:327
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:270
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
GLenum GLsizei len
RemoteEncoder * RemoteGetExistingRecorder(const ProgramInfo *pginfo)
bool IsErrored(void)
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:47
void SetRingBuffer(RingBuffer *rbuf)
Tells recorder to use an externally created ringbuffer.
VideoFrameType codec
Definition: mythframe.h:33