MediaStream 녹화

W3C 작업 초안,

이 문서에 대한 자세한 정보
이 버전:
https://www.w3.org/TR/2026/WD-mediastream-recording-20260316/
최신 공개 버전:
https://www.w3.org/TR/mediastream-recording/
편집자 초안:
https://w3c.github.io/mediacapture-record/
이전 버전:
이력:
https://www.w3.org/standards/history/mediastream-recording/
피드백:
public-webrtc@w3.org (제목란 “[mediastream-recording] … message topic …”) (아카이브)
GitHub
편집자:
(Google Inc.)
이전 편집자:
Jim Barnett (Genesis)
(Microsoft Corp.)
참여:
메일링 리스트
GitHub 저장소 (새 이슈, 열린 이슈)
구현:
Media Recording 사용 가능 여부?
Chromium 인코드 가속 지원

요약

이 문서는 MediaStream과 함께 사용하기 위한 녹화 API를 정의한다.

이 문서의 상태

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

본 문서는 웹 실시간 통신 작업 그룹에서 권고 작업 경로를 사용하여 워킹 드래프트로 발행되었습니다. 이 문서는 W3C 권고가 되는 것을 목표로 합니다.

본 문서에 대해 의견이 있으신 경우, public-webrtc@w3.org ( 구독, 아카이브)로 보내주시기 바랍니다. 이메일을 보낼 때에는 제목에 “mediastream-recording”이라는 문구를 포함해주세요. 예를 들면 다음과 같이 작성하시면 됩니다: “[mediastream-recording] …의견 요약…”. 모든 의견은 환영합니다.

워킹 드래프트로 발행되었다고 해서 W3C 및 회원의 승인을 의미하지는 않습니다. 이 문서는 초안 문서이므로 언제든 수정, 대체 또는 폐기될 수 있습니다. 이 문서를 진행 중인 작업 외의 목적으로 인용하는 것은 적절하지 않습니다.

본 문서는 W3C 특허 정책에 따라 운영되는 그룹이 작성하였습니다. W3C는 해당 그룹 발행물과 관련하여 공개된 특허 고지 목록을 관리하고 있으며, 그 페이지에는 특허 공개를 위한 안내도 포함되어 있습니다. 만약 필수 청구항이 포함되었다고 믿을 만한 특허에 대해 실제로 알고 있다면, W3C 특허 정책 6항에 따라 그 정보를 공개해야 합니다.

본 문서는 2025년 8월 18일 W3C 프로세스 문서를 따릅니다.

1. 개요

이 API는 기본 녹화를 매우 간단하게 만드는 한편, 더 복잡한 사용 사례도 지원할 수 있도록 설계되었다. 가장 단순한 경우에는 애플리케이션이 MediaRecorder 객체를 생성하고 start() 를 호출한 다음 stop() 를 호출하거나 MediaStreamTrack(들) [GETUSERMEDIA] 이 종료될 때까지 기다린다. 녹화 내용은 플랫폼의 기본 인코딩으로 ondataavailable 이벤트를 통해 제공된다. 플랫폼에서 사용 가능한 인코딩 집합을 질의하고, 필요하다면 원하는 인코딩을 선택할 수 있는 기능이 제공된다. 애플리케이션은 또한 한 번에 얼마나 많은 데이터를 받을지 선택할 수 있다. 기본적으로 전체 녹화를 포함하는 Blob 이 녹화가 끝날 때 반환된다. 그러나 애플리케이션은 정기적으로 더 작은 버퍼의 데이터를 받도록 선택할 수 있다.

2. Media Recorder API

[Exposed=Window]
    interface MediaRecorder : EventTarget {
    constructor(MediaStream stream, optional MediaRecorderOptions options = {});
    readonly attribute MediaStream stream;
    readonly attribute DOMString mimeType;
    readonly attribute RecordingState state;
    attribute EventHandler onstart;
    attribute EventHandler onstop;
    attribute EventHandler ondataavailable;
    attribute EventHandler onpause;
    attribute EventHandler onresume;
    attribute EventHandler onerror;
    readonly attribute unsigned long videoBitsPerSecond;
    readonly attribute unsigned long audioBitsPerSecond;
    readonly attribute BitrateMode audioBitrateMode;

    undefined start(optional unsigned long timeslice);
    undefined stop();
    undefined pause();
    undefined resume();
    undefined requestData();

    static boolean isTypeSupported(DOMString type);
    };
    

2.1. 생성자

