网络音频、自动播放政策和游戏

Tom Greenaway
崔宏成

2017 年 9 月,我们宣布即将对 Chrome 中的自动播放行为政策处理音频进行更改。这项政策变更已于 2018 年 5 月随 Chrome 66 稳定版发布。

根据网络音频开发社区的反馈,我们推迟了自动播放政策中网络音频部分的发布,以便开发者有更多的时间来更新他们的网站。我们还对网络音频政策的实施进行了一些更改,这将减少需要调整其代码的网站(尤其是网页游戏)的数量,从而为我们的用户提供更好的体验。

我们计划于 2018 年 12 月针对 Chrome 71 推出这项政策变更。

此次政策变更具体意味着什么?

自动播放是指在网页加载完毕后立即播放的一段内容的名称。对于原本预期能够自动播放内容的网站,这项变更会默认阻止视频播放。大多数情况下,系统会继续播放,但在其他情况下,则需要对代码稍作调整。具体来说,开发者必须添加在用户与网页互动时恢复其内容的代码。

不过,如果用户到达的网页包含自动播放内容,并且是从同一来源的网页导航到该网页,则系统永远不会屏蔽该内容。请参阅我们之前关于自动播放政策的博文,了解更详细的示例

此外,我们还添加了一种启发式方法,以便了解用户在自动播放音频的网站上之前的行为。我们检测到用户在访问网站的大部分时间经常让音频播放超过 7 秒时,会为该网站启用自动播放功能。

为此,我们需要根据设备上的一份 Chrome 个人资料在本地存储一个索引。该索引不会在设备间同步,只有在经过匿名化处理的用户统计信息中才会进行分享。我们将此索引称为媒体互动指数 (MEI),您可以通过 chrome://media-engagement 查看它。

MEI 会跟踪对某网站的访问中多少次访问了时长超过 7 秒的音频播放。根据用户的 MEI,我们相信我们可以了解用户是否需要特定网站的音频,并预测用户未来的意图。

如果用户经常允许网站网域播放音频超过 7 秒,我们就会认为用户将来希望网站有权自动播放音频。因此,我们授予该网站自动播放音频的权利,而无需用户与该网域中的标签页进行互动。

不过,我们无法保证此权利是无限期的。如果用户的行为发生变化(例如,在多次访问过程中停止播放音频或在 7 秒内关闭标签页),我们将撤消该网站的自动播放权利。

媒体 HTML 元素(视频和音频)和网络音频(JavaScript 实例化的 AudioContext 对象)的使用都会影响 MEI。为应对此政策的发布,与网络音频相关的用户行为将从 Chrome 70 及更高版本中开始为 MEI 贡献代码。这样可确保我们能够预测用户在自动播放方面的预期意图,以及他们经常访问的网站。

请注意,只有当嵌入 iframe 的父网页扩展到指定的 iframe 时,iframe 才可以在未经用户交互的情况下自动播放。

推迟变更以支持社区

当这项变更出现在 Chrome 稳定版中时,Web Audio 开发者社区(尤其是该社区中的 Web 游戏开发者和 WebRTC 开发者)注意到了这项变更。

社区反馈是,此次变更会对许多网页游戏和网络音频体验产生负面影响。具体而言,许多未更新的网站将不再向用户播放音频。因此,我们的团队认为有必要推迟此项变更,让网络音频开发者有更多时间更新自己的网站。

此外,我们还利用这段时间来:

  • 请认真考虑此政策变更是否为最佳应对措施。
  • 探索如何帮助减少包含音频问题的网站的数量。

对于前者,我们最终认为这项政策变更确实是改善大多数用户用户体验的必要措施。如需详细了解此次政策变更解决的是什么问题,请参阅本文的下一部分。

对于后者,我们调整了对网络音频的实施,以减少最初受影响的网站数量。在我们所知的受此变更影响的网站中,很多网站都是以网页游戏开发社区为范例,经过此次调整意味着超过 80% 的网站都能自动运行。我们可以查看这些示例网站的分析和测试结果。下文将更详细地介绍这种新的调整。

我们还进行了更改来支持 WebRTC 应用;当捕获会话有效时,系统将允许自动播放。

此项行为变更旨在解决什么问题?

一直以来,浏览器在帮助用户管理声音方面都很糟糕。如果用户在打开网页时听到了意想不到或不想听到的声音,用户体验将会很糟糕。这种糟糕的用户体验是我们试图解决的问题。不必要的噪声是用户不希望浏览器自动播放内容的主要原因。

不过,有时用户希望内容能够自动播放,因此在 Chrome 中屏蔽了一定数量的自动播放行为后,用户会接着播放这些内容。

因此,我们相信,通过向用户学习,并根据网站预测用户的意图,我们能够打造出最佳的用户体验。如果用户允许播放某个网站中的内容,我们以后就会自动播放该网站中的内容。反之,如果用户倾向于停止播放指定网站上的内容,我们会默认阻止该内容自动播放。

社区提出的一个方案是,将标签页静音,而不是暂停自动播放。不过,我们认为最好停止自动播放体验,让网站知道自动播放功能已被屏蔽,并允许网站开发者对此做出回应。例如,虽然有些开发者可能只想将音频静音,但还有一些开发者可能希望将音频内容暂停,直到用户主动与内容互动为止,否则用户可能会错过部分音频体验。

为帮助 Web 游戏开发者实施了新调整

开发者使用 Web Audio API 的最常见方式是创建两种类型的对象来播放音频:

网络音频开发者将创建用于播放音频的 AudioContext。为了在自动播放政策自动暂停其 AudioContext 后继续播放音频,他们需要在用户与标签页互动后对此对象调用 Resume() 函数:

    const context = new AudioContext();

    // Setup an audio graph with AudioNodes and schedule playback.
    ...

    // Resume AudioContext playback when user clicks a button on the page.
    document.querySelector('button').addEventListener('click', function() {
      context.resume().then(() => {
        console.log('AudioContext playback resumed successfully');
      });
    });

有许多接口继承自 AudioNode,其中一个是 AudioScheduledSourceNode 接口。实现 AudioscheduleSourceNode 接口的 AudioNode 通常称为源节点(例如 AudioBufferSourceNode、ConstantSourceNode 和 OscillatorNode)。源节点实现了 start() 方法。

源节点通常表示游戏播放的各个音频片段,例如:玩家收集硬币时播放的声音或当前关卡中播放的背景音乐。每当游戏需要这些声音时,游戏开发者都很可能在源节点上调用 start() 函数。

认识到网页游戏中这种常见的模式后,我们决定根据以下内容调整实现:

当满足两个条件时,AudioContext 将自动恢复

  • 用户与网页进行了互动。
  • 调用源节点的 start() 方法。

由于这一变更,大多数网页游戏现在会在用户开始玩游戏时恢复音频。

推动网络向前发展

为了推动网络平台向前发展,有时必须做出会破坏兼容性的更改。遗憾的是,音频自动播放很复杂,就属于这种变化。但进行这种转变对确保网络不会停滞或失去创新优势至关重要。

尽管如此,我们认识到,出于各种原因,对网站应用修复方法并非总能在短期内进行:

  • Web 开发者可能专注于新项目,维护旧网站是不可能立即实现的事情。
  • 网络游戏门户可能无法控制其目录中游戏的实施情况,因此更新数百个(甚至是数千个)游戏可能会非常耗时且成本高昂。
  • 有些网站可能只是非常老旧,出于某种原因,我们不再维护该网站,但仍会出于历史目的进行托管。

以下这段简短的 JavaScript 代码段会拦截新 AudioContext 对象的创建,并在用户执行各种用户互动时自动触发这些对象的恢复函数。此代码应在您的网页中创建任何 AudioContext 对象之前执行,例如,您可以将以下代码添加到网页的代码中:

(function () {
  // An array of all contexts to resume on the page
  const audioContextList = [];

  // An array of various user interaction events we should listen for
  const userInputEventNames = [
    'click',
    'contextmenu',
    'auxclick',
    'dblclick',
    'mousedown',
    'mouseup',
    'pointerup',
    'touchend',
    'keydown',
    'keyup',
  ];

  // A proxy object to intercept AudioContexts and
  // add them to the array for tracking and resuming later
  self.AudioContext = new Proxy(self.AudioContext, {
    construct(target, args) {
      const result = new target(...args);
      audioContextList.push(result);
      return result;
    },
  });

  // To resume all AudioContexts being tracked
  function resumeAllContexts(event) {
    let count = 0;

    audioContextList.forEach(context => {
      if (context.state !== 'running') {
        context.resume();
      } else {
        count++;
      }
    });

    // If all the AudioContexts have now resumed then we
    // unbind all the event listeners from the page to prevent
    // unnecessary resume attempts
    if (count == audioContextList.length) {
      userInputEventNames.forEach(eventName => {
        document.removeEventListener(eventName, resumeAllContexts);
      });
    }
  }

  // We bind the resume function for each user interaction
  // event on the page
  userInputEventNames.forEach(eventName => {
    document.addEventListener(eventName, resumeAllContexts);
  });
})();

请注意,除非此代码段包含在 iframe 本身的内容范围内,否则该代码段无法帮助恢复在 iframe 内实例化的 AudioContext。

更好地为用户提供服务

为配合此次政策变更,我们还推出了一种供用户停用自动播放政策的机制,以便应对自动学习无法按预期运行的情况,或网站因此项变更而无法使用的情况。这项变更将随 Chrome 71 中的新政策一同推出,您可以在“声音设置”中找到这项变更;将用户希望允许自动播放的网站添加到许可名单中。

如何为新用户构建 MEI?

如前所述,我们会根据用户的行为不断自动构建 MEI,从而预测其对具有自动播放内容的指定网站的预期意图。每个网站在此索引中的得分介于 0 到 1 之间。分数越高,表示用户期望通过该网站播放内容。

不过,对于新用户个人资料或用户清除浏览数据的情况,系统会使用基于匿名用户聚合 MEI 得分的前种子名单来确定哪些网站可以自动播放,而不是禁止在所有位置自动播放。此数据仅决定创建用户个人资料时 MEI 的初始状态。当用户浏览网页并与具有自动播放内容的网站互动时,其个人 MEI 会替换默认配置。

预先种子网站列表是系统通过算法生成的,而非手动挑选的,任何网站都符合加入条件。只要访问相应网站的用户允许在该网站上自动播放,系统就会将其添加到相应列表中。此阈值基于百分比,因此不会偏向于规模较大的网站。

正在查找余额

我们发布了新文档,让用户更深入地了解我们的决策流程和此政策背后的设计原理。以及关于预先种子网站列表工作原理的新文档。

我们始终坚持用户至上,但也不希望网络开发社区受到负面影响。有时,使用浏览器意味着必须小心地平衡这两个目标。我们相信,通过调整政策实施,并为网络音频开发者提供更多时间更新其代码,我们能够在 Chrome 71 中实现这一平衡。

反馈