Skip to content

Commit f580cb7

Browse files
committed
Add SFrame packetization handling for SFrameTransform
Introduce RTCRtpScriptTransformType with "sframe" value.
1 parent 3cd9f83 commit f580cb7

File tree

1 file changed

+75
-23
lines changed

1 file changed

+75
-23
lines changed

index.bs

+75-23
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ The <dfn abstract-op>readEncodedData</dfn> algorithm is given a |rtcObject| as p
138138
1. Increment |rtcObject|.`[[lastEnqueuedFrameCounter]]` by <code>1</code>.
139139
1. Let |frame| be the newly produced frame.
140140
1. Set |frame|.`[[owner]]` to |rtcObject|.
141+
1. Set |this|.`[[writable]]` to |this|.`[[transform]]`.`[[writable]]`.
141142
1. Set |frame|.`[[counter]]` to |rtcObject|.`[[lastEnqueuedFrameCounter]]`.
142143
1. If the frame has been produced by a {{RTCRtpReceiver}}:
143144
1. If the relevant RTP packet contains the
@@ -150,6 +151,7 @@ The <dfn abstract-op>readEncodedData</dfn> algorithm is given a |rtcObject| as p
150151
[[RTP-EXT-CAPTURE-TIME#timestamp-interpolation|timestamp interpolation]] and set |frame|.`[[senderCaptureTimeOffset]]`
151152
to the most recent value that was present.
152153
1. Otherwise, set |frame|.`[[captureTime]]` to undefined and set |frame|.`[[senderCaptureTimeOffset]]` to undefined.
154+
1. If |frame| was produced by a [=SFrame depacketizer=], set |frame|.`[[useSFrame]]` to true.
153155
1. If the frame has been produced by a {{RTCRtpSender}}, set |frame|.`[[captureTime]]` to the capture timestamp
154156
using the methodology described in [[RTP-EXT-CAPTURE-TIME#absolute-capture-timestamp]] and set frame.`[[senderCaptureTimeOffset]]`
155157
to undefined.
@@ -162,8 +164,10 @@ The <dfn abstract-op>writeEncodedData</dfn> algorithm is given a |rtcObject| as
162164
1. Let |data| be |frame|.`[[data]]`.
163165
1. Let |serializedFrame| be [$StructuredSerializeWithTransfer$](|frame|, « |data| »).
164166
1. Let |frameCopy| be [$StructuredDeserializeWithTransfer$](|serializedFrame|, |frame|'s [=relevant realm=]).
167+
1. If |frame|.`[[useSFrame]]` is true, set |frameCopy|.`[[useSFrame]]` to true.
165168
1. Enqueue |frameCopy| for processing as if it came directly from the encoded data source, by running one of the following steps:
166169
* If |rtcObject| is a {{RTCRtpSender}}, enqueue |frameCopy| to |rtcObject|'s packetizer, to be processed [=in parallel=].
170+
If |frameCopy|.`[[useSFrame]]` is true, |rtcObject|'s MUST use a [=SFrame packetizer=] or skip processing of |frameCopy|.
167171
* If |rtcObject| is a {{RTCRtpReceiver}}, enqueue |frameCopy| it to |rtcObject|'s decoder, to be processed [=in parallel=].
168172
1. Return [=a promise resolved with=] undefined.
169173

@@ -177,26 +181,40 @@ As [$writeEncodedData$] ensures that the transform cannot reorder frames, this w
177181

178182
## Extension attribute ## {#attribute}
179183

180-
A RTCRtpTransform has two private slots called `[[readable]]` and `[[writable]]`.
184+
A RTCRtpTransform has the following private slots:
185+
1. `[[readable]]` of type {{ReadableStream}}.
186+
1. `[[writable]]` of type {{WritableStream}}.
187+
1. `[[owner]]` of type {{RTCRtpSender}} or {{RTCRtpReceiver}}.
188+
1. `[[useSFrame]]` of type boolean.
181189

182190
Each RTCRtpTransform has an <dfn abstract-op for=RTCRtpTransform>association steps</dfn> set, which is empty by default.
183191

184192
The <dfn attribute for="RTCRtpSender,RTCRtpReceiver">transform</dfn> getter steps are:
185193
1. Return [=this=].`[[transform]]`.
186194

187195
The `transform` setter steps are:
188-
2. Let |transform| be the argument to the setter.
189-
3. Let |checkedTransform| set to |transform| if it is not null or to an [=identity transform stream=] otherwise.
190-
3. Let |reader| be the result of [=ReadableStream/getting a reader=] for |checkedTransform|.`[[readable]]`.
191-
4. Let |writer| be the result of [=WritableStream/getting a writer=] for |checkedTransform|.`[[writable]]`.
192-
5. Initialize |newPipeToController| to a new {{AbortController}}.
193-
6. If [=this=].`[[pipeToController]]` is not null, run the following steps:
196+
1. Let |transform| be the argument to the setter.
197+
1. Let |checkedTransform| set to |transform| if it is not null or to an [=identity transform stream=] otherwise.
198+
1. Let |reader| be the result of [=ReadableStream/getting a reader=] for |checkedTransform|.`[[readable]]`.
199+
1. Let |writer| be the result of [=WritableStream/getting a writer=] for |checkedTransform|.`[[writable]]`.
200+
1. Initialize |newPipeToController| to a new {{AbortController}}.
201+
1. If [=this=].`[[pipeToController]]` is not null, run the following steps:
194202
1. [=AbortSignal/Add=] the [$chain transform algorithm$] to [=this=].`[[pipeToController]]`'s [=AbortController/signal=].
195203
2. [=AbortController/signal abort=] on [=this=].`[[pipeToController]]`.
196-
7. Else, run the [$chain transform algorithm$] steps.
197-
8. Set [=this=].`[[pipeToController]]` to |newPipeToController|.
198-
9. Set [=this=].`[[transform]]` to |transform|.
199-
10. Run the steps in the set of [$association steps$] of |transform| with [=this=].
204+
1. Else, run the [$chain transform algorithm$] steps.
205+
1. If [=this=] is a {{RTCRtpSender}}, run the following substeps:
206+
1. Let |useSFrame| be true if [=this=] is configured to use a [=SFrame packetizer=] and false otherwise.
207+
1. If |useSFrame| is equal to |checkedTransform|.`[[useSFrame]]`, abort these substeps.
208+
1. Configure [=this=]'s packetizer to use SFrame if |checkedTransform|.`[[useSFrame]]` is true and to not use SFrame if |checkedTransform|.`[[useSFrame]]` is false.
209+
1. [=Update the negotiation-needed flag=] for [=this=]'s connection.
210+
1. Otherwise, run the following steps:
211+
1. Let |useSFrame| be true if [=this=] is configured to use a [=SFrame depacketizer=] and false otherwise.
212+
1. If |useSFrame| is equal to |checkedTransform|.`[[useSFrame]]`, abort these substeps.
213+
1. Configure [=this=]'s depacketizer to use SFrame if |checkedTransform|.`[[useSFrame]]` is true and to not use SFrame if |checkedTransform|.`[[useSFrame]]` is false.
214+
1. [=Update the negotiation-needed flag=] for [=this=]'s connection.
215+
1. Set [=this=].`[[pipeToController]]` to |newPipeToController|.
216+
1. Set [=this=].`[[transform]]` to |transform|.
217+
1. Run the steps in the set of [$association steps$] of |transform| with [=this=].
200218

201219
The <dfn abstract-op>chain transform algorithm</dfn> steps are defined as:
202220
1. If |newPipeToController|'s [=AbortController/signal=] is [=AbortSignal/aborted=], abort these steps.
@@ -243,7 +261,8 @@ SFrameTransform includes GenericTransformStream;
243261
enum SFrameTransformErrorEventType {
244262
"authentication",
245263
"keyID",
246-
"syntax"
264+
"syntax",
265+
"packetization"
247266
};
248267

249268
[Exposed=(Window,DedicatedWorker)]
@@ -270,13 +289,19 @@ The <dfn constructor for="SFrameTransform" lt="SFrameTransform(options)"><code>n
270289
5. Set |this|.`[[role]]` to |options|["{{SFrameTransformOptions/role}}"].
271290
6. Set |this|.`[[readable]]` to |this|.`[[transform]]`.`[[readable]]`.
272291
7. Set |this|.`[[writable]]` to |this|.`[[transform]]`.`[[writable]]`.
292+
7. Set |this|.`[[useSFrame]]` to true.
273293

274294
## Algorithm ## {#sframe-transform-algorithm}
275295

276296
The SFrame transform algorithm, given |sframe| as a SFrameTransform object and |frame|, runs these steps:
277297
1. Let |role| be |sframe|.`[[role]]`.
278-
1. If |frame|.`[[owner]]` is a {{RTCRtpSender}}, set |role| to 'encrypt'.
279-
1. If |frame|.`[[owner]]` is a {{RTCRtpReceiver}}, set |role| to 'decrypt'.
298+
1. If |sframe|.`[[owner]]` is a {{RTCRtpSender}}, set |role| to 'encrypt'.
299+
1. If |sframe|.`[[owner]]` is a {{RTCRtpReceiver}}, set |role| to 'decrypt'.
300+
1. If |sframe|.`[[owner]]` is a {{RTCRtpReceiver}} and |frame|.`[[useSFrame]]` is not true, [=queue a task=] to run the following steps:
301+
1. [=fire an event=] named {{SFrameTransform/onerror|error}} at |sframe|,
302+
using the {{SFrameTransformErrorEvent}} interface with its {{SFrameTransformErrorEvent/errorType}} attribute set to {{SFrameTransformErrorEventType/packetization}}
303+
and its {{SFrameTransformErrorEvent/frame}} attribute set to |frame|.
304+
1. Abort these steps.
280305
1. Let |data| be undefined.
281306
1. If |frame| is a {{BufferSource}}, set |data| to |frame|.
282307
1. If |frame| is a {{RTCEncodedAudioFrame}}, set |data| to |frame|.{{RTCEncodedAudioFrame/data}}
@@ -297,6 +322,7 @@ The SFrame transform algorithm, given |sframe| as a SFrameTransform object and |
297322
1. If |frame| is a {{BufferSource}}, set |frame| to |buffer|.
298323
1. If |frame| is a {{RTCEncodedAudioFrame}}, set |frame|.{{RTCEncodedAudioFrame/data}} to |buffer|.
299324
1. If |frame| is a {{RTCEncodedVideoFrame}}, set |frame|.{{RTCEncodedVideoFrame/data}} to |buffer|.
325+
1. Set |frame|.`[[useSFrame]]` to true.
300326
1. [=ReadableStream/Enqueue=] |frame| in |sframe|.`[[transform]]`.
301327

302328
## Methods ## {#sframe-transform-methods}
@@ -309,6 +335,27 @@ The <dfn method for="SFrameTransform">setEncryptionKey(|key|, |keyID|)</dfn> met
309335
3. [=Resolve=] |promise| with undefined.
310336
4. Return |promise|.
311337

338+
## SFrame packetization integration ## {#sframe-packetization}
339+
340+
A <dfn>SFrame packetizer</dfn> is responsible to generate SFrame packets from media content.
341+
In the context of this specification, the [=SFrame packetizer=] is not responsible for doing the actual encryption.
342+
Instead, the transform is responsible for doing so. The [=SFrame packetizer=] is responsible for splitting
343+
SFrame frames as needed so that they fit in RTP packets.
344+
345+
Similarly, a <dfn>SFrame depacketizer</dfn> is responsible to assemble RTP packets into a complete SFrame frame.
346+
It is not responsible for doing the actual decryption, the transform is responsible for doing so.
347+
348+
WebRTC encoded transform model is a per frame processing. SFrame can either be applied on each frame or on subframes.
349+
WebRTC encoded transform model is naturally aligned with aplying SFrame on a frame as a whole.
350+
To preserve WebRTC encoded transform model when applying SFrame on subframes, the following conceptual steps can be done:
351+
1. On sending side, the {{SFrameTransform}} may first split the media frame in subframes like would do a regular media packetizer,
352+
and apply the SFrame encryption on each subframe. It then concatenates the encrypted subframes as a unique encrypted frame.
353+
The transform provides the encrypted frame and information of each subframe so that the [=SFrame packetizer=]
354+
generates individual packets for each subframe.
355+
2. On receiving side, the [=SFrame depacketizer=] assembles all individual subframe RTP packets as a unique encrypted frame.
356+
It is responsible to give the necessary subframe information to the transform so that the transform can apply the SFrame decryption
357+
on each individual subframe contained in the unique encrypted frame and concatenate each decrypted subframe as a unique decrypted media frame.
358+
If decryption of a single subframe fails, the whole encrypted frame is discarded.
312359

313360
# RTCRtpScriptTransform # {#scriptTransform}
314361

@@ -942,9 +989,13 @@ interface RTCRtpScriptTransformer : EventTarget {
942989
readonly attribute any options;
943990
};
944991

992+
enum RTCRtpScriptTransformType {
993+
"sframe"
994+
};
995+
945996
[Exposed=Window]
946997
interface RTCRtpScriptTransform {
947-
constructor(Worker worker, optional any options, optional sequence&lt;object&gt; transfer);
998+
constructor(Worker worker, optional any options, optional sequence&lt;object&gt; transfer, optional RTCRtpScriptTransformType type);
948999
};
9491000

9501001
[Exposed=DedicatedWorker]
@@ -956,15 +1007,16 @@ interface KeyFrameRequestEvent : Event {
9561007

9571008
## Operations ## {#RTCRtpScriptTransform-operations}
9581009

959-
The <dfn constructor for="RTCRtpScriptTransform" lt="RTCRtpScriptTransform(worker, options)"><code>new RTCRtpScriptTransform(|worker|, |options|, |transfer|)</code></dfn> constructor steps are:
1010+
The <dfn constructor for="RTCRtpScriptTransform" lt="RTCRtpScriptTransform(worker, options)"><code>new RTCRtpScriptTransform(|worker|, |options|, |transfer|, |type|)</code></dfn> constructor steps are:
9601011
1. Set |t1| to an [=identity transform stream=].
961-
2. Set |t2| to an [=identity transform stream=].
962-
3. Set |this|.`[[writable]]` to |t1|.`[[writable]]`.
963-
4. Set |this|.`[[readable]]` to |t2|.`[[readable]]`.
964-
5. Let |serializedOptions| be the result of [$StructuredSerializeWithTransfer$](|options|, |transfer|).
965-
6. Let |serializedReadable| be the result of [$StructuredSerializeWithTransfer$](|t1|.`[[readable]]`, « |t1|.`[[readable]]` »).
966-
7. Let |serializedWritable| be the result of [$StructuredSerializeWithTransfer$](|t2|.`[[writable]]`, « |t2|.`[[writable]]` »).
967-
8. [=Queue a task=] on the DOM manipulation [=task source=] |worker|'s global scope to run the following steps:
1012+
1. Set |t2| to an [=identity transform stream=].
1013+
1. Set |this|.`[[writable]]` to |t1|.`[[writable]]`.
1014+
1. Set |this|.`[[readable]]` to |t2|.`[[readable]]`.
1015+
1. If |type| is equal to {{RTCRtpScriptTransformType/"sframe"}}, set |this|.`[[useSFrame]]` to true.
1016+
1. Let |serializedOptions| be the result of [$StructuredSerializeWithTransfer$](|options|, |transfer|).
1017+
1. Let |serializedReadable| be the result of [$StructuredSerializeWithTransfer$](|t1|.`[[readable]]`, « |t1|.`[[readable]]` »).
1018+
1. Let |serializedWritable| be the result of [$StructuredSerializeWithTransfer$](|t2|.`[[writable]]`, « |t2|.`[[writable]]` »).
1019+
1. [=Queue a task=] on the DOM manipulation [=task source=] |worker|'s global scope to run the following steps:
9681020
1. Let |transformerOptions| be the result of [$StructuredDeserializeWithTransfer$](|serializedOptions|, the current Realm).
9691021
2. Let |readable| be the result of [$StructuredDeserializeWithTransfer$](|serializedReadable|, the current Realm).
9701022
3. Let |writable| be the result of [$StructuredDeserializeWithTransfer$](|serializedWritable|, the current Realm).

0 commit comments

Comments
 (0)