MediaRecorder(MediaStream stream, optional MediaRecorderOptions options = {})
MediaRecorder() 생성자가 호출되면, User Agent는 다음 단계를 반드시 실행해야 한다:
  1. stream을 생성자의 첫 번째 인자로 설정한다.
  2. options를 생성자의 두 번째 인자로 설정한다.
  3. typeoptionsmimeType으로 설정한다.
  4. is type supportedtype과 true 값으로 호출했을 때 false를 반환하면, NotSupportedError DOMException 을 발생시키고 이 단계를 중단한다.
  5. recorder를 새로 생성한 MediaRecorder 객체로 설정한다.
  6. recorder[[ConstrainedMimeType]] 내부 슬롯을 생성하고, optionsmimeType 멤버의 값으로 초기화한다.
  7. recorder[[ConstrainedBitsPerSecond]] 내부 슬롯을 생성하고, optionsbitsPerSecond 멤버값이 있으면 그 값으로, 없으면 null로 초기화한다.
  8. recorder[[VideoKeyFrameIntervalDuration]] 내부 슬롯을 생성하고, optionsvideoKeyFrameIntervalDuration 멤버값이 있으면 그 값으로, 없으면 null로 초기화한다.
  9. recorder[[VideoKeyFrameIntervalCount]] 내부 슬롯을 생성하고, optionsvideoKeyFrameIntervalCount 멤버값이 있으면 그 값으로, 없으면 null로 초기화한다.
  10. recorderstream 속성을 stream으로 초기화한다.
  11. recordermimeType 속성을 recorder[[ConstrainedMimeType]] 슬롯의 값으로 초기화한다.
  12. recorderstate 속성을 inactive로 초기화한다.
  13. recordervideoBitsPerSecond 속성을 optionsvideoBitsPerSecond 멤버의 값으로 초기화한다 (존재하면). 그렇지 않으면 User Agent가 동영상에 적당하다고 판단하는 값으로 선택한다.
  14. recorderaudioBitsPerSecond 속성을 optionsaudioBitsPerSecond 멤버의 값으로 초기화한다 (존재하면). 그렇지 않으면 User Agent가 오디오에 적당하다고 판단하는 값으로 선택한다.
  15. 만약 recorder[[ConstrainedBitsPerSecond]] 슬롯이 null이 아니면, recordervideoBitsPerSecondaudioBitsPerSecond 속성값을 각 미디어 타입에 대해 User Agent가 적합하다고 판단하는 값으로 설정하되, 두 값의 합이 recorder[[ConstrainedBitsPerSecond]] 슬롯값에 가깝게 한다.
  16. recorderBitrateMode (즉 optionsaudioBitrateMode 멤버값) 를 지원하면, recorderaudioBitrateMode 속성에 optionsaudioBitrateMode 값을 할당한다. 그렇지 않으면 recorderaudioBitrateMode 속성값을 "variable"로 초기화한다.
  17. recorder를 반환한다.

2.2. 속성

stream, 유형 MediaStream, 읽기 전용
기록할 MediaStream [GETUSERMEDIA]입니다.
mimeType, 유형 DOMString, 읽기 전용
MediaRecorder 객체에서 사용하는 MIME 타입 [RFC2046]입니다. UA는 기록을 지원하는 모든 MIME 타입 재생이 가능해야 하며, 예를 들어 기록된 영상을 HTML <video> 태그에서 표시할 수 있어야 합니다.
mimeType 은 type/subtype 조합을 통해 녹화의 미디어 유형 및 컨테이너 포맷을 지정하며, 명확성이 필요한 경우 코덱 및/또는 프로필 파라미터 [RFC6381] 도 포함될 수 있습니다. 개별 코덱들은 추가적인 선택적 특정 파라미터를 가질 수 있습니다.
state, 유형 RecordingState, 읽기 전용
MediaRecorder 객체의 현재 상태입니다.
onstart, 유형 EventHandler
start 이벤트를 처리할 때 호출됩니다.
onstop, 유형 EventHandler
stop 이벤트를 처리할 때 호출됩니다.
ondataavailable, 유형 EventHandler
dataavailable 이벤트를 처리할 때 호출됩니다. 이 이벤트에는 기록된 데이터의 Blob 이 포함되어 있고, data 속성으로 접근할 수 있습니다.
onpause, 유형 EventHandler
pause 이벤트를 처리할 때 호출됩니다.
onresume, 유형 EventHandler
resume 이벤트를 처리할 때 호출됩니다.
onerror, 유형 EventHandler
ErrorEvent를 처리할 때 호출됩니다.
videoBitsPerSecond, 유형 unsigned long, 읽기 전용
비디오 트랙 인코딩 시 사용할 목표 비트레이트입니다.
audioBitsPerSecond, 유형 unsigned long, 읽기 전용
오디오 트랙 인코딩 시 사용할 목표 비트레이트입니다.
audioBitrateMode, 유형 BitrateMode, 읽기 전용
오디오 트랙 인코딩에 사용되는 BitrateMode입니다.

2.3. 메서드

