From 30b64d9b9cc43f6fb0112d6ec07b19142ab1dff2 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 8 Aug 2022 14:05:11 -0700 Subject: [PATCH] render: reimplemented deinterlacing with new shader-based format conversion --- app/codec/decoder.h | 1 + app/codec/ffmpeg/ffmpegdecoder.cpp | 39 ++++++++++++++++++++++++++++++ app/codec/ffmpeg/ffmpegdecoder.h | 1 + app/render/renderprocessor.cpp | 1 + app/shaders/yuv2rgb.frag | 19 ++++++++++++--- 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 841d1901c..983efb753 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -170,6 +170,7 @@ public: VideoParams::Format maximum_format = VideoParams::kFormatInvalid; CancelAtom *cancelled = nullptr; VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault; + VideoParams::Interlacing src_interlacing = VideoParams::kInterlaceNone; }; /** diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 556907ab3..3cb61d8f1 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -79,6 +79,7 @@ bool FFmpegDecoder::OpenInternal() working_frame_ = av_frame_alloc(); working_packet_ = av_packet_alloc(); + frame_rate_tb_ = rational::NaN; return true; } @@ -243,6 +244,29 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p) job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kInt, yuv_coeffs[3])); job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kInt, yuv_coeffs[1])); + int interlacing = 0; + if (p.src_interlacing != VideoParams::kInterlaceNone) { + if (frame_rate_tb_.isNull()) { + frame_rate_tb_ = av_guess_frame_rate(instance_.fmt_ctx(), instance_.avstream(), f.get()); + + // Double frame rate for interlaced fields + frame_rate_tb_ *= 2; + + // Flip frame rate so it can be used as a timebase + frame_rate_tb_.flip(); + } + + int64_t req = Timecode::time_to_timestamp(p.time, frame_rate_tb_); + int64_t frm = Timecode::rescale_timestamp(f->pts - instance_.avstream()->start_time, instance_.avstream()->time_base, frame_rate_tb_); + + bool first = (req == frm); + bool top_first = (p.src_interlacing == VideoParams::kInterlacedTopFirst); + + interlacing = (first == top_first) ? 1 : 2; + } + job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing)); + job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, f->height)); + tex = p.renderer->CreateTexture(vp); p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false); } @@ -896,6 +920,7 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params if (params.divider == filter_params_.divider && params.force_range == filter_params_.force_range && params.maximum_format == filter_params_.maximum_format + && params.src_interlacing == filter_params_.src_interlacing && filter_graph_ && input_fmt_ == input->format) { // We have an appropriate filter for these parameters, just return true @@ -962,6 +987,20 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params // Link filters as necessary AVFilterContext *last_filter = buffersrc_ctx_; + // Add deinterlace filter if necessary + if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) { + AVFilterContext* deint_filter; + + snprintf(filter_args, kFilterArgSz, "mode=1:parity=%s", + filter_params_.src_interlacing == VideoParams::kInterlacedTopFirst ? "0" : "1"); + + avfilter_graph_create_filter(&deint_filter, avfilter_get_by_name("yadif"), "deint", filter_args, nullptr, filter_graph_); + + avfilter_link(last_filter, 0, deint_filter, 0); + + last_filter = deint_filter; + } + // Add scale filter if necessary int dst_width, dst_height; if (filter_params_.divider > 1) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 756997098..c29f45c1a 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -159,6 +159,7 @@ private: VideoParams::Format native_internal_pix_fmt_; VideoParams::Format native_output_pix_fmt_; int native_channel_count_; + rational frame_rate_tb_; AVFrame *working_frame_; AVPacket *working_packet_; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 9e8ee169c..94cea35f8 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -481,6 +481,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ p.time = (stream_data.video_type() == VideoParams::kVideoTypeVideo) ? input_time : Decoder::kAnyTimecode; p.cancelled = GetCancelPointer(); p.force_range = stream_data.color_range(); + p.src_interlacing = stream_data.interlacing(); unmanaged_texture = decoder->RetrieveVideo(p); diff --git a/app/shaders/yuv2rgb.frag b/app/shaders/yuv2rgb.frag index 13e1cbbe1..8c40b5918 100644 --- a/app/shaders/yuv2rgb.frag +++ b/app/shaders/yuv2rgb.frag @@ -10,16 +10,29 @@ uniform int yuv_cgu; uniform int yuv_cgv; uniform int yuv_cbu; +uniform int interlacing; +uniform int pixel_height; + in vec2 ove_texcoord; out vec4 frag_color; void main() { + vec2 real_coord = ove_texcoord; + if (interlacing != 0) { + float field_height = float(pixel_height / 2); + real_coord.y = floor(real_coord.y * field_height) + 0.25; + if (interlacing == 2) { + real_coord.y += 0.5; + } + real_coord.y /= field_height; + } + // Sample YUV planes vec3 yuv; - yuv.r = texture(y_channel, ove_texcoord).r; - yuv.g = texture(u_channel, ove_texcoord).r; - yuv.b = texture(v_channel, ove_texcoord).r; + yuv.r = texture(y_channel, real_coord).r; + yuv.g = texture(u_channel, real_coord).r; + yuv.b = texture(v_channel, real_coord).r; // Pixels will have come in aligned to 16-bit regardless of their actual bit depth, so they must // be scaled as if they were actually 16-bit