OpenPWAStore
返回 News
Guide · May 19, 2026

Media Session API 让音频视频 PWA 拥有原生级播放体验

面向 PWA 媒体应用的 Media Session 实战模式,让播放控制与系统控件在锁屏与通知场景下保持同步。

OpenPWA Editorial2 min read
Media Session API 让音频视频 PWA 拥有原生级播放体验 cover

为什么媒体 PWA 需要集成 Media Session

当用户在 PWA 中播放音频或视频时,他们期望得到与原生应用相同的控制体验:锁屏播放、通知媒体控件,以及耳机按键处理。Media Session API 让这种集成成为可能,但前提是 PWA 在播放过程中主动设置并更新媒体元数据。

如果没有 Media Session,音频会继续播放,但在 OS 控制层却没有任何痕迹。当音频停止,或者用户切换到其他页面时,系统提供的常规暂停/前进控制会消失。实际效果是:应用在理应"原生化"的场景下缺乏原生感。

在播放开始时初始化 Media Session

媒体开始播放后立即初始化,而不是在页面加载时就做:

const audio = new Audio('track.mp3');

audio.addEventListener('play', () => {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: '曲目名称',
    artist: '演唱者',
    album: '专辑名称',
    artwork: [
      { src: '/album-art-96.png', sizes: '96x96', type: 'image/png' },
      { src: '/album-art-256.png', sizes: '256x256', type: 'image/png' },
      { src: '/album-art-512.png', sizes: '512x512', type: 'image/png' }
    ]
  });

  navigator.mediaSession.setActionHandler('play', () => {
    audio.play();
  });

  navigator.mediaSession.setActionHandler('pause', () => {
    audio.pause();
  });

  navigator.mediaSession.setActionHandler('previoustrack', () => {
    // 切到上一首
  });

  navigator.mediaSession.setActionHandler('nexttrack', () => {
    // 切到下一首
  });

  audio.play();
});

等待媒体元素实际开始播放后再设置元数据。设置得太早可能导致浏览器延迟处理或忽略会话。

持续更新可定位位置

锁屏控件与进度条只有在 PWA 主动更新 Media Session 播放状态时才能显示可定位范围:

function updateMediaSession() {
  if ('setPositionState' in navigator.mediaSession) {
    navigator.mediaSession.setPositionState({
      duration: audio.duration,
      playbackRate: audio.playbackRate,
      position: audio.currentTime
    });
  }
}

audio.addEventListener('timeupdate', () => {
  updateMediaSession();
});

// 也要在用户手动拖动时更新
audio.addEventListener('seeked', () => {
  updateMediaSession();
});

如果发现性能问题,可以对 timeupdate 事件做节流一秒钟通常足以满足 UI 更新需求。

告知浏览器哪些操作可用

Media Session 处理函数声明用户可以使用哪些功能。只为应用实际支持的操作设置处理函数:

function setAvailableActions(canSkip, canSeek) {
  navigator.mediaSession.setActionHandler('play', () => audio.play());
  navigator.mediaSession.setActionHandler('pause', () => audio.pause());

  if (canSkip) {
    navigator.mediaSession.setActionHandler('previoustrack', () => playPrevious());
    navigator.mediaSession.setActionHandler('nexttrack', () => playNext());
  } else {
    navigator.mediaSession.setActionHandler('previoustrack', null);
    navigator.mediaSession.setActionHandler('nexttrack', null);
  }

  if (canSeek) {
    navigator.mediaSession.setActionHandler('seekto', (details) => {
      audio.currentTime = details.seekTime;
    });
  } else {
    navigator.mediaSession.setActionHandler('seekto', null);
  }
}

每次曲目信息或应用状态变化时调用 setAvailableActions()——比如播放列表结束,或者当前曲目没有相邻曲目时。

链接前台与后台状态

PWA 处于后台时,service worker 或页面可能无法立即收到所有事件。处理可见性变化,保持媒体状态一致:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    // 更新媒体会话,与当前实际播放时间同步
    updateMediaSession();
  }
});

// 在 service worker 中,监听发送给客户端的媒体会话事件
self.addEventListener('message', (event) => {
  if (event.data.type === 'MEDIA_ACTION') {
    switch (event.data.action) {
      case 'play':
        // 在客户端安排播放
        event.ports[0].postMessage({ action: 'play' });
        break;
      case 'pause':
        event.ports[0].postMessage({ action: 'pause' });
        break;
    }
  }
});

某些浏览器将 Media Session 操作直接发送给活跃 worker,其他浏览器则发送给页面。请在锁屏控制最关键的手机端进行测试。

清单:可安装媒体 PWA 的 Media Session 就绪检查

  • [ ] Media Session 元数据在 play() 触发后设置,而不是更早。
  • [ ] 播放位置随 timeupdateseeked 事件持续更新。
  • [ ] 根据可用操作(跳过/拖动/停止)动态更新处理函数。
  • [ ] 页面重新可见时重新同步媒体状态。
  • [ ] 在目标浏览器上,service worker 或消息通道负责后台操作的派发。
  • [ ] 封面图 URL 无需认证并使用 HTTPS。
  • [ ] 仅在支持时使用 setPositionState,对旧浏览器做降级。
  • [ ] 不可用时将操作设为 null,避免 OS 控件出现困惑。

这对可安装的媒体 PWA 意味着什么

安装后,用户自然期望在应用切换时播放可以可靠持续。Media Session 正是这种期望的一部分:锁屏控件、耳机按钮、通知播放控件都应保持一致。当用户安装一个媒体 PWA,他们默认会拥有"原生般"的播放集成——这个 API 是实现体验的关键。

把 Media Session 当作播放核心的一部分,而不是可选增强。当播放状态是单一事实源,而 OS 控件只是反映该状态的 UI 层时,集成效果最佳。