什么是PES、PS、TS

有关于PES、PS、TS的介绍网上有不少教程,但是很多都是重复的,最可怕的是对于同一个知识点的介绍可能存在多篇介绍文档 中解释不一样的情况,而对于初学者就很难判断具体哪种才是权威的、准确的说法。为了解决这个问题,本篇博文从官方文档中摘取核心定义供读者学习,当然如果你对自己的英文很自信,可以直接参阅官方文档——《ISOIEC 13818-1.pdf

根据《ISOIEC 13818-1.pdf》文档介绍:

TS包图示:

什么是PES、PS、TS

TS包目前没怎么碰到,所以一带而过,不做过多的介绍。只需要知道它和PS类似是对多媒体流的二次封装(第一次封装发生在PES,至于为什么要进行二次封装,请查看黑宝的其他相关博文)。

0.4 Packetized Elementary Stream

Transport Streams and Program Streams are each logically constructed from PES packets, as indicated in the syntax definitions in 2.4.3.6 on page 33. PES packets shall be used to convert between Transport Streams and Program Streams; in some cases the PES packets need not be modified when performing such conversions. PES packets may be much larger than the size of a Transport Stream packet

黑宝来翻译:包化的原始流(ES)

TS流和PS流都是由PES包经过某种逻辑结构组织而成,具体的组织语法定义参见33页的2.4.3.6小节。PES包可以被用来完成从TS流转换到PS流的转换工作,有的时候不需要更改PES包就能完成这种转换。PES包的大小有时也可以大于TS包(也就是说有的时候一个PES包会被分装在多个TS包中传输)。(黑宝对自己的英文翻译能力还是比较自信,在此感谢何凯文老师)

PES包图示:

什么是PES、PS、TS

通过这个图我们很容易获取到这样的信息:PES包由3部分组成,分别是固定头、可选头、负载(即ES流)。固定头共有24+8+16 = 48位(即6个字节),固定头之后是可选头,可选头之后就是负载数据。固定头又由3部分组成,分别是packet_start_code_prefix、stream_id、PES Packet length,通过PES Packet length我们可以知道从PES Packet length域之后还剩多少字节长度,由于PES Packet length共16位,最大可表示2^16 -1 个字节即65535个字节,由此我们可以推算出PES包的最大长度。

2.4.3.7 Semantic definition of fields in PES packet

packet_start_code_prefix -- The packet_start_code_prefix is a 24-bit code. Together with the stream_id that follows it constitutes a packet start code that identifies the beginning of a packet. The packet_start_code_prefix is the bit string '0000 0000 0000 0000 0000 0001' (0x000001).

黑宝来翻译:PES数据包中的字段的语义定义

包起始码前缀:PES包的起始码是24位bit数据,起始码前缀与其后紧随着的stream_id一起构成了PES包的起始码,该起始码标志着一个PES包的开始,PES包起始码前缀的bit流是:'0000 0000 0000 0000 0000 0001' (0x000001).

stream_id --(……)

黑宝来翻译:stream_id这个域的介绍有点长,请参阅官方原文档

PES_packet_length -- A 16 bit field specifying the number of bytes in the PES packet following the last byte of the field. A value of 0 indicates that the PES packet length is neither specified nor bounded and is allowed only in PES packets whose payload is a video elementary stream contained in Transport Stream packets.

黑宝来翻译:

PES_packet_length:一个16位的域,该域说明了从该域最后一个字节之后还有多少个字节数据是属于该PES包,如果该值是0说明PES包的长度既没有明确说明也不做限定,当然这种情况只允许出现在PES包的负载是视频原始码流,并且该PES包归属于TS包的时候(也就是说二次分装的时候是TS包是才允许这种情况发生,如果是PS则不允许)。

通过上面的介绍我们能知道一个PES的开始,和这个PES包的总长度,但是PES包类似于其他的网络分包它分为两部分,分别是PES header 和 PES payload,我们怎么得知PES包中哪部分是PES header 哪部分是 PES payload 呢?其实我们被官方文档坑了一把,如上面 'F.0.2 PES Packet' 图示中第二行第2个字节,此处标明 '7 flags' ,这里的意思是说在这个字节中有7个标志位,第一个标志位占2位,剩余的6个标志位各占1为,加起来刚好是一个字节,请看官方文档

什么是PES、PS、TS

上图中红色框中的7项就代表着7个标志位,加起来共8位也就是1个字节,也就是'F.0.2 PES Packet' 中标注 '7 flags' 的地方为什么是8位的原因,好了,关子卖够了,那到底怎么区分PES包中的 PES header 和 PES payload 呢?请看'F.0.2 PES Packet' '7 flags' 域的下一个域 ' PES_header_data_length ' , 正是该域说明了PES header 剩余的字节数,如果该域域值为5,那么从此域之后的5个字节处就是PES header 和 PES payload的分界点,在分界点之前是PES header,之后是PES payload。至于域 ' PES_header_data_length ' 的解释,请看官方解释:

PES_header_data_length -- An 8 bit field specifying the total number of bytes occupied by the optional fields and any stuffing bytes contained in this PES packet header. The presence of optional fields is indicated in the byte that precedes the PES_header_data_length field.

黑宝来翻译:

PES_header_data_length:一个8位的域,该域说明了'optional fields' 域所占用的总字节数包括所有的PES Packet header的填充字节数,这个字节说明了 'optional fields' 是否存在,'optional fields' 比 'PES_header_data_length' 域还要重要。

总结一下:

PES包的起始码是(0x000001), 起始码之后是stream_id(1个字节), 在之后的两个字节是PES包的长度,再往后的两个字节不用理,再往后的一个字节说明了PES header的长度,PES header 之后就是 PES的负载了,即ES流。

上面这些是关于PES的介绍,一般情况下对于PES了解到这些信息应该足够用了,下面我们开始介绍PS。

0.2 Program Stream

The Program Stream is a stream definition which is tailored for communicating or storing one program of coded data and other data in environments where errors are very unlikely, and where processing of system coding, e. g. by software, is a major consideration.

黑宝来翻译:PS流

PS是流的定义,设计它的目的是用来在那些一般不容易发生错误的环境中,通过程序交流或存储一个已经编码的数据和另外的一些数据,同时系统编码的处理比如通过软件也是一个重要的考虑因素。(哈呸,这翻译的是啥?什么是PES、PS、TS,有点那啥词爸翻译出来的感觉中国人外国人都看不懂,但是想表达的意思读者朋友应该清楚了吧)。

 

PS流图示:

 

什么是PES、PS、TS

 

根据上图,PS头包括从PS包起始码开始一直到系统头结束(如果码流中包含系统头)。在此之后就是PS包的负载即PES包。

 

2.5.3.4 Semantic definition of fields in program stream pack

pack_start_code -- The pack_start_code is the bit string '0000 0000 0000 0000 0000 0001 1011 1010'

(0x000001BA). It identifies the beginning of a pack.

黑宝来翻译:程序流包中的字段的语义定义

包起始码:PS包起始码是一串比特流,该比特流的内容是'0000 0000 0000 0000 0000 0001 1011 1010'

(0x000001BA). 它标志着一个PS包的开始。

下面我们来解析一下这段码流:

例(I帧PS_header):

0000   00 00 01 ba 44 00 04 00 04 01 00 5f 6b f8 00 00  ....D......_k...
0010   01 bb 00 0c 80 cc f5 04 e1 7f e0 e0 e8 c0 c0 20  ...............
0020   00 00 01 bc 00 1e e1 ff 00 00 00 14 1b e0 00 0c  ................
0030   2a 0a 7f ff 00 00 07 08 1f fe a0 5a 90 c0 00 00  *..........Z....
0040   00 00 00 00 00 00 01 e0 00 17 80 80 05 21 00 01  .............!..
0050   00 01                                            ..

 

 

 

首先通过首部 '00 00 01 ba' 我们可以判断出这是PS包,然后通过PS包首部第14个字节的后3位判断填充数据的个数,第14个字节是 'f8' , 可以判断出首部之后无填充数据,然后紧随着的是系统头起始码 '0x000001bb' ,系统头起始码之后的16位(2个字节)说明了系统头的长度,此例中我们看到系统头长度为 '00 0c' (即12个字节); 再往后我们看到了PSM(Program Stream map)头起始码 '00 00 01 bc' ,PSM头起始码之后的16位表明了PSM头的长度,此处为 '00 1e'(即30个字节),从 次处再开始数30个字节后我们看到了 PES媒体包头 '00 00 01 e0' ,'e0' 表明该PES包中分装的是视频流(如果是'c0' 表明该PES包中封装的是音频流)。

细心的朋友一定看出来PSM头起始码是 '00 00 01' 这符合PES包的定义,没错,这个包确实是PES包,如果stream_id为 'bc' 那么这个PES包为PSM包;为什么敢这么肯定地说,请看《ISOIEC 13818-1.pdf》文档中给PSM的定义:

2.5.4 Program Stream map
The Program Stream Map provides a description of the elementary streams in the Program Stream and their relationship to one another. When carried in a Transport Stream this structure shall not be modified. The PSM is present as a PES packet when the stream_id value is 0xBC.

黑宝来翻译:PS流映射

在PS流中PSM用来提供es流的描述信息,以及各es流之间的关系。在TS流中这种结构是不能被改变的。PSM是以PES包的形式出现的,当PES包的stream_id字段的值是 '0xBC' 的时候就表明该PES包是PSM(哎呀,看看这翻译,又自信了一把^^)。

 

本文所涉及到的官方文档:

ISOIEC 13818-1.pdf