ts文件分析(纯c解析代码)
参考链接: 1. MPEG-2 TS码流分析 https://blog.csdn.net/zhubin215130/article/details/8958567
TS Header
PAT
PMT
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <arpa/inet.h> 5 6 #define TAB44 " " 7 #define TAB46 " " 8 #define PRINTF_DEBUG 9 10 #define TS_PAT_PID 0x0 11 12 #define TS_PAT_TABLE_PID 0x0 13 #define TS_PMT_TABLE_PID 0x2 14 15 #define MAX_PDTS_LEN 5 16 #define MAX_TS_PROGRAM_NUM 8 17 #define MAX_TS_STREAM_NUM 8 18 #define MAX_PDTS_STRING_LEN 12 19 #define MAX_TS_PACKET_LEN 188 /* 204, 188+16字节的CRC */ 20 21 22 /****************************************************************** 23 视频 24 MPEG-1 Video:stream_type为0x01 25 MPEG-2 Video:stream_type为0x02 26 AVC(H264):stream_type为0x1b 27 VC-1:stream_type为0xea 28 29 音频 30 Mpeg-1 Audio:stream_type为0x03 31 Mpeg-2 Audio:stream_type为0x04 32 Mpeg-2 AAC:stream_type为0x0f 33 Mpeg-4 AAC:stream_type为0x11 34 LPCM:stream_type为0x80 35 AC3:stream_type为0x81或0x06 36 DTS:stream_type为0x82 37 Dolby TrueHD:stream_type为0x83 38 AC3-Plus:stream_type为0x84 39 DTS_HD:stream_type为0x85 40 DTS-MA:stream_type为0x86 41 AC3-Plus_SEC:steam_type为0xa1 42 DTS_HD_SEC:stream_type为0xa2 43 44 字幕 45 PGS:stream_type为0x90 46 IGS:steam_type为0x91,暂不支持 47 Text Subtitle:stream_type为0x92 48 ********************************************************************/ 49 typedef enum t_ts_stream_type 50 { 51 E_STREAM_TYPE_MPEG1_VIDEO = 0x01, 52 E_STREAM_TYPE_MPEG2_VIDEO = 0x02, 53 E_STREAM_TYPE_AVC_VIDEO = 0x1B, 54 E_STREAM_TYPE_VC1_VIDEO = 0xEA, 55 E_STREAM_TYPE_MPEG1_AUDIO = 0x03, 56 E_STREAM_TYPE_MPEG2_AUDIO = 0x04, 57 E_STREAM_TYPE_MPEG2_AAC = 0x0F, 58 E_STREAM_TYPE_MPEG4_AAC = 0x11, 59 E_STREAM_TYPE_AC3 = 0x81, 60 } T_TS_STREAM_TYPE; 61 62 /* 4 bytes */ 63 typedef struct t_ts_packet_header 64 { 65 unsigned char sync_byte; 66 unsigned short transport_error_indictor:1, playload_unit_start_indictor:1, transport_priority:1, pid:13; 67 unsigned char transport_scrambling_control:2, adaptation_field_control:2, continuity_counter:4; 68 } T_TS_PACKET_HEADER; 69 70 /* PAT */ 71 typedef struct t_ts_program 72 { 73 unsigned short program_number; 74 unsigned short program_map_pid; 75 } T_TS_PROGRAM; 76 77 typedef struct t_ts_pat 78 { 79 unsigned char table_id; 80 unsigned short section_len; 81 unsigned char version_num:5; 82 unsigned short programNum; 83 84 T_TS_PROGRAM programs[MAX_TS_PROGRAM_NUM]; 85 } T_TS_PAT; 86 87 /* PMT */ 88 typedef struct t_ts_stream 89 { 90 unsigned char stream_type; 91 unsigned short elementary_pid; 92 } T_TS_STREAM; 93 94 typedef struct t_ts_pmt 95 { 96 unsigned short pmtIsFind; 97 unsigned char table_id; 98 unsigned short section_len; 99 unsigned short program_number; 100 unsigned char version_num:5; 101 unsigned short program_info_length; 102 103 unsigned short streamNum; 104 105 T_TS_STREAM streams[MAX_TS_STREAM_NUM]; 106 } T_TS_PMT; 107 108 /* PES */ 109 typedef struct t_ts_pes 110 { 111 unsigned char streamId; 112 113 unsigned short pesLength; 114 115 long long pts; 116 long long dts; 117 118 unsigned char ptsStr[MAX_PDTS_STRING_LEN+1]; 119 unsigned char dtsStr[MAX_PDTS_STRING_LEN+1]; 120 121 unsigned char pesHeaderLen; 122 } T_TS_PES; 123 124 T_TS_PAT g_TsPat = {0}; 125 T_TS_PMT g_TsPmt[MAX_TS_PROGRAM_NUM] = {0}; 126 127 static void ParseTsHeader(unsigned char* const headerData, T_TS_PACKET_HEADER *tsPacketHeader) 128 { 129 static int tsPacketNum = 0; 130 131 int offset = 0; 132 133 unsigned char *data = NULL; 134 135 T_TS_PACKET_HEADER tsHeader = {0}; 136 137 memset(&tsHeader, 0x0, sizeof(tsHeader)); 138 139 data = headerData; 140 141 tsHeader.sync_byte = data[0]; 142 tsHeader.transport_error_indictor = ((data[1]>>7)&0x1); 143 tsHeader.playload_unit_start_indictor = ((data[1]>>6)&0x1); 144 tsHeader.transport_priority = ((data[1]>>5)&0x1); 145 tsHeader.pid = (((data[1]&0x1f)<<8) | data[2]); 146 tsHeader.transport_scrambling_control = ((data[3]>>6)&0x3); 147 tsHeader.adaptation_field_control = ((data[3]>>4)&0x3); 148 tsHeader.continuity_counter = data[3]&0xf; 149 150 memcpy(tsPacketHeader, &tsHeader, sizeof(tsHeader)); 151 152 #ifdef PRINTF_DEBUG 153 offset = tsPacketNum*MAX_TS_PACKET_LEN; 154 155 switch (tsHeader.adaptation_field_control) 156 { 157 case 1: 158 if (tsHeader.playload_unit_start_indictor) 159 { 160 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n", 161 offset, tsHeader.pid, MAX_TS_PACKET_LEN-4, tsHeader.continuity_counter); 162 } 163 else 164 { 165 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n", 166 offset, tsHeader.pid, MAX_TS_PACKET_LEN-4, tsHeader.continuity_counter); 167 } 168 169 break; 170 171 case 2: 172 if (tsHeader.playload_unit_start_indictor) 173 { 174 printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d, Start indicactor}\n", 175 offset, tsHeader.pid, tsHeader.continuity_counter); 176 } 177 else 178 { 179 printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d}\n", 180 offset, tsHeader.pid, tsHeader.continuity_counter); 181 } 182 183 break; 184 185 case 3: 186 if (tsHeader.playload_unit_start_indictor) 187 { 188 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n", 189 offset, tsHeader.pid, MAX_TS_PACKET_LEN-4-1-data[4], tsHeader.continuity_counter); 190 } 191 else 192 { 193 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n", 194 offset, tsHeader.pid, MAX_TS_PACKET_LEN-4-1-data[4], tsHeader.continuity_counter); 195 } 196 197 break; 198 199 default: 200 break; 201 202 } 203 204 tsPacketNum++; 205 #endif 206 } 207 208 static void ParseTsPat(unsigned char* const patData, T_TS_PAT *tsPat) 209 { 210 int i = 0; 211 int sectionLen = 0; 212 213 unsigned char *data = NULL; 214 215 T_TS_PAT pat = {0}; 216 217 memset(&pat, 0x0, sizeof(pat)); 218 219 data = patData; 220 221 pat.table_id = data[0]; 222 223 if (TS_PAT_TABLE_PID != pat.table_id) 224 { 225 return; 226 } 227 228 sectionLen = ((data[1]&0xf)<<8) | data[2]; 229 pat.section_len = sectionLen; 230 231 pat.version_num = ((data[5]>>1) & 0x1f); 232 233 data += 8; 234 sectionLen -= (5+4); /* len is after section_len and not have crc */ 235 236 while (sectionLen>0) 237 { 238 if (i >= MAX_TS_PROGRAM_NUM) 239 { 240 break; 241 } 242 243 pat.programs[i].program_number = ((data[0]<<8) | data[1]); 244 245 if (0 != pat.programs[i].program_number) 246 { 247 pat.programs[i].program_map_pid = (((data[2]&0x1f)<<8) | data[3]); 248 } 249 250 data += 4; 251 sectionLen -= 4; 252 253 i++; 254 255 pat.programNum = i; 256 } 257 258 memcpy(tsPat, &pat, sizeof(pat)); 259 260 #ifdef PRINTF_DEBUG 261 printf("%s%s Program Association Table, version: %d\n", TAB46, TAB44, pat.version_num); 262 263 for (i=0; i<pat.programNum; i++) 264 { 265 printf("%s%s%s program_number: %d, program_map_PID: 0x%x\n", TAB46, TAB44, TAB44, pat.programs[i].program_number, pat.programs[i].program_map_pid); 266 } 267 #endif 268 } 269 270 static void ParseTsPmt(unsigned char* const pmtData, T_TS_PMT *tsPmt) 271 { 272 int i = 0; 273 int sectionLen = 0; 274 int program_info_length = 0; 275 int es_info_length = 0; 276 277 unsigned char *data = NULL; 278 279 T_TS_PMT pmt = {0}; 280 281 memset(&pmt, 0x0, sizeof(pmt)); 282 283 data = pmtData; 284 285 pmt.table_id = data[0]; 286 287 if (TS_PMT_TABLE_PID != pmt.table_id) 288 { 289 return; 290 } 291 292 sectionLen = ((data[1]&0xf)<<8) | data[2]; 293 pmt.section_len = sectionLen; 294 295 pmt.program_number = data[3]<<8 | data[4]; 296 297 pmt.version_num = ((data[5]>>1) & 0x1f); 298 299 data += 10; 300 sectionLen -= (7+4); 301 302 program_info_length = ((data[0]&0xf)<<8) | data[1]; 303 304 data += 2; 305 sectionLen -= 2; 306 307 data += program_info_length; 308 sectionLen -= program_info_length; 309 310 while (sectionLen > 0) 311 { 312 if (i >= MAX_TS_STREAM_NUM) 313 { 314 break; 315 } 316 317 pmt.streams[i].stream_type = data[0]; 318 pmt.streams[i].elementary_pid = (((data[1]&0x1f)<<8) | data[2]); 319 320 es_info_length = ((data[3]&0xf)<<8) | data[4]; 321 322 data += 5; 323 sectionLen -= 5; 324 325 data += es_info_length; 326 sectionLen -= es_info_length; 327 328 i++; 329 330 pmt.streamNum = i; 331 } 332 333 pmt.pmtIsFind = 1; 334 335 memcpy(tsPmt, &pmt, sizeof(pmt)); 336 337 #ifdef PRINTF_DEBUG 338 printf("%s%s Program Map Table, version: %d\n", TAB46, TAB44, pmt.version_num); 339 340 for (i=0; i<pmt.streamNum; i++) 341 { 342 printf("%s%s%s stream_type: 0x%x(%d), elementary_pid: 0x%x(%d)\n", TAB46, TAB44, TAB44, pmt.streams[i].stream_type, pmt.streams[i].stream_type, 343 pmt.streams[i].elementary_pid, pmt.streams[i].elementary_pid); 344 } 345 #endif 346 } 347 348 static void getPdts(unsigned char *pdtsData, long long *pdts, unsigned char *pdtsString) 349 { 350 int hour = 0; 351 int minute = 0; 352 int second = 0; 353 int msecond = 0; 354 355 long long pts = 0; 356 long long pts2Ms = 0; 357 358 unsigned char ptsStr[MAX_PDTS_STRING_LEN+1] = {0}; 359 360 /* 5个字节转33位的值 */ 361 pts = (((pdtsData[0]>>1) & 0x7) << 30) | (pdtsData[1] << 22) | (((pdtsData[2]>>1) & 0x7f) << 15) | (pdtsData[3] << 7) | (pdtsData[4]>>1 & 0x7f); 362 363 /* 90KHz, 1000ms/90 */ 364 pts2Ms = pts/90; 365 366 hour = pts2Ms/(60*60*1000); 367 minute = (pts2Ms - hour * (60*60*1000)) / (60*1000); 368 second = (pts2Ms - hour * (60*60*1000) - minute * (60*1000)) / 1000; 369 msecond = pts2Ms - hour * (60*60*1000) - minute * (60*1000) - second * 1000; 370 371 sprintf(ptsStr, "%02d:%02d:%02d:%03d", hour, minute, second, msecond); 372 373 ptsStr[MAX_PDTS_STRING_LEN] = '\0'; 374 375 memcpy(pdtsString, ptsStr, MAX_PDTS_STRING_LEN); 376 377 *pdts = pts; 378 } 379 380 /************************************************************************************** 381 1. 根据playload_unit_start_indictor=1, 只解析这个ts包(该字段指示section, pes等的起始标识, 382 若要拼包则根据这个字段, 一个整包结束这个字段会置0); 383 2. 这里暂时不获取所有的pes包去解析, 只解析PES的头; 384 3. 可能的问题是一个ts包可能有填充字段, 根据此字段的指示adaptation_field_control(判断有无填充), 385 然后4字节包头后的第5个字节指示填充字段的长度, 若该长度大于一个ts包的长度, 则在此处是解析 386 不到PES的数据的, 真正的PES数据应该在下一包; 387 4. adaptation_field_control(适配域控制): 表示包头是否有调整字段或有效负载. 388 '00'为ISO/IEC未来使用保留; 389 '01'仅含有效载荷, 无调整字段; 390 '10'无有效载荷, 仅含调整字段; 391 '11'调整字段后为有效载荷, 调整字段中的前一个字节表示调整字段的长度length, 有效载荷开始的位置应再偏移length个字节; 392 空包应为'10'. 393 ** 这个地方有一个未证实的(基本不会错), 记录下来: adapt=1时, 貌似对于PSI/SI数据在第5个字节会写一个00(代码处理的地方为main(),ParseTsPat(&tsPacket[4+1], &g_TsPat)), 394 对于pes数据若为1, 直接跟有效数据(代码处理的地方为ParsePes(), data += (4+1+data[4]). 395 5. 此处还有一个需说明的, 有时会发现packet_length为0. 这个比较特殊, 标准里有说明. 396 因为pes_packet_length为16个字节, 最大只能支持到65535, 当一个pes包大于这个数值的时候, 397 处理方法是这个值为0, 实际的大小由上层协议决定.(只有当ts传输pes的时候才能这么做, 398 通过根据playload_unit_start_indictor=1/0就可以确定这个pes包的真实长度.) 399 6. 对于H264, 可能的就是将SPS, PPS等信息(数据量比较小)放到一个ts包中. 400 ***************************************************************************************/ 401 static void ParsePes(unsigned char* const pesData, T_TS_PACKET_HEADER* const tsHeader) 402 { 403 unsigned char pts_dts_flag; 404 405 unsigned char *data = NULL; 406 407 unsigned char pts[MAX_PDTS_LEN+1] = {0}; 408 unsigned char dts[MAX_PDTS_LEN+1] = {0}; 409 410 T_TS_PES pes = {0}; 411 412 data = pesData; 413 414 memset(&pes, 0x0, sizeof(pes)); 415 416 /* deal adaptation */ 417 if ((0 == tsHeader->adaptation_field_control) 418 || (2 == tsHeader->adaptation_field_control)) 419 { 420 return; 421 } 422 423 if (1 == tsHeader->adaptation_field_control) 424 { 425 data += 4; 426 } 427 else /* 3 */ 428 { 429 /* header(4) + adaptlen(1) + adaptdata(adaptlen) */ 430 data += (4+1+data[4]); 431 } 432 433 434 data += 3; /* start code 00 00 01*/ 435 436 pes.streamId = data[0]; 437 pes.pesLength = (data[1]<<8) | data[2]; 438 439 data += 3; /* streamid(1) + pes_len(2) */ 440 441 pts_dts_flag = data[1]>>6 & 0x3; 442 443 pes.pesHeaderLen = data[2]; 444 445 data += 3; 446 447 switch (pts_dts_flag) 448 { 449 case 0: /* 00, no pts, dts */ 450 break; 451 452 case 2: /* 10, only pts*/ 453 memset(pts, 0x0, sizeof(pts)); 454 455 memcpy(pts, data, MAX_PDTS_LEN); 456 457 getPdts(pts, &pes.pts, pes.ptsStr); 458 459 break; 460 461 case 3: /* 11 pts & dts*/ 462 memset(pts, 0x0, sizeof(pts)); 463 memset(dts, 0x0, sizeof(dts)); 464 465 memcpy(pts, data, MAX_PDTS_LEN); 466 memcpy(dts, data+MAX_PDTS_LEN, MAX_PDTS_LEN); 467 468 getPdts(pts, &pes.pts, pes.ptsStr); 469 getPdts(dts, &pes.dts, pes.dtsStr); 470 471 break; 472 473 default: 474 break; 475 } 476 477 #ifdef PRINTF_DEBUG 478 if ((pes.streamId>=0xC0) && (pes.streamId<=0xDF)) 479 { 480 printf("%s%s PES Packet(Audio) {stream_id = 0x%x}\n", TAB46, TAB44, pes.streamId); 481 } 482 483 if ((pes.streamId>=0xE0) && (pes.streamId<=0xEF)) 484 { 485 printf("%s%s PES Packet(Video) {stream_id = 0x%x}\n", TAB46, TAB44, pes.streamId); 486 } 487 488 printf("%s%s%s packet_length = %d, PES_header_data_length = %d\n", TAB46, TAB44, TAB44, pes.pesLength, pes.pesHeaderLen); 489 printf("%s%s%s PTS: %s(%lld), DTS: %s(%lld)\n", TAB46, TAB44, TAB46, pes.ptsStr, pes.pts, pes.dtsStr, pes.dts); 490 #endif 491 492 /* 493 1. todo: this test video is h264, parse pes data; 494 2. get pes data, find h264 startcode, parse nual. 495 */ 496 } 497 498 int main(int argc, char *argv[]) 499 { 500 int i = 0; 501 int j = 0; 502 int k = 0; 503 int patIsFind = 0; 504 int allPmtIsFind = 0; 505 506 unsigned char tsPacket[MAX_TS_PACKET_LEN] = {0}; 507 508 T_TS_PACKET_HEADER tsPacketHeader = {0}; 509 510 FILE *fp = NULL; 511 512 if (2 != argc) 513 { 514 printf("Usage: tsparse **.ts\n"); 515 516 return -1; 517 } 518 519 memset(&g_TsPat, 0x0, sizeof(T_TS_PAT)); 520 memset(g_TsPmt, 0x0, sizeof(g_TsPmt)); 521 522 fp = fopen(argv[1], "rb"); 523 if (!fp) 524 { 525 printf("open file[%s] error!\n", argv[1]); 526 527 return -1; 528 } 529 530 while (1) 531 { 532 memset(tsPacket, 0x0, MAX_TS_PACKET_LEN); 533 memset(&tsPacketHeader, 0x0, sizeof(tsPacketHeader)); 534 535 if (MAX_TS_PACKET_LEN != fread(tsPacket, 1, MAX_TS_PACKET_LEN, fp)) 536 { 537 break; 538 } 539 540 ParseTsHeader(tsPacket, &tsPacketHeader); 541 542 /* pat->pmt->(audio/video pid)->video data */ 543 if (0 == patIsFind) 544 { 545 if (TS_PAT_PID == tsPacketHeader.pid) 546 { 547 /* 4(header) + 1(adapt len)*/ 548 ParseTsPat(&tsPacket[4+1], &g_TsPat); 549 550 patIsFind = 1; 551 } 552 } 553 554 if ((1 == patIsFind) && (1 != allPmtIsFind)) 555 { 556 for (i=0; i<g_TsPat.programNum; i++) 557 { 558 if ((g_TsPat.programs[i].program_map_pid == tsPacketHeader.pid) 559 && (0 == g_TsPmt[j].pmtIsFind)) 560 { 561 ParseTsPmt(&tsPacket[4+1], &g_TsPmt[j]); 562 563 j++; 564 } 565 } 566 567 for (i=0; i<g_TsPat.programNum; i++) 568 { 569 if (0 == g_TsPmt[i].pmtIsFind) 570 { 571 break; 572 } 573 } 574 575 if (i == g_TsPat.programNum) 576 { 577 allPmtIsFind = 1; 578 } 579 } 580 581 if (allPmtIsFind) 582 { 583 for (i=0; i<g_TsPat.programNum; i++) 584 { 585 for (k=0; k<g_TsPmt[i].streamNum; k++) 586 { 587 if ((g_TsPmt[i].streams[k].elementary_pid == tsPacketHeader.pid) 588 && (1 == tsPacketHeader.playload_unit_start_indictor)) 589 { 590 ParsePes(tsPacket, &tsPacketHeader); 591 } 592 } 593 } 594 } 595 } 596 597 fclose(fp); 598 }
最后如果您觉得本篇对您有帮助,可以打赏下,谢谢!!!