如何在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
相关产品推荐
相关产品推荐

