ffmpeg编解码过程中AVCodecContex的作用分析
主要内容
本章主要内容就是讲解ffmpeg中实现硬件加速的代码的AVCodecContext的讲解内容,主要是基于ffmpeg 4.0代码进行分析。以下流程以h264硬件编解码为例。
硬件解码过程中AVCodecContext的变化
用户设置AVCodecContext项目
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| hw_device_ctx | 用户设置 | AVHWDeviceContext的AVBufferRef指针 |
AVCodecContext在不同的API中的作用和内部变量都会发生变化。
avcodec_alloc_context3
avcodec_alloc_context3(libavcodec\options.c)会根据AVCodec的值来分配和初始化AVCodecContext结构体。
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| av_class | init_context_defaults | AVClass | av_codec_context_class |
| codec_type | init_context_defaults | AVMediaType | AVMEDIA_TYPE_VIDOE,AVMEDIA_TYPE_AUDIO等 |
| codec | init_context_defaults | AVCode指针 | decoder的指针 |
| codec_id | init_context_defaults | AVCodecID | AV_CODEC_ID_H264,AV_CODEC_ID_HEVC等 |
| time_base | init_context_defaults | AVRational | 设置为1 |
| framerate | init_context_defaults | AVRational | 设置为1 |
| pkt_timebase | init_context_defaults | AVRational | 设置为1 |
| get_buffer2 | init_context_defaults | 函数指针 | avcodec_default_get_buffer2 |
| get_format | init_context_defaults | 函数指针 | avcodec_default_get_format |
| execute | init_context_defaults | 函数指针 | avcodec_default_execute |
| execute2 | init_context_defaults | 函数指针 | avcodec_default_execute2 |
| sample_aspect_ratio | init_context_defaults | AVRational | 设置为1 |
| pix_fmt | init_context_defaults | 函数指针 | AV_PIX_FMT_NONE |
| sw_pix_fmt | init_context_defaults | 函数指针 | AV_PIX_FMT_NONE |
| sample_fmt | init_context_defaults | 函数指针 | AV_SAMPLE_FMT_NONE |
| reordered_opaque | init_context_defaults | 函数指针 | AV_NOPTS_VALUE |
| priv_data | init_context_defaults | 普通指针 | 使用malloc根据AVCodec的priv_data_size分配,对h264而言就是H264Context结构体,并将AVCodec->priv_class赋值给priv_data |
avcodec_parameters_to_context
avcodec_parameters_to_context完全是拷贝赋值操作,将AVStream中的AVCodecParameters赋值给AVCodecContext,没有调用其他函数。
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| codec_type | avcodec_parameters_to_context | AVMediaType | AVMEDIA_TYPE_VIDOE,AVMEDIA_TYPE_AUDIO等 |
| codec_id | avcodec_parameters_to_context | AVCodecID | AV_CODEC_ID_H264,AV_CODEC_ID_HEVC等 |
| codec_tag | avcodec_parameters_to_context | int | |
| bit_rate | avcodec_parameters_to_context | int | |
| bits_per_coded_sample | avcodec_parameters_to_context | int | |
| bits_per_raw_sample | avcodec_parameters_to_contextint | int | |
| profile | avcodec_parameters_to_context | int | |
| level | avcodec_parameters_to_context | int | |
| pix_fmt | avcodec_parameters_to_context | AVPixelFormat | 属于视频参数,如AV_PIX_FMT_RGB24,AV_PIX_FMT_VAAPI等 |
| width | avcodec_parameters_to_context | int | 属于视频和subtile参数 |
| heiight | avcodec_parameters_to_context | int | 属于视频和subtile参数 |
| field_order | avcodec_parameters_to_context | int | 属于视频参数 |
| color_range | avcodec_parameters_to_context | AVColorRange | 属于视频参数 |
| color_primaries | avcodec_parameters_to_context | AVColorPrimaries | 属于视频参数 |
| color_trc | avcodec_parameters_to_context | AVColorTransferCharacteristic | 属于视频参数 |
| colorspace | avcodec_parameters_to_context | AVColorSpace | 属于视频参数 |
| chroma_sample_location | avcodec_parameters_to_context | AVChromaLocation | 属于视频参数 |
| sample_aspect_ratio | avcodec_parameters_to_context | AVRational | 属于视频参数 |
| has_b_frames | avcodec_parameters_to_context | int | 属于视频参数 |
| sample_fmt | avcodec_parameters_to_context | AVSampleFormat | 属于音频参数 |
| channel_layout | avcodec_parameters_to_context | uint64_t | 属于音频参数 |
| channels | avcodec_parameters_to_context | int | 属于音频参数 |
| sample_rate | avcodec_parameters_to_context | int | 属于音频参数 |
| block_align | avcodec_parameters_to_context | int | 属于音频参数 |
| frame_size | avcodec_parameters_to_context | int | 属于音频参数 |
| delay | avcodec_parameters_to_context | int | 属于音频参数 |
| initial_padding | avcodec_parameters_to_context | int | 属于音频参数 |
| trailing_padding | avcodec_parameters_to_context | int | 属于音频参数 |
| seek_preroll | avcodec_parameters_to_context | int | 属于音频参数 |
| initial_padding | avcodec_parameters_to_context | int | 属于音频参数 |
| initial_padding | avcodec_parameters_to_context | int | 属于音频参数 |
| extradata | avcodec_parameters_to_context | 普通指针 | 使用av_mallocz分配AVCodecParameters->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE(64)大小空间,并将AVCodecParameters->extradata其数据拷贝到extradata中 |
| extradata_size | avcodec_parameters_to_context | int | AVCodecParameters->extradata_size赋值给extradata_size |
av_hwdevice_ctx_create
av_hwdevice_ctx_create主要是进行device创建和初始化工作。并将创建和初始化完成的AVHWDeviceContext的AVBufferRef指针赋值给AVCodecContext->hw_device_ctx。表格中的属于AVHWDeviceContext *device_ctx的成员。
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| user_opaque | av_hwdevice_ctx_create | VAAPIDevicePriv | VAAPIDevicePriv.display保存XOpenDisplay的Display指针 |
| internal | av_hwdevice_ctx_create | AVHWDeviceInternal | av_mallocz分配内存 |
| internal->priv | av_hwdevice_ctx_create | VAAPIDeviceContext | 根据(HWContextType)hw_type->device_priv_size使用av_mallocz分配内存 |
| internal->hw_type | av_hwdevice_ctx_create | HWContextType | 对于VAAPI而言就是ff_hwcontext_type_vaapi |
| hwctx | av_hwdevice_ctx_create | AVVAAPIDeviceContext | 根据(HWContextType)hw_type->device_hwctx_size使用av_mallocz分配内存 |
| av_class | av_hwdevice_ctx_create | AVClass | hwframe_ctx_class实例 |
| type | av_hwdevice_ctx_create | AVHWDeviceType | 对于VAAPI而言就是AV_HW_DEVICE_TYPE_VAAPI |
| sw_ format | av_hwdevice_ctx_create | AVPixelFormat | AV_PIX_FMT_NONE |
| display | vaapi_device_connect | VADisplay | av_hwdevice_ctx_create调用函数vaapi_device_connect |
avcodec_open2
使用硬件解码过程中需要在avcodec_open2之前对AVCodecContext.hw_device_ctx使用av_hwdevice_ctx_create根据AVHWDeviceType进行创建,并赋值给AVCodecContext.hw_device_ctx。avcodec_open2调用h264_decode_init进行进一步初始化操作
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| internal | avcodec_open2 | AVCodecInternal | 使用malloc分配内存 |
| internal->pool | avcodec_open2 | FramePool | 使用malloc分配内存 |
| internal->to_free | avcodec_open2 | AVFrame指针 | 使用av_frame_alloc分配内存 |
| internal->compat_decode_frame | avcodec_open2 | AVFrame指针 | 使用av_frame_alloc分配内存 |
| internal->buffer_frame | avcodec_open2 | AVFrame指针 | 使用av_frame_alloc分配内存 |
| internal->buffer_pkt | avcodec_open2 | AVPacket指针 | 使用av_packet_alloc分配内存 |
| internal->ds.in_pkt | avcodec_open2 | AVPacket指针 | 使用av_packet_alloc分配内存 |
| internal->last_pkt_props | avcodec_open2 | AVPacket指针 | 使用av_packet_alloc分配内存 |
| internal->skip_samples_multiplier | avcodec_open2 | int | 设置为1 |
| priv_data | avcodec_open2 | void指针 | 根据codec->priv_data_size 使用malloc分配内存 |
| codec_ width | ff_set_dimensions | int | 视频编码宽度 |
| codec_height | ff_set_dimensions | int | 视频比编码宽度 |
| width | ff_set_dimensions | int | 视频宽度,与视频宽度有区别,大部分时候相同 |
| height | ff_set_dimensions | int | 视频长度,与视频长度有区别,大部分时候相同 |
| codec | avcodec_open2 | AVCodec指针 | 直接赋值给AVCodecContext.AVCodec指针 |
| frame_number | avcodec_open2 | AVCodec指针 | 设置0 |
| codec_descriptor | avcodec_open2 | AVCodecDescriptor指针 | avcodec_descriptor_get根据codec_id获取AVCodecDescriptor |
| sw_pix_fmt | avcodec_open2 | AVPixelFormat | 当AVCodecContext.hw_frames_ctx初始化后,将frames_ctx->sw_format直接赋值给AVCodecContext.sw_pix_fmt |
| pts_correction_num_faulty_pts,pts_correction_num_faulty_dts | avcodec_open2 | int | 设置为0 |
| pts_correction_last_pts,pts_correction_last_dts | avcodec_open2 | int6_t | 设置为INT64_MIN |
av_read_frame
av_read_frame主要是从AVFormatContext中获取AVPacket,不会对AVCodecContext造成影响。
avcodec_send_packet
avcodec_send_packet是将packet发送给ffmpeg进行内部解码,并进行逐级调用,直到ff_get_format调用hwaccel_init。
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| sw_pix_fmt | ff_get_format | AVPixelFormat | AV_PIX_FMT_NONE |
| internal->hwaccel_priv_data | ff_get_format | 普通指针 | 根据hwaccel->priv_data_size使用malloc分配内存 |
| hwaccel | hwaccel_init | AVHWAccel指针 | 根据AVPixelFormat格式将avctx->codec->hw_configs[i]中的AVHWaccel赋值给hwaccel |
hwaccel_init
hwaccel_init实质调用ff_vaapi_decode_init进行初始化操作,初始化avctx->internal->hwaccel_priv_data,即VAAPIDecodeContext结构体。表格中为VAAPIDecodeContext *vaapictx的成员
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| AVCodecContext->internal->hwaccel_priv_data | hwaccel_init | VAAPIDecodeContext | 根据(AVHWAccel)hwaccel->hwaccel_priv_size使用av_mallocz分配内存 |
| have_old_context | ff_vaapi_decode_init | AVPixelFormat | AV_PIX_FMT_NONE |
| old_context | ff_vaapi_decode_init | 普通指针 | 根据hwaccel->priv_data_size使用malloc分配内存 |
| device_ref | ff_vaapi_decode_init | AVBufferRef指针 | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| device | ff_vaapi_decode_init | AVHWDeviceContext指针 | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| hwctx | ff_vaapi_decode_init | AVVAAPIDeviceContext指针 | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| hwctx->display | ff_vaapi_decode_init | VADisplay | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| va_config | ff_vaapi_decode_init | VAConfigID | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| va_context | ff_vaapi_decode_init | VAContextID | have_old_context为1时有效,为旧版本的VAAPI使用方法 |
| AVCodecContext.hw_frames_ctx | ff_decode_get_hw_frames_ctx | AVBufferRef指针,AVHWFramesContext数据 | ff_decode_get_hw_frames_ctx–>avcodec_get_hw_frames_parameters–>av_hwframe_ctx_alloc |
| AVCodecContext->hwaccel | hwaccel_init | AVHWAccel | 将ff_h264_vaapi_hwaccel结构体指针赋值给hwaccel |
ff_decode_get_hw_frames_ctx
ff_decode_get_hw_frames_ctx进行AVHWFramesContext的初始化操作,初始化avctx->hw_frames_ctx,ff_decode_get_hw_frames_ctx会调用avcodec_get_hw_frames_parameters,然后再avcodec_get_hw_frames_parameters中进行av_hwframe_ctx_alloc,AVHWAccel.frame_params(ff_vaapi_common_frame_params)和av_hwframe_ctx_init操作。
av_hwframe_ctx_alloc
av_hwframe_ctx_alloc进行AVHWFramesContext内存分配操作,初始化avctx->hw_frames_ctx,即AVHWFramesContext结构体。表格中为AVHWFramesContext*ctx的成员
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| internal | av_hwframe_ctx_alloc | AVHWFramesInternal | av_mallocz分配内存 |
| internal->priv | av_hwframe_ctx_alloc | VAAPIFramesContext | 根据(HWContextType)hw_type->frames_priv_size使用av_mallocz分配内存 |
| hwctx | av_hwframe_ctx_alloc | AVVAAPIFramesContext | 根据(HWContextType)hw_type->frames_hwctx_size使用av_mallocz分配内存 |
| av_class | av_hwframe_ctx_alloc | AVClass | hwframe_ctx_class实例 |
| device_ref | av_hwframe_ctx_alloc | AVBufferRef指针 | avctx->hw_device_ctx数据的AVBufferRef指针 |
| device_ctx | av_hwframe_ctx_alloc | AVHWDeviceContext指针 | avctx->hw_device_ctx数据 |
| format | av_hwframe_ctx_alloc | AVPixelFormat | AV_PIX_FMT_NONE |
| sw_ format | av_hwframe_ctx_alloc | AVPixelFormat | AV_PIX_FMT_NONE |
ff_vaapi_common_frame_params
ff_vaapi_common_frame_params调用vaapi_decode_make_config初始化 AVHWFramesContext结构体即AVHWFramesContext结构体。表格中为AVHWFramesContext*ctx的初始化成员
| 成员 | 函数 | 说明(description) |
|---|---|---|
| format | ff_vaapi_common_frame_params | 确定解码类型的格式AV_PIX_FMT_VAAPI或者AV_PIX_FMT_VDPAU |
| width | ff_vaapi_common_frame_params | 解码的宽度,来自于AVCodecContext的coded_width,用来创建surface,并会将其传递给AVFrame的width |
| height | ff_vaapi_common_frame_params | 解码的高度,来自于AVCodecContext的coded_height,用来创建surface,并会将其传递给AVFrame的height |
| sw_format | vaapi_decode_find_best_format | 解码的格式,通过查询驱动确定,vaapi_decode_make_config调用 |
| initial_pool_size | ff_vaapi_common_frame_params | 用来确定surface分配的个数 |
av_hwframe_ctx_init
av_hwframe_ctx_init进行AVHWFramesContext判断操作,并不对AVHWFramesContext结构体的数值进行修改
vaapi_decode_make_config
vaapi_decode_make_config对AVVAAPIHWConfig(内部只有VAConfigID成员)进行av_hwdevice_hwconfig_alloc内存分配操作,和使用av_hwdevice_get_hwframe_constraints对AVHWFramesConstraints成员的初始化,这两个结构体不对外部AVCodecContext结构体产生影响,只是作为内部数据参考,只有VAConfigID 赋值给VAAPIDecodeContext ->va_config
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| AVHWFramesContext.format | vaapi_decode_make_config | AVPixelFormat | AV_PIX_FMT_VAAPI |
| AVHWFramesContext.width | vaapi_decode_make_config | int | avctx->coded_width |
| AVHWFramesContext.height | vaapi_decode_make_config | int | avctx->coded_height |
| AVHWFramesContext.initial_pool_size | vaapi_decode_make_config | int | 原值为1,根据不同的code_id增加,H264(+16),HEVC(+16),VP9(+8),VP8(+3),OTHERS(+2),除此之外所有的还会在增加3,所以对于h264而言surface的最大数目就是20 |
硬件编码过程中AVCodecContext的变化
编码过程的很多函数和解码过程中的函数是通用的,以下只对差异点进行介绍
用户设置AVCodecContext项目
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| width | 用户设置 | int | 编码宽度 |
| height | 用户设置 | int | 编码高度 |
| time_base | 用户设置 | AVRational | 可以为{1, 25} |
| framerate | 用户设置 | AVRational | 可以为{1, 25} |
| sample_aspect_ratio | 用户设置 | AVRational | 可以为{1, 1} |
| pix_fmt | 用户设置 | AVPixelFormat | AV_PIX_FMT_VAAPI |
| hw_frames_ctx | 用户设置 | AVHWFramesContext的AVBufferRef指针 | 用于确定编码surface的size和format |
对于hw_frames_ctx需要设置以下项目:
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| format | 用户设置 | AVPixelFormat | 编码的硬件格式标准,如AV_PIX_FMT_VAAPI |
| sw_format | 用户设置 | AVPixelFormat | 编码的硬件像素格式,如AV_PIX_FMT_NV12 |
| width | 用户设置 | int | 编码surface的宽度 |
| height | 用户设置 | int | 编码surface的高度 |
| initial_pool_size | 用户设置 | int | 编码surface的的使用个数最大值 |
avcodec_alloc_context3
与解码相同
av_hwdevice_ctx_create
与解码相同
avcodec_open2
使用硬件编码过程中需要在avcodec_open2之前对AVCodecContext.hw_device_ctx使用av_hwdevice_ctx_create根据AVHWDeviceType进行创建,并赋值给AVCodecContext.hw_device_ctx。avcodec_open2对AVCodecContext成员进行初始化与解码过程相同,avcodec_open2会调用vaapi_encode_h264_init对AVCodecContext的成员造成影响,主要是VAAPIEncodeContext结构体,即AVCodecContext.priv_data分配的内存。以下表格中为VAAPIEncodeContext成员
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| codec | vaapi_encode_h264_init | VAAPIEncodeType | 全局变量vaapi_encode_type_h264指针 |
| AVCodecContext.profile | vaapi_encode_h264_init | int | 用户设定,如果不存在就使用默认值 |
| AVCodecContext.level | vaapi_encode_h264_init | int | 用户设定,如果不存在就使用默认值 |
| va_profile | vaapi_encode_h264_init | VAProfile | 根据AVCodecContext.profile设定 |
| va_entrypoint | vaapi_encode_h264_init | VAEntrypoint | 根据VA_CHECK_VERSION版本设定,低版本为VAEntrypointEncSliceLP,高版本为VAEntrypointEncSlice |
| va_rt_format | vaapi_encode_h264_init | int | 设定为VA_RT_FORMAT_YUV420 |
| va_rc_mode | vaapi_encode_h264_init | int | 设定为VA_RC_CBR,VA_RC_VBR,VA_RC_CQP |
| va_packed_headers | vaapi_encode_h264_init | int | 设定为VA_ENC_PACKED_HEADER_SEQUENCE,VA_ENC_PACKED_HEADER_SLICE,VA_ENC_PACKED_HEADER_MISC |
| surface_width | vaapi_encode_h264_init | int | 设定为avctx->width的16字节对齐值 |
| surface_height | vaapi_encode_h264_init | int | 设定为设定为avctx->height的16字节对齐值 |
| codec_options | ff_vaapi_encode_init | void* | 设定为本身的codec_options |
| input_frames_ref | ff_vaapi_encode_init | AVBufferRef* | 设定为AVCodecContext.hw_frames_ctx的引用 |
| priv_data | ff_vaapi_encode_init | void* | av_mallocz分配VAAPIEncodeTyp.priv_data_size大小的内存 |
| input_frames | ff_vaapi_encode_init | AVHWFramesContext* | |
| device_ref | ff_vaapi_encode_init | AVBufferRef* | 设定为AVCodecContext.hw_frames_ctx的引用 |
| device | ff_vaapi_encode_init | AVHWDeviceContext* | 设定为device_ref的data |
| hwctx | ff_vaapi_encode_init | AVHWDeviceContext* | 设定为device的hwctx |
| va_config | ff_vaapi_encode_init | VAConfigID | 调用vaCreateConfig |
| va_context | ff_vaapi_encode_init | VAContextID | 调用vaCreateContext |
| output_buffer_pool | ff_vaapi_encode_init | VAContextID | 调用av_buffer_pool_init2用来获取VABufferID |
av_hwframe_get_buffer
获取一个free的surface,不会对结构体AVCodecContext产生影响。
avcodec_send_frame
avcodec_send_frame主要是调用do_encode进行编码。
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| internal->buffer_pkt_valid | do_encode | int | 编码的packet是否成功,不成功时avcodec_receive_packet调用do_encode |
avcodec_receive_packet
| 成员 | 函数 | 类型 | 说明(description) |
|---|---|---|---|
| internal->buffer_pkt_valid | do_encode | int | 编码的packet是否成功,不成功时avcodec_receive_packet调用do_encode |
ffmpeg硬件解码对于AVCodecContext的影响如上所示,其他不做多余介绍。