一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

關(guān)于TS流的解析

 昵稱286123 2009-09-08
TS即是"Transport Stream"的縮寫。他是分包發(fā)送的,每一個包長為188字節(jié)。在TS流里可以填入很多類型的數(shù)據(jù),如視頻、音頻、自定義信息等。他的包的結(jié)構(gòu)為,包頭為4個字節(jié),負載為184個字節(jié)(這184個字節(jié)不一定都是有效數(shù)據(jù),有一些可能為填充數(shù)據(jù))。
工作形式:
因為在TS流里可以填入很多種東西,所以有必要有一種機制來確定怎么來標(biāo)識這些數(shù)據(jù)。制定TS流標(biāo)準(zhǔn)的機構(gòu)就規(guī)定了一些數(shù)據(jù)結(jié)構(gòu)來定義。比如: PSI(Program Specific Information)表,所以解析起來就像這樣: 先接收一個負載里為PAT的數(shù)據(jù)包,在整個數(shù)據(jù)包里找到一個PMT包的ID。然后再接收一個含有PMT的數(shù)據(jù)包,在這個數(shù)據(jù)包里找到有關(guān)填入數(shù)據(jù)類型的ID。之后就在接收到的TS包里找含有這個ID的負載內(nèi)容,這個內(nèi)容就是填入的信息。根據(jù)填入的數(shù)據(jù)類型的ID的不同,在TS流復(fù)合多種信息是可行的。關(guān)鍵就是找到標(biāo)識的ID號。
現(xiàn)在以一個例子來說明具體的操作:
在開始之前先給出一片實際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;
}
這是一個調(diào)整TS流數(shù)據(jù)包頭的函數(shù),這里牽扯到位段調(diào)整的問題。現(xiàn)在看一下TS流數(shù)據(jù)包頭的結(jié)構(gòu)的定義:
// 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包的標(biāo)識(即sync_byte)為0x47,并且為了確保這個TS包里的數(shù)據(jù)有效,所以我們一開始查找47 40 00這三組16進制數(shù),為什么這樣?具體的奧秘在TS包的結(jié)構(gòu)上,前面已經(jīng)說了sync_byte固定為0x47?,F(xiàn)在往下看transport_error_indicator、payload_unit_start_indicator、transport_priority和PID這四個元素,PID為0x00,這是PAT的標(biāo)識。transport_error_indicator為0,transport_priority為0。把他們看成是兩組8位16進制數(shù)就是:40 00?,F(xiàn)在看看我們的TS流片斷例子,看來正好是47 40 00開頭的,一個TS流的頭部占據(jù)了4個字節(jié)。剩下的負載部分的內(nèi)容由PID來決定,例子看來就是一個PAT表。在這里有個地方需要注意一下,payload_unit_start_indicator為1時,在前4個字節(jié)之后會有一個調(diào)整字節(jié),它的數(shù)值決定了負載內(nèi)容的具體開始位置。現(xiàn)在看例子中的數(shù)據(jù)47 40 00 17 00第五個字節(jié)是00,說明緊跟著00之后就是具體的負載內(nèi)容。
下面給出PAT表的結(jié)構(gòu)體:
// 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表字段調(diào)整函數(shù):
// 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;
    }
}
通過上面的分析,例子中的數(shù)據(jù)00 B0 0D 00 01 C1 00 00 00 01 E0 20 A2 C3 29 41就是具體的PAT表的內(nèi)容,然后根據(jù)PAT結(jié)構(gòu)體來具體分析PAT表。但是我們需要注意的是在PAT表里有program_number、network_PID的元素不只有一個,這兩個元素是通過循環(huán)來確定的。循環(huán)的次數(shù)通過section_length元素的確定。在這個例子中program_map_PID為20,所以下面來PMT分析時,就是查找47 40 20的開頭的TS包。
下面來分析PMT表,先給出PMT(Program Map Table)的結(jié)構(gòu)體:
// 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;
在給出調(diào)整字段函數(shù):
// 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流可以復(fù)合很多的節(jié)目的視頻和音頻,但是解碼器是怎么來區(qū)分的呢?答案就在PMT表里,如其名節(jié)目映射表。他就是來解決這個問題的。現(xiàn)在看PMT結(jié)構(gòu)體里的stream_type、elementary_PID這兩個元素,前一個用來確定后一個作為標(biāo)識PID的內(nèi)容具體是什么,音頻或視頻等。還有要注意他們不只有一個,所以他們是通過循環(huán)讀取來確保所有的值都被讀取了,當(dāng)然循環(huán)也是有規(guī)定的(具體看調(diào)整函數(shù)上)。從例子上來看,我們在倒數(shù)第三行找到了上面分析來的PMT表的PID為0x20的TS包。然后就可以把數(shù)據(jù)是用調(diào)整函數(shù)填入結(jié)構(gòu)中。然后得到具體節(jié)目的PID為視頻0x21, 音頻0x22。
PS. 文章里的PID是用來判斷具體TS包是什么包的。分析每個包得到的PID值,都可以復(fù)合在TS頭部結(jié)構(gòu)體的PID里。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日韩欧美精品一区二区三区| 国产亚洲神马午夜福利| 中国一区二区三区人妻| 日韩精品一区二区三区av在线| 激情国产白嫩美女在线观看| 日本不卡视频在线观看| 国产欧美日韩精品一区二区| 国产高清一区二区不卡| 东京热男人的天堂久久综合| 午夜久久久精品国产精品 | 国内欲色一区二区三区| 日本久久中文字幕免费| 九九热国产这里只有精品| 亚洲熟妇熟女久久精品 | 五月天丁香亚洲综合网| 国产一区二区精品丝袜| 成人日韩视频中文字幕| 老司机精品在线你懂的| 欧美黄色成人真人视频| 国产精品欧美激情在线播放| 久久99精品日韩人妻| 精品熟女少妇av免费久久野外| 国产在线一区二区三区不卡 | 亚洲欧美日韩色图七区| 亚洲性日韩精品一区二区| 亚洲精品偷拍视频免费观看| 日本婷婷色大香蕉视频在线观看| 亚洲精品熟女国产多毛| 日韩欧美黄色一级视频| 欧美日韩亚洲精品在线观看| 亚洲黄片在线免费小视频| 日本免费一级黄色录像| 黄色片国产一区二区三区| 日韩一区二区三区在线欧洲| 有坂深雪中文字幕亚洲中文| 国产日韩在线一二三区| 精品国产日韩一区三区| 九九九热在线免费视频| 国产高清在线不卡一区| 久久国产亚洲精品成人| 又大又长又粗又黄国产|