发布网友 发布时间:2024-10-01 14:51
共1个回答
热心网友 时间:2024-10-13 04:00
实时传输协议(RTP)和实时控制协议(RTCP)结合使用,可以监视大型多播网络的数据传递。RTP承载媒体流,而RTCP用于监视传输统计信息和服务质量。监视使接收器能够检测是否有任何丢包并补偿任何延迟抖动。
两种协议都独立于基础传输层协议和网络层协议工作。RTP标头中的信息告诉接收器如何重建数据,并描述编解码器比特流的打包方式。
下面我们重点讲解RTCP功能、RTCP信息包等。最后RTCP协议解析实现。
RTCP有哪些功能
1、RTCP主要功能是提供有关质量的反馈数据分发。这是RTP角色不可或缺的一部分,传输协议,与流量和拥塞有关其他传输协议的控制功能。
2、RTCP带有RTP的持久性传输级别标识符源称为规范名称或CNAME。自从如果发现冲突或程序重新启动,接收方要求CNAME跟踪每个参与者。
接收者也可能要求CNAME将来自给定参与者的多个数据流关联到集合中相关RTP会话的数量,例如同步音频和视频。媒体间同步还需要NTP和RTP数据发送方在RTCP数据包中包含的时间戳。
3、前两个功能要求所有参与者发送RTCP数据包,因此必须控制速率以使RTP能够扩大到大量参与者。通过让每个参与者将其控制包发送给所有其他人,每个人都可以独立观察参与者的数量。
4、这项功能对于参加者可以任意进入和离开的松散会话进程十分有用,参加者可以自由进入或离开,没有成员控制或参数协调。
功能1-3应该在所有环境中使用,尤其是在IP多播环境。RTP应用程序设计师应避免只能在单播模式下工作且无法扩展到的机制更大的数字。RTCP的传输可以单独控制对于发送者和接收者,适用于例如单向链接,而接收者没有反馈可能的。
RTCP协议的端口
RTSP通常使用RTP协议来传送实时流,RTP一般使用偶数端口,而RTCP使用相邻的奇数端口,即RTP端口号+1。
RTP端口
RTCP端口
RTCP信息包有哪些
在RTCP通信控制中,RTCP协议的功能是通过不同类型的RTCP包来实现的。RTCP也是基于UDP包来传送的,主要有五种类型的封包:
SR:发送端报告,由发送RTP数据报的应用程序或中端发出的。
RR:接收端报告,由接受但不发送RTP数据报的应用程序或中端发出。
SDES:源描述,传递与会话成员有关的标识信息的载体,如用户名、邮件、电话等。
BYE:通知离开,通知回话中的其他成员将退出会话。
APP:由应用程序自己定义,作为RTCP协议的扩展。
#defineRTCP_SR200#defineRTCP_RR201#defineRTCP_SDES202#defineRTCP_BYE203#defineRTCP_APP204我们可以根据这五种类型包判断RTCP头部
staticbooldissect_rtcp_heur(u_char*rtcp_info,intPayloadLen){unsignedintoffset=0;unsignedintfirst_byte=0;unsignedintpacket_type=0;/*查看第一个字节*/first_byte=rtcp_info[offset];/*版本位是否设置为2*///printf("version:%d\n",((first_byte&0xC0)>>6));if(((first_byte&0xC0)>>6)!=2){returnfalse;}/*看包类型*/offset+=1;packet_type=rtcp_info[offset];//printf("packet_type:%d\n",packet_type);/*复合数据包中的第一个数据包应该是发送方或接收者报告*/if(!((packet_type==RTCP_SR)||(packet_type==RTCP_RR)||(packet_type==RTCP_BYE)||(packet_type==RTCP_APP)||(packet_type==RTCP_PSFB))){returnfalse;}/*总长度必须是4个字节的倍数*///printf("PayloadLen:%d\n",PayloadLen);if(PayloadLen%4){returnfalse;}/*OK,dissectasRTCP*/dissect_rtcp(rtcp_info,packet_type,offset,PayloadLen);returntrue;}RTCP协议报文格式
SR:发送端报告
版本(V):同RTP包头部
填充(P):同RTP包头部。
接收报告计数器(RC):5b该SR包中接收的报告块的数目。
包类型(PT):8bitSR包类型为200
长度(length):SR包以32bit为1单位的长度减1
同步源(SSRC):SR包发送的同步源标识符。与对应RTP包中的SSRC一样。
NTP时间戳(NetworkTimeProtocol):SR包发送时的绝对时间。用于同步不同的流。
RTP时间戳:与NTP时间戳对应,与RTP包中的时间戳具有相同的初始值。
Send’sPacketcount:从开始发包到产生这个SR包的这段时间内发送者发送的有效数据的总字节数,不包括头部和填充,发送者改变SSRC时,该域要清零。
同步源n的SSRC标识符:该报告中包含的是从该源接收到的包的统计信息。
丢失率:表明从上一个SR或RR包发出依来从同步源n发送的RTP包的丢失率。
累计丢失数据:从开始接受SSRC_n的包到发送SR这个时间段内SSRC_n发送的RTP丢失的总数目。
收到的扩展最大序列号:从SSRC_n收到的从RTP数据包中的最大序列号。
接收抖动(Interarrivaljitter):RTP数据包接收时间的统计方差估计。
上次SR时间戳(LastSR):取最近从SSRC_n收到的SR包中的NTP时间戳中的中间32bit。如果还未收到SR包,则为0。
上次依赖SR延迟(DelaysinceLastSR):从上次SSRC_n收到SR包到发送本包的延迟
Wireshark抓包:
活动会话的参与者在发送和接收RTP分组时使用SR。SR有三个不同的部分:报头信息、发送方信息和许多接收方报告块。SR也可以有一个与大纲相关的扩展域。
Wireshark抓包:
SDES:源描述
SDES提供了传递与会话成员有关的标识信息的载体,如用户名、邮件、电话等。每个RTCP混合分组中必须有SDES分组。
报头包含一个长度域、一个净荷类型域(PT=202)和一个源计数(RC)域。RC域5个bit,表示分组中信息块的数量。
每个信息块包含一个SSRC或CSRC值,其后跟着一个或多个的标识符和一些可用于SSRC或CSRC的信息。
CNAME项的SDES包必须包含在每个组合RTCP包中。SDES包可能包括其他源描述项,这要根据特别的应用需要,并同时考虑带宽*。
Wireshark抓包:
SDES源描述包提供了直观的文本信息来描述会话的参加者,包括CNAME、NAME、EMAIL、PHONE、LOC等源描述项。
这些为接收方获取发送方的有关信息提供了方便。SDES包由包头与数据块组成,数据块可以没有,也可有多个。包头由版本(V)、填充(P)、长度指示、包类型(PT)和源计数(SC)组成。
PT占8位,用于识别RTCP的SDES包,SC占5位,指示包含在SDES包中的SSRC/CSRC块数量,零值有效,但没有意义。
BYE:通知离开
BYE分组用于表示一个或多个媒体源不再是处于激活状态。
Wireshark抓包:
作为可选项,BYE包可包括一个8位八进制计数,后跟文本信息,表示离开原因。
最后,组合包中每个RTCP包可独立处理,而不需要按照包组合的先后顺序处理。
在组合包中有以下几条强制约束。
只要带宽允许,在SR包或RR包中的接收统计应该经常发送,因此每个周期发送的组合RTCP包中应包含报告包。
每个组合包中都应该包含SDESCNAME,因为新接收者需要通过接收CNAME来识别源,并与媒体联系进行同步。
组合包前面是包类型数量,其增长应该受到*。
RTCP协议如何实现媒体流的同步
通过抓包分析RTCP发送端报告,RTP的同步其实就靠这三个域:
senderSSRC:SR包发送的同步源标识符。与对应RTP包中的SSRC一样。
NTPtimestamp:SR包发送时的绝对时间。用于同步不同的流。
RTPtimestamp:与NTP时间戳对应,与RTP包中的时间戳具有相同的初始值。
那怎么计算NTP时间呢?在RTCP中NTP时间存放在8个字节中,分为:MSW和LSW,分别占用4个字节。
constchar*tvb_ntp_fmt_ts_sec(u_char*rtcp_info,intoffset){uint32_ttempstmp=0;time_ttemptime=0;structtm*bd;char*buff=NULL;tempstmp=ntohl(*(uint32_t*)(rtcp_info+offset));if(tempstmp==0){return"NULL";}/*Weneedatemporaryvariableheresotheunsignedmath*workscorrectly(foryears>2036accordingtoRFC2030*chapter3).*/temptime=(time_t)(tempstmp-NTP_BASETIME);bd=gmtime(&temptime);if(!bd){return"Notrepresentable";}buff=(char*)malloc(NTP_TS_SIZE);snprintf(buff,NTP_TS_SIZE,"%s%2d,%d%02d:%02d:%02dUTC",mon_names[bd->tm_mon],bd->tm_mday,bd->tm_year+1900,bd->tm_hour,bd->tm_min,bd->tm_sec);returnbuff;}NTPtimestamp
/*NTPtimestamp*/ts_msw=ntohl(*(uint32_t*)(rtcp_info+offset));printf("ts_msw:0x%x\n",ts_msw);ts_lsw=ntohl(*(uint32_t*)(rtcp_info+offset+4));printf("ts_lsw:0x%x\n",ts_lsw);printf("MSW:%s\n",tvb_ntp_fmt_ts_sec(rtcp_info,offset));offset+=8;RTCP协议实现
下面我给出对RTCP协议解析实现的代码,根据回放报文,解析字段信息。
/*接收者/发送者计数是最后5位*/#defineRTCP_COUNT(octet)((octet)&0x1F)#defineRTCP_PT_MIN192/*SupplementalH.261specificRTCPpackettypesaccordingtoSectionC.3.5*/#defineRTCP_FIR192#defineRTCP_NACK193#defineRTCP_SMPTETC194#defineRTCP_IJ195/*RTCPpackettypesaccordingtoSectionA.11.1*//*Andhttps://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml*/#defineRTCP_SR200#defineRTCP_RR201#defineRTCP_SDES202#defineRTCP_BYE203#defineRTCP_APP204#defineRTCP_RTPFB205#defineRTCP_PSFB206#defineRTCP_XR207#defineRTCP_AVB208#defineRTCP_RSI209#defineRTCP_TOKEN210#defineRTCP_PT_MAX210staticconstcharmon_names[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};/**datastructuretoholdtimevalueswithnanosecondresolution*/typedefstruct{time_tsecs;intnsecs;}nstime_t;/**1900-01-0100:00:00(proleptic?)UTC.*Usedbyanumberoftimeformats.*/#defineEPOCH_DELTA_1900_01_01_00_00_00_UTC2208988800U/**NTP_BASETIMEisinfactepoch-ntp_start_time;ntp_start_time*isJanuary1,2036,00:00:00UTC.*/#defineNTP_BASETIMEEPOCH_DELTA_1900_01_01_00_00_00_UTC#defineNTP_FLOAT_DENOM4294967296.0#defineNTP_TS_SIZE100/*解剖长度字段。附加到此字段的文字表示转换为的实际字节数(即(原始值+1)*4)*/staticintdissect_rtcp_length_field(u_char*rtcp_info,intoffset){uint16_traw_length=ntohs(*(uint16_t*)(rtcp_info+offset));printf("(%ubytes)\n",(raw_length+1)*4);offset+=2;returnoffset;}staticintdissect_rtcp_rr(u_char*rtcp_info,intoffset,intcount,intpacket_length){intcounter=0;uint8_trr_flt=0;intrr_offset=offset;counter=1;while(counter<=count){uint32_tlsr=0,dlsr=0;/*Createanewsubtreeforalengthof24bytes*//*SSRC_nsourceidentifier,32bits*/offset+=4;/*Fractionlost,8bits*/rr_flt=rtcp_info[offset];offset++;/*Cumulativenumberofpacketslost,24bits*/offset+=3;/*Sequencenumbercycles*/offset+=2;/*highestsequencenumberreceived*/offset+=2;/*Interarrivaljitter*/offset+=4;/*LastSRtimestamp*/lsr=ntohl(*(uint32_t*)(rtcp_info+offset));printf("LastSRtimestamp:0x%x\n",lsr);offset+=4;/*DelaysincelastSRtimestamp*/dlsr=ntohl(*(uint32_t*)(rtcp_info+offset));printf("(%dmilliseconds)\n",(int)(((double)dlsr/(double)65536)*1000.0));offset+=4;counter++;}returnoffset;}constchar*tvb_ntp_fmt_ts_sec(u_char*rtcp_info,intoffset){uint32_ttempstmp=0;time_ttemptime=0;structtm*bd;char*buff=NULL;tempstmp=ntohl(*(uint32_t*)(rtcp_info+offset));if(tempstmp==0){return"NULL";}/*Weneedatemporaryvariableheresotheunsignedmath*workscorrectly(foryears>2036accordingtoRFC2030*chapter3).*/temptime=(time_t)(tempstmp-NTP_BASETIME);bd=gmtime(&temptime);if(!bd){return"Notrepresentable";}buff=(char*)malloc(NTP_TS_SIZE);snprintf(buff,NTP_TS_SIZE,"%s%2d,%d%02d:%02d:%02dUTC",mon_names[bd->tm_mon],bd->tm_mday,bd->tm_year+1900,bd->tm_hour,bd->tm_min,bd->tm_sec);returnbuff;}staticintdissect_rtcp_sr(u_char*rtcp_info,intoffset,intcount,intpacket_length){uint32_tts_msw=0,ts_lsw=0;intsr_offset=offset;/*NTPtimestamp*/ts_msw=ntohl(*(uint32_t*)(rtcp_info+offset));printf("ts_msw:0x%x\n",ts_msw);ts_lsw=ntohl(*(uint32_t*)(rtcp_info+offset+4));printf("ts_lsw:0x%x\n",ts_lsw);//printf("offset:0x%x0x%x0x%x0x%x\n",rtcp_info[offset],rtcp_info[offset+1],rtcp_info[offset+2],rtcp_info[offset+3]);printf("MSW:%s\n",tvb_ntp_fmt_ts_sec(rtcp_info,offset));offset+=8;/*RTPtimestamp,32bits*/offset+=4;/*Sender'spacketcount,32bits*/offset+=4;/*Sender'soctetcount,32bits*/offset+=4;/*TherestofthepacketisequaltotheRRpacket*/if(count!=0)offset=dissect_rtcp_rr(rtcp_info,offset,count,packet_length-(offset-sr_offset));else{/*Iflengthremaining,assumeprofile-specificextensionbytes*/if((offset-sr_offset)<packet_length){offset=sr_offset+packet_length;}}returnoffset;}staticintdissect_rtcp_sdes(u_char*rtcp_info,intoffset,intcount){intchunk=0;intstart_offset=0;intitems_start_offset=0;uint32_tssrc=0;unsignedintitem_len=0;unsignedintsdes_type=0;unsignedintprefix_len=0;chunk=1;while(chunk<=count){/*Createasubtreeforthischunk;wedon'tyetknowthelength.*/start_offset=offset;ssrc=ntohl(*(uint32_t*)(rtcp_info+offset));printf("Chunk%u,SSRC/CSRC0x%X\n",chunk,ssrc);/*SSRC_nsourceidentifier,32bits*/offset+=4;/*CreateasubtreefortheSDESitems;wedon'tyetknowthelength*//**Noteverymessageisendedwith"null"bytes,socheckfor*endofframeaswell.*//*ID,8bits*/sdes_type=rtcp_info[offset];printf("Type:%d\n",sdes_type);if(sdes_type==0)break;offset++;/*Itemlength,8bits*/item