您的位置:首页 > 其它

视频云直播中的关键帧技术探秘

2017-07-16 23:46 232 查看


H.264视频标准中普通I帧与IDR帧的区别

普通I帧与IDR(Instantaneous Decoding Refresh,瞬时解码刷新)帧为均采用帧内预测技术的视频帧,同属于I帧。

区别是:采用IDR帧编码,会导致DPB(Decoded Picture Buffer,直译为解码图像缓冲区,即指参考帧列表)完成一次清空处理,而普通I帧不会。

那么,这样带来的直接结果是怎样的呢?

即,当前IDR帧后面的帧不会将当前IDR帧之前已经编码的帧作为参考帧;而当前普通I帧之后的帧有可能将当前I帧之前的帧作为参考帧。

那么,这样带来的直接效果是怎样的呢?

即,播放视频的时候,可以直接从IDR帧开始播放,因为IDR帧之前的帧与当前IDR帧和之后的帧再也没有直接地关联;而普通I帧则不行。

可以看到,一般来讲,一段视频的第0帧(也即首帧)是I帧,那么它算不算是IDR帧呢?答:算。

所以,IDR帧一定是I帧,反之不成立。

在H.264标准参考软件(JM)下,通过IDRIntraEnable来设置是否支持IDR帧。
I和IDR帧都是使用帧内预测的。在编码和解码中为了方便,要首个I帧和其他I帧区别开,所以才把首个I帧叫做IDR帧,这样就方便控制编码和解码流程。IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码。而I帧不具有随机访问的能力,这个功能是由IDR承担。IDR会导致DPB(参考帧列表——这是关键所在)清空,而I帧不会。
IDR图像一定是I图像,但是I图像不一定是IDR图像。一个序列中可以有很多的I图像,I图像之后的图像课引用I图像之间的图像做运动参考。
对于IDR帧来说,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容,与此相反,对于普通的I帧来说,位于其之后的B和P帧可以引用位于普通I帧之前的I帧。从随机抽取的视频流中,播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。但是,不能在一个没有IDR帧的视频中从任意点开始播放,因为后面的帧总是会引用前面的帧。
IDR图像的标识:idr_pic_id。不同的IDR图像有不同的idr_pic_id值。只有在作为IDR图像的I帧才有这个句法元素。在场模式下,IDR的两个场有相同的idr_pic_id值。idr_pic_id的取值范围是[0,65535],和frame_num类似,当它的值超过这个范围时,它会以循环的方式重新开始计数。

一.关键帧的痛点

在视频领域,电影、电视、数字视频等可视为随时间连续变换的许多张画面,而帧则指这些画面当中的每一张。如果把这些帧转换成图片文件,并原封不动的根据一个指定的格式连续摆放的话,就得到了一个视频文件。当然,这个文件有点类似电影的胶片。

不过如果按照如此的方式存储视频的话,文件势必会变得很大,而且其间有很多重复的数据。所以需要专门的算法对视频文件进行编码。对于视频的编码格式来讲,常见的就是H264。

一旦视频进行编码之后,得到的文件可以看做是连续的一组帧的集合,而这一组帧中的每一个都是有自己的类型的。帧的类型分为以下3种:

* Inter Frame(I帧)

* P-Frame(P帧)

* B-Frame(B帧)

其中只有I帧中的数据是可以自描述的,也就是说当我们获得I帧的数据之后,就可以直接解码出当前帧的图像,对于B帧和P帧来说需要找到对应的一个或者多个参考帧才能解码出来,见图一所示;

图一

因此对于非I帧来说想要进行解码就需要多个参考帧进行计算,并得出最终的结果。由此引出了Group of Picture的概念。

对于P帧和B帧来说,他们所包含的内容可以理解为针对其参考帧的一个patch,也就是一个变化量,所以他们不用包含整个图像的信息,只要描述好与参考帧之间的变化关系即可。所以在字节大小方面,P帧和B帧要远远小于I帧的。这也是视频压缩能节省空间的一个原因所在。

二.GoP性能调优分析

Group of Picture(以下简称GoP)顾名思义就是有一组帧组成的一个序列。Wikipedia上给出的一个图简单的解释了GoP是怎么回事(见图二):

图二

GoP由I帧开始,后面跟随者一组B帧和P帧,直到下一个I帧之前的帧为一个GoP。了解了GoP之后,就会发现播放器只有在拿到某个GoP中的I帧之后才能播放视频。对于GoP来说,编码器都是可以进行设置的,像OBS,ffmpeg等程序可以通过对应的设置和参数对视频的GoP进行设置。

那么引出了一个问题:GoP到底应该设置多大?那么GoP的大小到底有什么影响呢?

GoP设置比较大时:

