音视频 TS格式解析

前言

本文介绍TS文件封装格式,是一种被广泛应用的文件格式。全称为MPEG2-TS。其中TS即"Transport Stream"的缩写。

PS和TS

如下图所示,PES封包后能按需打出PS流和TS流,其中区别如下:

MPEG2-PS主要应用于存储的具有固定时长的节目,全称是Program Stream,如DVD电影;

MPEG-TS则主要应用于实时传送的节目,比如实时广播的电视节目。

PS 流 (Program Stream):节目流,PS 流由 PS 包组成,而一个 PS 包又由若干个 PES 包组成。一个 PS 包由具有同一时间基准的一个或多个 PES 包复合合成。

TS 流 (Transport Stream):传输流,TS 流由固定长度(188 字节)的 TS 包组成,TS 包是对 PES 包的另一种封装方式,同样由具有同一时间基准的一个或多个 PES 包复合合成。PS 包是不固定长度,而 TS 包为固定长度。

这两种格式的主要区别是将DVD上的VOB文件的前面一截剪掉(或者干脆就是数据损坏),那么就会导致整个文件无法解码,而电视节目是你任何时候打开电视机都能解码(收看)的。

所以,MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。

image.png

TS格式概览

TS是一种音视频封装格式,全称为MPEG2-TS。其中TS即"Transport Stream"的缩写。
TS格式是主要用于直播的码流结构,具有很好的容错能力。通常TS流的后缀是.ts、.mpg或者.mpeg,多数播放器直接支持这种格式的播放。
TS流中不包含快速seek的机制,只能通过协议层实现seek。HLS协议基于TS流实现的。

TS文件(码流)由多个TS Packet组成的,其TS包大小固定为188字节,以包头sync_byte为起始的一连串流。
TS文件(码流)可以分为三层:TS层(Transport Stream)、PES层(Packet Elemental Stream)、ES层(Elementary Stream)。
ES层就是音视频数据;
PES层是在音视频数据上加了时间戳等对数据帧的说明信息;
TS层是在PES层上加入了数据流识别和传输的必要信息。

image.png

image.png 

TS层

TS层分为三个部分:TS Header、Adaptation Field、Payload。
TS Header固定4个字节;Adaptation Field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;Payload是PES数据。

image.png

header,4字节

image.png

sync_byte(同步字节):
固定为0x47;该字节由解码器识别,使包头和有效负载可相互分离。

transport_error_indicator(传输错误标志):
1表示在相关的传输包中至少有一个不可纠正的错误位。当被置1后,在错误被纠正之前不能重置为0。

payload_unit_start_indicator(负载起始标志):
1表示该包是一个完整的包的开始;0表示是不是一个完整的包;

transport_priority(传输优先级标志):
‘1’表明当前TS包的优先级比其他具有相同PID, 但此位没有被置‘1’的TS包高。

PID:
指示存储与分组有效负载中数据的类型。
PID的作用就好比是一份文件的文件名。有了标识值的TS包,会放进一个叫节目映射表(PMT)的控制信息中,PMT本身就是一个TS包,所以也有自己的PID值(有PAT包指定),这个表里面的PID值与该路节目ES的音频、视频、数据的PID一一对应。最后将与该路节目有关的传送包复接起来,共同形成单路节目传送流,即TS流,从上图可以看出,视频数据的PID值是45,音频数据的PID值是78,填充数据的PID值是69…。
在多路节目传送流中,还有一个特殊的控制信息PAT(它是节目辅助表的意思),PAT本身也是一个TS包(在ISO/IEC 13818-1里有说明,PAT(Program Association Table)的PID值为0x00,所有TS包的标识(即sync_byte)为0x47),即上图中的表格数据部分。在PAT中,包含的就是每路TS流对应的PMT表所在的TS包的PID信息,其实PAT表中的数据为TS包的PID,这个TS包的内容为PMT。
通过对PID的译码,就可以对单个节目传送流进行解码。
所以解析起来就像这样: 先接收一个负载为PAT的TS包(TS包头为0x47 0xXX 0x00 0xXX),在整个数据包里找到一个PMT包的PID(program_map_PID)。然后再接收一个含有PMT的TS包(TS包头为0x47 0x00 program_map_PID 0xXX),在这个TS包里找到有关填入数据类型的PID,PMT中可能含有不止一个TS包的PID数据,PMT中stream_type、elementary_PID决定了TS包的类型和PID,然后用PMT中的PID值匹配接收到的TS包,如果匹配上,那么该TS包的负载内容就是填入实际的数据。

