TS 流解析

TS即是"Transport Stream"的缩写。他是分包发送的,每一个包长为188字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。他的包的结构为,包头为4个字节,负载为184个字节(这184个字节不一定都是有效数据,有一些可能为填充数据)。ts流是由很多不同种类的包所组成的,这些数据包都是188个字节大小,188个字节包含两部分,包头和负载,包头包括同步信息,包信息等等,而负载则是传输的数据,而这些负载则可以组成PES流或者私有流等等数据流.

工作形式: 
因为在TS流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定TS流标准的机构就规定了一些数据结构来定义。比如: PSIProgram Specific Information)表,所以解析起来就像这样: 先接收一个负载里为PAT的数据包,在整个数据包里找到一个PMT包的ID。然后再接收一个含有PMT的数据包,在这个数据包里找到有关填入数据类型的ID。之后就在接收到的TS包里找含有这个ID的负载内容,这个内容就是填入的信息。根据填入的数据类型的ID的不同,在TS流复合多种信息是可行的。关键就是找到标识的ID号。

TS包头定义:

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

 

TS包头解析代码:

HRESULTadjust_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

 return0;
}

如下为一个TS包数据:

0x47 0x40 0x000x12 0x00 0x00 0xb0 0x0d 0x00 0x00 0xc1 0x00 0x00 0x00 0x01 0xe3 0xe80xf0 0x0b 0xd7 0x79 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff

 

下面我们来分析,在ISO/IEC 13818-1里有说明,PAT(ProgramAssociation Table)PID值为0x00TS包的标识(sync_byte)0x47,并且为了确保这个TS包里的数据有效,所以我们一开始查找47 40 00这三组16进制数,为什么这样?具体的奥秘在TS包的结构上,前面已经说了sync_byte固定为0x47。现在往下看transport_error_indicatorpayload_unit_start_indicatortransport_priorityPID这四个元素,PID0x00,这是PAT的标识。transport_error_indicator0transport_priority0。把他们看成是两组816进制数就是:40 00。现在看看我们的TS流片断例子,看来正好是47 40 00开头的,一个TS流的头部占据了4个字节。剩下的负载部分的内容由PID来决定,例子看来就是一个PAT表。在这里有个地方需要注意一下,payload_unit_start_indicator1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据47 40 0017 00第五个字节是00,说明紧跟着00之后就是具体的负载内容。

分析知道前四位0x47 0x40 0x000x12TS头部即为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

 

下面给出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 0000 01 E0 20 A2 C3 29 41就是具体的PAT表的内容,然后根据PAT结构体来具体分析PAT表。但是我们需要注意的是在PAT表里有program_numbernetwork_PID的元素不只有一个,这两个元素是通过循环来确定的。循环的次数通过section_length元素的确定在这个例子中program_map_PID20,所以下面来PMT分析时,就是查找4740 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_s_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_typeelementary_PID这两个元素,前一个用来确定后一个作为标识PID的内容具体是什么,音频或视频等。还有要注意他们不只有一个,所以他们是通过循环读取来确保所有的值都被读取了,当然循环也是有规定的(具体看调整函数上)。从例子上来看,我们在倒数第三行找到了上面分析来的PMT表的PID0x20TS包。然后就可以把数据是用调整函数填入结构中。然后得到具体节目的PID为视频0x21, 音频0x22
PS.
 文章里的PID是用来判断具体TS包是什么包的。分析每个包得到的PID值,都可以复合在TS头部结构体的PID里。

 

 

 

 

扩展知识:

TS 188 字节流结构图

http://www.360doc.com/content/08/0403/09/8442_1161051.shtml

 

DVB各种表的解析顺序

http://www.360doc.com/content/08/0402/09/8442_1158980.shtml

 

TS流分析:

http://www.360doc.com/content/08/0419/14/8442_1200239.shtml

 

PSI/SI 各种表的解析(经典):

http://blog.sina.com.cn/s/blog_a57c156801014p57.html

PSI/SI解析(各种id说明)

http://blog.sina.com.cn/s/blog_a57c156801014p2m.html

 

 

数字电视中的常用PSI/SI表

http://blog.chinaunix.net/uid-20270058-id-1711801.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值