前言
本章主要对数字广播DVB做一个系统的概况的描述,以及一些spc的相关的内容,虽然流程分析的不多,但是做为后续 章节资料的源泉,也是不可或缺的。
本文的主要目的是帮助DVB开发人员了解ATSC标准。本文并不涉及两种标准在物理层面(如调制解调)的差别,而主要讨论上层协议的区别,主要集中在如下四个方面:
- 音频压缩
- 服务信息系统
- 条件接收系统
- 数据广播
1 .音频压缩
ATSC数字电视接收器必须具备Digital Audio Compression (AC-3)音频压缩算法的解码能力,算法的具体细节可以参考文献[2]。
AC-3算法与ISO/IEC 13818-3(参考文献[3])相比,在多通道编码方面的表现比较出色。AC-3一共包含有5.1个声道:左前(L)、中央(C)、右前(R)、左环绕(Ls)、右环绕(Rs)和一个超重低音(LFE)。超重低音频宽是其它几个声道的1/10,因此称为5.1声道。通道的结合方式有1/0、1+1/0、2+1/0、3+1/0、2/1、3/1、2/2、3/2这8种,其中斜杠前表示前声道的个数,斜杠后表示后环绕声的个数,在8种组合中都可选择加设超低音通道
对比DVB
DVB的音频压缩算法必须遵从ISO/IEC 13818-3[3]标准,因此ISO/IEC 13818-3模块对DVB的接收系统来说是必不可少的;相比之下,ATSC采用的是AC-3标准,因此,ISO/IEC 13818-3对于ATSC接收器来说是可选模块,而AC-3音频解码模块是不可缺少的。
虽然在DVB和ATSC系统中都可以把AC-3的音频流映射到MPEG的Multiplex上传输,但是在实现方式上还存在着一些差异。
表1列出了AC-3音频流在ATSC和DVB系统中的传输比较。其中registration descriptor仅仅存在于ATSC系统,因为在DVB系统中,AC-3
descriptor是公开的描述符,无需用registration descriptor来注册。
descriptor是公开的描述符,无需用registration descriptor来注册。
2 服务信息表
电子节目指南(EPG)是数字电视中非常重要的部分,它相当于传统报纸上的节目表。而电子节目指南则是基于TS流中的Service Information(SI)而构造的。
DVB和ATSC标准分别定义了各自不同的SI结构,采用不同的机制来产生EPG。本章后续部分将介绍ATSC的SI1系统,并将其与DVB进行对比。
● 层次结构
ATSC包含了层次化的表格来描述系统信息和节目指南数据(参考文献[4])。其中,一个公用的基础PID(Base PID)是所有表格的入口,它被显示定义为0x1FFB,根据Base PID可以在地面广播系统和有线电视系统中定位如下表格。
- System Time Table (STT)——包含同步所需的时间信息
- Master Guide Table (MGT)——包含其它相关表格的大小,PID以及版本号
- Rating Region Table (RRT)——包含不同地区和国家的节目等级信息
- Virtual Channel Table (VCT)——包含节目导航和转换的虚拟频道表格
- The optional Directed Channel Change Table (DCCT)——在特定时间特定环境下要求接受者转换到特定的虚拟频道上去
- The optional Directed Channel Change Selection Code Table (DCCSCT)——基本种类列表和位置代码表的扩展
事件信息表(Event Information Table, EIT)也是SI系统的一部分,它们的PID定义在MGT之中。每一个EIT都按照VCT里面所定义的虚拟频道列出电视节目(事件)清单,并按照时间先后从EIT-0到EIT-127排序。ATSC标准规定,每一个EIT必须有3个小时的时间长度,而且所有EIT的开始时间都被限制在0:00(午夜),3:00,&nbs
p; 6:00, 9:00,12:00(中午),15:00,18:00和21:00(所有的时间都是UTC制)。这样,EIT-0代表的就是当前3个小时内的所有电视节目,EIT-1代表的是接下来3个小时的信息,而每隔三个小时,原先的EIT-0就被废弃,之前的EIT-1将取代EIT-0的位置,依此类推。另外,ATSC还定义了可选的扩展文字表(Extended Text Table,ETT),ETT包含了有关EIT的相对比较长的文字描述,它们的PID也同样包含在MGT中。
p; 6:00, 9:00,12:00(中午),15:00,18:00和21:00(所有的时间都是UTC制)。这样,EIT-0代表的就是当前3个小时内的所有电视节目,EIT-1代表的是接下来3个小时的信息,而每隔三个小时,原先的EIT-0就被废弃,之前的EIT-1将取代EIT-0的位置,依此类推。另外,ATSC还定义了可选的扩展文字表(Extended Text Table,ETT),ETT包含了有关EIT的相对比较长的文字描述,它们的PID也同样包含在MGT中。
图1描述了上述表格的层次结构。在一个数字电视TS流中,SI表格必须包含对当前TS流中所有的数字电视频道的描述,同时为了方便起见,还可能包括了相关的模拟频道信息以及其它TS流中的数字频道信息。对于地面广播系统,STT, TVCT(VCT的地面版本),MGT和前4个EIT都是强制要求的;对于有线电视系统,STT,CVCT(VCT的有线版本)或者TVCT,以及MGT都是必需的。
● 与DVB-SI的比较
图 2说明了DVB SI表格的层次结构。总的来说,DVB-SI和ATSC-SI的目的都是为了构造EPG,他们在功能上是相似的。例如:ATSC的STT就时间同步功能来说等价于DVB中的TDT,ATSC的VCT就虚拟频道(服务)描述来说和DVB中的SDT等价,ATSC和DVB中的EIT同样提供TV节目清单。
(可见EIT里放的就是EPG了。。)
即便如此,ATSC-SI和DVB-SI仍然存在如下一些差异:
1. 节目标识:在DVB系统中,节目总是以original network id/ transport stream id/ service id的方式标识的。其中original network id是由授权中心颁发给经认证的运营商或者广播商的。采用这种联合方式来标识能够方便收费和订阅信息的管理。而在ATSC系统中,不存在original network id的标识,用户能看到的是一个类似于2-1主次号结合的标识符,这里主号2是广播商的NTSC牌照,该广播商的所有服务都采用该主号来标识;次号1是用来在该主号群组中标识特定的服务,次号0预留给该广播商的模拟频道服务,1-999可以用来标识数字电视服务(图3显示的是采用主次频道结合标识的一个例子)。这种机制使得transport
stream id和service id对电视终端用户不可见。
stream id和service id对电视终端用户不可见。
2. 事件信息表(EIT):ATSC和DVB都有各自的EIT,虽然名字相同,但它们的结构有很大的差别。另外,ATSC中的每个EIT都只有3个小时的有效期,每隔3个小时,EIT-0会被废弃,后继的EIT会取代前继EIT的位置(可以采用修改MGT中PID来实现)。另外,ATSC对EIT的开始时间也有限制。如果一个节目时间跨越了好几个EIT,那么它必须同时出现在这些EIT之中,而且事件ID必须相同。而在DVB标准中,就不存在上述限制。
3. 定向频道转换(DCC):DCC是ATSC标准中的新特性,它在Base PID(0x1FFB)的数据包中定义了2个表:定向频道转换表(DCCT)和定向频道转换选择码表(DCCSCT)。定向频道转换是提供给广播公司的可选功能,它能把用户导航到相关可选的广播节目内容,这种定向转换可以是自动的,也可以是交互式的。而类似的机制并没有出现在DVB系统中。
4. 冗余表:DVB的TS流除了包含当前TS流中SI表之外,还可能包含了其它TS流的SI表,以方便频道搜索。虽然ATSC也允许VCT中包含其它TS流的信息,但一般来说,出于加速访问事件的目的,ATSC的SI表只包含了当前TS流的节目指南信息。
5. 跳过对PAT/PMT的解析:ATSC中TVCT包含了service location descriptor,这个描述符包含了PMT里面的所有信息,包括stream type和PID。也就是说,ATSC的解码器可以跳过PAT和PMT的解析,直接从TVCT的信息跳转到目标逻辑频道。
6. 相关束表(BAT) :DVB中的BAT提供了相关服务的群组列表,方便用户定购和浏览。而ATSC中并没有类似的机制。
● EPG的构造过程
形成电子节目指南(EPG)是SI系统最终目的。参照图 1可以得到如下的EPG构造过程:
1. 调频到某个特定的RF频道。
2. 过滤Base PID,从Base PID的TS数据包中构造MGT,TVCT 和 STT等。
3. 解析MGT,获取EIT以及相关ETT的PID。
4. 把每个虚拟频道和它相关的TV节目列表联系起来。
5. 调频到另一个RF频道,跳转到第2步。如果没有其它的频道,则跳转到第6步。
6. 把所有的节目列表以及相关信息显示给用户,捕捉用户的输入指令,并根据指令查找对应的service location descriptor,解码显示用户所选择的电视节目。
与DVB EPG构造过程的比较:
1. DVB和ATSC结构上的不同,导致了EPG的构造过程的不同。
2. 调频到某个RF频道,基于这个频道解析NIT表,获取当前网络的所有TS流信息。
3. 基于当前频道来解析当前SDT和其它TS流的SDT表,或者扫描当前网络中所有的频道,一一获取SDT信息。
4. 基于当前频道获取当前的EIT和其它TS流的EIT,或者扫描当前网络中所有的频道,一一获取EIT信息。
5. 显示用户节目列表。当用户转换到某个节目,transport id以及相应的各个PID将从PAT和PMT中解析出来,以便解码。
和DVB相比,ATSC-SI的结构试图在不增加网络带宽的前提下,加速事件的处理。为了达到这个目的,ATSC采用了固定的PID,单独的MGT来缩短PID解析的时间。但ATSC采用的机制是否真正比DVB有效,还有待检验。
3 条件接收系统
- PES级别加扰
- 加扰算法
- 事件加扰控制
- 可置换安全接口
4 数据广播
- 数据下载协议
- 数据预告
- 数据发现
- IP协议封装
- 对DSM-CC标准的背离
完。
二、TS流概述
1.、TS流概述
ES流(Elementary Stream,基本流):数字电视各组成部分编码后所形成的直接表示基本元素内容的流,包含视频、音频或数据的连续码流。
PES流(Paketized Elementary Stream,打包基本码流):是将基本的码流ES流根据需要分成长度不等的数据包,并加上包头就形成了打包的基本码流PES流。
PS (Program Stream,节目流):将一个节目的多个组成部分按照它们之间的互相关系进行组织并加入各组成部分关系描述后的码流。PS流是一种多路复用数字音频、视频等的封装容器,它一个或多个具有共同的时间基准的PES流合并成一个整体流,主要用于节目存储。其包长不固定,且较长,一旦失去同步信息,接收机无法确定下一包的同步位置,会造成失步,导致严重的信息丢失。PS流适用于误码小、信道较好的环境,如演播室、家庭环境和存储介质中。
TS流(Transport Stream,传输流):是将一个节目的多个组成部分按照它们之间的互相关系进行组织并加入各组成部分关系描述和节目组成信息,并进一步封装成传输包后的码流。TS流是将视频、音频、PSI等数据打包成传输包进行传送。主要用于节目传输。TS的传输包长度固定,一般为188字节。
TS流和PS流是MPEG-2标准中规定的两种输出码流。TS格式中,从视频流的任意一片断开始都可以独立解码,而PS格式不可以。由于TS流具备较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS流的包格式。
TS流的播放:播放前将TS流文件的后缀名该为.mpg或者.mpeg,用可以直接播放MPEG-TS流的播放器(一般的播放器都可以)打开播放即可。
TS流的优点:
1、动态带宽分配:由于TS的传输包长度是固定的,因此可过PID可以将规定的信道总频带在视频、音频和数据信息见进行实时的、灵活的分配。利用这一特性,可在广播付费节目前实时地将解密钥匙插入到TS流中送给广大用户。
2、可分级性:允许一个复用的传输码流与其他视音频基本码流进行二次复用,生产占用频带给宽的更高一级的TS流。
3、可扩展性
4、抗干扰性
5、接收机成本低廉
TS流的形成过程:
1、 压缩【显示单元】产生【进入单元】,连续的【进入单元】组成一个基本码流。ES
2、 对ES(基本码流)进行打包形成PES。 就是带包头信息的ES
3、 在PES包中加入定时信息(PTS/DTS)。
4、 将PES包内容分配到一系列固定长度的传输包中。
5、 在传输包中加入定时信息(PCR)。
6、 在传输包中加入节目专用信息(PSI) 。
7、 连续输出传输包形成具有恒定比特率的MPEG-TS流。
2、TS流传输包(简称TS包)结构分析
MPEG-2中规定TS传输包的长度为188 字节。但通信媒介会为包添加错误校验字节,从而有了不同于188字节的包长。例如:
DVB 规定中,使用204字节作为包长:
1、通过调制器时,在每个传输包后增加了16 字节的里德所罗门前向纠错码,因而形成了204字节的数据包。
调制 后总存在204 字节的数据包。
2、调制之前存复用器插入RS码或虚构
的RS码。
ATSC规定中,使用208字节作为包长:添加20 字节的 RS(Reed-Solomon)前向纠错码。与DVB不同,ATSC规定RS码只能出现在调制的TS流中。
所有的TS包都分为包头和净荷部分。TS包中可以填入很多东西(填入的东西都是填入到净荷部分),有:视频、音频、数据(包括PSI、SI以及其它任何形式的数据)。
2.1、TS包包头
TS包的包头提供关于传输方面的信息:同步、有无差错、有无加扰、PCR(节目参考时钟)等标志。TS包的包头长度不固定,前32比特(4个字节)固定,后面可能跟有自适应字段(适配域)。32个比特(4个字节)是最小包头。包头的结构固定如下:
因此有的包头可能不止32bit,那么又是如何区分的呢?原来是这个适配区捣的鬼!!
同步字节 | 传输错误指示 | 开始指示 | 传输优先级 | PID | 加扰控制 | 适配域控制 | 连续性计数器 | 适配域 |
8bit | 1 | 1 | 1 | 13 | 2 | 2 | 4 |
注意表里的数据是bit位数
typedef struct TS_packet_header
{
unsigned sync_byte : 8; 同步字节
unsigned transport_error_indicator : 1; 传输错误指示
unsigned payload_unit_start_indicator : 1; 开始指示
unsigned transport_priority : 1; 传输优先级
unsigned PID : 13; PID
unsigned transport_scrambling_control : 2; 加扰控制
unsigned adaption_field_control : 2; 适配域控制
unsigned continuity_counter : 4; 连续性计数器
} TS_packet_header;
sync_byte (同步字节):固定为0100 0111 (0x47);该字节由解码器识别,使包头和有效负载可相互分离。
transport_error_indicator(传输错误指示):
‘1’表示在相关的传输包中至少有一个不可纠正的错误位。当被置1后,在错误被纠正之前不能重置为0。
payload_unit_start_indicator(开始指示):为1时,在前4个字节之后会有一个调整字节,其的数值为后面调整字段的长度length。因此有效载荷开始的位置应再偏移1+[length]个字节。原来有效负载是这样来的
transport_priority(传输优先级):‘1’表明优先级比其他具有相同PID 但此位没有被置‘1’的分组高。
PID:指示存储与分组有效负载中数据的类型。PID 值 0x0000—0x000F 保留。其中0x0000为PAT保留;0x0001为CAT保留;0x1fff为分组保留,即空包。
transport_scrambling_control(加扰控制):表示TS流分组有效负载的加密模式。空包为‘00’,如果传输包包头中包括调整字段,不应被加密。
adaptation_field_control(适配域控制):表示包头是否有调整字段或有效负载。‘00’为ISO/IEC未来使用保留;‘01’仅含有效载荷,无调整字段;‘10’
无有效载荷,仅含调整字段;‘11’ 调整字段后为有效载荷,调整字段中的前一个字节表示调整字段的长度length,有效载荷开始的位置应再偏移[length]个字节。空包应为‘10’。
无有效载荷,仅含调整字段;‘11’ 调整字段后为有效载荷,调整字段中的前一个字节表示调整字段的长度length,有效载荷开始的位置应再偏移[length]个字节。空包应为‘10’。
continuity_counter(连续性计数器):随着每一个具有相同PID的TS流分组而增加,当它达到最大值后又回复到0。范围为0~15。
适配域:
2.2、TS包净荷部分
TS包中净荷所传输的信息包括两种类型:
1、视频、音频的PES包以及辅助数据;
2、节目专用信息PSI。
当然,TS包也可以是空包。空包用来填充TS流,可能在重新进行多路复用时被插入或删除。
在系统复用时,视频、音频的ES流需进行打包形成视频、音频的 PES流,辅助数据(如图文电视信息)不需要打成PES包。PES包非定长,音频的PES包小于等于64K,视频的一般为一帧一个PES包。一帧图象的PES包通常要由许多个TS包来传输。MPEG-2中规定,一个PES包必须由整数个TS包来传输。如果承载一个PES包的最后一个TS包没能装满,则用填充字节来填满;当下一个新的PES包形成时,需用新的TS包来开始传输。
节目专用信息PSI(Program Specific Information)
用来管理各种类型的TS数据包,需要有些特殊的TS包来确立各个TS数据包之间的关系。这些特殊的TS包里所包含的信息就是节目专用信息。在不同的标准中它有不同的名字:
• MPEG-2中称为PSI;
• DVB标准根据实际需要,对PSI扩展,称为SI信息;
• ATSC标准中为PSIP信息
MPEG-2中,规定的对PSI信息的描述方法有以下几种:
• 1、表Table: 节目信息的结构性的描述;
–节目关联表Program Association Table (PAT) 0x0000
–节目映射表Program Map Tables (PMT)
–条件接收表Conditional Access Table (CAT) 0x0001
–网络信息表Network Information Table(NIT) 0x0010
–传送流描述表Transport Stream Description Table (TSDT)
•2、节Section: 将表格的内容映射到TS流中;
专用段 Private_ section
• 3、描述符Descriptor:提供有关节目构成(视频流、音频流、语言、层次、系统时钟和码率等多方面)的信息;
ITU-T Rec.H.222.0|ISO /IEC 13818-1 中定义的 PSI表可被分成一段或多段置于传输流分组中。一段就是一个语法结构,用来将 ITU-T Rec.H.222.0|ISO /IEC 13818-1 中定义的 PSI表映射到传输流分组中。
PAT:
TS流中包含一个或者多个PAT表。PAT表由PID为0x0000的TS包传送,其作用是为复用的每一路传送流提供出所包含的节目和节目编号,以及对应节目的PMT的位置即PMT的TS包的PID值,同时还提供NIT的位置,即NIT的TS包的PID的值。
Table ID | Section syntax indicator | 0 | Section length | Transport stream ID | Version number | Current next indicator | Section Number | Last section number | N loop | ||
8bit | 1 | 1 | 2 | 12 | 16 | 2 | 5 | 1 | 8 | 8 |
N loop中为:
Program number0 | Network PID | …… | Program number1 | Program map PID-1 | …… | CRC 32 | ||
16bit | 3 | 13 | 16 | 3 | 13 | 32 |
table_id:固定为0x00,标志该表是PAT表。
section_syntax_indicator:段语法标志位,固定为1。
section_length:表示这个字节后面有用的字节数,包括CRC32。节目套数:(section
length-9)/4
length-9)/4
transport_stream_id:16位字段,表示该TS流的ID,区别于同一个网络中其它多路复用流。
version_number:表示PAT的版本号。
current_next_indicator:表示发送的PAT表是当前有效还是下一个PAT有效。
section_number:表示分段的号码。PAT可能分为多段传输,第一段为0,以后每个分段加1,最多可能有256个分段。
last_section_number:表示PAT最后一个分段的号码。
Program number:节目号
network_PID:网络信息表(NIT)的PID,节目号为0时对应ID为network_PID。
Program map PID:节目映射表(PMT)的PID号,节目号为大于等于1时,对应的ID为program_map_PID。一个PAT中可以有多个program_map_PID。
CRC_32:32位字段,CRC32校验码Cyclic
RedundancyCheck。
RedundancyCheck。
PMT:
PMT在传送流中用于指示组成某一套节目的视频、音频和数据在传送流中的位置,即对应的TS包的PID值,以及每路节目的节目时钟参考(PCR)字段的位置。
Table id | Section syntax indicator | 0 | Section length | Transport stream id | Version number | Current next indicator | Section number | Last section number | PCR PID | Program info length | N loop descriptors | N loop | CRC 32 | ||||
8 | 1 | 1 | 2 | 12 | 16 | 2 | 5 | 1 | 8 | 8 | 3 | 13 | 4 |
N loop中为:
Stream type | Elementary PID | ES info length | N loop descriptors | ||
8 | 3 | 13 | 4 | 12 |
Table id :固定为0x02,标志该表是PMT表。
Section syntax indicator:对于PMT表,设置为1。
Section length:表示这个字节后面有用的字节数,包括CRC32。
Program number:它指出该节目对应于可应用的Program map PID。
Version number:指出PMT的版本号。
Current next indicator:当该位置’1’时,当前传送的Program map section可用;当该位置’0’时,指示当前传送的Program map section不可用,下一个TS流的Programmap section有效。
Section number:总是置为0x00(因为PMT表里表示一个service的信息,一个section的长度足够)。
Last section number:该域的值总是0x00。
PCR PID:节目中包含有效PCR字段的传送流中PID。
Program info length:12bit域,前两位为00。该域指出跟随其后对节目信息的描述的byte数。
Stream type:8bit域,指示特定PID的节目元素包的类型。该处PID由elementary PID指定。下表所示为对应原始流的类型。
原始流的类型
值 | 描述 |
0x00 | ITU-T|ISO/IEC保留 |
0x01 | ISO/IEC 11172视频 |
0x02 | ITU-T Rec.H.262|ISO/IEC 13818-2视频 |
0x03 | ISO/IEC 11172音频 |
0x04 | ISO/IEC 13818-3音频 |
0x05 | ITU-T Rec.H.222.0|ISO/IEC 13818-1私用分段 |
0x06 | 含有私用数据的ITU-T Rec.H.222.0|ISO/IEC 13818-1分组 |
0x07 | ISO/IEC 13522 MHEG |
0x08 | ITU-T Rec.H.222.0|ISO/IEC 13818-1 DSM CC |
0x09 | ITU-T Rec.H.222.0|ISO/IEC 13818-1/11172-1 |
0x10 ~ 0x7F | ITU-T Rec.H.222.0|ISO/IEC 13818-1保留 |
0x80 ~ 0xFF | 用户私用 |
2.3 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;
注释: 这个结构体并没有显示缺省的bit,注意下面的两个缺省值
stream_type :0x1b // 8
reserved 0x07 // 3
elementary_PID :0x03 e9 // 13//该节目中包括的视频流,音频流等对应的TS分组的PID
reserved :0x0f // 4
ES_info_length :0x000 // 12
//PMT 表结构体 ,这个就是所谓的PMT表了
typedef struct TS_PMT
{
unsigned table_id : 8; //固定为0x02,
表示PMT表
表示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它指出该节目
对应于可应用的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 因为这个表一个section就够了,所以为0
unsigned last_section_number : 8; //固定为0x00 因为这个表一个section就够了,所以为0
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;
}
举例如下: 长度0x12==18字节,红色区域
0x47 0x43 0xe8 0x12 0x00 0x02
0xb0 0x120x00 0x01 0xc1 0x00 0x00 0xe3 0xe9 0xf0 0x00 0x1b 0xe3 0xe9 0xf0 0x00
0xf0 0xaf 0xb4 0x4f 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xb0 0x120x00 0x01 0xc1 0x00 0x00 0xe3 0xe9 0xf0 0x00 0x1b 0xe3 0xe9 0xf0 0x00
0xf0 0xaf 0xb4 0x4f 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
TS头部
sync_byte :0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority : 0x00
PID :0x03e8
transport_scrambling_control :0x00
adaptation_field_control :0x01
continuity_counter :0x02
PMT数据
table_id :0x02 // 8
section_syntax_indicator :0x01 // 1
'0' :0x00 // 1
reserved :0x03 // 2
section_length : 0x012 // 12
program_number :0x00 01 // 16
reserved :0x03 // 2
version_number :0x00 // 5
current_next_indicator 0x01 // 1
section_number :0x00 // 8
last_section_number :0x00 // 8
reserved 0x07 // 3
PCR_PID :0x03 e9 // PCR(节目参考时钟)所在TS分组的PID // 13
reserved :0x0f
//4
//4
program_info_length :0x000 // 12
stream_type :0x1b // 8
reserved 0x07 // 3
elementary_PID :0x03 e9 // 13//该节目中包括的视频流,音频流等对应的TS分组的PID
reserved :0x0f // 4
ES_info_length :0x000 // 12
CRC : 0xf0 af b4 4f
2.4 对TS包头的解析
表标识符值(table_id)的分配
值 描述
0x00 节目关联段(PAT)
0x01 条件接收段(CAT)
0x02 节目映射段(PMT)
0x03 传输流描述段
0x04 至 0x3F 预留
0x40 现行网络信息段(NIT actual)
0x41 其它网络信息段(NIT other)
0x42 现行传输流业务描述段(SDT actual)
0x43 至 0x45 预留使用
0x46 现行传输流业务描述段(SDT other)
0x47 至 0x49 预留使用
0x4A 业务群关联段(BAT)
0x4B 至 0x4D 预留使用
0x4E 现行传输流事件信息段,当前/后续(EIT PF actual)
0x4F 其它传输流事件信息段,当前/后续(EIT PF other)
0x50 至 0x5F 现行传输流事件信息段,时间表(EIT Schedule actual)
0x60 至 0x6F 其它传输流事件信息段,时间表(EIT Schedule other)
0x70 时间-日期段(TDT)
0x71 运行状态段(RST)
0x72 填充段(ST)
0x73 时间偏移段(TOT)
0x74 至 0x7D 预留使用
0x7E 不连续信息段
0x7F 选择信息段
0x80 至 0xFE 用户定义
0xFF 预留
业务信息的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
测量 0x001D
DIT 0x001E
SIT 0x001F
由pid和tid就可以确定并找到那些表了,如果TDT和TOT在同一个PID里,那么就要根据tid进行区分了
根据前一篇中各数据的定义及数据结构,对数据进行分别解析如下:
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;
}
如下为一个TS包数据:
0x47 0x40 0x00 0x12 0x00 0x00
0xb0 0x0d 0x00 0x00 0xc1 0x00 0x00 0x00 0x01 0xe3 0xe8 0xf0 0x0b 0xd7 0x79 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xb0 0x0d 0x00 0x00 0xc1 0x00 0x00 0x00 0x01 0xe3 0xe8 0xf0 0x0b 0xd7 0x79 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
分析知道前四位0x47 0x40 0x00 0x12TS头部即为TS包头数据,解析如下:
sync_byte :0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority : 0x00
PID :0x0000 说明是PAT表
transport_scrambling_control :0x00
adaptation_field_control :0x01
continuity_counter :0x02
PID = 0x0000,表示此TS包的内容为PSI信息表格的PAT表格数据,在4字节的TS包头之后的第一个字节的Point_field = 0x00, 表示偏移量为0,即紧随其后的即为PAT的数据信息
尝试着解析一下这个PAT
table_id:固定为0x00,标志该表是PAT表。 //8bit
section_syntax_indicator:段语法标志位,固定为1。//1
'0' :0x0 // 1
reserved :0x3 //2
section_length:表示这个字节后面有用的字节数,包括CRC32。节目套数:(section length-9)/4
0xd 说明后面有13字节 //12
transport_stream_id:16位字段,表示该TS流的ID,区别于同一个网络中其它多路复用流。 0x0
//16
reserved :0x3 //2
version_number:表示PAT的版本号。0x0 //5
current_next_indicator:表示发送的PAT表是当前有效还是下一个PAT有效。0x1 //1
section_number:表示分段的号码。PAT可能分为多段传输,第一段为0,以后每个分段加1,最多可能有256个分段。 0x0 //8
last_section_number:表示PAT最后一个分段的号码。 0x0 //8
Program number:节目号 0x01 //16
reserved :0x7 //3
network_PID:网络信息表(NIT)的PID,节目号为0时对应ID为network_PID。 0x3e8 //13
Program map PID:节目映射表(PMT)的PID号,节目号为大于等于1时,对应的ID为
program_map_PID。一个PAT中可以有多个program_map_PID。
由于节目号是0x1 因此这里应该是PMT表,而不是NIT表的PID,所以PMT表的PID===0x3e8
注意如果节目号是0x0那么就不需要给PID了,因为NIT表的PID=0x10,所以紧随其后的一定是
下一个节目号。
CRC_32:32位字段,CRC32校验码Cyclic RedundancyCheck。 0xf00bd779//32
2.5TS 流解码过程:
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中的。
ES是直接从编码器出来的数据流,可以是编码过的视频数据流,音频数据流,或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。PES包由包头和payload组成.
在PES层,主要是在PES包头信息中加入PTS(显示时间标签)和DTS(解码时间标签)用于视频、音频同步。其实,Mpeg-2用于视音频同步以及系统时钟恢复的时间标签分别在ES,PES和TS这3个层次中。在ES层,与同步有关的主要是视频缓冲验证VBV(Video Buffer Verifier),用以防止解码器的缓冲器出现上溢或下溢;在PES层,主要是在PES头信息里出现的显示时间标签PTS(Presentation
Time Stamp)和解码时间标签DTS(Decoding Time Stamp);在TS层中,TS头信息包含了节目时钟参考PCR(Program Clock Reference),用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。
Time Stamp)和解码时间标签DTS(Decoding Time Stamp);在TS层中,TS头信息包含了节目时钟参考PCR(Program Clock Reference),用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。
基本流程如下:
首先MPEG-2压缩编码得到的ES基本流,这个数据流很大,并且只是I,P,B的这些视频帧或音频取样信息,然后加入一些同步信息,打包成长度可变长度的数据包PES,原来是流的格式,现在成了数据包的分割形式。同时要注意的是,ES是只包含一种内容的数据流,如只含视频,或只含音频等,打包之后的PES也是只含一种性质的ES,如只含视频ES的PES,只含音频ES的PES等。可以知道,ES是编码视频数据流或音频数据流,每个ES都由若干个存取单元(AU)组成,每个视频AU或音频AU都是由头部和编码数据两部分组成,1个AU相当于编码的1幅视频图像或1个音频帧,也可以说,每个AU实际上是编码数据流的显示单元,即相当于解码的1幅视频图像或1个音频帧的取样。PEG-2对视频的压缩产生I帧、P帧、B帧。把帧顺序I1,P4,B2,B3,P7,B5,B6帧的编码ES,通过打包并在每个帧中插入 PTS/DTS标志,变成PES。在插入PTS/DTS标志时,由于在B帧PTS和DTS相等,所以无须在B帧多插入DTS。而对于I帧 和P帧,由于经过复用后数据包的顺序会发生变化,显示前一定要存储于视频解码器的从新排序缓存器中,经过从新排序后再显示,所以一定要同时插入PTS和 DTS作为从新排序的依据。
其中,有否PTS/DTS标志,是解决视音频同步显示、防止解码器输入缓存器上溢或下溢的关键所在。PTS表明显示单元出现在系统目标解码器(STD- System Target Decoder)的时间, DTS表明将存取单元全部字节从STD的ES解码缓存器移走的时刻。视频编码图像帧次序为 I1,P4,B2,B3,P7,B5,B6,I10,B8,B9的ES,加入PTS/DTS后,打包成一个个视频PES包。每个PES包都有一个包头,用于定义PES内的数据内容,提供定时资料。每个I、P、B帧的包头都有一个PTS和DTS,但PTS与DTS对B帧都是一样的,无须标出B帧的DTS。对I帧和P帧,显示前一定要存储于视频解码器的重新排序缓存器中,经过延迟(重新排序)后再显示,一定要分别标明PTS和DTS。例如,解码器输入的图像帧次序为I1,P4,B2,B3,P7,B5,B6,I10,B8,B9,依解码器输出的帧次序,应该P4比B2、B3在先,但显示时P4一定要比B2、B3在后,即P4要在提前插入数据流中的时间标志指引下,经过缓存器重新排序,以重建编码前视频帧次序I1,B2,B3,P4,B5,B6,P7,B8,B9,I10。显然,PTS/DTS标志表明对确定事件或确定信息解码的专用时标的存在,依靠专用时标解码器,可知道该确定事件或确定信息开始解码或显示的时刻。例如,PTS/DTS标志可用于确定编码、多路复用、解码、重建的时间。
PCR
PCR是TS里面的,即TS packet的header里面可能会有,他用来指定所期望的该ts packet到达decoder的时间,他的作用于SCR类似
DTS, PTS
对于一个ES来说,比如视频,他有许多I,P,B帧,而P, B帧都是以I,P帧作为参考。由于B帧是前向后向参考,因此要对B帧作decode的话,就必须先decode该B帧后面的帧(P,或者I帧),于是,decode的时间与帧的真正的present的时间就不一致了,按照DTS一次对各个帧进行decode,然后再按照PTS对各个帧进行展现。
有时候PES包头里面也会有DTS,PTS,对于PTS来说,他代表了这个PES包得payload里面的第一个完整地audio access unit或者video access unit的PTS时间(并不是每个audio/video access unit都带有PTS/DTS,因此,你可以在PES里面指定一个,作为开始)。
PES包头的DTS也是这个原理,需要注意的是:对于video来说他的DTS和PTS是可以不一样的,因为B帧的存在使其顺序可以倒置。而对于audio来说,audio没有双向的预测,他的DTS和PTS可以看成是一个顺序的,因此可一直采用一个,即可只采用PTS。
2.6TS流分析
1 从TS流开始
数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。
(一) TS流、PS流、PES流和ES流都是什么?
ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。
PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)。
*NOTE:TS流和PS流的区别:TS流的包结构是长度是固定的;PS流的包结构是可变长度的。这导致了TS流的抵抗传输误码的能力强于PS流(TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。而PS包由于长度是变化的,一旦某一 PS包的同步信息丢失,接收机无法确定下一包的同步位置,就会造成失步,导致严重的信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流。)
由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格。
(二) TS流是如何产生的?
从上图可以看出,视频ES和音频ES通过打包器和共同或独立的系统时间基准形成一个个PES,通过TS复用器复用形成的传输流。注意这里的TS流是位流格式(分析Packet的时候会解释),也即是说TS流是可以按位读取的。
(三) TS流的格式是怎样的?
TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了16字节的CRC校验数据,其他格式一样)。整个TS流组成形式如下:
Packet Header(包头)信息说明 | |||
1 | sync_byte | 8bits | 同步字节 |
2 | transport_error_indicator | 1bit | 错误指示信息(1:该包至少有1bits传输错误) |
3 | payload_unit_start_indicator | 1bit | 负载单元开始标志(packet不满188字节时需填充) |
4 | transport_priority | 1bit | 传输优先级标志(1:优先级高) |
5 | PID | 13bits | Packet ID号码,唯一的号码对应不同的包 |
6 | transport_scrambling_control | 2bits | 加密标志(00:未加密;其他表示已加密) |
7 | adaptation_field_control | 2bits | 附加区域控制 |
8 | continuity_counter | 4bits | 包递增计数器 |
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 |
EIT,ST | 0x0012 |
RST,ST | 0x0013 |
TDT,TOT,ST | 0x0014 |
注释:PID相同的表,最后根据tid来区分
下面以一个TS流的其中一个Packet中的Packet Header为例进行说明:
sync_byte=01000111, 就是0x47,这是DVB
TS规定的同步字节,固定是0x47.
TS规定的同步字节,固定是0x47.
transport_error_indicator=0,
表示当前包没有发生传输错误.
表示当前包没有发生传输错误.
payload_unit_start_indicator=0,
含义参考ISO13818-1标准文档
含义参考ISO13818-1标准文档
transport_priority=0, 表示当前包是低优先级.
PID=00111 11100101即0x07e5,
Video PID
Video PID
transport_scrambling_control=00, 表示节目没有加密
adaptation_field_control=01 即0x01,具体含义请参考ISO13818-1
continuity_counte=0010 即0x02,表示当前传送的相同类型的包是第3个
TS流的基本内容就是这些了。
回顾一下,TS流是一种位流(当然就是数字的),它是由ES流分割成PES后复用而成的;它经过网络传输被机顶盒接收到;数字电视机顶盒接收到TS流后将解析TS流。
TS流是由一个个Packet(包)构成的,每个包都是由Packet Header(包头)和Packet Data(包数据)组成的。其中Packet Header指示了该Packet是什么属性的,并给出了该Packet Data的数据的唯一网络标识符PID。
到这里,我们对TS流已经有了一定的了解,下面将从TS流转向PAT表和PMT表的学习。
2 从TS流到PAT、PMT
说完了TS流的基本概念,就该开始对TS流进行更深入的研究了。首先需要想一想:TS流的本质是什么?它的确是一段码流,并且是一段由数据包(Packet)组成的码流。那么这些数据包究竟是怎样的呢?它和我们收看的电视节目之间又有什么区别?这些都是这部分需要了解的内容。
在上一节中,我们可以看到PID这个被标红的字段频繁地出现。PID是当前TS流的Packet区别于其他Packet类型的唯一识别符,通过读取每个包的Packet Header,我们可以知道这个Packet的数据属于何种类型。上一节列出了几项固定的PID值,它们用于识别存储了特殊信息的Packet。下面要谈的PAT表的PID值就是固定的0x0000。
(一) PAT表(Program Association Table,节目关联表)
由于下面的内容比较繁杂,这里先给出一个大纲,方便查阅:
1. PAT表的描述(表格+分析)
2. PAT表的定义(代码+分析)
3. PAT表的结构(代码+分析)
4. PAT表的解析(代码+分析)
5. 通过一段TS流中一个Packet分析PAT表(表格+分析)
下面,开始正式的分析!
1. PAT表的描述(表格+分析)
PAT表定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,要查寻找节目必须从PAT表开始查找。
PAT表携带以下信息:
2. PAT表的定义(代码+分析)
PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据。下面将PAT表的定义给出:
[cpp] view
plaincopy
plaincopy
- typedef struct TS_PAT_Program
- {
- unsigned program_number : 16; //节目号
- unsigned program_map_PID : 13; // 节目映射表的PID,节目号大于0时对应的PID,每个节目对应
- 一个
- }TS_PAT_Program
3. PAT表的结构(代码+分析)
再将PAT表的结构体给出:
[cpp] view
plaincopy
plaincopy
- 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,区别于一个网络中其它多路复用
- 的流 就是这个pat表的PID
- 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 ,如果节目号不为0,则为这
- 个节目的PMT表的PID
- unsigned CRC_32 : 32; //CRC32校验码
- } TS_PAT;
4. PAT表的解析(代码+分析)
接下来给出的是PAT表的解析代码:
[cpp] view
plaincopy
plaincopy
- 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;
- }
从for()开始,就是描述了当前流中的频道数目(N),每一个频道对应的PMT PID是什么。解复用程序需要接收所有的频道号码和对应的PMT
的PID,并把这些信息在缓冲区中保存起来。在后部的处理中需要使用到PMT的 PID。
的PID,并把这些信息在缓冲区中保存起来。在后部的处理中需要使用到PMT的 PID。
5. 通过一段TS流中一个Packet分析PAT表(表格+分析)
这里我们分析一段TS流其中一个Packet的Packet Data部分:
首先给出一个数据包,其数据如下:
分析Packet Header如下表所示:
PID == 0 说明这个是一个PAT表
根据包头数据格式,我们可以知晓整个数据包的属性,列表如下:
sync_byte | 0x47 | 固定同步字节 |
transport_error_indicator | “0” | 没有传输错误 |
payload_unit_start_indicator | “1” | 在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。即上面数据中红色部分不属于有效数据包。 |
transport_priority | “0” | 传输优先级低 |
PID | 0x0000 | PID=0x0000说明数据包是PAT表信息 |
transport_scrambling_control | “00” | 未加密 |
adaptation_field_control | “01” | 附加区域控制 |
continuity_counte | “0000” | 包递增计数器 |
如上表所示,我们可以知道,首先Packet的Packet Data是PAT信息表,因为其PID为0x0000,并且在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1")。这样,Packet Data就应该是“00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff”。
由以上几个表可以分析出PAT表和PMT表有着内在的联系。也就是之前提到的。PAT表描述了当前流的NIT(Network Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号。而PAT表和PMT表到底有什么深层次的联系呢?在讨论完了PMT表和SDT表后再做讨论吧。
6. 过滤PAT表信息的伪代码(代码)
[cpp] view
plaincopy
plaincopy
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- { int I; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } // 如果PID为0x0000,则该Packet Data为PAT信息,
- 因此调用处理PAT表的函数
- else{ // 这里buff+4 意味着从Packet Header之后进行解析
- (包头占4个字节)
- ……
- }
- }
(二) PMT表(Program Map Table,节目映射表)(Service Descriptor Table)
1. PMT表的描述
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。
PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
2. PMT表的定义(代码)
[cpp] view
plaincopy
plaincopy
- <span style="font-size:14px;">//PMT 表定义</span>
[cpp] view
plaincopy
plaincopy
- <span style="font-size:14px;">
- 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;
- </span>
3. PMT表的结构体定义(代码)
[cpp] view
plaincopy
plaincopy
- //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;
4. PMT表的解析(代码)
[cpp] view
plaincopy
plaincopy
- //PMT 表的解析
[cpp] view
plaincopy
plaincopy
- 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;
- }
5. 通过一段TS流中一个Packet分析PMT表(表格+分析)
老样子,还是通过分析一段TS流的数据包Packet来学习PMT表。
下面给出了一段TS流数据中的一个Packet(十六进制数)
首先解析Packet Header,分析如下:
PID=0x03e8为其PID ,怎么就根据这个PID断定他就是PMT呢?难道是PAT中有说明吧。
下面是详细的解析表
Packet Header分析 | |||
Packet Header:0x47 0x40 0x00 0x10 | |||
1 | sync_byte | 0x47 | 固定同步字节 |
2 | transport_error_indicator | “0” | 没有传输错误 |
3 | payload_unit_start_indicator | “1” | 在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。 |
4 | transport_priority | “0” | 传输优先级低 |
5 | PID | 0x03e8 | PID=0x03e8说明数据包是PMT表信息 |
6 | transport_scrambling_control | “00” | 未加密 |
7 | adaptation_field_control | “01” | 附加区域控制 |
8 | continuity_counte | “0010” | 包递增计数器 |
因为payload_unit_start_indicator=‘1’,在解析数据包的时候需要去除Packet Data的第一个字节。下面是对Packet Data的详细解析:
(三) 解复用模型(代码)
[cpp] view
plaincopy
plaincopy
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- {
- int i; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } //PAT表的PID为0x0000
- else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); } //PID指示该数据包为视频包
- else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); } //PID指示该数据包为音频包
- else{ // buff+4 意味着要除去buff前4个字节(即包头)
- for( i=0;i<64;i++)
- { if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; }
- } } }
解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。这样就可以将Video、Audio或者其他业务信息的数据区分开来。
(四) DVB搜台原理以及SDT表(Service Descriptor Table,业务描述表)
机顶盒先调整高频头到一个固定的频率(如498MHZ),如果此频率有数字信号,则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG- 2 decoder。 MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000的Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID。接着循环查找是否出现PMT,如果发现了,则自动进入PMT分析,获取该频段所有的频道数据并保存。如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描。
原来有没有信号是通过判断有没有PAT或者PMT来的啊
在解析TS流的时候,首先寻找PAT表,根据PAT获取所有PMT表的PID;再寻找PMT表,获取该频段所有节目数据并保存。这样,只需要知道节目的PID就可以根据PacketHeade给出的PID过滤出不同的Packet,从而观看不同的节目。这些就是PAT表和PMT表之间的关系。而由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了SDT表来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。(这就是PMT和SDT的联系了)
SDT可以提供的信息包括:
(1) 该节目是否在播放中
(2) 该节目是否被加密
(3) 该节目的名称
3、 从PAT开始,走向更远
在本章的学习中,我们发现了一个特点:所有的TS流的解析都是从寻找PAT表开始的,只有找到了PAT表,我们才能继续下一步的解析。因此,在进行了TS流、PAT表和PMT表的初步知识储备后,在接下来的学习中将从PAT表开始,学习更多的PSI/SI相关的表,将走得更远。
2.7PSI分析
"SI是对多个TS流的描述,它包含了PSI"
PSI只提供了单个TS流的信息,使接收机能够对单个TS流中的不同节目进行解码;但是,它不能提供多个TS流的相关业务,也不能提供节目的类型、节目名称、开始时间、节目简介等信息。因此,DVB对PSI进行了扩展,提供了其他不同类型的表,形成了SI。
SI定义了PAT、PMT和CAT之外的9张表,通过组织SI提供的这些数据,可以生成类似节目报表的形式,方便用户查看多种信息,这就是所谓的电子节目指南(EPG)。
下面给出了SI定义的十张表(NIT的由SI规定的)
NIT | Network Information Table | 网络信息表 | 描述整个网络,如多少TS流、频点和调制方式等信息 |
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 | 选择信息表 | 仅用于码流片段中,如记录的一段码流,包含描述该码流片段业务信息段的地方 | |
DIT | 间断信息表 | 仅用于码流片段,如记录的一段码流中,它将插入到码流片段业务信息间断的地方 |
SI定义的表,并不需要全部传输,其中,NIT、SDT、EIT和TDT是必须传输的;而又以NIT、SDT和EIT最为重要,利用这三个表可以构成功能不同的EPG,如提供节目附加信息、节目分类、节目预定和家长分级控制等。
解析
NIT描述了数字电视网络中与网络相关的信息"
NIT描述了数字电视网络中与网络相关的信息,但这个表本身的信息有限,更多的信息是依靠插入表中的描述符来提供的。NIT常用的描述符有:网络名称描述符(network_name_descriptor)、有线传送系统(cable_delivery_system_descriptor)、业务列表描述符(service_list_descriptor)和链接描述符(linkage_descriptor)。下图是实际的一个例子中的两种描述符。
NIT的表结构分析
网络信息表(NIT)传递了与通过一个给定的网络传输的复用流/TS流的物理结构相关的信息,以及与网络自身特性相关的信息。
网络信息段
Syntax (句法结构) No. of bits(所占位数) Identifier(识别符) Note(注释)
network_information_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
Reserved_future_use 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
Network_id 16 uimsbf
见注释
见注释
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
Reserved_future_use 4 bslbf
Network_descriptors_length 12 uimsbf
for(i=0;i<N;i++){
descriptor() //First descriptor loop
}
reserved_future_use 4 bslbf
transport_stream_loop_length 12 uimsbf
for(i=0;i<N;i++){
transport_stream_id
16 uimsbf 见注释
16 uimsbf 见注释
original_network_id
16 uimsbf 见注释
16 uimsbf 见注释
reserved_future_use 4 bslbf
transport_descriptors_length 12 uimsbf
for(j=0;j<N;j++){
descriptor() //Second descriptor loop
}
}
CRC_32 32 rpchof 见注释
}
在SI标准中规定:original_network_id和transport_stream_id两个标识符相结合唯一确定了网络中的TS流。各网络被分配独立的network_id值作为网络的唯一识别码。当NIT表在生成TS流的网络上传输时,network_id和original_network_id将取同一值。
- NIT表被切分为网络信息段(network_information_section)
- 任何NIT的段都必须由PID为0x0010的TS包传输
- 现行网络的NIT表任何段的table_id值应为0x40,且具有相同的table_id_extension即(network_id);
- 现行网络以外的其他网络NIT表的段table_id值应为0x41
注意到这里出现了两个循环,分别成为第一层循环和第二层循环;每层循环都插入了一个描述符,也就是一共插入了两个描述符。这两个描述符的特点如下:
第一层描述符 | 作用域是针对整个网络的,如插入网络名称描述符、链接描述符等 |
第二层描述符 | 作用域是第一层循环所代表的一个TS流,如插入有线传输系统描述符 |
上图提供的即是一个真实的NIT表,红色框内有两个描述符,分别是业务列表描述符service_list_descriptor和有线传输系统描述符cable_delivery_system_descriptor;它们都位于第二层循环。
NIT的业务列表描述符结构分析
业务列表描述符通过业务标识符和业务类型提供业务服务。其目的在于:告诉接收机每一个TS流中存在多少个业务以及每一个业务的类型。其结构如下:
业务列表描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
service_list_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
for(i=0;i<N;i++){
service_id 16 uimsbf
service_type 8 uimsbf
}
}
service_id(业务标识符):唯一标识TS中的一个业务,它与program_map_section中的program_number(参看PMT表结构)相同。但当业务类型为0x04时(即NVOD参考业务,service_id没有对应的program_number)。
service_type(业务类型):指示业务的类型。如0x01为数字电视业务,0x02为数字音频广播业务,0x04为NVOD参考业务,0x05为NVOD时移业务。
HINT:下表给出了业务类型编码方法
上图所示只是其中一个section的业务列表描述符,如果将所有的section全解析,那么此网络中所有TS流包含的所有service_id和service_type都可以获得。这样,用户可以据此了解整个网络业务的构成,并以此为基础对业务进行分类、整合。
NIT的有线传输系统描述符结构分析
有线传输系统描述符(Cable_Delivery_System_descriptor)是NIT业务中重要的描述符,也是基本运营所必需的描述符。它主要描述了每一个TS流的物理参数(频率、调制方式、符码率等),使接收机能正确锁频、得到数据。其结构如下:
有线传输系统描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
cable_delivery_system_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
frequency 32 bslbf
reserved_future_use 12 bslbf
FEC_outer 4 bslbf 前向纠错外码
modulation 8 bslbf
symbol_rate 28 bslbf
FEC_inner 4 bslbf 前向纠错内码
}
Frequency(频率):32位,以8个4位BCD码给出频率。其单位是MHz,小数点位于第四个BCD码之后,如0312.0000MHz。
Modulation(调制方式):指出有线传输系统的调制方式。其定义如下表:
symbol_rate(符码率):28位,以7个4位BCD码表示符码率。其单位是MSPS(兆符号每秒),小数点位于第三个BCD码后,如027.4500
MSPS
MSPS
上图所示为一个真实TS流里的一个有线传输系统描述符。可以看到其频率为331.0000MHz,符码率为6.8750MSym/s,调制方式为64 QAM。
NIT的网络名称描述符结构分析
网络名称描述符的功能比较简单,它以字符串的形式将网络的名称告诉接收机。下面是其描述符结构列表:
网络名称描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
network_name_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
for(i=0;i<N;i++){
char 8 uimsbf
}
}
char(字符):8位字段,一个字符串,给出NIT表指定的传送系统的名称。
NIT的链接描述符结构分析
ANALYZEOFTHE STRUCTUREOFLINKAGE_DESCRIPTORINNIT
链接描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
linkage_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
transport_stream_id 16 uimsbf
original_network_id 16 uimsbf
service_id 16 uimsbf
linkage_type 8 uimsbf
for(i=0;i<N;i++){
private_data_byte 8 bslbf
}
}
如果用户要求得到SI系统描述的特定实体的进一步信息,则可以使用链接描述符。链接描述符包含的信息给出了该实体的位置。如:可以在一个NIT中使用一个链接描述符,给出网络中可存放业务附加信息的位置;或者在BAT表中的链接描述符可链接到业务群的相关信息等。
使用链接描述符,也可以提供一种业务,以替换CA系统的功能。如果CA系统不能处理相应功能的时候,接收机就可以用这个业务来取代CA。
使用链接描述符,还可以以一种备用业务,用以替换主业务。若主业务的运行状态被置为“not_running”,那么,接收机可以自动选择被链接描述符指引的备用业务,以取代该业务。
使用链接描述符,还可以向移动接收机提供漫游功能。若现行业务其service_id下无法使用,IRD可以自动选择该功能。hand-over_type指明link_descriptor是否链接到不同国家的同一业务,或者链接到本地变更业务或关联业务。
总之,链接描述符为运营商提供了一种业务扩充功能,使得运营商可以更加灵活地组合各种业务。
NIT的软件下载链接描述符结构分析
软件下载描述符用于运营商在网络内进行多于一个机顶盒的软件下载更新标识,在网络信息表NIT的第一个循环内,并且可能多次出现。针对同一组制造商号(manufacturer_id)、硬件类型(hw_model)、硬件版本(hw_version)和软件类型(sw_model),可能有不只一组的不同软件版本(sw_version)和开始时间(start_time)参数,并可能出现在同一个或多个链接描述符(linkage_descriptor)的各个循环内。
解析
"BAT将网络中的所有业务分成了多个业务群,以此界定用户"
BAT即业务群关联表(BouquetAssociation Table),它将网络中所有的业务分成了多个业务群,以此界定用户。例如,将网络中所有业务分为两个业务群,一个是境内节目业务群,另一个是境外节目业务群。这样,国内的运营商就可以利用这样划分的业务群,充分利用节目资源,在不违反现有广电总局规定的前提下,同时分别满足境内用户和境外用户。
BAT本身可以跨网络存在,但在国内运营体系来看几乎没有得到真正使用。国内的运营使用中,BAT还可以存在分级运营的运营体系中,用于区分不同的地域用户。
BAT被切分为业务群关联段,所有的业务群关联段都在TS中传输,其PID为0x0011。所有的业务群关联段的table_id都取0x4A。
HINT:SDT的TS包PID也为0x0011,其table_id为0x42或0x46。
BAT的表结构分析
下面给出了BAT表的段结构:
业务群关联段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
bouquet_association_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
Reserved_future_use 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
bouquet_id 16 uimsbf 见注释
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
Reserved_future_use 4 bslbf
bouquet_descriptors_length 12 uimsbf
for(i=0;i<N;i++){
descriptor()
}
reserved_future_use 4 bslbf
transport_stream_loop_length 12 uimsbf
for(i=0;i<N;i++){
transport_stream_id 16 uimsbf 见注释
original_network_id 16 uimsbf 见注释
reserved_future_use 4 bslbf
transport_descriptors_length 12 uimsbf
for(j=0;j<N;j++){
descriptor()
}
}
CRC_32 32 rpchof 见注释
}
注意到BAT的结构和NIT类似,其作用和描述符的使用也与NIT大致雷同。其中,可以插入传输系统描述符、业务列表描述符等。这些表和NIT中的表已介绍,这里不再赘述。
BAT的业务群名称描述符结构分析
下面给出的是业务群名称描述符,它以文本方式给出了业务群的名称:
业务群名称描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
bouquet_name_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
for(i=0;i<N;i++){
char
8 uimsbf
8 uimsbf
}
}
char(字符):8位字段,用于描述BAT子表叙述的节目业务群名称
业务名称描述符和网络名称描述符类型,只是作为业务群的一个代号,其实际意义只存在于用户界面上,可以给用户一个良好的操作体验。
SDT解析
"SDT描述了业务内容及信息,连接了NIT与EIT和PMT(PSI)"
SDT即服务描述表(Service Description Table),它描述了一个业务中的内容以及信息,它承上启下,以transport_stream_ID连接了NIT和EIT;SDT的servicID必须与PMT中的Program_no一致,因此,SDT又与PSI的信息连接。
对于一般的音视频业务,SDT表中不会有太多的信息需要添加。如果是其他业务(如NVOD、马赛克、数据广播等),在SDT中需要插入相应业务的描述符。
SDT的表结构分析
SDT表被切分成业务描述段(service_description_section),由PID为0x0011的TS包传输(BAT段也由PID为0x0011的TS包传输,但table_id不同)。
描述现行TS(即包含SDT表的TS)的SDT表的任何段的table_id都为0x42,且具有相同的table_id_extension(transport_stream_id)以及相同的original_network_id。
指向非现行TS的SDT表的任何段的table_id都应取0x46。
下面给出了SDT的业务描述段:
业务描述段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
service_description_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
Reserved_future_use 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
transport_stream_id 16
uimsbf 给出TS识别号
uimsbf 给出TS识别号
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
original_nerwork_id 16 uimsbf 见注释
reserved_future_use 8 bslbf
for(i=0;i<N;i++){
service_id 16 uimsbf 见注释
reserved_future_use 6 bslbf
EIT_schedule_flag 1 bslbf 见注释
EIT_present_following_flag 1 bslbf 见注释
running_status
3 uimsbf 见下面分析
3 uimsbf 见下面分析
freed_CA_mode 1 bslbf 见注释
descriptors_loop_length 12 uimsbf
for(j=0;j<N;j++){
descriptor()
}
}
CRC_32 32 rpchof 见注释
}
running_status(运行状态):表示业务状态,下表给出了业务状态
值 | 含义 |
0 | 未定义 |
1 | 未运行 |
2 | 几秒后开始(如录像) |
3 | 暂停 |
4 | 运行 |
5~7 | 预留 |
SDT是描述一个TS流中所有业务信息的一张表,重要的字段包含transport_stream_id,明确这些业务是属于哪个TS流的;另一个重要字段时候service_id,这是作为频道索引信息存在的。
SDT的业务描述符分析
业务描述符与业务类型一起,以文本形式给出业务提供者的名称和业务名称,如下表:
业务描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
service_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
service_type 8 uimsbf
service_provider_name_length 8 uimsbf
for(i=0;i<N;i++){
Char 8 uimsbf
}
service_name_length 8 uimsbf
for(i=0;i<N;i++){
Char 8 uimsbf
}
}
service_type(业务类型):这里的业务类型和NIT中的业务列表描述符中的service_type编码是一致的:
service_type | 描述 |
0x00 | 预留使用 |
0x01 | 数字电视业务 |
0x02 | 数字音频广播业务 |
0x03 | 图文电视业务 |
0x04 | NVOD参考业务 |
0x05 | NVOD时移业务 |
0x06 | 马赛克业务 |
0x07 | PAL制编码信号 |
0x08 | SECAM制编码信号 |
0x09 | D/D2-MAC |
0x0A | 调频广播 |
0x0B | NTSC制信号 |
0x0C | 数据广播业务 |
0x0D | 公共接口使用预留 |
0x0E | RCS映射(见EN 301 790) |
0x0F | RCS FLS(见EN 301 790) |
0x10 | DVB MHP业务 |
0x11~0x7F | 预留使用 |
0x80~0xFE | 用户定义 |
0xFF | 预留使用 |
业务描述符是SDT中最重要的描述符,也是运营商中必须的描述符。其中的service_type描述这个业务类型的域;service_name描述频道名。
SDT的实例分析
这里主要演示SDT如何连接PSI和NIT。
首先,根据SDT描述符的service_id可以和PMT中的program_number建立连接。如图所示,SDT的service_id=201=0xc9,与PMT中的program_number一致。
上图所示,根据SDT的transport_stream_id和NIT的transport_stream_id,可以将SDT和NIT联系起来。根据上图的信息,我们可以根据SDT获取该业务(频道)的业务列表信息和有线传输信息(位于NIT中)。
EIT解析
"EIT按时间顺序提供每一个业务所包含的事件信息"
EIT即事件信息表(Event Information Table),它是EPG中绝大部分信息的携带者。事实上,EPG主要就是通过SDT和EIT信息的获取和重组实现的。SDT只提供了频道信息,而EIT则提供各频道下的所有节目的信息。
EIT的主要信息也是通过插入的描述符来实现的。EIT按照时间顺序提供每一个业务所包含的事件的相关信息(如节目名称、节目简介)。
按照不同的table_id,一共有四类EIT。其中,当前TS流的所有EIT子表都有相同的transport_stream_id和original_stream_id。下图给出了一个实例的码流中的EIT信息,这里一共有三类EIT表,观察其table_id就可以知道它们代表什么类型的EIT
第一个EIT_PF_Actual应该是当前TS流的“当前/后续事件信息”;第二个EIT_PF_Other是其他TS流的“当前/后续事件信息”;第三个EIT_Schedule_Actual是当前TS流的“事件时间表信息”。
EIT的表结构分析
EIT表被切分成事件信息段。任何构成EIT的段都由PID=0x0012的TS包传输。下面给出的是事件信息段的结构:
事件信息段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
event_information_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
Reserved_future_use 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
service_id 16
uimsbf 与PAT中的program_number一致
uimsbf 与PAT中的program_number一致
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
transport_stream_id 16 uimsbf
见注释
见注释
original_nerwork_id 16 uimsbf 见注释
segment_last_section_number 8 uimsbf 见注释
last_table_id 8 uimsbf 见注释
for(i=0;i<N;i++){
event_id
16 uimsbf 事件(节目)id
16 uimsbf 事件(节目)id
start_time
40 bslbf 事件(节目)开始时间
40 bslbf 事件(节目)开始时间
duration 24 bslbf 事件(节目)持续始时间
running_status
3 uimsbf 见注释
3 uimsbf 见注释
freed_CA_mode 1 bslbf 见注释
descriptors_loop_length 12 uimsbf
for(j=0;j<N;j++){
descriptor()
}
}
CRC_32 32 rpchof 见注释
}
start_time(开始时间):40位字段,指的是事件(节目)的开始时间,它包含以UTC和MJD形式表示的事件(节目)的起始时间及日期。此字段前16位表示MJD日期码,其余24位按4位BCD编码,表示6个数字。例如:93/10/13
12:45:00被编码为:“0xc079124500”
12:45:00被编码为:“0xc079124500”
HINT:如果事件起始时间未定,则所有位都置为“1”,例如对NVOD业务的一个事件。
duration(持续时间):24位字段,表示事件的持续时间,以时、分、秒的格式表示,即由6个4位BCD码显示。如:01:45:30被编码为“0x14530”。
running_status(运行状态):参看SDT表解析部分。
下面给出一个具体的例子:
EIT主要承载的信息就是Event的相关内容,即节目信息。其中,节目所在频道(service_id)、开始时间(start_time)、结束时间(start_time+duration)可在EIT子表获取;而节目描述、节目名称、节目分类等信息就要在插入EIT的描述符中获取了。
EIT的短事件描述符分析
短事件描述符(Short_event_descriptor)以文本方式提供了事件名称和该事件的简短描述。描述符结构如下:
短事件描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
short_event_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
ISO_639_language_code 24 bslbf
event_name_length 8 uimsbf
for(i=0;i<event_name_length;i++){
event_name_char
8 uimsbf
8 uimsbf
}
text_length 8 uimsbf
for(i=0;i<text_length;i++){
text_char
8 uimsbf
8 uimsbf
}
}
ISO_639_language_code(ISO
639-2语言代码):24位字段,指明后续文本字段的语言。该字段包含一个由ISO 639-2定义的3字符代码。ISO 639-2/B和ISO 639-2/T均可使用。每个字符按照GB/T15273.1-1994编码为8位,并依次插入24位字段。如:法语的3字符代码“fre”,可编码为:“0110 01100111 0010 0110 0101”。
639-2语言代码):24位字段,指明后续文本字段的语言。该字段包含一个由ISO 639-2定义的3字符代码。ISO 639-2/B和ISO 639-2/T均可使用。每个字符按照GB/T15273.1-1994编码为8位,并依次插入24位字段。如:法语的3字符代码“fre”,可编码为:“0110 01100111 0010 0110 0101”。
event_name_char(事件名称字符):一个字符串给出事件的名字。
text_char(文本字符):一个字符串给出事件的文本描述。
下面给出的是一个实际案例的短时间描述符,可见中文的3字符代码是“chi”。
EIT的扩展事件描述符分析
扩展事件描述符(Extended_Event_Descriptor)拓展事件描述符给出了一个事件的详细文本描述。如果一个事件的信息长度超过256字节,可以使用多于一个相关联的扩展事件描述符来描述。文本信息可以分为两个栏目,一栏为条目的描述,另一栏为条目的内容。
下面给出了扩展事件描述符的结构。
扩展事件描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
extended_event_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
descriptor_number 4 uimsbf
last_descriptor_number 4 uimsbf
ISO_639_language_code 24 bslbf
length_of_items 8 uimsbf
for(i=0;i<N;i++){
item_description_length 8 uimsbf
for(j=0;j<N;j++){
item_description_char
8 uimsbf
8 uimsbf
}
item_length
for(j=0;j<N;j++){
item_char
}
}
text_length
for(i-0;i<N;i++){
text_char
}
}
下面给出的是一个实例截图:
EIT的内容描述符分析
内容描述符的目的是为事件提供清晰的信息描述符。根据这个描述符的信息,接收机可以清晰地知道事件的分类,并告知观众。下面给出了内容描述符的结构:
内容描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
content_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
for(i=0;i<N;i++){
content_nibble_level1
4 uimsbf
4 uimsbf
content_nibble_level2
4 uimsbf
4 uimsbf
user_nibble 4 uimsbf
user_nibble 4 uimsbf
}
}
这里主要需要介绍的就是 content_nibble_level1和content_nibble_level2:根据EN
300 468V1.3.1(1998-02)中的Table 18可以确定该节目的具体分类。
300 468V1.3.1(1998-02)中的Table 18可以确定该节目的具体分类。
下面给出了一个实例。
TDT解析
"TDT仅传送UTC时间和日期信息,只有一个段"
TDT为时间和日期表(Time&Date Table),它仅传送UTC时间和日期信息。并且TDT仅包含一个段,其结构如下:
时间和日期段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
time_date_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
Reserved_future_use 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
UTC_time 40 bslbf
见注释
见注释
}
UTC_time(UTC时间):40位字段,包含以UTC和MJD形式表示的当前时间和日期。此字段前16位为MJD日期码,后24位按4位BCD编码,表示6个数字。如:93/10/13
12:45:00被编码为“0xC079124500”。
12:45:00被编码为“0xC079124500”。
下图给出了一个实例:
TOT解析
"TOT是TDT的一个扩展,增加了一个描述符"
TOT为时间偏移表(Time Offset Table),它包含了UTC时间和日期信息及当地时间偏移。传输此表的TS包PID为0x0014,table_id=0x73。下面给出了时间偏移段的结构:
时间偏移段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
time_offset_section(){
table_id 8 uimsbf
section_syntax_indicator 1 bslbf 通常设为“1”
reserved_future_use 1 bslbf
reserved 2 bslbf
section_length 12 uimsbf 见注释
UTC_time 40 bslbf
见注释
见注释
reserved 4 bslbf
descriptors_loop_length 12 uimsbf
for(i=0;i<N;i++){
descriptor()
}
CRC_32 32 rpchof 见注释
}
这里的UTC_time和TDT表是一致的,都是以UTC和MJD形式表示当前时间和日期;其格式也与TDT的UTC_time相同,这里不再赘述。需要特别注意的是这里的描述符descriptor()。下面给出了本地时间偏移描述符的结构:
本地时间偏移描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
local_time_offset_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
for(i=0;i<N;i++){
coutry_code 24 bslbf
country_region_id 6 bslbf
reserved 1 bslbf
local_time_offset_polarity 1 bslbf
local_time_offset 16 bslbf
time_of_change 40 bslbf
next_time_offset 16 bslbf
}
}
国家代码 country_code
24位字段,按照ISO 3166用3字符代码指明国家。每个字符根据GB/T
15273.1-1994编码为8位,并依次插入24位字段。假设3个字符代表了一个900至999的数字,那么country_code指定了一组ETSI定义的国家。其分配见ETR 162。国家组的国家代码应该被限制在同一时区内。
15273.1-1994编码为8位,并依次插入24位字段。假设3个字符代表了一个900至999的数字,那么country_code指定了一组ETSI定义的国家。其分配见ETR 162。国家组的国家代码应该被限制在同一时区内。
例如:英国由3字符代码“GBR”表示,编码为:“01000111 0100 0010 0101 0010”。
国家区域标识符 country_region_id
6位字段,表示country_code指明的国家所在的时区。若国家内部里没有时差,则置“000000”。
本地时间偏移极性 local_time_offset_polarity
1位字段,用于指明随后的local_time_offset的极性。置“0”时,极性为正,说明本地时间早于UTC时间(通常在格林威治以东);置“1”时,极性为负,说明本地时间晚于UTC时间。
本地时间偏移 local_time_offset
16位字段,指出由country_code和country_region_id确定的区域的相对于UTC的时间偏移,范围为-12小时至+13小时。16比特含有4个4位BCD码,顺序为小时的十位,小时的个位,分的十位,分的个位。
时间变化 time_of_change
40位字段,指明时间改变时当前的日期(MJD)与时间(UTC),见附录C。该字段分为两部分,前16位给出了LSB格式的日期(MJD),后24位给出了UTC时间(6个4位BCD码)。
下一时间偏移 next_time_offset
16位字段,指出由country_code和country_region_id确定的区域,当UTC时间变化时的下一个时间偏移,范围为-12小时至+13小时。此16比特域为4个4位BCD码,依次为时的十位,时的个位,分的十位,分的个位。
小结
SI
1) SDT(业务描述表):描述了包含在特定TS流中的全部业务的相关信息。
业务描述表包含了描述系统中业务的数据,例如业务名称、业务提供者等。
2) EIT(事件信息表):描述了包含在特定业务中的所有事件的相关信息。
事件信息表包含了与事件或节目相关的数据,例如事件名称、始时间、持续时间等。
不同的描述符用于不同类型的事件信息的传输,例如不同的业务类型。
3) TDT(时间和日期表):时间和日期表给出了与当前的时间和日期相关的信息。
由于这些信息频繁更新,所以需要使用一个单独的表。
4) BAT (业务群关联表) 给出业务群的名称及其业务列表等信息
5) RST(运行状态表)
6) TOT(时间偏移表) 给出了当前时间日期与本地时间偏移的信息
7) ST (填充表)
8) SIT(选择信息表)
9) DIT (间断信息表)
参考资料
《数字电视业务支撑系统》 | 廖洪涛、徐征著 | 电子工业出版社 |
DVB和MPEG-II中的表格 | 网络资料 | —— |
《中文SI版本1.0》 | 行业标准 | —— |
预备知识1:PSI/SI关键字
- HINT:上表中,绿色底纹的为PSI信息,蓝色底纹的为SI信息。但是,虽然NIT是PSI信息,但NIT标准是由SI规定的。
预备知识2:常识
PRE-LEARNING2
- 生成EPG的数据来源于DVB码流中的业务信息(SI)和节目引导信息(PSI)
- 视频、音频、图片、文字等处理后成为数据,以MPEG-2标准打包成为固定长度(188字节)的传送包,并将这些包复用形成传输流(TS流)
- 通常,一个频道对应一个TS流,一个频道的TS流由多个节目及业务组成
预备知识3:ES、PES、PS和TS
PRE-LEARNING3
ES流(ElementaryStream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。
PS流(ProgramStream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(TransportStream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)。
不论是视频、音频、文字、图片还是业务信息(包括PSI/SI)都将被划分为最基本的ES,并将它们通过PES的形式组装起来,经过复用形成TS流
对TS流的解析:首先获取到TS流中的包Packet,根据其PID过滤出需要的包,根据其section的结构还原成section,再由上层协议来处理。
预备知识4:业务与事件
PRE-LEARNING4
关于业务(Service)与事件(Event)。
这里将会大量出现这两个词。那么什么是“业务”,什么是“事件”呢?一般来说,接下来所说的“业务”就是指“频道”,“事件”就是“节目”。这里需要明确“节目”的概念,举个例子:CCTV1是一个频道,也就是我们所说的“业务(Service)”;《新闻联播》是一个节目,也就是我们所说的“事件”。分清楚了这两个概念,我们才能区分下面的很多SI信息。
"PSI是对单一TS流的描述,是TS流中的引导信息"
PSI信息由节目关联表PAT、条件接收表CAT、节目映射表PMT和网络信息表NIT组成。这些表会被插入到TS流中。PSI信息是对单一TS流的描述,它是TS流的引导信息;PSI信息指定了如何从一个携带多个节目的传输流中找到指定的节目(见Q1)。
下面给出的是节目引导信息(或称节目特定信息,PSI)的四个表结构。
虽然这四个结构可以视为简单的表,但事实上,它们被进一步划分为各个段,并插入到TS包中
PAT解析
PARSINGOF PAT
"PAT是机顶盒接收的入口点,是它获取数据的开始"
节目关联表PAT的意义在于,它描述了当前TS流中包含了哪些PID;只有根据获得的PID,用户才可以以此作为凭据找出其他表(如PMT表)及其信息。所以PAT是机顶盒接收的入口点,是它获取数据的开始;要保证一个TS流能被正常接收,则至少要有一个完整有效的PAT。
下面给出了PID字段的取值要求:
值 | 描述 |
0x0000 | PAT |
0x0001 | CAT |
0x0002~0x000F | 保留 |
0x0010~0x1FFE | 可赋给network_PID、Program_map_PID、elementary_PID或作其他用途 |
0x1FFF | 空的分组 |
PAT的表结构分析
首先,对PAT的表结构进行分析。下面给出了PAT表的基本结构:
节目关联段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
program_association_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
"0" 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
transport_stream_id 16 uimsbf 见注释
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
for(i=0;i<N;i++){
program_number 16 uimsbf
见注释
见注释
reserved 3 bslbf
if(program_number == 0){
network_PID 13
uimsbf 见注释
uimsbf 见注释
}
else{
program_map_PID 13 uimsbf 见注释
}
}
CRC_32 32 rpchof 见注释
}
这里我们注意关注五个字段:
table_id(赋值):PAT的table_id应为0x00
transport_stream_id(传输流标志):用以标识来源于网络中任何其他复合流的TS流
program_number(节目号):规定program_map_PID可适用的节目。当值为0x0000时,其后的PID参照将是网络PID。它可以作为一个指示符号,例如用于广播通道。
network_PID(网络PID):仅当program_number为0x00时使用
program_map_PID(节目映射PID):据此找出相应的PMT表
PAT的实例分析
借助码流分析仪,我们可以很清楚地以实例的方式更加清晰地了解PAT的结构。下面给出的就是一个TS流的PAT分析情况:
根据上表可以看出,这个TS流里共有6套节目,其映射关系PID分别为0xc9、0xcb、0xce、0xcc、0xcd和0xca。(注:0xc9=201)
下面给出的是PMT的简单解析:
两相比较,可以很清楚地知道,PAT是如何锁定各个PMT的(每个节目都有一个自己的PMT)。
Q1:如何找到一个特定的节目?
1.已知节目名称(或id等信息)
P248
首先要从节目关联表PAT中获取这个节目的节目映射表PMT的PID值,再从TS流中找出与此PID值相对应的节目映射表PMT;再从PMT中获取构成这个节目的基本码流的PID值,从而根据该PID过滤出相应的视频、音频和数据等基本码流。最后解码恢复为原始信号。
PMT解析
"PMT是连接节目号与节目元素的桥梁"
节目映射表PMT的意义在于,它给出了节目号与组成这个节目元素之间的映射;也就是说,PMT是连接节目号与节目元素的桥梁。我们知道,一个电视节目至少包含了视频和音频数据,而每一个节目的视音频数据都是以包的形式在TS流中传输的;所以说,一个TS流包含了多个节目的视频和音频数据包。要想过滤出一个TS流中其中一个节目的视频和音频,则需要知道这个节目中视频和音频的标识号PID。PMT表的作用就在于,它提供了每个节目视频、音频(或其他)数据包的PID。
PMT的表结构分析
先给出PMT表的段结构:
Syntax (句法结构) No. of bits(所占位数) Identifier(识别符) Note(注释)
program_map_section(){
table_id
8 uimsbf
8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
"0" 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
program_number 16
uimsbf
uimsbf
Reserved 2 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
reserved 3 bslbf
PCR_PID 13 uimsbf 见注释
reserved 4 bslbf
program_info_length 12 uimsbf 头两位为"00"
for(i=0;i<N;i++){
descriptor()
}
for(i=0;i<N1;i++){
stream_type 8 uimsbf
见注释
见注释
reserved 3 bslbf
elementary_PID 13 uimsbf
见注释
见注释
reserved 4 bslbf
ES_info_length 12 uimsbf 头两位为"00"
for(j=0;j<N2;j++){
descriptor();
}
}
CRC_32 32 rpchof 见注释
}
这里着重介绍两个字段:
elementary_PID:标识了该节目(program_number)携带的基本流的识别号PID
stream_type:流类型,指出了PID为elementary_PID的基本流的类型
附:流类型取值说明
取值 | 描述 |
0x00 | 国际标准保留 |
0x01 | 视频 |
0x02 | 视频或受限参数视频流 |
0x03 | 音频 |
0x04 | 音频 |
0x05 | private_sections |
0x06 | 包含专用数据的PES分组 |
0x07 | ISO/IEC 13533 MHEG |
0x08 | |
0x09 | ITU-T Rec.H.222.1 |
0x0A~0x0D | GB/T类型 |
0x0E | GB/T辅助 |
0x0F~0x7F | GB/T保留 |
0x80~0xFF | 用户专用 |
PMT的实例分析
下面给出了码流分析仪分析一段TS流的PMT表:
首先,注意到这张PMT表的progam_number=0xc9,就是我们在上面解析PAT表得到的。
从上面可以知道,节目号为0xc9(即201)的节目有两个单元流:
流名称 | 标识号 | 类型 | 说明 |
MPEG2视频 | ES PID=0x21 | stream_type=0x02 | ES PID即为elementary_PID |
MPEG1音频 | ES PID=0x28 | stream_type=0x03 | ES PID即为elementary_PID |
elementary_PID和stream_type的意义在于,告知接收机基本流的类型和识别PID。要想获取这个节目的视频,只需要对PID为0x21的数据包进行过滤,并交由视频处理模块处理。下图所示(除了节目名称,还需要SDT表信息)即为PAT和PMT共同获取的信息。
CAT解析
PARSINGOF CAT
"CAT描述了节目的加密方式"
条件接收表CAT描述了节目的加密方式,它包含了节目的EMM注1识别PID。它给出了一个或多个CA系统、EMM流以及与CA相关的特定参数之间的关系。
注1:EMM(Entitlement Management
Message),即授权管理信息。描述了用户可看的节目或时间段、用户的付费等。
Message),即授权管理信息。描述了用户可看的节目或时间段、用户的付费等。
CA描述符既用于规定像EMM这样的系统范围条件接收管理信息,也用于规定像ECM这样的基本流特定信息。
- 如果一个基本流(Elementary Stream)是加扰的,那么包含该基本流的节目信息PMT中需要一个CA描述符
- 如果一个TS流中有任何一个系统范围的条件接收管理信息,则条件接收表中应有CA描述符。
下图给出的PMT表即是基本流加扰的情况,因此PMT表需要有CA描述符:
CAT的表结构分析
条件存取段
Syntax(句法结构) No. ofbits(所占位数) Identifier(识别符) Note(注释)
conditional_access_section(){
table_id 8 uimsbf
Section_syntax_indicator 1 bslbf 通常设为“1”
"0" 1 bslbf
Reserved 2 bslbf
Section_length 12 uimsbf 见注释
reserved 18 bslbf
Version_number 5 uimsbf 见注释
Current_next_indicator 1 bslbf 见注释
Section_number 8 uimsbf 见注释
last_section_number 8 uimsbf 见注释
for(i=0;i<N;i++){
descriptor()
}
CRC_32 32 rpchof 见注释
}
条件存取段主要作用是指示本TS流中存在加扰节目,并且在描述符循环中必须插入CA描述符来作进一步指引。下面给出的是条件接收的描述符
条件接收描述符
条件接收描述符
Syntax(句法结构) No. of bits(所占位数)Identifier(识别符)
CA_identifier_descriptor(){
descriptor_tag 8 uimsbf
descriptor_length 8 uimsbf
CA_system_ID 16
uimsbf
uimsbf
reserved 3 bslbf
CA_PID 13
uimsbf
uimsbf
for(i=0;i<N;i++){
private_data_byte 8
}}
CA_system_ID(CA系统ID):指出用于相关ECM及(或)EMM流的CA系统类型。
CA_PID(CA
PID):指出包含用于相关CA_system_ID所规定的CA系统的ECM或EMM信息传输流分组的PID(即在TS流中过滤出相应PID的分组)。
PID):指出包含用于相关CA_system_ID所规定的CA系统的ECM或EMM信息传输流分组的PID(即在TS流中过滤出相应PID的分组)。
下面给出的TS流中并没有CAT:
但在PMT表中有CA描述符:
PSI小结
上图给出了在一个TS流中PSI各表之间的联系。首先从PAT表出发,获取当前有哪些节目号(program_map_PID),再根据这些节目号找出program_number相同的PMT;获取到PMT后,即可根据elementary_PID和stream_type来确定要过滤哪些含有基本流的TS包,以及这些包里面是什么类型的数据。这样,机顶盒就可以过滤出相应的节目(视频和音频数据)来收看了。
2.8Tuner数据处理流程
大概流程:
1.cable ts
信号(signel)--》tuner--》demux-》video,audio解码编码-》输出显示
2.本地ts
file--》demux-》video,audio解码编码-》输出显示
如果有解扰逻辑,则需要在demux后对video,audio包解扰处理。
3.对cable ts 的录制即:
信号(signel)--》tuner--》demux-》磁盘--》demux--》video,audio解码编码-》输出显示
即多了一个步骤,需要将解扰的流保存到磁盘中,如果需要播放磁盘中的文件,则将磁盘中的文件数据传给demux,也就是一个逆向过程。
当然demux的处理牵涉到section,filter另做讨论。
以下将分节讨论:
1.TUNER 调谐器
一般有有线cable,卫星satellite,地面territory 3种type
tuner工作需要知道频率freq,符号率symb,QAM大小,tuner获取到的信号signel有信号强度,误码率,质量,等级等,主要就是锁住某个需要的频点去获取信息(模拟信号-》数字信号)。一般问题就是锁不住频点,拿不到signel。
目前一般是STB 的tuner某一时刻只能处理一个频点信息,也许以后会启动同时处理多个频点或多个tuner的双xx模式或多xx模式。终端屏幕的越来越大,完全是可以在一个屏上显示多个流的。
2.Demux
终端主要是解复用,也就是解析自己想要的数据,并分类的处理。这块需要专业的MPEG/DVB相关协议知识,主要是PSI/SI。
一般牵涉到的概念有filter,channel,section,pid等,一般来说一个Channel对应一个PID,对应多个Filter。
首现要知道频点,需要tuner到某个频点上去锁频,将模拟数据转换为数字信号。
然后数字信号会送到Demux解复用,这一坨数据很多,你需要通过一定的规则去获取你想要的数据。
filter过滤用,就是tuner拿到的那一坨数据有很多,但只需要将你想要的数据过滤出来。怎么过滤?一般我们要知道我们想要的数据在哪个PID,对应的段数据可能还需要再细分类,那你还需要将section前几个字节(byte)传下去,让demux只过滤出PID下同时匹配section前几个字节数据对应的包数据。比如。。。xx xx xx 01(PID)xx 02 03 04 05 06 07
xx 。。。当前频点拿到的数据很多,但我只需要PID为0x1, section前3个字节为02.03.04的表的数据,首先filter会过滤出pid为0x01的所有的数据,然后在这些数据中过滤出前3个字节为02 03 04 的那些数据。再细化分类的话拿到这些数据你就要按SI/PSI协议或你自己私有的协议来了。
xx 。。。当前频点拿到的数据很多,但我只需要PID为0x1, section前3个字节为02.03.04的表的数据,首先filter会过滤出pid为0x01的所有的数据,然后在这些数据中过滤出前3个字节为02 03 04 的那些数据。再细化分类的话拿到这些数据你就要按SI/PSI协议或你自己私有的协议来了。
channe通道l我们可以认为是一个容器,或再简单点就认为它是一块内存。这块容器就是用来装我们filter出来的数据的,所以一般channel是挂在filter上的。一个filter可以有多个channels,或一个channel有多个filter,具体要看平台实现需要。
当然由于硬件特性,signel环境,参数情况,可能获取不到我们想要的数据,不可能一直让filter,channel一直去那那个不存在或有问题的数据的,就需要有一个超时机制:在这个时间段无法拿到数据,通知上层,demux拿不到你想要的数据,你改干嘛干嘛去吧。同时demux释放filter,channel等相关资源。
demux处理后,filter过滤的数据通过回调或其他方式给上层,上层拿到想要的数据再实现具体的应用。
3.video,audio 编解码
一般来说解码后就可以输出output了。
目前常用的数据电视用到的制式有PAL,NTSC等
视频输出一般有CVBS,ypbpr,YUV,hdmi(高清用的多)等
视频输出一般用到视频层,静态层(still,IFRAME一般用到)
输出节目长度宽度比国内一般有4/3,16/9
常用支持的视频有:MPEG1,2,4;H264等
常用支持的音频有:MPEG1,2,ADTS,AC3,DTS等
一般对音视频会分配专门的固定的filter,channel。要播放某个节目,必须通过SI/PSI获取到,freq,vpid,apid,pcrpid等。
播放音视频就是让tuner锁对应freq的频点,demux去过滤出vpid,apid的数据包。其中通过pcrpid可以获取一个时间参考系。vpid拿到视频包,其中有I,P,B帧;
apid拿到音频包,其中有I,P,B帧。音视频同步就是通过I,P,B帧的PTS去同步pcrpid获取的时间参考系。
2.9LCN
逻辑频道号(DVB) 之 NIT表
逻辑频道号(DVB) 之 NIT表
LCN存在于NIT表中,描述符TAG=0x83,不过version2.0 好像TAG=0x87,并且语法结构也做了修改。
描述符基本结构如下:
logical_channel_desciptor{
descriptor_tag //8bit , 0x83
descriptor_length// =8bit
for(i=0;i<N;i++)
{
service_id //16bit
visible_service_flag//1bit
reserved //5bit
logical_channel_number//10bit
}
}
不同版本的LCN差异体现在logical_channel_number比特位的不同,NORDIG-Unified_ver_2.1版本中LCD V1的描述子结构为:
logical_channel_desciptor{
descriptor_tag //8bit , 0x83
descriptor_length// =8bit
for(i=0;i<N;i++)
{
service_id //16bit
visible_service_flag//1bit
reserved //1bit
logical_channel_number//14bit
}
}
当然,还有其他描述,此处不一一列举。以上两种TAG是一样的,都是0x83
下面看一张,带LCN的实例Svt_1.ts
这是一张NIT表,LCN就在transport_streams里,展开如下
找到transport_descriptors里的descriptor_tag==0x83 这个描述的就是LCN
把descriptor_data 数据放大:leng为32字节如下
03 f2 c0 0113 c4 c0 02
03 66 c0 6211 a8 40 00
11 9e 40 00 11 c6 40 00
14 32 c0 63
03 66 c0 6211 a8 40 00
11 9e 40 00 11 c6 40 00
14 32 c0 63
03 70 c0 61
根据描述符的基本结构如下:一个LCN应该占4字节32bit
logical_channel_desciptor{
descriptor_tag //8bit , 0x83
descriptor_length// =8bit
for(i=0;i<N;i++)
{
service_id //16bit
visible_service_flag//1bit
reserved //5bit
logical_channel_number//10bit
}
}
那么就根据结构来分析一下吧:这里的N==8
在分析之前,看看它的PAT表吧
根据pat可以看出总共6个节目
1.
03 f2 c0 01
03 f2 c0 01
service_id //16bit 是 03 f2 这个不正好是上面的program_number=0x03f2吗
visible_service_flag//1bit 1
reserved //5bit
logical_channel_number//10bit LCN = 0x001
2.13 c4 c0 02
service_id //16bit 是 13 c4 这个不正好是上面的program_number=0x13c4吗
visible_service_flag//1bit 1
reserved //5bit
logical_channel_number//10bit LCN = 0x002
3.03 66 c0 62
service_id //16bit 是 03 66 这个不正好是上面的program_number=0x0366吗
visible_service_flag//1bit 1
reserved //5bit
logical_channel_number//10bit LCN = 0x062
4. 11 a8 40 00
service_id //16bit 是 11 a8 这个好像没有0x11a8这个台,所以LCN也没有,可能也正常吧
visible_service_flag//1bit 0 难道visible_service_flag 可以控制是否有台?
reserved //5bit
logical_channel_number//10bit LCN = 0x0
5. 11 9e 40 00
service_id //16bit 是 11 9e 这个好像没有0x11 9e这个台,所以LCN也没有,可能也正常吧
visible_service_flag//1bit 0
reserved //5bit
logical_channel_number//10bit LCN = 0x0
6.
11 c6 40 00
11 c6 40 00
service_id //16bit 是 11 c6 这个不正好是上面的program_number=0x11 c6吗
visible_service_flag//1bit 0 但是这个台RTK没有搜到
reserved //5bit
logical_channel_number//10bit LCN = 0x0
7.
14 32 c0 63
14 32 c0 63
service_id //16bit 是 14 32 这个不正好是上面的program_number=0x14 32吗
visible_service_flag//1bit 1
reserved //5bit
logical_channel_number//10bit LCN = 0x63
8.
03 70 c0 61
03 70 c0 61
service_id //16bit 是 03 70 这个不正好是上面的program_number=0x03 70吗
visible_service_flag//1bit 1
reserved //5bit
logical_channel_number//10bit LCN = 0x61
下面咱们再解释为什么在NIT表中transport_streams有两个transport_stream_id:0x03fd和0x03fe
那么就为难了,选哪一个呢,当然有办法,不卖关子了,在pat表里:
来再看看pat表,看到了吗?蓝色的区域 明白了吧。