TS全称transport stream,是基于MPEG-2的封装格式(所以也叫MPEG-TS),通常后缀为.ts,.mpg,.mpeg。TS封装格式如今广泛应用于数字电视,在即时通讯传输业务上大方光彩,本文将详细介绍TS文件的生成过程,以及对其数据结构的详细分析。
关于视音频的基础知识可以参考雷神的博客:https://blog.csdn.net/leixiaohua1020/article/details/18893769 抓包工具和相关素材的下载:https://download.csdn.net/download/qq_41786131/11191889 使用工具抓包分析TS的各种packet_type请参考我的另一篇文章:TS封装格式小白入门分析总结之抓包分析
(一)TS文件生成过程和分层结构
因TS是对基本码流的封装,所以TS也应是分层结构。TS主要分为三层:TS层(Transport Stream),PES层(Packet Element Stream),ES层(Element Stream),ES层就是压缩编码(视频采用H264,音频采用acc)后的基本的音视频码流,PES在ES层的基础上加入了时间戳(PTS/DTS)等信息,而TS层就是在PES层加入了定时信息(PCR)和节目专用信息(PSI)进而形成我们所需的稳定MPEG-TS码流。这就是ts文件生成的基本过程,其中的名词稍后会继续介绍。汇总为结构图如下所示:
(二)TS层的结构分析
观察图1,TS每个包大小固定188字节,其中TS Heather固定4个字节,adaptation field为自适应区, payload为有效负载,两者共计184个字节。TS Header结构以及相关作用如下所示:
typedef struct Transport_Packet{ unsigned Sync_byte :8 //同步标志。值固定0x47 unsigned Transport_error_indicator :1 //传输错误指示符。为1时代表出错 unsigned Payload_unit_start_indicator :1 //有效负载起始指示符。为1时代表是一帧的开始 unsigned Transport_priority :1 //传输优先级,为1代表优先级高于相同PID的字段 unsigned PID :13 //重点,稍后详解 unsigned Transport_scrambling_control :2 //加扰方式。00未加扰,其他值用户填写 unsigned Adapaction_field_control :2 //当前包是否有调整字段或者有效负载。00保留,01无调整字段有有效负载,10有调整字段无有效负载,11两者都有 unsigned Continuity_counter :4 //从0到15连续循环递增,代表连续的包,不一定从0开始 }
可以看出,TS Header里主要是一些控制操作,它也是每个TS包的起始位置,我们之后抓包分析TS数据都要从这里开始。adapation field(调整字段)句法以及要点概括如下所示(牢记各个字段占位大小,有利于以后对数据包的分析):
Adaptation_field_length | 8b | 其数值指示的是之后数据长度 |
Discontinuity_indicator | 1b | 1代表当前TS包不连续状态为真 |
Random_access_indicator | 1b | 为1时表明下一个相同PID的包应该含有PTS和原始访问点 |
Elementary_stream_priority_indicator | 1b | 为1时表明比相同PID包的优先级高 |
PCR_flag | 1b | 为1时代表调整字段中含有PCR(节目时钟参考,用于恢复与编码端一致的时钟顺序),其结构尚未列出(下同) |
OPCR_flag | 1b | 为1时代表调整字段中含有OPCR。 |
Splicling_point_flag | 1b | 为1时代表调整字段中存在拼接点splice_countdown |
Transport_private_data_flag | 1b | 为1时代表调整字段中存在至少一个私有数据 |
Adapatation_filed_extension_flag | 1b | 为1时代表存在调整字段的扩展 |
然后就是有效负载payload部分,它的数据类型并不一定是PES结构,也可能包含PAT,PMT等,那这到底由谁决定呢?就是我们之前在TS Header里看到的PID(包指示符:packet identifiter)!不同的PID值指示了不同的payload的数据类型。 (打包ts流时PAT和PMT表是没有调整字段的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。调整字段的最后也是用0xff填充字节,所以抓包的数据“FF”数据一般在中部。) 我用EasyICE抓包了一段ts文件,常见的PID取值如下:
可见payload承载的信息一般是包含着音视频数据的pes层或者空包,PID与包类型的对应关系请看我的另一篇博客: TS封装格式小白入门分析总结之抓包分析
(三)PES层介绍
有必要说一点,PES层是将ES层(一个片)加入了PES头部,一般ES层的数据是很大的(由PES包长度这个字段指出),所以加入PES头时必须将ES分割,我们只在第一个分割后的ES上加PES,所以就形成了这种情况(即很多TS包并不包含PES头部,直接是裸ES数据,稍后结合工具分析详解):
PES层主要分为三个部分PES header(固定6个字节),optional header(可选),和pes payload(也就是ES层了)。PES的字段比较多,在此只列出它的句法结构图和几个比较重要的字段(摘自:https://blog.csdn.net/cabbage2008/article/details/49612011):
可见每个PES包固定了三个字段:起始码流(packet_start_code_prefix,24b),流id(stream id,8b),包长度(PES_packet_length,16b)。这三个字段占据了6个字节,是我们分析PES包的起始标志,请牢记。然后是pes optional header(里面包含了很多字段),最后是pes payload。在此仅介绍几个比较重要的字段(标志flag对应了任选字段的内容是否存在)。
起始码流(packet_start_code_prefix) | 24b | 固定为00 00 01,借此可判断出PES包起始位置 |
流id(stream id) | 8b | 用来指示pes包的数据类型。表示音频通常取值0xc0,表示视频通常取值0xe0 |
包长度(PES_packet_length) | 16b | 用来指示接下来数据类型的大小,一般不会超过0xffff,除非包含的是视频数据 |
PES加扰控制(PES_scrambling_control) | 2b | 与TS包头加扰控制一致,00不加扰,其他值用户自定义 |
PES优先级(PES_priority) | 1b | 置为1时代表比其他置为0的有效负载拥有更高的优先级 |
版权(copyright) | 1b | 1代表此有效负载被版权保护,0代表不确定 |
原始的或复制的 (original_or_copy) | 1b | 1代表原始的,0代表复制的 |
显示解码时间戳标识符(PTS_DTS_flags) | 2b | 两个位分别代表是否有PTS(显示时间戳)或者DTS(解码时间戳)。此flag占据两个位,后面还有6个flag用来指示相应字段,这也是为什么上图7个flag占据8个位的原因。 |
PES头数据长度(PES_header_data_length) | 8b | 此指示符的数值代表的是填充字段和任选字段数值的大小 |
PTS DTS | 33b | 各33b, |
ES速率(ES Rate) | 22b | 由前面的flag指定是否存在,用来指定系统解码器解码PES包的速率,以50B/s为基本单位,这个速率会持续到新的ES Rate出现。 |
特技方式(trick_mode_control) | 3b | 由前面的相应flag指定是否存在。其中000快进,001慢动作,010冻结帧,011快速反向,100慢反向,其他值保留。 |
填充字节(stuffing_byte) | 8b | 一般为11111111,由编码器插入,由解码器丢弃 |
覆盖字节(padding_byte) | 8b | 一般为11111111,由解码器丢弃。 |
pes层中很多字段都是有它前面的flag指定,一般情况下很多字段是不存在的,最常见的是PTS/DTS.对PES各个字段更加详细的介绍请看相关的介绍文档:https://blog.csdn.net/cabbage2008/article/details/49848937。
(四)ES层介绍
Element Stream,即最基本的视音频数据了,在Ts封装格式中,视频使用H264压缩编码的,音频则是ACC压缩编码。
h264视频:
对h264编码格式的详细介绍我们留在下一篇博客:。现在我们需要知道要传输h264视频必须在前面加上一个NALU(network abstract layout uint)头部,这个NALU头部格式如下图所示:
F(frobiden) 1b | 禁止位,发生语法错误时此为为1 |
NRI 2b | 参考级别,值越大,此NAL越重要 |
NAL_TYPE 5b | 指明了此NAL单元的类型,常见的取值如下图 |
nal_unit_type | 说明 |
0 | 未使用 |
1 | 非IDR图像片,IDR指关键帧 |
2 | 片分区A |
3 | 片分区B |
4 | 片分区C |
5 | IDR图像片,即关键帧 |
6 | 补充增强信息单元(SEI) |
7 | SPS序列参数集 |
8 | PPS图像参数集 |
9 | 分解符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13~23 | 保留 |
24~31 | 未使用 |
牢记关键帧非关键帧,SEI,SPS,PPS的对应取值。对h264编码的详细介绍请看另一篇博客: