C++中如何通过FFmpeg的AVFilterGraph为帧应用多个滤镜
FFmpeg AVFilter实现crop+eq组合滤镜的正确方案
你的代码存在3个会直接导致运行失败的核心问题:
- 硬编码滤镜实例名获取buffersink上下文逻辑错误:滤镜的
Parsed_<滤镜名>_<索引>后缀索引是按滤镜在字符串中出现的顺序从0开始计数的,你的4个滤镜(buffer/crop/eq/buffersink)对应索引0-3,你写的Parsed_buffersink_2实际指向的是eq滤镜的上下文,根本不是输出节点。 - 帧引用逻辑错误:你提前对输出帧
f调用av_frame_ref绑定输入帧的内存,后续往f写入滤镜输出结果时会覆盖原有引用,既会造成原帧内存泄漏,也可能触发内存写冲突。 - 滤镜图释放时机错误:你在返回输出帧之前就释放了整个AVFilterGraph,而输出帧的图像缓冲区是依附于滤镜图内存分配的,释放图后输出帧就变成野指针,访问会直接触发段错误。
修正后的可运行实现
AVFrame *FFmpegDecoder::cropFrame(AVFrame *frame, int left, int top, int right, int bottom) { int ret; AVFilterContext *buffersrc_ctx = nullptr; AVFilterContext *buffersink_ctx = nullptr; AVFilterGraph *filter_graph = avfilter_graph_alloc(); if (!filter_graph) return nullptr; // 输出尺寸计算 const int out_w = frame->width - left - right; const int out_h = frame->height - top - bottom; // 滤镜字符串:线性链直接用逗号连接即可,不需要多余的中间pad命名 char args[1024]; snprintf(args, sizeof(args), "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/1:pixel_aspect=0/1[in];" "[in]crop=x=%d:y=%d:out_w=%d:out_h=%d,eq=brightness=0.06:saturation=2:contrast=1[out];" "[out]buffersink", frame->width, frame->height, frame->format, left, top, out_w, out_h); AVFilterInOut *inputs = nullptr, *outputs = nullptr; ret = avfilter_graph_parse2(filter_graph, args, &inputs, &outputs); if (ret < 0) { avfilter_graph_free(&filter_graph); return nullptr; } ret = avfilter_graph_config(filter_graph, nullptr); if (ret < 0) { avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); avfilter_graph_free(&filter_graph); return nullptr; } // 直接从解析出的inout结构拿src和sink上下文,避免硬编码名字出错 buffersrc_ctx = outputs->filter_ctx; // parse2返回的outputs对应滤镜图的输入端口 buffersink_ctx = inputs->filter_ctx; // parse2返回的inputs对应滤镜图的输出端口 avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); // 输入帧送入滤镜:直接传原frame即可,滤镜内部会自己管理引用 ret = av_buffersrc_add_frame(buffersrc_ctx, frame); if (ret < 0) { avfilter_graph_free(&filter_graph); return nullptr; } // 输出帧必须是干净的空帧,不要提前ref原帧 AVFrame *out_frame = av_frame_alloc(); ret = av_buffersink_get_frame(buffersink_ctx, out_frame); if (ret < 0) { av_frame_free(&out_frame); avfilter_graph_free(&filter_graph); return nullptr; } // 深拷贝输出帧保证返回的帧独立可用,之后再释放滤镜图避免野指针 AVFrame *ret_frame = av_frame_clone(out_frame); av_frame_free(&out_frame); avfilter_graph_free(&filter_graph); return ret_frame; }
额外优化建议
- 不要每处理一帧就新建/销毁一次AVFilterGraph,这个操作开销非常大,建议在解码器初始化、或者输入帧格式尺寸第一次确定的时候创建一次滤镜图,后续帧直接送入处理,只有当输入帧尺寸/格式变化的时候再重建滤镜图,性能会提升一个数量级。
- 线性串联的滤镜直接用逗号连接即可,不需要给每个中间节点都命名pad,写法更简洁也不容易出错。
- 不要依赖硬编码的
Parsed_xxx_x名字获取滤镜上下文,不同FFmpeg版本、不同滤镜写法下索引可能变化,从解析返回的AVFilterInOut结构拿指针是最稳妥的方式。
内容的提问来源于stack exchange,提问作者hameed abbasi
相关产品推荐
相关产品推荐

