1.TS格式介绍
TS:全称为MPEG2-TS。TS即"Transport Stream"的缩写。它是分包发送的,每一个包长为188字节(还有192和204个字节的包)。包的结构为,包头为4个字节(第一个字节为0x47),负载为184个字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。MPEG2-TS主要应用于实时传送的节目,比如实时广播的电视节目。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。简单地说,将DVD上的VOB文件的前面一截cut掉(或者是数据损坏数据)就会导致整个文件无法解码,而电视节目是任何时候打开电视机都能解码(收看)的。
TS解析需要参考:ISO/IEC 13818-1的2.4 Transport Stream bitstream requirements
2.TS流包含的内容
一段TS流,必须包含PAT包、PMT包、多个音频包、多个视频包、多个PCR包、以及其他信息包。
解析TS流数据的流程:查找PID为0x0的包,解析PAT,PAT包中的program_map_PID表示PMT的PID;查找PMT,PMT包中的elementary_PID表示音视频包的PID,PMT包中的PCR_PID表示PCR的PID,有的时候PCR的PID跟音频或者视频的PID相同,说明PCR会融进音视频的包,注意解析,有的时候PCR是自己单独的包;CAT、NIT、SDT、EIT的PID分别为: 0x01、0x10、0x11、0x12。
3.TS包头解析
TS包头有4个字节
//Transport Stream header
typedef
struct
TS_header
{
unsigned sync_byte :8;
//同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的
unsigned transport_error_indicator :1;
//传输错误标志位,一般传输错误的话就不会处理这个包了
unsigned payload_unit_start_indicator :1;
//有效负载的开始标志,根据后面有效负载的内容不同功能也不同
// payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。
unsigned transport_priority :1;
//传输优先级位,1表示高优先级
unsigned PID :13;
//有效负载数据的类型
unsigned transport_scrambling_control :2;
//加密标志位,00表示未加密
unsigned adaption_field_control :2;
//调整字段控制,。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。
unsigned continuity_counter :4;
//一个4bit的计数器,范围0-15
} TS_header;
//特殊参数说明:
//sync_byte:0x47
//payload_unit_start_indicator:0x01表示含有PSI或者PES头
//PID:0x0表示后面负载内容为PAT,不同的PID表示不同的负载
//adaption_field_control:
// 0x0: // reserved for future use by ISO/IEC
// 0x1: // 无调整字段,仅含有效负载
// 0x2: // 仅含调整字段,无有效负载
// 0x3: // 调整字段后含有效负载
// Parse TS header
int
Parse_TS_header(unsigned
char
*pTSBuf, TS_header *pheader)
{
pheader->sync_byte = pTSBuf[0];
if
(pheader->sync_byte != 0x47)
return
-1;
pheader->transport_error_indicator = pTSBuf[1] >> 7;
pheader->payload_unit_start_indicator = pTSBuf[1] >> 6 & 0x01;
pheader->transport_priority = pTSBuf[1] >> 5 & 0x01;
pheader->PID = (pTSBuf[1] & 0x1F) << 8 | pTSBuf[2];
pheader->transport_scrambling_control = pTSBuf[3] >> 6;
pheader->adaption_field_control = pTSBuf[3] >> 4 & 0x03;
pheader->continuity_counter = pTSBuf[3] & 0x0F;
return
0;
}
|
TS包头解析需要参考:ISO/IEC 13818-1的2.4.3.2 Transport Stream packet layer
4.TS负载格式解析
4.1 PAT解析
TS_header包头中的PID值为0x0,表示当前负载为PAT(Program Association Table)。PAT数据的信息可以理解为整个TS流包含的节目信息。
// Program Association Table
typedef
struct
PAT_Packet_tag
{
unsigned table_id : 8;
//固定为0x00 ,标志是该表是PAT
unsigned section_syntax_indicator : 1;
//段语法标志位,固定为1
unsigned zero : 1;
//0
unsigned reserved_1 : 2;
// 保留位
unsigned section_length : 12;
//表示这个字节后面有用的字节数,包括CRC32
unsigned transport_stream_id : 16;
//该传输流的ID,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2;
// 保留位
unsigned version_number : 5;
//范围0-31,表示PAT的版本号
unsigned current_next_indicator : 1;
//发送的PAT是当前有效还是下一个PAT有效
unsigned section_number : 8;
//分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
unsigned last_section_number : 8;
//最后一个分段的号码
// for(i=0; i<N; i++)
// {
unsigned program_number : 16;
unsigned reserved_3 : 3;
unsigned network_PID : 16;
// 或者program_map_PID
// }
unsigned CRC_32 : 32;
} PAT_Packet;
// Parse PAT
int
Parse_PAT(unsigned
char
*pTSBuf, PAT_Packet *packet)
{
TS_header TSheader;
if
(Parse_TS_packet_header(pTSBuf, &TSheader) != 0)
return
-1;
if
(TSheader.payload_unit_start_indicator == 0x01)
// 表示含有PSI或者PES头
{
if
(TSheader.PID == 0x0)
// 表示PAT
{
int
iBeginlen = 4;
int
adaptation_field_length = pTSBuf[4];
switch
(TSheader.adaption_field_control)
{
case
0x0:
// reserved for future use by ISO/IEC
return
-1;
case
0x1:
// 无调整字段,仅含有效负载
iBeginlen += pTSBuf[iBeginlen] + 1;
// + pointer_field
break
;
case
0x2:
// 仅含调整字段,无有效负载
return
-1;
case
0x3:
// 调整字段后含有效负载
if
(adaptation_field_length > 0)
{
iBeginlen += 1;
// adaptation_field_length占8位
iBeginlen += adaptation_field_length;
// + adaptation_field_length
}
else
{
iBeginlen += 1;
// adaptation_field_length占8位
}
iBeginlen += pTSBuf[iBeginlen] + 1;
// + pointer_field
break
;
default
:
break
;
}
unsigned
char
*pPAT = pTSBuf + iBeginlen;
packet->table_id = pPAT[0];
packet->section_syntax_indicator = pPAT[1] >> 7;
packet->zero = pPAT[1] >> 6 & 0x1;
packet->reserved_1 = pPAT[1] >> 4 & 0x3;
packet->section_length = (pPAT[1] & 0x0F) << 8 |pPAT[2];
packet->transport_stream_id = pPAT[3] << 8 | pPAT[4];
packet->reserved_2 = pPAT[5] >> 6;
packet->version_number = pPAT[5] >> 1 & 0x1F;
packet->current_next_indicator = (pPAT[5] << 7) >> 7;
packet->section_number = pPAT[6];
packet->last_section_number = pPAT[7];
int
len = 0;
len = 3 + packet->section_length;
packet->CRC_32 = (pPAT[len-4] & 0x000000FF) << 24
| (pPAT[len-3] & 0x000000FF) << 16
| (pPAT[len-2] & 0x000000FF) << 8
| (pPAT[len-1] & 0x000000FF);
int
n = 0;
for
( n = 0; n < (packet->section_length - 12); n += 4 )
{
packet->program_number = pPAT[8 + n ] << 8 | pPAT[9 + n ];
packet->reserved_3 = pPAT[10 + n ] >> 5;
if
( packet->program_number == 0x00)
{
packet->network_PID = (pPAT[10 + n ] & 0x1F) << 8 |pPAT[11 + n ];
}
else
{
// 有效的PMT的PID,然后通过这个PID值去查找PMT包
program_map_PID = (pPAT[10 + n] & 0x1F) << 8 |pPAT[11 + n];
}
}
return
0;
}
}
return
-1;
}
|
PAT数据解析需要参考:ISO/IEC 13818-1的2.4.4.3 Program Association Table
4.2 PMT解析
由PAT包中的program_map_PID可以确定PMT(Program Map Table)的PID。PMT数据的信息可以理解为这个节目包含的音频和视频信息。
// Program Map Table
typedef
struct
PMT_Packet_tag
{
unsigned table_id : 8;
unsigned section_syntax_indicator : 1;
unsigned zero : 1;
unsigned reserved_1 : 2;
unsigned section_length : 12;
unsigned program_number : 16;
unsigned reserved_2 : 2;
unsigned version_number : 5;
unsigned current_next_indicator : 1;
unsigned section_number : 8;
unsigned last_section_number : 8;
unsigned reserved_3 : 3;
unsigned PCR_PID : 13;
unsigned reserved_4 : 4;
unsigned program_info_length : 12;
// for(i=0; i<N; i++)
// {
unsigned stream_type : 8;
unsigned reserved_5 : 3;
unsigned elementary_PID : 13;
unsigned reserved_6 : 4;
unsigned ES_info_length : 12;
// }
unsigned CRC_32 : 32;
} PMT_Packet;
// Parse PMT
int
Parse_PMT(unsigned
char
*pTSBuf, PMT_Packet *packet)
{
// 参考Parse_PAT()来做就行了
// ...
return
0;
}
|
PMT数据解析需要参考:ISO/IEC 13818-1的2.4.4.8 Program Map Table
4.3 PES解析
根据文档参考PAT、PMT的解析流程就能完成PES的解析了。
需要注意的是PES中PTS的解析,一般来说在90 kHz 中,PTS/9000的值为秒单位。
unsigned
long
long
Parse_PTS(unsigned *pBuf)
{
unsigned
long
long
llpts = (((unsigned
long
long
)(pBuf[0] & 0x0E)) << 29)
| (unsigned
long
long
)(pBuf[1] << 22)
| (((unsigned
long
long
)(pBuf[2] & 0xFE)) << 14)
| (unsigned
long
long
)(pBuf[3] << 7)
| (unsigned
long
long
)(pBuf[4] >> 1);
return
llpts;
}
PES结构详解
|
传输流(TS)
将具有共同时间基准或具有独立时间基准的一个或多个PES组合而成的单一的数据流称为传输流(Transport Stream)。TS实际是面向数字化分配媒介(有线、卫星、地面网)的传输层接口。对具有共同时间基准的两个以上的PES先进行节目复用,然后再对相互可有独立时间基准的各个PS进行传输复用,即将每个PES再细分为更小的TS包
TS包由包头、自适应区和包数据3部分组成。每个包长度为固定的188B,包头长度占4 B,自适应区和包数据长度占184B。184B为有用信息空间,用于传送已编码的视音频数据流。当节目时钟基准(PCR-Program Clock Reference)存在时,包头还包括可变长度的自适应区,包头的长度就会大于4B。考虑到与通信的关系,整个传输包固定长度应相当于4个ATM包。考虑到加密是按照8B顺序加扰的,代表有用信息的自适应区和包数据的长度应该是8B的整数倍,即自适应区和包数据为23×8B =184B。
TS包的包头由如图所示的同步字节、传输误码指示符、有效载荷单元起始指示符、传输优先、包识别(PID-Packet Identification)、传输加扰控制、自适应区控制和连续计数器8个部分组成。其中,可用同步字节位串的自动相关特性,检测数据流中的包限制,建立包同步;传输误码指示符,是指有不能消除误码时,采用误码校正解码器可表示1bit 的误码,但无法校正;有效载荷单元起始指示符,表示该数据包是否存在确定的起始信息;传输优先,是给TS包分配优先权;PID值是由用户确定的,解码器根据PID将TS上从不同ES来的TS包区别出来,以重建原来的ES;传输加扰控制,可指示数据包内容是否加扰,但包头和自适应区永远不加扰;自适应区控制,用2 bit表示有否自适应区,即(01)表示有有用信息无自适应区,(10)表示无有用信息有自适应区,(11)表示有有用信息有自适应区,(00)无定义;连续计数器可对PID包传送顺序计数,据计数器读数,接收端可判断是否有包丢失及包传送顺序错误。显然,包头对TS包具有同步、识别、检错及加密功能。
TS包自适应区由自适应区长、各种标志指示符、与插入标志有关的信息和填充数据4部分组成。其中标志部分由间断指示符、随机存取指示符、ES优化指示符、PCR标志、接点标志、传输专用数据标志、原始PCR标志、自适应区扩展标志8个部分组成。
TS包语法结构如下:
Syntax | No. of bits | Mnemonic |
transport_packet(){ | ||
sync_byte | 8 | bslbf |
transport_error_indicator | 1 | bslbf |
payload_unit_start_indicator | 1 | bslbf |
transport_priority | 1 | bslbf |
PID | 13 | uimsbf |
transport_scrambling_control | 2 | bslbf |
adaptation_field_control | 2 | bslbf |
continuity_counter | 4 | uimsbf |
if(adaptation_field_control=='10' || adaptation_field_control=='11'){ | ||
adaptation_field() | ||
} | ||
if(adaptation_field_control=='01' || adaptation_field_control=='11') { | ||
for (i=0;i<N;i++){ | ||
data_byte | 8 | bslbf |
} | ||
} | ||
} |
自适应控制区语法结构如下: