12 #include <QMutexLocker>
13 #include <QRegularExpression>
14 #include <QStringList>
15 #include <QWaitCondition>
16 #include <QtAlgorithms>
19 #include "libmythbase/mythconfig.h"
46 #include "libavcodec/avcodec.h"
47 #include "libswscale/swscale.h"
50 #define LOC QString("Transcode: ")
71 long delta = curframe - lastkey;
82 int height,
int frameRate)
84 if (profileName.toLower() ==
"autodetect")
89 QString autoProfileName = QObject::tr(
"Autodetect from %1").arg(height);
90 if (frameRate == 25 || frameRate == 30)
91 autoProfileName +=
"i";
92 if (frameRate == 50 || frameRate == 60)
93 autoProfileName +=
"p";
96 LOG(VB_GENERAL, LOG_NOTICE,
97 QString(
"Transcode: Looking for autodetect profile: %1")
98 .arg(autoProfileName));
101 if (!result && encodingType ==
"MPEG-2")
104 autoProfileName =
"MPEG2";
106 if (!result && (encodingType ==
"MPEG-4" || encodingType ==
"RTjpeg"))
110 autoProfileName =
"RTjpeg/MPEG4";
114 LOG(VB_GENERAL, LOG_ERR,
115 QString(
"Transcode: Couldn't find profile for : %1")
121 LOG(VB_GENERAL, LOG_NOTICE,
122 QString(
"Transcode: Using autodetect profile: %1")
123 .arg(autoProfileName));
128 int profileID = profileName.toInt(&isNum);
130 if (isNum && profileID > 0)
134 LOG(VB_GENERAL, LOG_ERR, QString(
"Couldn't find profile #: %1")
144 if (player_ctx ==
m_ctx)
151 #if CONFIG_LIBMP3LAME
158 LOG(VB_GENERAL, LOG_ERR,
LOC +
159 QString(
"get_str_option(...%1): Option not in profile.").arg(name));
166 QString ret_str = get_str_option(
profile, name);
167 if (ret_str.isEmpty())
171 int ret_int = ret_str.toInt(&ok);
175 LOG(VB_GENERAL, LOG_ERR,
LOC +
176 QString(
"get_int_option(...%1): Option is not an int.").arg(name));
184 return get_int_option(
profile, name) != 0;
187 static void TranscodeWriteText(
void *ptr,
unsigned char *buf,
int len,
188 std::chrono::milliseconds timecode,
int pagenr)
191 nvr->WriteText(buf, len, timecode, pagenr);
193 #endif // CONFIG_LIBMP3LAME
196 const QString &outputname,
197 [[maybe_unused]]
const QString &profileName,
198 bool honorCutList,
bool framecontrol,
199 int jobID,
const QString& fifodir,
200 bool fifo_info,
bool cleanCut,
206 QDateTime statustime = curtime;
208 std::unique_ptr<Cutter> cutter =
nullptr;
209 std::unique_ptr<MythAVFormatWriter> avfw =
nullptr;
210 std::unique_ptr<MythAVFormatWriter> avfw2 =
nullptr;
211 std::unique_ptr<HTTPLiveStream> hls =
nullptr;
212 int hlsSegmentSize = 0;
213 int hlsSegmentFrames = 0;
236 #if CONFIG_LIBMP3LAME
239 LOG(VB_GENERAL, LOG_ERR,
240 "Not compiled with libmp3lame support");
253 LOG(VB_GENERAL, LOG_ERR,
254 QString(
"Transcoding aborted, error: '%1'")
259 player_ctx->SetRingBuffer(rb);
263 if (player ==
nullptr)
265 LOG(VB_GENERAL, LOG_ERR,
266 QString(
"Transcoding aborted, failed to retrieve MythPlayer object"));
272 player->SetWatchingRecording(
true);
277 statustime = statustime.addSecs(5);
283 player->GetAudio()->SetAudioOutput(audioOutput);
284 player->SetTranscoding(
true);
286 if (player->OpenFile() < 0)
288 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, error opening file.");
293 if (AudioTrackNo > -1)
295 LOG(VB_GENERAL, LOG_INFO,
296 QString(
"Set audiotrack number to %1").arg(AudioTrackNo));
300 long long total_frame_count = player->GetTotalFrameCount();
301 long long new_frame_count = total_frame_count;
304 LOG(VB_GENERAL, LOG_INFO,
"Honoring the cutlist while transcoding");
306 frm_dir_map_t::const_iterator it;
308 long long lastStart = 0;
310 if (deleteMap.empty())
313 for (it = deleteMap.cbegin(); it != deleteMap.cend(); ++it)
317 if (!cutStr.isEmpty())
319 cutStr += QString(
"%1-").arg((
long)it.key());
320 lastStart = it.key();
324 if (cutStr.isEmpty())
326 cutStr += QString(
"%1").arg((
long)it.key());
327 new_frame_count -= (it.key() - lastStart);
330 if (cutStr.isEmpty())
332 else if (cutStr.endsWith(
'-') && (total_frame_count > lastStart))
334 new_frame_count -= (total_frame_count - lastStart);
335 cutStr += QString(
"%1").arg(total_frame_count);
337 LOG(VB_GENERAL, LOG_INFO, QString(
"Cutlist : %1").arg(cutStr));
338 LOG(VB_GENERAL, LOG_INFO, QString(
"Original Length: %1 frames")
339 .arg((
long)total_frame_count));
340 LOG(VB_GENERAL, LOG_INFO, QString(
"New Length : %1 frames")
341 .arg((
long)new_frame_count));
346 LOG(VB_GENERAL, LOG_INFO,
"Transcoding aborted, cutlist changed");
351 curtime = curtime.addSecs(60);
354 player->GetAudio()->ReinitAudio();
355 QString encodingType = player->GetEncodingType();
356 bool copyvideo =
false;
357 bool copyaudio =
false;
359 QString vidsetting =
nullptr;
360 QString audsetting =
nullptr;
361 QString vidfilters =
nullptr;
363 QSize buf_size = player->GetVideoBufferSize();
364 int video_width = buf_size.width();
365 int video_height = buf_size.height();
367 if (video_height == 1088) {
368 LOG(VB_GENERAL, LOG_NOTICE,
369 "Found video height of 1088. This is unusual and "
370 "more than likely the video is actually 1080 so mythtranscode "
371 "will treat it as such.");
376 float video_frame_rate = player->GetFrameRate();
377 int newWidth = video_width;
378 int newHeight = video_height;
379 bool halfFramerate =
false;
380 bool skippedLastFrame =
false;
382 m_kfaTable =
new std::vector<struct kfatable_entry>;
400 if (newHeight > video_height)
402 newHeight = video_height;
412 if (newHeight == 0 && newWidth > 0)
413 newHeight = (int)(1.0F * newWidth / video_aspect);
414 else if (newWidth == 0 && newHeight > 0)
415 newWidth = (int)(1.0F * newHeight * video_aspect);
416 else if (newWidth == 0 && newHeight == 0)
419 newWidth = (int)(1.0F * 480 * video_aspect);
423 newHeight = (int)(1.0F * 640 / video_aspect);
428 newHeight = (newHeight + 15) & ~0xF;
429 newWidth = (newWidth + 15) & ~0xF;
431 avfw = std::make_unique<MythAVFormatWriter>();
434 LOG(VB_GENERAL, LOG_ERR,
435 "Transcoding aborted, error creating AVFormatWriter.");
454 hls = std::make_unique<HTTPLiveStream>(inputname, newWidth, newHeight,
461 LOG(VB_GENERAL, LOG_ERR,
"Unable to create new stream");
469 LOG(VB_GENERAL, LOG_NOTICE,
470 QString(
"HLS: Using segment size of %1 seconds")
477 avfw2 = std::make_unique<MythAVFormatWriter>();
491 hls->
UpdateSizeInfo(newWidth, newHeight, video_width, video_height);
495 LOG(VB_GENERAL, LOG_ERR,
"hls->InitForWrite() failed");
500 if (video_frame_rate > 30)
502 halfFramerate =
true;
508 hlsSegmentSize = (int)(segmentSize * video_frame_rate / 2);
517 hlsSegmentSize = (int)(segmentSize * video_frame_rate);
543 LOG(VB_GENERAL, LOG_NOTICE,
544 QString(
"x264 HLS using: %1 threads, '%2' profile and '%3' tune")
545 .arg(QString::number(threads), preset, tune));
556 LOG(VB_GENERAL, LOG_ERR,
"avfw->Init() failed");
563 LOG(VB_GENERAL, LOG_ERR,
"avfw->OpenFile() failed");
568 if (avfw2 && !avfw2->
Init())
570 LOG(VB_GENERAL, LOG_ERR,
"avfw2->Init() failed");
577 LOG(VB_GENERAL, LOG_ERR,
"avfw2->OpenFile() failed");
584 #if CONFIG_LIBMP3LAME
585 else if (fifodir.isEmpty())
587 if (!
GetProfile(profileName, encodingType, video_height,
588 (
int)round(video_frame_rate))) {
589 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, no profile found.");
595 QMap<QString, QString> recorderOptionsMap;
599 .split(
",", Qt::SkipEmptyParts);
603 QStringList tokens =
options[loop].split(
"=");
604 if (tokens.length() < 2)
606 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, invalid option settings.");
609 recorderOptionsMap[tokens[0]] = tokens[1];
615 vidsetting = get_str_option(
m_recProfile,
"videocodec");
616 audsetting = get_str_option(
m_recProfile,
"audiocodec");
617 vidfilters = get_str_option(
m_recProfile,
"transcodefilters");
619 if (encodingType ==
"MPEG-2" &&
622 LOG(VB_GENERAL, LOG_NOTICE,
"Switching to MPEG-2 transcoder.");
630 vidsetting = encodingType;
633 else if (get_bool_option(
m_recProfile,
"transcoderesize"))
635 int actualHeight = (video_height == 1088 ? 1080 : video_height);
642 if (newHeight == 0 && newWidth > 0)
643 newHeight = (int)(1.0 * newWidth * actualHeight / video_width);
644 else if (newWidth == 0 && newHeight > 0)
645 newWidth = (int)(1.0 * newHeight * video_width / actualHeight);
646 else if (newWidth == 0 && newHeight == 0)
649 newWidth = (int)(1.0 * 480 * video_width / actualHeight);
653 newHeight = (int)(1.0 * 640 * actualHeight / video_width);
657 if (encodingType.startsWith(
"mpeg", Qt::CaseInsensitive))
660 newHeight = (newHeight + 15) & ~0xF;
661 newWidth = (newWidth + 15) & ~0xF;
664 LOG(VB_GENERAL, LOG_INFO, QString(
"Resizing from %1x%2 to %3x%4")
665 .arg(video_width).arg(video_height)
666 .arg(newWidth).arg(newHeight));
674 m_nvr->SetOption(
"inpixfmt",
FMT_YV12);
676 m_nvr->SetOption(
"width", newWidth);
677 m_nvr->SetOption(
"height", newHeight);
682 m_nvr->SetFrameRate(video_frame_rate);
683 m_nvr->SetVideoAspect(video_aspect);
684 m_nvr->SetTranscoding(
true);
686 if ((vidsetting ==
"MPEG-4") ||
687 (recorderOptionsMap[
"videocodec"] ==
"mpeg4"))
689 m_nvr->SetOption(
"videocodec",
"mpeg4");
698 #ifdef USING_FFMPEG_THREADS
699 m_nvr->SetIntOption(
m_recProfile,
"encodingthreadcount");
702 else if ((vidsetting ==
"MPEG-2") ||
703 (recorderOptionsMap[
"videocodec"] ==
"mpeg2video"))
705 m_nvr->SetOption(
"videocodec",
"mpeg2video");
709 #ifdef USING_FFMPEG_THREADS
710 m_nvr->SetIntOption(
m_recProfile,
"encodingthreadcount");
713 else if ((vidsetting ==
"RTjpeg") ||
714 (recorderOptionsMap[
"videocodec"] ==
"rtjpeg"))
716 m_nvr->SetOption(
"videocodec",
"rtjpeg");
718 m_nvr->SetIntOption(
m_recProfile,
"rtjpegchromafilter");
721 else if (vidsetting.isEmpty())
723 LOG(VB_GENERAL, LOG_ERR,
"No video information found!");
724 LOG(VB_GENERAL, LOG_ERR,
"Please ensure that recording profiles "
725 "for the transcoder are set");
731 LOG(VB_GENERAL, LOG_ERR,
732 QString(
"Unknown video codec: %1").arg(vidsetting));
738 if (audsetting ==
"MP3")
740 m_nvr->SetOption(
"audiocompression", 1);
744 else if (audsetting ==
"Uncompressed")
746 m_nvr->SetOption(
"audiocompression", 0);
750 LOG(VB_GENERAL, LOG_ERR,
751 QString(
"Unknown audio codec: %1").arg(audsetting));
754 m_nvr->AudioInit(
true);
757 if (!recorderOptionsMap.empty())
759 QMap<QString, QString>::Iterator it;
762 for (it = recorderOptionsMap.begin();
763 it != recorderOptionsMap.end(); ++it)
768 LOG(VB_GENERAL, LOG_NOTICE,
769 QString(
"Forcing Recorder option '%1' to '%2'")
772 static const QRegularExpression kNonDigitRE {
"\\D" };
773 if (value.contains(kNonDigitRE))
774 m_nvr->SetOption(key, value);
776 m_nvr->SetOption(key, value.toInt());
779 newWidth = (value.toInt() + 15) & ~0xF;
780 else if (key ==
"height")
781 newHeight = (value.toInt() + 15) & ~0xF;
782 else if (key ==
"videocodec")
784 if (value ==
"mpeg4")
785 vidsetting =
"MPEG-4";
786 else if (value ==
"mpeg2video")
787 vidsetting =
"MPEG-2";
788 else if (value ==
"rtjpeg")
789 vidsetting =
"RTjpeg";
794 if ((vidsetting ==
"MPEG-4") ||
795 (vidsetting ==
"MPEG-2"))
796 m_nvr->SetupAVCodecVideo();
797 else if (vidsetting ==
"RTjpeg")
798 m_nvr->SetupRTjpeg();
802 m_nvr->WriteHeader();
803 m_nvr->StreamAllocate();
806 if (vidsetting == encodingType && !framecontrol && !
m_avfMode &&
807 fifodir.isEmpty() && honorCutList &&
808 video_width == newWidth && video_height == newHeight)
811 LOG(VB_GENERAL, LOG_INFO,
"Reencoding video in 'raw' mode");
813 #endif // CONFIG_LIBMP3LAME
815 if (honorCutList && !deleteMap.empty())
822 cutter = std::make_unique<Cutter>();
829 player->SetCutList(deleteMap);
833 player->InitForTranscode(copyaudio, copyvideo);
834 if (player->IsErrored())
836 LOG(VB_GENERAL, LOG_ERR,
837 "Unable to initialize MythPlayer for Transcode");
843 if (
m_hlsMode && player->GetVideoOutput())
850 bool nonAligned = vidsetting ==
"RTjpeg" || !fifodir.isEmpty();
851 bool rescale = (video_width != newWidth) || (video_height != newHeight) || nonAligned;
862 video_width, video_height == 1080 ? 1088 : video_height, 0 );
866 frame.
Init(
FMT_YV12, newbuffer, newSize, video_width, video_height,
nullptr, 0);
874 if (!fifodir.isEmpty())
877 const char *audio_codec_name {
nullptr};
881 case AV_CODEC_ID_AC3:
882 audio_codec_name =
"ac3";
884 case AV_CODEC_ID_EAC3:
885 audio_codec_name =
"eac3";
887 case AV_CODEC_ID_DTS:
888 audio_codec_name =
"dts";
890 case AV_CODEC_ID_TRUEHD:
891 audio_codec_name =
"truehd";
893 case AV_CODEC_ID_MP3:
894 audio_codec_name =
"mp3";
896 case AV_CODEC_ID_MP2:
897 audio_codec_name =
"mp2";
899 case AV_CODEC_ID_AAC:
900 audio_codec_name =
"aac";
902 case AV_CODEC_ID_AAC_LATM:
903 audio_codec_name =
"aac_latm";
906 audio_codec_name =
"unknown";
910 audio_codec_name =
"raw";
913 if (honorCutList && fifo_info)
917 player->TranscodeGetNextFrame(did_ff, is_key,
true);
919 QSize buf_size2 = player->GetVideoBufferSize();
920 video_width = buf_size2.width();
921 video_height = buf_size2.height();
922 video_aspect = player->GetVideoAspect();
923 video_frame_rate = player->GetFrameRate();
927 LOG(VB_GENERAL, LOG_INFO,
928 QString(
"FifoVideoWidth %1").arg(video_width));
929 LOG(VB_GENERAL, LOG_INFO,
930 QString(
"FifoVideoHeight %1").arg(video_height));
931 LOG(VB_GENERAL, LOG_INFO,
932 QString(
"FifoVideoAspectRatio %1").arg(video_aspect));
933 LOG(VB_GENERAL, LOG_INFO,
934 QString(
"FifoVideoFrameRate %1").arg(video_frame_rate));
935 LOG(VB_GENERAL, LOG_INFO,
936 QString(
"FifoAudioFormat %1").arg(audio_codec_name));
937 LOG(VB_GENERAL, LOG_INFO,
938 QString(
"FifoAudioChannels %1").arg(arb->
m_channels));
939 LOG(VB_GENERAL, LOG_INFO,
946 unlink(outputname.toLocal8Bit().constData());
951 QString audfifo = fifodir + QString(
"/audout");
952 QString vidfifo = fifodir + QString(
"/vidout");
956 LOG(VB_GENERAL, LOG_INFO,
"Enforcing sync on fifos");
962 LOG(VB_GENERAL, LOG_ERR,
963 "Error initializing fifo writer. Aborting");
964 unlink(outputname.toLocal8Bit().constData());
968 LOG(VB_GENERAL, LOG_INFO,
969 QString(
"Video %1x%2@%3fps Audio rate: %4")
970 .arg(video_width).arg(video_height)
971 .arg(video_frame_rate)
973 LOG(VB_GENERAL, LOG_INFO,
"Created fifos. Waiting for connection.");
976 #if CONFIG_LIBMP3LAME
977 bool forceKeyFrames = (
m_fifow ==
nullptr) ? framecontrol :
false;
978 bool writekeyframe =
true;
979 long lastKeyFrame = 0;
980 int num_keyframes = 0;
983 frm_dir_map_t::iterator dm_iter;
987 long curFrameNum = 0;
992 std::chrono::milliseconds lasttimecode = 0ms;
994 std::chrono::milliseconds lastWrittenTime = 0ms;
996 std::chrono::milliseconds timecodeOffset = 0ms;
999 float vidFrameTime = 1000.0F / video_frame_rate;
1001 int wait_recover = 0;
1003 bool is_key =
false;
1004 bool first_loop =
true;
1007 struct SwsContext *scontext =
nullptr;
1010 LOG(VB_GENERAL, LOG_INFO,
"Dumping Video and Audio data to fifos");
1012 LOG(VB_GENERAL, LOG_INFO,
"Copying Audio while transcoding Video");
1014 LOG(VB_GENERAL, LOG_INFO,
"Transcoding for HTTP Live Streaming");
1016 LOG(VB_GENERAL, LOG_INFO,
"Transcoding to libavformat container");
1018 LOG(VB_GENERAL, LOG_INFO,
"Transcoding Video and Audio");
1024 QElapsedTimer flagTime;
1028 cutter->
Activate(vidFrameTime * rateTimeConv, total_frame_count);
1030 bool stopSignalled =
false;
1039 while ((!stopSignalled) &&
1040 (lastDecode = videoBuffer->GetFrame(did_ff, is_key)))
1044 copyaudio = player->GetRawAudioState();
1048 float new_aspect = lastDecode->
m_aspect;
1058 frame.
m_timecode = lasttimecode + vidFrameTimeMs;
1065 scontext = sws_getCachedContext(scontext,
1068 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1071 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1072 lastDecode->
m_height, imageOut.data, imageOut.linesize);
1076 std::chrono::milliseconds auddelta = frame.
m_timecode - audbufTime;
1078 std::chrono::milliseconds viddelta = frame.
m_timecode - vidTime;
1079 std::chrono::milliseconds delta = viddelta - auddelta;
1080 std::chrono::milliseconds absdelta = std::chrono::abs(delta);
1081 if (absdelta < 500ms && absdelta >= vidFrameTimeMs)
1083 QString msg = QString(
"Audio is %1ms %2 video at # %3: "
1084 "auddelta=%4, viddelta=%5")
1085 .arg(absdelta.count())
1086 .arg(((delta > 0ms) ?
"ahead of" :
"behind"))
1087 .arg((
int)curFrameNum)
1088 .arg(auddelta.count())
1089 .arg(viddelta.count());
1090 LOG(VB_GENERAL, LOG_INFO, msg);
1091 dropvideo = (delta > 0ms) ? 1 : -1;
1094 else if (delta >= 500ms && delta < 10s)
1096 if (wait_recover == 0)
1101 else if (wait_recover == 1)
1105 while (delta > vidFrameTimeMs)
1111 delta -= vidFrameTimeMs;
1113 QString msg = QString(
"Added %1 blank video frames")
1115 LOG(VB_GENERAL, LOG_INFO, msg);
1116 curFrameNum += count;
1132 int buflen = (int)(arb->audiobuffer_len / rateTimeConv);
1133 LOG(VB_GENERAL, LOG_DEBUG,
1134 QString(
"%1: video time: %2 audio time: %3 "
1135 "buf: %4 exp: %5 delta: %6")
1136 .arg(curFrameNum) .arg(frame.
m_timecode.count())
1137 .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime.count())
1138 .arg(delta.count()));
1155 LOG(VB_GENERAL, LOG_INFO,
"Dropping video frame");
1174 player->GetCC608Reader()->FlushTxtBuffers();
1179 #if CONFIG_LIBMP3LAME
1182 if (!player->GetRawAudioState())
1185 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, MythPlayer "
1186 "is not in raw audio mode.");
1188 unlink(outputname.toLocal8Bit().constData());
1191 videoBuffer->stop();
1201 writekeyframe =
true;
1204 writekeyframe = is_key;
1214 long sync_offset = 0;
1215 m_nvr->UpdateSeekTable(num_keyframes, sync_offset);
1218 lastKeyFrame = curFrameNum;
1227 timecodeOffset += (frame.
m_timecode - lasttimecode - vidFrameTimeMs);
1233 if ((did_ff != 0) || !copyvideo)
1235 if (video_aspect != new_aspect)
1237 video_aspect = new_aspect;
1238 m_nvr->SetNewVideoParams(video_aspect);
1241 QSize buf_size3 = player->GetVideoBufferSize();
1243 if (video_width != buf_size3.width() ||
1244 video_height != buf_size3.height())
1246 video_width = buf_size3.width();
1247 video_height = buf_size3.height();
1249 LOG(VB_GENERAL, LOG_INFO,
1250 QString(
"Resizing from %1x%2 to %3x%4")
1251 .arg(video_width).arg(video_height)
1252 .arg(newWidth).arg(newHeight));
1260 writekeyframe =
true;
1268 int bottomBand = (lastDecode->
m_height == 1088) ? 8 : 0;
1269 scontext = sws_getCachedContext(scontext,
1272 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1274 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1276 imageOut.data, imageOut.linesize);
1279 m_nvr->WriteVideo(rescale ? &frame : lastDecode,
true, writekeyframe);
1281 player->GetCC608Reader()->FlushTxtBuffers();
1283 LOG(VB_GENERAL, LOG_ERR,
1284 "Not compiled with libmp3lame support. Should never get here");
1286 #endif // CONFIG_LIBMP3LAME
1293 timecodeOffset += (frame.
m_timecode - lasttimecode -
1297 if (video_aspect != new_aspect)
1299 video_aspect = new_aspect;
1300 #if CONFIG_LIBMP3LAME
1302 m_nvr->SetNewVideoParams(video_aspect);
1307 QSize buf_size4 = player->GetVideoBufferSize();
1309 if (video_width != buf_size4.width() ||
1310 video_height != buf_size4.height())
1312 video_width = buf_size4.width();
1313 video_height = buf_size4.height();
1315 LOG(VB_GENERAL, LOG_INFO,
1316 QString(
"Resizing from %1x%2 to %3x%4")
1317 .arg(video_width).arg(video_height)
1318 .arg(newWidth).arg(newHeight));
1326 int bottomBand = (lastDecode->
m_height == 1088) ? 8 : 0;
1327 scontext = sws_getCachedContext(scontext,
1330 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1332 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1334 imageOut.data, imageOut.linesize);
1339 while ((ab = arb->
GetData(lastWrittenTime)) !=
nullptr)
1341 auto *buf = (
unsigned char *)ab->
data();
1346 std::chrono::milliseconds tc = ab->
m_time - timecodeOffset;
1358 tc = ab->
m_time - timecodeOffset;
1365 #if CONFIG_LIBMP3LAME
1368 m_nvr->SetOption(
"audioframesize", ab->
size());
1369 m_nvr->WriteAudio(buf, audioFrame++,
1370 (ab->
m_time - timecodeOffset));
1371 if (m_nvr->IsErrored())
1373 LOG(VB_GENERAL, LOG_ERR,
1374 "Transcode: Encountered irrecoverable error in "
1378 videoBuffer->stop();
1389 #if CONFIG_LIBMP3LAME
1390 player->GetCC608Reader()->
1391 TranscodeWriteText(&TranscodeWriteText, (
void *)(m_nvr));
1393 LOG(VB_GENERAL, LOG_ERR,
1394 "Not compiled with libmp3lame support");
1403 if (halfFramerate && !skippedLastFrame)
1405 skippedLastFrame =
true;
1409 skippedLastFrame =
false;
1413 (hlsSegmentFrames > hlsSegmentSize) &&
1422 hlsSegmentFrames = 0;
1427 lastWrittenTime = frame.
m_timecode + timecodeOffset;
1434 #if CONFIG_LIBMP3LAME
1438 m_nvr->WriteVideo(rescale ? &frame : lastDecode,
true,
true);
1440 m_nvr->WriteVideo(rescale ? &frame : lastDecode);
1441 lastWrittenTime = frame.
m_timecode + timecodeOffset;
1449 LOG(VB_GENERAL, LOG_INFO,
1450 QString(
"Processed: %1 of %2 frames(%3 seconds)").
1451 arg(curFrameNum).arg((
long)total_frame_count).
1452 arg((
long)(curFrameNum / video_frame_rate)));
1458 stopSignalled =
true;
1468 LOG(VB_GENERAL, LOG_NOTICE,
1469 "Transcoding aborted, cutlist updated");
1471 unlink(outputname.toLocal8Bit().constData());
1474 videoBuffer->stop();
1482 LOG(VB_GENERAL, LOG_NOTICE,
1483 "Transcoding STOPped by JobQueue");
1485 unlink(outputname.toLocal8Bit().constData());
1488 videoBuffer->stop();
1497 float flagFPS = 0.0;
1498 float elapsed = flagTime.elapsed() / 1000.0;
1499 if (elapsed != 0.0F)
1500 flagFPS = curFrameNum / elapsed;
1502 total_frame_count = player->GetCurrentFrameCount();
1503 int percentage = curFrameNum * 100 / total_frame_count;
1511 QObject::tr(
"%1% Completed @ %2 fps.")
1512 .arg(percentage).arg(flagFPS));
1516 LOG(VB_GENERAL, LOG_INFO,
1517 QString(
"mythtranscode: %1% Completed @ %2 fps.")
1518 .arg(percentage).arg(flagFPS));
1528 player->DiscardVideoFrame(lastDecode);
1531 sws_freeContext(scontext);
1549 #if CONFIG_LIBMP3LAME
1552 m_nvr->WriteSeekTable();
1554 m_nvr->WriteKeyFrameAdjustTable(*
m_kfaTable);
1556 #endif // CONFIG_LIBMP3LAME
1577 videoBuffer->stop();