미디어 세션

W3C 작업 초안,

이 문서에 대한 추가 정보
현재 버전:
https://www.w3.org/TR/2025/WD-mediasession-20250919/
최신 공개 버전:
https://www.w3.org/TR/mediasession/
편집자 초안:
https://w3c.github.io/mediasession/
이전 버전:
히스토리:
https://www.w3.org/standards/history/mediasession/
피드백:
GitHub
편집자:
(Google Inc.)
(Apple Inc.)
이전 편집자:
(Google Inc.)
(Google Inc.)
(Google Inc.)
(Opera)
버전 히스토리:
https://github.com/w3c/mediasession/commits

요약

이 명세는 웹 개발자가 플랫폼 UI에 맞춤형 미디어 메타데이터를 표시하고, 사용 가능한 플랫폼 미디어 컨트롤을 사용자 정의하며, 키보드, 헤드셋, 리모컨에 있는 하드웨어 키와 모바일 기기의 알림 영역 및 잠금 화면에 있는 소프트웨어 키와 같은 플랫폼 미디어 키에 접근할 수 있도록 합니다.

이 문서의 상태

이 섹션은 해당 문서가 발행된 시점에서의 상태를 설명합니다. 현재 W3C 발행물 목록과 최신 기술 보고서 개정본은 W3C 표준 및 초안 색인에서 확인할 수 있습니다.

이 명세에 대한 피드백과 의견을 환영합니다. 이 명세에 대한 논의는 GitHub Issues를 통해 하는 것이 좋습니다. 또는 Media Working Group의 메일링 리스트 public-media-wg@w3.org (아카이브)로 의견을 보낼 수 있습니다. 이 초안은 워킹 그룹에서 아직 논의해야 할 대기 중인 이슈들을 강조합니다. 이러한 이슈의 결과에 대한 결정이나 유효성에 대해서는 아직 결정된 바가 없습니다.

이 문서는 Media Working Group에서 권고안 트랙을 사용하여 작업 초안으로 발행되었습니다. 이 문서는 W3C 권고안이 되는 것을 목표로 합니다.

작업 초안으로 발행되었다고 해서 W3C 및 그 회원들의 승인이나 지지를 의미하지는 않습니다.

이 문서는 초안 문서이며 언제든지 업데이트, 대체 또는 다른 문서로 폐기될 수 있습니다. 진행 중인 작업 외의 용도로 이 문서를 인용하는 것은 부적절합니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련하여 공개된 특허 공개 목록을 유지하고 있으며, 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 개인이 필수 청구를 포함한다고 믿는 특허에 대한 실제 지식이 있다면 W3C 특허 정책 섹션 6에 따라 정보를 공개해야 합니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서의 적용을 받습니다.

1. 소개

이 섹션은 비규범적입니다.

오늘날 미디어는 광범위하게 사용되고 있으며, 웹은 미디어 콘텐츠를 소비하는 주요 수단 중 하나입니다. 많은 플랫폼에서는 알림, 미디어 제어 센터, 기기 잠금 화면, 웨어러블 기기와 같은 다양한 UI 요소에 제목, 아티스트, 앨범 및 앨범 아트와 같은 미디어 메타데이터를 표시할 수 있습니다. 이 명세는 웹 페이지가 플랫폼 UI에 표시할 미디어 메타데이터를 지정하고, 플랫폼 UI나 미디어 키에서 발생할 수 있는 미디어 컨트롤에 응답하여 사용자 경험을 향상시키는 것을 목표로 합니다.

2. 개인정보 보호 고려사항

이 섹션은 비규범적입니다.

이 명세에서 도입된 API는 개인정보 측면에서 영향이 매우 적습니다. API의 일부는 웹사이트가 버튼이나 다른 형태의 컨트롤을 통해 사용자로부터 명령을 받을 수 있도록 하며, 때로는 사용자와 웹사이트 사이에 새로운 입력 레이어를 도입할 수도 있습니다.

2.1. 익명 모드

개인정보 보호를 위해, 익명 모드에서는 사용자 에이전트가 MediaMetadata 정보를 시스템과 공유할 때 주의해야 하며, 사용자를 해칠 수 있는 방식으로 사용되지 않도록 해야 합니다. 이 정보가 매우 눈에 띄게 표시된다면 사용자가 익명 모드로 브라우징하려는 의도에 반하는 것입니다. 가능하다면 UI 요소는 플랫폼에 비공개로 광고되어야 합니다.

2.2. 미디어 세션 액션

미디어 세션 액션은 웹 플랫폼에 새로운 입력 레이어를 제공합니다. 사용자 에이전트는 사용자의 행동이 활성 미디어 세션이 있는 웹사이트로 라우팅될 수 있음을 사용자가 인식하도록 해야 합니다. 특히, 이러한 액션이 헤드셋이나 기타 원격 장치와 같은 원격 장치에서 발생하는 경우에는 더욱 그렇습니다. 이러한 입력을 수신할 때 사용자 에이전트는 플랫폼의 관례를 따르는 것이 바람직하며, 이를 통해 사용자가 쉽게 이해할 수 있도록 해야 합니다.

3. 보안 고려사항

이 섹션은 비규범적입니다.

이 명세에서 도입된 API는 보안 측면에서도 영향이 매우 적습니다. API의 일부는 사용자 에이전트가 사용할 수 있는 메타데이터를 웹사이트가 노출할 수 있도록 합니다. 사용자 에이전트는 이 데이터를 신중하게 사용해야 합니다.

3.1. 사용자 인터페이스 지침

이 명세에서 도입된 MediaMetadata 를 통해 웹사이트는 재생 중인 항목에 대한 더 많은 정보를 제공할 수 있습니다. 사용자 에이전트는 이 정보를 미디어 재생과 관련된 모든 UI에서, 내부적으로나 플랫폼 내에서 활용하도록 설계되어 있습니다.

MediaMetadata 는 미디어 재생 맥락에서 사용될 것으로 예상되기 때문에 스푸핑이 어려워지지만, MediaMetadata 에는 텍스트 필드와 이미지 필드가 있으므로 악의적인 웹사이트가 다른 웹사이트의 정체성을 스푸핑하려고 시도할 수 있습니다. 사용자 에이전트는 메타데이터가 어느 웹사이트에서 왔는지 출처를 확인하거나 명확하게 노출하는 방법을 제공하는 것이 바람직합니다.

만약 사용자 에이전트가 MediaMetadata 기반으로 생성된 UI 요소에서 다시 웹사이트로 돌아가는 메커니즘을 제공한다면, 그 행동이 웹사이트에 의해 감지되지 않도록 하여 스푸핑 가능성을 줄이는 것이 바람직합니다.

일반적으로 웹사이트에서 알림을 표시하는 것과 관련된 모든 보안 고려사항이 여기에 적용되어야 합니다. MediaMetadata 는 일반 웹 알림보다 커스터마이즈가 적으므로 스푸핑이 더 어렵다는 점도 주목할 가치가 있습니다.

4. 모델

4.1. 재생 상태

