(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 对应的类型。