rtp协议详解

rtp协议详解RTP的头部格式版本号(V):2比特,用来标志使用的RTP版本。填充位(P):1比特,如果该位置位,则该RTP包的尾部就包含附加的填充字节。扩展位(X):1比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。CSRC计数器(CC):4比特,含有固定头部后面跟着的CSRC的数目。标记位(M):1比特,该位的解释由配置文档(Profile)来承担….

大家好,又见面了,我是你们的朋友全栈君。

一 rtp概述

RTP全名是Real-time Transport Protocol(实时传输协议)。它是IETF提出的一个标准,对应的RFC文档为RFC3550。RFC3550不仅定义了RTP,而且定义了配套的相关协议RTCP(Real-time Transport Control Protocol,即实时传输控制协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。
 

rtp协议详解

rtp协议详解

                                                        RTP的头部格式

前12字节是固定的,CSRC可以有多个或者0个。

1)V:RTP协议的版本号,占2位,当前协议版本号为2

2)P:填充标志,占1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分

3)X:扩展标志,占1位,如果X=1,则在RTP报头后跟有一个扩展报头

4)CC:CSRC计数器,占4位,指示CSRC标识符个数

5)M:标志,占1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束;对于音频,标记会话的开始。

6)PT(payload type):有效荷载类型,占7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流,这样便于客户端进行解析。

7)序列号:占16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。当出现网络抖动的情况可以用来对数据进行重新排序。序列号的初始值是随机的,同时音频包和视频包的sequence是分别计数的。

8)时戳(Timestamp):占32位,必须使用90kHZ时钟频率(程序中的90000)。时戳反映了该RTP报文的第一个八位组的采样时刻。接受者使用时戳来计算延迟和延迟抖动,并进行同步控制。可以根据RTP包的时间戳来获得数据包的时序。

9)同步信源(SSRC)标识符:占32位,用于标识同步信源。同步信源是指产生媒体流的信源,他通过RTP报头中的一个32为数字SSRC标识符来标识,而不依赖网络地址,接收者将根据SSRC标识符来区分不同的信源,进行RTP报文的分组。

10)提供信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个CSRC。每个CSRC标识了包含在RTP报文有效载荷中的所有提供信源。

提供信源用来标识对一个RTP混合器产生的新包有贡献的所有RTP包的源。是指当混合器接收到一个或多个同步信源的RTP报文后,经过混合处理产生一个新的组合RTP报文,并把混合器作为组合RTP报文的SSRC,将原来所有的SSRC都作为CSRC传送给接收者,是接受者知道组成组合报文的各个SSRC。

二 rtp码流

                             

rtp载荷h264媒体流:rtp协议头和h264码流

rtp协议详解

 rtp头后是rtp载荷,rtp载荷第一个字节格式和NALU头一样。

rtp协议详解

SODB(String Of Data Bits):最原始的编码数据, 长度不一定是8的倍数,此时需要对齐. 
RBSP:在SODB后添加结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐。
EBSP:在 RBSP 的基础上增加了防止伪起始码字节(0X03)
NALU是对RBSP的封装,而RTP是对NALU的封装。

rtp协议详解

F和NRI也跟NALU头一样,只有Type有些不一样:拓展24 – 31

0     没有定义; 1-23  NAL单元   单个 NAL 单元包.

  24    STAP-A   单一时间的组合包;  25    STAP-B   单一时间的组合包

  26    MTAP16   多个时间的组合包;  27    MTAP24   多个时间的组合包

  28    FU-A     分片的单元;  29    FU-B     分片的单元;  30-31 没有定义

1)   单个NAL单元包:荷载中只包含一个NAL单元。NAL头类型域等于原始 NAL单元(NALU)类型,即Type在范围1到23之间。

rtp协议详解

对于NALU(NAL单元)的长度小于MTU大小的包,一般采用单一NAL单元模式

定义在此的NAL单元包必须只包含一个。RTP序号必须符合NAL单元的解码顺序。这种情况下,NAL单元的第一字节和RTP荷载头第一个字节重合。如上图所示。

对于一个原始H264的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包即可。

如有一个 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)组合包:本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A(STAP-A)单时间聚合包类型B(STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16),多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A,STAP-B,MTAP16,MTAP24的NAL单元类型号(Type)分别是24 25 26 27

rtp协议详解

 当NALU的长度特别小时,可以把几个NALU单元封在一个RTP包中。这种模式下,有多个NALU头和NALU载荷。

3)分片包:用于分片单个NAL单元到多个RTP包。现存两个版本FU-A,FU-B,用NAL单元类型(Type)28 29标识

常用的打包时的分包规则:如果小于MTU采用单个NAL单元包,如果大于MTU就采用FUs分片方式

rtp协议详解

        当NALU的长度超过MTU时,就必须对NALU单元进行分片封包,也称为Fragmentation Units (FUs)NAL单元的一个分片由整数个连续NAL单元字节组成。每个NAL单元字节必须正好是该NAL单元一个分片的一部分。相同NAL单元的分片必须使用递增的RTP序号连续顺序发送(第一和最后分片之间没有其他的RTP包)。同时,NAL单元必须按照RTP顺序号的顺序装配。STAPs,MTAPs不可以被分片。 FUs不可以嵌套。 即, 一个FU 不可以包含另一个FU。

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

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

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

Type(类型):5 bit, 是NAL Header中的Type。

打包时,原始的NAL头的前三位为FU indicator的前三位,原始的NAL头的后五位(Type)为FU header的后五位(Type)。

