WebRTC 인코딩된 변환

W3C 워킹 드래프트,

이 문서에 대한 자세한 정보
이 버전:
https://www.w3.org/TR/2026/WD-webrtc-encoded-transform-20260521/
최신 공개 버전:
https://www.w3.org/TR/webrtc-encoded-transform/
편집자 초안:
https://w3c.github.io/webrtc-encoded-transform/
이전 버전:
이력:
https://www.w3.org/standards/history/webrtc-encoded-transform/
피드백:
public-webrtc@w3.org 로 제목 줄 “[webrtc-encoded-transform] … 메시지 주제 …”와 함께 (아카이브)
GitHub
편집자:
(Google)
(Google)
(Apple)

개요

이 API는 MediaStreamTrack에서 RTCPeerConnection을 통해 전송되는 비트를 조작하기 위한 API 표면을 정의한다.

이 문서의 상태

이 절은 이 문서가 공개된 시점의 상태를 설명한다. 현재 W3C 공개 문서와 이 기술 보고서의 최신 개정판 목록은 W3C 기술 보고서 색인에서 찾을 수 있다.

이 문서는 Web Real-Time Communications Working GroupRecommendation track을 사용하여 워킹 드래프트로 공개했다. 이 문서는 W3C 권고안이 되는 것을 목적으로 한다.

이 문서에 관해 의견을 보내고 싶다면 public-webrtc@w3.org로 보내기 바란다 (구독, 아카이브). 전자 메일을 보낼 때는 제목에 “webrtc-encoded-transform” 텍스트를 넣고, 가능하면 다음과 같이 작성하기 바란다: “[webrtc-encoded-transform] …의견 요약…”. 모든 의견을 환영한다.

워킹 드래프트로 공개되었다는 사실은 W3C 및 그 회원의 지지를 의미하지 않는다. 이는 초안 문서이며 언제든지 갱신되거나, 대체되거나, 다른 문서에 의해 폐기될 수 있다. 이 문서를 진행 중인 작업 이외의 것으로 인용하는 것은 적절하지 않다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹이 작성했다. W3C는 이 그룹의 산출물과 관련하여 이루어진 모든 특허 공개의 공개 목록을 유지한다; 이 페이지에는 특허를 공개하기 위한 지침도 포함되어 있다. 개인이 자신이 실제로 알고 있는 특허가 필수 청구항을 포함한다고 믿는 경우, 그 개인은 W3C 특허 정책 6절에 따라 해당 정보를 공개해야 한다.

이 문서는 2025년 8월 18일 W3C Process Document의 적용을 받는다.

1. 소개

[WEBRTC-NV-USE-CASES] 문서는 다음 사용 사례를 설명한다

이는 회의 서버가 평문 미디어에 접근할 수 없어야 함을 요구한다(요구 사항 N27).

이 명세는 인코딩된 미디어에 대한 접근을 제공한다. 인코딩된 미디어는 코덱의 인코더 부분의 출력이자 코덱의 디코더 부분의 입력이며, 이를 통해 사용자 에이전트가 암호화를 로컬에서 적용할 수 있게 한다.

이 인터페이스는 RTCPeerConnection의 설정 흐름을 유지하면서 이러한 기능에 접근할 수 있게 하기 위해 [WEBCODECS]에서 영감을 받았다

2. 명세

Streams 정의는 WebIDL을 많이 사용하지 않지만, WebRTC 명세는 사용한다. 이 명세는 WebRTC에 대한 IDL 확장을 보여준다.

이 명세는 파이프라인에 처리를 삽입하기 위해 RTCRtpSenderRTCRtpReceiver의 추가 API를 사용한다.

typedef (RTCRtpSFrameEncrypter or RTCRtpScriptTransform) RTCRtpSenderTransform;
typedef (RTCRtpSFrameDecrypter or RTCRtpScriptTransform) RTCRtpReceiverTransform;

// New methods for RTCRtpSender and RTCRtpReceiver
partial interface RTCRtpSender {
    attribute RTCRtpSenderTransform? transform;
};

partial interface RTCRtpReceiver {
    attribute RTCRtpReceiverTransform? transform;
};

이 API는 RTCRtpSender의 기본 인코더패킷화기의 처리 단계 사이, 그리고/또는 RTCRtpReceiver의 기본 역패킷화기디코더 사이의 미디어 파이프라인에서 인코딩된 프레임을 조작할 수 있게 한다.

인코더역패킷화기는 각각 빈 로 초기화되는 [[processedFramesQueue]] 내부 슬롯과, 인코딩된 프레임 frame이 주어졌을 때 frame을 반환하는 통과 알고리즘으로 초기화되는 [[transformFrameAlgorithm]] 내부 슬롯을 가진다.

인코더인코딩된 프레임을 출력할 때마다, 사용자 에이전트는 그 프레임에 대해 인코더.[[transformFrameAlgorithm]]을 호출해야 하며, 원래 프레임 대신 그 결과를 관련 패킷화기에 전달해야 한다.

역패킷화기인코딩된 프레임을 출력할 때마다, 사용자 에이전트는 그 프레임에 대해 역패킷화기.[[transformFrameAlgorithm]]을 호출해야 하며, 원래 프레임 대신 그 결과를 관련 디코더에 전달해야 한다.

2.1. 확장 동작

RTCRtpTransceiver를 생성할 때 다음 단계를 실행한다:

  1. this.[[useSFrame]]을 undefined로 초기화한다.

  2. this가 미디어 설명과 관련되어 있으면, 미디어 설명에서 this.[[useSFrame]]을 초기화한다. this.[[useSFrame]]이 true이면, this에 대해 SFrame RTP 패킷화를 활성화한다.

  3. 그렇지 않으면, 다음 단계를 실행하도록 태스크를 큐에 넣는다:

    1. this.[[useSFrame]]이 undefined이면, this.[[useSFrame]]을 false로 설정한다.

RTCRtpTransceiver.[[useSFrame]]은 이미 SDP m-section에 연결된 경우에는 동기적으로, 또는 SDP m-section에 연결되기 전에 비동기적으로 설정되어야 한다. 이를 통해 RTCRtpTransceiver.[[useSFrame]]과 해당 SDP가 항상 동기화되도록 보장한다. 이렇게 하면 negotiation-needed 플래그 확인 알고리즘과 같은 negotiation-needed 플래그 관련 처리가 RTCRtpTransceiver.[[useSFrame]]을 고려할 필요가 없다.

RTCRtpSender 또는 RTCRtpReceiver를 생성할 때 다음 단계를 실행한다:

  1. this.[[transform]]을 null로 초기화한다.

  2. this.[[pipeToController]]를 null로 초기화한다.

  3. this.[[frameSource]]thisRTCRtpSender인 경우 this인코더로, 그렇지 않으면 this역패킷화기로 초기화한다.

2.1.1. 스트림 처리

Streams 역압은 데이터 파이프라인에서 가능한 한 이른 시점에 데이터 생성을 일시 중지함으로써 처리량을 최적화하고 처리 및 메모리 소비를 제한할 수 있다. 이는 신뢰성이 필수이고 지연 시간이 그다지 중요하지 않은 맥락에서 유용하다. 반면 WebRTC 미디어 파이프라인은 신뢰성보다 낮은 지연 시간을 선호한다. 예를 들어 여러 위치에서 프레임을 버릴 수 있게 하고 복구 메커니즘을 사용한다. 변환 내부의 버퍼링은 웹 애플리케이션이 크게 적응할 수 있게 하지 않으면서 지연 시간을 추가하게 된다. 사용자 에이전트는 특히 변환의 양 끝을 모두 제어하므로 이러한 적응을 수행할 책임이 있다. 이러한 이유로 WebRTC 인코딩된 변환에서는 스트림 역압이 비활성화된다.

