一、引言
因为Apple公司提出的HLS(http live streaming)格式的流行,mpeg-ts封装的文件在互联网上已经随处可见。这套体系的强大之处就在于它的简单,试想,只要你有工具可以把一个或多个视频文件切割成一堆的小ts文件,并且生成一个内容非常简单的m3u8文件,然后只要有一个标准的web server(apache, nginx)就能对外提供点播视频流媒体服务了。假如可以对直播数据流进行切片存储成小文件,那就又具备了直播流媒体服务能力。
本文在此不对流媒体的应用进行研究,仅是对mpeg-ts格式进行分析,而分析的手段则是依照mpeg-ts的specification说明文档进行解构。之后会介绍一个非常好用的解析库bitstream,并提供据此库实现的ts解析工具与大家共同学习探讨。
二、基本结构
TS流的最小数据单元是188字节,所以可以认为一个TS文件的长度一定是188的整数倍。
2.1 TS包头部解析
TS包的头部固定占据四字节,这4个字节总共32bit分布着9个字段,现在对这些字段进行说明。
字段名称 |
长度 |
描述 |
备注 |
sync_byte |
8 bit |
同步字节 |
永远为0x47,可用作ts包是否完整的判断 |
Error indicator |
1 bit |
传输误码指示符 |
|
Payload unit start indicator |
1 bit |
有效单元荷载起始指示符 |
当若干TS包构成一个独立单元时,可以此作为判断依据 |
Transport priority |
1 bit |
优先传输 |
|
PID |
13 bit |
包标识符 |
最重要的字段,标识该包所属的流 |
Transport scrambling control |
2 bit |
传输控制标识 |
|
Adaption field control |
1 bit |
自适应区标识 |
此标志被置1意味着包内还有附加数据 |
Playload flag |
1 bit |
有效载荷标识 |
是否含有负载数据 |
Continue counter |
4 bit |
连续计数器 |
这是一个在0~15之间循环的数字,可用来判断TS包是否连续 |
通过头部四字包含的这9个字段数据,我们可以做的事情有:
1. 取得流PID(最重要)
2. 判断TS流是否完整
3. 判断TS流是否连续
2.2 Adaptation field解析
Adaptation field是自适应数据区,用以指示接下来的数据是否为逻辑上独立的一段内容,并且存放了PCR信息。
字段名称 |
长度 |
描述 |
备注 |
adaptation_field_length |
8 bit |
自适应区长度 |
值为0 表示仅为占位符,大于0则指示自适应区长度 |
discontinuity_indicator |
1 bit |
非连续提示符 |
值为1时说明基准时间戳发生变化,PCR值重置 |
random_access_indicator |
1 bit |
随机访问提示符 |
值为1表示该PES包是一帧视频或者音频的起始包 |
elementary_stream_priority_indicator |
1 bit |
元组流优先提示符 |
值为1表示ES包数据优先于其他包的数据 |
PCR_flag |
1 bit |
PCR标志 |
值为1表示自适区存在PCR值 |
OPCR_flag |
1 bit |
OPCR标志 |
值为1表示自适区存在OPCR值 |
splicing_point_flag |
1 bit |
分割点标志 |
值为1表示存在splice_countdown字段 |
transport_private_data_flag |
1 bit |
传输私有数据标志 |
值为1表示自适区内包含一段或多段私有数据 |
adaptation_field_extension_flag |
1 bit |
自适应区扩展标志 |
值为1 时表示自适区内存在扩展数据 |
program_clock_reference_base |
33 bit |
节目参考时间基线 |
占30位,其分布规律为从最高位3位起,间隔一个marker,然后每15位一段 |
reserved |
6 bit |
|
|
program_clock_reference_extension |
9 bit |
节目参考时钟扩展 |
基准PCR的扩展 |
2.3 PAT包解析
根据从头部取得的PID我们接下来要做的一件事情就是确定这个PID所属的类型。而类型可分为TS固有类型与音视频流类型等。TS流的特点是没有严格意义上的起始或结束部分,所以也不能像mp4或者avi格式那样从meta域取得整体信息,这样我们势必要有一个办法知道当前的TS流的结构是怎样的,包含哪些音视频流等。所有的一切都要从一个叫做PAT的包开始,就像世界来自上帝七天创世纪,宇宙始于大爆炸,你要结婚总要从遇见一个姑娘开始。
TS固有类型的第一个要知道的就是PAT包,它的全称是Program Association Table(节目关联表),要说明它的作用先从介绍它的结构开始。
如何判断是否PAT包?非常简单,2.1节中我们看到有PID这个字段,PID的值等于0就对了。0,这个在计算机程序中无比神圣的数字被赋予了PAT了,也可见PAT对于TS的重要性。PAT包采用section格式封装,section结构简单说来可分为头部与负载区,头部区由若通用干字段组成,下面对其结构进行说明。
字段名称 |
长度 |
描述 |
备注 |
Table id |
8 bit |
表标识 |
值为0 代表是PAT表 |
section_syntax_indicator |
1 bit |
段同步提示符 |
默认设置为1 |
‘0’ |
1 bit |
|
|
reserved |
2 bit |
|
|
section_length |
12 bit |
段长度 |
段总长度,包括CRC值在内 |
transport_stream_id |
16 bit |
传输流标识 |
可由用户自定义的传输流标识 |
reserved |
2 bit |
|
|
version_number |
5 bit |
版本号 |
在1至32数值之间递增,当current_next_indicator为1 时是当前可用的,为0则代表当然无效,需要等待下一个PAT |
current_next_indicator |
1 bit |
当前后续提示 |
为1 时是当前可用的,为0则代表当然无效,需要等待下一个PAT |
section_number |
8 bit |
段号 |
起始段号为0,随着段数加1 |
last_section_number |
8 bit |
最后段号 |
最大的段号 |
program_number |
16 bit |
节目号 |
用于在PAT包内区分不同节目 |
reserved |
3 bit |
|
|
program_map_PID |
13 bit |
节目映射标识 |
每个节目的PID值,可由用户自行定义,但须保证唯一 |
CRC_32 |
32 bit |
循环校验值 |
用于判断section段是否完整 |
2.4 PMT包解析
PMT包括了一个节目相关的所有流信息。流这个概念对应的是一路音频或一路视频。我们知道,一个节目播放时有图像也有声音,但在编码层面它们往往是分开并且独立的,因为它们在最终呈现时是由不同的硬件设备来渲染的。音频与视频的同步则要借助于PCR,它是同步参考时钟,决定着哪一帧画面与哪一段音频相对应。
字段名称 |
长度 |
描述 |
备注 |
Table id |
8 bit |
表标识 |
值设为0x02 |
section_syntax_indicator |
1 bit |
段同步提示符 |
必须设置为1 |
‘0’ |
1 bit |
|
|
reserved |
2 bit |
|
|
section_length |
12 bit |
段长度 |
段总长度,包括CRC值在内 |
program_number |
16 bit |
节目号 |
一个单独节目的标识,在复合流中相关的音视频流要归在此标识下用以分流 |
reserved |
2 bit |
|
|
version_number |
5 bit |
版本号 |
在1至32数值之间递增,当current_next_indicator为1 时是当前可用的,为0则代表当然无效,需要等待下一个PMT |
current_next_indicator |
1 bit |
接续提示符 |
当current_next_indicator为1 时是当前可用的,为0则代表当然无效,需要等待下一个PMT |
section_number |
8 bit |
段号 |
此值应设为0x00 |
last_section_number |
8 bit |
最后段号 |
此值应设为0x00 |
reserved |
3 bit |
|
|
pcr_pid |
13 bit |
同步时钟标识 |
此节目所对应的同步参考时钟的标识号 |
reserved |
4 bit |
|
|
program_info_length |
12 bit |
节目信息长度 |
头两位置0,后10位数值表示所有信息的字节数 |
stream_type |
8 bit |
流类型 |
用以指示流的音视频属性 |
reserved |
3 bit |
|
|
elementary_PID |
13 bit |
元组标识 |
节目内包含的元组元素的标识号 |
reserved |
4 bit |
|
|
ES_info_length |
12 bit |
元组信息长度 |
无级描述信息的长度,内容紧跟其后 |
CRC_32 |
32 bit |
循环校验值 |
用于判断section段是否完整 |
Streamtype值的列表:
0x00ITU-T | ISO/IEC Reserved
0x01ISO/IEC 11172 Video
0x02ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrainedparameter video stream
0x03ISO/IEC 11172 Audio
0x04ISO/IEC 13818-3 Audio
0x05ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections
0x06ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data
0x07ISO/IEC 13522 MHEG
0x08ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC
0x09ITU-T Rec. H.222.1
0x0AISO/IEC 13818-6 type A
0x0BISO/IEC 13818-6 type B
0x0CISO/IEC 13818-6 type C
0x0DISO/IEC 13818-6 type D
0x0EITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary
0x0FISO/IEC 13818-7 Audio with ADTS transport syntax
0x10ISO/IEC 14496-2 Visual
0x11ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC14496-3 / AMD 1
0x12ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets
0x13ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried inISO/IEC14496_sections.
0x14ISO/IEC 13818-6 Synchronized Download Protocol
0x15-0x7FITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
0x80-0xFF User Private.
2.5SDT包解析
SDT是流信息描述表,它的作用是传输节目的内容名称。例如我们在制作节目单时就可以从这个表中取出cctv 1, cctv2这样的内容名称。
字段名称 |
长度 |
描述 |
备注 |
Table id |
8 bit |
表标识 |
值设为0x02 |
section_syntax_indicator |
1 bit |
段同步提示符 |
必须设置为1 |
‘0’ |
1 bit |
|
|
reserved |
2 bit |
|
|
section_length |
12 bit |
段长度 |
段总长度,包括CRC值在内 |
reserved |
18 bit |
节目号 |
一个单独节目的标识,在复合流中相关的音视频流要归在此标识下用以分流 |
version_number |
5 bit |
版本号 |
在1至32数值之间递增,当current_next_indicator为1 时是当前可用的,为0则代表当然无效,需要等待下一个PMT |
current_next_indicator |
1 bit |
接续提示符 |
当current_next_indicator为1 时是当前可用的,为0则代表当然无效,需要等待下一个PMT |
section_number |
8 bit |
段号 |
起始段号为0,随着段数加1 |
last_section_number |
8 bit |
最后段号 |
最大的段号 |
descriptor |
|
|
|
CRC_32 |
32 bit |
循环校验值 |
用于判断section段是否完整 |
2.6 PES包解析
PES是一组ES包的集合,一个PES包内是一帧完整的视频或者音频数据。TS流的播放一般都会从关键帧所在的PES包起播。
字段名称 |
长度 |
描述 |
备注 |
packet_start_code_prefix |
24 bit |
Pes起始标识码 |
用于标识pes包起始,值为0x000001 |
stream_id |
8 bit |
流标识 |
用以区分ES的类型与序数 |
PES_packet_length |
16 bit |
包长度 |
PES包的总长度 |
'10' |
2 bit |
|
|
PES_scrambling_control |
2 bit |
扰流控制 |
用以指示PES包是否扰流模式 |
PES_priority |
1 bit |
优先级 |
值为1的包优先于值为0的包,合成器可据此判断包的先后 |
data_alignment_indicator |
1 bit |
数据排列指示符 |
值为1表示在PES包头之后紧跟着视频起始符或者音频同步位。 |
copyright |
1 bit |
版权标识 |
值为1表示PES包内容受版权保护 |
original_or_copy |
8 bit |
源或副本标识 |
值为1表示为源数据流,为0表示为副本 |
PTS_DTS_flags |
2 bit |
Pts与dts标志 |
值为01表示只有PTS,值为11表示同时有PTS与DTS |
ESCR_flag |
1 bit |
Escr标志 |
值为1表示在PES头部有ESCR数据与其扩展字段 |
ES_rate_flag |
1 bit |
Es码率标志 |
值为1表示PES头部有es码率值 |
DSM_trick_mode_flag |
1 bit |
DSM伪装模式 |
值为1表示存在DSM伪装模式字段 |
additional_copy_info_flag |
1 bit |
附加复制信息标志 |
值为1表示存在附加复制信息字段 |
PES_CRC_flag |
1 bit |
循环校验标志 |
值为1表示存在PES包循环校验数据 |
PES_extension_flag |
1 bit |
扩展标志 |
值为1表示存在PES扩展数据 |
PES_header_data_length |
8 bit |
头部数据长度 |
除去以上字段之外接下来的数据长度 |
'0011' |
4 bit |
|
|
PTS [32..30] |
3 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
PTS [29..15] |
15 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
PTS [14..0] |
15 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
'0001' |
4 bit |
|
|
DTS [32..30] |
3 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
DTS [29..15] |
15 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
DTS [14..0] |
15 bit |
|
|
marker_bit |
1 bit |
标志位 |
值为1 |
注意:PTS与DTS占30位,其分布规律为从最高位3位起,间隔一个marker,然后每15位一段。
三、参考文档
《ISO/IEC 13818-1》