TS数据结构分析

本文详细介绍了TS(Transport Stream)包的数据结构,包括TS包头的各个字段及其解析过程。同时,深入讲解了PSI(Program Specific Information)的概念,如PAT、PMT、CAT和NIT表格的作用和数据结构,以及如何解析这些表格以获取TS流中的节目信息。
摘要由CSDN通过智能技术生成

1.TS包得数据结构

2.

// Transport packet header
typedef struct TS_packet_header
{
    unsigned sync_byte                                         :    8;    //同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的
    unsigned transport_error_indicator              :    1;      //传输错误标志位,一般传输错误的话就不会处理这个包了
    unsigned payload_unit_start_indicator        :     1;      //有效负载的开始标志,根据后面有效负载的内容不同功能也不同
    unsigned transport_priority                             :     1;      //传输优先级位,1表示高优先级
    unsigned PID                                                     :     13;     //有效负载数据的类型
    unsigned transport_scrambling_control      :     2;      //加密标志位,00表示未加密
    unsigned adaption_field_control                   :     2;      //调整字段控制,。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。
    unsigned continuity_counter                           :    4;    //一个4bit的计数器,范围0-15
} TS_packet_header; 

 

 

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;
}

 

3.PSI

全称Program Specific Information,意为节目专用信息。传输流中是多路节目复用的,那么,怎么知道这些节目在传输流中的位置,区分属于不同节目呢?所以就还需要一些附加信息,这就是PSI。PSI也是插入到TS分组中的,它们的PID是特定值。MPEG-2中规定了4个PSI,包括PAT(节目关联表),CAT(条件访问表),PMT(节目映射表),NIT(网络信息表),这些PSI包含了进行多路解调和显示节目的必要的和足够的信息.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,一个节目包含多少个节目就含有多少PMT。

CAT (Conditional Access Table )条件访问表CAT所在分组的PID=1CAT中列出了条件控制信息(ECM)和条件管理信息(EMM)所在分组的PID。CAT用于节目的加密和解密 NIT( Network Information Table)网络信息表

NIT所在分组的PID由PAT指定NIT提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数(如频率,调制方式等)。NIT一般是解码器内部使用的数据,当然也可以做为EPG的一个显示数据提供给用户做为参考。

几种PSI之间的关系,如下图所示:首先PAT中指定了传输流中所存在的节目,及每个节目对应的PMT的PID号。 比如Program 1对应的PMT 的PID=22,然后找到PID=22的TS分组,解出PMT,得到这个节目中包含的原始流的PID,再根据原始流的PID去找相应的TS分组,获取原始流的数据,然后就可以送入解码器解码了。

 

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

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

4.PAT表数据结构

 

数据结构PAT Table

//PAT Table
program_association_section() {
       table_id                      : 8;    //固定为0x00 ,标志是该表是PAT
   section_syntax_indicator           : 1;    //段语法标志位,固定为1    
   '0'                                : 1;    //0
   reserved                           : 2;    //保留
   section_length                     : 12;    //表示这个字节后面有用的字节数,包括CRC32。假如后面的字节加上前面的字节数少于188,后面会用0XFF填充。假如这个数值比较大,则PAT会分成几部分来传输


   transport_stream_id                : 16    //该传输流的ID,区别于一个网络中其它多路复用的流。
   reserved                           : 2    //保留
   version_number                     : 5    //范围0-31表示PAT的版本号,标注当前节目的版本这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.
   current_next_indicator             : 1    //表示发送的PAT是当前有效还是下一个PAT有效。
   section_number                     : 8    //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
   last_section_number                : 8    //最后一个分段的号码
   for (i=0; i<N;i++) {
       program_number                 : 16    //节目号
       reserved                       : 3    //保留
     if(program_number == '0') {
         network_PID                  : 13    //网络信息表(NIT)的PID,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为0时对应的PID为network_PID
       }
     else {
        program_map_PID               : 13    //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
      }
  }
 CRC_32                               : 32
}

 

//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;
}

 

//PMT Table

TS_program_map_section(){ 
    table_id : 8;                //固定为0x02 ,标志是该表是PMT。
    section_syntax_indicator :  1;        //段语法标志位,固定为1    
     '0' : 1;                //0
    reserved : 2;                //保留
    section_length : 12;            //表示这个字节后面有用的字节数,
    program_number : 16;            //节目号,表示该PMT对应的节目
    reserved : 2;                //保留
    version_number : 5;
    current_next_indicator :1;        
    section_number : 8;
    last_section_number : 8;
    reserved : 3;     
    PCR_PID : 13                 //PCR(节目时钟参考)所在TS分组的PID,根据PID可以去搜索相应的TS分组,解出PCR信息。
    reserved : 4;
    program_info_length:  12;        //该节目的信息长度
    for (i=0; i<N; i++) { 
        descriptor() 
    } 
    for (i=0;i<N1;i++) { 
        stream_type : 8;         //指示了PID为elementary_PID的PES分组中原始流的类型,比如视频流,音频流等,见后面的表
        reserved : 3;            
        elementary_PID : 13;        //该节目中包括的视频流,音频流等对应的TS分组的PID
        reserved : 4; 
        ES_info_length : 12;        //该节目相关原始流的描述符的信息长度
        for (i=0; i<N2; i++) { 
            descriptor() 
        } 
    } 
    CRC_32 : 32 
}

 

 

PMT结构定义:


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;

//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;

 

解析代码为:

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;
}

 

//CheckOut 数据宏

从byteVar的某位,获取获取指定的位值

#define GET1BIT(byteVar, fromBit) ((byteVar >> (8 - (fromBit))) & 0x01)
#define GET2BIT(byteVar, fromBit) ((byteVar >> (7 - (fromBit))) & 0x03)
#define GET3BIT(byteVar, fromBit) ((byteVar >> (6 - (fromBit))) & 0x07)
#define GET4BIT(byteVar, fromBit) ((byteVar >> (5 - (fromBit))) & 0x0F)
#define GET5BIT(byteVar, fromBit) ((byteVar>>  (4 - (fromBit))) & 0x1F)
#define GET6BIT(byteVar, fromBit) ((byteVar >> (3 - (fromBit))) & 0x3F)
#define GET7BIT(byteVar, fromBit) ((byteVar >> (2 - (fromBit))) & 0x7F)
#define GET8BIT(byteVar, fromBit) ((byteVar >> (1 - (fromBit))) & 0xFF)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值