TS解析

传输流Transport Stream(通常称为一路码流),是最基本的传输实现,数据最终以码流的方式输出。码流部分其实就是DVB协议的最底层,类似于TCP/IP协议的数据链路层,这一层关心的是数据打包,数据帧结构和传输,而不关心负载是什么。

码流中最基本的单位是188字节的包(Packet,又称为分组),前4BYTE是包头,后184为负载



码流的速率称为码率,单位是bit/s,因此可以计算出一个100M的码流文件在码流发生器上以38M码率发送时,持续时间是:
100M(BYTE) × 8 / 38M = 21.05秒
码流中包的传输
从下往上看看这个图:
视频基本流(即压缩好了的视频信号)先是被封装,成为视频打包基本流(VPES);
因为TS流的基本传输单元是TS包,因此VPES再次被打包成TS包(图中的TP分组);
然后它和其它的众多TS包一起,混合(复用)到TS流中送出



在TS包的结构中,前面4个BYTE定义为包头。
其中有一个叫PID(Packet_ID)的字段。
协议规定,对于要进行传输的一个组件,或者一个Section,当被封装为TS包时,其PID相同。
例如:某电影频道的视频数据,装载它的各个包,其PID都为0152;其音频数据则都在PID为0196的包中传输。


有少数一些PID被规定用于传输某些特定数据;而另一些(用户定义)则可灵活使用


Ts包结构


语法描述:
syn_byte: 这是一个1B长的字段,其值为0x47,该字段是MPEG-2TS的传送包标识符。
transport_error_indicator: 这是一个1b长度的字段。值为1时,表示在相关的传送包中至少有一个不可纠正的错误,只有在错误纠正之后,该胃才能被重新置0。
payload_unit_start_indicator: 这时1b长度的字段。该字段用来表示TS包的有效净荷有PES包或者PSI数据的情况。
当TS包带有PES包数据时,payload_unit_start_indicator具有以下的特点:置为1,标识TS包的有效净荷以PES包的第一个字节开始;置为0,表示TS包的开始不是PES包。
当 TS包带有PSI数据时,payload_unit_start_indicator具有以下特点:置为1,表示TS包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field;置为0,表示TS包不带有一个PSI部分的第一个字节,即在有效净荷中没有指针point_field。
对于空包的包,payload_unit_start_indicator应该置为0。
transport_priority: 这是一个1b长度的字段。当该字段置为1,表示相关的包比其他具有相同PID但此字段为“0” 的包有更高的优先权。可以根据此字段确定在一个原始流中数据的传送优先级。
PID: 这个13b长度的字段,表示存储于传送包的有效净荷中数据的类型。PID=ox0000表示净荷的数据为节目关联表;PID=0x0001表示净荷的数据为条件访问表;PID=0x0003~0x000F为保留;PID=0x1FFF表示净荷的数据空包;其他PID值表示净荷的数据为节目映射表,网络信息,以及由用户定义打包的音频/视频数据PES包等。
transport_scrambling_control: 这是一个2b长度字段。该字段用来指示传送流包有效净荷的加扰方式。如果传送流包首部包含调整字段,则不应该被加扰。对于空包,transport_scrambling_control的值为“00”。
adaptation_field_control: 这是一个2b长度字段,表示传送流包首部是否跟随有调整字段和/或有效净荷。
continuity_counter: 这是一个4b长度的字段。随着具有相同的PID TS包的增加而增加,当它达到最大时,有恢复为“0”。如果调整字段控制值adaptation_field_control为“00”或“10“,该连续计数器不增加。
data_byte: 该字段为1B长度的数据,在此之前,应根据该TS包头的结构定义及其相关含义与TS包的188B大小计算循环字节长度N,数据字节可以是PES包、PSI部分以及私有数据的连续字节。


在MPEG-2协议中,采用了一种索引的思路来进行节目的寻找
1、  找到PAT表
2、  PAT表之所以叫做节目关联表,就是因为它指出了   当前这个TS流中包含的各个节目其各自所对应的   PMT表的PID
3、  既然我们能知道某个节目它的PMT表所在的TS包   的PID了,那我们就能通过检索PID的方式,把这个      PMT表找出来
4、  PMT表叫做节目映像表,它指出了它所描述的节目 其所对应的视频流、音频流、PCR(时间参考信息)      的PID
5、  既然有了流所在TS包的PID,      那就在当前TS流中过滤出PID等于这个PID值的包,    这些过滤出来的包依序排列,就可以从中还原出流了。
6、  有了视频基本流、音频基本流、参考时      钟,机顶盒就能够对节目进行解码,输出显示在电视     画面上了。
 
下面以一个实际例子说明:


PAT(Program Association Table)的PID值为0x00,TS包的标识(即sync_byte)为0x47,并且为了确保这个TS包里的数据有效,所以我们一开始查找47 40 00这三组16进制数为什么这样?具体的奥秘在TS包的结构上,前面已经说了sync_byte固定为0x47。现在往下看transport_error_indicator、payload_unit_start_indicator、transport_priority和PID这四个元素,PID为0x00,这是PAT的标识。transport_error_indicator为0,transport_priority为0。把他们看成是两组8位16进制数就是:40 00。现在看看我们的TS流片断例子,看来正好是47 40 00开头的,一个TS流的头部占据了4个字节。剩下的负载部分的内容由PID来决定,例子看来就是一个PAT表。在这里有个地方需要注意一下,payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据47 40 00 10 00第五个字节是00,说明紧跟着00之后就是具体的负载内容。
下面给出PAT表的结构体:

PAT语法字段介绍: 
table_id : 这是一个1B的字段,该字段标识了一个TS PSI分段的内容是节目关联分段,条件访问分段还是TS节目映射分段等。对于PAT,置为0x0。 
section_syntax_indicator : 这是一个1bit的字段,对于PAT,置为0x1. 
section_length : 这是一个12bit的字段,该字段指示分段的字节数,由分段长的字段开始,包含CRC,其值不超过1021。 
transport_stream_id : 这是一个2个B的字段,作为一个标签,该字段指出在网络中与其他复用流的区别标志,其值由用户定义。 
version_number : 这是一个5b字段,该字段指出所有的PAT的版本号。一旦PAT有变化,版本号加1,当增加到31时,版本号循环回到0。 
current_next_indicator : 这个1b指示位,置为1时,表示传送的PAT当前可以使用;置为0时,表示该传送的表不能使用,下一个表变为有效。 
section_number : 这是1B字段,给出拉该分段的数目。在PAT中的第一个分段的section_number位0x00,PAT中每一个分段将加1。 
last_session_number : 这时一个1B字段,该字段指出了最后一个分段号。在整个PAT中即分段号的最大数目。 
program_number : 这是一个2B字段,该字段指出了节目对于哪一个program_map_PID是可以使用的。如果是0x0000,那么后面的PID是网络PID,则其他值由用户定义。 
network_PID : 这是一个13b字段,该字段指出含有NIT的TS包的PID值。此值由用户定义。 
program_map_PID : 这是一个13b字段,该字段知道TS包中的PID值。该TS应包含适用于program_number所指明的节目的program_map_section,该字段由节目号指定。一个节目号有一个progarm_map_PID的定义。该字段由用户定义。 
CRC_32 : 这是一个32b字段,用来检验数据正确性的循环冗余校验码。

typedef struct TS_PAT_Program
{
 unsigned program_number    :16; 
  unsigned reserved_3       : 3; 
  union PID{
	 unsigned program_map_PID   :13;   
	 unsigned network_PID       : 13; 
	 }u_pid;
}TS_PAT_Program;

//PAT表结构体
typedef struct TS_PAT
{
    unsigned table_id                        : 8; 
    unsigned section_syntax_indicator        : 1; 
    unsigned zero                            : 1; 
    unsigned reserved_1                        : 2; 
    unsigned section_length                    : 12; 
    unsigned transport_stream_id            : 16; 
    unsigned reserved_2                        : 2;
    unsigned version_number                    : 5; 
    unsigned current_next_indicator            : 1; 
    unsigned section_number                    : 8; 
    unsigned last_section_number            : 8;  
 
 	std::vector<TS_PAT_Program> program;
    unsigned CRC_32                            : 32;  
} TS_PAT
typedef struct TS_PAT_Program
{
 unsigned program_number    :16; //节目号 
  unsigned reserved_3       : 3; 
  union PID{
	 unsigned program_map_PID   :13;   //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个 unsigned network_PID 
	 unsigned network_PID       : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID
	 }u_pid;
}TS_PAT_Program;

