halleyhuang 发表于 2016-10-11 15:33:02

TS的解码方式和传输流数据包格式

TS的解码方式是从PID为0 的TS packet内,解析出PAT table,然后 PAT table 找到各个program 源的PID。解码器根据PMT talbe 的ES streaming 的PID,将TS流上的packet 进行区分,并按不同的ES流进行解码。

TS格式解析
1.TS格式介绍
   TS:全称为MPEG2-TS。TS即"Transport Stream"的缩写。它是分包发送的,每一个包长为188字节(还有192和204个字节的包)。包的结构为,包头为4个字节(第一个字节为0x47),负载为184个字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。MPEG2-TS主要应用于实时传送的节目,比如实时广播的电视节目。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。简单地说,将DVD上的VOB文件的前面一截cut掉(或者是数据损坏数据)就会导致整个文件无法解码,而电视节目是任何时候打开电视机都能解码(收看)的。

2.TS流包含的内容
    一段TS流,必须包含PAT包、PMT包、多个音频包、多个视频包、多个PCR包、以及其他信息包。

    解析TS流数据的流程:查找PID为0x0的包,解析PAT,PAT包中的program_map_PID表示PMT的PID;查找PMT,PMT包中的elementary_PID表示音视频包的PID,PMT包中的PCR_PID表示PCR的PID,有的时候PCR的PID跟音频或者视频的PID相同,说明PCR会融进音视频的包,注意解析,有的时候PCR是自己单独的包;CAT、NIT、SDT、EIT的PID分别为: 0x01、0x10、0x11、0x12。

3.TS包头解析
    TS包头有4个字节

//Transport Stream header
typedef struct TS_header
{
         unsigned sync_byte                  :8;      //同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的
         unsigned transport_error_indicator       :1;      //传输错误标志位,一般传输错误的话就不会处理这个包了
         unsigned payload_unit_start_indicator    :1;      //有效负载的开始标志,根据后面有效负载的内容不同功能也不同
         // payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。
         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_header;
    //特殊参数说明:
   //sync_byte:0x47
   //payload_unit_start_indicator:0x01表示含有PSI或者PES头
   //PID:0x0表示后面负载内容为PAT,不同的PID表示不同的负载
   //adaption_field_control:
      // 0x0: // reserved for future use by ISO/IEC
      // 0x1: // 无调整字段,仅含有效负载   
      // 0x2: // 仅含调整字段,无有效负载
      // 0x3: // 调整字段后含有效负载

// Parse TS header
int Parse_TS_header(unsigned char *pTSBuf, TS_header *pheader)
{
    pheader->sync_byte                                     = pTSBuf;
    if (pheader->sync_byte != 0x47)
      return -1;
    pheader->transport_error_indicator       = pTSBuf >> 7;
    pheader->payload_unit_start_indicator    = pTSBuf >> 6 & 0x01;
    pheader->transport_priority             = pTSBuf >> 5 & 0x01;
    pheader->PID                         = (pTSBuf & 0x1F) << 8 | pTSBuf;
    pheader->transport_scrambling_control   = pTSBuf >> 6;
    pheader->adaption_field_control         = pTSBuf >> 4 & 0x03;
    pheader->continuity_counter            = pTSBuf & 0x0F;
    return 0;
}
      TS包头解析需要参考:ISO/IEC 13818-1的2.4.3.2 Transport Stream packet layer

4.TS负载格式解析
4.1 PAT解析

   TS_header包头中的PID值为0x0,表示当前负载为PAT(Program Association Table)。PAT数据的信息可以理解为整个TS流包含的节目信息。

// Program Association Table
typedef struct PAT_Packet_tag
{
    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; //最后一个分段的号码
    // for(i=0; i<N; i++)
    // {
    unsigned program_number                  : 16;
    unsigned reserved_3                      : 3;
    unsigned network_PID                     : 16;// 或者program_map_PID
    unsigned CRC_32                        : 32;
    // }
} PAT_Packet;

// Parse PAT
int Parse_PAT(unsigned char *pTSBuf, PAT_Packet *packet)
{
    TS_header TSheader;
    if (Parse_TS_packet_header(pTSBuf, &TSheader) != 0)
      return -1;
    if (TSheader.payload_unit_start_indicator == 0x01) // 表示含有PSI或者PES头
    {
      if (TSheader.PID == 0x0)// 表示PAT
      {
             int iBeginlen = 4;
             int adaptation_field_length = pTSBuf;
             switch(TSheader.adaption_field_control)
             {
             case 0x0:                                    // reserved for future use by ISO/IEC
                  return -1;
             case 0x1:                                    // 无调整字段,仅含有效负载      
                  iBeginlen += pTSBuf + 1;// + pointer_field
                  break;
             case 0x2:                                     // 仅含调整字段,无有效负载
                  return -1;
             case 0x3: // 调整字段后含有效负载
               if (adaptation_field_length > 0)
               {
                      iBeginlen += 1;                   // adaptation_field_length占8位
                      iBeginlen += adaptation_field_length; // + adaptation_field_length
               }
               else
               {
                      iBeginlen += 1;                     // adaptation_field_length占8位
               }
               iBeginlen += pTSBuf + 1;         // + pointer_field
               break;
            default:
               break;
            }
            unsigned char *pPAT = pTSBuf + iBeginlen;
            packet->table_id                  = pTSBuf;
            packet->section_syntax_indicator    = pTSBuf >> 7;
            packet->zero                        = pTSBuf >> 6 & 0x1;
            packet->reserved_1                  = pTSBuf >> 4 & 0x3;
            packet->section_length            = (pTSBuf & 0x0F) << 8 | pTSBuf;
            packet->transport_stream_id         = pTSBuf << 8 | pTSBuf;
            packet->reserved_2                  = pTSBuf >> 6;
            packet->version_number            = pTSBuf >> 1 &0x1F;
            packet->current_next_indicator      = (pTSBuf << 7) >> 7;
            packet->section_number            = pTSBuf;
            packet->last_section_number         = pTSBuf;
            int len = 0;
            len = 3 + packet->section_length;
            packet->CRC_32                      = (pTSBuf & 0x000000FF) << 24
                                                | (pTSBuf & 0x000000FF) << 16
                                                | (pTSBuf & 0x000000FF) << 8
                                                | (pTSBuf & 0x000000FF);

            int n = 0;
            for ( n = 0; n < (packet->section_length - 12); n += 4 )
            {
               packet->program_number = pTSBuf << 8 | pTSBuf;
               packet->reserved_3                = pTSBuf >> 5;
               if ( packet->program_number == 0x00)
               {
                     packet->network_PID = (pTSBuf & 0x1F) << 8 | pTSBuf;
               }
               else
               {
                     // 有效的PMT的PID,然后通过这个PID值去查找PMT包
                     program_map_PID = (pTSBuf & 0x1F) << 8 | pTSBuf;
               }
            }
            return 0;
         }
    }
    return -1;
}
  PAT数据解析需要参考:ISO/IEC 13818-1的2.4.4.3 Program Association Table

4.2 PMT解析

    由PAT包中的program_map_PID可以确定PMT(Program Map Table)的PID。PMT数据的信息可以理解为这个节目包含的音频和视频信息。



// Program Map Table
typedef struct PMT_Packet_tag
{
   unsigned table_id                        : 8;
   unsigned section_syntax_indicator      : 1;
   unsigned zero                            : 1;
   unsigned reserved_1                      : 2;
   unsigned section_length                  : 12;
   unsigned program_number                  : 16;
   unsigned reserved_2                      : 2;
   unsigned version_number                  : 5;
   unsigned current_next_indicator          : 1;
   unsigned section_number                  : 8;
   unsigned last_section_number             : 8;
   unsigned reserved_3                      : 3;
   unsigned PCR_PID                         : 13;
   unsigned reserved_4                      : 4;
   unsigned program_info_length             : 12;
   // for(i=0; i<N; i++)
   // {
   unsigned stream_type                     : 8;
   unsigned reserved_5                      : 3;
   unsigned elementary_PID                  : 13;
   unsigned reserved_6                      : 4;
   unsigned ES_info_length                  : 12;
   // }
   unsigned CRC_32                        : 32;
} PMT_Packet;
   // Parse PMT
int Parse_PMT(unsigned char *pTSBuf, PMT_Packet *packet)
{
    // 参考Parse_PAT()来做就行了
    // ...

    return 0;
}
  PMT数据解析需要参考:ISO/IEC 13818-1的2.4.4.8 Program Map Table

4.3 PES解析

    根据文档参考PAT、PMT的解析流程就能完成PES的解析了。

    需要注意的是PES中PTS的解析,一般来说在90 kHz 中,PTS/9000的值为秒单位。



unsigned long long Parse_PTS(unsigned *pBuf)
{
   unsigned long long llpts = (((unsigned long long)(pBuf & 0x0E)) << 29)
         | (unsigned long long)(pBuf << 22)
         | (((unsigned long long)(pBuf & 0xFE)) << 14)
         | (unsigned long long)(pBuf << 7)
         | (unsigned long long)(pBuf >> 1);
   return llpts;
}
   PES数据解析需要参考:2.5.5.1 Syntax of the PES packet syntax for Program Stream directory

5.码流分析工具
5.1 Elecard Stream Analyzer

5.2 EasyICE




bean.yang 发表于 2016-10-18 11:29:54

科普
页: [1]
查看完整版本: TS的解码方式和传输流数据包格式