zoukankan      html  css  js  c++  java
  • 左右TS分析流

    TS即是"Transport Stream"的缩写。他是分包发送的,每个包长为188字节。在TS流里能够填入非常多类型的数据。如视频、音频、自己定义信息等。他的包的结构为,包头为4个字节,负载为184个字节(这184个字节不一定都是有效数据。有一些可能为填充数据)。
    工作形式: 
    由于在TS流里能够填入非常多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定TS流标准的机构就规定了一些数据结构来定义。

    比方: PSIProgram 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值为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 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_numbernetwork_PID的元素不仅仅有一个,这两个元素是通过循环来确定的。

    循环的次数通过section_length元素的确定。在这个样例中program_map_PID20,所以以下来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_typeelementary_PID这两个元素,前一个用来确定后一个作为标识PID的内容详细是什么。音频或视频等。还有要注意他们不仅仅有一个。所以他们是通过循环读取来确保全部的值都被读取了,当然循环也是有规定的(详细看调整函数上)。从样例上来看。我们在倒数第三行找到了上面分析来的PMT表的PID0x20TS包。

    然后就能够把数据是用调整函数填入结构中。然后得到详细节目的PID为视频0x21, 音频0x22

    PS. 文章里的PID是用来推断详细TS包是什么包的。分析每个包得到的PID值。都能够复合在TS头部结构体的PID于。

    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    LeetCode "Palindrome Partition II"
    LeetCode "Longest Substring Without Repeating Characters"
    LeetCode "Wildcard Matching"
    LeetCode "Best Time to Buy and Sell Stock II"
    LeetCodeEPI "Best Time to Buy and Sell Stock"
    LeetCode "Substring with Concatenation of All Words"
    LeetCode "Word Break II"
    LeetCode "Word Break"
    Some thoughts..
    LeetCode "Longest Valid Parentheses"
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4834691.html
Copyright © 2011-2022 走看看