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 定义了一个 API 表面,用于操控经由 MediaStreamTrack 发送的 RTCPeerConnection 上的比特。

本文档的状态

本节描述本文档在其发布时的状态。 当前 W3C 出版物列表以及本技术报告的最新修订版 可在 W3C 技术报告 索引中找到。

本文档由 Web 实时通信工作组 作为工作草案发布,使用 推荐标准 轨道。本文档旨在成为 W3C 推荐标准。

如果你希望就本文档发表评论,请发送至 public-webrtc@w3.org订阅归档)。 发送电子邮件时, 请在主题中包含文本“webrtc-encoded-transform”, 最好像这样: “[webrtc-encoded-transform] …评论摘要…”。 欢迎所有评论。

作为工作草案发布并不意味着 W3C 及其成员的认可。这是一份草案文档,可能会 随时被其他文档更新、替换或 废弃。除作为进行中的工作之外,不宜引用本文档。

本文档由一个根据 W3C 专利政策运作的小组生成。 W3C 维护一份与该小组交付物相关的 任何专利披露的公开列表; 该页面还包括披露专利的说明。 任何实际知晓某项专利包含其认为的必要权利要求 的个人,必须按照 W3C 专利政策第 6 节 披露该信息。

本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。

1. 引言

[WEBRTC-NV-USE-CASES] 文档描述了以下用例

这要求会议服务器无法访问 明文媒体(要求 N27)。

本规范提供对编码媒体的访问, 编码媒体是编解码器的编码器部分的输出,也是编解码器的 解码器部分的输入,这允许用户代理在本地应用加密。

该接口受 [WEBCODECS] 启发, 旨在在保留 RTCPeerConnection 设置流程的同时, 提供对此类功能的访问

2. 规范

Streams 定义不怎么使用 WebIDL,但 WebRTC 规范会使用。 本规范展示 WebRTC 的 IDL 扩展。

它在 RTCRtpSenderRTCRtpReceiver 上使用附加 API, 以将处理插入到流水线中。

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

// RTCRtpSender 和 RTCRtpReceiver 的新方法
partial interface RTCRtpSender {
    attribute RTCRtpSenderTransform? transform;
};

partial interface RTCRtpReceiver {
    attribute RTCRtpReceiverTransform? transform;
};

此 API 允许在媒体 流水线中操控编码帧, 位置在 RTCRtpSender 的 底层编码器打包器的处理步骤之间,和/或 在 RTCRtpReceiver 的 底层解包器解码器之间。

编码器解包器各自都有一个 [[processedFramesQueue]] 内部槽,初始化为空的 队列, 以及一个 [[transformFrameAlgorithm]] 内部槽,初始化为 直通 算法,该算法给定一个编码帧 frame,返回 frame

每当编码器输出一个编码帧时,用户 代理必须在其上调用编码器[[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]]

在构造每个 RTCRtpSenderRTCRtpReceiver 时,运行以下步骤:

  1. this.[[transform]] 初始化为 null。

  2. this.[[pipeToController]] 初始化为 null。

  3. 如果 this 是 一个 RTCRtpSender, 则将 this.[[frameSource]] 初始化为 this编码器, 否则初始化为 this解包器

2.1.1. 流处理

Streams 背压可以通过尽早暂停数据流水线中的 数据生产,来优化吞吐量并限制处理和内存消耗。 这在可靠性至关重要而延迟不那么受关注的上下文中很有用。 另一方面,WebRTC 媒体流水线更偏向低延迟而不是可靠性,例如允许在 不同位置丢弃帧,并使用恢复机制。 在变换中进行缓冲会增加延迟,却不能让 Web 应用进行太多适配。 用户代理负责进行这些适配,尤其是因为它控制变换的两端。 基于这些原因,WebRTC 编码变换中禁用了 streams 背压。

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]] 设置为 绝对 捕获时间戳字段,并且如果存在 frame.[[senderCaptureTimeOffset]], 则将其设置为捕获 时钟偏移字段

    2. 否则,如果相关 RTP 包不包含 RTP Header Extension for Absolute Capture Time 但先前的 RTP 包包含, 则将 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]] 设置为捕获时间戳,并将 frame.[[senderCaptureTimeOffset]] 设置为 undefined。

  5. frame 入队transformer.[[readable]]

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相关 realm)。

  7. processedFrameframeCopy 的底层编码帧

  8. 并行地,将 processedFrame 入队transformer.[[frameSource]].[[processedFramesQueue]]

  9. 返回一个以 undefined 兑现的 promise。

