参考链接: 1. MPEG-2 TS码流分析 https://blog.csdn.net/zhubin215130/article/details/8958567

TS Header

PAT

PMT

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <arpa/inet.h>

 #define TAB44 "    "
 #define TAB46 "      "
 #define PRINTF_DEBUG

 #define TS_PAT_PID 0x0

 #define TS_PAT_TABLE_PID 0x0
 #define TS_PMT_TABLE_PID 0x2

 #define MAX_PDTS_LEN 5
 #define MAX_TS_PROGRAM_NUM 8
 #define MAX_TS_STREAM_NUM 8
 #define MAX_PDTS_STRING_LEN 12
 #define MAX_TS_PACKET_LEN 188 /* 204, 188+16字节的CRC */

 /******************************************************************
 视频
 MPEG-1 Video:stream_type为0x01
 MPEG-2 Video:stream_type为0x02
 AVC(H264):stream_type为0x1b
 VC-1:stream_type为0xea

 音频
 Mpeg-1 Audio:stream_type为0x03
 Mpeg-2 Audio:stream_type为0x04
 Mpeg-2 AAC:stream_type为0x0f
 Mpeg-4 AAC:stream_type为0x11
 LPCM:stream_type为0x80
 AC3:stream_type为0x81或0x06
 DTS:stream_type为0x82
 Dolby TrueHD:stream_type为0x83
 AC3-Plus:stream_type为0x84
 DTS_HD:stream_type为0x85
 DTS-MA:stream_type为0x86
 AC3-Plus_SEC:steam_type为0xa1
 DTS_HD_SEC:stream_type为0xa2

 字幕
 PGS:stream_type为0x90
 IGS:steam_type为0x91,暂不支持
 Text Subtitle:stream_type为0x92
 ********************************************************************/
 typedef enum t_ts_stream_type
 {
     E_STREAM_TYPE_MPEG1_VIDEO         = 0x01,
     E_STREAM_TYPE_MPEG2_VIDEO         = 0x02,
     E_STREAM_TYPE_AVC_VIDEO         = 0x1B,
     E_STREAM_TYPE_VC1_VIDEO         = 0xEA,
     E_STREAM_TYPE_MPEG1_AUDIO         = 0x03,
     E_STREAM_TYPE_MPEG2_AUDIO         = 0x04,
     E_STREAM_TYPE_MPEG2_AAC         = 0x0F,
     E_STREAM_TYPE_MPEG4_AAC         = 0x11,
     E_STREAM_TYPE_AC3                 = 0x81,
 } T_TS_STREAM_TYPE;

 /* 4 bytes */
 typedef struct t_ts_packet_header
 {
     unsigned char sync_byte;
     unsigned , playload_unit_start_indictor:, transport_priority:, pid:;
     unsigned , adaptation_field_control:, continuity_counter:;
 } T_TS_PACKET_HEADER;

 /* PAT */
 typedef struct t_ts_program
 {
     unsigned short program_number;
     unsigned short program_map_pid;
 } T_TS_PROGRAM;

 typedef struct t_ts_pat
 {
     unsigned char table_id;
     unsigned short section_len;
     unsigned ;
     unsigned short programNum;

     T_TS_PROGRAM programs[MAX_TS_PROGRAM_NUM];
 } T_TS_PAT;

 /* PMT */
 typedef struct t_ts_stream
 {
     unsigned char stream_type;
     unsigned short elementary_pid;
 } T_TS_STREAM;

 typedef struct t_ts_pmt
 {
     unsigned short pmtIsFind;
     unsigned char table_id;
     unsigned short section_len;
     unsigned short program_number;
     unsigned ;
     unsigned short program_info_length;

     unsigned short streamNum;

     T_TS_STREAM streams[MAX_TS_STREAM_NUM];
 } T_TS_PMT;

 /* PES */
 typedef struct t_ts_pes
 {
     unsigned char streamId;

     unsigned short pesLength;

     long long pts;
     long long dts;

     unsigned ];
     unsigned ];

     unsigned char pesHeaderLen;
 } T_TS_PES;

 T_TS_PAT g_TsPat = {};
 T_TS_PMT g_TsPmt[MAX_TS_PROGRAM_NUM] = {};

 static void ParseTsHeader(unsigned char* const headerData, T_TS_PACKET_HEADER *tsPacketHeader)
 {
     ;

     ;

     unsigned char *data = NULL;

     T_TS_PACKET_HEADER tsHeader = {};

     memset(&tsHeader, 0x0, sizeof(tsHeader));

     data = headerData;

     tsHeader.sync_byte = data[];
     tsHeader.transport_error_indictor = ((data[]>>)&0x1);
     tsHeader.playload_unit_start_indictor = ((data[]>>)&0x1);
     tsHeader.transport_priority = ((data[]>>)&0x1);
     tsHeader.pid = (((data[]&) | data[]);
     tsHeader.transport_scrambling_control = ((data[]>>)&0x3);
     tsHeader.adaptation_field_control = ((data[]>>)&0x3);
     tsHeader.continuity_counter = data[]&0xf;

     memcpy(tsPacketHeader, &tsHeader, sizeof(tsHeader));

 #ifdef PRINTF_DEBUG
     offset = tsPacketNum*MAX_TS_PACKET_LEN;

     switch (tsHeader.adaptation_field_control)
     {
         :
             if (tsHeader.playload_unit_start_indictor)
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n",
                         offset, tsHeader.pid, MAX_TS_PACKET_LEN-, tsHeader.continuity_counter);
             }
             else
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n",
                         offset, tsHeader.pid, MAX_TS_PACKET_LEN-, tsHeader.continuity_counter);
             }

             break;

         :
             if (tsHeader.playload_unit_start_indictor)
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d, Start indicactor}\n",
                         offset, tsHeader.pid, tsHeader.continuity_counter);
             }
             else
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = NO, Counter = %d}\n",
                         offset, tsHeader.pid, tsHeader.continuity_counter);
             }

             break;

         :
             if (tsHeader.playload_unit_start_indictor)
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d, Start indicactor}\n",
                         offset, tsHeader.pid, MAX_TS_PACKET_LEN---data[], tsHeader.continuity_counter);
             }
             else
             {
                 printf("0x%08x Transport Packet{PID = 0x%x, Payload = YES(%d), Counter = %d}\n",
                         offset, tsHeader.pid, MAX_TS_PACKET_LEN---data[], tsHeader.continuity_counter);
             }

             break;

         default:
             break;

     }

     tsPacketNum++;
 #endif
 }

 static void ParseTsPat(unsigned char* const patData, T_TS_PAT *tsPat)
 {
     ;
     ;

     unsigned char *data = NULL;

     T_TS_PAT pat = {};

     memset(&pat, 0x0, sizeof(pat));

     data = patData;

     pat.table_id = data[];

     if (TS_PAT_TABLE_PID != pat.table_id)
     {
         return;
     }

     sectionLen = ((data[]&) | data[];
     pat.section_len = sectionLen;

     pat.version_num = ((data[]>>) & 0x1f);

     data += ;
     sectionLen -= (+); /* len is after section_len and not have crc */

     )
     {
         if (i >= MAX_TS_PROGRAM_NUM)
         {
             break;
         }

         pat.programs[i].program_number = ((data[]<<) | data[]);

          != pat.programs[i].program_number)
         {
             pat.programs[i].program_map_pid = (((data[]&) | data[]);
         }

         data += ;
         sectionLen -= ;

         i++;

         pat.programNum = i;
     }

     memcpy(tsPat, &pat, sizeof(pat));

 #ifdef PRINTF_DEBUG
     printf("%s%s Program Association Table, version: %d\n", TAB46, TAB44, pat.version_num);

     ; i<pat.programNum; i++)
     {
         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);
     }
 #endif
 }

 static void ParseTsPmt(unsigned char* const pmtData, T_TS_PMT *tsPmt)
 {
     ;
     ;
     ;
     ;

     unsigned char *data = NULL;

     T_TS_PMT pmt = {};

     memset(&pmt, 0x0, sizeof(pmt));

     data = pmtData;

     pmt.table_id = data[];

     if (TS_PMT_TABLE_PID != pmt.table_id)
     {
         return;
     }

     sectionLen = ((data[]&) | data[];
     pmt.section_len = sectionLen;

     pmt.program_number = data[]<< | data[];

     pmt.version_num = ((data[]>>) & 0x1f);

     data += ;
     sectionLen -= (+);

     program_info_length = ((data[]&) | data[];

     data += ;
     sectionLen -= ;

     data += program_info_length;
     sectionLen -= program_info_length;

     )
     {
         if (i >= MAX_TS_STREAM_NUM)
         {
             break;
         }

         pmt.streams[i].stream_type = data[];
         pmt.streams[i].elementary_pid = (((data[]&) | data[]);

         es_info_length = ((data[]&) | data[];

         data += ;
         sectionLen -= ;

         data += es_info_length;
         sectionLen -= es_info_length;

         i++;

         pmt.streamNum = i;
     }

     pmt.pmtIsFind = ;

     memcpy(tsPmt, &pmt, sizeof(pmt));

 #ifdef PRINTF_DEBUG
     printf("%s%s Program Map Table, version: %d\n", TAB46, TAB44, pmt.version_num);

     ; i<pmt.streamNum; i++)
     {
         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,
             pmt.streams[i].elementary_pid, pmt.streams[i].elementary_pid);
     }
 #endif
 }

 static void getPdts(unsigned char *pdtsData, long long *pdts, unsigned char *pdtsString)
 {
     ;
     ;
     ;
     ;

     ;
     ;

     unsigned ] = {};

     /* 5个字节转33位的值 */
     pts = (((pdtsData[]>>) & ) | (pdtsData[] << ) | (((pdtsData[]>>) & ) | (pdtsData[] << ) | (pdtsData[]>> & 0x7f);

     /* 90KHz, 1000ms/90 */
     pts2Ms = pts/;

     hour = pts2Ms/(**);
     minute = (pts2Ms - hour * (**)) / (*);
     second = (pts2Ms - hour * (**) - minute * (*)) / ;
     msecond = pts2Ms - hour * (**) - minute * (*) - second * ;

     sprintf(ptsStr, "%02d:%02d:%02d:%03d", hour, minute, second, msecond);

     ptsStr[MAX_PDTS_STRING_LEN] = '\0';

     memcpy(pdtsString, ptsStr, MAX_PDTS_STRING_LEN);

     *pdts = pts;
 }

 /**************************************************************************************
 1. 根据playload_unit_start_indictor=1, 只解析这个ts包(该字段指示section, pes等的起始标识,
    若要拼包则根据这个字段, 一个整包结束这个字段会置0);
 2. 这里暂时不获取所有的pes包去解析, 只解析PES的头;
 3. 可能的问题是一个ts包可能有填充字段, 根据此字段的指示adaptation_field_control(判断有无填充),
    然后4字节包头后的第5个字节指示填充字段的长度, 若该长度大于一个ts包的长度, 则在此处是解析
    不到PES的数据的, 真正的PES数据应该在下一包;
 4. adaptation_field_control(适配域控制): 表示包头是否有调整字段或有效负载.
     '00'为ISO/IEC未来使用保留;
     '01'仅含有效载荷, 无调整字段;
     '10'无有效载荷, 仅含调整字段;
     '11'调整字段后为有效载荷, 调整字段中的前一个字节表示调整字段的长度length, 有效载荷开始的位置应再偏移length个字节;
     空包应为'10'.
     ** 这个地方有一个未证实的(基本不会错), 记录下来: adapt=1时, 貌似对于PSI/SI数据在第5个字节会写一个00(代码处理的地方为main(),ParseTsPat(&tsPacket[4+1], &g_TsPat)),
        对于pes数据若为1, 直接跟有效数据(代码处理的地方为ParsePes(), data += (4+1+data[4]).
 5. 此处还有一个需说明的, 有时会发现packet_length为0. 这个比较特殊, 标准里有说明.
    因为pes_packet_length为16个字节, 最大只能支持到65535, 当一个pes包大于这个数值的时候,
    处理方法是这个值为0, 实际的大小由上层协议决定.(只有当ts传输pes的时候才能这么做,
    通过根据playload_unit_start_indictor=1/0就可以确定这个pes包的真实长度.)
 6. 对于H264, 可能的就是将SPS, PPS等信息(数据量比较小)放到一个ts包中.
 ***************************************************************************************/
 static void ParsePes(unsigned char* const pesData, T_TS_PACKET_HEADER* const tsHeader)
 {
     unsigned char pts_dts_flag;

     unsigned char *data = NULL;

     unsigned ] = {};
     unsigned ] = {};

     T_TS_PES pes = {};

     data = pesData;

     memset(&pes, 0x0, sizeof(pes));

     /* deal adaptation */
      == tsHeader->adaptation_field_control)
         || ( == tsHeader->adaptation_field_control))
     {
         return;
     }

      == tsHeader->adaptation_field_control)
     {
         data += ;
     }
     else /* 3 */
     {
         /* header(4) + adaptlen(1) + adaptdata(adaptlen) */
         data += (++data[]);
     }

     data += ; /* start code 00 00 01*/

     pes.streamId = data[];
     pes.pesLength = (data[]<<) | data[];

     data += ; /* streamid(1) + pes_len(2) */

     pts_dts_flag = data[]>> & 0x3;

     pes.pesHeaderLen = data[];

     data += ;

     switch (pts_dts_flag)
     {
         : /* 00, no pts, dts */
             break;

         : /* 10, only pts*/
             memset(pts, 0x0, sizeof(pts));

             memcpy(pts, data, MAX_PDTS_LEN);

             getPdts(pts, &pes.pts, pes.ptsStr);

             break;

         : /* 11 pts & dts*/
             memset(pts, 0x0, sizeof(pts));
             memset(dts, 0x0, sizeof(dts));

             memcpy(pts, data, MAX_PDTS_LEN);
             memcpy(dts, data+MAX_PDTS_LEN, MAX_PDTS_LEN);

             getPdts(pts, &pes.pts, pes.ptsStr);
             getPdts(dts, &pes.dts, pes.dtsStr);

             break;

         default:
             break;
     }

 #ifdef PRINTF_DEBUG
     if ((pes.streamId>=0xC0) && (pes.streamId<=0xDF))
     {
         printf("%s%s PES Packet(Audio) {stream_id = 0x%x}\n", TAB46, TAB44, pes.streamId);
     }

     if ((pes.streamId>=0xE0) && (pes.streamId<=0xEF))
     {
         printf("%s%s PES Packet(Video) {stream_id = 0x%x}\n", TAB46, TAB44, pes.streamId);
     }

     printf("%s%s%s packet_length = %d, PES_header_data_length = %d\n", TAB46, TAB44, TAB44, pes.pesLength, pes.pesHeaderLen);
     printf("%s%s%s PTS: %s(%lld), DTS: %s(%lld)\n", TAB46, TAB44, TAB46, pes.ptsStr, pes.pts, pes.dtsStr, pes.dts);
 #endif

     /*
       1. todo: this test video is h264, parse pes data;
       2. get pes data, find h264 startcode, parse nual.
     */
 }

 int main(int argc, char *argv[])
 {
     ;
     ;
     ;
     ;
     ;

     unsigned };

     T_TS_PACKET_HEADER tsPacketHeader = {};

     FILE *fp = NULL;

      != argc)
     {
         printf("Usage: tsparse **.ts\n");

         ;
     }

     memset(&g_TsPat, 0x0, sizeof(T_TS_PAT));
     memset(g_TsPmt, 0x0, sizeof(g_TsPmt));

     fp = fopen(argv[], "rb");
     if (!fp)
     {
         printf(]);

         ;
     }

     )
     {
         memset(tsPacket, 0x0, MAX_TS_PACKET_LEN);
         memset(&tsPacketHeader, 0x0, sizeof(tsPacketHeader));

         , MAX_TS_PACKET_LEN, fp))
         {
             break;
         }

         ParseTsHeader(tsPacket, &tsPacketHeader);

         /* pat->pmt->(audio/video pid)->video data */
          == patIsFind)
         {
             if (TS_PAT_PID == tsPacketHeader.pid)
             {
                 /* 4(header) + 1(adapt len)*/
                 ParseTsPat(&tsPacket[+], &g_TsPat);

                 patIsFind = ;
             }
         }

          == patIsFind) && ( != allPmtIsFind))
         {
             ; i<g_TsPat.programNum; i++)
             {
                 if ((g_TsPat.programs[i].program_map_pid == tsPacketHeader.pid)
                     && ( == g_TsPmt[j].pmtIsFind))
                 {
                     ParseTsPmt(&tsPacket[+], &g_TsPmt[j]);

                     j++;
                 }
             }

             ; i<g_TsPat.programNum; i++)
             {
                  == g_TsPmt[i].pmtIsFind)
                 {
                     break;
                 }
             }

             if (i == g_TsPat.programNum)
             {
                 allPmtIsFind = ;
             }
         }

         if (allPmtIsFind)
         {
             ; i<g_TsPat.programNum; i++)
             {
                 ; k<g_TsPmt[i].streamNum; k++)
                 {
                     if ((g_TsPmt[i].streams[k].elementary_pid == tsPacketHeader.pid)
                         && ( == tsPacketHeader.playload_unit_start_indictor))
                     {
                         ParsePes(tsPacket, &tsPacketHeader);
                     }
                 }
             }
         }
     }

     fclose(fp);
 }

