简要
ts是一种封装格式,全名为MPEG-TS,文件分为三层:ts层(Transport Stream)、pes层(Packet Elemental Stream)、es层(Elementary Stream)。es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层是在pes层上加入了数据流识别和传输的必要信息(header)。
相关概念
PAT:Program Association Table,节目关联表
PMT:Program Map Table,节目映射表
ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES分割成段,在每一个视频/音频帧上加入了时间戳等信息。
PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)
ES你可以理解为一段音频或者视频的数据,ES流可能会很大,所以要拆分成PES包,PES包的长度是有限制的,64KB。所以并不是一个ES对应一个PES。
TS:ts_header(0x47开头)+payload(负载)-------188个字节
PES:pes_header( 0x000001开头)+ES(包含时间戳)------64K
ES:NAL(0x0001开头(3或者4个字节),SPS(0x67),PPS(0x68),I帧(0x65),I帧(0x65))+编码data-----不限制大小,比如I帧可能有好几百K
scrambling (system): 加扰,以改变视频,音频或编码数据流的特性,防止以明文形式传输,使未经授权者接收明文数据。
PCR (system): Program Clock Reference
PTS(system):presentation time-stamp 显示时间戳,可能存在于PES包头中的字段,用于指示在系统目标解码器中显示帧数据的时间。
DTS(system):decoding time-stamp 解码时间戳,可能存在于PES包头中的字段,用于指示在系统目标解码器中解码访问帧数据的时间。
CRC:Cyclic Redundancy Check 循环冗余检查,以验证数据的正确性。
PSI:Program Specific Information 程序特定信息,提供了单个TS流的信息,使接收方能够对单个TS流中的不同节目进行解码,这些信息都存在用表的形式提供给,如PAT、PMT、CAT等。但它无法提供多个TS流的相关业务,也不能提供节目的类型、节目名称、开始时间、节目简介等信息。
SI:Specific Information 特定信息,PSI中无法提供的相关信息,SI定义了NIT、SDT、EIT和TDT等9张表,方便用户查看多种信息。
结构图
通过av_read_frame获取到原始的es数据流对比可以看出.和ts原始数据缺少了ts和pes的头.左边为.ts文件,右边为.264文件。
流程图
编码过程
TS解析图
这个图主要解析的是流的基本信息:format,filesize,duration,bitrate以及音视频的Count
PAT和PMT的相关基本信息
下面图是多音轨的TS
各种流的PID,索引及数量占的百分比,可以看到PAT和PMT是一一对应的,同时对应多个音频或者视频数据
分析
TS层的packet都是固定等长的188字节包,由ts header、adaptation field、payload组成,其中ts header固定4个字节(32位);adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。结构图如下
Ts header部分
这里我们分析一段TS流其中一个Packet的Packet Data部分:
首先给出一个数据包,其数据如下:
Packet Header | Packet Data |
0x47 0x40 0x00 0x10 | 0000 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff…… ff ff |
分析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 | 4 | 0 | 0 | 0 | 1 | 0 | … | ||||||||||||||||||||||||
Packet(二进制) | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | … |
Packet Header Bits | 1 sync_byte=0x47 | 2 | 3 | 4 | 5 PID=0x0000 | 6 | 7 | 8 | … |
根据包头数据格式,我们可以知晓整个数据包的属性,列表如下:
sync_byte | 0x47 | 固定同步字节 |
transport_error_indicator | “0” | 没有传输错误 |
payload_unit_start_indicator | “1” | 在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。即上面数据中红色部分不属于有效数据包。 |
transport_priority | “0” | 传输优先级低 |
PID | 0x0000 | PID=0x0000说明数据包是PAT表信息 |
transport_scrambling_control | “00” | 未加密 |
adaptation_field_control | “01” | 附加区域控制 |
continuity_counte | “0000” | 包递增计数器 |
如上表所示,我们可以知道,首先Packet的Packet Data是PAT信息表,因为其PID为0x0000,并且在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1"),即上面数据中红色部分不属于有效数据包。这样,Packet Data就应该是“ 00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff ”。
Packet Data分析 | |||||||||||||||||||||||||
第n个字节 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | … | ||||
Packet Data(除去开头的0x00) | 00 | b0 | 11 | 00 | 01 | c1 | 00 | 00 | 00 | 00 | e0 | 1f | 00 | 01 | e1 | 00 | 24 | ac | 48 | 84 | … | ||||
字段名 | 位 | 具体值 | 次序 | 说明 | |||||||||||||||||||||
table_id | 8 | 0000 | 第1个字节 0000 0000B(0x00) | PAT的table_id只能是0x00 | |||||||||||||||||||||
section_syntax_indicator | 1 | 1 | 第2、3个字节 1011 0000 0001 0001B(0xb0 11) | 段语法标志位,固定为1 | |||||||||||||||||||||
zero | 1 | 0 | |||||||||||||||||||||||
reserved | 2 | 11 | |||||||||||||||||||||||
section_length | 12 | 0000 0001 0001B=0x011=17 | 段长度为17字节 | ||||||||||||||||||||||
transport_stream_id | 16 | 0x0001 | 第4、5个字节 0x00 0x01 | ||||||||||||||||||||||
reserved | 2 | 11 | 第6个字节 1100 0001B(0xc1) | ||||||||||||||||||||||
version_number | 5 | 00000 | 一旦PAT有变化,版本号加1 | ||||||||||||||||||||||
current_next_indicator | 1 | 1 | 当前传送的PAT表可以使用,若为0则要等待下一个表 | ||||||||||||||||||||||
section_number | 8 | 0x00 | 第7个字节0x00 | ||||||||||||||||||||||
last_section_number | 8 | 0x00 | 第8个字节 0x00 | ||||||||||||||||||||||
开始循环 | |||||||||||||||||||||||||
program_number | 16 | 0x0000-第一次 | 2个字节(0x00 00) | 节目号 | |||||||||||||||||||||
reserved | 3 | 111 | 2个字节 1110 0000 0001 1111B(0xe0 1f) | ||||||||||||||||||||||
network_id(节目号为0时) program_map_PID(节目号为其他时) | 13 | 0 0000 0001 1111B=31 -第一次 | 节目号为0x0000时,表示这是NIT,PID=0x001f,即31 节目号为0x0001时,表示这是PMT,PID=0x100,即256 | ||||||||||||||||||||||
结束循环 | |||||||||||||||||||||||||
CRC_32 | 32 | -- | 4个字节 |
PID
PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。
从这两个图,可以看出,PAT的PID是0。 下表给出了一些表的PID值,这些值是固定的,不允许用于更改。
PAT
PAT表(Program Association Table,节目关联表) ,PAT的PID为0x0000,它的主要作用是针对复用的每一路传输流,提供传输流中包含哪些节目、节目的编号以及对应节目的节目映射表(PMT)的位置,即PMT的TS包的包标识符(PID)的值,同时还提供网络信息表(NIT)的位置,即NIT的TS包的包标识符(PID)的值。
- table_id: 标识一个TS PSI 分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PAT,置为0x00。
- section_syntax_indicator: 对于PAT,置为0x01。
- section_length: 分段长度字段,其值为从section_length(包括在内)到CRC_32字段的字节数,其值不超过1021。
- transport_stream_id: 区别与其他复用流的标识。
- version_number: PAT的版本号,如果PAT有变,则版本号加1。
- current_next_indicator:置0时,表明该传送的表分段不能使用,下一个表分段才有效。
- section_number: 表明该TS包属于该PAT的第几个分段,分段号从0开始。
- last_section_number: 表明最后一个分段号,同时表明该PAT的最大分段数目。一般,一个PAT表由一个TS包传送。
- program_number: 节目的编号。
- network_PID: NIT表的PID值。
- program_map_PID: PMT表的PID值。
- CRC_32: CRC校验。
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;
从图中可以看到,这个TS里有一个节目,而PMT_PID=0x1000(4096)
PMT
PMT表(Program Map Table,节目映射表)
指明该节目包含的内容,即该节目由哪些流组成,这些流的类型(音频、视频、数据),以及组成该节目的流的位置,即对应的TS包的PID值,每路节目的节目时钟参考(PCR)字段的位置。
PMT表中包含的数据如下:
- 当前节目中所有Video ES流的PID
- 当前节目所有Audio ES流的PID
- 当前节目PCR的PID等。
从图中可以看到这个TS包含一个program,program里包含一路视频和四路音频
主要的字段解析如下:
- table_id: 标识一个TS PSI 分段的内容是节目关联分段,条件访问分段还是节目映射分段。对于PMT,置为0x02。
- section_syntax_indicator: 对于PMT,置为0x01。
- section_length: 分段长度字段,其值为从section_length(包括在内)到CRC_32字段的字节数,其值不超过1021。
- program_number: 表明一共有多少个节目。
- version_number: PMT的版本号,如果字段中有关信息有变,则版本号以32为模加1。版本号是对一个节目的定义。
- current_next_indicator:置0时,表明该传送的表分段不能使用,下一个表分段才有效。
- section_number: 总为0x00。
- last_section_number: 总为0x00。
- PCR_PID: 指示含有该节目的PCR字段的TS包的PID。
- program_info_length: 表明跟随其后的对节目信息描述的字节数,也就是第一个N loop descriptors的字节数。
- stream_type: 表明PES流的类型。譬如,0x01表明是MPEG-1视频,0X03表明是MPEG-1音频。
- elementary_PID: 表明该负载有该PES流的TS包的PID值。
- ES_info_length: 表明跟随其后的描述相关节目元素的字节数,也就是第二个N loop descriptors的字节数。
- CRC_32: 在CEDARX代码中仅对DVB的场景下作校验。
typedef struct TS_PMT_Stream
{ unsigned stream_type : 8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定 unsigned elementary_PID : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素 unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数 unsigned descriptor;
}TS_PMT_Stream;//PMT 表结构体
typedef struct TS_PMT
{unsigned table_id : 8; //固定为0x02, 表示PMT表unsigned section_syntax_indicator : 1; //固定为0x01unsigned zero : 1; //0x01unsigned reserved_1 : 2; //0x03unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。unsigned program_number : 16;// 指出该节目对应于可应用的Program map PIDunsigned reserved_2 : 2; //0x03unsigned 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; //固定为0x00unsigned last_section_number : 8; //固定为0x00unsigned reserved_3 : 3; //0x07unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,//该PCR值对应于由节目号指定的对应节目。//如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。unsigned reserved_4 : 4; //预留为0x0Funsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定unsigned reserved_5 : 3; //0x07unsigned reserved_6 : 4; //0x0Funsigned CRC_32 : 32;
} TS_PMT;
PAT与PMT两张表帮助我们找到该传送流中的所有节目与流,PAT告诉我们,该TS流由哪些节目组成,每个节目的节目映射表PMT的PID是什么,而PMT告诉我们,该节目由哪些流组成,每一路流的类型与PID是什么。
TS流的形成过程:
- 将原始音视频数据压缩之后,压缩结果组成一个基本码流(ES)。
- 对ES(基本码流)进行打包形成PES。
- 在PES包中加入时间戳信息(PTS/DTS)。
- 将PES包内容分配到一系列固定长度的传输包(TS Packet)中。
- 在传输包中加入定时信息(PCR)。
- 在传输包中加入节目专用信息(PSI) 。
- 连续输出传输包形成具有恒定比特率的MPEG-TS流。
TS流的解析过程:
- 从复用的MPEG-TS流中解析出TS包;
- 从TS包中获取PAT及对应的PMT(PSI中的表格);
- 从而获取特定节目的音视频PID;
- 通过PID筛选出特定音视频相关的TS包,并解析出PES;
- 从PES中读取到PTS/DTS,并从PES中解析出基本码流ES;
- 将ES交给解码器,获得压缩前的原始音视频数据
分析TS解析过程
ffmpeg对mpeg2-TS详细解析_ChenYuanshen的博客-CSDN博客_ffmpeg mpeg2
FFMpeg对MPEG2 TS流解码的流程分析
FFMpeg对MPEG2 TS流解码的流程分析_iteye_4476的博客-CSDN博客
FFMpeg对MPEG2 TS流解码的流程分析[2]
FFMpeg对MPEG2 TS流解码的流程分析[2] - it610.com
TS流PAT/PMT详解
TS流PAT/PMT详解_rolandz_的博客-CSDN博客_pat pmt
TS 流解析流程
- 复用的MPEG-TS流中解析出TS包;
- 从TS包中获取PAT及对应的PMT;
- 从而获取特定节目的音视频PID;
- 通过PID筛选出特定音视频相关的TS包,并解析出PES;
- 从PES中读取到PTS/DTS,并从PES中解析出基本码流ES;
- 将ES交给解码器,获得压缩前的原始音视频数据。
总下简单的说就是,解析ts的过程就是通过找到PAT表,从PAT表中找出对应存在的节目的id,按照这些id找到这些节目的PMT表,从中获到这些节目总的相对的媒体数据id,然后通过这些id,再从ts文件中找到这些文件的es数据,来完成解码或者别的什么操作。
TS流PAT/PMT详解 - rlandj - 博客园