【FFmpeg】avcodec_open2函数

目录

  • 1. avcodec_open2
    • 1.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)
    • 1.2 编解码器的初始化(init)
    • 1.3 释放编解码器(ff_codec_close)

FFmpeg相关记录:

示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染

流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析

结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVIOContext结构体
【FFmpeg】AVPacket结构体

函数分析:
【通用】
【FFmpeg】avcodec_find_encoder和avcodec_find_decoder
【FFmpeg】关键结构体的初始化和释放(AVFormatContext、AVIOContext等)

【推流】
【FFmpeg】avformat_open_input函数
【FFmpeg】avformat_find_stream_info函数
【FFmpeg】avformat_alloc_output_context2函数
【FFmpeg】avio_open2函数
【FFmpeg】avformat_write_header函数
【FFmpeg】av_write_frame函数

avcodec_open2的函数调用关系为
在这里插入图片描述

1. avcodec_open2

/**
 * 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.
 *
 * Depending on the codec, you might need to set options in the codec context
 * also for decoding (e.g. width, height, or the pixel or audio sample format in
 * the case the information is not available in the bitstream, as when decoding
 * raw audio or video).
 *
 * Options in the codec context can be set either by setting them in the options
 * AVDictionary, or by setting the values in the context itself, directly or by
 * using the av_opt_set() API before calling this function.
 *
 * Example:
 * @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
 *
 * In the case AVCodecParameters are available (e.g. when demuxing a stream
 * using libavformat, and accessing the AVStream contained in the demuxer), the
 * codec parameters can be copied to the codec context using
 * avcodec_parameters_to_context(), as in the following example:
 *
 * @code
 * AVStream *stream = ...;
 * context = avcodec_alloc_context3(codec);
 * if (avcodec_parameters_to_context(context, stream->codecpar) < 0)
 *     exit(1);
 * if (avcodec_open2(context, codec, NULL) < 0)
 *     exit(1);
 * @endcode
 *
 * @note Always call this function before using decoding routines (such as
 * @ref avcodec_receive_frame()).
 *
 * @param avctx The context to initialize.
 * @param codec The codec to open this context for. If a non-NULL codec has been
 *              previously passed to avcodec_alloc_context3() or
 *              for this context, then this parameter MUST be either NULL or
 *              equal to the previously passed codec.
 * @param options A dictionary filled with AVCodecContext and codec-private
 *                options, which are set on top of the options already set in
 *                avctx, can be NULL. On return this object will be filled with
 *                options that were not found in the avctx codec context.
 *
 * @return zero on success, a negative value on error
 * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
 *      av_dict_set(), av_opt_set(), av_opt_find(), avcodec_parameters_to_context()
 */
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

函数的定义位于libavcodec\avcodec.c中,从注释上看,主要的功能是使用给定的AVCodec情况下,来初始化AVCodecContext。在使用此函数之前,必须使用avcodec_alloc_context3来分配上下文,可以在外面配置options,作为codec初始化的参数

