固阳音箱协会

打开天窗-谈谈H5的视频播放器

前进社ForwardStudio2019-01-09 15:57:21

2016年,对于很多宅男来说是一个丰收之年,直播的火爆前所未有,各路群雄崛起。直播技术的发展将一个个打扮妖娆的主播推到屏幕之上。

 

作为一名技术人员,视频直播技术远比主播更具吸引力。借此热点,今天我们就来聊一聊web前端视频播放,并附上一些实践经验。(看官们可能很奇怪今天的配图系列,可以在文末点击阅读原文欣赏我们一百种生活新一集的视频)


需要什么样的搬运工具

视频也是文件的一种存在形式,不管是直播还是录播,客户端都需要从服务器端下载文件,在客户端播放,这个过程效率的好坏直接影响了播放体验。人类对于速度的追求从未停止过,尤其是这种具有上下文信息的视频,卡顿不能忍。我们需要可靠的协议来完成视频传输的任务,这个协议需要有这些特点:稳定可靠、兼容性好、尽可能降低文件一次传输的大小以加快速度。目前,前端可选的视频解决方案大致分为两种:


1. HTTP Range

HTTP Range是简单粗暴的方式,利用了http的range特性来实现的,适合1-2m的短视频播放要求,老的短视频网站都在使用,我们项目的视频较大,不太合适,这里不做赘述

 

2. HLS (HTTP Live Streaming)

HLS协议是苹果公司实现基于HTTP的流媒体传输协议,可实现流媒体的直播和点播。原理是将视频流分片成一系列HTTP下载文件,可以这样认为HLS是以点播的技术方式来实现直播。由于数据通过HTTP协议传输,所以完全不用考虑防火墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。但是由于分片下载的特点,会有较高的时间延迟(10s)。就兼容性来说,苹果和安卓都良好的支持HLS协议。

上图是苹果官网的截图,可见HLS是套完整的视频解决方案,包括三个部分:Server/Distribution/Client。它可以支持直播和录播,无论对输入的视频格式的兼容处理,还是播放的性能优化,都做了很好的设计优化,故考虑一系列因素,在短视频播放项目中,我们选取了兼容性更好的HLS协议。


还差一个前端播放器

视频播放的项目中,视频是预先拍摄好的,通过工具切片存储在服务器上,HLS协议是依靠m3u8文件下载视频流切片。简单来说,m3u8就是一个视频资源索引。HLS从m3u8文件得到视频流信息,然后发出一次次HTTP请求,将视频流切片不断的下载到客户端。那么,问题来了,web前端还需要一个播放器。感谢伟大的开源社区,已经有大牛造好了轮子video.js。

 

我们知道,标准的HTML5是支持video标签的,即H5页面本身支持视频播放能力。原生的H5支持三种视频格式: mp4, webm, ogg。但从系统层面来说,MP4目前是通吃的。

回到我们刚才引出的主角: video.js。为什么要video.js?我本人认为有两点原因:解决兼容性和提升播放体验。

 

虽然video标签已经成为H5标准的一部分,而且各大浏览器厂商也都支持H5。但是H5本身提供有关video的功能较少,UI层面更是没有一个统一标准,各种浏览器对video标签的渲染更是各有特点,这就造成播放体验的下降,要知道现在的用户都是很挑的。video.js本身通过js渲染UI层面,统一了标准(虽然还是会出现点兼容性的问题),提供了统一的播放界面及上层API。另外,video.js还提供了对不支持H5 video的兼容处理,当检测到浏览器不支持H5 video标签时,采用Flash播放视频。

 

HLS协议出自苹果,苹果自家的产品对其支持是最好的,safari浏览器中的video标签是直接支持HLS协议的,也就是说你可以直接把m3u8资源给video标签,safari浏览器本身给你做解码工作。而其他浏览器使用HLS是需要解码器的。video.js本身不支持HLS协议解码,这又要感谢伟大的开源社区,video.js支持插件机制,通过开源的hls插件即可解决基于HLS视频流播放的问题。


实践踩坑记录

先简单介绍下项目技术背景: 整个站点是采用Angular构建的单页面应用,播放的视频存储在文件服务器上,通过接口可以拿到m3u8文件。前端播放视频组件使用video.js及其配套的HLS解码插件。

 

前端H5页面的代码没什么好说的,就是标准的video布局列表

<div class="video-player">
    <video  id="videoBox" vjs-control class="video-js vjs-16-9 vjs-default-skin vjs-big-play-centered " controls preload="auto"   data-setup='{}'  >
</video>

 

需要注意的是class属性不能少,这些class决定了播放器的皮肤样式及一些初始化设置。可以参考官方的doc。

 

自己之前也没有使用过video.js,下面的这些坑也是开发过程中遇到到,解决方案希望能帮助正在同样踩坑的小伙伴们:


1. 初次进入播放页播放页面渲染正常,可以播放视频,浏览器返回到前一个页面,然后再进入到播放页,视频无法播放,页面渲染不正常,刷新后正常

 