readEncodedData 알고리즘은 RTCRtpScriptTransformer transformer를 매개변수로, frame을 입력으로 받는다. 이는 다음 단계를 실행하여 정의된다:

  1. frame.[[owner]]transformer.[[frameSource]]로 설정한다.

  2. frame.[[counter]]transformer.[[lastEnqueuedFrameCounter]]로 설정한다.

  3. frame.[[owner]]역패킷화기이면:

    1. 관련 RTP 패킷이 RTP Header Extension for Absolute Capture Time을 포함하는 경우, frame.[[captureTime]]absolute capture timestamp 필드로 설정하고, 존재하는 경우 frame.[[senderCaptureTimeOffset]]capture clock offset 필드로 설정한다.

    2. 그렇지 않고, 관련 RTP 패킷이 RTP Header Extension for Absolute Capture Time을 포함하지 않지만 이전 RTP 패킷이 포함했던 경우, timestamp interpolation에 따라 absolute capture timestamp를 계산한 결과로 frame.[[captureTime]]을 설정하고, frame.[[senderCaptureTimeOffset]]을 존재했던 가장 최근 값으로 설정한다.

    3. 그렇지 않으면, frame.[[captureTime]]을 undefined로 설정하고 frame.[[senderCaptureTimeOffset]]을 undefined로 설정한다.

  4. frame.[[owner]]인코더이면, RTP Header Extension for Absolute Capture Time § absolute-capture-timestamp에 설명된 방법론을 사용하여 frame.[[captureTime]]을 capture timestamp로 설정하고 frame.[[senderCaptureTimeOffset]]을 undefined로 설정한다.

  5. frametransformer.[[readable]]Enqueue한다.

writeEncodedData 알고리즘은 RTCRtpScriptTransformer transformer를 매개변수로, frame을 입력으로 받는다. 이는 다음 단계를 실행하여 정의된다:

  1. frame.[[owner]]transformer.[[frameSource]]와 같지 않으면, 이 단계를 중단하고 undefined로 해결된 promise를 반환한다. 프로세서는 프레임을 생성하거나 스트림 사이에서 프레임을 이동할 수 없다.

  2. frame.[[counter]]transformer.[[lastReceivedFrameCounter]]보다 작거나 같으면, 이 단계를 중단하고 undefined로 해결된 promise를 반환한다. 프로세서는 프레임의 순서를 바꿀 수 없지만, 지연시키거나 버릴 수는 있다.

  3. transformer.[[lastReceivedFrameCounter]]frame.[[counter]]로 설정한다.

  4. dataframe.[[data]]라고 한다.

  5. serializedFrameStructuredSerializeWithTransfer(frame, « data »)라고 한다.

  6. frameCopyStructuredDeserializeWithTransfer(serializedFrame, frame관련 렐름)이라고 한다.

  7. processedFrameframeCopy의 기본 인코딩된 프레임이라고 한다.

  8. 병렬로, processedFrametransformer.[[frameSource]].[[processedFramesQueue]]enqueue한다.

  9. undefined로 해결된 promise를 반환한다.

송신자 측에서는 readEncodedData의 일부로, 인코더가 생성한 프레임이 enqueued되어야 하며, 그 대상은 인코더의 출력 순서대로 transformer.[[readable]]이다. writeEncodedData가 변환이 프레임 순서를 바꿀 수 없도록 보장하므로, 인코더의 출력 순서는 패킷화기가 RTP 패킷을 생성하고 RTP 패킷 시퀀스 번호를 할당할 때 따르는 순서이기도 하다. 패킷화기는 변환된 데이터가 여전히 원래 형식을 준수할 것을 기대할 수 있다. 예를 들어 Annex B 시작 코드로 구분된 NAL 유닛의 연속이 이에 해당한다.

수신자 측에서는 readEncodedData의 일부로, 역패킷화기가 생성한 프레임이 동일한 인코더의 출력 순서대로 transformer.[[readable]]enqueued되어야 한다. 순서가 지켜지도록 하기 위해 역패킷화기는 일반적으로 RTP 패킷 시퀀스 번호를 사용하여 필요한 경우 RTP 패킷의 순서를 재정렬한 뒤, 프레임을 transformer.[[readable]]enqueuing한다. writeEncodedData가 변환이 프레임 순서를 바꿀 수 없도록 보장하므로, 이는 디코더가 기대하는 순서가 된다.

2.1.2. RTCRtpTransform 공통 처리

RTCRtpTransform은 private 슬롯을 가진다:

각 RTCRtpTransform은 연결 알고리즘연결 해제 알고리즘을 가지며, 둘 다 기본적으로 비어 있다.

2.2. 확장 속성

transform getter 단계는 this.[[transform]]을 반환하는 것이다. setter 단계는 다음과 같다:

  1. transform을 setter의 인수라고 한다.

  2. transceiverthis와 관련된 RTCRtpTransceiver라고 한다.

  3. transform.[[useSFrame]]이 true이면, 다음 단계를 실행한다:

    1. transceiver.[[useSFrame]]이 false이면, InvalidModificationError를 throw하고 이 단계를 중단한다.

    2. 그렇지 않고 transceiver.[[useSFrame]]이 undefined이면, 다음 단계를 실행한다:

      1. transceiver.[[useSFrame]]을 true로 설정한다.

      2. transceiver에 대해 SFrame RTP 패킷화를 활성화한다.

  4. 그렇지 않으면, 다음 단계를 실행한다:

    1. transceiver.[[useSFrame]]이 true이면, InvalidModificationError를 throw하고 이 단계를 중단한다.

    2. transceiver.[[useSFrame]]을 false로 설정한다.

  5. transform이 null이 아니고 transform.[[owner]]가 null이 아니면, InvalidStateError를 throw하고 이 단계를 중단한다.

  6. transform.[[owner]]this로 설정한다.

  7. oldTransformthis.[[transform]]이라고 한다.

  8. oldTransform이 null이 아니면, oldTransform연결 해제 알고리즘을 실행한다.

  9. this.[[transform]]transform으로 설정한다.

  10. transform이 null이 아니면, transform연결 알고리즘this와 함께 실행한다.

  11. transform이 null이면, 다음 단계를 실행한다:

    1. frameSourcethis.[[frameSource]]라고 한다.

    2. 병렬로, frameSource.[[transformFrameAlgorithm]]통과 알고리즘으로 설정한다.

이 알고리즘은 변환이 동적으로 업데이트될 수 있도록 정의된다. 이전 변환에서 새 변환으로 전환이 어느 프레임에서 발생할지에 대한 보장은 없다.

웹 애플리케이션이 RTCRtpSender 생성 시점에 동기적으로 transform을 설정하면(예를 들어 addTrack을 호출할 때), transform은 RTCRtpSender의 인코더가 생성한 첫 번째 프레임을 받게 된다. 마찬가지로, 웹 애플리케이션이 RTCRtpReceiver 생성 시점에 동기적으로 transform을 설정하면(예를 들어 addTrack을 호출할 때 또는 track 이벤트 핸들러에서), transform은 RTCRtpReceiver의 패킷화기가 생성한 첫 번째 완전한 프레임을 받게 된다.

3. SFrame 변환

이 절에서 제시하는 API는 애플리케이션이 [RFC9605]에 정의된 특정 암호 스위트를 사용하여 SFrame 데이터를 처리할 수 있게 한다.

// List of supported cipher suites, as defined in [[RFC9605]] section 4.5 and in https://datatracker.ietf.org/doc/draft-barnes-sframe-iana-256/.
enum SFrameCipherSuite {
     "AES_128_CTR_HMAC_SHA256_80",
     "AES_128_CTR_HMAC_SHA256_64",
     "AES_128_CTR_HMAC_SHA256_32",
     "AES_128_GCM_SHA256_128",
     "AES_256_GCM_SHA512_128",
     "AES_256_CTR_HMAC_SHA512_80",
     "AES_256_CTR_HMAC_SHA512_64",
     "AES_256_CTR_HMAC_SHA512_32"
};

dictionary SFrameTransformOptions {
    required SFrameCipherSuite cipherSuite;
};

enum SFrameType {
    "per-frame",
    "per-packet"
};

dictionary RTCRtpSFrameEncrypterOptions : SFrameTransformOptions {
    SFrameType type = "per-frame";
};

typedef [EnforceRange] unsigned long long SmallCryptoKeyID;
typedef (SmallCryptoKeyID or bigint) CryptoKeyID;

interface mixin SFrameEncrypterManager {
    Promise<undefined> setEncryptionKey(CryptoKey key, CryptoKeyID keyId);
};

interface mixin SFrameDecrypterManager {
    Promise<undefined> addDecryptionKey(CryptoKey key, CryptoKeyID keyId);
    Promise<undefined> removeDecryptionKey(CryptoKeyID keyId);
    attribute EventHandler onerror;
};

[Exposed=Window]
interface RTCRtpSFrameEncrypter {
    constructor(RTCRtpSFrameEncrypterOptions options);
};
RTCRtpSFrameEncrypter includes SFrameEncrypterManager;

