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

如何将FFmpeg提取的视频帧推入FFmpeg缓冲区?

嘿,我之前刚好做过类似的需求,其实你要的逻辑核心就藏在buffersrc.c里的av_buffersrc_add_frame()系列函数里,可能你没把初始化和推入的流程串起来。下面给你一步步拆解可行的实现方案,结合你看过的文件对应理解:

核心思路:利用AVBufferSrcContext管理帧缓冲区

FFmpeg的buffer滤镜(对应buffersrc.c的实现)就是专门用来接收外部帧数据的缓冲区源,你需要先初始化这个缓冲区的参数,再把提取到的帧推入其中,甚至可以结合fifo.c里的帧队列做缓存解耦。

1. 先初始化缓冲区源滤镜

首先要创建滤镜图,并添加buffer滤镜作为输入源,必须指定和你提取的视频帧完全匹配的参数(像素格式、宽高、帧率等),否则推入帧时会报错:

#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>

AVFilterGraph *filter_graph = avfilter_graph_alloc();
if (!filter_graph) {
    // 内存分配错误处理
    return -1;
}

AVFilterContext *buffersrc_ctx = NULL;
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
AVDictionary *opts = NULL;

// 配置缓冲区参数,替换成你提取的帧的实际参数
av_dict_set(&opts, "video_size", "1920x1080", 0);
av_dict_set(&opts, "pix_fmt", "yuv420p", 0);
av_dict_set(&opts, "framerate", "30/1", 0);
av_dict_set(&opts, "time_base", "1/30", 0);

// 创建缓冲区源上下文
int ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "input_src", NULL, &opts, filter_graph);
if (ret < 0) {
    // 错误处理,比如参数不合法
    av_dict_free(&opts);
    avfilter_graph_free(&filter_graph);
    return ret;
}
av_dict_free(&opts);

2. 将提取的视频帧推入缓冲区

假设你已经通过解码流程得到了AVFrame(这部分你应该已经实现了),直接调用av_buffersrc_add_frame()就能把帧推入缓冲区:

AVFrame *extracted_frame = ...; // 你从视频中提取的帧

// 推入帧到缓冲区
ret = av_buffersrc_add_frame(buffersrc_ctx, extracted_frame);
if (ret < 0) {
    // 错误处理:比如帧参数和缓冲区不匹配,或者缓冲区已满
    av_frame_free(&extracted_frame);
    return ret;
}

// 注意:如果extracted_frame是引用计数帧,推入后不需要立即释放,缓冲区会接管引用
// 如果是自己分配的帧,根据情况决定是否释放

如果需要强制推入(比如忽略一些格式不匹配的警告),可以用av_buffersrc_add_frame_flags(),传入AV_BUFFERSRC_FLAG_KEEP_REF还能保留原帧的引用。

3. 结合FIFO做帧缓存(对应fifo.c的使用)

如果你需要解耦帧提取和缓冲区推入的速度(比如解码太快但后续处理跟不上),可以用AVFrameFifo(基于fifo.c实现的帧队列)先缓存提取的帧,再批量推入缓冲区:

// 初始化FIFO队列,参数和帧一致
AVFrameFifo *frame_fifo = av_frame_fifo_alloc(AV_PIX_FMT_YUV420P, 1920, 1080);
if (!frame_fifo) {
    // 内存分配错误
    return -1;
}

// 将提取的帧加入FIFO
ret = av_frame_fifo_write(frame_fifo, extracted_frame);
if (ret < 0) {
    // 错误处理
    av_frame_free(&extracted_frame);
    return ret;
}

// 从FIFO取出帧推入缓冲区
AVFrame *fifo_frame = av_frame_alloc();
ret = av_frame_fifo_read(frame_fifo, fifo_frame);
if (ret >= 0) {
    av_buffersrc_add_frame(buffersrc_ctx, fifo_frame);
}
av_frame_free(&fifo_frame);

4. 后续处理:从缓冲区读取帧(可选)

如果推入缓冲区后需要对帧做滤镜处理,你可以添加buffersink滤镜作为输出,连接到缓冲区源,然后读取处理后的帧:

AVFilterContext *buffersink_ctx = NULL;
const AVFilter *buffersink = avfilter_get_by_name("buffersink");

ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "output_sink", NULL, NULL, filter_graph);
if (ret < 0) {
    // 错误处理
    return ret;
}

// 连接源和sink滤镜
ret = avfilter_link(buffersrc_ctx, 0, buffersink_ctx, 0);
if (ret < 0) {
    // 错误处理
    return ret;
}

// 配置sink接受的像素格式
enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);

// 初始化滤镜图
ret = avfilter_graph_config(filter_graph, NULL);
if (ret < 0) {
    // 错误处理
    return ret;
}

// 读取处理后的帧
AVFrame *processed_frame = av_frame_alloc();
ret = av_buffersink_get_frame(buffersink_ctx, processed_frame);
if (ret == AVERROR(EAGAIN)) {
    // 缓冲区还没有足够的帧,需要继续推入
} else if (ret == AVERROR_EOF) {
    // 所有帧都处理完毕
} else if (ret < 0) {
    // 错误处理
}

最后提醒:一定要注意内存管理,用完的AVFilterGraph、AVFrame、AVFrameFifo都要调用对应的free函数释放;另外帧的参数必须和缓冲区初始化的参数严格匹配,否则会出现推入失败的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:43:03