From f49dfd54a35cd720e2038a6dccdd97cf0a0bc415 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Mon, 16 May 2022 15:52:45 -0700 Subject: [PATCH 01/12] Revise processing model, begin transitioning AudioDecoder --- index.src.html | 155 +++++++++++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 55 deletions(-) diff --git a/index.src.html b/index.src.html index 1d4de8c0..1b34e443 100644 --- a/index.src.html +++ b/index.src.html @@ -213,57 +213,80 @@ codec tasks can be scheduled while previous tasks are still pending. For example, web authors can call `decode()` without waiting for a previous `decode()` to complete. This is achieved by offloading underlying codec tasks - to a separate thread for parallel execution. + to a separate [=parallel queue=] for parallel execution. This section describes threading behaviors as they are visible from the - perspective of web authors. Implementers can choose to use more or less - threads as long as the exernally visible behaviors of blocking and sequencing - are maintained as follows. + perspective of web authors. Implementers can choose to use more threads, as + long as the exernally visible behaviors of blocking and sequencing are + maintained as follows. -Control Thread and Codec Thread {#control-thread-and-codec-thread} ------------------------------------------------------------------- +Control Messages {#control-messages} +------------------------------------ -All steps in this specification will run on either a [=control thread=] or -a [=codec thread=]. +A control message defines a sequence of steps corresponding to a +method invocation on a [=codec=] instance (e.g. `encode()`). -The control thread is the thread from which authors will construct -a [=codec=] and invoke its methods. Invoking a codec's methods will typically -result in the creation of [=control messages=] which are later executed on the -[=codec thread=]. Each [=webappapis/global object=] has a separate control -thread. - -The codec thread is the thread from which a [=codec=] will -[=dequeue=] [=control messages=] and execute their steps. Each [=codec=] -instance has a separate codec thread. The lifetime of a codec thread matches -that of its associated [=codec=] instance. - -The [=control thread=] uses a traditional event loop, as described in -[[!HTML]]. - -The [=codec thread=] uses a specialized [=codec processing loop=]. - -Communication from the [=control thread=] to the [=codec thread=] is done using -[=control message=] passing. Communication in the other direction is done using -regular event loop tasks. - -Each [=codec=] instance has a single control message queue that is -a [=queue=] of control messages. +A control message queue is a a [=queue=] of +[=control messages=]. Each [=codec=] instance has a control message queue +stored in an internal slot named +[[control message queue]]. Queuing a control -message means [=enqueuing=] the message to a [=codec=]’s [=control -message queue=]. Invoking codec methods will often queue a control message +message means [=enqueuing=] the message to a [=codec=]’s [=[[control +message queue]]=]. Invoking codec methods will generally queue a control message to schedule work. Running a control message means performing a sequence of steps specified by the method that enqueued the message. -The codec processing loop MUST run these steps: -1. While true: - 1. If the [=control message queue=] is empty, [=continue=]. - 2. Dequeue |front message| from the [=control message queue=]. - 3. Run [=control message steps=] described by |front message|. +The steps of a given control message can block processing later messages in the +control message queue. Each [=codec=] instance has a boolean internal slot named +[[message queue blocked]] that is set to `true` when this occurs. A +blocking message will conclude by setting [=[[message queue blocked]]=] to +`false` and rerunning the [=Process the control message queue=] steps. + +All control messages will return either `"processed"` or `"not processed"`. +Returning `"processed"` indicates the message steps are being (or have been) +executed and the message may be removed from the [=control message queue=]. +`"not processed"` indicates the message must could not be processed at this time +and should remain in the [=control message queue=] to be retried later. + +To Process the control message queue, run these steps: +1. While [=[[message queue blocked]]=] is `false` and + [=[[message queue blocked]]=] is `false`: + 1. Let |front message| be the first message in + [=[[control message queue]]=]. + 2. Let |outcome| be the result of running the [=control message steps=] + described by |front message|. + 3. If |outcome| equals `"not processed"`, break. + 4. Otherwise, dequeue |front message| from the + [=[[control message queue]]=]. + +Codec Work Parallel Queue {#codec-work-parallel-queue} +------------------------------------------------------ + +Each [=codec=] instance has an internal slot named +[[codec work queue]] that is a [=parallel queue=]. + +Each [=codec=] instance has an internal slot named +[[codec implementation]] that refers to the underlying platform +encoder or decoder. Except for assignment, any steps that reference +[=[[codec implementation]]=] will be enqueued to the [=[[codec work queue]]=]. + +Each [=codec=] instance has a unique codec task source. Tasks +[=queue a task|queued=] from the [=[[codec work queue]]=] to the [=/event loop=] +will use the [=codec task source=]. + +DELETE ME {#delete-me} +---------------------- +These dfns should be removed by the final commit in this PR. I've placed them +here while I iterate to avoid breaking their references. + +control thread, +codec thread + AudioDecoder Interface {#audiodecoder-interface} ================================================ @@ -297,6 +320,12 @@ --------------------------------------------- : [[codec implementation]] :: Underlying decoder implementation provided by the User Agent. +: [[message queue blocked]] +:: A boolean indicating when processing the [=control message queue=] is blocked + by a pending [=control message=]. See [=[[message queue blocked]]=]. +: [[codec work queue]] +:: A [=parallel queue=] used for running parallel steps that reference the + {{AudioDecoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. : [[output callback]] :: Callback given at construction for decoded outputs. : [[error callback]] @@ -319,11 +348,16 @@ AudioDecoder(init) 1. Let d be a new {{AudioDecoder}} object. -2. Assign init.output to {{AudioDecoder/[[output callback]]}}. -3. Assign init.error to {{AudioDecoder/[[error callback]]}}. -4. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. -5. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} -6. Return d. +2. Assign `null` to {{AudioDecoder/[[codec implementation]]}}. +3. Assign `false` to {{AudioDecoder/[[message queue blocked]]}}. +4. Assign the result of starting a new [=parallel queue=] to {{AudioDecoder/[[codec work queue]]}}. +5. Assign init.output to {{AudioDecoder/[[output callback]]}}. +6. Assign init.error to {{AudioDecoder/[[error callback]]}}. +7. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. +8. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} +9. Assign `0` to {{AudioDecoder/[[decodeQueueSize]]}}. +10. Assign a new [=list=] to {{AudioDecoder/[[pending flush promises]]}}. +11. Return d. Attributes {#audiodecoder-attributes} ------------------------------------- @@ -360,6 +394,7 @@ 3. Set {{AudioDecoder/[[state]]}} to `"configured"`. 4. Set {{AudioDecoder/[[key chunk required]]}} to `true`. 5. [=Queue a control message=] to configure the decoder with |config|. + 6. [=Process the control message queue=]. [=Running a control message=] to configure the decoder means running these steps: @@ -369,7 +404,13 @@ {{AudioDecoder/[[codec implementation]]}} with an implementation supporting |config|. 3. Otherwise, run the Close AudioDecoder algorithm with - {{NotSupportedError}}. + {{NotSupportedError}} and return `"processed"`. + 4. Assign `true` to {{AudioDecoder/[[message queue blocked]]}}. + 5. Enqueue the following steps to {{AudioDecoder/[[codec work queue]]}}: + 1. Configure {{AudioDecoder/[[codec implementation]]}} with |config|. + 2. Assign `false` to {{AudioDecoder/[[message queue blocked]]}}. + 3. [=Queue a task=] to [=Process the control message queue=]. + 6. Return `"processed"`.
decode(chunk)
@@ -390,21 +431,25 @@ {{AudioDecoder/[[key chunk required]]}}. 3. Increment {{AudioDecoder/[[decodeQueueSize]]}}. 4. [=Queue a control message=] to decode the |chunk|. + 5. [=Process the control message queue=]. [=Running a control message=] to decode the chunk means performing these steps: - 1. Attempt to use {{AudioDecoder/[[codec implementation]]}} to decode the - chunk. - 2. If decoding results in an error, queue a task on the [=control thread=] - event loop to run the [=Close AudioDecoder=] algorithm with - {{EncodingError}}. - 3. Queue a task on the [=control thread=] event loop to decrement - {{AudioDecoder/[[decodeQueueSize]]}}. - 4. Let |decoded outputs| be a [=list=] of decoded audio data outputs emitted - by {{AudioDecoder/[[codec implementation]]}}. - 5. If |decoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output AudioData=] algorithm with - |decoded outputs|. + 1. If the {{AudioDecoder/[[codec implementation]]}} is unable to accept + additional work at this time (saturated by prior calls to `decode()`), + return `"not processed"`. + 2. Decrement {{AudioDecoder/[[decodeQueueSize]]}}. + 3. Enqueue the following steps to the {{AudioDecoder/[[codec work queue]]}}: + 1. Attempt to use {{AudioDecoder/[[codec implementation]]}} to decode + the chunk. + 2. If decoding results in an error, [=queue a task=] to run the + [=Close AudioDecoder=] algorithm with {{EncodingError}}. + 3. Let |decoded outputs| be a [=list=] of decoded audio data outputs + emitted by {{AudioDecoder/[[codec implementation]]}}. + 4. If |decoded outputs| is not empty, [=queue a task=] to run the + [=Output AudioData=] algorithm with |decoded outputs|. + 5. [=Queue a task=] to [=process the control message queue=]. + 4. Return `"processed"`.
flush()
From c147bfcb8bba3fb423c21bbe8ad23a5627c5eae6 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Wed, 18 May 2022 09:42:04 -0700 Subject: [PATCH 02/12] Fix typo in processing loop --- index.src.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.src.html b/index.src.html index 1b34e443..ef3adf6e 100644 --- a/index.src.html +++ b/index.src.html @@ -255,7 +255,7 @@ To Process the control message queue, run these steps: 1. While [=[[message queue blocked]]=] is `false` and - [=[[message queue blocked]]=] is `false`: + [=[[control message queue]]=] is not empty: 1. Let |front message| be the first message in [=[[control message queue]]=]. 2. Let |outcome| be the result of running the [=control message steps=] From 41a621a146ec18f95c11c7d37e7efe8edbf145d3 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Thu, 19 May 2022 22:01:30 -0700 Subject: [PATCH 03/12] Explicitly define codec saturation --- index.src.html | 55 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/index.src.html b/index.src.html index ef3adf6e..4fdf4543 100644 --- a/index.src.html +++ b/index.src.html @@ -200,6 +200,17 @@ "matrix" → {{VideoMatrixCoefficients/bt709}}, "fullRange" → `false`]». +: Codec Saturation +:: The state of an underlying codec implementation where the number of active + decoding or encoding requests has reached an implementation specific + maximum such that it is temporarily unable to accept more work. The maximum + may be any value greater than 1, including infinity (no maximum). While + saturated, additional calls to `decode()` or `encode()` will be buffered + in the [=control message queue=], and will increment the respective + `decodeQueuSize` and `encodeQueueSize` attributes. The codec implementation + will become unsaturated after making sufficient progress on the current + workload. + Codec Processing Model {#codec-processing-model-section} =================================================================== @@ -326,6 +337,9 @@ : [[codec work queue]] :: A [=parallel queue=] used for running parallel steps that reference the {{AudioDecoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. +: [[codec saturated]] +:: A boolean indicating when the [[codec implementation]] is unable to accept + additional decoding work. : [[output callback]] :: Callback given at construction for decoded outputs. : [[error callback]] @@ -351,13 +365,14 @@ 2. Assign `null` to {{AudioDecoder/[[codec implementation]]}}. 3. Assign `false` to {{AudioDecoder/[[message queue blocked]]}}. 4. Assign the result of starting a new [=parallel queue=] to {{AudioDecoder/[[codec work queue]]}}. -5. Assign init.output to {{AudioDecoder/[[output callback]]}}. -6. Assign init.error to {{AudioDecoder/[[error callback]]}}. -7. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. -8. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} -9. Assign `0` to {{AudioDecoder/[[decodeQueueSize]]}}. -10. Assign a new [=list=] to {{AudioDecoder/[[pending flush promises]]}}. -11. Return d. +5. Assign `false` to {{AudioDecoder/[[codec saturated]]}}. +6. Assign init.output to {{AudioDecoder/[[output callback]]}}. +7. Assign init.error to {{AudioDecoder/[[error callback]]}}. +8. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. +9. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} +10. Assign `0` to {{AudioDecoder/[[decodeQueueSize]]}}. +11. Assign a new [=list=] to {{AudioDecoder/[[pending flush promises]]}}. +12. Return d. Attributes {#audiodecoder-attributes} ------------------------------------- @@ -435,21 +450,27 @@ [=Running a control message=] to decode the chunk means performing these steps: - 1. If the {{AudioDecoder/[[codec implementation]]}} is unable to accept - additional work at this time (saturated by prior calls to `decode()`), - return `"not processed"`. - 2. Decrement {{AudioDecoder/[[decodeQueueSize]]}}. - 3. Enqueue the following steps to the {{AudioDecoder/[[codec work queue]]}}: + 1. If {{AudioDecoder/[[codec saturated]]}} equals `true`, return `"not + processed"`. + 2. If decoding chunk will cause the + {{AudioDecoder/[[codec implementation]]}} to become [=saturated=], + assign `true` to {{AudioDecoder/[[codec saturated]]}}. + 3. Decrement {{AudioDecoder/[[decodeQueueSize]]}}. + 4. Enqueue the following steps to the {{AudioDecoder/[[codec work queue]]}}: 1. Attempt to use {{AudioDecoder/[[codec implementation]]}} to decode the chunk. 2. If decoding results in an error, [=queue a task=] to run the - [=Close AudioDecoder=] algorithm with {{EncodingError}}. - 3. Let |decoded outputs| be a [=list=] of decoded audio data outputs + [=Close AudioDecoder=] algorithm with {{EncodingError}} and return. + 3. If {{AudioDecoder/[[codec saturated]]}} equals `true` and + {{AudioDecoder/[[codec implementation]]}} is no longer + [=saturated=], [=queue a task=] to perform the following steps: + 1. Assign `false` to {{AudioDecoder/[[codec saturated]]}}. + 2. [=Process the control message queue=]. + 4. Let |decoded outputs| be a [=list=] of decoded audio data outputs emitted by {{AudioDecoder/[[codec implementation]]}}. - 4. If |decoded outputs| is not empty, [=queue a task=] to run the + 5. If |decoded outputs| is not empty, [=queue a task=] to run the [=Output AudioData=] algorithm with |decoded outputs|. - 5. [=Queue a task=] to [=process the control message queue=]. - 4. Return `"processed"`. + 5. Return `"processed"`.
flush()
From 6cb4288b094aa57ff3f7ca08afc4178225f0b615 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 1 Jul 2022 16:43:54 -0700 Subject: [PATCH 04/12] Finish transitioning AudioDecoder to new model --- index.src.html | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/index.src.html b/index.src.html index 4fdf4543..d495c1db 100644 --- a/index.src.html +++ b/index.src.html @@ -329,11 +329,15 @@ Internal Slots {#audiodecoder-internal-slots} --------------------------------------------- +: [[control message queue]] +:: A [=queue=] of [=control messages=] to be performed upon this [=codec=] + instance. See [=[[control message queue]]=]. +: [[message queue blocked]] +:: A boolean indicating when processing the + {{AudioDecoder/[[control message queue]]}} is blocked by a pending + [=control message=]. See [=[[message queue blocked]]=]. : [[codec implementation]] :: Underlying decoder implementation provided by the User Agent. -: [[message queue blocked]] -:: A boolean indicating when processing the [=control message queue=] is blocked - by a pending [=control message=]. See [=[[message queue blocked]]=]. : [[codec work queue]] :: A [=parallel queue=] used for running parallel steps that reference the {{AudioDecoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. @@ -483,22 +487,24 @@ [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}. 2. Set {{AudioDecoder/[[key chunk required]]}} to `true`. 3. Let |promise| be a new Promise. - 4. [=Queue a control message=] to flush the codec with |promise|. - 5. Append |promise| to {{AudioDecoder/[[pending flush promises]]}}. - 6. Return |promise|. + 4. Append |promise| to {{AudioDecoder/[[pending flush promises]]}}. + 5. [=Queue a control message=] to flush the codec with |promise|. + 6. [=Process the control message queue=]. + 7. Return |promise|. - [=Running a control message=] to flush the codec means performing these steps - with |promise|. - 1. Signal {{AudioDecoder/[[codec implementation]]}} to emit all [=internal - pending outputs=]. - 2. Let |decoded outputs| be a [=list=] of decoded audio data outputs emitted - by {{AudioDecoder/[[codec implementation]]}}. - 3. If |decoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output AudioData=] algorithm with - |decoded outputs|. - 4. Queue a task on the [=control thread=] event loop to run these steps: - 1. Remove |promise| from {{AudioDecoder/[[pending flush promises]]}}. - 2. Resolve |promise|. + [=Running a control message=] to flush the codec means performing these + steps with |promise|. + 1. Enqueue the following steps to the {{AudioDecoder/[[codec work queue]]}}: + 1. Signal {{AudioDecoder/[[codec implementation]]}} to emit all + [=internal pending outputs=]. + 2. Let |decoded outputs| be a [=list=] of decoded audio data outputs + emitted by {{AudioDecoder/[[codec implementation]]}}. + 3. [=Queue a task=] to perform these steps: + 1. If |decoded outputs| is not empty, run the [=Output AudioData=] + algorithm with |decoded outputs|. + 2. Remove |promise| from + {{AudioDecoder/[[pending flush promises]]}}. + 3. Resolve |promise|.
reset()
@@ -580,7 +586,8 @@ 2. Set {{AudioDecoder/[[state]]}} to `"unconfigured"`. 3. Signal {{AudioDecoder/[[codec implementation]]}} to cease producing output for the previous configuration. - 4. Remove all [=control messages=] from the [=control message queue=]. + 4. Remove all [=control messages=] from the + {{AudioDecoder/[[control message queue]]}}. 5. Set {{AudioDecoder/[[decodeQueueSize]]}} to zero. 6. For each |promise| in {{AudioDecoder/[[pending flush promises]]}}: 1. Reject |promise| with |exception|. @@ -593,8 +600,8 @@ 2. Set {{AudioDecoder/[[state]]}} to `"closed"`. 3. Clear {{AudioDecoder/[[codec implementation]]}} and release associated [=system resources=]. - 4. If |exception| is not an {{AbortError}} {{DOMException}}, queue a task on - the [=control thread=] event loop to invoke the {{AudioDecoder/[[error callback]]}} with |exception|. + 4. If |exception| is not an {{AbortError}} {{DOMException}}, + [=queue a task=] to invoke the {{AudioDecoder/[[error callback]]}} with |exception|. From b99b77f1888bd717061d141c67e6f584191ecc61 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 1 Jul 2022 16:46:57 -0700 Subject: [PATCH 05/12] update HTTP RFC --- index.src.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.src.html b/index.src.html index d495c1db..54e0c4bd 100644 --- a/index.src.html +++ b/index.src.html @@ -4943,7 +4943,7 @@ 6. Return `true`. A valid image MIME type is a string that is a [=valid MIME type -string=] and for which the `type`, per Section 3.1.1.1 of [[RFC7231]], is +string=] and for which the `type`, per Section 3.1.1.1 of [[RFC9110]], is `image`. : type From e9f6574b4f613110e00796d8ecf884ff717c795d Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 1 Jul 2022 22:45:05 -0700 Subject: [PATCH 06/12] Add missing AudoiDecoder slot init --- index.src.html | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/index.src.html b/index.src.html index 54e0c4bd..d519fd8e 100644 --- a/index.src.html +++ b/index.src.html @@ -366,17 +366,19 @@ AudioDecoder(init) 1. Let d be a new {{AudioDecoder}} object. -2. Assign `null` to {{AudioDecoder/[[codec implementation]]}}. +2. Assign a new [=queue=] to {{AudioDecoder/[[control message queue]]}}. 3. Assign `false` to {{AudioDecoder/[[message queue blocked]]}}. -4. Assign the result of starting a new [=parallel queue=] to {{AudioDecoder/[[codec work queue]]}}. -5. Assign `false` to {{AudioDecoder/[[codec saturated]]}}. -6. Assign init.output to {{AudioDecoder/[[output callback]]}}. -7. Assign init.error to {{AudioDecoder/[[error callback]]}}. -8. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. -9. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} -10. Assign `0` to {{AudioDecoder/[[decodeQueueSize]]}}. -11. Assign a new [=list=] to {{AudioDecoder/[[pending flush promises]]}}. -12. Return d. +4. Assign `null` to {{AudioDecoder/[[codec implementation]]}}. +5. Assign the result of starting a new [=parallel queue=] to + {{AudioDecoder/[[codec work queue]]}}. +6. Assign `false` to {{AudioDecoder/[[codec saturated]]}}. +7. Assign init.output to {{AudioDecoder/[[output callback]]}}. +8. Assign init.error to {{AudioDecoder/[[error callback]]}}. +9. Assign `true` to {{AudioDecoder/[[key chunk required]]}}. +10. Assign `"unconfigured"` to {{AudioDecoder/[[state]]}} +11. Assign `0` to {{AudioDecoder/[[decodeQueueSize]]}}. +12. Assign a new [=list=] to {{AudioDecoder/[[pending flush promises]]}}. +13. Return d. Attributes {#audiodecoder-attributes} ------------------------------------- From 1eecb0f6bd2f5c4f28009324425f22ff2e94813d Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 1 Jul 2022 23:02:04 -0700 Subject: [PATCH 07/12] Transition VideoDecoder to new model --- index.src.html | 127 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 42 deletions(-) diff --git a/index.src.html b/index.src.html index d519fd8e..83667b79 100644 --- a/index.src.html +++ b/index.src.html @@ -637,8 +637,21 @@ Internal Slots {#videodecoder-internal-slots} --------------------------------------------- +: [[control message queue]] +:: A [=queue=] of [=control messages=] to be performed upon this [=codec=] + instance. See [=[[control message queue]]=]. +: [[message queue blocked]] +:: A boolean indicating when processing the + {{VideoDecoder/[[control message queue]]}} is blocked by a pending + [=control message=]. See [=[[message queue blocked]]=]. : [[codec implementation]] :: Underlying decoder implementation provided by the User Agent. +: [[codec work queue]] +:: A [=parallel queue=] used for running parallel steps that reference the + {{VideoDecoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. +: [[codec saturated]] +:: A boolean indicating when the [[codec implementation]] is unable to accept + additional decoding work. : [[output callback]] :: Callback given at construction for decoded outputs. : [[error callback]] @@ -663,12 +676,21 @@ VideoDecoder(init) -1. Let d be a new VideoDecoder object. -2. Assign `init.output` to the {{VideoDecoder/[[output callback]]}} internal slot. -3. Assign `init.error` to the {{VideoDecoder/[[error callback]]}} internal slot. -4. Assign `true` to {{VideoDecoder/[[key chunk required]]}}. -5. Assign `"unconfigured"` to {{VideoDecoder/[[state]]}}. -5. Return d. +1. Let d be a new {{VideoDecoder}} object. +2. Assign a new [=queue=] to {{VideoDecoder/[[control message queue]]}}. +3. Assign `false` to {{VideoDecoder/[[message queue blocked]]}}. +4. Assign `null` to {{VideoDecoder/[[codec implementation]]}}. +5. Assign the result of starting a new [=parallel queue=] to + {{VideoDecoder/[[codec work queue]]}}. +6. Assign `false` to {{VideoDecoder/[[codec saturated]]}}. +7. Assign init.output to {{VideoDecoder/[[output callback]]}}. +8. Assign init.error to {{VideoDecoder/[[error callback]]}}. +9. Assign `null` to {{VideoDecoder/[[active decoder config]]}}. +10. Assign `true` to {{VideoDecoder/[[key chunk required]]}}. +11. Assign `"unconfigured"` to {{VideoDecoder/[[state]]}} +12. Assign `0` to {{VideoDecoder/[[decodeQueueSize]]}}. +13. Assign a new [=list=] to {{VideoDecoder/[[pending flush promises]]}}. +14. Return d. Attributes {#videodecoder-attributes} ------------------------------------- @@ -696,17 +718,19 @@ chunks as described by |config|. NOTE: This method will trigger a {{NotSupportedError}} if the User Agent - does not support |config|. Authors are encouraged to first check support by - calling {{VideoDecoder/isConfigSupported()}} with |config|. User Agents + does not support |config|. Authors are encouraged to first check support + by calling {{VideoDecoder/isConfigSupported()}} with |config|. User Agents don't have to support any particular codec type or configuration. When invoked, run these steps: 1. If |config| is not a [=valid VideoDecoderConfig=], throw a {{TypeError}}. - 2. If {{VideoDecoder/[[state]]}} is `“closed”`, throw an {{InvalidStateError}}. + 2. If {{VideoDecoder/[[state]]}} is `“closed”`, throw an + {{InvalidStateError}}. 3. Set {{VideoDecoder/[[state]]}} to `"configured"`. 4. Set {{VideoDecoder/[[key chunk required]]}} to `true`. 5. [=Queue a control message=] to configure the decoder with |config|. + 6. [=Process the control message queue=]. [=Running a control message=] to configure the decoder means running these steps: @@ -716,8 +740,14 @@ {{VideoDecoder/[[codec implementation]]}} with an implementation supporting |config|. 3. Otherwise, run the Close VideoDecoder algorithm with - {{NotSupportedError}} and abort these steps. - 4. Set {{VideoDecoder/[[active decoder config]]}} to `config`. + {{NotSupportedError}} and return `"processed"`. + 4. Assign `true` to {{VideoDecoder/[[message queue blocked]]}}. + 5. Enqueue the following steps to {{VideoDecoder/[[codec work queue]]}}: + 1. Configure {{VideoDecoder/[[codec implementation]]}} with |config|. + 2. Set {{VideoDecoder/[[active decoder config]]}} to `config`. + 3. Assign `false` to {{VideoDecoder/[[message queue blocked]]}}. + 4. [=Queue a task=] to [=Process the control message queue=]. + 6. Return `"processed"`.
decode(chunk)
@@ -749,20 +779,31 @@ {{VideoDecoder/[[key chunk required]]}}. 3. Increment {{VideoDecoder/[[decodeQueueSize]]}}. 4. [=Queue a control message=] to decode the |chunk|. + 5. [=Process the control message queue=]. [=Running a control message=] to decode the chunk means performing these steps: - 1. Attempt to use {{VideoDecoder/[[codec implementation]]}} to decode the - chunk. - 2. If decoding results in an error, queue a task on the [=control thread=] - event loop to run the [=Close VideoDecoder=] algorithm with - {{EncodingError}}. - 3. Queue a task on the [=control thread=] event loop to decrement - {{VideoDecoder/[[decodeQueueSize]]}} - 4. Let |decoded outputs| be a [=list=] of decoded video data outputs emitted - by {{VideoDecoder/[[codec implementation]]}} in presentation order. - 5. If |decoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output VideoFrames=] algorithm with - |decoded outputs|. + 1. If {{VideoDecoder/[[codec saturated]]}} equals `true`, return `"not + processed"`. + 2. If decoding chunk will cause the + {{VideoDecoder/[[codec implementation]]}} to become [=saturated=], + assign `true` to {{VideoDecoder/[[codec saturated]]}}. + 3. Decrement {{VideoDecoder/[[decodeQueueSize]]}}. + 4. Enqueue the following steps to the {{VideoDecoder/[[codec work queue]]}}: + 1. Attempt to use {{VideoDecoder/[[codec implementation]]}} to decode + the chunk. + 2. If decoding results in an error, [=queue a task=] to run the + [=Close VideoDecoder=] algorithm with {{EncodingError}} and return. + 3. If {{VideoDecoder/[[codec saturated]]}} equals `true` and + {{VideoDecoder/[[codec implementation]]}} is no longer + [=saturated=], [=queue a task=] to perform the following steps: + 1. Assign `false` to {{VideoDecoder/[[codec saturated]]}}. + 2. [=Process the control message queue=]. + 4. Let |decoded outputs| be a [=list=] of decoded video data outputs + emitted by {{VideoDecoder/[[codec implementation]]}} in + presentation order. + 5. If |decoded outputs| is not empty, [=queue a task=] to run the + [=Output VideoFrame=] algorithm with |decoded outputs|. + 5. Return `"processed"`.
flush()
@@ -775,22 +816,24 @@ [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}. 2. Set {{VideoDecoder/[[key chunk required]]}} to `true`. 3. Let |promise| be a new Promise. - 4. [=Queue a control message=] to flush the codec with |promise|. - 5. Append |promise| to {{VideoDecoder/[[pending flush promises]]}}. - 6. Return |promise|. + 4. Append |promise| to {{VideoDecoder/[[pending flush promises]]}}. + 5. [=Queue a control message=] to flush the codec with |promise|. + 6. [=Process the control message queue=]. + 7. Return |promise|. - [=Running a control message=] to flush the codec means performing these steps - with |promise|. - 1. Signal {{VideoDecoder/[[codec implementation]]}} to emit all [=internal - pending outputs=]. - 2. Let |decoded outputs| be a [=list=] of decoded video data outputs emitted - by {{VideoDecoder/[[codec implementation]]}}. - 3. If |decoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output VideoFrames=] algorithm with - |decoded outputs|. - 4. Queue a task on the [=control thread=] event loop to run these steps: - 1. Remove |promise| from {{VideoDecoder/[[pending flush promises]]}}. - 2. Resolve |promise|. + [=Running a control message=] to flush the codec means performing these + steps with |promise|. + 1. Enqueue the following steps to the {{VideoDecoder/[[codec work queue]]}}: + 1. Signal {{VideoDecoder/[[codec implementation]]}} to emit all + [=internal pending outputs=]. + 2. Let |decoded outputs| be a [=list=] of decoded video data outputs + emitted by {{VideoDecoder/[[codec implementation]]}}. + 3. [=Queue a task=] to perform these steps: + 1. If |decoded outputs| is not empty, run the [=Output VideoFrame=] + algorithm with |decoded outputs|. + 2. Remove |promise| from + {{VideoDecoder/[[pending flush promises]]}}. + 3. Resolve |promise|.
reset()
@@ -882,7 +925,8 @@ 2. Set {{VideoDecoder/state}} to `"unconfigured"`. 3. Signal {{VideoDecoder/[[codec implementation]]}} to cease producing output for the previous configuration. - 4. Remove all [=control messages=] from the [=control message queue=]. + 4. Remove all [=control messages=] from the + {{VideoDecoder/[[control message queue]]}}. 5. Set {{VideoDecoder/[[decodeQueueSize]]}} to zero. 6. For each |promise| in {{VideoDecoder/[[pending flush promises]]}}: 1. Reject |promise| with |exception|. @@ -895,9 +939,8 @@ 2. Set {{VideoDecoder/state}} to `"closed"`. 3. Clear {{VideoDecoder/[[codec implementation]]}} and release associated [=system resources=]. - 4. If |exception| is not an {{AbortError}} {{DOMException}}, queue a task on - the [=control thread=] event loop to invoke the - {{VideoDecoder/[[error callback]]}} with |exception|. + 4. If |exception| is not an {{AbortError}} {{DOMException}}, + [=queue a task=] to invoke the {{VideoDecoder/[[error callback]]}} with |exception|. From d947306760adfe751c24c7b51f80afbec4ff4c45 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 1 Jul 2022 23:31:34 -0700 Subject: [PATCH 08/12] Transition AudioEncoder to new model --- index.src.html | 180 +++++++++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 73 deletions(-) diff --git a/index.src.html b/index.src.html index 83667b79..07df984c 100644 --- a/index.src.html +++ b/index.src.html @@ -977,47 +977,58 @@ Internal Slots {#audioencoder-internal-slots} --------------------------------------------- -
-
[[codec implementation]]
-
Underlying encoder implementation provided by the User Agent.
-
[[output callback]]
-
Callback given at construction for encoded outputs.
-
[[error callback]]
-
Callback given at construction for encode errors.
-
[[active encoder config]]
-
The {{AudioEncoderConfig}} that is actively applied.
-
[[active output config]]
-
- The {{AudioDecoderConfig}} that describes how to decode the most recently - emitted {{EncodedAudioChunk}}. -
-
\[[state]]
-
- The current {{CodecState}} of this {{AudioEncoder}}. -
-
\[[encodeQueueSize]]
-
- The number of pending encode requests. This number will decrease as the - underlying codec is ready to accept new input. -
-
[[pending flush promises]]
-
- A list of unresolved promises returned by calls to {{AudioEncoder/flush()}}. -
-
+: [[control message queue]] +:: A [=queue=] of [=control messages=] to be performed upon this [=codec=] + instance. See [=[[control message queue]]=]. +: [[message queue blocked]] +:: A boolean indicating when processing the + {{AudioEncoder/[[control message queue]]}} is blocked by a pending + [=control message=]. See [=[[message queue blocked]]=]. +: [[codec implementation]] +:: Underlying encoder implementation provided by the User Agent. +: [[codec work queue]] +:: A [=parallel queue=] used for running parallel steps that reference the + {{AudioEncoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. +: [[codec saturated]] +:: A boolean indicating when the [[codec implementation]] is unable to accept + additional encoding work. +: [[output callback]] +:: Callback given at construction for encoded outputs. +: [[error callback]] +:: Callback given at construction for encode errors. +: [[active encoder config]] +:: The {{AudioEncoderConfig}} that is actively applied. +: [[active output config]] +:: The {{AudioDecoderConfig}} that describes how to decode the most recently + emitted {{EncodedAudioChunk}}. +: \[[state]] +:: The current {{CodecState}} of this {{AudioEncoder}}. +: \[[encodeQueueSize]] +:: The number of pending encode requests. This number will decrease as the + underlying codec is ready to accept new input. +: [[pending flush promises]] +:: A list of unresolved promises returned by calls to {{AudioEncoder/flush()}}. Constructors {#audioencoder-constructors} ----------------------------------------- AudioEncoder(init) -1. Let e be a new AudioEncoder object. -2. Assign `init.output` to the {{AudioEncoder/[[output callback]]}} internal slot. -3. Assign `init.error` to the {{AudioEncoder/[[error callback]]}} internal slot. -4. Assign `"unconfigured"` to {{AudioEncoder/[[state]]}}. -5. Assign `null` to {{AudioEncoder/[[active encoder config]]}}. -6. Assign `null` to {{AudioEncoder/[[active output config]]}}. -7. Return e. +1. Let e be a new {{AudioEncoder}} object. +2. Assign a new [=queue=] to {{AudioEncoder/[[control message queue]]}}. +3. Assign `false` to {{AudioEncoder/[[message queue blocked]]}}. +4. Assign `null` to {{AudioEncoder/[[codec implementation]]}}. +5. Assign the result of starting a new [=parallel queue=] to + {{AudioEncoder/[[codec work queue]]}}. +6. Assign `false` to {{AudioEncoder/[[codec saturated]]}}. +7. Assign init.output to {{AudioEncoder/[[output callback]]}}. +8. Assign init.error to {{AudioEncoder/[[error callback]]}}. +9. Assign `null` to {{AudioEncoder/[[active encoder config]]}}. +10. Assign `null` to {{AudioEncoder/[[active output config]]}}. +11. Assign `"unconfigured"` to {{AudioEncoder/[[state]]}} +12. Assign `0` to {{AudioEncoder/[[encodeQueueSize]]}}. +13. Assign a new [=list=] to {{AudioEncoder/[[pending flush promises]]}}. +14. Return e. Attributes {#audioencoder-attributes} ------------------------------------- @@ -1050,20 +1061,28 @@ When invoked, run these steps: 1. If |config| is not a [=valid AudioEncoderConfig=], throw a {{TypeError}}. - 2. If {{AudioEncoder/[[state]]}} is `"closed"`, throw an {{InvalidStateError}}. + 2. If {{AudioEncoder/[[state]]}} is `"closed"`, throw an + {{InvalidStateError}}. 3. Set {{AudioEncoder/[[state]]}} to `"configured"`. 4. [=Queue a control message=] to configure the encoder using |config|. + 5. [=Process the control message queue=]. - [=Running a control message=] to configure the encoder means performing these - steps: + [=Running a control message=] to configure the encoder means performing + these steps: 1. Let |supported| be the result of running the Check Configuration Support algorithm with |config|. 2. If |supported| is `true`, assign {{AudioEncoder/[[codec implementation]]}} with an implementation supporting |config|. 3. Otherwise, run the Close AudioEncoder algorithm with - {{NotSupportedError}} and abort these steps. - 4. Assign |config| to {{AudioEncoder/[[active encoder config]]}} + {{NotSupportedError}} and return `"processed"`. + 4. Assign `true` to {{AudioEncoder/[[message queue blocked]]}}. + 5. Enqueue the following steps to {{AudioEncoder/[[codec work queue]]}}: + 1. Configure {{AudioEncoder/[[codec implementation]]}} with |config|. + 2. Set {{AudioEncoder/[[active encoder config]]}} to `config`. + 3. Assign `false` to {{AudioEncoder/[[message queue blocked]]}}. + 4. [=Queue a task=] to [=Process the control message queue=]. + 6. Return `"processed"`.
encode(data)
@@ -1071,27 +1090,39 @@ [=Enqueues a control message=] to encode the given |data|. When invoked, run these steps: - 1. If the value of |data|'s {{platform object/[[Detached]]}} internal slot is - `true`, throw a {{TypeError}}. + 1. If the value of |data|'s {{platform object/[[Detached]]}} internal slot + is `true`, throw a {{TypeError}}. 2. If {{AudioEncoder/[[state]]}} is not `"configured"`, throw an {{InvalidStateError}}. 3. Let |dataClone| hold the result of running the [=Clone AudioData=] algorithm with |data|. 4. Increment {{AudioEncoder/[[encodeQueueSize]]}}. 5. [=Queue a control message=] to encode |dataClone|. + 6. [=Process the control message queue=]. - [=Running a control message=] to encode the data means performing these steps. - 1. Attempt to use {{AudioEncoder/[[codec implementation]]}} to encode - the [=media resource=] described by |dataClone|. - 2. If encoding results in an error, queue a task on the [=control thread=] - event loop to run the [=Close AudioEncoder=] algorithm with - {{EncodingError}}. - 3. Queue a task on the [=control thread=] event loop to decrement - {{AudioEncoder/[[encodeQueueSize]]}}. - 4. Let |encoded outputs| be a [=list=] of encoded audio data outputs - emitted by {{AudioEncoder/[[codec implementation]]}}. - 5. If |encoded outputs| is not empty, queue a task on the - [=control thread=] event loop to run the [=Output EncodedAudioChunks=] algorithm with |encoded outputs|. + [=Running a control message=] to encode the data means performing these + steps: + 1. If {{AudioEncoder/[[codec saturated]]}} equals `true`, return `"not + processed"`. + 2. If encoding chunk will cause the + {{AudioEncoder/[[codec implementation]]}} to become [=saturated=], + assign `true` to {{AudioEncoder/[[codec saturated]]}}. + 3. Decrement {{AudioEncoder/[[encodeQueueSize]]}}. + 4. Enqueue the following steps to the {{AudioEncoder/[[codec work queue]]}}: + 1. Attempt to use {{AudioEncoder/[[codec implementation]]}} to encode + the [=media resource=] described by |dataClone|. + 2. If encoding results in an error, [=queue a task=] to run the + [=Close AudioEncoder=] algorithm with {{EncodingError}} and return. + 3. If {{AudioEncoder/[[codec saturated]]}} equals `true` and + {{AudioEncoder/[[codec implementation]]}} is no longer + [=saturated=], [=queue a task=] to perform the following steps: + 1. Assign `false` to {{AudioEncoder/[[codec saturated]]}}. + 2. [=Process the control message queue=]. + 4. Let |encoded outputs| be a [=list=] of encoded audio data outputs + emitted by {{AudioEncoder/[[codec implementation]]}}. + 5. If |encoded outputs| is not empty, [=queue a task=] to run the + [=Output EncodedAudioChunks=] algorithm with |encoded outputs|. + 5. Return `"processed"`.
flush()
@@ -1103,22 +1134,24 @@ 1. If {{AudioEncoder/[[state]]}} is not `"configured"`, return [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}. 2. Let |promise| be a new Promise. - 3. [=Queue a control message=] to flush the codec with |promise|. - 4. Append |promise| to {{AudioEncoder/[[pending flush promises]]}}. - 5. Return |promise|. + 3. Append |promise| to {{AudioEncoder/[[pending flush promises]]}}. + 4. [=Queue a control message=] to flush the codec with |promise|. + 5. [=Process the control message queue=]. + 6. Return |promise|. - [=Running a control message=] to flush the codec means performing these steps - with |promise|. - 1. Signal {{AudioEncoder/[[codec implementation]]}} to emit all [=internal - pending outputs=]. - 2. Let |encoded outputs| be a [=list=] of encoded audio data outputs - emitted by {{AudioEncoder/[[codec implementation]]}}. - 3. If |encoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output EncodedAudioChunks=] algorithm with - |encoded outputs|. - 4. Queue a task on the [=control thread=] event loop to run these steps: - 1. Remove |promise| from {{AudioEncoder/[[pending flush promises]]}}. - 2. Resolve |promise|. + [=Running a control message=] to flush the codec means performing these + steps with |promise|. + 1. Enqueue the following steps to the {{AudioEncoder/[[codec work queue]]}}: + 1. Signal {{AudioEncoder/[[codec implementation]]}} to emit all + [=internal pending outputs=]. + 2. Let |encoded outputs| be a [=list=] of encoded audio data outputs + emitted by {{AudioEncoder/[[codec implementation]]}}. + 3. [=Queue a task=] to perform these steps: + 1. If |encoded outputs| is not empty, run the + [=Output EncodedAudioChunks=] algorithm with |encoded outputs|. + 2. Remove |promise| from + {{AudioEncoder/[[pending flush promises]]}}. + 3. Resolve |promise|.
reset()
@@ -1230,7 +1263,8 @@ 4. Set {{AudioEncoder/[[active output config]]}} to `null`. 5. Signal {{AudioEncoder/[[codec implementation]]}} to cease producing output for the previous configuration. - 6. Remove all [=control messages=] from the [=control message queue=]. + 6. Remove all [=control messages=] from the + {{AudioEncoder/[[control message queue]]}}. 7. Set {{AudioEncoder/[[encodeQueueSize]]}} to zero. 8. For each |promise| in {{AudioEncoder/[[pending flush promises]]}}: 1. Reject |promise| with |exception|. @@ -1243,9 +1277,9 @@ 2. Set {{AudioEncoder/[[state]]}} to `"closed"`. 3. Clear {{AudioEncoder/[[codec implementation]]}} and release associated [=system resources=]. - 4. If |exception| is not an {{AbortError}} {{DOMException}}, queue a task on - the [=control thread=] event loop to invoke the - {{AudioDecoder/[[error callback]]}} with |exception|. + 4. If |exception| is not an {{AbortError}} {{DOMException}}, + [=queue a task=] to invoke the + {{AudioEncoder/[[error callback]]}} with |exception|. From 93e1d2a2e2501dfc867a9c99b0c6a50b12d09e5a Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Sat, 2 Jul 2022 00:04:00 -0700 Subject: [PATCH 09/12] Transition VideoEncoder to new model --- index.src.html | 182 +++++++++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 73 deletions(-) diff --git a/index.src.html b/index.src.html index 07df984c..755f5e2f 100644 --- a/index.src.html +++ b/index.src.html @@ -1104,7 +1104,7 @@ steps: 1. If {{AudioEncoder/[[codec saturated]]}} equals `true`, return `"not processed"`. - 2. If encoding chunk will cause the + 2. If encoding |data| will cause the {{AudioEncoder/[[codec implementation]]}} to become [=saturated=], assign `true` to {{AudioEncoder/[[codec saturated]]}}. 3. Decrement {{AudioEncoder/[[encodeQueueSize]]}}. @@ -1332,45 +1332,58 @@ Internal Slots {#videoencoder-internal-slots} --------------------------------------------- -
-
[[codec implementation]]
-
Underlying encoder implementation provided by the User Agent.
-
[[output callback]]
-
Callback given at construction for encoded outputs.
-
[[error callback]]
-
Callback given at construction for encode errors.
-
[[active encoder config]]
-
The {{VideoEncoderConfig}} that is actively applied.
-
[[active output config]]
-
- The {{VideoDecoderConfig}} that describes how to decode the most recently - emitted {{EncodedVideoChunk}}. -
-
\[[state]]
-
- The current {{CodecState}} of this {{VideoEncoder}}. -
-
\[[encodeQueueSize]]
-
- The number of pending encode requests. This number will decrease as the - underlying codec is ready to accept new input. -
-
[[pending flush promises]]
-
- A list of unresolved promises returned by calls to {{VideoEncoder/flush()}}. -
-
+: [[control message queue]] +:: A [=queue=] of [=control messages=] to be performed upon this [=codec=] + instance. See [=[[control message queue]]=]. +: [[message queue blocked]] +:: A boolean indicating when processing the + {{VideoEncoder/[[control message queue]]}} is blocked by a pending + [=control message=]. See [=[[message queue blocked]]=]. +: [[codec implementation]] +:: Underlying encoder implementation provided by the User Agent. +: [[codec work queue]] +:: A [=parallel queue=] used for running parallel steps that reference the + {{VideoEncoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. +: [[codec saturated]] +:: A boolean indicating when the [[codec implementation]] is unable to accept + additional encoding work. +: [[output callback]] +:: Callback given at construction for encoded outputs. +: [[error callback]] +:: Callback given at construction for encode errors. +: [[active encoder config]] +:: The {{VideoEncoderConfig}} that is actively applied. +: [[active output config]] +:: The {{VideoDecoderConfig}} that describes how to decode the most recently + emitted {{EncodedVideoChunk}}. +: \[[state]] +:: The current {{CodecState}} of this {{VideoEncoder}}. +: \[[encodeQueueSize]] +:: The number of pending encode requests. This number will decrease as the + underlying codec is ready to accept new input. +: [[pending flush promises]] +:: A list of unresolved promises returned by calls to {{VideoEncoder/flush()}}. Constructors {#videoencoder-constructors} ----------------------------------------- VideoEncoder(init) -1. Let e be a new VideoEncoder object. -2. Assign `init.output` to the {{VideoEncoder/[[output callback]]}} internal slot. -3. Assign `init.error` to the {{VideoEncoder/[[error callback]]}} internal slot. -4. Assign "unconfigured" to {{VideoEncoder/[[state]]}}. -5. Return e. +1. Let e be a new {{VideoEncoder}} object. +2. Assign a new [=queue=] to {{VideoEncoder/[[control message queue]]}}. +3. Assign `false` to {{VideoEncoder/[[message queue blocked]]}}. +4. Assign `null` to {{VideoEncoder/[[codec implementation]]}}. +5. Assign the result of starting a new [=parallel queue=] to + {{VideoEncoder/[[codec work queue]]}}. +6. Assign `false` to {{VideoEncoder/[[codec saturated]]}}. +7. Assign init.output to {{VideoEncoder/[[output callback]]}}. +8. Assign init.error to {{VideoEncoder/[[error callback]]}}. +9. Assign `null` to {{VideoEncoder/[[active encoder config]]}}. +10. Assign `null` to {{VideoEncoder/[[active output config]]}}. +11. Assign `"unconfigured"` to {{VideoEncoder/[[state]]}} +12. Assign `0` to {{VideoEncoder/[[encodeQueueSize]]}}. +13. Assign a new [=list=] to {{VideoEncoder/[[pending flush promises]]}}. +14. Return e. Attributes {#videoencoder-attributes} ------------------------------------- @@ -1404,20 +1417,28 @@ When invoked, run these steps: 1. If |config| is not a [=valid VideoEncoderConfig=], throw a {{TypeError}}. - 2. If {{VideoEncoder/[[state]]}} is `"closed"`, throw an {{InvalidStateError}}. + 2. If {{VideoEncoder/[[state]]}} is `"closed"`, throw an + {{InvalidStateError}}. 3. Set {{VideoEncoder/[[state]]}} to `"configured"`. 4. [=Queue a control message=] to configure the encoder using |config|. + 5. [=Process the control message queue=]. - [=Running a control message=] to configure the encoder means performing these - steps: + [=Running a control message=] to configure the encoder means performing + these steps: 1. Let |supported| be the result of running the Check Configuration Support algorithm with |config|. 2. If |supported| is `true`, assign {{VideoEncoder/[[codec implementation]]}} with an implementation supporting |config|. 3. Otherwise, run the Close VideoEncoder algorithm with - {{NotSupportedError}} and abort these steps. - 4. Assign |config| to {{VideoEncoder/[[active encoder config]]}}. + {{NotSupportedError}} and return `"processed"`. + 4. Assign `true` to {{VideoEncoder/[[message queue blocked]]}}. + 5. Enqueue the following steps to {{VideoEncoder/[[codec work queue]]}}: + 1. Configure {{VideoEncoder/[[codec implementation]]}} with |config|. + 2. Set {{VideoEncoder/[[active encoder config]]}} to `config`. + 3. Assign `false` to {{VideoEncoder/[[message queue blocked]]}}. + 4. [=Queue a task=] to [=Process the control message queue=]. + 6. Return `"processed"`.
encode(|frame|, |options|)
@@ -1425,28 +1446,39 @@ [=Enqueues a control message=] to encode the given |frame|. When invoked, run these steps: - 1. If the value of |frame|'s {{platform object/[[Detached]]}} internal slot is - `true`, throw a {{TypeError}}. + 1. If the value of |frame|'s {{platform object/[[Detached]]}} internal slot + is `true`, throw a {{TypeError}}. 2. If {{VideoEncoder/[[state]]}} is not `"configured"`, throw an {{InvalidStateError}}. 3. Let |frameClone| hold the result of running the [=Clone VideoFrame=] algorithm with |frame|. 4. Increment {{VideoEncoder/[[encodeQueueSize]]}}. 5. [=Queue a control message=] to encode |frameClone|. + 6. [=Process the control message queue=]. - [=Running a control message=] to encode the frame means performing these steps. - 1. Attempt to use {{VideoEncoder/[[codec implementation]]}} to encode - |frameClone| according to |options|. - 2. If encoding results in an error, queue a task on the [=control thread=] - event loop to run the [=Close VideoEncoder=] algorithm with - {{EncodingError}}. - 3. Queue a task on the [=control thread=] event loop to decrement - {{VideoEncoder/[[encodeQueueSize]]}}. - 4. Let |encoded outputs| be a [=list=] of encoded video data outputs - emitted by {{VideoEncoder/[[codec implementation]]}}. - 5. If |encoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output EncodedVideoChunks=] algorithm with - |encoded outputs|. + [=Running a control message=] to encode the frame means performing these + steps: + 1. If {{VideoEncoder/[[codec saturated]]}} equals `true`, return `"not + processed"`. + 2. If encoding |frame| will cause the + {{VideoEncoder/[[codec implementation]]}} to become [=saturated=], + assign `true` to {{VideoEncoder/[[codec saturated]]}}. + 3. Decrement {{VideoEncoder/[[encodeQueueSize]]}}. + 4. Enqueue the following steps to the {{VideoEncoder/[[codec work queue]]}}: + 1. Attempt to use {{VideoEncoder/[[codec implementation]]}} to encode + the |frameClone| according to |options|. + 2. If encoding results in an error, [=queue a task=] to run the + [=Close VideoEncoder=] algorithm with {{EncodingError}} and return. + 3. If {{VideoEncoder/[[codec saturated]]}} equals `true` and + {{VideoEncoder/[[codec implementation]]}} is no longer + [=saturated=], [=queue a task=] to perform the following steps: + 1. Assign `false` to {{VideoEncoder/[[codec saturated]]}}. + 2. [=Process the control message queue=]. + 4. Let |encoded outputs| be a [=list=] of encoded video data outputs + emitted by {{VideoEncoder/[[codec implementation]]}}. + 5. If |encoded outputs| is not empty, [=queue a task=] to run the + [=Output EncodedVideoChunks=] algorithm with |encoded outputs|. + 5. Return `"processed"`.
flush()
@@ -1458,22 +1490,24 @@ 1. If {{VideoEncoder/[[state]]}} is not `"configured"`, return [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}. 2. Let |promise| be a new Promise. - 3. [=Queue a control message=] to flush the codec with |promise|. - 4. Append |promise| to {{VideoEncoder/[[pending flush promises]]}}. - 5. Return |promise|. - - [=Running a control message=] to flush the codec means performing these steps - with |promise|. - 1. Signal {{VideoEncoder/[[codec implementation]]}} to emit all [=internal - pending outputs=]. - 2. Let |encoded outputs| be a [=list=] of encoded video data outputs - emitted by {{VideoEncoder/[[codec implementation]]}}. - 3. If |encoded outputs| is not empty, queue a task on the [=control thread=] - event loop to run the [=Output EncodedVideoChunks=] algorithm with - |encoded outputs|. - 4. Queue a task on the [=control thread=] event loop to run these steps: - 1. Remove |promise| from {{VideoEncoder/[[pending flush promises]]}}. - 2. Resolve |promise|. + 3. Append |promise| to {{VideoEncoder/[[pending flush promises]]}}. + 4. [=Queue a control message=] to flush the codec with |promise|. + 5. [=Process the control message queue=]. + 6. Return |promise|. + + [=Running a control message=] to flush the codec means performing these + steps with |promise|: + 1. Enqueue the following steps to the {{VideoEncoder/[[codec work queue]]}}: + 1. Signal {{VideoEncoder/[[codec implementation]]}} to emit all + [=internal pending outputs=]. + 2. Let |encoded outputs| be a [=list=] of encoded video data outputs + emitted by {{VideoEncoder/[[codec implementation]]}}. + 3. [=Queue a task=] to perform these steps: + 1. If |encoded outputs| is not empty, run the + [=Output EncodedVideoChunks=] algorithm with |encoded outputs|. + 2. Remove |promise| from + {{VideoEncoder/[[pending flush promises]]}}. + 3. Resolve |promise|.
reset()
@@ -1600,7 +1634,8 @@ 4. Set {{VideoEncoder/[[active output config]]}} to `null`. 5. Signal {{VideoEncoder/[[codec implementation]]}} to cease producing output for the previous configuration. - 6. Remove all [=control messages=] from the [=control message queue=]. + 6. Remove all [=control messages=] from the + {{VideoEncoder/[[control message queue]]}}. 7. Set {{VideoEncoder/[[encodeQueueSize]]}} to zero. 8. For each |promise| in {{VideoEncoder/[[pending flush promises]]}}: 1. Reject |promise| with |exception|. @@ -1613,8 +1648,9 @@ 2. Set {{VideoEncoder/[[state]]}} to `"closed"`. 3. Clear {{VideoEncoder/[[codec implementation]]}} and release associated [=system resources=]. - 4. If |exception| is not an {{AbortError}} {{DOMException}}, queue a task on - the [=control thread=] event loop to invoke the {{AudioDecoder/[[error callback]]}} with |exception|. + 4. If |exception| is not an {{AbortError}} {{DOMException}}, + [=queue a task=] to invoke the {{VideoEncoder/[[error callback]]}} with + |exception|. From def0c8a50ec8ffb1c7414ad149c864a2e15efd43 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Sat, 2 Jul 2022 00:06:13 -0700 Subject: [PATCH 10/12] Transition VideoFrame to new model --- index.src.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.src.html b/index.src.html index 755f5e2f..b73c69b1 100644 --- a/index.src.html +++ b/index.src.html @@ -3546,7 +3546,7 @@ [=computed plane layout/destinationStride=]. 4. Increment |row| by `1`. 9. Increment |planeIndex| by `1`. - 5. Queue a task on the [=control thread=] event loop to resolve |p|. + 5. [=Queue a task=] to resolve |p|. 9. Return |p|. : clone() From 18da5ad7e546b764d5ab561cecaf039f231f3797 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Sat, 2 Jul 2022 00:18:38 -0700 Subject: [PATCH 11/12] Transition ImageDecoder to new model --- index.src.html | 150 +++++++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/index.src.html b/index.src.html index b73c69b1..aff115c6 100644 --- a/index.src.html +++ b/index.src.html @@ -290,14 +290,6 @@ [=queue a task|queued=] from the [=[[codec work queue]]=] to the [=/event loop=] will use the [=codec task source=]. -DELETE ME {#delete-me} ----------------------- -These dfns should be removed by the final commit in this PR. I've placed them -here while I iterate to avoid breaking their references. - -control thread, -codec thread - AudioDecoder Interface {#audiodecoder-interface} ================================================ @@ -4532,6 +4524,19 @@ ### Internal Slots ### {#imagedecoder-internal-slots} +: [[control message queue]] +:: A [=queue=] of [=control messages=] to be performed upon this [=codec=] + instance. See [=[[control message queue]]=]. + +: [[message queue blocked]] +:: A boolean indicating when processing the + {{ImageDecoder/[[control message queue]]}} is blocked by a pending + [=control message=]. See [=[[message queue blocked]]=]. + +: [[codec work queue]] +:: A [=parallel queue=] used for running parallel steps that reference the + {{ImageDecoder/[[codec implementation]]}}. See [=[[codec work queue]]=]. + : \[[ImageTrackList]] :: An {{ImageTrackList}} describing the tracks found in {{ImageDecoder/[[encoded data]]}} @@ -4563,7 +4568,7 @@ : [[internal selected track index]] :: Identifies the image track within {{ImageDecoder/[[encoded data]]}} that is - used by decoding algorithms on the [=codec thread=]. + used by decoding algorithms. : [[tracks established]] :: A boolean indicating whether the track list has been established in @@ -4596,32 +4601,37 @@ 2. Let |d| be a new {{ImageDecoder}} object. In the steps below, all mentions of {{ImageDecoder}} members apply to |d| unless stated otherwise. - 3. Assign {{ImageDecoder/[[ImageTrackList]]}} a new {{ImageTrackList}} + 3. Assign a new [=queue=] to {{ImageDecoder/[[control message queue]]}}. + 4. Assign `false` to {{ImageDecoder/[[message queue blocked]]}}. + 5. Assign the result of starting a new [=parallel queue=] to + {{ImageDecoder/[[codec work queue]]}}. + 6. Assign {{ImageDecoder/[[ImageTrackList]]}} a new {{ImageTrackList}} initialized as follows: 1. Assign a new [=list=] to {{ImageTrackList/[[track list]]}}. 2. Assign `-1` to {{ImageTrackList/[[selected index]]}}. - 4. Assign {{ImageDecoderInit/type}} to {{ImageDecoder/[[type]]}}. - 5. Assign `null` to {{ImageDecoder/[[codec implementation]]}}. - 6. If `init.preferAnimation` [=map/exists=], assign `init.preferAnimation` + 7. Assign {{ImageDecoderInit/type}} to {{ImageDecoder/[[type]]}}. + 8. Assign `null` to {{ImageDecoder/[[codec implementation]]}}. + 9. If `init.preferAnimation` [=map/exists=], assign `init.preferAnimation` to the {{ImageDecoder/[[prefer animation]]}} internal slot. Otherwise, assign 'null' to {{ImageDecoder/[[prefer animation]]}} internal slot. - 7. Assign a new [=list=] to {{ImageDecoder/[[pending decode promises]]}}. - 8. Assign `-1` to {{ImageDecoder/[[internal selected track index]]}}. - 9. Assign `false` to {{ImageDecoder/[[tracks established]]}}. - 10. Assign `false` to {{ImageDecoder/[[closed]]}}. - 11. Assign a new [=map=] to {{ImageDecoder/[[progressive frame + 10. Assign a new [=list=] to {{ImageDecoder/[[pending decode promises]]}}. + 11. Assign `-1` to {{ImageDecoder/[[internal selected track index]]}}. + 12. Assign `false` to {{ImageDecoder/[[tracks established]]}}. + 13. Assign `false` to {{ImageDecoder/[[closed]]}}. + 14. Assign a new [=map=] to {{ImageDecoder/[[progressive frame generations]]}}. - 12. If |init|'s {{ImageDecoderInit/data}} member is of type + 15. If |init|'s {{ImageDecoderInit/data}} member is of type {{ReadableStream}}: 1. Assign a new [=list=] to {{ImageDecoder/[[encoded data]]}}. 2. Assign `false` to {{ImageDecoder/[[complete]]}} 3. [=Queue a control message=] to [=configure the image decoder=] with |init|. - 4. Let |reader| be the result of [=getting a reader=] for + 4. [=Process the control message queue=]. + 5. Let |reader| be the result of [=getting a reader=] for {{ImageDecoderInit/data}}. - 5. In parallel, perform the [=Fetch Stream Data Loop=] on |d| with + 6. In parallel, perform the [=Fetch Stream Data Loop=] on |d| with |reader|. - 13. Otherwise: + 16. Otherwise: 1. Assert that `init.data` is of type {{BufferSource}}. 2. Assign a copy of `init.data` to {{ImageDecoder/[[encoded data]]}}. 3. Assign `true` to {{ImageDecoder/[[complete]]}}. @@ -4629,28 +4639,33 @@ 5. Queue a control message to [=configure the image decoder=] with |init|. 6. Queue a control message to [=decode track metadata=]. - 14. return |d|. + 7. [=Process the control message queue=]. + 17. return |d|. [=Running a control message=] to configure the image decoder means running these steps: 1. Let |supported| be the result of running the [=ImageDecoder/Check Type Support=] algorithm with `init.type`. - 2. If |supported| is `false`, queue a task on the [=control thread=] event - loop to run the [=ImageDecoder/Close ImageDecoder=] algorithm - with a {{NotSupportedError}} {{DOMException}} and abort - these steps. - 3. If |supported| is `true`, assign the - {{ImageDecoder/[[codec implementation]]}} internal slot with an - implementation supporting `init.type` - 4. Configure {{ImageDecoder/[[codec implementation]]}} in accordance with - the values given for {{ImageDecoderInit/premultiplyAlpha}}, - {{ImageDecoderInit/colorSpaceConversion}}, - {{ImageDecoderInit/desiredWidth}}, and - {{ImageDecoderInit/desiredHeight}}. + 2. If |supported| is `false`, run the [=ImageDecoder/Close ImageDecoder=] + algorithm with a {{NotSupportedError}} {{DOMException}} and return + `"processed"`. + 3. Otherwise, assign the {{ImageDecoder/[[codec implementation]]}} internal + slot with an implementation supporting `init.type` + 3. Assign `true` to {{ImageDecoder/[[message queue blocked]]}}. + 3. Enqueue the following steps to the {{ImageDecoder/[[codec work queue]]}}: + 1. Configure {{ImageDecoder/[[codec implementation]]}} in accordance + with the values given for {{ImageDecoderInit/premultiplyAlpha}}, + {{ImageDecoderInit/colorSpaceConversion}}, + {{ImageDecoderInit/desiredWidth}}, and + {{ImageDecoderInit/desiredHeight}}. + 2. Assign `false` to {{ImageDecoder/[[message queue blocked]]}}. + 3. [=Queue a task=] to [=Process the control message queue=]. + 4. Return `"processed"`. [=Running a control message=] to decode track metadata means running these steps: - 1. Run the [=ImageDecoder/Establish Tracks=] algorithm. + 1. Enqueue the following steps to the {{ImageDecoder/[[codec work queue]]}}: + 1. Run the [=ImageDecoder/Establish Tracks=] algorithm. ### Attributes ### {#imagedecoder-attributes} : type @@ -4692,19 +4707,23 @@ 3. If |options| is `undefined`, assign a new {{ImageDecodeOptions}} to |options|. 4. Let |promise| be a new {{Promise}}. - 5. [=Queue a control message=] to decode the image with |options|, and + 5. Append |promise| to {{ImageDecoder/[[pending decode promises]]}}. + 6. [=Queue a control message=] to decode the image with |options|, and |promise|. - 6. Append |promise| to {{ImageDecoder/[[pending decode promises]]}}. - 7. Return |promise|. + 7. [=Process the control message queue=]. + 8. Return |promise|. [=Running a control message=] to decode the image means running these steps: - 1. Wait for {{ImageDecoder/[[tracks established]]}} to become `true`. - 2. If |options|.{{ImageDecodeOptions/completeFramesOnly}} is `false` and - the image is a [=Progressive Image=] for which the User Agent supports - progressive decoding, run the [=Decode Progressive Frame=] algorithm with |options|.{{ImageDecodeOptions/frameIndex}} and |promise|. - 3. Otherwise, run the [=Decode Complete Frame=] algorithm with - |options|.{{ImageDecodeOptions/frameIndex}} and |promise|. + 1. Enqueue the following steps to the {{ImageDecoder/[[codec work queue]]}}: + 1. Wait for {{ImageDecoder/[[tracks established]]}} to become `true`. + 2. If |options|.{{ImageDecodeOptions/completeFramesOnly}} is `false` and + the image is a [=Progressive Image=] for which the User Agent + supports progressive decoding, run the [=Decode Progressive Frame=] + algorithm with |options|.{{ImageDecodeOptions/frameIndex}} and + |promise|. + 3. Otherwise, run the [=Decode Complete Frame=] algorithm with + |options|.{{ImageDecodeOptions/frameIndex}} and |promise|. : reset() :: Immediately aborts all pending work. @@ -4739,9 +4758,8 @@ : [=read request/chunk steps=], given |chunk| :: 1. If {{ImageDecoder/[[closed]]}} is `true`, abort these steps. - 2. If |chunk| is not a Uint8Array object, queue a task on the - [=control thread=] event loop to run the - [=ImageDecoder/Close ImageDecoder=] algorithm with a + 2. If |chunk| is not a Uint8Array object, [=queue a task=] to run + the [=ImageDecoder/Close ImageDecoder=] algorithm with a {{DataError}} {{DOMException}} and abort these steps. 3. Let |bytes| be the byte sequence represented by the Uint8Array object. @@ -4757,9 +4775,8 @@ 2. Resolve {{ImageDecoder/[[completed promise]]}}. : [=read request/error steps=] - :: 1. Queue a task on the [=control thread=] event loop to run the - [=ImageDecoder/Close ImageDecoder=] algorithm with a - {{NotReadableError}} {{DOMException}} + :: 1. [=Queue a task=] to run the [=ImageDecoder/Close ImageDecoder=] + algorithm with a {{NotReadableError}} {{DOMException}} 2. Read a chunk from |reader| given |readRequest|. @@ -4768,11 +4785,10 @@ 1. Assert {{ImageDecoder/[[tracks established]]}} is `false`. 2. If {{ImageDecoder/[[encoded data]]}} does not contain enough data to determine the number of tracks: - 1. If {{ImageDecoder/complete}} is `true`, queue a task on the - [=control thread=] event loop to run the [=ImageDecoder/Close ImageDecoder=] algorithm. + 1. If {{ImageDecoder/complete}} is `true`, [=queue a task=] to run the + [=ImageDecoder/Close ImageDecoder=] algorithm. 2. Abort these steps. - 3. If the number of tracks is found to be `0`, queue a task on the - [=control thread=] event loop to run the + 3. If the number of tracks is found to be `0`, [=queue a task=] to run the [=ImageDecoder/Close ImageDecoder=] algorithm and abort these steps. 4. Let |newTrackList| be a new [=list=]. 5. For each |image track| found in {{ImageDecoder/[[encoded data]]}}: @@ -4812,8 +4828,7 @@ 8. Assign |selectedTrackIndex| to {{ImageDecoder/[[internal selected track index]]}}. 9. Assign `true` to {{ImageDecoder/[[tracks established]]}}. - 10. Queue a task on the [=control thread=] event loop to perform the - following steps: + 10. [=Queue a task=] to perform the following steps: 1. Assign |newTrackList| to the {{ImageDecoder/tracks}} {{ImageTrackList/[[track list]]}} internal slot. 2. Assign |selectedTrackIndex| to {{ImageDecoder/tracks}} @@ -4862,8 +4877,7 @@ [=track update struct/frame count=] is |latestFrameCount|. 2. Append |change| to |tracksChanges|. 5. If |tracksChanges| is [=list/empty=], abort these steps. - 6. Queue a task on the [=control thread=] event loop to perform the - following steps: + 6. [=Queue a task=] to perform the following steps: 1. For each update in |trackChanges|: 1. Let |updateTrack| be the {{ImageTrack}} at position `update.trackIndex` within {{ImageDecoder/tracks}}' @@ -4975,7 +4989,7 @@ 18. Resolve |promise| with |decodeResult|. : Resolve Decode (with |promise| and |result|) -:: 1. Queue a task on the [=control thread=] event loop to run these steps: +:: 1. [=Queue a task=] to perform these steps: 1. If {{ImageDecoder/[[closed]]}}, abort these steps. 2. Assert that |promise| is an element of {{ImageDecoder/[[pending decode promises]]}}. @@ -4988,7 +5002,7 @@ 2. If {{ImageDecoder/complete}} is `true`, let |exception| be a {{RangeError}}. Otherwise, let |exception| be an {{InvalidStateError}} {{DOMException}}. - 3. Queue a task on the [=control thread=] event loop to run these steps: + 3. [=Queue a task=] to perform these steps: 1. If {{ImageDecoder/[[closed]]}}, abort these steps. 2. Assert that |promise| is an element of {{ImageDecoder/[[pending decode promises]]}}. @@ -4996,7 +5010,7 @@ 4. Reject |promise| with |exception|. : Fatally Reject Bad Data -:: 1. Queue a task on the [=control thread=] event loop to run these steps: +:: 1. [=Queue a task=] to perform these steps: 1. If {{ImageDecoder/[[closed]]}}, abort these steps. 2. Run the [=ImageDecoder/Close ImageDecoder=] algorithm with an {{EncodingError}} {{DOMException}}. @@ -5294,13 +5308,17 @@ 11. [=Queue a control message=] to {{ImageTrack/[[ImageDecoder]]}}'s [=control message queue=] to update the internal selected track index with |selectedIndex|. + 12. [=Process the control message queue=] belonging to + {{ImageTrack/[[ImageDecoder]]}}. [=Running a control message=] to update the internal selected track index means running these steps: - 1. Assign |selectedIndex| to - {{ImageDecoder/[[internal selected track index]]}}. - 2. Remove all entries from - {{ImageDecoder/[[progressive frame generations]]}}. + 1. Enqueue the following steps to {{ImageTrack/[[ImageDecoder]]}}'s + {{ImageDecoder/[[codec work queue]]}}: + 1. Assign |selectedIndex| to + {{ImageDecoder/[[internal selected track index]]}}. + 2. Remove all entries from + {{ImageDecoder/[[progressive frame generations]]}}. ### Event Summary ### {#imagetracklist-eventsummary} From 1012fcdf324ead50582b1cc153dac0f79943fc34 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Sat, 2 Jul 2022 00:52:40 -0700 Subject: [PATCH 12/12] line wrap fix --- index.src.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.src.html b/index.src.html index aff115c6..6957d403 100644 --- a/index.src.html +++ b/index.src.html @@ -3889,7 +3889,8 @@ 1. Assign |minAllocationSize| to |computedLayout|'s [=computed plane layout/destinationOffset=]. - 2. Assign |computedLayout|'s [=computed plane layout/sourceWidthBytes=] to + 2. Assign |computedLayout|'s + [=computed plane layout/sourceWidthBytes=] to |computedLayout|'s [=computed plane layout/destinationStride=]. 13. Let |planeSize| be the product of multiplying |computedLayout|'s [=computed plane layout/destinationStride=] and