[Exposed=Window]
interface RTCRtpSFrameDecrypter : EventTarget {
    constructor(SFrameTransformOptions options);
};
RTCRtpSFrameDecrypter includes SFrameDecrypterManager;

[Exposed=(Window,DedicatedWorker)]
interface SFrameEncrypterStream {
    constructor(SFrameTransformOptions options);
};
SFrameEncrypterStream includes GenericTransformStream;
SFrameEncrypterStream includes SFrameEncrypterManager;

[Exposed=(Window,DedicatedWorker)]
interface SFrameDecrypterStream : EventTarget {
    constructor(SFrameTransformOptions options);
};
SFrameDecrypterStream includes GenericTransformStream;
SFrameDecrypterStream includes SFrameDecrypterManager;

enum SFrameTransformErrorEventType {
    "authentication",
    "keyID",
    "syntax"
};

[Exposed=(Window,DedicatedWorker)]
interface SFrameTransformErrorEvent : Event {
    constructor(DOMString type, SFrameTransformErrorEventInit eventInitDict);

    readonly attribute SFrameTransformErrorEventType errorType;
    readonly attribute CryptoKeyID? keyID;
    readonly attribute any frame;
};

dictionary SFrameTransformErrorEventInit : EventInit {
    required SFrameTransformErrorEventType errorType;
    required any frame;
    CryptoKeyID? keyID;
};

new RTCRtpSFrameEncrypter(options) 생성자 단계는 다음과 같다:

  1. options를 메서드의 첫 번째 인자로 둔다.

  2. thisoptionsSFrame 초기화 알고리즘을 실행한다.

  3. this.[[role]]을 'encrypt'로 설정한다.

  4. this.[[useSFrame]]을 true로 설정한다.

new RTCRtpSFrameDecrypter(options) 생성자 단계는 다음과 같다:

  1. options를 메서드의 첫 번째 인자로 둔다.

  2. thisoptionsSFrame 초기화 알고리즘을 실행한다.

  3. this.[[role]]을 'decrypt'로 설정한다.

  4. this.[[useSFrame]]을 true로 설정한다.

new SFrameEncrypterStream(options) 생성자 단계는 다음과 같다:

  1. options를 메서드의 첫 번째 인자로 둔다.

  2. thisoptionsSFrame 초기화 알고리즘을 실행한다.

  3. this.[[role]]을 'encrypt'로 설정한다.

new SFrameDecrypterStream(options) 생성자 단계는 다음과 같다:

  1. options를 메서드의 첫 번째 인자로 둔다.

  2. thisoptionsSFrame 초기화 알고리즘을 실행한다.

  3. this.[[role]]을 'decrypt'로 설정한다.

3.1. 알고리즘

thisoptions가 주어진 SFrame 초기화 알고리즘은 다음 단계를 실행한다:

  1. transformAlgorithmframe을 입력으로 받아 thisframe으로 SFrame 변환 알고리즘을 실행하는 알고리즘으로 둔다.

  2. options["type"]가 존재하면, 다음 단계를 실행한다:

    1. options["type"]가 'per-frame'이면, [RTP-SFRAME-PAYLOAD]에 정의된 프레임별 전송을 사용한다.

    2. 그렇지 않으면, [RTP-SFRAME-PAYLOAD]에 정의된 패킷별 전송을 사용한다.

  3. this.[[transform]]을 새 TransformStream으로 설정한다.

  4. 설정한다. this.[[transform]]transformAlgorithmtransformAlgorithm으로 설정되도록 설정한다.

  5. this.[[cipherSuite]]options["cipherSuite"]로 설정한다.

  6. this.[[readable]]this.[[transform]].[[readable]]로 설정한다.

  7. this.[[writable]]this.[[transform]].[[writable]]로 설정한다.

thisframe이 주어진 SFrame 변환 알고리즘은 다음 단계를 실행한다:

  1. rolethis.[[role]]이라고 한다.

  2. this.[[owner]]RTCRtpSender이면, role을 'encrypt'로 설정한다.

  3. this.[[owner]]RTCRtpReceiver이면, role을 'decrypt'로 설정한다.

  4. data를 undefined라고 한다.

  5. frameBufferSource이면, dataframe으로 설정한다.

  6. frameRTCEncodedAudioFrame이면, dataframe.data로 설정한다

  7. frameRTCEncodedVideoFrame이면, dataframe.data로 설정한다

  8. data가 undefined이면, 이 단계를 중단한다.

  9. bufferdata, this.[[cipherSuite]]role을 매개변수로 하여 SFrame 알고리즘을 실행한 결과라고 한다. 이 알고리즘은 [RFC9605]에 정의되어 있으며 ArrayBuffer를 반환한다.

  10. SFrame 알고리즘이 오류와 함께 갑자기 종료되면, 다음 하위 단계를 실행하도록 태스크를 큐에 넣는다:

    1. 복호화 측의 처리가 data가 SFrame 형식을 따르지 않아서 실패하면, this에서 error라는 이름의 이벤트를 발생시킨다. 이때 SFrameTransformErrorEvent 인터페이스를 사용하고, 그 errorType 속성은 syntax로 설정하며, 그 frame 속성은 frame으로 설정한다.

    2. 복호화 측의 처리가 data에서 파싱된 키 식별자를 알 수 없어서 실패하면, this에서 error라는 이름의 이벤트를 발생시킨다. 이때 SFrameTransformErrorEvent 인터페이스를 사용하고, 그 errorType 속성은 keyID로 설정하며, 그 frame 속성은 frame으로 설정하고, 그 keyID 속성은 SFrame 헤더에서 파싱된 keyID 값으로 설정한다.

    3. 복호화 측의 처리가 인증 태그 검증 때문에 실패하면, this에서 error라는 이름의 이벤트를 발생시킨다. 이때 SFrameTransformErrorEvent 인터페이스를 사용하고, 그 errorType 속성은 authentication으로 설정하며, 그 frame 속성은 frame으로 설정한다.

    4. 이 단계를 중단한다.

  11. frameBufferSource이면, framebuffer로 설정한다.

  12. frameRTCEncodedAudioFrame이면, frame.databuffer로 설정한다.

  13. frameRTCEncodedVideoFrame이면, frame.databuffer로 설정한다.

  14. framethis.[[transform]]Enqueue한다.

3.2. 메서드

setEncryptionKey(key, keyId) 메서드 단계는 다음과 같다:
  1. promise새 promise라고 한다.

  2. keyId가 0 이상 264-1 이하의 정수로 표현될 수 없는 bigint이면, promiseRangeError 예외로 거부하고 이 단계를 중단한다.

  3. 병렬로, 다음 단계를 실행한다:

    1. [RFC9605]에 정의된 대로 SFrame 변환 암호화 알고리즘의 키 자료를 keykeyId로 설정한다.

    2. 키 자료 설정이 실패하면, promiseInvalidModificationError 예외로 거부하도록 태스크를 큐에 넣고 이 단계를 중단한다.

    3. promise를 undefined로 해결하도록 태스크를 큐에 넣는다.

  4. promise를 반환한다.

addDecryptionKey(key, keyId) 메서드 단계는 다음과 같다:

  1. promise새 promise라고 한다.

  2. keyId가 0 이상 264-1 이하의 정수로 표현될 수 없는 bigint이면, promiseRangeError 예외로 거부하고 이 단계를 중단한다.

  3. 병렬로, 다음 단계를 실행한다:

    1. keyStore[RFC9605]에 정의된 SFrame 변환 알고리즘에 사용되는 키 저장소라고 한다.

    2. keyStore[keyId]를 key설정한다.

    3. 키 자료 설정이 실패하면, promiseInvalidModificationError 예외로 거부하도록 태스크를 큐에 넣고 이 단계를 중단한다.

    4. promise를 undefined로 해결하도록 태스크를 큐에 넣는다.

  4. promise를 반환한다.

removeDecryptionKey(keyId) 메서드 단계는 다음과 같다:

  1. promise새 promise라고 한다.

  2. keyId가 0 이상 264-1 이하의 정수로 표현될 수 없는 bigint이면, promiseRangeError 예외로 거부하고 이 단계를 중단한다.

  3. 병렬로, 다음 단계를 실행한다:

    1. keyStore[RFC9605]에 정의된 SFrame 변환 알고리즘에 사용되는 키 저장소라고 한다.

    2. keyStore[keyId]를 제거한다.

    3. promise를 undefined로 해결하도록 태스크를 큐에 넣는다.

  4. promise를 반환한다.

4. 스크립트 변환