//PAT表结构体
typedef struct TS_PAT
{
    unsigned table_id                        : 8; 
    unsigned section_syntax_indicator        : 1; 
    unsigned zero                            : 1; 
    unsigned reserved_1                        : 2; 
    unsigned section_length                    : 12; 
    unsigned transport_stream_id            : 16; 
    unsigned reserved_2                        : 2;
    unsigned version_number                    : 5; 
    unsigned current_next_indicator            : 1; 
    unsigned section_number                    : 8; 
    unsigned last_section_number            : 8;  
 
 	std::vector<TS_PAT_Program> program;
    unsigned CRC_32                            : 32;  
} TS_PAT;

RESULT 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 )
    {
    	TS_PAT_Program PAT_program;
			PAT_program.program_number = buffer[8 + n ] << 8 | buffer[9 + n ];  
			
			PAT_program.reserved_3    = buffer[10 + n ] >> 5;  
			packet->pid.network_PID = 0x00;
			if ( program_num == 0x00)
			{  
					PAT_program.u_pid.network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
					
			}
			else
			{				
					PAT_program.u_pid.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];
		
			}  
			packet->program.push_back( PAT_program );       
    }
 return 0;
}

通过分析,我们也可以分析出PAT表各字段的值,如program_map_PID为0xfff,我们也可以用ts_reader.exe工具分析:

由此我们知道PMT表的pid为0xfff,所以查找47 4f ff的开头的TS包


下面来分析PMT表,先给出PMT(Program Map Table)的结构体:


PMT语法字段介绍: 
table_id : 这是一个1B的字段,对于PMT置为0x02。 
section_syntax_indicatior : 这是一个1b的字段,对于PMT,值为1。 
section_lenth: 这是一个12b的字段,前两位置为00,表示字段的字节数,包括section_length字段和CRC字段。 
program_number: 该字段指出该节目对应于可应用的program_map_PID。一个节目定义仅含一个TS的program_map_section,因此一个节目定义长度不超过1026B。 
version_number : 这是一个5b的字段,该字段指出了program_map_section的版本号。当字段中有关信息发生变化时,版本号将以32为模加1。版本号时关于一个节目的定义,因此版本号是关于单一段的定义。 
curren_next_indicator : 这是一个1b的字段,当字段置为0时,表示当前传送的program_map_section可以;当置为0时,表示当前传送的program_map_section不可用,下一个TS的program_map_section有效。
section_number : 这是一个1B长度的字段,该字段值总是0x00。 
last_section_number:这是一个1B长度的字段,该字段值总是0x00。 
PCR_PID: 这是一个13b长度的字段,该字段指示TS包的PID值。该TS包含有PCR字段,而该PCR值对应于由节目号指定的节目。如果对于私有数据流的急忙定义与PCR无关,该字段的值将位0x1FFF。 
program_info_length: 这是一个12b长的字段,前2位是00。该字段指出跟随其后对节目信息描述的字节数。 
stream_type: 这是一个1B长度的字段,该字段指定特定PID的节目元素包的类型。即他定义了在TS包中PES流的类型,如下表。该处的PID由element_PID指定。
element_PID: 这是一个13b长度的字段。该字段指示TS包的PID值。这些TS包含有相关的节目元素。 
ES_info_length: 这是一个12b长度的字段,前2位为00。该字段指示跟随其后的描述相关节目元素的字节数。 
CRC_32:这是一个32b字段,用来检验数据正确性的循环冗余校验码。 


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表
--------byte: 1-------------
    unsigned section_syntax_indicator        : 1; //固定为0x01
    unsigned zero                            : 1; //0x01
    unsigned reserved_1                      : 2; //0x03
    unsigned section_length  : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
--------byte: 4-------------
    unsigned program_number                    : 16;// 指出该节目对应于可应用的Program map PID
