TS结构解析(详细的PAT和PMT解析代码)

http://blog.csdn.net/tuan891205/article/details/8557661


TS 即是 "Transport Stream" 的缩写。他是分包发送的,每一个包长为 188 字节。在 TS 流里可以填入很多类型的数据,如视频、音频、自定义信息等。他的包的结构为,包头为 4 个字节,负载为 184 个字节(这 184 个字节不一定都是有效数据,有一些可能为填充数据)。
工作形式:  
因为在 TS 流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定 TS 流标准的机构就规定了一些数据结构来定义。比如 : PSI Program Specific Information )表,所以解析起来就像这样 先接收一个负载里为 PAT 的数据包,在整个数据包里找到一个 PMT 包的 ID 。然后再接收一个含有 PMT 的数据包,在这个数据包里找到有关填入数据类型的 ID 。之后就在接收到的 TS 包里找含有这个 ID 的负载内容,这个内容就是填入的信息。根据填入的数据类型的 ID 的不同,在 TS 流复合多种信息是可行的。关键就是找到标识的 ID 号。
现在以一个例子来说明具体的操作:
在开始之前先给出一片实际 TS 流例子:  
0000f32ch: 47 40 00 17 00 00 B0 0D 00 01 C1 00 00 00 01 E0 ; G@....?..?...? 
0000f33ch: 20 A2 C3 29 41 FF FF FF FF FF FF FF FF FF FF FF ;  
)A
0000f34ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f35ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f36ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f37ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f38ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f39ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f3ach: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f3bch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f3cch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 

0000f3dch: FF FF FF FF FF FF FF FF FF FF FF FF 47 40 20 17 ; 
G@ .
0000f3ech: 00 02 B0 1B 00 01 C1 00 00 E0 21 F0 00 1B E0 21 ; ..?..?.??.?
0000f3fch: F0 04 2A 02 7E 1F 03 E0 22 F0 00 5D 16 BD 48    ; ?*.~..??].
紿
具体的分析就以这个例子来分析。
复制代码
//  Adjust TS packet header
void adjust_TS_packet_header(TS_packet_header* pheader)
{
    unsigned  char buf[ 4];
    memcpy(buf, pheader,  4);
    pheader->transport_error_indicator        = buf[ 1] >>  7;
    pheader->payload_unit_start_indicator    = buf[ 1] >>  6 &  0x01;
    pheader->transport_priority                = buf[ 1] >>  5 &  0x01;
    pheader->PID                            = (buf[ 1] &  0x1F) <<  8 | buf[ 2];
    pheader->transport_scrambling_control    = buf[ 3] >>  6;
    pheader->adaption_field_control            = buf[ 3] >>  4 &  0x03;
    pheader->continuity_counter                = buf[ 3] &  0x03;
}
复制代码
这是一个调整 TS 流数据包头的函数,这里牵扯到位段调整的问题。现在看一下 TS 流数据包头的结构的定义:
复制代码
//  Transport packet header
typedef  struct TS_packet_header
{
    unsigned sync_byte                        :  8;
    unsigned transport_error_indicator        :  1;
    unsigned payload_unit_start_indicator    :  1;
    unsigned transport_priority                :  1;
    unsigned PID                            :  13;
    unsigned transport_scrambling_control    :  2;
    unsigned adaption_field_control            :  2;
    unsigned continuity_counter                :  4;
} TS_packet_header;
复制代码
下面我们来分析,在 ISO/IEC 13818-1 里有说明, PAT(Program Association Table) PID 值为 0x00 TS 包的标识 ( sync_byte) 0x47 ,并且为了确保这个 TS 包里的数据有效,所以我们一开始查找 47 40 00 这三组 16 进制数,为什么这样?具体的奥秘在 TS 包的结构上,前面已经说了 sync_byte 固定为 0x47 。现在往下看 transport_error_indicator payload_unit_start_indicator transport_priority PID 这四个元素, PID 0x00 ,这是 PAT 的标识。 transport_error_indicator 0 transport_priority 0 。把他们看成是两组 8 16 进制数就是: 40 00 。现在看看我们的 TS 流片断例子,看来正好是 47 40 00 开头的,一个 TS 流的头部占据了 4 个字节。剩下的负载部分的内容由 PID 来决定,例子看来就是一个 PAT 表。在这里有个地方需要注意一下, payload_unit_start_indicator 1 时,在前 4 个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据 47 40 00 17 00 第五个字节是 00 ,说明紧跟着 00 之后就是具体的负载内容。
下面给出 PAT 表的结构体:
复制代码
//  PAT table
//  Programm Association Table
typedef  struct TS_PAT
{
    unsigned table_id                        :  8;
    unsigned section_syntax_indicator        :  1;
    unsigned zero                            :  1;
    unsigned reserved_1                        :  2;
    unsigned section_length                    :  12;
    unsigned transport_stream_id            :  16;
    unsigned reserved_2                        :  2;
    unsigned version_number                    :  5;
    unsigned current_next_indicator            :  1;
    unsigned section_number                    :  8;
    unsigned last_section_number            :  8;
    unsigned program_number                    :  16;
    unsigned reserved_3                        :  3;
    unsigned network_PID                    :  13;
    unsigned program_map_PID                :  13;
    unsigned CRC_32                            :  32;
} TS_PAT;
复制代码
再给出 PAT 表字段调整函数:

