听《富婆KTV》让我学到个新的API

起因

作为一名前端开发,我有在网页上听歌的习惯,那天我如常边听我的每日推荐边写代码,突然一首非常攒劲带感 的音乐成功引起了我的注意,我想去调大我耳机的声音,就点了下右下角任务栏的声音,我发现弹出来的面板上面竟然有播放控件 、歌曲名字 、作者 和专辑封面的信息。

好奇

突然让我思考一个问题:

操作系统是怎么知道我在网页上听的是什么歌呢?

在我一通探索下,发现了 MediaSession 这个 API。

Media Session API 是一个 Web API

Media Session API 允许网页为媒体播放提供自定义行为,并与设备的原生媒体控制界面(如通知栏、锁屏、键盘媒体键、耳机按钮等)交互。主要功能包括: 元数据共享:通过 MediaMetadata 对象,网页可以向浏览器和操作系统提供当前播放媒体的标题、艺术家、专辑、封面等信息,这些信息会显示在设备的媒体控制界面上。

  1. 自定义控制行为:通过 setActionHandler 方法,开发者可以为播放、暂停、快进、快退、上一曲、下一曲等操作设置回调函数,响应用户通过设备媒体控制触发的动作。

  2. 播放状态同步:通过 playbackState 属性,网页可以声明媒体的播放状态(playing、paused 或 none),确保设备界面与网页状态一致。

  3. 位置状态更新:通过 setPositionState 方法,网页可以更新媒体的当前播放位置、总时长和播放速度,供设备界面显示进度条等信息。

  4. 视频会议支持:支持视频会议相关的操作,如开关麦克风、摄像头或挂断通话(togglemicrophone、togglecamera、hangup)。

  5. 画中画模式支持:在画中画(Picture-in-Picture)模式下,可以为幻灯片导航等场景提供自定义控制(如 previousslide、nextslide)。

Media Session API 广泛应用于音频播放器、视频播放器、播客应用、流媒体网站等场景,让用户可以通过设备的原生界面或硬件控制媒体播放。

navigator.mediaSession 是访问 Media Session API 的入口点,它是一个只读属性,返回一个 MediaSession 对象。开发者通过这个对象配置媒体元数据、设置动作处理程序等。

这些功能都怎么用呢?

设置媒体元数据

在播放歌曲的网页打开控制台,看看 navigator.mediaSession 的内容你就大概了解了:

js 复制代码
navigator.mediaSession.metadata = new MediaMetadata({
    title: '歌曲名称',
    artist: '作者',
    album: '专辑名称',
    artwork: [
      { src: 'https://xxx.image.com/96x96', sizes: '96x96', type: 'image/png' },
      { src: 'https://xxx.image.com/128x128', sizes: '128x128', type: 'image/png' },
      { src: 'https://xxx.image.com/512x512', sizes: '512x512', type: 'image/png' }
    ]
  });

需要注意的:

  • artwork 是一个 MediaImage 对象的数组,建议提供多种尺寸的图片(如 96x96、128x128、512x512)以适配不同设备。
  • 如果未设置 artwork,浏览器可能使用页面的 <link rel="icon"> 作为默认图片

设置动作处理程序

通过 setActionHandler 方法为媒体控制动作设置回调函数,支持的动作包括:

  • play:播放

  • pause:暂停

  • stop:停止

  • seekbackward:后退(默认 10 秒)

  • seekforward:前进(默认 10 秒)

  • seekto:跳转到指定时间

  • previoustrack:上一曲

  • nexttrack:下一曲

  • skipad:跳过广告

  • togglemicrophone:开关麦克风

  • togglecamera:开关摄像头

  • hangup:挂断通话

  • previousslide / nextslide:幻灯片导航(画中画模式)

给一个为播放和暂停设置动作处理程序,并同步播放状态的例子:

js 复制代码
const audio = document.querySelector('#player');
audio.src = 'song.mp3';

if ('mediaSession' in navigator) {
  // 设置播放动作
  navigator.mediaSession.setActionHandler('play', async () => {
    await audio.play();
    navigator.mediaSession.playbackState = 'playing';
  });

  // 设置暂停动作
  navigator.mediaSession.setActionHandler('pause', () => {
    audio.pause();
    navigator.mediaSession.playbackState = 'paused';
  });

  // 监听音频元素状态变化
  audio.addEventListener('play', () => {
    navigator.mediaSession.playbackState = 'playing';
  });
  audio.addEventListener('pause', () => {
    navigator.mediaSession.playbackState = 'paused';
  });
}

