参考链接: 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);
}
最后如果您觉得本篇对您有帮助,可以打赏下,谢谢!!!