一:编码的理论基础
H.264的基本流(elementary stream,ES)分为两层,视频编码层(VCL):负责高效的视频内容的表示。网络适配层(NAL):负责以网络所要求的恰当的方式对数据进行打包和传送。
H.264基本流由一系列的NALU(Network Abstraction Layer Unit)组成,其中NALU的数据量各不相同。每个NALU之前都有一个起始码(Start Code):0x000001(3字节)或 0x00000001 (4字节),起始码用来表示NALU的起始位和终止位。
注:其中0x000001(3字节)的Start Code 只用于一个场合,即一个完整的帧被编为多个slice(片)的时候,包含这些slice的NALU使用这种3个字节的起始码。
NALU单元由NALU头(NALU Header)和若干个字节的载荷数据(RBSP)组成。
RBSP序列:SPS | SEI | PPS | I帧 |图像界定符 | P帧 | P帧
即:NALU单元 = NALU Header + PBSP
而:NALU Header = 禁止位(forbidden_bit,1bit)+ 优先级(nal_reference_bit,2bits,优先级越大越重要)+ NALU类型(NAL_Unit_type,5bits)
例:00 00 00 01 67
其中 00 00 00 01:是起始码。 将十六进制的67转为二进制:01100111,其中0,为禁止位;11,优先级;00111,NALU类型,转化为十进制等于7,(sps,序列参数集)
例:00 00 00 01 68
其中 00 00 00 01:是起始码。 将十六进制的68转为二进制:01101000,其中0,为禁止位;11,优先级;01000,NALU类型,转化为十进制等于8,(pps,图像参数集)
例:00 00 00 01 65
其中 00 00 00 01:是起始码。 将十六进制的65转为二进制:01100101,其中0,为禁止位;11,优先级;00101,NALU类型,转化为十进制等于5,(IDR图像中的片,即I帧)
以上3种类型,是正常需要输入解码器的码流类型。
【注:以上内容是解码基础,百度都可以轻松找到。但是记录笔记本上的时候,没有特定看哪篇博客,看了很多,也就不贴上链接了。以下内容,纯原创,转载请注明出处,谢谢。】
二:现阶段优化的问题
- 较差网络环境下,较长时间播放视频,视频的播放时间和实际时间相比,变慢问题。
答:产生的原因:因为网络环境较差,来包速度较慢,所以如果按照正常速度播放,播放的显示时间会 比实际播放时间慢。
原方案:
未处理。
改进方案:
①判断来包中的pts,若设第一次来的包中的pts1,此时记住当时的实际时间time1。
②当后面再来包的pts2,pts3......可求得pts2-pts1+time1,则可得第二个也就是pts2的理论应该播放的正确时间correctPlayTime。
③用这个时间去和当前获取这个时间的实际时间time2比较,如果我们设置容错差值为3s,那么我们就可以 correctPlayTime - time2 > 3,则表示播放速度快于当前时间,等待播放(注:理论不会快于,这里就要做一个防止抖动处理);如果该值小于 -3,则加速播放。加速播放,通过丢弃当前的两个I帧之间的所有的p帧实现。
注:录像回放播放,要区别处理,不需要加速丢帧播放。
- 对于ps码流,ps头的处理的优化。
答:产生原因:在一个完整帧后面跟着00 00这种情况,下个帧判断就会出现问题,可能会丢弃。
原方案:
①之前只是在来的数据上直接改动,将拿到的数据直接去掉ps头,若拿到的是一个完整的帧或者多个完整的帧,都会去掉相应的ps头,并输入解码器。
②但是,如果在最后一帧并不是一个完整的一帧则,直接丢弃。一般来的数据,都不会是完全完整的包,所以这样导致了,会有绿屏的出现。
改进方案:
①将来的数据出入到ps处理器中进行去头操作,来一段可能是多个包,一直处理到该段完整的最后一个包结束,将这段数据回调回来,在进行类型判断,如果类型为5,7,8,输入到解码器中。
②但是刚才的没有下一个startCode的不完整的包的剩余部分以及读到的位置和剩余长度也同样回调回来,和新来的数据进行拼接,拼接好的数据再次输入到ps处理器中,如此循环以复。
- 对于p2p方式连接的RTSP码流的处理。
答:产生原因:之前RTSP来的码流没有pts这个参数。未能做播放慢于当前时间的,加速处理,以及快于的,等待处理。
原方案:
没有pts参数,故没有处理。
改进方案:
①参考问题一的解决方案,该方案与问题一方案一致。但是该方式来的码流,是ES流。不用进行ps头处理,直接进行类型判断,对应类型输入解码器即可。变慢的情况下,现在服务器这边会有一个时间戳来代替pts,用时间戳来判断播放与当前时间是否一致。
- 在1问题中,出现的防抖动处理。
答:产生原因:tcp发包间隔,以及较差网络下造成的,如0秒接收的数据,在0.5秒的时候一次性接受到,下个0.5s的数据,在0.6秒的时候一次性接受到。这样忽快忽慢的来包。
原方案:
未处理。
改进方案:
①将来包中的第一个pts1的应该记录的时间time1,人为的延时0.3s左右,以及以后进行判断的时间都依次顺移0.3s,具体结合问题1进行具体操作。
- 在p2p的码流中(或者正常ps流),主码播放时候,此时I帧特别大。之前会出现播放图像出现一半,或者播放不出来的问题。
答:产生原因:I帧较大的时候,未进行拼帧处理。
原方案:
未做拼帧处理。
改进方案:
①I帧较大的时候,进行拼接成一个完整I帧。
- 长时间播放的情况下,若刚来的pts数值就很大,则可能会出现pts数值溢出,清零的情况。
答:产生原因:因为来的pts值很大,播放时间很长的话,pts是固定间隔增加,当到一定时间,就会溢出。
原方案:
未处理。
改进方案:
①当pts2-pts1为负数并且负数值很大的话,则可以判断出pts存在数值溢出的情况。
②在这种情况下,应该将第一次记录的time1,清空,重新记录下在这种情况后面来的pts,当前时间记录为time1。注:可参考问题一的步骤。
- 对于视频码流只有SEI的情况下,视频解析不出来的问题。
答:产生原因:将分析出来的sei数据输入到解码器,解析不出来。是黑屏状态。
原方案:
直接输入到解码器,但是未能实现解析。
改进方案:
暂无。