H264基础及RTP分包解包

2023-05-16

一、 H.264基础概念

SODB: 数据比特串-->最原始的编码数据

RBSP: 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐。

EBSP: 扩展字节序列载荷-- >在RBSP基础上填加了仿校验字节(0X03)它的原因是: 在NALU加到Annexb上时,需要填加每组NALU之前的开始码 StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示 ox000001.
为了使NALU主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉。 也称为脱壳操作。

H.264的功能分为两层,视频编码层(VCL)和网络提取层(NAL)
VCL数据即被压缩编码后的视频数据序列。在VCL数据要封装到NAL单元中之后,才可以用来传输或存储。NAL单元格式如下图:

H.264 的编码视频序列包括一系列的NAL 单元,每个NAL 单元包含一个RBSP,见表1。编码片(包括数据分割片IDR片)和序列RBSP 结束符被定义为VCL NAL 单元,其余为NAL 单元。典型的RBSP 单元序列如图2 所示。每个单元都按独立的NAL 单元传送。单元的信息头(一个字节)定义了RBSP 单元的类型,NAL 单元的其余部分为RBSP 数据。

NAL单元

每个NAL单元是一个一定语法元素的可变长字节字符串,包括包含一个字节的头信息(用来表示数据类型),以及若干整数字节的负荷数据。一个NAL单元可以携带一个编码片、A/B/C型数据分割或一个序列或图像参数集。

NALU 头由一个字节组成, 它的语法如下:

NAL单元按RTP序列号按序传送。其中,T为负荷数据类型,占5bit;R为重要性指示位,占2个bit;最后的F为禁止位,占1bit。具体如下:
(1)NALU类型位
可以表示NALU的32种不同类型特征,类型1~12是H.264定义的,类型24~31是用于H.264以外的,RTP负荷规范使用这其中的一些值来定义包聚合和分裂,其他值为H.264保留。
(2)重要性指示位
用于在重构过程中标记一个NAL单元的重要性,值越大,越重要。值为0表示这个NAL单元没有用于预测,因此可被解码器抛弃而不会有错误扩散;值高于0表示此NAL单元要用于无漂移重构,且值越高,对此NAL单元丢失的影响越大。
(3)禁止位
编码中默认值为0,当网络识别此单元中存在比特错误时,可将其设为1,以便接收方丢掉该单元,主要 用于适应不同种类的网络环境(比如有线无线相结合的环境)。

264常见的帧头数据为:

00 00 00 01 67 (SPS)

00 00 00 01 68 (PPS)

00 00 00 01 65 ( IDR 帧)

00 00 00 01 61 (P帧)

等等,那么他们代表的意思是什么呢?

上述的67,68,65,61,还有41等,都是该NALU的识别级别。

F:禁止为,0表示正常,1表示错误,一般都是0

NRI:重要级别,11表示非常重要。

TYPE:表示该NALU的类型是什么,

见下表,由此可知7为序列参数集(SPS),8为图像参数集(PPS),5代表I帧。1代表非I帧。

由此可知,61和41其实都是P帧(type值为1),只是重要级别不一样(它们的NRI一个是11BIN,一个是10BIN)

NALU类型是我们判断帧类型的利器,从官方文档中得出如下图:

H264(NAL简介与I帧判断)

我们还是接着看最上面图的码流对应的数据来层层分析,以00 00 00 01分割之后的下一个字节就是NALU类型,将其转为二进制数据后,

解读顺序为从左往右算,如下:

(1)第1位禁止位,值为1表示语法出错

(2)第2~3位为参考级别

(3)第4~8为是nal单元类型

例如上面00000001后有67,68以及65

其中0x67的二进制码为:

0110 0111

4-8为00111,转为十进制7,参考第二幅图:7对应序列参数集SPS

其中0x68的二进制码为:

0110 1000
4-8为01000,转为十进制8,参考第二幅图:8对应图像参数集PPS

其中0x65的二进制码为:

011 00101

4-8位为00101,转为十进制5,参考第二幅图:5对应IDR图像中的片(I帧)

所以判断是否为I帧的算法为:

(NALU类型 & 0001 1111) = 5 即 (NALU类型 & 31) = 5
比如0x65 & 31 = 5

二、RTP打包发送H264之封包详解

RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,

H264的码流结构

1、单个NAL包单元

