【FFmpeg】avcodec_open2函数

news2024/11/16 19:58:26

目录

  • 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue H5页面video 视频流自动播放, 解决ios不能自动播放问题

视频组件 <videostyle"width: 100%; height: 100%;object-fit: fill"class"player"refplayer_big_boxcontrolspreloadautoplay //自动播放muted //是否静音playsinline"true"x5-playsinline""webkit-playsinline"tru…

麒麟服务器操作系统漏洞补丁包怎么快速下载

第一种方案:【建议方案】 1、将漏洞公告里的“受影响的软件包”全部复制出来到文本文件中 2、在对应版本的服务器系统中修改好repo文件,如果在x86系统中下载aarch64的补丁包,可以将repo中的$basearch替换为aarch64. [root@localhost ~]# vim package.txt #将第一步的软件…

澳大利亚新闻.科技.汽车.旅行.商业类单发媒体

每日简报Daily Bulletin 澳大利亚西部时间ModernAustralian.com 澳大利亚垂直新闻.科技.汽车.旅行.商业类媒体&#xff0c;ModernAustralian.com是澳大利亚西部地区的一家权威媒体平台&#xff0c;提供全面的新闻报道、科技资讯、汽车信息、旅行指南、商业动态等内容。每日简报…

横截面数据回归

横截面数据回归 一些笔记 观测值一定要比参数值多 p值<0.05,拒绝H0. 参数显著&#xff0c;不能说明模型对 AIC与BIC准则&#xff0c;越小越好的指标值AIC 回归分析一定要进行残差的正态性检验。所有的残差都大于0&#xff0c;小于0&#xff0c;都不正常。残差正常应该是分…

p2p、分布式,区块链笔记:试用ZeroTier组网

ZeroTier 是一种用于创建和管理虚拟局域网&#xff08;Virtual Local Area Network&#xff0c;VLAN&#xff09;的软件定义网络&#xff08;SDN&#xff09;解决方案。它可以通过互联网将多个设备安全地连接在一起&#xff0c;就像它们在同一个本地网络上一样。主要开发语言为…

rust + mingw安装教程

0. 说明 windows上安装rust时&#xff0c;需要在电脑上安装C/C构建工具。推荐的的两种工具链可以选择&#xff1a; visual studio build toolsmingw 官方推荐使用visual studio&#xff0c;若你的电脑上已经安装了visual studio&#xff0c;则无需再安装&#xff0c;直接安装…

GPT Prompt冠军调教技巧CO-STAR

新加坡政府科技局 (GovTech) 举办的首届 GPT-4 提示工程大赛冠军Sheila Teo分享了她的一些提示撰写技巧及案例分析。 使用 CO-STAR 框架撰写Prompt CO-STAR是结构化的Prompt模版六大要素的首字母缩写&#xff0c;即&#xff1a; © Context 上下文&#xff1a;为任务提供背…

vue3进阶,渲染函数使用

目录 渲染函数使用场景 h() 渲染函数 渲染函数基础写法 渲染函数的组件传参&#xff0c;事件传递 渲染函数的插槽使用 结语 渲染函数使用场景 在写这篇文章之前&#xff0c;我会先简单说一下渲染函数&#xff0c;并且我会在第一个渲染函数的介绍中&#xff0c;标名渲染函数…

算法力扣刷题记录 二十八【225. 用队列实现栈】

前言 栈和队列篇。 记录 二十八【225. 用队列实现栈】 一、题目阅读 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void p…

单片机关键任务优先级的实现学习

与总体产品联调时&#xff0c;需要各个单机系统严格按照总体要求&#xff0c;进行数据输出&#xff0c;时间的偏差将出现系统异常&#xff0c;控制失败等不稳定情况产生&#xff0c;甚至影响到产品安全。 因此必须确保某些关键任务的优先执行。单片机任务优先级一般有两种方式…

My sql 安装,环境搭建

以下以MySQL 8.0.36为例。 一、下载软件 1.下载地址官网&#xff1a;https://www.mysql.com 2. 打开官网&#xff0c;点击DOWNLOADS 然后&#xff0c;点击 MySQL Community(GPL) Downloads 3. 点击 MySQL Installer for Windows 4.点击Archives选择合适版本 5.选择后下载…

【国产开源可视化引擎Meta2d.js】锚点

国产开源 乐吾乐潜心研发&#xff0c;自主可控&#xff0c;持续迭代优化 Github&#xff1a;GitHub - le5le-com/meta2d.js: The meta2d.js is real-time data exchange and interactive web 2D engine. Developers are able to build Web SCADA, IoT, Digital twins and so …

神经网络入门:从零到训练

想要认识神经网络&#xff0c;个人认为还是需要先从回归开始理解 线性回归 回归&#xff08;regression&#xff09;是能为一个或多个自变量与因变量之间关系建模的一类方法。 在自然科学和社会科学领域&#xff0c;回归经常用来表示输入和输出之间的关系。 在机器学习领域中…

【Python机器学习】算法链与管道——构建管道

目录 1、首先&#xff0c;我们构建一个由步骤列表组成的管道对象。 2、向任何其他scikit-learn估计器一样来拟合这个管道 3、调用pipe.score 我们来看下如何使用Pipeline类来表示在使用MinMaxScaler缩放数据后&#xff0c;再训练一个SVM的工作流程&#xff08;暂时不用网格搜…

你知道是怎么运作的吗?神经网络内部原理解析

你知道神经网络是怎么运作的吗&#xff1f;神经网络内部原理解析 “神经网络就是一个具有输入和输出的黑盒” 神经网络模型就是模仿人类大脑神经元传递的过程&#xff0c;从使用者的角度来说&#xff0c;神经网络就是一个具有输入和输出的黑盒模型。 简化模型如下图&#xf…

python 比webdriver更好用的ChromiumPage

优点&#xff08;目前发现的&#xff09;&#xff1a; 不用配合selenium不用下载对应浏览器的webdriver&#xff0c;不用对应浏览器版本不用设置webdriver路径之类的设置目前没看到有出现像webdriver类似的浏览器被控制的提示&#xff0c;使用过程中好像也没被检测出来。每次不…

JAVA实现二分查找,斐波那契数列,深度优先搜索详情教程【包含代码】

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

《企业实战分享 · 内存溢出分析》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;如需交流&#xff…

计算机系统中---信息就是位+上下文

hello.c #include <stdio.h>int main(){printf("hello,world\n");return 0; }hello程序的生命周期是从一个源程序(或者说源文件)开始的&#xff0c;即程序员通过编辑器创建并保存的文本文件&#xff0c;文件名是he11o.c。源程序实际上就是一个由值0和1组成的位…

推荐三款常用接口测试工具!

接口测试是软件开发中至关重要的一环&#xff0c;通过对应用程序接口进行测试&#xff0c;可以验证其功能、性能和稳定性。随着互联网和移动应用的快速发展&#xff0c;接口测试变得越来越重要。为了提高测试效率和质量&#xff0c;开发人员和测试人员需要使用专业的接口测试工…