TS流解析

1)ES- Elementary Streams (原始流),对视频、音频信号及其他数据进行编码压缩后的数据流称为原始流。原始流包括访问单元,比如视频原始流的访问单元就是一副图像的编码数据。


(2) PES- Packetized Elementary Streams (分组的原始流),原始流形成的分组称为PES分组,是用来传递原始流的一种数据结构


(3)节目是节目元素的集合。节目元素可能是原始流,这些原始流有共同的时间基点,用来做同步显示。


(4)传输流和节目流TS-Transport Stream 翻译为传输流”PS-Program Stream  翻译为节目流”PS用来传输和保存一道节目的编码数据或其他数据。PS的组成单位是PES分组。TS用来传输和保存多道节目的编码数据或其他数据,TS的组成单位是节目。PS适用于不容易发生错误的环境,以及涉及到软件处理的应用,典型应用如DVD光盘的文件存储TS适用于容易发生错误的环境,典型应用就是数字电视信号的传输。TSPS是可以互相转换的,比如从TS中抽取一道节目的内容并产生有效的PS是可能。 


(5)传输流分组和PES分组原始流分成很多PES分组,保持串行顺序,一个PES分组只包含一个原始流的编码数据。PES分组长度很大,最大可为64K字节。PES分组分为分组首部(header)”有效负载(payload)”有效负载指跟随在首部字节之后的字节。首部的前4个字节构成分组的起始码,标识了该分组所属原始流的类型和ID号。TS分组也就是传输流数据形成的数据包。每个TS分组长度为188字节,包括分组首部有效负载,前4个字节是分组首部,包含了这个分组的一些信息。有些情况下需要更多的信息时,需在后面添加调整字段(adaption field)”。两者之间的关系:PES分组是插入到TS分组中的,每个PES分组首部的第一字节就是TS分组有效负载的第一字节。一个PID值的TS分组只带有来自一个原始流的数据。 


(6)PSI   全称 Program Specific Information ,意为节目专用信息。传输流中是多路节目复用的,那么,怎么知道这些节目在传输流中的位置,区分属于不同节目呢?所以就还需要一些附加信息,这就是 PSI PSI 也是插入到 TS 分组中的,它们的 PID 是特定值。 MPEG-2 中规定了 4 PSI ,包括 PAT( 节目关联表 ) CAT( 条件访问表 ) PMT( 节目映射表 ) NIT( 网络信息表 ) ,这些 PSI 包含了进行多路解调和显示节目的必要的和足够的信息。应用中可能包括更多的信息,比如 DVB-T 中定义了 SDT( 服务描述表 ),EIT( 环境信息表 ),BAT( 节目组相关表 ),TDT( 时间日期表 ) 等,统称为 DVB-SI( 服务信息 )   PSI PID 是特定的,含 PSI 的数据包必须周期性的出现在传输流中。
PMT (Program Map Table )节目映射表 PMT 所在分组的 PID PAT 指定,所以要先解出 PAT ,再解 PMT PMT 中包含了属于同一节目的视频、音频和数据原始流的 PID 。找到了 PMT ,解多路复用器就可找到一道节目对应的每个原始流的 PID ,再根据原始流 PID ,去获取原始流。
PAT (Program Association   Table )节目关联表 PAT 所在分组的 PID=0 PAT 中列出了传输流中存在的节目流 PAT 指定了传输流中每个节目对应 PMT 所在分组的 PIDPAT 的第一条数据指定了 NIT 所在分组的 PID   ,其他数据指定了 PMT 所在分组的 PID
CAT (Conditional Access Table   )条件访问表 CAT 所在分组的 PID=1CAT 中列出了条件控制信息 (ECM) 和条件管理信息 (EMM) 所在分组的 PID CAT 用于节目的加密和解密   NIT( Network Information Table) 网络信息表 NIT所在分组的PIDPAT指定 NIT 提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数 ( 如频率 , 调制方式等 ) NIT 一般是解码器内部使用的数据,当然也可以做为 EPG 的一个显示数据提供给用户做为参考。几种 PSI 之间的关系,如下图所示:首先 PAT 中指定了传输流中所存在的节目,及每个节目对应的 PMT PID 号。   比如 Program 1 对应的 PMT   PID=22, 然后找到 PID=22 TS 分组,解出 PMT ,得到这个节目中包含的原始流的 PID ,再根据原始流的 PID 去找相应的 TS 分组,获取原始流的数据,然后就可以送入解码器解码了。