이 절에서 캡처 시스템은 미디어가 공급되는 시스템을 의미하고, 송신자 시스템은 RTCEncodedFrameMetadata 데이터가 채워지는 수신자 시스템으로 RTP 및 RTCP 패킷을 보내는 시스템을 의미한다.

4.1. RTCEncodedFrameMetadata 딕셔너리

dictionary RTCEncodedFrameMetadata {
    unsigned long synchronizationSource;
    octet payloadType;
    sequence<unsigned long> contributingSources;
    unsigned long rtpTimestamp;
    DOMHighResTimeStamp receiveTime;
    DOMHighResTimeStamp captureTime;
    DOMHighResTimeStamp senderCaptureTimeOffset;
    DOMString mimeType;
};

4.1.1. 멤버

synchronizationSource, 타입은 unsigned longunsigned long

동기화 소스(ssrc) 식별자는 [RFC3550]에 따른 unsigned 정수 값이며, 인코딩된 프레임 객체가 설명하는 RTP 패킷 스트림을 식별하는 데 사용된다.

payloadType, 타입은 octetoctet

페이로드 타입은 [RFC3550]에 따른 0에서 127까지 범위의 unsigned 정수 값이며, RTP 페이로드의 형식을 설명하는 데 사용된다.

contributingSources, 타입은 sequence<unsigned long>sequence<unsigned long>

[RFC3550]에 정의된 기여 소스 목록(csrc 목록).

rtpTimestamp, 타입은 unsigned longunsigned long

RTP 타임스탬프 식별자는 [RFC3550]에 따른 unsigned 정수 값이며, RTP 데이터 패킷의 첫 번째 옥텟의 샘플링 시점을 반영한다.

receiveTime, 타입은 DOMHighResTimeStampDOMHighResTimeStamp

RTCRtpReceiver에서 오는 프레임의 경우, 이 미디어 프레임을 생성하는 데 사용된 마지막 수신 패킷의 타임스탬프를 나타낸다. 이 타임스탬프는 Performance.timeOrigin을 기준으로 한다.

captureTime, 타입은 DOMHighResTimeStampDOMHighResTimeStamp

캡처 시스템의 시계에서 이 프레임의 캡처 시간. 이 멤버를 채울 때 사용자 에이전트는 프레임의 [[captureTime]] 슬롯 값을 반환해야 하며, 그 값은 Performance.timeOrigin을 기준으로 하도록 이동되어야 한다.

senderCaptureTimeOffset, 타입은 DOMHighResTimeStampDOMHighResTimeStamp

senderCaptureTimeOffset은 동일한 프레임에 대해, 송신자 시스템이 자신의 NTP 시계와 captureTime이 유래한 캡처 시스템의 NTP 시계 사이의 오프셋을 추정한 값이다. 이 멤버를 채울 때 사용자 에이전트는 프레임의 [[senderCaptureTimeOffset]] 슬롯 값을 반환해야 한다.

mimeType, 타입은 DOMStringDOMString

IANA 미디어 타입 레지스트리 [IANA-MEDIA-TYPES]에 정의된 코덱 MIME 미디어 타입/서브타입. 예: audio/opus 또는 video/VP8.

4.2. RTCEncodedVideoFrameMetadata 딕셔너리

dictionary RTCEncodedVideoFrameMetadata : RTCEncodedFrameMetadata {
    unsigned long long frameId;
    sequence<unsigned long long> dependencies;
    unsigned short width;
    unsigned short height;
    unsigned long spatialIndex;
    unsigned long temporalIndex;
    long long timestamp;    // microseconds
};

4.2.1. 멤버

frameId, 타입은 unsigned long longunsigned long long

인코딩된 프레임의 식별자로, 디코드 순서에서 단조 증가한다. 하위 16비트는 존재하는 경우 [AV1-RTP-SPEC] 부록 A에 정의된 AV1 Dependency Descriptor Header Extension의 frame_number와 일치한다. Dependency Descriptor Header Extension이 존재하는 경우에만 수신된 프레임에 존재한다.

dependencies, 타입은 sequence<unsigned long long>sequence<unsigned long long>

이 프레임이 참조하는 프레임들의 frameId 목록. [AV1-RTP-SPEC] 부록 A에 정의된 AV1 Dependency Descriptor Header Extension이 존재하는 경우에만 수신된 프레임에 존재한다.

timestamp, 타입은 long longlong long

원시 프레임의 미디어 표시 타임스탬프(PTS)이며, 단위는 마이크로초이고 이 프레임에 대응하는 원시 프레임의 timestamp와 일치한다.

4.3. RTCEncodedVideoFrame 인터페이스

dictionary RTCEncodedVideoFrameOptions {
    RTCEncodedVideoFrameMetadata metadata;
};

// New interfaces to define RTC specific encoded video and audio frames used by RTCRtpScriptTransform.
[Exposed=(Window,DedicatedWorker), Serializable]
interface RTCEncodedVideoFrame {
    constructor(RTCEncodedVideoFrame originalFrame, optional RTCEncodedVideoFrameOptions options = {});
    readonly attribute EncodedVideoChunkType type;
    attribute ArrayBuffer data;
    RTCEncodedVideoFrameMetadata getMetadata();
};

4.3.1. 생성자

constructor()

주어진 originalFrameoptions.[metadata]에서 새로운 RTCEncodedVideoFrame을 생성한다. 새로 생성된 프레임은 originalFrame과 완전히 독립적이며, 그 [[data]]originalFrame.[[data]]의 깊은 복사본이다. 새 프레임의 [[metadata]]originalFrame.[[metadata]]의 깊은 복사본이며, 필드는 options.[metadata]에 존재하는 필드의 깊은 복사본으로 대체된다.

호출될 때 다음 단계를 실행한다:

  1. this.[[type]]originalFrame.[[type]]으로 설정한다.

  2. this.[[data]][CloneArrayBuffer](originalFrame.[[data]], 0, originalFrame.[[data]].[[ArrayBufferByteLength]])의 결과라고 한다.

  3. [[metadata]]가 이 새로 생성된 프레임과 관련된 메타데이터를 나타낸다고 한다.

    1. originalFrame.[[getMetadata()]]의 각 {[[key]],[[value]]} 쌍에 대해, [[metadata]].[[key]][[value]]의 깊은 복사본으로 설정한다.

    2. options.[metadata]의 각 {[[key]],[[value]]} 쌍에 대해, [[metadata]].[[key]][[value]]의 깊은 복사본으로 설정한다.

4.3.2. 멤버

type, 타입은 EncodedVideoChunkType, readonlyEncodedVideoChunkType

type 속성을 통해 애플리케이션은 프레임이 키 프레임인지 델타 프레임인지 확인할 수 있다. 가져올 때는 this.[[type]]을 반환해야 한다.

data, 타입은 ArrayBufferArrayBuffer

인코딩된 프레임 데이터. 데이터의 형식은 프레임을 인코딩/디코딩하는 데 사용되는 비디오 코덱에 따라 달라지며, 이는 mimeType을 보면 확인할 수 있다. SVC의 경우, 각 공간 계층은 별도로 변환된다. 가져올 때는 this.[[data]]를 반환해야 한다. 설정할 때는 this.[[data]]를 새 값으로 설정해야 한다.

패킷화기가 특정 요소, 예를 들어 AV1 temporal delimiter OBU를 버릴 수 있으므로, 수신 측 변환의 입력은 송신 측 변환의 출력과 다를 수 있다.

다음 표는 여러 예를 제공한다:

mimeType 데이터 형식
video/VP8 데이터는 section 9.1 of [RFC6386]에 정의된 "uncompressed data chunk"로 시작하고, 그 뒤에 나머지 프레임 데이터가 이어진다. VP8 payload descriptor에는 접근할 수 없다.
video/VP9 데이터는 [VP9]의 Section 6에 설명된 프레임이다. VP9 payload descriptor에는 접근할 수 없다.
video/H264 데이터는 [ITU-T-REC-H.264] Annex B에 정의된 Annex B 형식의 NAL 유닛 연속이다.
video/AV1 데이터는 [AV1]의 Section 5에 설명된 low-overhead bitstream format을 준수하는 OBU의 연속이다. AV1 aggregation header에는 접근할 수 없다.

4.3.3. 메서드

getMetadata()

프레임과 관련된 메타데이터를 반환한다.

4.3.4. 직렬화

