Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【bigo】网页视频autoplay兼容及解决方案 #88

Open
LaiTaoGDUT opened this issue Jan 13, 2022 · 0 comments
Open

【bigo】网页视频autoplay兼容及解决方案 #88

LaiTaoGDUT opened this issue Jan 13, 2022 · 0 comments

Comments

@LaiTaoGDUT
Copy link

LaiTaoGDUT commented Jan 13, 2022

网页视频自动播放的局限

自动播放是指无需经过用户的同意就可以开始播放视频。这包括在video元素使用autoplay属性或者通过JavaScript代码直接调用video元素的play方法。
<video src="/video.mp4" autoplay>

videoElement.play();

非常遗憾的是,各个浏览器都为多媒体的自动播放设置了不尽相同的限制策略,也就意味着目前想要实现有良好兼容性的自动播放,是很难办到的。

各个浏览器对视频自动播放的限制

IOS

IOS9以下版本:

(在当时移动互联网的条件下,播放一个视频的流量和电量成本都是非常高的,因此视频的播放必须要先经过用户同意)

无法自动播放

IOS10以上版本:

(浏览器厂商不给自动播视频,开发者只好曲线救国,使用GIF动图代替视频实现自动播放,但是GIF动图需要消耗的流量是视频的12倍,性能消耗是视频的2倍,并且移动互联网发展飞速,用户对视频播放占用的流量和电量也不再这么敏感,于是决定给移动设备的视频自动播放放宽限制)

<video autoplay>满足下列条件可以自动播放:
1.视频的源是没有音轨的或video元素使用了muted属性手动静音
2.video元素需要在屏幕上可见
3.video元素设置了playinline属性

videoElement.play()满足下列条件可以自动播放:
1.视频的源是没有音轨的或video标签使用了muted属性手动静音
2.video元素设置了playinline属性

以下行为将导致自动播放失效:

<video>元素在没有用户手势的情况下有了音轨或取消了静音,播放将被暂停


Chrome in Android

Android 4.3及以下版本:

4.3及以下版本的安卓,使用的是基于Webkit实现的内核,和ios有着相同的表现
无法自动播放

Android 4.4及以上版本:

4.4及以上版本的安卓,用上了Bink/Chromium内核,有了自己的一套限制规则

Chrome 53版本以前:

​ 无法自动播放

Chrome 53版本以后,Chrome 58版本以前:

<video autoplay>videoElement.play()满足下列条件可以自动播放:
1.视频的源是没有音轨的或video元素使用了muted属性手动静音
2.用户未开启流量节省模式

Chrome 58版本以后,Chrome66版本以前:

<video autoplay>满足下列条件可以自动播放:
1.视频的源是没有音轨的或video元素使用了muted属性手动静音
2.用户未开启流量节省模式
3.video元素需要在屏幕上可见

​ 1.站点被"添加到主屏幕",且视频的源在manifest文件标识的范围内

videoElement.play()满足下列条件可以自动播放:
1.视频的源是没有音轨的或video元素使用了muted属性手动静音
2.用户未开启流量节省模式

Chrome 66版本以后:

​ 在Chrome 58版本的基础上移除了“未开启流量节省模式”的限制


Chrome in PC

Chrome 66版本以前:

​ 完全支持自动播放

Chrome 66版本及以后:

​ 视频的源是没有音轨的或video元素使用了muted属性手动静音

​ 站点是一个PWA应用,并且用户把它安装到了桌面

Safari in PC

Safari 11版本以前:

​ 完全支持自动播放!


Safari 11版本以后:

​ 视频的源是没有音轨的或video元素使用了muted属性手动静音

可以发现,无论是什么浏览器内核,它们对于视频自动播放限制的改动趋势都是相近的,移动端在不断地放松限制,而桌面端则在不断地收紧限制,直到达成了一个近乎统一的标准:只有静音视频才能自动播放。

参考:
Muted Autoplay on Mobile: Say Goodbye to Canvas Hacks and Animated GIFs!
Media updates in Chrome 58
Chrome's autoplay feature
updated video policies for iOS
Auto-Play Policy Changes for macOS

解决办法

1. 静音自动播放

只在桌面端使用的网页,采取静音的方式自动播放视频,移动端则无法在低版本手机中正常运行。

2. 通过用户交互行为解除自动播放限制

在桌面端浏览器上,可以通过在调用video.play()方法之前引导用户与页面产生交互行为,即可使自动播放限制解除。

在移动端,只允许通过用户交互来触发有声媒体的播放,而不是在用户与页面产生交互后解除自动播放限制,因此需要把video.play()方法放到HTMLElement容器的交互事件回调中(点击/触摸)。

document.body.addEventListener('click', () => {
    console.log('触发播放')
    this.videoRef.play();
})

结合静音自动播放与交互播放

  • 视频自动播放时设置 muted: true。
  • video.play()方法绑定到HTMLElement容器的交互事件回调中(点击/触摸)。
  • 在播放界面上通过图标显示当前视频被静音,引导用户点击。
  • 当用户点击绑定的容器时,在事件的回调中将视频再次播放,此时无需设置静音,同时更改静音图标

!用户手势令牌过期

如果需要在获得用户手势令牌后,延迟数秒进行video.play()方法的调用,比如说想要在交互事件回调函数中先异步请求视频链接再进行播放,则需要注意在移动端,用户的手势令牌可能会在N秒后过期,在不同的机型中N的大小也不同,即延迟调用video.play()方法可能会失效。

尝试在点击事件回调中延迟几秒调用play()方法并捕获报错:Play() can only be initiated by a user gesture.

Snipaste_2022-01-13_16-15-11

在线尝试

解决办法:先调用video.load(),再去延迟调用video.play()

<video id="video"></video>
<button id="button"></button>

<script>
  button.addEventListener('click', onButtonClick);

  function onButtonClick() {
    // This will allow us to play video later...
    video.load();
    fetchVideoAndPlay();
  }

  function fetchVideoAndPlay() {
    fetch('https://example.com/srcApi')
    .then(src => {
      video.src = src;
      return video.play();
    })
    .then(_ => {
      // Video playback started ;)
    })
    .catch(e => {
      // Video playback failed ;(
    })
  }
</script>

参考:

DOMException: The play() request was interrupted

3. 检测自动播放,播放失败时回退到用户交互触发播放
  • 通过play API返回的Promise检测自动播放成功还是失败

不使用autoplay属性,而是调用play API来尝试进行自动播放,高版本浏览器会返回一个Promise,如果自动播放失败,则Promise会拒绝(低版本浏览器不会返回Promise,此时可以通过事件或参数来检测自动播放)

var promise = document.querySelector('video').play();

if (promise !== undefined) {
    promise.catch(error => {
        // Auto-play was prevented
        // Show a UI element to let the user manually start playback
    }).then(() => {
        // Auto-play started
    });
}
  • 通过video事件或参数检测自动播放成功,通过超时判断自动播放失败

使用autoplay属性,或调用play API来尝试进行自动播放,通过监听由自动播放触发的play事件,监听timeupdate事件,查看currentTime是否发生了变化等等办法来检测自动播放成功,并通过设置定时器超时来作为判断自动播放失败的依据。

this.videoRef.addEventListener('timeupdate', (e) => {
	// console.log('视频自动播放成功');
})
this.videoRef.addEventListener('canplay', (e) => {
    // console.log('视频已就绪');
	setTimeout(() => {
		// console.log('视频自动播放失败');
	}, this.maxWaitTime)
})

不要假设video标签一定会按照预期触发某个事件或改变某个属性。

4. 增加网站视频的受众,解除自动播放的限制

浏览器的限制策略不是绝对的,如果在本地尝试将你的网页代理到知名的视频网站(比如youtube.com),会发现自动播放限制被解除了。

直接尝试自动播放失败,并报错显示“调用play()方法失败,因为用户尚未与文档产生交互”

Snipaste_2022-01-13_14-57-46

将页面代理到知名视频网站的域名后,自动播放成功

Snipaste_2022-01-13_14-58-20

桌面端Chorme会针对用户给每个网站统计一个MEI指数,用来衡量用户在网站上消费多媒体的倾向强烈程度,并在浏览器内维护一个MEI列表(无法通过JS探测)。当用户在网站上观看视频并满足以下条件时

  • 观看时长大于 7 秒。

  • 视频音轨存在并且没有静音。

  • 带有视频的选项卡处于活跃状态。

  • 视频的像素大小大于200x140。

浏览器就会提高该网站的MEI指数,当网站的MEI指数足够高时,自动播放的限制就会被解除。

新用户会加载一个初始MEI列表,这个初始列表会预先植入一些被很多用户打了MEI高分的网站,也就是说如果一个网站有足够多的用户允许自动播放,那么这个网站就会默认得到新用户的MEI高分,并放开自动播放限制(这个初始列表是完全由算法生成的)。并且这个初始列表会被用户个人的MEI行为所覆盖。

通过访问chrome://media-engagement来查看你的MEI列表(不包含初始MEI列表)

桌面端Safari也有类似的策略,它宣称“使用自动推理引擎来阻止大多数网站自动播放带有声音的视频”,但没有公开具体的策略内容。

参考:

媒体参与指数(Media Engagement Index,MEI)

Web Audio, Autoplay Policy and Games

5. 使用gif图片,顺序展示序列图片等手段模拟视频效果

使用gif图片模拟视频播放效果。

使用动态绘制图片到canvas的方式模拟视频播放效果:

  1. 图片对象预加载,放在内存中;
  2. 播放开始,canvas擦除上一帧图片,同时绘制当前帧图片。

使用动态更新图片dom的方式模拟视频播放效果:

  1. 图片对象预加载,放在内存中;
  2. 播放开始,页面添加当前图片元素,同时移除上一帧图片元素,保证页面中仅有一个图片元素。

由于动态更新图片dom的方式本质是播放html元素,因此还可以实现弱网状态下“抽帧播放”,在“视频播放”中手动添加额外信息等。

屏幕录制2022-01-13 11 42 35 (1)

查看在线演示

参考:

https://www.didiglobal.com

https://juejin.cn/post/6869587681458782215

@LaiTaoGDUT LaiTaoGDUT changed the title 【bigo】视频autoplay兼容及解决方案 【bigo】网页视频autoplay兼容及解决方案 Jan 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant