Skip to content
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0565a30
Add decryption/encryption dedicated APIs
youennf Nov 14, 2025
4867772
Introduce sender and receiver transforms
youennf Nov 18, 2025
25562b2
Apply suggestions from code review
youennf Nov 20, 2025
226007d
Update index.bs
youennf Nov 20, 2025
68c323b
Update index.bs
youennf Dec 17, 2025
d733f5f
Update index.bs
youennf Dec 17, 2025
8594e5d
Update index.bs
youennf Dec 17, 2025
b5097af
Reuse infra map set
youennf Dec 17, 2025
f842e48
Fix bikeshed link
youennf Dec 17, 2025
58262ce
Fix video frame type
youennf Dec 17, 2025
c94cbd3
Update index.bs
youennf Dec 18, 2025
eeefa7a
Update index.bs
youennf Dec 18, 2025
de082cd
Update index.bs
youennf Dec 18, 2025
45d2d9b
Update index.bs
youennf Dec 18, 2025
647e642
Update index.bs
youennf Dec 18, 2025
f8b612b
Update index.bs
youennf Dec 18, 2025
caae168
Update index.bs
youennf Dec 18, 2025
58f13e1
Update index.bs
youennf Dec 18, 2025
f8361b5
Update index.bs
youennf Dec 18, 2025
a036914
Update index.bs
youennf Dec 18, 2025
fcaf724
Update index.bs
youennf Dec 18, 2025
3f5bff2
Update index.bs
youennf Dec 18, 2025
6497737
Update index.bs
youennf Dec 19, 2025
25e3635
Update index.bs
youennf Dec 19, 2025
32bc221
Update index.bs
youennf Dec 19, 2025
f0e9a73
Update index.bs
youennf Dec 19, 2025
1badf3c
Update index.bs
youennf Dec 19, 2025
7e5676f
Update index.bs
youennf Dec 19, 2025
5448ecb
Update index.bs
youennf Dec 19, 2025
d867b65
Update index.bs
youennf Dec 19, 2025
9a634e4
Fix links
youennf Dec 19, 2025
e72f350
Remove keyId undefined handling
youennf Dec 19, 2025
5905a63
Use infra remove like done for set
youennf Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 58 additions & 20 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,16 @@ It uses an additional API on {{RTCRtpSender}} and {{RTCRtpReceiver}} to
insert the processing into the pipeline.

<pre class="idl">
typedef (SFrameTransform or RTCRtpScriptTransform) RTCRtpTransform;
typedef (SFrameSenderTransform or RTCRtpScriptTransform) RTCRtpSenderTransform;
typedef (SFrameReceiverTransform or RTCRtpScriptTransform) RTCRtpReceiverTransform;

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

partial interface RTCRtpReceiver {
attribute RTCRtpTransform? transform;
attribute RTCRtpReceiverTransform? transform;
};
</pre>

Expand Down Expand Up @@ -203,7 +204,7 @@ There is no guarantee on which frame will happen the switch from the previous tr
If a web application sets the transform synchronously at creation of the {{RTCRtpSender}} (for instance when calling addTrack), the transform will receive the first frame generated by the {{RTCRtpSender}}'s encoder.
Similarly, if a web application sets the transform synchronously at creation of the {{RTCRtpReceiver}} (for instance when calling addTrack, or at track event handler), the transform will receive the first full frame generated by the {{RTCRtpReceiver}}'s packetizer.

# SFrameTransform # {#sframe}
# SFrame transforms # {#sframe}