RTCEncodedVideoFrame 객체는 직렬화 가능 객체이다. value, serialized, forStorage가 주어진 이 객체들의 직렬화 단계는 다음과 같다:

  1. forStorage가 true이면, DataCloneError를 throw한다.

  2. serialized.[[type]]value.[[type]]의 값으로 설정한다.

  3. serialized.[[metadata]]value 메타데이터의 내부 표현으로 설정한다.

  4. serialized.[[data]]value.[[data]]하위 직렬화로 설정한다.

serialized, valuerealm이 주어진 이 객체들의 역직렬화 단계는 다음과 같다:

  1. value.[[type]]serialized.[[type]]으로 설정한다.

  2. value의 메타데이터를 serialized.[[metadata]]의 플랫폼 객체 표현으로 설정한다.

  3. value.[[data]]serialized.[[data]]하위 역직렬화로 설정한다.

직렬화된 RTCEncodedVideoFrame의 내부 형식은 관찰할 수 없다; 이는 주로 writeEncodedData 알고리즘 및 structuredClone() 작업에서 프레임 복제와 함께 사용할 수 있도록 정의된다. 따라서 구현은 가장 잘 작동하는 어떤 방법이든 자유롭게 선택할 수 있다.

4.4. RTCEncodedAudioFrameMetadata 딕셔너리

dictionary RTCEncodedAudioFrameMetadata : RTCEncodedFrameMetadata {
    short sequenceNumber;
    double audioLevel;
};

4.4.1. 멤버

sequenceNumber, 타입은 shortshort

[RFC3550]에 정의된 RTP 시퀀스 번호. 들어오는 오디오 프레임에만 존재한다.

두 시퀀스 번호를 비교하려면 [RFC1982]에 설명된 일련 번호 산술이 필요하다.

audioLevel, 타입은 doubledouble

이 프레임의 오디오 레벨. 값은 0..1(선형) 사이이며, 1.0은 0 dBov를, 0은 무음을, 0.5는 0 dBov에서 음압 레벨이 대략 6 dBSPL 변화한 것을 나타낸다.

프레임이 원격에서 공급된 트랙에서 온 경우, 이 값은 [RFC6464]에 정의된 레벨 값에서 변환되어야 한다. 프레임의 수신 패킷에 [RFC6464] 헤더 확장이 존재하지 않으면, 이 값은 없어야 한다. 이 RFC는 오디오 레벨을 0에서 127까지의 정수 값으로 정의하며, 시스템이 인코딩할 수 있는 가장 큰 신호를 기준으로 한 음의 데시벨 단위의 오디오 레벨을 나타낸다. 따라서 0은 시스템이 인코딩할 수 있는 가장 큰 신호를 나타내고, 127은 무음을 나타낸다. 이 값을 선형 0..1 범위로 변환하려면, 값 127은 0으로 변환되고, 그 밖의 모든 값은 다음 식을 사용하여 변환된다: 10^(-rfc_level/20).

프레임이 로컬에서 공급된 트랙에서 온 경우, 레벨은 소스에서 직접 가져와야 하며, 협상된 경우 [RFC6464] 헤더 확장 값을 생성하기 위한 입력으로 사용되어야 한다.

4.5. RTCEncodedAudioFrame 인터페이스

dictionary RTCEncodedAudioFrameOptions {
    RTCEncodedAudioFrameMetadata metadata;
};

[Exposed=(Window,DedicatedWorker), Serializable]
interface RTCEncodedAudioFrame {
    constructor(RTCEncodedAudioFrame originalFrame, optional RTCEncodedAudioFrameOptions options = {});
    attribute ArrayBuffer data;
    RTCEncodedAudioFrameMetadata getMetadata();
};

4.5.1. 생성자

constructor()

주어진 originalFrameoptions.[metadata]에서 새로운 RTCEncodedAudioFrame을 생성한다. 새로 생성된 프레임은 originalFrame과 완전히 독립적이며, 그 [[data]]originalFrame.[[data]]의 깊은 복사본이다. 새 프레임의 [[metadata]]originalFrame.[[metadata]]의 깊은 복사본이며, 필드는 options.[metadata]에 존재하는 필드의 깊은 복사본으로 대체된다.

호출될 때 다음 단계를 실행한다:

  1. this.[[data]][CloneArrayBuffer](originalFrame.[[data]], 0, originalFrame.[[data]].[[ArrayBufferByteLength]])의 결과라고 한다.

  2. [[metadata]]가 이 새로 생성된 프레임과 관련된 메타데이터를 나타낸다고 한다.

    1. originalFrame.[[getMetadata()]]의 각 {[[key]],[[value]]} 쌍에 대해, [[metadata]].[[key]][[value]]의 깊은 복사본으로 설정한다.

    2. options.[metadata]의 각 {[[key]],[[value]]} 쌍에 대해, [[metadata]].[[key]][[value]]의 깊은 복사본으로 설정한다.

4.5.2. 멤버

data, 타입은 ArrayBufferArrayBuffer

인코딩된 프레임 데이터. 데이터의 형식은 프레임을 인코딩/디코딩하는 데 사용되는 오디오 코덱에 따라 달라지며, 이는 mimeType을 보면 확인할 수 있다. 가져올 때는 this.[[data]]를 반환해야 한다. 설정할 때는 this.[[data]]를 새 값으로 설정해야 한다. 다음 표는 여러 예를 제공한다:

mimeType 데이터 형식
audio/opus 데이터는 section 3 of [RFC6716]에 설명된 Opus 패킷이다.
audio/PCMU 데이터는 임의 길이의 바이트 시퀀스이며, 각 바이트는 [ITU-G.711]의 표 2a 및 2b에 정의된 u-law 인코딩 PCM 샘플이다.
audio/PCMA 데이터는 임의 길이의 바이트 시퀀스이며, 각 바이트는 [ITU-G.711]의 표 1a 및 1b에 정의된 A-law 인코딩 PCM 샘플이다.
audio/G722 데이터는 [ITU-G.722]에 설명된 G.722 오디오이다.
audio/RED 데이터는 section 3 of [RFC2198]에 설명된 중복 오디오 데이터이다.
audio/CN 데이터는 section 3 of [RFC3389]에 설명된 Comfort Noise이다.

4.5.3. 메서드

getMetadata()

프레임과 관련된 메타데이터를 반환한다.

4.5.4. 직렬화

RTCEncodedAudioFrame 객체는 직렬화 가능 객체이다. value, serializedforStorage가 주어진 이 객체들의 직렬화 단계는 다음과 같다:

  1. forStorage가 true이면, DataCloneError를 throw한다.

  2. serialized.[[metadata]]value 메타데이터의 내부 표현으로 설정한다.

  3. serialized.[[data]]value.[[data]]하위 직렬화로 설정한다.

serialized, valuerealm이 주어진 이 객체들의 역직렬화 단계는 다음과 같다:

  1. value의 메타데이터를 serialized.[[metadata]]의 플랫폼 객체 표현으로 설정한다

  2. value.[[data]]serialized.[[data]]하위 역직렬화로 설정한다.

5. RTCRtpScriptTransform 인터페이스

enum RTCRtpScriptTransformType {
    "sframe"
};

dictionary WorkerAndParameters {
     required Worker worker;
     RTCRtpScriptTransformType type;
};

typedef (Worker or WorkerAndParameters) WorkerOrWorkerAndParameters;

[Exposed=Window]
interface RTCRtpScriptTransform {
    constructor(WorkerOrWorkerAndParameters workerOrWorkerAndParameters, optional any options, optional sequence<object> transfer);
};

5.1. 내부 슬롯

RTCRtpScriptTransform 객체는 다음 내부 슬롯을 가진다:

내부 슬롯 설명(비규범적)
[[worker]] 생성자에 제공된 Worker.

5.2. 생성자

new RTCRtpScriptTransform(workerOrWorkerAndParameters, options, transfer) 생성자 단계는 다음과 같다:

  1. worker를 undefined라고 한다.

  2. useSFrame을 undefined라고 한다.

  3. workerOrWorkerAndParametersWorker 객체이면, workerworkerOrWorkerAndParameters로 설정하고 useSFrame을 false로 설정한다.

  4. 그렇지 않으면, 다음 하위 단계를 실행한다:

    1. workerworkerOrWorkerAndParameters["worker"]로 설정한다.

    2. workerOrWorkerAndParameters["type"]이 "sframe"이면 useSFrame을 true로, 그렇지 않으면 false로 설정한다.

  5. this의 내부 슬롯을 다음과 같이 초기화한다:

    [[worker]]

    worker

  6. this.[[useSFrame]]useSFrame으로 초기화한다.

  7. serializedOptionsStructuredSerializeWithTransfer(options, transfer)의 결과라고 한다.

  8. workerWorkerGlobalScope와 함께 DOM 조작 태스크 소스에서 전역 태스크를 큐에 넣어 다음 단계를 실행한다:

    1. transformerOptionsStructuredDeserializeWithTransfer(serializedOptions, 현재 Realm)의 결과라고 한다.

    2. transformertransformerOptionsRTCRtpScriptTransformer생성한 결과라고 한다.

    3. transformer관련 전역 객체에서, transformertransformer로 설정된 RTCTransformEvent를 사용하여, rtctransform이라는 이름의 이벤트를 발생시킨다.