好处:由于B帧和P帧的字节大小会比I帧小很多,所以GoP越长,所包含的B帧和P帧越多,响应的压缩比也会更高,或者说同样的码率下,视频会更清晰一些。

坏处:对于视频直播来说,播放器连接到服务器的时间是不固定的,当播放器在GoP中间连接服务器,并获取了中间的B帧和P帧,这时播放器是无法对这些帧进行解码的,需要进行丢弃。所以会导致客户端的首屏播放时间变长(客户端需要等待一段时间才能看到图像)。

2. 如果 GoP设置比较小时:

好处:由于GoP设置小可以降低I帧间隔时间,对于直播来说可以实现秒开的功能。

坏处:由于GoP时间比较短,会导致I帧的比例增高,压缩比降低。同样码率情况下视频的质量会有所下降。

三 .为什么我的HLS视频加载会慢?

HLS (HTTP Live Streaming),Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。HLS格式的视频分为两个部分的。首先,HLS会根据指定的切片时间和实际的GoP大小对视频进行切割,并生成.ts文件。其次,HLS会生成一个.m3u8文件来保存这些ts文件的索引。

HLS协议可以用来做点播,也可以用来做直播。HLS直播是对直播流实时进行格式转换,并切片出.ts文件,同时更新.m3u8文件。客户端通过间歇获取新的.m3u8文件来获取新的.ts文件的索引。HLS点播是通过预先转码好的视频进行切片,并生成一个完整的.m3u8文件,客户端通过获取.m3u8文件来得到视频的时长和各个.ts切片文件的索引。

对于HLS格式的直播来说,.m3u8文件会在生成完一个.ts文件之后才生成。所以对于HLS直播来说,刚开始推流时,到第一个.ts文件生成完毕之前是无法打开的。同理,HLS的延迟也是跟.ts文件切片时间相关的。也就是说HLS的ts文件切片时间为1秒的话,HLS直播的延迟最小为1秒。

当然对于播放端来讲,下载.m3u8文件,然后下载第一个.ts文件也是需要花费一点时间的,那么这个时间也会加在延迟中。

对于.ts文件的切割来讲,并不是告诉直播服务器指定1秒切一个.ts文件他就能保证1秒切一个.ts文件的。.ts文件的切割还是要根据直播视频的实际GoP大小来进行切割的。之前已经讲过,任何一个视频流在播放端需要能获取到完整的GoP才能播放,所以一个.ts文件所实际包含的时间是GoP的整数倍。

例如:当视频的GoP设置为1秒,.ts切片时间为2秒时,实际的.ts文件切片所包含的视频为2秒。当视频的GoP设置为5秒,.ts切片时间为2妙时,实际的.ts文件切片所包含的视频为5秒。

从上面两个例子不难发现,视频流的实际GoP对HLS切片的时间影响非常大。如果视频流的GoP大小设置不合适的话,那么HLS的切片时间就会变长,同时也会增加HLS的延迟。这个特性对于HLS直播来讲简直就是延迟杀手。如果推流上来的GoP为10秒的话,不要说切出来一个.ts文件就要10秒,同时下载一个.ts文件所花费的时间也会大大增加。

另外如果是HLS点播的话,流的GoP设置过大也会影响点播视频的加载时间。一般的一个720P的视频,如果切片时间为2秒的话,单个.ts文件也就是在百K字节上下。对于现有的网速来讲,下载这么一个.ts文件很快。但如果源视频的GoP很大,会导致第一个.ts文件所包含的视频时常变长(比如10秒),同时导致.ts文件的大小膨胀到接近1M字节上下。想想看,如果第一个.ts文件是1M字节的话,播放器下载这个.ts文件的时间会是多久。

四.为什么我的RTMP直播首屏渲染速度很慢?

RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议,主要用来在Flash/AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。

对于RTMP协议的直播视频来说,它并不像HLS协议需要切.ts文件,它只是把视频流实时地进行转发即可。RTMP协议本身也会抽象出一个Packet的概念来封装H264编码中的帧,也就是一个Packet会包含1到多个帧,播放器以Packet为单位来进行解码。那么RTMP的问题在于客户端连接的时间点是否合适。

例如一个RTMP直播流的GoP设置为2秒,如果客户端接入时间刚好是第4秒,那么客户端会获取一个包含I帧的Packet,由于I帧是自描述的,所以客户端可以直接解码出该帧的画面并显示出来。但是当客户端的接入之间为第5秒,那么他会获得一个包含B帧或者P帧的Packet,由于客户端拿到的数据是一个不完整的GoP,所以客户端只好抛弃当前获取的Packet中视频的数据,而且只有当获取到包含下一个GoP的I帧的Packet时才能解码出图像。因此客户端会等待1秒才能播放出画面。

