(1)TS流是基于Packet的位流格式,每个包是188字节或者204字节(一般是188字节,204字节的格式仅仅是在188字节的Packet后部加上16字节的CRC数据,其他格式是一样的),整个TS流组成如下所示:
Packet 1 Packet 2 ...... Packet n
在实际使用中,因为TS流已经内部具有很强的错误处理能力,所以一般使用较多的是188字节一个包的格式,204字节一个包的格式据说一般在高清节目中使用较多.
所有的Packet格式都是统一的,包括一个Packet header和Packet datas.其中Packet header包含了同步字节(该字节固定是0x47,表示这个包的数据开始是正确的),该Packet的唯一号码(即PID)和其他一些信息.格式如下(用C格式表示)
typedef struct { unsigned sync_byte:8;/*8 bits的同步字节,固定为0x47*/ unsigned transport_error_indicator:1;/*1 bit的错误指示信息,1表示当前Packet至少有1bit的传输错误,0表示所有数据都正确*/ unsigned payload_unit_start_indicator:1;/*负载单元开始标志。详细下面介绍*/ unsigned transport_priority:1;/*1 bit的传输优先级标志,1表示高优先级,0表示低优先级*/ unsigned PID:13;/*13 bits的Packet ID号码,唯一的号码对应不同的包*/ unsigned transport_scrambling_control:2;/*2 bits的加扰标志,00表示没有加扰,其他表示已被加扰*/ unsigned adaptation_field_control:2;/* 2 bits的附加区域控制,详细下面介绍*/ unsigned continuity_counter:4;/*4 bits的包递增计数器*/ }PACKET_HEADER;
payload_unit_start_indicator
这是1b长度字段,该字段用来表示ts包的有效净荷带有PES包或者PSI数据的情况。 当ts包带有PES包数据时,该字段具有以下特点:置为1,表示ts包的有效净荷以PES包的第一个字节开始;置为0,表示ts包的开始不是PES包。 当ts包带有PSI数据时,该字段具有以下特点:置为1,表示ts包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field;置为0,表示ts包不带有一个PSI部分的第一个字节,即有效净荷中没有指针pointer_field。 对于空包该字段应该置为0; 也就是说,为1时,在前4个字节(包头)之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。
PID 这个在头部的32b的字段十分重要,它是辨别码流信息性质的关键,是节目信息的“身份证”,不同的电视节目和业务信息SI对应有不同的PID值。对于一台节目解码接收机而言,为了找到他所要接收的电视节目,他首先通过PID值找到节目专用信息PSI与业务信息SI所对应的不同表,这些表包括PAT、PMT、 CAT、NIT、SDT、EIT和TDT等,通过这些有关节目的信息表,尤其是节目专用信息表,可以查到所要接受节目的PID值和对应的CRC,这样节目就可以被还原。除了PAT表包的PID永远为0外,还有两种包的PID是预留的:一个是空包,它用作码流填充,其PID是8191,是PID所有允许值0-8191的最后一个, 另一个是条件接收表包CAT,其PID值总是1。
transport_scrambling_control 如果传送流包首部包括调整字段,则不应该被加扰。对于空包。该字段的值置为“00”。
adaptation_field_control 这是一个2b长度的字段,表示传送流包首部是否跟随有调整字段和/或有效净荷。 00:保留 01:没有调整字段,仅含有184B长度的有效净荷 10:没有有效净荷,仅含有183B长度的调整字段 11:0~182B的调整字段后为有效净荷
continuity_counter 随着具有相同PID包的增加二增加,当它达到最大时,又恢复为0.如果调整字段控制值 transport_scrambling_control为“00”和“10”,该连续计数器不增加。
以上结构刚好占用32 bits,即4个字节,因此一个TS流的Packet头部的4字节是header信息,剩下的184个字节的内容由PID决定。分析该header信息就可以知道当前Packet的属性.剩下的184字节有可能是Video数据,也有可能是Audio数据,也有可能是DVB SI信息,怎 么区分呢?其实很简单,就是利用header中的PID信息.上一章说了PAT是节目关联表,它的PID是0x0000.这个PID就是对应这里 header的PID.换句话就是说,如果我们发现一个Packet的PID等于0x0000,那么说明这个Packet是DVB的PAT表格而不是 Video数据或者Audio数据.
其他184个字节里面基本都装的是音频或者视频解码数据。如果给定一个TS文件,怎么去寻找解码音视频解码数据呢?
每个TS包的前4个字节的包头里都有一个PID,首先,一个个遍历TS包,我们找到PID为0的TS包,这个包叫PAT,这个PAT包里包含了PMT的PID号,所以我们再遍历TS包又又可以找到名为PMT的TS包,PMT里包含了video TS包的PID和它的codec,audio TS包的PID和它的codec 。有了codec我们知道要选择什么解码器,有PID我们就可以获得解码数据。
我们先来说说video数据和audio数据是怎么分散在TS包里的。video和audio其实都是以一种叫PES(Packetized Elementary Stream)的形式组织的。一帧视频就是一个PES包。我们都知道一个TS包只有188个字节,除掉包头还剩184个字节,这是不可能放下一帧的。实际上一个PES包是分配在连续的几个TS包中,所以如果我们要获得一帧数据,那么我们需要把连续的几个TS包里的数据全部取出来才能组合成一个PES。那我们怎么知道一个PES的开始和结尾呢?那我们还是一个个遍历每一个TS包,寻找包头里payload_unit_start_indicator为1包,这个标志位代表着是一个PES的开始,那么我从这开始,一直到下一个payload_unit_start_indicator为1,这中间的TS包组成起来就是一个PES。
实际上,在信号编码成TS码流的时候,不同节目的Video,Audio等数据都分配了不同的PID.例如,一个节目有两路Video,三路Audio,那 么分配PID的时候可能是Video 1==0x100,Video 2==0x101,Audio 1==0x102,Audio 2==0x103, Audio 3==0x104,这样传输的TS码流中的PID就可能有以上的PID.因此,如果我们需要在程序中过滤出第一路Video和第二路 Audio就可以这样处理了(伪代码描述):
void Process_Packet(unsigned char*buff){ int PID=GETPID(buff);/*从当前的188字节缓冲区中获取PID信息*/ if(PID==0x100) /*PID等于第一路Video的PID,说明当前数据是Video数据*/ { SaveToVideoBuffer(buff+4);/*把header后部的数据存到Video缓冲区,待后部处理*/ } else if(PID==0x103)/*PID等于第二路Audio的PID,说明当前数据是Audio数据*/ { SaveToAudioBuffer(buff+4);/*把header后部的数据存到Audio缓冲区*/ } else/*其他PID则丢弃,当然如果PID是DVB系统保留的PID如PAT,PMT则必须处理*/ { printf("unknown PID!\n"); } }
现 在的问题是,编码的时候分配好的PID,在解码的时候是怎么知道什么PID对应什么数据呢?
这 里仅仅截取了3个Packet的信息,请注意图中用红色标注的部分,这就是TS流Packet的4个字节的头信息.这个TS流是采用每个包共188字节的 格式,因为两个头信息的间隔是188个字节(第一个0x47到第二个0x47的间隔).以后的所有的Packet都将是188字节的格式,这是 DVB TS标准规定的固定大小.那么这三个包分别包含的是什么数据,下面我们可以自己分析一下.
先 看第一个包,头信息数据是"0x47 0x07 0xe5 0x12",刚才已经知道了,header信息都是按位操作的(这就是为什么TS码流也可以叫 做位流的原因),特别要注意的是定义和传输的时候都是MSB first,也就是说,先出现的位是数据的最高位.先转化成2进制格式:
01000111 00000111 11100101 00010010
请对照上面的PACKET_HEADER结构:
typedef struct { unsigned sync_byte:8; unsigned transport_error_indicator:1; unsigned payload_unit_start_indicator:1; unsigned transport_priority:1; unsigned PID:13; unsigned transport_scrambling_control:2; unsigned adaptation_field_control:2; unsigned continuity_counter:4; }PACKET_HEADER;
那么对照一下,我们可以发现:
sync_byte=01000111,就是0x47,这是DVB TS规定的同步字节,固定是0x47.
transport_error_indicator=0,表示当前包没有发生传输错误.
payload_unit_start_indicator=0,含义请参考ISO13818-1标准文档
transport_priority=0,表示当前包是低优先级.
PID=00111 11100101即0x07e5,这代表是什么呢,暂时还不知道(实际上是Video PID,参考下图)
transport_scrambling_control=00,表示节目没有加密
adaptation_field_control=01即0x01,具体含义请参考ISO13818-1
continuity_counte=0010即0x02,表示当前传送的相同类型的包是第3个
依此类推,再看一下第二个包"0x47 0x07 0xe5 0x13",二进制是:01000111 00000111 11100101 00010011
sync_byte=01000111,就是0x47,这是DVB TS规定的同步字节,固定是0x47.
transport_error_indicator=0,表示当前包没有发生传输错误.
payload_unit_start_indicator=0,含义请参考ISO13818-1标准文档
transport_priority=0,表示当前包是低优先级.
PID=00111 11100101即0x07e5,这代表是什么呢,暂时还不知道(实际上是Video PID,参考下图)
transport_scrambling_control=00,表示节目没有加密
adaptation_field_control=01即0x01,具体含义请参考ISO13818-1
continuity_counte=0011即0x03,表示当前传送的相同类型的包是第4个(注意到了吧,以上两个包的PID都是0x07e5,所以这里的continuity_counte就递增一次)
第三个包是"0x47 0x07 0xf1 0x18",2进制是01000111 00000111 11110001 00011000.
sync_byte=01000111,就是0x47,这是DVB TS规定的同步字节,固定是0x47.
transport_error_indicator=0,表示当前包没有发生传输错误.
payload_unit_start_indicator=0,含义请参考ISO13818-1标准文档
transport_priority=0,表示当前包是低优先级.
PID=00111 11100101即0x07f1,这代表是什么呢,暂时还不知道(实际上是Audio PID,参考下图)
transport_scrambling_control=00,表示节目没有加密
adaptation_field_control=01即0x01,具体含义请参考ISO13818-1
continuity_counte=1000即0x08,表示当前传送的相同类型的包是第9个
参考博客:http://www.360doc.com/content/11/0824/08/7399771_142838446.shtml