背景
ffmpeg 通过 avcodec_alloc_context3 解析编码器,本文主要来解析一下,这个函数主要做了什么。
具体代码分析
主要是创建了 AVCodecContext ,并给结构体参数赋予初值。
初值设置主要分成两块,1. 所有编码器都相同的部分;2.每个编码器独有的参数设置。
接下来主要分析后者是如何设置的。
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)
{AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext));if (!avctx)return NULL;if (init_context_defaults(avctx, codec) < 0) {av_free(avctx);return NULL;}return avctx;
}static int init_context_defaults(AVCodecContext *s, const AVCodec *codec)
{const FFCodec *const codec2 = ffcodec(codec);int flags=0;memset(s, 0, sizeof(AVCodecContext));s->av_class = &av_codec_context_class;s->codec_type = codec ? codec->type : AVMEDIA_TYPE_UNKNOWN;if (codec) {s->codec = codec;s->codec_id = codec->id;}if(s->codec_type == AVMEDIA_TYPE_AUDIO)flags= AV_OPT_FLAG_AUDIO_PARAM;else if(s->codec_type == AVMEDIA_TYPE_VIDEO)flags= AV_OPT_FLAG_VIDEO_PARAM;else if(s->codec_type == AVMEDIA_TYPE_SUBTITLE)flags= AV_OPT_FLAG_SUBTITLE_PARAM;av_opt_set_defaults2(s, flags, flags);av_channel_layout_uninit(&s->ch_layout);s->time_base = (AVRational){0,1};s->framerate = (AVRational){ 0, 1 };s->pkt_timebase = (AVRational){ 0, 1 };s->get_buffer2 = avcodec_default_get_buffer2;s->get_format = avcodec_default_get_format;s->get_encode_buffer = avcodec_default_get_encode_buffer;s->execute = avcodec_default_execute;s->execute2 = avcodec_default_execute2;s->sample_aspect_ratio = (AVRational){0,1};s->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC;s->pix_fmt = AV_PIX_FMT_NONE;s->sw_pix_fmt = AV_PIX_FMT_NONE;s->sample_fmt = AV_SAMPLE_FMT_NONE;s->reordered_opaque = AV_NOPTS_VALUE;if(codec && codec2->priv_data_size){s->priv_data = av_mallocz(codec2->priv_data_size);if (!s->priv_data)return AVERROR(ENOMEM);if(codec->priv_class){*(const AVClass**)s->priv_data = codec->priv_class;av_opt_set_defaults(s->priv_data);}}if (codec && codec2->defaults) {int ret;const FFCodecDefault *d = codec2->defaults;while (d->key) {ret = av_opt_set(s, d->key, d->value, 0);av_assert0(ret >= 0);d++;}}return 0;
}
每个编码器独有参数设置
avcodec_alloc_context3 时,会将 codec2->priv_data_size 创建大小的内存赋值给 priv_data,并将 codec 的priv_class 赋值给 priv_data。
av_opt_set_defaults 会把 options 中的默认值,设置到 priv_data 中,相当于给编码器的参数结构体设置了默认值。
if(codec && codec2->priv_data_size){s->priv_data = av_mallocz(codec2->priv_data_size);if (!s->priv_data)return AVERROR(ENOMEM);if(codec->priv_class){*(const AVClass**)s->priv_data = codec->priv_class;av_opt_set_defaults(s->priv_data);}}
为啥 codec 的priv_class 可以直接赋值给 priv_data。以 aacenc 为例,codec 的 priv_class 为 &aacenc_class,而 codec 的 priv_data_size 为 sizeof(AACEncContext)。所有相当于 AVCodecContext 创建了 sizeof(AACEncContext) 大小的 priv_data。之所以可以这样,是因为 AACEncContext 第一个参数就是 AVClass*。相当于这也是 ffmpeg 的小技巧,应该所有的编码器变量结构体,第一个参数都是 AVClass* (看了一下 nvenc.h 也是这样的)。
static const AVClass aacenc_class = {.class_name = "AAC encoder",.item_name = av_default_item_name,.option = aacenc_options,.version = LIBAVUTIL_VERSION_INT,
};const FFCodec ff_aac_encoder = {.p.name = "aac",CODEC_LONG_NAME("AAC (Advanced Audio Coding)"),.p.type = AVMEDIA_TYPE_AUDIO,.p.id = AV_CODEC_ID_AAC,.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |AV_CODEC_CAP_SMALL_LAST_FRAME,.priv_data_size = sizeof(AACEncContext),.init = aac_encode_init,FF_CODEC_ENCODE_CB(aac_encode_frame),.close = aac_encode_end,.defaults = aac_encode_defaults,.p.supported_samplerates = ff_mpeg4audio_sample_rates,.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,.p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP,AV_SAMPLE_FMT_NONE },.p.priv_class = &aacenc_class,
};typedef struct AACEncContext {AVClass *av_class;AACEncOptions options; ///< encoding optionsPutBitContext pb;FFTContext mdct1024; ///< long (1024 samples) frame transform contextFFTContext mdct128; ///< short (128 samples) frame transform contextAVFloatDSPContext *fdsp;AACPCEInfo pce; ///< PCE data, if neededfloat *planar_samples[16]; ///< saved preprocessed inputint profile; ///< copied from avctxint needs_pce; ///< flag for non-standard layoutLPCContext lpc; ///< used by TNSint samplerate_index; ///< MPEG-4 samplerate indexint channels; ///< channel countconst uint8_t *reorder_map; ///< lavc to aac reorder mapconst uint8_t *chan_map; ///< channel configuration mapChannelElement *cpe; ///< channel elementsFFPsyContext psy;struct FFPsyPreprocessContext* psypp;const AACCoefficientsEncoder *coder;int cur_channel; ///< current channel for coder contextint random_state;float lambda;int last_frame_pb_count; ///< number of bits for the previous framefloat lambda_sum; ///< sum(lambda), for Qvg reportingint lambda_count; ///< count(lambda), for Qvg reportingenum RawDataBlockType cur_type; ///< channel group type cur_channel belongs toAudioFrameQueue afq;DECLARE_ALIGNED(16, int, qcoefs)[96]; ///< quantized coefficientsDECLARE_ALIGNED(32, float, scoefs)[1024]; ///< scaled coefficientsuint16_t quantize_band_cost_cache_generation;AACQuantizeBandCostCacheEntry quantize_band_cost_cache[256][128]; ///< memoization area for quantize_band_costvoid (*abs_pow34)(float *out, const float *in, const int size);void (*quant_bands)(int *out, const float *in, const float *scaled,int size, int is_signed, int maxval, const float Q34,const float rounding);struct {float *samples;} buffer;
} AACEncContext;