=================================================================
音视频入门基础:H.264专题系列文章:
音视频入门基础:H.264专题(1)------H.264官方文档下载
音视频入门基础:H.264专题(2)------使用FFmpeg命令生成H.264裸流文件
音视频入门基础:H.264专题(3)------EBSP, RBSP和SODB
音视频入门基础:H.264专题(4)------NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介
音视频入门基础:H.264专题(5)------FFmpeg源码中 解析NALU Header的函数分析
音视频入门基础:H.264专题(6)------FFmpeg源码:从H.264码流中提取NALU Header、EBSP、RBSP和SODB
音视频入门基础:H.264专题(7)------FFmpeg源码中 指数哥伦布编码的解码实现
音视频入门基础:H.264专题(8)------H.264官方文档的描述符
音视频入门基础:H.264专题(10)------FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析
音视频入门基础:H.264专题(11)------计算视频分辨率的公式
音视频入门基础:H.264专题(12)------FFmpeg源码中通过SPS属性计算视频分辨率的实现
音视频入门基础:H.264专题(13)------FFmpeg源码中通过SPS属性获取视频色彩格式的实现
音视频入门基础:H.264专题(14)------计算视频帧率的公式
音视频入门基础:H.264专题(15)------FFmpeg源码中通过SPS属性获取视频帧率的实现
音视频入门基础:H.264专题(16)------FFmpeg源码中,判断某文件是否为H.264裸流文件的实现
音视频入门基础:H.264专题(17)------FFmpeg源码中,获取H.264视频的profile的实现
音视频入门基础:H.264专题(18)------FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程
音视频入门基础:H.264专题(19)------AVCDecoderConfigurationRecord简介
音视频入门基础:H.264专题(20)------FFmpeg源码中,获取avcC封装的H.264码流中每个NALU的长度的实现
音视频入门基础:H.264专题(21)------FFmpeg源码中,解码AVCDecoderConfigurationRecord的实现
音视频入门基础:H.264专题(22)------通过FFprobe显示H.264裸流每个packet的信息
=================================================================
一、引言
通过FFmpeg命令可以查看到H.264视频的profile(规格):
cpp
./ffmpeg -i XXX
这个profile是通过解码SPS的profile_idc属性获取到的。profile_idc属性固定占1字节,表示当前这路H.264码流的编码档次:
比如,profile_idc的值为66表示是Baseline profile:
profile_idc的值为77表示是Main profile:
profile_idc的值为88表示是Extended profile:
profile_idc的值为100表示是High profile:
除此之外,还有其它profile,具体可以查阅H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》。
二、FFmpeg源码中,获取H.264视频的profile的实现
从文章《音视频入门基础:H.264专题(10)------FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析》中,我们可以知道,FFmpeg源码中通过ff_h264_decode_seq_parameter_set函数解码SPS,从而拿到SPS中的属性。
ff_h264_decode_seq_parameter_set函数中通过下面代码解析出SPS中的profile_idc属性, 存贮到sps->profile_idc中:
cpp
int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
H264ParamSets *ps, int ignore_truncation)
{
//...
profile_idc = get_bits(gb, 8);
//...
sps->profile_idc = profile_idc;
//...
}
然后在libavcodec/h264_parser.c的parse_nal_units函数中,通过下面代码将profile_idc属性赋值给AVCodecContext的profile:
cpp
static inline int parse_nal_units(AVCodecParserContext *s,
AVCodecContext *avctx,
const uint8_t * const buf, int buf_size)
{
//...
avctx->profile = ff_h264_get_profile(sps);
//...
}
ff_h264_get_profile函数定义在libavcodec/h264_parse.c中,该函数是用来计算SPS的profile_idc属性和constraint_set?_flags属性的:
cpp
/**
* Compute profile from profile_idc and constraint_set?_flags.
*
* @param sps SPS
*
* @return profile as defined by AV_PROFILE_H264_*
*/
int ff_h264_get_profile(const SPS *sps)
{
int profile = sps->profile_idc;
switch (sps->profile_idc) {
case AV_PROFILE_H264_BASELINE:
// constraint_set1_flag set to 1
profile |= (sps->constraint_set_flags & 1 << 1) ? AV_PROFILE_H264_CONSTRAINED : 0;
break;
case AV_PROFILE_H264_HIGH_10:
case AV_PROFILE_H264_HIGH_422:
case AV_PROFILE_H264_HIGH_444_PREDICTIVE:
// constraint_set3_flag set to 1
profile |= (sps->constraint_set_flags & 1 << 3) ? AV_PROFILE_H264_INTRA : 0;
break;
}
return profile;
}
最后在dump_stream_format函数中,通过avcodec_string函数中的语句:profile = avcodec_profile_name(enc->codec_id, enc->profile)拿到上一步得到的AVCodecContext的profile。再在dump_stream_format函数中将profile打印出来:
cpp
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
profile = avcodec_profile_name(enc->codec_id, enc->profile);
//...
}