数据结构(1TS分组前面提到,TS分组由188个字节构成,其结构如下: 
transport_packet(){ 
sync_byte                                                                    // 8
transport_error_indicator                                          //1
payload_unit_start_indicator                                    //1
transport_priority                                                       // 1  PID                                                                             //13
transport_scrambling_control                                  // 2
adaptation_field_control                                            //2
continuity_counter                                                      //4
if(adaptation_field_control=='10'  || adaptation_field_control=='11'){ 
  adaptation_field() 
} 
if(adaptation_field_control=='01' || adaptation_field_control=='11') { 
  for (i=0;i<N;i++){ 
   data_byte                                                                   //8
  } 
} 
}

前面32bit的数据即TS分组首部,它指出了这个分组的属性。

sync_byte  同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47

transport_error_indicator 传输错误标志位,一般传输错误的话就不会处理这个包了

payload_unit_start_indicator 这个位功能有点复杂,字面意思是有效负载的开始标志,根据后面有效负载的内容不同功能也不同,后面用到的时候再说。

transport_priority  传输优先级位,1表示高优先级,传输机制可能用到,解码好像用不着。

PID  这个比较重要,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容。前面已经叙述过。

transport_scrambling_control 加密标志位,表示TS分组有效负载的加密模式。TS分组首部(也就是前面这32bit)是不应被加密的,00表示未加密。

 adaption_field_control  翻译为调整字段控制,表示TS分组首部后面是否跟随有调整字段和有效负载。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。空分组没有调整字段

 continuity_counter   一个4bit的计数器,范围0-15,具有相同的PIDTS分组传输时每次加1,到15后清0。不过,有些情况下是不计数的。如下:(1)TS分组无有效负载(2)复制的TS分组和原分组这个值一样(3)后面讲到的一个标志discontinuity_indicator1

adaptation_field() 调整字段的处理

data_byte 有效负载的剩余部分,可能为PES分组,PSI,或一些自定义的数据。


2  PAT数据结构如下:
program_association_section() {
   table_id                                // 8
   section_syntax_indicator                // 1
   '0'                                     // 1
   reserved                                // 2
   section_length                          // 12
   transport_stream_id                     // 16
   reserved                                // 2
   version_number                          // 5
   current_next_indicator                  // 1
   section_number                          // 8
   last_section_number                     // 8
   for (i=0; i<N;i++) {
       program_number                         // 16
       reserved                               // 3
     if(program_number == '0') {
         network_PID                           // 13
       }
     else {
        program_map_PID                       // 13
      }
  }
 CRC_32                                  //  32
}

table_id  固定为0x00 ,标志是该表是PAT

section_syntax_indicator 段语法标志位,固定为1

section_length         表示这个字节后面有用的字节数,包括CRC32。假如后面的字节加上前面的字节数少于188,后面会用0XFF填充。假如这个数值比较大,则PAT会分成几部分来传输。

transport_stream_id    该传输流的ID,区别于一个网络中其它多路复用的流。

version_number范围0-31,表示PAT的版本号,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.

current_next_indicator 表示发送的PAT是当前有效还是下一个PAT有效。

section_number分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段

last_section_number 最后一个分段的号码

 program_number 节目号

network_PID 网络信息表(NIT)的PID,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为0时对应的PIDnetwork_PID

program_map_PID 节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个

CRC_32   CRC32校验码

上面program_numbernetwork_PIDprogram_map_PID 是循环出现的。program_number等于0时对应network_PIDprogram_number等于其它值时对应program_map_PID

 

(3)PMT   PMT数据结构如下:
TS_program_map_section() {
table_id                               // 8
section_syntax_indicator              //  1
'0'                                   //  1
reserved                              //  2
section_length                        //  12
program_number                        //  16
reserved                              //  2
version_number                        //  5
current_next_indicator                //  1
section_number                        //  8
last_section_number                   //  8
reserved                              //  3
PCR_PID                               //  13
reserved 4
program_info_length                   //  12
for (i=0; i<N; i++) {
  descriptor()
}
for (i=0;i<N1;i++) {
  stream_type                           //  8
  reserved                              //  3
  elementary_PID                        //  13
  reserved                              //  4
  ES_info_length                        //  12
  for (i=0; i<N2; i++) {
   descriptor()
  }
}
CRC_32                                 //  32
}

table_id  固定为0x02 ,标志是该表是PMT

section_syntax_indicator section_length      version_number       current_next_indicator 以上四个字段意思和PAT相同,可参考上面解释

section_number   last_section_number 以上两个字段意思和PAT相同,不过值都固定为0x00,我觉得这样的原因可能是因为PMT不需要有先后顺序,因为先定义哪个节目都是无所谓。

program_number 节目号,表示该PMT对应的节目

PCR_PID PCR(节目时钟参考)所在TS分组的PID,根据PID可以去搜索相应的TS分组,解出PCR信息。

program_info_length 该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息

stream_type 指示了PIDelementary_PIDPES分组中原始流的类型,比如视频流,音频流等,见后面的表

elementary_PID 该节目中包括的视频流,音频流等对应的TS分组的PID

ES_info_length 该节目相关原始流的描述符的信息长度。

根据前一篇中各数据的定义及数据结构,对数据进行分别解析如下:

TS包头定义:

typedef struct TS_packet_header
{
    unsigned sync_byte                        : 8; //同步字节, 固定为0x47,表示后面的是一个TS分组
    unsigned transport_error_indicator        : 1; //传输误码指示符
    unsigned payload_unit_start_indicator    : 1; //有效荷载单元起始指示符
   
    unsigned transport_priority              : 1; //传输优先, 1表示高优先级,传输机制可能用到,解码用不着
    unsigned PID                            : 13; //PID
    unsigned transport_scrambling_control    : 2; //传输加扰控制 
    unsigned adaption_field_control            : 2; //自适应控制 01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00解码器不进行处理
    unsigned continuity_counter                : 4; //连续计数器 一个4bit的计数器,范围0-15
} TS_packet_header;

 

TS包头解析代码:

HRESULT CTS_Stream_Parse::adjust_TS_packet_header( TS_packet_header* TS_header )
{
 unsigned char buf[4]; 
 
    memcpy(buf, TS_header, 4);
    TS_header->transport_error_indicator        = buf[1] >> 7;
    TS_header->payload_unit_start_indicator    = buf[1] >> 6 & 0x01;
    TS_header->transport_priority                = buf[1] >> 5 & 0x01;
    TS_header->PID                            = (buf[1] & 0x1F) << 8 | buf[2];
    TS_header->transport_scrambling_control    = buf[3] >> 6;
    TS_header->adaption_field_control            = buf[3] >> 4 & 0x03;
    TS_header->continuity_counter                = buf[3] & 0x0F; // 四位数据,应为0x0F xyy 09.03.18

 return 0;
}

如下为一个TS包数据:

0x47 0x40 0x00 0x12 0x00 0x00 0xb0 0x0d 0x00 0x00 0xc1 0x00 0x00 0x00 0x01 0xe3 0xe8 0xf0 0x0b 0xd7 0x79 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff

分析知道前四位0x47 0x40 0x00 0x12TS头部即为TS包头数据,解析如下:

sync_byte   0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority  : 0x00

  PID                     :0x0000
transport_scrambling_control  :0x00
adaptation_field_control  :0x01                                     

continuity_counter   :0x02

PID = 0x0000,表示此TS包的内容为PSI信息表格的PAT表格数据,在4字节的TS包头之后的第一个字节的Point_field = 0x00, 表示偏移量为0,即紧随其后的即为PAT的数据信息

PAT表格定义如下:

typedef struct TS_PAT_Program
{
 unsigned program_number    :16; //节目号
 unsigned program_map_PID   :13;   //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
}TS_PAT_Program;

//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时对应的PIDnetwork_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;
}

因此,PAT数据解析结果如下:

PAT数据

table_id    0x00                            //8  
section_syntax_indicator   0x01           // 1
'0'              0x00                       // 1
reserved             0x03                // 2
section_length      0x00d                    // 12
transport_stream_id    0x0000                 // 16
reserved                        0x03        // 2
version_number            0x00              // 5
current_next_indicator   0x01               // 1
section_number              0x00            // 8
last_section_number         0x00            // 8
program_number     0x0001                    // 16
  reserved                0x07               // 3
program_map_PID      0x03e8             // 13
CRC         0x f0 0b d7 79

由解析结构可知,该PAT表格中没有网络信息包信息,只包含一个节目,其PID0x03e8

PMT结构定义:


typedef struct TS_PMT_Stream
{
 unsigned stream_type                    : 8; //指示特定PID的节目元素包的类型。该处PIDelementary PID指定
 unsigned elementary_PID                    : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
 unsigned ES_info_length                    : 12; //前两位bit00。该域指示跟随其后的描述相关节目元素的byte
 unsigned descriptor;
}TS_PMT_Stream;

//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; //前两位bit00。该域指出跟随其后对节目信息的描述的byte数。
    
 std::vector<TS_PMT_Stream> PMT_Stream;  //每个元素包含8, 指示特定PID的节目元素包的类型。该处PIDelementary PID指定
    unsigned reserved_5                        : 3; //0x07
    unsigned reserved_6                        : 4; //0x0F
    unsigned CRC_32                            : 32; 
} TS_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;
}

举例如下:

0x47 0x43 0xe8 0x12 0x00 0x02 0xb0 0x12 0x00 0x01 0xc1 0x00 0x00 0xe3 0xe9 0xf0 0x00  0x1b 0xe3 0xe9 0xf0 0x00 0xf0 0xaf 0xb4 0x4f 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff

TS头部

sync_byte   0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority  : 0x00

  PID                     :0x03e8
transport_scrambling_control  :0x00
adaptation_field_control  :0x01                                     

continuity_counter   :0x02

 

PMT数据         

table_id     :0x02                          // 8
section_syntax_indicator  :0x01            //  1
'0'                :0x00                   //  1
reserved       :0x03                       //  2
section_length :      0x012                 //  12
program_number    :0x00 01                    //  16
reserved               :0x03               //  2
version_number    :0x00                    //  5
current_next_indicator   0x01             //  1
section_number      :0x00                  //  8
last_section_number    :0x00               //  8 
reserved                   0x07           //  3
PCR_PID           :0x03 e9   // PCR(节目参考时钟)所在TS分组的PID          //  13
reserved       :0x0f                 //4
program_info_length     :0x000              //  12
stream_type       :0x1b                    //  8
reserved            0x07                  //  3
elementary_PID        :0x03 e9    //  13//该节目中包括的视频流,音频流等对应的TS分组的PID
reserved                    :0x0f          //  4
ES_info_length         :0x000               //  12

MPEG2 TS和数字电视是紧密不可分割的,值得总结一下其中的一些关系。

ISO/IEC138181:系统部分;
ISO/IEC138182:视频;
ISO/IEC138183:音频;
ISO/IEC138184:一致性测试;
ISO/IEC138185:软件部分;
ISO/IEC138186:数字存储媒体命令与控制;
ISO/IEC138187:高级音频编码;
ISO/IEC138188:系统解码实时接口;

MPEG2系统任务包括:
1. 规定以包传输数据的协议;
2. 规定收发两端数据流同步的协议;
3. 提供多个数据流的复用和解复用协议;
4. 提供数据流加密的协议。以包形式存储和传送数据流是MPEG2系统之要点。

ES是直接从编码器出来的数据流,可以是编码过的视频数据流,音频数据流,或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。PES包由包头和payload组成,具体格式摘录如下:

 

可以看到PTS/DTS是打在PES包里面的,这两个parameters是解决视音频同步显示,防止解码器输入缓存上溢或下溢的关键。PTS表示 显示单元出现在系统目标解码器(STD: system target decoder)的时间,DTS表示将存取单元全部字节从STDES解码缓存器移走的时刻。每个IPB帧的包头都有一个PTSDTS,但PTSDTSB帧都是一样的,无须标出B帧的DTS。对I帧和P帧,显示前一定要存储于视频解码器的重新排序缓存器中,经过延迟(重新排序)后再显示,一定要分别标明PTSDTS

        上面介绍过,ES首先需打包成PES流包,然后PES根据需要打包成PSTS包进行存储或传输。其每路ES只包含一路信源的编码数据流,所以每路PES也只包含相对应信源的数据流。

PS流而言,每个PES包头含有PTSDTS,流识别码,用于区别不同性质ES。然后通过PS复用器将PES包复用成PS包。实际上是将PES 包分解为更细小的PS包。在解码的时候,解复用器将PS分解成一个个PES包,拆包器然后将PES包拆成视频和音频的ES,最后输入至各自解码器进行解 码。一个问题是:各个ES在解码时,如何保证视音频的同步呢?除了PTSDTS的配合工作外,还有一个重要的参数是SCR(system clock reference)。在编码的时候,PTSDTSSCR都是由STC(system time clock)生成的,在解码时,STC会再生,并通过锁相环路(PLLphase lock loop),用本地SCR相位与输入的瞬时SCR相位锁相比较,以确定解码过程是否同步,若不同步,则用这个瞬时SCR调整27MHz的本地时钟频率。最 后,PTSDTSSCR一起配合,解决视音频同步播放的问题。PS格式摘录如下:

 

 

PS包的长度比较长且可变,主要用于无误码环境里,因为越长的话,同步越困难,且在丢包的情况下,重组也越困难。所以,PS适合于节目信息的编辑和本地内容应用的application

TS流也是由一个或多个PES组合而来的,他们可以具有相同的时间基准,也可以不同。其基本的复用思想是,对具有相同时间基准的多个PES现进行节目复用,然后再对相互有独立时间基准的各个PS进行传输复用,最终产生出TS

TS包由包头和包数据2部分组成,其中包头还可以包括扩展的自适用区。包头长度占4bytes,自使用区和包数据共占184bytes,整个TS包长度相当于4ATM包长。TS包的包头由如下图摘录所示的同步字节、传输误码指示符、有效载荷单元起始指示符、传输优先、包识别(PID-Packet Identification)、传输加扰控制、自适应区控制和连续计数器8个部分组成。

 


其中,可用同步字节位串的自动相关特性,检测数据流中的包限制,建立包同步;传输误码指示符,是指有不能消除误码时,采用误码校正解码器可表示1bit 的误码,但无法校正;有效载荷单元起始指示符,表示该数据包是否存在确定的起始信息;传输优先,是给TS包分配优先权;PID值是由用户确定的,解码器根据PIDTS上从不同ES来的TS包区别出来,以重建原来的ES;传输加扰控制,可指示数据包内容是否加扰,但包头和自适应区永远不加扰;自适应区控制,用2 bit表示有否自适应区,即(01)表示有有用信息无自适应区,(10)表示无有用信息有自适应区,(11)表示有有用信息有自适应区,(00)无定义;连续计数器可对PID包传送顺序计数,据计数器读数,接收端可判断是否有包丢失及包传送顺序错误。显然,包头对TS包具有同步、识别、检错及加密功能。

 

TS包自适应区由自适应区长、各种标志指示符、与插入标志有关的信息和填充数据4部分组成。其中标志部分由间断指示符、随机存取指示符、ES优化指示符、PCR标志、接点标志、传输专用数据标志、原始PCR标志、自适应区扩展标志8个部分组成。重要的是标志部分的PCR字段,可给编解码器的27MHz时钟提供同步资料,进行同步。其过程是,通过PLL,用解码时本地用PCR相位与输入的瞬时PCR相位锁相比较,确定解码过程是否同步,若不同步,则用这个瞬时PCR调整时钟频率。因为,数字图像采用了复杂而不同的压缩编码算法,造成每幅图像的数据各不相同,使直接从压缩编码图像数据的开始部分获取时钟信息成为不可能。为此,选择了某些(而非全部)TS包的自适应区来传送定时信息。于是,被选中的TS包的自适应区,可用于测定包信息的控制bit和重要的控制信息。自适应区无须伴随每个包都发送,发送多少主要由选中的TS包的传输专用时标参数决定。标志中的随机存取指示符和接点标志,在节目变动时,为随机进入I帧压缩的数据流提供随机进入点,也为插入当地节目提供方便。自适应区中的填充数据是由于PES包长不可能正好转为TS包的整数倍,最后的TS包保留一小部分有用容量,通过填充字节加以填补,这样可以防止缓存器下溢,保持总码率恒定不变。



前面总结了MPEG2 TS的基本格式,其中包括PESPSTS,以及相关字段的介绍。那么作为一种传输流,TS将内容进行打包/复用,让其媒体内容变成TS传输,并最终在解码端解码。简单来看,TS是一个传输层的协议栈,它可以承载各种内容的传输,比如MPEGWMVH264,甚至是IP,那么其中的传输规范是如何定义的呢?这个即是PSI(节目特定信息)要做的事情。

PSI由四张表构成:PATPMTCATNIT,这四张表分别描述了一个TS所包括的所有ES流的传输结构。首先的一个概念是,TS是以包形式传播,在编解码端都需要以一定的包ID来标识TS流里承载的内容,比如,PAT表会存在于一个或多个TS包里,所以要用一个特别的包ID来表示,另外,不同的ES流也需要不同的包ID来标识。我们有了PATPMT这两种表,解码器就可以根据 PID,将TS上从不同ES来的TS包区分出来进行解码。

TS的解码分两步进行,其一,是从PID0 TS包里,解析出PAT表,然后从PAT表里找到各个节目源的PID,一般此类节目源都由若干个ES流组成,并描述在PMT表里面,然后通过节目源的 PID,就可以在PMT表里检索到各个ESPID。其二,解码器根据PMT表里的ES流的PID,将TS流上的包进行区分,并按不同的ES流进行解码。所以,TS是经过节目复用和传输复用两层完成的,即在节目复用时,加入了PMT,在传输复用时,加入了PAT。同样在节目解复用时,可以得到PMT,在传输解复用时,可以得到PAT。下图很好地概述了其思想。

TS是支持多路复用的,所以它可用来传输经复用后的多层节目。在复用过程中,要注意的是,解码过程中所需要面对的时间参考和同步问题,因为解复用是需要各种信息同步进行的,所以在复用过程中,就需要插入相关的时间信息:PTSDTSPCR

TS形成过程中,PTSDTS是在ES打包成PES时,根据STC的参考,将其时钟信息注入PES包中的,而之后在PES切成TS时,再将 PIDPCR信息注入到TS包中,当多路TS再进行复用的时候,各路TSPCR将会被提取出来,再进行分析,然后再根据统一的STC参考,将新的 PCR生成并注入到TS中去,最后,因为原来PAT表信息不在适用,所以新的PAT表需要再生成,并附加到新的TS流中去。经过这多层的复用之后,新的 TS流即可以进入调制,传输阶段。过程可参见下图:

 

 

解码过程要面对的问题是:解复用,视音频的同步,解码缓存器无上下溢。解复用即是将TS在同一信道里不同时序进行传输的节目分离出来;视音频同步由 DTS, PTSPCR三者协调完成,并且PCR是重建系统时间基准的绝对时标,而DTSPTS是解码和重现时刻的相对时标;对解码缓存器无上下溢的问题,必须 借助于系统目标解码器(STD)模型来对其进行实现,基本思想如下:

1. TS流进入解码器后,首先由换向器,按照一定的时序关系,将各种ES流分解出来(其中也包括PSI信息流)。

2. 分解过后的ES流会进入各自的传输缓存器,通过之后,其PES流进入各自的主存储器,注意的是:PSI信息流会进入系统缓存器,最后也到达主存储器。

3. 最后,解码器根据DTS信息,从各个主存储器分别提取媒体或系统信息,进行解码,并根据PTS信息,将媒体内容进行显示处理。

其过程可参见下图:

 

 

猜你喜欢

转载自blog.csdn.net/yexiangcsdn/article/details/79862434
今日推荐