使用jrtplib(RTP)傳輸H.264視訊檔案(1)
繼續上一篇部落格,在使用x264對視訊進行編碼之後,我們需要將編碼好的視訊進行網路傳輸,傳送給其他的客戶端,之後再進行解碼。那麼,這篇部落格我將介紹下如何使用jrtplib這個庫對編碼後的檔案傳輸。
RTP協議基礎知識:
首先,我們依然要先了解一些關於協議的基礎知識,不然對於其中的傳輸引數與傳送的資料包順序什麼的我們也就無從下手。在這裡,我們僅對一些傳輸需要了解的關鍵知識進行介紹,若想了解更全面的關於RTP協議的知識,請直接參看RTP協議的rfc文件,另外,這裡有一份翻譯的中文版。或者你可以參考這篇部落格:按照RFC3984協議實現H264視訊流媒體(附原始碼),其中有很多有用的資料。
RTP(Real-time Transport Protocol)協議,詳細說明了在網際網路上傳遞音訊和視訊的標準資料包格式。它一開始被設計為一個多播協議,但後來被用在很多單播應用中,所以這個協議既支援單播又支援多播。RTP協議和RTP控制協議RTCP一起使用,而且它是建立在UDP協議上的。但是網上也有人說如果不使用RTSP協議(不瞭解的話,點
既然RTP協議定義的是一個數據包格式,那麼下面我們來具體研究下對於傳輸H264來說,其資料包時如何定義的。
首先,我們來看RTP資料包的結構,RTP資料包由RTPheader和RTPpayload組成,下圖為RTP資料頭部格式:
圖1,RTP資料包格式
各個欄位代表含義如下:
V:版本號,一般為2;
P:填充欄位標識;
X:擴充套件頭標識;
M:標誌 1bit,在傳輸h264時表示h264 nalu的最後一包
PT: 負載型別 7 bits, H264型別為96
SN:序列號16 bits
Timestamp: 時間戳32bits,如果為視訊的話,應該設定為1/9000,音訊為1/8000
SSRC: 3bits,用以識別同步源。
CSRC:作用我也未搞清楚,我並未使用該欄位。
下面我們來探討RTP的負載包(payload),在此之前,我們必須先了解H264的NALU的負載,對於H264編碼後的資料,經過網路層(NAL)編碼後產生NALU,其格式已在上篇部落格中說明,下面我們介紹naluheader結構,nalu的頭部只有一個位元組,如下圖所示:
其中各個部分定義如下:
F: 1 個位元.
forbidden_zero_bit. 在 H.264 規範中規定了這一位必須為 0.
NRI: 2 個位元.
nal_ref_idc. 取 00 ~ 11, 似乎指示這個 NALU的重要性, 如 00 的 NALU 解碼器可以丟棄它而不影響影象的回放. 不過一般情況下不太關心這個屬性.
Type: 5 個位元.
nal_unit_type. 這個 NALU 單元的型別. 簡述如下:
0 沒有定義
1-23 NAL單元 單個 NAL 單元包.
24 STAP-A 單一時間的組合包
24 STAP-B 單一時間的組合包
26 MTAP16 多個時間的組合包
27 MTAP24 多個時間的組合包
28 FU-A 分片的單元
29 FU-B 分片的單元
30-31 沒有定義
欄位Type:這個RTPpayload 中 NAL 單元的型別. 這個欄位和 H.264 中型別欄位的區別是, 當 type的值為 24 ~ 31 表示這是一個特別格式的 NAL 單元, 而 H.264 中, 只取 1~23 是有效的值.
關於NALU使用RTP包進行傳送可能的型別有:
1. 單一 NAL 單元模式
即一個 RTP 包僅由一個完整的 NALU 組成. 這種情況下 RTP NAL 頭型別欄位和原始的H.264的NALU 頭型別欄位是一樣的.
2. 組合封包模式
即可能是由多個 NAL 單元組成一個 RTP 包. 分別有4種組合方式:STAP-A, STAP-B, MTAP16, MTAP24。那麼這裡的型別值分別是 24,25, 26 以及 27.
3. 分片封包模式
用於把一個 NALU單元封裝成多個 RTP 包. 存在兩種型別 FU-A 和 FU-B. 型別值分別是 28 和 29.
在我的使用中只遇到第1種和第3中情況,因此我就對這兩種進行下詳細介紹:
1. 單一NAL單元傳送:
對於 NALU的長度小於 MTU 大小的包, 一般採用單一 NAL 單元模式.
對於一個原始的H.264 NALU 單元常由 [StartCode] [NALU Header] [NALU Payload] 三部分組成, 其中 Start Code 用於標示這是一個NALU 單元的開始, 必須是"00 00 00 01" 或"00 00 01", NALU 頭僅一個位元組, 其後都是 NALU 單元內容.打包時去除 "00 00 01" 或"00 00 00 01" 的開始碼, 把其他資料封包的 RTP 包即可.有如下例子:
有一個H.264 的 NALU 是這樣的:
[00 0000 01 67 42 A0 1E 23 56 0E 2F ... ]
這是一個序列引數集 NAL 單元. [00 00 00 01] 是四個位元組的開始碼, 67 是 NALU 頭, 42 開始的資料是 NALU 內容.
封裝成 RTP 包將如下:
[ RTPHeader ] [ 67 42 A0 1E 23 56 0E 2F ]
(在這裡要說明的是,如果客戶端是通用的播放器,比如VLC或者JM的話需要將前導碼去掉,但是如果使用的是ffmpeg在客戶端解碼的話,傳送前不需要去掉前導碼,去掉之後可能會導致ffmpeg解碼錯誤)。
2. 分片封包模式
而當 NALU 的長度超過 MTU 時, 就必須對 NALU 單元進行分片封包. 也稱為Fragmentation Units(FUs).將NALU拆分成小於MTU的資料包進行傳送,如果使用的是VLC等網路播放器的話,需要設定FU header,如下圖所示:
但是,仍然注意一點,如果使用的是ffmpeg自行進行資料包接收與解碼,則完全不必寫FU header。
下面稍微介紹下RTCP協議:
RTCP的主要功能是:服務質量的監視與反饋,媒體間的同步,以及多播組中成員的標識。由於RTCP分組很短,因此把多個RTCP分組封裝在一個UDP使用者資料報中。RTCP分組週期性的在網上傳送,它帶有傳送端和接收端對服務質量的統計資訊報告(如已傳送的分組數和位元組數,分組丟失率,分組到達時間間隔的抖動等)。
RTCP可以說是控制交通的協議,它提供了:
1)SR傳送者報告分組:用來使傳送端週期的向所有接收端用多播方式進行報告。內容包括:該RTP流的SSRC;該RTP流中最新產生的RTP分組的時間戳和絕對時鐘時間(或稱牆上時間:wallclock time);該RTP流包含的分組數;該RTP流包含的位元組數。
絕對時鐘時間是必要的。因為RTP要求每一種媒體使用一個流。有了絕對時鐘時間就可以進行圖形和聲音的同步。
2)RR接收者報告分組:用來使接收端週期性的向所有的點用多播方式進行報告。內容包括:所接收到的RTP流的SSRC;該RTP流的分組丟失率;在該RTP流中的最後一個RTP分組的序號;分組到達時間間隔的抖動等。
傳送RR分組有兩個目的:
第一,可以使所有的接收端和傳送端了解當前網路的狀態。
第二,可以使所有傳送RTCP分組的站點自適應的調整自己傳送RTCP分組的速率,RTCP分組的通訊量不超過網路中的資料分組的通訊量的5%,而接收端分組報告分組的通訊量又應小於所有RTCP分組的通訊量的75%。
3)SDES源描述分組:給出會話中參加者的描述,包括參加者的規範名(CNAME)
4)BYE分組:關閉一個數據流。
5)APP分組:應用程式能夠定義新的分組型別。
在這其中我用到4,5分組,其餘的可以用來做QoS,暫時我還沒有用到。另外,關於顯示網路當前狀況的RTCP包是由jrtplib自動傳送的,不需要自己寫程式碼生成,但是如果你要使用這些包做QoS的話,是需要自己新增訊息響應函式來處理的,jrtplib本身並沒有提供這樣的機制。關於怎樣使用jrtplib進行傳輸我在下一篇部落格中進行介紹。