音视频封装 - TS 封装格式
TS
全称是 MPEG2-TS
,MPEG2-TS
是一种标准容器格式,传输与存储音视频、节目与系统信息协议数据,广泛应用于数字广播系统,我们日常数字机顶盒接收到的就是 TS
(Transport Stream
,传输流)流。
概念
首先需要先分辨 TS
传输流中几个基本概念:
ES
(Elementary Stream
):基本流,直接从编码器出来的数据流,可以是编码过的音频、视频或其他连续码流PES
(Packetized Elementary Streams
):PES
流是ES
流经过PES
打包器处理后形成的数据流,在这个过程中完成了将ES
流分组、加入包头信息(PTS
、DTS
等)操作。PES
流的基本单位是PES
包,PES
包由包头和 payload 组成PS
流 (Program Stream
):节目流,PS 流由 PS 包组成,而一个 PS 包又由若干个PES
包组成。一个 PS 包由具有同一时间基准的一个或多个PES
包复合合成。TS
流 (Transport Stream
):传输流,TS
流由固定长度(188
字节)的TS
包组成,TS
包是对PES
包的另一种封装方式,同样由具有同一时间基准的一个或多个PES
包复合合成。PS 包是不固定长度,而TS
包为固定长度。
TS 文件
TS
文件分为三次:TS
层、PES
层、ES
层。ES
层就是音视频数据,PES
层是在音视频数据上加了时间戳等数据帧的说明信息,TS
层是在 PES
层上加入了数据流识别和传输的必要信息。
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS | = | Packet 1 | Packet 2 | Packet 3 | ... | Packet n-1| Packet n |
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
One Packet: 4bytes 184bytes
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet | = | Packet header | Packet data |
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TS 层
TS
包大小固定为 188
字节,TS
层分为三个部分:TS header
、adaptation field
、payload
:
TS header
固定4
个字节adaptation field
可能存在也可能不存在,主要作用是给不足188
字节的数据做填充payload
是PES
数据+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TS header | adaptation field | payload(PES) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TS header
|0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ① |②|③|④| ⑤ | ⑥ | ⑦ | ⑧ |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
序号 | 名称 | 大小 | 说明 |
---|---|---|---|
1 | sync_byte | 8bit | 同步字节,固定为 0x47 |
2 | transport_error_indicator | 1bit | 传输错误指示符,表明在 TS 头的 adapt 域后由一个无用字节,通常都为 0,这个字节算在 adapt 域长度内 |
3 | payload_unit_start_indicator | 1bit | 负载单元起始标示符,一个完整的数据包开始时标记为 1 |
4 | transport_priority | 1bit | 传输优先级,0 为低优先级,1 为高优先级,通常取 0 |
5 | PID | 13bit | PID 值 |
6 | transport_scrambling_control | 2bit | 传输加扰控制,00 表示未加密 |
7 | adaptation_field_control | 2bit | 是否包含自适应区,'00'保留;'01'为无自适应域,仅含有效负载;'10'为仅含自适应域,无有效负载;'11'为同时带有自适应域和有效负载。 |
8 | continuity_counter | 4bit | 递增计数器,从 0-f,起始值不一定取 0,但必须是连续的 |
TS
层的内容是通过 PID
值来标识的,主要内容包括:PAT
表、PMT
表、音频流、视频流。解析 TS
流要先找到 PAT
表,只要找到 PAT
就可以找到 PMT
,然后就可以找到音视频流了。PAT
表和 PMT
表需要定期插入 TS
流,因为用户随时可能加入 TS
流,这个间隔比较小,通常每隔几个视频帧就要加入 PAT
和 PMT
。PAT
和 PMT
表是必须的,还可以加入其它表如 SDT
(业务描述表)等,不过 HLS
流只要有 PAT
和 PMT
就可以播放了。
PAT
表:他主要的作用就是指明了PMT
表的PID
值。PMT
表:他主要的作用就是指明了音视频流的PID
值。- 音频流 / 视频流:承载音视频内容。
PID
是 TS
流中唯一识别标志,Packet Data
是什么内容就是由 PID
决定的。下表给出了一些表的 PID 值:
表类型 | PID 值 |
---|---|
PAT | 0x0000 |
CAT | 0x0001 |
TSDT | 0x0002 |
EIT,ST | 0x0012 |
RST,ST | 0x0013 |
adaptation field
名称 | 大小 | 说明 |
---|---|---|
adaptation_field_length | 8bit | 自适应域长度,后面的字节数(从 flag 开始) |
flag | 8bit | 取 0x50 表示包含 PCR 或 0x40 表示不包含 PCR |
PCR | 40bit | Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟 STC(System Time Clock)。 |
stuffing_bytes | xbit | 填充字节,取值 0xFF |
自适应区的长度要包含传输错误指示符标识的一个字节。PCR
是节目时钟参考,PCR
、DTS
、PTS
都是对同一个系统时钟的采样值,pcr 是递增的,因此可以将其设置为 DTS
值,音频数据不需要 PCR
。如果没有字段,打包 TS
流时 PAT
和 PMT
表是没有 adaptation field
的,不够的长度直接补 0xFF
即可。视频流和音频流都需要加 adaptation field
,通常加在一个帧的第一个 TS
包和最后一个 TS
包里,中间的 TS
包不加。
PAT
Program Association Table
节目关联表,每个 TS
流对应一张,用来描述该 TS
流中有多少个节目。
TS
流中中,PAT
包重复实现,大约0.5
秒出现一个,保证实时解码性- 表示
PAT
表的TS
包PID
值为0
,便于识别 PAT
的 payload 中传送特殊PID
的列表,每个PID
对应一个节目(对应一张PMT
表)PAT
表是TS
流的基础,任何一个TS
流解析寻找节目都是从PAT
表开始查找
typedef struct TS_PAT_Program {
unsigned program_number :16; // 节目号,为 0x0000 时表示这是 NIT,节目号为 0x0001 时, 表示这是 PMT
unsigned reserved :3 // 保留,固定为 111
unsigned program_map_PID :13; // 节目号对应内容的 PID 值
} TS_PAT_Program;
typedef struct TS_PAT {
unsigned table_id : 8; // 固定 0x00 ,标志是该表是 PAT
unsigned section_syntax_indicator : 1; // 段语法标志位,固定为 1
unsigned zero : 1; // 0
unsigned reserved_1 : 2; // 保留位,固定为 11
unsigned section_length : 12; // 段长度,表示从下一个字段开始到 CRC32(含) 之间有用的字节数
unsigned transport_stream_id : 16; // TS 流 ID,一般为 0x0001,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2; // 保留位,固定为 11
unsigned version_number : 5; // PAT 版本号,固定为 00000,如果 PAT 有变化则版本号加 1
unsigned current_next_indicator : 1; // 固定为 1,表示这个 PAT 表有效,如果为 0 则要等待下一个 PAT 表
unsigned section_number : 8; // 分段的号码。PAT 可能分为多段传输,第一段为 00,以后每个分段加 1,最多可能有 256 个分段
unsigned last_section_number : 8; // 最后一个分段的号码
std::vector<TS_PAT_Program> program;
unsigned CRC_32 : 32; // CRC32 校验码
} TS_PAT;
PMT
Program Map Table
,节目映射表,该表的 PID
是由 PAT
表 提供给出的。表征一路节目所有流信息。包含:
- 当前节目中包含的所有
Video
数据的PID
- 当前节目中包含的所有
Audio
数据的PID
- 与当前节目关联在一起的其他数据的
PID
(如数字广播, 数据通讯等使用的PID
)
如果 TS
流中包含多个节目,那么就会有多个 PMT
表。只要我们处理了 PMT
表,那么我们就可以获取该节目中所有的流信息,如当前节目包含多少个 Video、多少个 Audio
和其他数据及每种数据对用的流 PID
分别是多少。
typedef struct TS_PMT_Stream {
unsigned stream_type : 8; // 指示本节目流的类型,H.264 编码对应 0x1b,AAC 编码对应 0x0f,MP3 编码对应 0x03
unsigned reserved1 : 3; // 保留位,固定为 111
unsigned elementary_PID : 13; // 指示该流的 PID 值
unsigned reserved2 : 3; // 保留位,固定为 1111
unsigned ES_info_length : 12; // 前两位 bit 为 00,指示跟随其后的描述相关节目元素的字节数
std::vector<unsigned> descriptor;
} TS_PMT_Stream;
typedef struct TS_PMT {
unsigned table_id : 8; // 取值随意,一般使用 0x02, 表示 PMT 表
unsigned section_syntax_indicator : 1; // 段语法标志位,固定为 1
unsigned zero : 1; // 固定为 0
unsigned reserved_1 : 2; // 保留位,固定为 11
unsigned section_length : 12; // 段长度,表示从下一个字段开始到 CRC32(含) 之间有用的字节数
unsigned program_number : 16; // 当前 PMT 表映射到的节目号,1、2、3
unsigned reserved_2 : 2; // 保留位,固定为 11
unsigned version_number : 5; // PMT 版本号码,固定为 00000,如果 PAT 有变化则版本号加 1
unsigned current_next_indicator : 1; // 发送的 PMT 表 是当前有效还是下一个 PMT 有效
unsigned section_number : 8; // 分段的号码。PMT 可能分为多段传输,第一段为 00,以后每个分段加 1,最多可能有 256 个分段
unsigned last_section_number : 8; // 分段数
unsigned reserved_3 : 3; // 保留位,固定为 111
unsigned PCR_PID : 13; // 指明 TS 包的 PID 值,该 TS 包含有 PCR 同步时钟,
unsigned reserved_4 : 4; // 预留位,固定为 1111
unsigned program_info_length : 12; // 前 2bit 为 00,该域指出跟随其后对节目信息的描述的字节数。
std::vector<TS_PMT_Stream> PMT_Stream;
unsigned reserved_5 : 3; // 保留位,0x07
unsigned reserved_6 : 4; // 保留位,0x0F
unsigned CRC_32 : 32; // CRC32 校验码
} TS_PMT;