int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
    int ret = 0;
    AVCodecInternal *avci;
    const FFCodec *codec2;
	// 1.输入检查
	// 已经打开,直接返回
    if (avcodec_is_open(avctx))
        return 0;
	// 一些错误信息打印
    if (!codec && !avctx->codec) {
        av_log(avctx, AV_LOG_ERROR, "No codec provided to avcodec_open2()\n");
        return AVERROR(EINVAL);
    }
    if (codec && avctx->codec && codec != avctx->codec) {
        av_log(avctx, AV_LOG_ERROR, "This AVCodecContext was allocated for %s, "
                                    "but %s passed to avcodec_open2()\n", avctx->codec->name, codec->name);
        return AVERROR(EINVAL);
    }
    if (!codec)
        codec = avctx->codec;
    codec2 = ffcodec(codec);

    if ((avctx->codec_type != AVMEDIA_TYPE_UNKNOWN && avctx->codec_type != codec->type) ||
        (avctx->codec_id   != AV_CODEC_ID_NONE     && avctx->codec_id   != codec->id)) {
        av_log(avctx, AV_LOG_ERROR, "Codec type or id mismatches\n");
        return AVERROR(EINVAL);
    }

    avctx->codec_type = codec->type;
    avctx->codec_id   = codec->id;
    avctx->codec      = codec;

    if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE)
        return AVERROR(EINVAL);
	// 2.部分结构体内存分配
    avci = av_codec_is_decoder(codec) ?
        ff_decode_internal_alloc()    :
        ff_encode_internal_alloc();
    if (!avci) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avctx->internal = avci;

    avci->buffer_frame = av_frame_alloc();
    avci->buffer_pkt = av_packet_alloc();
    if (!avci->buffer_frame || !avci->buffer_pkt) {
        ret = AVERROR(ENOMEM);
        goto free_and_end;
    }
 
    if (codec2->priv_data_size > 0) {
        if (!avctx->priv_data) {
            avctx->priv_data = av_mallocz(codec2->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, options)) < 0)
            goto free_and_end;
    } else {
        avctx->priv_data = NULL;
    }
    if ((ret = av_opt_set_dict(avctx, options)) < 0)
        goto free_and_end;
    // 3.一些琐碎的检查
	// 白名单的检查
    if (avctx->codec_whitelist && av_match_list(codec->name, avctx->codec_whitelist, ',') <= 0) {
        av_log(avctx, AV_LOG_ERROR, "Codec (%s) not on whitelist \'%s\'\n", codec->name, avctx->codec_whitelist);
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }

    // only call ff_set_dimensions() for non H.264/VP6F/DXV codecs so as not to overwrite previously setup dimensions
    // 对于非H.264/VP6F/DXV编解码器,只调用ff_set_dimensions(),以免覆盖先前设置的尺寸
    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)) {
        av_log(avctx, AV_LOG_WARNING, "Ignoring invalid width/height values\n");
        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) {
            av_log(avctx, AV_LOG_WARNING, "ignoring invalid SAR: %u/%u\n",
                   avctx->sample_aspect_ratio.num,
                   avctx->sample_aspect_ratio.den);
            avctx->sample_aspect_ratio = (AVRational){ 0, 1 };
        }
    }
	// 检查采样率
    if (avctx->sample_rate < 0) {
        av_log(avctx, AV_LOG_ERROR, "Invalid sample rate: %d\n", avctx->sample_rate);
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }

    if (avctx->block_align < 0) {
        av_log(avctx, AV_LOG_ERROR, "Invalid block align: %d\n", avctx->block_align);
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }

    /* AV_CODEC_CAP_CHANNEL_CONF is a decoder-only flag; so the code below
     * in particular checks that nb_channels is set for all audio encoders. */
    if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && !avctx->ch_layout.nb_channels
        && !(codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF)) {
        av_log(avctx, AV_LOG_ERROR, "%s requires channel layout to be set\n",
               av_codec_is_decoder(codec) ? "Decoder" : "Encoder");
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }
    // 检查音频的声道数
    if (avctx->ch_layout.nb_channels && !av_channel_layout_check(&avctx->ch_layout)) {
        av_log(avctx, AV_LOG_ERROR, "Invalid channel layout\n");
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }
    if (avctx->ch_layout.nb_channels > FF_SANE_NB_CHANNELS) {
        av_log(avctx, AV_LOG_ERROR, "Too many channels: %d\n", avctx->ch_layout.nb_channels);
        ret = AVERROR(EINVAL);
        goto free_and_end;
    }

    avctx->frame_num = 0;
    avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id);
	// 如果codec的能力包括了experimental
    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;
        av_log(avctx, AV_LOG_ERROR,
               "The %s '%s' is experimental but experimental codecs are not enabled, "
               "add '-strict %d' if you want to use it.\n",
               codec_string, codec->name, FF_COMPLIANCE_EXPERIMENTAL);
        codec2 = av_codec_is_encoder(codec) ? avcodec_find_encoder(codec->id) : avcodec_find_decoder(codec->id);
        if (!(codec2->capabilities & AV_CODEC_CAP_EXPERIMENTAL))
            av_log(avctx, AV_LOG_ERROR, "Alternatively use the non experimental %s '%s'.\n",
                codec_string, codec2->name);
        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;
    }
	// 4.编解码器的预初始化
    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 (HAVE_THREADS && !avci->frame_thread_encoder) {
        /* Frame-threaded decoders call FFCodec.init for their child contexts. */
        lock_avcodec(codec2);
        ret = ff_thread_init(avctx);
        unlock_avcodec(codec2);
        if (ret < 0) {
            goto free_and_end;
        }
    }
    if (!HAVE_THREADS && !(codec2->caps_internal & FF_CODEC_CAP_AUTO_THREADS))
        avctx->thread_count = 1;

    if (!(avctx->active_thread_type & FF_THREAD_FRAME) ||
        avci->frame_thread_encoder) {
        if (codec2->init) {
            lock_avcodec(codec2);
            // 5.编解码器的初始化
            ret = codec2->init(avctx);
            unlock_avcodec(codec2);
            if (ret < 0) {
                avci->needs_close = codec2->caps_internal & FF_CODEC_CAP_INIT_CLEANUP;
                goto free_and_end;
            }
        }
        avci->needs_close = 1;
    }

    ret=0;
	// 如果是解码器
    if (av_codec_is_decoder(avctx->codec)) {
        if (!avctx->bit_rate)
            avctx->bit_rate = get_bit_rate(avctx);

        /* validate channel layout from the decoder */
        // 从解码器验证声道布局
        if ((avctx->ch_layout.nb_channels && !av_channel_layout_check(&avctx->ch_layout)) ||
            avctx->ch_layout.nb_channels > FF_SANE_NB_CHANNELS) {
            ret = AVERROR(EINVAL);
            goto free_and_end;
        }
        if (avctx->bits_per_coded_sample < 0) {
            ret = AVERROR(EINVAL);
            goto free_and_end;
        }
    }
    if (codec->priv_class)
        av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class);

end:

    return ret;
free_and_end:
	// 6.创建失败则释放codec
    ff_codec_close(avctx);
    goto end;
}

从代码中看,函数主要的流程分为几个步骤:
(1)一些检查,例如codec是否给入,检查codec type等等
(2)部分结构体内存的分配
(3)一些琐碎的检查
(4)编解码器的预初始化(ff_encode_preinit和ff_decode_preinit)
(5)编解码器的初始化(codec2->init)
(6)创建失败则释放codec(ff_codec_close)

1.1 编解码器的预初始化(ff_encode_preinit & ff_decode_preinit)

两个预初始化函数都定义在libavcodec\encode.c中
(1)编码器的预初始化(ff_encode_preinit)

/*
 * Perform encoder initialization and validation.
 * Called when opening the encoder, before the FFCodec.init() call.
 */
// 执行编码器初始化和验证
// 在打开编码器时调用,在FFCodec.init()调用之前
int ff_encode_preinit(AVCodecContext *avctx)
{
    AVCodecInternal *avci = avctx->internal;
    EncodeContext     *ec = encode_ctx(avci);
    int ret = 0;
	// 检查编码器的时间基
    if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) {
        av_log(avctx, AV_LOG_ERROR, "The encoder timebase is not set.\n");
        return AVERROR(EINVAL);
    }

    if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE &&
        !(avctx->codec->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE)) {
        av_log(avctx, AV_LOG_ERROR, "The copy_opaque flag is set, but the "
               "encoder does not support it.\n");
        return AVERROR(EINVAL);
    }
	
    switch (avctx->codec_type) {
    case AVMEDIA_TYPE_VIDEO: ret = encode_preinit_video(avctx); break; // 视频预初始化
    case AVMEDIA_TYPE_AUDIO: ret = encode_preinit_audio(avctx); break;
    }
    if (ret < 0)
        return ret;
	// 检查码率是否太小
    if (   (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)
        && avctx->bit_rate>0 && avctx->bit_rate<1000) {
        av_log(avctx, AV_LOG_WARNING, "Bitrate %"PRId64" is extremely low, maybe you mean %"PRId64"k\n", avctx->bit_rate, avctx->bit_rate);
    }

    if (!avctx->rc_initial_buffer_occupancy)
        avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3LL / 4;

    // AV_CODEC_PROP_INTRA_ONLY表示只进行帧内压缩,仅用于video和audio
    if (avctx->codec_descriptor->props & AV_CODEC_PROP_INTRA_ONLY)
        ec->intra_only_flag = AV_PKT_FLAG_KEY;
	// FF_CODEC_CB_TYPE_ENCODER表示编解码器是使用encode回调的编码器;仅限音频和视频编解码器
    if (ffcodec(avctx->codec)->cb_type == FF_CODEC_CB_TYPE_ENCODE) {
        avci->in_frame = av_frame_alloc();
        if (!avci->in_frame)
            return AVERROR(ENOMEM);
    }
	// AV_CODEC_FLAG_RECON_FRAME表示请求编码器输出重构的帧
    if ((avctx->flags & AV_CODEC_FLAG_RECON_FRAME)) {
        if (!(avctx->codec->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME)) {
            av_log(avctx, AV_LOG_ERROR, "Reconstructed frame output requested "
                   "from an encoder not supporting it\n");
            return AVERROR(ENOSYS);
        }

        avci->recon_frame = av_frame_alloc();
        if (!avci->recon_frame)
            return AVERROR(ENOMEM);
    }

    if (CONFIG_FRAME_THREAD_ENCODER) {
        ret = ff_frame_thread_encoder_init(avctx);
        if (ret < 0)
            return ret;
    }

    return 0;
}

上面的代码中,主要是进行一些检查和配置,如时间基,码率,编码flag等,还会调用encode_preinit_video进行编码信息的预初始化,如下所示,主要进行了pix fmt的检查和color_range的设置

static int encode_preinit_video(AVCodecContext *avctx)
{
    const AVCodec *c = avctx->codec;
    const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt);
    int i;

    if (!av_get_pix_fmt_name(avctx->pix_fmt)) {
        av_log(avctx, AV_LOG_ERROR, "Invalid video pixel format: %d\n",
               avctx->pix_fmt);
        return AVERROR(EINVAL);
    }
	// 检查编码格式
    if (c->pix_fmts) {
        for (i = 0; c->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
            if (avctx->pix_fmt == c->pix_fmts[i])
                break;
        if (c->pix_fmts[i] == AV_PIX_FMT_NONE) {
        	// 编码格式为NONE
            av_log(avctx, AV_LOG_ERROR,
                   "Specified pixel format %s is not supported by the %s encoder.\n",
                   av_get_pix_fmt_name(avctx->pix_fmt), c->name);
			// 输出支持的format
            av_log(avctx, AV_LOG_ERROR, "Supported pixel formats:\n");
            for (int p = 0; c->pix_fmts[p] != AV_PIX_FMT_NONE; p++) {
                av_log(avctx, AV_LOG_ERROR, "  %s\n",
                       av_get_pix_fmt_name(c->pix_fmts[p]));
            }

            return AVERROR(EINVAL);
        }
        // 设置color_range
        if (c->pix_fmts[i] == AV_PIX_FMT_YUVJ420P ||
            c->pix_fmts[i] == AV_PIX_FMT_YUVJ411P ||
            c->pix_fmts[i] == AV_PIX_FMT_YUVJ422P ||
            c->pix_fmts[i] == AV_PIX_FMT_YUVJ440P ||
            c->pix_fmts[i] == AV_PIX_FMT_YUVJ444P)
            avctx->color_range = AVCOL_RANGE_JPEG;
    }
	// bit depth的检查
    if (    avctx->bits_per_raw_sample < 0
        || (avctx->bits_per_raw_sample > 8 && pixdesc->comp[0].depth <= 8)) {
        av_log(avctx, AV_LOG_WARNING, "Specified bit depth %d not possible with the specified pixel formats depth %d\n",
            avctx->bits_per_raw_sample, pixdesc->comp[0].depth);
        avctx->bits_per_raw_sample = pixdesc->comp[0].depth;
    }
    if (avctx->width <= 0 || avctx->height <= 0) {
        av_log(avctx, AV_LOG_ERROR, "dimensions not set\n");
        return AVERROR(EINVAL);
    }

#if FF_API_TICKS_PER_FRAME
FF_DISABLE_DEPRECATION_WARNINGS
    if (avctx->ticks_per_frame && avctx->time_base.num &&
        avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) {
        av_log(avctx, AV_LOG_ERROR,
               "ticks_per_frame %d too large for the timebase %d/%d.",
               avctx->ticks_per_frame,
               avctx->time_base.num,
               avctx->time_base.den);
        return AVERROR(EINVAL);
    }
FF_ENABLE_DEPRECATION_WARNINGS
#endif

    if (avctx->hw_frames_ctx) {
        AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
        if (frames_ctx->format != avctx->pix_fmt) {
            av_log(avctx, AV_LOG_ERROR,
                   "Mismatching AVCodecContext.pix_fmt and AVHWFramesContext.format\n");
            return AVERROR(EINVAL);
        }
        // 软编格式的检查
        if (avctx->sw_pix_fmt != AV_PIX_FMT_NONE &&
            avctx->sw_pix_fmt != frames_ctx->sw_format) {
            av_log(avctx, AV_LOG_ERROR,
                   "Mismatching AVCodecContext.sw_pix_fmt (%s) "
                   "and AVHWFramesContext.sw_format (%s)\n",
                   av_get_pix_fmt_name(avctx->sw_pix_fmt),
                   av_get_pix_fmt_name(frames_ctx->sw_format));
            return AVERROR(EINVAL);
        }
        avctx->sw_pix_fmt = frames_ctx->sw_format;
    }

    return 0;
}

(2)解码器的预初始化(ff_decode_preinit)

int ff_decode_preinit(AVCodecContext *avctx)
{
    AVCodecInternal *avci = avctx->internal;
    DecodeContext     *dc = decode_ctx(avci);
    int ret = 0;

    /* if the decoder init function was already called previously,
     * free the already allocated subtitle_header before overwriting it */
    // 如果先前已经调用了解码器init函数,则在覆盖之前释放已经分配的subtitle_header
    av_freep(&avctx->subtitle_header);
	// lowres的检查
    if (avctx->codec->max_lowres < avctx->lowres || avctx->lowres < 0) {
        av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
               avctx->codec->max_lowres);
        avctx->lowres = avctx->codec->max_lowres;
    }
    // sub_charenc表示输入字幕文件的字符编码
    if (avctx->sub_charenc) {
        if (avctx->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            av_log(avctx, AV_LOG_ERROR, "Character encoding is only "
                   "supported with subtitles codecs\n");
            return AVERROR(EINVAL);
        } else if (avctx->codec_descriptor->props & AV_CODEC_PROP_BITMAP_SUB) {
            av_log(avctx, AV_LOG_WARNING, "Codec '%s' is bitmap-based, "
                   "subtitles character encoding will be ignored\n",
                   avctx->codec_descriptor->name);
            avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_DO_NOTHING;
        } else {
            /* input character encoding is set for a text based subtitle
             * codec at this point */
            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) {
#if CONFIG_ICONV
                iconv_t cd = iconv_open("UTF-8", avctx->sub_charenc);
                if (cd == (iconv_t)-1) {
                    ret = AVERROR(errno);
                    av_log(avctx, AV_LOG_ERROR, "Unable to open iconv context "
                           "with input character encoding \"%s\"\n", avctx->sub_charenc);
                    return ret;
                }
                iconv_close(cd);
#else
                av_log(avctx, AV_LOG_ERROR, "Character encoding subtitles "
                       "conversion needs a libavcodec built with iconv support "
                       "for this codec\n");
                return AVERROR(ENOSYS);
#endif
            }
        }
    }

    dc->pts_correction_num_faulty_pts =
    dc->pts_correction_num_faulty_dts = 0;
    dc->pts_correction_last_pts =
    dc->pts_correction_last_dts = INT64_MIN;

    if (   !CONFIG_GRAY && avctx->flags & AV_CODEC_FLAG_GRAY
        && avctx->codec_descriptor->type == AVMEDIA_TYPE_VIDEO)
        av_log(avctx, AV_LOG_WARNING,
               "gray decoding requested but not enabled at configuration time\n");
    if (avctx->flags2 & AV_CODEC_FLAG2_EXPORT_MVS) {
        avctx->export_side_data |= AV_CODEC_EXPORT_DATA_MVS;
    }

    if (avctx->nb_side_data_prefer_packet == 1 &&
        avctx->side_data_prefer_packet[0] == -1)
        dc->side_data_pref_mask = ~0ULL;
    else {
    	// 检查side data
        for (unsigned i = 0; i < avctx->nb_side_data_prefer_packet; i++) {
            int val = avctx->side_data_prefer_packet[i];

            if (val < 0 || val >= AV_PKT_DATA_NB) {
                av_log(avctx, AV_LOG_ERROR, "Invalid side data type: %d\n", val);
                return AVERROR(EINVAL);
            }

            for (unsigned j = 0; j < FF_ARRAY_ELEMS(sd_global_map); j++) {
                if (sd_global_map[j].packet == val) {
                    val = sd_global_map[j].frame;

                    // this code will need to be changed when we have more than
                    // 64 frame side data types
                    if (val >= 64) {
                        av_log(avctx, AV_LOG_ERROR, "Side data type too big\n");
                        return AVERROR_BUG;
                    }

                    dc->side_data_pref_mask |= 1ULL << val;
                }
            }
        }
    }
	// 分配pkt的内存
    avci->in_pkt         = av_packet_alloc();
    avci->last_pkt_props = av_packet_alloc();
    if (!avci->in_pkt || !avci->last_pkt_props)
        return AVERROR(ENOMEM);
	// 解码的bitstream filter初始化
    ret = decode_bsfs_init(avctx);
    if (ret < 0)
        return ret;

#if FF_API_DROPCHANGED
    if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED)
        av_log(avctx, AV_LOG_WARNING, "The dropchanged flag is deprecated.\n");
#endif

    return 0;
}

1.2 编解码器的初始化(init)

在进行了前面的参数检查和配置之后,需要进行编解码器的初始化,这是依据具体编解码器进行的,参考雷博的文章,记录一下libx264的初始化过程,参考下面的代码,初始化使用的是X264_init()函数

FFCodec ff_libx264_encoder = {
    .p.name           = "libx264",
    CODEC_LONG_NAME("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .p.type           = AVMEDIA_TYPE_VIDEO,
    .p.id             = AV_CODEC_ID_H264,
    .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
                        AV_CODEC_CAP_OTHER_THREADS |
                        AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE |
                        AV_CODEC_CAP_ENCODER_FLUSH |
                        AV_CODEC_CAP_ENCODER_RECON_FRAME,
    .p.priv_class     = &x264_class,
    .p.wrapper_name   = "libx264",
    .priv_data_size   = sizeof(X264Context),
    .init             = X264_init,
    FF_CODEC_ENCODE_CB(X264_frame),
    .flush            = X264_flush,
    .close            = X264_close,
    .defaults         = x264_defaults,
#if X264_BUILD < 153
    .init_static_data = X264_init_static,
#else
    .p.pix_fmts       = pix_fmts_all,
#endif
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS
#if X264_BUILD < 158
                      | FF_CODEC_CAP_NOT_INIT_THREADSAFE
#endif
                      ,
};

X264_init函数的定义位于libavcodec\libx264.c中,进行x264编码器的初始化,下面配置参数都是x264当中一些参数,包括帧间预测、码控、编码profile、编码level等等一些信息的初始化

static av_cold int X264_init(AVCodecContext *avctx)
{
    X264Context *x4 = avctx->priv_data;
    AVCPBProperties *cpb_props;
    int sw,sh;
    int ret;

    if (avctx->global_quality > 0)
        av_log(avctx, AV_LOG_WARNING, "-qscale is ignored, -crf is recommended.\n");

#if CONFIG_LIBX262_ENCODER
    if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
        x4->params.b_mpeg2 = 1;
        x264_param_default_mpeg2(&x4->params);
    } else
#endif
	// 配置默认的参数,x4从actx->priv_data中来
    x264_param_default(&x4->params);
	// 是否进行环路滤波
    x4->params.b_deblocking_filter         = avctx->flags & AV_CODEC_FLAG_LOOP_FILTER;
	// 检查是否配置了preset档位或者是tune优化
    if (x4->preset || x4->tune)
        if (x264_param_default_preset(&x4->params, x4->preset, x4->tune) < 0) {
            int i;
            av_log(avctx, AV_LOG_ERROR, "Error setting preset/tune %s/%s.\n", x4->preset, x4->tune);
            av_log(avctx, AV_LOG_INFO, "Possible presets:");
            for (i = 0; x264_preset_names[i]; i++)
                av_log(avctx, AV_LOG_INFO, " %s", x264_preset_names[i]);
            av_log(avctx, AV_LOG_INFO, "\n");
            av_log(avctx, AV_LOG_INFO, "Possible tunes:");
            for (i = 0; x264_tune_names[i]; i++)
                av_log(avctx, AV_LOG_INFO, " %s", x264_tune_names[i]);
            av_log(avctx, AV_LOG_INFO, "\n");
            return AVERROR(EINVAL);
        }

    if (avctx->level > 0)
        x4->params.i_level_idc = avctx->level;
	// log信息配置
    x4->params.pf_log               = X264_log;
    x4->params.p_log_private        = avctx;
    x4->params.i_log_level          = X264_LOG_DEBUG;
    x4->params.i_csp                = convert_pix_fmt(avctx->pix_fmt);
#if X264_BUILD >= 153
    x4->params.i_bitdepth           = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth;
#endif

    PARSE_X264_OPT("weightp", wpredp);
	// 码控参数配置
    if (avctx->bit_rate) {
        if (avctx->bit_rate / 1000 > INT_MAX || avctx->rc_max_rate / 1000 > INT_MAX) {
            av_log(avctx, AV_LOG_ERROR, "bit_rate and rc_max_rate > %d000 not supported by libx264\n", INT_MAX);
            return AVERROR(EINVAL);
        }
        x4->params.rc.i_bitrate   = avctx->bit_rate / 1000;
        x4->params.rc.i_rc_method = X264_RC_ABR;
    }
    x4->params.rc.i_vbv_buffer_size = avctx->rc_buffer_size / 1000;
    x4->params.rc.i_vbv_max_bitrate = avctx->rc_max_rate    / 1000;
    x4->params.rc.b_stat_write      = avctx->flags & AV_CODEC_FLAG_PASS1;
    if (avctx->flags & AV_CODEC_FLAG_PASS2) {
        x4->params.rc.b_stat_read = 1;
    } else {
        if (x4->crf >= 0) {
            x4->params.rc.i_rc_method   = X264_RC_CRF;
            x4->params.rc.f_rf_constant = x4->crf;
        } else if (x4->cqp >= 0) {
            x4->params.rc.i_rc_method   = X264_RC_CQP;
            x4->params.rc.i_qp_constant = x4->cqp;
        }

        if (x4->crf_max >= 0)
            x4->params.rc.f_rf_constant_max = x4->crf_max;
    }

    if (avctx->rc_buffer_size && avctx->rc_initial_buffer_occupancy > 0 &&
        (avctx->rc_initial_buffer_occupancy <= avctx->rc_buffer_size)) {
        x4->params.rc.f_vbv_buffer_init =
            (float)avctx->rc_initial_buffer_occupancy / avctx->rc_buffer_size;
    }

    PARSE_X264_OPT("level", level);

    if (avctx->i_quant_factor > 0)
        x4->params.rc.f_ip_factor         = 1 / fabs(avctx->i_quant_factor);
    if (avctx->b_quant_factor > 0)
        x4->params.rc.f_pb_factor         = avctx->b_quant_factor;

    if (x4->chroma_offset)
        x4->params.analyse.i_chroma_qp_offset = x4->chroma_offset;

    if (avctx->gop_size >= 0)
        x4->params.i_keyint_max         = avctx->gop_size;
    if (avctx->max_b_frames >= 0)
        x4->params.i_bframe             = avctx->max_b_frames;

    if (x4->scenechange_threshold >= 0)
        x4->params.i_scenecut_threshold = x4->scenechange_threshold;
	// 编码质量参数
    if (avctx->qmin >= 0)
        x4->params.rc.i_qp_min          = avctx->qmin;
    if (avctx->qmax >= 0)
        x4->params.rc.i_qp_max          = avctx->qmax;
    if (avctx->max_qdiff >= 0)
        x4->params.rc.i_qp_step         = avctx->max_qdiff;
    if (avctx->qblur >= 0)
        x4->params.rc.f_qblur           = avctx->qblur;     /* temporally blur quants */
    if (avctx->qcompress >= 0)
        x4->params.rc.f_qcompress       = avctx->qcompress; /* 0.0 => cbr, 1.0 => constant qp */
    if (avctx->refs >= 0)
        x4->params.i_frame_reference    = avctx->refs;
    else if (x4->params.i_level_idc > 0) {
        int i;
        int mbn = AV_CEIL_RSHIFT(avctx->width, 4) * AV_CEIL_RSHIFT(avctx->height, 4);
        int scale = X264_BUILD < 129 ? 384 : 1;

        for (i = 0; i<x264_levels[i].level_idc; i++)
            if (x264_levels[i].level_idc == x4->params.i_level_idc)
                x4->params.i_frame_reference = av_clip(x264_levels[i].dpb / mbn / scale, 1, x4->params.i_frame_reference);
    }
	// 运动搜索参数配置
    if (avctx->trellis >= 0)
        x4->params.analyse.i_trellis    = avctx->trellis;
    if (avctx->me_range >= 0)
        x4->params.analyse.i_me_range   = avctx->me_range;
    if (x4->noise_reduction >= 0)
        x4->params.analyse.i_noise_reduction = x4->noise_reduction;
    if (avctx->me_subpel_quality >= 0)
        x4->params.analyse.i_subpel_refine   = avctx->me_subpel_quality;
    if (avctx->keyint_min >= 0)
        x4->params.i_keyint_min = avctx->keyint_min;
    if (avctx->me_cmp >= 0)
        x4->params.analyse.b_chroma_me = avctx->me_cmp & FF_CMP_CHROMA;

    if (x4->aq_mode >= 0)
        x4->params.rc.i_aq_mode = x4->aq_mode;
    if (x4->aq_strength >= 0)
        x4->params.rc.f_aq_strength = x4->aq_strength;
    PARSE_X264_OPT("psy-rd", psy_rd);
    PARSE_X264_OPT("deblock", deblock);
    PARSE_X264_OPT("partitions", partitions);
    PARSE_X264_OPT("stats", stats);
    if (x4->psy >= 0)
        x4->params.analyse.b_psy  = x4->psy;
    if (x4->rc_lookahead >= 0)
        x4->params.rc.i_lookahead = x4->rc_lookahead;
    if (x4->weightp >= 0)
        x4->params.analyse.i_weighted_pred = x4->weightp;
    if (x4->weightb >= 0)
        x4->params.analyse.b_weighted_bipred = x4->weightb;
    if (x4->cplxblur >= 0)
        x4->params.rc.f_complexity_blur = x4->cplxblur;
	
    if (x4->ssim >= 0)
        x4->params.analyse.b_ssim = x4->ssim;
    if (x4->intra_refresh >= 0)
        x4->params.b_intra_refresh = x4->intra_refresh;
    if (x4->bluray_compat >= 0) {
        x4->params.b_bluray_compat = x4->bluray_compat;
        x4->params.b_vfr_input = 0;
    }
    if (x4->avcintra_class >= 0)
#if X264_BUILD >= 142
        x4->params.i_avcintra_class = x4->avcintra_class;
#else
        av_log(avctx, AV_LOG_ERROR,
               "x264 too old for AVC Intra, at least version 142 needed\n");
#endif

    if (x4->avcintra_class > 200) {
#if X264_BUILD < 164
        av_log(avctx, AV_LOG_ERROR,
                "x264 too old for AVC Intra 300/480, at least version 164 needed\n");
        return AVERROR(EINVAL);
#else
        /* AVC-Intra 300/480 only supported by Sony XAVC flavor */
        x4->params.i_avcintra_flavor = X264_AVCINTRA_FLAVOR_SONY;
#endif
    }
	// 帧结构
    if (x4->b_bias != INT_MIN)
        x4->params.i_bframe_bias              = x4->b_bias;
    if (x4->b_pyramid >= 0)
        x4->params.i_bframe_pyramid = x4->b_pyramid;
    if (x4->mixed_refs >= 0)
        x4->params.analyse.b_mixed_references = x4->mixed_refs;
    // dct变换
    if (x4->dct8x8 >= 0)
        x4->params.analyse.b_transform_8x8    = x4->dct8x8;
    if (x4->fast_pskip >= 0)
        x4->params.analyse.b_fast_pskip       = x4->fast_pskip;
    if (x4->aud >= 0)
        x4->params.b_aud                      = x4->aud;
    // 是否启用mbtree
    if (x4->mbtree >= 0)
        x4->params.rc.b_mb_tree               = x4->mbtree;
    if (x4->direct_pred >= 0)
        x4->params.analyse.i_direct_mv_pred   = x4->direct_pred;

    if (x4->slice_max_size >= 0)
        x4->params.i_slice_max_size =  x4->slice_max_size;

    if (x4->fastfirstpass)
        x264_param_apply_fastfirstpass(&x4->params);

    x4->profile = x4->profile_opt;
    /* Allow specifying the x264 profile through AVCodecContext. */
    // 检查profile
    if (!x4->profile)
        switch (avctx->profile) {
        case AV_PROFILE_H264_BASELINE:
            x4->profile = "baseline";
            break;
        case AV_PROFILE_H264_HIGH:
            x4->profile = "high";
            break;
        case AV_PROFILE_H264_HIGH_10:
            x4->profile = "high10";
            break;
        case AV_PROFILE_H264_HIGH_422:
            x4->profile = "high422";
            break;
        case AV_PROFILE_H264_HIGH_444:
            x4->profile = "high444";
            break;
        case AV_PROFILE_H264_MAIN:
            x4->profile = "main";
            break;
        default:
            break;
        }

    if (x4->nal_hrd >= 0)
        x4->params.i_nal_hrd = x4->nal_hrd;

    if (x4->motion_est >= 0)
        x4->params.analyse.i_me_method = x4->motion_est;

    if (x4->coder >= 0)
        x4->params.b_cabac = x4->coder;

    if (x4->b_frame_strategy >= 0)
        x4->params.i_bframe_adaptive = x4->b_frame_strategy;

    if (x4->profile)
        if (x264_param_apply_profile(&x4->params, x4->profile) < 0) {
            int i;
            av_log(avctx, AV_LOG_ERROR, "Error setting profile %s.\n", x4->profile);
            av_log(avctx, AV_LOG_INFO, "Possible profiles:");
            for (i = 0; x264_profile_names[i]; i++)
                av_log(avctx, AV_LOG_INFO, " %s", x264_profile_names[i]);
            av_log(avctx, AV_LOG_INFO, "\n");
            return AVERROR(EINVAL);
        }
	// 长宽和fps的设置
    x4->params.i_width          = avctx->width;
    x4->params.i_height         = avctx->height;
    av_reduce(&sw, &sh, avctx->sample_aspect_ratio.num, avctx->sample_aspect_ratio.den, 4096);
    x4->params.vui.i_sar_width  = sw;
    x4->params.vui.i_sar_height = sh;
    x4->params.i_timebase_den = avctx->time_base.den;
    x4->params.i_timebase_num = avctx->time_base.num;
    if (avctx->framerate.num > 0 && avctx->framerate.den > 0) {
        x4->params.i_fps_num = avctx->framerate.num;
        x4->params.i_fps_den = avctx->framerate.den;
    } else {
        x4->params.i_fps_num = avctx->time_base.den;
FF_DISABLE_DEPRECATION_WARNINGS
        x4->params.i_fps_den = avctx->time_base.num
#if FF_API_TICKS_PER_FRAME
            * avctx->ticks_per_frame
#endif
            ;
FF_ENABLE_DEPRECATION_WARNINGS
    }

    x4->params.analyse.b_psnr = avctx->flags & AV_CODEC_FLAG_PSNR;

    x4->params.i_threads      = avctx->thread_count;
    if (avctx->thread_type)
        x4->params.b_sliced_threads = avctx->thread_type == FF_THREAD_SLICE;

    x4->params.b_interlaced   = avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT;

    x4->params.b_open_gop     = !(avctx->flags & AV_CODEC_FLAG_CLOSED_GOP);

    x4->params.i_slice_count  = avctx->slices;

    if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED)
        x4->params.vui.b_fullrange = avctx->color_range == AVCOL_RANGE_JPEG;
    else if (avctx->pix_fmt == AV_PIX_FMT_YUVJ420P ||
             avctx->pix_fmt == AV_PIX_FMT_YUVJ422P ||
             avctx->pix_fmt == AV_PIX_FMT_YUVJ444P)
        x4->params.vui.b_fullrange = 1;

    if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED)
        x4->params.vui.i_colmatrix = avctx->colorspace;
    if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED)
        x4->params.vui.i_colorprim = avctx->color_primaries;
    if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED)
        x4->params.vui.i_transfer  = avctx->color_trc;
    if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED)
        x4->params.vui.i_chroma_loc = avctx->chroma_sample_location - 1;

    handle_side_data(avctx, &x4->params);

    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)
        x4->params.b_repeat_headers = 0;

    if (avctx->flags & AV_CODEC_FLAG_RECON_FRAME)
        x4->params.b_full_recon = 1;

    if(x4->x264opts){
        const char *p= x4->x264opts;
        while(p){
            char param[4096]={0}, val[4096]={0};
            if(sscanf(p, "%4095[^:=]=%4095[^:]", param, val) == 1){
                ret = parse_opts(avctx, param, "1");
                if (ret < 0)
                    return ret;
            } else {
                ret = parse_opts(avctx, param, val);
                if (ret < 0)
                    return ret;
            }
            p= strchr(p, ':');
            if (p) {
                ++p;
            }
        }
    }

#if X264_BUILD >= 142
    /* Separate headers not supported in AVC-Intra mode */
    if (x4->avcintra_class >= 0)
        x4->params.b_repeat_headers = 1;
#endif

    {
        AVDictionaryEntry *en = NULL;
        while (en = av_dict_get(x4->x264_params, "", en, AV_DICT_IGNORE_SUFFIX)) {
           if ((ret = x264_param_parse(&x4->params, en->key, en->value)) < 0) {
               av_log(avctx, AV_LOG_WARNING,
                      "Error parsing option '%s = %s'.\n",
                       en->key, en->value);
#if X264_BUILD >= 161
               if (ret == X264_PARAM_ALLOC_FAILED)
                   return AVERROR(ENOMEM);
#endif
           }
        }
    }

    x4->params.analyse.b_mb_info = x4->mb_info;

    // update AVCodecContext with x264 parameters
    avctx->has_b_frames = x4->params.i_bframe ?
        x4->params.i_bframe_pyramid ? 2 : 1 : 0;
    if (avctx->max_b_frames < 0)
        avctx->max_b_frames = 0;

    avctx->bit_rate = x4->params.rc.i_bitrate*1000LL;
	// 打开编码器
    x4->enc = x264_encoder_open(&x4->params);
    if (!x4->enc)
        return AVERROR_EXTERNAL;

    if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
        ret = set_extradata(avctx);
        if (ret < 0)
            return ret;
    }
	// cpb信息配置
    cpb_props = ff_encode_add_cpb_side_data(avctx);
    if (!cpb_props)
        return AVERROR(ENOMEM);
    cpb_props->buffer_size = x4->params.rc.i_vbv_buffer_size * 1000;
    cpb_props->max_bitrate = x4->params.rc.i_vbv_max_bitrate * 1000LL;
    cpb_props->avg_bitrate = x4->params.rc.i_bitrate         * 1000LL;

    // Overestimate the reordered opaque buffer size, in case a runtime
    // reconfigure would increase the delay (which it shouldn't).
    x4->nb_reordered_opaque = x264_encoder_maximum_delayed_frames(x4->enc) + 17;
    x4->reordered_opaque    = av_calloc(x4->nb_reordered_opaque,
                                        sizeof(*x4->reordered_opaque));
    if (!x4->reordered_opaque) {
        x4->nb_reordered_opaque = 0;
        return AVERROR(ENOMEM);
    }

    return 0;
}