playpause 액션이 제대로 작동하려면, 사용자 에이전트는 브라우징 컨텍스트활성 미디어 세션에서 미디어를 재생 중인지 여부를 파악할 수 있어야 하며, 이를 추정 재생 상태라고 합니다. 추정 재생 상태를 결정하는 권장 방법은, 노드 문서의 브라우징 컨텍스트브라우징 컨텍스트인 미디어 요소들을 모니터링하는 것입니다. 브라우징 컨텍스트추정 재생 상태"playing" 이 해당 요소 중 하나라도 잠재적으로 재생 중이고 음소거되지 않았다면이고, 그렇지 않으면 "paused" 입니다. WebAudio와 플러그인과 같은 다른 정보도 고려해야 합니다.

playbackState 속성은 선언된 재생 상태브라우징 컨텍스트로부터 지정합니다. 이 상태는 추정 재생 상태와 결합되어 실제 재생 상태를 계산하며, 이는 playpause 액션에 사용됩니다.

실제 재생 상태는 다음과 같이 계산됩니다:

playbackState 속성은 페이지가 미디어가 일시정지될 때 준비 단계를 수행하고 싶지만 pause 액션에 의해 준비 단계가 중단될 수 있도록 하는 경우에 유용할 수 있습니다. 예시는 Setting playbackState를 참고하세요.

활성 미디어 세션실제 재생 상태가 변경될 때마다, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘을 실행해야 합니다.

4.2. 라우팅

사용자 에이전트에 여러 탭이 있을 수 있으므로 동시에 여러 MediaSession 객체가 존재할 수 있습니다. 각 탭에는 최상위 traversable과 하위 navigable이 포함될 수 있으며, 각 navigable마다 MediaSession 객체가 있을 수 있습니다.

사용자 에이전트는 MediaSession 객체 중 최대 하나만 사용자에게 표시하도록 선택해야 하며, 이를 활성 미디어 세션이라 합니다. 활성 미디어 세션은 null일 수도 있습니다. 선택 방법은 사용자 에이전트에 맡겨지며, 바람직하게는 사용자의 선호 경험을 기반으로 해야 합니다. playbackState 속성은 미디어 세션 라우팅에 영향을 주지 않아야 하며, 활성 미디어 세션에만 적용됩니다.

사용자 에이전트가 활성 미디어 세션을 선택할 때 오디오 포커스를 관리하는 것이 권장됩니다. 탭 또는 브라우징 컨텍스트가 현재 오디오를 재생 중이거나 사용자가 해당 미디어를 제어할 것으로 예상되면 오디오 포커스가 있다고 합니다. AudioFocus API는 이 영역을 목표로 하며, 완성되면 사용할 수 있습니다.

활성 미디어 세션이 변경될 때마다, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘메타데이터 업데이트 알고리즘을 실행해야 합니다.

4.3. 메타데이터

활성 미디어 세션의 미디어 메타데이터는 플랫폼 관례에 따라 플랫폼 UI에 표시될 수 있습니다. 활성 미디어 세션이 변경되거나 metadata 를 설정할 때마다 활성 미디어 세션에 대해, 사용자 에이전트는 메타데이터 업데이트 알고리즘을 반드시 실행해야 합니다. 단계는 다음과 같습니다:

  1. 활성 미디어 세션이 null인 경우, 플랫폼에 표시된 미디어 메타데이터를 해제하고, 이 단계를 종료합니다.
  2. 활성 미디어 세션metadata빈 메타데이터라면, 플랫폼에 표시된 미디어 메타데이터를 해제하고 이 단계를 종료합니다.
  3. 플랫폼에 표시된 미디어 메타데이터를 활성 미디어 세션metadata 와 일치하도록 업데이트합니다.
  4. 사용자 에이전트가 아트워크 이미지를 표시하고 싶다면, 이미지 가져오기 알고리즘을 실행해야 합니다.

이미지 가져오기 알고리즘은 다음과 같습니다:

  1. 실행 중인 이미지 가져오기 알고리즘이 다른 것이 있다면, 기존 알고리즘 실행 인스턴스를 취소합니다.
  2. 활성 미디어 세션metadataartwork 가 비어 있으면, 이 단계를 종료합니다.
  3. 플랫폼이 미디어 아트워크 표시를 지원한다면, metadataartwork 중에서 선호 아트워크 이미지를 선택합니다.
  4. request를 새 request로 설정합니다. 이 URL선호 아트워크 이미지src이고, destination 은 "image", mode는 "no-cors", credentials mode는 "include", 그리고 use-URL-credentials flag가 설정됨.
  5. Fetch request를 실행하며, fetch의 processResponse 알고리즘으로 다음 단계를 실행합니다.
    1. response를 fetch의 processResponse 알고리즘에서 제공된 response로 설정합니다.
    2. responsetype"error"가 아니면, responsebody를 이미지로 디코딩 시도합니다.
    3. 이미지 형식이 지원된다면 해당 이미지를 플랫폼 UI에 표시할 아트워크로 사용합니다. 그렇지 않으면 이미지 가져오기 알고리즘 은 실패하고 종료합니다.

이미지 가져오기 알고리즘에서 이미지를 가져오지 못한 경우, 사용자 에이전트는 기본 이미지를 아트워크로 표시하는 등 대체 동작을 할 수 있습니다.

4.4. 액션

미디어 세션 액션은 페이지가 MediaSession에서 사용자가 상호작용할 수 있도록 처리할 수 있는 액션입니다. 예를 들어, 페이지는 사용자가 헤드셋이나 기타 원격 장치의 버튼을 누를 때 트리거되는 액션을 처리할 수 있습니다.

미디어 세션 액션 소스미디어 세션 액션을 생성할 수 있는 소스입니다. 이러한 소스는 플랫폼이거나 사용자 에이전트가 생성한 UI 표면이 될 수 있습니다.

미디어 세션 액션 소스는 선택적으로 target을 가질 수 있으며, 이 target은 미디어 세션 액션 소스에서 생성된 미디어 세션 액션의 수신자가 되어야 합니다. 미디어 세션 액션 소스targetnull이라면, 모든 미디어 세션 액션 소스의 액션의 수신자는 활성 미디어 세션이 됩니다.

미디어 세션 액션MediaSessionAction으로 표현되며, 다음 값 중 하나를 가질 수 있습니다:

  • play: 재생을 다시 시작하는 의도입니다.
  • pause: 현재 활성 재생을 일시정지하는 의도입니다.
  • seekbackward: 재생 시간을 짧은 시간(예: 몇 초) 뒤로 이동하는 의도입니다.
  • seekforward: 재생 시간을 짧은 시간(예: 몇 초) 앞으로 이동하는 의도입니다.
  • previoustrack: 재생에 시작점 개념이 있으면 현재 재생을 처음부터 시작하거나, 재생에 플레이리스트 개념이 있으면 이전 항목으로 이동하는 의도입니다.
  • nexttrack: 재생에 플레이리스트 개념이 있다면 다음 항목으로 이동하는 의도입니다.
  • skipad: 현재 재생 중인 광고를 건너뛰는 의도입니다.
  • stop: 재생을 중지하고 필요하면 상태를 초기화하는 의도입니다.
  • seekto: 재생 시간을 특정 시간으로 이동하는 의도입니다.
  • togglemicrophone: 사용자의 마이크를 음소거 또는 음소거 해제하는 의도입니다.
  • togglecamera: 사용자의 활성 카메라를 켜거나 끄는 의도입니다.
  • togglescreenshare: 사용자의 활성 화면 공유를 켜거나 끄는 의도입니다.
  • hangup: 통화를 종료하는 의도입니다.
  • previousslide: 슬라이드를 발표할 때 이전 슬라이드로 돌아가는 의도입니다.
  • nextslide: 슬라이드를 발표할 때 다음 슬라이드로 이동하는 의도입니다.
  • enterpictureinpicture: 미디어 세션을 화면 속 화면(picture-in-picture) 창에서 열려는 의도입니다.
  • voiceactivity: 마이크에서 음성 활동이 감지되었음을 웹 페이지에 알리는 의도입니다.

모든 MediaSession에는 지원되는 미디어 세션 액션의 맵이 있습니다. 키는 미디어 세션 액션이고, 값은 MediaSessionActionHandler입니다.

주어진 MediaSession에서 actionhandler 파라미터로 액션 핸들러 업데이트 알고리즘이 호출되면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:

  1. handlernull이면, MediaSession지원되는 미디어 세션 액션에서 action을 제거하고, 이 단계들을 중단합니다.
  2. MediaSession지원되는 미디어 세션 액션action을 추가하고 handler와 연결합니다.

지원되는 미디어 세션 액션이 변경될 때, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘을 실행해야 합니다. 사용자 에이전트는 같은 이벤트 루프에서 여러 액션이 수정될 때 UI 깜빡임을 피하기 위해 작업을 큐에 추가하여 미디어 세션 액션 업데이트 알고리즘을 실행할 수 있습니다.

사용자 에이전트가 source라는 미디어 세션 액션 소스로부터 action이라는 미디어 세션 액션이 트리거되었다는 알림을 받으면, 사용자 에이전트는 작업을 큐에 추가해야 하며, 사용자 상호작용 작업 소스를 이용하여 다음 미디어 세션 액션 처리 단계를 실행해야 합니다:

  1. sessionsourcetarget으로 설정합니다.
  2. sessionnull이면, session활성 미디어 세션으로 설정합니다.
  3. sessionnull이면, 이 단계들을 중단합니다.
  4. actionssession지원되는 미디어 세션 액션으로 설정합니다.
  5. actionsaction 키가 없으면, 이 단계들을 중단합니다.
  6. handleractions에서 action 키에 연결된 MediaSessionActionHandler로 설정합니다.
  7. details 파라미터를 MediaSessionActionDetails로 설정하여 handler를 실행합니다.
  8. session과 연결된 브라우징 컨텍스트에서 활성화 알림 단계를 실행합니다.

사용자 에이전트가 playpause에 대한 합동 명령(예: 헤드셋 버튼 클릭)을 받으면, 작업을 큐에 추가하여 사용자 상호작용 작업 소스를 사용해 다음 단계를 실행해야 합니다:

  1. 활성 미디어 세션null이면, 이 단계들을 중단합니다.
  2. action미디어 세션 액션으로 설정합니다.
  3. 활성 미디어 세션실제 재생 상태playing이면, actionpause로 설정합니다.
  4. 그렇지 않으면 actionplay로 설정합니다.
  5. action으로 미디어 세션 액션 처리 단계를 실행합니다.

playpause 미디어 세션 액션에 대해 활성 미디어 세션에 핸들러가 제공되지 않은 경우, 사용자 에이전트가 기본 핸들러를 구현하는 것이 권장됩니다.

togglemicrophone, togglecamera, togglescreenshare, hangup 미디어 세션 액션에 대해 활성 미디어 세션에 핸들러가 제공되지 않은 경우, 사용자 에이전트가 기본 핸들러를 구현할 수 있습니다.

사용자 에이전트는 MediaStreamTrackmuted 속성을 통해, togglemicrophone, togglecamera, togglescreenshare 미디어 세션 액션 외에도 마이크, 카메라, 화면 공유 상태를 웹 페이지에 노출할 수 있습니다. 이 경우, 사용자 에이전트는 MediaSessionActionHandler를 먼저 실행한 뒤, 별도의 작업으로 트랙의 음소거 상태 설정 단계를 실행해야 합니다.

voiceactivity 액션 소스는 항상 문서에 live 마이크 MediaStreamTrack가 있어야 합니다. 사용자 에이전트는 MediaSessionActionHandlervoiceactivity에 대해, 마이크에서 하나 이상의 live MediaStreamTrack에서 음성 활동이 감지될 때만 호출해야 합니다. 사용자 에이전트는 마이크가 음소거되지 않았고 마이크에 연결된 모든 MediaStreamTrackenabled 상태라면 음성 활동을 무시할 수 있습니다. 사용자 에이전트는 MediaSessionActionHandlervoiceactivity에 대해 프라이버시 및 전력 효율 정책에 따라 최소 호출 간격을 설정하는 것이 권장됩니다.

voiceactivity는 음성 활동의 시작만 나타냅니다. 사용자가 MediaStreamTrack이 음소거된 상태에서 말을 하면 알림을 표시하거나, 오디오 처리를 위해 AudioWorklet을 시작할 수 있습니다. 음성 활동이 끝나는 시점에 대한 동작은 정의되어 있지 않습니다. 다른 액션과 달리 사용자가 명시적으로 트리거하지 않고, voiceactivity는 사용자 에이전트 또는 시스템의 음성 활동 감지 알고리즘에 따라 다릅니다. 프라이버시 및 전력 효율을 위해 웹 페이지는 음성 활동이 끝나고 곧바로 다시 시작된 경우 알림을 받지 않을 수 있습니다. 마지막 voiceactivity 액션 이후 곧바로 다시 시작된 경우도 포함됩니다.

페이지는 미디어 세션 액션을 처리할 수 있을 때만 MediaSessionActionHandler를 등록해야 합니다. 사용자 에이전트는 이를 지원되는 미디어 세션 액션으로 표시하고 미디어 세션 액션 소스를 업데이트합니다.

미디어 세션 액션 업데이트 알고리즘이 호출되면, 사용자 에이전트는 다음 단계를 반드시 실행해야 합니다:

  1. available actions미디어 세션 액션 배열로 설정합니다.
  2. 활성 미디어 세션이 null이면 available actions를 빈 배열로 설정합니다.
  3. 그렇지 않으면 available actions활성 미디어 세션지원되는 미디어 세션 액션의 키 목록으로 설정합니다.
  4. 미디어 세션 액션 소스 source에 대해 다음 하위 단계를 실행합니다:
    1. 선택적으로, 활성 미디어 세션이 null이 아니면:
      1. 활성 미디어 세션실제 재생 상태playing이면, available actions에서 play를 제거합니다.
      2. 그렇지 않으면 available actions에서 pause를 제거합니다.
    2. source가 사용자 에이전트가 만든 UI 요소라면, 공간에 비해 액션이 너무 많을 경우 available actions에서 일부 요소를 제거할 수 있습니다.
    3. sourceavailable actions의 업데이트된 목록을 알립니다.