--------byte: 6-------------
    unsigned reserved_2                        : 2; //0x03
    unsigned version_number                    : 5; //指出TS流中Program map section的版本号
    unsigned current_next_indicator        : 1; //当该位置1时,当前传送的Program map section可用;
--------byte: 7-------------
   //当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
    unsigned section_number                    : 8; //固定为0x00
    unsigned last_section_number            : 8; //固定为0x00
--------byte: 9-------------
    unsigned reserved_3                        : 3; //0x07
    unsigned PCR_PID                        : 13; //指明TS包的PID值,该TS包含有PCR域,
            //该PCR值对应于由节目号指定的对应节目。
            //如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
--------byte: 11-------------
    unsigned reserved_4                        : 4; //预留为0x0F
    unsigned program_info_length            : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
    --------byte: 13-------------
 std::vector<TS_PMT_Stream> PMT_Stream;  //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
    unsigned reserved_5                        : 3; //0x07
    unsigned reserved_6                        : 4; //0x0F
------single: 5 Bytes---------
    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;
}


PMT结构体里的stream_type、elementary_PID这两个元素,前一个用来确定后一个作为标识PID的内容具体是什么,音频或视频等
stream_type 值定义在iso13818-1中
从这里, 我们就可以得到各路流的PiD,接着就可以收取相关pid流的数据,


TS 流解码过程:
1. 获取TS中的PAT
2. 获取TS中的PMT
3. 根据PMT可以知道当前网络中传输的视频(音频)类型(H264),相应的PID,PCR的PID等信息。
4. 设置demux 模块的视频Filter 为相应视频的PID和stream type等。
5. 从视频Demux Filter 后得到的TS数据包中的payload 数据就是 one piece of PES,在TS header中有一些关于此 payload属于哪个 PES的 第多少个数据包。 因此软件中应该将此payload中的数据copy到PES的buffer中,用于拼接一个PES包。
6. 拼接好的PES包的包头会有 PTS,DTS信息,去掉PES的header就是 ES。
7. 直接将 被被拔掉 PES包头的ES包送给decoder就可以进行解码。解码出来的数据就是一帧一帧的视频数据,这些数据至少应当与PES中的PTS关联一下,以便进行视音频同步。
8. I,B,B,P 信息是在ES中的。





  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: MPEG-2 TS解析工具是一种用于解析MPEG-2转移流(MPEG-2 TS)的工具。MPEG-2 TS是一种标准格式,用于数字电视广播和其他流媒体应用。MPEG-2 TS通常被用于传输高清视频、音频和其他媒体数据。通过使用MPEG-2 TS解析工具,用户可以方便地分析MPEG-2 TS流的内容,包括视频、音频和数据流等。该工具提供了许多功能,例如,可以检测视频、音频和数据流是否符合MPEG-2 TS规范,可以提取特定的数据流,以及可以解码视频和音频。使用MPEG-2 TS解析工具,用户可以更深入地了解MPEG-2 TS的规范和特性,并可帮助开发人员在开发流媒体应用程序时更好地理解MPEG-2 TS编码和解码的工作原理。MPEG-2 TS解析工具是一个重要的工具,对于那些需要处理数字视频和音频流的专业人员来说,具有非常重要的作用。 ### 回答2: MPEG2TS是一种视频传输格式,通常用于数字电视广播、视频录制和存储等领域。为了解析和处理这种格式的数据流,需要使用一种称为MPEG2TS解析工具的软件工具。 MPEG2TS解析工具可以解析MPEG2TS格式的媒体文件,并提供对单个数据包或整个数据流的分析。这种工具能够识别视频、音频和其他元数据,并显示其一些信息,如数据的时间戳、长度和类型。这使得用户能够进行数据捕获、数据流重组、错误分析和调试等操作。 除此之外,MPEG2TS解析工具还能够提供一些附加功能,例如视频和音频解码、视频播放和转码等。这些功能使得用户能够进行更加深入的数据分析和处理。 在数字电视广播和视频存储领域,MPEG2TS解析工具是必不可少的工具之一。它能够帮助用户分析和处理MPEG2TS格式的数据流,从而使得数字电视和视频录制等应用更加顺畅。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值