역사적인 이유로, 아래 메서드들은 state를 동기적으로 변경하며 이벤트는 비동기적으로 발생시킨다.
start(optional unsigned long timeslice)
MediaRecorder 객체의 start() 메서드가 호출되면, UA는 다음 단계를 반드시 실행해야 한다:
  1. recorder를 메서드가 호출된 MediaRecorder 객체로 설정한다.
  2. timeslice를 메서드의 첫 번째 인자가 존재하면 해당 값으로, 아니면 undefined로 설정한다.
  3. streamrecorderstream 속성 값으로 설정한다.
  4. tracksstream트랙 집합live 트랙들의 집합으로 설정한다.
  5. recorderstate 속성 값이 inactive가 아니면, InvalidStateError DOMException 을 throw 하며, 이 단계를 중단한다.
  6. isolation 속성stream에 대해 recorder의 접근을 허용하지 않으면, SecurityError DOMException 을 throw 하며, 이 단계를 중단한다.
  7. streaminactive 상태이면, NotSupportedError DOMException 을 throw 하며, 이 단계를 중단한다.
  8. [[ConstrainedMimeType]] 슬롯에 미디어 종류, 컨테이너, 코덱이 지정된 경우, recorder의 설정을 해당 값에 맞게 제한한다.
  9. recorder[[ConstrainedBitsPerSecond]] 슬롯이 null이 아니면, recordervideoBitsPerSecondaudioBitsPerSecond 속성을 각각 대응하는 미디어 타입에 대해 UA가 적절하다고 판단하는 값으로 설정하고, tracks의 모든 트랙에 대해 videoBitsPerSecondaudioBitsPerSecond 의 합이 recorder[[ConstrainedBitsPerSecond]] 값에 가까워지도록 한다.
  10. videoBitraterecordervideoBitsPerSecond 속성 값으로 설정하고, recorder의 설정을 모든 비디오 트랙에 대해 videoBitrate 비트/초의 총 비트레이트를 목표로 제한한다. videoBitrate는 인코더에게 제시하는 힌트이며 실제로 초과될 수도, 달성되지 않을 수도, 오랜 시간 뒤에야 달성될 수도 있다.
  11. audioBitraterecorderaudioBitsPerSecond 속성 값으로 설정하고, recorder의 설정을 모든 오디오 트랙에 대해 audioBitrate 비트/초의 총 비트레이트를 목표로 제한한다. audioBitrate 역시 인코더에 대한 힌트이며 실제로 초과 또는 달성 실패, 지연 달성 가능성이 있다.
  12. videoKeyFrameIntervalDurationrecorder.[[VideoKeyFrameIntervalDuration]] 값으로, videoKeyFrameIntervalCountrecorder.[[VideoKeyFrameIntervalCount]] 값으로 설정한다. UA는 다음 규칙에 따라 recorder의 설정을 조정해야 한다:
    • videoKeyFrameIntervalDurationnull이 아니고, videoKeyFrameIntervalCountnull이면, 마지막 키 프레임 이후 videoKeyFrameIntervalDuration 밀리초가 지나고 도착한 첫 프레임에 인코더가 키프레임을 생성한다.
    • videoKeyFrameIntervalCountnull이 아니고, videoKeyFrameIntervalDurationnull이면, 마지막 키 프레임 이후 videoKeyFrameIntervalCount 프레임 이후 도착한 첫 프레임에서 인코더가 키프레임을 생성한다.
    • 둘 다 null이 아니면, NotSupportedError DOMException 을 throw 하며, 이 단계를 중단한다.
    • 둘 다 null이면, UA는 필요에 따라 키 프레임을 생성할 수 있다.

    인코더는 상황에 따라 독립적으로 키프레임 생성 여부를 결정할 수 있다.

  13. recorderBitrateMode 값을 이용해 모든 오디오 트랙의 인코딩 방식을 제한한다.
  14. tracks의 각 트랙에 대해, User Agent가 현재 설정으로 해당 트랙을 기록할 수 없다면, NotSupportedError DOMException 을 throw 하며, 이 단계를 중단한다.
  15. recorderstaterecording으로 설정하고, 다음 단계를 병렬로 실행한다:
    1. 녹화에 사용할 컨테이너와 코덱이 아직 완전히 지정되지 않았다면, UA가 recorder의 현재 설정에서 이를 지정한다. UA는 tracks 내 트랙들의 소스를 고려해 컨테이너/코덱을 고를 수 있다.
      tracks 내 트랙 소스를 이용해, 원격 트랙(RTCPeerConnection 등)인 경우 동일한 코덱을 선택함으로써 재인코딩을 피할 수 있다. 단, RTP 스트림 코덱이 녹화 중에 변경되면 UA는 끊김 방지를 위해 재인코딩해야 한다.
    2. User Agent가 지정된 미디어 타입/서브타입, 코덱, 컨테이너 조합을 지원하지 않으면, 남은 단계를 중단하고, DOM 조작 태스크 소스를 이용해 태스크를 큐에 추가하여 다음 단계 실행:
      1. 녹화기 비활성화recorder에 수행
      2. 에러 이벤트 NotSupportedErrorrecorder에서 발생시킨다.
      3. stop 이벤트를 recorder 에서 발생시킨다.
    3. recorder의 현 설정을 이용해 tracks 내 모든 트랙을 녹화하고 데이터를 Blob blob에 수집한다. DOM 조작 태스크 소스에서 다음 단계를 큐에 추가:
      1. extendedMimeTyperecorder[[ConstrainedMimeType]] 값으로 설정한다.
      2. MediaRecorder가 사용한 설정 반영 미디어 타입, 서브타입, 코덱 등의 파라미터를 extendedMimeType에 추가(이미 없을 시에 한함). [RFC6381] profile 파라미터나 추가 코덱별 파라미터 포함될 수 있음.
      3. recordermimeType 속성을 extendedMimeType로 지정.
      4. start 이벤트를 recorder 에서 발생시킴.
    4. 녹화 도중 언젠가 streamisolation 속성이 변경되어 MediaRecorder 의 접근이 더 이상 허용되지 않으면, UA는 데이터 수집을 중단 및 폐기하고, 다음 단계를 DOM 조작 태스크 소스에 큐잉한다.
      1. 녹화기 비활성화recorder에 수행
      2. SecurityError 이벤트를 recorder 에서 발생시킴
      3. dataavailable 블롭 이벤트를 recorder, blob과 함께 발생시킴
      4. stop 이벤트를 recorder 에서 발생시킴
    5. 녹화 도중 stream트랙 집합에 트랙이 추가 또는 제거되면, UA는 데이터 수집을 중단하고, 다음 단계를 큐잉한다:
      1. 녹화기 비활성화recorder에 수행
      2. InvalidModificationError 이벤트를 recorder에서 발생시킴
      3. dataavailable 블롭 이벤트를 recorder, blob과 함께 발생시킴
      4. stop 이벤트를 recorder 에서 발생시킴
    6. UA가 isolation 속성이나 stream의 트랙 집합 이외의 이유로 데이터 수집을 지속할 수 없게 되면, UA는 다음 단계를 큐잉한다:
      1. 녹화기 비활성화recorder에 수행
      2. UnknownError 이벤트를 recorder에서 발생시킴
      3. dataavailable 블롭 이벤트를 recorder, blob과 함께 발생시킴
      4. stop 이벤트를 recorder 에서 발생시킴
    7. timesliceundefined가 아니면, 최소 timeslice 밀리초 데이터가 수집되었거나 UA가 정한 최소값이 충족되면, 새 Blob blob에 데이터를 수집하고, DOM 조작 태스크 소스에서 dataavailable 블롭 이벤트를 recorder, blob과 함께 발생시킬 태스크를 큐잉한다.

      timesliceundefined이면 가장 큰 unsigned long 값으로 간주됨.

    8. 모든 녹화 트랙이 ended 상태가 되면, 데이터 수집을 중단하고, 다음 단계를 큐잉한다:
      1. 녹화기 비활성화recorder에 수행
      2. dataavailable 블롭 이벤트를 recorder, blob과 함께 발생시킴
      3. stop 이벤트를 recorder 에서 발생시킴

