TS(Transport Stream) 是一种常见的视频封装格式, 主要用于数字电视和在线视频传输领域。优缺点如下所示:
优点:
1.容错性强: 传输过程中发生丢包或错误信息丢失的情况, 可以快速恢复, 保障数据的完整性。
2.支持随机访问: TS格式对数据进行了分段, 每段可以单独访问和控制, 实现了视频的随机访问。
3.实时性好: TS 封装格式采用分段传输的方式,每个数据片段的大小相对较小,可以有效降低延迟,实现了实时数据传输。
4.可靠性好: TS 封装格式支持多路复用,能够对多个数据流进行混合,提高数据传输效率和可靠性。
缺点:
1.编解码复杂: TS 封装格式需要将视频数据进行分段和整合,对音视频编码技术的要求较高
2.文件较大: TS分段机制, 引入了一定的数据头信息
3.不支持字幕处理:TS 封装格式不支持对字幕数据进行处理,用户需要手动添加字幕
TS封装格式的数据单元是ts包。 每个包都有自己的pid, 包的大小固定为188字节。
ts包分为三类分别为:pat包、pmt包、pes包:
在TS流中, PAT和PMT包重复实现, 大约0.5秒出现一个,保证实时解码性。PAT表是TS流的基础, 任何一个TS流解析寻找节目都是从PAT表开始查找。
ts包固定大小为188个字节, 对应的结构公式如下所示:
tsPacket = tsheader + adaption field + payload;
tsheader: 为ts数据包的包头, 每个数据包都有, 文件头固定为4个字节
adaptation field: 数据填充字段, 有的数据包存在该字段,有的数据包不存在该字段(用作数据填充)
palyload: 音视频数据字段,用来存储pes数据和音视频的原始数据。
各种类型的数据包的结构如下所示:
TS流: 由一个个188字节的数据包组成
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS | = | Packet 1 | Packet 2 | Packet 3 | ... | Packet n-1| Packet n |
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
一个Packet: 4bytes 184bytes
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet | = | ts header | Packet data |
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
PAT/PMT包结构, 只包含tsheader和PMT/PAT, 不包含adaption和pes
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | PAT/PMT | Stuffing Bytss |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
PES包结构,可能包含adaption和pes字段
4 byte x byte 184-x byte
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | adaptation field | payload(pes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
每个TS数据包都包含ts header字段, 并且大小固定为4个字节, 四个字节中各个数据位的含义如下所示:
总共4Byte(32bit)
sync_byte (8bit) 同步字节,固定为0x47
transport_error_indicator (1bit) 传输错误指示符, 通常都为'0'
payload_unit_start_indicator (1bit) 负载单元起始标示符,一个完整的数据包开始时标记为 '1'
transport_priority (1bit) 传输优先级,0为低优先级,1为高优先级,通常取0
pid (13bit) 传输包的PID, pat包固定为0x0000
transport_scrambling_control (2bit) 传输加扰控制,00表示未加密
adaptation_field_control (2bit) 是否包含自适应区(adaption):00保留;01无自适应域,含有效负载;10含自适应域,无有效负载;11为同时带有自适应域和有效负载
continuity_counter (4bit) 递增计数器,从0-f,起始值不一定取0,但必须是连续的
PAT(Program Associate Table)节目关联表的结构如下所示:
table_id (8bit) PAT表固定为0x00
section_syntax_indicator (1bit) 段语法标志位, 固定为1
zero (1bit) 固定为0
reserved (2bit) 保留位,固定为11
section_length (12bit) 段长度(后面数据的长度),表示从下一个字段开始到CRC32(包含)之间有用的字节数
transport_stream_id (16bit) 传输流ID,固定为0x0001, 区别于一个网络中其它多路复用的流
reserved (2bit) 固定为11
version_number (5bit) 版本号,固定为00000,如果PAT有变化则版本号加1
current_next_indicator (1bit) 固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表
section_number (8bit) 分段的号码. PAT可能分为多段传输, 第一段为00, 以后每个分段加1, 最多可能有256个分段
last_section_number (8bit) 固定为0x00, 最后一个分段的号码
开始循环,按顺序可能包含多个下面结构
program_number (16bit) 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PAT
reserved (3bbit) 固定为111
PID (13bit) 节目号对应内容的PID值0x1000, 也就是pmt的pid
结束循环
CRC32 (32bit) 前面数据的CRC32校验码
PMT(Program Map Table), 节目映射表, 该包的PID可以由PAT表获取。
table_id (8bit) PMT表取值随意, 这里给定0x02
section_syntax_indicator (1bit) 段语法标志位, 固定为1
zero (1bit) 固定为0
reserved (2bit) 保留位,固定为11
section_length (12bit) 后面数据的长度, 表示从下一个字段开始到CRC32(含)之间有用的字节数
program_number (16bit) 频道号码,表示当前的PMT关联到的频道, 取值0x0001
reserved (2bit) 固定为11
version_number (5bit) PMT版本号码,固定为00000,如果PAT有变化则版本号加1
current_next_indicator (1bit) 发送的PMT表是当前有效还是下一个PMT有效, 固定为1
section_number (8bit) 固定为0x00, PMT可能分为多段传输,第一段为00, 以后每个分段加1, 最多可能有256个分段
last_section_number (8bit) 分段数,固定为0x00
reserved (3bit) 保留位, 固定为111
PCR_PID (13bit) PCR(节目参考时钟)所在TS分组的PID,指定为视频PID
reserved (4bit) 固定为1111
program_info_length (12bit) 节目描述信息,指定为0x000表示没有,前2bit为00, 该域指出跟随其后对节目信息的描述的字节数。
开始循环,按顺序可能包含多个
stream_type (8bit) 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03
reserved (3bit) 固定为111
elementary_PID (13bit) 与stream_type对应的PID
reserved (4bit) 固定为1111
ES_info_length (12bit) 描述信息,指定为0x000表示没有
结束循环
reserved_5 3bit 保留位, 固定为0x07
reserved_6 4bit 保留位, 固定为0x0F
CRC32 32bit 前面数据的CRC32校验码
adaptation字段用在音视频数据不足的时候的数据填充.
adaptation_field_length (8bit) adaption字段的长度(后面的字段不包括当前字段)
discontinuity_indicator (1bit) 指示数据流是否存在不连续的情况
randort7_acce55_indicator (1bit) 指示数据流中是否存在重要的组或关键帧
elementary_stream_priority_indicator (1bit) 指示数据流的优先级
PCR_flag (1bit) PCR(Programme Clock Reference)字段是否存在
OPCR_flag (1bit) OPCR字段是否存在
splicing_point_flag (1bit) 数据流中是否存在插播点
transport_private_data_flag (1bit) 是否存在私有数据
adaptation_field_extension_flag (1bit) 用于指示是否存在ADAPTATION FIELD EXTENSION
//PCR_flag==1 控制TS数据流同步的42位时间戳
program_clock_reference_base (33bit)
Reserved (6bit)
program_clock_reference_extension (9bit)
//OPCR_flag==1 ( Optional Program Clock Reference)
//用于同步接收到的多个数据流,其在时间戳方面与 PCR 相似,但只在数据流中的某些节目中使用
original_program_clock_reference_base 33bit
Reserved 6bit
original_program_clock_reference_extension 9bit
//splicing_point_flag==1
splice_countdown (8bit)
//transport_private_data_flag==1
transport_private_data_length (8bit)
for (1=0,i<transport_private_data_length; i++){
private data byte (8bit)
}
//adaptation_field_extension_flag==1
adaptation_field_extension_length (8bit)
Itw_flag (1bit)
piecewise_rate_flag (1bit)
seamless_splice_flag (1bit)
Reserved (5bit)
//Itw_flag==1
ltw_valid_flag (1bit)
ltw_offset (15bit)
//piecewise_rate_flag ==1
reserved (2bit)
piecewise_rate (22bit)
//seamless_splice_flag==1
Splice_type (4bit)
DTS_next_AU[32..30] (3bit)
marker_bit (1bit)
DTS_next_AU[29 ..15] (15bit)
marker_bit (1bit)
DTS_next_AU[14..0] (15bit)
marker_bit (1bit)
for (i=0;i<N;i++){
reserved (8bit)
}
//last
for(i=0i<N;i++){
stuffing_byte (8bit)
}
打包TS流时PAT和PMT表是没有Adaptation 字段的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field, 通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。
打包ts文件的时候需要注意, 需要添加PCR时钟, 否则在VLC播放器中播放会失败。无法获取视频的时长。
这里以java实现为例,介绍一下PCR值生成的方法:
void create_adaption_field(AdaptionField adption_field, long pts, boolean has_pcr)
{
int index = 0;
Arrays.fill(adption_field.adaption_buffer,0,adption_field.adaption_buffer.length, (byte) 0xFF);
if (has_pcr)
{
//包含pcr
long pcrValue = pts * 300;
long PCR_BSE = pcrValue / 300;
long PCR_EXT = pcrValue % 300;
后续可能会更新,7个字节
adption_field.adaption_buffer[index++] = 0x07;
//取0x50表示包含PCR
adption_field.adaption_buffer[index++] = 0x50;
adption_field.adaption_buffer[index++] = (byte)((byte)0xff & (PCR_BSE >> 25)); //PCR_BASE的前8位
adption_field.adaption_buffer[index++] = (byte)((byte)0xff & (PCR_BSE >> 17)); //PCR_BASE的紧跟8位
adption_field.adaption_buffer[index++] = (byte)((byte)0xff & (PCR_BSE >> 9)); //PCR_BASE的紧跟8位
adption_field.adaption_buffer[index++] = (byte)((byte)0xff & (PCR_BSE >> 1)); //PCR_BASE的紧跟8位
adption_field.adaption_buffer[index++] = (byte)((PCR_BSE << 7 | PCR_EXT >> 8 | 0x7e) & 0xFF);
adption_field.adaption_buffer[index++] = (byte)(0xff & PCR_EXT);
}
else
{
//不包含pcr
adption_field.adaption_buffer[index++] = 0x07; //后续可能会更新, 7个字节
adption_field.adaption_buffer[index++] = 0x00; //0x00都不包含
}
adption_field.adaption_field_size = index;
}
pes层(ts层中payload)是在每一个视频/音频帧上加入了时间戳等信息, pes包内容很多, 我们只留下最常用的。
长度为14(只包含pts)/18(包含pts和dts) 个字节
pes_start_code (24bit) 开始码,固定为0x000001
stream id (8bit) 音频取值(0xc0~0xdf),通常为0xc0,视频取值(0xe0-0xef),通常为0xe0
pes_packet_length (16bit) 后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff
flag (8bit) 是否加密, 通常取值0x80,表示数据不加密、无优先级、备份的数据
flag (8bit) 是否包含pts和dts, 取值0x80表示只含有pts,取值0xc0表示含有pts和dts
pes_data_length (8bit) 后面数据的长度,取值5或10
pts (40bit) pts显示时间戳
dts (40bit) dts解码时间戳
pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要, 音频数据的pts和dts相同,所以只需要pts。
有pts和dts两种时间戳是B帧引起的, I帧和P帧的pts等于dts。如果一个视频没有B帧, 则pts永远和dts相同。从文件中顺序读取视频帧, 取出的帧顺序和dts顺序相同。
这里以一帧AAC音频数据为例说明一下TS封装格式的分包策略, 通常一个TS数据包无法传输一个完整的帧, 需要将一帧音视频数据拆分成多个数据包, 详细策略如下:
1.首先在流里面添加PAT包和PMT包作为流的开始
2.然后添加第一个PES包作为数据包的开始, 包含Adaption、PES。包中包含此帧的长度, 采样率, pts/dts等信息。
3.中间添加拆分的数据PES包, 包中只包含tsheader和音视频数据
4.最后一个数据包,作为结尾包含ts header、Adaption、音视频数据,但不包含PES字段。
对应的结构如下图所示:
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
| TS | = | PAT包 | PMT包 | PES1 | ... | PES n |
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
第一个PES包: 4Byte 8Byte 14Byte(pts) 162Btte
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet | = | ts header | Adaption | PES | paylod |
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
中间的PES包, 只包含tsheader和payload, 不包含adaption和pes
4Byte 184Byte
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet | = | ts header | payload |
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
最后一个PES包 包含tsheader、adaption、payload
4 byte x byte 184-x byte
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | adaptation field | payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+