// FIXME: 오류 처리를 설명할 것(RTCRtpScriptTransform 생성 시점에 worker closing flag가 true인 경우, 그리고 transform이 데이터를 처리하는 동안 worker가 종료되는 경우).

5.3. 알고리즘

RTCRtpScriptTransformrtcObject가 주어졌을 때 다음 연결 알고리즘을 가진다:

  1. transform연결 알고리즘을 소유하는 RTCRtpScriptTransform 객체라고 한다.

  2. frameSourcertcObject[[frameSource]]라고 한다.

  3. workerGlobalScopetransform.[[worker]]WorkerGlobalScope라고 한다.

  4. workerGlobalScope와 함께 DOM 조작 태스크 소스에서 전역 태스크를 큐에 넣어 다음 단계를 실행한다:

    1. transformertransform과 관련된 RTCRtpScriptTransformer 객체라고 한다.

    2. transformer.[[frameSource]]frameSource로 설정한다.

  5. 병렬로, frameSource.[[transformFrameAlgorithm]]을 다음 단계로 설정한다. 이 단계는 인코딩된 프레임 frame을 입력으로 받는다:

    1. workerGlobalScope와 함께 DOM 조작 태스크 소스에서 전역 태스크를 큐에 넣어 다음 단계를 실행한다:

      1. transformertransform과 관련된 RTCRtpScriptTransformer 객체라고 한다.

      2. jsFrameframe이 비디오 프레임이면 frame에서 만든 새로운 RTCEncodedVideoFrame이라고 하고, 그렇지 않으면 frame에서 만든 새로운 RTCEncodedAudioFrame이라고 한다.

      3. transformerjsFrame으로 readEncodedData를 호출한다.

    2. frameSource.[[processedFramesQueue]]가 비어 있지 않게 될 때까지 기다린다.

    3. frameSource.[[processedFramesQueue]]에서 dequeueing한 결과를 반환한다.

RTCRtpScriptTransform은 다음 연결 해제 알고리즘을 가진다:

  1. transform연결 해제 알고리즘을 소유하는 RTCRtpScriptTransform 객체라고 한다.

  2. transform.[[worker]]WorkerGlobalScope와 함께 DOM 조작 태스크 소스에서 전역 태스크를 큐에 넣어 다음 단계를 실행한다:

    1. transformertransform과 관련된 RTCRtpScriptTransformer 객체라고 한다.

    2. transformer.[[readable]]cancel한다.

    3. transformer.[[writable]]abort한다.

6. RTCRtpScriptTransformer 인터페이스

[Exposed=DedicatedWorker]
interface RTCRtpScriptTransformer : EventTarget {
    // Attributes and methods related to the transformer source
    readonly attribute ReadableStream readable;
    Promise<undefined> generateKeyFrame(optional DOMString rid);
    Promise<undefined> sendKeyFrameRequest();
    // Attributes and methods related to the transformer sink
    readonly attribute WritableStream writable;
    attribute EventHandler onkeyframerequest;
    // Attributes for configuring the Javascript code
    readonly attribute any options;
};

6.1. 내부 슬롯

RTCRtpScriptTransformer 객체는 다음 내부 슬롯을 가진다:

내부 슬롯 설명(비규범적)
[[frameSource]] 인코더, 역패킷화기, 또는 undefined.
[[options]] 선택적 Object, 또는 null.
[[readable]] ReadableStream.
[[writable]] WritableStream.
[[lastReceivedFrameCounter]] 수신된 프레임의 수.
[[lastEnqueuedFrameCounter]] 큐에 넣은 프레임의 수.
options 객체가 주어졌을 때, RTCRtpScriptTransformer생성하려면 다음 단계를 수행한다:
  1. transformer를 다음을 가진 RTCRtpScriptTransformer 객체라고 한다:

    [[frameSource]]

    undefined

    [[options]]

    options

    [[readable]]

    ReadableStream

    [[writable]]

    WritableStream

    [[lastReceivedFrameCounter]]

    0

    [[lastEnqueuedFrameCounter]]

    0

  2. transformer.[[readable]]설정한다.

    this가 매개변수로 주어진 readEncodedData 알고리즘은 인코딩된 프레임을 여기에 제공한다.

  3. writeAlgorithmframe이 주어졌을 때 this를 매개변수로, frame을 입력으로 하여 writeEncodedData를 실행하는 동작이라고 한다.

  4. transformer.[[writable]]을 그 writeAlgorithmwriteAlgorithm로 설정되고, 그 highWaterMarkInfinity로 설정되도록 설정한다.

    highWaterMark는 역압을 명시적으로 비활성화하기 위해 Infinity로 설정된다.

  5. transformer를 반환한다.

6.2. 메서드

generateKeyFrame(rid) 메서드 단계는 다음과 같다:

  1. promise를 새 promise라고 한다.

  2. promise, this.[[frameSource]]rid키 프레임 생성 알고리즘을 실행한다.

  3. promise를 반환한다.

sendKeyFrameRequest() 메서드 단계는 다음과 같다:

  1. promise를 새 promise라고 한다.

  2. promisethis.[[frameSource]]키 프레임 요청 전송 알고리즘을 실행한다.

  3. promise를 반환한다.

6.3. 속성

options getter 단계는 다음과 같다:

  1. this.[[options]]을 반환한다.

readable getter 단계는 다음과 같다:

  1. this.[[readable]]을 반환한다.

writable getter 단계는 다음과 같다:

  1. this.[[writable]]을 반환한다.

onbandwidthestimate EventHandler는 bandwidthestimate 타입을 가진다.

onkeyframerequest EventHandler는 keyframerequest 타입을 가진다.

6.4. 이벤트

[Exposed=DedicatedWorker]
interface RTCTransformEvent : Event {
    readonly attribute RTCRtpScriptTransformer transformer;
};

partial interface DedicatedWorkerGlobalScope {
    attribute EventHandler onrtctransform;
};

[Exposed=DedicatedWorker]
interface KeyFrameRequestEvent : Event {
  constructor(DOMString type, optional DOMString rid);
  readonly attribute DOMString? rid;
};

다음 이벤트는 RTCRtpScriptTransformer에서 발생한다:

KeyFrameRequestEvent 타입의 이벤트를 생성하는 단계는 다음과 같다:

관련 RTCRtpScriptTransformer transformer인코더가 예를 들어 들어오는 RTCP Picture Loss Indication(PLI) 또는 Full Intra Refresh(FIR)에서 키프레임 요청을 받으면, 다음 단계를 수행하도록 태스크를 큐에 넣는다:

  1. rid를 적절한 계층의 RID로 설정하거나, 요청이 특정 계층에 대한 것이 아니면 undefined로 설정한다.

  2. transformer에서 keyframerequest라는 이름의 이벤트를 발생시킨다. 이때 KeyFrameRequestEvent를 사용하고, 그 cancelable 속성은 "true"로 초기화하며, ridrid로 설정한다.

  3. 이벤트의 canceled flag가 true이면, 이 단계를 중단한다.

  4. 새 promise, transformer.[[frameSource]]rid키 프레임 생성 알고리즘을 실행한다.

6.5. KeyFrame 알고리즘

