TS视频封装协议详细说明

TS封装格式概述

TS(Transport Stream) 是一种常见的视频封装格式, 主要用于数字电视和在线视频传输领域。优缺点如下所示:
优点:
1.容错性强: 传输过程中发生丢包或错误信息丢失的情况, 可以快速恢复, 保障数据的完整性。
2.支持随机访问: TS格式对数据进行了分段, 每段可以单独访问和控制, 实现了视频的随机访问。
3.实时性好: TS 封装格式采用分段传输的方式,每个数据片段的大小相对较小,可以有效降低延迟,实现了实时数据传输。
4.可靠性好: TS 封装格式支持多路复用,能够对多个数据流进行混合,提高数据传输效率和可靠性。

缺点:
1.编解码复杂: TS 封装格式需要将视频数据进行分段和整合,对音视频编码技术的要求较高
2.文件较大: TS分段机制, 引入了一定的数据头信息
3.不支持字幕处理:TS 封装格式不支持对字幕数据进行处理,用户需要手动添加字幕

TS数据包

TS封装格式的数据单元是ts包。 每个包都有自己的pid, 包的大小固定为188字节。
ts包分为三类分别为:pat包、pmt包、pes包:

  • PAT(Program Associate Table)包为第一个包, 也就是ts的入口, 它的pid固定为为0x00; pat包里面有pmt包的pid, 通过pat包能定位到pmt包
  • PMT(Program Map Table) 用于描述一个流的内容信息,包括音频、视频或数据信息。 pmt包里面存的就是数据流包的pid,音频包的PID是0x102, 视频包的PID是0x101
  • PES(Packetized Elementary Stream)包是一种基本的数据单元,用于传输音频、视频等多媒体数据信息。

在TS流中, PAT和PMT包重复实现, 大约0.5秒出现一个,保证实时解码性。PAT表是TS流的基础, 任何一个TS流解析寻找节目都是从PAT表开始查找。

TS数据包的结构

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 header的数据结构

每个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字段的数据结构

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字段的数据结构

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字段用在音视频数据不足的时候的数据填充.

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字段的数据结构

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)        	后面数据的长度,取值510
pts	                (40bit)	        pts显示时间戳
dts	                (40bit)	        dts解码时间戳

pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要, 音频数据的pts和dts相同,所以只需要pts。
有pts和dts两种时间戳是B帧引起的, I帧和P帧的pts等于dts。如果一个视频没有B帧, 则pts永远和dts相同。从文件中顺序读取视频帧, 取出的帧顺序和dts顺序相同。

TS数据流分包策略

这里以一帧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        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

你可能感兴趣的:(音视频,音视频,封装协议)