運用 Media Session API 自訂媒體通知和播放控制項

如何整合硬體媒體鍵、自訂媒體通知等。

François Beaufort
François Beaufort

為了讓使用者瞭解瀏覽器目前正在播放的內容,並且在不返回啟動該內容的頁面下控制內容,我們推出了 Media Session API。這項功能可讓網頁開發人員透過自訂媒體通知中的結構描述、媒體事件 (例如播放、暫停、快轉、變更音軌) 和視訊會議事件 (例如將麥克風設為靜音/取消靜音、開啟/關閉相機、掛斷電話) 自訂這項體驗。這些自訂項目可用於多種情境,包括電腦版媒體中心、行動裝置上的媒體通知,甚至是穿戴式裝置。我會在本文中說明這些客製化設定。

媒體工作階段背景資訊的螢幕截圖。
電腦版媒體中心、行動裝置媒體通知和穿戴式裝置。

關於 Media Session API

Media session API 提供多項優點和功能:

  • 支援硬體媒體金鑰。
  • 媒體通知會在行動裝置、電腦和配對的穿戴式裝置上自訂。
  • 媒體中心可在電腦上使用。
  • 螢幕鎖定媒體控制項適用於 ChromeOS 和行動裝置。
  • 您可以使用子母畫面視窗控制項,進行音訊播放視訊會議簡報
  • 行動裝置上的 Google 助理整合功能現已推出。

瀏覽器支援

  • Chrome:73。
  • Edge:79。
  • Firefox:82。
  • Safari:15。

資料來源

以下列舉幾個例子說明其中幾點。

範例 1:如果使用者按下鍵盤的「下一首曲目」媒體鍵,無論瀏覽器是在前景或背景,網頁開發人員都能處理這項使用者動作。

範例 2:如果使用者在裝置螢幕處於鎖定狀態時在網路上收聽 Podcast,仍可點選螢幕鎖定螢幕媒體控制項的「倒轉」圖示,讓網頁開發人員將播放時間往前移動幾秒鐘。

範例 3:如果使用者有播放音訊的分頁,他們可以輕鬆透過桌機媒體中心停止播放,讓網頁開發人員有機會清除狀態。

範例 4:如果使用者正在進行視訊通話,可以在子母畫面視窗中按下「切換麥克風」控制項,讓網站停止接收麥克風資料。

這一切都是透過兩個不同的介面完成:MediaSession 介面和 MediaMetadata 介面。第一個檔案可讓使用者控制要播放的內容其二是告知 MediaSession 需要控制的項目。

舉例來說,下圖顯示這些介面與特定媒體控制項的關聯方式,在本例中是行動裝置上的媒體通知。

媒體工作階段介面的插圖。
剖析行動裝置上的媒體通知。

讓使用者瞭解播放內容

網站播放音訊或影片時,使用者會自動收到媒體通知,無論是在行動裝置的通知匣,還是電腦版的媒體中心,都會收到通知。瀏覽器會盡力使用文件標題和可找到的最大圖示圖片,顯示適當的資訊。您可以使用 Media Session API,透過更豐富的媒體中繼資料 (例如標題、藝人名稱、專輯名稱和圖片) 自訂媒體通知,如下所示。

只有在媒體時間長度「至少 5 秒」時,Chrome 才會要求「完整」音訊專注功能來顯示媒體通知。這樣可確保叮咚等附帶音效不會顯示通知。

// After media (video or audio) starts playing
await document.querySelector("video").play();

if ("mediaSession" in navigator) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    artist: 'Rick Astley',
    album: 'Whenever You Need Somebody',
    artwork: [
      { src: 'https://via.placeholder.com/96',   sizes: '96x96',   type: 'image/png' },
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/192', sizes: '192x192', type: 'image/png' },
      { src: 'https://via.placeholder.com/256', sizes: '256x256', type: 'image/png' },
      { src: 'https://via.placeholder.com/384', sizes: '384x384', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  });

  // TODO: Update playback state.
}

播放結束後,系統會自動移除通知,因此您不必「釋放」媒體工作階段。請注意,系統會在下次播放開始時使用 navigator.mediaSession.metadata。因此,當媒體播放來源變更時,請務必進行更新,確保媒體通知會顯示相關資訊。

媒體中繼資料有幾點要注意。

  • 通知圖片陣列支援 blob 網址和資料網址。
  • 如果未定義圖片,且有使用 <link rel=icon> 指定的圖示圖片 (且圖片大小合適),媒體通知會使用該圖片。
  • Android 版 Chrome 的通知圖片目標大小為 512x512。在低階裝置上,則為 256x256
  • 媒體 HTML 元素的 title 屬性會用於「Now playing」macOS 小工具。
  • 如果媒體資源已嵌入 (例如在 iframe 中),則必須從嵌入內容設定 Media Session API 資訊。請參閱下方的程式碼片段。
<iframe id="iframe">
  <video>...</video>
</iframe>
<script>
  iframe.contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    ...
  });
</script>

您也可以在媒體中繼資料中加入個別章節資訊,例如章節標題、時間戳記和螢幕截圖。這樣一來,使用者就能瀏覽媒體內容。

navigator.mediaSession.metadata = new MediaMetadata({
  // title, artist, album, artwork, ...
  chapterInfo: [{
    title: 'Chapter 1',
    startTime: 0,
    artwork: [
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  }, {
    title: 'Chapter 2',
    startTime: 42,
    artwork: [
      { src: 'https://via.placeholder.com/128', sizes: '128x128', type: 'image/png' },
      { src: 'https://via.placeholder.com/512', sizes: '512x512', type: 'image/png' },
    ]
  }]
});
ChromeOS 媒體通知中顯示的章節資訊。
內含 ChromeOS 章節的媒體通知

讓使用者控制正在播放的內容

媒體工作階段動作是指網站可在使用者與目前媒體播放互動時,為使用者處理的動作 (例如「播放」或「暫停」)。動作與事件類似,運作方式也大致相同。與事件一樣,您可以透過在適當物件 (在本例中為 MediaSession 的例項) 上設定處理常式,實作動作。使用者按下耳機、其他遠端裝置、鍵盤的按鈕,或與媒體通知互動時,系統就會觸發某些動作。

Windows 10 中的媒體通知螢幕截圖。
Windows 10 中的自訂媒體通知。

由於部分媒體工作階段動作可能不受支援,建議在設定時使用 try…catch 區塊。

const actionHandlers = [
  ['play',          () => { /* ... */ }],
  ['pause',         () => { /* ... */ }],
  ['previoustrack', () => { /* ... */ }],
  ['nexttrack',     () => { /* ... */ }],
  ['stop',          () => { /* ... */ }],
  ['seekbackward',  (details) => { /* ... */ }],
  ['seekforward',   (details) => { /* ... */ }],
  ['seekto',        (details) => { /* ... */ }],
  /* Video conferencing actions */
  ['togglemicrophone', () => { /* ... */ }],
  ['togglecamera',     () => { /* ... */ }],
  ['hangup',           () => { /* ... */ }],
  /* Presenting slides actions */
  ['previousslide', () => { /* ... */ }],
  ['nextslide',     () => { /* ... */ }],
];

for (const [action, handler] of actionHandlers) {
  try {
    navigator.mediaSession.setActionHandler(action, handler);
  } catch (error) {
    console.log(`The media session action "${action}" is not supported yet.`);
  }
}

取消設定媒體工作階段動作處理常式的方式,就跟將其設為 null 一樣簡單。

try {
  // Unset the "nexttrack" action handler at the end of a playlist.
  navigator.mediaSession.setActionHandler('nexttrack', null);
} catch (error) {
  console.log(`The media session action "nexttrack" is not supported yet.`);
}

設定完成後,媒體工作階段動作處理常式會透過媒體播放持續顯示。這與事件監聽器模式相似,但處理事件代表瀏覽器會停止執行任何預設行為,並將此做為網站支援媒體動作的信號。因此,除非設定了適當的動作處理常式,否則系統不會顯示媒體動作控制項。

macOS Big Sur 中「現在播放中」小工具的螢幕截圖。
macOS Big Sur 中的「正在播放」小工具。

播放/暫停

"play" 動作表示使用者想繼續媒體播放,而 "pause" 則表示使用者想暫停播放。

「播放/暫停」圖示一律會顯示在媒體通知中,瀏覽器會自動處理相關媒體事件。如要覆寫其預設行為,請處理如下的「播放」和「暫停」媒體動作。

舉例來說,瀏覽器可能會認為網站在尋找或載入時並未播放媒體。在這種情況下,請將 navigator.mediaSession.playbackState 設為 "playing""paused",藉此覆寫這項行為,確保網站 UI 與媒體通知控制項保持同步。

const video = document.querySelector('video');

navigator.mediaSession.setActionHandler('play', async () => {
  // Resume playback
  await video.play();
});

navigator.mediaSession.setActionHandler('pause', () => {
  // Pause active playback
  video.pause();
});

video.addEventListener('play', () => {
  navigator.mediaSession.playbackState = 'playing';
});

video.addEventListener('pause', () => {
  navigator.mediaSession.playbackState = 'paused';
});

播放上一首曲目

如果媒體播放內容含有「開始」概念,"previoustrack" 動作會表示使用者要從頭開始播放目前的媒體,如果媒體播放內容含有「播放清單」概念,則會表示使用者要移動至播放清單中的前一項。

navigator.mediaSession.setActionHandler('previoustrack', () => {
  // Play previous track.
});

播放下一首曲目

如果媒體播放內容含有播放清單概念,"nexttrack" 動作會指出使用者想要將媒體播放內容移至播放清單中的下一個項目。

navigator.mediaSession.setActionHandler('nexttrack', () => {
  // Play next track.
});

停止

"stop" 動作表示使用者想要停止媒體播放,並在適當情況下清除狀態。

navigator.mediaSession.setActionHandler('stop', () => {
  // Stop playback and clear state if appropriate.
});

倒轉/快轉

"seekbackward" 動作表示使用者希望將媒體播放時間向後移動一段時間,而 "seekforward" 則表示希望將媒體播放時間向前移動一段時間。在兩種情況下,短時間指的是幾秒鐘。

在動作處理常式中提供的 seekOffset 值,是用來移動媒體播放時間的時間長度 (以秒為單位)。如果未提供 (例如 undefined),則應使用合理的時間 (例如 10 到 30 秒)。

const video = document.querySelector('video');
const defaultSkipTime = 10; /* Time to skip in seconds by default */

navigator.mediaSession.setActionHandler('seekbackward', (details) => {
  const skipTime = details.seekOffset || defaultSkipTime;
  video.currentTime = Math.max(video.currentTime - skipTime, 0);
  // TODO: Update playback state.
});

navigator.mediaSession.setActionHandler('seekforward', (details) => {
  const skipTime = details.seekOffset || defaultSkipTime;
  video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
  // TODO: Update playback state.
});

跳轉到特定時間

"seekto" 動作表示使用者想要將媒體播放時間移至特定時間。

在動作處理常式中提供的 seekTime 值,是指要將媒體播放時間移至何時,以秒為單位。

如果這項動作在序列中多次呼叫,且這不是該序列中的最後一次呼叫,則在動作處理常式中提供的 fastSeek 布林值為 true。

const video = document.querySelector('video');

navigator.mediaSession.setActionHandler('seekto', (details) => {
  if (details.fastSeek && 'fastSeek' in video) {
    // Only use fast seek if supported.
    video.fastSeek(details.seekTime);
    return;
  }
  video.currentTime = details.seekTime;
  // TODO: Update playback state.
});

設定播放位置

在通知中準確顯示媒體播放位置,只要在適當時間設定位置狀態即可,如以下所示。位置狀態由媒體播放速率、持續時間和目前時間組成。

ChromeOS 螢幕鎖定畫面的媒體控制選項螢幕截圖。
ChromeOS 的螢幕鎖定媒體控制項。

時間長度必須提供正數。位置必須是正數,且小於時間長度。播放速率必須大於 0。

const video = document.querySelector('video');

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

// When video starts playing, update duration.
await video.play();
updatePositionState();

// When user wants to seek backward, update position.
navigator.mediaSession.setActionHandler('seekbackward', (details) => {
  /* ... */
  updatePositionState();
});

// When user wants to seek forward, update position.
navigator.mediaSession.setActionHandler('seekforward', (details) => {
  /* ... */
  updatePositionState();
});

// When user wants to seek to a specific time, update position.
navigator.mediaSession.setActionHandler('seekto', (details) => {
  /* ... */
  updatePositionState();
});

// When video playback rate changes, update position state.
video.addEventListener('ratechange', (event) => {
  updatePositionState();
});

重設位置狀態的做法很簡單,只要將其設為 null 即可。

// Reset position state when media is reset.
navigator.mediaSession.setPositionState(null);

視訊會議動作

當使用者將視訊通話放入子母畫面視窗時,瀏覽器可能會顯示麥克風和攝影機的控制選項,以及掛斷通話的選項。使用者點選這些項目後,網站會透過下方的視訊會議動作進行處理。如需範例,請參閱視訊會議範例

子母畫面視窗中的視訊會議控制項螢幕截圖。
在子母畫面視窗中顯示的視訊會議控制項。

切換麥克風

"togglemicrophone" 動作表示使用者想將麥克風靜音或取消靜音。setMicrophoneActive(isActive) 方法會告知瀏覽器,網站目前是否認為麥克風處於啟用狀態。

let isMicrophoneActive = false;

navigator.mediaSession.setActionHandler('togglemicrophone', () => {
  if (isMicrophoneActive) {
    // Mute the microphone.
  } else {
    // Unmute the microphone.
  }
  isMicrophoneActive = !isMicrophoneActive;
  navigator.mediaSession.setMicrophoneActive(isMicrophoneActive);
});

切換相機

"togglecamera" 動作表示使用者想要開啟或關閉有效的相機。setCameraActive(isActive) 方法則表示瀏覽器是否將網站視為有效。

let isCameraActive = false;

navigator.mediaSession.setActionHandler('togglecamera', () => {
  if (isCameraActive) {
    // Disable the camera.
  } else {
    // Enable the camera.
  }
  isCameraActive = !isCameraActive;
  navigator.mediaSession.setCameraActive(isCameraActive);
});

掛斷

"hangup" 動作表示使用者想要結束通話。

navigator.mediaSession.setActionHandler('hangup', () => {
  // End the call.
});

播放簡報動作

當使用者將簡報放入「子母畫面」視窗時,瀏覽器可能會顯示用於瀏覽投影片的控制項。當使用者點按這些按鈕時,網站會透過 Media Session API 處理這些按鈕。如需範例,請參閱「簡報幻燈片範例」。

上一張投影片

"previousslide" 動作表示使用者在播放投影片時想返回上一張投影片。

navigator.mediaSession.setActionHandler('previousslide', () => {
  // Show previous slide.
});

瀏覽器支援

  • Chrome:111。
  • Edge:111。
  • Firefox:不支援。
  • Safari:不支援。

下一張投影片

"nextslide" 動作表示使用者在簡報時,想要前往下一張投影片。

navigator.mediaSession.setActionHandler('nextslide', () => {
  // Show next slide.
});

瀏覽器支援

  • Chrome:111。
  • Edge:111。
  • Firefox:不支援。
  • Safari:不支援。

範例

請查看一些媒體工作階段範例,其中包含 Blender FoundationJan Morgenstern 的作品

說明 Media Session API 的螢幕側錄。

資源