音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析

=================================================================

音视频入门基础: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官方文档的描述符

=================================================================

一、引言

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 中:

cpp 复制代码
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":

cpp 复制代码
#define AVERROR_INVALIDDATA        FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input

所以如果forbidden_zero_bit的值不为0,返回AVERROR_INVALIDDATA。

所以有下面语句:

cpp 复制代码
if (get_bits1(gb) != 0)
        return AVERROR_INVALIDDATA;

然后通过下面语句,读取NALU Header中的nal_ref_idc和nal_unit_type,分别保存到nal->ref_idc和nal->type中。

cpp 复制代码
nal->ref_idc = get_bits(gb, 2);
nal->type    = get_bits(gb, 5);

最后通过

cpp 复制代码
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的名称。其定义如下:

cpp 复制代码
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对应:

相关推荐
小鱼仙官10 小时前
Windonws 视频存储,10s/不限时
开发语言·qt·音视频
福老板的生意经11 小时前
AI 短视频全链路创作分发系统架构解析:模块化设计与核心技术实现
人工智能·系统架构·音视频
hz5678911 小时前
2026应急指挥场景视频会议系统架构设计与私有化部署实践
系统架构·音视频·实时音视频·信息与通信·视频编解码
Hommy8811 小时前
【剪映小助手】音频处理工具接口
aigc·音视频·剪映小助手·视频剪辑自动化
alphageek812 小时前
JeffMony开源的VideoDownloader,Android平台视频下载SDK
android·其他·开源·音视频
kyle-fang12 小时前
Decord详解
音视频·视频解析
kyle-fang13 小时前
手术视频预处理构想
音视频
DogDaoDao13 小时前
H.266/VVC 视频编解码标准最新优化研究综述
论文·音视频·实时音视频·视频编解码·vvc·vtm·h.266
EasyCVR13 小时前
从连锁门店到城市级项目,国标GB28181视频监控平台EasyCVR的全场景适配能力有多绝?
运维·网络·音视频
REDcker14 小时前
QUIC协议系列导读
音视频·webrtc·实时音视频·webtransport