從TS流到PAT和PMT

一 從TS流開始

        最近開始學習數字電視機頂盒的開發,從MPEG-2到DVB,看着看着突然就出現了一大堆表格,什麼PAT、PMT、CAT……如此多的表該怎樣深入瞭解呢?

        我們知道,數字電視機頂盒接收到的是一段段的碼流,我們稱之爲TS(Transport Stream,傳輸流),每個TS流都攜帶一些信息,如Video、Audio以及我們需要學習的PAT、PMT等信息。因此,我們首先需要了解TS流是什麼,以及TS流是怎樣形成、有着怎樣的結構。

(一)TS流、PS流、PES流和ES流都是什麼?


       ESElementary Stream):基本碼流,不分段的音頻、視頻或其他信息的連續碼流。

       PES:把基本流ES分割成段,並加上相應頭文件打包成形的打包基本碼流。

       PSProgram Stream):節目流,將具有共同時間基準的一個或多個PES組合(複合)而成的單一數據流(用於播放或編輯系統,如m2p)。

       TSTransport Stream):傳輸流,將具有共同時間基準或獨立時間基準的一個或多個PES組合(複合)而成的單一數據流(用於數據傳輸)。


(二)

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 111001010x07e5,       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流中所有的節目,其PID0x0000,它是PSI的根節點,要查尋找節目必須從PAT表開始查找。

       PAT表攜帶以下信息:

TSID

transport_stream_id

ID標誌唯一的流ID

節目頻道號

program_number

該號碼標誌TS流中的一個頻道,該頻道可以包含很多的節目(即可以包含多個Video PIDAudio PID)

PMTPID

program_map_PID

表示本頻道使用哪個PID做爲PMTPID,因爲可以有很多的頻道,因此DVB規定PMTPID可以由用戶自己定義

 

2、PAT的定義
PAT表主要包含頻道號碼和每一個頻道對應的PMTPID號碼,這些信息我們在處理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的解析
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  

  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)

PATtable_id只能是0x00

section_syntax_indicator

1

1

 

23個字節

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

45個字節 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,節目映射表)
1、

 PMT表的描述  

 如果一個TS流中含有多個頻道,那麼就會包含多個PID不同的PMT表。

        PMT表中包含的數據如下:

(1) 當前頻道中包含的所有Video數據的PID

(2) 當前頻道中包含的所有Audio數據的PID

(3) 和當前頻道關聯在一起的其他數據的PID(如數字廣播,數據通訊等使用的PID)

        只要我們處理了PMT,那麼我們就可以獲取頻道中所有的PID信息,如當前頻道包含多少個Video、共多少個Audio和其他數據還能知道每種數據對應的PID分別是什麼。這樣如果我們要選擇其中一個VideoAudio收看,那麼只需要把要收看的節目的Video PIDAudio 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;
}

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 Header0x47 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

 

23個字節

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

45個字節 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

910個字節

1110 0011 1110 1001B=0xe3e9

 

PCR_PID

13

000111110B=0x3e9

PCR(節目參考時鐘)所在TS分組的PID

reserved

4

1111B=0x0f

 

 

 

1112個字節

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

1415個字節

1110 0011 1110 1001B=0xe3e9

 

elementary_PID

13

000111110 1001=0x3e9

該節目中包括的視頻流,音頻流等對應的TS分組的PID

reserved

4

1111B=0x0f

1617個字節

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相關的表,將走得更遠。





發佈了85 篇原創文章 · 獲贊 16 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章