참고로 stop(), requestData(), 그리고 pause() 역시 녹화 동작에 영향을 준다.

UA는 stream을 재생 시 원 트랙을 복구할 수 있도록 기록해야 한다. Blob이 여러 개 반환되는 경우(timeslice 또는 requestData()로 인해), 개별 Blob은 재생 불가여도 되나, 모든 Blob을 합치면 최종 결과는 반드시 재생 가능해야 한다.

MediaStream 내 어떤 트랙이 muted 되거나 enabled 가 아니면, UA는 해당 트랙의 출력(검은 프레임, 무음)만 기록한다.

녹화기 비활성화 알고리즘, recorder가 주어지면, 아래를 따른다:

  1. recordermimeType 속성을 [[ConstrainedMimeType]] 슬롯의 값으로 지정한다.
  2. recorderstate 속성을 inactive로 지정한다.
  3. recorder[[ConstrainedBitsPerSecond]] 슬롯이 undefined가 아니면, recordervideoBitsPerSecondaudioBitsPerSecond 속성을 각각 적절한 값으로 지정한다. 두 속성의 합이 recorder[[ConstrainedBitsPerSecond]] 값에 가깝도록 한다.
stop()
MediaRecorder 객체의 stop() 메서드가 호출되면, UA는 다음 단계를 반드시 실행한다:
  1. recorder를, 해당 메서드가 호출된 MediaRecorder 객체로 설정한다.
  2. recorderstate 속성이 inactive이면, 이후 단계를 중단한다.
  3. 녹화기 비활성화recorder에 실행한다.
  4. DOM 조작 태스크 소스를 사용하여 아래 단계를 실행하는 태스크를 큐에 추가한다:
    1. 데이터 수집을 중단한다.
    2. 지금까지 수집된 데이터 Blob을 blob으로 하고, dataavailable이라는 블롭 이벤트를 recorderblob과 함께 발생시킨다.
    3. stop이라는 이벤트를 recorder에 발생시킨다.
  5. undefined를 반환한다.
pause()
MediaRecorder 객체의 pause() 메서드가 호출되면, UA는 반드시 다음 단계를 실행한다:
  1. stateinactive이면, InvalidStateError DOMException 을 throw 하며, 이 단계를 중단한다.
  2. statepaused라면, 이 단계를 중단한다.
  3. statepaused로 지정하고, DOM 조작 태스크 소스에서 다음 단계를 실행한다:
    1. blob에 데이터 수집을 중단한다 (단, 미래의 녹화 재개가 가능하도록 해당 blob은 유지).
    2. target을 MediaRecorder 컨텍스트 객체로 두고, pause 이벤트를 target에 발생시킨다.
  4. undefined를 반환한다.
resume()
MediaRecorder 객체의 resume() 메서드가 호출되면, UA는 다음 단계를 반드시 실행한다:
  1. stateinactive이면, InvalidStateError DOMException 을 throw 하며, 이 단계를 중단한다.
  2. staterecording이면, 이 단계를 중단한다.
  3. staterecording으로 지정하고, DOM 조작 태스크 소스에서 다음 실행:
    1. 현재 blob에 데이터 수집 다시(계속) 시작
    2. target을 MediaRecorder 컨텍스트 객체로 하고, resume 이벤트를 target에 발생시킨다.
  4. undefined를 반환한다.
