RTP 協議介紹#
即時傳輸協議 RTP(Real-time Transport Protocol)是一個網路傳輸協議,它是由 IETF 的多媒體傳輸工作小組 1996 年在 RFC 1889 中公布的,後在 RFC3550 中進行更新。
它作為互聯網標準在 RFC 3550 有詳細說明。
RTP 協議詳細說明了在互聯網上傳遞音頻和視頻的標準數據包格式。它一開始被設計為一個多播協議,但後來被用在很多單播應用中。RTP 協議常用於串流媒體系統(配合 RTSP 協議)、視頻會議和一鍵通(Push to Talk)系統(配合 H.323 或 SIP),使它成為 IP 電話產業的技術基礎。RTP 協議和 RTP 控制協議 RTCP 一起使用,而且它是建立在用戶數據報協議上的(UDP)。
RTP 和 RTCP#
-
數據傳輸協議 RTP 用於即時傳輸數據。該協議提供的信息包括:時間戳(用於同步)、序列號(用於丟包和重排序檢測)、以及負載格式(用於說明數據的編碼格式)。
-
控制協議 RTCP,用於 QoS 反饋和同步媒體流。相對於 RTP 來說,RTCP 所佔的帶寬非常小,通常只有 5%。
RTP 的優勢#
提到串流媒體傳輸、視頻監控、視頻會議、語音電話(VOIP),都離不開 RTP 協議的應用,但是為什麼要使用 RTP 來進行串流媒體的傳輸呢?為什麼一定要用 RTP?
像 TCP 這樣的可靠傳輸協議,通過超時和重傳機制來保證傳輸數據流中的每一個 bit 的正確性,但這樣會使得無論從協議的實現還是傳輸的過程都變得非常的複雜。而且,當傳輸過程中有數據丟失的時候,由於對數據丟失的檢測(超時檢測)和重傳,會使數據流的傳輸被迫暫停和延遲。
RTP 協議是一種基於 UDP 的傳輸協議,RTP 本身並不能為按順序傳送數據包提供可靠的傳送機制,也不提供流量控制或擁塞控制,它依靠 RTCP 提供這些服務。這樣,對於那些丟失的數據包,不存在由於超時檢測而帶來的延遲,同時,對於那些被丟棄的包,也可以由上層根據其重要性來選擇性地重傳。
RTP 的協議層次#
串流媒體體系結構
串流媒體應用中典型的協議體系結構。
從圖中可以看出,RTP 被劃分在傳輸層,它建立在 UDP 上。同 UDP 協議一樣,為了實現其即時傳輸功能,RTP 也有固定的封裝形式。RTP 用來為端到端的即時傳輸提供時間信息和流同步,但並不保證服務質量。服務質量由 RTCP 來提供。
RTP 工作機制#
當應用程序建立一個 RTP 會話時,應用程序將確定一對目的傳輸地址。目的傳輸地址由一個網路地址和一對端口組成,有兩個端口:一個給 RTP 包,一個給 RTCP 包,使得 RTP/RTCP 數據能夠正確發送。RTP 數據發向偶數的 UDP 端口,而對應的控制信號 RTCP 數據發向相鄰的奇數 UDP 端口(偶數的 UDP 端口 + 1),這樣就構成一個 UDP 端口對。RTP 的發送過程如下,接收過程則相反。
- RTP 協議從上層接收串流媒體信息碼流(如 H.263),封裝成 RTP 數據包;RTCP 從上層接收控制信息,封裝成 RTCP 控制包。
- RTP 將 RTP 數據包發往 UDP 端口對中偶數端口;RTCP 將 RTCP 控制包發往 UDP 端口對中的奇數端口。
RTP 分組只包含 RTP 數據,而控制是由 RTCP 協議提供。RTP 在 1025 到 65535 之間選擇一個未使用的偶數 UDP 端口號,而在同一次會話中的 RTCP 則使用下一個奇數 UDP 端口號。端口號 5004 和 5005 分別用作 RTP 和 RTCP 的默認端口號。RTP 分組的首部格式如圖 2 所示,其中前 12 個字節是必須的。
應用層#
RTP 應當是應用層的一部分。在應用的發送端,開發者必須編寫用 RTP 封裝分組的程序代碼,然後把 RTP 分組交給 UDP。在接收端,RTP 分組通過 UDP 接口進入應用層後,還要利用開發者編寫的程序代碼從 RTP 分組中把應用數據塊提取出來。
RTP 報文#
首先,我們看看 RTP 的包頭。RTP 報文頭格式(見 RFC3550 Page12):
- 版本號(V):2 比特,用來標誌使用的 RTP 版本。
- 填充位(P):1 比特,如果該位置位,則該 RTP 包的尾部就包含附加的填充字節。
- 擴展位(X):1 比特,如果該位置位的話,RTP 固定頭部後面就跟有一個擴展頭部。
- CSRC 計數器(CC):4 比特,含有固定頭部後面跟著的 CSRC 的數目。
- 標記位(M):1 比特,該位的解釋由配置文檔(Profile)來承擔。對於在 RTP 以最小的控制配置文件下在音頻和視頻會議下運行的音頻流,標記位設置為 1,表示在一段靜默期後發送的第一個數據包,否則設置為 0。
- 載荷類型(PayloadType):7 比特,標識了 RTP 載荷的類型。
- 序列號(SN):16 比特,每發送一個 RTP 數據包,序列號增加 1。接收端可以據此檢測丟包和重建包序列。
- 時間戳(Timestamp):2 比特,記錄了該包中數據的第一個字節的採樣時刻。
- 同步源標識符(SSRC):32 比特,同步源就是指 RTP 包流的來源。在同一個 RTP 會話中不能有兩個相同的 SSRC 值。該標識符是隨機選取的 RFC1889 推薦了 MD5 隨機算法。
- 贡献源列表(CSRC List):0~15 項,每項 32 比特,用來標誌對一個 RTP 混合器產生的新包有貢獻的所有 RTP 包的源。由混合器將這些有貢獻的 SSRC 標識符插入表中。SSRC 標識符都被列出來,以便接收端能正確指出交談雙方的身份。
RTP 擴展頭結構#
若 RTP 固定頭中的擴展比特位置 1(注意:如果有 CSRC 列表,則在 CSRC 列表之後),則一個長度可變的頭擴展部分被加到 RTP 固定頭之後。頭擴展包含 16 比特的長度域,指示擴展項中 32 比特字的個數,不包括 4 個字節擴展頭(因此零是有效值)。
RTP 固定頭之後只允許有一個頭擴展。為允許多個互操作實現獨立生成不同的頭擴展,或某種特定實現有多種不同的頭擴展,擴展項的前 16 比特用以識別標識符或參數。這 16 比特的格式由具體實現的上層協議定義。基本的 RTP 說明並不定義任何頭擴展本身。
RTP 會話#
當應用程序建立一個 RTP 會話時,應用程序將確定一對目的傳輸地址。目的傳輸地址由一個網路地址和一對端口組成,有兩個端口:一個給 RTP 包,一個給 RTCP 包,使得 RTP/RTCP 數據能夠正確發送。RTP 數據發向偶數的 UDP 端口,而對應的控制信號 RTCP 數據發向相鄰的奇數 UDP 端口(偶數的 UDP 端口 + 1),這樣就構成一個 UDP 端口對。
RTP 發送過程#
- RTP 協議從上層接收串流媒體信息碼流(如 H.263),封裝成 RTP 數據包;RTCP 從上層接收控制信息,封裝成 RTCP 控制包。
- RTP 數據包發往 UDP 端口對中偶數端口;RTCP 將 RTCP 控制包發往 UDP 端口對中的接收端口。
RTP profile 機制#
RTP 為具體的應用提供了非常大的靈活性,它將傳輸協議與具體的應用環境、具體的控制策略分開,傳輸協議本身只提供完成即時傳輸的機制,開發者可以根據不同的應用環境,自主選擇合適的配置環境、以及合適的控制策略。
這裡所說的控制策略指的是你可以根據自己特定的應用需求,來實現特定的一些 RTCP 控制算法,比如前面提到的丟包的檢測算法、丟包的重傳策略、一些視頻會議應用中的控制方案等等(這些策略我可能將在後續的文章中進行描述)。
對於上面說的合適的配置環境,主要是指 RTP 的相關配置和負載格式的定義。RTP 協議為了廣泛地支持各種多媒體格式(如 H.264, MPEG-4, MJPEG, MPEG),沒有在協議中體現出具體的應用配置,而是通過 profile 配置文件以及負載類型格式說明文件的形式來提供。對於任何一種特定的應用,RTP 定義了一個 profile 文件以及相關的負載格式說明。
RTCP#
服務質量的監視與反饋、媒體間的同步,以及多播組中成員的標識。在 RTP 會話期間,各參與者周期性地傳送 RTCP 包。RTCP 包中含有已發送的數據包的數量、丟失的數據包的數量等統計資料,因此,各參與者可以利用這些信息動態地改變傳輸速率,甚至改變有效載荷類型。RTP 和 RTCP 配合使用,它們能以有效的反饋和最小的開銷使傳輸效率最佳化,因而特別適合傳送網上的即時數據。
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| IC | PT | Length |
+---------------+---------------+-------------------------------+
| |
| Format-specific information |
| |
| +-----------------------+
| | Padding if P = 1 |
+---------------------------------------+-----------------------+
各個字段的含義為:
- 版本號,固定為 2;
- 填充(Padding)標誌位,1 表示有填充;
- 條目數量(Item Count,簡稱 IC),在 RTCP 包的內容是條目列表時,用來表明條目數量;其他情況可作為別的含義;
- 包類型(Packet Type,也簡稱 PT),rfc3550 定義了五種標準包類型,分別是 sender report (SR), receiver report (RR), source description (SDES), goodbye (BYE), application-specific message (APP);
- 長度,包頭之後的內容總長度,單位是四字節,允許為 0;
RTCP 包不會單獨的被傳輸,它需要打包在一起形成複合包(compound packets)進行傳輸。每一個複合包都會被一個底層的包封裝(通常是 UDP/IP 包)用來傳輸。如果要對複合包進行加密,那麼 RTCP 的包組的前綴通常是一個 32 位的隨機數。複合包的結構如下圖所示:
+---------------------------------------------------------------+
| |
| IP header |
| |
+---------------------------------------------------------------+
| |
| UDP header |
| |
+---------------------------------------------------------------+
| Random prefix (if encrypted) |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|V=2|P| IC | PT | Length |
+---------------+---------------+-------------------------------+
| | first
| | RTCP
| Format-specific information | packet
| |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|V=2|P| IC | PT | Length |
+---------------+---------------+-------------------------------+
| | second
| | RTCP
| Format-specific information | packet
| |
+---------------------------------------------------------------+
RTCP 也是用 UDP 來傳送的,但 RTCP 封裝的僅僅是一些控制信息,因而分組很短,所以可以將多個 RTCP 分組封裝在一個 UDP 包中。RTCP 有如下五種分組類型。
類型 | 縮寫表示 | 用途 |
---|---|---|
200 | SR(Sender Report) | 發送端報告 |
201 | RR(Receiver Report) | 接收端報告 |
202 | SDES(Source Description Items) | 源點描述 |
203 | BYE | 結束傳輸 |
204 | .APP | 特定應用 |
上述五種分組的封裝大同小異,下面只講述 SR 類型,而其它類型請參考 RFC3550。
發送端報告分組 SR(Sender Report)用來使發送端以多播方式向所有接收端報告發送情況。SR 分組的主要內容有:相應的 RTP 流的 SSRC,RTP 流中最新產生的 RTP 分組的時間戳和 NTP,RTP 流包含的分組數,RTP 流包含的字節數。SR 包的封裝如圖所示:
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| RC | PT=SR=200 | Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of packet sender |
+---------------------------------------------------------------+
| NTP timestamp |
| |
+---------------------------------------------------------------+
| RTP timestamp |
+---------------------------------------------------------------+
| Sender's packet count |
+---------------------------------------------------------------+
| Sender's octet count |
+---------------------------------------------------------------+
| Receiver report block(s) |
| |
- 版本(V):同 RTP 包頭域。
- 填充(P):同 RTP 包頭域。
- 接收報告計數器(RC):5 比特,該 SR 包中的接收報告塊的數目,可以為零。
- 包類型(PT):8 比特,SR 包是 200。
- 長度域(Length):16 比特,其中存放的是該 SR 包以 32 比特為單位的總長度減一。
- 同步源(SSRC):SR 包發送者的同步源標識符。與對應 RTP 包中的 SSRC 一樣。
- NTP Timestamp(Network time protocol):SR 包發送時的絕對時間值。NTP 的作用是同步不同的 RTP 媒體流。
- RTP Timestamp:與 NTP 時間戳對應,與 RTP 數據包中的 RTP 時間戳具有相同的單位和隨機初始值。
- Sender's packet count:從開始發送包到產生這個 SR 包這段時間裡,發送者發送的 RTP 數據包的總數。SSRC 改變時,這個域清零。
- Sender's octet count:從開始發送包到產生這個 SR 包這段時間裡,發送者發送的淨荷數據的總字節數(不包括頭部和填充)。發送者改變其 SSRC 時,這個域要清零。
- 同步源 n 的 SSRC 標識符:該報告塊中包含的是從該源接收到的包的統計信息。
- 丟失率 (Fraction Lost):表明從上個 SR 或 RR 包發出以來從同步源 n (SSRC_n) 來的 RTP 數據包的丟失率。
- 累計的包丟失數目:從開始接收到 SSRC_n 的包到發送 SR, 從 SSRC_n 傳過來的 RTP 數據包的丟失總數。
- 收到的擴展最大序列號:從 SSRC_n 收到的 RTP 數據包中最大的序列號,
- 接收抖動 (Interarrival jitter):RTP 數據包接受時間的統計方差估計
- 上次 SR 時間戳 (Last SR,LSR):取最近從 SSRC_n 收到的 SR 包中的 NTP 時間戳的中間 32 比特。如果目前還沒收到 SR 包,則該域清零。
- 上次 SR 以來的延時 (Delay since last SR,DLSR):上次從 SSRC_n 收到 SR 包到發送本報告的延時。
RTP 時間戳#
時間戳反映了 RTP 分組中的數據的第一個字節的採樣時刻,在一次會話開始時的時間戳初值也是隨機選擇的。即使是沒有信號發送時,時間戳的數值也要隨時間不斷的增加。接收端使用時間戳可準確知道應當在什麼時間還原哪一個數據塊,從而消除傳輸中的抖動。時間戳還可用來使視頻應用中聲音和圖像同步。
在 RTP 協議中並沒有規定時間戳的粒度,這取決於有效載荷的類型,如採樣頻率為 90000 HZ,那麼時間戳單位為 1/90000, 如果每秒發送 30 幀,那麼時間戳增量就是 90000/30 = 3000。
時間戳增量是發送第二個 RTP 包相距發送第一個 RTP 包時的時間間隔,如果是視頻應該是發送每幀的間隔時間。
代碼#
RTP header
/*
* RTP header
*/
typedef struct
{
#if 0 //BIG_ENDIA
unsigned int version:2; /* protocol version */
unsigned int p:1; /* padding flag */
unsigned int x:1; /* header extension flag */
unsigned int cc:4; /* CSRC count */
unsigned int m:1; /* marker bit */
unsigned int pt:7; /* payload type */
unsigned int seq:16; /* sequence number */
#else
unsigned int cc:4; /* CSRC count */
unsigned int x:1; /* header extension flag */
unsigned int p:1; /* padding flag */
unsigned int version:2; /* protocol version */
unsigned int pt:7; /* payload type */
unsigned int m:1; /* marker bit */
unsigned int seq:16; /* sequence number */
#endif
u_int32 ts; /* timestamp */
u_int32 ssrc; /* synchronization source */
u_int32 csrc[1]; /* optional CSRC list */
} rtp_hdr_t;
RTCP Common header
/*
* RTCP common header word
*/
typedef struct {
#if 0 //BIG_ENDIA
unsigned int version:2; /* protocol version */
unsigned int p:1; /* padding flag */
unsigned int count:5; /* varies by packet type */
#else
unsigned int count:5; /* varies by packet type */
unsigned int p:1; /* padding flag */
unsigned int version:2; /* protocol version */
#endif
unsigned int pt:8; /* RTCP packet type */
unsigned short length; /* pkt len in words, w/o this word */
} rtcp_common_t;