<p>
The APIs presented in this section allow applications to process SFrame data using specific cipher suites defined in [[RFC9605]].
Expand All @@ -226,30 +227,42 @@ dictionary SFrameTransformOptions {
typedef [EnforceRange] unsigned long long SmallCryptoKeyID;
typedef (SmallCryptoKeyID or bigint) CryptoKeyID;

interface mixin SFrameKeyManagement {
Promise<undefined> setEncryptionKey(CryptoKey key, optional CryptoKeyID keyID);
interface mixin SFrameEncrypterManager {
Promise<undefined> setEncryptionKey(CryptoKey key, CryptoKeyID keyId);
attribute EventHandler onerror;
};

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

[Exposed=Window]
interface SFrameSenderTransform {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a question in the past if SFrameTransform should have a 'RTC' prefix as other webrtc related stuff. I believe the answer not to add here a prefix had a good explanation that sframe is something bigger than just webrtc. Is this still relevant in current situation when sframe transform got splitted to sender/receiver? For me it in some way points now that webrtc will be main user of this transform.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to rename to RTCSFrameTransfrom, since it can only be used in RTC context now.
Let's do this in a follow-up though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uploaded #291

constructor(optional SFrameTransformOptions options = {});
};
SFrameSenderTransform includes SFrameEncrypterManager;

[Exposed=Window]
interface SFrameTransform : EventTarget {
interface SFrameReceiverTransform : EventTarget {
constructor(optional SFrameTransformOptions options = {});
};
SFrameTransform includes SFrameKeyManagement;
SFrameReceiverTransform includes SFrameDecrypterManager;

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

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

enum SFrameTransformErrorEventType {
"authentication",
Expand All @@ -273,9 +286,15 @@ dictionary SFrameTransformErrorEventInit : EventInit {
};
</xmp>

The <dfn constructor for="SFrameTransform" lt="SFrameTransform(options)"><code>new SFrameTransform(<var>options</var>)</code></dfn> constructor steps are:
The <dfn constructor for="SFrameSenderTransform" lt="SFrameSenderTransform(options)"><code>new SFrameSenderTransform(<var>options</var>)</code></dfn> constructor steps are:
1. Let |options| be the method's first argument.
1. Run the [=SFrame initialization algorithm=] with |this| and |options|.
1. Set |this|.`[[role]]` to 'encrypt'.

The <dfn constructor for="SFrameReceiverTransform" lt="SFrameReceiverTransform(options)"><code>new SFrameReceiverTransform(<var>options</var>)</code></dfn> constructor steps are:
1. Let |options| be the method's first argument.
1. Run the [=SFrame initialization algorithm=] with |this| and |options|.
1. Set |this|.`[[role]]` to 'decrypt'.

The <dfn constructor for="SFrameEncrypterStream" lt="SFrameEncrypterStream(options)"><code>new SFrameEncrypterStream(<var>options</var>)</code></dfn> constructor steps are:
1. Let |options| be the method's first argument.
Expand Down Expand Up @@ -324,14 +343,33 @@ The <dfn>SFrame transform algorithm</dfn>, given |this| and |frame|, runs these
1. [=ReadableStream/Enqueue=] |frame| in |this|.`[[transform]]`.

## Methods ## {#sframe-transform-methods}
The <dfn method for="SFrameTransform">setEncryptionKey(|key|, |keyID|)</dfn> method steps are:
The <dfn method for="SFrameEncrypterManager">setEncryptionKey(|key|, |keyId|)</dfn> method steps are:
1. Let |promise| be [=a new promise=].
2. If |keyID| is a {{bigint}} which cannot be represented as a integer between 0 and 2<sup>64</sup>-1 inclusive, [=reject=] |promise| with a {{RangeError}} exception.
3. Otherwise, [=in parallel=], run the following steps:
1. Set |key| with its optional |keyID| as key material to use for the SFrame transform algorithm, as defined by [[RFC9605]].
2. If setting the key material fails, [=reject=] |promise| with an {{InvalidModificationError}} exception and abort these steps.
3. [=Resolve=] |promise| with undefined.
4. Return |promise|.
1. If |keyId| is a {{bigint}} which cannot be represented as a integer between 0 and 2<sup>64</sup>-1 inclusive, [=reject=] |promise| with a {{RangeError}} exception and abort these steps.
1. [=In parallel=], run the following steps:
1. Set the SFrame transform encryption algorithm's key material to |key| and |keyId|, as defined by [[RFC9605]].
1. If setting the key material fails, [=queue a task=] to [=reject=] |promise| with an {{InvalidModificationError}} exception and abort these steps.
1. [=Queue a task=] to [=resolve=] |promise| with undefined.
1. Return |promise|.

The <dfn method for="SFrameDecrypterManager">addDecryptionKey(|key|, |keyId|)</dfn> method steps are:
1. Let |promise| be [=a new promise=].
1. If |keyId| is a {{bigint}} which cannot be represented as a integer between 0 and 2<sup>64</sup>-1 inclusive, [=reject=] |promise| with a {{RangeError}} exception, and abort these steps.
1. [=In parallel=], run the following steps:
1. Let |keyStore| be the key store used for the SFrame transform algorithm, as defined by [[RFC9605]].
1. [=map/set|Set=] |keyStore|[|keyId|] to |keyValue|.
1. If setting the key material fails, [=queue a task=] to [=reject=] |promise| with an {{InvalidModificationError}} exception and abort these steps.
1. [=Queue a task=] to [=resolve=] |promise| with undefined.
1. Return |promise|.

The <dfn method for="SFrameDecrypterManager">removeDecryptionKey(|keyId|)</dfn> method steps are:
1. Let |promise| be [=a new promise=].
1. If |keyId| is a {{bigint}} which cannot be represented as a integer between 0 and 2<sup>64</sup>-1 inclusive, [=reject=] |promise| with a {{RangeError}} exception, and abort these steps.
1. [=In parallel=], run the following steps:
1. Let |keyStore| be the key store used for the SFrame transform algorithm, as defined by [[RFC9605]].
1. [=map/remove|Remove=] |keyStore|[|keyId|].
1. [=Queue a task=] to [=resolve=] |promise| with undefined.
1. Return |promise|.


# Script Transform # {#scriptTransform}
Expand Down Expand Up @@ -528,7 +566,7 @@ interface RTCEncodedVideoFrame {
### Members ### {#RTCEncodedVideoFrame-members}
<dl dfn-for="RTCEncodedVideoFrame" class="dictionary-members">
<dt>
<dfn attribute>type</dfn> <span class="idlMemberType">RTCEncodedVideoFrameType</span>
<dfn attribute>type</dfn> <span class="idlMemberType">EncodedVideoChunkType</span>
</dt>
<dd>
<p>
Expand Down Expand Up @@ -1087,7 +1125,7 @@ The <dfn abstract-op>generate key frame algorithm</dfn>, given |promise|, |frame
For any [=encoder=] associated with an {{RTCRtpScriptTransformer}} |transformer|, the user agent MUST run the following steps just before any |frame| is [=ReadableStream/enqueued=] into |transformer|.{{[[readable]]}}:
1. Let |encoder| be |transformer|.{{[[frameSource]]}}.
1. If |encoder|.`[[pendingKeyFrameTasks]]` is undefined, abort these steps.
1. If |frame| is not a video {{RTCEncodedVideoFrameType/"key"}} frame, abort these steps.
1. If |frame| is not a video {{EncodedVideoChunkType/"key"}} frame, abort these steps.
1. For each |task| in |encoder|.`[[pendingKeyFrameTasks]]`, run the following steps:
1. If |frame| was generated for a layer [=list/contained=] in |task|.`[[layers]]`,
then run the following steps:
Expand Down