参考链接
- FFmpeg源代码简单分析:avcodec_open2()_雷霄骅的博客-CSDN博客
avcodec_open2()
- 该函数用于初始化一个音视频编解码器的AVCodecContext
- avcodec_open2()的声明位于libavcodec\avcodec.h,如下所示。
/*** Initialize the AVCodecContext to use the given AVCodec. Prior to using this* function the context has to be allocated with avcodec_alloc_context3().* 初始化 AVCodecContext 以使用给定的 AVCodec* 在使用此函数之前,必须使用 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.* 函数 avcodec_find_decoder_by_name()、avcodec_find_encoder_by_name()、avcodec_find_decoder() * avcodec_find_encoder() 为检索编解码器提供了一种简单的方法** @note Always call this function before using decoding routines (such as* @ref avcodec_receive_frame()).** @code* av_dict_set(&opts, "b", "2.5M", 0);* codec = avcodec_find_decoder(AV_CODEC_ID_H264);* if (!codec)* exit(1);** context = avcodec_alloc_context3(codec);** if (avcodec_open2(context, codec, opts) < 0)* exit(1);* @endcode** @param avctx The context to initialize.* @param codec The codec to open this context for. 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.* On return this object will be filled with options that were not found.** @return zero on success, a negative value on error* @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),* av_dict_set(), av_opt_find().*/
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
- 用中文简单转述一下avcodec_open2()各个参数的含义:
- avctx:需要初始化的AVCodecContext。
- codec:输入的AVCodec
- options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置。
函数调用关系图
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{int ret = 0;AVCodecInternal *avci;const FFCodec *codec2;//如果已经打开,直接返回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);avci = av_mallocz(sizeof(*avci));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;}avci->skip_samples_multiplier = 1;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;}//将输入的AVDictionary形式的选项设置到AVCodecContextif ((ret = av_opt_set_dict(avctx, options)) < 0)goto free_and_end;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 dimensionsif (!(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;}#if FF_API_OLD_CHANNEL_LAYOUT
FF_DISABLE_DEPRECATION_WARNINGS/* compat wrapper for old-style callers */if (avctx->channel_layout && !avctx->channels)avctx->channels = av_popcount64(avctx->channel_layout);if ((avctx->channels > 0 && avctx->ch_layout.nb_channels != avctx->channels) ||(avctx->channel_layout && (avctx->ch_layout.order != AV_CHANNEL_ORDER_NATIVE ||avctx->ch_layout.u.mask != avctx->channel_layout))) {if (avctx->channel_layout) {av_channel_layout_from_mask(&avctx->ch_layout, avctx->channel_layout);} else {avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;avctx->ch_layout.nb_channels = avctx->channels;}}
FF_ENABLE_DEPRECATION_WARNINGS
#endifif (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_number = 0;avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id);//检查编码器是否出于“实验”阶段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;}//检查输入参数是否符合【编码器】要求if (av_codec_is_encoder(avctx->codec))ret = ff_encode_preinit(avctx);elseret = ff_decode_preinit(avctx);if (ret < 0)goto free_and_end;if (CONFIG_FRAME_THREAD_ENCODER && av_codec_is_encoder(avctx->codec)) {ret = ff_frame_thread_encoder_init(avctx);if (ret < 0)goto free_and_end;}if (HAVE_THREADS&& !(avci->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) {/* 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);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);#if FF_API_OLD_CHANNEL_LAYOUT
FF_DISABLE_DEPRECATION_WARNINGS/* update the deprecated fields for old-style callers */avctx->channels = avctx->ch_layout.nb_channels;avctx->channel_layout = avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ?avctx->ch_layout.u.mask : 0;/* validate channel layout from the decoder */if (avctx->channel_layout) {int channels = av_get_channel_layout_nb_channels(avctx->channel_layout);if (!avctx->channels)avctx->channels = channels;else if (channels != avctx->channels) {char buf[512];av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);av_log(avctx, AV_LOG_WARNING,"Channel layout '%s' with %d channels does not match specified number of channels %d: ""ignoring specified channel layout\n",buf, channels, avctx->channels);avctx->channel_layout = 0;}}if (avctx->channels && avctx->channels < 0 ||avctx->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;}
FF_ENABLE_DEPRECATION_WARNINGS
#endif#if FF_API_AVCTX_TIMEBASEif (avctx->framerate.num > 0 && avctx->framerate.den > 0)avctx->time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1}));
#endif}if (codec->priv_class)av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class);end:return ret;
free_and_end:avcodec_close(avctx);goto end;
}
- avcodec_open2()的源代码量是非常长的,但是它的调用关系非常简单——它只调用了一个关键的函数,即AVCodec的init(),后文将会对这个函数进行分析。
- 我们可以简单梳理一下avcodec_open2()所做的工作,如下所列:
- (1)为各种结构体分配内存(通过各种av_malloc()实现)。
- (2)将输入的AVDictionary形式的选项设置到AVCodecContext。
- (3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。
- (4)如果是编码器,检查输入参数是否符合编码器的要求
- (5)调用AVCodec的init()初始化具体的解码器。
AVCodec->init()
- avcodec_open2()中最关键的一步就是调用AVCodec的init()方法初始化具体的编码器。
- AVCodec的init()是一个函数指针,指向具体编解码器中的初始化函数。
- 这里我们以libx264为例,看一下它对应的AVCodec的定义。
- libx264对应的AVCodec的定义位于libavcodec\libx264.c,如下所示。
#if X264_BUILD >= 153
const
#endif
FFCodec ff_libx264_encoder = {.p.name = "libx264",.p.long_name = NULL_IF_CONFIG_SMALL("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,.p.priv_class = &x264_class,.p.wrapper_name = "libx264",.priv_data_size = sizeof(X264Context),.init = X264_init,FF_CODEC_ENCODE_CB(X264_frame),.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_INIT_THREADSAFE
#endif,
};
#endif
- 可以看出在ff_libx264_encoder中init()指向X264_init()。X264_init()的定义同样位于libavcodec\libx264.c,如下所示。
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
#endifx264_param_default(&x4->params);x4->params.b_deblocking_filter = avctx->flags & AV_CODEC_FLAG_LOOP_FILTER;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;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;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;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);/* Allow specifying the x264 profile through AVCodecContext. */if (!x4->profile)switch (avctx->profile) {case FF_PROFILE_H264_BASELINE:x4->profile = av_strdup("baseline");break;case FF_PROFILE_H264_HIGH:x4->profile = av_strdup("high");break;case FF_PROFILE_H264_HIGH_10:x4->profile = av_strdup("high10");break;case FF_PROFILE_H264_HIGH_422:x4->profile = av_strdup("high422");break;case FF_PROFILE_H264_HIGH_444:x4->profile = av_strdup("high444");break;case FF_PROFILE_H264_MAIN:x4->profile = av_strdup("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);}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;x4->params.i_fps_den = avctx->time_base.num * avctx->ticks_per_frame;}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;if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)x4->params.b_repeat_headers = 0;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, ':');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}}}// 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) {x264_nal_t *nal;uint8_t *p;int nnal, s, i;s = x264_encoder_headers(x4->enc, &nal, &nnal);avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE);if (!p)return AVERROR(ENOMEM);for (i = 0; i < nnal; i++) {/* Don't put the SEI in extradata. */if (nal[i].i_type == NAL_SEI) {av_log(avctx, AV_LOG_INFO, "%s\n", nal[i].p_payload+25);x4->sei_size = nal[i].i_payload;x4->sei = av_malloc(x4->sei_size);if (!x4->sei)return AVERROR(ENOMEM);memcpy(x4->sei, nal[i].p_payload, nal[i].i_payload);continue;}memcpy(p, nal[i].p_payload, nal[i].i_payload);p += nal[i].i_payload;}avctx->extradata_size = p - avctx->extradata;}cpb_props = ff_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_malloc_array(x4->nb_reordered_opaque,sizeof(*x4->reordered_opaque));if (!x4->reordered_opaque)return AVERROR(ENOMEM);return 0;
}
- X264_init()的代码的两项工作:
- (1)设置X264Context的参数。X264Context主要完成了libx264和FFmpeg对接的功能。可以看出代码主要在设置一个params结构体变量,该变量的类型即是x264中存储参数的结构体x264_param_t。
- (2)调用libx264的API进行编码器的初始化工作。例如调用x264_param_default()设置默认参数,调用x264_param_apply_profile()设置profile,调用x264_encoder_open()打开编码器等等。
- 最后附上X264Context的定义,位于libavcodec\libx264.c,如下所示。
typedef struct X264Context {AVClass *class;x264_param_t params;x264_t *enc;x264_picture_t pic;uint8_t *sei;int sei_size;char *preset;char *tune;char *profile;char *level;int fastfirstpass;char *wpredp;char *x264opts;float crf;float crf_max;int cqp;int aq_mode;float aq_strength;char *psy_rd;int psy;int rc_lookahead;int weightp;int weightb;int ssim;int intra_refresh;int bluray_compat;int b_bias;int b_pyramid;int mixed_refs;int dct8x8;int fast_pskip;int aud;int mbtree;char *deblock;float cplxblur;char *partitions;int direct_pred;int slice_max_size;char *stats;int nal_hrd;int avcintra_class;int motion_est;int forced_idr;int coder;int a53_cc;int b_frame_strategy;int chroma_offset;int scenechange_threshold;int noise_reduction;int udu_sei;AVDictionary *x264_params;int nb_reordered_opaque, next_reordered_opaque;X264Opaque *reordered_opaque;/*** If the encoder does not support ROI then warn the first time we* encounter a frame with ROI side data.*/int roi_warned;
} X264Context;