TS流基础:PAT、PMT

TS数据流的解析 TS数据流的解析 文档修改信息 1 TS 数据流简介 1.1 TS流的构成 1.2 TS流的产生 1.3 TS流的格式 1.4 参考文档 1.5 小结 2 TS数据流包头的解析 2.1 解析步骤 2.2 包长解析 2.3 PID解析 2.4 负载单元开始标志解析 2.5 小结 3 PAT表的解析 3.1 解析步骤 3.2 PAT表的描述 3.3 PAT表的获取 3.4 PAT表信息的解析 3.5 小结 4 PMT表的解析 4.1 解析步骤 4.2 PMT表的描述 4.3 . 阅读详情


http://blog.youkuaiyun.com/rongdeguoqian/article/details/18214627

一 从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流是如何产生的?




  从上图可以看出,视频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 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">typedef struct TS_PAT_Program  
  2. {  
  3.     unsigned program_number   :  16;  //节目号  
  4.     unsigned program_map_PID :  13; // 节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个  
  5. }TS_PAT_Program</span>  
3、PAT的结构
 再将PAT表的结构体给出:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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的解析
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">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. }</span>  

    从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表信息的伪代码
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">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. }</span>  

(二)PMT表 (Program Map 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的时候进行过滤即可实现。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">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;  <span style="color:#006600;"> </span></span>  

3、

PMT表的结构体定义

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">//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;</span>  
4、

 PMT表的解析


[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">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. }</span><span style="color:#ff6600;">  
  60. </span>  

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 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-weight: normal;">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. } } }</span>  
  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打包方式详解 TS定义的很多,其实我们真正进行TS的封装和解封装的代码开发的时候,所需要关注的字段很少,封装和解封装很多字段都是默认值,可选字段大部分都是没有的,具体可参照ffmpeg相关代码。 阅读详情

相关推荐

ts流中PAT表与PMT表详解

关于mpeg2中ts流PAT表与PMT表详解

【PSI/SI学习系列】1.从TS流到PAT和PMT

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=

Destiny的专栏 2万+

【GD32F407】i2c主机发送,从机中断接收

前言 现在手里负责的一个项目需要用i2c和主芯片进行通信主动发送,中断接收,这就需要支持i2c主机模式和从机模式,先查看原理图 这里我们用到了两条i2c总线进行数据的交替传输,避免在使用一条总线是发送数据和接收数据冲突 初始化i2c控制器 s32_k i2cDevInit(u8 byBusId, TI2cBus *pTI2cBus) { //PCLK = 36000000 BOOL retFlg = FALSE; int I2xSCL = pTI2cBus->byFastMode ?

Enosji的博客 6244

什么是TS流?

数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 3421

从AND到XOR:5种门控电路在PrimeTime中的检查策略差异

本文深入探讨了AND、NAND、OR、NOR和XOR五种门控电路在PrimeTime静态时序分析中的检查策略差异。重点解析了XOR门控因其非单调性而无法被工具自动推断,需要工程师手动配置的特殊性,并提供了针对各类门控的配置模板与调试技巧,以确保时钟网络的完整性与芯片设计的可靠性。

rgv2345678的博客 269

Linux内存压力测试工具【memtester】详且精

对Linux下的压力测试工具memtester有一个比较人性化的概述,以及避坑心得。

weixin_44118923的博客 1万+

TS/PAT/PMT

/PMT 表结构体//固定为0x02, 表示PMT表//固定为0x01//0x01//0x03//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。// 指出该节目对应于可应用的Program map PID//0x03//指出TS流中Program map section的版本号//当该位置1时,当前传送的Program map section可用;

qq_71345785的博客 1350

SAP PS项目结算实操:从CJ20N到CJ88,手把手搞定项目成本结转与凭证生成

本文详细解析SAP PS项目结算全流程,从CJ20N到CJ88的操作要点,涵盖成本归集、结算规则配置、结果分析及凭证生成等关键环节。特别针对FICO模块中的项目凭证流转问题提供实战解决方案,帮助财务人员规避常见配置错误,提升项目结算效率与准确性。

weixin_26833503的博客 813

从TS流到PAT和PMT

一 从TS流开始         最近开始学习数字电视机顶盒的开发,从MPEG-2到DVB,看着看着突然就出现了一大堆表格,什么PAT、PMT、CAT……如此多的表该怎样深入了解呢?         我们知道,数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PM

rongdeguoqian的专栏 2万+

33、Windows Server 2016 系统管理与安全防护指南

本文详细介绍了Windows Server 2016系统中的三大核心管理与安全功能:WSUS补丁管理、Windows Defender反恶意软件配置以及Windows Server Backup数据备份与恢复。涵盖WSUS初始同步、组策略配置客户端、混合环境更新评估流程;Windows Defender的图形化与PowerShell管理方式,以及通过组策略集中管控;Windows Server Backup的安装、单次与计划备份设置、增量备份机制和文件恢复操作。内容适用于企业IT管理员进行系统维护与安全防护

beta5的博客 97

SAP FI 会计科目表(COA)可直接套用配置模板

摘要:本文提供了SAP S/4HANA环境下可直接套用的会计科目表(COA)配置模板,包含科目编码规则、号段规划、科目组设置等核心内容。模板采用10位数字编码,按资产(1000)、负债(2000)、权益(3000)、成本(4000)、收入(5000)、费用(6000)六大类划分号段,并预设科目组控制字段状态。适用于集团/单一公司、国内/跨国企业场景,支持多表架构映射,配置完成后可通过事务码SAKO验证科目清单。模板遵循"前4位固定一级科目"原则,企业可根据行业特性调整明细科目。

金牌架构师 266

TS系列二:从TS到PAT和PMT

PID 是当前TS流的Packet区别于其他Packet类型的唯一识别符,通过读取每个包的Packet Header, 我们可以知道这个Packet的数据属于何种类型。下表列出了几项固定的 PID 值,它们用于识别存储了特殊信息的Packet。 下面要谈的PAT表的 PID 值就是固定的0x0000。

WHEgqing的专栏 2488

手把手教你学基带SOC芯片(4.4.9)--基带芯片的电源管理实例:高通 Snapdragon 系列实例(十)

Snapdragon 888 通过支持多种深度睡眠模式,如 AOAC 模式、低功耗模式和深度睡眠模式,以及智能电源管理技术,实现了对功耗的精细管理。这些模式不仅延长了设备的电池寿命,还确保了用户在各种使用场景下的良好体验。无论是保持网络连接、限制后台活动,还是完全关闭不必要的功能,Snapdragon 888 都能够灵活应对,提供高效的功耗管理方案。

MHD0815的博客 740

ts 流基础(白话讲解).

00 00 section_number 和last_section_number 都是0,在本节就把表数据表示完了. sections 个数,最多也不超过256个,我们分配256个等着它。00 01 c1 00 00: 扩展id 2bytes, version号1, sec_num:00, last_sec_num:00,一个表搞定.

hjjdebug的专栏 2901

PyTorch实战(2)——使用PyTorch构建神经网络

在本节中,我们使用 PyTorch 在简单数据集上构建了一个神经网络,训练神经网络来映射输入和输出,并通过执行反向传播来更新权重值以最小化损失值,并利用 Sequential 类简化网络构建过程;介绍了获取网络中间值的常用方法,以及如何使用 save、load 方法保存和加载模型,以避免再次训练模型。

盼小辉丶的博客 1万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

流媒体的各种流知识点

一 从TS流开始         最近开始学习数字电视机顶盒的开发,从MPEG-2到DVB,看着看着突然就出现了一大堆表格,什么PAT、PMT、CAT……如此多的表该怎样深入了解呢?         我们知道,数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PM

dafeng5455的博客 2682

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

TS流PAT/PMT详解

一 从TS流开始         最近开始学习数字电视机顶盒的开发,从MPEG-2到DVB,看着看着突然就出现了一大堆表格,什么PAT、PMT、CAT……如此多的表该怎样深入了解呢?         我们知道,数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PM

一直在努力... 5378

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

TS流详细解析

一 从TS流开始         数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。 (一) TS流、PS流、PES流和ES流都是什么?        ES流(Ele

I_am_a_Little_bird的博客 1709

TS流分析

一 从TS流开始 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS...

houxiaoni01的博客 929

TS流概述

、TS流概述 ES流(Elementary Stream,基本流):数字电视各组成部分编码后所形成的直接表示基本元素内容的流,包含视频、音频或数据的连续码流。 PES流(Paketized Elementary Stream,打包基本码流):是将基本的码流ES流根据需要分成长度不等的数据包,并加上包头就形成了打包的基本码流PES流。 PS (Program Stream,节目流):将一个节目

zxh821112的专栏 2472

TS文件格式详解

最近彻底研究分析了ts文件格式,这里做下学习总结: 简单的来说,ts文件中的信息其实就是通过负载类型字段来找,找到后把数据从负载中提取出来,ts中可以有很多媒体类型数据,比如说可以同时又音频和视频数据, 可是要如何区分ts文件中的数据是音频还是视频呢?这就需要动用ts文件中的PSI描述说明了。 PSI: 在MPEG-II中定义了节目特定信息(PSI),PSI用来描述传送流的组成结构

zwlq1314521的专栏 1万+

【PSI/SI学习系列】1.从TS流到PAT和PMT——神文001

http://blog.youkuaiyun.com/kkdestiny/article/details/9850587   前言   欢迎到我的网站阅读:http://www.onelib.biz/blog/stb 一 从TS流开始         最近开始学习数字电视机顶盒的开发,从MPEG-2到DVB,看着看着突然就出现了一大堆表格,什么PAT、PMT、CAT……如

STN_LCD的专栏 1314
上一篇: TS流的基本概念
下一篇: libdvbpsi源码结构
xb_smart
博客等级 码龄16年 124粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值