音視頻同步!RTCP協議解析及代碼實現

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTCP是實時控制協議(Real-Time Control Protocol)的縮寫。RTCP由RFC 3550定義(取代作廢的RFC 1889)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實時傳輸協議(RTP)和實時控制協議(RTCP)結合使用,可以監視大型多播網絡的數據傳遞。RTP承載媒體流,而RTCP用於","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"監視傳輸統計信息和服務質量","attrs":{}},{"type":"text","text":"。監視使接收器能夠檢測是否有任何丟包並補償任何延遲抖動。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"兩種協議都獨立於基礎傳輸層協議和網絡層協議工作。RTP標頭中的信息告訴接收器如何重建數據,並描述編解碼器比特流的打包方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我們重點講解RTCP功能、RTCP信息包等。最後RTCP協議解析實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTCP有哪些功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、RTCP主要功能是提供有關質量的反饋數據分發。這是RTP角色不可或缺的一部分,傳輸協議,與流量和擁塞有關其他傳輸協議的控制功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、RTCP帶有RTP的持久性傳輸級別標識符源稱爲規範名稱或CNAME。自從如果發現衝突或程序重新啓動,接收方要求CNAME跟蹤每個參與者。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接收者也可能要求CNAME將來自給定參與者的多個數據流關聯到集合中相關RTP會話的數量,例如同步音頻和視頻。媒體間同步還需要NTP和RTP數據發送方在RTCP數據包中包含的時間戳。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、前兩個功能要求所有參與者發送RTCP數據包,因此必須控制速率以使RTP能夠擴大到大量參與者。通過讓每個參與者將其控制包發送給所有其他人,每個人都可以獨立觀察參與者的數量。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、這項功能對於參加者可以任意進入和離開的鬆散會話進程十分有用,參加者可以自由進入或離開,沒有成員控制或參數協調。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能1-3應該在所有環境中使用,尤其是在IP多播環境。RTP應用程序設計師應避免只能在單播模式下工作且無法擴展到的機制更大的數字。RTCP的傳輸可以單獨控制對於發送者和接收者,適用於例如單向鏈接,而接收者沒有反饋可能的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTCP協議的端口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTSP通常使用RTP協議來傳送實時流,RTP一般使用偶數端口,而RTCP使用相鄰的奇數端口,即RTP端口號+1。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTP端口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7b/7b0f8c4c67099633dbf7eb64bea9ed31.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTCP端口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/84/84e1b9719ac80843e115017c0a771b4d.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTCP信息包有哪些","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在RTCP通信控制中,RTCP協議的功能是通過不同類型的RTCP包來實現的。RTCP也是基於UDP包來傳送的,主要有五種類型的封包:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"SR:發送端報告,由發送RTP數據報的應用程序或中端發出的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"RR:接收端報告,由接受但不發送RTP數據報的應用程序或中端發出。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"SDES: 源描述,傳遞與會話成員有關的標識信息的載體,如用戶名、郵件、電話等。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"BYE: 通知離開,通知回話中的其他成員將退出會話。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"APP: 由應用程序自己定義,作爲RTCP協議的擴展。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"c"},"content":[{"type":"text","text":"#define RTCP_SR 200\n#define RTCP_RR 201\n#define RTCP_SDES 202\n#define RTCP_BYE 203\n#define RTCP_APP 204","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以根據這五種類型包判斷RTCP頭部","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"static bool dissect_rtcp_heur(u_char *rtcp_info,int PayloadLen)\n{\n unsigned int offset = 0;\n unsigned int first_byte = 0;\n unsigned int packet_type = 0;\n\n\n /* 查看第一個字節 */\n first_byte = rtcp_info[offset];\n\n /* 版本位是否設置爲2*/\n\t//printf(\"version: %d\\n\",((first_byte & 0xC0) >> 6));\n if (((first_byte & 0xC0) >> 6) != 2)\n {\n return false;\n }\n\n /* 看包類型 */\n\toffset += 1;\n packet_type = rtcp_info[offset];\n\t//printf(\"packet_type: %d\\n\",packet_type);\n /* 複合數據包中的第一個數據包應該是發送方或接收者報告 */\n if (!((packet_type == RTCP_SR) || (packet_type == RTCP_RR) ||\n (packet_type == RTCP_BYE) || (packet_type == RTCP_APP) ||\n (packet_type == RTCP_PSFB)))\n {\n return false;\n }\n\n /*總長度必須是4個字節的倍數*/\n\t//printf(\"PayloadLen: %d\\n\",PayloadLen);\n if (PayloadLen % 4)\n {\n return false;\n }\n\n /* OK, dissect as RTCP */\n dissect_rtcp(rtcp_info,packet_type,offset,PayloadLen);\n return true;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTCP協議報文格式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SR:發送端報告","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/57/572aa27793a84d6b9a1cb534669562d3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"版本(V)","attrs":{}},{"type":"text","text":":同RTP包頭部","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"填充 ( P )","attrs":{}},{"type":"text","text":" :同RTP包頭部。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"接收報告計數器(RC)","attrs":{}},{"type":"text","text":":5b 該SR包中接收的報告塊的數目。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"包類型(PT)","attrs":{}},{"type":"text","text":": 8bit SR包類型爲200","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"長度(length)","attrs":{}},{"type":"text","text":":SR包以32bit爲1單位的長度減1","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同步源(SSRC)","attrs":{}},{"type":"text","text":":SR包發送的同步源標識符。與對應RTP包中的SSRC一樣。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"NTP時間戳(Network Time Protocol)","attrs":{}},{"type":"text","text":":SR包發送時的絕對時間。用於同步不同的流。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTP時間戳","attrs":{}},{"type":"text","text":":與NTP時間戳對應,與RTP包中的時間戳具有相同的初始值。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Send’s Packet count","attrs":{}},{"type":"text","text":":從開始發包到產生這個SR包的這段時間內發送者發送的有效數據的總字節數,不包括頭部和填充,發送者改變SSRC時,該域要清零。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同步源n的SSRC標識符","attrs":{}},{"type":"text","text":":該報告中包含的是從該源接收到的包的統計信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"丟失率","attrs":{}},{"type":"text","text":":表明從上一個SR或RR包發出依來從同步源n發送的RTP包的丟失率。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"累計丟失數據","attrs":{}},{"type":"text","text":":從開始接受SSRC_n的包到發送SR這個時間段內SSRC_n發送的RTP丟失的總數目。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"收到的擴展最大序列號","attrs":{}},{"type":"text","text":":從SSRC_n收到的從RTP數據包中的最大序列號。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"接收抖動(Interarrival jitter)","attrs":{}},{"type":"text","text":":RTP數據包接收時間的統計方差估計。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"上次SR時間戳(Last SR)","attrs":{}},{"type":"text","text":":取最近從SSRC_n收到的SR包中的NTP時間戳中的中間32bit。如果還未收到SR包,則爲0。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"上次依賴SR延遲(Delay since Last SR)","attrs":{}},{"type":"text","text":":從上次SSRC_n收到SR包到發送本包的延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Wireshark抓包:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/70/70a9ce4605a32b2ff12cf1dc1b4440fa.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"活動會話的參與者在發送和接收RTP分組時使用SR。SR有三個不同的部分:報頭信息、發送方信息和許多接收方報告塊。SR也可以有一個與大綱相關的擴展域。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8d/8dbca109994317172b276cda17ba13e2.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Wireshark抓包:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9d/9d6c4054cffdc73312dc91a0e4ba059a.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SDES: 源描述","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SDES提供了傳遞與會話成員有關的標識信息的載體,如用戶名、郵件、電話等。每個RTCP混合分組中必須有SDES分組。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/02/02f134588c10594b7e4cd5e6ec038752.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"報頭包含一個長度域、一個淨荷類型域(PT=202)和一個源計數(RC)域。RC域5個bit,表示分組中信息塊的數量。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個信息塊包含一個SSRC或CSRC值,其後跟着一個或多個的標識符和一些可用於SSRC或CSRC的信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CNAME 項的SDES包必須包含在每個組合RTCP包中。SDES包可能包括其他源描述項,這要根據特別的應用需要,並同時考慮帶寬限制。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Wireshark抓包:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/78cfb47577b028adb71043d03f0c52b7.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SDES源描述包提供了直觀的文本信息來描述會話的參加者,包括CNAME、NAME、EMAIL、PHONE、LOC等源描述項。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些爲接收方獲取發送方的有關信息提供了方便。SDES 包由包頭與數據塊組成,數據塊可以沒有,也可有多個。包頭由版本(V)、填充(P)、長度指示、包類型(PT)和源計數(SC)組成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PT佔8位,用於識別RTCP的SDES包,SC佔5位,指示包含在SDES包中的SSRC/CSRC塊數量,零值有效,但沒有意義。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"BYE: 通知離開","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"BYE分組用於表示一個或多個媒體源不再是處於激活狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/49/49fe19e19a6a1c01aed12468bf6386ce.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Wireshark抓包:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fb/fb5a24478fa26dccdc3fc25d7d7f6e17.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲可選項,BYE包可包括一個8位八進制計數,後跟文本信息,表示離開原因。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,組合包中每個RTCP包可獨立處理,而不需要按照包組合的先後順序處理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在組合包中有以下幾條強制約束。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"只要帶寬允許,在SR包或RR包中的接收統計應該經常發送,因此每個週期發送的組合RTCP 包中應包含報告包。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"每個組合包中都應該包含SDES CNAME,因爲新接收者需要通過接收CNAME來識別源,並與媒體聯繫進行同步。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"組合包前面是包類型數量,其增長應該受到限制。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTCP協議如何實現媒體流的同步","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過抓包分析RTCP發送端報告,RTP的同步其實就靠這三個域:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"sender SSRC :SR包發送的同步源標識符。與對應RTP包中的SSRC一樣","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"NTP timestamp:SR包發送時的絕對時間。用於同步不同的流。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"RTP timestamp:與NTP時間戳對應,與RTP包中的時間戳具有相同的初始值。","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1b/1bc8a64fb2f82722461e8fbee7258593.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那怎麼計算NTP時間呢?在RTCP中NTP時間存放在8個字節中,分爲:MSW和LSW,分別佔用4個字節。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"const char *tvb_ntp_fmt_ts_sec(u_char *rtcp_info, int offset)\n{\n\tuint32_t tempstmp = 0;\n\ttime_t temptime = 0;\n\tstruct tm *bd;\n\tchar *buff = NULL;\n\t\n\ttempstmp = ntohl(*(uint32_t*)(rtcp_info + offset));\n\tif (tempstmp == 0){\n\t\treturn \"NULL\";\n\t}\n\n\t/* We need a temporary variable here so the unsigned math\n\t* works correctly (for years > 2036 according to RFC 2030\n\t* chapter 3).\n\t*/\n\ttemptime = (time_t)(tempstmp - NTP_BASETIME);\n\tbd = gmtime(&temptime);\n\tif (!bd){\n\t\treturn \"Not representable\";\n\t}\n\n\tbuff = (char *)malloc(NTP_TS_SIZE);\n\tsnprintf(buff, NTP_TS_SIZE,\n\t\t\"%s %2d, %d %02d:%02d:%02d UTC\",\n\t\tmon_names[bd->tm_mon],\n\t\tbd->tm_mday,\n\t\tbd->tm_year + 1900,\n\t\tbd->tm_hour,\n\t\tbd->tm_min,\n\t\tbd->tm_sec);\n\treturn buff;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NTP timestamp","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"c"},"content":[{"type":"text","text":"\n /* NTP timestamp */\n ts_msw = ntohl(*(uint32_t*)(rtcp_info + offset));\n\tprintf(\"ts_msw: 0x%x\\n\",ts_msw);\n ts_lsw = ntohl(*(uint32_t*)(rtcp_info + offset + 4));\n\tprintf(\"ts_lsw: 0x%x\\n\",ts_lsw);\n\tprintf(\"MSW: %s\\n\",tvb_ntp_fmt_ts_sec(rtcp_info,offset));\n offset += 8;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RTCP協議實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面我給出對RTCP協議解析實現的代碼,根據回放報文,解析字段信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"/* 接收者/發送者計數是最後5位 */\n#define RTCP_COUNT(octet) ((octet) & 0x1F)\n\n\n#define RTCP_PT_MIN 192\n/* Supplemental H.261 specific RTCP packet types according to Section C.3.5 */\n#define RTCP_FIR 192\n#define RTCP_NACK 193\n#define RTCP_SMPTETC 194\n#define RTCP_IJ 195\n/* RTCP packet types according to Section A.11.1 */\n/* And https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */\n#define RTCP_SR 200\n#define RTCP_RR 201\n#define RTCP_SDES 202\n#define RTCP_BYE 203\n#define RTCP_APP 204\n#define RTCP_RTPFB 205\n#define RTCP_PSFB 206\n#define RTCP_XR 207\n#define RTCP_AVB 208\n#define RTCP_RSI 209\n#define RTCP_TOKEN 210\n\n#define RTCP_PT_MAX 210\n\n\nstatic const char mon_names[12][4] = {\n\t\"Jan\",\n\t\"Feb\",\n\t\"Mar\",\n\t\"Apr\",\n\t\"May\",\n\t\"Jun\",\n\t\"Jul\",\n\t\"Aug\",\n\t\"Sep\",\n\t\"Oct\",\n\t\"Nov\",\n\t\"Dec\"\n};\n\n/** data structure to hold time values with nanosecond resolution*/\ntypedef struct {\n\ttime_t\tsecs;\n\tint\tnsecs;\n} nstime_t;\n\n\n/*\n * 1900-01-01 00:00:00 (proleptic?) UTC.\n * Used by a number of time formats.\n */\n#define EPOCH_DELTA_1900_01_01_00_00_00_UTC 2208988800U\n\n/*\n * NTP_BASETIME is in fact epoch - ntp_start_time; ntp_start_time\n * is January 1, 2036, 00:00:00 UTC.\n */\n#define NTP_BASETIME EPOCH_DELTA_1900_01_01_00_00_00_UTC\n#define NTP_FLOAT_DENOM 4294967296.0\n#define NTP_TS_SIZE 100\n\n\n/* 解剖長度字段。附加到此字段的文字表示轉換爲的實際字節數 (即 (原始值 + 1) * 4) */\nstatic int dissect_rtcp_length_field(u_char *rtcp_info, int offset)\n{\n\n uint16_t raw_length = ntohs(*(uint16_t*)(rtcp_info + offset));\n printf(\"(%u bytes)\\n\", (raw_length+1)*4);\n offset += 2;\n return offset;\n}\nstatic int dissect_rtcp_rr(u_char *rtcp_info, int offset,int count, int packet_length )\n{\n int counter = 0;\n uint8_t rr_flt = 0;\n int rr_offset = offset;\n\t\n counter = 1;\n while ( counter <= count ) {\n uint32_t lsr = 0, dlsr = 0;\n\n /* Create a new subtree for a length of 24 bytes */\n\n /* SSRC_n source identifier, 32 bits */\n\n offset += 4;\n\n /* Fraction lost, 8bits */\n rr_flt = rtcp_info[offset];\n\n offset++;\n\n /* Cumulative number of packets lost, 24 bits */\n offset += 3;\n\n\n /* Sequence number cycles */\n\n offset += 2;\n /* highest sequence number received */\n\n offset += 2;\n\n /* Interarrival jitter */\n\n offset += 4;\n\n /* Last SR timestamp */\n lsr = ntohl(*(uint32_t*)(rtcp_info + offset));\n\t\tprintf(\"Last SR timestamp: 0x%x\\n\",lsr);\n offset += 4;\n\n /* Delay since last SR timestamp */\n dlsr = ntohl(*(uint32_t*)(rtcp_info + offset));\n\n printf(\"(%d milliseconds)\\n\",(int)(((double)dlsr/(double)65536) * 1000.0));\n offset += 4;\n\n counter++;\n }\n\n return offset;\n}\n\nconst char *tvb_ntp_fmt_ts_sec(u_char *rtcp_info, int offset)\n{\n\tuint32_t tempstmp = 0;\n\ttime_t temptime = 0;\n\tstruct tm *bd;\n\tchar *buff = NULL;\n\t\n\ttempstmp = ntohl(*(uint32_t*)(rtcp_info + offset));\n\tif (tempstmp == 0){\n\t\treturn \"NULL\";\n\t}\n\n\t/* We need a temporary variable here so the unsigned math\n\t* works correctly (for years > 2036 according to RFC 2030\n\t* chapter 3).\n\t*/\n\ttemptime = (time_t)(tempstmp - NTP_BASETIME);\n\tbd = gmtime(&temptime);\n\tif (!bd){\n\t\treturn \"Not representable\";\n\t}\n\n\tbuff = (char *)malloc(NTP_TS_SIZE);\n\tsnprintf(buff, NTP_TS_SIZE,\n\t\t\"%s %2d, %d %02d:%02d:%02d UTC\",\n\t\tmon_names[bd->tm_mon],\n\t\tbd->tm_mday,\n\t\tbd->tm_year + 1900,\n\t\tbd->tm_hour,\n\t\tbd->tm_min,\n\t\tbd->tm_sec);\n\treturn buff;\n}\n\nstatic int dissect_rtcp_sr(u_char *rtcp_info, int offset,int count, int packet_length)\n{\n\n uint32_t ts_msw = 0, ts_lsw = 0;\n int sr_offset = offset;\n\n /* NTP timestamp */\n ts_msw = ntohl(*(uint32_t*)(rtcp_info + offset));\n\tprintf(\"ts_msw: 0x%x\\n\",ts_msw);\n ts_lsw = ntohl(*(uint32_t*)(rtcp_info + offset + 4));\n\tprintf(\"ts_lsw: 0x%x\\n\",ts_lsw);\n\n\t//printf(\"offset: 0x%x 0x%x 0x%x 0x%x\\n\",rtcp_info[offset],rtcp_info[offset + 1],rtcp_info[offset + 2],rtcp_info[offset + 3]);\n\tprintf(\"MSW: %s\\n\",tvb_ntp_fmt_ts_sec(rtcp_info,offset));\n offset += 8;\n\n /* RTP timestamp, 32 bits */\n\t\n offset += 4;\n /* Sender's packet count, 32 bits */\n\n offset += 4;\n /* Sender's octet count, 32 bits */\n\n offset += 4;\n\n\n /* The rest of the packet is equal to the RR packet */\n if ( count != 0 )\n offset = dissect_rtcp_rr(rtcp_info, offset, count, packet_length-(offset-sr_offset));\n else\n {\n /* If length remaining, assume profile-specific extension bytes */\n if ((offset-sr_offset) < packet_length)\n {\n\n offset = sr_offset + packet_length;\n }\n }\n\n return offset;\n}\n\nstatic int dissect_rtcp_sdes(u_char *rtcp_info, int offset, int count)\n{\n int chunk = 0;\n\n int start_offset = 0;\n int items_start_offset = 0;\n uint32_t ssrc = 0;\n unsigned int item_len = 0;\n unsigned int sdes_type = 0;\n unsigned int prefix_len = 0;\n\n chunk = 1;\n while ( chunk <= count ) \n\t{\n\t\t/* Create a subtree for this chunk; we don't yet know\n\t\t the length. */\n\t\tstart_offset = offset;\n\n\t\tssrc = ntohl(*(uint32_t*)(rtcp_info + offset));\n\t\tprintf(\"Chunk %u, SSRC/CSRC 0x%X\\n\", chunk, ssrc);\n\n\t\t/* SSRC_n source identifier, 32 bits */\n\t\toffset += 4;\n\n\t\t/* Create a subtree for the SDES items; we don't yet know\n\t\t the length */\n\n\n\t\t/*\n\t\t * Not every message is ended with \"null\" bytes, so check for\n\t\t * end of frame as well.\n\t\t */\n\t\t \n\t\t /* ID, 8 bits */\n\t\tsdes_type = rtcp_info[offset];\n\t\tprintf(\"Type: %d\\n\",sdes_type);\n\t\tif (sdes_type == 0)\n\t\t\tbreak;\n\t\toffset++;\n\t\t/* Item length, 8 bits */\n\t\titem_len = rtcp_info[offset];\n\t\tprintf(\"Length: %d\\n\",item_len);\n\t\toffset++;\n\t\t\n\t\tchar *pszText = (char*)malloc(item_len);\n\t\tif (pszText != 0)\n\t\t{\n\t\t\tmemcpy(pszText, rtcp_info + offset,item_len);\n\t\t\tpszText[item_len] = '\\0';\n\t\t\tprintf(\"Text = %s\\n\",pszText);\n\t\t}\t\n\n\t\tchunk++;\n }\n\n return offset;\n}\n\nstatic void dissect_rtcp(u_char *rtcp_info,int packet_type, int offset,int PayloadLen)\n{\n\tunsigned int temp_byte = 0;\n\tint elem_count = 0;\n\tint packet_length = 0;\n\tint total_packet_length = 0;\n\tint loop = 2;\n\tbool flag_rtcp = false;\n\n\t\t/*檢查是否爲有效類型*/\n\t\tif ( ( packet_type < RTCP_PT_MIN ) || ( packet_type > RTCP_PT_MAX ) )\n\t\t\texit(-1);\n\n\t\t/*\n\t\t * 獲取完整的RTCP數據包的長度\n\t\t */\n\t\t \n\t\tpacket_length = (ntohs(*(uint16_t*)(rtcp_info + offset + 1)) + 1) * 4 ;\n\t\t//printf(\"packet_length: %d\\n\",packet_length);\n\n\t\t\n\t\ttemp_byte = rtcp_info[offset-1];\n\t\telem_count = RTCP_COUNT( temp_byte );/* Source count, 5 bits */\n\t\tprintf(\"Reception report count: %d\\n\",elem_count); \n\n\t\tswitch ( packet_type ) \n\t\t{\n\t\t\t\n\t\t\tcase RTCP_SR:\n\t\t\tcase RTCP_RR:\n\t\t\t\t/*\n\t\t\t\t\tReal-time Transport Control Protocol (Receiver Report)\n\t\t\t\t\t10.. .... = Version: RFC 1889 Version (2)\n\t\t\t\t\t..0. .... = Padding: False\n\t\t\t\t\t...0 0001 = Reception report count: 1\n\t\t\t\t\tPacket type: Receiver Report (201)\n\t\t\t\t\tLength: 7 (32 bytes)\n\t\t\t\t\tSender SSRC: 0xb584b03e (3045371966)\n\t\t\t\t\tSource 1\n\t\t\t\t*/\n\n\t\t\t\t/* Packet type, 8 bits */\n\t\t\t\toffset++;\n\t\t\t\t/* Packet length in 32 bit words MINUS one, 16 bits */\n\t\t\t\toffset = dissect_rtcp_length_field(rtcp_info, offset);\n\t\t\t\t/* Sender Synchronization source, 32 bits */\n\t\t\t\toffset += 4;\n\n\t\t\t\tif ( packet_type == RTCP_SR )\n\t\t\t\t{\n\t\t\t\t\toffset = dissect_rtcp_sr(rtcp_info, offset, elem_count, packet_length-8 );\n\t\t\t\t\tprintf(\"dissect_rtcp_sr\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t\n\t\t\t\t\toffset = dissect_rtcp_rr(rtcp_info, offset, elem_count, packet_length-8 );\t\t\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//uint16_t second_packet_type = ntohs(*(uint16_t*)(rtcp_info + offset));\n\n\t\t\t\t//printf(\"111offset: 0x%x 0x%x 0x%x 0x%x\\n\",rtcp_info[offset],rtcp_info[offset + 1],rtcp_info[offset + 2],rtcp_info[offset + 3]);\n\t\t\t\t\n\t\t\t\tif (rtcp_info[offset + 1] == RTCP_SDES)\n\t\t\t\t{\n\t\t\t\t\t\n\t\t\t\t\t/* Source count, 5 bits */\n\t\t\t\t\toffset++;\n\t\t\t\t\t/* Packet type, 8 bits */\n\t\t\t\t\toffset++;\n\t\t\t\t\t/* Packet length in 32 bit words MINUS one, 16 bits */\n\t\t\t\t\toffset = dissect_rtcp_length_field(rtcp_info, offset);\n\t\t\t\t\toffset = dissect_rtcp_sdes(rtcp_info,offset,elem_count);\n\n\t\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t/*\n\t\t\t\t * To prevent endless loops in case of an unknown message type\n\t\t\t\t * increase offset. Some time the while will end :-)\n\t\t\t\t */\n\t\t\t\toffset++;\n\t\t\t\tbreak;\n\n\t\t}\n\t\t\n}\n\n\nstatic bool dissect_rtcp_heur(u_char *rtcp_info,int PayloadLen)\n{\n unsigned int offset = 0;\n unsigned int first_byte = 0;\n unsigned int packet_type = 0;\n\n\n /* 查看第一個字節 */\n first_byte = rtcp_info[offset];\n\n /* 版本位是否設置爲2*/\n\t//printf(\"version: %d\\n\",((first_byte & 0xC0) >> 6));\n if (((first_byte & 0xC0) >> 6) != 2)\n {\n return false;\n }\n\n /* 看包類型 */\n\toffset += 1;\n packet_type = rtcp_info[offset];\n\t//printf(\"packet_type: %d\\n\",packet_type);\n /* 複合數據包中的第一個數據包應該是發送方或接收者報告 */\n if (!((packet_type == RTCP_SR) || (packet_type == RTCP_RR) ||\n (packet_type == RTCP_BYE) || (packet_type == RTCP_APP) ||\n (packet_type == RTCP_PSFB)))\n {\n return false;\n }\n\n /*總長度必須是4個字節的倍數*/\n\t//printf(\"PayloadLen: %d\\n\",PayloadLen);\n if (PayloadLen % 4)\n {\n return false;\n }\n\n /* OK, dissect as RTCP */\n dissect_rtcp(rtcp_info,packet_type,offset,PayloadLen);\n return true;\n}\n\n\nstatic void confirm_rtcp_packet(struct ip *pIp)\n{\n\tint iIpTotalLen = ntohs(pIp->ip_len);\n\tint offset = 0;\n\tint nFragSeq = 0;\n\tstruct udphdr* pUdpHdr = (struct udphdr*)((char*)pIp + (pIp->ip_hl<<2));\n\tif (pIp->ip_p == IPPROTO_UDP) \n\t{\n\t\tprintf(\"\\n\");\n\t\t\n\t\tint iPayloadLen = iIpTotalLen - (pIp->ip_hl<<2) - 8;\n\t\tprintf(\"UDP Payload Len %d\\n\", iPayloadLen);\n\t\t\n\t\tu_char *pDnsHdr = (u_char*)(pUdpHdr+1);\n\t\tdissect_rtcp_heur(pDnsHdr,iPayloadLen);\n\t\t\n\t}\t\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"編譯運行:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/88/8853ff58f287bc19b757a5977f9482bb.png","alt":"在這裏插入圖片描述","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RTCP協議是流媒體通信的基石。RTCP協議則負責可靠傳輸、流量控制和擁塞控制等服務質量保證。上面講解了RTCP功能、RTCP數據包格式及代碼實現。最後,學習一個新的協議,最好還是研究學習官方文檔,因爲這是最權威的資料。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章