TS流講解--什么是ts流


目錄(?)[-]

  1. 一 從TS流開始
    1. 一 TS流PS流PES流和ES流都是什么
    2. 二 TS流是如何產生的
    3. 三 TS流的格式是怎樣的
  2. 二 從TS流到PATPMT
    1. 一 PAT表Program Association Table節目關聯表
      1. 1 PAT表的描述表格分析
      2. 2 PAT表的定義代碼分析
      3. 3 PAT表的結構代碼分析       
      4. 4 PAT表的解析代碼分析
      5. 5 通過一段TS流中一個Packet分析PAT表表格分析
      6. 6 過濾PAT表信息的偽代碼代碼
    2. 二 PMT表Program Map Table節目映射表Service Descriptor Table
      1. 1 PMT表的描述     
      2.  2 PMT表的定義代碼  
      3.  3 PMT表的結構體定義代碼  
      4. 4 PMT表的解析代碼  
      5. 5 通過一段TS流中一個Packet分析PMT表表格分析
    3. 三 解復用模型代碼       
    4. 四 DVB搜台原理以及SDT表Service Descriptor Table業務描述表
  3. 三 從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表的定義給出:

[cpp]  view plain copy
 
 
 
  1. typedef struct TS_PAT_Program  
  2. {  
  3.     unsigned program_number   :  16;  //節目號  
  4.     unsigned program_map_PID :  13; // 節目映射表的PID,節目號大於0時對應的PID,每個節目對應一個  
  5. }TS_PAT_Program  

 

3. PAT表的結構(代碼+分析)       

        再將PAT表的結構體給出:

[cpp]  view plain copy
 
 
 
  1. typedef struct TS_PAT  
  2. {  
  3.     unsigned table_id                     : 8; //固定為0x00 ,標志是該表是PAT表  
  4.     unsigned section_syntax_indicator     : 1; //段語法標志位,固定為1  
  5.     unsigned zero                         : 1; //0  
  6.     unsigned reserved_1                   : 2; // 保留位  
  7.      unsigned section_length               : 12; //表示從下一個字段開始到CRC32(含)之間有用的字節數  
  8.     unsigned transport_stream_id          : 16; //該傳輸流的ID,區別於一個網絡中其它多路復用的流  
  9.     unsigned reserved_2                   : 2;// 保留位  
  10.     unsigned version_number               : 5; //范圍0-31,表示PAT的版本號  
  11.     unsigned current_next_indicator       : 1; //發送的PAT是當前有效還是下一個PAT有效  
  12.     unsigned section_number               : 8; //分段的號碼。PAT可能分為多段傳輸,第一段為00,以后每個分段加1,最多可能有256個分段  
  13.     unsigned last_section_number          : 8;  //最后一個分段的號碼  
  14.    
  15.     std::vector<TS_PAT_Program> program;  
  16.     unsigned reserved_3                    : 3; // 保留位  
  17.     unsigned network_PID                    : 13; //網絡信息表(NIT)的PID,節目號為0時對應的PID為network_PID  
  18.     unsigned CRC_32                        : 32;  //CRC32校驗碼  
  19. } TS_PAT;   

 

4. PAT表的解析(代碼+分析)

        接下來給出的是PAT表的解析代碼:

[cpp]  view plain copy
 
 
 
  1. HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)  
  2. {  
  3.     packet->table_id                    = buffer[0];  
  4.     packet->section_syntax_indicator    = buffer[1] >> 7;  
  5.     packet->zero                        = buffer[1] >> 6 & 0x1;  
  6.     packet->reserved_1                  = buffer[1] >> 4 & 0x3;  
  7.     packet->section_length              = (buffer[1] & 0x0F) << 8 | buffer[2];   
  8.    
  9.     packet->transport_stream_id           = buffer[3] << 8 | buffer[4];  
  10.    
  11.     packet->reserved_2                    = buffer[5] >> 6;  
  12.     packet->version_number                = buffer[5] >> 1 &  0x1F;  
  13.     packet->current_next_indicator        = (buffer[5] << 7) >> 7;  
  14.     packet->section_number                = buffer[6];  
  15.     packet->last_section_number           = buffer[7];   
  16.   
  17.     int len = 0;  
  18.     len = 3 + packet->section_length;  
  19.     packet->CRC_32                        = (buffer[len-4] & 0x000000FF) << 24  
  20.   | (buffer[len-3] & 0x000000FF) << 16  
  21.   | (buffer[len-2] & 0x000000FF) << 8   
  22.   | (buffer[len-1] & 0x000000FF);   
  23.    
  24.     int n = 0;  
  25.     for ( n = 0; n < packet->section_length - 12; n += 4 )  
  26.     {  
  27.         unsigned  program_num = buffer[8 + n ] << 8 | buffer[9 + n ];    
  28.         packet->reserved_3           = buffer[10 + n ] >> 5;   
  29.     
  30.         packet->network_PID = 0x00;  
  31.         if ( program_num == 0x00)  
  32.        {    
  33.             packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];  
  34.   
  35.             TS_network_Pid = packet->network_PID; //記錄該TS流的網絡PID  
  36.   
  37.              TRACE(" packet->network_PID %0x /n/n", packet->network_PID );  
  38.         }  
  39.         else  
  40.         {  
  41.            TS_PAT_Program PAT_program;  
  42.            PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];  
  43.            PAT_program.program_number = program_num;  
  44.            packet->program.push_back( PAT_program );  
  45.            TS_program.push_back( PAT_program );//向全局PAT節目數組中添加PAT節目信息       
  46.         }           
  47.     }  
  48.     return 0;  
  49. }  

         從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)

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表信息的偽代碼(代碼)

