洞察 video 超能力系列——玩转 flv


从2016年10月(Chrome 54)开始,Chrome再也不内置flash,而是改成用户第一次访问flash资源时提示安装。从Chrome62开始,再也不提供“click to play的选项”,改成点击视频box后,左上方弹出html

这意味着,flash做为过期的标准将被新技术所取代。前端


前言

从咱们能够在网站上播放视频开始,到h5播放器们如火如荼地发展以前,使用flash一直都是web播放视频的不二之选。甚至于说得更加普遍一些,在html5成为主流以前,网站的多媒体能力,包括动画、游戏和视频一直都是adobe生态在掌控。那么随着html5标准的推出,这一切都成为了过去式。html5

flv与直播方案

flv的全程是 Flash Video,顾名思义,就是专门给flash播放器提供的播放格式,这种格式具备结构简单、清晰的优势,最先出现是为了解决flash导出的swf文件体积过大,不适合在web中播放的问题。随着flash的逐渐淘汰,新的video标签天然是不支持flv格式的,人们开始把web视频的重点放在mp4或者hls上,可是随着直播这种视频形式的火热兴起,flv迎来了新一轮的生机。git

咱们来横向对比一下可用的直播方案:github

flv播放实现·跳转·清晰度切换web

能够看到,flv因为其编码格式的特色,只须要一个MetaData 以及音视频Track各自的Header就能够在任意的时间点播放,极大地符合实时直播的需求,在GOP足够小的状况下甚至能够达到0延迟,能够说在现有的技术方案里,http-flv是最理想的一个,正是所以,flv依然是web播放器不可或缺的一个格式支持。
算法

首先咱们经过一张图了解一下前端播放flv的过程缓存

从上图能够看到,flv播放的过程实际上是从flvt中提取元信息、音视频header以及数据,而后转码成fmp4盒子结构的过程,再经过MSE交给video的过程。由于flv的结构自己就是流式的,也就是说,它的数据被拆分到了不少个小的tag中,因此咱们能够很方便地作数据的封装,也就是说将一段flv tag数据封装成一个独立的moof_mdat盒子对,而后就能够直接交给MSE处理,很是的方便。这里咱们讨论flv是如何处理加载、跳转播放、重放及清晰度切换问题的服务器

数据加载 数据加载直播和点播两种模式,首先来聊一下点播:针对点播的数据获取,咱们采用Range这个参数做为分段加载的控制参数
websocket

如上图,Range这个参数向服务器描述了但愿加载一个flv文件的 100000 到 3987705 个字节 这段数据,相应的,服务器也须要可以根据Range参数正确返回这段数据。这里展现一个极简的服务端代码:

从上述代码能够看到,服务端是先是解析range,根据range切出文件分段,而后返回一个readableStream交给前端。

再看一下直播:直播跟点播是很是不同的数据流动结构,咱们看一下简单的直播流程

能够看到,直播的流程是一个 推流->服务端格式编码-> 终端播放 的一个流程。那么这里就带来了一个问题,咱们不能够像点播时那样去加载数据了。由于服务端实际上保存的是一个文件流,不断有数据推到服务端,播放器也不存在缓存一段数据这样的状况,而是不断向服务器请求,只要有新的流到达服务端,播放器就要拿到这一段进行解码播放,达到推流和拉流同步进行的一个直播效果。为了实现这种效果,在播放器内,咱们使用 fetch+ streamReader,简单的实现以下:

从上述代码咱们能够看到,咱们经过一个reader递归地从流里面读取数据,再将数据交给解码器进行处理。关于直播的数据加载还有更加先进的websocket方式,这里再也不深刻探讨,有兴趣的同窗能够自行查阅相关资料。

播放时跳转 播放时点击进度条跳转(下称seek)是一个很是高频的操做,尤为是在长视频中。用户遇到不想看的部分,或者想重看的片断,都会点击进度条触发seek。举个例子,一段视频,可能用户实际观看的部分只占不到50%, 若是咱们将整段视频加载,那剩下加载的50%就浪费掉了。因此咱们要作的事情就是精确地加载用户但愿播放的部分,节约流量。解决方法以下:

  1. 获取用户将要跳转到的时间点,下称seekTime根据seekTime计算出离该时间点最近的一帧的位置,称为 startPos

  2. 以预加载时间为30s 为例, 计算出seekTime + 30s 这个时间点最近的帧位置 称为 endPos

  3. 咱们以 Range: startPos-endPos 为请求参数,向服务端请求这一段数据

  4. 解码播放

这里提及来简单,可是涉及到了一个跟flv格式紧密相关的问题,那就是flv的onMetaData信息里是否具备keyframes这个属性。咱们上述所提到的,基于时间点计算某一帧位置的算法,是彻底依赖keyframes这个属性的,它记录了flv中全部关键帧的时间点和文件偏移量,keyframes大概长这个样子:

times中每个时间点都对应着同位置的fileposition的偏移量,正是基于这一点,咱们能够计算须要加载的数据Range。值得注意的一点是,并非全部flv文件都携带了keyframes头,flv文件缺失部分onMetaData属性是很常见的事情。缺失了keyframes信息,咱们就没办法作跳转了,所以咱们须要借助额外的工具帮咱们补全flv的onMetaData。这里推荐使用yamdi,一个轻量级的工具,有兴趣的同窗能够看一下它的使用。

清晰度切换 清晰度切换是一个很是重要的功能,为何重要呢,由于用户会根据网速的快慢,去选择更清晰或者更流畅的视频。假设没有这个选项,用户看视频卡顿了无法切换清晰度,就只能愤愤地关掉窗口了。那么多个清晰度的视频源咱们如何作到无缝的切换呢?咱们分状况来讨论一下:

点播中的清晰度切换方案 点播的切换清晰度是比较容易实现的,流程以下

如图,简而言之,就是尝试加载视频B,经过从视频B的onMetaData中提取关键帧信息,推算出当前应该加载哪个关键帧,而后加载这个位置以后的数据,直到数据加载到以后,将视频B的数据交给MSE,同时,清除掉以前视频A在buffer中的缓存。

直播中的清晰度切换方案 直播清晰度切换目前尚未发现无缝的方案,所以建议在切换时,直接重建解码器以及MSE,关于这方面的问题咱们还在探究。更多内容请关注咱们的开源播放器,也欢迎来咱们github提issue。

相关文章
相关标签/搜索