如何在浏览器中处理video标签以实现视频的自动播放和声音播放

2,050 阅读4分钟

“Achuy,最近我们公司需要做一个网页端的直播,能自动播放有声视频,你研究一下”,领导说道。我内心想着:我记得我看b站直播也是自动播放无声视频,要自己手动开声音的呀,难不成有hack技术能自动播放有声视频?但我嘴上说到:“好的领导”。

_757120cf-293b-42ae-919a-279c3fb886a1.jpg

    在iOS/Android系统上,声音无法自动播放一直是一个常见的现象。2017年,桌面版的Safari在其11版本中也宣布禁止带有声音的多媒体自动播放功能,紧随其后,2018年4月发布的Chrome 66也正式关闭了声音自动播放,这意味着在桌面版浏览器上,这一功能也将不再生效。

    最初,移动端浏览器完全禁止音视频自动播放,主要考虑到手机的带宽以及对电池的消耗。然而,后来这一政策发生了改变,原因在于浏览器厂商发现网页开发人员采用其他方法来规避这一限制:
  1. 使用GIF动态图代替视频实现自动播放

  2. 使用canvas实现自动播放

    然而,使用这些方法会带来性能问题。正如IOS文档所述,使用GIF的带宽流量是Video(h264)格式的12倍,而播放性能消耗是2倍,这对用户来说反而是不利的。

因此,浏览器厂商放宽了对多媒体自动播放的限制,只要满足以下条件,就可以自动播放:

  • 没有音频轨道,或者设置了muted属性
  • 在视图中是可见的,需要插入到DOM中,并且不是display: none或者visibility: hidden的,没有滑出可视区域

但最终的结果是,只允许静音自动播放视频。

聪明的你可能会想到,我用js操作videoElement.muted = false;videoElement.play()不就可以了吗?但是在Chrome 66中,为了避免标签产生随机噪音,禁止在没有用户交互前使用js进行播放,会出现提示:DOMException: play() failed because the user didn’t interact with the document first.

根据chrome新政策,始终允许静音模式下自动播放,在以下的情况中,带声音播放会被允许:

①用户已经与当前的域进行了交互(也就是click,tap事件)。

②在桌面设备上,用户的媒体参与度指数阈值已经超过,这意味着用户之前播放过有声视频。

③用户已经将网站添加到移动设备上的主屏幕或允在桌面上安装了PWA

有声自动播放video思路

既然如此,需要用户交互才能自动播放,那么我们可以尝试使用单页面应用来处理。Vue是单页面富应用,虽然我们看上去页面是从一个页面跳转到另一个页面,但是这些路由页面始终在同一个文档(页面)中,当我们点击实现路由跳转时就已经实现了与文档的交互,那么就不会出现这个错误了。

对于第二种情况来说,媒体参与度是指用户与媒体内容进行互动的程度,可以通过多个指标来衡量。这些指标主要包括观看时间、观看率、转化率、交互行为等。Chrome基于这个媒体参与度有一套自己算法,我们可以通过:chrome://media-engagement/ 查看。

实际上,即使是抖音,b站等这些在网页端的产品,也无法绕过浏览器的限制,多数是静音自动播放,用提示来引导用户解除静音播放。当然也可以多次访问达到政策第二条的条件,用户也能自动有声播放

而第三种不在我的需求之内。

接下来,我们可以尝试编写一个Vue的demo,不需要使用vue-cli创建,那么如何在html文件上完成编写呢?

首先引入相应的库

  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.5.1/vue-router.js"></script>

开始定义组件

const com1 = Vue.extend({
    template: `
          <div>
            <div>路由1</div>
            <div @click='toPage'>跳转video路由</div>
          </div>
        `,
    methods: {
      toPage(){
        this.$router.push({
          path: '/video'
        })
      }
    }
  })
  const com2 = Vue.extend({
    template: `
        <div>
            <div>路由2</div>
            <video src="./video_160X120.mp4" autoplay></video>
          </div>
        `,
  })

然后定义路由,将路由放在根Vue上

  const routes = [
    { path: '/', component: com1 },
    { path: '/video', component: com2 }
  ]

  const router = new VueRouter({
    routes // (缩写)相当于 routes: routes
  })

  const app = new Vue({
    router
  }).$mount('#app');

最终可以实现,但是刷新或者外部链接跳转会失效,还需要根据实际需求,通过检测,引导用户解除限制。

完整代码实现

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Video auto play demo</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.5.1/vue-router.js"></script>
</head>
<body>
  <div id="app">
    <router-view></router-view>
  </div>
</body>
<script>
  const com1 = Vue.extend({
    template: `
          <div>
            <div>路由1</div>
            <div @click='toPage'>跳转video路由</div>
          </div>
        `,
    data: function () {
      return {
        firstName: 'Walter',
        lastName: 'White',
        alias: 'Heisenberg'
      }
    },
    methods: {
      toPage() {
        this.$router.push({
          path: '/video'
        })
      }
    }
  })
  const com2 = Vue.extend({
    template: `
        <div>
            <div>路由2</div>
            <video src="./video_160X120.mp4" autoplay></video>
          </div>
        `,
    data: function () {
      return {
      }
    }
  })
  const routes = [
    { path: '/', component: com1 },
    { path: '/video', component: com2 }
  ]
  const router = new VueRouter({
    routes // (缩写)相当于 routes: routes
  })
  const app = new Vue({
    router
  }).$mount('#app');
</script>
</html>