打包TS流
做这个东西很久了,从去年⼗⼆⽉份开始的,快5个⽉了。。。期间因为⼯作⼀直断断续续,直到最近才有了些进展,也就到此为⽌吧。
先说下我做的是什么吧,总的来说,就是把H264视频流与AAC⾳频流封装成TS格式。要完成这么个功能⾸先要解析H264和AAC,获得视频帧和⾳频帧以及⼀些关键信息,⽐如帧率、采样率什么的为以后的打包做⼯作。分析到每⼀帧数据后,再加上PES头,封装成PES数据,在这个过程要注意打PTS、DTS,以及PES类型的设定,⼀般来说,视频PTS与DTS都要打,⽽⾳频只需要打PTS。最后⼀步就是将PES数据封装成TS了,这⼀步的坑⽐较多。做完这些,最难个⼈感觉就是⾳视频同步了,⾄今还⽐较疑惑,视频播出来总是有瑕疵。
⼀、解析h264
H264是以0x00 00 01或0x00 00 00 01作为分割NALU单元分割符的,整个H264也是以NALU作为基本单元的,NALU的结构如图⼀所⽰:
图1
NALU包含头和负载RBSP,头部的句法结构如图⼆所⽰:
·图2
nal_unit_type指明的NALU的类型,不多说了,贴图吧。。。过膝靴搭配
这⼀个字节便包含了整个头部的信息,也就是去除分割符后的头⼀个字节。nal_unit_type
图3
NALU的负载RBSP在读取有效数据之前⾸先要做去除竞争码的操作,遇到0x00 00 03将字节0x03去掉即可。
·0x00 00 00 -----> 0x00 00 03 00
·0x00 00 01 -----> 0x00 00 03 01
·0x00 00 02 -----> 0x00 00 03 02
·0x00 00 03 -----> 0x00 00 03 03
slice),去除竞争码后的负载采⽤的是哥伦布编负载的类型由前⽂nal_unit_type
nal_unit_type指定,当值⼤于等于1,⼩于等于5的时候指明该负载是⽚数据(slice
(v),给张图看的⽐较清楚
ue(v)、(v)
码。在看⽚数据句法结构的时候可以留意到每个字段后⾯都有⼀些符号说明,如ue(v)
美好造句图4
slice_type分析出即可,要想完整的解出⽚还我贴的是⽚头句法的⼀部分,在合成TS的时候只要判断⽚类型就可以了,也就是说,只要将图4的slice_type
是有点难度的,不过判断⽚的类型并不难。
回到前⾯的哥伦布编码,这是⼀种变长编码,括号⾥⾯带v v的均是指编码的长度不固定。
解码哥伦布编码的字段可以选⽤⽹上的库,看起来还⾏,但因为是在研究学习这个东西,捭阖纵横
我选择了⾃⼰实现这个算法,也不是特别复杂。⾸先说下⽆符号的哥伦布编码,按⽹上的定义,哥伦布的编码⽅式有0阶、1阶、2阶...每种好像都不⼀样,也没有都去研究,因为H264⽤到的只是0阶的哥伦布编码,弄懂这个就够⽤了。
⽆符号0阶指数哥伦布编码(u
(u e)
1.1⽆符号0阶指数哥伦布编码
1.1
按照算法的描述,应先将数据以⼆进制⽅式排列,接着连续读取M个0⽐特位,碰到1⽐特位结束读取,这时解码的数据长度就已经确定了,为团购网站
2*M+1个⽐特,包含前⾯已经读取的M个0⽐特。再将最后的M位⽐特转化成⼗进制数值W,即可获得解码结果为W-1,到此为⽌,⼀次完整的⽆符号解码就完成了。
·⽆符号哥伦布编码测试例⼦:
(解码后)
0x4C 0x85 0x31 0xC4 0x09 =====> 1 2 3 4 5 6 7 8
有符号0阶指数哥伦布编码(()
1.2有符号0阶指数哥伦布编码
1.2
有符号的哥伦布编码建⽴在⽆符号的基础上,按照⽆符号的解码步骤得到⼗进制数n后,再实⾏以下的动作即可。
·获取n被2除后的商w(注意这个商是去除了⼩数的整数部分)
·n不能被2整除,w加1为解码结果
·n可以被2整除,w的相反数即为解码结果
(解码后)
0x4C 0x85 0x31 0xC4 0x09 =====>1 -1 2 -2 3 -3 4 -4
1.3帧类型
帧类型
1.3
slice_type与帧类前⾯在解析⽚数据的时候有提到过⽚的类型,实际上这⾥指明的是⽚所装载的视频帧的类型,按定义分为I,P,B,SP,SI,图5给出slice_type
型的对应关系
图5
我在解码的时候只关⼼了I,P,B帧,另外的两种帧的意义感觉和I,P帧类似,所以没做过多的研究。I,P,B帧的差别在于编码⽅式的差异,I帧数据不依赖其他的帧数据,可独⽴解码,⽽P帧使⽤前向预测编码,它的解码依靠前⾯解码的I帧和P帧,B帧⽤了双向预测编码,解码B帧图像,需要同时参考前后的帧数据。它们压缩数据的能⼒也依次增强,⼀般来说,B帧⽐较多的⽹络视频流同等⽐特量所传输的视频质量会更⾼,但是相应的,播放器的解码压⼒也更⼤。由于P、B帧的解码需要参考其他帧,所以在解码的时候可能会产⽣误码扩散的情况,但由于播放器在遇到I帧的时候会丢弃之前解码的视频数据,再以当前的I帧作为以后P、B帧的参考帧,因此这⼀举措及时的阻⽌了错误的扩散,说到这⾥,我得额外提下HLS协议,这个协议在制作切⽚的时候总是以I帧开头,不仅是为了在播放视频的时候⽴马刷出图像,我想更多的是因为H264这种编码格式的限制⽽不得不以I 帧开头,因为只有I帧是可以独⽴解码的,如果开头的是其他需要参考的帧,他们的解码毫⽆意义。设备基础施工方案
PES
打包PES张家界十大名吃
⼆、打包
PES是对裸流的⼀层封装,H264和AAC就是两种裸流,他们就像还没装订的书,随意的被堆放在⾓落⾥,PES的封装就是将散乱的纸打上页码装成书的过程。这⾥的页码指的就是PTS与DTS,显⽰时间戳和解码时间戳,这个时间可以指⽰播放器何时显⽰⼀帧图像或者⾳频,⼀般来说视频帧PTS、DTS都需要打,尤其是在有B帧的情况下,因为B帧需要参考前后两帧,所以它要等到前后帧都被解码出后才能解此B帧,因此B帧的显⽰时间和解码时间是不⼀致的,⽽⾳频帧没有这种情况,解⼀帧放⼀帧即可,它是不需要DTS的。PES头的句法结构如图6
描写外貌的好句子
图6(1)
图6(2)
PES的整结构没有完整的贴上来,只贴到PTS、DTS这部分句法,其实在打包TS的时候,这部分已经够⽤了,其他的可以不去关⼼。接下来说明⼀下⼏个重要的字段。
·packet_start_code_prefix
这3个字节是PES的起始码,将PES包⼀⼀隔开,起始是固定的0x00 0x00 0x01
·stream_id
stream_id
指明PES负载的类型,视频为0xE0,⾳频为0xC0
PES_packe_length
·PES_packe_length
说明在此字段最后⼀个字节之后PES分组的字节数。‘0’值表明PES分组的长度既没有说明也没有限制,这种情况只有在PES分组的有效负载是传送流分组中的视频原始流时才允许。
PTS_DTS_flag
·PTS_DTS_flag
2位标志,若为‘10’,则PES分组⾸部有PTS字段;若为‘11’,则PES分组⾸部有PTS和DTS字段;若为’00’则PTS和DTS都不在PES分组⾸部出现;‘01’值被禁⽌。
PES_header_data_length
·PES_header_data_length
说明在此字段最后⼀个字节还剩余的PES头部分组数据量,⼀般可能指的是PTS与DTS部分编码的长度。
PES分组中⽐较重要的字段就上⾯这些的,对于PTS与DTS的编解码,按照PTS_DTS_flag以及相应的句法指⽰去做即可。
合成TS
三、合成TS
TS流每个包的⼤⼩都介于188-204字节之间,⼀般就是188字节,每个PES包基本都⼤于188字节,所以在把PES封装成TS时,需要将PES分割成多个TS包,每个包都有唯⼀的ID标识它的类型,⾳视频PES打包成TS包的ID由TS中的PMT表指定,PMT表被单独打成⼀个TS包,它也有ID标识,这个标识由PAT表指定,与PMT类似,PAT也被打成独⽴的包,它的ID是固定的0,所以,不管是解码还是编码TS流,都应从PAT表⼊⼿。
包格式
3.1 TS包格式
3.1 TS
图7
TS包由头与负载组成,头部占四个字节,如果还有⾃适应区,那么⾃适应区的长度由第五个字节指定。这⾥先简要的分析下TS头的前四个字节。
sync_byte
·sync_byte
此处是同步字节,固定为0x47
·transport_error_indicator
这个⽐特位我没有⽤到过,⼀直都是0
·payload_unit_start_indicator
payload_unit_start_indicator
此⽐特位为1,标志着PES包负载的开始,不仅如此,如果是PAT、PMT包,这⾥也被置为1
transport_priority
·transport_priority
我暂没⽤到,默认是0
PID
·PID
每个包种类的标识
·transport_scrambling_control
transport_scrambling_control
我暂未使⽤
adaptation_field_control
·adaptation_field_control
此字段标识是否有⾃适应区
00:是保留值。
01:负载中只有有效载荷。
10:负载中只有⾃适应字段。
11:先有⾃适应字段,再有有效载荷。
如果含有⾃适应区,那么头四个字节后就是⾃适应区的码流结构,最后才是TS包的负载部分
continuity_counter
·continuity_counter
这个字段的值从00 - 15反复连续变化
⾃适应区格式
3.2
3.2⾃适应区格式音声相和