RTP协议浅解
在工作中经常与RTP打交道,在这里总结一点工作经常使用和注意的地方。
基本概念
RTP(Real-time Transport Protocol,实时传输协议)是一种用于在IP网络中传输多媒体数据的协议,由IETF(Internet Engineering Task Force)制定。其主要功能是提供端到端的实时数据传输服务,特别适用于音频、视频等多媒体数据的传输。
RTP协议基于多播或单播进行连续传输媒体数据的实时传输服务,RTP通常与RTCP配合使用,RTP主要负责实时数据的传输,而RTCP则负责监控传输质量、提供控制信息和管理会话参与者的信息,RTCP通过周期性地发送统计信息和反馈报告,帮助RTP调整传输速率和解决网络拥塞问题。由于TCP需要较多的开销不适合传输实时数据,一般采用RTP/UDP来传输实时媒体数据(也可以用UDP/TCP)。
RTP传输H264数据的过程如下:
RTP封装H264
RTP报文
RTP报文由两部分组成:报头+有效负载(H264数据),RTP报头格式如下:
V:版本号,2比特,用来标志使用的RTP版本;
P:填充位,1比特,如果P=1,则在报文尾部将填充一个或多个额外的八位组,它们不是有效载荷的一部分。
X:扩展位,1比特,如果X=1,则在RTP头部后跟有扩展头(有些多路视频源的场合可以使用)。
CC:CSRC计数器,4比特,指示CSRC标识符的个数。主要用于多流组合传播,比如说有x个人发送RTP到网关边缘,边缘会把这x个RTP转发给另一个边缘,然后解码出原始流。
M:标记位,1比特,对于视频来说表示一帧的结束,对于音频标记会话的开始。
PT:载荷类型,7比特,标识RTP载荷的类型。
SN:序列号,16比特,标识发送这发送的RTP报文的序列号,每发送一个报文,序列号+1,接收者通过序列号检测报文丢失情况,重新排序报文,恢复数据。
TimeStamp:时间戳,32位,记录了包中数据的第一个字节的采样时刻,即使没有没有信号发送时,时间戳的数值也会不断增加,时间戳主要作用是计算延迟和延迟抖动,进行同步控制这些。
SSRC:同步源标识符,32位,指RTP包的来源,同一个会话不会有两个一样的,MD5算法生成。
CSRC:特约信源,每一个占32位,可以有0~15个,标识了包含在RTP报文有效载荷中的所有有贡献的源。
#ifndef _rtp_header_h_
#define _rtp_header_h_
#include
/*
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 |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
#define RTP_VERSION 2 // RTP version field must equal 2 (p66)
typedef struct _rtp_header_t
{
uint32_t v:2; /* protocol version */
uint32_t p:1; /* padding flag */
uint32_t x:1; /* header extension flag */
uint32_t cc:4; /* CSRC count */
uint32_t m:1; /* marker bit */
uint32_t pt:7; /* payload type */
uint32_t seq:16; /* sequence number */
uint32_t timestamp; /* timestamp */
uint32_t ssrc; /* synchronization source */
} rtp_header_t;
#define RTP_V(v) ((v >> 30) & 0x03) /* protocol version */
#define RTP_P(v) ((v >> 29) & 0x01) /* padding flag */
#define RTP_X(v) ((v >> 28) & 0x01) /* header extension flag */
#define RTP_CC(v) ((v >> 24) & 0x0F) /* CSRC count */
#define RTP_M(v) ((v >> 23) & 0x01) /* marker bit */
#define RTP_PT(v) ((v >> 16) & 0x7F) /* payload type */
#define RTP_SEQ(v) ((v >> 00) & 0xFFFF) /* sequence number */
#endif /* !_rtp_header_h_ */
H264
H264是一种由国际电信联盟和国际话组织联合开发的视频压缩标准,它旨在通过高效的压缩算法实现高质量视频的传输和存储,同时减低所诉的带宽和存储空间。
H264有两种组织格式
1. AVCC格式,MPEG-4格式,字节对齐,主要用于mp4/flv/mkv等封装中,AVCC格式使用NALU长度进行分割,在封装或者流的头部包含extradata信息,exteadata中含有NALU长度的字节数以及SPS和PPS信息。
2. Annex-B格式,MPEG-2 transport stream format格式,ts流中常用这种格式,本文也主要介绍这一种格式,Annex-B格式使用start code进行分割,start code有两种,一种为0x00 00 01,另一种0x00 00 00 01。
接收到SPS+PPS+SEI后表示这一帧为IDR帧。视频由frame组成,frame分为IDR帧、I帧、B帧、P帧,在推流H264视频时首先发送的就是一帧IDR帧,因为接收到IDR帧就得到了PPS和SPS,就可以知道视频的信息知道怎么解码,后面可以全是P帧,可以有B帧,可以有I帧,直播一般只有IDR帧、I帧和P帧。
NALU组成: [start code] [NAL头] [NAL payload]
看一下NAL头部的定义:
F : 1比特,一般为0。
NRI:2比特,指示NALU单元的重要性,一般不太关心。
TYPE:5比特,表示这个NALU单元的类型。
RTP三种封包模式
IP协议中MTU最大长度为1500字节:IP报头:20字节,UDP报头:8字节,RTP头12字节,NALU的长度不可以超过1460字节(RTP负载的NALU长度)。如果用TCP的话TCP报头20字节,自己算,就是一共不可以超过1500字节。
1. 单一封包模式:NALU可以放入一个RTP中不超过1500字节的可以使用这种模式,打包时除去start code即可,把其他的数据放入RTP包中即可。
2. 组合封包模式:当NALU长度很小,可以把几个NALU放入一个RTP包中且不超过1500字节的可以使用这种模式。常用的是STAP-A模式,这种模式同样去掉start code,然后在第一个NAL payload前加入 STAP-A头(1字节)紧接着是第一个NAL payload长度(2字节)然后紧接 NAL payload 。
RTP header + STAP-A头 + 第一个NAL payload长度 + 第二个NAL payload + 第二个NAL payload长度 + 第二个NAL payload ....
3. 分片封包模式:当NALU的长度超过了MTU时,使用分片的模式。在封装时需要使用FU indicator + FU header 替换NALU原来的头部。这种方式称为Fragmentation Units(FUs)。
FU indicator + FU header + FU payload ....
FU indicator:
F、NRI与NAL头相同含义,TYPE固定28。
FU Header
S:S = 1 表示分片打包的第一个包。
E:E = 1 表示分片打包的最后一个包。
TYPE:如果是H264的话就是NALU的TYPE。
FU payload 为去掉起始码和头部的NAL的NAL payload
RTP封装H264解析
RTP封装常用的就是单一封包和分片封包模式,组合基本不用。
RTP序号每发送一个包就 + 1 ,同一个NALU的分片的RTP包时间戳不变。
H264类型:
0没有定义1~23NAL单元 单个NAL单元包24STAP - A 单一时间的组合包25STAP - B 单一时间的组合包26MTAP16 多个时间的组合包27MTAP24 多个时间的组合包28FU - A 分片的单元29FU - B 分片的单元30~31没有定义解析时按照每一位去解析即可,最简单的解析方式使用wireshark去抓包一个字节一个去解析。