- 一 從TS流開始
- 二 從TS流到PATPMT
- 一 PAT表Program Association Table節目關聯表
- 1 PAT表的描述表格分析
- 2 PAT表的定義代碼分析
- 3 PAT表的結構代碼分析
- 4 PAT表的解析代碼分析
- 5 通過一段TS流中一個Packet分析PAT表表格分析
- 6 過濾PAT表信息的偽代碼代碼
- 二 PMT表Program Map Table節目映射表Service Descriptor Table
- 三 解復用模型代碼
- 四 DVB搜台原理以及SDT表Service Descriptor Table業務描述表
- 三 從PAT開始走向更遠
一 從TS流開始
數字電視機頂盒接收到的是一段段的碼流,我們稱之為TS(Transport Stream,傳輸流),每個TS流都攜帶一些信息,如Video、Audio以及我們需要學習的PAT、PMT等信息。因此,我們首先需要了解TS流是什么,以及TS流是怎樣形成、有着怎樣的結構。
(一) TS流、PS流、PES流和ES流都是什么?
ES流(Elementary Stream):基本碼流,不分段的音頻、視頻或其他信息的連續碼流。
PES流:把基本流ES分割成段,並加上相應頭文件打包成形的打包基本碼流。
PS流(Program Stream):節目流,將具有共同時間基准的一個或多個PES組合(復合)而成的單一數據流(用於播放或編輯系統,如m2p)。
TS流(Transport Stream):傳輸流,將具有共同時間基准或獨立時間基准的一個或多個PES組合(復合)而成的單一數據流(用於數據傳輸)。
*NOTE:TS流和PS流的區別:TS流的包結構是長度是固定的;PS流的包結構是可變長度的。這導致了TS流的抵抗傳輸誤碼的能力強於PS流(TS碼流由於采用了固定長度的包結構,當傳輸誤碼破壞了某一TS包的同步信息時,接收機可在固定的位置檢測它后面包中的同步信息,從而恢復同步,避免了信息丟失。而PS包由於長度是變化的,一旦某一 PS包的同步信息丟失,接收機無法確定下一包的同步位置,就會造成失步,導致嚴重的信息丟失。因此,在信道環境較為惡劣,傳輸誤碼較高時,一般采用TS碼流;而在信道環境較好,傳輸誤碼較低時,一般采用PS碼流。)
由於TS碼流具有較強的抵抗傳輸誤碼的能力,因此目前在傳輸媒體中進行傳輸的MPEG-2碼流基本上都采用了TS碼流的包格。
(二) TS流是如何產生的?
從上圖可以看出,視頻ES和音頻ES通過打包器和共同或獨立的系統時間基准形成一個個PES,通過TS復用器復用形成的傳輸流。注意這里的TS流是位流格式(分析Packet的時候會解釋),也即是說TS流是可以按位讀取的。
(三) TS流的格式是怎樣的?
TS流是基於Packet的位流格式,每個包是188個字節(或204個字節,在188個字節后加上了16字節的CRC校驗數據,其他格式一樣)。整個TS流組成形式如下:
Packet Header(包頭)信息說明 |
|||
1 |
sync_byte |
8bits |
同步字節 |
2 |
transport_error_indicator |
1bit |
錯誤指示信息(1:該包至少有1bits傳輸錯誤) |
3 |
payload_unit_start_indicator |
1bit |
負載單元開始標志(packet不滿188字節時需填充) |
4 |
transport_priority |
1bit |
傳輸優先級標志(1:優先級高) |
5 |
PID |
13bits |
Packet ID號碼,唯一的號碼對應不同的包 |
6 |
transport_scrambling_control |
2bits |
加密標志(00:未加密;其他表示已加密) |
7 |
adaptation_field_control |
2bits |
附加區域控制 |
8 |
continuity_counter |
4bits |
包遞增計數器 |
PID是TS流中唯一識別標志,Packet Data是什么內容就是由PID決定的。如果一個TS流中的一個Packet的Packet Header中的PID是0x0000,那么這個Packet的Packet Data就是DVB的PAT表而非其他類型數據(如Video、Audio或其他業務信息)。下表給出了一些表的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個
TS流的基本內容就是這些了。
回顧一下,TS流是一種位流(當然就是數字的),它是由ES流分割成PES后復用而成的;它經過網絡傳輸被機頂盒接收到;數字電視機頂盒接收到TS流后將解析TS流。
TS流是由一個個Packet(包)構成的,每個包都是由Packet Header(包頭)和Packet Data(包數據)組成的。其中Packet Header指示了該Packet是什么屬性的,並給出了該Packet Data的數據的唯一網絡標識符PID。
到這里,我們對TS流已經有了一定的了解,下面將從TS流轉向PAT表和PMT表的學習。
二 從TS流到PAT、PMT
說完了TS流的基本概念,就該開始對TS流進行更深入的研究了。首先需要想一想:TS流的本質是什么?它的確是一段碼流,並且是一段由數據包(Packet)組成的碼流。那么這些數據包究竟是怎樣的呢?它和我們收看的電視節目之間又有什么區別?這些都是這部分需要了解的內容。
在上一節中,我們可以看到PID這個被標紅的字段頻繁地出現。PID是當前TS流的Packet區別於其他Packet類型的唯一識別符,通過讀取每個包的Packet Header,我們可以知道這個Packet的數據屬於何種類型。上一節列出了幾項固定的PID值,它們用於識別存儲了特殊信息的Packet。下面要談的PAT表的PID值就是固定的0x0000。
(一) PAT表(Program Association Table,節目關聯表)
由於下面的內容比較繁雜,這里先給出一個大綱,方便查閱:
1. PAT表的描述(表格+分析)
2. PAT表的定義(代碼+分析)
3. PAT表的結構(代碼+分析)
4. PAT表的解析(代碼+分析)
5. 通過一段TS流中一個Packet分析PAT表(表格+分析)
下面,開始正式的分析!
1. PAT表的描述(表格+分析)
PAT表定義了當前TS流中所有的節目,其PID為0x0000,它是PSI的根節點,要查尋找節目必須從PAT表開始查找。
PAT表攜帶以下信息:
TS流ID |
transport_stream_id |
該ID標志唯一的流ID |
節目頻道號 |
program_number |
該號碼標志TS流中的一個頻道,該頻道可以包含很多的節目(即可以包含多個Video PID和Audio PID) |
PMT的PID |
program_map_PID |
表示本頻道使用哪個PID做為PMT的PID,因為可以有很多的頻道,因此DVB規定PMT的PID可以由用戶自己定義 |
2. PAT表的定義(代碼+分析)
PAT表主要包含頻道號碼和每一個頻道對應的PMT的PID號碼,這些信息我們在處理PAT表格的時候會保存起來,以后會使用到這些數據。下面將PAT表的定義給出:
- typedef struct TS_PAT_Program
- {
- unsigned program_number : 16; //節目號
- unsigned program_map_PID : 13; // 節目映射表的PID,節目號大於0時對應的PID,每個節目對應一個
- }TS_PAT_Program
3. PAT表的結構(代碼+分析)
再將PAT表的結構體給出:
- typedef struct TS_PAT
- {
- unsigned table_id : 8; //固定為0x00 ,標志是該表是PAT表
- unsigned section_syntax_indicator : 1; //段語法標志位,固定為1
- unsigned zero : 1; //0
- unsigned reserved_1 : 2; // 保留位
- unsigned section_length : 12; //表示從下一個字段開始到CRC32(含)之間有用的字節數
- unsigned transport_stream_id : 16; //該傳輸流的ID,區別於一個網絡中其它多路復用的流
- unsigned reserved_2 : 2;// 保留位
- unsigned version_number : 5; //范圍0-31,表示PAT的版本號
- unsigned current_next_indicator : 1; //發送的PAT是當前有效還是下一個PAT有效
- unsigned section_number : 8; //分段的號碼。PAT可能分為多段傳輸,第一段為00,以后每個分段加1,最多可能有256個分段
- unsigned last_section_number : 8; //最后一個分段的號碼
- std::vector<TS_PAT_Program> program;
- unsigned reserved_3 : 3; // 保留位
- unsigned network_PID : 13; //網絡信息表(NIT)的PID,節目號為0時對應的PID為network_PID
- unsigned CRC_32 : 32; //CRC32校驗碼
- } TS_PAT;
4. PAT表的解析(代碼+分析)
接下來給出的是PAT表的解析代碼:
- HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)
- {
- packet->table_id = buffer[0];
- packet->section_syntax_indicator = buffer[1] >> 7;
- packet->zero = buffer[1] >> 6 & 0x1;
- packet->reserved_1 = buffer[1] >> 4 & 0x3;
- packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
- packet->transport_stream_id = buffer[3] << 8 | buffer[4];
- packet->reserved_2 = buffer[5] >> 6;
- packet->version_number = buffer[5] >> 1 & 0x1F;
- packet->current_next_indicator = (buffer[5] << 7) >> 7;
- packet->section_number = buffer[6];
- packet->last_section_number = buffer[7];
- int len = 0;
- len = 3 + packet->section_length;
- packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
- | (buffer[len-3] & 0x000000FF) << 16
- | (buffer[len-2] & 0x000000FF) << 8
- | (buffer[len-1] & 0x000000FF);
- int n = 0;
- for ( n = 0; n < packet->section_length - 12; n += 4 )
- {
- unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ];
- packet->reserved_3 = buffer[10 + n ] >> 5;
- packet->network_PID = 0x00;
- if ( program_num == 0x00)
- {
- packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
- TS_network_Pid = packet->network_PID; //記錄該TS流的網絡PID
- TRACE(" packet->network_PID %0x /n/n", packet->network_PID );
- }
- else
- {
- TS_PAT_Program PAT_program;
- PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];
- PAT_program.program_number = program_num;
- packet->program.push_back( PAT_program );
- TS_program.push_back( PAT_program );//向全局PAT節目數組中添加PAT節目信息
- }
- }
- return 0;
- }
從for()開始,就是描述了當前流中的頻道數目(N),每一個頻道對應的PMT PID是什么。解復用程序需要接收所有的頻道號碼和對應的PMT 的PID,並把這些信息在緩沖區中保存起來。在后部的處理中需要使用到PMT的 PID。
5. 通過一段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表對應的頻道號。而PAT表和PMT表到底有什么深層次的聯系呢?在討論完了PMT表和SDT表后再做討論吧。
6. 過濾PAT表信息的偽代碼(代碼)
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- { int I; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } // 如果PID為0x0000,則該Packet Data為PAT信息,因此調用處理PAT表的函數
- else{ // 這里buff+4 意味着從Packet Header之后進行解析(包頭占4個字節)
- ……
- }
- }
(二) PMT表(Program Map Table,節目映射表)(Service Descriptor Table)
1. PMT表的描述
如果一個TS流中含有多個頻道,那么就會包含多個PID不同的PMT表。
PMT表中包含的數據如下:
(1) 當前頻道中包含的所有Video數據的PID
(2) 當前頻道中包含的所有Audio數據的PID
(3) 和當前頻道關聯在一起的其他數據的PID(如數字廣播,數據通訊等使用的PID)
只要我們處理了PMT,那么我們就可以獲取頻道中所有的PID信息,如當前頻道包含多少個Video、共多少個Audio和其他數據,還能知道每種數據對應的PID分別是什么。這樣如果我們要選擇其中一個Video和Audio收看,那么只需要把要收看的節目的Video PID和Audio PID保存起來,在處理Packet的時候進行過濾即可實現。
2. PMT表的定義(代碼)
- <span style="font-size:14px;">//PMT 表定義</span>
- <span style="font-size:14px;">typedef struct TS_PMT_Stream
- {
- unsigned stream_type : 8; //指示特定PID的節目元素包的類型。該處PID由elementary PID指定
- unsigned elementary_PID : 13; //該域指示TS包的PID值。這些TS包含有相關的節目元素
- unsigned ES_info_length : 12; //前兩位bit為00。該域指示跟隨其后的描述相關節目元素的byte數
- unsigned descriptor;
- }TS_PMT_Stream;
- </span>
3. PMT表的結構體定義(代碼)
- //PMT 表結構體
- typedef struct TS_PMT
- {
- unsigned table_id : 8; //固定為0x02, 表示PMT表
- unsigned section_syntax_indicator : 1; //固定為0x01
- unsigned zero : 1; //0x01
- unsigned reserved_1 : 2; //0x03
- unsigned section_length : 12;//首先兩位bit置為00,它指示段的byte數,由段長度域開始,包含CRC。
- unsigned program_number : 16;// 指出該節目對應於可應用的Program map PID
- unsigned reserved_2 : 2; //0x03
- unsigned version_number : 5; //指出TS流中Program map section的版本號
- unsigned current_next_indicator : 1; //當該位置1時,當前傳送的Program map section可用;
- //當該位置0時,指示當前傳送的Program map section不可用,下一個TS流的Program map section有效。
- unsigned section_number : 8; //固定為0x00
- unsigned last_section_number : 8; //固定為0x00
- unsigned reserved_3 : 3; //0x07
- unsigned PCR_PID : 13; //指明TS包的PID值,該TS包含有PCR域,
- //該PCR值對應於由節目號指定的對應節目。
- //如果對於私有數據流的節目定義與PCR無關,這個域的值將為0x1FFF。
- unsigned reserved_4 : 4; //預留為0x0F
- unsigned program_info_length : 12; //前兩位bit為00。該域指出跟隨其后對節目信息的描述的byte數。
- std::vector<TS_PMT_Stream> PMT_Stream; //每個元素包含8位, 指示特定PID的節目元素包的類型。該處PID由elementary PID指定
- unsigned reserved_5 : 3; //0x07
- unsigned reserved_6 : 4; //0x0F
- unsigned CRC_32 : 32;
- } TS_PMT;
4. PMT表的解析(代碼)
- //PMT 表的解析
- HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )
- {
- packet->table_id = buffer[0];
- packet->section_syntax_indicator = buffer[1] >> 7;
- packet->zero = buffer[1] >> 6 & 0x01;
- packet->reserved_1 = buffer[1] >> 4 & 0x03;
- packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
- packet->program_number = buffer[3] << 8 | buffer[4];
- packet->reserved_2 = buffer[5] >> 6;
- packet->version_number = buffer[5] >> 1 & 0x1F;
- packet->current_next_indicator = (buffer[5] << 7) >> 7;
- packet->section_number = buffer[6];
- packet->last_section_number = buffer[7];
- packet->reserved_3 = buffer[8] >> 5;
- packet->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;
- PCRID = packet->PCR_PID;
- packet->reserved_4 = buffer[10] >> 4;
- packet->program_info_length = (buffer[10] & 0x0F) << 8 | buffer[11];
- // Get CRC_32
- int len = 0;
- len = packet->section_length + 3;
- packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
- | (buffer[len-3] & 0x000000FF) << 16
- | (buffer[len-2] & 0x000000FF) << 8
- | (buffer[len-1] & 0x000000FF);
- int pos = 12;
- // program info descriptor
- if ( packet->program_info_length != 0 )
- pos += packet->program_info_length;
- // Get stream type and PID
- for ( ; pos <= (packet->section_length + 2 ) - 4; )
- {
- TS_PMT_Stream pmt_stream;
- pmt_stream.stream_type = buffer[pos];
- packet->reserved_5 = buffer[pos+1] >> 5;
- pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;
- packet->reserved_6 = buffer[pos+3] >> 4;
- pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];
- pmt_stream.descriptor = 0x00;
- if (pmt_stream.ES_info_length != 0)
- {
- pmt_stream.descriptor = buffer[pos + 5];
- for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )
- {
- pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];
- }
- pos += pmt_stream.ES_info_length;
- }
- pos += 5;
- packet->PMT_Stream.push_back( pmt_stream );
- TS_Stream_type.push_back( pmt_stream );
- }
- return 0;
- }
5. 通過一段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 |
… |
PID=0x03e8為其PID
下面是詳細的解析表
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 |
—— |
—— |
|
(三) 解復用模型(代碼)
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- {
- int i; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } //PAT表的PID為0x0000
- else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); } //PID指示該數據包為視頻包
- else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); } //PID指示該數據包為音頻包
- else{ // buff+4 意味着要除去buff前4個字節(即包頭)
- for( i=0;i<64;i++)
- { if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; }
- } } }
解復用的意義在於,由於TS流是一種復用的碼流,里面混雜了多種類型的包;解復用TS流可以將類型相同的Packet存入相同緩存,分別處理。這樣就可以將Video、Audio或者其他業務信息的數據區分開來。
(四) DVB搜台原理以及SDT表(Service Descriptor Table,業務描述表)
機頂盒先調整高頻頭到一個固定的頻率(如498MHZ),如果此頻率有數字信號,則COFDM芯片(如MT352)會自動把TS流數據傳送給MPEG- 2 decoder。 MPEG-2 decoder先進行數據的同步,也就是等待完整的Packet的到來.然后循環查找是否出現PID== 0x0000的Packet,如果出現了,則馬上進入分析PAT的處理,獲取了所有的PMT的PID。接着循環查找是否出現PMT,如果發現了,則自動進入PMT分析,獲取該頻段所有的頻道數據並保存。如果沒有發現PAT或者沒有發現PMT,說明該頻段沒有信號,進入下一個頻率掃描。
在解析TS流的時候,首先尋找PAT表,根據PAT獲取所有PMT表的PID;再尋找PMT表,獲取該頻段所有節目數據並保存。這樣,只需要知道節目的PID就可以根據PacketHeade給出的PID過濾出不同的Packet,從而觀看不同的節目。這些就是PAT表和PMT表之間的關系。而由於PID是一串枯燥的數字,用戶不方便記憶、且容易輸錯,所以需要有一張表將節目名稱和該節目的PID對應起來,DVB設計了SDT表來解決這個問題。 該表格標志一個節目的名稱,並且能和PMT中的PID聯系起來,這樣用戶就可以通過直接選擇節目名稱來選擇節目了。
SDT可以提供的信息包括:
(1) 該節目是否在播放中
(2) 該節目是否被加密
(3) 該節目的名稱
三、 從PAT開始,走向更遠
在本章的學習中,我們發現了一個特點:所有的TS流的解析都是從尋找PAT表開始的,只有找到了PAT表,我們才能繼續下一步的解析。因此,在進行了TS流、PAT表和PMT表的初步知識儲備后,在接下來的學習中將從PAT表開始,學習更多的PSI/SI相關的表,將走得更遠。