promise, frameSourcerid가 주어진 키 프레임 생성 알고리즘은 다음 단계를 실행하여 정의된다:

  1. frameSource인코더가 아니면, promiseInvalidStateError로 거부하고, 이 단계를 중단한다.

  2. encoderframeSource라고 한다.

  3. encoder가 비디오 RTCRtpSender에 속하지 않으면, promiseInvalidStateError로 거부하고, 이 단계를 중단한다.

  4. rid가 정의되어 있지만 [RFC8851]의 Section 10에 지정된 문법 요구 사항을 준수하지 않으면, promiseTypeError로 거부하고 이 단계를 중단한다.

  5. 병렬로, 다음 단계를 실행한다:

    1. layers를 이 encoder에 대한 계층의 새 목록이라고 하며, 협상된 encoding 인덱스순으로 정렬한다.

    2. layers에서 active가 아니거나, 해당 RTCRtpSender 트랙이 종료된 모든 계층을 제거한다.

    3. rid가 undefined가 아니면, layers에서 RID가 rid가 아닌 모든 계층을 제거한다.

    참고: rid가 전달되지 않으면, 모든 활성 계층에 대해 키프레임이 생성된다.

    1. layers가 이제 비어 있으면, promiseNotFoundError로 거부하도록 태스크를 큐에 넣고 이 단계를 중단한다.

    2. encoder.[[pendingKeyFrameTasks]]에 있는 어떤 태스크의 [[layers]]에서든 이미 발견된 모든 계층을 layers에서 제거한다.

    3. task.[[layers]]layers로 설정되고 task.[[promise]]promise로 설정된, task라는 pending key frame task를 생성한다.

    4. encoder.[[pendingKeyFrameTasks]]가 undefined이면, encoder.[[pendingKeyFrameTasks]]를 빈 set으로 초기화한다.

    5. taskencoder.[[pendingKeyFrameTasks]]추가한다.

    6. layers의 각 layer(있는 경우)에 대해, 그 layer에 다음으로 제공되는 비디오 프레임에 대한 키 프레임을 생성하도록 encoder에 지시한다.

RTCRtpScriptTransformer transformer와 관련된 모든 인코더에 대해, 사용자 에이전트는 어떤 frame이든 transformer.[[readable]]enqueued되기 직전에 다음 단계를 실행해야 한다:

  1. encodertransformer.[[frameSource]]라고 한다.

  2. encoder.[[pendingKeyFrameTasks]]가 undefined이면, 이 단계를 중단한다.

  3. frame이 비디오 "key" 프레임이 아니면, 이 단계를 중단한다.

  4. encoder.[[pendingKeyFrameTasks]]의 각 task에 대해 다음 단계를 실행한다:

    1. frametask.[[layers]]포함된 계층에 대해 생성되었다면, 다음 단계를 실행한다:

      1. 그 계층을 task.[[layers]]에서 제거한다.

      2. task.[[layers]]가 이제 비어 있으면, taskencoder.[[pendingKeyFrameTasks]]에서 제거한다.

      3. task.[[promise]]를 undefined로 해결한다.

RTCRtpScriptTransformer의 readable에 해당 키 프레임을 큐에 넣기 직전에 promise를 해결함으로써, promise의 resolution callback은 항상 해당 키 프레임이 노출되기 바로 전에 실행된다. promise가 여러 계층과 관련된 경우, 모든 계층에 대해 키 프레임이 큐에 들어간 뒤에 한 번 해결된다.

promiseframeSource가 주어진 키 프레임 요청 전송 알고리즘은 다음 단계를 실행하여 정의된다:

  1. frameSource역패킷화기가 아니면, promiseInvalidStateError로 거부하고, 이 단계를 중단한다.

  2. depacketizerframeSource라고 한다.

  3. depacketizer가 비디오 RTCRtpReceiver에 속하지 않으면, promiseInvalidStateError로 거부하고, 이 단계를 중단한다.

  4. 병렬로, 다음 단계를 실행한다:

    1. depacketizer의 receiver가 Full Intra Request(FIR)를 보내는 것이 적절하지 않다고 판단되면, promise를 undefined로 해결하고 이 단계를 중단한다. [RFC5104] 4.3.1절은 Full Intra Request를 보내는 것이 어떻게 그리고 언제 적절한지에 대한 지침을 제공한다.

    2. [RFC5104] 4.3.1절에 정의된 대로 Full Intra Request(FIR) 패킷을 생성하고 depacketizer의 receiver를 통해 보낸다.

    3. promise를 undefined로 해결하도록 태스크를 큐에 넣는다.

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

이 API는 Javascript가 미디어 스트림의 콘텐츠에 접근할 수 있게 한다. 이는 Canvas 및 WebAudio 같은 다른 소스에서도 가능하다.

그러나 [WEBRTC-IDENTITY]에 지정된 것처럼 격리된 스트림이나 다른 출처로 오염된 스트림은 이 API로 접근할 수 없다. 그렇게 하면 격리 규칙을 깨뜨리기 때문이다.

이 API는 그 밖에는 사용할 수 없는 일부 타이밍 정보 측면에 대한 접근을 허용하므로, 일부 fingerprinting surface를 허용한다.

이 API는 인코딩된 미디어에 대한 접근을 제공한다. 이는 JS 애플리케이션이 packetizer나 decoder 같은 내부 구성 요소에 전달되는 내용을 완전히 제어하게 됨을 의미한다. 이로 인해 이러한 구성 요소 내부에서 데이터가 처리되는 방식에 대한 감사에 추가적인 주의가 필요할 수 있다.

예를 들어 packetizer는 신뢰할 수 있는 인코더에서 온 데이터만 볼 것으로 기대할 수 있으며, 신뢰할 수 없는 소스에서 온 데이터를 받는 경우에 대해 감사되지 않았을 수 있다.

8. 예제

explainer 문서를 참고한다.

적합성

문서 규약

적합성 요구 사항은 서술적 단언과 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, 이는 정보성 참고이다.

적합 알고리즘

알고리즘의 일부로 명령형으로 표현된 요구 사항 (예: "strip any leading space characters" 또는 "return false and abort these steps")은 해당 알고리즘을 도입하는 데 사용된 핵심 단어 ("must", "should", "may" 등)의 의미로 해석해야 한다.

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

색인

이 명세가 정의하는 용어

참조로 정의된 용어

참고 문헌