取一段rtp码流分析如下:

80 60 01 0f 00 0e 10 00 00 00 00 00 7c 85 88 82 €`……….|???

00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 …??.;.>.?.+’&?

89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.

84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.

17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$

       前12字节是RTP Header,7c是FU indicator,85是FU Header

FU indicator(0x7C)和FU Header(0x85)二进制  0111 1100 1000 0101

按顺序解析如下:

0                            是F

11                          是NRI

11100                    是FU Type,这里是28,即FU-A

1                            是S,Start,说明是分片的第一包

0                            是E,End,如果是分片的最后一包,设置为1,这里不是

0                            是R,Remain,保留位,总是0

00101                    是NAL Type,这里是5,说明是关键帧

打包时,FU indicator的F、NRI是NAL Header中的F、NRI,Type是28(FU-A);FU Header的S、E、R分别按照分片起始位置设置,Type是NAL Header中的Type。

解包时,取FU indicator的前三位和FU Header的后五位,即0110 0101(0x65)为NAL类型。

4)SPS/PPS

一个frame分割成多个slice,一个slice分割成多个宏块。一个Slice编码之后被打包进一个NAL单元。不过NAL单元除了容纳Slice编码的码流外,还可以容纳其他数据,比如序列参数集SPS。
 

NALU头由一个字节组成:forbidden_zero_bit(1bit) + nal_ref_idc(2bit) + nal_unit_type(5bit)

forbidden_zero_bit: 禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
nal_ref_idc:nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。

nal_unit_type:NALU常用类型取值如下表所示:

 5     IDR图像中的片
 6     补充增强信息单元(SEI)
 7     SPS
 8     PPS
 9     分割符
10    序列结束符
11    流结束符
12    填充数据

H.264码流第一个 NALU是 SPS(序列参数集Sequence Parameter Set)

H.264码流第二个 NALU是 PPS(图像参数集Picture Parameter Set)

SPS和PPS都是特殊的NALU。一个MP4文件只有一个SPS,但是有很多PPS,SPS必须在所有NALU的最开头。

H.264码流第三个 NALU 是 IDR(即时解码器刷新),第一个I帧称为IDR帧,IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码。解码器收到IDR帧时,将所有的参考帧队列丢弃,把所有的PPS和SPS参数进行更新。

rtp协议详解rtp协议详解

rtp协议详解

H.264编码时,在每个NAL前添加起始码 0x000001,解码器在码流中检测到起始码,当前NAL结束。为了防止NAL内部出现0x000001的数据,h.264又提出’防止竞争 emulation prevention”机制,在编码完一个NAL时,如果检测出有连续两个0x00字节,就在后面插入一个0x03。当解码器在NAL内部检测到0x000003的数据,就把0x03抛弃,恢复原始数据。
   0x000000  ———–>  0x00000300
   0x000001  ———–>  0x00000301
   0x000002  ———–>  0x00000302
   0x000003  ———–>  0x00000303

 总的来说,H264的码流的打包方式有两种,一种为annex-b byte stream format 的格式,这个是绝大部分编码器的默认输出格式,就是每个帧的开头的3~4个字节是H264的start_code,0x00000001或者0x000001;另一种是原始的NAL打包格式,就是开始的若干字节(1,2,4字节)是NAL的长度,而不是start_code,此时必须借助某个全局的数据来获得编 码器的profile,level,PPS,SPS等信息才可以解码。
 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153953.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 使用volatile关键字的场景

    使用volatile关键字的场景

  • 驱动开发必备硬件知识「建议收藏」

    驱动开发必备硬件知识「建议收藏」综述:在嵌入式领域,可分为硬件开发和软件开发。对于软件开发又可分为底层开发(模块驱动编写,uboot,内核),上层开发(应用,QT)。作为一名软件驱动开发的工程师,我们不需要去设计硬件的原理图,PCB。我们只需看懂硬件开发人员提供的硬件模块时序就行了,但是我们应该也需了解如下硬件知识。      一)处理器     1,可分为通用处理器(单片机,ARM),数字处理器(DSP),其他专用处理器…

  • Redis远程连接Redis客户端

    Redis远程连接Redis客户端在虚拟机的linux系统中一进入redis下的目录二启动redis服务三连接客户端四在windows本地中安装redis可视化软件五在linux关闭客户端六设置防火墙规则:我的因为已经设置过设置防火墙规则:firewall-cmd–zone=public–add-port=6379/tcp–permanent七设置密码:修改redis.conf…

  • executescalar mysql_ExecuteScalar()

    executescalar mysql_ExecuteScalar()ExecuteScalar()方法的作用是:执行查询,并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。它的返回值时object,若是想判断某条数据在数据库里存不存在便可使用该方法,//sql文privatestringm_str_variationInfo=@”SELECTvariationinfoMngnoFROMsellersandvariationmngtbl…

  • laravel 分页样式「建议收藏」

    laravel 分页样式「建议收藏」第一种//控制器$news=News::where(‘type_id’,1)->where(‘is_del’,1)->paginate(3);//前端@if($news->currentPage()==1)@else<li><ahref=”{{url(‘news?page=1’)}}”>首页</a></li><li><ahref=”{{$news->previousPag

  • 函数声明[通俗易懂]

    函数声明[通俗易懂]语法描述通过函数声明构造的函数是Function对象,所以拥有一切Function对象所有的属性,方法和行为。函数默认返回undefined,如果想返回其他值,函数必须使用return语句来返回

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号