ts文件格式解析

  • Post author:
  • Post category:其他




TS格式解析


by ahuner

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                     : 13;


// 或者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结构详解

2013年08月05日




编程


⁄ 共 1100字



评论数 2


⁄ 被围观 3,562+

PES是Packetized Elementary Stream的简称,是将原始ES流打包后形成的,再将PES经过不同的打包方式可以组成MPEG program stream 和 MPEG transport stream,即PS流和TS流。

PES的组成结构如图,包括6个字节的包头字段,加上3个字节基本流信息字段,根据信息字段的设置可在之后附加其他字段。

<a href=”http://www.yunlipiao.com/wp-content/uploads/2013/08/pes.jpg” class=”cboxElement” rel=”example4″ 208″=”” style=”text-decoration: none; color: rgb(1, 150, 227);”>
PES结构

PES结构

前三字节是包头起始标识字段,内容为0x000001

第四个字节是流ID字段,不同的流ID有不用的意义,如图,音频流ID范围从0xC0到0xDF,视频流ID范围从0xE0到0xEF。


PES流ID字段

PES流ID字段


第五六个字节是PES包长度,表示PES包头部在该字段之后的长度,单位是字节

接下来的第七八九字节是PES的扩展头部字段,用于设置流的基本信息,结构如图


PES可选扩展

PES可选扩展

第六字节的高两位是标识位,值为10b

第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构


pts结构

pts结构

11表示PES头部字段会附加PTS和DTS结构


pts和dts结构

pts和dts结构

其中PTS和DTS使用的是90KHZ时钟单位,即1PTS表示1/90000秒,PTS和DTS虽然是33位,但占用了5个字节

ESCR FLAG字段设为1,会在头部附加6个字节的ESCR结构,ES RATE FLAG字段设置为1,会在头部附加3个字节ES rate结构,其他标识位如果设置为1也会相应的在头部附加对应字段。


ES rate结构

ES rate结构


ESCR结构

ESCR结构


本文地址:

http://www.yunlipiao.com/208.html

,出自

云里飘博客

,转载请保留链接