跳转到内容

Media Session API

发布于 更新于

一句话: 根据 MDN 的说明,Media Session API “提供了一种自定义媒体通知的方式”——它让页面 描述当前正在播放的内容,并接收来自设备物理或屏幕媒体控件的事件,与页面自身已有的 播放控件并存。

入口是 navigator.mediaSession,一个 MediaSession 对象。根据 MDN 的 setActionHandler() 参考文档,它的动作“让 Web 应用能够在用户操作设备内置的物理或屏幕 媒体控件(例如播放、停止或快进/快退按钮)时收到通知”;根据 MDN 的 metadata 参考文档, MediaSession.metadata 持有一个 MediaMetadata 对象,为设备自身的媒体控制界面提供 当前播放内容的描述信息。

根据 MDN 的 browser-compat-data,MediaSession 接口(及其 setActionHandler() 方法) 自 Chrome 73 起受支持,自 Safari 15 起受支持,自 Firefox 82 起受支持。Chrome for Android 自 57 起支持,但 Firefox for Android 只是部分实现——同一份数据指出 Firefox for Android “暴露了该 API,但没有提供对应的用户可见媒体控制界面”。该数据将 Android WebView 记录为不支持(version_added: false)。

设置 metadata 为一个 MediaMetadata 对象,然后为页面能够响应的动作注册处理函数:

const playlist = [
{ title: "Unforgettable", artist: "Nat King Cole", album: "The Ultimate Collection (Remastered)" },
{ title: "L-O-V-E", artist: "Nat King Cole", album: "The Ultimate Collection (Remastered)" },
];
let currentTrackIndex = 0;
const audioElement = document.querySelector("audio") ?? document.createElement("audio");
function loadTrack(index) {
currentTrackIndex = index;
const track = playlist[currentTrackIndex];
audioElement.src = `/audio/${track.title}.mp3`;
navigator.mediaSession.metadata = new MediaMetadata({
title: track.title,
artist: track.artist,
album: track.album,
artwork: [{ src: "/art/unforgettable-96.png", sizes: "96x96", type: "image/png" }],
});
}
function playPreviousTrack() {
if (currentTrackIndex > 0) {
loadTrack(currentTrackIndex - 1);
audioElement.play();
}
}
function playNextTrack() {
if (currentTrackIndex < playlist.length - 1) {
loadTrack(currentTrackIndex + 1);
audioElement.play();
}
}
loadTrack(0);
const actionHandlers = {
play: () => audioElement.play(),
pause: () => audioElement.pause(),
previoustrack: playPreviousTrack,
nexttrack: playNextTrack,
};
for (const [action, handler] of Object.entries(actionHandlers)) {
try {
navigator.mediaSession.setActionHandler(action, handler);
} catch {
// 该浏览器未实现这个媒体会话动作——跳过。
}
}

根据 MDN 的 setActionHandler() 参考文档,将 null 作为回调传入会移除此前为该动作 设置的处理函数,例如 navigator.mediaSession.setActionHandler("nexttrack", null)。

在读写之前先对 navigator 上的 mediaSession 做特性检测,不支持时回退为显示页面自身的 播放/暂停/快进控件,将其作为唯一的控制方式。下面的示例自行创建了这个回退元素 (一个原生的 <audio controls>),因此直接粘贴运行即可生效,而不是假设页面上已经存在 从未添加过的标记:

function supportsMediaSession() {
return "mediaSession" in navigator;
}
const playerControls = document.querySelector("#player-controls") ?? createPlayerControls();
if (supportsMediaSession()) {
navigator.mediaSession.metadata = new MediaMetadata({ title: "Unforgettable" });
} else {
// 此处不支持 Media Session——依赖页面自身的播放/暂停/快进控件,
// 而不是操作系统级别的媒体控件。
showOnPageTransportControls();
}
function createPlayerControls() {
const audio = document.createElement("audio");
audio.id = "player-controls";
audio.controls = true;
audio.src = "/audio/unforgettable.mp3";
audio.hidden = true;
document.body.appendChild(audio);
return audio;
}
function showOnPageTransportControls() {
playerControls.hidden = false;
}
  • 根据 MDN 的兼容性数据,Firefox for Android “暴露了该 API,但没有提供对应的用户可见 媒体控制界面”——在该平台上不要因为 setActionHandler() 没有抛出异常就假定用户能 实际接触到已设置的处理函数。
  • 同一份数据将 Android WebView 记录为不支持——嵌入在 WebView 中的应用仍需要页面自身的 回退控件。
  • 根据 MDN 的 setActionHandler() 参考文档,各个动作字符串(例如 skipad、 previousslide、nextslide)的支持情况因浏览器而异,对某浏览器未实现的动作调用 该方法可能会抛出异常——MDN 自己的示例代码将每次 setActionHandler() 调用都包裹在 独立的 try...catch 中,避免一个不受支持的动作影响其余设置。
  • 根据 MDN 的 metadata 参考文档,MediaSession.metadata 在页面设置之前为 null—— 应做防御性读取,而不要假定 MediaMetadata 对象已经存在。