音视频入门基础:H.264专题(19)——FFmpeg源码中,获取avcC封装的H.264码流中每个NALU的长度的实现

一、引言

从《音视频入门基础:H.264专题(18)------AVCDecoderConfigurationRecord简介》中可以知道,avcC跟AnnexB不一样,avcC包装的H.264码流中,每个NALU前面没有起始码。avcC通过在每个NALU前加上NALUnitLength,在读取某个NALU之前,先把该NALU前面的NALUnitLength读取出来,拿到该NALU的总长度,然后根据该长度读取相应的字节数,读取出整个NALU。

而FFmpeg源码内部使用get_nalsize函数读取出某个NALU前面的NALUnitLength。

二、get_nalsize函数的定义

get_nalsize函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavcodec/h2645_parse.h中:

cpp 复制代码
static inline int get_nalsize(int nal_length_size, const uint8_t *buf,
                              int buf_size, int *buf_index, void *logctx)
{
    int i, nalsize = 0;

    if (*buf_index >= buf_size - nal_length_size) {
        // the end of the buffer is reached, refill it
        return AVERROR_INVALIDDATA;
    }

    for (i = 0; i < nal_length_size; i++)
        nalsize = ((unsigned)nalsize << 8) | buf[(*buf_index)++];
    if (nalsize <= 0 || nalsize > buf_size - *buf_index) {
        av_log(logctx, AV_LOG_ERROR,
               "Invalid NAL unit size (%d > %d).\n", nalsize, buf_size - *buf_index);
        return AVERROR_INVALIDDATA;
    }
    return nalsize;
}

该函数作用是读取avcC封装的H.264码流中某个NALU前面的NALUnitLength,或者读取AVCDecoderConfigurationRecord中某个SPS前面的sequenceParameterSetLength,或者读取AVCDecoderConfigurationRecord中某个PPS前面的pictureParameterSetLength。由于SPS和PPS都属于一种特殊的NALU,所以sequenceParameterSetLength和pictureParameterSetLength也算是一种NALUnitLength。只是存贮每个sequenceParameterSetLength和pictureParameterSetLength需要固定2字节空间,而存贮每个NALUnitLength所需的空间由AVCDecoderConfigurationRecord中的lengthSizeMinusOne属性决定而已。

形参nal_length_size:输入型参数。如果是要读取某个NALU前面的NALUnitLength,形参nal_length_size的值为"存贮该NALU对应NALUnitLength所需的以字节为单位的空间";如果是要读取sequenceParameterSetLength或pictureParameterSetLength,形参nal_length_size的值固定为2。

形参buf:输入型参数,指向某个缓冲区。

1.如果是要读取某个NALU前面的NALUnitLength,该缓冲区存放该NALU对应的NALUnitLength + NALU Header + EBSP;

2.如果是要读取某个SPS前面的sequenceParameterSetLength,该缓冲区存放该SPS对应的sequenceParameterSetLength + NALU Header + 该SPS实际的NALU数据(该SPS的EBSP);

3.如果是要读取某个PPS前面的pictureParameterSetLength,该缓冲区存放该PPS对应的pictureParameterSetLength + NALU Header + 该PPS实际的NALU数据(该PPS的EBSP);

形参buf_size:形参buf指向的缓冲区的长度,单位为字节。

形参buf_index:既是输入型参数也是输出型参数。表示读取到形参buf指向的缓冲区的第几个字节了。

形参logctx:输入型参数。用来输出日志,可忽略。

返回值:执行成功返回读取到的NALUnitLength或sequenceParameterSetLength或pictureParameterSetLength,失败返回一个负数。

三、get_nalsize函数的内部实现分析

get_nalsize函数中,首先判断是否读到了形参buf指向的缓冲区的末尾。如果已经读到了末尾,返回AVERROR_INVALIDDATA:

cpp 复制代码
    if (*buf_index >= buf_size - nal_length_size) {
        // the end of the buffer is reached, refill it
        return AVERROR_INVALIDDATA;
    }

如果还没读到末尾,继续执行。通过下面语句,读取NALUnitLength或sequenceParameterSetLength或pictureParameterSetLength,存贮到局部变量nalsize中:

cpp 复制代码
    for (i = 0; i < nal_length_size; i++)
        nalsize = ((unsigned)nalsize << 8) | buf[(*buf_index)++];

如果读取到的该NALU的总长度小于0,或者读取到的该NALU的总长度大于形参buf指向的缓冲区中剩下的还未被读取的空间,打印日志"Invalid NAL unit size",并返回AVERROR_INVALIDDATA:

cpp 复制代码
    if (nalsize <= 0 || nalsize > buf_size - *buf_index) {
        av_log(logctx, AV_LOG_ERROR,
               "Invalid NAL unit size (%d > %d).\n", nalsize, buf_size - *buf_index);
        return AVERROR_INVALIDDATA;
    }

读取成功返回NALUnitLength或sequenceParameterSetLength或pictureParameterSetLength的值:

cpp 复制代码
    return nalsize;
相关推荐
yantaohk8 小时前
一键下载微信视频号所有页面视频,支持批量下载、加密视频解密、自动去重
网络·微信·音视频
EasyGBS11 小时前
国标GB28181视频平台EasyGBS解决多格式视频流无缝转换难题
ffmpeg·音视频
byte轻骑兵12 小时前
【LE Audio】CAP精讲[2]: 三大角色+服务映射,CAP配置核心流程全拆解
人工智能·音视频·le audio·低功耗音频·蓝牙通话
非凡ghost16 小时前
视频下载神器:直播回放、视频链接一键抓取,还能自动监听!
java·前端·javascript·音视频
做萤石二次开发的哈哈17 小时前
萤石×广联达 | 智能视觉融合数字建造,让工地更透明、更安全
人工智能·安全·音视频·智能硬件
小短腿的代码世界1 天前
QtAV音视频播放实战深度解析:从零构建高性能跨平台播放器
qt·音视频
树下水月1 天前
关于使用ffmpeg的一些使用方法
ffmpeg
憧憬成为原神糕手1 天前
FFmpeg 音视频开发笔记(一):H.264 解码为 YUV
笔记·ffmpeg·音视频
ai产品老杨1 天前
突破品牌壁垒:基于 GB28181 与 RTSP 的异构 AI 视频平台架构深度解析(支持 Docker 与源码交付)
人工智能·架构·音视频
AI服务老曹1 天前
【架构深析】打破安防“黑盒”:GB28181/RTSP 视频管理平台如何通过源码交付与 API 驱动节省 95% 开发成本
架构·音视频