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_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 == 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);
}

猜你喜欢

转载自blog.csdn.net/u011686167/article/details/123080394
今日推荐