ts文件分析(纯c解析代码)
参考链接: 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解析代码)的更多相关文章
- h264文件分析(纯c解析代码)
参考链接:1. 解析H264的SPS信息 https://blog.csdn.net/lizhijian21/article/details/80982403 2. h.2 ...
- mpeg4文件分析(纯c解析代码)
参考链接: 1. MPEG4码流的帧率计算 https://blog.csdn.net/littlebee90/article/details/68924690 2. M ...
- h265文件分析(纯c解析代码)
参考链接: 1. HEVC码流解析 https://blog.csdn.net/CrystalShaw/article/details/80624804 2. HEVC编码结构:序列参数集SPS. ...
- mpeg2文件分析(纯c解析代码)
参考链接: 1. MPEG-2码流结构分析 https://www.cnblogs.com/CoderTian/p/9246225.html(本文语法采用这里的截图,代码原创) 1. mpeg2的码流 ...
- ps文件解析(纯c解析代码)
参考链接:1. PS流的格式和解析总结 http://www.cnblogs.com/lihaiping/p/4181607.html 2. TS科普5 PES包解析 https://blog.cs ...
- flv文件解析(纯c解析代码)
参考链接: 1. FLV科普12 FLV脚本数据解析-Metadata Tag解析 https://blog.csdn.net/cabbage2008/article/details/50500021 ...
- mp4文件解析(纯c解析代码)
参考链接:1. mp4文件格式解析 https://www.cnblogs.com/ranson7zop/p/7889272.html 2. MP4文件格式分析及分割实现(附源码) https: ...
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- linux内核中链表代码分析---list.h头文件分析(二)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...
随机推荐
- BZOJ 5261 Rhyme
思路 考虑一个匹配的过程,当一个节点x向后拼接一个c的时候,为了满足题目条件的限制,应该向suflink中最深的len[x]+1>=k的节点转移(保证该后缀拼上一个c之后,长度为k的子串依然属于 ...
- LeetCode174-Dungeon Game-数组,动态规划
题目描述 The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dun ...
- jquery AJAX数据传输路径写法~
$.post('{:url("index/index/logininfo")}',{'username':name,'password':pwd},function(data){ ...
- SVN导出差异版本更新的文件列表
对于在服务器上没有使用版本控制的运维人员来说,每次SVN修改的文件都需要查看更改日志,一个个查找出来再更新到服务器,过程实在是痛苦 那么有没有一种方法跑个命令比对一下版本就哗啦啦的把修改好的文件复制出 ...
- ECharts导出word 图表模糊失真
在项目中会有这样的需求,echars生成图表导入到word中 在项目中用的插件 博主有一篇文章将的是 vue使用jquery的三方插件jquery.wordexport.js https://b ...
- vue 打开新窗口
const {href} = this.$router.resolve({ name: 'foo', query: { bar } }) window.open(href, '_blank')
- Codeforces 803C. Maximal GCD
题目链接:http://codeforces.com/contest/803/problem/C 中了若干trick之后才过... k个数的严格递增序列最小权值和就是${n*(n+1)/2}$,枚举这 ...
- OpenGL.Tutorial03_Matrices_测试
1. 2. // ZC: 工程-->右键-->属性--> 配置属性: // ZC: C/C++ -->常规-->附加包含目录,里面添加: // ZC: E:\OpenGL ...
- 11-类中的__call__函数
__call__是一个很神奇的特性,只要某个类型中有__call__方法,,我们可以把这个类型的对象当作函数来使用. 举例: >>>class Reader(): def __ini ...
- particular.js
参数 键值 参数选项/ 说明 实例 particles.number.value number 数量 40 particles.number.density.enable boolean t ...