媒体和 Web Audio API 自动播放指南
在页面加载时立即自动开始播放音频(或带有音轨的视频)可能会给用户带来意想不到的惊喜。虽然媒体的自动播放有其用处,但应谨慎使用,并且仅在需要时使用。为了让用户能够控制这一点,浏览器通常会提供各种形式的自动播放阻止。在本指南中,我们将介绍各种媒体和 Web Audio API 中的自动播放功能,包括如何使用自动播放以及如何与浏览器协作以优雅地处理自动播放阻止的简要概述。
当源媒体没有音轨或音轨已静音时,自动播放阻止不适用于 <video> 元素。具有活动音轨的媒体被认为是可听见的,自动播放阻止适用于它们。不可听见的媒体不受自动播放阻止的影响。
自动播放和自动播放阻止
术语自动播放指任何导致媒体在用户未明确请求播放开始的情况下开始播放的功能。这包括使用 HTML 属性自动播放媒体以及在处理用户输入上下文之外使用 JavaScript 代码开始播放。
这意味着以下两种情况都被视为自动播放行为,因此受浏览器自动播放阻止策略的约束
<audio src="/music.mp3" autoplay></audio>
and
audioElement.play();
以下 Web 功能和 API 可能会受到自动播放阻止的影响
- HTML
<audio>和<video>元素 - Web Audio API
从用户的角度来看,一个未经警告就突然发出声音的网页或应用程序可能会令人不安、不便或令人反感。因此,浏览器通常只允许在特定情况下成功进行自动播放。
自动播放可用性
一般来说,你可以假定只有在满足以下至少一项条件时,媒体才会被允许自动播放
- 音频已静音或其音量设置为 0
- 用户已与站点互动(通过点击、轻触、按键等)
- 如果该站点已被列入允许列表;如果浏览器确定用户经常与媒体互动,这可能会自动发生,或者通过偏好设置或其他用户界面功能手动发生
- 如果使用自动播放 权限策略 来授予
<iframe>及其文档自动播放支持。
否则,播放很可能会被阻止。导致阻止的具体情况以及站点如何被列入允许列表的细节因浏览器而异,但上述是很好的指导原则。
有关详细信息,请参阅 Google Chrome 和 WebKit 的自动播放策略。
注意:换句话说,如果在尚未进行任何用户交互的标签页中以编程方式启动播放,则通常会阻止播放任何包含音频的媒体。浏览器还可以选择在其他情况下阻止。
媒体元素的自动播放
现在我们已经介绍了什么是自动播放以及什么可以阻止自动播放,我们将看看您的网站或应用程序如何在页面加载时自动播放媒体,如何检测自动播放失败的情况,以及当浏览器拒绝自动播放时如何应对的提示。
自动播放属性
自动播放内容最简单的方法是将 autoplay 属性添加到您的 <audio> 或 <video> 元素中,这会将元素的 autoplay 属性设置为 true。当 autoplay 为 true 时,媒体将在以下情况发生后尽快自动开始播放
- 页面被允许使用自动播放功能
- 元素已在页面加载期间创建
- 已接收足够的媒体以开始播放并持续播放到媒体结束而不会中断,假设网络性能或带宽没有显著变化。
示例:自动播放属性
使用 autoplay 属性的 <audio> 元素可能看起来像这样
<audio id="musicplayer" autoplay>
<source src="/music/chapter1.mp3" />
</audio>
示例 2:检测是否允许自动播放
如果自动播放对您的应用程序很重要,您可能需要根据是否允许、不允许或仅支持不可听内容来定制行为。例如,如果您的应用程序需要自动播放视频,并且您知道页面只允许自动播放不可听内容,您可以将其静音或提供没有音轨的视频。同样,如果您知道完全不允许自动播放,您可以为视频提供默认图像(使用 poster 属性),或者选择延迟加载视频直到它被请求。
可以使用 Navigator.getAutoplayPolicy() 方法来检查文档中媒体功能类型(即所有媒体元素或所有音频上下文)的自动播放策略,或者检查特定媒体元素或音频上下文是否可以自动播放。
以下示例展示了如何传递 mediaelement 字符串以获取文档中所有媒体元素的自动播放策略(传递 audiocontext 以获取音频上下文的策略)。代码假定 video 是使用 <video> 标签或 HTMLVideoElement 的 HTMLVideoElement 媒体元素,并且默认配置为自动播放音频。如果自动播放只允许不可听内容,我们会静音音频;如果自动播放被禁止,我们确保视频显示占位符图像。
if (navigator.getAutoplayPolicy("mediaelement") === "allowed") {
// The video element will autoplay with audio.
} else if (navigator.getAutoplayPolicy("mediaelement") === "allowed-muted") {
// Mute audio on video
video.muted = true;
} else if (navigator.getAutoplayPolicy("mediaelement") === "disallowed") {
// Set a default placeholder image.
video.poster = "http://example.com/poster_image_url";
}
测试特定元素或音频上下文的代码是相同的,只是您传入要测试的元素或上下文而不是类型字符串。这里我们传入要测试的 video 对象。
if (navigator.getAutoplayPolicy(video) === "allowed") {
// The video element will autoplay with audio.
} else if (navigator.getAutoplayPolicy(video) === "allowed-muted") {
// Mute audio on video
video.muted = true;
} else if (navigator.getAutoplayPolicy(video) === "disallowed") {
// Set a default placeholder image.
video.poster = "http://example.com/poster_image_url";
}
由于用户与站点、页面或特定元素的交互,某种类型的自动播放策略可能会更改。同样,在某些浏览器上,即使类型的策略没有更改(例如,在某些浏览器上,触摸特定元素可以只允许该元素自动播放),特定元素的策略也可能更改。
由于无法在自动播放策略更改时收到通知(无论是针对类型还是元素),我们通常建议在加载页面时使用类型检查策略。
示例 3:检测自动播放失败作为回退
自动播放成功或失败不会触发特定事件(或其他通知),因此不支持 Navigator.getAutoplayPolicy() 的浏览器无法轻松确定是否支持自动播放,或者在触发或未触发时做出反应。
一种方法是监听 play 事件的第一次实例,该事件在媒体元素暂停后恢复以及发生自动播放时触发。这意味着 play 事件第一次触发时,您就知道您的媒体在页面打开后第一次开始播放,
考虑一下这个媒体元素的 HTML
<video src="my-video.mp4" id="video" autoplay></video>
这里我们有一个 <video> 元素,其 autoplay 属性已设置,并且设置了 play 事件处理程序;该事件由一个名为 handleFirstPlay() 的函数处理,该函数接收 play 事件作为输入。
handleFirstPlay() 如下所示
const video = document.getElementById("video");
video.addEventListener("play", handleFirstPlay);
let hasPlayed = false;
function handleFirstPlay(event) {
if (!hasPlayed) {
hasPlayed = true;
// Remove listener so this only gets called once.
const vid = event.target;
vid.removeEventListener("play", handleFirstPlay);
// Start whatever you need to do after first playback has started
}
}
从 Event 对象的 target 获取视频元素的引用后,我们使用它来移除事件监听器。这将阻止任何未来的 play 事件传递给处理程序。如果视频被用户暂停并恢复,或者当文档在后台标签页中时由浏览器自动暂停和恢复,则可能会发生这种情况。
此时,您的站点或应用程序可以开始任何需要依靠视频已启动的操作。
play() 方法
“自动播放”一词也指脚本尝试在处理用户输入事件的上下文之外触发包含音频的媒体播放的场景。这是通过调用媒体元素的 play() 方法来完成的。
注意:强烈建议您尽可能使用 autoplay 属性,因为 autoplay 属性对自动播放偏好的支持比其他自动播放媒体的方式更广泛。它还让浏览器负责启动播放,让它优化播放的时间。
示例:播放视频
此示例播放文档中找到的第一个 <video> 元素。除非文档有权自动播放媒体,否则 play() 不会允许播放开始。
document.querySelector("video").play();
示例:处理 play() 失败
当您使用 play() 方法启动媒体时,检测自动播放失败要容易得多。play() 返回一个 Promise,该 Promise 在媒体成功开始播放时解决,并在播放失败时(例如自动播放被拒绝时)拒绝。当自动播放失败时,您可能希望提供一种方法让用户手动告诉浏览器请求用户授予播放媒体的权限。
您可以使用以下代码来完成这项工作
let startPlayPromise = videoElem.play();
if (startPlayPromise !== undefined) {
startPlayPromise
.then(() => {
// Start whatever you need to do only after playback
// has begun.
})
.catch((error) => {
if (error.name === "NotAllowedError") {
showPlayButton(videoElem);
} else {
// Handle a load or playback error
}
});
}
我们对 play() 结果所做的第一件事是确保它不是 undefined。我们检查这一点是因为在 HTML 规范的早期版本中,play() 不返回任何值。最近才添加了返回 Promise 以让您确定操作的成功或失败的功能。检查 undefined 可以防止此代码在旧版本的 Web 浏览器上因错误而失败。
如果 play() 返回的 Promise 顺利解决,则会运行 then() 子句,并可以开始在自动播放开始时需要完成的任何操作。
然后我们为 Promise 添加一个 catch() 处理程序。它会检查错误的 name 以查看它是否是 NotAllowedError。这表示播放因权限问题而失败,例如自动播放被拒绝。如果是这种情况,我们应该呈现一个用户界面,让用户手动开始播放;这里由函数 showPlayButton() 处理。
任何其他错误都会酌情处理。
如果您想在与页面第一次交互后开始播放视频,可以使用 setInterval() 来实现此目的
let playAttempt = setInterval(() => {
videoElem
.play()
.then(() => {
clearInterval(playAttempt);
})
.catch((error) => {
console.log("Unable to play the video, User has not interacted yet.");
});
}, 3000);
使用 Web Audio API 自动播放
在 Web Audio API 中,网站或应用程序可以使用链接到 AudioContext 的源节点上的 start() 方法开始播放音频。在处理用户输入事件的上下文之外这样做受自动播放规则的约束。
自动播放权限策略
除了上述浏览器端对自动播放功能的管理和控制之外,Web 服务器还可以表达其允许自动播放功能的意愿。HTTP Permissions-Policy 头的 autoplay 指令用于控制哪些域(如果有)可以用于自动播放媒体。默认情况下,autoplay 权限策略设置为 self,表示自动播放是允许的,因为它们托管在与文档相同的域上。
您还可以指定一个空允许列表 (()) 以完全禁用自动播放,* 以允许所有域的自动播放,或一个或多个可以自动播放媒体的特定来源。这些来源由空格字符分隔。
在 <iframe> 上使用 allow 属性为该框架及其嵌套框架指定权限策略时,您还可以指定值 'src' 以仅允许来自与框架 src 属性指定的域相同的媒体自动播放。
示例:仅允许文档域自动播放
要使用 Permissions-Policy 头仅允许媒体从文档的 来源 自动播放
Permissions-Policy: autoplay=(self)
对 <iframe> 执行相同操作
<iframe src="mediaplayer.html" allow="autoplay"> </iframe>
示例:允许自动播放和全屏模式
在上一个示例中添加 全屏 API 权限会导致如下所示的 Permissions-Policy 头(如果允许全屏访问,无论域如何;也可以根据需要添加域限制)。
Permissions-Policy: autoplay=(self), fullscreen=(self)
使用 <iframe> 元素的 allow 属性授予相同的权限,如下所示
<iframe src="mediaplayer.html" allow="autoplay; fullscreen"> </iframe>
示例:允许特定来源自动播放
允许媒体从文档(或 <iframe>)自己的域和 https://example.media 播放的 Permissions-Policy 头如下所示
Permissions-Policy: autoplay=(self "https://example.media")
可以这样编写 <iframe> 以指定此自动播放策略应应用于自身和任何子框架
<iframe
width="300"
height="200"
src="mediaplayer.html"
allow="autoplay 'src' https://example.media">
</iframe>
示例:禁用自动播放
将 autoplay 权限策略设置为 ()/none 会完全禁用文档或 <iframe> 以及所有嵌套框架的自动播放。HTTP 头是
Permissions-Policy: autoplay=()
使用 <iframe> 的 allow 属性
<iframe src="mediaplayer.html" allow="autoplay 'none'"> </iframe>
最佳实践
此处提供了提示和推荐的最佳实践,以帮助您充分利用自动播放功能。
使用媒体控件处理自动播放失败
自动播放的常见用例是自动开始播放与文章、广告或页面主要功能的预览相伴的视频剪辑。要自动播放此类视频,您有两个选择:不带音轨,或者带音轨但将 <video> 元素配置为默认静音,如下所示
<video
src="/videos/awesomevid.webm"
controls
autoplay
playsinline
muted></video>
此视频元素配置为包含用户控件(通常是播放/暂停、在视频时间轴上拖动、音量控制和静音);此外,由于包含了 muted 属性以及 Safari 中自动播放所需的 playsinline 属性,视频将自动播放但音频静音。然而,用户可以选择通过单击控件中的取消静音按钮来重新启用音频。
浏览器配置选项
浏览器可能具有控制自动播放工作方式或如何处理自动播放阻止的偏好设置。这里列出了作为 Web 开发人员对您可能具有特殊意义或重要性的任何此类偏好设置。这些包括任何可能有助于测试或调试的偏好设置,以及任何您需要准备处理的设置方式。
Firefox
media.allowed-to-play.enabled-
一个布尔偏好设置,指定非标准
HTMLMediaElement.allowedToPlay属性是否暴露给 Web。目前默认为false(在夜间构建中默认为true)。如果此值为false,则allowedToPlay属性在HTMLMediaElement接口中缺失,因此在<audio>或<video>元素上均不存在。 media.autoplay.allow-extension-background-pages-
此布尔偏好设置,如果为
true,则允许浏览器扩展的后台脚本自动播放音频媒体。将此值设置为false会禁用此功能。默认值为true。 media.autoplay.allow-muted-
一个布尔偏好设置,如果为
true(默认值),则允许当前静音的音频媒体自动播放。如果此值已更改为false,则即使静音,带有音轨的媒体也无法播放。 media.autoplay.block-webaudio-
一个布尔偏好设置,指示是否将自动播放阻止应用于 Web Audio API。如果为
false,则始终允许 Web 音频自动播放。如果为true,则只有在页面具有 粘性激活 后,音频上下文才能播放。默认设置为true。 media.autoplay.default-
一个整数偏好设置,指定是否默认允许(
0)、阻止(1)或按使用提示(2)进行自动播放支持的每域配置。默认值为0。 media.autoplay.enabled.user-gestures-needed(仅限夜间构建)-
一个布尔偏好设置,控制是否允许用户手势检测覆盖
media.autoplay.default的设置。如果media.autoplay.default未设置为0(默认允许自动播放),则此偏好设置为true允许在页面已通过用户手势激活的情况下自动播放带音轨的媒体,并且不限制不可听媒体。 media.block-autoplay-until-in-foreground-
一个布尔偏好设置,指示当在后台标签页中启动时是否阻止媒体播放。默认值
true意味着即使在其他情况下可用,自动播放也不会发生,直到标签页被带到前台。这可以防止令人分心的情况,即标签页开始播放声音而用户无法在所有标签页和窗口中找到该标签页。