image.png

transport_scrambling_control(加扰控制标志):
表示TS流分组有效负载的加密模式。空包为‘00’,如果传输包包头中包括调整字段,不应被加密。其他取值含义是用户自定义的。

adaptation_field_control(适配域控制标志):
表示包头是否有调整字段或有效负载。‘00’为ISO/IEC未来使用保留;‘01’仅含有效载荷,无调整字段;‘10’ 无有效载荷,仅含调整字段;‘11’ 调整字段后为有效载荷,调整字段中的前一个字节表示调整字段的长度length,有效载荷开始的位置应再偏移[length]个字节。空包应为‘10’。

image.png

  • continuity_counter(连续性计数器):
    随着每一个具有相同PID的TS流分组而增加,当它达到最大值后又回复到0。范围为0~15。

文章最底部,领取最新最全C++音视频学习提升资料,内容包括(C/C++Linux 服务器开发,FFmpeg webRTC rtmp hls rtsp ffplay srs

Adaptation Field

Adaptation Field的长度要包含传输错误指示符标识的一个字节。
PCR是节目时钟参考,PCR、DTS、PTS都是对同一个系统时钟的采样值,PCR是递增的,因此可以将其设置为DTS值,音频数据不需要PCR。
打包TS流时PAT和PMT表是没有Adaptation Field的,不够的长度直接补0xff即可。
视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。

image.png

Payload

TS包中Payload所传输的信息包括两种类型:视频、音频的PES包以及辅助数据;节目专用信息PSI。
TS包也可以是空包。空包用来填充TS流,可能在重新进行多路复用时被插入或删除。
视频、音频的ES流需进行打包形成视频、音频的 PES流。辅助数据(如图文电视信息)不需要打成PES包。

PES层

从上面的结构图可以看出,PES层是在每一个视频/音频帧上加入了时间戳等信息,PES包内容很多,下面我们说明一下最常用的字段:

packet_start_code_prefix:
3字节,开始码,固定为0x000001。
stream id:
音频取值(0xc0-0xdf),通常为0xc0;视频取值(0xe0-0xef),通常为0xe0。
pes packet length:
后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff。
pes data length:
后面数据的长度,取值5或10。
pts:
33bit值
dts:
33bit值

image.png 

Xnip2021-08-12_16-19-09.jpg ES层

ES层指的就是音视频数据。
一般的,视频为H.264视频,音频为AAC音频。

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;

TS流生成及解析流程

TS 流生成流程
将原始音视频数据压缩之后,压缩结果组成一个基本码流(ES)。
对ES(基本码流)进行打包形成PES。
在PES包中加入时间戳信息(PTS/DTS)。
将PES包内容分配到一系列固定长度的传输包(TS Packet)中。
在传输包中加入定时信息(PCR)。
在传输包中加入节目专用信息(PSI) 。
连续输出传输包形成具有恒定比特率的MPEG-TS流。

TS 流解析流程
复用的MPEG-TS流中解析出TS包;
从TS包中获取PAT及对应的PMT;
从而获取特定节目的音视频PID;
通过PID筛选出特定音视频相关的TS包,并解析出PES;
从PES中读取到PTS/DTS,并从PES中解析出基本码流ES;
将ES交给解码器,获得压缩前的原始音视频数据。

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125207225