ts文件分析(纯c解析代码)的更多相关文章

  1. h264文件分析(纯c解析代码)

    参考链接:1. 解析H264的SPS信息 https://blog.csdn.net/lizhijian21/article/details/80982403               2. h.2 ...

  2. mpeg4文件分析(纯c解析代码)

    参考链接: 1. MPEG4码流的帧率计算 https://blog.csdn.net/littlebee90/article/details/68924690                2. M ...

  3. h265文件分析(纯c解析代码)

    参考链接: 1. HEVC码流解析 https://blog.csdn.net/CrystalShaw/article/details/80624804   2. HEVC编码结构:序列参数集SPS. ...

  4. mpeg2文件分析(纯c解析代码)

    参考链接: 1. MPEG-2码流结构分析 https://www.cnblogs.com/CoderTian/p/9246225.html(本文语法采用这里的截图,代码原创) 1. mpeg2的码流 ...

  5. ps文件解析(纯c解析代码)

    参考链接:1. PS流的格式和解析总结 http://www.cnblogs.com/lihaiping/p/4181607.html  2. TS科普5 PES包解析 https://blog.cs ...

  6. flv文件解析(纯c解析代码)

    参考链接: 1. FLV科普12 FLV脚本数据解析-Metadata Tag解析 https://blog.csdn.net/cabbage2008/article/details/50500021 ...

  7. mp4文件解析(纯c解析代码)

     参考链接:1. mp4文件格式解析 https://www.cnblogs.com/ranson7zop/p/7889272.html   2. MP4文件格式分析及分割实现(附源码) https: ...

  8. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  9. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

