ハードウェア メディアキーとの統合方法、メディア通知のカスタマイズ方法など。
ブラウザで現在再生されているコンテンツをユーザーに知らせ、再生を開始したページに戻ることなくそのコンテンツを操作できるようにするため、Media Session API が導入されました。これにより、ウェブ デベロッパーは、カスタム メディア通知のメタデータ、メディア イベント(再生、一時停止、シーク、トラックの変更など)、ビデオ会議イベント(マイクのオンとオフ、カメラのオンとオフ、通話の終了など)を通じて、このエクスペリエンスをカスタマイズできます。これらのカスタマイズは、パソコンのメディアハブ、モバイルのメディア通知、ウェアラブル デバイスなど、さまざまなコンテキストで利用できます。この記事では、これらのカスタマイズについて説明します。
Media Session API について
Media session API には次のようなメリットと機能があります。
- ハードウェア メディアキーがサポートされています。
- メディア通知は、モバイル、パソコン、ペア設定されたウェアラブル デバイスでカスタマイズされます。
- メディアハブはパソコンで利用できます。
- ロック画面のメディア コントロールは、ChromeOS とモバイルで利用できます。
- ピクチャー イン ピクチャー ウィンドウのコントロールは、音声の再生、ビデオ会議、スライドのプレゼンテーションで使用できます。
- モバイルでのアシスタントとの統合が利用可能。
そのいくつかを、例で説明します。
例 1: ユーザーがキーボードの「次のトラック」メディアキーを押した場合、ウェブ デベロッパーは、ブラウザがフォアグラウンドにあるかバックグラウンドにあるかにかかわらず、このユーザー アクションを処理できます。
例 2: デバイスの画面がロックされているときにユーザーがウェブでポッドキャストを聴いている場合、ロック画面のメディア コントロールから「早送り」アイコンを押して、ウェブ デベロッパーが再生時間を数秒戻すことができます。
例 3: ユーザーがタブで音声を再生している場合、デスクトップのメディアハブから簡単に再生を停止できるため、ウェブ デベロッパーは状態を消去できます。
例 4: ユーザーがビデオ通話中の場合、ピクチャー イン ピクチャー ウィンドウの [マイクを切り替え] コントロールを押すと、ウェブサイトがマイクのデータを受信しないようにできます。
これらはすべて、MediaSession
インターフェースと MediaMetadata
インターフェースの 2 つの異なるインターフェースを通じて行われます。1 つ目は、再生中のコンテンツをユーザーが操作できるようにします。2 つ目は、制御する必要があることを MediaSession
に指示する方法です。
たとえば、次の図は、これらのインターフェースが特定のメディア操作(この場合はモバイルのメディア通知)にどのように関連しているかを示しています。
再生中の曲をユーザーに知らせる
ウェブサイトで音声または動画が再生されると、モバイルの通知トレイまたはパソコンのメディアハブにメディア通知が自動的に表示されます。ブラウザは、ドキュメントのタイトルと、見つけられる中で最も大きいアイコン画像を使用して、適切な情報を表示するよう最善を尽くします。Media Session API を使用すると、以下に示すように、タイトル、アーティスト名、アルバム名、アートワークなどのリッチメディア メタデータでメディア通知をカスタマイズできます。
Chrome では、メディア時間が 5 秒以上の場合にのみメディア通知を表示するため、「フル」音声フォーカスをリクエストします。これにより、通知が鳴ったときに通知が表示されなくなります。
// 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 URL とデータ URL をサポートしています。
- アートワークが定義されておらず、適切なサイズのアイコン画像(
<link rel=icon>
を使用して指定)がある場合は、メディア通知でその画像が使用されます。 - Chrome for Android の通知アートワークのターゲット サイズは
512x512
です。ローエンド デバイスの場合は、256x256
です。 - メディア HTML 要素の
title
属性は、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' },
]
}]
});
ユーザーが再生中のコンテンツを操作できるようにする
メディア セッション アクションとは、ユーザーが現在のメディア再生を操作するときにウェブサイトがユーザーに代わって処理できるアクション(「再生」や「一時停止」など)です。アクションはイベントに類似しており、イベントとほぼ同じように機能します。イベントと同様に、アクションは、適切なオブジェクト(この場合は MediaSession
のインスタンス)にハンドラを設定することで実装されます。一部のアクションは、ユーザーがヘッドセット、別のリモート デバイス、キーボードのボタンを押したり、メディア通知を操作したりしたときにトリガーされます。
一部のメディア セッション アクションはサポートされていない場合があるため、設定する際は 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.`);
}
設定すると、メディア セッション アクション ハンドラはメディアの再生中も維持されます。これはイベント リスナー パターンに似ていますが、イベントを処理すると、ブラウザはデフォルトの動作を停止し、ウェブサイトがメディア アクションをサポートしていることを示すシグナルとして使用します。そのため、適切なアクション ハンドラが設定されていない限り、メディア アクション コントロールは表示されません。
再生 / 一時停止
"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.
});
再生位置を設定する
通知にメディアの再生位置を正確に表示するには、適切なタイミングで位置のステータスを設定するだけです。位置情報の状態は、メディアの再生レート、再生時間、現在の時刻の組み合わせです。
期間は正の値で指定してください。位置は正の値で、時間より小さくする必要があります。再生レートは 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.
});
対応ブラウザ
次のスライド
"nextslide"
アクションは、ユーザーがスライドを表示する際に次のスライドに移動することを示します。
navigator.mediaSession.setActionHandler('nextslide', () => {
// Show next slide.
});
対応ブラウザ
サンプル
Blender Foundation と Jan Morgenstern の作品をフィーチャーしたメディア セッションのサンプルをご覧ください。
リソース
- メディア セッション仕様: wicg.github.io/mediasession
- 仕様に関する問題: github.com/WICG/mediasession/issues
- Chrome のバグ: crbug.com