1. Introduction
The [WEBRTC-NV-USE-CASES] document describes the use-case of
-
Untrusted JavaScript Cloud Conferencing
This specification provides access to encoded media, which is the output of the encoder part of a codec and the input to the decoder part of a codec which allows the user agent to apply encryption locally.
The interface is inspired by [WEBCODECS] to provide access to such functionality while retaining the setup flow of RTCPeerConnection
2. Specification
The Streams definition doesn’t use WebIDL much, but the WebRTC spec does. This specification shows the IDL extensions for WebRTC.
It uses an additional API on RTCRtpSender and RTCRtpReceiver to
insert the processing into the pipeline.
typedef (SFrameTransform or RTCRtpScriptTransform ); // New methods for RTCRtpSender and RTCRtpReceiverRTCRtpTransform partial interface RTCRtpSender {attribute RTCRtpTransform ?transform ; };partial interface RTCRtpReceiver {attribute RTCRtpTransform ?transform ; };
This API allows manipulation of encoded frames in the media
pipeline between the processing steps of an RTCRtpSender’s
underlying encoder and packetizer, and/or
between an RTCRtpReceiver’s underlying depacketizer
and decoder.
The encoder and depacketizer each have a [[processedFramesQueue]] internal slot initialized to an empty queue, and a [[transformFrameAlgorithm]] internal slot initialized to the passthrough algorithm which, given an encoded frame frame, is to return frame.
Whenever the encoder outputs an encoded frame, the user agent MUST invoke the encoder.[[transformFrameAlgorithm]] on it and pass the result to the associated packetizer in place of the original frame.
Whenever the depacketizer outputs an encoded frame, the user agent MUST invoke the depacketizer.[[transformFrameAlgorithm]] on it and pass the result to the associated decoder in place of the original frame.
2.1. Extension operation
At construction of each RTCRtpSender or RTCRtpReceiver, run the following steps:
-
Initialize this.
[[transform]]to null. -
Initialize this.
[[pipeToController]]to null. -
Initialize this.
[[frameSource]]to this’s encoder if this is anRTCRtpSenderor this’s depacketizer otherwise.
2.1.1. Stream processing
Streams backpressure can optimize throughput while limiting processing and memory consumption by pausing data production as early as possible in a data pipeline. This proves useful in contexts where reliability is essential and latency is less of a concern. On the other hand, WebRTC media pipelines favour low latency over reliability, for instance by allowing to drop frames at various places and by using recovery mechanisms. Buffering within a transform would add latency without allowing web applications to adapt much. The User Agent is responsible for doing these adaptations, especially since it controls both ends of the transform. For those reasons, streams backpressure is disabled in WebRTC encoded transforms.
The readEncodedData algorithm is given an RTCRtpScriptTransformer
transformer as parameter, and frame as input. It is defined by running the following steps:
-
Set frame.
[[owner]]to transformer.[[frameSource]]. -
Set frame.
[[counter]]to transformer.[[lastEnqueuedFrameCounter]]. -
If frame.
[[owner]]is a depacketizer:-
If the relevant RTP packet contains the RTP Header Extension for Absolute Capture Time, set frame.
[[captureTime]]to the absolute capture timestamp field and set frame.[[senderCaptureTimeOffset]]to the capture clock offset field if it is present. -
Otherwise, if the relevant RTP packet does not contain the RTP Header Extension for Absolute Capture Time but a previous RTP packet did, set frame.
[[captureTime]]to the result of calculating the absolute capture timestamp according to timestamp interpolation and set frame.[[senderCaptureTimeOffset]]to the most recent value that was present. -
Otherwise, set frame.
[[captureTime]]to undefined and set frame.[[senderCaptureTimeOffset]]to undefined.
-
-
If frame.
[[owner]]is an encoder, set frame.[[captureTime]]to the capture timestamp using the methodology described in RTP Header Extension for Absolute Capture Time § absolute-capture-timestamp and set frame.[[senderCaptureTimeOffset]]to undefined. -
Enqueue frame into transformer.
[[readable]].
The writeEncodedData algorithm is given an RTCRtpScriptTransformer
transformer as parameter and a frame as input. It is defined by running the following steps:
-
If frame.
[[owner]]is not equal to transformer.[[frameSource]], abort these steps and return a promise resolved with undefined. A processor cannot create frames, or move frames between streams. -
If frame.
[[counter]]is equal or smaller than transformer.[[lastReceivedFrameCounter]], abort these steps and return a promise resolved with undefined. A processor cannot reorder frames, although it may delay them or drop them. -
Set transformer.
[[lastReceivedFrameCounter]]to frame.[[counter]]. -
Let data be frame.
[[data]]. -
Let serializedFrame be StructuredSerializeWithTransfer(frame, « data »).
-
Let frameCopy be StructuredDeserializeWithTransfer(serializedFrame, frame’s relevant realm).
-
Let processedFrame be frameCopy’s underlying encoded frame.
-
In parallel, enqueue processedFrame onto transformer.
[[frameSource]].[[processedFramesQueue]]. -
Return a promise resolved with undefined.
On the sender side, as part of readEncodedData, frames produced by the encoder MUST be enqueued into transformer.[[readable]] in the encoder’s output order.
As writeEncodedData ensures that the transform cannot reorder frames, the encoder’s output order is also the order followed by packetizers to generate RTP packets and assign RTP packet sequence numbers.
The packetizer may expect the transformed data to still conform to the original format, e.g. a series of NAL units separated by Annex B start codes.
On the receiver side, as part of readEncodedData, frames produced by the depacketizer MUST be enqueued into transformer.[[readable]] in the same encoder’s output order.
To ensure the order is respected, the depacketizer will typically use RTP packet sequence numbers to reorder RTP packets as needed before enqueuing frames into transformer.[[readable]].
As writeEncodedData ensures that the transform cannot reorder frames, this will be the order expected by the decoder.
2.1.2. RTCRtpTransform common processing
An RTCRtpTransform has a private slot:
-
[[owner]]of typeRTCRtpSenderorRTCRtpReceiver, initialized to null.
Each RTCRtpTransform has an association algorithm and a disassociation algorithm, both empty by default.
2.2. Extension attribute
The transform getter steps are to
return this.[[transform]]. The setter steps are:
-
Let transform be the argument to the setter.
-
If transform is not null and transform.
[[owner]]is not null, throw aInvalidStateErrorand abort these steps. -
Set transform.
[[owner]]to this. -
Let oldTransform be this.
[[transform]]. -
If oldTransform is not null, run oldTransform’s disassociation algorithm.
-
Set this.
[[transform]]to transform. -
If transform is not null, run transform’s association algorithm with this.
-
If transform is null, run the following steps:
-
Let frameSource be this.
[[frameSource]]. -
In parallel, set frameSource.[[transformFrameAlgorithm]] to the passthrough algorithm.
-
This algorithm is defined so that transforms can be updated dynamically. There is no guarantee on which frame will happen the switch from the previous transform to the new transform.
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.
3. SFrameTransform
The APIs presented in this section allow applications to process SFrame data using specific cipher suites defined in [RFC9605].
// List of supported cipher suites, as defined in [[RFC9605]] section 4.5.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" dictionary {SFrameTransformOptions required SFrameCipherSuite ; };cipherSuite typedef [EnforceRange ]unsigned long long ;SmallCryptoKeyID typedef (SmallCryptoKeyID or bigint );CryptoKeyID interface mixin {SFrameKeyManagement Promise <undefined >(setEncryptionKey CryptoKey ,key optional CryptoKeyID );keyID attribute EventHandler ; }; [onerror Exposed =Window ]interface :SFrameTransform EventTarget {constructor (optional SFrameTransformOptions = {}); };options SFrameTransform includes SFrameKeyManagement ; [Exposed =(Window ,DedicatedWorker )]interface :SFrameEncrypterStream EventTarget {constructor (optional SFrameTransformOptions = {}); };options SFrameEncrypterStream includes GenericTransformStream ;SFrameEncrypterStream includes SFrameKeyManagement ; [Exposed =(Window ,DedicatedWorker )]interface :SFrameDecrypterStream EventTarget {constructor (optional SFrameTransformOptions = {}); };options SFrameDecrypterStream includes GenericTransformStream ;SFrameDecrypterStream includes SFrameKeyManagement ;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
The new SFrameTransform(options) constructor steps are:
-
Let options be the method’s first argument.
-
Run the SFrame initialization algorithm with this and options.
The new SFrameEncrypterStream(options) constructor steps are:
-
Let options be the method’s first argument.
-
Run the SFrame initialization algorithm with this and options.
-
Set this.
[[role]]to 'encrypt'.
The new SFrameDecrypterStream(options) constructor steps are:
-
Let options be the method’s first argument.
-
Run the SFrame initialization algorithm with this and options.
-
Set this.
[[role]]to 'decrypt'.
3.1. Algorithms
The SFrame initialization algorithm, given this and options, runs these steps:
-
Let transformAlgorithm be an algorithm which takes a frame as input and runs the SFrame transform algorithm with this and frame.
-
Set this.
[[transform]]to a newTransformStream. -
Set up this.
[[transform]]with transformAlgorithm set to transformAlgorithm. -
Set this.
[[cipherSuite]]to options["cipherSuite"]. -
Set this.
[[readable]]to this.[[transform]].[[readable]]. -
Set this.
[[writable]]to this.[[transform]].[[writable]].
The SFrame transform algorithm, given this and frame, runs these steps:
-
Let role be this.
[[role]]. -
If this.
[[owner]]is anRTCRtpSender, set role to 'encrypt'. -
If this.
[[owner]]is anRTCRtpReceiver, set role to 'decrypt'. -
Let data be undefined.
-
If frame is a
BufferSource, set data to frame. -
If frame is a
RTCEncodedAudioFrame, set data to frame.data -
If frame is a
RTCEncodedVideoFrame, set data to frame.data -
If data is undefined, abort these steps.
-
Let buffer be the result of running the SFrame algorithm with data, this.
[[cipherSuite]], and role as parameters. This algorithm is defined by [RFC9605] and returns anArrayBuffer. -
If the SFrame algorithm exits abruptly with an error, queue a task to run the following sub steps:
-
If the processing fails on decryption side due to data not following the SFrame format, fire an event named
errorat this, using theSFrameTransformErrorEventinterface with itserrorTypeattribute set tosyntaxand itsframeattribute set to frame. -
If the processing fails on decryption side due to the key identifier parsed in data being unknown, fire an event named
errorat this, using theSFrameTransformErrorEventinterface with itserrorTypeattribute set tokeyID, itsframeattribute set to frame and itskeyIDattribute set to the keyID value parsed in the SFrame header. -
If the processing fails on decryption side due to validation of the authentication tag, fire an event named
errorat this, using theSFrameTransformErrorEventinterface with itserrorTypeattribute set toauthenticationand itsframeattribute set to frame. -
Abort these steps.
-
-
If frame is a
BufferSource, set frame to buffer. -
If frame is a
RTCEncodedAudioFrame, set frame.datato buffer. -
If frame is a
RTCEncodedVideoFrame, set frame.datato buffer. -
Enqueue frame in this.
[[transform]].
3.2. Methods
ThesetEncryptionKey(key, keyID) method steps are:
-
Let promise be a new promise.
-
If keyID is a
bigintwhich cannot be represented as a integer between 0 and 264-1 inclusive, reject promise with aRangeErrorexception. -
Otherwise, in parallel, run the following steps:
-
Set key with its optional keyID as key material to use for the SFrame transform algorithm, as defined by [RFC9605].
-
If setting the key material fails, reject promise with an
InvalidModificationErrorexception and abort these steps. -
Resolve promise with undefined.
-
-
Return promise.
4. Script Transform
In this section, the capture system refers to the system where media is sourced from and the sender system
refers to the system that is sending RTP and RTCP packets to the receiver system where RTCEncodedFrameMetadata data is populated.
4.1. RTCEncodedFrameMetadata dictionary
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. Members
-
synchronizationSource, of type unsigned longunsigned long -
The synchronization source (ssrc) identifier is an unsigned integer value per [RFC3550] used to identify the stream of RTP packets that the encoded frame object is describing.
-
payloadType, of type octetoctet -
The payload type is an unsigned integer value in the range from 0 to 127 per [RFC3550] that is used to describe the format of the RTP payload.
-
contributingSources, of typesequence<unsigned long>sequence<unsigned long> -
The list of contribution sources (csrc list) as defined in [RFC3550].
-
rtpTimestamp, of type unsigned longunsigned long -
The RTP timestamp identifier is an unsigned integer value per [RFC3550] that reflects the sampling instant of the first octet in the RTP data packet.
-
receiveTime, of type DOMHighResTimeStampDOMHighResTimeStamp -
For frames coming from an RTCRtpReceiver, represents the timestamp of the last received packet used to produce this media frame. This timestamp is relative to
Performance.timeOrigin. -
captureTime, of type DOMHighResTimeStampDOMHighResTimeStamp -
The capture time of this frame in the capture system’s clock. On populating this member, the user agent MUST return the value of the frame’s
[[captureTime]]slot, shifted to be relative toPerformance.timeOrigin. -
senderCaptureTimeOffset, of type DOMHighResTimeStampDOMHighResTimeStamp -
The
senderCaptureTimeOffsetis the sender system’s estimate of the offset between its own NTP clock and the capture system’s NTP clock, for the same frame that thecaptureTimewas originated from. On populating this member, the user agent MUST return the value of the frame’s[[senderCaptureTimeOffset]]slot. -
mimeType, of type DOMStringDOMString -
The codec MIME media type/subtype defined in the IANA media types registry [IANA-MEDIA-TYPES], e.g. audio/opus or video/VP8.
4.2. RTCEncodedVideoFrameType dictionary
// New enum for video frame types. Will eventually re-use the equivalent defined // by WebCodecs.enum RTCEncodedVideoFrameType {"empty" ,"key" ,"delta" , };
| Enum value | Description |
|---|---|
empty
|
This frame contains no data. |
key
|
This frame can be decoded without reference to any other frames. |
delta
|
This frame references another frame and can not be decoded without that frame. |
4.3. RTCEncodedVideoFrameMetadata dictionary
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.3.1. Members
-
frameId, of type unsigned long longunsigned long long -
An identifier for the encoded frame, monotonically increasing in decode order. Its lower 16 bits match the frame_number of the AV1 Dependency Descriptor Header Extension defined in Appendix A of [AV1-RTP-SPEC], if present. Only present for received frames if the Dependency Descriptor Header Extension is present.
-
dependencies, of typesequence<unsigned long long>sequence<unsigned long long> -
List of frameIds of frames this frame references. Only present for received frames if the AV1 Dependency Descriptor Header Extension defined in Appendix A of [AV1-RTP-SPEC] is present.
-
timestamp, of type long longlong long -
The media presentation timestamp (PTS) in microseconds of raw frame, matching the
timestampfor raw frames which correspond to this frame.
4.4. RTCEncodedVideoFrame interface
dictionary {RTCEncodedVideoFrameOptions RTCEncodedVideoFrameMetadata ; }; // New interfaces to define encoded video and audio frames. Will eventually // re-use or extend the equivalent defined in WebCodecs. [metadata Exposed =(Window ,DedicatedWorker ),Serializable ]interface RTCEncodedVideoFrame {(constructor RTCEncodedVideoFrame ,originalFrame optional RTCEncodedVideoFrameOptions = {});options readonly attribute RTCEncodedVideoFrameType type ;attribute ArrayBuffer data ;RTCEncodedVideoFrameMetadata getMetadata (); };
4.4.1. Constructor
-
constructor() -
Creates a new
RTCEncodedVideoFramefrom the given originalFrame and options.[metadata]. The newly created frame is completely independent of originalFrame, with its[[data]]being a deep copy of originalFrame.[[data]]. The new frame’s[[metadata]]is a deep copy of originalFrame.[[metadata]], with fields replaced with deep copies of the fields present in options.[metadata].When called, run the following steps:
-
Set this.
[[type]]to originalFrame.[[type]]. -
Let this.
[[data]]be the result of [CloneArrayBuffer](originalFrame.[[data]], 0, originalFrame.[[data]].[[ArrayBufferByteLength]]). -
Let
[[metadata]]represent the metadata associated with this newly constructed frame.-
For each {
[[key]],[[value]]} pair of originalFrame.[[getMetadata()]], set[[metadata]].[[key]]to a deep copy of[[value]]. -
For each {
[[key]],[[value]]} pair of options.[metadata], set[[metadata]].[[key]]to a deep copy of[[value]].
-
-
4.4.2. Members
-
type, of type RTCEncodedVideoFrameType, readonlyRTCEncodedVideoFrameType -
The type attribute allows the application to determine when a frame is a key frame or a delta frame. On getting, this.
[[type]]MUST be returned. -
data, of type ArrayBufferArrayBuffer -
The encoded frame data. The format of the data depends on the video codec that is used to encode/decode the frame which can be determined by looking at the
mimeType. For SVC, each spatial layer is transformed separately. On getting, this.[[data]]MUST be returned. On setting, this.[[data]]MUST be set to the new value.Since packetizers may drop certain elements, e.g. AV1 temporal delimiter OBUs, the input to a receive-side transform may be different from the output of a send-side transform.
The following table gives a number of examples:
mimeType Data format video/VP8 The data starts with the "uncompressed data chunk" defined in section 9.1 of [RFC6386] and is followed by the rest of the frame data. The VP8 payload descriptor is not accessible. video/VP9 The data is a frame as described in Section 6 of [VP9]. The VP9 payload descriptor is not accessible. video/H264 The data is a series of NAL units in Annex B format, as defined in [ITU-T-REC-H.264] Annex B. video/AV1 The data is a series of OBUs compliant to the low-overhead bitstream format as described in Section 5 of [AV1]. The AV1 aggregation header is not accessible.
4.4.3. Methods
-
getMetadata() -
Returns the metadata associated with the frame.
4.4.4. Serialization
RTCEncodedVideoFrame objects are serializable objects.
Their serialization steps, given value, serialized, and forStorage, are:
-
If forStorage is true, then throw a
DataCloneError. -
Set serialized.
[[type]]to the value of value.[[type]]. -
Set serialized.
[[metadata]]to an internal representation of value’s metadata. -
Set serialized.
[[data]]to the sub-serialization of value.[[data]].
Their deserialization steps, given serialized, value and realm, are:
-
Set value.
[[type]]to serialized.[[type]]. -
Set value’s metadata to the platform object representation of serialized.
[[metadata]]. -
Set value.
[[data]]to the sub-deserialization of serialized.[[data]].
The internal form of a serialized RTCEncodedVideoFrame is not observable;
it is defined chiefly so that it can be used with frame cloning in the
writeEncodedData algorithm and in the structuredClone() operation.
An implementation is therefore free to choose whatever method works best.
4.5. RTCEncodedAudioFrameMetadata dictionary
dictionary RTCEncodedAudioFrameMetadata :RTCEncodedFrameMetadata {short sequenceNumber ;double audioLevel ; };
4.5.1. Members
-
sequenceNumber, of type shortshort -
The RTP sequence number as defined in [RFC3550]. Only exists for incoming audio frames.
Comparing two sequence numbers requires serial number arithmetic described in [RFC1982].
-
audioLevel, of type doubledouble -
The audio level of this frame. The value is between 0..1 (linear), where 1.0 represents 0 dBov, 0 represents silence, and 0.5 represents approximately 6 dBSPL change in the sound pressure level from 0 dBov.
If the frame comes from a remotely sourced track, this MUST be converted from the level value defined in [RFC6464]. If the [RFC6464] header extension is not present in the received packets of the frame, this value MUST be absent. This RFC defines the audio level as an integral value from 0 to 127 representing the audio level in negative decibels relative to the loudest signal that the system could possibly encode. Thus, 0 represents the loudest signal the system could possibly encode, and 127 represents silence. To convert these values to the linear 0..1 range, a value of 127 is converted to 0, and all other values are converted using the equation:
10^(-rfc_level/20).If the frame comes from a locally sourced track, the level MUST be taken directly from the source and used as input to generate a value for the [RFC6464] header extension, if negotiated.
4.6. RTCEncodedAudioFrame interface
dictionary {RTCEncodedAudioFrameOptions RTCEncodedAudioFrameMetadata ; }; [metadata Exposed =(Window ,DedicatedWorker ),Serializable ]interface RTCEncodedAudioFrame {(constructor RTCEncodedAudioFrame ,originalFrame optional RTCEncodedAudioFrameOptions = {});options attribute ArrayBuffer data ;RTCEncodedAudioFrameMetadata getMetadata (); };
4.6.1. Constructor
-
constructor() -
Creates a new
RTCEncodedAudioFramefrom the given originalFrame and options.[metadata]. The newly created frame is completely independent of originalFrame, with its[[data]]being a deep copy of originalFrame.[[data]]. The new frame’s[[metadata]]is a deep copy of originalFrame.[[metadata]], with fields replaced with deep copies of the fields present in options.[metadata].When called, run the following steps:
-
Let this.
[[data]]be the result of [CloneArrayBuffer](originalFrame.[[data]], 0, originalFrame.[[data]].[[ArrayBufferByteLength]]). -
Let
[[metadata]]represent the metadata associated with this newly constructed frame.-
For each {
[[key]],[[value]]} pair of originalFrame.[[getMetadata()]], set[[metadata]].[[key]]to a deep copy of[[value]]. -
For each {
[[key]],[[value]]} pair of options.[metadata], set[[metadata]].[[key]]to a deep copy of[[value]].
-
-
4.6.2. Members
-
data, of type ArrayBufferArrayBuffer -
The encoded frame data. The format of the data depends on the audio codec that is used to encode/decode the frame which can be determined by looking at the
mimeType. On getting, this.[[data]]MUST be returned. On setting, this.[[data]]MUST be set to the new value. The following table gives a number of examples:mimeType Data format audio/opus The data is Opus packets, as described in section 3 of [RFC6716]. audio/PCMU The data is a sequence of bytes of arbitrary length, where each byte is a u-law encoded PCM sample as defined by Table 2a and 2b in [ITU-G.711]. audio/PCMA The data is a sequence of bytes of arbitrary length, where each byte is an A-law encoded PCM sample as defined by Tables 1a and 1b in [ITU-G.711]. audio/G722 The data is G.722 audio as described in [ITU-G.722]. audio/RED The data is Redundant Audio Data as described in section 3 of [RFC2198]. audio/CN The data is Comfort Noise as described in section 3 of [RFC3389].
4.6.3. Methods
-
getMetadata() -
Returns the metadata associated with the frame.
4.6.4. Serialization
RTCEncodedAudioFrame objects are serializable objects.
Their serialization steps, given value, serialized, and forStorage, are:
-
If forStorage is true, then throw a
DataCloneError. -
Set serialized.
[[metadata]]to an internal representation of value’s metadata. -
Set serialized.
[[data]]to the sub-serialization of value.[[data]].
Their deserialization steps, given serialized, value and realm, are:
-
Set value’s metadata to the platform object representation of serialized.
[[metadata]] -
Set value.
[[data]]to the sub-deserialization of serialized.[[data]].
5. RTCRtpScriptTransform interface
[Exposed =Window ]interface RTCRtpScriptTransform {constructor (Worker ,worker optional any ,options optional sequence <object >); };transfer
5.1. Internal slots
An RTCRtpScriptTransform object has the following internal slot:
| Internal Slot | Description (non-normative) |
|---|---|
[[worker]]
| The Worker provided in the constructor.
|
5.2. Constructor
The new RTCRtpScriptTransform(worker, options, transfer) constructor steps are:
-
Initialize this’s internal slot as follows:
[[worker]]-
worker
-
Let serializedOptions be the result of StructuredSerializeWithTransfer(options, transfer).
-
Queue a global task on the DOM manipulation task source with worker’s
WorkerGlobalScopeto run the following steps:-
Let transformerOptions be the result of StructuredDeserializeWithTransfer(serializedOptions, the current Realm).
-
Let transformer be the result of creating a
RTCRtpScriptTransformerwith transformerOptions. -
Fire an event named
rtctransformusingRTCTransformEventwithtransformerset to transformer on transformer’s relevant global object.
-
// FIXME: Describe error handling (worker closing flag true at RTCRtpScriptTransform creation time. And worker being terminated while transform is processing data).
5.3. Algorithms
Each RTCRtpScriptTransform has the following association algorithm, given rtcObject:
-
Let transform be the
RTCRtpScriptTransformobject that owns the association algorithm. -
Let frameSource be rtcObject’s
[[frameSource]]. -
Let workerGlobalScope be transform.
[[worker]]’sWorkerGlobalScope. -
Queue a global task on the DOM manipulation task source with workerGlobalScope to run the following steps:
-
Let transformer be the
RTCRtpScriptTransformerobject associated with transform. -
Set transformer.
[[frameSource]]to frameSource.
-
-
In parallel, set frameSource.[[transformFrameAlgorithm]] to the following steps, given an encoded frame frame as input:
-
Queue a global task on the DOM manipulation task source with workerGlobalScope to run the following steps:
-
Let transformer be the
RTCRtpScriptTransformerobject associated with transform. -
Let jsFrame be a new
RTCEncodedVideoFramefrom frame if frame is a video frame, or a newRTCEncodedAudioFramefrom frame otherwise. -
Invoke readEncodedData with transformer and jsFrame.
-
-
Wait for frameSource.[[processedFramesQueue]] to become non-empty.
-
Return the result of dequeueing from frameSource.[[processedFramesQueue]].
-
Each RTCRtpScriptTransform has the following disassociation algorithm:
-
Let transform be the
RTCRtpScriptTransformobject that owns the disassociation algorithm. -
Queue a global task on the DOM manipulation task source with transform.
[[worker]]’sWorkerGlobalScopeto run the following steps:-
Let transformer be the
RTCRtpScriptTransformerobject associated with transform. -
cancel transformer.
[[readable]]. -
abort transformer.
[[writable]].
-
6. RTCRtpScriptTransformer interface
[Exposed =DedicatedWorker ]interface RTCRtpScriptTransformer :EventTarget { // Attributes and methods related to the transformer sourcereadonly attribute ReadableStream readable ;Promise <undefined >generateKeyFrame (optional DOMString );rid Promise <undefined >sendKeyFrameRequest (); // Attributes and methods related to the transformer sinkreadonly attribute WritableStream writable ;attribute EventHandler onkeyframerequest ; // Attributes for configuring the Javascript codereadonly attribute any options ; };
6.1. Internal slots
An RTCRtpScriptTransformer object has the following internal slots:
| Internal Slot | Description (non-normative) |
|---|---|
[[frameSource]]
| An encoder, a depacketizer, or undefined. |
[[options]]
| An optional Object, or null.
|
[[readable]]
| A ReadableStream.
|
[[writable]]
| A WritableStream.
|
[[lastReceivedFrameCounter]]
| A count of frames received. |
[[lastEnqueuedFrameCounter]]
| A count of frames enqueued. |
RTCRtpScriptTransformer, given an options object, perform the following steps:
-
Let transformer be a new
RTCRtpScriptTransformer, with:[[frameSource]]-
undefined
[[options]]-
options
[[readable]]-
A new
ReadableStream [[writable]]-
A new
WritableStream [[lastReceivedFrameCounter]]-
0
[[lastEnqueuedFrameCounter]]-
0
-
Set up transformer.
[[readable]].The readEncodedData algorithm, given this as parameter, provides encoded frames to it.
-
Let writeAlgorithm be an action that runs writeEncodedData with this as parameter and frame as input, given frame.
-
Set up transformer.
[[writable]]with its writeAlgorithm set to writeAlgorithm and its highWaterMark set toInfinity.highWaterMark is set to Infinity to explicitly disable backpressure.
-
Return transformer.
6.2. Methods
The generateKeyFrame(rid) method steps are:
-
Let promise be a new promise.
-
Run the generate key frame algorithm with promise, this.
[[frameSource]]and rid. -
Return promise.
The sendKeyFrameRequest() method steps are:
-
Let promise be a new promise.
-
Run the send request key frame algorithm with promise and this.
[[frameSource]]. -
Return promise.
6.3. Attributes
The options getter steps are:
-
Return this.
[[options]].
The readable getter steps are:
-
Return this.
[[readable]].
The writable getter steps are:
-
Return this.
[[writable]].
The onbandwidthestimate EventHandler has type bandwidthestimate.
The onkeyframerequest EventHandler has type keyframerequest.
6.4. Events
[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
The following event fires on an RTCRtpScriptTransformer:
-
keyframerequest of type
KeyFrameRequestEvent- fired when the sink determines that a key frame has been requested.
The steps that generate an event of type KeyFrameRequestEvent are as follows:
When the encoder of an associated RTCRtpScriptTransformer transformer receives a keyframe request, for instance from an incoming RTCP Picture Loss Indication (PLI)
or Full Intra Refresh (FIR), queue a task to perform the following steps:
-
Set rid to the RID of the appropriate layer, or undefined if the request is not for a specific layer.
-
Fire an event named
keyframerequestat transformer usingKeyFrameRequestEventwith itscancelableattribute initialized to "true", and withridset to rid. -
If the event’s canceled flag is true, abort these steps.
-
Run the generate key frame algorithm with a new promise, transformer.
[[frameSource]]and rid.
6.5. KeyFrame Algorithms
The generate key frame algorithm, given promise, encoder and rid, is defined by running these steps:
-
If encoder is not an encoder, reject promise with
InvalidStateError, abort these steps. -
If encoder does not belong to a video
RTCRtpSender, reject promise withInvalidStateError, abort these steps. -
If rid is defined, but does not conform to the grammar requirements specified in Section 10 of [RFC8851], then reject promise with
TypeErrorand abort these steps. -
In parallel, run the following steps:
-
Let layers be a new list of the layers for this encoder, ordered by negotiated encoding index.
-
Remove from layers all layers that are not
active, or whose correspondingRTCRtpSendertrack has ended. -
If rid is not undefined, remove from layers all layers whose RID is not rid.
Note: If no rid is passed in, keyframes are generated for all active layers.
-
If layers is now empty, queue a task to reject promise with
NotFoundErrorand abort these steps. -
Remove from layers all layers already found in any
[[layers]]of any tasks in encoder.[[pendingKeyFrameTasks]]. -
Create a pending key frame task called task with task.
[[layers]]set to layers and task.[[promise]]set to promise. -
If encoder.
[[pendingKeyFrameTasks]]is undefined, initialize encoder.[[pendingKeyFrameTasks]]to an empty set. -
Append task to encoder.
[[pendingKeyFrameTasks]]. -
For each layer in layers (if any), instruct encoder to generate a key frame for its next provided video frame to that layer.
-
For any encoder associated with an RTCRtpScriptTransformer transformer, the user agent MUST run the following steps just before any frame is enqueued into transformer.[[readable]]:
-
Let encoder be transformer.
[[frameSource]]. -
If encoder.
[[pendingKeyFrameTasks]]is undefined, abort these steps. -
If frame is not a video
"key"frame, abort these steps. -
For each task in encoder.
[[pendingKeyFrameTasks]], run the following steps:
By resolving the promises just before enqueuing the corresponding key frame in a RTCRtpScriptTransformer’s readable,
the resolution callbacks of the promises are always executed just before the corresponding key frame is exposed.
If the promise is associated with several layers, it will be resolved once key frames have been enqueued for all of them.
The send request key frame algorithm, given promise and depacketizer, is defined by running these steps:
-
If depacketizer is not a depacketizer, reject promise with
InvalidStateError, abort these steps. -
If depacketizer does not belong to a video
RTCRtpReceiver, reject promise withInvalidStateError, abort these steps. -
In parallel, run the following steps:
-
If sending a Full Intra Request (FIR) by depacketizer’s receiver is not deemed appropriate, resolve promise with undefined and abort these steps. Section 4.3.1 of [RFC5104] provides guidelines of how and when it is appropriate to sending a Full Intra Request.
-
Generate a Full Intra Request (FIR) packet as defined in section 4.3.1 of [RFC5104] and send it through depacketizer’s receiver.
-
Queue a task to resolve promise with undefined.
-
7. Privacy and security considerations
This API gives Javascript access to the content of media streams. This is also available from other sources, such as Canvas and WebAudio.
However, streams that are isolated (as specified in [WEBRTC-IDENTITY]) or tainted with another origin, cannot be accessed using this API, since that would break the isolation rule.
The API will allow access to some aspects of timing information that are otherwise unavailable, which allows some fingerprinting surface.
The API will give access to encoded media, which means that the JS application will have full control over what’s delivered to internal components like the packetizer or the decoder. This may require additional care with auditing how data is handled inside these components.
For instance, packetizers may expect to see data only from trusted encoders, and may not be audited for reception of data from untrusted sources.
8. Examples
See the explainer document.