复制代码
//  Adjust PAT table
void adjust_PAT_table ( TS_PAT * packet,  char * buffer )
{
     int n =  0, i =  0;
     int len =  0;
    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];
     //  Get CRC_32
    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);
     //  Parse network_PID or program_map_PID
     for ( n =  0; n < packet->section_length -  4; n ++ )
    {
        packet->program_number            = buffer[ 8] <<  8 | buffer[ 9];
        packet->reserved_3                = buffer[ 10] >>  5;
         if ( packet->program_number ==  0x0 )
            packet->network_PID = (buffer[ 10] <<  3) <<  5 | buffer[ 11];
         else
        {
            packet->program_map_PID = (buffer[ 10] <<  3) <<  5 | buffer[ 11];
        }
        n +=  5;
    }
}
复制代码
通过上面的分析,例子中的数据 00 B0 0D 00 01 C1 00 00 00 01 E0 20 A2 C3 29 41 就是具体的 PAT 表的内容,然后根据 PAT 结构体来具体分析 PAT 表。但是我们需要注意的是在 PAT 表里有 program_number network_PID 的元素不只有一个,这两个元素是通过循环来确定的。循环的次数通过 section_length 元素的确定。在这个例子中 program_map_PID 20 ,所以下面来 PMT 分析时,就是查找 47 40 20 的开头的 TS 包。
下面来分析 PMT 表,先给出 PMT(Program Map Table) 的结构体:
复制代码
//  PMT table
//  Program Map Table
typedef  struct TS_PMT
{
    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;
   
    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;
} TS_PMT;
复制代码
在给出调整字段函数:
复制代码
//  Adjust PMT table
void adjust_PMT_table ( TS_PMT * packet,  char * buffer )
{
     int pos =  12, len =  0;
     int i =  0;
    packet->table_id                            = buffer[ 0];
    packet->section_syntax_indicator            = buffer[ 1] >>  7;
    packet->zero                                = buffer[ 1] >>  6;
    packet->reserved_1                            = buffer[ 1] >>  4;
    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;
    packet->reserved_4                            = buffer[ 10] >>  4;
    packet->program_info_length                    = (buffer[ 10] &  0x0F) <<  8 | buffer[ 11];
     //  Get CRC_32
    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);
     //  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; )
    {
        packet->stream_type                            = buffer[pos];
        packet->reserved_5                            = buffer[pos+ 1] >>  5;
        packet->elementary_PID                        = ((buffer[pos+ 1] <<  8) | buffer[pos+ 2]) &  0x1FFF;
        packet->reserved_6                            = buffer[pos+ 3] >>  4;
        packet->ES_info_length                        = (buffer[pos+ 3] &  0x0F) <<  8 | buffer[pos+ 4];
         //  Store in es
        es[i].type = packet->stream_type;
        es[i].pid = packet->elementary_PID;
         if ( packet->ES_info_length !=  0 )
        {
            pos = pos+ 5;
            pos += packet->ES_info_length;
        }
         else
        {
            pos +=  5;
        }
        i++;
    }
}
复制代码
TS 流可以复合很多的节目的视频和音频,但是解码器是怎么来区分的呢?答案就在 PMT 表里,如其名节目映射表。他就是来解决这个问题的。现在看 PMT 结构体里的 stream_type elementary_PID 这两个元素,前一个用来确定后一个作为标识 PID 的内容具体是什么,音频或视频等。还有要注意他们不只有一个,所以他们是通过循环读取来确保所有的值都被读取了,当然循环也是有规定的 ( 具体看调整函数上 ) 。从例子上来看,我们在倒数第三行找到了上面分析来的 PMT 表的 PID 0x20 TS 包。然后就可以把数据是用调整函数填入结构中。然后得到具体节目的 PID 为视频 0x21,  音频 0x22
PS.  文章里的 PID 是用来判断具体 TS 包是什么包的。分析每个包得到的 PID 值,都可以复合在 TS 头部结构体的 PID 里。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mpeg2-ts(MPEG-2 Transport Stream)是一种用于数字广播和视频传输的协议。它的目标是将音频、视频和其他数据以数据包的形式进行传输和存储。 mpeg2-ts的在线解析涉及将以mpeg2-ts格式编码的媒体文件进行解析和分析,以便播放或进行其他操作。解析过程包括以下几个步骤: 1. 读取文件头信息:首先读取文件的头部信息,其中包括文件的标识符、版本号、编码信息等。通过分析头部信息,可以确定文件的类型和结构。 2. 解析PAT表(Program Association Table):PAT表记录了各个节目的信息和各个PID(Packet ID)的映射关系。通过解析PAT表,可以获取到TS流中的所有节目的PID。 3. 解析PMT表(Program Map Table):PMT表描述了各个节目中包含的音频、视频和其他数据的PID,并提供了流的详细信息。通过解析PMT表,可以获取到各个节目中各个流的PID以及编码格式等相关信息。 4. 解析数据包:根据PID的信息,解析相应的数据包,提取出音频、视频和其他数据。对于音视频数据,还需要进行解码和解封装操作,以便进行播放或进一步处理。 5. 分析和处理数据:分析音视频和其他数据的格式、编码方式以及其他相关信息,以便进行后续处理,如解码、转码、封装等。 通过对mpeg2-ts文件进行在线解析,可以实现对媒体文件的播放、编辑、转码等功能。同时,也可以提取音视频数据进行分析和处理,以满足不同的应用需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值