参考链接: 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 short transport_error_indictor:, playload_unit_start_indictor:, transport_priority:, pid:;
unsigned char transport_scrambling_control:, 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 char version_num:;
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 char version_num:;
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 char ptsStr[MAX_PDTS_STRING_LEN+];
unsigned char dtsStr[MAX_PDTS_STRING_LEN+]; 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)
{
static int tsPacketNum = ; int offset = ; 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[]&0x1f)<<) | 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)
{
case :
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; case :
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; case :
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)
{
int i = ;
int sectionLen = ; 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[]&0xf)<<) | data[];
pat.section_len = sectionLen; pat.version_num = ((data[]>>) & 0x1f); data += ;
sectionLen -= (+); /* len is after section_len and not have crc */ while (sectionLen>)
{
if (i >= MAX_TS_PROGRAM_NUM)
{
break;
} pat.programs[i].program_number = ((data[]<<) | data[]); if ( != pat.programs[i].program_number)
{
pat.programs[i].program_map_pid = (((data[]&0x1f)<<) | 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); for (i=; 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)
{
int i = ;
int sectionLen = ;
int program_info_length = ;
int es_info_length = ; 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[]&0xf)<<) | data[];
pmt.section_len = sectionLen; pmt.program_number = data[]<< | data[]; pmt.version_num = ((data[]>>) & 0x1f); data += ;
sectionLen -= (+); program_info_length = ((data[]&0xf)<<) | data[]; data += ;
sectionLen -= ; data += program_info_length;
sectionLen -= program_info_length; while (sectionLen > )
{
if (i >= MAX_TS_STREAM_NUM)
{
break;
} pmt.streams[i].stream_type = data[];
pmt.streams[i].elementary_pid = (((data[]&0x1f)<<) | data[]); es_info_length = ((data[]&0xf)<<) | 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); for (i=; 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)
{
int hour = ;
int minute = ;
int second = ;
int msecond = ; long long pts = ;
long long pts2Ms = ; unsigned char ptsStr[MAX_PDTS_STRING_LEN+] = {}; /* 5个字节转33位的值 */
pts = (((pdtsData[]>>) & 0x7) << ) | (pdtsData[] << ) | (((pdtsData[]>>) & 0x7f) << ) | (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 char pts[MAX_PDTS_LEN+] = {};
unsigned char dts[MAX_PDTS_LEN+] = {}; T_TS_PES pes = {}; data = pesData; memset(&pes, 0x0, sizeof(pes)); /* deal adaptation */
if (( == tsHeader->adaptation_field_control)
|| ( == tsHeader->adaptation_field_control))
{
return;
} if ( == 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)
{
case : /* 00, no pts, dts */
break; case : /* 10, only pts*/
memset(pts, 0x0, sizeof(pts)); memcpy(pts, data, MAX_PDTS_LEN); getPdts(pts, &pes.pts, pes.ptsStr); break; case : /* 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[])
{
int i = ;
int j = ;
int k = ;
int patIsFind = ;
int allPmtIsFind = ; unsigned char tsPacket[MAX_TS_PACKET_LEN] = {}; T_TS_PACKET_HEADER tsPacketHeader = {}; FILE *fp = NULL; if ( != argc)
{
printf("Usage: tsparse **.ts\n"); return -;
} memset(&g_TsPat, 0x0, sizeof(T_TS_PAT));
memset(g_TsPmt, 0x0, sizeof(g_TsPmt)); fp = fopen(argv[], "rb");
if (!fp)
{
printf("open file[%s] error!\n", argv[]); return -;
} while ()
{
memset(tsPacket, 0x0, MAX_TS_PACKET_LEN);
memset(&tsPacketHeader, 0x0, sizeof(tsPacketHeader)); if (MAX_TS_PACKET_LEN != fread(tsPacket, , MAX_TS_PACKET_LEN, fp))
{
break;
} ParseTsHeader(tsPacket, &tsPacketHeader); /* pat->pmt->(audio/video pid)->video data */
if ( == patIsFind)
{
if (TS_PAT_PID == tsPacketHeader.pid)
{
/* 4(header) + 1(adapt len)*/
ParseTsPat(&tsPacket[+], &g_TsPat); patIsFind = ;
}
} if (( == patIsFind) && ( != allPmtIsFind))
{
for (i=; 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++;
}
} for (i=; i<g_TsPat.programNum; i++)
{
if ( == g_TsPmt[i].pmtIsFind)
{
break;
}
} if (i == g_TsPat.programNum)
{
allPmtIsFind = ;
}
} if (allPmtIsFind)
{
for (i=; i<g_TsPat.programNum; i++)
{
for (k=; 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. 从0开始的Hexo主题制作

    从0开始的Hexo主题制作 从零开始制作 Hexo 主题 H2O主题 先坑着

  2. 容器中的诊断与分析3——live diagnosis——lldb

    windows下,我们对于.net程序发生Crash,资源泄露,死锁等问题的分析,有神器windbg .net core程序运行在linux上时,该怎么进行对对Core Dump文件进行分析呢?今天介 ...

  3. Best Practices for Assembly Loading

    原文链接 This article discusses ways to avoid problems of type identity that can lead to InvalidCastExce ...

  4. python2 与 python3的区别

    python2 与 python3的区别 几乎所有的python2程序都需要一些修改才能正常的运行在python3的环境下.为了简化这个转换过程,Python3自带了一个2to3的实用脚本.这个脚本会 ...

  5. ArcGIS Pro开发Web3D应用(4)——Portal发布三维没有“发布”选项解决

    如题,将三维场景包.slpk上传并发布,未看到发布选项. 问题核心就在于环境没配置好,主要有2点: 1.验证联合托管状态 需要将托管服务器与Portal进行联合,并进行验证,此情况需要在WebAdap ...

  6. Autofac IOC框架

    ASP.NET Core自带了一个轻量级的IOC容器     但是只提供了最基本的AddXXX方法来绑定实例关系     需要一个一个添加   可以使用其他IOC容器来替换内置的 ABP自带Castl ...

  7. vue 组件复用不刷新

    情景: 两个路由"/a", "/b"公用一个页面组件, 在"/a"路由中, 第一列是序号, 在"/b"路由中, 第一列是 ...

  8. Android虹软人脸识别sdk使用工具类

    public class FaceUtil { private static final String TAG = FaceUtil.class.getSimpleName(); private st ...

  9. vue动态绑定src加字符串拼接

    关键点 1.img中的src的字符串动态拼接 2.style中的background属性赋值 一.img中的src的字符串动态拼接 1.问题是这样子的,瞧瞧 基本网络链接 : http://openw ...

  10. Hadoop OutputCommitter

    1. OutputCommitters MapReduce使用一个提交协议来确保作业(job)和任务(task)都完全成功或失败.这个通过 OutputCommiter来实现. 新版本 MapRedu ...