FFmpeg在libavcodec模块提供编解码能力,使用流程:寻找编解码器、分配编解码器上下文、打开编解码器、编码成AVPacket/解码成AVFrame、关闭编解码器。本文以avcodec_open()打开编解码器为主,对编解码整体流程进行分析。
一、寻找编解码器 寻找解码器的函数位于libavcodec/allcodecs.c。可以通过avcodec_find_encoder()根据AVCodecID寻找编码器,也可以通过avcodec_find_encoder_by_name()根据名称进行查找。同样地,可以使用avcodec_find_decoder()和avcodec_find_decoder_by_name()来寻找解码器。具体查找流程可参考:FFmpeg源码分析——寻找编解码器。
二、分配编解码器上下文 寻找到编解码器后,使用AVCodec分配编解码器上下文,代码位于libavcodec/options.c。首先使用av_malloc()来分配内存,然后调用init_context_defaults()初始化默认参数,代码如下:
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
{
AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext));
if (!avctx)
return NULL;
if (init_context_defaults(avctx, codec) < 0) {
av_free(avctx);
return NULL;
}return avctx;
}
三、打开编解码器 1、avcodec_open
在分配好编解码器上下文后,使用AVCodecContext来打开编解码器。其函数声明位于libavcodec/avcodec.h:
/**
* Initialize the AVCodecContext to use the given AVCodec. Prior to using this
* function the context has to be allocated with avcodec_alloc_context3().
*
* The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
* avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
* retrieving a codec.
*
* @warning This function is not thread safe!
*
* @note Always call this function before using decoding routines (such as
* @ref avcodec_receive_frame()).
*
* @code
* av_dict_set(&opts, "b", "2.5M", 0);
* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
* if (!codec)
*exit(1);
*
* context = avcodec_alloc_context3(codec);
*
* if (avcodec_open2(context, codec, opts) < 0)
*exit(1);
* @endcode
*
* @param avctx The context to initialize.
* @param codec The codec to open this context for.
* @param options A dictionary filled with AVCodecContext and codec-private options.
*
* @return zero on success, a negative value on error
*/
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
由描述可知,该函数不是线程安全,在执行编解码操作之前调用该函数。 函数实现位于libavcodec/avcodec.c,代码如下:
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
int ret = 0;
int codec_init_ok = 0;
AVDictionary *tmp = NULL;
AVCodecInternal *avci;
// 检查codec是否已经打开
if (avcodec_is_open(avctx))
return 0;
// 检查codec是否有效
if (!codec && !avctx->codec) {
return AVERROR(EINVAL);
}
if (codec && avctx->codec && codec != avctx->codec) {
return AVERROR(EINVAL);
}
if (!codec)
codec = avctx->codec;
if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE)
return AVERROR(EINVAL);
// 拷贝options参数集合
if (options)
av_dict_copy(&tmp, *options, 0);
lock_avcodec(codec);
// 分配AVCodecInternal内存
avci = av_mallocz(sizeof(*avci));
if (!avci) {
ret = AVERROR(ENOMEM);
goto end;
}
// 分配frame、packet、fifo内存
avctx->internal= avci;
avci->buffer_frame= av_frame_alloc();
avci->buffer_pkt= av_packet_alloc();
avci->es.in_frame= av_frame_alloc();
avci->ds.in_pkt= av_packet_alloc();
avci->last_pkt_props = av_packet_alloc();
avci->pkt_props= av_fifo_alloc(sizeof(*avci->last_pkt_props));
if (!avci->buffer_frame || !avci->buffer_pkt||
!avci->es.in_frame|| !avci->ds.in_pkt||
!avci->last_pkt_props || !avci->pkt_props) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
avci->skip_samples_multiplier = 1;
if (codec->priv_data_size > 0) {
if (!avctx->priv_data) {
avctx->priv_data = https://www.it610.com/article/av_mallocz(codec->priv_data_size);
if (!avctx->priv_data) {
ret = AVERROR(ENOMEM);
goto free_and_end;
}
if (codec->priv_class) {
*(const AVClass **)avctx->priv_data = https://www.it610.com/article/codec->priv_class;
av_opt_set_defaults(avctx->priv_data);
}
}
if (codec->priv_class && (ret = av_opt_set_dict(avctx->priv_data, &tmp)) < 0)
goto free_and_end;
} else {
avctx->priv_data = https://www.it610.com/article/NULL;
}
if ((ret = av_opt_set_dict(avctx, &tmp)) < 0)
goto free_and_end;
if (avctx->codec_whitelist && av_match_list(codec->name, avctx->codec_whitelist, ',') <= 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (!(avctx->coded_width && avctx->coded_height && avctx->width && avctx->height &&
(avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_VP6F
|| avctx->codec_id == AV_CODEC_ID_DXV))) {
if (avctx->coded_width && avctx->coded_height)
ret = ff_set_dimensions(avctx, avctx->coded_width, avctx->coded_height);
else if (avctx->width && avctx->height)
ret = ff_set_dimensions(avctx, avctx->width, avctx->height);
if (ret < 0)
goto free_and_end;
}
// 检查查视频宽高
if ((avctx->coded_width || avctx->coded_height || avctx->width || avctx->height)
&& (av_image_check_size2(avctx->coded_width, avctx->coded_height,
avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx) < 0
|| av_image_check_size2(avctx->width, avctx->height,
avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx) < 0)) {
ff_set_dimensions(avctx, 0, 0);
}
// 检查视频宽高比
if (avctx->width > 0 && avctx->height > 0) {
if (av_image_check_sar(avctx->width, avctx->height,
avctx->sample_aspect_ratio) < 0) {
avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
}
}
// 检查音频声道
if (avctx->channels > FF_SANE_NB_CHANNELS || avctx->channels < 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
// 如果是音频解码器,检查声道数
if (av_codec_is_decoder(codec) &&
codec->type == AVMEDIA_TYPE_AUDIO &&
!(codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF) &&
avctx->channels == 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
// 检查音频采样率
if (avctx->sample_rate < 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
// 检查块对齐
if (avctx->block_align < 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
avctx->codec = codec;
if ((avctx->codec_type == AVMEDIA_TYPE_UNKNOWN || avctx->codec_type == codec->type) &&
avctx->codec_id == AV_CODEC_ID_NONE) {
avctx->codec_type = codec->type;
avctx->codec_id= codec->id;
}
// 检查codec_id和codec_type
if (avctx->codec_id != codec->id || (avctx->codec_type != codec->type
&& avctx->codec_type != AVMEDIA_TYPE_ATTACHMENT)) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
avctx->frame_number = 0;
avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id);
// 检查codec能力集,codec是否属于实验性的
if ((avctx->codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) &&
avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
const char *codec_string = av_codec_is_encoder(codec) ? "encoder" : "decoder";
const AVCodec *codec2;
codec2 = av_codec_is_encoder(codec) ?
avcodec_find_encoder(codec->id) : avcodec_find_decoder(codec->id);
if (!(codec2->capabilities & AV_CODEC_CAP_EXPERIMENTAL))
ret = AVERROR_EXPERIMENTAL;
goto free_and_end;
}
// 检查音频时间基
if (avctx->codec_type == AVMEDIA_TYPE_AUDIO &&
(!avctx->time_base.num || !avctx->time_base.den)) {
avctx->time_base.num = 1;
avctx->time_base.den = avctx->sample_rate;
}if (av_codec_is_encoder(avctx->codec))
ret = ff_encode_preinit(avctx);
else
ret = ff_decode_preinit(avctx);
if (ret < 0)
goto free_and_end;
// 初始化编码器线程
if (CONFIG_FRAME_THREAD_ENCODER && av_codec_is_encoder(avctx->codec)) {
unlock_avcodec(codec);
ret = ff_frame_thread_encoder_init(avctx, options ? *options : NULL);
lock_avcodec(codec);
if (ret < 0)
goto free_and_end;
}
if (HAVE_THREADS
&& !(avci->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) {
ret = ff_thread_init(avctx);
if (ret < 0) {
goto free_and_end;
}
}
if (!HAVE_THREADS && !(codec->caps_internal & FF_CODEC_CAP_AUTO_THREADS))
avctx->thread_count = 1;
if (avctx->codec->init && (!(avctx->active_thread_type&FF_THREAD_FRAME)
|| avci->frame_thread_encoder)) {
ret = avctx->codec->init(avctx);
if (ret < 0) {
codec_init_ok = -1;
goto free_and_end;
}
codec_init_ok = 1;
}ret=0;
// 如果是解码器
if (av_codec_is_decoder(avctx->codec)) {
// 检查码率
if (!avctx->bit_rate)
avctx->bit_rate = get_bit_rate(avctx);
// 检查音频声道布局
if (avctx->channel_layout) {
int channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
if (!avctx->channels)
avctx->channels = channels;
else if (channels != avctx->channels) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
avctx->channel_layout = 0;
}
}
if (avctx->channels && avctx->channels < 0 ||
avctx->channels > FF_SANE_NB_CHANNELS) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
// 检查每个sample的位数
if (avctx->bits_per_coded_sample < 0) {
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->sub_charenc) {
if (avctx->codec_type != AVMEDIA_TYPE_SUBTITLE) {
ret = AVERROR(EINVAL);
goto free_and_end;
} else if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) {
avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_DO_NOTHING;
} else {
if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_AUTOMATIC)
avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_PRE_DECODER;
if (avctx->sub_charenc_mode == FF_SUB_CHARENC_MODE_PRE_DECODER) {
ret = AVERROR(ENOSYS);
goto free_and_end;
}
}
}
}
if (codec->priv_data_size > 0 && avctx->priv_data && codec->priv_class) {
av_assert0(*(const AVClass **)avctx->priv_data =https://www.it610.com/article/= codec->priv_class);
}end:
unlock_avcodec(codec);
if (options) {
av_dict_free(options);
*options = tmp;
}return ret;
free_and_end:
if (avctx->codec && avctx->codec->close &&
(codec_init_ok > 0 || (codec_init_ok < 0 &&
avctx->codec->caps_internal & FF_CODEC_CAP_INIT_CLEANUP)))
avctx->codec->close(avctx);
if (HAVE_THREADS && avci->thread_ctx)
ff_thread_free(avctx);
if (codec->priv_class && avctx->priv_data)
av_opt_free(avctx->priv_data);
av_opt_free(avctx);
if (av_codec_is_encoder(avctx->codec)) {
av_freep(&avctx->extradata);
avctx->extradata_size = 0;
}
av_dict_free(&tmp);
av_freep(&avctx->priv_data);
av_freep(&avctx->subtitle_header);
av_frame_free(&avci->buffer_frame);
av_packet_free(&avci->buffer_pkt);
av_packet_free(&avci->last_pkt_props);
av_fifo_freep(&avci->pkt_props);
av_packet_free(&avci->ds.in_pkt);
av_frame_free(&avci->es.in_frame);
av_bsf_free(&avci->bsf);
av_buffer_unref(&avci->pool);
av_freep(&avci);
avctx->internal = NULL;
avctx->codec = NULL;
goto end;
}
由源码可知,avcodec_open()主要是分配各种内存,然后进行各种参数检查,包括:视频宽高、音频采样率、声道数、声道布局、时间基、codec能力集等等。
2、avcodec_parameters_from_context
【音视频开发|FFmpeg源码分析(avcodec_open()打开编解码器)】由于AVCodecContext已经被标记为过时,建议使用AVCodecParameters代替。在avcodec_par.c提供avcodec_parameters_from_context()把AVCodecContext参数拷贝到AVCodecParameters中。相反,如果需要把AVCodecParameters参数拷贝到AVCodecContext中,则调用avcodec_parameters_to_context()。
四、音视频编解码 在FFmpeg3.4版本,旧的编解码API已经标记为过时,新版改为异步方式。我们对新旧版本API进行介绍:
1、编码
旧版的视频编码API为avcodec_encode_video2(),音频编码API为avcodec_encode_audio2()。而新版本API统一为avcodec_send_frame/avcodec_receive_packet。
2、解码
旧版的视频编码API为avcodec_decode_video2(),音频编码API为avcodec_decode_audio4()。而新版本API统一为avcodec_send_packet/avcodec_receive_frame。
五、关闭编解码器 1、avcodec_close
avcodec_close()函数用于关闭编解码器。函数声明位于libavcodec/avcodec.h,声明如下:
/**
* Close a given AVCodecContext and free all the data associated with it
* (but not the AVCodecContext itself).
*
* @note Do not use this function. Use avcodec_free_context() to destroy a
* codec context (either open or closed).
*/
int avcodec_close(AVCodecContext *avctx);
由描述可知,该函数用于关闭给定的编解码器上下文,以及关联的数据,但不包括AVCodecContext本身。代码如下:
int avcodec_close(AVCodecContext *avctx)
{
int i;
if (!avctx)
return 0;
if (avcodec_is_open(avctx)) {
// 退出线程
if (CONFIG_FRAME_THREAD_ENCODER &&
avctx->internal->frame_thread_encoder && avctx->thread_count > 1) {
ff_frame_thread_encoder_free(avctx);
}
if (HAVE_THREADS && avctx->internal->thread_ctx)
ff_thread_free(avctx);
if (avctx->codec && avctx->codec->close)
avctx->codec->close(avctx);
avctx->internal->byte_buffer_size = 0;
av_freep(&avctx->internal->byte_buffer);
av_frame_free(&avctx->internal->buffer_frame);
av_packet_free(&avctx->internal->buffer_pkt);
av_packet_unref(avctx->internal->last_pkt_props);
// 清空fifo队列
while (av_fifo_size(avctx->internal->pkt_props) >=
sizeof(*avctx->internal->last_pkt_props)) {
av_fifo_generic_read(avctx->internal->pkt_props,
avctx->internal->last_pkt_props,
sizeof(*avctx->internal->last_pkt_props),
NULL);
av_packet_unref(avctx->internal->last_pkt_props);
}
av_packet_free(&avctx->internal->last_pkt_props);
av_fifo_freep(&avctx->internal->pkt_props);
av_packet_free(&avctx->internal->ds.in_pkt);
av_frame_free(&avctx->internal->es.in_frame);
av_buffer_unref(&avctx->internal->pool);
// 硬件加速反初始化
if (avctx->hwaccel && avctx->hwaccel->uninit)
avctx->hwaccel->uninit(avctx);
av_freep(&avctx->internal->hwaccel_priv_data);
// 释放bitstream filter资源
av_bsf_free(&avctx->internal->bsf);
av_freep(&avctx->internal);
}for (i = 0;
i < avctx->nb_coded_side_data;
i++)
av_freep(&avctx->coded_side_data[i].data);
av_freep(&avctx->coded_side_data);
avctx->nb_coded_side_data = https://www.it610.com/article/0;
av_buffer_unref(&avctx->hw_frames_ctx);
av_buffer_unref(&avctx->hw_device_ctx);
if (avctx->priv_data && avctx->codec && avctx->codec->priv_class)
av_opt_free(avctx->priv_data);
av_opt_free(avctx);
av_freep(&avctx->priv_data);
if (av_codec_is_encoder(avctx->codec)) {
av_freep(&avctx->extradata);
}
avctx->codec = NULL;
avctx->active_thread_type = 0;
return 0;
}
2、avcodec_free_context
avcodec_free_context()函数用于释放AVCodecContext上下文,与上面的分配上下文avcodec_alloc_context3()相对应。同样位于options.c,首先调用avcodec_close()函数来关闭编解码器,最终释放AVCodecContext结构体,具体代码如下:
void avcodec_free_context(AVCodecContext **pavctx)
{
AVCodecContext *avctx = *pavctx;
if (!avctx)
return;
// 调用avcodec_close关闭编解码器
avcodec_close(avctx);
av_freep(&avctx->extradata);
av_freep(&avctx->subtitle_header);
av_freep(&avctx->intra_matrix);
av_freep(&avctx->inter_matrix);
av_freep(&avctx->rc_override);
// 释放AVCodecContext结构体
av_freep(pavctx);
}