diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 8ae47c820..3bdfae0aa 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -836,24 +836,13 @@ bool FFmpegDecoder::InitScaler(const RetrieveVideoParams& params) AVFilterContext *last_filter = buffersrc_ctx_; // Add interlacing filter if necessary - if (filter_params_.src_interlacing != filter_params_.dst_interlacing) { - // Determine what kind of interlacing we'll be doing + if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) { + // Footage is interlaced, our renderer works in progressive so we'll need to de-interlace 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 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_); - } + 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_); avfilter_link(last_filter, 0, interlace_filter, 0); last_filter = interlace_filter; diff --git a/app/codec/frame.cpp b/app/codec/frame.cpp index cb97725cc..9f3cb2bc5 100644 --- a/app/codec/frame.cpp +++ b/app/codec/frame.cpp @@ -60,6 +60,30 @@ void Frame::set_video_params(const VideoParams ¶ms) linesize_pixels_ = linesize_ / params_.GetBytesPerPixel(); } +FramePtr Frame::Interlace(FramePtr top, FramePtr bottom) +{ + if (top->video_params() != bottom->video_params()) { + qCritical() << "Tried to interlace two frames that had incompatible parameters"; + return nullptr; + } + + FramePtr interlaced = Frame::Create(); + interlaced->set_video_params(top->video_params()); + interlaced->allocate(); + + int linesize = interlaced->linesize_bytes(); + + for (int i=0; iheight(); i++) { + FramePtr which = (i%2 == 0) ? top : bottom; + + memcpy(interlaced->data() + i*linesize, + which->const_data() + i*linesize, + linesize); + } + + return interlaced; +} + int Frame::generate_linesize_bytes(int width, VideoParams::Format format, int channel_count) { // Align to 32 bytes (not sure if this is necessary?) diff --git a/app/codec/frame.h b/app/codec/frame.h index 1859332bc..5c1c4343a 100644 --- a/app/codec/frame.h +++ b/app/codec/frame.h @@ -51,6 +51,8 @@ public: const VideoParams& video_params() const; void set_video_params(const VideoParams& params); + static FramePtr Interlace(FramePtr top, FramePtr bottom); + static int generate_linesize_bytes(int width, VideoParams::Format format, int channel_count); int linesize_pixels() const diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index de9a29fef..3e4739a2a 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -40,6 +40,78 @@ RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, S { } +FramePtr RenderProcessor::GenerateFrame(const rational& time, const rational& frame_length) +{ + ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); + + NodeValueTable table; + NodeOutput texture_output = viewer->GetConnectedTextureOutput(); + if (texture_output.IsValid()) { + table = GenerateTable(texture_output.node(), texture_output.output(), + TimeRange(time, time + frame_length)); + } + + TexturePtr texture = table.Get(NodeValue::kTexture).value(); + + // Set up output frame parameters + VideoParams frame_params = GetCacheVideoParams(); + + QSize frame_size = ticket_->property("size").value(); + if (!frame_size.isNull()) { + frame_params.set_width(frame_size.width()); + frame_params.set_height(frame_size.height()); + } + + VideoParams::Format frame_format = static_cast(ticket_->property("format").toInt()); + if (frame_format != VideoParams::kFormatInvalid) { + frame_params.set_format(frame_format); + } + + frame_params.set_channel_count(texture ? texture->channel_count() : VideoParams::kRGBChannelCount); + + FramePtr frame = Frame::Create(); + frame->set_timestamp(time); + frame->set_video_params(frame_params); + frame->allocate(); + + if (!texture) { + // Blank frame out + memset(frame->data(), 0, frame->allocated_size()); + } else { + // Dump texture contents to frame + ColorProcessorPtr output_color_transform = ticket_->property("coloroutput").value(); + const VideoParams& tex_params = texture->params(); + + if (tex_params.effective_width() != frame_params.effective_width() + || tex_params.effective_height() != frame_params.effective_height() + || tex_params.format() != frame_params.format() + || output_color_transform) { + TexturePtr blit_tex = render_ctx_->CreateTexture(frame_params); + + QMatrix4x4 matrix = ticket_->property("matrix").value(); + + if (output_color_transform) { + // Yes color transform, blit color managed + render_ctx_->BlitColorManaged(output_color_transform, texture, true, blit_tex.get(), true, matrix); + } else { + // No color transform, just blit + ShaderJob job; + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture))); + job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); + + render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get()); + } + + // Replace texture that we're going to download in the next step + texture = blit_tex; + } + + render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels()); + } + + return frame; +} + void RenderProcessor::Run() { // Depending on the render ticket type, start a job @@ -48,74 +120,30 @@ void RenderProcessor::Run() switch (type) { case RenderManager::kTypeVideo: { - ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); - const VideoParams& video_params = ticket_->property("vparam").value(); - SetCacheVideoParams(video_params); + SetCacheVideoParams(ticket_->property("vparam").value()); rational time = ticket_->property("time").value(); - NodeValueTable table; - NodeOutput texture_output = viewer->GetConnectedTextureOutput(); - if (texture_output.IsValid()) { - table = GenerateTable(texture_output.node(), texture_output.output(), - TimeRange(time, time + video_params.frame_rate_as_time_base())); + rational frame_length = GetCacheVideoParams().frame_rate_as_time_base(); + if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) { + frame_length /= 2; } - TexturePtr texture = table.Get(NodeValue::kTexture).value(); + FramePtr frame = GenerateFrame(time, frame_length); - // Set up output frame parameters - VideoParams frame_params = ticket_->property("vparam").value(); + if (GetCacheVideoParams().interlacing() != VideoParams::kInterlaceNone) { + // Get next between frame and interlace it + FramePtr next_frame = GenerateFrame(time + frame_length, frame_length); - QSize frame_size = ticket_->property("size").value(); - if (!frame_size.isNull()) { - frame_params.set_width(frame_size.width()); - frame_params.set_height(frame_size.height()); - } - - VideoParams::Format frame_format = static_cast(ticket_->property("format").toInt()); - if (frame_format != VideoParams::kFormatInvalid) { - frame_params.set_format(frame_format); - } - - frame_params.set_channel_count(texture ? texture->channel_count() : VideoParams::kRGBChannelCount); - - FramePtr frame = Frame::Create(); - frame->set_timestamp(time); - frame->set_video_params(frame_params); - frame->allocate(); - - if (!texture) { - // Blank frame out - memset(frame->data(), 0, frame->allocated_size()); - } else { - // Dump texture contents to frame - ColorProcessorPtr output_color_transform = ticket_->property("coloroutput").value(); - const VideoParams& tex_params = texture->params(); - - if (tex_params.effective_width() != frame_params.effective_width() - || tex_params.effective_height() != frame_params.effective_height() - || tex_params.format() != frame_params.format() - || output_color_transform) { - TexturePtr blit_tex = render_ctx_->CreateTexture(frame_params); - - QMatrix4x4 matrix = ticket_->property("matrix").value(); - - if (output_color_transform) { - // Yes color transform, blit color managed - render_ctx_->BlitColorManaged(output_color_transform, texture, true, blit_tex.get(), true, matrix); - } else { - // No color transform, just blit - ShaderJob job; - job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture))); - job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); - - render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get()); - } - - // Replace texture that we're going to download in the next step - texture = blit_tex; + FramePtr top, bottom; + if (GetCacheVideoParams().interlacing() == VideoParams::kInterlacedTopFirst) { + top = frame; + bottom = next_frame; + } else { + top = next_frame; + bottom = frame; } - render_ctx_->DownloadFromTexture(texture.get(), frame->data(), frame->linesize_pixels()); + frame = Frame::Interlace(top, bottom); } ticket_->Finish(QVariant::fromValue(frame)); @@ -272,7 +300,7 @@ QVariant RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const ra // Check the still frame cache. On large frames such as high resolution still images, uploading // and color managing them for every frame is a waste of time, so we implement a small cache here // to optimize such a situation - const VideoParams& render_params = ticket_->property("vparam").value(); + const VideoParams& render_params = GetCacheVideoParams(); VideoParams stream_data = stream.video_params(); ColorManager* color_manager = Node::ValueToPtr(ticket_->property("colormanager")); @@ -448,7 +476,7 @@ QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range } } - VideoParams tex_params = ticket_->property("vparam").value(); + VideoParams tex_params = GetCacheVideoParams(); tex_params.set_channel_count(GetChannelCountFromJob(job)); @@ -499,7 +527,7 @@ QVariant RenderProcessor::ProcessFrameGeneration(const Node *node, const Generat { FramePtr frame = Frame::Create(); - VideoParams frame_params = ticket_->property("vparam").value(); + VideoParams frame_params = GetCacheVideoParams(); frame_params.set_channel_count(GetChannelCountFromJob(job)); frame->set_video_params(frame_params); frame->allocate(); diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 3f8ffbeb8..8e91ec774 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -62,6 +62,8 @@ protected: private: RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, StillImageCache* still_image_cache, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader); + FramePtr GenerateFrame(const rational &time, const rational &frame_length); + void Run(); DecoderPtr ResolveDecoderFromInput(const QString &decoder_id, const Decoder::CodecStream& stream);