You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:06:24