12 #include <QMutexLocker>
13 #include <QStringList>
14 #include <QWaitCondition>
15 #include <QtAlgorithms>
18 #include "libmythbase/mythconfig.h"
45 #include "libavcodec/avcodec.h"
46 #include "libswscale/swscale.h"
49 #define LOC QString("Transcode: ")
70 long delta = curframe - lastkey;
81 int height,
int frameRate)
83 if (profileName.toLower() ==
"autodetect")
88 QString autoProfileName = QObject::tr(
"Autodetect from %1").arg(height);
89 if (frameRate == 25 || frameRate == 30)
90 autoProfileName +=
"i";
91 if (frameRate == 50 || frameRate == 60)
92 autoProfileName +=
"p";
95 LOG(VB_GENERAL, LOG_NOTICE,
96 QString(
"Transcode: Looking for autodetect profile: %1")
97 .arg(autoProfileName));
100 if (!result && encodingType ==
"MPEG-2")
103 autoProfileName =
"MPEG2";
105 if (!result && (encodingType ==
"MPEG-4" || encodingType ==
"RTjpeg"))
109 autoProfileName =
"RTjpeg/MPEG4";
113 LOG(VB_GENERAL, LOG_ERR,
114 QString(
"Transcode: Couldn't find profile for : %1")
120 LOG(VB_GENERAL, LOG_NOTICE,
121 QString(
"Transcode: Using autodetect profile: %1")
122 .arg(autoProfileName));
127 int profileID = profileName.toInt(&isNum);
129 if (isNum && profileID > 0)
133 LOG(VB_GENERAL, LOG_ERR, QString(
"Couldn't find profile #: %1")
143 if (player_ctx ==
m_ctx)
150 #if CONFIG_LIBMP3LAME
157 LOG(VB_GENERAL, LOG_ERR,
LOC +
158 QString(
"get_str_option(...%1): Option not in profile.").arg(name));
165 QString ret_str = get_str_option(
profile, name);
166 if (ret_str.isEmpty())
170 int ret_int = ret_str.toInt(&ok);
174 LOG(VB_GENERAL, LOG_ERR,
LOC +
175 QString(
"get_int_option(...%1): Option is not an int.").arg(name));
183 return get_int_option(
profile, name) != 0;
186 static void TranscodeWriteText(
void *ptr,
unsigned char *buf,
int len,
187 std::chrono::milliseconds timecode,
int pagenr)
190 nvr->WriteText(buf, len, timecode, pagenr);
192 #endif // CONFIG_LIBMP3LAME
195 const QString &outputname,
196 const QString &profileName,
197 bool honorCutList,
bool framecontrol,
198 int jobID,
const QString& fifodir,
199 bool fifo_info,
bool cleanCut,
205 QDateTime statustime = curtime;
207 std::unique_ptr<Cutter> cutter =
nullptr;
208 std::unique_ptr<MythAVFormatWriter> avfw =
nullptr;
209 std::unique_ptr<MythAVFormatWriter> avfw2 =
nullptr;
210 std::unique_ptr<HTTPLiveStream> hls =
nullptr;
211 int hlsSegmentSize = 0;
212 int hlsSegmentFrames = 0;
214 #if !CONFIG_LIBMP3LAME
229 hls->UpdateStatusMessage(
"Transcoding Starting");
239 #if CONFIG_LIBMP3LAME
242 LOG(VB_GENERAL, LOG_ERR,
243 "Not compiled with libmp3lame support");
256 LOG(VB_GENERAL, LOG_ERR,
257 QString(
"Transcoding aborted, error: '%1'")
262 player_ctx->SetRingBuffer(rb);
266 if (player ==
nullptr)
268 LOG(VB_GENERAL, LOG_ERR,
269 QString(
"Transcoding aborted, failed to retrieve MythPlayer object"));
275 player->SetWatchingRecording(
true);
280 statustime = statustime.addSecs(5);
286 player->GetAudio()->SetAudioOutput(audioOutput);
287 player->SetTranscoding(
true);
289 if (player->OpenFile() < 0)
291 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, error opening file.");
296 if (AudioTrackNo > -1)
298 LOG(VB_GENERAL, LOG_INFO,
299 QString(
"Set audiotrack number to %1").arg(AudioTrackNo));
303 long long total_frame_count = player->GetTotalFrameCount();
304 long long new_frame_count = total_frame_count;
307 LOG(VB_GENERAL, LOG_INFO,
"Honoring the cutlist while transcoding");
309 frm_dir_map_t::const_iterator it;
311 long long lastStart = 0;
313 if (deleteMap.empty())
316 for (it = deleteMap.cbegin(); it != deleteMap.cend(); ++it)
320 if (!cutStr.isEmpty())
322 cutStr += QString(
"%1-").arg((
long)it.key());
323 lastStart = it.key();
327 if (cutStr.isEmpty())
329 cutStr += QString(
"%1").arg((
long)it.key());
330 new_frame_count -= (it.key() - lastStart);
333 if (cutStr.isEmpty())
335 else if (cutStr.endsWith(
'-') && (total_frame_count > lastStart))
337 new_frame_count -= (total_frame_count - lastStart);
338 cutStr += QString(
"%1").arg(total_frame_count);
340 LOG(VB_GENERAL, LOG_INFO, QString(
"Cutlist : %1").arg(cutStr));
341 LOG(VB_GENERAL, LOG_INFO, QString(
"Original Length: %1 frames")
342 .arg((
long)total_frame_count));
343 LOG(VB_GENERAL, LOG_INFO, QString(
"New Length : %1 frames")
344 .arg((
long)new_frame_count));
349 LOG(VB_GENERAL, LOG_INFO,
"Transcoding aborted, cutlist changed");
354 curtime = curtime.addSecs(60);
357 player->GetAudio()->ReinitAudio();
358 QString encodingType = player->GetEncodingType();
359 bool copyvideo =
false;
360 bool copyaudio =
false;
362 QString vidsetting =
nullptr;
363 QString audsetting =
nullptr;
364 QString vidfilters =
nullptr;
366 QSize buf_size = player->GetVideoBufferSize();
367 int video_width = buf_size.width();
368 int video_height = buf_size.height();
370 if (video_height == 1088) {
371 LOG(VB_GENERAL, LOG_NOTICE,
372 "Found video height of 1088. This is unusual and "
373 "more than likely the video is actually 1080 so mythtranscode "
374 "will treat it as such.");
379 float video_frame_rate = player->GetFrameRate();
380 int newWidth = video_width;
381 int newHeight = video_height;
382 bool halfFramerate =
false;
383 bool skippedLastFrame =
false;
385 m_kfaTable =
new std::vector<struct kfatable_entry>;
403 if (newHeight > video_height)
405 newHeight = video_height;
415 if (newHeight == 0 && newWidth > 0)
416 newHeight = (int)(1.0F * newWidth / video_aspect);
417 else if (newWidth == 0 && newHeight > 0)
418 newWidth = (int)(1.0F * newHeight * video_aspect);
419 else if (newWidth == 0 && newHeight == 0)
422 newWidth = (int)(1.0F * 480 * video_aspect);
426 newHeight = (int)(1.0F * 640 / video_aspect);
431 newHeight = (newHeight + 15) & ~0xF;
432 newWidth = (newWidth + 15) & ~0xF;
434 avfw = std::make_unique<MythAVFormatWriter>();
437 LOG(VB_GENERAL, LOG_ERR,
438 "Transcoding aborted, error creating AVFormatWriter.");
444 avfw->SetHeight(newHeight);
445 avfw->SetWidth(newWidth);
446 avfw->SetAspect(video_aspect);
457 hls = std::make_unique<HTTPLiveStream>(inputname, newWidth, newHeight,
464 LOG(VB_GENERAL, LOG_ERR,
"Unable to create new stream");
470 int segmentSize = hls->GetSegmentSize();
472 LOG(VB_GENERAL, LOG_NOTICE,
473 QString(
"HLS: Using segment size of %1 seconds")
478 int audioOnlyBitrate = hls->GetAudioOnlyBitrate();
480 avfw2 = std::make_unique<MythAVFormatWriter>();
481 avfw2->SetContainer(
"mpegts");
482 avfw2->SetAudioCodec(
"aac");
483 avfw2->SetAudioBitrate(audioOnlyBitrate);
489 avfw->SetContainer(
"mpegts");
490 avfw->SetVideoCodec(
"libx264");
491 avfw->SetAudioCodec(
"aac");
493 hls->UpdateStatusMessage(
"Transcoding Starting");
494 hls->UpdateSizeInfo(newWidth, newHeight, video_width, video_height);
496 if (!hls->InitForWrite())
498 LOG(VB_GENERAL, LOG_ERR,
"hls->InitForWrite() failed");
503 if (video_frame_rate > 30)
505 halfFramerate =
true;
506 avfw->SetFramerate(video_frame_rate/2);
509 avfw2->SetFramerate(video_frame_rate/2);
511 hlsSegmentSize = (int)(segmentSize * video_frame_rate / 2);
515 avfw->SetFramerate(video_frame_rate);
518 avfw2->SetFramerate(video_frame_rate);
520 hlsSegmentSize = (int)(segmentSize * video_frame_rate);
523 avfw->SetKeyFrameDist(30);
525 avfw2->SetKeyFrameDist(30);
528 avfw->SetFilename(hls->GetCurrentFilename());
530 avfw2->SetFilename(hls->GetCurrentFilename(
true));
537 avfw->SetFilename(outputname);
538 avfw->SetFramerate(video_frame_rate);
539 avfw->SetKeyFrameDist(30);
546 LOG(VB_GENERAL, LOG_NOTICE,
547 QString(
"x264 HLS using: %1 threads, '%2' profile and '%3' tune")
548 .arg(QString::number(threads), preset, tune));
550 avfw->SetThreadCount(threads);
551 avfw->SetEncodingPreset(preset);
552 avfw->SetEncodingTune(tune);
555 avfw2->SetThreadCount(1);
559 LOG(VB_GENERAL, LOG_ERR,
"avfw->Init() failed");
564 if (!avfw->OpenFile())
566 LOG(VB_GENERAL, LOG_ERR,
"avfw->OpenFile() failed");
571 if (avfw2 && !avfw2->Init())
573 LOG(VB_GENERAL, LOG_ERR,
"avfw2->Init() failed");
578 if (avfw2 && !avfw2->OpenFile())
580 LOG(VB_GENERAL, LOG_ERR,
"avfw2->OpenFile() failed");
587 #if CONFIG_LIBMP3LAME
588 else if (fifodir.isEmpty())
590 if (!
GetProfile(profileName, encodingType, video_height,
591 (
int)round(video_frame_rate))) {
592 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, no profile found.");
598 QMap<QString, QString> recorderOptionsMap;
601 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
603 .split(
",", QString::SkipEmptyParts);
606 .split(
",", Qt::SkipEmptyParts);
611 QStringList tokens =
options[loop].split(
"=");
612 if (tokens.length() < 2)
614 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, invalid option settings.");
617 recorderOptionsMap[tokens[0]] = tokens[1];
623 vidsetting = get_str_option(
m_recProfile,
"videocodec");
624 audsetting = get_str_option(
m_recProfile,
"audiocodec");
625 vidfilters = get_str_option(
m_recProfile,
"transcodefilters");
627 if (encodingType ==
"MPEG-2" &&
630 LOG(VB_GENERAL, LOG_NOTICE,
"Switching to MPEG-2 transcoder.");
638 vidsetting = encodingType;
641 else if (get_bool_option(
m_recProfile,
"transcoderesize"))
643 int actualHeight = (video_height == 1088 ? 1080 : video_height);
650 if (newHeight == 0 && newWidth > 0)
651 newHeight = (int)(1.0 * newWidth * actualHeight / video_width);
652 else if (newWidth == 0 && newHeight > 0)
653 newWidth = (int)(1.0 * newHeight * video_width / actualHeight);
654 else if (newWidth == 0 && newHeight == 0)
657 newWidth = (int)(1.0 * 480 * video_width / actualHeight);
661 newHeight = (int)(1.0 * 640 * actualHeight / video_width);
665 if (encodingType.startsWith(
"mpeg", Qt::CaseInsensitive))
668 newHeight = (newHeight + 15) & ~0xF;
669 newWidth = (newWidth + 15) & ~0xF;
672 LOG(VB_GENERAL, LOG_INFO, QString(
"Resizing from %1x%2 to %3x%4")
673 .arg(video_width).arg(video_height)
674 .arg(newWidth).arg(newHeight));
682 m_nvr->SetOption(
"inpixfmt",
FMT_YV12);
684 m_nvr->SetOption(
"width", newWidth);
685 m_nvr->SetOption(
"height", newHeight);
690 m_nvr->SetFrameRate(video_frame_rate);
691 m_nvr->SetVideoAspect(video_aspect);
692 m_nvr->SetTranscoding(
true);
694 if ((vidsetting ==
"MPEG-4") ||
695 (recorderOptionsMap[
"videocodec"] ==
"mpeg4"))
697 m_nvr->SetOption(
"videocodec",
"mpeg4");
706 #ifdef USING_FFMPEG_THREADS
707 m_nvr->SetIntOption(
m_recProfile,
"encodingthreadcount");
710 else if ((vidsetting ==
"MPEG-2") ||
711 (recorderOptionsMap[
"videocodec"] ==
"mpeg2video"))
713 m_nvr->SetOption(
"videocodec",
"mpeg2video");
717 #ifdef USING_FFMPEG_THREADS
718 m_nvr->SetIntOption(
m_recProfile,
"encodingthreadcount");
721 else if ((vidsetting ==
"RTjpeg") ||
722 (recorderOptionsMap[
"videocodec"] ==
"rtjpeg"))
724 m_nvr->SetOption(
"videocodec",
"rtjpeg");
726 m_nvr->SetIntOption(
m_recProfile,
"rtjpegchromafilter");
729 else if (vidsetting.isEmpty())
731 LOG(VB_GENERAL, LOG_ERR,
"No video information found!");
732 LOG(VB_GENERAL, LOG_ERR,
"Please ensure that recording profiles "
733 "for the transcoder are set");
739 LOG(VB_GENERAL, LOG_ERR,
740 QString(
"Unknown video codec: %1").arg(vidsetting));
746 if (audsetting ==
"MP3")
748 m_nvr->SetOption(
"audiocompression", 1);
752 else if (audsetting ==
"Uncompressed")
754 m_nvr->SetOption(
"audiocompression", 0);
758 LOG(VB_GENERAL, LOG_ERR,
759 QString(
"Unknown audio codec: %1").arg(audsetting));
762 m_nvr->AudioInit(
true);
765 if (!recorderOptionsMap.empty())
767 QMap<QString, QString>::Iterator it;
770 for (it = recorderOptionsMap.begin();
771 it != recorderOptionsMap.end(); ++it)
776 LOG(VB_GENERAL, LOG_NOTICE,
777 QString(
"Forcing Recorder option '%1' to '%2'")
780 static const QRegularExpression kNonDigitRE {
"\\D" };
781 if (value.contains(kNonDigitRE))
782 m_nvr->SetOption(key, value);
784 m_nvr->SetOption(key, value.toInt());
787 newWidth = (value.toInt() + 15) & ~0xF;
788 else if (key ==
"height")
789 newHeight = (value.toInt() + 15) & ~0xF;
790 else if (key ==
"videocodec")
792 if (value ==
"mpeg4")
793 vidsetting =
"MPEG-4";
794 else if (value ==
"mpeg2video")
795 vidsetting =
"MPEG-2";
796 else if (value ==
"rtjpeg")
797 vidsetting =
"RTjpeg";
802 if ((vidsetting ==
"MPEG-4") ||
803 (vidsetting ==
"MPEG-2"))
804 m_nvr->SetupAVCodecVideo();
805 else if (vidsetting ==
"RTjpeg")
806 m_nvr->SetupRTjpeg();
810 m_nvr->WriteHeader();
811 m_nvr->StreamAllocate();
814 if (vidsetting == encodingType && !framecontrol && !
m_avfMode &&
815 fifodir.isEmpty() && honorCutList &&
816 video_width == newWidth && video_height == newHeight)
819 LOG(VB_GENERAL, LOG_INFO,
"Reencoding video in 'raw' mode");
821 #endif // CONFIG_LIBMP3LAME
823 if (honorCutList && !deleteMap.empty())
830 cutter = std::make_unique<Cutter>();
831 cutter->SetCutList(deleteMap,
m_ctx);
832 player->SetCutList(cutter->AdjustedCutList());
837 player->SetCutList(deleteMap);
841 player->InitForTranscode(copyaudio, copyvideo);
842 if (player->IsErrored())
844 LOG(VB_GENERAL, LOG_ERR,
845 "Unable to initialize MythPlayer for Transcode");
851 if (
m_hlsMode && player->GetVideoOutput())
858 bool nonAligned = vidsetting ==
"RTjpeg" || !fifodir.isEmpty();
859 bool rescale = (video_width != newWidth) || (video_height != newHeight) || nonAligned;
870 video_width, video_height == 1080 ? 1088 : video_height, 0 );
874 frame.
Init(
FMT_YV12, newbuffer, newSize, video_width, video_height,
nullptr, 0);
882 if (!fifodir.isEmpty())
885 const char *audio_codec_name {
nullptr};
889 case AV_CODEC_ID_AC3:
890 audio_codec_name =
"ac3";
892 case AV_CODEC_ID_EAC3:
893 audio_codec_name =
"eac3";
895 case AV_CODEC_ID_DTS:
896 audio_codec_name =
"dts";
898 case AV_CODEC_ID_TRUEHD:
899 audio_codec_name =
"truehd";
901 case AV_CODEC_ID_MP3:
902 audio_codec_name =
"mp3";
904 case AV_CODEC_ID_MP2:
905 audio_codec_name =
"mp2";
907 case AV_CODEC_ID_AAC:
908 audio_codec_name =
"aac";
910 case AV_CODEC_ID_AAC_LATM:
911 audio_codec_name =
"aac_latm";
914 audio_codec_name =
"unknown";
918 audio_codec_name =
"raw";
921 if (honorCutList && fifo_info)
925 player->TranscodeGetNextFrame(did_ff, is_key,
true);
927 QSize buf_size2 = player->GetVideoBufferSize();
928 video_width = buf_size2.width();
929 video_height = buf_size2.height();
930 video_aspect = player->GetVideoAspect();
931 video_frame_rate = player->GetFrameRate();
935 LOG(VB_GENERAL, LOG_INFO,
936 QString(
"FifoVideoWidth %1").arg(video_width));
937 LOG(VB_GENERAL, LOG_INFO,
938 QString(
"FifoVideoHeight %1").arg(video_height));
939 LOG(VB_GENERAL, LOG_INFO,
940 QString(
"FifoVideoAspectRatio %1").arg(video_aspect));
941 LOG(VB_GENERAL, LOG_INFO,
942 QString(
"FifoVideoFrameRate %1").arg(video_frame_rate));
943 LOG(VB_GENERAL, LOG_INFO,
944 QString(
"FifoAudioFormat %1").arg(audio_codec_name));
945 LOG(VB_GENERAL, LOG_INFO,
946 QString(
"FifoAudioChannels %1").arg(arb->
m_channels));
947 LOG(VB_GENERAL, LOG_INFO,
954 unlink(outputname.toLocal8Bit().constData());
959 QString audfifo = fifodir + QString(
"/audout");
960 QString vidfifo = fifodir + QString(
"/vidout");
964 LOG(VB_GENERAL, LOG_INFO,
"Enforcing sync on fifos");
970 LOG(VB_GENERAL, LOG_ERR,
971 "Error initializing fifo writer. Aborting");
972 unlink(outputname.toLocal8Bit().constData());
976 LOG(VB_GENERAL, LOG_INFO,
977 QString(
"Video %1x%2@%3fps Audio rate: %4")
978 .arg(video_width).arg(video_height)
979 .arg(video_frame_rate)
981 LOG(VB_GENERAL, LOG_INFO,
"Created fifos. Waiting for connection.");
984 #if CONFIG_LIBMP3LAME
985 bool forceKeyFrames = (
m_fifow ==
nullptr) ? framecontrol :
false;
986 bool writekeyframe =
true;
987 long lastKeyFrame = 0;
988 int num_keyframes = 0;
991 frm_dir_map_t::iterator dm_iter;
995 long curFrameNum = 0;
1000 std::chrono::milliseconds lasttimecode = 0ms;
1002 std::chrono::milliseconds lastWrittenTime = 0ms;
1004 std::chrono::milliseconds timecodeOffset = 0ms;
1007 float vidFrameTime = 1000.0F / video_frame_rate;
1009 int wait_recover = 0;
1011 bool is_key =
false;
1012 bool first_loop =
true;
1015 struct SwsContext *scontext =
nullptr;
1018 LOG(VB_GENERAL, LOG_INFO,
"Dumping Video and Audio data to fifos");
1020 LOG(VB_GENERAL, LOG_INFO,
"Copying Audio while transcoding Video");
1022 LOG(VB_GENERAL, LOG_INFO,
"Transcoding for HTTP Live Streaming");
1024 LOG(VB_GENERAL, LOG_INFO,
"Transcoding to libavformat container");
1026 LOG(VB_GENERAL, LOG_INFO,
"Transcoding Video and Audio");
1032 QElapsedTimer flagTime;
1036 cutter->Activate(vidFrameTime * rateTimeConv, total_frame_count);
1038 bool stopSignalled =
false;
1044 hls->UpdateStatusMessage(
"Transcoding");
1047 while ((!stopSignalled) &&
1048 (lastDecode = videoBuffer->GetFrame(did_ff, is_key)))
1052 copyaudio = player->GetRawAudioState();
1056 float new_aspect = lastDecode->
m_aspect;
1066 frame.
m_timecode = lasttimecode + vidFrameTimeMs;
1073 scontext = sws_getCachedContext(scontext,
1076 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1079 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1080 lastDecode->
m_height, imageOut.data, imageOut.linesize);
1084 std::chrono::milliseconds auddelta = frame.
m_timecode - audbufTime;
1086 std::chrono::milliseconds viddelta = frame.
m_timecode - vidTime;
1087 std::chrono::milliseconds delta = viddelta - auddelta;
1088 std::chrono::milliseconds absdelta = std::chrono::abs(delta);
1089 if (absdelta < 500ms && absdelta >= vidFrameTimeMs)
1091 QString msg = QString(
"Audio is %1ms %2 video at # %3: "
1092 "auddelta=%4, viddelta=%5")
1093 .arg(absdelta.count())
1094 .arg(((delta > 0ms) ?
"ahead of" :
"behind"))
1095 .arg((
int)curFrameNum)
1096 .arg(auddelta.count())
1097 .arg(viddelta.count());
1098 LOG(VB_GENERAL, LOG_INFO, msg);
1099 dropvideo = (delta > 0ms) ? 1 : -1;
1102 else if (delta >= 500ms && delta < 10s)
1104 if (wait_recover == 0)
1109 else if (wait_recover == 1)
1113 while (delta > vidFrameTimeMs)
1115 if (!cutter || !cutter->InhibitDummyFrame())
1119 delta -= vidFrameTimeMs;
1121 QString msg = QString(
"Added %1 blank video frames")
1123 LOG(VB_GENERAL, LOG_INFO, msg);
1124 curFrameNum += count;
1138 int buflen = (int)(arb->audiobuffer_len / rateTimeConv);
1139 LOG(VB_GENERAL, LOG_DEBUG,
1140 QString(
"%1: video time: %2 audio time: %3 "
1141 "buf: %4 exp: %5 delta: %6")
1142 .arg(curFrameNum) .arg(frame.
m_timecode.count())
1143 .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime.count())
1144 .arg(delta.count()));
1150 !cutter->InhibitUseAudioFrames(ab->
m_frames, &totalAudio))
1158 if (cutter && cutter->InhibitDropFrame())
1161 LOG(VB_GENERAL, LOG_INFO,
"Dropping video frame");
1167 if (!cutter || !cutter->InhibitUseVideoFrame())
1172 if (!cutter || !cutter->InhibitDummyFrame())
1180 player->GetCC608Reader()->FlushTxtBuffers();
1185 #if CONFIG_LIBMP3LAME
1188 if (!player->GetRawAudioState())
1191 LOG(VB_GENERAL, LOG_ERR,
"Transcoding aborted, MythPlayer "
1192 "is not in raw audio mode.");
1194 unlink(outputname.toLocal8Bit().constData());
1197 videoBuffer->stop();
1201 hls->UpdateStatusMessage(
"Transcoding Errored");
1207 writekeyframe =
true;
1210 writekeyframe = is_key;
1221 player->UpdateStoredFrameNum(curFrameNum);
1222 m_nvr->UpdateSeekTable(num_keyframes, sync_offset);
1225 lastKeyFrame = curFrameNum;
1234 timecodeOffset += (frame.
m_timecode - lasttimecode - vidFrameTimeMs);
1240 if (!player->WriteStoredData(
m_outBuffer, (did_ff == 0), timecodeOffset))
1242 if (video_aspect != new_aspect)
1244 video_aspect = new_aspect;
1245 m_nvr->SetNewVideoParams(video_aspect);
1248 QSize buf_size3 = player->GetVideoBufferSize();
1250 if (video_width != buf_size3.width() ||
1251 video_height != buf_size3.height())
1253 video_width = buf_size3.width();
1254 video_height = buf_size3.height();
1256 LOG(VB_GENERAL, LOG_INFO,
1257 QString(
"Resizing from %1x%2 to %3x%4")
1258 .arg(video_width).arg(video_height)
1259 .arg(newWidth).arg(newHeight));
1267 writekeyframe =
true;
1275 int bottomBand = (lastDecode->
m_height == 1088) ? 8 : 0;
1276 scontext = sws_getCachedContext(scontext,
1279 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1281 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1283 imageOut.data, imageOut.linesize);
1286 m_nvr->WriteVideo(rescale ? &frame : lastDecode,
true, writekeyframe);
1288 player->GetCC608Reader()->FlushTxtBuffers();
1290 LOG(VB_GENERAL, LOG_ERR,
1291 "Not compiled with libmp3lame support. Should never get here");
1293 #endif // CONFIG_LIBMP3LAME
1300 timecodeOffset += (frame.
m_timecode - lasttimecode -
1304 if (video_aspect != new_aspect)
1306 video_aspect = new_aspect;
1307 #if CONFIG_LIBMP3LAME
1309 m_nvr->SetNewVideoParams(video_aspect);
1314 QSize buf_size4 = player->GetVideoBufferSize();
1316 if (video_width != buf_size4.width() ||
1317 video_height != buf_size4.height())
1319 video_width = buf_size4.width();
1320 video_height = buf_size4.height();
1322 LOG(VB_GENERAL, LOG_INFO,
1323 QString(
"Resizing from %1x%2 to %3x%4")
1324 .arg(video_width).arg(video_height)
1325 .arg(newWidth).arg(newHeight));
1333 int bottomBand = (lastDecode->
m_height == 1088) ? 8 : 0;
1334 scontext = sws_getCachedContext(scontext,
1337 SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
1339 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
1341 imageOut.data, imageOut.linesize);
1346 while ((ab = arb->
GetData(lastWrittenTime)) !=
nullptr)
1348 auto *buf = (
unsigned char *)ab->
data();
1353 std::chrono::milliseconds tc = ab->
m_time - timecodeOffset;
1354 avfw->WriteAudioFrame(buf, audioFrame, tc);
1358 if ((avfw2->GetTimecodeOffset() == -1ms) &&
1359 (avfw->GetTimecodeOffset() != -1ms))
1361 avfw2->SetTimecodeOffset(
1362 avfw->GetTimecodeOffset());
1365 tc = ab->
m_time - timecodeOffset;
1366 avfw2->WriteAudioFrame(buf, audioFrame, tc);
1372 #if CONFIG_LIBMP3LAME
1375 m_nvr->SetOption(
"audioframesize", ab->
size());
1376 m_nvr->WriteAudio(buf, audioFrame++,
1377 (ab->
m_time - timecodeOffset));
1378 if (m_nvr->IsErrored())
1380 LOG(VB_GENERAL, LOG_ERR,
1381 "Transcode: Encountered irrecoverable error in "
1385 videoBuffer->stop();
1396 #if CONFIG_LIBMP3LAME
1397 player->GetCC608Reader()->
1398 TranscodeWriteText(&TranscodeWriteText, (
void *)(m_nvr));
1400 LOG(VB_GENERAL, LOG_ERR,
1401 "Not compiled with libmp3lame support");
1410 if (halfFramerate && !skippedLastFrame)
1412 skippedLastFrame =
true;
1416 skippedLastFrame =
false;
1419 (avfw->GetFramesWritten()) &&
1420 (hlsSegmentFrames > hlsSegmentSize) &&
1421 (avfw->NextFrameIsKeyFrame()))
1424 avfw->ReOpen(hls->GetCurrentFilename());
1427 avfw2->ReOpen(hls->GetCurrentFilename(
true));
1429 hlsSegmentFrames = 0;
1432 if (avfw->WriteVideoFrame(rescale ? &frame : lastDecode) > 0)
1434 lastWrittenTime = frame.
m_timecode + timecodeOffset;
1441 #if CONFIG_LIBMP3LAME
1445 m_nvr->WriteVideo(rescale ? &frame : lastDecode,
true,
true);
1447 m_nvr->WriteVideo(rescale ? &frame : lastDecode);
1448 lastWrittenTime = frame.
m_timecode + timecodeOffset;
1456 LOG(VB_GENERAL, LOG_INFO,
1457 QString(
"Processed: %1 of %2 frames(%3 seconds)").
1458 arg(curFrameNum).arg((
long)total_frame_count).
1459 arg((
long)(curFrameNum / video_frame_rate)));
1462 if (hls && hls->CheckStop())
1465 stopSignalled =
true;
1475 LOG(VB_GENERAL, LOG_NOTICE,
1476 "Transcoding aborted, cutlist updated");
1478 unlink(outputname.toLocal8Bit().constData());
1481 videoBuffer->stop();
1489 LOG(VB_GENERAL, LOG_NOTICE,
1490 "Transcoding STOPped by JobQueue");
1492 unlink(outputname.toLocal8Bit().constData());
1495 videoBuffer->stop();
1499 hls->UpdateStatusMessage(
"Transcoding Stopped");
1504 float flagFPS = 0.0;
1505 float elapsed = flagTime.elapsed() / 1000.0;
1506 if (elapsed != 0.0F)
1507 flagFPS = curFrameNum / elapsed;
1509 total_frame_count = player->GetCurrentFrameCount();
1510 int percentage = curFrameNum * 100 / total_frame_count;
1513 hls->UpdatePercentComplete(percentage);
1518 QObject::tr(
"%1% Completed @ %2 fps.")
1519 .arg(percentage).arg(flagFPS));
1523 LOG(VB_GENERAL, LOG_INFO,
1524 QString(
"mythtranscode: %1% Completed @ %2 fps.")
1525 .arg(percentage).arg(flagFPS));
1535 player->DiscardVideoFrame(lastDecode);
1538 sws_freeContext(scontext);
1556 #if CONFIG_LIBMP3LAME
1559 m_nvr->WriteSeekTable();
1561 m_nvr->WriteKeyFrameAdjustTable(*
m_kfaTable);
1563 #endif // CONFIG_LIBMP3LAME
1573 hls->UpdateStatusMessage(
"Transcoding Completed");
1574 hls->UpdatePercentComplete(100);
1579 hls->UpdateStatusMessage(
"Transcoding Stopped");
1584 videoBuffer->stop();