一、引言
因为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》