總括: 一幀視頻數據可以編碼成多個H264的NALU, 每個NALU的開頭爲00 00 00 01; 一個RTP包可以傳送 部分、一個或多個 NALU,看NALU的大小而定。
之前寫過一篇文章,分析了h264使用rtp進行封包的格式介紹:RTP封裝h264 (見下面)。但裏面好像沒有把拆分以及一些需要注意的情況說清楚,因此這裏做補充,也作爲自己的備忘(自己記性好像不太好)。
關於時間戳,需要注意的是h264的採樣率爲90000HZ(被標準固定死的,爲了方便轉換成npt時間,見維基百科:https://en.wikipedia.org/wiki/RTP_audio_video_profile),因此時間戳的單位爲1(秒)/90000,因此如果當前視頻幀率爲25fps,那時間戳間隔或者說增量應該爲3600,如果幀率爲30fps,則增量爲3000,以此類推。
關於h264拆包,按照FU-A方式說明:
1)第一個FU-A包的FU indicator:F應該爲當前NALU頭的F,而NRI應該爲當前NALU頭的NRI,Type則等於28,表明它是FU-A包。FU header生成方法:S = 1,E = 0,R = 0,Type則等於NALU頭中的Type。
2)後續的N個FU-A包的FU indicator和第一個是完全一樣的,如果不是最後一個包,則FU header應該爲:S = 0,E = 0,R = 0,Type等於NALU頭中的Type。
3)最後一個FU-A包FU header應該爲:S = 0,E = 1,R = 0,Type等於NALU頭中的Type。
因此總結就是:同一個NALU分包厚的FU indicator頭是完全一致的,FU header只有S以及E位有區別,分別標記開始和結束,它們的RTP分包的序列號應該是依次遞增的,並且它們的時間戳必須一致,而負載數據爲NALU包去掉1個字節的NALU頭後對剩餘數據的拆分,這點很關鍵,你可以認爲NALU頭被拆分成了FU indicator和FU header,所以不再需要1字節的NALU頭了。
關於SPS以及PPS,配置幀的傳輸我採用了先發SPS,再發送PPS,並使用同樣的時間戳,或者按照正常時間戳增量再或者組包發送的形式處理貌似都可以,看播放器怎麼解碼了,另外提一下,如果我們使用vlc進行播放的話,可以在sdp文件中設置SPS以及PPS,這樣就可以不用發送它們了。
使用VLC播放時,sdp文件中的分包模式選項:packetization-mode=1,否則有問題。另外sdp裏面設置的編碼type必須和rtp包中的一致。
轉自 http://blog.csdn.net/jwybobo2007/article/details/7235942
項目中的總結
(1) FU-A 還原的時候,也是0x00 00 00 01 開始,不需要自己額外添加0x00 00 00 01
(2) FU-A 的的解析,start end等數據要解析好
(3) single nal unit 也是以0x00 00 00 01開始,也不需要自己添加分隔符
---------------------------------------------------------------------------------------------------------------------------------------------
h264 NALU格式
網絡抽象層單元類型 (NALU): 一個原始的 H.264 NALU 單元常由 [Start Code] [NALU Header] [NALU Payload] 三部分組成,形如 00 00 00 01 67 42 A0 1E 23, 則67是nalu header, 42之後的是payload(有效的原始視頻數據).
有些代碼中的 nal_octet 變量指的就是 nalu header 字節。
NALU頭(NALU Header)由一個字節組成,它的語法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
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 單一時間的組合包
25 STAP-B 單一時間的組合包
26 MTAP16 多個時間的組合包
27 MTAP24 多個時間的組合包
28 FU-A 分片的單元
29 FU-B 分片的單元
30-31 沒有定義
h264僅用1-23, 24以後的用在RTP H264負載類型頭中(即24以後在rtp中使用)
---------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------
RTP格式:
總括:[RTP Packet] = [RTP Header] + [RTP PayLoad]
RTP 格式頭的結構:
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|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
負載類型 Payload type(PT): 7bits (請和H264 NALU的payload區別開來,H264的payload指的是純視頻編碼數據,這裏的payload type指的是rtp的載荷類型)
rfc裏面對一些早期的格式定義了這個payload
type。但是後來的,如h264並沒有分配,那就用96來代替。因此現在96以上都不表示特定的格式,具體表示什麼要用sdp或者其他協議來協商。
序列號 Sequence number(SN): 16bits
時間戳 Timestamp: 32bits (rtp打包時使用的時間戳都是pts,因爲dts是解碼器根據I、P、B幀的順序自己設定的解碼順序,而pts是編碼時按照顯示順序輸出的幀序列,幾乎每個編碼器都會按照顯示順序輸出幀序 列:如 I B1 B2 B3 P4 B5 B6 B7 B8 B9 P10;而dts是解碼器重新排列幀順序後的解碼順序,前面的解碼順序[dts]爲: I P4 B1 B2 B3 P10 B5 B6 B7 B8 B9)
RTP 格式Payload的結構,下面介紹的全部都是RTP PayLoad的部分,即RTP PayLoad 封包介紹:
單一NAL單元模式
對於 NALU 的長度小於 MTU 大小的包, 一般採用單一 NAL 單元模式.
對於一個原始的 H.264 NALU 單元常由 [Start Code] [NALU Header] [NALU Payload] 三部分組成, 其中 Start Code 用於標示這是一個
NALU 單元的開始, 必須是 "00 00 00 01" 或 "00 00 01", NALU 頭僅一個字節, 其後都是 NALU 單元內容.
打包時去除 "00 00 01" 或 "00 00 00 01" 的開始碼, 把其他數據封包的 RTP 包即可. 以下即是一個被打包進rtp的NALU單元(不包含rtp頭),第一個字節是NALU頭:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
例:
如有一個 H.264 的 NALU 是這樣的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
這是一個序列參數集 NAL 單元. [00 00 00 01] 是四個字節的開始碼, 67 是 NALU 頭, 42 開始的數據是 NALU 內容.
封裝成 RTP 包將如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 個字節的開始碼就可以了.
組合封包模式
其次, 當 NALU 的長度特別小時, 可以把幾個 NALU 單元封在一個 RTP 包中.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Data |
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 Data |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
這裏只介紹STAP-A模式,如果是STAP-B的話會多加入一個DON域,另外還有MTAP16、MTAP24,具體不介紹,可以看rfc文檔,文章尾貼一個鏈接可以去看。
轉載的話註明一下作者:jwybobo2007 出處:http://blog.csdn.net/jwybobo2007/article/details/7054140
例:
如有一個 H.264 的 NALU 是這樣的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
[00 00 00 01 68 42 B0 12 58 6A D4 FF ... ]
封裝成 RTP 包將如下:
[ RTP Header ] [78 (STAP-A頭,佔用1個字節)] [第一個NALU長度 (佔用兩個字節)] [ 67 42 A0 1E 23 56 0E 2F ] [第二個NALU長度 (佔用兩個字節)] [68 42 B0 12 58 6A D4 FF ... ]
分片的單元:
當NALU的長度超過MTU時,就必須對NALU單元進行分片封包.也稱爲Fragmentation Units(FUs).
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 14. RTP payload format for FU-A
The FU indicator octet has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
別被名字嚇到這個格式就是上面提到的RTP h264負載類型,Type爲FU-A
The FU header has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
S bit爲1表示分片的NAL開始,當它爲1時,E不能爲1
E bit爲1表示結束,當它爲1,S不能爲1
R bit保留位
Type就是NALU頭中的Type,取1-23的那個值
H.264 RTP Streaming
1.H.264 raw data
以00 00 01 或 00 00 00 01作為開頭(Start Code),接著是8 bit NALU
NALU的format
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
F : forbidden zero bit, 一定為0
NRI : nal_ref_idc, 表示資料的重要性, 00為最不重要.
Type :nal_unit_type, H.264只定義1~23的範圍
一個H.264 raw data看起來像這樣
00 00 00 01 09 30 ......
2.RTP header
因為一個H.264 video frame資料的大小往往會在數k bytes到數十K bytes,
在傳送封包時就會將資料切割分別封裝,也因此需要加入一些額外的參數讓
接收端可以正確組合被分割的video frame.這也是RFC3984最主要的目的.
RTP header 中有三個參數要注意
timestamp : 以90KHz作為基準,以30 fps為例,timestamp遞增 90000 / 30.
實務上是以payload實際間隔時間作計算.同一個video frame的
分割資料timestamp是相同的
sequence : 每個RTP封包sequence number都遞增.
mark bit : RTP封包封裝的是最後一個分割的video frame時mark bit 為 1.
2.Payload format
1~23 : Single NAL unit packet.
RFC3984使用了H.264 NALU中未定義的type 24~29 (相當於增加H.264 nal_unit_type定義)
24 : STAP-A 單一時間組合
25 : STAP-B 單一時間組合
26 : MTAP16 多個時間組合
27 : MTAP32 多個時間組合
28 : FU-A 分割資料
29 : FU-B 分割資料
比較常見的是28,29.
3.Single NAL unit packet
當資料少於MTU的大小就用此方式封裝.
H.264 raw data foramt 為 [Start code][NALU][Raw Data]
封裝時去掉Start Code即可.Format 如下
[RTP Header][NALU][Raw Date]
4.FU-A (Fragmentation unit)
當資料大於MTU以此方式分割
H.264 raw data foramt 為 [Start code][NALU][Raw Data]
去掉[Start code],[NALU],以不超過MTU大小分割[Raw Data]
以NALU產生FU indicator, FU Header.
RFC 3984
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 14. RTP payload format for FU-A
The FU indicator octet has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
The FU header has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
[FU indicator] = (NALU & 0x60) | 28;
[FU Header] = (start << 7) | (end << 6) | (NALU & 0x1f);
format如下
[RTP Header][FU indicator][FU header][Raw Data Part 0]
[RTP Header][FU indicator][FU header][Raw Data Part 1]
[RTP Header][FU indicator][FU header][Raw Data Part 2]
...
5.FU-B (Fragmentation unit)
RFC 3984
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | DON |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 15. RTP payload format for FU-B
format如下
[RTP Header][FU indicator][FU header][DON][Raw Data Part 0]
[RTP Header][FU indicator][FU header][DON][Raw Data Part 1]
[RTP Header][FU indicator][FU header][DON][Raw Data Part 2]