(1)ES- Elementary Streams ( 原始流 ) ,对视频、音频信号及其他数据进行编码压缩后的数据流称为原始流。原始流包括访问单元,比如视频原始流的访问单元就是一副图像的编码数据。
(2) PES- Packetized Elementary Streams ( 分组的原始流 ) ,原始流形成的分组称为 PES 分组,是用来传递原始流的一种数据结构
(3) 节目是节目元素的集合。节目元素可能是原始流,这些原始流有共同的时间基点,用来做同步显示。
(4) 传输流和节目流 TS-Transport Stream 翻译为 “ 传输流 ”PS-Program Stream 翻译为 “ 节目流 ”PS 用来传输和保存一道节目的编码数据或其他数据。 PS 的组成单位是 PES 分组。 TS 用来传输和保存多道节目的编码数据或其他数据, TS 的组成单位是节目。 PS 适用于不容易发生错误的环境,以及涉及到软件处理的应用,典型应用如 DVD 光盘的文件存储 TS 适用于容易发生错误的环境,典型应用就是数字电视信号的传输。 TS 和 PS 是可以互相转换的,比如从 TS 中抽取一道节目的内容并产生有效的 PS 是可能。
(5) 传输流分组和 PES 分组原始流分成很多 PES 分组,保持串行顺序,一个 PES 分组只包含一个原始流的编码数据。 PES 分组长度很大,最大可为 64K 字节。 PES 分组分为 “ 分组首部 (header)” 和 “ 有效负载 (payload)” 。 “ 有效负载 ” 指跟随在首部字节之后的字节。首部的前 4 个字节构成分组的起始码,标识了该分组所属原始流的类型和 ID 号。 TS 分组也就是传输流数据形成的数据包。每个 TS 分组长度为 188 字节,包括 “ 分组首部 ” 和 “ 有效负载,前 4 个字节是分组首部,包含了这个分组的一些信息。有些情况下需要更多的信息时,需在后面添加 “ 调整字段 (adaption field)” 。两者之间的关系 :PES 分组是插入到 TS 分组中的,每个 PES 分组首部的第一字节就是 TS 分组有效负载的第一字节。一个 PID 值的 TS 分组只带有来自一个原始流的数据。
(6)PSI 全称 Program Specific Information ,意为节目专用信息。传输流中是多路节目复用的,那么,怎么知道这些节目在传输流中的位置,区分属于不同节目呢?所以就还需要一些附加信息,这就是 PSI 。 PSI 也是插入到 TS 分组中的,它们的 PID 是特定值。 MPEG-2 中规定了 4 个 PSI ,包括 PAT( 节目关联表 ) , CAT( 条件访问表 ) , PMT( 节目映射表 ) , NIT( 网络信息表 ) ,这些 PSI 包含了进行多路解调和显示节目的必要的和足够的信息。应用中可能包括更多的信息,比如 DVB-T 中定义了 SDT( 服务描述表 ),EIT( 环境信息表 ),BAT( 节目组相关表 ),TDT( 时间日期表 ) 等,统称为 DVB-SI( 服务信息 ) 。 PSI 的 PID 是特定的,含 PSI 的数据包必须周期性的出现在传输流中。
PMT (Program Map Table ) 节目映射表 PMT 所在分组的 PID 由 PAT 指定,所以要先解出 PAT ,再解 PMT 。 PMT 中包含了属于同一节目的视频、音频和数据原始流的 PID 。找到了 PMT ,解多路复用器就可找到一道节目对应的每个原始流的 PID ,再根据原始流 PID ,去获取原始流。
PAT (Program Association Table ) 节目关联表 PAT 所在分组的 PID=0 PAT 中列出了传输流中存在的节目流 PAT 指定了传输流中每个节目对应 PMT 所在分组的 PIDPAT 的第一条数据指定了 NIT 所在分组的 PID ,其他数据指定了 PMT 所在分组的 PID 。
CAT (Conditional Access Table ) 条件访问表 CAT 所在分组的 PID=1CAT 中列出了条件控制信息 (ECM) 和条件管理信息 (EMM) 所在分组的 PID 。 CAT 用于节目的加密和解密 NIT( Network Information Table) 网络信息表 NIT 所在分组的 PID 由 PAT 指定 NIT 提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数 ( 如频率 , 调制方式等 ) 。 NIT 一般是解码器内部使用的数据,当然也可以做为 EPG 的一个显示数据提供给用户做为参考。几种 PSI 之间的关系,如下图所示:首先 PAT 中指定了传输流中所存在的节目,及每个节目对应的 PMT 的 PID 号。 比如 Program 1 对应的 PMT 的 PID=22, 然后找到 PID=22 的 TS 分组,解出 PMT ,得到这个节目中包含的原始流的 PID ,再根据原始流的 PID 去找相应的 TS 分组,获取原始流的数据,然后就可以送入解码器解码了。
数据结构( 1 ) TS 分组 前面提到, TS 分组由 188 个字节构成,其结构如下:
transport_packet(){
sync_byte //8
transport_error_indicator //1
payload_unit_start_indicator //1
transport_priority // 1
PID //13
transport_scrambling_control // 2
adaptation_field_control //2
continuity_counter //4
if(adaptation_field_control=='10' || adaptation_field_control=='11'){
adaptation_field()
}
if(adaptation_field_control=='01' || adaptation_field_control=='11') {
for (i=0;i<N;i++){
data_byte
}
}
}
前面 32bit 的数据即 TS 分组首部,它指出了这个分组的属性。
sync_byte 同步字节,固定为 0x47 ,表示后面的是一个 TS 分组,当然,后面包中的数据是不会出现 0x47 的
transport_error_indicator 传输错误标志位,一般传输错误的话就不会处理这个包了
payload_unit_start_indicator 这个位功能有点复杂,字面意思是有效负载的开始标志,根据后面有效负载的内容不同功能也不同,后面用到的时候再说。
transport_priority 传输优先级位, 1 表示高优先级,传输机制可能用到,解码好像用不着。
PID 这个比较重要,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容。前面已经叙述过。
transport_scrambling_control 加密标志位,表示 TS 分组有效负载的加密模式。 TS 分组首部 ( 也就是前面这 32bit) 是不应被加密的, 00 表示未加密。
adaption_field_control 翻译为 “ 调整字段控制 ” ,表示 TS 分组首部后面是否跟随有调整字段和有效负载。 01 仅含有效负载, 10 仅含调整字段, 11 含有调整字段和有效负载。为 00 的话解码器不进行处理。空分组没有调整字段
continuity_counter 一个 4bit 的计数器,范围 0-15 ,具有相同的 PID 的 TS 分组传输时每次加 1 ,到 15 后清 0 。不过,有些情况下是不计数的。如下: (1)TS 分组无有效负载 (2) 复制的 TS 分组和原分组这个值一样 (3) 后面讲到的一个标志 discontinuity_indicator 为 1 时
adaptation_field() 调整字段的处理
data_byte 有效负载的剩余部分,可能为 PES 分组, PSI ,或一些自定义的数据。
( 2 ) PAT 数据结构如下:
program_association_section() {
table_id // 8
section_syntax_indicator // 1
'0' // 1
reserved // 2
section_length // 12
transport_stream_id // 16
reserved // 2
version_number // 5
current_next_indicator // 1
section_number // 8
last_section_number // 8
for (i=0; i<N;i++) {
program_number // 16
reserved // 3
if(program_number == '0') {
network_PID // 13
}
else {
program_map_PID // 13
}
}
CRC_32 // 32
}
table_id 固定为 0x00 ,标志是该表是 PAT
section_syntax_indicator 段语法标志位,固定为 1
section_length 表示这个字节后面有用的字节数,包括 CRC32 。假如后面的字节加上前面的字节数少于 188 ,后面会用 0XFF 填充。假如这个数值比较大,则 PAT 会分成几部分来传输。
transport_stream_id 该传输流的 ID ,区别于一个网络中其它多路复用的流。
version_number 范围 0-31 ,表示 PAT 的版本号,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明 TS 流中的节目已经变化了,程序必须重新搜索节目.
current_next_indicator 表示发送的 PAT 是当前有效还是下一个 PAT 有效。
section_number 分段的号码。 PAT 可能分为多段传输,第一段为 00 ,以后每个分段加 1 ,最多可能有 256 个分段
last_section_number 最后一个分段的号码
program_number 节目号
network_PID 网络信息表( NIT )的 PID ,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为 0 时对应的 PID 为 network_PID
program_map_PID 节目映射表的 PID ,节目号大于 0 时对应的 PID ,每个节目对应一个
CRC_32 CRC32 校验码
上面 program_number , network_PID , program_map_PID 是循环出现的。 program_number 等于 0 时对应 network_PID , program_number 等于其它值时对应 program_map_PID 。
(3)PMT PMT 数据结构如下:
TS_program_map_section() {
table_id // 8
section_syntax_indicator // 1
'0' // 1
reserved // 2
section_length // 12
program_number // 16
reserved // 2
version_number // 5
current_next_indicator // 1
section_number // 8
last_section_number // 8
reserved // 3
PCR_PID // 13
reserved 4
program_info_length // 12
for (i=0; i<N; i++) {
descriptor()
}
for (i=0;i<N1;i++) {
stream_type // 8
reserved // 3
elementary_PID // 13
reserved // 4
ES_info_length // 12
for (i=0; i<N2; i++) {
descriptor()
}
}
CRC_32 // 32
}
table_id 固定为 0x02 ,标志是该表是 PMT 。
section_syntax_indicator section_length version_number current_next_indicator 以上四个字段意思和 PAT 相同,可参考上面解释
section_number last_section_number 以上两个字段意思和 PAT 相同,不过值都固定为 0x00 ,我觉得这样的原因可能是因为 PMT 不需要有先后顺序,因为先定义哪个节目都是无所谓。
program_number 节目号,表示该 PMT 对应的节目
PCR_PID PCR (节目时钟参考)所在 TS 分组的 PID ,根据 PID 可以去搜索相应的 TS 分组,解出 PCR 信息。
program_info_length 该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息
stream_type 指示了 PID 为 elementary_PID 的 PES 分组中原始流的类型,比如视频流,音频流等,见后面的表
elementary_PID 该节目中包括的视频流,音频流等对应的 TS 分组的 PID
ES_info_length 该节目相关原始流的描述符的信息长度。 stream_type 对应的类型。