4.5. 포지션 상태

사용자 에이전트는 플랫폼 관례에 따라 플랫폼 UI에서 현재 재생 위치재생 길이 를 표시할 수 있습니다. 포지션 상태 는 다음 요소의 조합입니다:

  • 미디어의 재생 길이(초 단위)
  • 미디어의 재생 속도. 계수(coefficient)입니다.
  • 미디어의 마지막 보고된 재생 위치. 포지션 상태가 생성될 때의 미디어 재생 위치(초 단위)입니다.

포지션 상태MediaPositionState로 표현되며, 반드시 마지막 위치 갱신 시간과 함께 저장되어야 합니다. 이는 포지션 상태가 마지막으로 갱신된 시간(초 단위)입니다.

포지션 상태를 결정하는 권장 방법은 노드 문서의 브라우징 컨텍스트가 브라우징 컨텍스트인 미디어 요소를 모니터링하는 것입니다.

실제 재생 속도는 다음과 같이 계산되는 계수입니다:

초 단위의 현재 재생 위치는 다음과 같이 계산됩니다:

5. MediaSession 인터페이스

[Exposed=Window]
partial interface Navigator {
  [SameObject] readonly attribute MediaSession mediaSession;
};

enum MediaSessionPlaybackState {
  "none",
  "paused",
  "playing"
};

enum MediaSessionAction {
  "play",
  "pause",
  "seekbackward",
  "seekforward",
  "previoustrack",
  "nexttrack",
  "skipad",
  "stop",
  "seekto",
  "togglemicrophone",
  "togglecamera",
  "togglescreenshare",
  "hangup",
  "previousslide",
  "nextslide",
  "enterpictureinpicture",
  "voiceactivity"
};

enum MediaSessionEnterPictureInPictureReason {
  "other",
  "useraction",
  "contentoccluded"
};

callback MediaSessionActionHandler = undefined(MediaSessionActionDetails details);

[Exposed=Window]
interface MediaSession {
  attribute MediaMetadata? metadata;

  attribute MediaSessionPlaybackState playbackState;

  undefined setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);

  undefined setPositionState(optional MediaPositionState state = {});

  Promise<undefined> setMicrophoneActive(boolean active);

  Promise<undefined> setCameraActive(boolean active);

  Promise<undefined> setScreenshareActive(boolean active);
};

MediaSession 객체는 특정 문서의 미디어 세션을 나타내며, 문서가 사용자 에이전트와 재생 및 처리 방법에 대한 정보를 소통할 수 있도록 합니다.

MediaSessionmetadata 객체를 가지고 있으며, 이는 MediaMetadata로 표현됩니다. 초기값은 null입니다.

mediaSession 속성은 MediaSession 인스턴스를 반환해야 하며, 이는 Navigator 객체와 연관되어 있습니다.

metadata 속성은 MediaSessionmetadata를 반영합니다. getter는 MediaSessionmetadata를 반환해야 합니다. setter는 새 값을 value로 설정할 때 다음 단계를 수행해야 합니다:

  1. MediaSessionmetadatanull이 아니면, media sessionnull로 설정합니다.
  2. MediaSessionmetadatavalue로 설정합니다.
  3. MediaSessionmetadatanull이 아니면, media session을 현재 MediaSession으로 설정합니다.
  4. 병렬로 update metadata algorithm을 실행합니다.

playbackState 속성은 media session선언된 재생 상태를 나타내며, 세션이 자신의 브라우징 컨텍스트에서 미디어를 재생 중인지 선언합니다. 초기값은 none입니다. setter는 새 값이 MediaSessionPlaybackState의 유효한 값이라면 IDL 속성에 설정해야 합니다. getter는 마지막으로 설정된 유효한 값을 반환해야 합니다. playbackState 속성은 사용자 에이전트가 해당 브라우징 컨텍스트가 재생 중인지 일시정지 중인지 판단하는 힌트입니다.

playbackState를 설정하면 실제 재생 상태가 변경될 수 있으며, 미디어 세션 액션 업데이트 알고리즘이 실행될 수 있습니다.

MediaSessionPlaybackState 열거형은 브라우징 컨텍스트가 미디어를 재생 중인지 여부를 나타내며, 값은 다음과 같이 설명됩니다:

setActionHandler(action, handler) 메서드를 호출하면, MediaSession에서 actionhandler를 사용하여 액션 핸들러 업데이트 알고리즘을 실행해야 합니다.

setPositionState(state) 메서드를 호출하면 다음 단계를 수행해야 합니다:

setMicrophoneActive(active) 메서드는 페이지가 원하는 마이크 캡처 상태를 사용자 에이전트에 알립니다(예: 페이지가 더 이상 오디오를 통화로 전송하지 않기 때문에 마이크를 "비활성"으로 간주하면 setMicrophoneActive(false)를 호출). 호출 시 다음 단계를 수행해야 합니다:

  1. documentthis관련 글로벌 객체연결된 Document로 설정합니다.
  2. captureKind를 "microphone"으로 설정합니다.
  3. 캡처 상태 업데이트 알고리즘document, active, captureKind로 실행한 결과를 반환합니다.

setCameraActive(active) 메서드도 페이지가 원하는 카메라 캡처 상태를 사용자 에이전트에 알리며, 호출 시 다음 단계를 수행해야 합니다:

  1. documentthis관련 글로벌 객체연결된 Document로 설정합니다.
  2. captureKind를 "camera"로 설정합니다.
  3. 캡처 상태 업데이트 알고리즘document, active, captureKind로 실행한 결과를 반환합니다.

setScreenshareActive(active) 메서드도 페이지가 원하는 화면 공유 캡처 상태를 사용자 에이전트에 알리며, 호출 시 다음 단계를 수행해야 합니다:

  1. documentthis관련 글로벌 객체연결된 Document로 설정합니다.
  2. captureKind를 "screenshare"로 설정합니다.
  3. 캡처 상태 업데이트 알고리즘document, active, captureKind로 실행한 결과를 반환합니다.

캡처 상태 업데이트 알고리즘document, active, captureKind로 호출될 때 다음 단계를 수행해야 합니다:

  1. document완전히 활성화된 상태가 아니면 거부된 promiseInvalidStateError와 함께 반환합니다.
  2. activetrue이고 documentvisibility state가 "visible"이 아니면, 사용자 에이전트는 거부된 promiseInvalidStateError와 함께 반환할 수 있습니다.
  3. p를 새 promise로 설정합니다.
  4. 병렬로 다음 단계를 실행합니다:
    1. applyPausePolicy를, 만약 사용자 에이전트가 UI에 따라 captureKind 타입의 모든 입력 소스 일시정지 정책을 구현한다면 true로, 그렇지 않으면 false로 설정한다.
    2. applyPausePolicytrue라면, 다음 하위 단계를 실행한다:
      1. currentlyActive를, 사용자 에이전트가 현재 captureKind 타입의 모든 입력 소스 일시정지 중이면 false로, 그렇지 않으면 true로 설정한다.
      2. activecurrentlyActive가 같다면, queue a taskuser interaction task source로 추가하여 pundefined로 resolve하고 이 단계들을 중단한다.
      3. activetrue라면, 사용자 에이전트는 예를 들어 사용자에게 프롬프트를 표시하기 위해 진행을 잠시 대기할 수 있다.
      4. 사용자 에이전트가 캡처 상태 업데이트 요청을 거부하면, queue a taskuser interaction task source로 추가하여 pNotAllowedError로 reject하고 이 단계들을 중단한다.
    3. 사용자 에이전트의 캡처 상태 UI를 captureKindactive에 따라 업데이트한다.
    4. Queue a taskuser interaction task source로 추가하여 pundefined로 resolve한다.
    5. applyPausePolicytrue라면, 다음 하위 단계를 실행한다:
      1. newMutedState를, activefalsetrue로, 그렇지 않으면 false로 설정한다.
      2. captureKind 타입의 소스를 가진 각 MediaStreamTrack에 대해, queue a taskuser interaction task source로 추가하여 트랙의 음소거 상태 설정newMutedState로 실행한다.
  5. p를 반환합니다.

setMicrophoneActive(active), setCameraActive(active)setScreenshareActive(active) 메서드는 사용자 에이전트의 특성에 따라 거부될 수 있습니다. 특히 페이지에서 마이크, 카메라, 화면 공유를 활성화(음소거 해제)하려고 할 때 거부될 수 있습니다. 사용자 에이전트는 이 경우 일시적 활성화를 요구할 수 있습니다. 실제 결정을 위해 사용자 입력(프롬프트 등)을 요구할 수도 있습니다.

사용자 에이전트는 미디어 세션 액션에 대한 핸들러를 호출하는 UI를 표시할 수 있습니다.

6. MediaMetadata 인터페이스

[Exposed=Window]
interface MediaMetadata {
  constructor(optional MediaMetadataInit init = {});
  attribute DOMString title;
  attribute DOMString artist;
  attribute DOMString album;
  attribute FrozenArray<object> artwork;
  [SameObject] readonly attribute FrozenArray<ChapterInformation> chapterInfo;
};

dictionary MediaMetadataInit {
  DOMString title = "";
  DOMString artist = "";
  DOMString album = "";
  sequence<MediaImage> artwork = [];
  sequence<ChapterInformationInit> chapterInfo = [];
};

MediaMetadata 객체는 MediaSession 과 연관된 메타데이터를 표현하며, 사용자 에이전트가 맞춤형 사용자 인터페이스를 제공하는 데 사용할 수 있습니다.

MediaMetadatamedia session과 연관될 수 있습니다.

MediaMetadatatitle, artist, album을 가지며, 모두 DOMString 타입입니다.

MediaMetadataartwork images라는 MediaImage 타입의 시퀀스를 가집니다. 또한 MediaMetadata converted artwork images를 가지며, 초기값은 undefined입니다.

MediaMetadata chapter information이라는 리스트를 가집니다.

MediaMetadatanull이거나 다음 모든 조건을 만족하면 empty metadata라고 합니다:

MediaMetadata(init) 생성자를 호출하면 다음 단계를 수행해야 합니다:

  1. metadata를 새 MediaMetadata 객체로 설정합니다.
  2. metadatatitleinittitle로 설정합니다.
  3. metadataartistinitartist로 설정합니다.
  4. metadataalbuminitalbum으로 설정합니다.
  5. initartworkinput으로 하여 convert artwork algorithm을 실행하고 성공하면 결과를 metadataartwork images로 설정합니다.
  6. chaptersChapterInformation 타입의 빈 리스트로 설정합니다.
  7. initchapterInfo의 각 entry에 대해, create a ChapterInformation을 실행하여 chapters에 추가합니다.
  8. metadatachapter informationcreating a frozen array의 결과로 설정합니다.
  9. metadata를 반환합니다.

input 파라미터로 convert artwork algorithm이 호출되면, inputMediaImage 타입의 시퀀스일 때 사용자 에이전트는 다음 단계를 실행해야 합니다:

  1. outputMediaImage 타입의 빈 리스트로 설정합니다.
  2. input의 각 entry(MediaImage 리스트)에 대해 다음 단계를 실행합니다:
    1. image를 새 MediaImage로 설정합니다.
    2. baseURLentry settings object에서 지정된 API base URL로 설정합니다.
    3. entrysrcbaseURLParse합니다. 실패가 아니면 imagesrc를 반환값으로 설정합니다. 실패 시 TypeError를 throw하고 단계를 중단합니다.
    4. imagesizesentrysizes로 설정합니다.
    5. imagetypeentrytype으로 설정합니다.
    6. imageoutput에 추가합니다.
  3. output을 결과값으로 반환합니다.

title 속성은 MediaMetadatatitle을 반영합니다. getter는 MediaMetadatatitle을 반환해야 하며, setter는 MediaMetadatatitle을 주어진 값으로 설정해야 합니다.

artist 속성은 MediaMetadataartist를 반영합니다. getter는 MediaMetadataartist를 반환해야 하며, setter는 MediaMetadataartist를 주어진 값으로 설정해야 합니다.

album 속성은 MediaMetadataalbum을 반영합니다. getter는 MediaMetadataalbum을 반환해야 하며, setter는 MediaMetadataalbum을 주어진 값으로 설정해야 합니다.

artwork 속성은 MediaMetadataartwork images를 반영합니다. getter는 다음 단계를 실행해야 합니다:

  1. MediaMetadataconverted artwork imagesundefined라면 다음 단계를 실행합니다:
    1. frozenArtwork을 JavaScript Array 값으로 설정합니다.
    2. MediaMetadataartwork images의 각 entry에 대해 다음 단계를 실행합니다:
      1. imageconverting to a JavaScript object entry의 결과로 설정합니다.
      2. SetIntegrityLevel(image, "frozen")를 실행하여 스크립트에 의한 의도치 않은 변경을 방지합니다.
      3. imagefrozenArtwork에 추가합니다.
    3. SetIntegrityLevel(frozenArtwork, "frozen")을 실행합니다.
    4. MediaMetadataconverted artwork imagesfrozenArtwork로 설정합니다.
  2. MediaMetadataconverted artwork images를 반환합니다.

setter는 value를 새 값으로 설정할 때 다음 단계를 실행해야 합니다:

  1. convertedArtworkconverting valueMediaImage 타입의 시퀀스로 변환한 결과로 설정합니다.
  2. convertedArtworkconvert artwork algorithm을 실행하여 성공하면 MediaMetadataartwork images로 결과값을 설정합니다.
  3. MediaMetadataconverted artwork imagesundefined로 설정합니다.

MediaMetadatatitle, artist, album, artwork images가 수정될 때 사용자 에이전트는 다음 단계를 실행해야 합니다:

  1. 인스턴스에 media session이 연관되지 않았다면 단계를 중단합니다.
  2. 그렇지 않으면 작업을 큐에 추가하여 다음 하위 단계를 실행합니다:
    1. 인스턴스가 더 이상 media session과 연관되지 않으면 단계를 중단합니다.
    2. 그렇지 않으면 병렬로 update metadata algorithm을 실행합니다.