在发送端,作为 readEncodedData 的一部分,由编码器生成的帧必须按 编码器的输出顺序被入队transformer.[[readable]] 中。 由于 writeEncodedData 确保变换不能 对帧重新排序,因此编码器的输出顺序也是打包器生成 RTP 包 并分配 RTP 包序列号时遵循的顺序。 打包器可以预期 变换后的数据仍然符合原始格式,例如由 Annex B 起始码分隔的一系列 NAL 单元。

在接收端,作为 readEncodedData 的一部分,由解包器生成的帧必须按 相同编码器的输出顺序被入队transformer.[[readable]] 中。 为确保遵守该顺序,解包器通常会使用 RTP 包序列号在需要时对 RTP 包重新排序,然后再将帧入队transformer.[[readable]]。 由于 writeEncodedData 确保变换不能 对帧重新排序,因此这将是解码器所期望的顺序。

2.1.2. RTCRtpTransform 通用处理

RTCRtpTransform 有一个私有槽:

每个 RTCRtpTransform 都有一个关联算法 和一个解除关联算法,二者默认 为空。

2.2. 扩展属性

transform getter 步骤为 返回 this.[[transform]]。setter 步骤为:

  1. transform 为传给 setter 的实参。

  2. transceiver 为与 this 关联的 RTCRtpTransceiver

  3. 如果 transform.[[useSFrame]] 为 true,则运行以下步骤:

    1. 如果 transceiver.[[useSFrame]] 为 false,则抛出 InvalidModificationError 并中止这些步骤。

    2. 否则,如果 transceiver.[[useSFrame]] 为 undefined,则运行 以下步骤:

      1. transceiver.[[useSFrame]] 设置为 true。

      2. transceiver 启用 SFrame RTP 打包。

  4. 否则,运行以下步骤:

    1. 如果 transceiver.[[useSFrame]] 为 true,则抛出 InvalidModificationError 并中止这些步骤。

    2. transceiver.[[useSFrame]] 设置为 false。

  5. 如果 transform 不为 null 且 transform.[[owner]] 不为 null, 则抛出 InvalidStateError 并中止这些步骤。

  6. transform.[[owner]] 设置为 this

  7. oldTransformthis.[[transform]]

  8. 如果 oldTransform 不为 null,则运行 oldTransform解除关联 算法

  9. this.[[transform]] 设置为 transform

  10. 如果 transform 不为 null,则使用 this 运行 transform关联算法

  11. 如果 transform 为 null,则运行以下步骤:

    1. frameSourcethis.[[frameSource]]

    2. 并行地,将 frameSource.[[transformFrameAlgorithm]] 设置为直通算法

此算法的定义方式使得变换可以动态更新。 无法保证从先前变换切换到新变换会发生在哪一帧上。

如果 Web 应用在创建 RTCRtpSender 时同步设置变换(例如调用 addTrack 时),则该变换将接收由 RTCRtpSender 的 编码器生成的第一帧。 类似地,如果 Web 应用在创建 RTCRtpReceiver 时同步设置变换(例如调用 addTrack 时,或在 track 事件处理程序中),则该变换将接收由 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. 使用 thisoptions 运行 SFrame 初始化算法

  3. this.[[role]] 设置为 'encrypt'。

  4. this.[[useSFrame]] 设置为 true。

new RTCRtpSFrameDecrypter(options) 构造器步骤如下:

  1. options 为该方法的第一个参数。

  2. 使用 thisoptions 运行 SFrame 初始化算法

  3. this.[[role]] 设置为 'decrypt'。

  4. this.[[useSFrame]] 设置为 true。

new SFrameEncrypterStream(options) 构造器步骤如下:

  1. options 为该方法的第一个参数。

  2. 使用 thisoptions 运行 SFrame 初始化算法

  3. this.[[role]] 设置为 'encrypt'。

new SFrameDecrypterStream(options) 构造器步骤如下:

  1. options 为该方法的第一个参数。

  2. 使用 thisoptions 运行 SFrame 初始化算法

  3. this.[[role]] 设置为 'decrypt'。

3.1. 算法

给定 thisoptionsSFrame 初始化算法运行以下步骤:

  1. transformAlgorithm 为一个算法,该算法接受 frame 作为输入,并使用 thisframe 运行 SFrame 转换算法

  2. 如果 options["type"] 存在,则运行以下步骤:

    1. 如果 options["type"] 为 'per-frame',则使用 [RTP-SFRAME-PAYLOAD] 中定义的逐帧发送。

    2. 否则,使用 [RTP-SFRAME-PAYLOAD] 中定义的逐包发送。

  3. this.[[transform]] 设置为一个新的 TransformStream

  4. 设置 this.[[transform]],并将 transformAlgorithm 设置为 transformAlgorithm

  5. this.[[cipherSuite]] 设置为 options["cipherSuite"]。

  6. this.[[readable]] 设置为 this.[[transform]].[[readable]]

  7. this.[[writable]] 设置为 this.[[transform]].[[writable]]

