jsmpeg系列二 基础知识 TS码流 PAT PMT

2018-09-26  本文已影响307人  合肥黑

参考
【PSI/SI学习系列】第一章:预备知识
【PSI/SI学习系列】第二章:从TS到PAT和PMT

最近开始学习数字电视机顶盒的开发,从MPEG-2到DVB,看着看着突然就出现了一大堆表格, 什么PAT、PMT、CAT……如此多的表该怎样深入了解呢?
我们知道,数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流), 每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。 因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。

一、概述

参考ES、PES、PS以及TS码流
TS全称是 MPEG 2 Transport Stream(MPEG2 传输流),广泛用于广播电视系统,比如说数字电视,以及IPTV。ts流最早应用于数字电视领域,其格式非常复杂包含的配置信息表多达十几个,视频格式主要是mpeg2。苹果公司发明的http live stream流媒体是基于ts文件的,不过他大大简化了传统的ts流,只需要2个最基本的配置表PAT和PMT,再加上音视频内容就可以了,hls流媒体视频编码的主要格式为h264/mpeg4,音频为aac/mp3。

1.概念
ts文件分为三层:ts层(Transport Stream)、pes层(Packet Elementary Stream)、es层(Elementary Stream)。


image.png

ES(Elementary Stream)流是基本码流,包含音频、视频、数据的连续码流。直接从编码器出来的数据流,可以是编码过的视频数据流(H.264,MJPEG等),音频数据流(AAC),或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。

PES(Packet Elementary Stream 分组的ES)ES形成的分组称为PES分组,是用来传递ES的一种数据结构。PES流是ES流经过PES打包器处理后形成的数据流,在这个过程中完成了将ES流分组、打包、加入包头信息等操作(对ES流的第一次打包)。PES流的基本单位是PES包。PES包由包头和payload组成。

TS(Transport Stream)流,也叫传输流。是在pes层上加入了数据流识别和传输的必要信息。是由固定长度的188字节的包组成。含有独立是一个或者多个program,一个program又可以包含多个视频,音频和文字信息的ES流。每个ES流会有不同的PID标示。为了分析这些ES流,TS有些固定的PID来间隔发送Program和ES信息表格:PAT表和PMT表。

PS流(Program Stream) 节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。PS流由PS包组成,而一个PS包又由若干个PES包组成(到这里,ES经过了两层的封装)。PS包的包头中包含了同步信息与时钟恢复信息。一个PS包最多可包含具有同一时钟基准的16个视频PES包和32个音频PES包。

TS流和PS流的区别:TS流的包结构是长度是固定的;PS流的包结构是可变长度的。 这导致了 TS流的抵抗传输误码的能力强于PS流。TS码流由于采用了固定长度的包结构, 当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。 而PS包由于长度是变化的,一旦某一 PS包的同步信息丢失, 接收机无法确定下一包的同步位置,就会造成失步,导致严重的信息丢失。 因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流(相对无错的环境下传输与存储,比如DVD)。由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格。

2.流程
(1)A/D转换后,通过MPEG-2压缩编码得到的ES基本流。这个数据流很大,并且只是I,P,B的这些视频帧或音频取样信息。
(2)通过PES打包器,打包并在每个帧中插入 PTS/DTS标志,变成PES。原来是流的格式,现在成了数据包的分割形式。
(3)PES根据需要打包成PS或TS包进行存储(DVD)或传输(DVB)。因每路音/视频只包含一路的编码数据流,所以每路PES也只包含相应的数据流。

3.这里介绍一下PTS/DTS:
PTS--PresentationTime Stamp(显示时间标记)表示显示单元出现在系统目标解码器(H.264、MJPEG等)的时间。

DTS--Decoding Time Stamp(解码时间标记)表示将存取单元全部字节从解码缓存器移走的时间。

PTS/DTS是打在PES包的包头里面的,这两个参数是解决音视频同步显示,防止解码器输入缓存上溢或下溢的关键。每一个I(关键帧)、P(预测帧)、B(双向预测 帧)帧的包头都有一个PTS和DTS,但PTS与DTS对于B帧不一样,无需标出B帧的DTS,对于I帧和P帧,显示前一定要存储于视频解码器的重新排序缓存器中,经过延迟(重新排序)后再显示,所以一定要分别标明PTS和DTS。

以下参考深入理解pts,dts,time_base
pts反映帧什么时候开始显示,dts反映数据流什么时候开始解码。怎么理解这里的“什么时候”呢?如果有某一帧,假设它是第10秒开始显示。那么它的pts是多少呢。是10?还是10s?两者都不是。

为了回答这个问题,先引入FFmpeg中时间基的概念,也就是time_base。它也是用来度量时间的。 如果把1秒分为25等份,你可以理解就是一把尺,那么每一格表示的就是1/25秒。此时的time_base={1,25} 。如果你是把1秒分成90000份,每一个刻度就是1/90000秒,此时的time_base={1,90000}。 所谓时间基表示的就是每个刻度是多少秒。pts的值就是占多少个时间刻度(占多少个格子)。它的单位不是秒,而是时间刻度。只有pts加上time_base两者同时在一起,才能表达出时间是多少。

下面理解时间基的转换,为什么要有时间基转换。 首先,不同的封装格式,timebase是不一样的。另外,整个转码过程,不同的数据状态对应的时间基也不一致。拿mpegts封装格式25fps来说(只说视频,音频大致一样,但也略有不同)。非压缩时候的数据(即YUV或者其它),在ffmpeg中对应的结构体为AVFrame,它的时间基为AVCodecContext 的time_base ,AVRational{1,25}。 压缩后的数据(对应的结构体为AVPacket)对应的时间基为AVStream的time_base,AVRational{1,90000}。 因为数据状态不同,时间基不一样,所以我们必须转换,在1/25时间刻度下占10格,在1/90000下是占多少格。这就是pts的转换。

比如从某TS码流中得到播放顺序第0帧的PTS为0xB0C0697A,第一帧的PTS为0xB0C0778A。两帧的PTS之差为0xB0C0778A - 0xB0C0679A = 0xE10 = 3600。则两帧图像的播放时间间隔为3600 * ( 1/ 90k ) = 0.04s。正好是25帧每秒的视频的时间间隔。

二、TS层

ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。

image.png

ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。

1.TS header

名称 长度 说明
sync_byte 8bit 同步字节,固定为0x47
transport_error_indicator 1bit 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内
payload_unit_start_indicator 1bit 负载单元起始标示符,一个完整的数据包开始时标记为1
transport_priority 1bit 传输优先级,0为低优先级,1为高优先级,通常取0
pid 13bit pid值(Packet ID号码,唯一的号码对应不同的包)
transport_scrambling_control 2bit 传输加扰控制,00表示未加密
adaptation_field_control 2bit 是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。
continuity_counter 4bit 递增计数器,从0-f,起始值不一定取0,但必须是连续的

上面表格是一个包(Package)的头(Header)的说明,其中需要注意的是:PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。 如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。

下表给出了一些表的PID值,这些值是固定的,不允许更改。

PID值 说明
PAT 0x0000 -
CAT 0x0001 -
TSDT 0x0002 -
预留 0x0003 至0x000F
NIT, ST 0x0010 -
SDT , BAT, ST 0x0011 -
EIT, ST 0x0012 -
RST, ST 0x0013 -
TDT, TOT, ST 0x0014 -
网络同步 0x0015
预留使用 0x0016 至 0x001B
带内信令 0x001C
DIT 0x001E
SIT 0x001F

解复用程序需要使用到的表格只有PAT、PMT、SDT;而CA解扰还需要使用CAT;EPG则还要有NIT、EIT、TDT、TOT等表。

2.header例子
下面以一个TS流的其中一个Packet中的Packet Header为例进行说明:
16进制为:0x4707e512
sync_byte 占8位,固定是0x47
然后16进制的07,变为二进制是00000111
transport_error_indicator 1 bits 值为0,表示当前包没有发生传输错误。错误指示信息(1:该包至少有1bits传输错误)
payload_unit_start_indicator 1 bits 值为0,含义参考ISO13818-1标准文档。负载单元开始标志(packet不满188字节时需填充)
transport_priority 1 bits 值为0,表示当前包是低优先级。传输优先级标志(1:优先级高)
这里的07,实际上只用了前三位,后面的5位加上后面的16进制e5就是13位的PID了
PID 13 bits PID=00111 11100101即0x07e5,是Video PID。Packet ID号码,唯一的号码对应不同的包
还剩下16进制的最后两位12,对应二进制是00010010
transport_scrambling_control 2 bits 值为0x00,表示节目没有加密。加密标志(00:未加密;其他表示已加密)
adaptation_field_control 2 bits 值为0x01,具体含义请参考ISO13818-1。附加区域控制
continuity_counter 4 bits 值为0x02,表示当前传送的相同类型的包是第3个。包递增计数器

三、PSI/SI缩略语
关键字 全称 中文翻译 备注
PSI Program Specific Information 节目引导信息 对单一码流的描述
SI Service Information 业务信息 对系统中所有码流的描述
TS Transport Stream 传输流(常称为TS流) 一个频道(多个节目及业务)的TS包复用后称TS流
TS包 Transport Packet 传输包 数字视音频、图文数据打包成TS包

PSI节目引导信息

关键字 全称 中文翻译 备注
PAT Program Association Table 节目关联表 将节目号码和节目映射表PID相关联,获取数据的开始
PMT Program Map Table 节目映射表 指定一个或多个节目的PID
CAT Conditional Access Table 条件接收表 将一个或多个专用EMM流分别与唯一的PID相关联
NIT Network Information Table 网络信息表 描述整个网络,如多少TS流、频点和调制方式等信息

SI业务信息

关键字 全称 中文翻译 备注
SDT Service Description Table 业务描述表 包含业务数据(如业务名称、起始时间、持续时间等)
BAT Bouquet Association Table 业务群关联表 给出业务群的名称及其业务列表等信息
EIT Event Information Table 事件信息表 包含事件或节目相关数据,是生成EPG的主要表
RST Running Status Table 运行状态表 给出事件的状态(运行/非运行)
TDT Time&Date Table 时间和日期表 给出当前事件和日期相关信息,更新频繁
TOT Time Offset Table 时间偏移表 给出了当前时间日期与本地时间偏移的信息
ST Stuffing Table 填充表 用于使现有的段无效,如在一个传输系统的边界
SIT Selection Information Table 选择信息表 仅用于码流片段中,如记录的一段码流,包含描述该码流片段业务信息段的地方
DIT Discontinuity Information Table DVB 间断信息表 仅用于码流片段,如记录的一段码流中,它将插入到码流片段业务信息间断的地方
四、从TS到PAT和PMT

参考【PSI/SI学习系列】第二章:从TS到PAT和PMT
说完了TS流相关的基本概念,就该开始对TS流进行更深入的研究了。 首先需要想一想:TS流的本质是什么? TS是一段码流,并且是一段由数据包(Packet)组成的码流。 那么我们要如何组织和处理如此庞大的数据包呢?

如果我们把码流看作一本书,那么我们就需要给这本书制作一个目录;有了这样的目录,我们就可以“按图索骥”地得到我们需要的信息。

是的,PSI/SI就是这样“目录”。而PAT则是“目录”的“目录”。 通过PAT及其他 PSI 信息,我们可以一点一点地把整个TS流的信息组织和管理起来,从而分离出不同的业务(Service), 以及该业务的视频、音频、图文(Teletext)、字幕(Subtitle)、其他音轨等分量; 再根据 SI 信息,我们就可以得到每个业务的名称、事件(Event)等附加信息, 从而形成 EPG(Electronic Program Guide电子节目指南) ,更好地管理各个业务。

需要区分的一点是,不管是PSI还是SI,都只是一种“管理信息”,它并不包含任何的视频或音频数据。

1.一切从PAT开始
PAT表(Program Association Table,节目关联表) 定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,要查寻找节目必须从PAT表开始查找。PAT表携带以下信息:

TS流ID transport_stream_id 该ID标志唯一的流ID
节目频道号 program_number 该号码标志TS流中的一个频道,该频道可以包含很多的节目(即可以包含多个Video PID和Audio PID)
PMT的PID program_map_PID 表示本频道使用哪个PID做为PMT的PID。因为可以有很多的业务,因此DVB规定PMT的PID可以由用户自己定义
PAT表的数据包

前4个字节为包头,后面的184个字节为包数据。

2.包头47 40 00 1C
参照上文解析方式,其中重点看PID=0x0000,这说明是PAT表。还有payload_unit_start_indicator=1,说明在包头后需要除去一个字节才是有效数据。(这里暂时不作解释,具体原因参考jsmpeg系列四 源码ts.js TS格式解析流程)

3.包数据
00 B0 1D 22 01 CF 00 00 00 00 E0 10 40 13 E1 30 40 18 E1 80 40 0A E0 A0 40 0E E0 B5 10 A5 84 FF ... FF FF
注意,由于包头中payload_unit_start_indicator=1,说明在包头后需要除去一个字节才是有效数据。
下面对前8个字节00 B0 1D 22 01 CF 00 00进行分析
(1)00
PAT的table_id只能是0x00

(2)B0 1D
二进制1011 0000
section_syntax_indicator 1bit 值为1 段语法标志位,固定为1
zero 1bit 值为0
reserved 2bits 值为11 (Binary)
section_length 12bits 值为00 00 00 01 11 01(即0x1D,十进制值为29) 意思是 段长度为29字节

(3)22 01
transport_stream_id 16bits TS的识别号

(4)CF
二进制1100 1111
reserved 2bits 值为11 TS的识别号
version_number 5bits 值为0 01 11 一旦PAT有变化,版本号加1
current_next_indicator 1bit 值为1 当前传送的PAT表可以使用,若为0则要等待下一个表
(5)00
section_number 0x00 给出section号,在sub_table中,第一个section其section_number为"0x00",每增加一个section,section_number加一
(6)00
last_section_number 0x00 sub_table中最后一个section的section_number

4.包数据前8个字节解析完,后面就是循环数据(循环数据结束后,还有最后4个字节的CRC_32)
B0 1D 22 01 CF 00 00 00 00 E0 10 40 13 E1 30 40 18 E1 80 40 0A E0 A0 40 0E E0 B5 10 A5 84 FF ... FF FF
每一个循环都是4个字节(32 bits),我们在这里就先解析第一个循环00 00 E0 10

字段名 占位 具体值 说明
program_number 16 bits 0x0000 program_number = 0x0000
reserved 3 bits 111 -
program_map_PID 13 bits 0x0000 因为program_number为0x0000,所以这里是network_id(NIT的PID)

继续解析下一个循环40 13 E1 30

字段名 占位 具体值 说明
program_number 16 bits 0x4013 program_number = 0x0000
reserved 3 bits 111 -
program_map_PID 13 bits 0x130 因为program_number不为0x0000,所以这里是program_map_PID = 0x130(即PMT_PID为0x130)

通过这个循环,我们可以知道,在这个TS中,有一个节目号为0x4013(即十进制16403)的节目,其PMT的PID为0x130。那么要想获取这个节目的详细信息,就要去解析PID为0x130的PMT表。

继续解析下一个循环 40 18 E1 80
结论同上,有一个节目号为0x4018(即十进制16408)的节目,其PMT的PID为0x180。那么要想获取这个节目的详细信息,就要去解析PID为0x180的PMT表。

将所有循环遍历完毕,我们就能找出4个节目,如下图


image.png

对我们来说,PAT就是一个总入口。PAT告诉了我们,这个TS流中有几个节目,以及它们的PMT PID分别是多少。有了PMT的PID,我们就可以继续下一步了。

5.PMT表(Program Map Table节目映射表)概述
PMT表中包含的数据如下:

只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息, 如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。 这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来, 在处理Packet的时候进行过滤即可实现。

6.以PID=0x130来分析


image.png

表头的分析方式和PAT表头的相同,就不详细介绍了。数据包中,数据包头是47 41 30 19。这里payload_unit_start_indicator=1,所以数据包忽略00,从02 B0 开始看。

(1)片断1: 02 B0 43 40 13 C5

字段名 占位 具体值 说明
table_id 8 bits 0x02
Section_syntax_indicator 1 bit 1 通常设为“1”
zero 1 bit 0
Reserved 2 bits 11
Section_length 12 bits 0x43 段长度,从program_number开始,到CRC_32(含)的字节总数
program_number 16 bits 0x4013 节目号为16403
Reserved 2 bits 11
Version_number 5 bits 00010 = 0x02
Current_next_indicator 1 bit 1 当前未来标志符

(2)片段2:00 00 E1 31 F0 00

字段名 占位 具体值 说明
Section_number 8 bits 0x00 当前段号码
last_section_number 8 bits 0x00 最后段号码,含义和PAT中的对应字段相同
reserved 3 bits 0x00
PCR_PID 13 bits 0x0131 PCR(节目参考时钟)所在TS分组的PID
reserved 4 bits 1111
program_info_length 12 bits 0x00 节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes)紧接着就是频道内部包含的节目类型和对应的PID号码了

注意,上面的program_info_length之后的是N个描述符结构,但一般可以忽略掉。这个字段就代表描述符总的长度,单位是Bytes;这里是0x00,也就是意味着后面没有描述符结构。

(3)片段3:02 E1 31 F0 03
因为program_info_length=0x00,所以紧接着的是频道内部包含的节目类型和对应的PID号码

字段名 占位 具体值 说明
stream_type 8 bits 0x02 流类型,标志是Video还是Audio还是其他数据。这里是MPEG2视频类型
reserved 3 bits 111
elementary_PID 13 bits 0x131 该节目的音频或视频PID。这里是视频PID=0x131
reserved 4 bits 1111
ES_info_length 12 bits 0x003 后面紧跟着3个字节的描述子

(4)片段3补充:52 01 01
因为ES_info_length=0x03,所以后续的三个字节,为Stream identifier descriptor

字段名 占位 具体值 说明
descriptor_tag 8 bits 0x52
descriptor_length 8 bits 0x01
component_tag 8 bits 0x01

至此,第一段单元流(Elementary Stream)的信息解析出来了。这是视频ES(MPEG2),其PID为0x131

片段4:04 E1 32 F0 09
接下来,继续第二个单元流信息解析。参照片段3的方式,先讲下一个单元流的基本信息解析出来:
0000 0100 111 0 0001 0011 0010 1111 0000 0000 1001

字段名 占位 具体值 说明
stream_type 8 bits 0x04 流类型,标志是Video还是Audio还是其他数据。这里是MPEG2音频类型
reserved 3 bits 111
elementary_PID 13 bits 0 0001 0011 0010 = 0x132 该节目的音频或视频PID。这里是音频PID=0x132
reserved 4 bits 1111
ES_info_length 12 bits 0000 0000 1001 = 0x09 后面紧跟着9个字节的描述子
9个字节的描述子

上图是完整的PID为0x132的ES信息,可以看到这个ES有两个描述子(descriptor)。 解析时,第一个字节代表了描述子tag,第二个代表该描述子的长度。看第一个描述子ISO_639_language_descriptor,第一个字节值为0xa,长度为0x4 看第二个描述子stream_identifier_descriptor,第一个字节值为0x52,长度为0x1。

其余单元流和PMT PID的分析略。

7.根据PAT和PMT的信息,我们可以得到下面的信息

节目号 PMT PID ES PID ES说明
16403 0x130 0x131 MPEG2视频
0x132 MPEG2音频
0x137 MPEG2私用PES
0x138 MPEG2私用PES
16408 0x180 0x181 MPEG2视频
0x182 MPEG2音频
0x187 MPEG2私用PES
0x188 MPEG2私用PES
16394 0xA0 0xa1 MPEG2视频
0xa2 MPEG2音频
0xa7 MPEG2私用PES
16398 0xE0 0xe1 MPEG2视频
0xe2 MPEG2音频
0xe7 MPEG2私用PES

当机顶盒等接收器收到的数据,自然就是被复用后的一个个包(Package)了。这样的数据是不能直接使用的。 这时候,我们要做的就是把被复用的TS流 解复用(Demultiplexing, 简称Demux)。解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。 这样就可以将Video、Audio或者其他业务信息(如PSI/SI信息)的数据区分开来。

上述表格,其实就是解复用获得的信息。 通过它,我们可以组织起这个频点下的所有 节目。 以节目号为16403的 节目 为例, 过滤PID为0x131的所有包数据,就可以组成这个节目的视频; 过滤PID为0x132的所有包数据,就可以组成这个节目的音频; 这样,节目16403需要的视音频数据就集齐了;当然,要正常播放这个台,还需要时钟来同步。

五、DVB搜台过程

机顶盒先调整高频头到一个固定的频率(一般是主频点,如深圳天威的主频点是259MHZ),如果此频率有数字信号, 则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG-2 decoder。 MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID==0x0000的Packet; 如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID。 接着循环查找是否出现PMT,如果发现了,则自动进入PMT分析,获取该频段所有的频道数据并保存。 如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描。

在解析TS流的时候,首先寻找PAT表,根据PAT获取所有PMT表的PID;再寻找PMT表,获取该频段所有节目数据并保存。 这样,只需要知道节目的PID就可以根据PacketHeade给出的PID过滤出不同的Packet,从而观看不同的节目。 这些就是PAT表和PMT表之间的关系。

六、其它表

1.SDT表
由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了 SDT表 来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。

节目号 SDT表信息
16403 Prosieben
16408 SAT.1
16394 KABEL1
16398 N24

2.NIT表
在国内,NIT表一般会给出整个网络的频点列表。 在搜索主频点的同时解析出NIT表,就可以得到这个网络的频点列表。 机顶盒按顺序对所有频点锁频、解析,就可以将整个网络的业务搜索出来了。

但在国外,一般不会有这样的情况。首先,小运营商只是租用其中一个或几个频点,不可能对整个网络“指手画脚”,自然就不能定义NIT表了。 而大运营商,它们有自己的业务群(Bouquet),更倾向于使用BAT表来组织自己的节目。

上一篇下一篇

猜你喜欢

热点阅读