由此我们可以得出一个结论:GoP的大小会影响RTMP播放端的首帧加载时间。也就是说首帧加载时间最久为一个GoP的时间。当然,如何客户端运气够好的话,可以瞬间播放。

为了优化首帧加载时间,我们可以在流媒体服务器端增加一个缓存,把上一个GoP缓存在内存中。如果客户端接入的话,我们首先放出来的是上一个GoP。这样客户端接到的数据永远是一I帧开头的数据。

不过这种方案对于延迟要求比较高的场景下就不适合。毕竟GoP的缓存会增加一个延迟,具体延迟的时间也是跟GoP大小相关的。如果要实时性,那么GoP缓存并不能很好的解决问题,只能通过减少关键帧间隔的方式来进行调优了。

五.结论

在视频直播和点播盛行的年代,对于GoP大小的取舍还是需要看具体应用场景。对于直播来讲,对延迟要求敏感的应用来说,1~2秒的GoP大小还是比较合适的,至于GoP缓存来讲,还是不用为好。如果是对延迟要求不敏感,对首屏播放时间很敏感的应用,GoP还是1~2秒最为合适,GoP缓存应该是必备的。另外直播使用HLS的话,延迟是绝对PK不过RTMP的。

对于点播的应用来说,视频加载速度是个硬指标,如果不是HLS格式的话,GoP大小适当选大一点可以降低视频文件大小,提高视频打开速度。HLS格式的话,还是推荐在2秒左右,否则很影响视频打开速度的。

其实视频直播技术的挑战很多,这次分享的只是其中一小部分,也是迅达云SpeedyCloud研发团队的经验总结,希望能够和大家多交流,一起为技术社区发展做些有益的事情。

六.Q&A;

问题1:求推荐视频直播的知识资源,关于rmtp、h264编码的更多知识。大量视频文件存储服务器技术方案有哪些?

这方面的内容,可以通过翻阅wikipedia,Google来获取想要的知识。另外看一些开源项目的文档也会有所帮助。比如Nginx-RTMP-Module,Simple-rtmp-server,ffmpeg

问题2:视频直播这块有没有开源项目可以来练手的?

上面说到了Nginx-rtmp,SRS,FFMPEG

问题3:nginx 的rtmp模块,可以配置gop缓存时间吗?

nginx-rtmp是没有GoP Cache的。Simple-RTMP-Server是有的。

问题4:求现在创业公司如何快速搭建自己的直播平台,是自研还是使用第三方平台,有哪些第三方平台可以推荐?

搭建直播平台的话,涉及的东西会很多,一般都是自研一部分,外包一部分。

问题5:老师说到B侦和P侦需要参考侦才能解码,但在GoP的那张图中没有看到参考侦呀?

最前面和最后的两个橘黄色的是I帧。另外参考不一定是I帧,前后的B帧和P帧都可以做参考

问题6:ffmepg里,哪个选项是配置 x264的 gop的?

-g参数可以设置gop长度,单位是帧。

问题7:就rtmpserver来说,哪些server 使用者更多些?

这个看应用场景了,如果是自己用,Nginx-rtmp比较容易一些,如果是做CDN的话,simple-rtmp-server是个比较好的选择。

Hello,

I have a suggestion for a new x264-parameter.

Example: I've the following parameters:

--scenecut 40 --keyint 300

The first frame be an I-frame. Now, P- and B-frames are following. If the scene change is less then
40%, no further I-Frame is set. If there are 300 frames, an I-frame is set, independed from the percental scene change at this frame. Depending on the movie there are up to 50% I-frames set at the value, specified with --keyint.

We suppose that the percental scene change of all frames is less then 20%, except frame 295. The percental
scene change of frame 295 is 35%. But x264 sets the I-Frame at position 300. I think, it's more clever to set the I-frame at position 295 instead of position 300.

Now my suggestion for a new parameter, for example "--scenecut-cutback":

--scenecut 40 --keyint 300 --scenecut-cutback 250,10

x264 uses the scenecut value 40 up to frame 250. From frame 250 to 300, the scenecut value is decreased
by 10 to 30.

Or another syntax:

--scenecut 40 --keyint 300 --scenecut-cutback 250

x264 uses the scenecut value 40 up to frame 250. From frame 250 to 300, the scenecut value is linear
decreased to zero.

And now my favourite syntax:

--scenecut 40 --keyint 300 --scenecut-cutback 250,10

x264 uses the scenecut value 40 up to frame 250. From frame 250 to 300, the scenecut value is linear
decreased to 10.

Goal is to reduce the number of I-frames set at the position --keyint, because the scene change value
of this frame may be low.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: