From 2484daf141609efd68a5d90992f96b4a7319528a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 3 May 2021 13:56:54 +1000 Subject: [PATCH] implemented full support for interlaced footage Includes importing and exporting interlaced footage. Fixes #1340. --- app/codec/ffmpeg/ffmpegdecoder.cpp | 45 +++++++++++++++++++++++++++++- app/codec/ffmpeg/ffmpegdecoder.h | 2 ++ app/render/rendermanager.cpp | 8 ++++-- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 2666b0961..8ae47c820 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -204,6 +204,21 @@ int FFmpegDecoder::GetFilteredFrame(AVPacket* packet, AVFrame* output_frame, con ret = instance_.GetFrame(packet, working_frame); if (ret >= 0) { + // Override this frame's interlacing parameters from user + switch (filter_params_.src_interlacing) { + case VideoParams::kInterlaceNone: + working_frame->interlaced_frame = 0; + break; + case VideoParams::kInterlacedTopFirst: + working_frame->interlaced_frame = 1; + working_frame->top_field_first = 1; + break; + case VideoParams::kInterlacedBottomFirst: + working_frame->interlaced_frame = 1; + working_frame->top_field_first = 0; + break; + } + // If succeeded in pulling from codec, send to buffer source ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, working_frame, AV_BUFFERSRC_FLAG_KEEP_REF); @@ -559,6 +574,15 @@ uint64_t FFmpegDecoder::ValidateChannelLayout(AVStream* stream) return av_get_default_channel_layout(stream->codecpar->channels); } +const char *FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing) +{ + if (interlacing == VideoParams::kInterlacedTopFirst) { + return "tff"; + } else { + return "bff"; + } +} + /* OLD UNUSED CODE: Keeping this around in case the code proves useful void FFmpegDecoder::CacheFrameToDisk(AVFrame *f) @@ -629,6 +653,13 @@ void FFmpegDecoder::ClearFrameCache() FFmpegFramePool::ElementPtr FFmpegDecoder::RetrieveFrame(const rational& time, const RetrieveVideoParams ¶ms) { int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time); + + if (params.dst_interlacing == VideoParams::kInterlaceNone && params.src_interlacing != VideoParams::kInterlaceNone) { + // If we are de-interlacing, the timebase is doubled because we get one frame per field, so we + // double the target timestamp too + target_ts *= 2; + } + int64_t seek_ts = target_ts; bool still_seeking = false; @@ -807,13 +838,25 @@ bool FFmpegDecoder::InitScaler(const RetrieveVideoParams& params) // Add interlacing filter if necessary if (filter_params_.src_interlacing != filter_params_.dst_interlacing) { // Determine what kind of interlacing we'll be doing + AVFilterContext* interlace_filter; + if (filter_params_.dst_interlacing == VideoParams::kInterlaceNone) { // Deinterlace source + snprintf(filter_args, kFilterArgSz, "mode=1:parity=%s", + GetInterlacingModeInFFmpeg(filter_params_.src_interlacing)); + avfilter_graph_create_filter(&interlace_filter, avfilter_get_by_name("yadif"), "yadif", filter_args, nullptr, filter_graph_); } else if (filter_params_.src_interlacing == VideoParams::kInterlaceNone) { - // We will be interlacing a previous progressive source + // We will be interlacing an originally progressive source + snprintf(filter_args, kFilterArgSz, "scan=%s", GetInterlacingModeInFFmpeg(filter_params_.dst_interlacing)); + avfilter_graph_create_filter(&interlace_filter, avfilter_get_by_name("interlace"), "interlace", filter_args, nullptr, filter_graph_); } else { // We will simply be flipping the fields + snprintf(filter_args, kFilterArgSz, "fieldorder=%s", GetInterlacingModeInFFmpeg(filter_params_.dst_interlacing)); + avfilter_graph_create_filter(&interlace_filter, avfilter_get_by_name("fieldorder"), "fieldorder", filter_args, nullptr, filter_graph_); } + + avfilter_link(last_filter, 0, interlace_filter, 0); + last_filter = interlace_filter; } // Add scale filter if necessary diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index e74f8514f..fc87043fb 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -132,6 +132,8 @@ private: static uint64_t ValidateChannelLayout(AVStream *stream); + static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing); + FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const; void ClearFrameCache(); diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 4ebdf22a0..7327f0650 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -110,10 +110,12 @@ QByteArray RenderManager::Hash(const Node *n, const QString& output, const Video int width = params.effective_width(); int height = params.effective_height(); VideoParams::Format format = params.format(); + VideoParams::Interlacing interlacing = params.interlacing(); - hasher.addData(reinterpret_cast(&width), sizeof(int)); - hasher.addData(reinterpret_cast(&height), sizeof(int)); - hasher.addData(reinterpret_cast(&format), sizeof(VideoParams::Format)); + hasher.addData(reinterpret_cast(&width), sizeof(width)); + hasher.addData(reinterpret_cast(&height), sizeof(height)); + hasher.addData(reinterpret_cast(&format), sizeof(format)); + hasher.addData(reinterpret_cast(&interlacing), sizeof(interlacing)); if (n) { n->Hash(output, hasher, time, params);