一 從TS流開始
最近開始學習數字電視機頂盒的開發,從MPEG-2到DVB,看着看着突然就出現了一大堆表格,什麼PAT、PMT、CAT……如此多的表該怎樣深入瞭解呢?
我們知道,數字電視機頂盒接收到的是一段段的碼流,我們稱之爲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組合(複合)而成的單一數據流(用於數據傳輸)。
(二)
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的定義
typedef struct TS_PAT_Program
{
unsigned program_number : 16; //節目號
unsigned program_map_PID : 13; // 節目映射表的PID,節目號大於0時對應的PID,每個節目對應一個
}TS_PAT_Program
3、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;
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。
通過一段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 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個字節 |
|
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,節目映射表)
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的時候進行過濾即可實現。
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;
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表的解析
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;
}
老樣子,還是通過分析一段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可以提供的信息包括:
三、從PAT開始,走向更遠(1) 該節目是否在播放中
(2) 該節目是否被加密
(3) 該節目的名稱