ijkplayer框架簡析 -- avcodec_open2
avcodec_open2()
主要作用是初始化一個視音訊編解碼器的 AVCodecContext
avcodec_open2
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options) { int ret = 0; AVDictionary *tmp = NULL; const AVPixFmtDescriptor *pixdesc; 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; if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE) return AVERROR(EINVAL); if (options) av_dict_copy(&tmp, *options, 0); ret = ff_lock_avcodec(avctx, codec); if (ret < 0) return ret; avctx->internal = av_mallocz(sizeof(AVCodecInternal)); if (!avctx->internal) { ret = AVERROR(ENOMEM); goto end; } avctx->internal->pool = av_mallocz(sizeof(*avctx->internal->pool)); if (!avctx->internal->pool) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->to_free = av_frame_alloc(); if (!avctx->internal->to_free) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->compat_decode_frame = av_frame_alloc(); if (!avctx->internal->compat_decode_frame) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->buffer_frame = av_frame_alloc(); if (!avctx->internal->buffer_frame) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->buffer_pkt = av_packet_alloc(); if (!avctx->internal->buffer_pkt) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->ds.in_pkt = av_packet_alloc(); if (!avctx->internal->ds.in_pkt) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->last_pkt_props = av_packet_alloc(); if (!avctx->internal->last_pkt_props) { ret = AVERROR(ENOMEM); goto free_and_end; } avctx->internal->skip_samples_multiplier = 1; if (codec->priv_data_size > 0) { if (!avctx->priv_data) { avctx->priv_data = av_mallocz(codec->priv_data_size); if (!avctx->priv_data) { ret = AVERROR(ENOMEM); goto end; } if (codec->priv_class) { *(const AVClass **)avctx->priv_data = codec->priv_class; av_opt_set_defaults(avctx->priv_data); } } if (codec->priv_class && (ret = av_opt_set_dict(avctx->priv_data, &tmp)) < 0) goto free_and_end; } else { avctx->priv_data = NULL; } // 將輸入的 AVDictionary 形式的選項設定到 AVCodecContext if ((ret = av_opt_set_dict(avctx, &tmp)) < 0) goto free_and_end; if (avctx->codec_whitelist && av_match_list(codec->name, avctx->codec_whitelist, ',') <= 0) { 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 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 the decoder init function was already called previously, * free the already allocated subtitle_header before overwriting it */ if (av_codec_is_decoder(codec)) av_freep(&avctx->subtitle_header); if (avctx->channels > FF_SANE_NB_CHANNELS) { ret = AVERROR(EINVAL); goto free_and_end; } avctx->codec = codec; if ((avctx->codec_type == AVMEDIA_TYPE_UNKNOWN || avctx->codec_type == codec->type) && avctx->codec_id == AV_CODEC_ID_NONE) { avctx->codec_type = codec->type; avctx->codec_id= codec->id; } if (avctx->codec_id != codec->id || (avctx->codec_type != codec->type && avctx->codec_type != AVMEDIA_TYPE_ATTACHMENT)) { av_log(avctx, AV_LOG_ERROR, "Codec type or id mismatches\n"); 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"; 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 (!HAVE_THREADS) av_log(avctx, AV_LOG_WARNING, "Warning: not compiled with thread support, using thread emulation\n"); if (CONFIG_FRAME_THREAD_ENCODER && av_codec_is_encoder(avctx->codec)) { ff_unlock_avcodec(codec); //we will instantiate a few encoders thus kick the counter to prevent false detection of a problem ret = ff_frame_thread_encoder_init(avctx, options ? *options : NULL); ff_lock_avcodec(avctx, codec); if (ret < 0) goto free_and_end; } if (HAVE_THREADS && !(avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))) { ret = ff_thread_init(avctx); if (ret < 0) { goto free_and_end; } } if (!HAVE_THREADS && !(codec->capabilities & AV_CODEC_CAP_AUTO_THREADS)) avctx->thread_count = 1; 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; } #if FF_API_VISMV if (avctx->debug_mv) av_log(avctx, AV_LOG_WARNING, "The 'vismv' option is deprecated, " "see the codecview filter instead.\n"); #endif // 檢查輸入引數是否符合【編碼器】要求 if (av_codec_is_encoder(avctx->codec)) { int i; #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS avctx->coded_frame = av_frame_alloc(); if (!avctx->coded_frame) { ret = AVERROR(ENOMEM); goto free_and_end; } FF_ENABLE_DEPRECATION_WARNINGS #endif if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) { av_log(avctx, AV_LOG_ERROR, "The encoder timebase is not set.\n"); ret = AVERROR(EINVAL); goto free_and_end; } // 如果包含取樣率引數(表明是音訊),檢查取樣率是否符合要求 if (avctx->codec->sample_fmts) { // 遍歷編碼器支援的所有采樣率 for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) { // 如果設定的取樣率 == 編碼器支援的取樣率,跳出迴圈 if (avctx->sample_fmt == avctx->codec->sample_fmts[i]) break; if (avctx->channels == 1 && av_get_planar_sample_fmt(avctx->sample_fmt) == av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) { avctx->sample_fmt = avctx->codec->sample_fmts[i]; break; } } // 取樣率取值是否正確 if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) { char buf[128]; snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt); av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported\n", (char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf)); ret = AVERROR(EINVAL); goto free_and_end; } } // 畫素格式 if (avctx->codec->pix_fmts) { for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) if (avctx->pix_fmt == avctx->codec->pix_fmts[i]) break; if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE && !((avctx->codec_id == AV_CODEC_ID_MJPEG || avctx->codec_id == AV_CODEC_ID_LJPEG) && avctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL)) { char buf[128]; snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt); av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported\n", (char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf)); ret = AVERROR(EINVAL); goto free_and_end; } if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) avctx->color_range = AVCOL_RANGE_JPEG; } // 取樣率 if (avctx->codec->supported_samplerates) { for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++) if (avctx->sample_rate == avctx->codec->supported_samplerates[i]) break; if (avctx->codec->supported_samplerates[i] == 0) { av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", avctx->sample_rate); ret = AVERROR(EINVAL); goto free_and_end; } } if (avctx->sample_rate < 0) { av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", avctx->sample_rate); ret = AVERROR(EINVAL); goto free_and_end; } // 聲道佈局 if (avctx->codec->channel_layouts) { if (!avctx->channel_layout) { av_log(avctx, AV_LOG_WARNING, "Channel layout not specified\n"); } else { for (i = 0; avctx->codec->channel_layouts[i] != 0; i++) if (avctx->channel_layout == avctx->codec->channel_layouts[i]) break; if (avctx->codec->channel_layouts[i] == 0) { char buf[512]; av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout); av_log(avctx, AV_LOG_ERROR, "Specified channel layout '%s' is not supported\n", buf); ret = AVERROR(EINVAL); goto free_and_end; } } } // 聲道 if (avctx->channel_layout && avctx->channels) { int channels = av_get_channel_layout_nb_channels(avctx->channel_layout); if (channels != avctx->channels) { char buf[512]; av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout); av_log(avctx, AV_LOG_ERROR, "Channel layout '%s' with %d channels does not match number of specified channels %d\n", buf, channels, avctx->channels); ret = AVERROR(EINVAL); goto free_and_end; } } else if (avctx->channel_layout) { avctx->channels = av_get_channel_layout_nb_channels(avctx->channel_layout); } if (avctx->channels < 0) { av_log(avctx, AV_LOG_ERROR, "Specified number of channels %d is not supported\n", avctx->channels); ret = AVERROR(EINVAL); goto free_and_end; } if(avctx->codec_type == AVMEDIA_TYPE_VIDEO) { pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt); 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"); ret = AVERROR(EINVAL); goto free_and_end; } } // 位元速率 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; 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); goto free_and_end; } 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"); ret = AVERROR(EINVAL); goto free_and_end; } 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)); ret = AVERROR(EINVAL); goto free_and_end; } avctx->sw_pix_fmt = frames_ctx->sw_format; } } avctx->pts_correction_num_faulty_pts = avctx->pts_correction_num_faulty_dts = 0; avctx->pts_correction_last_pts = avctx->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->codec->init && (!(avctx->active_thread_type&FF_THREAD_FRAME) || avctx->internal->frame_thread_encoder)) { ret = avctx->codec->init(avctx); if (ret < 0) { goto free_and_end; } } ret=0; #if FF_API_AUDIOENC_DELAY if (av_codec_is_encoder(avctx->codec)) avctx->delay = avctx->initial_padding; #endif // 解碼器的引數大部分都是由系統自動設定而不是由使用者設定,因而不怎麼需要檢查 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->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->sub_charenc) { if (avctx->codec_type != AVMEDIA_TYPE_SUBTITLE) { av_log(avctx, AV_LOG_ERROR, "Character encoding is only " "supported with subtitles codecs\n"); ret = AVERROR(EINVAL); goto free_and_end; } 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); goto free_and_end; } 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"); ret = AVERROR(ENOSYS); goto free_and_end; #endif } } } #if FF_API_AVCTX_TIMEBASE if (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_data_size > 0 && avctx->priv_data && codec->priv_class) { av_assert0(*(const AVClass **)avctx->priv_data == codec->priv_class); } end: ff_unlock_avcodec(codec); if (options) { av_dict_free(options); *options = tmp; } return ret; free_and_end: if (avctx->codec && (avctx->codec->caps_internal & FF_CODEC_CAP_INIT_CLEANUP)) avctx->codec->close(avctx); if (codec->priv_class && codec->priv_data_size) av_opt_free(avctx->priv_data); av_opt_free(avctx); #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS av_frame_free(&avctx->coded_frame); FF_ENABLE_DEPRECATION_WARNINGS #endif av_dict_free(&tmp); av_freep(&avctx->priv_data); if (avctx->internal) { av_frame_free(&avctx->internal->to_free); av_frame_free(&avctx->internal->compat_decode_frame); av_frame_free(&avctx->internal->buffer_frame); av_packet_free(&avctx->internal->buffer_pkt); av_packet_free(&avctx->internal->last_pkt_props); av_packet_free(&avctx->internal->ds.in_pkt); av_freep(&avctx->internal->pool); } av_freep(&avctx->internal); avctx->codec = NULL; goto end; }
其中最主要是呼叫了 AVCodec 的 init() 初始化,其他還做的有:
av_malloc()
av_codec_is_encoder
當滿足了av_codec_is_encoder()
滿足的情況下,進行了引數是否滿足編碼器規範要求的檢查,如:
if (avctx->codec->pix_fmts) { for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) if (avctx->pix_fmt == avctx->codec->pix_fmts[i]) break; if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE && !((avctx->codec_id == AV_CODEC_ID_MJPEG || avctx->codec_id == AV_CODEC_ID_LJPEG) && avctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL)) { char buf[128]; snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt); av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported\n", (char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf)); ret = AVERROR(EINVAL); goto free_and_end; } if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) avctx->color_range = AVCOL_RANGE_JPEG; }
將 AVCodecContext 中的 pix_fmt 與編碼器 AVCodec 中的 pix_fmts 陣列中的元素進行對比,其中 AVCodec 中的 pix_fmts 陣列儲存了該種編碼器支援的畫素格式,並且規定以 AV_PIX_FMT_NONE(AV_PIX_FMT_NONE 取值為 - 1)為結尾
static const enum AVPixelFormat pix_fmts_8bit[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NV12, AV_PIX_FMT_NV16, AV_PIX_FMT_NONE };
以 libx264 的 pix_fmts 陣列為例,從 pix_fmts_8bit 的定義可以看出 libx264 主要支援的是以 YUV 為主的畫素格式
avctx->codec->init
AVCodec ff_libx264_encoder = { .name= "libx264", .long_name= NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), .type= AVMEDIA_TYPE_VIDEO, .id= AV_CODEC_ID_H264, .priv_data_size= sizeof(X264Context), .init= X264_init, .encode2= X264_frame, .close= X264_close, .capabilities= AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, .priv_class= &x264_class, .defaults= x264_defaults, .init_static_data = X264_init_static, .caps_internal= FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, };
以 libx264 為例,init
方法對應的是X264_init
static av_cold int X264_init(AVCodecContext *avctx) { X264Context *x4 = avctx->priv_data; AVCPBProperties *cpb_props; int sw,sh; 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 x264_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); PARSE_X264_OPT("weightp", wpredp); if (avctx->bit_rate) { 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 FF_API_PRIVATE_OPT FF_DISABLE_DEPRECATION_WARNINGS if (avctx->chromaoffset >= 0) x4->chroma_offset = avctx->chromaoffset; FF_ENABLE_DEPRECATION_WARNINGS #endif if (x4->chroma_offset >= 0) 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 FF_API_PRIVATE_OPT FF_DISABLE_DEPRECATION_WARNINGS if (avctx->scenechange_threshold >= 0) x4->scenechange_threshold = avctx->scenechange_threshold; FF_ENABLE_DEPRECATION_WARNINGS #endif 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->level) { int i; int mbn = AV_CEIL_RSHIFT(avctx->width, 4) * AV_CEIL_RSHIFT(avctx->height, 4); int level_id = -1; char *tail; int scale = X264_BUILD < 129 ? 384 : 1; if (!strcmp(x4->level, "1b")) { level_id = 9; } else if (strlen(x4->level) <= 3){ level_id = av_strtod(x4->level, &tail) * 10 + 0.5; if (*tail) level_id = -1; } if (level_id <= 0) av_log(avctx, AV_LOG_WARNING, "Failed to parse level\n"); for (i = 0; i<x264_levels[i].level_idc; i++) if (x264_levels[i].level_idc == level_id) 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 FF_API_PRIVATE_OPT FF_DISABLE_DEPRECATION_WARNINGS if (avctx->noise_reduction >= 0) x4->noise_reduction = avctx->noise_reduction; FF_ENABLE_DEPRECATION_WARNINGS #endif 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 FF_API_PRIVATE_OPT FF_DISABLE_DEPRECATION_WARNINGS if (avctx->b_frame_strategy >= 0) x4->b_frame_strategy = avctx->b_frame_strategy; FF_ENABLE_DEPRECATION_WARNINGS #endif if (avctx->keyint_min >= 0) x4->params.i_keyint_min = avctx->keyint_min; #if FF_API_CODER_TYPE FF_DISABLE_DEPRECATION_WARNINGS if (avctx->coder_type >= 0) x4->coder = avctx->coder_type == FF_CODER_TYPE_AC; FF_ENABLE_DEPRECATION_WARNINGS #endif 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->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 FF_API_MOTION_EST FF_DISABLE_DEPRECATION_WARNINGS } else { if (avctx->me_method == ME_EPZS) x4->params.analyse.i_me_method = X264_ME_DIA; else if (avctx->me_method == ME_HEX) x4->params.analyse.i_me_method = X264_ME_HEX; else if (avctx->me_method == ME_UMH) x4->params.analyse.i_me_method = X264_ME_UMH; else if (avctx->me_method == ME_FULL) x4->params.analyse.i_me_method = X264_ME_ESA; else if (avctx->me_method == ME_TESA) x4->params.analyse.i_me_method = X264_ME_TESA; FF_ENABLE_DEPRECATION_WARNINGS #endif } 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; 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; x4->params.vui.b_fullrange = avctx->pix_fmt == AV_PIX_FMT_YUVJ420P || avctx->pix_fmt == AV_PIX_FMT_YUVJ422P || avctx->pix_fmt == AV_PIX_FMT_YUVJ444P || avctx->color_range == AVCOL_RANGE_JPEG; 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->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){ OPT_STR(param, "1"); }else OPT_STR(param, val); p= strchr(p, ':'); p+=!!p; } } if (x4->x264_params) { AVDictionary *dict= NULL; AVDictionaryEntry *en = NULL; if (!av_dict_parse_string(&dict, x4->x264_params, "=", ":", 0)) { while ((en = av_dict_get(dict, "", en, AV_DICT_IGNORE_SUFFIX))) { if (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); } av_dict_free(&dict); } } // 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*1000; 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 * 1000; cpb_props->avg_bitrate = x4->params.rc.i_bitrate* 1000; return 0; }
大概的工作
- 設定 X264Context 的引數。X264Context 主要完成了 libx264 和 FFmpeg 對接的功能。可以看出程式碼主要在設定一個 params 結構體變數,該變數的型別即是 x264 中儲存引數的結構體 x264_param_t。
- 呼叫 libx264 的 API 進行編碼器的初始化工作。例如呼叫 x264_param_default() 設定預設引數,呼叫 x264_param_apply_profile() 設定 profile,呼叫 x264_encoder_open() 開啟編碼器等等。