1.3 释放编解码器(ff_codec_close)

函数的主要功能是释放编解码器,其核心是调用了close进行编解码器的关闭,对于libx264而言,调用的是X264_close()函数

av_cold void ff_codec_close(AVCodecContext *avctx)
{
    int i;

    if (!avctx)
        return;

    if (avcodec_is_open(avctx)) {
        AVCodecInternal *avci = avctx->internal;

        if (CONFIG_FRAME_THREAD_ENCODER &&
            avci->frame_thread_encoder && avctx->thread_count > 1) {
            ff_frame_thread_encoder_free(avctx);
        }
        if (HAVE_THREADS && avci->thread_ctx)
            ff_thread_free(avctx);
        if (avci->needs_close && ffcodec(avctx->codec)->close)
            ffcodec(avctx->codec)->close(avctx); // 关闭编解码器
        avci->byte_buffer_size = 0;
        av_freep(&avci->byte_buffer);
        av_frame_free(&avci->buffer_frame);
        av_packet_free(&avci->buffer_pkt);
        av_packet_free(&avci->last_pkt_props);

        av_packet_free(&avci->in_pkt);
        av_frame_free(&avci->in_frame);
        av_frame_free(&avci->recon_frame);

        ff_refstruct_unref(&avci->pool);

        ff_hwaccel_uninit(avctx);

        av_bsf_free(&avci->bsf);

#if FF_API_DROPCHANGED
        av_channel_layout_uninit(&avci->initial_ch_layout);
#endif

#if CONFIG_LCMS2
        ff_icc_context_uninit(&avci->icc);
#endif

        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->extradata_size = 0;
    } else if (av_codec_is_decoder(avctx->codec))
        av_freep(&avctx->subtitle_header);

    avctx->codec = NULL;
    avctx->active_thread_type = 0;
}

X264_close函数的定义位于libavcodec\libx264.c中,如下所示,先释放了sei信息和opaque信息,随后调用x264_param_cleanup()清理x264相关的参数,最后调用x264_encoder_close来关闭x264的encoder

static av_cold int X264_close(AVCodecContext *avctx)
{
    X264Context *x4 = avctx->priv_data;

    av_freep(&x4->sei);

    for (int i = 0; i < x4->nb_reordered_opaque; i++)
        opaque_uninit(&x4->reordered_opaque[i]);
    av_freep(&x4->reordered_opaque);
	
#if X264_BUILD >= 161
	// 清理x264相关参数
    x264_param_cleanup(&x4->params);
#endif

    if (x4->enc) {
    	// 关闭x264的encoder
        x264_encoder_close(x4->enc);
        x4->enc = NULL;
    }

    return 0;
}

CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/766499.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python数据分析-房价预测机器学习

一、研究背景 房地产市场作为经济活动的关键领域之一&#xff0c;对于经济的发展和社会的稳定起着至关重要的作用。在当今全球化和信息化的背景下&#xff0c;房地产市场的波动和房价的变化不仅受到国内因素的影响&#xff0c;还受到全球经济环境和国际政治形势等外部因素的影…

