本文主要是介绍FFmpeg源码分析:avcodec_open()打开编解码器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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 = 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 = 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 = 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_typeif (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);elseret = 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 == 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
由于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 = 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);
}
这篇关于FFmpeg源码分析:avcodec_open()打开编解码器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!