注意:

  1. 使用 try-catch 检测动作是否支持,因为某些动作(如 stop、seekto)可能在部分浏览器中不可用

  2. 要移除动作处理程序,可将回调设为 null:navigator.mediaSession.setActionHandler('play', null)。

  3. 动作处理程序会持久存在,直到页面卸载或手动移除。

更新播放位置

通过 setPositionState 方法更新媒体的播放位置、时长和速度,适用于显示进度条或时间信息:

js 复制代码
function updatePositionState() {
  if ('setPositionState' in navigator.mediaSession) {
    navigator.mediaSession.setPositionState({
      duration: audio.duration, // 媒体总时长(秒)
      playbackRate: audio.playbackRate, // 播放速度
      position: audio.currentTime // 当前位置(秒)
    });
  }
}

// 在时间更新或动作触发时调用
audio.addEventListener('timeupdate', updatePositionState);

// 示例:处理快退动作
navigator.mediaSession.setActionHandler('seekbackward', (details) => {
  const skipTime = details.seekOffset || 10; // 默认后退 10 秒
  audio.currentTime = Math.max(audio.currentTime - skipTime, 0);
  updatePositionState();
});

注意:

  1. duration 必须为正数,直播流可用 Infinity 表示无固定时长。
  2. 在媒体加载完成前,audio.duration 可能为 NaN,需进行检查。

处理播放列表导航

支持 previoustrack 和 nexttrack 动作,用于切换播放列表中的曲目。

js 复制代码
const playlist = ['song1.mp3', 'song2.mp3', 'song3.mp3'];
let index = 0;

function playAudio() {
  audio.src = playlist[index];
  audio.play().then(() => {
    navigator.mediaSession.metadata = new MediaMetadata({
      title: `Song ${index + 1}`,
      artist: 'Artist Name',
      album: 'Album Name',
      artwork: [{ src: 'cover.jpg', sizes: '512x512', type: 'image/jpeg' }]
    });
    updatePositionState();
  });
}

navigator.mediaSession.setActionHandler('previoustrack', () => {
  index = (index - 1 + playlist.length) % playlist.length;
  playAudio();
});

navigator.mediaSession.setActionHandler('nexttrack', () => {
  index = (index + 1) % playlist.length;
  playAudio();
});

视频会议场景

为视频会议设置麦克风和摄像头控制。

js 复制代码
let isMicrophoneActive = false;
navigator.mediaSession.setActionHandler('togglemicrophone', () => {
  isMicrophoneActive = !isMicrophoneActive;
  // 切换麦克风状态的逻辑
  navigator.mediaSession.setMicrophoneActive(isMicrophoneActive);
});

视频会议动作(如 togglemicrophone)目前支持有限,主要在 Chrome 92+ 桌面版中可用

画中画模式支持

在画中画模式下,为幻灯片导航设置动作

js 复制代码
navigator.mediaSession.setActionHandler('previousslide', () => {
  // 切换到上一页幻灯片
});
navigator.mediaSession.setActionHandler('nextslide', () => {
  // 切换到下一页幻灯片
});

最后总结

MediaSession 通过 navigator.mediaSession 提供,允许网页与设备的媒体控制界面交互,支持元数据共享、自定义动作和状态同步。歌也听了,API 也学到了,要是觉得歌有意思或者这个API有用,就给点个关注点个赞吧,彦祖、亦菲们~。我是 Senar,是不是分享点有用没用的前端开发知识,我们下期再见~

相关推荐
Hello.Reader5 分钟前
Three.js / Cesium 卡顿怎么办?把三维渲染搬到 GPU 服务器
服务器·开发语言·javascript
chi11306 分钟前
【无标题】
前端·javascript·python
萝卜兔00833 分钟前
PIKACHU_UnsafeFileUpload
javascript·php
IT_陈寒1 小时前
JavaScript实战技巧总结:深挖数组遍历的性能陷阱与实战解法
前端·人工智能·后端
步行cgn1 小时前
为什么既要 web.xml 又要 springmvc.xml?能不能只配一个?
xml·前端·hive
风骏时光牛马1 小时前
大模型综合能力评测:多维度性能与对齐效果对比
前端
winfredzhang1 小时前
从零手写一个毛玻璃风格的页面搭建台:单文件 HTML 实现组件放置、面板磁吸与图层管理
前端·html
葡萄城技术团队10 小时前
1000 人协同压测的环境瓶颈排查:CPU 争用与 Wi\-Fi
前端
excel11 小时前
Marked 与 Markdown 有什么区别?该怎么选?
前端
Da Da 泓12 小时前
五子棋 Web 端测试用例报告
前端·网络·websocket·网络协议·测试用例·项目