1. 列举
业界通用的编解码器有很多,有的存粹用软件比如x264,有的依赖硬件,比如Nvidia的NVENC开发包使用自家显卡GPU加速,Intel的Quick Sync开发包使用Intel CPU上集成的GPU加速。
ffmpeg通过封装上述第三方编解码器,和少数自身实现的编解码器,抽象定义出一个AVCodec
结构体,并支持通过命令ffmpeg -decoders
来列举支持的所有解码器,由于内容太多,下面使用ffmpeg -decoders | grep h264
来显示h264解码器:
1
2
3
VFS..D h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
V....D h264_qsv H264 video (Intel Quick Sync Video acceleration) (codec h264)
V..... h264_cuvid Nvidia CUVID H264 decoder (codec h264)
第一个的解码实现是ffmpeg官方开发。通过名字也可以看出来,第二个基于Intel QSV,第三个基于Nvidia显卡。每个解码器开头部分的含义如下,当命令行中通过管道使用grep后,这些输出被过滤了:
1
2
3
4
5
6
7
8
9
Decoders:
V..... = Video
A..... = Audio
S..... = Subtitle
.F.... = Frame-level multithreading
..S... = Slice-level multithreading
...X.. = Codec is experimental
....B. = Supports draw_horiz_band
.....D = Supports direct rendering method 1
同理,使用ffmpeg -encoders | grep h264
显示h264编码器:
1
2
3
4
5
6
7
V..... libx264 libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (codec h264)
V....D h264_amf AMD AMF H.264 Encoder (codec h264)
V....D h264_mf H264 via MediaFoundation (codec h264)
V....D h264_nvenc NVIDIA NVENC H.264 encoder (codec h264)
V..... h264_qsv H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration) (codec h264)
V..... nvenc NVIDIA NVENC H.264 encoder (codec h264)
V..... nvenc_h264 NVIDIA NVENC H.264 encoder (codec h264)
第一个基于x264,带qsv字样的基于Intel QSV,带nvenc字样的基于Nvidia显卡。开头部分的含义如下:
1
2
3
4
5
6
7
8
9
Encoders:
V..... = Video
A..... = Audio
S..... = Subtitle
.F.... = Frame-level multithreading
..S... = Slice-level multithreading
...X.. = Codec is experimental
....B. = Supports draw_horiz_band
.....D = Supports direct rendering method 1
2. AVCodec
每个编解码器通过定义自己的AVCodec
结构体变量,提供名称,类型、CodecId、若干关键回调函数等描述自身数据,使得外部访问者只要找到对应编解码器的AVCodec
,即可屏蔽不同实现的差异:
Type
- 音频AVMEDIA_TYPE_AUDIO
- 视频AVMEDIA_TYPE_VIDEO
- 字幕AVMEDIA_TYPE_AUDIO
CodecId
- AV_CODEC_ID_H264
- AV_CODEC_ID_AC3
回调函数
- init
- close
- flush
- receive_packet
- decode
- receive_frame
区分那么多AVCodec
的标准很简单,那些提供了decode
或receive_frame
函数指针的AVCodec
代表解码器,否则就是编码器。下面是FFMPEG内置的h264解码器(采用CPU解码)定义,源自libavcodec/h264dec.c,只保留了关键数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
AVCodec ff_h264_decoder = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
HWACCEL_DXVA2(h264),
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING |
FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP,
.flush = h264_decode_flush,
.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.priv_class = &h264_class,
};
3. 硬件加速之Nvidia NvENC
ffmpeg支持Nvidia NvENC硬件编解码h264、h265、av1、vp9等多种视频格式,其在libavcodec/cuviddec.c中为每种视频格式分别定义不同的解码器对应AVCodec
变量:
- ff_av1_cuvid_decoder
- ff_h264_cuvid_decoder
- ff_hevc_cuvid_decoder
- ff_vp9_cuvid_decoder
每个变量中的name
字符串对应ffmpeg -decoders | grep cuvid
列举出来的解码器名称:
- av1_cuvid
- h264_cuvid
- hevc_cuvid
- vp9_cuvid
4. 编解码器数组codec_list
在libavcodec/allcodecs.c中通过extern
语法逐个声明散落在不同源代码中编解码器的变量,比如:
1
2
3
4
extern AVCodec ff_av1_cuvid_decoder;
extern AVCodec ff_h264_cuvid_decoder;
extern AVCodec ff_hevc_cuvid_decoder;
extern AVCodec ff_vp9_cuvid_decoder;
随后执行configure
时提取名称带encoder
的编码器变量名填充到codec_list.c
中codec_list
数组,同理继续提取解码器并填充,此后codec_list
数组包含了ffmpeg支持的所有编解码器,并由libavcodec/allcodecs.c提供API来访问她。
下面是脚本中对应代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
find_things_extern(){
thing=$1
pattern=$2
file=$source_path/$3
out=${4:-$thing}
sed -n "s/^[^#]*extern.*$pattern *ff_\([^ ]*\)_$thing;/\1_$out/p" "$file"
}
find_filters_extern(){
file=$source_path/$1
sed -n 's/^extern AVFilter ff_[avfsinkrc]\{2,5\}_\([[:alnum:]_]\{1,\}\);/\1_filter/p' $file
}
ENCODER_LIST=$(find_things_extern encoder AVCodec libavcodec/allcodecs.c)
DECODER_LIST=$(find_things_extern decoder AVCodec libavcodec/allcodecs.c)
CODEC_LIST="
$ENCODER_LIST
$DECODER_LIST
"