leaffei

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 }
View Code

  最后如果您觉得本篇对您有帮助,可以打赏下,谢谢!!!

posted on 2019-03-19 15:35  leaffei  阅读(2436)  评论(0编辑  收藏  举报

导航