文章目录
- 前言
- 一、RTP 协议
- 1、RTP 协议概述
- 2、RTP 工作机制
- 3、RTP 协议的报文结构
- 4、wireshark 抓取 RTP 报文
- 二、RTCP 协议
- 1、RTCP 协议概述
- 2、RTCP 工作机制
- 3、RTCP 数据报
- 4、wireshark 抓取 RTCP 报文
- 三、RTSP 和 RTP 的关系
- 四、易混淆概念
- 1、RTP over UDP 和 RTP over RTSP 有什么区别?
- 2、RTP over RTSP 是不是就是 RTP over TCP?
- 3、有 PS 流的封装格式吗?
- 4、RTP over TCP 需要 4 字节的头部
前言
目前,支持流媒体传输的协议主要有实时传输协议 RTP(Real-Time Transport Protocol)、实时传输控制协议 RTCP(Real-Time Transport Control Protocol)和实时流协议 RTSP(Real-Time Streaming Protocol)等。上文我们针对 RTSP 进行了详细的分析及讲解:RTSP协议抓包及讲解。
流媒体协议栈如下图所示:
本文对 RTP 及 RTCP 协议进行了讲解,以及使用 wireshark 抓包对其抓到的报文进行详细分析。
一、RTP 协议
1、RTP 协议概述
RTP(Real-time Transport Protocol)是用于 Internet 上针对多媒体数据流的一种传输层协议,RTP 协议和 RTP 控制协议 RTCP 一起使用。RTP 被定义为在一对一或一对多的传输情况下工作,其目的是提供时间信息和实现流同步。RTP 的典型应用建立在 UDP 上,但也可以在 TCP 或 ATM 等其他协议之上工
作。
RTP 不像 http 和 ftp 可完整的下载整个影视文件,它是以固定的数据率在网络上发送数据,客户端也是按照这种速度观看影视文件,当影视画面播放过后,就不可以再重复播放,除非重新向服务器端要求数据。
RTP 本身只保证实时数据的传输,并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 RTCP 提供这些服务。
2、RTP 工作机制
rtp 协议就是提供了时间标签,序列号以及其它的结构用于控制适时数据的流放。在流的概念中” 时间标签” 是最重要的信息。发送端依照即时的采样在数据包里隐蔽的设置了时间标签。在接受端收到数据包后,就依照时间标签按照正确的速率恢复成原始的适时的数据。不同的媒体格式调时属性是不一样的。
但是 rtp 本身并不负责同步,rtp 只是传输层协议,为了简化运输层处理,提高该层的效率。 将部分运输层协议功能(比如流量控制)上移到应用层完成。同步就是属于应用层协议完成的。它没有运输层协议的完整功能,不提供任何机制来保证实时地传输数据,不支持资源预留,也不保证服务质量。rtp 报文甚至不包括长度和报文边界的描述。同时 rtp 协议的数据报文和控制报文的使用相邻的不同端口,这样大大提高了协议的灵活性和处理的简单性。
rtp 协议和 udp 二者共同完成运输层协议功能。udp 协议只是传输数据包,不管数据包传输的时间顺序。rtp 的协议数据单元是用 udp 分组来承载的。在承载 rtp 数据包的时候,有时候一帧数据被分割成几个包具有相同的时间标签,则可以知道时间标签并不是必须的。而 udp 的多路复用让 rtp 协议利用支持显式的多点投递,可以满足多媒体会话的需求。
3、RTP 协议的报文结构
每一个 RTP 数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。
RTP 头格式如图所示:
开始 12 个八进制出现在每个 RTP 包中,而 CSRC 标识列表仅出现在混合器插入时。各段含义如下:
- 版本(V)
- 2 位, 标识 RTP 版本。
- 填充标识(P)
- 1 位,如设置填充位,在包尾将包含附加填充字,它不属于有效载荷。填充的最后一个八进制包含应该忽略的八进制计数。某些加密算法需要固定大小的填充字,或为在底层协议数据单元中携带几个 RTP 包。
- 扩展(X)
- 1 位,如设置扩展位,固定头后跟一个头扩展。
- CSRC 计数(CC)
- 4 位,CSRC 计数包括紧接在固定头后 CSRC 标识符个数。
- 标记(M)
- 1 位,标记解释由设置定义,目的在于允许重要事件在包流中标记出来。设置可定义其他标示位,或通过改变位数量来指定没有标记位。
- 载荷类型(PT)
- 7 位,记录后面资料使用哪种 Codec ,receiver 端找出相应的 decoder 解码出来
- 7 位,记录后面资料使用哪种 Codec ,receiver 端找出相应的 decoder 解码出来
- 序列号
- 16 位,序列号随每个 RTP 数据包而增加 1,由接收者用来探测包损失。序列号初值是随机的,使对加密的文本攻击更加困难。
- 时间戳
- 32 位,时间戳反映 RTP 数据包中第一个八进制数的采样时刻,采样时刻必须从单调、线性增加的时钟导出,以允许同步与抖动计算。时标可以让 receiver 端知道在正确的时间将资料播放出来。
由上图可知,如果只有序列号,并不能完整按照顺序的将 data 播放出来,因为如果 data 中间有一段是没有资料的,只有序列号的话会造成错误,需搭配上让它知道在哪个时间将 data 正确播放出来,如此我们才能播放出正确无误的信息。
- 32 位,时间戳反映 RTP 数据包中第一个八进制数的采样时刻,采样时刻必须从单调、线性增加的时钟导出,以允许同步与抖动计算。时标可以让 receiver 端知道在正确的时间将资料播放出来。
- SSRC
- 32 位,SSRC 段标识同步源。此标识不是随机选择的,目的在于使同一 RTP 包连接中没有两个同步源有相同的 SSRC 标识。尽管多个源选择同一个标识的概率很低,所有 RTP 实现都必须探测并解决冲突。如源改变源传输地址,也必须选择一个新 SSRC 标识以避免插入成环行源。
- CSRC 列表
- 0 到 15 项,每项 32 位。CSRC 列表表示包内的对载荷起作用的源。标识数量由 CC 段给出。 如超出 15 个作用源,也仅标识 15 个。CSRC 标识由混合器插入,采用作用源的 SSRC 标识。
RTP 用到的地方就是 PLAY ,服务器往客户端传输数据用 UDP 协议,RTP 是在传输数据的前面加了个 12 字节的头(描述信息)。
RTP 载荷封装设计本文的网络传输是基于 IP 协议,所以最大传输单元(MTU)最大为 1500 字节,在使用 IP/UDP/RTP 的协议层次结构的时候,这其中包括至少 20 字节的 IP 头,8 字节的 UDP 头,以及 12 字节的 RTP 头。这样,头信息至少要占用 40 个字节,那么 RTP 载荷的最大尺寸为 1460 字节。以 H264 为例,如果一帧数据大于 1460,则需要分片打包,然后到接收端再拆包,组合成一帧数据,进行解码播放。
4、wireshark 抓取 RTP 报文
抓取方法参考RTSP协议抓包及讲解。
我们可以参考上面的 RTP 协议的报文结构对下面一包 RTP 报文进行分析
二、RTCP 协议
1、RTCP 协议概述
RTCP(Real-time Transport Control Protocol 或 RTP Control Protocol 或简写 RTCP),实时传输控制协议,是实时传输协议(RTP)的一个姐妹协议。
注:RTP 协议和 RTP 控制协议(RTCP)一起使用,而且它是建立在 UDP 协议上的(一般用于视频会议)
2、RTCP 工作机制
当应用程序开始一个 rtp 会话时将使用两个端口:一个给 rtp,一个给 rtcp。rtp 本身并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 rtcp 提供这些服务。
RTCP 负责管理传输质量在当前应用进程之间交换控制信息。在 RTP 会话期间,各参与者周期性地传送 RTCP 包,包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料。因此,服务器可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。
RTP 和 RTCP 配合使用,能以有效的反馈和最小的开销使传输效率最佳化,故特别适合传送网上的实时数据。根据用户间的数据传输反馈信息,可以制定流量控制的策略,而会话用户信息的交互,可以制定会话控制的策略。
3、RTCP 数据报
在 RTCP 通信控制中,RTCP 协议的功能是通过不同的 RTCP 数据报来实现的,主要有如下几种类型:
- SR:发送端报告,所谓发送端是指发出 RTP 数据报的应用程序或者终端,发送端同时也可以是接收端。
- RR:接收端报告,所谓接收端是指仅接收但不发送 RTP 数据报的应用程序或者终端。
- SDES:源描述,主要功能是作为会话成员有关标识信息的载体,如用户名、邮件地址、电话号码等,此外还具有向会话成员传达会话控制信息的功能。
- BYE:通知离开,主要功能是指示某一个或者几个源不再有效,即通知会话中的其他成员自己将退出会话。
- APP:由应用程序自己定义,解决了 RTCP 的扩展性问题,并且为协议的实现者提供了很大的灵活性。
4、wireshark 抓取 RTCP 报文
我们可以参考上面的 RTCP 数据报对下面一包 RTP 报文进行分析
过滤 rtcp,可以看到下面第 397、401 包报文为接收端报告+源描述,第 473、475 包报文为发送端报告 + 源描述报文
三、RTSP 和 RTP 的关系
RTSP 与 RTP 最大的区别在于:RTSP 是一种双向实时数据传输协议,它允许客户端向服务器端发送请求,如回放、快进、倒退等操作。当然,RTSP 可基于 RTP 来传送数据,还可以选择 TCP、UDP、组播 UDP 等通道来发送数据,具有很好的扩展性,它是一种类似于 http 协议的网络应用层协议。作为一个应用层协议, RTSP 提供了一个可供扩展的框架,它的意义在于使得实时流媒体数据的受控和点播变得可能。RTP 是实时传输协议,一般不作为单独应用层协议处理;
我们来看看下图所示的例子:服务器端实时采集、编码并发送两路视频,客户端接收并显示两路视频。由于客户端不必对视频数据做任何回放、倒退等操作,可直接采用 UDP+RTP+组播实现。
rtsp 可基于 rtp 之上,比如常见的视频流传输过程:视频压缩文件 -> rtp 打包 -> 基于 udp 的 rtsp 网络传输;也可以不做成 rtp 包,直接基于 udp 传送,如视频压缩文件 -> 基于 udp 的 rtsp 网络传输。
具体协议内容可参看以下标准文档:
RTP/RTCP-------------------------RFC3550/RFC3551
RTSP -------------------------------RFC2326
四、易混淆概念
1、RTP over UDP 和 RTP over RTSP 有什么区别?
不一样,RTP over UDP 是 RTP 下层使用 udp 传输,RTP over RTSP 是指的用 rtsp 协议建立会话, 然后使用 RTP 协议传输数据;
2、RTP over RTSP 是不是就是 RTP over TCP?
不是。RTP over RTSP 是指的用用 rtsp 协议建立会话,然后使用 RTP 协议传输数据,至于下面用 udp 还是 tcp 是不确定的。
3、有 PS 流的封装格式吗?
PS,TS,ES 都有
rtp 和 rtsp 协议是应用层的,tcp 和 udp 是传输层的,所以只能说 rtp over tcp/udp,而且一般情况下一个点播需要 rtsp+rtp+rtcp 三个协议共同来实现。
4、RTP over TCP 需要 4 字节的头部
RTP,RTCP 数据和 RTSP 数据共享 TCP 数据通道,所以必须有一个标识来区别三种数据。RTP 和 RTCP 数据会以【 $符号+1 个字节的通道编号+2 个字节的数据长度】,共 4 个字节的前缀开始,RTSP 数据是没有前缀数据的。
RTP 数据和 RTCP 数据的区别在于第二个字节的通道编号,据观察 RTP 通道编号是偶数,RTCP通道编号是奇数。
两种发送方式的 RTP 包的打包方式和内容都是一样的,不同的地方主要是 rtsp 会话交互发送的信息还有通过 TCP 方式发送的 RTP 包前面再加四个字节的头,加四个字节头以及数据发送的代码如下:
static int send_rtp_packet(rtpclientparam_info* pinfo, unsigned char *buf,int len)
{
int sfd = 0;
int sendtonum;
struct sockaddr_in s;
if(!pinfo || !buf)
return 1;
if(pinfo->rtpovertcp)// rtp over tcp:需要另外添加 4 字节的头部
{
unsigned char tcp_pkt[len + 4];//tcp 包: 缓冲区
uint16 *intlvd_ch = (uint16 *)&tcp_pkt[2];//缓冲区长度
tcp_pkt[0] = '$';
tcp_pkt[1] = (unsigned char)(pinfo->dst_videoport);//在这里目标端口号就是 Channel
id
printf("tcp_pkt[1]=%d,port=%d\n",tcp_pkt[1],pinfo->dst_videoport);
*intlvd_ch = htons((uint16)len);//两个字节的缓冲区长度
memcpy(tcp_pkt + 4, buf, len);
sendtonum = send(pinfo->tcpfd, tcp_pkt, len+4, MSG_NOSIGNAL);
}
else
{
s.sin_family = AF_INET;
s.sin_addr.s_addr = pinfo->dstip;
sfd = video_sockethandle;
s.sin_port = htons(pinfo->dst_videoport);
sendtonum = sendto(sfd, buf, len, 0, (struct sockaddr *)&s, sizeof(s));
}
if(sendtonum != len)
return 2;
return 0;
}
我的qq:2442391036,欢迎交流!