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. 재생 상태
play
및
pause
액션이 제대로 작동하려면, 사용자 에이전트는 브라우징 컨텍스트가 활성 미디어 세션에서 미디어를 재생 중인지 여부를
파악할 수 있어야 하며, 이를 추정 재생 상태라고 합니다. 추정 재생 상태를 결정하는 권장 방법은, 노드
문서의 브라우징 컨텍스트가 브라우징 컨텍스트인 미디어 요소들을 모니터링하는 것입니다. 브라우징 컨텍스트의 추정 재생 상태는
"playing"
이 해당 요소 중 하나라도 잠재적으로 재생 중이고 음소거되지 않았다면이고, 그렇지 않으면
"paused"
입니다. WebAudio와 플러그인과 같은 다른 정보도 고려해야 합니다.
playbackState
속성은 선언된
재생 상태를 브라우징 컨텍스트로부터 지정합니다. 이 상태는 추정 재생 상태와 결합되어 실제 재생 상태를
계산하며, 이는 play
및 pause
액션에 사용됩니다.
실제 재생 상태는 다음과 같이 계산됩니다:
playbackState
속성은 페이지가 미디어가 일시정지될 때 준비 단계를 수행하고 싶지만 pause
액션에 의해 준비 단계가 중단될 수 있도록 하는 경우에 유용할 수 있습니다. 예시는 Setting
playbackState를 참고하세요.
활성 미디어 세션의 실제 재생 상태가 변경될 때마다, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘을 실행해야 합니다.
4.2. 라우팅
사용자 에이전트에 여러 탭이 있을 수 있으므로 동시에 여러 MediaSession
객체가 존재할 수 있습니다. 각 탭에는 최상위 traversable과 하위 navigable이 포함될 수 있으며,
각 navigable마다 MediaSession
객체가 있을 수 있습니다.
사용자 에이전트는 MediaSession
객체 중 최대 하나만 사용자에게 표시하도록 선택해야 하며, 이를 활성 미디어 세션이라 합니다.
활성 미디어
세션은 null일 수도 있습니다. 선택 방법은 사용자 에이전트에 맡겨지며, 바람직하게는 사용자의 선호 경험을 기반으로 해야 합니다. playbackState
속성은 미디어 세션 라우팅에 영향을 주지 않아야 하며, 활성 미디어 세션에만 적용됩니다.
사용자 에이전트가 활성 미디어 세션을 선택할 때 오디오 포커스를 관리하는 것이 권장됩니다. 탭 또는 브라우징 컨텍스트가 현재 오디오를 재생 중이거나 사용자가 해당 미디어를 제어할 것으로 예상되면 오디오 포커스가 있다고 합니다. AudioFocus API는 이 영역을 목표로 하며, 완성되면 사용할 수 있습니다.
활성 미디어 세션이 변경될 때마다, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘과 메타데이터 업데이트 알고리즘을 실행해야 합니다.
4.3. 메타데이터
활성 미디어
세션의 미디어 메타데이터는 플랫폼 관례에 따라 플랫폼 UI에 표시될 수 있습니다. 활성
미디어 세션이 변경되거나 metadata
를 설정할 때마다
활성 미디어
세션에 대해, 사용자 에이전트는 메타데이터 업데이트
알고리즘을 반드시 실행해야 합니다. 단계는 다음과 같습니다:
- 활성 미디어 세션이 null인 경우, 플랫폼에 표시된 미디어 메타데이터를 해제하고, 이 단계를 종료합니다.
-
활성
미디어 세션의
metadata
가 빈 메타데이터라면, 플랫폼에 표시된 미디어 메타데이터를 해제하고 이 단계를 종료합니다. -
플랫폼에 표시된 미디어 메타데이터를 활성
미디어 세션의
metadata
와 일치하도록 업데이트합니다. - 사용자 에이전트가 아트워크 이미지를 표시하고 싶다면, 이미지 가져오기 알고리즘을 실행해야 합니다.
이미지 가져오기 알고리즘은 다음과 같습니다:
- 실행 중인 이미지 가져오기 알고리즘이 다른 것이 있다면, 기존 알고리즘 실행 인스턴스를 취소합니다.
-
활성
미디어 세션의 metadata의
artwork
가 비어 있으면, 이 단계를 종료합니다. -
플랫폼이 미디어 아트워크 표시를 지원한다면, metadata의
artwork
중에서 선호 아트워크 이미지를 선택합니다. - request를 새 request로 설정합니다. 이 URL은
선호
아트워크 이미지의
src
이고, destination 은 "image", mode는 "no-cors", credentials mode는 "include", 그리고 use-URL-credentials flag가 설정됨. -
Fetch request를 실행하며, fetch의 processResponse 알고리즘으로 다음 단계를 실행합니다.
- response를 fetch의 processResponse 알고리즘에서 제공된 response로 설정합니다.
-
response의 type이
"error"
가 아니면, response의 body를 이미지로 디코딩 시도합니다. - 이미지 형식이 지원된다면 해당 이미지를 플랫폼 UI에 표시할 아트워크로 사용합니다. 그렇지 않으면 이미지 가져오기 알고리즘 은 실패하고 종료합니다.
이미지 가져오기 알고리즘에서 이미지를 가져오지 못한 경우, 사용자 에이전트는 기본 이미지를 아트워크로 표시하는 등 대체 동작을 할 수 있습니다.
4.4. 액션
미디어 세션
액션은 페이지가 MediaSession
에서
사용자가 상호작용할 수 있도록 처리할 수 있는 액션입니다.
예를 들어, 페이지는 사용자가 헤드셋이나 기타 원격 장치의 버튼을 누를 때 트리거되는 액션을 처리할 수 있습니다.
미디어 세션 액션 소스는 미디어 세션 액션을 생성할 수 있는 소스입니다. 이러한 소스는 플랫폼이거나 사용자 에이전트가 생성한 UI 표면이 될 수 있습니다.
미디어 세션 액션 소스는 선택적으로 target을 가질 수 있으며, 이 target은 미디어 세션 액션 소스에서 생성된 미디어 세션 액션의 수신자가 되어야 합니다. 미디어 세션 액션 소스의 target이 null
이라면, 모든 미디어 세션 액션 소스의 액션의 수신자는 활성 미디어 세션이 됩니다.
미디어 세션
액션은 MediaSessionAction
으로
표현되며, 다음 값 중 하나를 가질 수 있습니다:
-
play
: 재생을 다시 시작하는 의도입니다. -
pause
: 현재 활성 재생을 일시정지하는 의도입니다. -
seekbackward
: 재생 시간을 짧은 시간(예: 몇 초) 뒤로 이동하는 의도입니다. -
seekforward
: 재생 시간을 짧은 시간(예: 몇 초) 앞으로 이동하는 의도입니다. -
previoustrack
: 재생에 시작점 개념이 있으면 현재 재생을 처음부터 시작하거나, 재생에 플레이리스트 개념이 있으면 이전 항목으로 이동하는 의도입니다. -
nexttrack
: 재생에 플레이리스트 개념이 있다면 다음 항목으로 이동하는 의도입니다. -
skipad
: 현재 재생 중인 광고를 건너뛰는 의도입니다. -
stop
: 재생을 중지하고 필요하면 상태를 초기화하는 의도입니다. -
seekto
: 재생 시간을 특정 시간으로 이동하는 의도입니다. -
togglemicrophone
: 사용자의 마이크를 음소거 또는 음소거 해제하는 의도입니다. -
togglecamera
: 사용자의 활성 카메라를 켜거나 끄는 의도입니다. -
togglescreenshare
: 사용자의 활성 화면 공유를 켜거나 끄는 의도입니다. -
hangup
: 통화를 종료하는 의도입니다. -
previousslide
: 슬라이드를 발표할 때 이전 슬라이드로 돌아가는 의도입니다. -
nextslide
: 슬라이드를 발표할 때 다음 슬라이드로 이동하는 의도입니다. -
enterpictureinpicture
: 미디어 세션을 화면 속 화면(picture-in-picture) 창에서 열려는 의도입니다. -
voiceactivity
: 마이크에서 음성 활동이 감지되었음을 웹 페이지에 알리는 의도입니다.
모든 MediaSession
에는
지원되는 미디어 세션 액션의 맵이 있습니다. 키는 미디어 세션 액션이고, 값은 MediaSessionActionHandler
입니다.
주어진 MediaSession
에서
action 및 handler 파라미터로
액션
핸들러 업데이트 알고리즘이 호출되면, 사용자 에이전트는 다음 단계를 반드시 수행해야 합니다:
-
handler가
null
이면,MediaSession
의 지원되는 미디어 세션 액션에서 action을 제거하고, 이 단계들을 중단합니다. -
MediaSession
의 지원되는 미디어 세션 액션에 action을 추가하고 handler와 연결합니다.
지원되는 미디어 세션 액션이 변경될 때, 사용자 에이전트는 미디어 세션 액션 업데이트 알고리즘을 실행해야 합니다. 사용자 에이전트는 같은 이벤트 루프에서 여러 액션이 수정될 때 UI 깜빡임을 피하기 위해 작업을 큐에 추가하여 미디어 세션 액션 업데이트 알고리즘을 실행할 수 있습니다.
사용자 에이전트가 source라는 미디어 세션 액션 소스로부터 action이라는 미디어 세션 액션이 트리거되었다는 알림을 받으면, 사용자 에이전트는 작업을 큐에 추가해야 하며, 사용자 상호작용 작업 소스를 이용하여 다음 미디어 세션 액션 처리 단계를 실행해야 합니다:
- session을 source의 target으로 설정합니다.
-
session이
null
이면, session을 활성 미디어 세션으로 설정합니다. -
session이
null
이면, 이 단계들을 중단합니다. - actions를 session의 지원되는 미디어 세션 액션으로 설정합니다.
- actions에 action 키가 없으면, 이 단계들을 중단합니다.
-
handler를 actions에서 action 키에 연결된
MediaSessionActionHandler
로 설정합니다. -
details 파라미터를
MediaSessionActionDetails
로 설정하여 handler를 실행합니다. - session과 연결된 브라우징 컨텍스트에서 활성화 알림 단계를 실행합니다.
사용자 에이전트가 play와 pause에 대한 합동 명령(예: 헤드셋 버튼 클릭)을 받으면, 작업을 큐에 추가하여 사용자 상호작용 작업 소스를 사용해 다음 단계를 실행해야 합니다:
-
활성 미디어
세션이
null
이면, 이 단계들을 중단합니다. - action을 미디어 세션 액션으로 설정합니다.
- 활성 미디어 세션의 실제 재생 상태가 playing이면, action을 pause로 설정합니다.
- 그렇지 않으면 action을 play로 설정합니다.
- action으로 미디어 세션 액션 처리 단계를 실행합니다.
play와 pause 미디어 세션 액션에 대해 활성 미디어 세션에 핸들러가 제공되지 않은 경우, 사용자 에이전트가 기본 핸들러를 구현하는 것이 권장됩니다.
togglemicrophone, togglecamera, togglescreenshare, hangup 미디어 세션 액션에 대해 활성 미디어 세션에 핸들러가 제공되지 않은 경우, 사용자 에이전트가 기본 핸들러를 구현할 수 있습니다.
사용자 에이전트는 MediaStreamTrack
의
muted
속성을 통해, togglemicrophone
,
togglecamera
,
togglescreenshare
미디어 세션
액션 외에도 마이크, 카메라, 화면 공유 상태를 웹 페이지에 노출할 수 있습니다.
이 경우, 사용자 에이전트는 MediaSessionActionHandler
를
먼저 실행한 뒤, 별도의 작업으로
트랙의 음소거 상태 설정 단계를 실행해야 합니다.
voiceactivity
액션 소스는 항상 문서에 live
마이크 MediaStreamTrack
가
있어야 합니다.
사용자 에이전트는 MediaSessionActionHandler
를
voiceactivity
에
대해, 마이크에서 하나 이상의 live
MediaStreamTrack
에서
음성 활동이 감지될 때만 호출해야 합니다.
사용자 에이전트는 마이크가 음소거되지 않았고 마이크에 연결된 모든 MediaStreamTrack
이
enabled
상태라면 음성 활동을 무시할 수 있습니다.
사용자 에이전트는 MediaSessionActionHandler
를
voiceactivity
에
대해 프라이버시 및 전력 효율 정책에 따라 최소 호출 간격을 설정하는 것이 권장됩니다.
voiceactivity
는
음성 활동의 시작만 나타냅니다.
사용자가 MediaStreamTrack
이
음소거된 상태에서 말을 하면 알림을 표시하거나, 오디오 처리를 위해 AudioWorklet
을
시작할 수 있습니다. 음성 활동이 끝나는 시점에 대한 동작은 정의되어 있지 않습니다.
다른 액션과 달리 사용자가 명시적으로 트리거하지 않고,
voiceactivity
는
사용자 에이전트 또는 시스템의 음성 활동 감지 알고리즘에 따라 다릅니다.
프라이버시 및 전력 효율을 위해 웹 페이지는 음성 활동이 끝나고 곧바로 다시 시작된 경우 알림을 받지 않을 수 있습니다. 마지막 voiceactivity
액션 이후 곧바로 다시 시작된 경우도 포함됩니다.
페이지는 미디어 세션
액션을 처리할 수 있을 때만 MediaSessionActionHandler
를
등록해야 합니다. 사용자 에이전트는 이를 지원되는 미디어 세션 액션으로 표시하고 미디어 세션 액션 소스를
업데이트합니다.
미디어 세션 액션 업데이트 알고리즘이 호출되면, 사용자 에이전트는 다음 단계를 반드시 실행해야 합니다:
- available actions를 미디어 세션 액션 배열로 설정합니다.
- 활성 미디어 세션이 null이면 available actions를 빈 배열로 설정합니다.
- 그렇지 않으면 available actions를 활성 미디어 세션의 지원되는 미디어 세션 액션의 키 목록으로 설정합니다.
- 각 미디어 세션 액션 소스 source에 대해 다음 하위 단계를 실행합니다:
4.5. 포지션 상태
사용자 에이전트는 플랫폼 관례에 따라 플랫폼 UI에서 현재 재생 위치와 재생 길이 를 표시할 수 있습니다. 포지션 상태 는 다음 요소의 조합입니다:
- 미디어의 재생 길이(초 단위)
- 미디어의 재생 속도. 계수(coefficient)입니다.
- 미디어의 마지막 보고된 재생 위치. 포지션 상태가 생성될 때의 미디어 재생 위치(초 단위)입니다.
포지션 상태는 MediaPositionState
로
표현되며,
반드시 마지막
위치 갱신 시간과 함께 저장되어야 합니다.
이는 포지션 상태가 마지막으로 갱신된
시간(초 단위)입니다.
포지션 상태를 결정하는 권장 방법은 노드 문서의 브라우징 컨텍스트가 브라우징 컨텍스트인 미디어 요소를 모니터링하는 것입니다.
실제 재생 속도는 다음과 같이 계산되는 계수입니다:
초 단위의 현재 재생 위치는 다음과 같이 계산됩니다:
- 경과 시간을 시스템 시간(초 단위)에서 마지막 위치 갱신 시간을 뺀 값으로 설정합니다.
- 경과 시간에 실제 재생 속도를 곱합니다.
- position을 경과 시간에 마지막 보고된 재생 위치를 더한 값으로 설정합니다.
- position이 0보다 작으면 0을 반환합니다.
- position이 재생 길이보다 크면 재생 길이를 반환합니다.
- position을 반환합니다.
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
객체는 특정 문서의 미디어 세션을 나타내며, 문서가 사용자 에이전트와 재생 및 처리 방법에 대한 정보를 소통할 수 있도록 합니다.
MediaSession
은 metadata
객체를 가지고 있으며, 이는 MediaMetadata
로
표현됩니다.
초기값은 null
입니다.
mediaSession
속성은
MediaSession
인스턴스를 반환해야 하며, 이는 Navigator
객체와 연관되어 있습니다.
metadata
속성은
MediaSession
의
metadata
를
반영합니다.
getter는 MediaSession
의
metadata
를
반환해야 합니다.
setter는 새 값을 value로 설정할 때 다음 단계를 수행해야 합니다:
-
MediaSession
의metadata
가null
이 아니면, media session을null
로 설정합니다. -
MediaSession
의metadata
를 value로 설정합니다. -
MediaSession
의metadata
가null
이 아니면, media session을 현재MediaSession
으로 설정합니다. - 병렬로 update metadata algorithm을 실행합니다.
playbackState
속성은 media
session의 선언된
재생 상태를 나타내며, 세션이 자신의 브라우징 컨텍스트에서 미디어를 재생 중인지 선언합니다. 초기값은 none입니다. setter는 새 값이 MediaSessionPlaybackState
의
유효한 값이라면 IDL 속성에 설정해야 합니다. getter는 마지막으로 설정된 유효한 값을 반환해야 합니다. playbackState
속성은 사용자 에이전트가 해당 브라우징 컨텍스트가 재생 중인지 일시정지 중인지 판단하는 힌트입니다.
playbackState
를
설정하면 실제 재생 상태가
변경될 수 있으며, 미디어 세션 액션 업데이트 알고리즘이 실행될 수 있습니다.
MediaSessionPlaybackState
열거형은 브라우징 컨텍스트가 미디어를 재생 중인지 여부를 나타내며, 값은 다음과 같이 설명됩니다:
-
none
: 브라우징 컨텍스트가 재생 중 또는 일시정지 중임을 명시하지 않음을 의미하며,playbackState
속성에서만 사용할 수 있습니다. -
playing
: 브라우징 컨텍스트가 현재 미디어를 재생 중이며 일시정지할 수 있음을 의미합니다. -
paused
: 브라우징 컨텍스트가 미디어를 일시정지했으며 다시 재생할 수 있음을 의미합니다.
setActionHandler(action, handler)
메서드를 호출하면, MediaSession
에서
action과 handler를 사용하여 액션 핸들러 업데이트 알고리즘을 실행해야 합니다.
setPositionState(state)
메서드를 호출하면
다음 단계를 수행해야 합니다:
- state가 빈 딕셔너리라면, 포지션 상태를 초기화하고 이 단계를 중단합니다.
- state의 duration 이 존재하지 않으면 TypeError를 throw합니다.
-
state의
duration
이 음수 또는NaN
이면 TypeError를 throw합니다. -
state의
position
이 존재하지 않으면 0으로 설정합니다. - state의 position 이 음수이거나 duration보다 크면, TypeError를 throw합니다.
- state의 playbackRate가 존재하지 않으면 1.0으로 설정합니다.
-
state의
playbackRate
가 0이면 TypeError를 throw합니다. - 포지션 상태와 마지막 위치 갱신 시간을 업데이트합니다.
setMicrophoneActive(active)
메서드는 페이지가 원하는
마이크 캡처 상태를 사용자 에이전트에 알립니다(예: 페이지가 더 이상 오디오를 통화로 전송하지 않기 때문에 마이크를 "비활성"으로 간주하면
setMicrophoneActive(false)
를 호출). 호출 시 다음 단계를 수행해야 합니다:
- document를 this의 관련 글로벌 객체의 연결된 Document로 설정합니다.
- captureKind를 "microphone"으로 설정합니다.
- 캡처 상태 업데이트 알고리즘을 document, active, captureKind로 실행한 결과를 반환합니다.
setCameraActive(active)
메서드도 페이지가 원하는 카메라 캡처 상태를 사용자 에이전트에 알리며, 호출 시 다음 단계를 수행해야 합니다:
- document를 this의 관련 글로벌 객체의 연결된 Document로 설정합니다.
- captureKind를 "camera"로 설정합니다.
- 캡처 상태 업데이트 알고리즘을 document, active, captureKind로 실행한 결과를 반환합니다.
setScreenshareActive(active)
메서드도 페이지가 원하는 화면 공유 캡처 상태를 사용자 에이전트에 알리며, 호출 시 다음 단계를 수행해야 합니다:
- document를 this의 관련 글로벌 객체의 연결된 Document로 설정합니다.
- captureKind를 "screenshare"로 설정합니다.
- 캡처 상태 업데이트 알고리즘을 document, active, captureKind로 실행한 결과를 반환합니다.
캡처 상태 업데이트 알고리즘은 document, active, captureKind로 호출될 때 다음 단계를 수행해야 합니다:
- document가 완전히 활성화된 상태가 아니면 거부된 promise를 InvalidStateError와 함께 반환합니다.
-
active가
true
이고 document의 visibility state가 "visible"이 아니면, 사용자 에이전트는 거부된 promise를 InvalidStateError와 함께 반환할 수 있습니다. - p를 새 promise로 설정합니다.
-
병렬로 다음 단계를 실행합니다:
-
applyPausePolicy를, 만약 사용자 에이전트가 UI에 따라 captureKind 타입의 모든 입력
소스 일시정지 정책을 구현한다면
true
로, 그렇지 않으면false
로 설정한다. -
applyPausePolicy가
true
라면, 다음 하위 단계를 실행한다:-
currentlyActive를, 사용자 에이전트가 현재 captureKind 타입의 모든 입력 소스 일시정지 중이면
false
로, 그렇지 않으면true
로 설정한다. -
active와 currentlyActive가 같다면, queue a task를 user interaction task source로 추가하여
p를
undefined
로 resolve하고 이 단계들을 중단한다. -
active가
true
라면, 사용자 에이전트는 예를 들어 사용자에게 프롬프트를 표시하기 위해 진행을 잠시 대기할 수 있다. - 사용자 에이전트가 캡처 상태 업데이트 요청을 거부하면, queue a task를 user interaction task source로 추가하여 p를 NotAllowedError로 reject하고 이 단계들을 중단한다.
-
currentlyActive를, 사용자 에이전트가 현재 captureKind 타입의 모든 입력 소스 일시정지 중이면
- 사용자 에이전트의 캡처 상태 UI를 captureKind와 active에 따라 업데이트한다.
-
Queue a task를 user interaction task source로 추가하여
p를
undefined
로 resolve한다. -
applyPausePolicy가
true
라면, 다음 하위 단계를 실행한다:-
newMutedState를, active가
false
면true
로, 그렇지 않으면false
로 설정한다. -
captureKind 타입의 소스를 가진 각
MediaStreamTrack
에 대해, queue a task를 user interaction task source로 추가하여 트랙의 음소거 상태 설정을 newMutedState로 실행한다.
-
newMutedState를, active가
-
applyPausePolicy를, 만약 사용자 에이전트가 UI에 따라 captureKind 타입의 모든 입력
소스 일시정지 정책을 구현한다면
- 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
과 연관된 메타데이터를 표현하며, 사용자 에이전트가 맞춤형 사용자 인터페이스를 제공하는 데 사용할 수 있습니다.
MediaMetadata
는 media session과 연관될 수 있습니다.
MediaMetadata
는 title,
artist, album을 가지며, 모두 DOMString 타입입니다.
MediaMetadata
는 artwork images라는 MediaImage
타입의 시퀀스를 가집니다.
또한 MediaMetadata
는
converted artwork images를 가지며, 초기값은 undefined
입니다.
MediaMetadata
는
chapter information이라는 리스트를 가집니다.
MediaMetadata
가 null
이거나 다음 모든 조건을 만족하면 empty metadata라고 합니다:
- title이 빈 문자열이다.
- artist가 빈 문자열이다.
- album이 빈 문자열이다.
- artwork images의 길이가
0
이다. - chapter information의 길이가
0
이다.
MediaMetadata(init)
생성자를 호출하면 다음 단계를 수행해야 합니다:
-
metadata를 새
MediaMetadata
객체로 설정합니다. -
metadata의
title
을 init의title
로 설정합니다. -
metadata의
artist
를 init의artist
로 설정합니다. -
metadata의
album
을 init의album
으로 설정합니다. -
init의
artwork
를 input으로 하여 convert artwork algorithm을 실행하고 성공하면 결과를 metadata의 artwork images로 설정합니다. -
chapters를
ChapterInformation
타입의 빈 리스트로 설정합니다. -
init의
chapterInfo
의 각 entry에 대해, create a ChapterInformation을 실행하여 chapters에 추가합니다. - metadata의 chapter information을 creating a frozen array의 결과로 설정합니다.
- metadata를 반환합니다.
input 파라미터로 convert artwork algorithm이 호출되면, input이 MediaImage
타입의 시퀀스일 때 사용자 에이전트는 다음 단계를 실행해야 합니다:
-
output을
MediaImage
타입의 빈 리스트로 설정합니다. -
input의 각 entry(
MediaImage
리스트)에 대해 다음 단계를 실행합니다:-
image를 새
MediaImage
로 설정합니다. - baseURL을 entry settings object에서 지정된 API base URL로 설정합니다.
-
entry의
src
를 baseURL로 Parse합니다. 실패가 아니면 image의src
를 반환값으로 설정합니다. 실패 시 TypeError를 throw하고 단계를 중단합니다. -
image의
sizes
를 entry의sizes
로 설정합니다. -
image의
type
을 entry의type
으로 설정합니다. - image를 output에 추가합니다.
-
image를 새
- output을 결과값으로 반환합니다.
title
속성은 MediaMetadata
의
title을 반영합니다.
getter는 MediaMetadata
의
title을 반환해야 하며,
setter는 MediaMetadata
의
title을 주어진 값으로
설정해야 합니다.
artist
속성은 MediaMetadata
의
artist를 반영합니다.
getter는 MediaMetadata
의
artist를 반환해야 하며,
setter는 MediaMetadata
의
artist를 주어진 값으로
설정해야 합니다.
album
속성은 MediaMetadata
의
album을 반영합니다.
getter는 MediaMetadata
의
album을 반환해야 하며,
setter는 MediaMetadata
의
album을 주어진 값으로
설정해야 합니다.
artwork
속성은 MediaMetadata
의
artwork images를 반영합니다. getter는 다음 단계를 실행해야 합니다:
-
MediaMetadata
의 converted artwork images가undefined
라면 다음 단계를 실행합니다:- frozenArtwork을 JavaScript Array 값으로 설정합니다.
-
MediaMetadata
의 artwork images의 각 entry에 대해 다음 단계를 실행합니다:- image를 converting to a JavaScript object entry의 결과로 설정합니다.
-
SetIntegrityLevel(image, "
frozen
")를 실행하여 스크립트에 의한 의도치 않은 변경을 방지합니다. - image를 frozenArtwork에 추가합니다.
-
SetIntegrityLevel(frozenArtwork, "
frozen
")을 실행합니다. -
MediaMetadata
의 converted artwork images를 frozenArtwork로 설정합니다.
-
MediaMetadata
의 converted artwork images를 반환합니다.
setter는 value를 새 값으로 설정할 때 다음 단계를 실행해야 합니다:
-
convertedArtwork을 converting value를
MediaImage
타입의 시퀀스로 변환한 결과로 설정합니다. -
convertedArtwork로 convert artwork algorithm을 실행하여 성공하면
MediaMetadata
의 artwork images로 결과값을 설정합니다. -
MediaMetadata
의 converted artwork images를undefined
로 설정합니다.
MediaMetadata
의
title, artist, album, artwork images가 수정될 때 사용자 에이전트는 다음 단계를 실행해야 합니다:
- 인스턴스에 media session이 연관되지 않았다면 단계를 중단합니다.
-
그렇지 않으면 작업을 큐에 추가하여 다음 하위 단계를 실행합니다:
- 인스턴스가 더 이상 media session과 연관되지 않으면 단계를 중단합니다.
- 그렇지 않으면 병렬로 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 = 0;
startTime sequence <MediaImage >= []; };
artwork
ChapterInformation
객체는 개별 챕터에 대한 메타데이터(섹션 제목, 해당 타임스탬프, 해당 섹션의 스크린샷 이미지 데이터 등)를 표현하며, 사용자 에이전트가 맞춤형 사용자 인터페이스를 제공하는 데 사용할 수
있습니다.
ChapterInformation
은 title이라는 DOMString 값을 가집니다.
ChapterInformation
은
startTime이라는 double 값을 가집니다.
ChapterInformation
은
artwork images 리스트를 가집니다.
create a
ChapterInformation
을 init으로 생성하려면, 다음 단계를 실행합니다:
-
chapterInfo를 새
ChapterInformation
객체로 설정합니다. -
chapterInfo의
title
을 init의title
로 설정합니다. -
chapterInfo의
startTime
을 init의startTime
으로 설정합니다. startTime이 음수이거나 duration보다 크면 TypeError를 throw합니다. -
artwork
에 init의artwork
를 input으로 하여 convert artwork algorithm을 실행한 결과를 저장합니다. -
chapterInfo의 artwork
images를
artwork
에서 creating a frozen array의 결과로 설정합니다. - chapterInfo를 반환합니다.
title
속성은 ChapterInformation
의
title을
반영합니다. getter는 ChapterInformation
의
title을
반환해야 합니다.
startTime
속성은 ChapterInformation
의
startTime(초 단위)을 반영합니다. getter는 ChapterInformation
의
startTime을 반환해야 합니다.
artwork
속성은 ChapterInformation
의
artwork 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 ; };
MediaSessionActionHandler
는 details 파라미터(딕셔너리 타입 MediaSessionActionDetails
)와
함께 실행되어야 합니다.
action
딕셔너리
멤버는 MediaSessionActionHandler
가
연관된 media session
action을 지정합니다.
seekOffset
딕셔너리 멤버는 media session
action이 seekbackward
또는 seekforward
일
때 제공될 수 있습니다.
재생 시간을 이동할 초 단위 시간이며, 있으면 항상 양수여야 합니다. 제공되지 않으면 사이트가 적절한 시간(예: 몇 초)을 선택해야 합니다.
media
session action이 seekto
일
때:
-
seekTime
딕셔너리 멤버는 반드시 제공되어야 하며, 재생 시간을 이동할 초 단위 시간입니다. -
fastSeek
딕셔너리 멤버는 제공될 수 있으며, action이 여러 번 연속 호출되고 마지막 호출이 아닌 경우 true입니다.
isActivating
딕셔너리 멤버는 사용자 에이전트가 모든 입력 소스 일시정지를 수행할 예정이면
false
이고, 그렇지 않으면 true
입니다. 사용자 에이전트가 모든 입력 소스 일시정지 정책을 구현하고 media session
action이 togglecamera
,
togglemicrophone
또는 togglescreenshare
인
경우 이 딕셔너리 멤버는 반드시 존재해야 합니다.
media
session action이 enterpictureinpicture
일
때,
enterPictureInPictureReason
딕셔너리 멤버는 반드시 제공되어야 하며, UA가 이 액션을 트리거한 이유입니다.
enterPictureInPictureReason
는 다음 값 중 하나를 가질 수 있습니다:
-
other
: picture-in-picture에 들어가는 이유가 기존 열거형 값에 속하지 않음 -
useraction
: 사용자가 명시적으로 picture-in-picture 버튼(예: 사용자 에이전트 UI에서 picture-in-picture 버튼 클릭)을 눌러 진입함 -
contentoccluded
: 페이지가 가려져서(탭 전환, 탭 최소화 등) 사용자 에이전트가 picture-in-picture를 요청함
11. 권한 정책 통합
이 명세는 "mediasession"이라는 문자열로 식별되는 정책 제어 기능을 정의합니다. 기본 허용 목록은 *입니다.
문서의 권한 정책은 해당 문서의 콘텐츠가 MediaSession API를 사용할 수 있는지 결정합니다. 문서에서 비활성화된 경우, 사용자 에이전트는 해당 문서의 미디어 세션을 활성 미디어 세션으로 선택하지 않아야 합니다.
12. 예시
이 섹션은 비규범적입니다.
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"
가 우선적으로 선택됩니다.
오디오북의 플레이리스트나 챕터에서는 여러 미디어 요소가 하나의 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);
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(); }
// 미디어가 로드되면, 길이(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++ ; // 현재 슬라이드 설정. 구현 생략. });
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에게는 이 명세가 실제로 만들어질 수 있도록 초기 참여와 피드백, 지원을 해주신 것에 감사드립니다.