深入理解ThreadLocal原理

以下内容首发于我的个人网站&#xff0c;来这里看更舒适&#xff1a;https://riun.xyz/work/9898775 ThreadLocal是一种用于实现线程局部变量的机制&#xff0c;它允许每个线程有自己独立的变量&#xff0c;从而达到了线程数据隔离的目的。 基于JDK8 使用 通常在项目中是这样…

JS爬虫实战之Fastmoss

Fastmoss参数逆向 逆向前准备思路1- 确认接口2- 参数确认3- 重试校验参数逻辑4- 寻找逆向入口1- 方式一&#xff08;search搜索&#xff09;&#xff1a;2- 方式二&#xff08;堆栈搜索&#xff09;&#xff1a; 5- 获取加密算法1- fm-sign字段是有zn来的&#xff0c;我们查看z…

机器学习 C++ 的opencv实现SVM图像二分类的训练 (二)【附源码】

本节讲机器学习 C 的opencv实现SVM图像二分类的训练&#xff0c;下节讲测试&#xff1a; 数据集合data内容如下&#xff1a; 下载地址为&#xff1a;https://download.csdn.net/download/hgaohr1021/89506900 #include <stdio.h> #include <time.h> #include…

C语言课程回顾:六、C语言循环控制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 C语言循环控制 6 循环控制6.1 概述6.2 goto语句以及用goto语句构成循环6.3 while语句6.4 do-while语句6.5 for语句6.6 循环的嵌套6.7 几种循环的比较6.8 break和continue语句…

Windows系统安装NVM,实现Node.js多版本管理

目录 一、前言 二、NVM简介 三、准备工作 1、卸载Node 2、创建文件夹 四、下载NVM 五、安装NVM 六、使用NVM 1、NVM常用操作命令 2、查看NVM版本信息 3、查看Node.js版本列表&#xff1b; 4、下载指定版本Node.js 5、使用指定版本Node.js 6、查看已安装Node.js列…

Java知识点整理 18 — Lambda表达式

一. 简介 Lambda 表达式是函数式编程思想的体现&#xff0c;强调做什么&#xff0c;而不是以什么方式去做。 面向对象编程思想强调的是对象&#xff0c;必须通过对象的形式来做一些事情。比如多线程执行任务&#xff0c;需要创建对象&#xff0c;对象需要实现指定接口&#x…

Rust监控可观测性

可观测性 在监控章节的引言中&#xff0c;我们提到了老板、前端、后端眼中的监控是各不相同的&#xff0c;那么有没有办法将监控模型进行抽象、统一呢&#xff1f; 来简单分析一下&#xff1a; 业务指标实时展示&#xff0c;这是一个指标型的数据( metric )手机 APP 上传的数…

若依 ruoyi vue上传控件 el-upload上传文件 判断是否有文件 判断文件大小

console.info(this.$refs.upload.uploadFiles.length)//this.$refs.upload.uploadFiles.length 获取当前上传控件中已选择的文件大小//判断是否存在已上传文件 if(this.$refs.upload.uploadFiles.length 0){this.$modal.msgWarning("请上传文件");return; }

轻松配置,无需重复操作:PyCharm新建项目后,如何让当前新建项目使用既有虚拟环境

1、点击右上角的设置按钮 2、点击Settings 3、点击profect 4、点击python Interprter&#xff0c;这个是python解释器 5、点击 add interpreter&#xff0c;这个是增加python解释器 6、再点击add Local interpreter 7、选择第一个Virtualenv Environment,然后选择Existin…

交叉编译tslib库和上机测试

目录 一、tslib 介绍 二、tslib 框架分析 三、交叉编译、测试 tslib 1.安装工具链 tslib &#xff08;1&#xff09;设置交叉编译工具链 &#xff08;2&#xff09;进入tslib目录 &#xff08;3&#xff09;安装工具链 &#xff08;4&#xff09;确定工具链中头文件、库…

Linux源码阅读笔记09-进程NICE案例分析1

task_nice task_nice函数功能&#xff1a;获取某个进程的nice值&#xff0c;其中nice值为进程的优先级&#xff0c;与静态优先级有关&#xff08;nicestatic_prio-120&#xff09;。 nice的取值范围&#xff1a;-20 ~ 19 内核源码 根据内核的注释可以知道&#xff1a;task_n…

13-Django项目--文件上传

目录 前端展示 路由: 数据库字段: 函数视图: 前端展示 {% extends "index/index.html" %}{% block content %}<div class"container"><input type"button" id"btnAdd" value"上传荣耀" class"btn btn-succ…

鼠标点击器免费版?详细介绍鼠标连点器的如何使用

随着科技的发展&#xff0c;鼠标连点器逐渐成为了我们生活和工作中不可或缺的工具。它不仅能够帮助我们完成频繁且重复的点击任务&#xff0c;还能在很大程度上减少我们的手部疲劳&#xff0c;提高工作效率。本文将详细介绍鼠标连点器的使用方法&#xff0c;并推荐三款好用的免…

to_json 出现乱码的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

来咯,他来咯 看GitHub Codespaces 如何帮助缩短开发设置时间

在快节奏的软件开发世界中&#xff0c;效率和速度起着重要作用。对于开发人员来说&#xff0c;设置开发环境可能是一项耗时的任务。GitHub Codespaces 是一个基于云的环境&#xff0c;旨在通过提供对配置设置的访问来应对这一挑战。 本指南将帮助你开始使用 GitHub Codespaces …

Spring boot 更改启动LOGO

在resources目录下创建banner.txt文件&#xff0c;然后编辑对应的图案即可 注释工具 Spring Boot Version: ${spring-boot.version},-.___,---.__ /|\ __,---,___,- \ -.____,- | -.____,- // -., | ~\ /~ | …

【面试干货】值传递与引用传递:理解Java中的参数传递机制

【面试干货】值传递与引用传递&#xff1a;理解Java中的参数传递机制 1、值传递&#xff08;Call by Value&#xff09;2、引用传递&#xff08;Call by Reference&#xff09;3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 值传递…

【Python】已解决:ERROR: No matching distribution found for JPype1

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;ERROR: No matching distribution found for JPype1 一、分析问题背景 在安装Python的第三方库时&#xff0c;有时会遇到“ERROR: No matching distribution fo…