FFmpeg源码分析:连接AVFilter的桥梁buffersrc与buffersink

FFmpeg在libavfilter模块提供音视频滤镜,而buffersrc与buffersink是连接AVFilter滤镜的桥梁。其中buffersrc是输入缓冲区,buffersink是输出缓冲区。通过调用av_buffersrc_add_frame_flags(),把待滤波的音视频帧推送到输入缓冲区;调用av_buffersink_get_frame_flags()从输出缓冲区取出滤波后的音视频帧。

 

一、buffersrc

buffersrc是滤波器的输入缓冲区。主要有BufferSourceContext结构体和av_buffersrc_add_frame_flags函数。

1、BufferSourceContext

输入缓存源上下文主要包含视频宽高、像素格式,音频采样率、声道数、声道布局、采样格式,结构体如下:

typedef struct BufferSourceContext {
    const AVClass    *class;
    AVRational        time_base;
    AVRational        frame_rate;
    unsigned          nb_failed_requests;

    /* video only */
    int                w, h;
	AVRational         pixel_aspect;
	AVBufferRef        *hw_frames_ctx;
    enum AVPixelFormat pix_fmt;

    /* audio only */
	int      channels;
    int      sample_rate;
    uint64_t channel_layout;
    char    *channel_layout_str;
	enum     AVSampleFormat sample_fmt;

    int eof;
} BufferSourceContext;

2、av_buffersrc_add_frame_flags

av_buffersrc_add_frame_flags(),向输入缓冲区添加音视频帧,带有flag。其中flag包括不检测格式是否发生变化、立即推送帧到输出队列、保持音视频帧的引用,枚举类型如下:

enum {
    // 不检测格式是否发生变化
    AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT = 1,

    // 立即推送帧到输出队列
    AV_BUFFERSRC_FLAG_PUSH = 4,

    // 保持音视频帧的引用
    AV_BUFFERSRC_FLAG_KEEP_REF = 8
};

与之相似函数是av_buffersrc_add_frame()。调用函数流程如下图所示:

av_buffersrc_add_frame_flags函数位于libavfilter/buffersrc.c,具体源码如下:

int av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
{
    BufferSourceContext *s = ctx->priv;
    AVFrame *copy;
    int refcounted, ret;

    if (frame && frame->channel_layout &&
        av_get_channel_layout_nb_channels(frame->channel_layout) != frame->channels) {
        return AVERROR(EINVAL);
    }
    s->nb_failed_requests = 0;
    if (!frame)
        return av_buffersrc_close(ctx, AV_NOPTS_VALUE, flags);
    if (s->eof)
        return AVERROR(EINVAL);
    refcounted = !!frame->buf[0];
    if (!(flags & AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT)) {
        switch (ctx->outputs[0]->type) {
        case AVMEDIA_TYPE_VIDEO:
		    // 检测视频参数:宽、高、像素格式
            CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height,
                                     frame->format, frame->pts);
            break;
        case AVMEDIA_TYPE_AUDIO:
            if (!frame->channel_layout)
                frame->channel_layout = s->channel_layout;
			// 检测音频参数:采样格式、采样率、声道布局、声道数
            CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
                                     frame->channels, frame->format, frame->pts);
            break;
        default:
            return AVERROR(EINVAL);
        }
    }
    if (!(copy = av_frame_alloc()))
        return AVERROR(ENOMEM);
    if (refcounted && !(flags & AV_BUFFERSRC_FLAG_KEEP_REF)) {
        av_frame_move_ref(copy, frame);
    } else {
        ret = av_frame_ref(copy, frame);
        if (ret < 0) {
            av_frame_free(&copy);
            return ret;
        }
    }
    // 执行滤波
    ret = ff_filter_frame(ctx->outputs[0], copy);
    if (ret < 0)
        return ret;
    // 推送到输出队列
    if ((flags & AV_BUFFERSRC_FLAG_PUSH)) {
        ret = push_frame(ctx->graph);
        if (ret < 0)
            return ret;
    }

    return 0;
}

其中,push_frame()函数只做一件事:调用ff_filter_graph_run_once进行滤波,代码如下:

static int push_frame(AVFilterGraph *graph)
{
    int ret;

    while (1) {
		// 遍历查找滤波器进行滤波
        ret = ff_filter_graph_run_once(graph);
        if (ret == AVERROR(EAGAIN))
            break;
        if (ret < 0)
            return ret;
    }
    return 0;
}

 而ff_filter_graph_run_once()函数位于avfiltergraph.c,主要是for循环遍历查找滤波器进行滤波:

int ff_filter_graph_run_once(AVFilterGraph *graph)
{
    AVFilterContext *filter;
    unsigned i;

    av_assert0(graph->nb_filters);
    filter = graph->filters[0];
    for (i = 1; i < graph->nb_filters; i++)
        if (graph->filters[i]->ready > filter->ready)
            filter = graph->filters[i];
    if (!filter->ready)
        return AVERROR(EAGAIN);
    return ff_filter_activate(filter);
}

二、buffersink

buffersink是滤波器的输出缓冲区。主要有BufferSinkContext结构体和av_buffersink_get_frame_flags函数。

1、BufferSinkContext

输出缓存池上下文主要包含视频像素格式,音频采样率、声道数、声道布局、采样格式,结构体如下:

typedef struct BufferSinkContext {
    const AVClass *class;
    unsigned warning_limit;

    /* only used for video */
    enum AVPixelFormat *pixel_fmts;
    int pixel_fmts_size;

    /* only used for audio */
    enum AVSampleFormat *sample_fmts;
    int sample_fmts_size;
    int64_t *channel_layouts;
    int channel_layouts_size;
    int *channel_counts;
    int channel_counts_size;
    int all_channel_counts;
    int *sample_rates;
    int sample_rates_size;

    AVFrame *peeked_frame;
} BufferSinkContext;

2、av_buffersink_get_frame_flags

该函数主要从输出缓冲池取出一帧数据,内部调用get_frame_internal()函数,代码如下:

static int get_frame_internal(AVFilterContext *ctx, AVFrame *frame, 
                              int flags, int samples)
{
    BufferSinkContext *buf = ctx->priv;
    AVFilterLink *inlink = ctx->inputs[0];
    int status, ret;
    AVFrame *cur_frame;
    int64_t pts;

    if (buf->peeked_frame)
        return return_or_keep_frame(buf, frame, buf->peeked_frame, flags);

    while (1) {
		// 获取采样数或帧数据
        ret = samples ? ff_inlink_consume_samples(inlink, samples, samples, &cur_frame) :
                        ff_inlink_consume_frame(inlink, &cur_frame);
        if (ret < 0) {
            return ret;
        } else if (ret) {
            return return_or_keep_frame(buf, frame, cur_frame, flags);
        } else if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
            return status;
        } else if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST)) {
            return AVERROR(EAGAIN);
        } else if (inlink->frame_wanted_out) {
            ret = ff_filter_graph_run_once(ctx->graph);
            if (ret < 0)
                return ret;
        } else {
            ff_inlink_request_frame(inlink);
        }
    }
}

int av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
{
    return get_frame_internal(ctx, frame, flags, ctx->inputs[0]->min_samples);
}

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值