TS 流、包结构以及同步
1. TS 流: 可以将TS流理解为一种单一码流、混合码流。
单一码流:TS流的基本组成单位是长度为188字节的TS包。
混合码流:TS流有多种数据组成,一个TS包中的数据可以是视频数据、音频数据、填充数据,PSI/SI表格数据.....(唯一的PID对应)
2. TS 包结构分析:
TS 包由包头、有效载荷区组成。(有些包中包括自适应区)。大小: 188 字节
TS包头:4 个字节
同步字节 0x47,用于检测码流是否同步。
包ID: PID, 解码器通过该标志号确定该TS包中的数据属于那种类型。
PCR: 自适应区, 解码器通过该时间参数,进行解码端的时钟重置。
有效载荷: 最高 184 字节
视频、音频 or 其他数据
语法:
五、
HLS,Http Live Streaming 是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。
1、M3U8文件
用文本方式对媒体文件进行描述,由一系列标签组成。
#EXTM3U
#EXT-X-TARGETDURATION:5
#EXTINF:5,
./0.ts
#EXTINF:5,
./1.ts
#EXTM3U:每个M3U8文件第一行必须是这个tag。
#EXT-X-TARGETDURATION:指定最大的媒体段时间长度(秒),#EXTINF中指定的时间长度必须小于或等于这个最大值。该值只能出现一次。
#EXTINF:描述单个媒体文件的长度。后面为媒体文件,如./0.ts
2、ts文件
ts文件为传输流文件,视频编码主要格式h264/mpeg4,音频为acc/MP3。
ts文件分为三层:ts 层 Transport Stream, 就是在pes层加入数据流的识别和传输必须的信息;
pes 层 Packet Elemental Stream,pes层是在音视频数据上加了时间戳等对数据帧的说明信息;
es层 Elementary Stream. es层就是音视频数据。
(1)ts层
ts包大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。
ts header
sync_byte | 8 | 同步字节,固定为0x47 |
transport_error_indicator | 1 | 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内 |
payload_unit_start_indicator | 1 | 负载单元起始标示符,一个完整的数据包开始时标记为1, 表示携带的是PSI或PES第一个包 |
transport_priority | 1 | 传输优先级,0为低优先级,1为高优先级,通常取0 |
pid | 13 | pid值 |
transport_scrambling_control | 2 | 传输加扰控制,00表示未加密,TS包中的有效数据未经加扰处理。 |
adaptation_field_control | 2 | 是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。 |
continuity_counter | 4 | 递增计数器,从0-f,起始值不一定取0,PID相同的包,计数器必须是连续的 |
ts层的内容是通过PID值来标识的,主要内容包括:PAT表、PMT表、音频流、视频流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音视频流了。PAT表的PID值固定为0。PAT表和PMT表需要定期插入ts流,因为用户随时可能加入ts流,这个间隔比较小,通常每隔几个视频帧就要加入PAT和PMT。PAT和PMT表是必须的,还可以加入其它表如SDT(业务描述表)等,不过hls流只要有PAT和PMT就可以播放了。
PAT表:主要的作用就是指明了PMT表的PID值。
PMT表:主要的作用就是指明了音视频流的PID值。
音频流/视频流:承载音视频内容。
PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。下表给出了一些表的PID值,这些值是固定的,不允许用于更改。
adaptio
adaptation_field_length | 1B | 自适应域长度,后面的字节数 |
flag | 1B | 取0x50表示包含PCR或0x40表示不包含PCR |
PCR | 5B | Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。 |
stuffing_bytes | xB | 填充字节,取值0xff |
自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有字段,ipad是可以播放的,但vlc无法播放。打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。
PAT格式
table_id | 8b | PAT表固定为0x00 |
section_syntax_indicator | 1b | 固定为1 |
zero | 1b | 固定为0 |
reserved | 2b | 固定为11 |
section_length | 12b | 后面数据的长度 |
transport_stream_id | 16b | 传输流ID,固定为0x0001 |
reserved | 2b | 固定为11 |
version_number | 5b | 版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1b | 固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 |
section_number | 8b | 固定为0x00, 表明这个段是该pat表的第几个段。 |
last_section_number | 8b | 固定为0x00, 表明PAT表一共有多少段。 |
开始循环 | ||
program_number | 16b | 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT |
reserved | 3b | 固定为111 |
PID | 13b | 节目号对应内容的PID值 |
结束循环 | ||
CRC32 | 32b | 前面数据的CRC32校验码 |
PMT格式
table_id | 8b | PMT表取值随意,0x02 |
section_syntax_indicator | 1b | 固定为1 |
zero | 1b | 固定为0 |
reserved | 2b | 固定为11 |
section_length | 12b | 后面数据的长度 |
program_number | 16b | 频道号码,表示当前的PMT关联到的频道,取值0x0001 |
reserved | 2b | 固定为11 |
version_number | 5b | 版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1b | 固定为1 |
section_number | 8b | 固定为0x00 |
last_section_number | 8b | 固定为0x00 |
reserved | 3b | 固定为111 |
PCR_PID | 13b | PCR(节目参考时钟)所在TS分组的PID,指定为视频PID |
reserved | 4b | 固定为1111 |
program_info_length | 12b | 节目描述信息,指定为0x000表示没有 |
开始循环 | ||
stream_type | 8b | 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03 |
reserved | 3b | 固定为111 |
elementary_PID | 13b | 与stream_type对应的PID |
reserved | 4b | 固定为1111 |
ES_info_length | 12b | 描述信息,指定为0x000表示没有 |
结束循环 | ||
CRC32 | 32b | 前面数据的CRC32校验码 |
(2)pes层
pes层是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。
pes start code | 3B | 开始码,固定为0x000001 |
stream id | 1B | 音频取值(0xc0-0xdf),通常为0xc0 视频取值(0xe0-0xef),通常为0xe0 |
pes packet length | 2B | 后面pes数据的长度,0表示长度不限制, 只有视频数据长度会超过0xffff |
flag | 1B | 通常取值0x80,表示数据不加密、无优先级、备份的数据 |
flag | 1B | 取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
pes data length | 1B | 后面数据的长度,取值5或10 |
pts | 5B | 33bit值 |
dts | 5B | 33bit值 |
pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的pts和dts相同,所以只需要pts。有pts和dts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量。
音频的pes中只有pts(同dts),视频的I、P帧两种时间戳都要有,视频B帧只要pts(同dts)。打包pts和dts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型。
举例说明:
I P B B B P
读取顺序: 1 2 3 4 5 6
dts顺序: 1 2 3 4 5 6
pts顺序: 1 5 3 2 4 6
点播视频dts算法:
dts = 初始值 + 90000 / video_frame_rate,初始值可以随便指定,但是最好不要取0,video_frame_rate就是帧率,比如23、30。
pts和dts是以timescale为单位的,1s = 90000 time scale , 一帧就应该是90000/video_frame_rate 个timescale。
用一帧的timescale除以采样频率就可以转换为一帧的播放时长
点播音频dts算法:
dts = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate,
audio_samples_per_frame这个值与编解码相关,aac取值1024,mp3取值1158,audio_sample_rate是采样率,比如24000、41000。AAC一帧解码出来是每声道1024个sample,也就是说一帧的时长为1024/sample_rate秒。所以每一帧时间戳依次0,1024/sample_rate,...,1024*n/sample_rate秒。
直播视频的dts和pts应该直接用直播数据流中的时间,不应该按公式计算。
(3)es层
es层指的就是音视频数据,我们只介绍h.264视频和aac音频。
h.264视频:
打包h.264数据我们必须给视频数据加上一个nalu(Network Abstraction Layer unit),nalu包括nalu header和nalu type,nalu header固定为0x00000001(帧开始)或0x000001(帧中)。h.264的数据是由slice组成的,slice的内容包括:视频、sps、pps等。nalu type决定了后面的h.264数据内容。
F | 1b | forbidden_zero_bit,h.264规定必须取0 |
NRI | 2b | nal_ref_idc,取值0~3,指示这个nalu的重要性,I帧、sps、pps通常取3,P帧通常取2,B帧通常取0 |
Type | 5b | 参考下表 |
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 | 未使用 |
红色字体显示的内容是最常用的,打包es层数据时pes头和es数据之间要加入一个type=9的nalu,关键帧slice前必须要加入type=7和type=8的nalu,而且是紧邻。
四、gs流
由于DVB—S在带宽利用率以及调制编码方面的不足,在第二代数字卫星广播标准(the second generation of digital video broadcasting.satellite, DVB・S2) 中,
采用了更先进的调制编码方式,在兼容TS流的基础上,引入一种全新的码流,即通用流 (general stream,GS)。在相同的条件下,DVB.S2比DVB.S节省了约30%的带宽 。
协议数据单元(protocol data unit,PDU)
通用流封装(general stream en. capsulation,GSE)
根据基带帧:
MATYPE的首个字节(MATYPE.1)的TS/GS域,指示了传输流格式是Ts流或GS流,
SIS/MIS域指示了输入流是单输人还是多输入。SIS/MIS值为“l”,表示单输入流,
MATYPE的第2个字节(MATYPE.2)等于输入流标示符(inputstream identi.tier,IsI),不为“0”
SIS/MIS值为“0”,表示多输入流,MATYPE.2为预留字节,值一般为“Ox00”。UPL域表示用户包长度,单位是比特。UPL值为“0000。。。”,表示输入流是连续流,UPL值为“188X8。”,表示用户包是MPEG传输流包,包长为188字节。
IP数据的GS流的基带帧头部有以下特征:(1)MATYPE.1的TS/GS值为二进制“01”;(2)MATYPE一1的SIS/MIS值为“1”时,MATYPE-2字节不为“0”;SIS/MIS值为“0”时,MATYPE一2字节为“0x00”;(3)UPL值为“0x0000”;(4)DFL值被8整除,因为DFL域的第1个字节为高位字节,所以DFL域的第2个字节即低位字节也被8整除:(5)CRC.8不对基带帧头部前9个字节进行错误校验。
若基带帧为GS流提取GSE header如下
(1)s域表明是起始分段,e域为结束分段
(2)lt为label_type类型:
值为“00”,label域为6字节,用于寻址,相当于以太网中的MAC地址;LT值为“11”,表示同一个基带帧中先前的GSE包已经使用过的label可以再用,该GSE包头不再有label域,也就是说,对于按顺序传输的具有相同label的GSE包,GSE包头的label域不需要重复出现。需要注意的是,基带帧第1个GSE包的LT值不能为“11”,即基带帧的第1个GSE包不能为PDU中间分段所在的GSE包。
(3)gse_length: 该gse包大小
(4)total_length: 该pdu整段总长,第一分段出现
(5)protocol_type : 协议类型, 第一分段出现
(6)frag_id:用来指示该pdu段属于哪一个pdu
(7)CRC:pdu_end段出现用来进行crc校验
TS数据流PAT和PMT分析
TS流,是基于packet的位流格式,每个packet是188个字节或者204个字节(一般是188字节,204字节格式是在188字节的packet后面加上16字节的CRC数据,其他格式相同),解析TS流,先解析每个packet ,然后从一个packet中,解析出PAT的PID,根据PID找到PAT包,然后从PAT包中解析出PMT的PID,根据PID找到PMT包,在从PMT包中解析出Video和Audio(也有的包括Teletext和EPG)的PID。然后根据PID找出相应的包。
所有packet格式都是同一的,包括packet header和packet datas。
acket header格式如下:
typedef struct {
unsigned sync_byte : 8位; /*8bits的同步字节,固定为0x47,表示后面是一个TS分组。*/
unsigned transport_error_indicator : 1位 /*1位的错误指示信息,1表示当前packet至少有1bit的传输错误,0表示所有数据都正确. 一般传输错误的话就不会处理这个包了。*/
unsigned payload_unit_start_indicator : 1位 /*负载单元开始标志,*/
unsigned transport_priority : 1位 /*1bit的传输优先级标志,1表示高优先级,0表示低优先级,传输机制可能用到,解码好像用不到*/
unsigned PID : 13 位 /*13 bits的packet ID号码,唯一的号码对应不同的包,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容*/
unsigned transport_scrambling_control : 2位 /*2 bits 的加密标志,00表示没有加密,其他表示已被加密,表示TS分组有效负载的加密模式。
TS首部(也就是前面着32个 bit)是不应被加密的*/
unsigned adaptation_field_control : 2位 /* 2bits的附加区域控制,表示TS分组首部后面是否跟随有调整字段和有效负载。
01仅有有效负载,10仅含调整字段,11含有调整字段和有效负载。00的话解码器不进行处理。空分组没有调整字段。*/
unsigned continuity_counter : 4位 /*4bits的包递增计数器,范围是0-15,具有相同PID的TS分组传输时每次加1,到15后清0.不过有些时候是不计数的。
如下:1 、TS分组无有效负载,2、复制的TS分组和原分组这个值一样。3、后面讲到的一个标志discontinuity——indicator为1时*/
} packet_header
PAT数据结构
program_association_section() {
table_id // 8位 固定为0x00,表示该表是PAT表
section_syntax_indicator // 1 段语法标志位,固定为1
'0' // 1 固定为1 为了防止和ISO13818Video流格式中的控制字冲突而设置的
reserved // 2 保留位,一般都是0
section_length // 12 段的大小,表示这个字节后面有用的字节数,包括CRC32.假如后面的字节加上前面的字节数少于188,后面会用0xFF填充,
假如这个数值比较大,则PAT会分成几部分来传输。
transport_stream_id // 16 该传输流的ID,区别于一个网络中其他多路复用的流,
reserved // 2
version_number // 5 范围0-31,表示PAT的版本号,标注当前节目的版本,这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经改变了,程序必须重新收索节目
current_next_indicator // 1 表示发送的PAT是当前有效还是下一个PAT有效
section_number // 8 分段的号码,PAT可能分为多个段传输,第一段为00,以后每个分段加1,最大可能有256个分段
last_section_number // 8 最后一个分段的号码
for(i=0;i<N;i++) {
program_number // 16 节目号
reserved // 3
if (program_number == '0') {
network_PID // 13 网络信息表(NIT)的PID,网络信息表提供了该物理网络的一些信息,和电视台相关的,节目号为0时对应的PID为network_PID
}
else {
program_map_PID // 13 节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
}
}
CRC_32 // 32 CRC32校验码
}
typedef struct TS_PAT
{
unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT表
unsigned section_syntax_indicator : 1; //段语法标志位,固定为1
unsigned zero : 1; //0
unsigned reserved_1 : 2; // 保留位
unsigned section_length : 12; //表示从下一个字段开始到CRC32(含)之间有用的字节数
unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2; // 保留位
unsigned version_number : 5; //范围0-31,表示PAT的版本号
unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效
unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
unsigned last_section_number : 8; //最后一个分段的号码
std::vector<TS_PAT_Program> program;
unsigned reserved_3 : 3; // 保留位
unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID
unsigned CRC_32 : 32; //CRC32校验码
} TS_PAT;
例子 47 40 00 1c 00 00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77 ff ff ff ff ......ff
其中,前4个字节是TS分组的头部,01000111 01000000 00000000 0001 1100 ,可以解析 出PID= (buf[1]&0x0f)<<8 | buf[2] =0x00,表示为该分组的有效负载是PAT。
第五个字节00称为“指针域”,表示一个偏移量,即从后面第几个字节开始时PAT部分。为00表示后面紧跟着的就是PAT:00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77(00000000 10110000 00010101 00010011 11110110 11100111 00000000 00000000 00000000 00000000 11100000 00010000 00000000 00000001 11100000 00100000 00000000 00000010 11100000 00100001 00011010 00110100 10110100 01110111)
利用PAT数据结构解析出PAT,可得到如下信息:
table_id : 0x00
section_syntax_indicator : 0x01
section_length : 0x015 表示后面有21个字节有用
transport_stream_id : 0x13f6
version_number : 0x13
current_next_indicator : 0x01
section_number : 0x00
last_section_number : 0x00 表示只有一个分段。
program_number : 0x0000
network_PID : 0010
program_number : 0001
program_map_PID : 0020
program_number : 0002
program_map_PID : 0021
CRC_32 : 1a34b477
此PAT只有一段,包含了三个节目,节目号0000对应于network_PID=0010 ,节目号0001对应于program_map_PID =0020,节目号0002对应于program_map_PID =0021,从实际的角度,我们应该把这三个节目号理解为三个频道,第一个频道中的内容是网络信息,第二、三个频道包含了节目信息。在数字电视中,一个频道上可以有多个节目,后面的PMT即是告诉了我们某个频道中所有节目对应的PID。
于是现在就搜寻PID=0x0020的TS分组,即是频道2对应的PMT信息。
PMT数据结构:
TS_program_map_section() {
table_id // 8 固定为0x02,表示该表是PMT
section_syntax_indicator // 1 段语法标志位,固定为1
'0' // 1
reserved // 2
section_length // 12 表示这个字节后面有用的字节数,包括CRC32。假如后面的字节加上前面的字节数少于188,后面会用0XFF填充。
假如这个数值比较大,则PAT会分成几部分来传输。
program_number // 16 节目号,表示该PMT对应的节目
reserved // 2
version_number // 5 范围0-31,表示PAT的版本号,标注当前节目的版本,这是个非常有用的参数,
当检测到这个字段改变时,说明TS流中的节目已经改变了,程序必须重新收索节目
current_next_indicator // 1 表示发送的PAT是当前有效还是下一个PAT有效
section_number // 8 分段的号码 固定为0x00
last_section_number // 8 最后分段的号码 固定为0x00
reserved // 3
PCR_PID // 13 PCR(节目时钟参考)所在TS分组的PID,根据PID可以去搜索相应的TS分组,解出PCR信息。
reserved // 4
program_info_length // 12 该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息
for (i=0; i<N; i++) {
descriptor()
}
for (i=0;i<N1;i++) {
stream_type // 8 指示了PID为elementary_PID的PES分组中原始流的类型,比如视频流,音频流等
reserved // 3
elementary_PID // 13 该节目中包括的视频流,音频流等对应的TS分组的PID
reserved // 4
ES_info_length // 12 该节目相关原始流的描述符的信息长度
for (i=0; i<N2; i++) {
descriptor()
}
}
CRC_32 // 32
}
//PMT 表结构体
typedef struct TS_PMT
{
unsigned table_id : 8; //固定为0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定为0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12; //首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
unsigned program_number : 16; // 指出该节目对应于可应用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本号
unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用;
//当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
unsigned section_number : 8; //固定为0x00
unsigned last_section_number : 8; //固定为0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,
//该PCR值对应于由节目号指定的对应节目。
//如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
unsigned reserved_4 : 4; //预留为0x0F
unsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
} TS_PMT;
例子PMT包: 47 40 20 1c 00 02 b0 1f 00 01 e7 00 00 e1 00 f0 00 02 e1 00 f0 05 02 03 b2 44 5f 04 e1 10 f0 03 03 01 67 c9 ab c8 d2
前4个字节是TS分组头部, 第五个字节00是指针域。后面的就是PMT的内容了。
table_id : 02
section_syntax_indicator : 01
section_length : 01f
program_number : 0001
version_number : 13
current_next_indicator : 01
section_number : 00
last_section_number : 00
PCR_PID : 0100
program_info_length : 000
descriptor:
steam_type : 00
elementary_PID : 0001
ES_info_length : 000
descriptor:
steam_type : 02
elementary_PID : 0001
ES_info_length : 005
descriptor : 02 03 b2 44 5f
steam_type : 04
elementary_PID : 0011
ES_info_length : 003
descriptor : 03 01 67
CRC_32 : c9abc8d2
可以看出,该节目号0001包含了三个流的信息,流类型分别为00,02,04,00的流为保留值,可以不考虑,02表示原始流为视频流,其elementary_PID为0001,04表示原始流为音频流,其elementary_PID为0011,两个流分别还带有descriptor(描述符),说明了该原始流的一些信息。
:流类型取值说明
取值 | 描述 |
0x00 | 国际标准保留 |
0x01 | 视频 |
0x02 | 视频或受限参数视频流 |
0x03 | 音频 |
0x04 | 音频 |
0x05 | private_sections |
0x06 | 包含专用数据的PES分组 |
0x07 | ISO/IEC 13533 MHEG |
0x08 | |
0x09 | ITU-T Rec.H.222.1 |
0x0A~0x0D | GB/T类型 |
0x0E | GB/T辅助 |
0x0F~0x7F | GB/T保留 |
0x80~0xFF | 用户专用 |
从TS开始
ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流(Packet Elementary Stream):把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。
PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)
PSI(Program Specific Information 节目特定信息)信息,其作用是从一个携带多个节目的某一个TS流中正确找到特定的节目。
PSI表包括节目关联表(PAT)、条件接收表(CAT)、节目映射表(PMT)和网络信息表(NIT)组成。
PAT 节目关联表(PAT Program Association Table): PAT是机顶盒接收的入口点,是它获取数据的开始
CAT 条件接收表(CAT Conditional Access Table):
PMT 节目映射表(PMT Program Map Table) PMT表的作用就在于,它提供了每个节目视频、音频(或其他)数据包的PID。
NIT 网络信息表(NIT Nerwork Information Table)
SI 业务信息表 (Service information ) : 由一下九个表组成,其中SDT、EIT、TDT是必须包括的,其它是可选的。
SDT表(Service Descriptor Table,业务描述表): 描述了包含在特定TS流中的全部业务的相关信息,如业务名称、业务提供者等。
EIT(事件信息表): 描述了包含在特定业务中的所有事件的相关信息。包含了与事件或节目相关的数据,如事件名称、开始时间、持续时间等。
TDT(时间和日期表): 给出了与当前的时间和日期相关的信息。由于这些信息频繁更新,所以需要使用一个单独的表。
BAT(业务群关联表)
RST(运行状态表)
TOT(时间偏移表)
ST(填充表)
SIT(选择信息表)
DIT(间断信息表)
TSDT 传输流描述表(TSDT Transport Stream Description Table)
TS是经过节目复用和传输复用两层完成的,即在节目复用时,加入了PMT,在传输复用时,加入了PAT。同样在节目解复用时,可以得到PMT,在传输解复用时,可以得到PAT。
结构名 | 中文 | 所定义标准 | PID | 描述 |
PAT | 节目关联表 | MPEG2标准 | 0x0000 | 将节目号码和节目映射表PID相关联,是获取数据的开始 |
PMT | 节目映射表 | MPEG2标准 | 在PAT中指出 | 指定一个或多个节目的PID |
CAT | 条件接收表 | MPEG2标准 | 0x0001 | 将一个或多个专用EMM流分别与唯一的PID相关联 |
NIT | 网络信息表 | SI标准 | PAT中指出 | 描述整个网络,如多少个TS流、频点和调制方式等信息 |
*NOTE:
TS流和PS流的区别:TS流的包结构是长度是固定的;PS流的包结构是可变长度的。这导致了TS流的抵抗传输误码的能力强于PS流(TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。而PS包由于长度是变化的,一旦某一 PS包的同步信息丢失,接收机无法确定下一包的同步位置,就会造成失步,导致严重的信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流。)
由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格。
从上图可以看出,视频ES和音频ES通过打包器和共同或独立的系统时间基准形成一个个PES,通过TS复用器复用形成的传输流。注意这里的TS流是位流格式(分析Packet的时候会解释),也即是说TS流是可以按位读取的。
TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了16字节的CRC校验数据,其他格式一样)。整个TS流组成形式如下:
Packet Header(包头)信息说明 | |||
1 | sync_byte | 8bits | 同步字节 |
2 | transport_error_indicator | 1bit | 错误指示信息(1:该包至少有1bits传输错误) |
3 | payload_unit_start_indicator | 1bit | 负载单元开始标志(packet不满188字节时需填充) |
4 | transport_priority | 1bit | 传输优先级标志(1:优先级高) |
5 | PID | 13bits | Packet ID号码,唯一的号码对应不同的包 |
6 | transport_scrambling_control | 2bits | 加密标志(00:未加密;其他表示已加密) |
7 | adaptation_field_control | 2bits | 附加区域控制 |
8 | continuity_counter | 4bits | 包递增计数器 |
PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。下表给出了一些表的PID值,这些值是固定的,不允许用于更改。
表 | PID 值 |
PAT | 0x0000 |
CAT | 0x0001 |
TSDT | 0x0002 |
EIT,ST | 0x0012 |
RST,ST | 0x0013 |
TDT,TOT,ST | 0x0014 |
下面给出了PID字段的取值要求:
值 | 描述 |
0x0000 | PAT |
0x0001 | CAT |
0x0002~0x000F | 保留 |
0x0010~0x1FFE | 可赋给network_PID、Program_map_PID、elementary_PID或作其他用途 |
0x1FFF | 空的分组 |
下面以一个TS流的其中一个Packet中的Packet Header为例进行说明:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | … | |
Packet(十六进制) | 4 | 7 | 0 | 7 | e | 5 | 1 | 2 | … | ||||||||||||||||||||||||
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | … |
Packet Header 信息 | 1 sync_byte=0x47 | 2 | 3 | 4 | 5 PID=0x07e5 | 6 | 7 | 8 | … |
sync_byte=01000111, 就是0x47,这是DVB TS规定的同步字节,固定是0x47.
transport_error_indicator=0, 表示当前包没有发生传输错误.
payload_unit_start_indicator=0, 含义参考ISO13818-1标准文档
transport_priority=0, 表示当前包是低优先级.
PID=00111 11100101即0x07e5, Video PID
transport_scrambling_control=00, 表示节目没有加密
adaptation_field_control=01 即0x01,具体含义请参考ISO13818-1
continuity_counte=0010 即0x02,表示当前传送的相同类型的包是第3个
PAT表定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,要查寻找节目必须从PAT表开始查找。
PAT表携带以下信息:
TS流ID | transport_stream_id | 该ID标志唯一的流ID |
节目频道号 | program_number | 该号码标志TS流中的一个频道,该频道可以包含很多的节目(即可以包含多个Video PID和Audio PID) |
PMT的PID | program_map_PID | 表示本频道使用哪个PID做为PMT的PID,因为可以有很多的频道,因此DVB规定PMT的PID可以由用户自己定义 |
PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据
PAT表描述了当前流的NIT(Network Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号。
(二) PMT表(Program Map Table,节目映射表)(Service Descriptor Table)
1. PMT表的描述
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。
每一个节目的所有信息必须包含在一个PMT中,但在一个PMT中可以包含多个节目信息。PMT本身的PID由PAT表格提供。
PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。这样就可以将Video、Audio或者其他业务信息的数据区分开来。
(四) DVB搜台原理以及SDT表(Service Descriptor Table,业务描述表)
机顶盒先调整高频头到一个固定的频率(如498MHZ),如果此频率有数字信号,则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG- 2 decoder。 MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000的Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID。接着循环查找是否出现PMT,如果发现了,则自动进入PMT分析,获取该频段所有的频道数据并保存。如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描。
在解析TS流的时候,首先寻找PAT表,根据PAT获取所有PMT表的PID;再寻找PMT表,获取该频段所有节目数据并保存。这样,只需要知道节目的PID就可以根据PacketHeade给出的PID过滤出不同的Packet,从而观看不同的节目。这些就是PAT表和PMT表之间的关系。而由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了SDT表来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。
SDT可以提供的信息包括:
(1) 该节目是否在播放中
(2) 该节目是否被加密
(3) 该节目的名称
三、 从PAT开始,走向更远
在本章的学习中,我们发现了一个特点:所有的TS流的解析都是从寻找PAT表开始的,只有找到了PAT表,我们才能继续下一步的解析。因此,在进行了TS流、PAT表和PMT表的初步知识储备后,在接下来的学习中将从PAT表开始,学习更多的PSI/SI相关的表,将走得更远。
TS流的解码过程-ES-PES-DTS-PTS-PCR
http://blog.csdn.net/soulxu/article/details/6167060
TS 流解码过程:
1. 获取TS中的PAT
2. 获取TS中的PMT
3. 根据PMT可以知道当前网络中传输的视频(音频)类型(H264),相应的PID,PCR的PID等信息。
4. 设置demux 模块的视频Filter 为相应视频的PID和stream type等。
5. 从视频Demux Filter 后得到的TS数据包中的payload 数据就是 one piece of PES,在TS header中有一些关于此 payload属于哪个 PES的 第多少个数据包。 因此软件中应该将此payload中的数据copy到PES的buffer中,用于拼接一个PES包。
6. 拼接好的PES包的包头会有 PTS,DTS信息,去掉PES的header就是 ES。
7. 直接将 被被拔掉 PES包头的ES包送给decoder就可以进行解码。解码出来的数据就是一帧一帧的视频数据,这些数据至少应当与PES中的PTS关联一下,以便进行视 音频同步。
8. I,B,B,P 信息是在ES中的。
ES是直接从编码器出来的数据流,可以是编码过的视频数据流,音频数据流,或其他编码数据流的统称。ES流经过PES打 包器之后,被转换成PES包。PES包由包头和payload组成.
在PES层, 主要是在PES包头信息中加入PTS(显 示时间标签)和DTS(解 码时间标签)用于视频、音频同步。其实,Mpeg-2用于视音频同步以及系统时钟恢复的时间标签分别在ES,PES和TS这3个层次 中。在ES层,与同步有关的主要是视频缓冲验证VBV(Video Buffer Verifier),用以防止解码器的缓冲器出现上溢或下溢;在PES层,主要是在PES头 信息里出现的显示时间标签PTS(Presentation Time Stamp)和解码时间标签DTS(Decoding Time Stamp);在TS层 中,TS头信息包含了节目时钟参考PCR(Program Clock Reference),用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。
基本流程如下:首先MPEG-2压缩编码得到的ES基本流,这个数据流很大,并且只是I,P,B的这些视频帧或音频取样信息,然后加入一些同步信息,打包成长度可变长度的数据包PES,原来是流的格式,现在成了数据包的分割形式。同时要注意的是,ES是只包含一种内容的数据流,如只含视频,或只含音频等,打包之后的PES也是只含一种性质的ES,如 只含视频ES的PES,只 含音频ES的PES等。 可以知道,ES是编码视频数据流或音频数据流,每个ES都由若干个存取单元(AU) 组成,每个视频AU或音频AU都是由头部和编码数据两部分组成,1个AU相当于编码的1幅视频图像或1个音频 帧,也可以说,每个AU实际上是编码数据流的显示单元,即相当于 解码的1幅视频图像或1个音频 帧的取样。PEG-2对视频的压缩产生I帧、P帧、B帧。把 帧顺序I1,P4,B2,B3,P7,B5,B6帧的编码ES,通 过打包并在每个帧中插入 PTS/DTS标志,变成PES。在插入PTS/DTS标志时,由于在B帧PTS和DTS相 等,所以无须在B帧多插入DTS。而对于I帧 和P帧,由 于经过复用后数据包的顺序会发生变化,显示前一定要存储于视频解码器的从新排序缓存器中,经过从新排序后再显示,所以一定要同时插入PTS和 DTS作 为从新排序的依据。
其中,有否PTS/DTS标志,是解决视音频同步显示、防止解码器输入缓存器上溢或下溢的关键所在。PTS表明显示单元出现在系统目标解码器(STD- System Target Decoder)的时间, DTS表明将存取单元全部字节从STD的ES解码缓存器移走的时刻。视频编码图像帧次序为 I1,P4,B2,B3,P7,B5,B6,I10,B8,B9的ES,加 入PTS/DTS后,打包成一个个视频PES包。每个PES包都有一个包头,用于定义PES内的数据内容,提供定时资料。每个I、P、B帧的包头都有一个PTS和DTS,但PTS与DTS对B帧都是 一样的,无须标出B帧的DTS。对I帧和P帧,显示前一定要存储于视频解码器的重新排序缓存器中,经过延迟(重新排序)后再显示,一定要分别标 明PTS和DTS。 例如,解码器输入的图像帧次序为I1,P4,B2,B3,P7,B5,B6,I10,B8,B9,依解码器输出的帧次序,应该P4比B2、B3在先,但显示时P4一定 要比B2、B3在 后,即P4要在提前插入数据流中的时间标志指引下,经过缓存器重新排序,以重建编码前视频帧次序I1,B2,B3,P4,B5,B6,P7,B8,B9,I10。显然,PTS/DTS标志表明对确定事件或确定信息解码的专用时标的存在,依靠专用时标解码器,可知道该确定事件或确定信息开始解码或显示的时刻。例如,PTS/DTS标志可用于确定编码、多路复用、解码、重建的时间。
PCR
PCR是TS里面的,即TS packet的header里面可能会有,他用来指定所期望的该ts packet到达decoder的时间,他 的作用于SCR类似。
DTS, PTS
对于一个ES来说,比如视频,他又许多I,P,B帧,而P, B帧都是以I,P帧作为参考。由于B帧是前向后向参考,因此要对B帧 作decode的话,就必须先decode该B帧后面的P,或者I帧,于是,decode的时间与帧的真正的present的时间就不一致了,按照DTS一 次对各个帧进行decode,然后再按照PTS对各个帧进行展现。
有时候PES包头里面也会有DTS,PTS,对于PTS来说,他代表了这个PES包得payload里面的第一个完整地audio access unit或者video access unit的PTS时间(并不是每个audio/video access unit都带有PTS/DTS,因此,你可以在PES里面指定一个,作为开始)。
PES包头的DTS也是这个原理,只 不过注意的是:对于video来说他的DTS和PTS是可以不一样的,因为B帧的存 在使其顺序可以倒置。而对于audio来说,audio没有双向的预测,他的DTS和PTS可以看成是一个顺序的,因此可一直采用一个,即可只采用PTS。
PES,TS,PS,RTP等流的打包格式解析之PES流
http://blog.csdn.net/appledurian/article/details/70851428
一、PES流
PES流是对原始ES流进行的第一层封装,PES流的基本单位是PES包,由包头和payload组成,ES流即音视频裸流,是从编码器里面出来的原始视频音频流;ES流只包含一种内容,里面是视频或者音频;封装时不对其进行改变,只在前面添加头部,如私有头,解码时,将私有头剥掉,将原始ES码流送进解码器解码,这也是解码通用性,若是修改了,则其他解码器就没法解码了;PES和ES一样,都是单一原始码流,一般我遇到的是一帧数据放在一个PES包里面,但是一个PES包的最大长度为65535字节,因此一帧数据有可能被分为多个PES;其包头格式如下:
可以看出,PES包是由固定包头,可选包头和负载三部分组成,其中固定包头固定6个字节;PES包长度字段占位16bit,最大值为65536,故一帧可能会分为多个PES包;下面依次介绍其每个字段的含义:
Packet start code prefix: 包头起始码,固定为0x000001,占位24bit;
Stream id: (UI)PES包中的负载流类型,一般视频为0xe0,音频为0xc0,占位8bit;
PES packet length: (UI)PES包长度,包括此字节后的可选包头和负载的长度,占位16bit;
Optional PES Header,顺序依次为:
’10’字段: 占位2bit;
PES scrambling control: 加密模式,占2bit;00未加密,01或10或11由用户定义;
PES priority: 有效负载的优先级,占位1bit;值为1比值为0的负载优先级高;
Data alignment indicator: 数据定位指示器,占位1bit;
Copyright: 版权信息,1为有版权,0无版权,占位1bit;
Original or copy: 原始或备份,1为原始,0为备份,占位1bit;
// 后面是7个flags(一般我们关注的就是PTS DTS的标志位):
PTS_DTS_flags :PTS和DTS标志位,占位2bit;10表示首部有PTS字段,11表示有PTS和DTS字段,00表示都没有,01被禁止,不会出现此种情况;
ESCR_flag :ESCR标志,占位1bit;1表示首部有ESCR字段,0则无此字段
ES_rate_flag :ES_rate字段,占位1bit;1表示首部有此字段,0无此字段;
DSM_trick_mode_flag :占位1bit;1表示有8位的DSM_trick_mode_flag字段,0无此字段;
Additional_copy_info_flag :占位1bit;1表示首部有此字段,0表示无此字段;
PES_CRC_flag :占位1bit;置1表示PES分组有CRC字段,0无此字段;
PES_extension_flag :占位1bit;扩展标志位,置1表示有扩展字段,0无此字段;
PES header data length :(UI)PES首部中可选字段和填充字段的长度;占位8bit;可选字段的内容由上面7个flags来进行控制;
Optional fields:可选字段的描述信息区域,其内容由上面的7个flag来控制;
PTS/DTS字段:显示时间戳/解码时间戳,占位40bit,当PTS_DTS_flags == 1x时此字段存在;时间占用33个bit,PTS和DTS的内容是在这40bit中取33位,方式相同;
PTS(presentation time stamp)显示时间戳和DTS(Decoding Time Stamp)解码时间戳,是用来音视频同步的,是打在PES包的包头里面的,PTS/DTS是相对SCR(系统参考)的时间戳,是以90000为单位的,PTS/DTS到ms的转换公式是PTS/90,系统时钟频率(H264采样频率?)为90Khz,所以转换到秒为PTS/90000,所以如果是以ms为单位的播放器,PTS/DTS是要使用公式ms=pts/90来转换才行的,而如果是以时钟频率为单位的话,则直接将PTS/DTS送进去解码即可;如果没有B帧,PTS和DTS的顺序应该是一致的,如果有B帧,则需要先解码P帧,才能解出来B帧,所以需要PTS和DTS来控制解码时间和显示时间;
字节顺序依次:
start_code :起始码,占位4bit;若PTS_DTS_flags == ‘10’,则说明只有PTS,起始码为0010;
若PTS_DTS_flags == ‘11’,则PTS和DTS都存在,PTS的起始码为0011,DTS的起始码为0001;(PTS的起始码后2个bit与flag相同)
PTS[32..30] :占位3bit;
marker_bit :占位1bit;
PTS[29..15] :占位15bit;
marker_bit :占位1bit;
PTS[14..0] :占位15bit;
marker_bit :占位1bit;
PTS/DTS = (PTS1 & 0x0e) << 29 + (PTS2 & 0xfffe) << 14 + (PTS3 & 0xfffe ) >> 1;
ESCR字段 :此字段占位48bit,由33bit的ESCR_base字段和9bit的ESCR_extension字段组成,ESCR_flag == 1时此字段存在;数据依次顺序:
Reserved :保留字段,占位2bit;
ESCR_base[32..30]:占位3bit;
marker_bit :占位1bit;
ESCR_base[29..15]:占位15bit;
marker_bit :占位1bit;
ESCR_base[14..0] :占位15bit;
marker_bit :占位1bit;
ESCR_extension :(UI)占位9bit;周期数,取值范围0~299;循环一次,base+1;
marker_bit :占位1bit;
ES rate字段 :目标解码器接收PES分组字节速率,禁止为0,占位24bit,ES_rate_flag == 1时此字段存在;数据顺序为:
marker_bit :占位1bit;
ES_rate:占位22bit;
marker_bit:占位1bit;
Trick mode control字段:表示哪种trick mode被应用于相应的视频流,占位8个bit,
DSM_trick_mode_flag == 1时此字段存在;其中trick_mode_control占前3个bit,根据其值后面有5个bit的不同内容;
如果trick_mode_control == ‘000’,依次字节顺序为:
field_id:占位2bit;
intra_slice_refresh :占位1bit;
frequency_truncation:占位2bit;
如果trick_mode_control == ‘001’,依次字节顺序为:
rep_cntrl:占位5bit;
如果trick_mode_control == ‘010’,依次字节顺序为:
field_id:占位2bit;
Reserved:占位3bit;
如果trick_mode_control == ‘011’,依次字节顺序为:
field_id:占位2bit;
intra_slice_refresh:占位1bit;
frequency_truncation:占位2bit;
如果trick_mode_control== ‘100’,依次字节顺序为:
rep_cntrl:占位5bit;
其他情况,字节顺序为:
reserved :占位5bit;
Additional copy info字段:占8个bit,Additional_copy_info_flag == 1时此字段存在;数据顺序为:
marker_bit:占位1bit;
copy info字段:占位7bit;表示和版权相关的私有数据;
Previous PES CRC字段:占位16bit字段,包含CRC值,PES_CRC_flag == 1时此字段存在;
PES extension字段:PES扩展字段,PES_extension_flag == 1时此字段存在;内容如下,字节顺序依次为:
PES_private_data_flag:占位1bit,置1表示有私有数据,0则无;
Pack_header_field_flag:占位1bit,置1表示有Pack_header_field字段,0则无;
Program_packet_sequence_counter_flag:占位1bit,置1表示有此字段,0则无;
P-STD_buffer_flag:占位1bit,置1表示有P-STD_buffer字段,0则无此字段;
Reserved字段:3个bit;
PES_extension_flag_2:占位1bit,置1表示有扩展字段,0则无此字段;
Optional field :PES扩展字段的可选字段内容顺序为:
PES_private_data字段:私有数据内容,占位128bit,PES_private_data_flag == 1时此字段存在;
Pack_header_field字段:Pack_header_field_flag == 1时此字段存在;字段组成顺序如下:
Pack_field_length字段:(UI)指定后面的field的长度,占位8bit;
pack_header_field():长度为Pack_field_length指定;
Program_packet_sequence_counter字段:计数器字段,16个bit;当flag字段Program_packet_sequence_counter_flag == 1时此字段存在;字节顺序依次为:
marker_bit:占位1bit;
packet_sequence_counter字段:(UI)占位7bit;
marker_bit:占位1bit;
MPEG1_MPEG2_identifier:占位1bit;置位1表示此PES包的负载来自MPEG1流,置位0表示此PES包的负载来自PS流;
original_stuff_length:(UI)占位6bit;表示PES头部填充字节长度;
P-STD_buffer字段:表示P-STD_buffer内容,占位16bit;P-STD_buffer_flag == '1'时此字段存在;字节顺序依次为:
’01’字段:占位2bit;
P-STD_buffer_scale:占位1bit;表示用来解释后面P-STD_buffer_size字段的比例因子;如果之前的stream_id表示音频流,则此值应为0,若之前的stream_id表示视频流,则此值应为1,对于其他stream类型,此值可以0或1;
P-STD_buffer_size:占位13bit;无符号整数;大于或等于所有P-STD输入缓冲区大小BSn的最大值;若P-STD_buffer_scale == 0,则P-STD_buffer_size以128字节为单位;若P-STD_buffer_scale == 1,则P-STD_buffer_size以1024字节为单位;
PES_extension2字段:扩展字段的扩展字段;占用N*8个bit,PES_extension_flag_2 == '1'时此字段存在;字节顺序依次为:
marker_bit:占位1bit;
PES_extension_field_length:占位7bit,表示扩展区域的长度;
Reserved字段:占位8*PES_extension_field_length个bit;
Stuffing bytes:填充字段,固定为0xFF;不能超过32个字节;
PES_packet_data_byte:PES包负载中的数据,即ES原始流数据;
PES包是TS和PS包封装的基础,TS和PS其实就是对PES包的再一次封装,下篇将讲解一下TS流
上图给出了在一个TS流中PSI各表之间的联系。首先从PAT表出发,获取当前有哪些节目号(program_map_PID),再根据这些节目号找出program_number相同的PMT;获取到PMT后,即可根据elementary_PID和stream_type来确定要过滤哪些含有基本流的TS包,以及这些包里面是什么类型的数据。这样,机顶盒就可以过滤出相应的节目(视频和音频数据)来收看了。
TS总结:
TS解包流程就是现在TS包的包头解出来PAT的PID,然后根据PID找到PAT,并从PAT中解出来每个节目所对应的PMT的PID,再根据PID找到所有节目的PMT,然后从每个节目的PMT中解出来当前节目所对应的不同流类型的TS包的PID,根据这些PID来找到对应的TS包,取出原始视频流,音频流和其他数据等;打包过程则是相反的;
TS头里面的PCR字段是基准时间戳,在音视频解码显示的时候,是根据PES头里面的PTS和DTS字段与其对比,相同就说明该进行解码和显示了;PCR字段是在TS的PMT中指定的PID,只有指定的PID的TS包里面的PCR字段才有用,我们打包的时候使用的是视频的PID中的PCR,只有每帧的第一包TS头里面才会有PCR,而PES头里面的PTS和DTS就是视频和音频的相对时间戳;测试遇到了音视频不同步的问题,原因就是TS打包时,PES头里面的音视频PTS都用了视频的时间戳,而我们在TS解析时是对音频有相对延后的操作,其采用的视频时间戳相对原来是有可能延后了多个视频帧的,所以导致音频有延后;
视频编解码概念:时间戳DTS和PTS的相关分析
http://blog.csdn.net/soaringlee_fighting/article/details/70941896
基本概念:
I frame :帧内编码帧 又称intra picture,I 帧通常是每个 GOP(MPEG 所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,做为随机访问的参考点,可以当成图象。I帧可以看成是一个图像经过压缩后的产物。
P frame: 前向预测编码帧 又称predictive-frame,通过充分将低于图像序列中前面已编码帧的时间冗余信息来压缩传输数据量的编码图像,也叫预测帧;
B frame: 双向预测内插编码帧 又称bi-directional interpolated prediction frame,既考虑与源图像序列前面已编码帧,也顾及源图像序列后面已编码帧之间的时间冗余信息来压缩传输数据量的编码图像,也叫双向预测帧;
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
在没有B帧存在的情况下DTS的顺序和PTS的顺序应该是一样的。
IPB帧的不同:
I frame:自身可以通过视频解压算法解压成一张单独的完整的图片。
P frame:需要参考其前面的一个I frame 或者B frame来生成一张完整的图片。
B frame:则要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片。
两个I frame之间形成一个GOP,在x264中同时可以通过参数来设定bf的大小,即:I 和p或者两个P之间B的数量。
通过上述基本可以说明如果有B frame 存在的情况下一个GOP的最后一个frame一定是P.
DTS和PTS的不同:
DTS主要用于视频的解码,在解码阶段使用.PTS主要用于视频的同步和输出.在display的时候使用.在没有B frame的情况下.DTS和PTS的输出顺序是一样的.
例子:
下面给出一个GOP为15的例子,其解码的参照frame及其解码的顺序都在里面:
如上图:I frame 的解码不依赖于任何的其它的帧.而p frame的解码则依赖于其前面的I frame或者P frame.B frame的解码则依赖于其前的最近的一个I frame或者P frame 及其后的最近的一个P frame.
FFmpeg里有两种时间戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顾名思义,前者是解码的时间,后者是显示的时间。要仔细理解这两个概念,需要先了解FFmpeg中的packet和frame的概念。
FFmpeg中用AVPacket结构体来描述解码前或编码后的压缩包,用AVFrame结构体来描述解码后或编码前的信号帧。 对于视频来说,AVFrame就是视频的一帧图像。这帧图像什么时候显示给用户,就取决于它的PTS。DTS是AVPacket里的一个成员,表示这个压缩包应该什么时候被解码。 如果视频里各帧的编码是按输入顺序(也就是显示顺序)依次进行的,那么解码和显示时间应该是一致的。可事实上,在大多数编解码标准(如H.264或HEVC)中,编码顺序和输入顺序并不一致。 于是才会需要PTS和DTS这两种不同的时间戳。
PTS - Presentation Timestamp,播放的时间戳。
DTS - Decompress Timestamp, 解码的时间戳。
这2个概念经常出现在音频视频编码和播放中,其实际意义是,PTS是真正录制和播放的时间戳,而DTS是解码的时间戳。对于普通的无B帧视频(H264 Baseline或者VP8),PTS/DTS应该是相等的,因为没有延迟编码。对于有B帧的视频,I帧的PTS依然等于DTS, P帧的PTS>DTS, B桢的PTS<DTS。PTS/DTS的初始值,一般来源于录制的视频的频率,按照h264的设定是90HZ, 所以PTS/DTS到毫秒的转换公式是:
ms=pts/90.对于需要以ms为单位的视频播放器(比如Flash Player),按照pts/90转换到ms就可以正常播放了。
可以简单地这样理解:
若视频没有B帧,则I和P都是解码后即刻显示。
若视频含有B帧,则I是解码后即刻显示,P是先解码后显示,B是后解码先显示。(B 和P的先、后是相对的)。
TS码流结构分析
http://blog.163.com/elvis1943@126/blog/static/638916982015615101821857/
ES流(Elementary Stream)由三部分组成:
※经MPEG-2视频编码器编码后的图像数据流;
※经MPEG-2音频编码器编码后的声音数据流;
※其他编码数据流;
PES流(Packetized Elementary Stream):PES流是ES流经过PES打包器处理后形成的数据流,在这个过程中完成了将ES流分组、打包、加入包头信息等操作(对ES流的第一次打包)。PES流的基本单位是PES包。
PS流(Program Stream)和TS流(Transport Stream):
※PS流和TS流是MPEG-2系统规范的两种标准码流。
※PS流用于相对无错环境下的传输与存储(如DVD中),其基本单位是PS包,长度可变。
※TS流用于相对有错环境下的传输与存储(如DVB中),其基本单位是TS包,长度固定188字节。
※ PS流由PS包组成,而一个PS包又由若干个PES包组成(到这里,ES经过了两层的封装)。
※ PS包的包头中包含了同步信息与时钟恢复信息。
※一个PS包最多可包含具有同一时钟基准的16个视频PES包和32个音频PES包。
※ TS流由定长的TS包组成(188字节),而TS包是对PES包的一个重新封装(到这里,ES经过了两层的封装) 。
※ PES包的包头信息依然存在于TS包中。
链接: https://wenku.baidu.com/view/cd092f03a8956bec0875e337.html
https://my.oschina.net/u/727148/blog/666824
http://blog.sina.com.cn/s/blog_6b94d5680101r5l6.html
http://blog.csdn.net/zxh821112/article/details/17587215
http://www.cnblogs.com/hjj801006/p/3837435.html
http://blog.csdn.net/zxh821112/article/details/17587325