2017 年 9 月,我们宣布 Chrome 中的自动播放行为政策即将发生变化,音频的处理方式将随之发生变化。此政策变更已于 2018 年 5 月随 Chrome 66 Stable 版一同发布。
在收到 Web Audio 开发社区的反馈后,我们推迟了自动播放政策的 Web Audio 部分的发布,以便开发者有更多时间更新其网站。我们还对 Web Audio 政策的实施进行了一些更改,这将减少需要调整代码的网站数量(尤其是 Web 游戏),从而为用户提供更好的体验。
此政策变更现已安排在 2018 年 12 月随 Chrome 71 一起发布。
此政策变更的具体影响是什么?
自动播放是指在网页加载后立即播放的内容。对于希望能够自动播放其内容的网站,此更改将默认阻止播放。在大多数情况下,系统会恢复播放,但在某些情况下,您需要对代码进行一些小调整。具体而言,开发者必须添加代码,以便在用户与网页互动时恢复其内容。
不过,如果用户访问包含自动播放内容的网页,并且是从具有相同来源的网页导航到该网页,则系统绝不会屏蔽该内容。如需查看更多详细示例,请参阅我们之前就自动播放政策发布的博文。
此外,我们还添加了启发词语,以便从用户在自动播放音频的网站上的过往行为中学习。当用户在访问某个网站的大部分时间里都让音频播放超过 7 秒时,我们会检测到这一情况,并为该网站启用自动播放。
为此,我们会为设备上的每个 Chrome 个人资料在本地存储一个索引,该索引不会跨设备同步,并且只会在匿名化用户统计信息中分享。我们将此指数称为媒体互动指数 (MEI),您可以通过 chrome://media-engagement 查看该指数。
MEI 会跟踪网站的访问次数中,有多少次访问包含时长超过 7 秒的音频播放。我们认为,根据用户的 MEI,我们可以了解用户是否希望从特定网站获取音频,并预测用户未来的意图。
如果用户经常允许某个网站的网域播放超过 7 秒的音频,那么我们日后会假定用户希望该网站有权自动播放音频。因此,我们会向该网站授予自动播放音频的权限,而无需用户与该网域中的标签页互动。
不过,我们无法保证此权利会无限期有效。如果用户的行为发生变化(例如,在多次访问过程中,在 7 秒内停止音频播放或关闭标签页),我们会撤消相应网站的自动播放权限。
使用媒体 HTML 元素(视频和音频)和 Web Audio(JavaScript 实例化的 AudioContext 对象)都会增加 MEI。为准备推出此政策,从 Chrome 70 开始,与 Web Audio 相关的用户行为将开始为 MEI 做出贡献。这样可以确保我们已经能够预测用户对自动播放功能和他们常访问的网站的预期意图。
请注意,只有在嵌入 iframe 的父级网页将此权限扩展到给定 iframe 的情况下,iframe 才能获得无需用户互动即可自动播放的权限。
推迟变更以支持社区
当此更改出现在 Chrome 稳定版频道中时,Web Audio 开发者社区(尤其是该社区中的 Web 游戏开发者和 WebRTC 开发者)就注意到了。
社区反馈显示,许多网络游戏和网络音频体验都会受到这一变更的负面影响,具体而言,许多未更新的网站将无法再向用户播放音频。因此,我们的团队决定推迟这项变更,以便 Web Audio 开发者有更多时间更新其网站。
此外,我们还利用这段时间:
- 认真考虑这项政策变更是否为最佳做法。
- 探索我们可以采取哪些措施来减少受影响的包含音频的网站数量。
对于前者,我们最终决定,为了改善大多数用户的体验,确实需要做出政策变更。如需详细了解这项政策变更要解决的问题,请参阅本文的下一部分。
对于后者,我们对 Web Audio 的实现进行了调整,这将减少最初受影响的网站数量。在我们知道因此次变更而出现问题的网站中(其中许多网站是由 Web 游戏开发社区提供的示例),有超过 80% 的网站会自动恢复正常。点击此处可查看我们对这些示例网站的分析和测试结果。下面将详细介绍这项新调整。
我们还做出了一项更改,以支持 WebRTC 应用;在有有效的捕获会话时,系统会允许自动播放。
此行为变更旨在解决什么问题?
浏览器历来在帮助用户管理音频方面表现欠佳。如果用户打开网页时收到了他们意料之外或不希望收到的声音,则会获得糟糕的用户体验。我们正致力于解决这种糟糕的用户体验问题。不必要的噪声是用户不希望浏览器自动播放内容的主要原因。
不过,有时用户希望内容自动播放,并且 Chrome 中被屏蔽的自动播放内容中有相当一部分会在用户之后播放。
因此,我们认为,通过从用户那里学习,并根据每个网站预测用户的意图,我们可以打造最佳用户体验。如果用户倾向于让系统自动播放某个网站上的内容,那么日后系统就会自动播放该网站上的内容。反之,如果用户倾向于停止自动播放某个网站上的内容,我们会默认阻止自动播放该内容。
社区提出的一个建议是,让标签页的音频静音,而不是暂停自动播放。不过,我们认为最好停止自动播放体验,以便网站知道自动播放已被屏蔽,并允许网站开发者对此做出回应。例如,有些开发者可能希望仅静音,而有些开发者可能希望在用户主动与内容互动之前暂停其音频内容,否则用户可能会错过部分音频体验。
为 Web 游戏开发者提供新的调整
开发者使用 Web Audio API 的最常见方式是创建两种类型的对象来播放音频:
- AudioContext
- 以及附加到上下文的 AudioNodes
Web 音频开发者将创建一个 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 接口。实现 AudioScheduledSourceNode 接口的 AudioNode 通常称为源节点(例如 AudioBufferSourceNode、ConstantSourceNode 和 OscillatorNode)。来源节点实现 start() 方法。
来源节点通常表示游戏播放的各个音频片段,例如:玩家收集硬币时播放的声音或当前阶段播放的背景音乐。每当游戏需要任何这些声音时,游戏开发者都很可能会对源节点调用 start() 函数。
在 Web 游戏中发现这一常见模式后,我们决定将实现方式调整为以下方式:
当满足以下两个条件时,AudioContext 将自动恢复:
- 用户与网页互动过。
- 调用源节点的 start() 方法。
受此更改影响,大多数 Web 游戏现在会在用户开始玩游戏时恢复音频。
推动 Web 技术发展
为了推动 Web 平台不断发展,有时需要做出可能破坏兼容性的更改。很遗憾,自动播放音频是一项复杂的功能,属于这类更改。不过,为了确保网络不会停滞不前或失去创新优势,做出这一转变至关重要。
不过,我们也认识到,出于各种原因,短期内并非总能为网站应用修复程序:
- Web 开发者可能专注于新项目,无法立即维护旧版网站。
- 网络游戏门户可能无法控制其目录中游戏的实现方式,而更新数百甚至数千款游戏对发布商来说既耗时又昂贵。
- 有些网站可能只是非常古老,由于种种原因,已不再维护,但出于历史原因仍在托管中。
以下是一个简短的 JavaScript 代码段,用于拦截新 AudioContext 对象的创建,并会在用户执行各种用户互动时自动触发这些对象的 resume 函数。此代码应在网页中创建任何 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 会替换默认配置。
预先添加的网站列表是通过算法生成的,而不是手动挑选的,任何网站都符合添加条件。如果访问某个网站的用户中有足够多的用户允许该网站自动播放视频,系统就会将该网站添加到名单中。此阈值以百分比表示,以免偏向大型网站。
寻找平衡
我们发布了新文档,以便您更深入地了解此政策的决策流程和设计理由。以及有关预先植入的网站列表的运作方式的新文档。
我们始终以用户为先,但也不想让 Web 开发社区失望。有时,作为浏览器,就必须仔细平衡这两个目标。我们认为,通过调整政策的实施方式,并为 Web 音频开发者额外提供更新代码的时间,我们将能够在 Chrome 71 中实现这一平衡。