TS數據結構分析
1.TS包得數據結構
2.
// Transport packet header
typedef struct TS_packet_header
{
unsigned sync_byte : 8; //同步字節,固定為0x47 ,表示后面的是一個TS分組,當然,后面包中的數據是不會出現0x47的
unsigned transport_error_indicator : 1; //傳輸錯誤標志位,一般傳輸錯誤的話就不會處理這個包了
unsigned payload_unit_start_indicator : 1; //有效負載的開始標志,根據后面有效負載的內容不同功能也不同
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_packet_header;
TS包頭定義:
typedef struct TS_packet_header
{
unsigned sync_byte : 8; //同步字節, 固定為0x47,表示后面的是一個TS分組
unsigned transport_error_indicator : 1; //傳輸誤碼指示符
unsigned payload_unit_start_indicator : 1; //有效荷載單元起始指示符
unsigned transport_priority : 1; //傳輸優先, 1表示高優先級,傳輸機制可能用到,解碼用不著
unsigned PID : 13; //PID
unsigned transport_scrambling_control : 2; //傳輸加擾控制
unsigned adaption_field_control : 2; //自適應控制 01僅含有效負載,10僅含調整字段,11含有調整字段和有效負載。為00解碼器不進行處理
unsigned continuity_counter : 4; //連續計數器 一個4bit的計數器,范圍0-15
} TS_packet_header;
TS包頭解析代碼:
HRESULT CTS_Stream_Parse::adjust_TS_packet_header( TS_packet_header* TS_header )
{
unsigned char buf[4];
memcpy(buf, TS_header, 4);
TS_header->transport_error_indicator = buf[1] >> 7;
TS_header->payload_unit_start_indicator = buf[1] >> 6 & 0x01;
TS_header->transport_priority = buf[1] >> 5 & 0x01;
TS_header->PID = (buf[1] & 0x1F) << 8 | buf[2];
TS_header->transport_scrambling_control = buf[3] >> 6;
TS_header->adaption_field_control = buf[3] >> 4 & 0x03;
TS_header->continuity_counter = buf[3] & 0x0F; // 四位數據,應為0x0F xyy 09.03.18
return 0;
}
3.PSI
全稱Program Specific Information,意為節目專用信息。傳輸流中是多路節目復用的,那么,怎么知道這些節目在傳輸流中的位置,區分屬于不同節目呢?所以就還需要一些附加信息,這就是PSI。PSI也是插入到TS分組中的,它們的PID是特定值。MPEG-2中規定了4個PSI,包括PAT(節目關聯表),CAT(條件訪問表),PMT(節目映射表),NIT(網絡信息表),這些PSI包含了進行多路解調和顯示節目的必要的和足夠的信息.PSI的PID是特定的,含PSI的數據包必須周期性的出現在傳輸流中。
PMT (Program Map Table )節目映射表PMT所在分組的PID由PAT指定,所以要先解出PAT,再解PMT。PMT中包含了屬于同一節目的視頻、音頻和數據原始流的PID。找到了PMT,解多路復用器就可找到一道節目對應的每個原始流的PID,再根據原始流PID,去獲取原始流。
PAT (Program Association Table )節目關聯表PAT所在分組的PID=0 PAT中列出了傳輸流中存在的節目流PAT指定了傳輸流中每個節目對應PMT所在分組的PIDPAT的第一條數據指定了NIT所在分組的PID ,其他數據指定了PMT所在分組的PID,一個節目包含多少個節目就含有多少PMT。
CAT (Conditional Access Table )條件訪問表CAT所在分組的PID=1CAT中列出了條件控制信息(ECM)和條件管理信息(EMM)所在分組的PID。CAT用于節目的加密和解密 NIT( Network Information Table)網絡信息表
NIT所在分組的PID由PAT指定NIT提供一組傳輸流的相關信息,以及于網絡自身特性相關的信息,比如網絡名稱,傳輸參數(如頻率,調制方式等)。NIT一般是解碼器內部使用的數據,當然也可以做為EPG的一個顯示數據提供給用戶做為參考。
幾種PSI之間的關系,如下圖所示:首先PAT中指定了傳輸流中所存在的節目,及每個節目對應的PMT的PID號。 比如Program 1對應的PMT 的PID=22,然后找到PID=22的TS分組,解出PMT,得到這個節目中包含的原始流的PID,再根據原始流的PID去找相應的TS分組,獲取原始流的數據,然后就可以送入解碼器解碼了。
4.PAT表數據結構
數據結構PAT Table
//PAT Table
program_association_section() {
table_id : 8; //固定為0x00 ,標志是該表是PAT
section_syntax_indicator : 1; //段語法標志位,固定為1
'0' : 1; //0
reserved : 2; //保留
section_length : 12; //表示這個字節后面有用的字節數,包括CRC32。假如后面的字節加上前面的字節數少于188,后面會用0XFF填充。假如這個數值比較大,則PAT會分成幾部分來傳輸
。
transport_stream_id : 16 //該傳輸流的ID,區別于一個網絡中其它多路復用的流。
reserved : 2 //保留
version_number : 5 //范圍0-31表示PAT的版本號,標注當前節目的版本這是個非常有用的參數,當檢測到這個字段改變時,說明TS流中的節目已經變化了,程序必須重新搜索節目.
current_next_indicator : 1 //表示發送的PAT是當前有效還是下一個PAT有效。
section_number : 8 //分段的號碼。PAT可能分為多段傳輸,第一段為00,以后每個分段加1,最多可能有256個分段
last_section_number : 8 //最后一個分段的號碼
for (i=0; i<N;i++) {
program_number : 16 //節目號
reserved : 3 //保留
if(program_number == '0') {
network_PID : 13 //網絡信息表(NIT)的PID,網絡信息表提供了該物理網絡的一些信息,和電視臺相關的。節目號為0時對應的PID為network_PID
}
else {
program_map_PID : 13 //節目映射表的PID,節目號大于0時對應的PID,每個節目對應一個
}
}
CRC_32 : 32
}
//PAT表結構體
typedef struct TS_PAT
{
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; //最后一個分段的號碼
std::vector<TS_PAT_Program> program;
unsigned reserved_3 : 3; // 保留位
unsigned network_PID : 13; //網絡信息表(NIT)的PID,節目號為0時對應的PID為network_PID
unsigned CRC_32 : 32; //CRC32校驗碼
} TS_PAT;
HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)
{
packet->table_id = buffer[0];
packet->section_syntax_indicator = buffer[1] >> 7;
packet->zero = buffer[1] >> 6 & 0x1;
packet->reserved_1 = buffer[1] >> 4 & 0x3;
packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
packet->transport_stream_id = buffer[3] << 8 | buffer[4];
packet->reserved_2 = buffer[5] >> 6;
packet->version_number = buffer[5] >> 1 & 0x1F;
packet->current_next_indicator = (buffer[5] << 7) >> 7;
packet->section_number = buffer[6];
packet->last_section_number = buffer[7];
int len = 0;
len = 3 + packet->section_length;
packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);
int n = 0;
for ( n = 0; n < packet->section_length - 12; n += 4 )
{
unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ];
packet->reserved_3 = buffer[10 + n ] >> 5;
packet->network_PID = 0x00;
if ( program_num == 0x00)
{
packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
TS_network_Pid = packet->network_PID; //記錄該TS流的網絡PID
TRACE(" packet->network_PID %0x /n/n", packet->network_PID );
}
else
{
TS_PAT_Program PAT_program;
PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];
PAT_program.program_number = program_num;
packet->program.push_back( PAT_program );
TS_program.push_back( PAT_program );//向全局PAT節目數組中添加PAT節目信息
}
}
return 0;
}
//PMT Table
TS_program_map_section(){
table_id : 8; //固定為0x02 ,標志是該表是PMT。
section_syntax_indicator : 1; //段語法標志位,固定為1
'0' : 1; //0
reserved : 2; //保留
section_length : 12; //表示這個字節后面有用的字節數,
program_number : 16; //節目號,表示該PMT對應的節目
reserved : 2; //保留
version_number : 5;
current_next_indicator :1;
section_number : 8;
last_section_number : 8;
reserved : 3;
PCR_PID : 13 //PCR(節目時鐘參考)所在TS分組的PID,根據PID可以去搜索相應的TS分組,解出PCR信息。
reserved : 4;
program_info_length: 12; //該節目的信息長度
for (i=0; i<N; i++) {
descriptor()
}
for (i=0;i<N1;i++) {
stream_type : 8; //指示了PID為elementary_PID的PES分組中原始流的類型,比如視頻流,音頻流等,見后面的表
reserved : 3;
elementary_PID : 13; //該節目中包括的視頻流,音頻流等對應的TS分組的PID
reserved : 4;
ES_info_length : 12; //該節目相關原始流的描述符的信息長度
for (i=0; i<N2; i++) {
descriptor()
}
}
CRC_32 : 32
}
PMT結構定義:
typedef struct TS_PMT_Stream
{
unsigned stream_type : 8; //指示特定PID的節目元素包的類型。該處PID由elementary PID指定
unsigned elementary_PID : 13; //該域指示TS包的PID值。這些TS包含有相關的節目元素
unsigned ES_info_length : 12; //前兩位bit為00。該域指示跟隨其后的描述相關節目元素的byte數
unsigned descriptor;
}TS_PMT_Stream;
//PMT 表結構體
typedef struct TS_PMT
{
unsigned table_id : 8; //固定為0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定為0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12;//首先兩位bit置為00,它指示段的byte數,由段長度域開始,包含CRC。
unsigned program_number : 16;// 指出該節目對應于可應用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本號
unsigned current_next_indicator : 1; //當該位置1時,當前傳送的Program map section可用;
//當該位置0時,指示當前傳送的Program map section不可用,下一個TS流的Program map section有效。
unsigned section_number : 8; //固定為0x00
unsigned last_section_number : 8; //固定為0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,該TS包含有PCR域,
//該PCR值對應于由節目號指定的對應節目。
//如果對于私有數據流的節目定義與PCR無關,這個域的值將為0x1FFF。
unsigned reserved_4 : 4; //預留為0x0F
unsigned program_info_length : 12; //前兩位bit為00。該域指出跟隨其后對節目信息的描述的byte數。
std::vector<TS_PMT_Stream> PMT_Stream; //每個元素包含8位, 指示特定PID的節目元素包的類型。該處PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
} TS_PMT;
解析代碼為:
HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )
{
packet->table_id = buffer[0];
packet->section_syntax_indicator = buffer[1] >> 7;
packet->zero = buffer[1] >> 6 & 0x01;
packet->reserved_1 = buffer[1] >> 4 & 0x03;
packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
packet->program_number = buffer[3] << 8 | buffer[4];
packet->reserved_2 = buffer[5] >> 6;
packet->version_number = buffer[5] >> 1 & 0x1F;
packet->current_next_indicator = (buffer[5] << 7) >> 7;
packet->section_number = buffer[6];
packet->last_section_number = buffer[7];
packet->reserved_3 = buffer[8] >> 5;
packet->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;
PCRID = packet->PCR_PID;
packet->reserved_4 = buffer[10] >> 4;
packet->program_info_length = (buffer[10] & 0x0F) << 8 | buffer[11];
// Get CRC_32
int len = 0;
len = packet->section_length + 3;
packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);
int pos = 12;
// program info descriptor
if ( packet->program_info_length != 0 )
pos += packet->program_info_length;
// Get stream type and PID
for ( ; pos <= (packet->section_length + 2 ) - 4; )
{
TS_PMT_Stream pmt_stream;
pmt_stream.stream_type = buffer[pos];
packet->reserved_5 = buffer[pos+1] >> 5;
pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;
packet->reserved_6 = buffer[pos+3] >> 4;
pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];
pmt_stream.descriptor = 0x00;
if (pmt_stream.ES_info_length != 0)
{
pmt_stream.descriptor = buffer[pos + 5];
for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )
{
pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];
}
pos += pmt_stream.ES_info_length;
}
pos += 5;
packet->PMT_Stream.push_back( pmt_stream );
TS_Stream_type.push_back( pmt_stream );
}
return 0;
}
//CheckOut 數據宏
從byteVar的某位,獲取獲取指定的位值
#define GET1BIT(byteVar, fromBit) ((byteVar >> (8 - (fromBit))) & 0x01)
#define GET2BIT(byteVar, fromBit) ((byteVar >> (7 - (fromBit))) & 0x03)
#define GET3BIT(byteVar, fromBit) ((byteVar >> (6 - (fromBit))) & 0x07)
#define GET4BIT(byteVar, fromBit) ((byteVar >> (5 - (fromBit))) & 0x0F)
#define GET5BIT(byteVar, fromBit) ((byteVar>> (4 - (fromBit))) & 0x1F)
#define GET6BIT(byteVar, fromBit) ((byteVar >> (3 - (fromBit))) & 0x3F)
#define GET7BIT(byteVar, fromBit) ((byteVar >> (2 - (fromBit))) & 0x7F)
#define GET8BIT(byteVar, fromBit) ((byteVar >> (1 - (fromBit))) & 0xFF)