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是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結構 前三字節是包頭起始標識字段,內容為0x000001 第四個字節是流ID字段,不同的流ID有不用的意義,如圖,音頻流ID范圍從0xC0到0xDF,視頻流ID范圍從0xE0到0xEF。 第五六個字節是PES包長度,表示PES包頭部在該字段之后的長度,單位是字節 接下來的第七八九字節是PES的擴展頭部字段,用於設置流的基本信息,結構如圖 第六字節的高兩位是標識位,值為10b 第七字節的高兩位是PTS和DTS指示位,00表示無PTS無DTS,01禁止使用,10表示PES頭部字段會附加PTS結構 11表示PES頭部字段會附加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也會相應的在頭部附加對應字段。
|