7. ChapterInformation 인터페이스

[Exposed=Window]
interface ChapterInformation {
  readonly attribute DOMString title;
  readonly attribute double startTime;
  [SameObject] readonly attribute FrozenArray<MediaImage> artwork;
};

dictionary ChapterInformationInit {
  DOMString title = "";
  double startTime = 0;
  sequence<MediaImage> artwork = [];
};

ChapterInformation 객체는 개별 챕터에 대한 메타데이터(섹션 제목, 해당 타임스탬프, 해당 섹션의 스크린샷 이미지 데이터 등)를 표현하며, 사용자 에이전트가 맞춤형 사용자 인터페이스를 제공하는 데 사용할 수 있습니다.

ChapterInformationtitle이라는 DOMString 값을 가집니다.

ChapterInformation startTime이라는 double 값을 가집니다.

ChapterInformation artwork images 리스트를 가집니다.

create a ChapterInformationinit으로 생성하려면, 다음 단계를 실행합니다:

  1. chapterInfo를 새 ChapterInformation 객체로 설정합니다.
  2. chapterInfotitleinittitle로 설정합니다.
  3. chapterInfostartTimeinitstartTime으로 설정합니다. startTime이 음수이거나 duration보다 크면 TypeError를 throw합니다.
  4. artworkinitartworkinput으로 하여 convert artwork algorithm을 실행한 결과를 저장합니다.
  5. chapterInfoartwork imagesartwork에서 creating a frozen array의 결과로 설정합니다.
  6. chapterInfo를 반환합니다.

title 속성은 ChapterInformationtitle을 반영합니다. getter는 ChapterInformationtitle을 반환해야 합니다.

startTime 속성은 ChapterInformationstartTime(초 단위)을 반영합니다. getter는 ChapterInformationstartTime을 반환해야 합니다.

artwork 속성은 ChapterInformationartwork images를 반영합니다. getter는 ChapterInformation artwork images를 반환해야 합니다.

8. MediaImage 딕셔너리

dictionary MediaImage {
  required USVString src;
  DOMString sizes = "";
  DOMString type = "";
};

MediaImage 딕셔너리 멤버는 ImageResource 명세를 참고했습니다 [IMAGE-RESOURCE].

src 딕셔너리 멤버MediaImage 객체의 source를 지정하는 데 사용됩니다. 이미지를 가져올 수 있는 URL입니다.

sizes 딕셔너리 멤버MediaImage 객체의 sizes를 지정하는 데 사용됩니다. HTML sizes 속성의 명세를 따르며, link 요소의 sizes 속성과 동일하게 파싱됩니다. 이 문자열은 고유한 공백으로 구분된 토큰의 무순서 집합이며, ASCII 대소문자 구분 없음으로 이미지 크기를 나타냅니다. 각 키워드는 "any" 문자열과 일치하거나, 앞에 0이 없는 두 개의 유효한 0이 아닌 정수를 X 문자로 구분한 값입니다. 키워드는 원시 픽셀 단위의 아이콘 크기를 나타냅니다(CSS 픽셀과 다름). 여러 이미지 객체가 있으면 사용자 에이전트는 이 값을 사용해 디스플레이 컨텍스트에 가장 적합한 아이콘을 선택할 수 있습니다. sizes 속성의 파싱 단계는 HTML link 요소의 sizes 속성 파싱 단계를 따라야 합니다.

type 딕셔너리 멤버MediaImage 객체의 MIME type을 지정하는 데 사용됩니다. 이미지의 미디어 타입을 나타내는 힌트입니다. 사용자 에이전트가 지원하지 않는 미디어 타입의 이미지를 무시할 수 있도록 합니다.

9. MediaPositionState 딕셔너리

dictionary MediaPositionState {
  unrestricted double duration;
  double playbackRate;
  double position;
};

MediaPositionState 딕셔너리는 MediaSession과 연관된 현재 재생 위치를 표현하며, 사용자 에이전트가 현재 재생 위치와 길이를 표시하는 사용자 인터페이스를 제공하는 데 사용할 수 있습니다.

duration 딕셔너리 멤버길이를 초 단위로 지정합니다. 항상 양수이어야 하며, 끝이 정의되지 않은 미디어(예: 라이브 재생)에는 양의 무한대를 사용할 수 있습니다.

playbackRate 딕셔너리 멤버재생 속도를 지정합니다. 양수면 정방향, 음수면 역방향 재생을 나타냅니다. 0이 되어서는 안 됩니다.

position 딕셔너리 멤버마지막 보고된 재생 위치(초 단위)를 지정합니다. 항상 양수여야 합니다.

10. MediaSessionActionDetails 딕셔너리

dictionary MediaSessionActionDetails {
  required MediaSessionAction action;
  double seekOffset;
  double seekTime;
  boolean fastSeek;
  boolean isActivating;
  MediaSessionEnterPictureInPictureReason enterPictureInPictureReason;
};

MediaSessionActionHandlerdetails 파라미터(딕셔너리 타입 MediaSessionActionDetails)와 함께 실행되어야 합니다.

action 딕셔너리 멤버MediaSessionActionHandler가 연관된 media session action을 지정합니다.

seekOffset 딕셔너리 멤버media session actionseekbackward 또는 seekforward일 때 제공될 수 있습니다. 재생 시간을 이동할 초 단위 시간이며, 있으면 항상 양수여야 합니다. 제공되지 않으면 사이트가 적절한 시간(예: 몇 초)을 선택해야 합니다.

media session actionseekto일 때:

isActivating 딕셔너리 멤버는 사용자 에이전트가 모든 입력 소스 일시정지를 수행할 예정이면 false이고, 그렇지 않으면 true입니다. 사용자 에이전트가 모든 입력 소스 일시정지 정책을 구현하고 media session actiontogglecamera, togglemicrophone 또는 togglescreenshare인 경우 이 딕셔너리 멤버는 반드시 존재해야 합니다.

media session actionenterpictureinpicture일 때, enterPictureInPictureReason 딕셔너리 멤버는 반드시 제공되어야 하며, UA가 이 액션을 트리거한 이유입니다.

enterPictureInPictureReason 는 다음 값 중 하나를 가질 수 있습니다:

11. 권한 정책 통합

이 명세는 "mediasession"이라는 문자열로 식별되는 정책 제어 기능을 정의합니다. 기본 허용 목록*입니다.

문서의 권한 정책은 해당 문서의 콘텐츠가 MediaSession API를 사용할 수 있는지 결정합니다. 문서에서 비활성화된 경우, 사용자 에이전트는 해당 문서의 미디어 세션을 활성 미디어 세션으로 선택하지 않아야 합니다.

12. 예시

이 섹션은 비규범적입니다.

metadata 설정하기:
navigator.mediaSession.metadata = new MediaMetadata({
  title: "Episode Title",
  artist: "Podcast Host",
  album: "Podcast Title",
  artwork: [{src: "podcast.jpg"}],
  chapterInfo: [
    {title: "Chapter 1", startTime: 0, artwork: [{src: "chapter1.jpg"}]},
    {title: "Chapter 2", startTime: 120, artwork: [{src: "chapter2.jpg"}]}
  ]
});

또는 metadata에 여러 아트워크 이미지를 제공하면 사용자 에이전트가 다양한 디스플레이 목적에 맞는 이미지를 선택하거나, 여러 화면에 더 잘 맞는 이미지를 선택할 수 있습니다(chapterInfo의 artwork도 동일):

navigator.mediaSession.metadata = new MediaMetadata({
  title: "Episode Title",
  artist: "Podcast Host",
  album: "Podcast Title",
  artwork: [
    {src: "podcast.jpg", sizes: "128x128", type: "image/jpeg"},
    {src: "podcast_hd.jpg", sizes: "256x256"},
    {src: "podcast_xhd.jpg", sizes: "1024x1024", type: "image/jpeg"},
    {src: "podcast.png", sizes: "128x128", type: "image/png"},
    {src: "podcast_hd.png", sizes: "256x256", type: "image/png"},
    {src: "podcast.ico", sizes: "128x128 256x256", type: "image/x-icon"}
  ],
  chapterInfo: [
    {title: "Chapter 1", startTime: 0, artwork: [
       {src: "chapter1_a.jpg", sizes: "128x128", type: "image/jpeg"},
       {src: "chapter1_b.png", sizes: "256x256", type: "image/png"}
     ]},
    {title: "Chapter 2", startTime: 120, artwork: [
       {src: "chapter2_a.jpg", sizes: "128x128", type: "image/jpeg"},
       {src: "chapter2_b.png", sizes: "256x256", type: "image/png"}
     ]}
  ]
});

예를 들어, 사용자 에이전트가 이미지를 아이콘으로 사용하려면 저화소 화면에는 "podcast.jpg" 또는 "podcast.png"를, 고화소 화면에는 "podcast_hd.jpg" 또는 "podcast_hd.png"를 선택할 수 있습니다. 잠금 화면 배경에 이미지를 사용하려면 "podcast_xhd.jpg"가 우선적으로 선택됩니다.

metadata 변경하기:

오디오북의 플레이리스트나 챕터에서는 여러 미디어 요소가 하나의 media session을 공유할 수 있습니다.

var audio1 = document.createElement("audio");
audio1.src = "chapter1.mp3";

var audio2 = document.createElement("audio");
audio2.src = "chapter2.mp3";

audio1.play();
audio1.addEventListener("ended", function() {
  audio2.play();
});

세션을 공유하므로 metadata는 현재 재생 중인 내용을 반영하도록 반드시 업데이트되어야 합니다.

function updateMetadata(event) {
  navigator.mediaSession.metadata = new MediaMetadata({
    title: event.target == audio1 ? "Chapter 1" : "Chapter 2",
    artist: "An Author",
    album: "A Book",
    artwork: [{src: "cover.jpg"}]
  });
}

audio1.addEventListener("play", updateMetadata);
audio2.addEventListener("play", updateMetadata);
media session actions 처리하기:
var tracks = ["chapter1.mp3", "chapter2.mp3", "chapter3.mp3"];
var trackId = 0;

var audio = document.createElement("audio");
audio.src = tracks[trackId];

function updatePlayingMedia() {
  audio.src = tracks[trackId];
  // metadata 업데이트 (생략)
}

navigator.mediaSession.setActionHandler("previoustrack", function() {
  trackId = (trackId + tracks.length - 1) % tracks.length;
  updatePlayingMedia();
});

navigator.mediaSession.setActionHandler("nexttrack", function() {
  trackId = (trackId + 1) % tracks.length;
  updatePlayingMedia();
});

navigator.mediaSession.setActionHandler("seekto", function(details) {
  audio.currentTime = details.seekTime;
});
playbackState 설정하기:

페이지가 미디어를 일시정지하고 iframe에서 제3자 광고를 재생할 때, UA는 세션을 "재생 중이 아님"으로 간주할 수 있습니다. 하지만 페이지는 사용자가 광고 재생을 일시정지하거나 광고가 끝난 후 대기 중인 재생을 취소할 수 있게 하기를 원합니다.

var adFrame;
var audio = document.createElement("audio");
audio.src = "foo.mp3";

function resetActionHandlers() {
  navigator.mediaSession.setActionHandler("play", _ => audio.play());
  navigator.mediaSession.setActionHandler("pause", _ => audio.pause());
}

resetActionHandlers();

// 페이지가 광고를 재생하려 할 때 호출되는 메서드
function pauseAudioAndPlayAd() {
  audio.pause();
  navigator.mediaSession.playbackState = "playing";
  setUpAdFrame();
  adFrame.contentWindow.postMessage("play_ad");
  navigator.mediaSession.setActionHandler("pause", pauseAd);
}

function pauseAd() {
  adFrame.contentWindow.postMessage("pause_ad");
  navigator.mediaSession.playbackState = "paused";
  navigator.mediaSession.setActionHandler("play", resumeAd);
}

function resumeAd() {
  adFrame.contentWindow.postMessage("resume_ad");
  navigator.mediaSession.playbackState = "playing";
  navigator.mediaSession.setActionHandler("pause", pauseAd);
}

window.onmessage = function(e) {
  if (e.data === "ad finished") {
    removeAdFrame();
    navigator.mediaSession.playbackState = "none";
    resetActionHandlers();
  }
}

function setUpAdFrame() {
  adFrame = document.createElement("iframe");
  adFrame.src = "https://example.com/ad-iframe.html";
  document.body.appendChild(adFrame);
}

function removeAdFrame() {
  adFrame.remove();
}
position state 설정하기:
// 미디어가 로드되면, 길이(duration) 설정.
navigator.mediaSession.setPositionState({
  duration: 60
});

// 미디어가 처음부터 재생 시작.
navigator.mediaSession.playbackState = "playing";

// 2배속 10초부터 재생 시작.
navigator.mediaSession.setPositionState({
  duration: 60,
  playbackRate: 2,
  position: 10
});

// 미디어 일시정지.
navigator.mediaSession.playbackState = "paused";

// 미디어 리셋(reset).
navigator.mediaSession.setPositionState(null);
화상 회의 액션 사용하기:
var isMicrophoneActive = false;
var isCameraActive = false;

navigator.mediaSession.setMicrophoneActive(isMicrophoneActive);
navigator.mediaSession.setCameraActive(isCameraActive);

navigator.mediaSession.setActionHandler("togglemicrophone", function() {
  if (isMicrophoneActive) {
    // 마이크 음소거. 구현 생략.
  } else {
    // 마이크 음소거 해제. 구현 생략.
  }
  isMicrophoneActive = !isMicrophoneActive;
  navigator.mediaSession.setMicrophoneActive(isMicrophoneActive);
});

navigator.mediaSession.setActionHandler("togglecamera", function() {
  if (isCameraActive) {
    // 카메라 끄기. 구현 생략.
  } else {
    // 카메라 켜기. 구현 생략.
  }
  isCameraActive = !isCameraActive;
  navigator.mediaSession.setCameraActive(isCameraActive);
});

