当前位置: 首页 > news >正文

【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的能力包括了experimentalif ((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);elseret = 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.创建失败则释放codecff_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和audioif (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) {// 编码格式为NONEav_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);// 输出支持的formatav_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_rangeif (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_WARNINGSif (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
#endifif (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_headerav_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_ICONViconv_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);
#elseav_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 datafor (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 typesif (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_DROPCHANGEDif (avctx->flags & AV_CODEC_FLAG_DROPCHANGED)av_log(avctx, AV_LOG_WARNING, "The dropchanged flag is deprecated.\n");
#endifreturn 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_ENCODERif (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 >= 153x4->params.i_bitdepth           = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].depth;
#endifPARSE_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 >= 142x4->params.i_avcintra_class = x4->avcintra_class;
#elseav_log(avctx, AV_LOG_ERROR,"x264 too old for AVC Intra, at least version 142 needed\n");
#endifif (x4->avcintra_class > 200) {
#if X264_BUILD < 164av_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;// 是否启用mbtreeif (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. */// 检查profileif (!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_WARNINGSx4->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 >= 161if (ret == X264_PARAM_ALLOC_FAILED)return AVERROR(ENOMEM);
#endif}}}x4->params.analyse.b_mb_info = x4->mb_info;// update AVCodecContext with x264 parametersavctx->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_DROPCHANGEDav_channel_layout_uninit(&avci->initial_ch_layout);
#endif#if CONFIG_LCMS2ff_icc_context_uninit(&avci->icc);
#endifav_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);
#endifif (x4->enc) {// 关闭x264的encoderx264_encoder_close(x4->enc);x4->enc = NULL;}return 0;
}

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

相关文章:

  • tls各个版本的安全性介绍
  • Appium启动APP时报错Security exception: Permission Denial
  • 高级Redis之Stream的用法示例
  • 同时在一台服务器上使用Java 8和Java 17的技术指南
  • Java基础(基于韩顺平老师的笔记)
  • Echarts中的热力图和漏斗图(在Vue中使用热力图和漏斗图)
  • 第三方支付平台如何完美契合跨境电商?
  • 抬头显示器HUD原理及特性
  • 列表元素合并之终结篇python实现
  • Node.js 入门
  • Unity热更方案HybridCLR+YooAsset,纯c#开发热更,保姆级教程,从零开始
  • 最快33天录用!一投就中的医学4区SCI,几乎不退稿~
  • 法国工程师IMT联盟 密码学及其应用 2023年期末考试补考题
  • 12个视觉艺术分类
  • c++ primer plus 第15章友,异常和其他
  • 「面试题」如何实现一个圣杯布局?
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 5、React组件事件详解
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  •  D - 粉碎叛乱F - 其他起义
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • swift基础之_对象 实例方法 对象方法。
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Vue组件定义
  • 阿里云应用高可用服务公测发布
  • 好的网址,关于.net 4.0 ,vs 2010
  • 前端
  • 区块链分支循环
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 少走弯路,给Java 1~5 年程序员的建议
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 【云吞铺子】性能抖动剖析(二)
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​学习一下,什么是预包装食品?​
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $jQuery 重写Alert样式方法
  • %@ page import=%的用法
  • (12)Linux 常见的三种进程状态
  • (9)目标检测_SSD的原理
  • (Java)【深基9.例1】选举学生会
  • (九)One-Wire总线-DS18B20
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .Net OpenCVSharp生成灰度图和二值图
  • .net 简单实现MD5
  • .net6 webapi log4net完整配置使用流程
  • .net6Api后台+uniapp导出Excel
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • .vue文件怎么使用_我在项目中是这样配置Vue的
  • .考试倒计时43天!来提分啦!
  • /proc/stat文件详解(翻译)
  • [2]十道算法题【Java实现】
  • [ActionScript][AS3]小小笔记
  • [AIGC] Redis基础命令集详细介绍