一、引言
FFmpeg源码中 通过h264_parse_nal_header函数将H.264码流的NALU Header解析出来。下面对h264_parse_nal_header函数进行分析。
二、h264_parse_nal_header函数定义
h264_parse_nal_header函数定义在FFmpeg源码(下面演示的FFmpeg源码版本是5.0.3)的源文件libavcodec/h2645_parse.c 中:
static int h264_parse_nal_header(H2645NAL *nal, void *logctx)
{
GetBitContext *gb = &nal->gb;
if (get_bits1(gb) != 0)
return AVERROR_INVALIDDATA;
nal->ref_idc = get_bits(gb, 2);
nal->type = get_bits(gb, 5);
av_log(logctx, AV_LOG_DEBUG,
"nal_unit_type: %d(%s), nal_ref_idc: %d\n",
nal->type, h264_nal_unit_name(nal->type), nal->ref_idc);
return 0;
}
该函数作用是:将NALU Header解析出来,由形参nal返回。
形参nal:
nal->gb:输入型参数。(&(nal->gb))->buffer指向存放NALU Header + RBSP 的缓冲区。
nal->ref_idc:输出型参数。执行h264_parse_nal_header函数后,nal->ref_idc的值为NALU Header中的nal_ref_idc。
nal->type:输出型参数。执行h264_parse_nal_header函数后,nal->type的值为NALU Header中的nal_unit_type。
形参logctx:输入型参数。用来输出日志,可以忽略。
返回值:解析NALU Header成功返回0。失败返回AVERROR_INVALIDDATA。
三、h264_parse_nal_header函数的内部实现原理
h264_parse_nal_header函数中,首先通过语句get_bits1(gb);拿到NALU Header中forbidden_zero_bit。关于get_bits1用法可以参考:《FFmpeg中位操作相关的源码:GetBitContext结构体,init_get_bits函数、get_bits1函数和get_bits函数分析》。
由于forbidden_zero_bit 的值应为0,如果它的值为1,则意味着比特流语法出错了,也就是FFmpeg头文件libavutil/error.h里面定义的“Invalid data found when processing input”:
#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input
所以如果forbidden_zero_bit的值不为0,返回AVERROR_INVALIDDATA。
所以有下面语句:
if (get_bits1(gb) != 0)
return AVERROR_INVALIDDATA;
然后通过下面语句,读取NALU Header中的nal_ref_idc和nal_unit_type,分别保存到nal->ref_idc和nal->type中。
nal->ref_idc = get_bits(gb, 2);
nal->type = get_bits(gb, 5);
最后通过
av_log(logctx, AV_LOG_DEBUG,
"nal_unit_type: %d(%s), nal_ref_idc: %d\n",
nal->type, h264_nal_unit_name(nal->type), nal->ref_idc);
日志输出NALU Header的信息。
h264_nal_unit_name函数是用来得到nal_unit_type的名称。其定义如下:
static const char *const h264_nal_type_name[32] = {
"Unspecified 0", //H264_NAL_UNSPECIFIED
"Coded slice of a non-IDR picture", // H264_NAL_SLICE
"Coded slice data partition A", // H264_NAL_DPA
"Coded slice data partition B", // H264_NAL_DPB
"Coded slice data partition C", // H264_NAL_DPC
"IDR", // H264_NAL_IDR_SLICE
"SEI", // H264_NAL_SEI
"SPS", // H264_NAL_SPS
"PPS", // H264_NAL_PPS
"AUD", // H264_NAL_AUD
"End of sequence", // H264_NAL_END_SEQUENCE
"End of stream", // H264_NAL_END_STREAM
"Filler data", // H264_NAL_FILLER_DATA
"SPS extension", // H264_NAL_SPS_EXT
"Prefix", // H264_NAL_PREFIX
"Subset SPS", // H264_NAL_SUB_SPS
"Depth parameter set", // H264_NAL_DPS
"Reserved 17", // H264_NAL_RESERVED17
"Reserved 18", // H264_NAL_RESERVED18
"Auxiliary coded picture without partitioning", // H264_NAL_AUXILIARY_SLICE
"Slice extension", // H264_NAL_EXTEN_SLICE
"Slice extension for a depth view or a 3D-AVC texture view", // H264_NAL_DEPTH_EXTEN_SLICE
"Reserved 22", // H264_NAL_RESERVED22
"Reserved 23", // H264_NAL_RESERVED23
"Unspecified 24", // H264_NAL_UNSPECIFIED24
"Unspecified 25", // H264_NAL_UNSPECIFIED25
"Unspecified 26", // H264_NAL_UNSPECIFIED26
"Unspecified 27", // H264_NAL_UNSPECIFIED27
"Unspecified 28", // H264_NAL_UNSPECIFIED28
"Unspecified 29", // H264_NAL_UNSPECIFIED29
"Unspecified 30", // H264_NAL_UNSPECIFIED30
"Unspecified 31", // H264_NAL_UNSPECIFIED31
};
static const char *h264_nal_unit_name(int nal_type)
{
av_assert0(nal_type >= 0 && nal_type < 32);
return h264_nal_type_name[nal_type];
}
可以看到h264_nal_unit_name函数内部通过nal_unit_type拿到数组h264_nal_type_name中对应的字符串(名称)。
h264_nal_type_name数组跟H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第65页描述的nal_unit_type对应: