9 #include <QWaitCondition>
11 #include <QMutexLocker>
12 #include <QtAlgorithms>
14 #include "mythconfig.h"
38 #include "libavcodec/avcodec.h"
39 #include "libswscale/swscale.h"
44 #define LOC QString("Transcode: ")
57 hlsMode(
false), hlsStreamID(-1),
58 hlsDisableAudioOnly(
false),
60 cmdContainer(
"mpegts"), cmdAudioCodec(
"aac"),
61 cmdVideoCodec(
"libx264"),
62 cmdWidth(480), cmdHeight(0),
63 cmdBitrate(600000), cmdAudioBitrate(64000)
81 long delta = curframe - lastkey;
92 int height,
int frameRate)
94 if (profileName.toLower() ==
"autodetect")
99 QString autoProfileName = QObject::tr(
"Autodetect from %1").arg(height);
100 if (frameRate == 25 || frameRate == 30)
101 autoProfileName +=
"i";
102 if (frameRate == 50 || frameRate == 60)
103 autoProfileName +=
"p";
106 LOG(VB_GENERAL, LOG_NOTICE,
107 QString(
"Transcode: Looking for autodetect profile: %1")
108 .arg(autoProfileName));
111 if (!result && encodingType ==
"MPEG-2")
114 autoProfileName =
"MPEG2";
116 if (!result && (encodingType ==
"MPEG-4" || encodingType ==
"RTjpeg"))
120 autoProfileName =
"RTjpeg/MPEG4";
124 LOG(VB_GENERAL, LOG_ERR,
125 QString(
"Transcode: Couldn't find profile for : %1")
131 LOG(VB_GENERAL, LOG_NOTICE,
132 QString(
"Transcode: Using autodetect profile: %1")
133 .arg(autoProfileName));
139 profileID = profileName.toInt(&isNum);
141 if (isNum && profileID > 0)
145 LOG(VB_GENERAL, LOG_ERR, QString(
"Couldn't find profile #: %1")
155 if (player_ctx ==
ctx)
168 LOG(VB_GENERAL, LOG_ERR,
LOC +
169 QString(
"get_str_option(...%1): Option not in profile.").arg(name));
171 return QString::null;
177 if (ret_str.isEmpty())
181 int ret_int = ret_str.toInt(&ok);
185 LOG(VB_GENERAL, LOG_ERR,
LOC +
186 QString(
"get_int_option(...%1): Option is not an int.").arg(name));
193 int timecode,
int pagenr)
196 nvr->
WriteText(buf, len, timecode, pagenr);
200 const QString &outputname,
201 const QString &profileName,
202 bool honorCutList,
bool framecontrol,
203 int jobID, QString fifodir,
204 bool fifo_info,
bool cleanCut,
210 QDateTime statustime = curtime;
216 int hlsSegmentSize = 0;
217 int hlsSegmentFrames = 0;
244 LOG(VB_GENERAL, LOG_ERR,
245 "Transcoding aborted, error creating NuppelVideoRecorder.");
246 return REENCODE_ERROR;
267 statustime = statustime.addSecs(5);
278 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, error opening file.");
280 return REENCODE_ERROR;
283 if (AudioTrackNo > -1)
285 LOG(VB_GENERAL, LOG_INFO,
286 QString(
"Set audiotrack number to %1").arg(AudioTrackNo));
291 long long new_frame_count = total_frame_count;
294 LOG(VB_GENERAL, LOG_INFO,
"Honoring the cutlist while transcoding");
296 frm_dir_map_t::const_iterator it;
298 long long lastStart = 0;
300 if (deleteMap.size() == 0)
303 for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
307 if (!cutStr.isEmpty())
309 cutStr += QString(
"%1-").arg((
long)it.key());
310 lastStart = it.key();
314 if (cutStr.isEmpty())
316 cutStr += QString(
"%1").arg((
long)it.key());
317 new_frame_count -= (it.key() - lastStart);
320 if (cutStr.isEmpty())
322 else if (cutStr.endsWith(
'-') && (total_frame_count > lastStart))
324 new_frame_count -= (total_frame_count - lastStart);
325 cutStr += QString(
"%1").arg(total_frame_count);
327 LOG(VB_GENERAL, LOG_INFO, QString(
"Cutlist : %1").arg(cutStr));
328 LOG(VB_GENERAL, LOG_INFO, QString(
"Original Length: %1 frames")
329 .arg((
long)total_frame_count));
330 LOG(VB_GENERAL, LOG_INFO, QString(
"New Length : %1 frames")
331 .arg((
long)new_frame_count));
336 LOG(VB_GENERAL, LOG_INFO,
"Transcoding aborted, cutlist changed");
338 return REENCODE_CUTLIST_CHANGE;
341 curtime = curtime.addSecs(60);
346 bool copyvideo =
false, copyaudio =
false;
348 QString vidsetting = NULL, audsetting = NULL, vidfilters = NULL;
351 int video_width = buf_size.width();
352 int video_height = buf_size.height();
354 if (video_height == 1088) {
355 LOG(VB_GENERAL, LOG_NOTICE,
356 "Found video height of 1088. This is unusual and "
357 "more than likely the video is actually 1080 so mythtranscode "
358 "will treat it as such.");
364 int newWidth = video_width;
365 int newHeight = video_height;
366 bool halfFramerate =
false;
367 bool skippedLastFrame =
false;
369 kfa_table =
new vector<struct kfatable_entry>;
381 if (newHeight == 0 && newWidth > 0)
382 newHeight = (
int)(1.0 * newWidth / video_aspect);
383 else if (newWidth == 0 && newHeight > 0)
384 newWidth = (
int)(1.0 * newHeight * video_aspect);
385 else if (newWidth == 0 && newHeight == 0)
388 newWidth = (
int)(1.0 * 480 * video_aspect);
392 newHeight = (
int)(1.0 * 640 / video_aspect);
397 newHeight = (newHeight + 15) & ~0xF;
398 newWidth = (newWidth + 15) & ~0xF;
403 LOG(VB_GENERAL, LOG_ERR,
404 "Transcoding aborted, error creating AVFormatWriter.");
406 return REENCODE_ERROR;
421 int segmentSize = 10;
422 int audioOnlyBitrate = 0;
426 audioOnlyBitrate = 48000;
431 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, error "
432 "creating low-bitrate AVFormatWriter.");
434 return REENCODE_ERROR;
442 #if CONFIG_LIBFAAC_ENCODER
445 # if CONFIG_LIBMP3LAME_ENCODER
465 #if CONFIG_LIBFAAC_ENCODER
468 # if CONFIG_LIBMP3LAME_ENCODER
480 segmentSize, audioOnlyBitrate);
485 LOG(VB_GENERAL, LOG_ERR,
"Unable to create new stream");
487 return REENCODE_ERROR;
493 hls->
UpdateSizeInfo(newWidth, newHeight, video_width, video_height);
497 LOG(VB_GENERAL, LOG_ERR,
"hls->InitForWrite() failed");
499 return REENCODE_ERROR;
502 if (video_frame_rate > 30)
504 halfFramerate =
true;
510 hlsSegmentSize = (
int)(segmentSize * video_frame_rate / 2);
519 hlsSegmentSize = (
int)(segmentSize * video_frame_rate);
549 LOG(VB_GENERAL, LOG_ERR,
"avfw->Init() failed");
551 return REENCODE_ERROR;
556 LOG(VB_GENERAL, LOG_ERR,
"avfw->OpenFile() failed");
558 return REENCODE_ERROR;
561 if (avfw2 && !avfw2->
Init())
563 LOG(VB_GENERAL, LOG_ERR,
"avfw2->Init() failed");
565 return REENCODE_ERROR;
570 LOG(VB_GENERAL, LOG_ERR,
"avfw2->OpenFile() failed");
572 return REENCODE_ERROR;
580 else if (fifodir.isEmpty())
582 if (!
GetProfile(profileName, encodingType, video_height,
583 (
int)round(video_frame_rate))) {
584 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, no profile found.");
586 return REENCODE_ERROR;
590 QMap<QString, QString> recorderOptionsMap;
594 .split(
",", QString::SkipEmptyParts);
596 while (loop < options.size())
598 QStringList tokens = options[loop].split(
"=");
599 recorderOptionsMap[tokens[0]] = tokens[1];
609 if (encodingType ==
"MPEG-2" &&
612 LOG(VB_GENERAL, LOG_NOTICE,
"Switching to MPEG-2 transcoder.");
614 return REENCODE_MPEG2TRANS;
620 vidsetting = encodingType;
625 int actualHeight = (video_height == 1088 ? 1080 : video_height);
632 if (newHeight == 0 && newWidth > 0)
633 newHeight = (
int)(1.0 * newWidth * actualHeight / video_width);
634 else if (newWidth == 0 && newHeight > 0)
635 newWidth = (
int)(1.0 * newHeight * video_width / actualHeight);
636 else if (newWidth == 0 && newHeight == 0)
639 newWidth = (
int)(1.0 * 480 * video_width / actualHeight);
643 newHeight = (
int)(1.0 * 640 * actualHeight / video_width);
647 if (encodingType.startsWith(
"mpeg", Qt::CaseInsensitive))
650 newHeight = (newHeight + 15) & ~0xF;
651 newWidth = (newWidth + 15) & ~0xF;
654 LOG(VB_GENERAL, LOG_INFO, QString(
"Resizing from %1x%2 to %3x%4")
655 .arg(video_width).arg(video_height)
656 .arg(newWidth).arg(newHeight));
674 if ((vidsetting ==
"MPEG-4") ||
675 (recorderOptionsMap[
"videocodec"] ==
"mpeg4"))
686 #ifdef USING_FFMPEG_THREADS
690 else if ((vidsetting ==
"MPEG-2") ||
691 (recorderOptionsMap[
"videocodec"] ==
"mpeg2video"))
697 #ifdef USING_FFMPEG_THREADS
701 else if ((vidsetting ==
"RTjpeg") ||
702 (recorderOptionsMap[
"videocodec"] ==
"rtjpeg"))
709 else if (vidsetting.isEmpty())
711 LOG(VB_GENERAL, LOG_ERR,
"No video information found!");
712 LOG(VB_GENERAL, LOG_ERR,
"Please ensure that recording profiles "
713 "for the transcoder are set");
715 return REENCODE_ERROR;
719 LOG(VB_GENERAL, LOG_ERR,
720 QString(
"Unknown video codec: %1").arg(vidsetting));
722 return REENCODE_ERROR;
726 if (audsetting ==
"MP3")
732 else if (audsetting ==
"Uncompressed")
738 LOG(VB_GENERAL, LOG_ERR,
739 QString(
"Unknown audio codec: %1").arg(audsetting));
745 if (recorderOptionsMap.size() > 0)
747 QMap<QString, QString>::Iterator it;
749 for (it = recorderOptionsMap.begin();
750 it != recorderOptionsMap.end(); ++it)
755 LOG(VB_GENERAL, LOG_NOTICE,
756 QString(
"Forcing Recorder option '%1' to '%2'")
757 .arg(key).arg(value));
759 if (value.contains(QRegExp(
"[^0-9]")))
765 newWidth = (value.toInt() + 15) & ~0xF;
766 else if (key ==
"height")
767 newHeight = (value.toInt() + 15) & ~0xF;
768 else if (key ==
"videocodec")
770 if (value ==
"mpeg4")
771 vidsetting =
"MPEG-4";
772 else if (value ==
"mpeg2video")
773 vidsetting =
"MPEG-2";
774 else if (value ==
"rtjpeg")
775 vidsetting =
"RTjpeg";
780 if ((vidsetting ==
"MPEG-4") ||
781 (vidsetting ==
"MPEG-2"))
783 else if (vidsetting ==
"RTjpeg")
792 if (vidsetting == encodingType && !framecontrol && !
avfMode &&
793 fifodir.isEmpty() && honorCutList &&
794 video_width == newWidth && video_height == newHeight)
797 LOG(VB_GENERAL, LOG_INFO,
"Reencoding video in 'raw' mode");
800 if (honorCutList && deleteMap.size() > 0)
821 LOG(VB_GENERAL, LOG_ERR,
822 "Unable to initialize MythPlayer for Transcode");
824 return REENCODE_ERROR;
832 if (video_height == 1080 && video_width == 1920)
833 vidSize = (1088 * 1920) * 3 / 2;
835 vidSize = (video_height * video_width) * 3 / 2;
839 frame.
width = newWidth;
841 frame.
size = newWidth * newHeight * 3 / 2;
843 if (!fifodir.isEmpty())
846 const char *audio_codec_name;
850 case AV_CODEC_ID_AC3:
851 audio_codec_name =
"ac3";
853 case AV_CODEC_ID_EAC3:
854 audio_codec_name =
"eac3";
856 case AV_CODEC_ID_DTS:
857 audio_codec_name =
"dts";
859 case AV_CODEC_ID_TRUEHD:
860 audio_codec_name =
"truehd";
862 case AV_CODEC_ID_MP3:
863 audio_codec_name =
"mp3";
865 case AV_CODEC_ID_MP2:
866 audio_codec_name =
"mp2";
868 case AV_CODEC_ID_AAC:
869 audio_codec_name =
"aac";
871 case AV_CODEC_ID_AAC_LATM:
872 audio_codec_name =
"aac_latm";
875 audio_codec_name =
"unknown";
879 audio_codec_name =
"raw";
882 if (honorCutList && fifo_info)
886 frm_dir_map_t::iterator dm_iter;
890 video_width = buf_size.width();
891 video_height = buf_size.height();
897 LOG(VB_GENERAL, LOG_INFO,
898 QString(
"FifoVideoWidth %1").arg(video_width));
899 LOG(VB_GENERAL, LOG_INFO,
900 QString(
"FifoVideoHeight %1").arg(video_height));
901 LOG(VB_GENERAL, LOG_INFO,
902 QString(
"FifoVideoAspectRatio %1").arg(video_aspect));
903 LOG(VB_GENERAL, LOG_INFO,
904 QString(
"FifoVideoFrameRate %1").arg(video_frame_rate));
905 LOG(VB_GENERAL, LOG_INFO,
906 QString(
"FifoAudioFormat %1").arg(audio_codec_name));
907 LOG(VB_GENERAL, LOG_INFO,
908 QString(
"FifoAudioChannels %1").arg(arb->
m_channels));
909 LOG(VB_GENERAL, LOG_INFO,
916 unlink(outputname.toLocal8Bit().constData());
921 QString audfifo = fifodir + QString(
"/audout");
922 QString vidfifo = fifodir + QString(
"/vidout");
926 LOG(VB_GENERAL, LOG_INFO,
"Enforcing sync on fifos");
929 if (!
fifow->
FIFOInit(0, QString(
"video"), vidfifo, vidSize, 50) ||
930 !
fifow->
FIFOInit(1, QString(
"audio"), audfifo, audio_size, 25))
932 LOG(VB_GENERAL, LOG_ERR,
933 "Error initializing fifo writer. Aborting");
934 unlink(outputname.toLocal8Bit().constData());
936 return REENCODE_ERROR;
938 LOG(VB_GENERAL, LOG_INFO,
939 QString(
"Video %1x%2@%3fps Audio rate: %4")
940 .arg(video_width).arg(video_height)
941 .arg(video_frame_rate)
943 LOG(VB_GENERAL, LOG_INFO,
"Created fifos. Waiting for connection.");
946 bool forceKeyFrames = (
fifow == NULL) ? framecontrol :
false;
948 frm_dir_map_t::iterator dm_iter;
949 bool writekeyframe =
true;
951 int num_keyframes = 0;
955 long curFrameNum = 0;
957 long lastKeyFrame = 0;
960 long long lasttimecode = 0;
961 long long lastWrittenTime = 0;
962 long long timecodeOffset = 0;
965 float vidFrameTime = 1000.0f / video_frame_rate;
966 int wait_recover = 0;
969 bool first_loop =
true;
970 unsigned char *newFrame = (
unsigned char *)
av_malloc(frame.
size);
971 frame.
buf = newFrame;
973 struct SwsContext *scontext = NULL;
976 LOG(VB_GENERAL, LOG_INFO,
"Dumping Video and Audio data to fifos");
978 LOG(VB_GENERAL, LOG_INFO,
"Copying Audio while transcoding Video");
980 LOG(VB_GENERAL, LOG_INFO,
"Transcoding for HTTP Live Streaming");
982 LOG(VB_GENERAL, LOG_INFO,
"Transcoding to libavformat container");
984 LOG(VB_GENERAL, LOG_INFO,
"Transcoding Video and Audio");
994 cutter->
Activate(vidFrameTime * rateTimeConv, total_frame_count);
996 bool stopSignalled =
false;
1005 while ((!stopSignalled) &&
1006 (lastDecode = videoBuffer->
GetFrame(did_ff, is_key)))
1014 float new_aspect = lastDecode->
aspect;
1022 frame.
timecode = (
long long)(lasttimecode + vidFrameTime);
1026 frame.
buf = lastDecode->
buf;
1028 int audbufTime = (
int)(totalAudio / rateTimeConv);
1029 int auddelta = frame.
timecode - audbufTime;
1030 int vidTime = (
int)(curFrameNum * vidFrameTime + 0.5);
1031 int viddelta = frame.
timecode - vidTime;
1032 int delta = viddelta - auddelta;
1033 int absdelta = delta < 0 ? -delta : delta;
1034 if (absdelta < 500 && absdelta >= vidFrameTime)
1036 QString msg = QString(
"Audio is %1ms %2 video at # %3: "
1037 "auddelta=%4, viddelta=%5")
1039 .arg(((delta > 0) ?
"ahead of" :
"behind"))
1040 .arg((
int)curFrameNum)
1043 LOG(VB_GENERAL, LOG_INFO, msg);
1044 dropvideo = (delta > 0) ? 1 : -1;
1047 else if (delta >= 500 && delta < 10000)
1049 if (wait_recover == 0)
1054 else if (wait_recover == 1)
1058 while (delta > vidFrameTime)
1064 delta -= (
int)vidFrameTime;
1066 QString msg = QString(
"Added %1 blank video frames")
1068 LOG(VB_GENERAL, LOG_INFO, msg);
1069 curFrameNum += count;
1083 int buflen = (
int)(arb->audiobuffer_len / rateTimeConv);
1084 LOG(VB_GENERAL, LOG_DEBUG,
1085 QString(
"%1: video time: %2 audio time: %3 "
1086 "buf: %4 exp: %5 delta: %6")
1087 .arg(curFrameNum) .arg(frame.
timecode)
1088 .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime)
1106 LOG(VB_GENERAL, LOG_INFO,
"Dropping video frame");
1135 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, MythPlayer "
1136 "is not in raw audio mode.");
1138 unlink(outputname.toLocal8Bit().constData());
1142 videoBuffer->
stop();
1143 return REENCODE_ERROR;
1147 writekeyframe =
true;
1150 writekeyframe = is_key;
1165 lastKeyFrame = curFrameNum;
1175 (frame.
timecode - lasttimecode - (
int)vidFrameTime);
1183 if (video_aspect != new_aspect)
1185 video_aspect = new_aspect;
1191 if (video_width != buf_size.width() ||
1192 video_height != buf_size.height())
1194 video_width = buf_size.width();
1195 video_height = buf_size.height();
1197 LOG(VB_GENERAL, LOG_INFO,
1198 QString(
"Resizing from %1x%2 to %3x%4")
1199 .arg(video_width).arg(video_height)
1200 .arg(newWidth).arg(newHeight));
1208 writekeyframe =
true;
1211 if ((video_width == newWidth) && (video_height == newHeight))
1213 frame.
buf = lastDecode->
buf;
1217 frame.
buf = newFrame;
1218 avpicture_fill(&imageIn, lastDecode->
buf, PIX_FMT_YUV420P,
1219 video_width, video_height);
1220 avpicture_fill(&imageOut, frame.
buf, PIX_FMT_YUV420P,
1221 newWidth, newHeight);
1223 int bottomBand = (video_height == 1088) ? 8 : 0;
1224 scontext = sws_getCachedContext(scontext, video_width,
1225 video_height, PIX_FMT_YUV420P, newWidth,
1226 newHeight, PIX_FMT_YUV420P,
1227 SWS_FAST_BILINEAR, NULL, NULL, NULL);
1229 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1230 video_height - bottomBand,
1231 imageOut.data, imageOut.linesize);
1245 (frame.
timecode - lasttimecode - (
int)vidFrameTime);
1248 if (video_aspect != new_aspect)
1250 video_aspect = new_aspect;
1258 if (video_width != buf_size.width() ||
1259 video_height != buf_size.height())
1261 video_width = buf_size.width();
1262 video_height = buf_size.height();
1264 LOG(VB_GENERAL, LOG_INFO,
1265 QString(
"Resizing from %1x%2 to %3x%4")
1266 .arg(video_width).arg(video_height)
1267 .arg(newWidth).arg(newHeight));
1270 if ((video_width == newWidth) && (video_height == newHeight))
1272 frame.
buf = lastDecode->
buf;
1276 frame.
buf = newFrame;
1277 avpicture_fill(&imageIn, lastDecode->
buf, PIX_FMT_YUV420P,
1278 video_width, video_height);
1279 avpicture_fill(&imageOut, frame.
buf, PIX_FMT_YUV420P,
1280 newWidth, newHeight);
1282 int bottomBand = (video_height == 1088) ? 8 : 0;
1283 scontext = sws_getCachedContext(scontext, video_width,
1284 video_height, PIX_FMT_YUV420P, newWidth,
1285 newHeight, PIX_FMT_YUV420P,
1286 SWS_FAST_BILINEAR, NULL, NULL, NULL);
1288 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1289 video_height - bottomBand,
1290 imageOut.data, imageOut.linesize);
1295 while ((ab = arb->
GetData(lastWrittenTime)) != NULL)
1297 unsigned char *
buf = (
unsigned char *)ab->
data();
1302 long long tc = ab->
m_time - timecodeOffset;
1314 tc = ab->
m_time - timecodeOffset;
1325 ab->
m_time - timecodeOffset);
1328 LOG(VB_GENERAL, LOG_ERR,
1329 "Transcode: Encountered irrecoverable error in "
1335 videoBuffer->
stop();
1337 return REENCODE_ERROR;
1354 if (halfFramerate && !skippedLastFrame)
1356 skippedLastFrame =
true;
1360 skippedLastFrame =
false;
1364 (hlsSegmentFrames > hlsSegmentSize) &&
1373 hlsSegmentFrames = 0;
1398 LOG(VB_GENERAL, LOG_INFO,
1399 QString(
"Processed: %1 of %2 frames(%3 seconds)").
1400 arg((
long)curFrameNum).arg((
long)total_frame_count).
1401 arg((
long)(curFrameNum / video_frame_rate)));
1407 stopSignalled =
true;
1417 LOG(VB_GENERAL, LOG_NOTICE,
1418 "Transcoding aborted, cutlist updated");
1420 unlink(outputname.toLocal8Bit().constData());
1424 videoBuffer->
stop();
1425 return REENCODE_CUTLIST_CHANGE;
1428 if ((jobID >= 0) || (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_INFO)))
1432 LOG(VB_GENERAL, LOG_NOTICE,
1433 "Transcoding STOPped by JobQueue");
1435 unlink(outputname.toLocal8Bit().constData());
1439 videoBuffer->
stop();
1440 return REENCODE_STOPPED;
1443 float flagFPS = 0.0;
1444 float elapsed = flagTime.elapsed() / 1000.0;
1446 flagFPS = curFrameNum / elapsed;
1449 int percentage = curFrameNum * 100 / total_frame_count;
1456 QObject::tr(
"%1% Completed @ %2 fps.")
1457 .arg(percentage).arg(flagFPS));
1459 LOG(VB_GENERAL, LOG_INFO,
1460 QString(
"mythtranscode: %1% Completed @ %2 fps.")
1461 .arg(percentage).arg(flagFPS));
1473 sws_freeContext(scontext);
1528 videoBuffer->
stop();