Media Session API로 미디어 알림 및 재생 컨트롤 맞춤설정

하드웨어 미디어 키와 통합하고 미디어 알림을 맞춤설정하는 방법을 알아보세요.

François Beaufort
François Beaufort

사용자에게 현재 브라우저에서 재생 중인 콘텐츠를 알리고 이를 실행한 페이지로 돌아가지 않고 제어할 수 있도록 Media Session API가 도입되었습니다. 이를 통해 웹 개발자는 맞춤 미디어 알림의 메타데이터, 재생, 일시중지, 탐색, 트랙 변경과 같은 미디어 이벤트, 마이크 음소거/음소거 해제, 카메라 켜기/끄기, 전화 끊기와 같은 화상 회의 이벤트를 통해 이 환경을 맞춤설정할 수 있습니다. 이러한 맞춤설정은 데스크톱 미디어 허브, 모바일의 미디어 알림, 웨어러블 기기 등 다양한 컨텍스트에서 사용할 수 있습니다. 이 문서에서는 이러한 맞춤설정을 설명합니다.

미디어 세션 컨텍스트의 스크린샷
데스크톱의 미디어 허브, 모바일의 미디어 알림, 웨어러블 기기

Media Session API 정보

Media Session API는 다음과 같은 몇 가지 이점과 기능을 제공합니다.

  • 하드웨어 미디어 키가 지원됩니다.
  • 미디어 알림은 모바일, 데스크톱, 페어링된 웨어러블 기기에서 맞춤설정할 수 있습니다.
  • 미디어 허브는 데스크톱에서 사용할 수 있습니다.
  • 잠금 화면 미디어 제어는 ChromeOS 및 모바일에서 사용할 수 있습니다.
  • PIP 창 컨트롤은 오디오 재생, 화상 회의, 슬라이드 프레젠테이션에 사용할 수 있습니다.
  • 모바일에서 어시스턴트 통합을 사용할 수 있습니다.

브라우저 지원

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

소스

몇 가지 예를 통해 이러한 사항을 설명하겠습니다.

예 1: 사용자가 키보드의 '다음 트랙' 미디어 키를 누르면 웹 개발자는 브라우저가 포그라운드에 있든 백그라운드에 있든 이 사용자 작업을 처리할 수 있습니다.

예 2: 사용자가 기기 화면이 잠겨 있는 동안 웹에서 팟캐스트를 듣는 경우에도 잠금 화면 미디어 컨트롤에서 '뒤로 탐색' 아이콘을 눌러 웹 개발자가 재생 시간을 몇 초 뒤로 이동할 수 있습니다.

예 3: 사용자가 오디오를 재생하는 탭이 있는 경우 데스크톱의 미디어 허브에서 재생을 쉽게 중지할 수 있으므로 웹 개발자가 상태를 삭제할 수 있습니다.

예 4: 사용자가 영상 통화를 하는 경우 PIP 창에서 '마이크 전환' 컨트롤을 눌러 웹사이트에서 마이크 데이터를 수신하지 못하도록 할 수 있습니다.

이 모든 작업은 MediaSession 인터페이스와 MediaMetadata 인터페이스라는 두 가지 인터페이스를 통해 이루어집니다. 첫 번째는 사용자가 재생 중인 항목을 제어할 수 있도록 합니다. 두 번째는 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>를 사용하여 지정됨)가 있는 경우 미디어 알림에서 이를 사용합니다.
  • Android용 Chrome의 알림 아트워크 대상 크기는 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' },
    ]
  }]
});
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"는 일시적으로 중지하려는 의도를 나타냅니다.

'재생/일시중지' 아이콘은 항상 미디어 알림에 표시되며 관련 미디어 이벤트는 브라우저에서 자동으로 처리됩니다. 기본 동작을 재정의하려면 아래와 같이 '재생' 및 '일시중지' 미디어 작업을 처리합니다.

예를 들어 브라우저는 탐색하거나 로드할 때 웹사이트가 미디어를 재생하지 않는 것으로 간주할 수 있습니다. 이 경우 웹사이트 UI가 미디어 알림 컨트롤과 동기화된 상태를 유지하도록 navigator.mediaSession.playbackState"playing" 또는 "paused"로 설정하여 이 동작을 재정의합니다.

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);

화상 회의 작업

사용자가 영상 통화를 PIP 모드 창에 배치하면 브라우저에 마이크 및 카메라 컨트롤과 통화 종료 컨트롤이 표시될 수 있습니다. 사용자가 클릭하면 웹사이트에서 아래의 화상 회의 작업을 통해 처리합니다. 예를 보려면 화상 회의 샘플을 참고하세요.

PIP 모드 창의 화상 회의 컨트롤 스크린샷
PIP 모드 창에서 화상 회의를 제어할 수 있습니다.

마이크 전환

"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.
});

슬라이드 작업 표시

사용자가 슬라이드 프레젠테이션을 PIP 창에 배치하면 브라우저에 슬라이드 탐색 컨트롤이 표시될 수 있습니다. 사용자가 이를 클릭하면 웹사이트에서 Media Session API를 통해 이를 처리합니다. 예를 보려면 Slides 프레젠테이션 샘플을 참고하세요.

이전 슬라이드

"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 Foundation얀 모겐슈타인 작업이 포함된 미디어 세션 샘플을 확인하세요.

Media Session API를 보여주는 스크린캐스트입니다.

리소스