目前,支持流媒体传输的协议主要有实时传输协议 RTP(Real-Time Transport Protocol)、实时传输控制协议 RTCP(Real-Time Transport Control Protocol)和实时流协议 RTSP(Real-Time Streaming Protocol)等。上文我们针对 RTSP 进行了详细的分析及讲解:RTSP协议抓包及讲解。
流媒体协议栈如下图所示:
本文对 RTP 及 RTCP 协议进行了讲解,以及使用 wireshark 抓包对其抓到的报文进行详细分析。
RTP(Real-time Transport Protocol)是用于 Internet 上针对多媒体数据流的一种传输层协议,RTP 协议和 RTP 控制协议 RTCP 一起使用。RTP 被定义为在一对一或一对多的传输情况下工作,其目的是提供时间信息和实现流同步。RTP 的典型应用建立在 UDP 上,但也可以在 TCP 或 ATM 等其他协议之上工
作。
RTP 不像 http 和 ftp 可完整的下载整个影视文件,它是以固定的数据率在网络上发送数据,客户端也是按照这种速度观看影视文件,当影视画面播放过后,就不可以再重复播放,除非重新向服务器端要求数据。
RTP 本身只保证实时数据的传输,并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 RTCP 提供这些服务。
rtp 协议就是提供了时间标签,序列号以及其它的结构用于控制适时数据的流放。在流的概念中” 时间标签” 是最重要的信息。发送端依照即时的采样在数据包里隐蔽的设置了时间标签。在接受端收到数据包后,就依照时间标签按照正确的速率恢复成原始的适时的数据。不同的媒体格式调时属性是不一样的。
但是 rtp 本身并不负责同步,rtp 只是传输层协议,为了简化运输层处理,提高该层的效率。 将部分运输层协议功能(比如流量控制)上移到应用层完成。同步就是属于应用层协议完成的。它没有运输层协议的完整功能,不提供任何机制来保证实时地传输数据,不支持资源预留,也不保证服务质量。rtp 报文甚至不包括长度和报文边界的描述。同时 rtp 协议的数据报文和控制报文的使用相邻的不同端口,这样大大提高了协议的灵活性和处理的简单性。
rtp 协议和 udp 二者共同完成运输层协议功能。udp 协议只是传输数据包,不管数据包传输的时间顺序。rtp 的协议数据单元是用 udp 分组来承载的。在承载 rtp 数据包的时候,有时候一帧数据被分割成几个包具有相同的时间标签,则可以知道时间标签并不是必须的。而 udp 的多路复用让 rtp 协议利用支持显式的多点投递,可以满足多媒体会话的需求。
每一个 RTP 数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。
RTP 头格式如图所示:
开始 12 个八进制出现在每个 RTP 包中,而 CSRC 标识列表仅出现在混合器插入时。各段含义如下:
RTP 用到的地方就是 PLAY ,服务器往客户端传输数据用 UDP 协议,RTP 是在传输数据的前面加了个 12 字节的头(描述信息)。
RTP 载荷封装设计本文的网络传输是基于 IP 协议,所以最大传输单元(MTU)最大为 1500 字节,在使用 IP/UDP/RTP 的协议层次结构的时候,这其中包括至少 20 字节的 IP 头,8 字节的 UDP 头,以及 12 字节的 RTP 头。这样,头信息至少要占用 40 个字节,那么 RTP 载荷的最大尺寸为 1460 字节。以 H264 为例,如果一帧数据大于 1460,则需要分片打包,然后到接收端再拆包,组合成一帧数据,进行解码播放。
抓取方法参考RTSP协议抓包及讲解。
我们可以参考上面的 RTP 协议的报文结构对下面一包 RTP 报文进行分析
RTCP(Real-time Transport Control Protocol 或 RTP Control Protocol 或简写 RTCP),实时传输控制协议,是实时传输协议(RTP)的一个姐妹协议。
注:RTP 协议和 RTP 控制协议(RTCP)一起使用,而且它是建立在 UDP 协议上的(一般用于视频会议)
当应用程序开始一个 rtp 会话时将使用两个端口:一个给 rtp,一个给 rtcp。rtp 本身并不能为按顺序传送数据包提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠 rtcp 提供这些服务。
RTCP 负责管理传输质量在当前应用进程之间交换控制信息。在 RTP 会话期间,各参与者周期性地传送 RTCP 包,包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料。因此,服务器可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。
RTP 和 RTCP 配合使用,能以有效的反馈和最小的开销使传输效率最佳化,故特别适合传送网上的实时数据。根据用户间的数据传输反馈信息,可以制定流量控制的策略,而会话用户信息的交互,可以制定会话控制的策略。
在 RTCP 通信控制中,RTCP 协议的功能是通过不同的 RTCP 数据报来实现的,主要有如下几种类型:
我们可以参考上面的 RTCP 数据报对下面一包 RTP 报文进行分析
过滤 rtcp,可以看到下面第 397、401 包报文为接收端报告+源描述,第 473、475 包报文为发送端报告 + 源描述报文
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
不一样,RTP over UDP 是 RTP 下层使用 udp 传输,RTP over RTSP 是指的用 rtsp 协议建立会话, 然后使用 RTP 协议传输数据;
不是。RTP over RTSP 是指的用用 rtsp 协议建立会话,然后使用 RTP 协议传输数据,至于下面用 udp 还是 tcp 是不确定的。
PS,TS,ES 都有
rtp 和 rtsp 协议是应用层的,tcp 和 udp 是传输层的,所以只能说 rtp over tcp/udp,而且一般情况下一个点播需要 rtsp+rtp+rtcp 三个协议共同来实现。
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,欢迎交流!