H264的RTP载荷格式
H264中对于数据的存储分为两层,分别是
VCL: video coding layer 视频编码层
这是h264中block, macro block 以及 slice级别的定义,目的是为了独立网络传输,进行高效的编解码NAL: network abstraction layer 网络提取层
NAL层的不同是为了根据不同的网络把VCL数据进行打包成不同的格式,进行传输
NAL
H264中的数据分组有头部(譬如: pkt->data),解码器可以很方便的检测到NAL的分界,依次去除NAL进行解码,但为了节省码流,NAL没有在数据头部设立特定元素,如果编码数据存储在介质上,NAL依次紧密相连(这时就无法通过对象取读取一个nal数据了),就无法区分出不同的nal单元,为了解决该问题,存储的时候会在每一个nal前添加0x000001或0x000000。
但数据内部也可能出现0x000001或0x000000,为此nal中规定不能出现0x000001或0x000000,源数据需要进行如下变换:
原数据 | 替换数据
-|-|
0X000000 | 0X00000300 |
0X000001 | 0X00000301 |
0X000002 |0X00000302 |
0X000003 | 0X00000303 |
在网络传输过程中, NAL中的数据被分成NALU(nal unit)单元,然后由RTP进行封装传输
如上图所示,VCL的数据经过上图所示
- 在SODB的后面添加了结尾若干比特"0",以用字节对齐成了RBSP
- 在RBSP的基础上,对数据进行循环检测替换上述的0x000000 ~ 0x000002,避免nal单元边界被破坏,成了EBSP
- 在EBSP上添加一个字节的header后成了 NALU,
NALU
上述EBSP添加的一个字节的header的结构如下所示:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F:0表示payload 内容没有错误,1表示payload中的内容可能有错误内容或语法错误。
NRI:用来表示当前NALU单元的重要性(从00 ~11),00最低,表示这不是一个用于帧间预测的参考帧,可以丢弃。
Type:表示当前NALU的类型,这些类型如下表所示
Table 1. Summary of NAL unit types and the corresponding packet
types
NAL Unit Packet Packet Type Name Section
Type Type
-------------------------------------------------------------
0 reserved -
1-23 NAL unit Single NAL unit packet 5.6
24 STAP-A Single-time aggregation packet 5.7.1
25 STAP-B Single-time aggregation packet 5.7.1
26 MTAP16 Multi-time aggregation packet 5.7.2
27 MTAP24 Multi-time aggregation packet 5.7.2
28 FU-A Fragmentation unit 5.8
29 FU-B Fragmentation unit 5.8
30-31 reserved
再具体一点如下所示:
nal_unit_type NAL类型
1 非IDR图像中不采用数据划分的片段
2 非IDR图像中A类数据划分片段
3 非IDR图像中B类数据划分片段
4 非IDR图像中C类数据划分片段
5 IDR图像的片
6 补充增强信息单元(SEI)
7 序列参数集
8 图像参数集
9 分界符
10 序列结束
11 码流结束
12 填充
13…23 保留
24…31 不保留(RTP打包时会用到)
比较重要的是
nal_unit_type = 7 RTP 负载的是序列参数集
nal_unit_type = 8 RTP 负载的是图像参数集
nal_unit_type = 5 IDR图像的片 (立即刷新图像,I帧给P帧和B帧作为参考)
23之前说明nalu中的数据类型,而超过23的时候RTP打包开始用到,决定NALU 如何打包进 RTP。因为NALU 大小有可能远远小于RTP payload,也有可能正好等于RTP payload,或者远大于RTP payload ,那么NALU 就需要再次拆分包,即一帧拆开发送,拆开发送时,rtp的markbit就可以用上来判断当前这个单元是否为一帧的结束,nalu被拆分的时候,timestamp是不变的
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
聚合包 nal_unit_type = 24~27
本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27;
下图的单时间聚合包
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Data |
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 Data |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 7. An example of an RTP packet including an STAP-A
containing two single-time aggregation units
分片单元 nal_unit_type = 28 或者 29
将NALU 单元拆分到多个RTP包中发送 典型的就是FU-A或者FU-B
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 14. RTP payload format for FU-A
其中FU indicator的格式如下
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
S:1表示第一包
E:1表示是最后一个包
R:1表示中间
Type:类型
RTP负载PS
H264还有另一种数据组织和传输方式, 如上图,其中:
- ES流(Elementary Stream原始流),是音、视频信号经过编码器之后或数据信号的基本码流。只包含一种内容。每个ES都由若 干个存取单元(AU)组成,每个视频或音频AU都是由头部和编码数据两部分组成,1个AU相当于编码的1幅视频图像或1个 音频帧 也可以说,每个AU实际上是编码数据流的显示单元,即相当于解码的1幅视频图像或1个音频帧的取样。
- PES(Paketized Elementary Stream)是ES经过打包后的码流,长度可变。视频一般一帧一个包,音频一般不超过64KB.
- PTS--(presentation time stamp)显示时间戳,表示显示单元出现在系统目标解码器(H.264、MJPEG等)的时间。
- DTS--(Decoding Time Stamp)解码时间戳,表示将存取单元全部字节从解码缓存器移走的时间。
- PTS和DTS打在PES包头内,是解决音视频同步,防止解码器输入缓存上溢或下溢的关键。每一个pes header都包含pts和dts,是相对SCR(系统参考)的时间戳,以90000为单位,系统时钟频率(H264采样频率)
最后由rtp将ps包传输出去,RFC2250中建议96 表示PS 封装,建议97 为MPEG-4,建议98 为H264
REF
H264 NALunit
RTP包 NALU FU-A等之间的关系
WEBRTC 接收H264 RTP数据流小结
RTP PS PES ES H264协议学习
原文地址:https://www.cnblogs.com/ishen/p/12021043.html
- 关于奇怪的并行进程分析(二) (r6笔记第46天)
- Linux性能分析工具与图形化方法
- MySQL和Oracle中的隐式转换(r6笔记第45天)
- R语言的数据导入与导出(write.table,CAT)
- gqlplus的简单使用(r6笔记第43天)
- Java基础-21(01)总结字符流,IO流编码问题,实用案例必做一遍
- zabbix中配置dg的监控(r6笔记第62天)
- Apache ActiveMQ 远程代码执行漏洞 (CVE-2016-3088)分析
- mysql5.5与mysq 5.6中禁用innodb引擎的方法
- 缓慢的update语句性能分析(r6笔记第61天)
- 一个dg警告发现的硬件问题 (r6笔记第60天)
- mysql几种存储引擎介绍
- Java基础-21(02)总结字符流,IO流编码问题,实用案例必做一遍
- DeDeCMS v5.7 密码修改漏洞分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 开发RTSP协议视频平台EasyNVR采用ffmpeg集成fdk-aac报no member named “encoderDelay”
- 微信小程序开发实战(17):用画布画一张笑脸
- PHP中Session ID的实现原理分析和实例解析
- 【好文推荐】黑莓OS手册是如何详细阐述底层的进程和线程模型的?
- 简析Spark Streaming/Flink的Kafka动态感知
- 微信小程序开发实战(19):页面导航
- Kotlin:02-函数的声明
- 「MoreThanJava」Day 5:面向对象进阶—继承详解
- 要点1:指针、数组和复合字面量
- akka-typed(9) - 业务分片、整合,谈谈lagom, 需要吗?
- com-IFileDlg 进行文件的 打开或者保存
- 哆啦A梦?不好记!安利一下Prometheus这款开源的企业监控报警平台
- Kotlin:03-变量、常量、数据类型
- nginx工程师,需要上承天命,下召九幽
- 新版视频流媒体平台EasyNVR如何在前端显示当前页面所在位置?