규범 참고 문헌

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. 2026년 3월 24일. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[IANA-MEDIA-TYPES]
Media Types. URL: https://www.iana.org/assignments/media-types/
[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년 10월 9일. CRD. URL: https://www.w3.org/TR/mediacapture-streams/
[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
[RFC6464]
J. Lennox, Ed.; E. Ivov; E. Marocco. A Real-time Transport Protocol (RTP) Header Extension for Client-to-Mixer Audio Level Indication. 2011년 12월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6464
[RFC8851]
A.B. Roach, Ed.. RTP Payload Format Restrictions. 2021년 1월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8851
[RTP-EXT-CAPTURE-TIME]
RTP Header Extension for Absolute Capture Time. URL: https://datatracker.ietf.org/doc/draft-ietf-avtcore-abs-capture-time/
[STREAMS]
Adam Rice; et al. Streams Standard. Living Standard. URL: https://streams.spec.whatwg.org/
[WEBCODECS]
Paul Adenot; Eugene Zemtsov. WebCodecs. 2026년 5월 5일. WD. URL: https://www.w3.org/TR/webcodecs/
[WEBCRYPTO-2]
Daniel Huigens. Web Cryptography Level 2. 2025년 4월 22일. FPWD. URL: https://www.w3.org/TR/webcrypto-2/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBRTC]
Cullen Jennings; et al. WebRTC: Real-Time Communication in Browsers. 2025년 3월 13일. REC. URL: https://www.w3.org/TR/webrtc/

정보성 참고 문헌

[AV1]
Peter de Rivaz; Jack Haughton. AV1 Bitstream & Decoding Process Specification. 2019년 1월 8일. Standard. URL: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
[AV1-RTP-SPEC]
RTP Payload Format For AV1. Draft Deliverable. URL: https://aomediacodec.github.io/av1-rtp-spec/
[CloneArrayBuffer]
CloneArrayBuffer. URL: https://tc39.es/ecma262/#sec-clonearraybuffer
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[ITU-G.711]
G.711 : Pulse code modulation (PCM) of voice frequencies. URL: https://www.itu.int/rec/T-REC-G.711/
[ITU-G.722]
G.722 : 7 kHz audio-coding within 64 kbit/s. URL: https://www.itu.int/rec/T-REC-G.722/
[ITU-T-REC-H.264]
H.264 : Advanced video coding for generic audiovisual services. URL: https://www.itu.int/rec/T-REC-H.264
[RFC1982]
R. Elz; R. Bush. Serial Number Arithmetic. 1996년 8월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc1982
[RFC2198]
C. Perkins; et al. RTP Payload for Redundant Audio Data. 1997년 9월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc2198
[RFC3389]
R. Zopf. Real-time Transport Protocol (RTP) Payload for Comfort Noise (CN). 2002년 9월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc3389
[RFC3550]
H. Schulzrinne; et al. RTP: A Transport Protocol for Real-Time Applications. 2003년 7월. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc3550
[RFC5104]
S. Wenger; et al. Codec Control Messages in the RTP Audio-Visual Profile with Feedback (AVPF). 2008년 2월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc5104
[RFC6386]
J. Bankoski; et al. VP8 Data Format and Decoding Guide. 2011년 11월. Informational. URL: https://www.rfc-editor.org/rfc/rfc6386
[RFC6716]
JM. Valin; K. Vos; T. Terriberry. Definition of the Opus Audio Codec. 2012년 9월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6716
[RFC9605]
E. Omara; et al. Secure Frame (SFrame): Lightweight Authenticated Encryption for Real-Time Media. 2024년 8월. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc9605
[RTP-SFRAME-PAYLOAD]
RTP Payload Format for SFrame. URL: https://datatracker.ietf.org/doc/draft-ietf-avtcore-rtp-sframe/
[VP9]
VP9 Bitstream & Decoding Process Specification. URL: https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
[WEBRTC-IDENTITY]
Cullen Jennings; Martin Thomson. Identity for WebRTC 1.0. 2018년 9월 27일. CR. URL: https://www.w3.org/TR/webrtc-identity/
[WEBRTC-NV-USE-CASES]
Bernard Aboba. WebRTC Extended Use Cases. 2023년 12월 14일. DNOTE. URL: https://www.w3.org/TR/webrtc-nv-use-cases/

IDL 색인

typedef (RTCRtpSFrameEncrypter or RTCRtpScriptTransform) RTCRtpSenderTransform;
typedef (RTCRtpSFrameDecrypter or RTCRtpScriptTransform) RTCRtpReceiverTransform;

// New methods for RTCRtpSender and RTCRtpReceiver
partial interface RTCRtpSender {
    attribute RTCRtpSenderTransform? transform;
};

partial interface RTCRtpReceiver {
    attribute RTCRtpReceiverTransform? transform;
};

// List of supported cipher suites, as defined in [[RFC9605]] section 4.5 and in https://datatracker.ietf.org/doc/draft-barnes-sframe-iana-256/.
enum SFrameCipherSuite {
     "AES_128_CTR_HMAC_SHA256_80",
     "AES_128_CTR_HMAC_SHA256_64",
     "AES_128_CTR_HMAC_SHA256_32",
     "AES_128_GCM_SHA256_128",
     "AES_256_GCM_SHA512_128",
     "AES_256_CTR_HMAC_SHA512_80",
     "AES_256_CTR_HMAC_SHA512_64",
     "AES_256_CTR_HMAC_SHA512_32"
};

dictionary SFrameTransformOptions {
    required SFrameCipherSuite cipherSuite;
};

enum SFrameType {
    "per-frame",
    "per-packet"
};

dictionary RTCRtpSFrameEncrypterOptions : SFrameTransformOptions {
    SFrameType type = "per-frame";
};

typedef [EnforceRange] unsigned long long SmallCryptoKeyID;
typedef (SmallCryptoKeyID or bigint) CryptoKeyID;

interface mixin SFrameEncrypterManager {
    Promise<undefined> setEncryptionKey(CryptoKey key, CryptoKeyID keyId);
};

interface mixin SFrameDecrypterManager {
    Promise<undefined> addDecryptionKey(CryptoKey key, CryptoKeyID keyId);
    Promise<undefined> removeDecryptionKey(CryptoKeyID keyId);
    attribute EventHandler onerror;
};

[Exposed=Window]
interface RTCRtpSFrameEncrypter {
    constructor(RTCRtpSFrameEncrypterOptions options);
};
RTCRtpSFrameEncrypter includes SFrameEncrypterManager;

[Exposed=Window]
interface RTCRtpSFrameDecrypter : EventTarget {
    constructor(SFrameTransformOptions options);
};
RTCRtpSFrameDecrypter includes SFrameDecrypterManager;

[Exposed=(Window,DedicatedWorker)]
interface SFrameEncrypterStream {
    constructor(SFrameTransformOptions options);
};
SFrameEncrypterStream includes GenericTransformStream;
SFrameEncrypterStream includes SFrameEncrypterManager;

[Exposed=(Window,DedicatedWorker)]
interface SFrameDecrypterStream : EventTarget {
    constructor(SFrameTransformOptions options);
};
SFrameDecrypterStream includes GenericTransformStream;
SFrameDecrypterStream includes SFrameDecrypterManager;

enum SFrameTransformErrorEventType {
    "authentication",
    "keyID",
    "syntax"
};

[Exposed=(Window,DedicatedWorker)]
interface SFrameTransformErrorEvent : Event {
    constructor(DOMString type, SFrameTransformErrorEventInit eventInitDict);

    readonly attribute SFrameTransformErrorEventType errorType;
    readonly attribute CryptoKeyID? keyID;
    readonly attribute any frame;
};

dictionary SFrameTransformErrorEventInit : EventInit {
    required SFrameTransformErrorEventType errorType;
    required any frame;
    CryptoKeyID? keyID;
};

dictionary RTCEncodedFrameMetadata {
    unsigned long synchronizationSource;
    octet payloadType;
    sequence<unsigned long> contributingSources;
    unsigned long rtpTimestamp;
    DOMHighResTimeStamp receiveTime;
    DOMHighResTimeStamp captureTime;
    DOMHighResTimeStamp senderCaptureTimeOffset;
    DOMString mimeType;
};

dictionary RTCEncodedVideoFrameMetadata : RTCEncodedFrameMetadata {
    unsigned long long frameId;
    sequence<unsigned long long> dependencies;
    unsigned short width;
    unsigned short height;
    unsigned long spatialIndex;
    unsigned long temporalIndex;
    long long timestamp;    // microseconds
};

dictionary RTCEncodedVideoFrameOptions {
    RTCEncodedVideoFrameMetadata metadata;
};

// New interfaces to define RTC specific encoded video and audio frames used by RTCRtpScriptTransform.
[Exposed=(Window,DedicatedWorker), Serializable]
interface RTCEncodedVideoFrame {
    constructor(RTCEncodedVideoFrame originalFrame, optional RTCEncodedVideoFrameOptions options = {});
    readonly attribute EncodedVideoChunkType type;
    attribute ArrayBuffer data;
    RTCEncodedVideoFrameMetadata getMetadata();
};

dictionary RTCEncodedAudioFrameMetadata : RTCEncodedFrameMetadata {
    short sequenceNumber;
    double audioLevel;
};

dictionary RTCEncodedAudioFrameOptions {
    RTCEncodedAudioFrameMetadata metadata;
};

[Exposed=(Window,DedicatedWorker), Serializable]
interface RTCEncodedAudioFrame {
    constructor(RTCEncodedAudioFrame originalFrame, optional RTCEncodedAudioFrameOptions options = {});
    attribute ArrayBuffer data;
    RTCEncodedAudioFrameMetadata getMetadata();
};

enum RTCRtpScriptTransformType {
    "sframe"
};

dictionary WorkerAndParameters {
     required Worker worker;
     RTCRtpScriptTransformType type;
};

typedef (Worker or WorkerAndParameters) WorkerOrWorkerAndParameters;

[Exposed=Window]
interface RTCRtpScriptTransform {
    constructor(WorkerOrWorkerAndParameters workerOrWorkerAndParameters, optional any options, optional sequence<object> transfer);
};

[Exposed=DedicatedWorker]
interface RTCRtpScriptTransformer : EventTarget {
    // Attributes and methods related to the transformer source
    readonly attribute ReadableStream readable;
    Promise<undefined> generateKeyFrame(optional DOMString rid);
    Promise<undefined> sendKeyFrameRequest();
    // Attributes and methods related to the transformer sink
    readonly attribute WritableStream writable;
    attribute EventHandler onkeyframerequest;
    // Attributes for configuring the Javascript code
    readonly attribute any options;
};

[Exposed=DedicatedWorker]
interface RTCTransformEvent : Event {
    readonly attribute RTCRtpScriptTransformer transformer;
};

partial interface DedicatedWorkerGlobalScope {
    attribute EventHandler onrtctransform;
};

[Exposed=DedicatedWorker]
interface KeyFrameRequestEvent : Event {
  constructor(DOMString type, optional DOMString rid);
  readonly attribute DOMString? rid;
};