[cpp]  view plain copy
 
 
 
  1. int Video_PID=0x07e5,Audio_PID=0x07e6;  
  2. void Process_Packet(unsigned char*buff)  
  3. int I; int PID=GETPID(buff);  
  4.   if(PID==0x0000) { Process_PAT(buff+4); }  // 如果PID為0x0000,則該Packet Data為PAT信息,因此調用處理PAT表的函數  
  5.   else{                                     // 這里buff+4 意味着從Packet Header之后進行解析(包頭占4個字節)  
  6.     ……  
  7.   }  
  8. }  

 

 

(二) 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表的定義(代碼)  

[cpp]  view plain copy
 
 
 
  1. <span style="font-size:14px;">//PMT 表定義</span>  
[cpp]  view plain copy
 
 
 
  1. <span style="font-size:14px;">typedef struct TS_PMT_Stream  
  2. {  
  3.  unsigned stream_type                       : 8; //指示特定PID的節目元素包的類型。該處PID由elementary PID指定  
  4.  unsigned elementary_PID                    : 13; //該域指示TS包的PID值。這些TS包含有相關的節目元素  
  5.  unsigned ES_info_length                    : 12; //前兩位bit為00。該域指示跟隨其后的描述相關節目元素的byte數  
  6.  unsigned descriptor;  
  7. }TS_PMT_Stream;   
  8. </span>  

 3. PMT表的結構體定義(代碼)  

[cpp]  view plain copy
 
 
 
  1. //PMT 表結構體  
  2. typedef struct TS_PMT  
  3. {  
  4.     unsigned table_id                        : 8; //固定為0x02, 表示PMT表  
  5.      unsigned section_syntax_indicator        : 1; //固定為0x01  
  6.     unsigned zero                            : 1; //0x01  
  7.     unsigned reserved_1                      : 2; //0x03  
  8.     unsigned section_length                  : 12;//首先兩位bit置為00,它指示段的byte數,由段長度域開始,包含CRC。  
  9.      unsigned program_number                    : 16;// 指出該節目對應於可應用的Program map PID  
  10.     unsigned reserved_2                        : 2; //0x03  
  11.     unsigned version_number                    : 5; //指出TS流中Program map section的版本號  
  12.      unsigned current_next_indicator            : 1; //當該位置1時,當前傳送的Program map section可用;  
  13.                                                      //當該位置0時,指示當前傳送的Program map section不可用,下一個TS流的Program map section有效。  
  14.      unsigned section_number                    : 8; //固定為0x00  
  15.     unsigned last_section_number            : 8; //固定為0x00  
  16.     unsigned reserved_3                        : 3; //0x07  
  17.     unsigned PCR_PID                        : 13; //指明TS包的PID值,該TS包含有PCR域,  
  18.             //該PCR值對應於由節目號指定的對應節目。  
  19.             //如果對於私有數據流的節目定義與PCR無關,這個域的值將為0x1FFF。  
  20.      unsigned reserved_4                        : 4; //預留為0x0F  
  21.     unsigned program_info_length            : 12; //前兩位bit為00。該域指出跟隨其后對節目信息的描述的byte數。  
  22.       
  23.      std::vector<TS_PMT_Stream> PMT_Stream;  //每個元素包含8位, 指示特定PID的節目元素包的類型。該處PID由elementary PID指定  
  24.      unsigned reserved_5                        : 3; //0x07  
  25.     unsigned reserved_6                        : 4; //0x0F  
  26.     unsigned CRC_32                            : 32;   
  27. } TS_PMT;  

 

