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

如何在FFmpeg 5.1中基于AVFrame实现帧去隔行函数?

在FFmpeg 5.1中实现基于AVFrame的去隔行处理函数

FFmpeg 5.1已完全移除老旧的avpicture_deinterlace函数,官方推荐使用libavfilter滤镜框架实现去隔行逻辑,这是兼容新版本的标准方案。以下是实现你需要的avframe_deinterlace函数的具体步骤和代码:

核心思路

利用FFmpeg内置的yadif滤镜(常用的实时去隔行滤镜),通过滤镜图将输入的隔行AVFrame转换为逐行帧。整个流程包含:初始化滤镜上下文、传入输入帧、获取处理后的输出帧、资源清理四个环节。

完整代码实现

#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/error.h>

int avframe_deinterlace(AVFrame* dst, const AVFrame* interlacedFrame, enum AVPixelFormat pix_fmt, int width, int height) {
    static AVFilterGraph *filter_graph = NULL;
    static AVFilterContext *buffersrc_ctx = NULL;
    static AVFilterContext *buffersink_ctx = NULL;
    int ret;

    // 首次调用时初始化滤镜图(避免重复初始化提升效率)
    if (!filter_graph) {
        const AVFilter *buffersrc = avfilter_get_by_name("buffer");
        const AVFilter *buffersink = avfilter_get_by_name("buffersink");
        AVFilterInOut *outputs = avfilter_inout_alloc();
        AVFilterInOut *inputs = avfilter_inout_alloc();
        char args[512];

        if (!outputs || !inputs) {
            ret = AVERROR(ENOMEM);
            goto cleanup;
        }

        // 分配滤镜图
        filter_graph = avfilter_graph_alloc();
        if (!filter_graph) {
            ret = AVERROR(ENOMEM);
            goto cleanup;
        }

        // 配置输入源滤镜参数:匹配输入帧的宽高、像素格式、时间基
        snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=1/1",
                 width, height, pix_fmt);
        ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);
        if (ret < 0) goto cleanup;

        // 配置输出 sink 滤镜:指定输出像素格式与输入一致
        ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph);
        if (ret < 0) goto cleanup;
        ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", (int[]){pix_fmt, AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
        if (ret < 0) goto cleanup;

        // 创建yadif去隔行滤镜,可调整参数适配需求
        const AVFilter *yadif_filter = avfilter_get_by_name("yadif");
        AVFilterContext *yadif_ctx = NULL;
        // 参数说明:mode=0(默认,输出逐行帧,丢弃冗余场)、parity=-1(自动检测场序)、auto_enable=1(自动检测隔行帧)
        ret = avfilter_graph_create_filter(&yadif_ctx, yadif_filter, "yadif", "mode=0:parity=-1:auto_enable=1", NULL, filter_graph);
        if (ret < 0) goto cleanup;

        // 连接滤镜链路:输入源 -> yadif -> 输出sink
        ret = avfilter_link(buffersrc_ctx, 0, yadif_ctx, 0);
        if (ret < 0) goto cleanup;
        ret = avfilter_link(yadif_ctx, 0, buffersink_ctx, 0);
        if (ret < 0) goto cleanup;

        // 配置滤镜图的输入输出端点
        outputs->name = av_strdup("in");
        outputs->filter_ctx = buffersrc_ctx;
        outputs->pad_idx = 0;
        outputs->next = NULL;

        inputs->name = av_strdup("out");
        inputs->filter_ctx = buffersink_ctx;
        inputs->pad_idx = 0;
        inputs->next = NULL;

        if (!outputs->name || !inputs->name) {
            ret = AVERROR(ENOMEM);
            goto cleanup;
        }

        // 初始化滤镜图
        ret = avfilter_graph_parse_ptr(filter_graph, NULL, &inputs, &outputs, NULL);
        if (ret < 0) goto cleanup;
        ret = avfilter_graph_config(filter_graph, NULL);
        if (ret < 0) goto cleanup;

        goto init_done;

    cleanup:
        avfilter_graph_free(&filter_graph);
        avfilter_inout_free(&inputs);
        avfilter_inout_free(&outputs);
        return ret;
    init_done:;
    }

    // 检查输入帧参数是否与初始化时一致(避免参数不匹配导致错误)
    if (interlacedFrame->width != width || interlacedFrame->height != height || interlacedFrame->format != pix_fmt) {
        // 参数变更时需要重新初始化滤镜图,此处可根据需求添加重置逻辑
        return AVERROR(EINVAL);
    }

    // 将输入帧送入滤镜(保留输入帧引用,避免被滤镜修改)
    ret = av_buffersrc_add_frame_flags(buffersrc_ctx, (AVFrame*)interlacedFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
    if (ret < 0) return ret;

    // 从滤镜获取处理后的逐行帧
    ret = av_buffersink_get_frame(buffersink_ctx, dst);
    if (ret < 0) {
        // EAGAIN表示需要更多输入帧才能生成输出,EOF表示输入结束,可根据业务逻辑处理
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return 0;
        return ret;
    }

    return 0;
}

// 当不再需要去隔行功能时,调用此函数释放资源
void avframe_deinterlace_cleanup() {
    avfilter_graph_free(&filter_graph);
}

关键注意事项

  • 滤镜参数调整:yadif滤镜支持多种参数,比如mode可选0-3(不同的去隔行模式)、parity指定场序(0=顶场优先,1=底场优先,-1=自动检测),可根据实际需求修改。
  • 资源管理:使用静态变量保存滤镜上下文避免重复初始化,当处理参数变化的帧时,需要重新初始化滤镜图;不再使用时调用avframe_deinterlace_cleanup释放内存。
  • 错误处理:注意av_buffersink_get_frame的返回值,AVERROR(EAGAIN)说明需要继续输入帧才能得到输出,AVERROR_EOF表示输入流结束。
  • 帧生命周期:使用AV_BUFFERSRC_FLAG_KEEP_REF确保输入帧的引用不被滤镜修改,你可以安全地继续使用输入帧。

内容的提问来源于stack exchange,提问作者jd750

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 11:55:20