navigator.mediaSession.setActionHandler("hangup", function() {
  // 통화 종료. 구현 생략.
});
슬라이드 발표 액션 처리하기:
var currentSlideIndex = 0;

navigator.mediaSession.setActionHandler("previousslide", function() {
  currentSlideIndex--;
  // 현재 슬라이드 설정. 구현 생략.
});

navigator.mediaSession.setActionHandler("nextslide", function() {
  currentSlideIndex++;
  // 현재 슬라이드 설정. 구현 생략.
});
picture-in-picture 처리하기:
navigator.mediaSession.setActionHandler("enterpictureinpicture", function() {
  remoteVideo.requestPictureInPicture();
});
음성 활동 처리하기:
// 오디오가 활성화된 MediaStream 생성.
const stream = await navigator.mediaDevices.getUserMedia({audio:true});
const track = stream.getAudioTracks()[0];
navigator.mediaSession.setActionHandler("voiceactivity", function() {
  if (track.muted) {
    // 음소거 해제 알림 표시. 사용자가 허용하면 setMicrophoneActive(true)를 호출하여 음소거 해제.
  }
});

감사의 글

편집자들은 Paul Adenot, Jake Archibald, Tab Atkins, Jonathan Bailey, François Beaufort, Marcos Caceres, Domenic Denicola, Ralph Giles, Anne van Kesteren, Tobie Langel, Michael Mahemoff, Jer Noble, Elliott Sprehn, Chris Wilson, 그리고 Jörn Zaefferer가 기술적 논의에 참여해 이 명세가 가능하도록 해주신 것에 감사를 표합니다.

특별히 Philip Jägenstedt와 David Vest에게는 미디어 세션의 모든 측면을 설계하는 데 도움을 주시고, 초기 설계 이슈를 해결하는 데 인내심을 보여주신 것에 깊이 감사드리며; Jer Noble에게는 iOS 오디오 포커스 모델과도 잘 작동하는 모델을 구축하는 데 도움을 주신 것에, 그리고 Mounir Lamouri와 Anton Vayvod에게는 이 명세가 실제로 만들어질 수 있도록 초기 참여와 피드백, 지원을 해주신 것에 감사드립니다.

적합성

문서 규약

적합성 요구 사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 이 문서의 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”과 같은 주요 단어들은 RFC 2119에 정의된 대로 해석되어야 합니다. 단, 가독성을 위해 이 명세에서는 이러한 단어들이 모두 대문자로 표기되어 있지 않습니다.

이 명세의 모든 텍스트는 규범적이며, 명시적으로 비규범적임을 표시한 섹션, 예시, 노트는 제외됩니다. [RFC2119]

이 명세의 예시는 “예를 들어(for example)”로 시작하거나, class="example"와 같이 규범적 텍스트와 구분되어 있습니다:

이것은 정보 제공용 예시입니다.

정보성 노트는 “Note”로 시작하며, class="note"와 같이 규범적 텍스트와 구분되어 있습니다:

Note, 이것은 정보 제공용 노트입니다.

적합 알고리즘

알고리즘의 일부로 명령형으로 표현된 요구 사항(예: "선행 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다")은 알고리즘을 소개할 때 사용된 주요 단어("must", "should", "may" 등)의 의미에 따라 해석되어야 합니다.

알고리즘 또는 특정 단계로 표현된 적합성 요구 사항은 최종 결과가 동일하다면 어떤 방식으로든 구현할 수 있습니다. 특히, 이 명세에서 정의된 알고리즘은 이해하기 쉽도록 설계된 것이며, 성능을 위한 것이 아닙니다. 구현자는 최적화를 권장합니다.

색인

이 명세에서 정의된 용어

참조로 정의된 용어

참고문헌

정규 참고문헌

[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MEDIACAPTURE-STREAMS]
Cullen Jennings; et al. Media Capture and Streams. 2025년 9월 11일. CRD. URL: https://www.w3.org/TR/mediacapture-streams/
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing Standard. Living Standard. URL: https://mimesniff.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 2025년 8월 6일. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

참고 참고문헌

[IMAGE-RESOURCE]
Aaron Gustafson; Rayan Kanso; Marcos Caceres. Image Resource. 2021년 6월 4일. WD. URL: https://www.w3.org/TR/image-resource/
[WEBAUDIO-1.1]
Paul Adenot; Hongchan Choi. Web Audio API 1.1. 2024년 11월 5일. FPWD. URL: https://www.w3.org/TR/webaudio-1.1/

IDL 색인

[Exposed=Window]
partial interface Navigator {
  [SameObject] readonly attribute MediaSession mediaSession;
};

enum MediaSessionPlaybackState {
  "none",
  "paused",
  "playing"
};

enum MediaSessionAction {
  "play",
  "pause",
  "seekbackward",
  "seekforward",
  "previoustrack",
  "nexttrack",
  "skipad",
  "stop",
  "seekto",
  "togglemicrophone",
  "togglecamera",
  "togglescreenshare",
  "hangup",
  "previousslide",
  "nextslide",
  "enterpictureinpicture",
  "voiceactivity"
};

enum MediaSessionEnterPictureInPictureReason {
  "other",
  "useraction",
  "contentoccluded"
};

callback MediaSessionActionHandler = undefined(MediaSessionActionDetails details);

[Exposed=Window]
interface MediaSession {
  attribute MediaMetadata? metadata;

  attribute MediaSessionPlaybackState playbackState;

  undefined setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);

  undefined setPositionState(optional MediaPositionState state = {});

  Promise<undefined> setMicrophoneActive(boolean active);

  Promise<undefined> setCameraActive(boolean active);

  Promise<undefined> setScreenshareActive(boolean active);
};

[Exposed=Window]
interface MediaMetadata {
  constructor(optional MediaMetadataInit init = {});
  attribute DOMString title;
  attribute DOMString artist;
  attribute DOMString album;
  attribute FrozenArray<object> artwork;
  [SameObject] readonly attribute FrozenArray<ChapterInformation> chapterInfo;
};

dictionary MediaMetadataInit {
  DOMString title = "";
  DOMString artist = "";
  DOMString album = "";
  sequence<MediaImage> artwork = [];
  sequence<ChapterInformationInit> chapterInfo = [];
};

[Exposed=Window]
interface ChapterInformation {
  readonly attribute DOMString title;
  readonly attribute double startTime;
  [SameObject] readonly attribute FrozenArray<MediaImage> artwork;
};

dictionary ChapterInformationInit {
  DOMString title = "";
  double startTime = 0;
  sequence<MediaImage> artwork = [];
};


dictionary MediaImage {
  required USVString src;
  DOMString sizes = "";
  DOMString type = "";
};

dictionary MediaPositionState {
  unrestricted double duration;
  double playbackRate;
  double position;
};

dictionary MediaSessionActionDetails {
  required MediaSessionAction action;
  double seekOffset;
  double seekTime;
  boolean fastSeek;
  boolean isActivating;
  MediaSessionEnterPictureInPictureReason enterPictureInPictureReason;
};