從mpeg ts文件中提取I幀(2):pat pmt解析

一、PAT用途
1、描述當前傳輸流中 PMT 的 PID 信息。
2、描述PMT,與SDT的對應關係。
3、program_number=0時爲network pid即nit的pid,接收pmt時注意跳過這nit。
4、pat是整個ts流的入口,依據pat描述的pmt pid就可以搜索出所有的pmt信息。

其語法結構如下圖所示:

PAT解析代碼:

int mpeg_psi_pat_parse(uint8_t *sec_buf, int32_t sec_len, int16_t pid)
{
    uint8_t* buf = sec_buf;
    uint32_t program_loop_length = (uint16_t)(((buf[1]&0x0f)<<8)|buf[2]) - 9;

    if (MPEGPSI_TID_PAT != buf[0]) {
        print_err("pat tid=%d\n", buf[0]);
        return -1;
    }

    printf("pat section pid=0x%04x\n", pid);
    printf(" |-table_id                =0x%x\n", (uint8_t )(  buf[0])                 );
    printf(" |-section_syntax_indicator=0x%x\n", (uint8_t )(( buf[1]&0x80)>>7)        );
    printf(" |-section_length          =0x%x\n", (uint16_t)(((buf[1]&0x0f)<<8)|buf[2]));
    printf(" |-extend_table_id         =0x%x\n", (uint16_t)(( buf[3]<<8)|buf[4])      );
    printf(" |-version_number          =0x%x\n", (uint8_t )(( buf[5]&0x3e)>>1)        );
    printf(" |-current_next_indicator  =0x%x\n", (uint8_t )(  buf[5]&0x01)            );
    printf(" |-section_number          =0x%x\n", (uint8_t )(  buf[6])                 );
    printf(" |-last_section_number     =0x%x\n", (uint8_t )(  buf[7])                 );

    g_mpeg_transport.transport_stream_id = (uint16_t)(( buf[3]<<8)|buf[4]);
    g_mpeg_transport.program_numb        = 0;
    buf = sec_buf + 8;
    while( program_loop_length > 0 ) {   
        uint16_t program_number  = (uint16_t)( buf[0]<<8       | buf[1]);
        uint16_t program_map_pid = (uint16_t)((buf[2]&0x1f)<<8 | buf[3]);
        printf("   |-program_number=0x%04x program_map_pid=0x%04x (%d)\n", program_number, program_map_pid, program_map_pid);           

        if (g_mpeg_transport.program_numb < MPEGPSI_MAX_PROGRAM) {
            g_mpeg_transport.program_info[g_mpeg_transport.program_numb].program_number  = program_number;
            g_mpeg_transport.program_info[g_mpeg_transport.program_numb].program_map_pid = program_map_pid;
            g_mpeg_transport.program_numb ++;
        }
        else {
            print_err("MPEGPSI_MAX_PROGRAM=%d overflow.\n", MPEGPSI_MAX_PROGRAM);
        }

        if( program_loop_length >= 4 ) {
            program_loop_length = program_loop_length - 4;    
            buf = buf + 4;
        }
        else {
            printf("pat parse error !!!\n");
            break;
        }   
    }

    buf = sec_buf + (sec_len-4);
    printf(" |-crc_32 = 0x%x 0x%x\n", (uint32_t)((buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|(buf[3])), mpeg_crc32(sec_buf, sec_len-4));
    printf("\n");

    return 0;
}

PAT解析運行結果:

pat section pid=0x0000
 |-table_id                =0x0
 |-section_syntax_indicator=0x1
 |-section_length          =0x25
 |-extend_table_id         =0x2
 |-version_number          =0x1
 |-current_next_indicator  =0x1
 |-section_number          =0x0
 |-last_section_number     =0x0
   |-program_number=0x0000 program_map_pid=0x0010 (16)
   |-program_number=0x00c9 program_map_pid=0x0100 (256)
   |-program_number=0x00ca program_map_pid=0x0200 (512)
   |-program_number=0x00cb program_map_pid=0x0300 (768)
   |-program_number=0x00cc program_map_pid=0x0400 (1024)
   |-program_number=0x00cd program_map_pid=0x0500 (1280)
   |-program_number=0x00ce program_map_pid=0x0600 (1536)
 |-crc_32 = 0x6012d6e2 0x6012d6e2

二、PMT用途
1、當前頻道中包含的所有Video數據的PID
2、當前頻道中包含的所有Audio數據的PID
3、和當前頻道關聯在一起的其他數據的PID(如數字廣播等使用的PID)
4、加擾節目授權控制信息 ECM PID


PMT解析代碼:

int mpeg_psi_pmt_parse(uint8_t *sec_buf, int32_t sec_len, int16_t pid)
{
    uint8_t *buf                 = sec_buf;
    uint8_t *ptr                 = NULL;
    uint16_t program_info_length = (uint16_t)(((buf[10]&0x0f)<<8)|buf[11]);
    uint16_t stream_loop_length  = 0;
    uint16_t section_length      = (uint16_t)(((buf[1]&0x0f)<<8)|buf[2]);
    mpeg_program_t *program      = NULL;
    int i                        = 0;

    if (MPEGPSI_TID_PMT != buf[0]) {
        print_err("pat tid=%d\n", buf[0]);
        return -1;
    }

    for (i=0; i<g_mpeg_transport.program_numb; i++) {
        if (g_mpeg_transport.program_info[i].program_map_pid == pid) {
            program = &(g_mpeg_transport.program_info[i]);
            break;
        }
    }

    printf("pmt section pid=0x%04x(%d)\n", pid, pid);
    printf("|-table_id                 = 0x%x\n", (uint8_t)(   buf[0])                 );
    printf("|-section_syntax_indicator = 0x%x\n", (uint8_t)((  buf[1]&0x80)>>7)        );
    printf("|-section_length           = 0x%x\n", (uint16_t)(((buf[1]&0x0f)<<8)|buf[2]));
    printf("|-program_number           = 0x%x\n", (uint16_t)(( buf[3]<<8)|buf[4])      );
    printf("|-version_number           = 0x%x\n", (uint8_t)((  buf[5]&0x3e)>>1)        );
    printf("|-current_next_indicator   = 0x%x\n", (uint8_t)(   buf[5]&0x01)            );
    printf("|-section_number           = 0x%x\n", (uint8_t)(   buf[6])                 );
    printf("|-last_section_number      = 0x%x\n", (uint8_t)(   buf[7])                 );
    printf("|-pcr_pid                  = 0x%x\n", (uint16_t)(((buf[8]&0x1f)<<8)|buf[9]));
    printf("|-program_info_length      = 0x%x\n", program_info_length                  );

    pmt_descriptor_parse(buf+12, program_info_length, 0);

    stream_loop_length = section_length - program_info_length - 13;
    ptr = buf + 12 + program_info_length;
    while (stream_loop_length) {
        uint16_t es_info_length = 0;
        uint16_t stream_type    = 0;
        uint16_t elementary_pid = 0;
        char     x              = '-';

        es_info_length = (uint16_t)(((ptr[3]&0x0f)<<8)|ptr[4]);
        if (es_info_length!=0) {
            x = '+';
        }

        elementary_pid = (uint16_t)(((ptr[1] & 0x1f) << 8) | ptr[2]);
        stream_type    = ptr[0];
        printf("|%ces_pid = 0x%x stream_type = 0x%x\n", x, elementary_pid, stream_type);
        
        if (NULL != program) {
            if (program->stream_numb < MPEGPSI_MAX_STREAM) {
                program->stream_info[program->stream_numb].elementary_pid = elementary_pid;
                program->stream_info[program->stream_numb].stream_type    = stream_type;
                program->stream_numb ++;
            }
            else {
                print_err("MPEGPSI_MAX_STREAM=%d overflow.\n", MPEGPSI_MAX_STREAM);
            }
        }

        pmt_descriptor_parse(ptr+5, es_info_length, 1);

        es_info_length += 5;
        if (stream_loop_length >= es_info_length) {
            stream_loop_length -= es_info_length;
            ptr +=es_info_length;
        }
        else {
            break;
        }
    }
    
    buf = sec_buf + (sec_len-4);
    printf("|-crc_32 = 0x%x 0x%x\n", (uint32_t)((buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|(buf[3])), mpeg_crc32(sec_buf, sec_len-4));
    printf("\n");

    return 0;
}

PMT解析運行結果:

mt section pid=0x0100(256)
|-table_id                 = 0x2
|-section_syntax_indicator = 0x1
|-section_length           = 0x4f
|-program_number           = 0xc9
|-version_number           = 0x1
|-current_next_indicator   = 0x1
|-section_number           = 0x0
|-last_section_number      = 0x0
|-pcr_pid                  = 0x1ffe
|-program_info_length      = 0x17
|+descriptor
 |-unknown_descriptor tag=0x0b len=0x2 4a 1f 
 |-unknown_descriptor tag=0x0c len=0x4 80 b4 81 68 
 |-unknown_descriptor tag=0x0e len=0x3 c0 1e c6 
 |-unknown_descriptor tag=0x10 len=0x6 c0 1e c6 c0 08 00 
|+es_pid = 0x101 stream_type = 0x2
 |-unknown_descriptor tag=0x02 len=0x3 1a 48 5f 
 |-unknown_descriptor tag=0x52 len=0x1 00 
 |-unknown_descriptor tag=0x0e len=0x3 c0 1c f0 
 |-unknown_descriptor tag=0x06 len=0x1 02 
|+es_pid = 0x102 stream_type = 0x4
 |-unknown_descriptor tag=0x03 len=0x1 67 
 |-unknown_descriptor tag=0x0a len=0x4 65 6e 67 00 
 |-unknown_descriptor tag=0x52 len=0x1 8a 
 |-unknown_descriptor tag=0x0e len=0x3 c0 01 68 
|-crc_32 = 0x580343a4 0x580343a4

三、爲什要解析pat pmt
1、用來校驗輸入的視頻pid是否合法,免去了遍歷整個ts的開銷。
2、當不知道視頻pid時,可以通過pat pmt的解析,顯示所有的視音頻pid。

mpeg2標準:https://download.csdn.net/download/maxzero/10402761
完整的代碼:https://download.csdn.net/download/maxzero/10572383

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章