FFmpeg:BitStreamFilter(AVBSFContext 和 AVBitStreamFilter)

前言

内容为个人总结的一些理解,如果有错误感谢提出交流~

1. 何时会用到?

一句话:BitStreamFilter一般用在同一编码间不同格式的转换,这过程不重新编解码
举个例子,比如h264编码有Annex B格式AVCC 格式(也叫 AVC1 格式或 MPEG-4 格式)。其中Annex B格式是编解码器需要的格式,但是mp4一般封装的是AVCC格式,这时就有可以用BitStreamFilter做格式的转换。

2. 如何使用?

FFmpeg针对BitStreamFilter在4.4版本做了改动,
旧的 AVBitStreamFilterContext 👉 在新 API 里被 AVBSFContext 替代,
旧的 AVBitStreamFilter 👉 在新 API 里变成只读的 const AVBitStreamFilter 描述符,
同时,一些api也被废弃。

比如旧版本的使用,简单写就是

//初始化:
bsfc = av_bitstream_filter_init("h264_mp4toannexb");
//使用
av_bitstream_filter_filter(bsfc,&CodecCtx,NULL,&dummy,&dummy_size,
		                          (const uint8_t*)pBuffer,
								  lUseLen,0);

这里不对旧版本api说明,毕竟在新版本已经废弃不能使用了,写出来只是为了与后面的新版本做一个对比。

相比过去BitStreamFilter一行代码解决,现在采用send 和 receive的方式,同时将CodocCtx解藕出去(可以看上面旧代码使用的过程中传入codec作为参数)。

先贴简化代码,然后再探讨探讨

//初始化
int InitBSFContext(AVBSFContext** bsf_ctx, char* filter_name, AVCodecContext *CodecCtx)
{
    
    AVBSFContext *ctx = nullptr;
    const AVBitStreamFilter *filter = nullptr;
    //获取需要的const AVBitStreamFilter描述符
    filter = av_bsf_get_by_name(filter_name);	

    //使用filter alloc AVBSFContext
    av_bsf_alloc(filter, &ctx);
	
    //获取codecCtx中参数到ctx
    avcodec_parameters_from_context(ctx->par_in, CodecCtx);

    //初始化ctx
    av_bsf_init(ctx);

    *bsf_ctx = ctx;

    return 0;
}

//使用(大致替代旧的av_bitstream_filter_filter)
/*
	bsfc:AVBSFContext
	poutbuf:处理后的data buf,外部管理
	poutbuf_size:处理后的data size
	buf:需要处理的data buf,外部管理
	buf_size:需要处理的data size
	is_keyframe:是否是关键帧
*/
int DoBSFFilter(AVBSFContext* bsfc, uint8_t **poutbuf, int *poutbuf_size, const uint8_t *buf, int buf_size, bool is_keyframe)
{
    AVPacket* pkt = ...

    av_packet_from_data(pkt, buf, buf_size);
    pkt->flags |= is_keyframe ? AV_PKT_FLAG_KEY : 0;

    av_bsf_send_packet(bsfc, pkt);

    av_bsf_receive_packet(bsfc, pkt);

    memcpy(*poutbuf, pkt->data, pkt->size);
    *poutbuf_size = pkt->size;

    av_packet_unref(pkt);

    return 0;
}

3. 探讨

3.1 AVBSFContext 和 AVBitStreamFilter内部是什么?

typedef struct AVBSFContext {
    const AVClass *av_class;
    
    //BSF描述符,后面详细再说
    const struct AVBitStreamFilter *filter;
    
    //运行时状态,保存bfs生命周期间的一些信息		
    void *priv_data;		

    //输入流的编码参数,从codec上下文中获取
    //在初始化代码中,avcodec_parameters_from_context(ctx->par_in, CodecCtx);
    AVCodecParameters *par_in;		

    //这个输出流的编码参数,一般使用者不去操作他,这个是给内部filter操作的,使用者读就好
    AVCodecParameters *par_out;

    //时间基,和上面类似,用的比较少
    AVRational time_base_in;
    AVRational time_base_out;
} AVBSFContext;

typedef struct AVBitStreamFilter {
    //filter描述符名,比如h264_mp4toannexb
    //在刚刚初始化代码中,av_bsf_get_by_name(filter_name);	
    const char *name;

    //当前这个filter支持的codec
    const enum AVCodecID *codec_ids;

    //私有参数配置,这里不关注
    const AVClass *priv_class;
} AVBitStreamFilter;

3.2 AVBitStreamFilter内部是怎么实现的?

AVBitStreamFilter在ffmpeg内部实际是下面这个样子的,各变量什么作用看名字就能知道,这里不做说明。

typedef struct FFBitStreamFilter {
    AVBitStreamFilter p;

    int priv_data_size;
    int (*init)(AVBSFContext *ctx);
    int (*filter)(AVBSFContext *ctx, AVPacket *pkt);
    void (*close)(AVBSFContext *ctx);
    void (*flush)(AVBSFContext *ctx);
} FFBitStreamFilter;

ffmpeg在内部定义了多个这个的filter描述符,这里用h264_mp4toannexb来作为例子。ffmpeg会在libavcodec下通过h264_mp4toannexb_bsf.c文件来编写这个filter。
1.
首先是H264BSFContext,这就是该filter运行时需要保存的状态信息,记录在AVBSFContext的priv_data中。

2.
通过函数指针,实现该filter的功能,这里三个函数指针不做说明,看名字也大概可以知道干什么,每个filter的实现去看对应的c文件就行,其中filter函数指针就是数据流过滤的实现
在这里插入图片描述
3.
av_bsf_get_by_name就是找到对应的filter,然后给到AVBSFContext。这里值的一提的是,这个函数会在bitstream_filters 数组中寻找,而这个数组的来源是"libavcodec/bsf_list.c",这是在执行 ./configure 时根据输入的参数 --enable-XXX 动态生成的。

3.3 av_bsf_init干了什么?

这个是在获取avcodec_parameters_from_context后调用的bsf初始化接口,源代码内部总的来说做了三件事

  1. 判断codec和filter是不是适配,avcodec_parameters_from_context(ctx->par_in, CodecCtx);中的CodecCtx在不在AVBitStreamFilter的codec_ids中
  2. 出入参、出入time_base拷贝
  3. 调用上面提到filter的init函数指针
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值