目前几乎全部的视频点播网站所有采用HTTP协议传输数据。由于相对于诸如RTMP等协议来讲,HTTP协议是无状态的,数据传输完毕就断开链接,这样服务器就能够腾出资源来服务更多的用户。而RTMP则会在用户播放期间一直维护一个链接,这样服务器的负载就很是有限。并且HTTP服务器,CDN等都已是很是成熟的技术,成本低性能好。另外HTTP的请求能够直接使用浏览器Cookie,容易和网站业务打通。最后,HTTP还能使用浏览器缓存,这算优势也算缺点,优势是请求一样的资源能够直接从缓存中取,缺点是安全性差了点。nginx
HTTP拥有更好的性能,可是无法传输太实时性的东西,不然性能还不如RTMP,好比视频聊天,直播这些。apache
有时候咱们被访问的视频可能须要作一些限制,好比防盗链,视频收费等等。若是采用HTTP协议的话,传统的鉴权方式就足够,Cookie里带token什么的判断是否有权限访问视频资源,细节我就不说了。惟一的问题是一旦用户有权限访问视频,就有可能把视频下载下来用做他用。浏览器
为了让HTTP能服务更多的用户,同时维护更少的链接,咱们须要传输尽快完毕。但这是咱们分段的理由吗?不是,由于不管分段不分段,一个用户加载彻底部的视频数据对服务器占用时间是必定的(假设传输速度必定),甚至会多占用不少建立链接和销毁链接的资源。缓存
可是咱们看到各大视频网站实际上都是有对视频分段的,这里我就谈谈视频分段的好处。安全
咱们在Flash端如何播放视频很大程度上受NetStream提供的功能所限。因此这里大体介绍下NetStream提供的功能和一些限制,这也是为何后面程序要这么设计的缘由。服务器
直接播放单个视频文件的方式我就不说了,我这里介绍的是如何像播一个完整文件同样播放通过分段的视频。这个方案有些许瑕疵,后续的方案都是基于这个方案进行优化的。网络
服务器咱们采用上面提到的第一种最简单的静态分段。而且在视频开始播放前咱们会拿到一个包含视频分段的开始时间,结束时间,以及分段地址的列表,还有个总的视频metadata信息。app
当视频列表加载完毕后就能够开始依次经过NetStream加载播放各个视频分片了,每一个分片用一个NetStream实例控制。如图所示。性能
咱们能够设定一个最大缓冲距离,结合当前播放进度,算出一个容许缓冲位置,在这个容许缓冲位置以内的切片均可以依次开始加载,开始加载的时候暂停住不播放。当一个切片开始加载以后是不会中止的,因此实际缓冲进度可能会大于容许缓冲位置。优化
当一个切片播放完毕以后不要急着把它关掉,它可能须要留着供后续的seek使用。紧接着,咱们把下一个分片执行resume方法来让他播放。这样多个分片按照顺序播放,对外界来讲就像播放一个完整的视频同样。
这种结构下,若外界须要对视频进行seek操做,能够分三种状况:
因此咱们能够看到,静态分片方式的在seek的处理仍是仍是有不少不足的,对未加载部份内容的seek都不能作到很是精确。不过若是将切片切得比较短小的话这个问题能够有所改善,可是还会带来另外的问题,这个问题我后面讲。另外咱们能够再静态分片的基础上引入了start参数,也就是上文提到的“静态分片+start参数”类型服务器。
引入了start参数后对上面的二、3两种seek状况进行了改进:
如此以来在任何状况下seek均可以精确到关键帧,缺点是把正在加载的切片关掉会形成数据浪费。从切片中间开始加载也会形成一个切片内容不完整。下次seek的时候若是不巧是在这个切片start位置以前,就须要从新加载该切片。这些都会形成数据浪费。好在通常用户不会吃饱了没事儿seek来seek去。
从上文的几个策略能够看出,若是视频分得越短小,不管对seek的精确度,仍是数据浪费状况都是有好处的,可是这带来的一个问题是须要实例化更多的NetStream来维护切片。另外对于时长较长的视频来讲,NetStream的数量也会变得不少。但实际上NetStream能同时开启的链接数量是有限的,这不是内存问题,而是Flash提供的链接数有限。超过了这个限制NetStream就没办法正常工做了,并且也不报错。这个限制在不一样浏览器下还不同,我怀疑这和浏览器底层有关。
因此为了限制NetStream的数量,咱们须要设计一个NetStream链接池来管理全部的NetStream。链接池上限不能小于最大缓冲举例可能加载的最多分片数,不然逻辑上就是有问题滴。
咱们能够从链接池中取得一个新的NetStream来使用(这个NetStream多是别的NetStream关闭后的,不过你能够把它当新的用)当链接池数量满的时候,他就会自动把一些老的处于链接状态的NetStream关闭掉。这个淘汰原则是基于空间局部性原理的,也就是说和当前播放头位置距离最远的切片应该首先被关掉(处于最大缓冲距离以内的切片不能关闭)。由于根据几率统计发现大部分的seek都出如今播放头附近(可能为了找什么情节)。
经过多个NetStream切换的方式播放视频,在切换的时候会出现不明显的爆音,可是仔细听仍是可以发现。这也是我在上文中提到的文件分割得过短小出现的另一个问题,爆音太频繁了,可能影响视频观看。
因此要从根本上解决这个问题,咱们就要放弃NetStream切换的方式,转用数据生成模式。数据生成模式能够把请求的切片作得很小(但也不要过小,不然服务器性能下降)。切片作小的一个好处是请求更快的完成,那么请求被打断的概率就会下降,当请求完成以后,下次请求一样的资源就能从浏览器缓冲中取。因此小切片更容易被缓存。而上文中的小切片产生的问题在这里不复存在。如图所示。
咱们根据播放头的位置,日后加载分片数据,直到最大缓冲距离,这和前面提到的方式相似。然后咱们把这些加载的二进制数据保存在内存中。从播放后日后必定的距离(咱们称做NS缓冲长度),若是有分片进入,那么就把它appendBytes到用于播放的NetStream中。图中所示的蓝色部分就是保存在内存中的数据,它也有前面提到的链接池相似的淘汰机制用于控制内存总大小。被从内存中释放掉的数据,咱们能够在浏览器缓存中找到(由于已经加载过了),若是要使用的话,咱们能够像请求服务端数据同样的方式快速请求到这些数据(固然比从内存中慢一些)。图中白色方框的是还未加载过的数据,他们在服务器上等待加载。如图所示就是数据的三级查询。
若是用户进行seek:
若是分片数据较大,seek的位置在分片中间,那么也能够从分片中间开始加载,这样能够从逻辑上把一个分片分为了两个。
数据生成模式从本质上保证了播放质量,杜绝了数据浪费,保证了seek精确度,服务器实现上也异常简单,真是视频播放首选!
这种方式须要服务器作实时分片并分发到CDN。好比服务器从直播数据源里把30秒的视频数据打包成一个数据包分发到CDN上,因此理论上直播至少会延迟30秒。不过对于实时性不是特别强的直播,这种方式的负载能力会更好。
传统的长链接方式直播,须要客户端和服务器一直保持链接,服务器须要维护每一个客户端的链接,但实际上传输30秒的视频数据只须要1秒,因此若是采用HTTP的方式,由于传输完毕就能够服务别人了,因此理论上维护链接的效率能够提升30倍。
这里咱们要求服务器提供一个视频地址列表,列表里提供了最新的N个视频分片地址。这样客户端经过轮询这个视频列表就能让客户端和直播保持同步。
如图所示客户端维护着一个切片列表队列,经过轮询服务器,咱们把最新的视频地址添加到队列中,而播放模块则从队列中取出最老的切片地址加载播放。
若是用户网络较差,那么播放就会卡顿,因此从队列中取出切片地址的频率就会下降,队列会愈来愈长。队列越长说明视频播放的延迟越大。
因此当队列长于某一个临界值时(咱们设定的),咱们就把队列清空到只剩一个最近的地址,直到下一次这个地址被取出时,才容许队列继续变长。这个队列清空的操做其实是对因播放卡顿引发的延迟作了矫正,让直播不要延迟得太厉害。