今天在处理视频播放的时候发现了一个bug:

DOMException: play() failed because the user didn't interact with the document first.

使用场景是需要打开网页后,视频自动播放,但是由于谷歌浏览器的自动播放限制,说是为了改善用户体验,最大程度地减少安装广告拦截器的动机,并减少昂贵和/或受限网络上的数据消耗。这些更改旨在更好地控制用户的播放,并使使用合法情况的发布者受益。

根据谷歌给出的方案,在以下条件下,是允许自动播放的:

  • 始终允许静音自动播放。
  • 在以下情况下,允许自动播放声音:
    1、用户已与域进行了交互(单击,点击等)。
    2、在台式机上,已经超过了用户的“媒体参与度索引”阈值,这意味着该用户以前曾播放带声音的视频。
    3、用户已将该网站添加到他们在移动设备上的主屏幕,或者在桌面上 安装了PWA。
  • 顶级框架可以将自动播放权限委派给其iframe,以允许自动播放声音。

但是,经过实测,第一条,就是在静音的情况下,谷歌浏览器也是不可以自动播放的,这个情况就跟谷歌给出的方案不一致了,于是我就去研究了一下,最后让我发现了一个问题,就是这个能够自动播放的条件,除了需要静音播放,还有一个隐藏条件,就是这个媒体标签一定是要在浏览器第一次解析dom树的时候,这个媒体标签就存在。只有这种情况下,才可以去进行自动播放,反之就不可以,简单来讲就是说浏览器不允许你自己添加媒体标签进行自动播放。

这个时候还有一个疑问,就是我在使用过程中是没有手动添加video标签,它是写在html里的啊。这个问题就涉及到使用的框架了,因为 我这边使用的是vue框架,它在渲染的时候是要先获取根节点,然后根据根节点往下去操作,这个就说明,它是需要dom树在第一次渲染出了之后,再进行相应的操作的(因为需要先有根节点才可以获取)。也就是说从根目录开始往下的所有节点都会进行二次渲染, 这就导致里面的video标签实质上是vue渲染的时候,后期添加添加进去的(具体过程可以去看vue渲染相关的文章)。所以就会导致,这个video标签即便是静音也无法自动播放。

这个时候就会有人说了,他也用的是类似的框架,但是是可以自动播放的,这个通常就是因为方案二中的交互问题了,因为这种单页面的框架,你点击跳转的时候,实际上是假跳转,所以除非你进入页面后,无需登录,也无需跳转,直接就自动播放视频,这种情况下,播放视频会失败,其余的情况,你总是需要交互的,无论是登录时的键盘输入和点击操作,还是跳转到视频界面的跳转操作(实际上是组件切换),都是有交互的,这样就满足了第二条中的交互,所以是可以通过play()方法实现自动播放的。

所以最后对这个事情进行了妥协,采用了第二条,只要用户和浏览器进行了交互(单击,点击等),视频就可以自动播放。

经过查询MDN,查询到了媒体标签的play()方法:

HTMLMediaElement.play() 方法会尝试播放媒体。这个方法返回一个 Promise,当媒体成功开始播放时被解决(resolved)。当播放因为任何原因失败时,这个 promise 被拒绝(rejected)。

这样的话,我们就可以获取play()方法返回的promise进而判断视频是否可以自动播放,然后如果播放失败了,就进行相应的处理,比如在播放失败后手动添加一个播放按钮,让用户点击播放。这种情况下虽然在新进入页面时视频无法自动播放,但是对自动播放失败的情况进行了处理。

1
2
3
4
5
6
7
let promise = vm.flvPlayer.play();					//播放数据流
promise.then(()=>{
console.log('播放成功----------',err)
}).catch(err=>{
console.log('播放失败----------',err)
})