从mpeg ts文件中提取I帧(2):PAT,PMT解释
程序员文章站
2022-03-23 09:05:24
...
一、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
下一篇: 回去要杀了我老公