zoukankan      html  css  js  c++  java
  • TS数据结构分析

    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)

  • 相关阅读:
    【C语言入门教程】5.6 函数库和文件
    【C语言入门教程】5.5 实现问题(效率)
    【C语言入门教程】5.4 递归
    【C语言入门教程】5.3 函数的调用 与 参数
    【C语言入门教程】5.2 函数的作用域规则(auto, static)
    bootstrap之双日历时间段选择控件示例—daterangepicker(中文汉化版)
    PHP导出数据到CSV文件函数 csv_export()
    MySQL 5.6 Warning: Using a password on the command line interface can be insecure
    【定时任务|开机启动】Windows Server 2008/2012 计划任务配置(任务计划程序)每分钟执行BAT
    【风雪之隅】写在PHP7发布之际一些话 2015-12-02
  • 原文地址:https://www.cnblogs.com/lidabo/p/3754136.html
Copyright © 2011-2022 走看看