随机推荐

  1. C#获得网卡信息 NetworkInterface IPInterfaceProperties

    System.Net.NetworkInformation下的 1:NetworkInterface类,提供网络适配器的配置和统计信息. 可以通过它检测本机配置了多少网卡,哪些网络连接可用,获得网卡的 ...

  2. coalesce函数用法

    COALESCE函数会依次检查输入的参数,返回第一个不是NULL的参数,只有当传入COALESCE函数的所有的参数都是NULL的时候,函数才会返回NULL

  3. Objective-C中的@property

    1:@property 是什么? 在Objective-C中,@property 是声明属性的语法,它可以快速方便的为实例变量创建getter/setter方法. 2:@property 的本质? @ ...

  4. OpenscenGraph中控制swapbuffer的方法(用于多机大屏幕同步显示机制)

    **************************************************************************************************** ...

  5. python:Attempted relative import in non-package

    problem:Attempted relative import in non-package 所谓相对路径其实就是相对于当前module的路径,但如果直接执行脚本,这个module的name就是“ ...

  6. 【leetcode】Word Break II (hard)★

    Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each ...

  7. poj2348(博弈)

    poj2348 给定两个数a,b,大的数能减少小的数的倍数,不能是的数小于0,谁先使得数等于0,谁就赢了 有三种情况 ① a % b ==0  这个状态是必胜的 ② a - b < b  这个状 ...

  8. 清理SharePoint 2010的SQL Server 2008 R2日志数据库的方法!

    //来源:http://www.cnblogs.com/nbpowerboy/p/3380079.html 公司用SharePoint 2010已有三年多的时间了,上BPM项目也有2年多的时间,之前供 ...

  9. Window Screen对象

    window.screen 对象包含有关用户屏幕的信息. window.screen对象在编写时可以不使用 window 这个前缀.一些属性: screen.availWidth // 可用的屏幕宽度 ...

  10. STL在算法比赛中简单应用

    STL基础 和 简单的贪心问题 STL(Standard Template Library) 即 标准模板库. 它包含了诸多在计算机科学领域里所常用的基本数据结构和算法.这些数据结构可以与标准算法一起 ...