ts流最早應用於數字電視領域,其格式非常復雜包含的配置信息表多達十幾個,視頻格式主要是mpeg2。蘋果公司發明的http live stream流媒體是基於ts文件的,不過他大大簡化了傳統的ts流,只需要2個最基本的配置表PAT和PMT,再加上音視頻內容就可以了,hls流媒體視頻編碼的主要格式為h264/mpeg4,音頻為aac/mp3。
ts文件分為三層:ts層(Transport Stream)、pes層(Packet Elemental Stream)、es層(Elementary Stream)。es層就是音視頻數據,pes層是在音視頻數據上加了時間戳等對數據幀的說明信息,ts層是在pes層上加入了數據流識別和傳輸的必要信息。
1.ts層
ts包大小固定為188字節,ts層分為三個部分:ts header、adaptation field、payload。ts header固定4個字節;adaptation field可能存在也可能不存在,主要作用是給不足188字節的數據做填充;payload是pes數據。
1.1.ts header
sync_byte | 8bit | 同步字節,固定為0x47 |
transport_error_indicator | 1bit | 傳輸錯誤指示符,表明在ts頭的adapt域后由一個無用字節,通常都為0,這個字節算在adapt域長度內 |
payload_unit_start_indicator | 1bit | 負載單元起始標示符,一個完整的數據包開始時標記為1 |
transport_priority | 1bit | 傳輸優先級,0為低優先級,1為高優先級,通常取0 |
pid | 13bit | pid值(Packet ID號碼,唯一的號碼對應不同的包) |
transport_scrambling_control | 2bit | 傳輸加擾控制,00表示未加密 |
adaptation_field_control | 2bit | 是否包含自適應區,‘00’保留;‘01’為無自適應域,僅含有效負載;‘10’為僅含自適應域,無有效負載;‘11’為同時帶有自適應域和有效負載。 |
continuity_counter | 4bit | 遞增計數器,從0-f,起始值不一定取0,但必須是連續的 |
ts層的內容是通過PID值來標識的,主要內容包括:PAT表、PMT表、音頻流、視頻流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音視頻流了。PAT表的PID值固定為0。PAT表和PMT表需要定期插入ts流,因為用戶隨時可能加入ts流,這個間隔比較小,通常每隔幾個視頻幀就要加入PAT和PMT。PAT和PMT表是必須的,還可以加入其它表如SDT(業務描述表)等,不過hls流只要有PAT和PMT就可以播放了。
- PAT表:他主要的作用就是指明了PMT表的PID值。
- PMT表:他主要的作用就是指明了音視頻流的PID值。
- 音頻流/視頻流:承載音視頻內容。
表 |
PID 值 |
PAT |
0x0000 |
CAT |
0x0001 |
TSDT |
0x0002 |
EIT,ST |
0x0012 |
RST,ST |
0x0013 |
TDT,TOT,ST |
0x0014 |
下面以一個TS流的其中一個Packet中的Packet Header為例進行說明:
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
Packet(十六進制) |
4 |
7 |
0 |
7 |
e |
5 |
1 |
2 |
… |
||||||||||||||||||||||||
Packet(二進制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
… |
Packet Header 信息 |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x07e5 |
6 |
7 |
8 |
… |
sync_byte=01000111, 就是0x47,這是DVB TS規定的同步字節,固定是0x47.
transport_error_indicator=0, 表示當前包沒有發生傳輸錯誤.
payload_unit_start_indicator=0, 含義參考ISO13818-1標准文檔
transport_priority=0, 表示當前包是低優先級.
PID=00111 11100101即0x07e5, Video PID
transport_scrambling_control=00, 表示節目沒有加密
adaptation_field_control=01 即0x01,具體含義請參考ISO13818-1
continuity_counte=0010 即0x02,表示當前傳送的相同類型的包是第3個
1.2.adaption
adaptation_field_length | 1B | 自適應域長度,后面的字節數 |
flag | 1B | 取0x50表示包含PCR或0x40表示不包含PCR |
PCR | 5B | Program Clock Reference,節目時鍾參考,用於恢復出與編碼端一致的系統時序時鍾STC(System Time Clock)。 |
stuffing_bytes | xB | 填充字節,取值0xff |
自適應區的長度要包含傳輸錯誤指示符標識的一個字節。pcr是節目時鍾參考,pcr、dts、pts都是對同一個系統時鍾的采樣值,pcr是遞增的,因此可以將其設置為dts值,音頻數據不需要pcr。如果沒有字段,ipad是可以播放的,但vlc無法播放。打包ts流時PAT和PMT表是沒有adaptation field的,不夠的長度直接補0xff即可。視頻流和音頻流都需要加adaptation field,通常加在一個幀的第一個ts包和最后一個ts包里,中間的ts包不加。
1.3.PAT格式(Program Association Table,節目關聯表)
PAT表定義了當前TS流中所有的節目,其PID為0x0000,它是PSI的根節點,要查尋找節目必須從PAT表開始查找。
table_id | 8b | PAT表固定為0x00 |
section_syntax_indicator | 1b | 固定為1 |
zero | 1b | 固定為0 |
reserved | 2b | 固定為11 |
section_length | 12b | 后面數據的長度 |
transport_stream_id | 16b | 傳輸流ID,固定為0x0001 |
reserved | 2b | 固定為11 |
version_number | 5b | 版本號,固定為00000,如果PAT有變化則版本號加1 |
current_next_indicator | 1b | 固定為1,表示這個PAT表可以用,如果為0則要等待下一個PAT表 |
section_number | 8b | 固定為0x00 |
last_section_number | 8b | 固定為0x00 |
開始循環 | ||
program_number | 16b | 節目號為0x0000時表示這是NIT,節目號為0x0001時,表示這是PMT |
reserved | 3b | 固定為111 |
PID | 13b | 節目號對應內容的PID值 |
結束循環 | ||
CRC32 | 32b | 前面數據的CRC32校驗碼 |
通過一段TS流中一個Packet分析PAT表,這里我們分析一段TS流其中一個Packet的Packet Data部分:
首先給出一個數據包,其數據如下:
Packet Header |
Packet Data |
0x47 0x40 0x00 0x10 |
0000 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff…… ff ff |
分析Packet Header如下表所示:
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
Packet(十六進制) |
4 |
7 |
4 |
0 |
0 |
0 |
1 |
0 |
… |
||||||||||||||||||||||||
Packet(二進制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
… |
Packet Header Bits |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x0000 |
6 |
7 |
8 |
… |
根據包頭數據格式,我們可以知曉整個數據包的屬性,列表如下:
sync_byte |
0x47 |
固定同步字節 |
transport_error_indicator |
“0” |
沒有傳輸錯誤 |
payload_unit_start_indicator |
“1” |
在前4個字節后會有一個調整字節。所以實際數據應該為去除第一個字節后的數據。即上面數據中紅色部分不屬於有效數據包。 |
transport_priority |
“0” |
傳輸優先級低 |
PID |
0x0000 |
PID=0x0000說明數據包是PAT表信息 |
transport_scrambling_control |
“00” |
未加密 |
adaptation_field_control |
“01” |
附加區域控制 |
continuity_counte |
“0000” |
包遞增計數器 |
如上表所示,我們可以知道,首先Packet的Packet Data是PAT信息表,因為其PID為0x0000,並且在包頭后需要除去一個字節才是有效數據(payload_unit_start_indicator="1")。這樣,Packet Data就應該是“00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff”。
Packet Data分析 |
|||||||||||||||||||||||||
第n個字節 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
… |
||||
Packet Data(除去開頭的0x00) |
00 |
b0 |
11 |
00 |
01 |
c1 |
00 |
00 |
00 |
00 |
e0 |
1f |
00 |
01 |
e1 |
00 |
24 |
ac |
48 |
84 |
… |
||||
字段名 |
位 |
具體值 |
次序 |
說明 |
|||||||||||||||||||||
table_id |
8 |
0000 |
第1個字節 0000 0000B(0x00) |
PAT的table_id只能是0x00 |
|||||||||||||||||||||
section_syntax_indicator |
1 |
1 |
第2、3個字節 1011 0000 0001 0001B(0xb0 11) |
段語法標志位,固定為1 |
|||||||||||||||||||||
zero |
1 |
0 |
|
||||||||||||||||||||||
reserved |
2 |
11 |
|
||||||||||||||||||||||
section_length |
12 |
0000 0001 0001B=0x011=17 |
段長度為17字節 |
||||||||||||||||||||||
transport_stream_id |
16 |
0x0001 |
第4、5個字節 0x00 0x01 |
|
|||||||||||||||||||||
reserved |
2 |
11 |
第6個字節 1100 0001B(0xc1) |
|
|||||||||||||||||||||
version_number |
5 |
00000 |
一旦PAT有變化,版本號加1 |
||||||||||||||||||||||
current_next_indicator |
1 |
1 |
當前傳送的PAT表可以使用,若為0則要等待下一個表 |
||||||||||||||||||||||
section_number |
8 |
0x00 |
第7個字節0x00 |
|
|||||||||||||||||||||
last_section_number |
8 |
0x00 |
第8個字節 0x00 |
|
|||||||||||||||||||||
開始循環 |
|||||||||||||||||||||||||
program_number |
16 |
0x0000-第一次 |
2個字節(0x00 00) |
節目號 |
|||||||||||||||||||||
reserved |
3 |
111 |
2個字節 1110 0000 0001 1111B(0xe0 1f) |
|
|||||||||||||||||||||
network_id(節目號為0時) program_map_PID(節目號為其他時) |
13 |
0 0000 0001 1111B=31 -第一次 |
節目號為0x0000時,表示這是NIT,PID=0x001f,即31 節目號為0x0001時,表示這是PMT,PID=0x100,即256 |
||||||||||||||||||||||
結束循環 |
|||||||||||||||||||||||||
CRC_32 |
32 |
-- |
4個字節 |
|
由以上幾個表可以分析出PAT表和PMT表有着內在的聯系。也就是之前提到的。PAT表描述了當前流的NIT(Network Information Table,網絡信息表)中的PID、當前流中有多少不同類型的PMT表及每個PMT表對應的頻道號。
1.4.PMT格式( Program Map Table,節目映射表 )
table_id | 8b | PMT表取值隨意,0x02 |
section_syntax_indicator | 1b | 固定為1 |
zero | 1b | 固定為0 |
reserved | 2b | 固定為11 |
section_length | 12b | 后面數據的長度 |
program_number | 16b | 頻道號碼,表示當前的PMT關聯到的頻道,取值0x0001 |
reserved | 2b | 固定為11 |
version_number | 5b | 版本號,固定為00000,如果PAT有變化則版本號加1 |
current_next_indicator | 1b | 固定為1 |
section_number | 8b | 固定為0x00 |
last_section_number | 8b | 固定為0x00 |
reserved | 3b | 固定為111 |
PCR_PID | 13b | PCR(節目參考時鍾)所在TS分組的PID,指定為視頻PID |
reserved | 4b | 固定為1111 |
program_info_length | 12b | 節目描述信息,指定為0x000表示沒有 |
開始循環 | ||
stream_type | 8b | 流類型,標志是Video還是Audio還是其他數據,h.264編碼對應0x1b,aac編碼對應0x0f,mp3編碼對應0x03 |
reserved | 3b | 固定為111 |
elementary_PID | 13b | 與stream_type對應的PID |
reserved | 4b | 固定為1111 |
ES_info_length | 12b | 描述信息,指定為0x000表示沒有 |
結束循環 | ||
CRC32 | 32b | 前面數據的CRC32校驗碼 |
通過一段TS流中一個Packet分析PMT表,通過分析一段TS流的數據包Packet來學習PMT表。下面給出了一段TS流數據中的一個Packet(十六進制數)
Packet Header |
Packet Data |
0x47 0x43 0xe8 0x12 |
00 02 b0 12 00 01 c1 00 00 e3 e9 f0 00 1b e3 e9 f0 00 f0 af b4 4f ff ff…… ff ff |
首先解析Packet Header,分析如下:
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
Packet(十六進制) |
4 |
7 |
4 |
3 |
e |
8 |
1 |
2 |
… |
||||||||||||||||||||||||
Packet(二進制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
… |
Packet Header Bits |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x03e8 |
6 |
7 |
8 |
… |
Packet Header分析 |
|||
|
Packet Header:0x47 0x40 0x00 0x10 |
||
1 |
sync_byte |
0x47 |
固定同步字節 |
2 |
transport_error_indicator |
“0” |
沒有傳輸錯誤 |
3 |
payload_unit_start_indicator |
“1” |
在前4個字節后會有一個調整字節。所以實際數據應該為去除第一個字節后的數據。 |
4 |
transport_priority |
“0” |
傳輸優先級低 |
5 |
PID |
0x03e8 |
PID=0x03e8說明數據包是PMT表信息 |
6 |
transport_scrambling_control |
“00” |
未加密 |
7 |
adaptation_field_control |
“01” |
附加區域控制 |
8 |
continuity_counte |
“0010” |
包遞增計數器 |
因為payload_unit_start_indicator=‘1’,在解析數據包的時候需要去除Packet Data的第一個字節。下面是對Packet Data的詳細解析:
PMT表的Packet Data分析 |
|||||||||||||||||||||||||
第n個字節 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
… |
||||
Packet Data |
02 |
b0 |
12 |
00 |
01 |
c1 |
00 |
00 |
e3 |
e9 |
f0 |
00 |
1b |
e3 |
e9 |
f0 |
00 |
f0 |
1b |
e3 |
… |
||||
字段名 |
位數 |
具體值 |
次序 |
說明 |
|||||||||||||||||||||
table_id |
8 |
0x02 |
第1個字節 |
|
|||||||||||||||||||||
section_syntax_indicator |
1 |
1B |
第2、3個字節 1011 0000 0001 0010B=0xb012 |
段語法標志 |
|||||||||||||||||||||
zero |
1 |
0B |
|
||||||||||||||||||||||
reserved |
2 |
11B=0x03 |
|
||||||||||||||||||||||
section_length |
12 |
0000 0001 0010B=0x12 |
段長度,從program_number開始,到CRC_32(含)的字節總數 |
||||||||||||||||||||||
program_number |
16 |
0x0001 |
第4、5個字節 0x00 01 |
頻道號碼,表示當前的PMT關聯到的頻道 |
|||||||||||||||||||||
reserved |
2 |
11B=0x03 |
第6個字節 1100 0001B=0xc1 |
|
|||||||||||||||||||||
version_number |
5 |
00000B=0x00 |
版本號碼,如果PMT內容有更新,則它會遞增1通知解復用程序需要重新接收節目信息 |
||||||||||||||||||||||
current_next_indicator |
1 |
1B=0x01 |
當前未來標志符 |
||||||||||||||||||||||
section_number |
8 |
0x00 |
第7個字節0x00 |
當前段號碼 |
|||||||||||||||||||||
last_section_number |
8 |
0x00 |
第8個字節 0x00 |
最后段號碼,含義和PAT中的對應字段相同 |
|||||||||||||||||||||
reserved |
3 |
111B=0x07 |
第9、10個字節 1110 0011 1110 1001B=0xe3e9 |
|
|||||||||||||||||||||
PCR_PID |
13 |
000111110B=0x3e9 |
PCR(節目參考時鍾)所在TS分組的PID |
||||||||||||||||||||||
reserved |
4 |
1111B=0x0f |
第11、12個字節 1111 0000 0000 0000=0xf000 |
|
|||||||||||||||||||||
program_info_length |
12 |
000000000000B=0x000 |
節目信息長度(之后的是N個描述符結構,一般可以忽略掉,這個字段就代表描述符總的長度,單位是Bytes)緊接着就是頻道內部包含的節目類型和對應的PID號碼了 |
||||||||||||||||||||||
stream_type |
8 |
0x1b |
第13個字節 0x1b |
流類型,標志是Video還是Audio還是其他數據 |
|||||||||||||||||||||
reserved |
3 |
111B=0x07 |
第14、15個字節 1110 0011 1110 1001B=0xe3e9 |
|
|||||||||||||||||||||
elementary_PID |
13 |
000111110 1001=0x3e9 |
該節目中包括的視頻流,音頻流等對應的TS分組的PID |
||||||||||||||||||||||
reserved |
4 |
1111B=0x0f |
第16、17個字節 1111 0000 0000 0000B=0xf000 |
|
|||||||||||||||||||||
ES_info_length |
12 |
0000 0000 0000=0x000 |
|
||||||||||||||||||||||
CRC |
32 |
—— |
—— |
|
1.4.pes層
pes層是在每一個視頻/音頻幀上加入了時間戳等信息,pes包內容很多,我們只留下最常用的。
pes start code | 3B | 開始碼,固定為0x000001 |
stream id | 1B | 音頻取值(0xc0-0xdf),通常為0xc0 視頻取值(0xe0-0xef),通常為0xe0 |
pes packet length | 2B | 后面pes數據的長度,0表示長度不限制, 只有視頻數據長度會超過0xffff |
flag | 1B | 通常取值0x80,表示數據不加密、無優先級、備份的數據 |
flag | 1B | 取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
pes data length | 1B | 后面數據的長度,取值5或10 |
pts | 5B | 33bit值 |
dts | 5B | 33bit值 |
1.5.es層
es層就是音視頻裸數據了,常用的音頻編碼格式為AAC,視頻編碼格式為H.264
2.打包H.264和AAC為TS
對於H.264視頻而言,每一幀的時間長度為
frame_duration = 1000/fps
當fps為25時,一幀時間為40ms
對於AAC音頻而言,每一幀的時間長度為
音頻幀的播放時間=一個AAC幀對應的采樣樣本的個數/采樣頻率(單位為s)
一幀 1024個 sample。采樣率 Samplerate 44100KHz,每秒44100個sample, 所以根據公式 音頻幀的播放時間=一個AAC幀對應的采樣樣本的個數/采樣頻率
當前AAC一幀的播放時間是= 1024*1000000/44100= 22.32ms(單位為ms)
由此得到了每一幀數據的持續時間,音視頻交叉存儲在容器中:一個時間軸:
時間軸:0 22.32 40 44.62 66.96 80 89.16 111.48 120 ................
音 頻 :0 22.32 44.62 66.96 89.16 111.48 ................
視 頻 :0 40 80 120 ................
即視頻的持續時間相加 和音頻的持續時間相加作比較,誰小寫入哪個。