requestData()
MediaRecorder 객체의 requestData() 메서드가 호출되면, UA는 다음 단계를 반드시 실행한다:
  1. stateinactive 이면, InvalidStateError DOMException 을 throw 하며 현재 단계를 종료한다. 아니면 UA는 다음을 DOM 조작 태스크 소스에서 큐잉한다:
    1. blobBlob 기반 현재까지 수집한 데이터로 설정, targetMediaRecorder 컨텍스트 객체로 하여, dataavailable 이벤트를 target, blob과 함께 발생시킨다. (아직 데이터가 없다면 blob은 빈 값일 수 있음.)
    2. 새 Blob을 생성해 이후 데이터는 새 Blob에 저장.
  2. undefined를 반환한다.
isTypeSupported(DOMString type)
이 메서드는 더 이상 사용을 권장하지 않는다. 과거 호환성 유지를 위한 것임. 동기적으로 노출된 코덱 식별자 목록은: "vp8", "vp9", "h264" 또는 "avc1", "av1" 또는 "av01", "hvc1", "hev1", "avc1", "avc3", "opus", "pcm".
하드웨어 지원 감지의 어려움으로 인해, 정확한 답변은 타이밍에 따라 달라질 수 있다. 사용자는 MediaCapabilities 를 통해 프로필 레벨이나 최신 코덱을 확인해야 한다:
const {supported} = await navigator.mediaCapabilities.encodingInfo({
    type: "record",
    video: {
        contentType: "video/webm;codecs=av01.0.19M.08",
        width: 640,
        height: 480,
        framerate: 30,
        bitrate: 300000,
    }
    });
    
MediaRecorder 가 지정된 MIME 타입으로 녹화할 수 있는지 확인한다. true를 반환하더라도 단지 MediaRecorder 구현이 해당 MIME으로 Blob 객체를 기록할 수 있다는 뜻일 뿐이다. 실제 녹화는 미디어 인코딩을 위한 충분한 자원이 없으면 실패할 수 있다. 이 메서드가 호출되면 UA는 is type supported 알고리즘을, 첫 인자를 넘기고 false 값과 함께 실행한다.
is type supported 알고리즘은 type과 불린 deferNewerCodecsCheck 인자를 받아 다음을 따른다.
  1. type이 빈 문자열이면 true를 반환한다 (이 경우 UA가 컨테이너/코덱 모두 자유롭게 선택).
  2. type이 유효한 MIME 타입 문자열이 아니면 false 반환.
  3. MediaRecorder가 지정된 미디어 타입/서브타입/컨테이너 조합을 지원하지 않으면 false 반환.
  4. codecStrings리스트 형태로, type에서 codecs= 뒤를 ","엄격하게 분할해서 구하고, 없으면 빈 리스트로 한다.
  5. codecStrings에 오디오/비디오 각각 코덱이 2개 이상 있으면 false 반환.
  6. codecIdentifiers는 빈 리스트로 한다.
  7. codecStrings의 각 codecString에 대해:
    1. codecIdentifiercodecString"."분할 후 첫 부분을 ASCII 소문자로 변환하여 얻는다.
    2. codecIdentifiers 리스트에 codecIdentifier 추가.
    나머지 알고리즘에서는 코덱 표기에서 점 이후 부분(예: "av01.0.19M.08")은 무시하고, 앞 부분(예: "av01")로 식별자 판단만 한다.
  8. codecIdentifiers의 각 codecIdentifier에 대해, 동기 노출 여부 검사:
    1. MediaRecorder가 type에 지정된 미디어 타입/서브타입 및 컨테이너와 codecIdentifier의 조합을 지원하지 않으면, false를 반환한다.
  9. codecIdentifiers에 동기적으로 노출되지 않은 코덱이 하나라도 있으면, deferNewerCodecsCheck를 반환.
  10. true 반환.
is synchronously exposed 알고리즘은 codecIdentifier가 주어졌을 때 다음 단계로 구성된다:
  1. 동기적으로 노출된 코덱 식별자 목록 중 어느 항목이라도 codecIdentifier와 정확히 일치하면 true를 반환하고, 아니라면 false를 반환한다.

2.4. 데이터 처리

blob 이벤트를 발생시키다Blob blob을(를) 사용하여 이벤트를 발생시키는 것을 의미한다. 이때 target에서 BlobEvent를 사용하고, data 속성은 blob으로 초기화된다.

일반적으로 blob은 UA가 마지막으로 recording state 상태로 전환한 이후 수집한 데이터가 된다.

2.5. MediaRecorderOptions

dictionary MediaRecorderOptions {
    DOMString mimeType = "";
    unsigned long audioBitsPerSecond;
    unsigned long videoBitsPerSecond;
    unsigned long bitsPerSecond;
    BitrateMode audioBitrateMode = "variable";
    DOMHighResTimeStamp videoKeyFrameIntervalDuration;
    unsigned long videoKeyFrameIntervalCount;
    };
    

2.5.1. 멤버

mimeType, 타입 DOMString, 기본값 ""
녹화를 위한 컨테이너 및 코덱 포맷 [RFC2046]이며, 포맷에 정의된 파라미터도 포함될 수 있다.
mimeType 은 녹화를 위한 미디어 타입과 컨테이너 포맷을 type/subtype 조합으로 지정하며, 필요 시 코덱 및/또는 프로파일 파라미터 [RFC6381] 도 명시된다. 개별 코덱은 추가로 옵션 또는 필수 파라미터를 가질 수 있다.
audioBitsPerSecond, 타입 unsigned long
오디오 트랙(들) 인코딩의 목표 총 비트레이트(비트/초, bps).
videoBitsPerSecond, 타입 unsigned long
비디오 트랙(들) 인코딩의 목표 총 비트레이트(비트/초, bps).
bitsPerSecond, 타입 unsigned long
모든 비디오 및 오디오 트랙 인코딩의 목표 총 비트레이트(비트/초, bps). 이 멤버는 audioBitsPerSecond 또는 videoBitsPerSecond 값을 대체하며, 실제 분배는 UA가 결정할 수 있다.
audioBitrateMode, 타입 BitrateMode, 기본값 "variable"
오디오 트랙 인코딩에 사용할 BitrateMode 를 지정한다.
videoKeyFrameIntervalDuration, 타입 DOMHighResTimeStamp
인코딩된 비디오 스트림에서 키프레임 간의 예상 시간(밀리초 단위) 간격을 지정. UA는 이 값과 videoKeyFrameIntervalCount 를 함께 고려해 키프레임 생성을 제어한다.
videoKeyFrameIntervalCount, 타입 unsigned long
인코딩된 비디오 스트림에서 키프레임 사이의 프레임 수 간격을 지정. UA는 이 값과 videoKeyFrameIntervalDuration 을 함께 고려해 키프레임 생성을 제어한다.

2.6. BitrateMode

enum BitrateMode {
    "constant",
    "variable"
    };
    

2.6.1.

constant
고정 비트레이트로 인코딩함.
variable
가변 비트레이트로 인코딩하여 신호가 복잡할수록 공간을 더 많이, 덜 복잡할수록 공간을 적게 사용하도록 함.

2.7. RecordingState

enum RecordingState {
    "inactive",
    "recording",
    "paused"
    };
    

2.7.1.

inactive
녹화가 진행 중이지 않음: 아직 시작되지 않았거나 이미 중지됨.
recording
녹화가 시작되어 UA가 데이터를 수집 중임.
paused
녹화가 시작되었다가 일시 정지된 상태이며, 아직 중지나 재개되지 않음.

3. Blob 이벤트

[Exposed=Window]
    interface BlobEvent : Event {
    constructor(DOMString type, BlobEventInit eventInitDict);
    [SameObject] readonly attribute Blob data;
    readonly attribute DOMHighResTimeStamp timecode;
    };
    

3.1. 생성자

BlobEvent(DOMString type, BlobEventInit eventInitDict)

3.2. 속성

data, 타입 Blob, 읽기 전용
인코딩된 Blob으로, 해당 type 속성은 블롭 데이터의 인코딩을 나타낸다.
timecode, 타입 DOMHighResTimeStamp, 읽기 전용
MediaRecorder 인스턴스의 경우, 최초로 생성된 BlobEventtimecode 값은 반드시 0을 포함해야 한다. 그 다음 BlobEventtimecode 값에는 해당 BlobEvent에서 생성된 첫 번째 청크의 타임스탬프와, 첫 번째 BlobEvent에서 생성된 첫 번째 청크의 타임스탬프와의 차이가 DOMHighResTimeStamp [HR-TIME] 형태로 포함된다.

3.3. BlobEventInit

dictionary BlobEventInit : EventInit {
    required Blob data;
    DOMHighResTimeStamp timecode;
    };
    

3.3.1. 멤버

data, 타입 Blob
Blob 객체로, BlobEvent 를 통해 전달할 데이터를 포함한다.
timecode, 타입 DOMHighResTimeStamp
BlobEvent 초기화에 사용되는 타임코드이다.

4. 오류 처리

4.1. 일반 원칙

이 절은 비규범적(non-normative)이다.

UA는 호출 시점에 오류를 감지할 수 있으면 DOMException 을 throw한다. 그 외의 경우, UA는 에러 이벤트를 발생시킨다. 녹화가 시작되어 아직 중지되지 않은 상태에서 오류가 발생하면, blob을 지금까지 수집된 데이터의 Blob 으로 둔다; 에러를 발생시킨 후 UA는 dataavailable 이벤트를 blob과 함께 발생시킨다; 그리고 곧바로 UA는 stop이라는 이벤트를 발생시킨다. UA는 지원하는 Blob 크기나, 동시에 기록 가능한 MediaStreamTrack 개수처럼 플랫폼별 제한을 둘 수 있다. 이러한 제한을 초과하면 치명적인 오류를 신호한다.

4.2. 오류 이벤트

오류 이벤트를 발생시킨다는 것은 ErrorEventeventConstructor로 사용하여 이벤트를 발생시키는 것을 말한다.