一开始遇到这个原因无从下手,在网上搜索也没有找到很明确的答案,后来自己大胆猜想可能是返回前一个页面的过程中,video.js构造出的player对象并没有销毁,重新进入播放页后因player对象没有销毁,所以video.js认为video标签已经渲染。解决的方式是监听播放页scope的destroy事件,触发destroy事件的时候直接使用video.js提供的API对player对象进行销毁,解决问题。

 

2. 接口请求m3u8资源造成播放页面渲染失败

主要的原因是接口请求资源和前端渲染video标签之间存在着时间差,在video.js渲染video标签的时候m3u8资源还未拿到,造成播放页渲染失败。解决的方法是在接口请求资源成功的回调函数里使用video.js的API主动触发渲染行为,代替默认加载页面就渲染video标签的行为。

    var player = videojs("videoBox");
    player.src([
    {type: "application/x-mpegURL", src:videoUrl}
    ]);

 

3. 移动端浏览器的兼容性问题

很大程度上,video.js已经统一了各种浏览器播放UI层标准,提供了标准的API。但是在作者实践的过程中还是遇到了较多的兼容性bug,而且解决这些bug没有什么好办法,需要花费较多的时间一个个调试解决。好在这些bug集中在ui层面,并不影响最基本的播放功能。先总结一下,方便大家调试开发:

a. 部分浏览器小窗口下显示双进度条

这是由于进度条的mouse样式在移动端浏览器上出现造成,解决方案是对样式进行覆盖操作.

 

b. 拖动进度条,时间更新滞缓

这在video.js的github主页上进行了讨论,时间seeking的机制有问题,在issue页有人建议用mouse on 时的时间数据替换目前的seeking机制,目前官方没有给出信息,静待新版本更新解决。

 

c. 播放的时候,出现浏览器自带控制条,点击播放,控制条消失,无法被再次唤醒

浏览器自带的控制条覆盖了videojs本身生成的控制条,解决方式是将videojs的控制条的z-index 设置到最大:

.video-player
  z-index 2147483647

.vjs-control-bar
 z-index  2147483647

 

 

还需要将浏览器的样式覆盖(user agent style)

 video::-webkit-media-controls
  display none !important

 

 video::-webkit-media-controls-panel
  display none !important

 video::-webkit-media-controls-enclosure
  display  none !important

以上工作,基本上可以消除原生html5 控制条对videojs样式的干扰。

 

其他的bug解决思路如上,就不再赘述。

 

4.压缩问题

项目目前使用gulp进行打包,一开始引入video.js是压缩后的文件,再次压缩代码出现了问题,建议遇到同样问题的小伙伴可以试一下使用非压缩的js,然后通过自己的压缩代码工具进行压缩,这样可以防止过度压缩带来的问题。


5. Firefox移动端浏览器无法播放

github主页上有这个方面的讨论,应该是firefox移动端对hls的支持有问题,我尝试使用flash播放依然不能解决问题,静待更新吧。好在移动端firefox占有市场率不高。


再说一说flv.js

前端时间bilibili开源的flv.js引起了一阵骚动,我第一时间关注了这个开源库,目前这个库在github上的星星数已经突破8000了,更是在hacker news前十排行榜上待了一段时间,非常了不起。这个离职前拿着月薪5000的大牛是要腾飞了,同时也替bilibili可惜一下。

 

简单来说,flv.js了不起的地方在于通过js实现在网页端播放flv格式的文件,摆脱了flash的依赖,君可知flv格式相比mp4格式要简单的不知道多少。

 

Mp4那复杂的数据结构图就不放了,辣眼睛。

 

flv.js的核心功能是建立在Media Source Extensions标准之上。那什么是Media Source Extensions(以下简称MSE)?详细的解释大家可以谷歌,我本人的理解是W3C颁布的一套标准,使得js能够对流媒体进行处理。

flv.js的source code 里有两个文件夹叫remux和demux, 通过名字可以猜出这个文件夹里的代码是解析视频流。我看了下源码,很多位运算,应该是通过原生的js对视频流进行解码,而不是先用c++写好解码器,再编译成js API。flv肯定需要靠协议进行传输, flv.js对传输过来的内容进行解码生成video标签支持的视频格式,然后不停的喂给video标签,这样就实现了不依靠Flash播放只通过js播放flv的目的。是不是非常厉害。摆脱了flash,mac看flv从此风扇不再狂转了!但是,flv.js本身是建立在MSE标准之上,MSE的兼容性还存在问题,这次不是IE惹祸,而是移动端的safari不支持MSE!所以目前大家还是持谨慎态度,等MSE的兼容性没问题的时候也许flv.js又要火一把了。

 

以上是我的一些关于h5视频播放技术的总结。


最后,向内容致敬,向技术致敬。


点击阅读原文,可以欣赏新一集视频,走心的。如果您安装了掌上生活,可以在首页下拉进入天窗视频,欣赏多支视频。


参考链接:

Video.js官网 http://videojs.com/    

github主页: https://github.com/videojs   

Flv.js github主页: https://github.com/Bilibili/flv.js 

Media Source Extensions主页:https://www.w3.org/TR/media-source/ 

Video file format wiki: https://en.wikipedia.org/wiki/Video_file_format 


Copyright © 固阳音箱协会@2017