只要我只移动裁剪矩形,下面的代码就可以完美工作,但是一旦我更改其大小,我就不再从过滤器中获取帧(
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;
如果您想要动态裁剪,则尺寸更改时需要更新
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
直接更新裁剪过滤器上下文上的参数。