给定 thisframeSFrame 变换算法运行以下步骤:

  1. rolethis.[[role]]

  2. 如果 this.[[owner]]RTCRtpSender, 则将 role 设置为 'encrypt'。

  3. 如果 this.[[owner]]RTCRtpReceiver, 则将 role 设置为 'decrypt'。

  4. data 为 undefined。

  5. 如果 frameBufferSource, 则将 data 设置为 frame

  6. 如果 frameRTCEncodedAudioFrame, 则将 data 设置为 frame.data

  7. 如果 frameRTCEncodedVideoFrame, 则将 data 设置为 frame.data

  8. 如果 data 为 undefined,则中止这些步骤。

  9. buffer 为使用 datathis.[[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, 则将 frame 设置为 buffer

  12. 如果 frameRTCEncodedAudioFrame, 则将 frame.data 设置为 buffer

  13. 如果 frameRTCEncodedVideoFrame, 则将 frame.data 设置为 buffer

  14. frame 入队this.[[transform]]

3.2. 方法

setEncryptionKey(key, keyId) 方法步骤为:
  1. promise一个新的 promise

  2. 如果 keyId 是一个无法表示为 0 到 264-1(含)之间整数的 bigint, 则用 RangeError 异常拒绝 promise 并中止这些步骤。

  3. 并行地运行以下步骤:

    1. 将 SFrame 变换加密算法的密钥材料设置为 keykeyId,如 [RFC9605] 所定义。

    2. 如果设置密钥材料失败,则排队一个任务,用 InvalidModificationError 异常拒绝 promise 并中止这些步骤。

    3. 排队一个任务,以 undefined 兑现 promise

  4. 返回 promise

addDecryptionKey(key, keyId) 方法步骤为:

  1. promise一个新的 promise

  2. 如果 keyId 是一个无法表示为 0 到 264-1(含)之间整数的 bigint, 则用 RangeError 异常拒绝 promise, 并中止这些步骤。

  3. 并行地运行以下步骤:

    1. keyStore 为 SFrame 变换算法使用的密钥存储,如 [RFC9605] 所定义。

    2. keyStore[keyId] 设置key

    3. 如果设置密钥材料失败,则排队一个任务,用 InvalidModificationError 异常拒绝 promise 并中止这些步骤。

    4. 排队一个任务,以 undefined 兑现 promise

  4. 返回 promise

removeDecryptionKey(keyId) 方法步骤为:

  1. promise一个新的 promise

  2. 如果 keyId 是一个无法表示为 0 到 264-1(含)之间整数的 bigint, 则用 RangeError 异常拒绝 promise, 并中止这些步骤。

  3. 并行地运行以下步骤:

    1. keyStore 为 SFrame 变换算法使用的密钥存储,如 [RFC9605] 所定义。

    2. 移除 keyStore[keyId]。

    3. 排队一个任务,以 undefined 兑现 promise

  4. 返回 promise

4. 脚本变换

在本节中,捕获系统指媒体来源所在的系统,发送端系统 指向接收端系统发送 RTP 和 RTCP 包的系统,在接收端系统中会填充 RTCEncodedFrameMetadata 数据。

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

synchronization source(ssrc)标识符是按 [RFC3550] 定义的无符号整数值,用于标识该编码帧对象所描述的 RTP 包流。

payloadType 类型为 octetoctet

payload type 是按 [RFC3550] 定义的、范围为 0 到 127 的无符号整数值, 用于描述 RTP payload 的格式。

contributingSources 类型为 sequence<unsigned long>sequence<unsigned long>

contribution sources 列表(csrc list),如 [RFC3550] 所定义。

rtpTimestamp 类型为 unsigned longunsigned long

RTP timestamp 标识符是按 [RFC3550] 定义的无符号整数值, 它反映 RTP 数据包中第一个 octet 的采样时刻。

receiveTime 类型为 DOMHighResTimeStampDOMHighResTimeStamp

对于来自 RTCRtpReceiver 的帧,表示用于产生此媒体帧的 最后接收到的包的时间戳。此 时间戳相对于 Performance.timeOrigin

captureTime 类型为 DOMHighResTimeStampDOMHighResTimeStamp

此帧在捕获系统时钟中的捕获时间。 填充此成员时,用户代理必须返回该帧的 [[captureTime]] 槽的值, 并将其偏移为相对于 Performance.timeOrigin

senderCaptureTimeOffset 类型为 DOMHighResTimeStampDOMHighResTimeStamp

senderCaptureTimeOffset 是发送端系统对其自身 NTP 时钟与捕获系统 NTP 时钟之间偏移量的估计, 针对的是 captureTime 所来源的同一帧。 填充此成员时,用户代理必须返回该帧的 [[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;    // 微秒
};

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

// 用于定义 RTCRtpScriptTransform 所使用的 RTC 专用编码视频帧和音频帧的新接口。
[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 数据以 第 9.1 节中定义的“uncompressed data chunk”开始, 该节属于 [RFC6386],随后是帧数据的其余 部分。 VP8 payload descriptor 不可访问。
video/VP9 数据是 [VP9] 第 6 节中描述的帧。 VP9 payload descriptor 不可访问。
video/H264 数据是一系列 Annex B 格式的 NAL 单元, 如 [ITU-T-REC-H.264] Annex B 中定义。
video/AV1 数据是一系列符合 low-overhead bitstream format 的 OBU,如 [AV1] 第 5 节所述。 AV1 aggregation header 不可访问。

4.3.3. 方法

getMetadata()

返回与该帧关联的元数据。

4.3.4. 序列化

RTCEncodedVideoFrame 对象是可序列化对象。 给定 valueserializedforStorage,它们的序列化步骤为:

  1. 如果 forStorage 为 true,则抛出 DataCloneError

  2. serialized.[[type]] 设置为 value.[[type]] 的值。

  3. serialized.[[metadata]] 设置为 value 的元数据的内部表示。

  4. serialized.[[data]] 设置为 value.[[data]]子序列化

给定 serializedvaluerealm,它们的反序列化步骤为:

  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

RTP sequence number,如 [RFC3550] 中定义。 仅存在于传入音频帧。

比较两个序列号需要使用 [RFC1982] 中描述的 serial number arithmetic。

audioLevel 类型为 doubledouble

此帧的音频电平。该值介于 0..1(线性)之间, 其中 1.0 表示 0 dBov,0 表示静音,0.5 表示 相对于 0 dBov 的声压级约 6 dBSPL 变化。

如果帧来自远程来源的 track,则必须从 [RFC6464] 中定义的 level 值转换而来。如果收到的该帧包中不存在 [RFC6464] header extension, 则必须不存在此值。 该 RFC 将音频电平定义为从 0 到 127 的整数值, 表示相对于系统可能编码的 最响信号的负分贝音频电平。因此,0 表示系统可能编码的最响信号, 127 表示静音。要将这些值转换为线性 0..1 范围,值 127 会转换为 0,所有其他值 使用以下公式转换: 10^(-rfc_level/20)

如果帧来自本地来源的 track,则 level 必须 直接从源取得,并在已协商时用作生成 [RFC6464] header extension 值的输入。

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 数据是 Opus 包,如 第 3 节所述,该节属于 [RFC6716]
audio/PCMU 数据是任意长度的字节序列,其中每个字节都是 u-law 编码的 PCM 采样,如 [ITU-G.711] 中表 2a 和 2b 所定义。
audio/PCMA 数据是任意长度的字节序列,其中每个字节都是 A-law 编码的 PCM 采样,如 [ITU-G.711] 中表 1a 和 1b 所定义。
audio/G722 数据是 G.722 音频,如 [ITU-G.722] 所述。
audio/RED 数据是 Redundant Audio Data,如 第 3 节所述,该节属于 [RFC2198]
audio/CN 数据是 Comfort Noise,如 第 3 节所述,该节属于 [RFC3389]

4.5.3. 方法

getMetadata()

返回与该帧关联的元数据。

4.5.4. 序列化

RTCEncodedAudioFrame 对象是可序列化对象。 给定 valueserializedforStorage,它们的序列化步骤为:

  1. 如果 forStorage 为 true,则抛出 DataCloneError

  2. serialized.[[metadata]] 设置为 value 的元数据的内部表示。

  3. serialized.[[data]] 设置为 value.[[data]]子序列化

给定 serializedvaluerealm,它们的反序列化步骤为:

  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. 如果 workerOrWorkerAndParameters 是一个 Worker 对象,则将 worker 设置为 workerOrWorkerAndParameters,并将 useSFrame 设置为 false。

  4. 否则,运行以下子步骤:

    1. worker 设置为 workerOrWorkerAndParameters["worker"]。

    2. 如果 workerOrWorkerAndParameters["type"] 为 "sframe",则将 useSFrame 设置为 true,否则设置为 false。

  5. 按如下方式初始化 this 的内部槽:

    [[worker]]

    worker

  6. this.[[useSFrame]] 初始化为 useSFrame

  7. serializedOptionsStructuredSerializeWithTransfer(options, transfer) 的结果。

  8. 在 DOM 操作排队一个全局任务任务源,并使用 workerWorkerGlobalScope 来运行以下步骤:

    1. transformerOptionsStructuredDeserializeWithTransfer(serializedOptions, 当前 Realm) 的结果。

    2. transformer 为用 transformerOptions 创建一个 RTCRtpScriptTransformer 的结果。

    3. transformer相关全局对象上,使用 RTCTransformEvent 触发一个名为 rtctransform 的事件,并将 transformer 设置为 transformer

// FIXME:描述错误处理(在创建 RTCRtpScriptTransform 时 worker closing flag 为 true,以及 worker 在 transform 正在处理数据时被终止)。

5.3. 算法

每个 RTCRtpScriptTransform 都有以下关联算法,给定 rtcObject

  1. transform 为拥有该关联算法RTCRtpScriptTransform 对象。

  2. frameSourcertcObject[[frameSource]]

  3. workerGlobalScopetransform.[[worker]]WorkerGlobalScope

  4. 在 DOM 操作排队一个全局任务任务源,并使用 workerGlobalScope 来运行以下步骤:

    1. transformer 为与 transform 关联的 RTCRtpScriptTransformer 对象。

    2. transformer.[[frameSource]] 设置为 frameSource

  5. 并行地,将 frameSource.[[transformFrameAlgorithm]] 设置为以下步骤, 给定一个编码 帧 frame 作为输入:

    1. 在 DOM 操作排队一个全局任务任务源,并使用 workerGlobalScope 来运行以下步骤:

      1. transformer 为与 transform 关联的 RTCRtpScriptTransformer 对象。

      2. 如果 frame 是视频 帧,则令 jsFrame 为从 frame 创建的一个新的 RTCEncodedVideoFrame; 否则为从 frame 创建的一个新的 RTCEncodedAudioFrame

      3. transformerjsFrame 调用 readEncodedData

    2. 等待 frameSource.[[processedFramesQueue]] 变为非空。

    3. 返回从 frameSource.[[processedFramesQueue]]出队的结果。

每个 RTCRtpScriptTransform 都有以下解除关联算法

  1. transform 为拥有该解除关联 算法RTCRtpScriptTransform 对象。

  2. 在 DOM 操作排队一个全局任务任务源,并使用 transform.[[worker]]WorkerGlobalScope 来运行以下步骤:

    1. transformer 为与 transform 关联的 RTCRtpScriptTransformer 对象。

    2. 取消 transformer.[[readable]]

    3. 中止 transformer.[[writable]]

6. RTCRtpScriptTransformer 接口

[Exposed=DedicatedWorker]
interface RTCRtpScriptTransformer : EventTarget {
    // 与 transformer 源相关的属性和方法
    readonly attribute ReadableStream readable;
    Promise<undefined> generateKeyFrame(optional DOMString rid);
    Promise<undefined> sendKeyFrameRequest();
    // 与 transformer 接收端相关的属性和方法
    readonly attribute WritableStream writable;
    attribute EventHandler onkeyframerequest;
    // 用于配置 JavaScript 代码的属性
    readonly attribute any options;
};

6.1. 内部槽

RTCRtpScriptTransformer 对象具有以下内部槽:

内部槽 描述(非规范性
[[frameSource]] 一个编码器、一个解包器,或 undefined。
[[options]] 一个可选的 Object, 或 null。
[[readable]] 一个 ReadableStream
[[writable]] 一个 WritableStream
[[lastReceivedFrameCounter]] 已接收帧的计数。
[[lastEnqueuedFrameCounter]] 已入队帧的计数。
创建一个 RTCRtpScriptTransformer, 给定一个 options 对象,执行以下步骤:
  1. transformer 为一个新的 RTCRtpScriptTransformer, 具有:

    [[frameSource]]

    undefined

    [[options]]

    options

    [[readable]]

    一个新的 ReadableStream

    [[writable]]

    一个新的 WritableStream

    [[lastReceivedFrameCounter]]

    0

    [[lastEnqueuedFrameCounter]]

    0

  2. 设置 transformer.[[readable]]

    readEncodedData 算法给定 this 作为参数,会向其提供编码帧。

  3. writeAlgorithm 为一个动作,给定 frame, 该动作以 this 为参数并以 frame 为输入运行 writeEncodedData

  4. 设置 transformer.[[writable]], 并将其 writeAlgorithm 设置为 writeAlgorithm,将其 highWaterMark 设置为 Infinity

    highWaterMark 被设置为 Infinity,以显式禁用背压。

  5. 返回 transformer

6.2. 方法

generateKeyFrame(rid) 方法步骤为:

  1. promise 为一个新的 promise。

  2. promisethis.[[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",并将 rid 设置为 rid

  3. 如果事件的已取消标志为 true,则中止这些步骤。

  4. 用一个新的 promise、transformer.[[frameSource]]rid 运行生成关键帧算法

6.5. 关键帧算法

生成关键帧算法,给定 promiseframeSourcerid,定义为运行以下步骤:

  1. 如果 frameSource 不是编码器,则用 InvalidStateError 拒绝 promise, 并中止这些步骤。

  2. encoderframeSource

  3. 如果 encoder 不属于视频 RTCRtpSender, 则用 InvalidStateError 拒绝 promise, 并中止这些步骤。

  4. 如果 rid 已定义,但不符合 [RFC8851] 第 10 节中指定的语法要求, 则用 TypeError 拒绝 promise 并中止 这些步骤。

  5. 并行地运行以下步骤:

    1. layers 为此 encoder 的层组成的一个新的 列表, 按协商的 encoding index 排序。

    2. layers移除所有不为 active 的层,或其 对应 RTCRtpSender track 已结束的层。

    3. 如果 rid 不是 undefined,则从 layers移除 所有 RID 不是 rid 的层。

    注:如果未传入 rid, 则为所有 active 层生成关键帧。

    1. 如果 layers 现在为空,则排队一个任务,用 NotFoundError 拒绝 promise 并中止这些步骤。

    2. layers移除已经在 encoder.[[pendingKeyFrameTasks]] 中任何任务的任何 [[layers]]找到的所有层。

    3. 创建一个名为 task 的待处理关键帧任务,其中 task.[[layers]] 设置为 layerstask.[[promise]] 设置为 promise

    4. 如果 encoder.[[pendingKeyFrameTasks]] 为 undefined,则将 encoder.[[pendingKeyFrameTasks]] 初始化为空集合。

    5. task 追加encoder.[[pendingKeyFrameTasks]]

    6. 对于 layers 中的每个 layer(如果有),指示 encoder 为其下一个提供给该 layer 的视频帧生成关键帧。

对于与 RTCRtpScriptTransformer transformer 关联的任何编码器, 用户代理必须在任何 frame入队transformer.[[readable]] 之前运行以下步骤:

  1. encodertransformer.[[frameSource]]

  2. 如果 encoder.[[pendingKeyFrameTasks]] 为 undefined,则中止这些步骤。

  3. 如果 frame 不是视频 "key" 帧,则中止这些步骤。

  4. 对于 encoder.[[pendingKeyFrameTasks]] 中的每个 task, 运行以下步骤:

    1. 如果 frame 是为 task.[[layers]]包含的某个层生成的, 则运行以下步骤:

      1. task.[[layers]]移除该层。

      2. 如果 task.[[layers]] 现在为空,则从 encoder.[[pendingKeyFrameTasks]]移除 task

      3. 用 undefined 兑现 task.[[promise]]

通过在相应关键帧入队到 RTCRtpScriptTransformer 的 readable 之前兑现 promise, promise 的兑现回调总是会在相应关键帧被暴露之前执行。 如果 promise 与多个层相关联,则会在所有这些层的关键帧都已入队后 兑现。

发送请求关键帧算法,给定 promiseframeSource,定义为运行以下步骤:

  1. 如果 frameSource 不是解包器,则用 InvalidStateError 拒绝 promise, 并中止这些步骤。

  2. depacketizerframeSource

  3. 如果 depacketizer 不属于视频 RTCRtpReceiver, 则用 InvalidStateError 拒绝 promise, 并中止这些步骤。

  4. 并行地运行以下步骤:

    1. 如果认为由 depacketizer 的 receiver 发送 Full Intra Request(FIR) 不合适,则用 undefined 兑现 promise 并中止这些 步骤。 [RFC5104] 第 4.3.1 节提供了如何以及何时适合发送 Full Intra Request 的指南。

    2. 生成 [RFC5104] 第 4.3.1 节中定义的 Full Intra Request(FIR)包, 并通过 depacketizer 的 receiver 发送它。

    3. 排队一个任务,以 undefined 兑现 promise

7. 隐私和安全考量

此 API 使 JavaScript 能够访问媒体流的内容。这 也可通过其他来源获得,例如 Canvas 和 WebAudio。

但是,隔离的流(如 [WEBRTC-IDENTITY] 中所规定)或被其他源污染的流,不能 使用此 API 访问,因为那会破坏隔离规则。

该 API 将允许访问一些原本不可用的时序信息方面, 这会带来一些指纹识别面。

该 API 将提供对编码媒体的访问,这意味着 JS 应用 将完全控制交付给内部组件(如 打包器或解码器)的内容。这可能需要额外注意 审计这些组件内部如何处理数据。

例如,打包器可能预期只看到来自受信任编码器的数据, 并且可能没有针对接收来自不受信任来源的数据进行审计。

8. 示例

参见解释 文档

一致性

文档 约定

一致性要求通过描述性断言 与 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 标准. Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. 高分辨率时间. 2026 年 3 月 24 日. 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/
[IANA-MEDIA-TYPES]
媒体类型. URL: https://www.iana.org/assignments/media-types/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. Living Standard. URL: https://infra.spec.whatwg.org/
[MEDIACAPTURE-STREAMS]
Cullen Jennings; et al. 媒体捕获和 流. 2025 年 10 月 9 日. CRD. URL: https://www.w3.org/TR/mediacapture-streams/
[RFC2119]
S. Bradner. 用于 RFC 中表示 要求级别的关键词. 1997 年 3 月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC6464]
J. Lennox, Ed.; E. Ivov; E. Marocco. 用于客户端到混音器音频电平指示的 实时传输协议(RTP)标头扩展. 2011 年 12 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6464
[RFC8851]
A.B. Roach, Ed.. RTP 载荷格式 限制. 2021 年 1 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8851
[RTP-EXT-CAPTURE-TIME]
用于绝对捕获时间的 RTP 标头扩展. URL: https://datatracker.ietf.org/doc/draft-ietf-avtcore-abs-capture-time/
[STREAMS]
Adam Rice; et al. Streams 标准. 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 标准. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBRTC]
Cullen Jennings; et al. WebRTC:浏览器中的实时通信. 2025 年 3 月 13 日. REC. URL: https://www.w3.org/TR/webrtc/

资料性参考文献

[AV1]
Peter de Rivaz; Jack Haughton. AV1 比特流与解码过程规范. 2019 年 1 月 8 日. Standard. URL: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
[AV1-RTP-SPEC]
AV1 的 RTP 载荷格式. Draft Deliverable. URL: https://aomediacodec.github.io/av1-rtp-spec/
[CloneArrayBuffer]
CloneArrayBuffer. URL: https://tc39.es/ecma262/#sec-clonearraybuffer
[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[ITU-G.711]
G.711:语音频率的脉冲编码调制 (PCM). URL: https://www.itu.int/rec/T-REC-G.711/
[ITU-G.722]
G.722:64 kbit/s 内的 7 kHz 音频编码. URL: https://www.itu.int/rec/T-REC-G.722/
[ITU-T-REC-H.264]
H.264:面向通用视听服务的高级视频编码. URL: https://www.itu.int/rec/T-REC-H.264
[RFC1982]
R. Elz; R. Bush. 序列号算术. 1996 年 8 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc1982
[RFC2198]
C. Perkins; et al. 冗余音频 数据的 RTP 载荷. 1997 年 9 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc2198
[RFC3389]
R. Zopf. 舒适噪声(CN)的实时传输协议(RTP)载荷. 2002 年 9 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc3389
[RFC3550]
H. Schulzrinne; et al. RTP:用于 实时应用的传输协议. 2003 年 7 月. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc3550
[RFC5104]
S. Wenger; et al. 带反馈(AVPF)的 RTP 音视频配置文件中的编解码器控制消息. 2008 年 2 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc5104
[RFC6386]
J. Bankoski; et al. VP8 数据格式和解码 指南. 2011 年 11 月. Informational. URL: https://www.rfc-editor.org/rfc/rfc6386
[RFC6716]
JM. Valin; K. Vos; T. Terriberry. Opus 音频编解码器的定义. 2012 年 9 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6716
[RFC9605]
E. Omara; et al. Secure Frame(SFrame):用于实时媒体的轻量级 认证加密. 2024 年 8 月. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc9605
[RTP-SFRAME-PAYLOAD]
SFrame 的 RTP 载荷格式. URL: https://datatracker.ietf.org/doc/draft-ietf-avtcore-rtp-sframe/
[VP9]
VP9 比特流与解码过程规范. URL: https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
[WEBRTC-IDENTITY]
Cullen Jennings; Martin Thomson. WebRTC 1.0 的身份. 2018 年 9 月 27 日. CR. URL: https://www.w3.org/TR/webrtc-identity/
[WEBRTC-NV-USE-CASES]
Bernard Aboba. WebRTC 扩展 用例. 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;
};