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

一、引言

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对应:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/743293.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程间通信_4

文章目录 七、进程间通信1. 进程间通信分类systeam V共享内存消息队列 未完待续 七、进程间通信 1. 进程间通信分类 systeam V共享内存 进程间通信的本质就是让不同进程看到同一份资源。而systeam V是通过让不同的进程经过页表映射到同一块内存空间&#xff08;操作系统完成…

Verilog描述一个带有异步置位和异步清零的D触发器

1 带有异步置位和异步清零的D触发器的真值表&#xff1a; 2 Verilog代码描述 module DFF_SR(CLK, D, Rd, Sd, Q, QN);input CLK, D, Rd, Sd;output Q, QN;reg Q_DFF;always (posedge CLKor negedge Rd or negedge Sd)beginif(!Rd)Q_DFF < 1b0;else if(!Sd)Q_DFF < 1b1;e…

硬盘监控和分析工具:Smartctl

文章目录 1. 概述2. 安装3. 使用4. smartctl属性信息介绍 1. 概述 Smartctl&#xff08;S.M.A.R.T 自监控&#xff0c;分析和报告技术&#xff09;是类Unix系统下实施SMART任务命令行套件或工具&#xff0c;它用于打印SMART自检和错误日志&#xff0c;启用并禁用SMRAT自动检测…

永洪bi知识点

1、下拉过滤组件和下拉参数组件的区别 下拉过滤组件只能对跟他绑有相同数据集的组件进行过滤 而下拉参数组件是当你设置了筛选条件以后&#xff0c;那么所有的组件&#xff0c;不管你绑定了什么样子的数据集&#xff0c;都能起作用&#xff0c;前提是你这个组件是需要去绑定参…

docker基础使用教程

1.准备工作 例子&#xff1a;工程在docker_test 生成requirements.txt文件命令&#xff1a;&#xff08;使用参考链接2&#xff09; pip list --formatfreeze > requirements.txt 参考链接1&#xff1a; 安装pipreqs可能比较困难 python 项目自动生成环境配置文件require…

图片如何去水印,分享4个小妙招,手把手教会你!

作为一个经常逛社区网站下载表情包、头像的人&#xff0c;遇到的一个大难题就是图片有水印。如何才能快速去除水印&#xff1f;询问了一圈身边朋友&#xff0c;搜集了各种资料&#xff0c;小编整理了4个超好用的方法。 如果大家和小编一样&#xff0c;能坐着就不站着&#xff0…

【C++LeetCode】【热题100】移动零【简单】-不同效率的题解【4】

题目&#xff1a; 暴力方法&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int counts0;//零的数量int swapCounts0;for(int i0;i<nums.size();i){if(nums[i]0){counts1;}}swapCountscounts;for(int i0;i<nums.size();i){fo…

强制用户裸奔,微软新规以后用Win11必须先注册了

想必在座资深搞机佬对 Windows「本地账户」这一大功能都不陌生。 简单来说&#xff0c;本地用户账户是过去在安装操作系统时自动为大家创建的用于系统管理的默认内置账户。 「本地」二字&#xff0c;代表了即便在电脑不联网的情况下也能自动创建成功。 更重要的是它不与 Micr…

优化了自制的浏览器主页的全屏功能

第一次修改 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>全屏功能</title><style>…

关于window的安装

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 第一windows的分类 旗舰版 个人版…

2024最新算法:北极海鹦优化(Arctic puffin optimization,APO)算法求解23个函数,MATLAB代码

一、算法介绍 北极海鹦优化&#xff08;Arctic puffin optimization&#xff0c;APO&#xff09;算法是2024年提出一种智能优化算法。该算法模拟海鹦在空中飞行和水下觅食两个阶段的行为&#xff0c;旨在实现勘探与开发之间更好的平衡。该算法包括几个关键操作&#xff0c;包括…

张家界首迎新V系客车!苏州金龙携手中信旅运打造旅游客运新范本

在国际张&#xff08;张家界&#xff09;这片美丽而又神奇的的土地上&#xff0c;伴随着旅游市场持续火爆的&#xff0c;是旅游客运行业的激烈竞争。 如何在旅游市场的近乎“白刃战”中突出重围&#xff0c;成为众多旅游运输企业亟待破解的一道难题。然而&#xff0c;作为张家…

Linux源码阅读笔记06-RCU机制和内存优化屏障

RCU机制 RCU&#xff08;Read-Copy-Update&#xff09;&#xff0c;是Linux内核重要的同步机制。Linux内核有原子操作&#xff0c;读写信号量&#xff0c;为什么要单独设计一个比较复杂的新机制呢&#xff1f; spinlock和mutex信号量都使用了原子操作&#xff0c;多CPU在访问…

在Python中定义和使用函数及模块

我将介绍如何在Python中定义和使用函数&#xff0c;以及如何创建和使用Python模块。这些基础知识将帮助你编写更高效和可复用的代码。 目录 函数基础定义和调用函数参数传递 函数式编程匿名函数高阶函数 装饰器模块和包常用的标准库模块 函数基础 定义和调用函数 定义函数&a…

这样制作宣传册,才能提升品牌的曝光度

​在当今竞争激烈的市场环境中&#xff0c;一份高质量的宣传册不仅可以展示企业的实力和特色&#xff0c;还能有效提升品牌的曝光度。要想制作出既能吸引眼球又能传达信息的宣传册&#xff0c;需要如何制作呢&#xff1f; 1.明确宣传目标&#xff1a;制作翻页电子宣传册之前&am…

C语言·动态内存管理

1. 为什么要有动态内存管理&#xff1f; 例1&#xff1a; //固定的向内存申请4个字节 int a 10;//申请连续的一块空间 int arr[10]; 这些数据一旦声明定义之后就会在内存中有一块空间&#xff0c;这些空间都是固定的&#xff0c;为了让内存使用更加灵活&#xff0c;这时我们…

Spark SQL 血缘解析方案

背景 项目背景建设数据中台,往往数据开发人员首先需要能够通过有效的途径检索到所需要的数据,然后根据检索的数据模型进行业务加工然后得到一些中间模型,最后再通过数据抽取工具或者OLAP分析工具直接将数据仓库中加工好的公共模型输出到应用层。这里我不在去介绍数据仓库为…

【Qt】day2

文章目录 菜单栏工具栏状态栏铆接部件&#xff08;浮动窗口&#xff09;中心部件添加图片对话框模态对话框非模态对话框标准对话框&#xff08;信息对话框&#xff09;错误对话框信息对话框提问对话框警告对话框 其他标准对话框颜色对话框文件对话框 字体对话框 登录窗口布局按…

《重构》读书笔记【第1章 重构,第一个示例,第2章 重构原则】

文章目录 第1章 重构&#xff0c;第一个示例1.1 重构前1.2 重构后 第2章 重构原则2.1 何谓重构2.2 两顶帽子2.3 为何重构2.4 何时重构2.5 重构和开发过程 第1章 重构&#xff0c;第一个示例 我这里使用的IDE是IntelliJ IDEA 1.1 重构前 plays.js export const plays {&quo…

将 MinIO 与 Keycloak OIDC 集成

Keycloak是一种单点登录解决方案。使用Keycloak&#xff0c;用户使用Keycloak而不是MinIO进行身份验证。如果没有Keycloak&#xff0c;您将不得不为每个用户创建一个单独的身份 - 从长远来看&#xff0c;这将很麻烦。您需要一个集中身份解决方案来管理 MinIO 的身份验证和授权。…