4. PMT表的解析(代碼)  

[cpp]  view plain copy
 
 
 
  1. //PMT 表的解析  
[cpp]  view plain copy
 
 
 
  1. HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )  
  2. {   
  3.     packet->table_id                            = buffer[0];  
  4.     packet->section_syntax_indicator            = buffer[1] >> 7;  
  5.     packet->zero                                = buffer[1] >> 6 & 0x01;   
  6.     packet->reserved_1                            = buffer[1] >> 4 & 0x03;  
  7.     packet->section_length                        = (buffer[1] & 0x0F) << 8 | buffer[2];      
  8.     packet->program_number                        = buffer[3] << 8 | buffer[4];  
  9.     packet->reserved_2                            = buffer[5] >> 6;  
  10.     packet->version_number                        = buffer[5] >> 1 & 0x1F;  
  11.     packet->current_next_indicator                = (buffer[5] << 7) >> 7;  
  12.     packet->section_number                        = buffer[6];  
  13.     packet->last_section_number                    = buffer[7];  
  14.     packet->reserved_3                            = buffer[8] >> 5;  
  15.     packet->PCR_PID                                = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;  
  16.   
  17.  PCRID = packet->PCR_PID;  
  18.   
  19.     packet->reserved_4                            = buffer[10] >> 4;  
  20.     packet->program_info_length                    = (buffer[10] & 0x0F) << 8 | buffer[11];   
  21.     // Get CRC_32  
  22.  int len = 0;  
  23.     len = packet->section_length + 3;      
  24.     packet->CRC_32                = (buffer[len-4] & 0x000000FF) << 24  
  25.   | (buffer[len-3] & 0x000000FF) << 16  
  26.   | (buffer[len-2] & 0x000000FF) << 8  
  27.   | (buffer[len-1] & 0x000000FF);   
  28.   
  29.  int pos = 12;  
  30.     // program info descriptor  
  31.     if ( packet->program_info_length != 0 )  
  32.         pos += packet->program_info_length;      
  33.     // Get stream type and PID      
  34.     for ( ; pos <= (packet->section_length + 2 ) -  4; )  
  35.     {  
  36.   TS_PMT_Stream pmt_stream;  
  37.   pmt_stream.stream_type =  buffer[pos];  
  38.   packet->reserved_5  =   buffer[pos+1] >> 5;  
  39.   pmt_stream.elementary_PID =  ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;  
  40.   packet->reserved_6     =   buffer[pos+3] >> 4;  
  41.   pmt_stream.ES_info_length =   (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];  
  42.     
  43.   pmt_stream.descriptor = 0x00;  
  44.   if (pmt_stream.ES_info_length != 0)  
  45.   {  
  46.    pmt_stream.descriptor = buffer[pos + 5];  
  47.      
  48.    for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )  
  49.    {  
  50.     pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];  
  51.    }  
  52.    pos += pmt_stream.ES_info_length;  
  53.   }  
  54.   pos += 5;  
  55.   packet->PMT_Stream.push_back( pmt_stream );  
  56.   TS_Stream_type.push_back( pmt_stream );  
  57.     }  
  58.  return 0;  
  59. }  

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

——

——

 

 

(三) 解復用模型(代碼)       

[cpp]  view plain copy
 
 
 
  1. int Video_PID=0x07e5,Audio_PID=0x07e6;  
  2. void Process_Packet(unsigned char*buff)  
  3. {   
  4.   int i; int PID=GETPID(buff);  
  5.   if(PID==0x0000) { Process_PAT(buff+4); }              //PAT表的PID為0x0000  
  6.   else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); }  //PID指示該數據包為視頻包  
  7.   else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); }  //PID指示該數據包為音頻包  
  8.   else{                               // buff+4 意味着要除去buff前4個字節(即包頭)  
  9.   for( i=0;i<64;i++)  
  10.   { if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; }  
  11. } } }  
  12.    

        解復用的意義在於,由於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相關的表,將走得更遠。

 

 

聲明: 本篇文章的部分代碼來自:
         同時還參考了一些前輩的資料,如:
         感謝各位前輩的努力,才有了我們這些后來人的輕松!
經驗水平所限,若有錯漏之處,期待大家的批評指正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM