第二章:从TS到PAT和PMT
从PAT表开始,解析TS流
说完了TS流相关的基本概念,就该开始对TS流进行更深入的研究了。 首先需要想一想:TS流的本质是什么? TS是一段码流,并且是一段由数据包(Packet)组成的码流。 那么我们要如何组织和处理如此庞大的数据包呢?
如果我们把码流看作一本书,那么我们就需要给这本书制作一个目录;有了这样的目录,我们就可以“按图索骥”地得到我们需要的信息。
是的,PSI/SI就是这样“目录”。而PAT则是“目录”的“目录”。 通过PAT及其他 PSI 信息,我们可以一点一点地把整个TS流的信息组织和管理起来,从而分离出不同的业务(Service), 以及该业务的视频、音频、图文(Teletext)、字幕(Subtitle)、其他音轨等分量; 再根据 SI 信息,我们就可以得到每个业务的名称、事件(Event)等附加信息, 从而形成 EPG ,更好地管理各个业务。
需要区分的一点是,不管是PSI还是SI,都只是一种“管理信息”,它并不包含任何的视频或音频数据。
什么是PID
Introduction of PID
在上一节《预备知识》中,我们可以看到 PID(Packet Identifier) 这个被标红的字段频繁地出现。
PID 是当前TS流的Packet区别于其他Packet类型的唯一识别符,通过读取每个包的Packet Header, 我们可以知道这个Packet的数据属于何种类型。下表列出了几项固定的 PID 值,它们用于识别存储了特殊信息的Packet。 下面要谈的PAT表的 PID 值就是固定的0x0000。
TS流中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等表。
使用工具说明
Tools Introduction
使用码流分析软件来分析一段码流,会事半功倍:泽华源码流分析软件 或 Tsparse。
下图是使用 泽华源码流分析软件 的界面。在打开码流并分析完毕后,点击下方选项卡“语法分析”可以出现这个窗口。
一切从PAT开始
Begin with 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表的数据包
Analyze the Package of PAT
上一幅图中,右下角粉红色的数据即为PAT表的数据包。
根据在上一章 《预备知识》中提到的包的分析方法,可以将上图的的数据这样划分:
包头(Package Header) | 包数据(Package Data) |
47 40 00 1C | 00 B0 1D 22 01 CF 00 00 00 00 E0 10 40 13 E1 30 40 18 E1 ... FF FF |
注:表格内数据均为16进制数值。前4个字节为包头,后面的184个字节为包数据。
PAT表的包头
Package Header of PAT
细分其表头(Package Header)数据(47 40 00 1C)如下:
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ... |
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | ... |
Packet(十六进制) | 4 | 7 | 4 | 0 | 0 | 0 | 1 | C | ... | ||||||||||||||||||||||||
Packet Header信息 | 0*: sync_byte=0x47 | 1* | 2* | 3* | 4*: PID (这里是0x0000) | 5* | 6* | 7* | ... |
拖动可显示更多数据
上表中,第一行为表头的位号(0-31,共32位), 第二行为每位的二进制数值, 第三行为每个字节的16进制数值, 最后一行的数据说明参看下面的表格。
Packet Header(包头)信息Demo | |||
# | 标识 | 位数 | 说明 |
0* | sync_byte | 8 bits | 固定是0x47 |
1* | transport_error_indicator | 1 bits | 值为0,表示当前包没有发生传输错误。错误指示信息(1:该包至少有1bits传输错误) |
2* | payload_unit_start_indicator | 1 bits | 值为1,含义参考ISO13818-1标准文档。负载单元开始标志(packet不满188字节时需填充) |
3* | transport_priority | 1 bits | 值为0,表示当前包是低优先级。传输优先级标志(1:优先级高) |
4* | PID | 13 bits | PID=0x0000,说明是PAT表。Packet ID号码,唯一的号码对应不同的包 |
5* | transport_scrambling_control | 2 bits | 值为0x00,表示节目没有加密。加密标志(00:未加密;其他表示已加密) |
6* | adaptation_field_control | 2 bits | 值为0x01,具体含义请参考ISO13818-1。附加区域控制 |
7* | continuity_counter | 4 bits | 值为0xC,表示当前传送的相同类型的包是第12个。包递增计数器 |
PAT表的包数据
Package Data of PAT
该包的数据部分(Package Data)为:
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
注意到这里有一个绿色的 00,
这是因为在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1")。
因此,真正要分析的数据应该是红色部分:
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
解析前8个字节
下面对前8个字节(0-7字节,共计64 bits)进行分析:
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
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ... |
Binary | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... |
Hex | 0 | 0 | B | 0 | 1 | D | 2 | 2 | 0 | 1 | C | F | 0 | 0 | ... | ||||||||||||||||||||||||||||||||||||||||||
结构 | 0*: table_id=0x00 | 1* | 2* | 3* | 4*: section_length (这里是0x01D) | 5* | 6* | 7* | 8* | 9* | 10* | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 次序 | 说明 |
0* | table_id | 8 bits | 0000 0000(即0x00) | 第0个字节 00 | PAT的table_id只能是0x00 |
1* | section_syntax_indicator | 1 bit | 1 | 第1、2个字节 B0 1D | 段语法标志位,固定为1 |
2* | zero | 1 bit | 0 | ||
3* | reserved | 2 bits | 11 (Binary) | ||
4* | section_length | 12 bits | 00 00 00 01 11 01(即0x1D,十进制值为29) | 意思是 段长度为29字节 | |
5* | transport_stream_id | 16 bits | 00 10 00 10 00 00 00 01(即0x2201) | 第3、4个字节 22 01 | TS的识别号 |
6* | reserved | 2 bits | 11 | 第5个字节 CF | TS的识别号 |
7* | version_number | 5 bits | 0 01 11 | 一旦PAT有变化,版本号加1 | |
8* | current_next_indicator | 1 bit | 1 | 当前传送的PAT表可以使用,若为0则要等待下一个表 | |
9* | section_number | 4 bits | 00 00(即0x00) | 第6个字节 00 | 给出section号,在sub_table中, 第一个section其section_number为"0x00", 每增加一个section,section_number加一 |
10* | last_section_number | 4 bits | 00 00(即0x00) | 第7个字节 00 | sub_table中最后一个section的section_number |
循环开始(循环内的数据解析见下一节内容!) | |||||
- | program_number | 16 bits | -- | -- | - |
- | reserved | 3 bits | -- | -- | - |
- | network_id 或 program_map_PID | 13 bits | -- | -- | program_number为0x0000时, 这里是network_id(NIT的PID); 其余情况是program_map_PID(PMT的PID) |
循环结束 | |||||
- | CRC_32 | 32 bits | -- | 最后4个字节 | - |
解析循环部分
下面对后续几个字节(第8个字符开始)进行分析:
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),我们在这里就先解析第一个循环:
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
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ... |
Binary | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | ... |
Hex | 0 | 0 | 0 | 0 | E | 0 | 1 | 0 | ... | ||||||||||||||||||||||||
结构 | 1*: program_number = 0x0000 | 2*: reserved | 3*: network_id = 0x10 | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
1* | program_number | 16 bits | 0x0000 | program_number = 0x0000 |
2* | reserved | 3 bits | 111 | - |
3* | program_map_PID | 13 bits | 0x0000 | 因为program_number为0x0000, 所以这里是network_id(NIT的PID) |
继续解析下一个循环:
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
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ... |
Binary | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | ... |
Hex | 4 | 0 | 1 | 3 | E | 1 | 3 | 0 | ... | ||||||||||||||||||||||||
结构 | 1*: program_number = 0x4013(即十进制16403) | 2*: reserved | 3*: program_map_PID = 0x130(即PMT_PID为0x130) | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
1* | program_number | 16 bits | 0x4013 | program_number = 0x4013(即十进制16403) |
2* | reserved | 3 bits | 111 | - |
3* | 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表。(关于PMT表的解析可参看下一节内容)
为了不占用过多空间来显示这样重复的内容,关于更多循环的分析就不显示在正文,如果你仍想看,可以 点击这里显示或隐藏。
循环汇总
将所有循环遍历完毕后,我们就能找出4个节目。下图是码流分析软件的截图。
PMT PID列表
下面表格是通过对PAT表分析后得到的数据。在分析完PMT表后,还将对这张表进行补充。
# | 节目号 | PMT PID |
1 | 16403 | 0x130 |
2 | 16408 | 0x180 |
3 | 16394 | 0xA0 |
4 | 16398 | 0xE0 |
小结
对我们来说,PAT就是一个总入口。PAT告诉了我们,这个TS流中有几个节目,以及它们的PMT PID分别是多少。有了PMT的PID,我们就可以继续下一步了。
PMT表分析
Analyze of PMT
PMT表概述
PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如Teletext、Subtitle、其他音轨等的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息, 如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。 这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来, 在处理Packet的时候进行过滤即可实现。
PMT表的数据包总览
以PMT PID=0x130来分析。我们找到PID为0x130的PMT表:
PMT表的数据包包头
表头的分析方式和PAT表头的相同,就不详细介绍了。
数据包头是红色部分:
47 41 30 19 00 02 B0 43 40 13 C5 00 00 E1 31 F0 00 02 E1 31
F0 03 52 01 01 04 04 E1 32 F0 09 0A 04 64 75 01 52 01 03 06
E1 37 F0 0A 52 01 0A 56 05 64 65 75 09 00 06 E1 38 F0 0C 0A
04 64 65 75 01 6A 01 00 52 01 11 28 09 C3 CB FF FF ... FF FF
分析如下:
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ... |
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | ... |
Packet(十六进制) | 4 | 7 | 4 | 1 | 3 | 0 | 1 | 9 | ... | ||||||||||||||||||||||||
Packet Header信息 | - | - | - | - | PMT PID (这里是0x130) | - | - | - | ... |
拖动可显示更多数据
PMT表的数据包数据
数据内容是红色部分:
47 41 30 19 00
片段1:02 B0 43 40 13 C5
片段2:00 00 E1 31 F0 00
片段3:02 E1 31 F0 03
片段3补充:52 01 01
片段4:04 E1 32 F0 09
0A 04 64 75 01 52 01 03 06
E1 37 F0 0A 52 01 0A 56 05 64 65 75 09 00 06 E1 38 F0 0C 0A
04 64 65 75 01 6A 01 00 52 01 11 28 09 C3 CB FF FF ... FF FF
片段1:02 B0 43 40 13 C5
位号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
Binary | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
Hex | 0 | 2 | B | 0 | 4 | 3 | 4 | 0 | 1 | 3 | C | 5 | ||||||||||||||||||||||||||||||||||||
结构 | 0*: table_id=0x02 | 1* | 2* | 3* | 4* | 5*: 0x4013 = 16403 | 6* | 7*: 0x02 | 8* |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
0* | table_id | 8 bits | 0x02 | |
1* | Section_syntax_indicator | 1 bit | 1 | 通常设为“1” |
2* | zero | 1 bit | 0 | |
3* | Reserved | 2 bits | 11 | |
4* | Section_length | 12 bits | 0x43 | 段长度,从program_number开始,到CRC_32(含)的字节总数 |
5* | program_number | 16 bits | 0x4013 | 节目号为16403 |
6* | Reserved | 2 bits | 11 | |
7* | Version_number | 5 bits | 00010 = 0x02 | |
8* | Current_next_indicator | 1 bit | 1 | 当前未来标志符 |
片段2:00 00 E1 31 F0 00
位号 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | ... |
Binary | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... |
Hex | 0 | 0 | 0 | 0 | E | 1 | 3 | 1 | F | 0 | 0 | 0 | ... | ||||||||||||||||||||||||||||||||||||
结构 | 9* | 10* | 11* | 12* | 13* | 14* | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
9* | Section_number | 8 bits | 0x00 | 当前段号码 |
10* | last_section_number | 8 bits | 0x00 | 最后段号码,含义和PAT中的对应字段相同 |
11* | reserved | 3 bits | 0x00 | |
12* | PCR_PID | 13 bits | 0x0131 | PCR(节目参考时钟)所在TS分组的PID |
13* | reserved | 4 bits | 1111 | |
14* | program_info_length | 12 bits | 0x00 | 节目信息长度 (之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes) 紧接着就是频道内部包含的节目类型和对应的PID号码了 |
注意,上面的program_info_length之后的是N个描述符结构,但一般可以忽略掉。这个字段就代表描述符总的长度,单位是Bytes;这里是0x00,也就是意味着后面没有描述符结构。
片段3:02 E1 31 F0 03
因为program_info_length=0x00,所以紧接着的是频道内部包含的节目类型和对应的PID号码
位号 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | ... |
Binary | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | ... |
Hex | 0 | 2 | E | 1 | 3 | 1 | F | 0 | 0 | 3 | ... | ||||||||||||||||||||||||||||||
结构 | 15* | 16* | 17* | 18* | 19* | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
15* | stream_type | 8 bits | 0x02 | 流类型,标志是Video还是Audio还是其他数据。这里是MPEG2视频类型 |
16* | reserved | 3 bits | 111 | |
17* | elementary_PID | 13 bits | 0x131 | 该节目的音频或视频PID。这里是视频PID=0x131 |
18* | reserved | 4 bits | 1111 | |
19* | ES_info_length | 12 bits | 0x003 | 后面紧跟着3个字节的描述子 |
片段3补充:52 01 01
因为ES_info_length=0x03,所以后续的三个字节,为Stream identifier descriptor
位号 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ... |
Binary | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | ... |
Hex | 5 | 2 | 0 | 1 | 0 | 1 | ... | ||||||||||||||||||
结构 | 20* | 21* | 22* | ... |
拖动可显示更多数据
# | 字段名 | 占位 | 具体值 | 说明 |
20* | descriptor_tag | 8 bits | 0x52 | |
21* | descriptor_length | 8 bits | 0x01 | |
22* | 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
# | 字段名 | 占位 | 具体值 | 说明 |
23* | stream_type | 8 bits | 0x04 | 流类型,标志是Video还是Audio还是其他数据。这里是MPEG2音频类型 |
24* | reserved | 3 bits | 111 | |
25* | elementary_PID | 13 bits | 0 0001 0011 0010 = 0x132 | 该节目的音频或视频PID。这里是音频PID=0x132 |
26* | reserved | 4 bits | 1111 | |
27* | ES_info_length | 12 bits | 0000 0000 1001 = 0x09 | 后面紧跟着9个字节的描述子 |
上图是完整的PID为0x132的ES信息,可以看到这个ES有两个描述子(descriptor)。 解析时,第一个字节代表了描述子tag,第二个代表该描述子的长度。
看第一个描述子ISO_639_language_descriptor,第一个字节值为0xa,长度为0x4
看第二个描述子stream_identifier_descriptor,第一个字节值为0x52,长度为0x1
其余单元流和PMT PID的分析略。
PMT表的数据包数据小结
根据PAT和PMT的信息,我们可以得到下面的信息:
# | 节目号 | PMT PID | ES PID | ES说明 |
1 | 16403 | 0x130 | 0x131 | MPEG2视频 |
0x132 | MPEG2音频 | |||
0x137 | MPEG2私用PES | |||
0x138 | MPEG2私用PES | |||
2 | 16408 | 0x180 | 0x181 | MPEG2视频 |
0x182 | MPEG2音频 | |||
0x187 | MPEG2私用PES | |||
0x188 | MPEG2私用PES | |||
3 | 16394 | 0xA0 | 0xa1 | MPEG2视频 |
0xa2 | MPEG2音频 | |||
0xa7 | MPEG2私用PES | |||
4 | 16398 | 0xE0 | 0xe1 | MPEG2视频 |
0xe2 | MPEG2音频 | |||
0xe7 | MPEG2私用PES |
TS的复用与解复用
Multiplexing and Demultiplexing of Transport Stream
在数字电视系统的前端,所有的数据(视频、音频、PSI/SI信息)都会被复用器(Multiplexer)复用成TS流,从而进行传送(见下图)。
TS复用(Multiplexing)当机顶盒等接收器收到的数据,自然就是被复用后的一个个包(Package)了。这样的数据是不能直接使用的。 这时候,我们要做的就是把被复用的TS流 解复用(Demultiplexing, 简称Demux)。
解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。 这样就可以将Video、Audio或者其他业务信息(如PSI/SI信息)的数据区分开来。
上一小节制成的表格,其实就是解复用获得的信息。 通过它,我们可以组织起这个频点下的所有 节目。 以节目号为16403的 节目 为例, 过滤PID为0x131的所有包数据,就可以组成这个节目的视频; 过滤PID为0x132的所有包数据,就可以组成这个节目的音频; 这样,节目16403需要的视音频数据就集齐了;当然,要正常播放这个台,还需要时钟来同步。
DVB搜台过程
Process of DVB Scanning
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表之间的关系。
SDT表
由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了 SDT表 来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。
根据SDT表信息(这里不再详细解析),我们可以把上一小节总结的表格进一步补充:
# | 节目号 | PMT PID | ES PID | ES说明 | SDT表信息 |
1 | 16403 | 0x130 | 0x131 | MPEG2视频 | ProSieben |
0x132 | MPEG2音频 | ||||
0x137 | MPEG2私用PES | ||||
0x138 | MPEG2私用PES | ||||
2 | 16408 | 0x180 | 0x181 | MPEG2视频 | SAT.1 |
0x182 | MPEG2音频 | ||||
0x187 | MPEG2私用PES | ||||
0x188 | MPEG2私用PES | ||||
3 | 16394 | 0xA0 | 0xa1 | MPEG2视频 | KABEL1 |
0xa2 | MPEG2音频 | ||||
0xa7 | MPEG2私用PES | ||||
4 | 16398 | 0xE0 | 0xe1 | MPEG2视频 | N24 |
0xe2 | MPEG2音频 | ||||
0xe7 | MPEG2私用PES |
NIT表
在国内,NIT表一般会给出整个网络的频点列表。 在搜索主频点的同时解析出NIT表,就可以得到这个网络的频点列表。 机顶盒按顺序对所有频点锁频、解析,就可以将整个网络的业务搜索出来了。
但在国外,一般不会有这样的情况。首先,小运营商只是租用其中一个或几个频点,不可能对整个网络“指手画脚”,自然就不能定义NIT表了。 而大运营商,它们有自己的业务群(Bouquet),更倾向于使用BAT表来组织自己的节目。
参考文档
References
# | 文档名称 | 作者 |
1 | 《1.从TS流到PAT和PMT》 | 林晓州 |
2 | 《PSI/SI深入学习》 | 林晓州 |
3 | 《En300468.V1.7.1_Specification for SI in DVB Systems.pdf》 | European Standard |
4 | 《数字视频广播中文业务信息规范》 | 国家广播电影电视总局 |
5 | 《数字电视业务PSI-SI学习系列》 | 网络 |
版本信息
Version Information
# | 发布日期 | 版本 | 更新内容 | 作者 | 审核 |
1 | 2013年08月09日 | V1.0 | 文档《1.从TS流到PAT和PMT》 | 林晓州 | —— |
2 | 2016年02月19日 | V2.0 | 整合了多个文档资料,对PSI/SI学习所需的知识进行系统的总结。 | 林晓州 | —— |