当裁剪大小发生变化时,动态 ffmpeg 裁剪、缩放和编码代码似乎会中断

问题描述 投票:0回答:1

只要我只移动裁剪矩形,下面的代码就可以完美工作,但是一旦我更改其大小,我就不再从过滤器中获取帧(

av_buffersink_get_frame
返回
-11
)。这太疯狂了,即使在尺寸改变之后,如果它最终改变为框架将经历的原始尺寸,那么它将回到不再提供框架。

有人知道我做错了什么吗?

我的过滤器设置(注意

crop
scale
组合,它应该(我认为?)将我裁剪的任何内容缩放到输出视频大小):

// buffer source -> buffer sink setup
auto args = std::format("video_size={}x{}:pix_fmt={}:time_base={}/{}:pixel_aspect={}/{}",
    inputCodecContext->width, inputCodecContext->height, (int)inputCodecContext->pix_fmt,
    inputCodecContext->pkt_timebase.num, inputCodecContext->pkt_timebase.den,
    inputCodecContext->sample_aspect_ratio.num, inputCodecContext->sample_aspect_ratio.den);

AVFilterContext* buffersrc_ctx = nullptr, * buffersink_ctx = nullptr;
check_av_result(avfilter_graph_create_filter(&buffersrc_ctx, bufferSource, "in",
    args.c_str(), nullptr, &*filterGraph));
check_av_result(avfilter_graph_create_filter(&buffersink_ctx, bufferSink, "out",
    nullptr, nullptr, &*filterGraph));
check_av_result(av_opt_set_bin(buffersink_ctx, "pix_fmts",
    (uint8_t*)&outputCodecContext->pix_fmt, sizeof(outputCodecContext->pix_fmt), AV_OPT_SEARCH_CHILDREN));

// filter command setup
auto filterSpec = std::format("crop,scale={}:{},setsar=1:1", outputCodecContext->width, outputCodecContext->height);

check_av_result(avfilter_graph_parse_ptr(&*filterGraph, filterSpec.c_str(), &filterInputs, &filterOutputs, nullptr));
check_av_result(avfilter_graph_config(&*filterGraph, nullptr));

帧裁剪:

check_av_result(avfilter_graph_send_command(&*filterGraph, "crop", "x", std::to_string(cropRectangle.CenterX() - cropRectangle.Width() / 2).c_str(), nullptr, 0, 0));
check_av_result(avfilter_graph_send_command(&*filterGraph, "crop", "y", std::to_string(cropRectangle.CenterY() - cropRectangle.Height() / 2).c_str(), nullptr, 0, 0));
check_av_result(avfilter_graph_send_command(&*filterGraph, "crop", "w", std::to_string(cropRectangle.Width()).c_str(), nullptr, 0, 0));
check_av_result(avfilter_graph_send_command(&*filterGraph, "crop", "h", std::to_string(cropRectangle.Height()).c_str(), nullptr, 0, 0));

// push the decoded frame into the filter graph
check_av_result(av_buffersrc_add_frame_flags(buffersrc_ctx, &*inputFrame, 0));

// pull filtered frames from the filter graph
while (1)
{
    ret = av_buffersink_get_frame(buffersink_ctx, &*filteredFrame);
    if (ret < 0)
    {
        // if no more frames, rewrite the code to 0 to show it as normal completion
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            ret = 0;
        break;
    }

    // write the filtered frame to the output file 
    // [...]
}

我还在创建文件之前设置了输出视频大小,并且按预期遵循:

outputCodecContext->width = (int)output.PixelSize().Width;
outputCodecContext->height = (int)output.PixelSize().Height;
c++ ffmpeg
1个回答
0
投票

如果您想要动态裁剪,则尺寸更改时需要更新

filterSpec
。有两种方法可以做到这一点,一种是重新初始化过滤器图,另一种不需要重新初始化。我将从概念上向您展示如何做到后者以获得更好的性能。

// buffer source -> buffer sink setup
auto args = std::format("video_size={}x{}:pix_fmt={}:time_base={}/{}:pixel_aspect={}/{}",
inputCodecContext->width, inputCodecContext->height, (int)inputCodecContext->pix_fmt,
inputCodecContext->pkt_timebase.num, inputCodecContext->pkt_timebase.den,
inputCodecContext->sample_aspect_ratio.num, inputCodecContext->sample_aspect_ratio.den);
    
AVFilterContext* buffersrc_ctx = nullptr, * buffersink_ctx = nullptr;
check_av_result(avfilter_graph_create_filter(&buffersrc_ctx, bufferSource, "in",
args.c_str(), nullptr, &*filterGraph));
check_av_result(avfilter_graph_create_filter(&buffersink_ctx, bufferSink, "out",
nullptr, nullptr, &*filterGraph));

check_av_result(av_opt_set_bin(buffersink_ctx, "pix_fmts",
        (uint8_t*)&outputCodecContext->pix_fmt, sizeof(outputCodecContext->pix_fmt), AV_OPT_SEARCH_CHILDREN));
    
// filter command setup
auto filterSpec = std::format(
    "[in] crop=x=0:y=0:w={}:h={}:flags=low_res_mv[cropped];"
    "[cropped] scale={}:{}[scaled];"
    "[scaled] setsar=1:1[out]",
    cropRectangle.Width(),
    cropRectangle.Height(),
    outputCodecContext->width,
    outputCodecContext->height
);
    
check_av_result(avfilter_graph_parse2(&*filterGraph, filterSpec.c_str(), &filterInputs, &filterOutputs));
check_av_result(avfilter_graph_config(&*filterGraph, nullptr));
    
// get references to the crop filter context
AVFilterContext* cropFilterContext = avfilter_graph_get_filter(&*filterGraph, "Parsed_crop_0");

if (!cropFilterContext) {
        // handle error: crop filter not found
        // ...
}

帧裁剪:

// update the crop parameters using av_opt_set
check_av_result(av_opt_set_int(cropFilterContext, "x", cropRectangle.CenterX() - cropRectangle.Width() / 2, 0));
check_av_result(av_opt_set_int(cropFilterContext, "y", cropRectangle.CenterY() - cropRectangle.Height() / 2, 0));
check_av_result(av_opt_set_int(cropFilterContext, "w", cropRectangle.Width(), 0));
check_av_result(av_opt_set_int(cropFilterContext, "h", cropRectangle.Height(), 0));
    
// push the decoded frame into the filter graph
check_av_result(av_buffersrc_add_frame_flags(buffersrc_ctx, &*inputFrame, 0));
    
// pull filtered frames from the filter graph
while (1) {
    ret = av_buffersink_get_frame(buffersink_ctx, &*filteredFrame);
    if (ret < 0) {
    // if no more frames, rewrite the code to 0 to show it as normal completion
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) ret = 0; break;
    }
    
// write the filtered frame to the output file 
// [...]
}

将更复杂的过滤器图与

crop
scale
setsar
过滤器一起使用,应允许在推送每个帧之前使用
av_opt_set_int
直接更新裁剪过滤器上下文上的参数。

© www.soinside.com 2019 - 2024. All rights reserved.