视频播放弱网提示实现
作者:陈盛靖
一、背景
业务群里面经常反馈,视频播放卡顿,视频播放总是停留在某一时刻就播放不了了。后面经过排查,发现这是因为弱网导致的。然而,用户数量众多,隔三差五总有人在群里反馈,有时问题一时半会好不了,用户就会怀疑不是网络,而是我们的系统问题。因此,我们希望能在弱网的时候展示提示,这样用户体验会更友好,同时也能减少一定的客诉。
二、现状分析
我们使用的播放器是chimee(www.chimee.org/index.html)。遗憾的是,chimee并没有视频播放卡顿自动展示loading的功能,不过我们可以通过其插件能力,来编写一个自定义video-loading的插件。
三、方案设计
使用NetworkInformation
常见的方法就是我们通过设定一个标准,然后检测用户设备的网络速度,在到达一定阈值时展示弱网提示。这里需要确定一个重要的点:什么情况下才算弱网?
我们的应用是h5,这里我们可以使用window对象中的NetworkInformation(developer.mozilla.org/zh-CN/docs/…),我们可以通过浏览器的debug工具,打印window.naviagtor.connection,这个对象内部就存储着网络信息:
![]()
其中各个属性含义如下表所示:
| 属性 | 含义 |
|---|---|
| downlink | 返回以兆比特每秒为单位的有效带宽估计,四舍五入到最接近的 25 千比特每秒的倍数。 |
| downlinkMax | 返回底层连接技术的最大下行速度,以兆比特每秒(Mbps)为单位。 |
| effectiveType | 返回连接的有效类型(意思是“slow-2g”、“2g”、“3g”或“4g”中的一个)。此值是使用最近观察到的往返时间和下行链路值的组合来确定的。 |
| rtt | 返回当前连接的有效往返时间估计,四舍五入到最接近的 25 毫秒的倍数。 |
| saveData | 如果用户在用户代理上设置了减少数据使用的选项,则返回 true。 |
| type | 返回设备用于网络通信的连接类型。它会是以下值之一: bluetooth cellular ethernet none wifi wimax other unknown |
| onchange | 接口的 change 事件在网络连接信息发生变化时被触发,并且该事件由 NetworkInformation(developer.mozilla.org/zh-CN/docs/…) 对象接收。 |
其中,我们可以通过effectiveType判断当前网络的大体情况,并且可以拿到一个预估的网络带宽(downlink)。我们可以通过监听onchange事件,在网络变差的时候,展示对应的弱网提示。
这个方案的优点是:
- 浏览器环境原生支持
- 实现相对简单
但缺点却十分明显:
- 网络状态变化非实时
effectiveType的变化可能是分钟级别的,对于短暂的网络波动,状态没办法做更精细的把控
- 存在兼容性问题
对于不同一些主流浏览器不支持,例如Firefox、Safari等
![]()
- 不同设备间存在差异
不同的设备和浏览器,由于其差异,在不同的网络情况下,视频的播放情况是不一样的,如果我们固定一个标准,可能会导致在不同设备下,同一个网络速度,有人明明正常播放视频,但是却提示网络异常,这样用户会感到疑惑。
那有没有更好的方法呢?
监听Video元素事件
chimee底层也是在html video上进行的二次封装,我们可以在插件的生命周期中,拿到对应的video元素节点。而在video标签中,存在这样两个事件:waiting和canplay。
其事件描述如下图所示:
![]()
当视频播放卡顿时,会触发waiting事件;而当视频播放恢复正常时,会触发canplay事件。只要监听这两个事件,我们就可以实现对应的功能了。
四、功能拓展
我们知道,现在大多数网站的视频在提示弱网的时候,都会展示当前设备的网络速度是多少。因此我们也希望在展示对应的信息。那么怎么实现网络速度的检测呢?
一个简单的方法是,我们可以通过获取一张固定大小的图片资源(不一定是图片,也可以是别的类型的资源),并统计请求该资源的请求速度,从而计算当前网络的带宽是多少。当然,图片大小要尽可能小一点,一是为了节省用户流量,二是为了避免在网络不好的情况下,图片请求太慢导致一直计算不出来。
具体代码如下:
funtion calculateSpeed() {
// 图片大小772Byte
const fileSize = 772;
// 拼接时间戳,避免缓存
const imgUrl = `https://xxx.png?timestamp=${new Date().getTime()}`;
return new Promise((resolve, reject) => {
let start = 0;
let end = 1000;
let img = document.createElement('img');
start = new Date().getTime();
img.onload = function (e) {
end = new Date().getTime();
// 计算出来的单位为 B/s
const speed = fileSize / (end > start ? end - start : 1000) * 1000;
resolve(speed);
}
img.src = imgUrl;
}).catch(err => { throw err });
}
function translateUnit(speed) {
if(speed === 0) return '0.00 B/s';
if(speed > 1024 * 1024) return `${(speed / 1024 / 1024).toFixed(2)} MB/s`;
if(speed > 1024) return `${(speed / 1024).toFixed(2)} KB/s`;
else return `${speed.toFixed(2)} B/s`;
}
我们可以通过setInterval来轮询调用该函数,从而实时展示当前网络情况。系统流程图如下:
![]()
五、总结
我们可以通过Chrome浏览器开发者工具中的Network中的网络配置来模拟弱网情况
具体效果如下:
![]()
成功实现视频弱网提示,完结撒花🎉🎉🎉🎉🎉🎉。