4.3. 예외 요약

이 문서에 정의된 각 예외는 특정 타입을 가진 DOMException 입니다.

이름 설명
InvalidStateError 허용되지 않은 객체에 대해 연산이 호출되었거나, 허용되지 않은 시점에 호출했거나, 이미 삭제되거나 제거된 소스 객체에 대해 요청을 한 경우 발생합니다.
NotSupportedError MIME 타입이 지원되지 않거나 주어진 트랙 집합을 해당 MIME 타입으로 기록할 수 없어서 연산을 수행할 수 없는 경우입니다. User agent는 message 속성에 최대한 부가 정보를 제공해야 합니다.
SecurityError MediaStream격리 속성(isolation properties) 으로 인해 MediaRecorder가 접근할 수 없는 경우입니다.
InvalidModificationError 기록 중인 MediaStreamMediaStreamTrack 집합이 변경되어 더 이상 기록이 불가능한 상황입니다.

5. 이벤트 요약

다음과 같은 추가 이벤트들이 MediaRecorder 객체에서 발생합니다:

이벤트 이름 인터페이스 발생 시점
start Event UA가 MediaStream에서 데이터 녹화를 시작했을 때 발생
stop Event UA가 MediaStream에서 데이터 녹화를 중단했을 때 발생
dataavailable BlobEvent UA가 데이터를 애플리케이션에 전달하기 위해 발생시키는 이벤트입니다. 이 이벤트의 data 속성에는 기록된 데이터의 Blob이 포함됩니다.
pause Event UA가 MediaStream에서 데이터 녹화를 일시 중지했을 때 발생
resume Event UA가 MediaStream에서 데이터 녹화를 다시 시작했을 때 발생
error ErrorEvent 메모리 부족 등 오류가 발생했거나, 녹화 중 stream에 변경이 발생해 더 이상 녹화가 불가능해진 경우 (예: 트랙이 추가/제거된 경우) 발생

6. 개인정보 보호 및 보안 고려사항

이 절은 비규범적(non-normative)이다.

MediaRecorder 의 데이터 소스는 항상 MediaStream 이므로, 보안의 대부분은 [GETUSERMEDIA]의 '개인정보 및 보안 고려사항' 절에 위임된다. 특히, 소스 MediaStream 이 신뢰된 보안 컨텍스트에서 공급된다는 것을 전제로 한다.

6.1. 자원 고갈

비디오/오디오 인코딩은 많은 시스템 자원을 소모할 수 있다. 악의적인 웹사이트는 매우 큰 프레임 해상도/프레임레이트 등으로 워크로드를 설정함으로써 UA를 방해하거나 다운시키려 할 수 있다.

MediaRecorderstart()timeslice 파라미터로 인코딩 데이터를 일정기간 보관하도록 설정할 수 있는데, 너무 큰 timeslice 값을 넘기면 UA가 많은 데이터를 버퍼링해야 하여 랙 및 메모리 고갈 현상이 발생할 수 있다.

UA는 인코딩 및 버퍼링 과정에서 시스템 자원이 고갈되지 않도록 예방책을 마련해야 한다.

6.2. 지문 추적

MediaRecorderisTypeSupported() 메서드를 통해 지원하는 비디오 및 오디오 MIME 타입 정보를 제공한다. 이 외에도 MediaRecorderOptions에 코덱/대역폭이 정의되지 않을 경우 적합한 값을 선택해, type 속성 등으로 해당 정보를 노출한다. 사용자가 명시한 MediaRecorderOptions 정보도 최대한 존중하려 시도한다.

악의적인 웹사이트가 다양한 방식(예: 지원 코덱/가속기/해상도, 플랫폼/버전 차이 등 분석, 성능 측정 등)으로 적극적 지문 추적에 쓸 수 있다.

UA는 지문 표면적(fingerprinting surface) 증가를 막기 위해 단일 MIME/코덱에 대한 폭넓은 지원을 하고, 아키텍처/하드웨어/버전별로 기능 차가 드러나지 않게 하며, 기본값을 조정해 식별 가능성을 낮추는 등의 조치를 취해야 한다.

7. 예제

이 예제들의 약간 수정된 버전은 예를 들어 이 codepen 모음집에서 볼 수 있습니다.

7.1. MediaRecorder 와 콘텐츠 타입 지원 여부 확인

이 예제는 구현체가 몇 가지 인기 있는 코덱/컨테이너 조합을 지원하는지 확인합니다.

아래 예제는 이 codepen에도 약간만 수정해서 포함되어 있습니다.
if (window.MediaRecorder == undefined) {
    console.error('MediaRecorder not supported, boo');
    } else {
    var contentTypes = ["video/webm",
                        "video/webm;codecs=vp8",
                        "video/x-matroska;codecs=avc1",
                        "audio/webm",
                        "video/mp4;codecs=avc1",
                        "video/invalid"];
    contentTypes.forEach(contentType => {
        console.log(contentType + ' is '
            + (MediaRecorder.isTypeSupported(contentType) ?
                'supported' : 'NOT supported '));
    });
    }
    

7.2. 웹캠 비디오와 오디오 녹화