12字节的RTP头后面的就是音视频数据,比较简单。一个封装单个NAL单元包到RTP的NAL单元流的RTP序号必须符合NAL单元的解码顺序。
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.
对于一个原始的 H.264 NALU 单元常由[Start Code] [NALU Header] [NALU Payload]三部分组成, 其中 Start Code 用于标示这是一个
NALU 单元的开始, 必须是 "00 00 00 01" 或 "00 00 01", NALU 头仅一个字节, 其后都是 NALU 单元内容.

打包时去除 "00 00 01" 或 "00 00 00 01" 的开始码, 把其他数据封包的 RTP 包即可.

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |F|NRI|  type   |                                               |
  +-+-+-+-+-+-+-+-+                                               |
  |                                                               |
  |               Bytes 2..n of a Single NAL unit                 |
  |                                                               |
  |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                               :...OPTIONAL RTP padding        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

如有一个 H.264 的 NALU 是这样的:

[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]

这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容.

封装成 RTP 包将如下:

[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]

即只要去掉 4 个字节的开始码就可以了.

2、组合封包模式

当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.

3、FU-A的分片格式

数据比较大的H264视频包,被RTP分片发送。12字节的RTP头后面跟随的就是FU-A分片:
而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).

   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   |
  +---------------+

FU指示字节的类型域Type=28表示FU-A。。NRI域的值必须根据分片NAL单元的NRI域的值设置。

FU header的格式如下:

  +---------------+
  |0|1|2|3|4|5|6|7|
  +-+-+-+-+-+-+-+-+
  |S|E|R|  Type   |
  +---------------+

S: 1 bit 当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0。

E: 1 bit 当设置成1, 结束位指示分片NAL单元的结束,即, 荷载的最后字节也是分片NAL单元的最后一个字节。
当跟随的FU荷载不是分片NAL单元的最后分片,结束位设置为0。

R: 1 bit
保留位必须设置为0,接收者必须忽略该位。

Type: 5 bits
NAL单元荷载类型定义见下表

表1. 单元类型以及荷载结构总结

  Type   Packet      Type name                       
  ---------------------------------------------------------
  0      undefined   -
  1-23   NAL unit    Single NAL unit packet per H.264  
  24     STAP-A      Single-time aggregation packet    
  25     STAP-B      Single-time aggregation packet    
  26     MTAP16      Multi-time aggregation packet     
  27     MTAP24      Multi-time aggregation packet     
  28     FU-A        Fragmentation unit                
  29     FU-B        Fragmentation unit                 
  30-31  undefined   -

4、拆包和解包

拆包:当编码器在编码时需要将原有一个NAL按照FU-A进行分片,原有的NAL的单元头与分片后的FU-A的单元头有如下关系:

原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位为FU header的后五位,

FU indicator与FU header的剩余位数根据实际情况决定。

解包:当接收端收到FU-A的分片数据,需要将所有的分片包组合还原成原始的NAl包时,FU-A的单元头与还原后的NAL的关系如下:

还原后的NAL头的八位是由FU indicator的前三位加FU header的后五位组成,即:

nal_unit_type = (fu_indicator & 0xe0) | (fu_header & 0x1f)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

H264基础及RTP分包解包 的相关文章

随机推荐

  • Android Volley源码分析(1)

    1 Volley newRequestQueue 我们从Volley中RequestQueue的初始化入手 xff0c 开始进行分析 应用利用Volley java的静态方法 xff0c 获取RequestQueue xff0c 开启使用V
  • Android Volley源码分析(2)

    1 RequestQueue的add接口 我们从RequestQueue的add接口入手 xff1a public lt T gt Request lt T gt add Request lt T gt request Tag the re
  • 删除git remote

    目前项目中有两个remote git remote v origin https github com test test git fetch origin https github com test test git push test
  • 当 git pull 碰到拒绝合并无关历史

    问题描述 很久之前在 github 上建了个仓库 xff0c 里面只有一个 README md 文件 突然有天 xff0c 我想把本地的一个项目传上去 xff0c 然后就碰到了这样一个问题 xff01 当我 添加远程仓库 后准备提交代码时
  • rxjava2源码解析(一)基本流程分析

    从基本使用入手 首先随便写一个rxjava2的基本用法 xff0c 我们根据这个简单的示例来看看rxjava2整个流程是什么样的 Observable create new ObservableOnSubscribe lt String g
  • rxjava2源码解析(二)线程切换分析

    使用方法 还是先从最基本的使用开始看 xff1a Observable create new ObservableOnSubscribe lt String gt 64 Override public void subscribe Obse
  • rxjava2源码解析(三)observeOn线程池原理分析

    observeOn 还是先说observeOn 直接看源码 xff1a public ObservableObserveOn ObservableSource lt T gt source Scheduler scheduler boole
  • RxJava 2.x 源码分析 之 FlatMap

    FlatMap 官方定义 xff1a 把被观察者发射出去的事件转化成新的子被观察者 xff0c 然后把这些发射量展开平铺后统一放到一个被观察者中 官方文档 简单来讲就是把被观察者每次发射的事件转化成一个子被观察者 xff0c 然后通过合并
  • Transformer 在RxJava中的使用

    Transformer 用途 Transformer xff0c 顾名思义是转换器的意思 早在 RxJava1 x 版本就有了Observable Transformer Single Transformer和Completable Tra
  • 彻底理解kubernetes CNI

    kubernetes各版本离线安装包 CNI接口很简单 xff0c 特别一些新手一定要克服恐惧心里 xff0c 和我一探究竟 xff0c 本文结合原理与实践 xff0c 认真读下来一定会对原理理解非常透彻 环境介绍 我们安装kubernet
  • 将本地项目上传到远程Git服务器

    1 先进入项目文件夹 通过命令 git init 把这个目录变成git可以管理的仓库 git init 2 把文件添加到版本库中 xff0c 使用命令git add 添加到暂存区里面去 xff0c 小数点 34 34 意为添加文件夹下的所有
  • 解决Manifest merger failed : Attribute application@appComponentFactory

    在将butterknife升级到10 0 0的时候遇到问题 xff0c 编译无法通过 Manifest merger failed Attribute application 64 appComponentFactory value 61
  • bindService不能触发onServiceConnected方法的原因

    在android项目中用到AIDL xff0c bindService service connection BIND AUTO CREATE 之后一直不调用 connection中的onServiceConnected方法 可能原因1 1
  • Android应用被浅杀和深杀

    onTaskRemoved 方法在当用户移除应用的一个Task栈时被调用 也就是当用户在最近任务界面把该应用的一个task划掉时 xff0c 或者在最近任务界面进行清理时 这两种情况下onTaskRemoved 都会被调用 xff0c 但在
  • java.lang.OutOfMemoryError: Could not allocate JNI Env

    最近有一些OOM的错误上报 java lang OutOfMemoryError Could not allocate JNI Env 极少量的 java lang OutOfMemoryError pthread create 1040K
  • RecycleView4种定位滚动方式演示

    概述 相信大家在项目中使用RecyclerView时 xff0c 经常会遇到这样的需求 xff1a 将RecyclerView滑动到指定位置 xff0c 或者检索RecyclerView的某一项 xff08 各个项的高度不确定 xff09
  • Java/Android中的引用类型及WeakReference应用实践

    一 背景 一般意义上而言 xff0c Java Android中的引用类型包括强引用 软引用 弱引用 虚引用 不同的引用类型具有各自适用的应用场景 xff0c 并与JVM的GC直接相关 作为Java Android中的引用类型之一 xff0
  • Android 网络切换 发送多次广播问题

    最近发现做项目监听网络切换广播 xff0c 根据网络条件切换一些设置 测试发现每次3G WIFI 或者WIFI到3G xff0c 网络切换的广播都会发出多次 比如3G gt WIFI 会发送三个广播 1 连接wifi 2 关闭手机网络 3
  • 在draw.io中创建容器形状

    draw io中的任何形状都可以变成容器 包含其他多个形状的形状 在绘图区域上移动容器时 xff0c 位于其中的形状将随容器移动 容器可用于指示流程图中的步骤或子过程组 xff0c 数据集合 xff0c 树形图或任何其他具有层次结构的图中的
  • H264基础及RTP分包解包

    一 H 264基础概念 SODB 数据比特串 xff0d xff0d xff1e 最原始的编码数据 RBSP 原始字节序列载荷 xff0d xff0d xff1e 在SODB的后面填加了结尾比特 xff08 RBSP trailing bi