이 예제는 MediaStreamgetUserMedia()로 비디오+오디오 캡처한 후, 이를 <video> 태그에 연결하고 녹화를 시도하며, ondataavailable 이벤트를 통해 기록된 청크를 받아옵니다. 참고: MediaRecorder를 stop() 하거나 기록되는 MediaStream의 모든 MediaStreamTrackended 될 때까지 녹화가 계속됩니다.

다음 예제 또한 이 codepen에서 조금만 수정해 확인할 수 있습니다.
<html>
    <body>
    <video autoplay/>
    <script>
    var recordedChunks = [];

    function gotMedia(stream) {
        // |video| shows a live view of the captured MediaStream.
        var video = document.querySelector('video');
        video.src = URL.createObjectURL(stream);

        var recorder = null;
        try {
        recorder = new MediaRecorder(stream, {mimeType: "video/webm"});
        } catch (e) {
        console.error('Exception while creating MediaRecorder: ' + e);
        return;
        }

        recorder.ondataavailable = (event) => {
        console.log(' Recorded chunk of size ' + event.data.size + "B");
        recordedChunks.push(event.data);
        };

        recorder.start(100);
    }

    navigator.mediaDevices.getUserMedia({video: true, audio: true})
        .then(gotMedia)
        .catch(e => { console.error('getUserMedia() failed: ' + e); });
    </script>
    </body>
    </html>
    
recordedChunks MediaRecorder Web Fundamentals 기사download() 함수 등으로 파일로 저장할 수 있습니다.

색인

이 명세에서 정의된 용어

참조로 정의된 용어

참고문헌

규범적 참고문헌

[DOM]
Anne van Kesteren. DOM 표준. Living Standard. URL: https://dom.spec.whatwg.org/
[FileAPI]
Marijn Kruisselbrink. 파일 API. 2025년 12월 3일. WD. URL: https://www.w3.org/TR/FileAPI/
[FINGERPRINTING-GUIDANCE]
Nick Doty; Tom Ritter. 웹 명세에서 브라우저 핑거프린팅 완화. 2025년 9월 25일. NOTE. URL: https://www.w3.org/TR/fingerprinting-guidance/
[GETUSERMEDIA]
Cullen Jennings; et al. 미디어 캡처 및 스트림. 2025년 10월 9일. CRD. URL: https://www.w3.org/TR/mediacapture-streams/
[HR-TIME]
Yoav Weiss. 고해상도 타임(High Resolution Time). 2026년 3월 2일. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; et al. HTML 표준. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 표준. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2046]
N. Freed; N. Borenstein. Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types. 1996년 11월. Draft Standard. URL: https://www.rfc-editor.org/rfc/rfc2046
[WEBDRIVER-BIDI]
James Graham; Alex Rudenko; Maksim Sadym. WebDriver BiDi. 2026년 3월 9일. WD. URL: https://www.w3.org/TR/webdriver-bidi/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 표준. Living Standard. URL: https://webidl.spec.whatwg.org/

정보 제공용 참고문헌

[MEDIA-CAPABILITIES]
Jean-Yves Avenard; Mark Foltz. 미디어 기능. 2026년 3월 16일. WD. URL: https://www.w3.org/TR/media-capabilities/
[RFC6381]
R. Gellens; D. Singer; P. Frojdh. 'Codecs' 및 'Profiles' 매개변수 ("Bucket" 미디어 타입용). 2011년 8월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6381

IDL 색인

[Exposed=Window]
    interface MediaRecorder : EventTarget {
    constructor(MediaStream stream, optional MediaRecorderOptions options = {});
    readonly attribute MediaStream stream;
    readonly attribute DOMString mimeType;
    readonly attribute RecordingState state;
    attribute EventHandler onstart;
    attribute EventHandler onstop;
    attribute EventHandler ondataavailable;
    attribute EventHandler onpause;
    attribute EventHandler onresume;
    attribute EventHandler onerror;
    readonly attribute unsigned long videoBitsPerSecond;
    readonly attribute unsigned long audioBitsPerSecond;
    readonly attribute BitrateMode audioBitrateMode;

    undefined start(optional unsigned long timeslice);
    undefined stop();
    undefined pause();
    undefined resume();
    undefined requestData();

    static boolean isTypeSupported(DOMString type);
    };

    dictionary MediaRecorderOptions {
    DOMString mimeType = "";
    unsigned long audioBitsPerSecond;
    unsigned long videoBitsPerSecond;
    unsigned long bitsPerSecond;
    BitrateMode audioBitrateMode = "variable";
    DOMHighResTimeStamp videoKeyFrameIntervalDuration;
    unsigned long videoKeyFrameIntervalCount;
    };

    enum BitrateMode {
    "constant",
    "variable"
    };

    enum RecordingState {
    "inactive",
    "recording",
    "paused"
    };

    [Exposed=Window]
    interface BlobEvent : Event {
    constructor(DOMString type, BlobEventInit eventInitDict);
    [SameObject] readonly attribute Blob data;
    readonly attribute DOMHighResTimeStamp timecode;
    };

    dictionary BlobEventInit : EventInit {
    required Blob data;
    DOMHighResTimeStamp timecode;
    };