From 4b0c79bc74ce70d270292955c4547ee40f6b3c6f Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Sun, 4 Apr 2021 23:47:08 -0700 Subject: [PATCH 1/7] Add refcounting semantics to VideoFrame Making it symmetric w/ AudioFrame. Adds clone/close Adds [[resource reference]] Updates constructors accordingly (and fix cruft/obsolete steps). Removes destroy (replaced by close). Fixes #129 (in comboniation w/ PR #162). Notes issues #165 and #166 for follow up. --- index.src.html | 452 +++++++++++++++++++++++++++---------------------- 1 file changed, 247 insertions(+), 205 deletions(-) diff --git a/index.src.html b/index.src.html index 64fccf0f..991ea9d8 100644 --- a/index.src.html +++ b/index.src.html @@ -512,6 +512,12 @@
[=Enqueues a control message=] to decode the given |chunk|. + NOTE: Authors should call {{VideoFrame/close()}} on ouput + {{VideoFrame}}s immediately when frames are no longer needed. The + underlying [=frame resource=]s are owned by the {{VideoDecoder}} and + failing to release them (or waiting of garbage collection) may cause + decoding to stall. + When invoked, run these steps: 1. If {{VideoDecoder/state}} is not `"configured"`, throw an {{InvalidStateError}}. @@ -610,25 +616,29 @@
Run these steps: 1. For each |output| in |outputs|: - 1. Let |planes| be a sequence of {{Plane}}s containing the decoded video - frame data from |output|. + 1. Let |planes| be a sequence of {{Plane}}s containing the decoded + video frame data from |output|. 2. Let |pixelFormat| be the {{PixelFormat}} of |planes|. - 3. Let |frameInit| be a {{VideoFrameInit}} with the following keys: - 1. Let {{VideoFrameInit/timestamp}} and {{VideoFrameInit/duration}} + 3. Let |frameInit| be a {{VideoFramePlanesInit}} with the following + keys: + 1. Let {{VideoFramePlanesInit/timestamp}} and + {{VideoFramePlanesInit/duration}} be the {{EncodedVideoChunk/timestamp}} and {{EncodedVideoChunk/duration}} from the {{EncodedVideoChunk}} associated with |output|. - 2. Let {{VideoFrameInit/codedWidth}} and - {{VideoFrameInit/codedHeight}} + 2. Let {{VideoFramePlanesInit/codedWidth}} and + {{VideoFramePlanesInit/codedHeight}} be the width and height of the decoded video frame |output| in pixels, prior to any cropping or aspect ratio adjustments. - 3. Let {{VideoFrameInit/cropLeft}}, {{VideoFrameInit/cropTop}}, - {{VideoFrameInit/cropWidth}}, and {{VideoFrameInit/cropHeight}} + 3. Let {{VideoFramePlanesInit/cropLeft}}, + {{VideoFramePlanesInit/cropTop}}, + {{VideoFramePlanesInit/cropWidth}}, and + {{VideoFramePlanesInit/cropHeight}} be the crop region of the decoded video frame |output| in pixels, prior to any aspect ratio adjustments. - 4. Let {{VideoFrameInit/displayWidth}} and - {{VideoFrameInit/displayHeight}} be the display size of the - decoded video frame in pixels. + 4. Let {{VideoFramePlanesInit/displayWidth}} and + {{VideoFramePlanesInit/displayHeight}} be the display size of + the decoded video frame in pixels. 4. Let |frame| be a {{VideoFrame}}, constructed with |pixelFormat|, |planes|, and |frameInit|. 5. Invoke {{VideoDecoder/[[output callback]]}} with |frame|. @@ -1018,7 +1028,7 @@ `true`, throw a {{TypeError}}. 2. If {{VideoEncoder/state}} is not `"configured"`, throw an {{InvalidStateError}}. - 3. Let |frameClone| hold the result of running the [=Clone Frame=] + 3. Let |frameClone| hold the result of running the [=Clone VideoFrame=] algorithm with |frame|. 4. Destroy the original |frame| by invoking `frame.destroy()`. 5. Increment {{VideoEncoder/encodeQueueSize}}. @@ -1882,6 +1892,9 @@ minimize the need for expensive copies, this specification defines a scheme for reference counting (`frame.clone()` and `frame.close()`). +NOTE: Authors should take care to invoke `close()` immediately when frames are + no longer needed. + ### Reference Counting ### {#raw-media-memory-model-reference-counting} A frame resource is storage for the actual pixel data or the audio @@ -2002,11 +2015,11 @@ [Exposed=(Window,DedicatedWorker)] interface VideoFrame { constructor(ImageBitmap imageBitmap, optional VideoFrameInit frameInit = {}); - constructor(PixelFormat pixelFormat, sequence<(Plane or PlaneInit)> planes, + constructor(PixelFormat format, sequence<(Plane or PlaneInit)> planesInit, optional VideoFrameInit frameInit = {}); readonly attribute PixelFormat format; - readonly attribute FrozenArray planes; + readonly attribute FrozenArray? planes; readonly attribute unsigned long codedWidth; readonly attribute unsigned long codedHeight; readonly attribute unsigned long cropLeft; @@ -2018,8 +2031,8 @@ readonly attribute unsigned long long? duration; readonly attribute unsigned long long? timestamp; - undefined destroy(); VideoFrame clone(); + undefined close(); Promise createImageBitmap( optional ImageBitmapOptions options = {}); @@ -2027,6 +2040,11 @@ }; dictionary VideoFrameInit { + unsigned long long duration; + unsigned long long timestamp; +}; + +dictionary VideoFramePlanesInit { unsigned long codedWidth; unsigned long codedHeight; unsigned long cropLeft; @@ -2042,111 +2060,131 @@ ### Internal Slots ###{#videoframe-internal-slots} -
-
\[[detached]]
-
- Boolean indicating whether {{destroy()}} was invoked and underlying - resources have been released. -
-
+: \[[detached]] +:: A boolean indicating whether {{VideoFrame/close()}} was invoked. -### Constructors ###{#videoframe-constructors} +: [[resource reference]] +:: A reference to the [=frame resource=] that stores the pixel data for + this frame. -NOTE: this section needs work. Current wording assumes a VideoFrame can always - be easily represented using one of the known pixel formats. In practice, the - underlying UA resources may be GPU backed or formatted in such a way that - conversion to an allowed pixel format requires expensive copies and - translation. When this occurs, we should allow planes to be null and format - to be "opaque" to avoid early optimization. We should make conversion - explicit and user controlled by offering a `videoFrame.convertTo(format)` - that returns a Promise containing a new VideoFrame for which the - copies/translations are performed. +### Constructors ###{#videoframe-constructors} VideoFrame(imageBitmap, frameInit) -1. If |frameInit| is not a [=valid VideoFrameInit=], throw a {{TypeError}}. -2. If the value of |imageBitmap|'s' {{PlatformObject/[[Detached]]}} internal - slot is set to `true`, then throw an {{InvalidStateError}} DOMException. -3. Let |frame| be a new {{VideoFrame}}. -4. Assign `false` to |frame|’s {{VideoFrame/[[detached]]}} internal slot. -5. Use a copy of the pixel data in |imageBitmap| to initialize to following - frame attributes: - 1. Initialize `frame.pixelFormat` be the underlying format of imageBitmap. - 2. Initialize `frame.planes` to describe the arrangement of memory of the - copied pixel data. - 3. Assign regions of the copied pixel data to the - {{Plane/[[plane buffer]]}} internal slot of each plane as - appropriate for the pixel format. - 4. Initialize `frame.codedWidth` and `frame.codedHeight` describe the width - and height of the imageBitamp prior to any cropping or aspect ratio - adjustments. -6. Use |frameInit| to initialize the remaining frame attributes: - 1. If `frameInit.cropLeft` is present, initialize it `frame.cropLeft`. - Otherwise, default `frame.cropLeft` to zero. - 2. If `frameInit.cropTop` is present, initialize it to `frame.cropTop`. - Otherwise, default `frame.cropTop` to zero. - 3. If `frameInit.cropWidth` is present, initialize it to `frame.cropWidth`. - Otherwise, default `frame.cropWidth` to `frame.codedWidth`. - 4. If `frameInit.cropHeight` is present, initialize it to - `frame.cropHeight`. Otherwise, default `frame.cropHeight` to - `frame.codedHeight`. - 5. If `frameInit.displayWidth` is present, initialize it to - `frame.displayWidth`. Otherwise, default `frame.displayWidth` to - `frame.codedWidth`. - 6. If `frameInit.displayHeight` is present, initialize it to - `frame.displayHeight`. Otherwise, default `frame.displayHeight` to - `frame.codedHeight`. - 7. If `frameInit.duration` is present, initialize it to `frame.duration`. - Otherwise, default `frame.duration` to null. - 8. If `frameInit.timestamp` is present, initialize it to `frame.timestamp`. - Otherwise default `frame.timestamp` to null. +1. If the value of |imageBitmap|'s' {{PlatformObject/[[Detached]]}} internal + slot is `true`, then throw an {{InvalidStateError}} + {{DOMException}}. +2. Let |resource| be the [=frame resource=] containing the pixel data for + |imageBitmap|. +3. Let |resourceReference| be a reference to |resource|. +4. Let |frame| be a new {{VideoFrame}}, initialized as follows: + 1. Assign |resourceReference| to + {{VideoFrame/[[resource reference]]}}. + 2. If |resource| uses a recognized {{PixelFormat}}: + 1. Assign the {{PixelFormat}} of |resource| to {{VideoFrame/format}}. + 2. Let |planes| be a list of {{Plane}}s describing the + [=frame resource=] in accordance with the {{VideoFrame/format}}. + + ISSUE: The spec should define explicit rules for each + {{PixelFormat}} and reference them in the step above. See + [#165](https://github.com/w3c/webcodecs/issues/165). + + 3. Assign |planes| to {{VideoFrame/planes}}. + 3. Otherwise (|resource| does not use a recognized {{PixelFormat}}): + 1. Assign `""` to {{VideoFrame/format}}. + 2. Assign `null` to {{VideoFrame/planes}}. + 4. Assign |imageBitmap|.{{ImageBitmap/width}} to {{VideoFrame/codedWidth}}, + {{VideoFrame/cropWidth}}, and {{VideoFrame/displayWidth}}. + 5. Assign |imageBitmap|.{{ImageBitmap/height}} to + {{VideoFrame/codedHeight}}, {{VideoFrame/cropHeight}}, and + {{VideoFrame/displayHeight}}. + 6. Assign `0` to {{VideoFrame/cropTop}} and {{VideoFrame/cropLeft}}. + 7. If {{VideoFrameInit/timestamp}} [=map/exists=] in |frameInit|, assign + `frameInit.timestamp` to {{VideoFrame/timestamp}}. Otherwise, assign + `null` to {{VideoFrame/timestamp}}. + 8. If {{VideoFrameInit/duration}} [=map/exists=] in |frameInit|, assign + `frameInit.duration` to {{VideoFrame/duration}}. Otherwise, assign + `null` to {{VideoFrame/duration}}. +5. Return |frame|. + + + VideoFrame(format, planesInit, frameInit) + +1. If either {{VideoFramePlanesInit/codedWidth}} or + {{VideoFramePlanesInit/codedHeight}} is not present in |frameInit|, + throw a {{TypeError}}. +2. If |frameInit| is not a [=valid VideoFramePlanesInit=], throw a + {{TypeError}}. +3. If |planesInit| is incompatible with the given |format| (e.g. wrong + number of planes), throw a TypeError. + + ISSUE: The spec should list additional format specific validation steps ( + e.g. number and order of planes, acceptable sizing, etc...). See + [#165](https://github.com/w3c/webcodecs/issues/165). + +4. Let |resource| be a new [=frame resource=] allocated in accordance with + |frameInit|. + + ISSUE: The spec should define explicit rules for each + {{PixelFormat}} and reference them in the steps above. See + [#165](https://github.com/w3c/webcodecs/issues/165). + + NOTE: The user agent may choose to allocate resource with a larger coded + size and plane strides to improve memory alignment. Increases will be + reflected by {{VideoFrame/codedWidth}}, {{VideoFrame/codedHeight}}, and + {{Plane/stride}}. + +5. Let |resourceReference| be a reference to |resource|. +6. Let |frame| be a new {{VideoFrame}} object initialized as follows: + 1. Assign |resourceReference| to + {{VideoFrame/[[resource reference]]}}. + 2. Assign |format| to {{VideoFrame/format}}. + 3. Assign a new [=list=] to {{VideoFrame/planes}}. + 4. For each |planeInit| in |planesInit|: + 1. Copy |planeInit|.{{PlaneInit/src}} to |resource|. + 2. Let |plane| be a new {{Plane}} initialized as follows: + 1. Assign |frame| to {{Plane/[[parent frame]]}}. + 2. Let |resourceStride| be the stride of the plane coresponding to + |planeInit| in |resource|. + + ISSUE: The spec should provide a definition (and possibly + diagrams) for stride. See + [#166](https://github.com/w3c/webcodecs/issues/166). + + 3. Assign |resourceStride| to {{Plane/stride}}. + 4. Assign |planeInit|.{{PlaneInit/rows}} to {{Plane/rows}}. + 5. Assign the product of ({{Plane/stride}} * {{Plane/rows}}) to + {{Plane/length}}. + 3. Append |plane| to {{VideoFrame/planes}}. + + 5. Let |resourceCodedWidth| be the coded width of |resource|. + 6. Let |resourceCodedHeight| be the coded height of |resource|. + + ISSUE: The spec should provide definitions (and possibly diagrams) for + coded size, crop size, and display size. See + [#166](https://github.com/w3c/webcodecs/issues/166). + + 7. Assign |resourceCodedWidth| to {{VideoFrame/codedWidth}}. + 8. Assign |resourceCodedHeight| to {{VideoFrame/codedHeight}}. + 9. Assign |init|.{{VideoFramePlanesInit/cropLeft}} to + {{VideoFrame/cropLeft}}. + 10. Assign |init|.{{VideoFramePlanesInit/cropTop}} to + {{VideoFrame/cropTop}}. + 11. Assign |init|.{{VideoFramePlanesInit/cropWidth}} to + {{VideoFrame/cropWidth}}. + 12. Assign |init|.{{VideoFramePlanesInit/cropHeight}} to + {{VideoFrame/cropHeight}}. + 13. Assign |init|.{{VideoFramePlanesInit/displayWidth}} to + {{VideoFrame/displayWidth}}. + 14. Assign |init|.{{VideoFramePlanesInit/displayHeight}} to + {{VideoFrame/displayHeight}}. + 15. Assign |init|.{{VideoFramePlanesInit/timestamp}} to + {{VideoFrame/timestamp}}. + 16. Assign |init|.{{VideoFramePlanesInit/duration}} to + {{VideoFrame/duration}}. 7. Return |frame|. - - VideoFrame(pixelFormat, planes, frameInit) - -1. If either {{VideoFrameInit/codedWidth}} or {{VideoFrameInit/codedHeight}} is - not present in |frameInit|, throw a {{TypeError}}. -2. If |frameInit| is not a [=valid VideoFrameInit=], throw a {{TypeError}}. -3. If the length of |planes| is incompatible with the given pixelFormat, throw a - TypeError. -4. Let |frame| be a new {{VideoFrame}} object. -5. Assign `false` to |frame|’s {{VideoFrame/[[detached]]}} internal slot. -6. Assign `init.pixelFormat` to `frame.pixelFormat`. -7. For each element |p| in |planes|: - 1. If |p| is a {{Plane}}, append a copy of p to `frame.planes`. Continue - processing the next element. - 2. If |p| is a {{PlaneInit}}, append a new {{Plane}} q to - `frame.planes` initialized as follows: - 2. Assign a copy of `p.src` to q's [[plane buffer]] internal slot. - - NOTE: the samples should be copied exactly, but the user agent may add - row padding as needed to improve memory alignment. - - 3. Assign the width of each row in [[plane buffer]], including any - padding, to `q.stride`. - 4. Assign `p.rows` to `q.rows`. - 5. Assign the product of (`q.rows` * `q.stride)` to `q.length` -8. Assign `frameInit.codedWidth` to `frame.codedWidth`. -9. Assign `frameInit.codedHeight` to `frame.codedHeight`. -10. If `frameInit.cropLeft` is present, assign it `frame.cropLeft`. Otherwise, - default `frame.cropLeft` to zero. -11. If `frameInit.cropTop` is present, assign it to `frame.cropTop`. Otherwise, - default `frame.cropTop` to zero. -12. If `frameInit.cropWidth` is present, assign it to `frame.cropWidth`. - Otherwise, default `frame.cropWidth` to `frame.codedWidth`. -13. If `frameInit.cropHeight` is present, assign it to `frame.cropHeight`. - Otherwise, default `frame.cropHeight` to `frame.codedHeight`. -14. If `frameInit.displayWidth` is present, assign it to `frame.displayWidth`. - Otherwise, default `frame.displayWidth` to `frame.codedWidth`. -15. If `frameInit.displayHeight` is present, assign it to `frame.displayHeight`. - Otherwise, default `frame.displayHeight` to `frame.codedHeight`. -16. If `frameInit.duration` is present, assign it to `frame.duration`. - Otherwise, default `frame.duration` to null. -17. If `frameInit.timestamp` is present, assign it to `frame.timestamp`. - Otherwise, default `frame.timestamp` to null. -18. Return frame. ### Attributes ###{#videoframe-attributes}
@@ -2201,82 +2239,106 @@
### Methods ###{#videoframe-methods} -destroy() -Immediately frees [=system resources=]. Destruction applies to all - references, including references that are serialized and passed across - Realms. - -NOTE: Authors should take care to manage frame lifetimes by calling - {{VideoFrame/destroy()}} immediately when frames are no longer needed. - -NOTE: Use clone() to create a deep copy. Cloned frames have their own lifetime - and will not be affected by destroying the original frame. - -When invoked, run these steps: -1. If {{VideoFrame/[[detached]]}} is `true`, throw an {{InvalidStateError}}. -2. Remove all {{Plane}}s from {{VideoFrame/planes}} and release associated - memory. -3. Assign `true` to the {{VideoFrame/[[detached]]}} internal slot. +: clone() +:: Creates a new {{VideoFrame}} with a reference to the same + [=frame resource=]. -clone() -Creates a new {{VideoFrame}} with a separate lifetime containing a deep copy of - this frame’s resources. + When invoked, run the these steps: + 1. If the value of |frame|’s {{VideoFrame/[[detached]]}} internal slot is + `true`, throw an {{InvalidStateError}} {{DOMException}}. + 2. Return the result of running the [=Clone VideoFrame=] algorithm with + [=this=]. -NOTE: VideoFrames may require a large amount of memory. Use - {{VideoFrame/clone()}} sparingly. -When invoked, run the following steps: -1. If the value of the {{VideoFrame/[[detached]]}} slot is `true`, return - [=a promise rejected with=] {{InvalidStateError}} {{DOMException}}. -2. Let |p| be a new Promise. -3. In parallel, resolve |p| with the result of running the Clone Frame - algorithm with this. -4. Return |p|. +: close() +:: Clears all state and releases the reference to the [=frame resource=]. + Close is final. -createImageBitmap(options) -Creates an ImageBitmap from this {{VideoFrame}}. + When invoked, run these steps: + 1. Assign `null` to {{VideoFrame/[[resource reference]]}}. + 2. Assign `true` to {{VideoFrame/[[detached]]}}. + 3. Assign `""` to {{VideoFrame/format}}. + 4. Assign `null` to {{VideoFrame/planes}}. + 5. Assign `0` to {{VideoFrame/codedWidth}}, {{VideoFrame/codedHeight}}, + {{VideoFrame/cropLeft}}, {{VideoFrame/cropTop}}, + {{VideoFrame/cropWidth}}, {{VideoFrame/cropHeight}}, + {{VideoFrame/displayWidth}}, and {{VideoFrame/displayHeight}}. + 6. Assign `null` to {{VideoFrame/duration}} and {{VideoFrame/timestamp}}. + +: createImageBitmap(options) +:: Creates an ImageBitmap from this {{VideoFrame}}. -When invoked, run these steps: -1. Let |p| be a new Promise. -2. If either |options|'s {{ImageBitmapOptions/resizeWidth}} or - {{ImageBitmap/resizeHeight}} is present and is 0, then return |p| rejected - with an {{InvalidStateError}} {{DOMException}}. -3. If the this' {{VideoFrame/[[detached]]}} internal slot is set to - `true`, then return |p| rejected with an {{InvalidStateError}} - {{DOMException}}. -4. Let |imageBitmap| be a new {{ImageBitmap}} object. -5. Set |imageBitmap|'s bitmap data to a copy of the {{VideoFrame}} pixel data, - at the frame's intrinsic width and intrinsic height (`i.e`., after any - aspect-ratio correction has been applied), cropped to the source rectangle - with formatting. -6. If the origin of |imageBitmap|'s image is not same origin with entry settings - object's origin, then set the origin-clean flag of |imageBitmap|'s bitmap to - `false`. -7. Run this step in parallel: - 1. Resolve p with imageBitmap. + When invoked, run these steps: + 1. Let |p| be a new Promise. + 2. If either |options|'s {{ImageBitmapOptions/resizeWidth}} or + {{ImageBitmap/resizeHeight}} is present and is 0, then return |p| + rejected with an {{InvalidStateError}} {{DOMException}}. + 3. If the this' {{VideoFrame/[[detached]]}} internal slot is set to + `true`, then return |p| rejected with an {{InvalidStateError}} + {{DOMException}}. + 4. Let |imageBitmap| be a new {{ImageBitmap}} object. + 5. Set |imageBitmap|'s bitmap data to a copy of the {{VideoFrame}} pixel + data, at the frame's intrinsic width and intrinsic height (`i.e`., after + any aspect-ratio correction has been applied), cropped to the source + rectangle with formatting. + 6. If the origin of |imageBitmap|'s image is not same origin with entry + settings object's origin, then set the origin-clean flag of + |imageBitmap|'s bitmap to `false`. + 7. Run this step in parallel: + 1. Resolve p with imageBitmap. ### Algorithms ###{#videoframe-algorithms} -To check if a {{VideoFrameInit}} is a valid VideoFrameInit, -run these steps: -1. If {{VideoFrameInit/codedWidth}} = 0 or {{VideoFrameInit/codedHeight}} = 0, - return `false`. -2. If {{VideoFrameInit/cropWidth}} = 0 or {{VideoFrameInit/cropHeight}} = 0, - return `false`. -3. If {{VideoFrameInit/cropTop}} + {{VideoFrameInit/cropHeight}} >= - {{VideoFrameInit/codedHeight}}, return `false`. -4. If {{VideoFrameInit/cropLeft}} + {{VideoFrameInit/cropWidth}} >= - {{VideoFrameInit/codedWidth}}, return `false`. -5. If {{VideoFrameInit/displayWidth}} = 0 or - {{VideoFrameInit/displayHeight}} = 0, return `false`. -6. Return `true`. - +: To check if a {{VideoFramePlanesInit}} is a + valid VideoFramePlanesInit, run these steps: +:: 1. If {{VideoFramePlanesInit/codedWidth}} = 0 or + {{VideoFramePlanesInit/codedHeight}} = 0,return `false`. + 2. If {{VideoFramePlanesInit/cropWidth}} = 0 or + {{VideoFramePlanesInit/cropHeight}} = 0, return `false`. + 3. If {{VideoFramePlanesInit/cropTop}} + + {{VideoFramePlanesInit/cropHeight}} >= + {{VideoFramePlanesInit/codedHeight}}, return `false`. + 4. If {{VideoFramePlanesInit/cropLeft}} + + {{VideoFramePlanesInit/cropWidth}} >= + {{VideoFramePlanesInit/codedWidth}}, return `false`. + 5. If {{VideoFramePlanesInit/displayWidth}} = 0 or + {{VideoFramePlanesInit/displayHeight}} = 0, return `false`. + 6. Return `true`. + +: Clone VideoFrame (with |frame|) +:: 1. Let |clone| be a new {{VideoFrame}} initialized as follows: + 1. Assign |frame|.{{VideoFrame/[[resource reference]]}} to + {{AudioFrame/[[resource reference]]}}. + 2. Assign |frame|.{{VideoFrame/format}} to {{VideoFrame/format}}. + 3. Assign a new [=list=] to {{VideoFrame/planes}}. + 4. For each |plane| in {{VideoFrame/planes}}: + 1. Let |clonePlane| be a new {{Plane}} initialized as follows: + 1. Assign |clone| to |clonePlane|.{{Plane/[[parent frame]]}}. + 2. Assign |plane|.{{Plane/stride}} to {{Plane/stride}}. + 3. Assign |plane|.{{Plane/rows}} to {{Plane/rows}}. + 4. Assign |plane|.{{Plane/length}} to {{Plane/length}}. + 2. Append |clonePlane| to {{VideoFrame/planes}}. + 5. Assign |frame|.{{VideoFrame/codedWidth}} to + {{VideoFrame/codedWidth}}. + 6. Assign |frame|.{{VideoFrame/codedHeight}} to + {{VideoFrame/codedHeight}}. + 7. Assign |frame|.{{VideoFrame/cropLeft}} to {{VideoFrame/cropLeft}}. + 8. Assign |frame|.{{VideoFrame/cropTop}} to {{VideoFrame/cropTop}}. + 9. Assign |frame|.{{VideoFrame/cropWidth}} to {{VideoFrame/cropWidth}}. + 10. Assign |frame|.{{VideoFrame/cropHeight}} to + {{VideoFrame/cropHeight}}. + 11. Assign |frame|.{{VideoFrame/codedHeight}} to + {{VideoFrame/codedHeight}}. + 12. Assign |frame|.{{VideoFrame/displayWidth}} to + {{VideoFrame/displayWidth}}. + 13. Assign |frame|.{{VideoFrame/displayHeight}} to + {{VideoFrame/displayHeight}}. + 14. Assign |frame|.{{VideoFrame/duration}} to {{VideoFrame/duration}}. + 15. Assign |frame|.{{VideoFrame/timestamp}} to + {{VideoFrame/timestamp}}. + 2. Return |clone|. Plane Interface {#plane-interface} ---------------------------------- -A {{Plane}} acts like a thin wrapper around an {{ArrayBuffer}}, but may actually - be backed by a texture. {{Plane}}s hide any padding before the first sample - or after the last row. - A {{Plane}} is solely constructed by its {{VideoFrame}}. During construction, the User Agent may use knowledge of the frame’s {{PixelFormat}} to add padding to the {{Plane}} to improve memory alignment. @@ -2284,7 +2346,8 @@ A {{Plane}} cannot be used after the {{VideoFrame}} is destroyed. A new {{VideoFrame}} can be assembled from existing {{Plane}}s, and the new {{VideoFrame}} will remain valid when the original is destroyed. This makes - it possible to efficiently add an alpha plane to an existing {{VideoFrame}}. + it possible to efficiently add an alpha plane to an existing + {{VideoFrame}}.
@@ -2309,8 +2372,6 @@
 
[[parent frame]]
Refers to the {{VideoFrame}} that constructed and owns this plane.
-
[[plane buffer]]
-
Internal storage for the plane’s pixel data.
### Attributes ###{#plane-attributes} @@ -2333,7 +2394,11 @@ {{InvalidStateError}}. 2. If {{Plane/length}} is greater than |`dst.byteLength`|, throw a {{TypeError}}. -3. Copy the {{Plane/[[plane buffer]]}} into dst. +3. Let |resource| be the [=frame resource=] referneced by + {{Plane/[[parent frame]]}}'s {{VideoFrame/[[resource reference]]}}. +4. Let |plane bytes| be the region of bytes in [=frame resource=] coresponding + to this plane. +5. Copy the |plane bytes| into dst. Pixel Format{#pixel-format} @@ -2362,29 +2427,6 @@
- -Algorithms{#raw-media-algorithms} ---------------------------------- -
-
Clone Frame (with |frame|)
-
- 1. Let |cloneFrame| be a new object of the same type as frame (either - {{AudioFrame}} or {{VideoFrame}}). - 2. Initialize each attribute and internal slot of clone with a copy of the - value from the corresponding attribute of this frame. - - NOTE: User Agents are encouraged to avoid expensive copies of large - objects (for instance, {{VideoFrame}} pixel data). Frame types are - immutable, so the above step may be implemented using memory sharing - techniques such as reference counting. - - 3. Return |cloneFrame|. - -
-
- - - Security Considerations{#security-considerations} ================================================= From e3522520a8c99c2439338f6df71e887b9ca6e3aa Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Mon, 5 Apr 2021 00:10:22 -0700 Subject: [PATCH 2/7] Restore Makefile to fail on warning --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index b5f3d7f7..46c82d50 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ remote-index: index.src.html --output index.html \ --write-out "%{http_code}" \ --header "Accept: text/plain, text/html" \ + -F die-on=warning \ -F file=@index.src.html) && \ [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ echo ""; cat index.html; echo ""; \ From 3d0a90d58baae227d14fe96b8ce1077f06d00705 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Thu, 8 Apr 2021 23:40:50 -0700 Subject: [PATCH 3/7] Update wording to address padenot@ feedback --- index.src.html | 124 +++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 65 deletions(-) diff --git a/index.src.html b/index.src.html index 943c8f0d..44e1aff4 100644 --- a/index.src.html +++ b/index.src.html @@ -38,6 +38,7 @@ for: ImageBitmap; type: attribute; text: resizeWidth; url:#dom-imagebitmapoptions-resizewidth type: attribute; text: resizeHeight; url:#dom-imagebitmapoptions-resizeheight + type: dfn; text: cropped to the source rectangle with formatting; url: imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting type: dfn; text: global object; url: webappapis.html#global-object spec: mediacapture-streams; urlPrefix: https://www.w3.org/TR/mediacapture-streams/ @@ -515,7 +516,7 @@ NOTE: Authors should call {{VideoFrame/close()}} on ouput {{VideoFrame}}s immediately when frames are no longer needed. The underlying [=frame resource=]s are owned by the {{VideoDecoder}} and - failing to release them (or waiting of garbage collection) may cause + failing to release them (or waiting for garbage collection) may cause decoding to stall. When invoked, run these steps: @@ -2055,8 +2056,8 @@ }; dictionary VideoFramePlanesInit { - unsigned long codedWidth; - unsigned long codedHeight; + required unsigned long codedWidth; + required unsigned long codedHeight; unsigned long cropLeft; unsigned long cropTop; unsigned long cropWidth; @@ -2085,7 +2086,7 @@ 1. If the value of |imageBitmap|'s' {{PlatformObject/[[Detached]]}} internal slot is `true`, then throw an {{InvalidStateError}} {{DOMException}}. -2. Let |resource| be the [=frame resource=] containing the pixel data for +2. Let |resource| be the [=frame resource=] containing the bitmap data for |imageBitmap|. 3. Let |resourceReference| be a reference to |resource|. 4. Let |frame| be a new {{VideoFrame}}, initialized as follows: @@ -2121,19 +2122,16 @@ VideoFrame(format, planesInit, frameInit) -1. If either {{VideoFramePlanesInit/codedWidth}} or - {{VideoFramePlanesInit/codedHeight}} is not present in |frameInit|, - throw a {{TypeError}}. -2. If |frameInit| is not a [=valid VideoFramePlanesInit=], throw a +1. If |frameInit| is not a [=valid VideoFramePlanesInit=], throw a {{TypeError}}. -3. If |planesInit| is incompatible with the given |format| (e.g. wrong +2. If |planesInit| is incompatible with the given |format| (e.g. wrong number of planes), throw a TypeError. ISSUE: The spec should list additional format specific validation steps ( e.g. number and order of planes, acceptable sizing, etc...). See [#165](https://github.com/w3c/webcodecs/issues/165). -4. Let |resource| be a new [=frame resource=] allocated in accordance with +3. Let |resource| be a new [=frame resource=] allocated in accordance with |frameInit|. ISSUE: The spec should define explicit rules for each @@ -2145,55 +2143,67 @@ reflected by {{VideoFrame/codedWidth}}, {{VideoFrame/codedHeight}}, and {{Plane/stride}}. -5. Let |resourceReference| be a reference to |resource|. -6. Let |frame| be a new {{VideoFrame}} object initialized as follows: +4. Let |resourceReference| be a reference to |resource|. +5. Let |frame| be a new {{VideoFrame}} object initialized as follows: 1. Assign |resourceReference| to {{VideoFrame/[[resource reference]]}}. 2. Assign |format| to {{VideoFrame/format}}. 3. Assign a new [=list=] to {{VideoFrame/planes}}. 4. For each |planeInit| in |planesInit|: 1. Copy |planeInit|.{{PlaneInit/src}} to |resource|. + + NOTE: The user agent may use {{VideoFramePlanesInit/cropLeft}} + and {{VideoFramePlanesInit/cropTop}} to copy only the crop + region. It may also reposition the crop region within + |resource|. The final position will be reflected by + {{VideoFrame/cropLeft}} and {{VideoFrame/cropTop}}. + 2. Let |plane| be a new {{Plane}} initialized as follows: - 1. Assign |frame| to {{Plane/[[parent frame]]}}. - 2. Let |resourceStride| be the stride of the plane coresponding to - |planeInit| in |resource|. - - ISSUE: The spec should provide a definition (and possibly - diagrams) for stride. See - [#166](https://github.com/w3c/webcodecs/issues/166). - - 3. Assign |resourceStride| to {{Plane/stride}}. - 4. Assign |planeInit|.{{PlaneInit/rows}} to {{Plane/rows}}. - 5. Assign the product of ({{Plane/stride}} * {{Plane/rows}}) to - {{Plane/length}}. + 1. Assign |frame| to {{Plane/[[parent frame]]}}. + 2. Let |resourceStride| be the stride of the plane coresponding to + |planeInit| in |resource|. + + ISSUE: The spec should provide a definition (and possibly + diagrams) for stride. See + [#166](https://github.com/w3c/webcodecs/issues/166). + + 3. Assign |resourceStride| to {{Plane/stride}}. + 4. Assign |planeInit|.{{PlaneInit/rows}} to {{Plane/rows}}. + 5. Assign the product of ({{Plane/stride}} * {{Plane/rows}}) to + {{Plane/length}}. 3. Append |plane| to {{VideoFrame/planes}}. 5. Let |resourceCodedWidth| be the coded width of |resource|. 6. Let |resourceCodedHeight| be the coded height of |resource|. + 7. Let |resourceCropLeft| be the left offset of the crop origin of + |resource|. + 8. Let |resourceCropTop| be the top offset of the crop origin of + |resource|. ISSUE: The spec should provide definitions (and possibly diagrams) for coded size, crop size, and display size. See [#166](https://github.com/w3c/webcodecs/issues/166). - 7. Assign |resourceCodedWidth| to {{VideoFrame/codedWidth}}. - 8. Assign |resourceCodedHeight| to {{VideoFrame/codedHeight}}. - 9. Assign |init|.{{VideoFramePlanesInit/cropLeft}} to - {{VideoFrame/cropLeft}}. - 10. Assign |init|.{{VideoFramePlanesInit/cropTop}} to - {{VideoFrame/cropTop}}. - 11. Assign |init|.{{VideoFramePlanesInit/cropWidth}} to - {{VideoFrame/cropWidth}}. - 12. Assign |init|.{{VideoFramePlanesInit/cropHeight}} to - {{VideoFrame/cropHeight}}. - 13. Assign |init|.{{VideoFramePlanesInit/displayWidth}} to - {{VideoFrame/displayWidth}}. - 14. Assign |init|.{{VideoFramePlanesInit/displayHeight}} to - {{VideoFrame/displayHeight}}. - 15. Assign |init|.{{VideoFramePlanesInit/timestamp}} to - {{VideoFrame/timestamp}}. - 16. Assign |init|.{{VideoFramePlanesInit/duration}} to - {{VideoFrame/duration}}. -7. Return |frame|. + 9. Assign |resourceCodedWidth|, |resourceCodedHeight|, |resourceCropLeft|, + and |resourceCropTop| to {{VideoFrame/codedWidth}}, + {{VideoFrame/codedHeight}}, {{VideoFrame/cropLeft}}, and + {{VideoFrame/cropTop}} respectively. + 10. If |init|.{{VideoFramePlanesInit/cropWidth}} [=map/exists=], assign it + to {{VideoFrame/cropWidth}}. Otherwise, assign + {{VideoFrame/codedWidth}} to {{VideoFrame/cropWidth}}. + 11. If |init|.{{VideoFramePlanesInit/cropHeight}} [=map/exists=], assign it + to {{VideoFrame/cropHeight}}. Otehrwise, assign + {{VideoFrame/codedHeight}} to {{VideoFrame/cropHeight}}. + 12. If |init|.{{VideoFramePlanesInit/displayWidth}} [=map/exists=], assign + it to {{VideoFrame/displayWidth}}. Otherwise, assign + {{VideoFrame/cropWidth}} to {{VideoFrame/displayWidth}}. + 13. If |init|.{{VideoFramePlanesInit/displayHeight}} [=map/exists=], assign + it to {{VideoFrame/displayHeight}}. Otherwise, assign + {{VideoFrame/cropHeight}} to {{VideoFrame/displayHeight}}. + 14. Assign |init|'s {{VideoFramePlanesInit/timestamp}} and + {{VideoFramePlanesInit/duration}} to {{VideoFrame/timestamp}} and + {{VideoFrame/duration}} respectively. +6. Return |frame|. ### Attributes ###{#videoframe-attributes} @@ -2288,9 +2298,9 @@ {{DOMException}}. 4. Let |imageBitmap| be a new {{ImageBitmap}} object. 5. Set |imageBitmap|'s bitmap data to a copy of the {{VideoFrame}} pixel - data, at the frame's intrinsic width and intrinsic height (`i.e`., after - any aspect-ratio correction has been applied), cropped to the source - rectangle with formatting. + data, at the frame's intrinsic width and intrinsic height (`i.e`., + after any aspect-ratio correction has been applied), + [=ImageBitmap/cropped to the source rectangle with formatting=]. 6. If the origin of |imageBitmap|'s image is not same origin with entry settings object's origin, then set the origin-clean flag of |imageBitmap|'s bitmap to `false`. @@ -2327,24 +2337,8 @@ 3. Assign |plane|.{{Plane/rows}} to {{Plane/rows}}. 4. Assign |plane|.{{Plane/length}} to {{Plane/length}}. 2. Append |clonePlane| to {{VideoFrame/planes}}. - 5. Assign |frame|.{{VideoFrame/codedWidth}} to - {{VideoFrame/codedWidth}}. - 6. Assign |frame|.{{VideoFrame/codedHeight}} to - {{VideoFrame/codedHeight}}. - 7. Assign |frame|.{{VideoFrame/cropLeft}} to {{VideoFrame/cropLeft}}. - 8. Assign |frame|.{{VideoFrame/cropTop}} to {{VideoFrame/cropTop}}. - 9. Assign |frame|.{{VideoFrame/cropWidth}} to {{VideoFrame/cropWidth}}. - 10. Assign |frame|.{{VideoFrame/cropHeight}} to - {{VideoFrame/cropHeight}}. - 11. Assign |frame|.{{VideoFrame/codedHeight}} to - {{VideoFrame/codedHeight}}. - 12. Assign |frame|.{{VideoFrame/displayWidth}} to - {{VideoFrame/displayWidth}}. - 13. Assign |frame|.{{VideoFrame/displayHeight}} to - {{VideoFrame/displayHeight}}. - 14. Assign |frame|.{{VideoFrame/duration}} to {{VideoFrame/duration}}. - 15. Assign |frame|.{{VideoFrame/timestamp}} to - {{VideoFrame/timestamp}}. + 5. Assign all remaining attributes of |frame| ( + {{VideoFrame/codedWidth}}, {{VideoFrame/codedHeight}}, etc.) to those of the same name in |clone|. 2. Return |clone|. Plane Interface {#plane-interface} @@ -2404,7 +2398,7 @@ {{InvalidStateError}}. 2. If {{Plane/length}} is greater than |`dst.byteLength`|, throw a {{TypeError}}. -3. Let |resource| be the [=frame resource=] referneced by +3. Let |resource| be the [=frame resource=] refrenced by {{Plane/[[parent frame]]}}'s {{VideoFrame/[[resource reference]]}}. 4. Let |plane bytes| be the region of bytes in [=frame resource=] coresponding to this plane. From 763f62958a297119992e81bde18c131569bc03c2 Mon Sep 17 00:00:00 2001 From: Chris Cunningham Date: Fri, 16 Apr 2021 17:00:25 -0700 Subject: [PATCH 4/7] Fold VideoFrame format into dictionary argument, fix naming pluralization --- index.src.html | 109 +++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/index.src.html b/index.src.html index 44e1aff4..8e1df78e 100644 --- a/index.src.html +++ b/index.src.html @@ -381,11 +381,11 @@
Run these steps: 1. For each |output| in |outputs|: - 1. Let |frameInit| be an {{AudioFrameInit}} with the following keys: + 1. Let |init| be an {{AudioFrameInit}} with the following keys: 1. Let {{AudioFrameInit/buffer}} be an {{AudioBuffer}} containing the decoded audio data from |output|. 2. Let {{AudioFrameInit/timestamp}} be the timestamp from |output|. - 2. Let |frame| be an {{AudioFrame}} constructed with |frameInit|. + 2. Let |frame| be an {{AudioFrame}} constructed with |init|. 3. Invoke {{AudioDecoder/[[output callback]]}} with frame.
Reset AudioDecoder
@@ -620,28 +620,28 @@ 1. Let |planes| be a sequence of {{Plane}}s containing the decoded video frame data from |output|. 2. Let |pixelFormat| be the {{PixelFormat}} of |planes|. - 3. Let |frameInit| be a {{VideoFramePlanesInit}} with the following + 3. Let |init| be a {{VideoFramePlaneInit}} with the following keys: - 1. Let {{VideoFramePlanesInit/timestamp}} and - {{VideoFramePlanesInit/duration}} + 1. Let {{VideoFramePlaneInit/timestamp}} and + {{VideoFramePlaneInit/duration}} be the {{EncodedVideoChunk/timestamp}} and {{EncodedVideoChunk/duration}} from the {{EncodedVideoChunk}} associated with |output|. - 2. Let {{VideoFramePlanesInit/codedWidth}} and - {{VideoFramePlanesInit/codedHeight}} + 2. Let {{VideoFramePlaneInit/codedWidth}} and + {{VideoFramePlaneInit/codedHeight}} be the width and height of the decoded video frame |output| in pixels, prior to any cropping or aspect ratio adjustments. - 3. Let {{VideoFramePlanesInit/cropLeft}}, - {{VideoFramePlanesInit/cropTop}}, - {{VideoFramePlanesInit/cropWidth}}, and - {{VideoFramePlanesInit/cropHeight}} + 3. Let {{VideoFramePlaneInit/cropLeft}}, + {{VideoFramePlaneInit/cropTop}}, + {{VideoFramePlaneInit/cropWidth}}, and + {{VideoFramePlaneInit/cropHeight}} be the crop region of the decoded video frame |output| in pixels, prior to any aspect ratio adjustments. - 4. Let {{VideoFramePlanesInit/displayWidth}} and - {{VideoFramePlanesInit/displayHeight}} be the display size of + 4. Let {{VideoFramePlaneInit/displayWidth}} and + {{VideoFramePlaneInit/displayHeight}} be the display size of the decoded video frame in pixels. 4. Let |frame| be a {{VideoFrame}}, constructed with |pixelFormat|, - |planes|, and |frameInit|. + |planes|, and |init|. 5. Invoke {{VideoDecoder/[[output callback]]}} with |frame|.
Reset VideoDecoder
@@ -2025,9 +2025,9 @@ [Exposed=(Window,DedicatedWorker)] interface VideoFrame { - constructor(ImageBitmap imageBitmap, optional VideoFrameInit frameInit = {}); - constructor(PixelFormat format, sequence<(Plane or PlaneInit)> planesInit, - optional VideoFrameInit frameInit = {}); + constructor(ImageBitmap imageBitmap, optional VideoFrameInit init = {}); + constructor(sequence<(Plane or PlaneInit)> planes, + VideoFramePlaneInit init); readonly attribute PixelFormat format; readonly attribute FrozenArray<Plane>? planes; @@ -2055,7 +2055,8 @@ unsigned long long timestamp; }; -dictionary VideoFramePlanesInit { +dictionary VideoFramePlaneInit { + required PixelFormat format; required unsigned long codedWidth; required unsigned long codedHeight; unsigned long cropLeft; @@ -2080,8 +2081,8 @@ ### Constructors ###{#videoframe-constructors} -<dfn constructor for=VideoFrame title="VideoFrame(imageBitmap, frameInit)"> - VideoFrame(imageBitmap, frameInit) +<dfn constructor for=VideoFrame title="VideoFrame(imageBitmap, init)"> + VideoFrame(imageBitmap, init) </dfn> 1. If the value of |imageBitmap|'s' {{PlatformObject/[[Detached]]}} internal slot is `true`, then throw an {{InvalidStateError}} @@ -2111,28 +2112,28 @@ {{VideoFrame/codedHeight}}, {{VideoFrame/cropHeight}}, and {{VideoFrame/displayHeight}}. 6. Assign `0` to {{VideoFrame/cropTop}} and {{VideoFrame/cropLeft}}. - 7. If {{VideoFrameInit/timestamp}} [=map/exists=] in |frameInit|, assign - `frameInit.timestamp` to {{VideoFrame/timestamp}}. Otherwise, assign + 7. If {{VideoFrameInit/timestamp}} [=map/exists=] in |init|, assign + `init.timestamp` to {{VideoFrame/timestamp}}. Otherwise, assign `null` to {{VideoFrame/timestamp}}. - 8. If {{VideoFrameInit/duration}} [=map/exists=] in |frameInit|, assign - `frameInit.duration` to {{VideoFrame/duration}}. Otherwise, assign + 8. If {{VideoFrameInit/duration}} [=map/exists=] in |init|, assign + `init.duration` to {{VideoFrame/duration}}. Otherwise, assign `null` to {{VideoFrame/duration}}. 5. Return |frame|. -<dfn constructor for=VideoFrame title="VideoFrame(format, planesInit, frameInit)"> - VideoFrame(format, planesInit, frameInit) +<dfn constructor for=VideoFrame title="VideoFrame(planes, init)"> + VideoFrame(planes, init) </dfn> -1. If |frameInit| is not a [=valid VideoFramePlanesInit=], throw a +1. If |init| is not a [=valid VideoFramePlaneInit=], throw a {{TypeError}}. -2. If |planesInit| is incompatible with the given |format| (e.g. wrong - number of planes), throw a TypeError. +2. If |planes| is incompatible with the given {{VideoFramePlaneInit/format}} + (e.g. wrong number of planes), throw a {{TypeError}}. ISSUE: The spec should list additional format specific validation steps ( e.g. number and order of planes, acceptable sizing, etc...). See [#165](https://github.com/w3c/webcodecs/issues/165). 3. Let |resource| be a new [=frame resource=] allocated in accordance with - |frameInit|. + |init|. ISSUE: The spec should define explicit rules for each {{PixelFormat}} and reference them in the steps above. See @@ -2147,13 +2148,13 @@ 5. Let |frame| be a new {{VideoFrame}} object initialized as follows: 1. Assign |resourceReference| to {{VideoFrame/[[resource reference]]}}. - 2. Assign |format| to {{VideoFrame/format}}. + 2. Assign {{VideoFramePlaneInit/format}} to {{VideoFrame/format}}. 3. Assign a new [=list=] to {{VideoFrame/planes}}. - 4. For each |planeInit| in |planesInit|: + 4. For each |planeInit| in |planes|: 1. Copy |planeInit|.{{PlaneInit/src}} to |resource|. - NOTE: The user agent may use {{VideoFramePlanesInit/cropLeft}} - and {{VideoFramePlanesInit/cropTop}} to copy only the crop + NOTE: The user agent may use {{VideoFramePlaneInit/cropLeft}} + and {{VideoFramePlaneInit/cropTop}} to copy only the crop region. It may also reposition the crop region within |resource|. The final position will be reflected by {{VideoFrame/cropLeft}} and {{VideoFrame/cropTop}}. @@ -2188,20 +2189,20 @@ and |resourceCropTop| to {{VideoFrame/codedWidth}}, {{VideoFrame/codedHeight}}, {{VideoFrame/cropLeft}}, and {{VideoFrame/cropTop}} respectively. - 10. If |init|.{{VideoFramePlanesInit/cropWidth}} [=map/exists=], assign it + 10. If |init|.{{VideoFramePlaneInit/cropWidth}} [=map/exists=], assign it to {{VideoFrame/cropWidth}}. Otherwise, assign {{VideoFrame/codedWidth}} to {{VideoFrame/cropWidth}}. - 11. If |init|.{{VideoFramePlanesInit/cropHeight}} [=map/exists=], assign it + 11. If |init|.{{VideoFramePlaneInit/cropHeight}} [=map/exists=], assign it to {{VideoFrame/cropHeight}}. Otehrwise, assign {{VideoFrame/codedHeight}} to {{VideoFrame/cropHeight}}. - 12. If |init|.{{VideoFramePlanesInit/displayWidth}} [=map/exists=], assign + 12. If |init|.{{VideoFramePlaneInit/displayWidth}} [=map/exists=], assign it to {{VideoFrame/displayWidth}}. Otherwise, assign {{VideoFrame/cropWidth}} to {{VideoFrame/displayWidth}}. - 13. If |init|.{{VideoFramePlanesInit/displayHeight}} [=map/exists=], assign + 13. If |init|.{{VideoFramePlaneInit/displayHeight}} [=map/exists=], assign it to {{VideoFrame/displayHeight}}. Otherwise, assign {{VideoFrame/cropHeight}} to {{VideoFrame/displayHeight}}. - 14. Assign |init|'s {{VideoFramePlanesInit/timestamp}} and - {{VideoFramePlanesInit/duration}} to {{VideoFrame/timestamp}} and + 14. Assign |init|'s {{VideoFramePlaneInit/timestamp}} and + {{VideoFramePlaneInit/duration}} to {{VideoFrame/timestamp}} and {{VideoFrame/duration}} respectively. 6. Return |frame|. @@ -2308,20 +2309,20 @@ 1. Resolve p with imageBitmap. ### Algorithms ###{#videoframe-algorithms} -: To check if a {{VideoFramePlanesInit}} is a - <dfn>valid VideoFramePlanesInit</dfn>, run these steps: -:: 1. If {{VideoFramePlanesInit/codedWidth}} = 0 or - {{VideoFramePlanesInit/codedHeight}} = 0,return `false`. - 2. If {{VideoFramePlanesInit/cropWidth}} = 0 or - {{VideoFramePlanesInit/cropHeight}} = 0, return `false`. - 3. If {{VideoFramePlanesInit/cropTop}} + - {{VideoFramePlanesInit/cropHeight}} >= - {{VideoFramePlanesInit/codedHeight}}, return `false`. - 4. If {{VideoFramePlanesInit/cropLeft}} + - {{VideoFramePlanesInit/cropWidth}} >= - {{VideoFramePlanesInit/codedWidth}}, return `false`. - 5. If {{VideoFramePlanesInit/displayWidth}} = 0 or - {{VideoFramePlanesInit/displayHeight}} = 0, return `false`. +: To check if a {{VideoFramePlaneInit}} is a + <dfn>valid VideoFramePlaneInit</dfn>, run these steps: +:: 1. If {{VideoFramePlaneInit/codedWidth}} = 0 or + {{VideoFramePlaneInit/codedHeight}} = 0,return `false`. + 2. If {{VideoFramePlaneInit/cropWidth}} = 0 or + {{VideoFramePlaneInit/cropHeight}} = 0, return `false`. + 3. If {{VideoFramePlaneInit/cropTop}} + + {{VideoFramePlaneInit/cropHeight}} >= + {{VideoFramePlaneInit/codedHeight}}, return `false`. + 4. If {{VideoFramePlaneInit/cropLeft}} + + {{VideoFramePlaneInit/cropWidth}} >= + {{VideoFramePlaneInit/codedWidth}}, return `false`. + 5. If {{VideoFramePlaneInit/displayWidth}} = 0 or + {{VideoFramePlaneInit/displayHeight}} = 0, return `false`. 6. Return `true`. : <dfn>Clone VideoFrame</dfn> (with |frame|) From 105250f9db0c9b2ee2efc58154dbe8759b089440 Mon Sep 17 00:00:00 2001 From: Chris Cunningham <chcunningham@chromium.org> Date: Mon, 19 Apr 2021 15:52:32 -0700 Subject: [PATCH 5/7] Construct VideoFrame from CanvasImageSource (including VideoFrame) Replaces the ImageBitmap constructor with more generic constructor for CanvasImageSource. VideoFrame is itself a CanvasImageSource. Fixes #158 --- index.src.html | 201 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 65 deletions(-) diff --git a/index.src.html b/index.src.html index 8e1df78e..6099e101 100644 --- a/index.src.html +++ b/index.src.html @@ -39,7 +39,16 @@ type: attribute; text: resizeWidth; url:#dom-imagebitmapoptions-resizewidth type: attribute; text: resizeHeight; url:#dom-imagebitmapoptions-resizeheight type: dfn; text: cropped to the source rectangle with formatting; url: imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting - type: dfn; text: global object; url: webappapis.html#global-object + type: dfn; text: bitmap data; url: imagebitmap-and-animations.html#concept-imagebitmap-bitmap-data + for: Canvas; + type: dfn; text: Check the usability of the image argument; url: canvas.html#check-the-usability-of-the-image-argument + for: origin; + type: dfn; text: origin; url: origin.html#concept-origin + for: webappapis; + type: dfn; text: global object; url: webappapis.html#global-object + type: dfn; text: entry settings object; url: webappapis.html#entry-settings-object + for: media; + type: dfn; text: current playback position; url: media.html#current-playback-position spec: mediacapture-streams; urlPrefix: https://www.w3.org/TR/mediacapture-streams/ for: mediaDevices; @@ -74,6 +83,11 @@ type: dfn; text: acquire the content; url: #acquire-the-content for: AudioBuffer type: method; text: copyToChannel(); url: #dom-audiobuffer-copytochannel + +spec: css-images-3; urlPrefix: https://www.w3.org/TR/css-images-3/ + type: dfn; text: natural dimensions; url: #natural-dimensions + type: dfn; text: natural width; url: #natural-width + type: dfn; text: natural height; url: #natural-height </pre> <pre class='biblio'> @@ -139,14 +153,16 @@ The <dfn>control thread</dfn> 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 [=global object=] has a separate control thread. +[=codec thread=]. Each [=webappapis/global object=] has a separate control +thread. The <dfn>codec thread</dfn> 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 [=control thread=] uses a traditional event loop, as described in +[[!HTML]]. The [=codec thread=] uses a specialized [=codec processing loop=]. @@ -1017,7 +1033,7 @@ 2. Set {{VideoEncoder/[[active encoder config]]}} to `config`. </dd> - <dt><dfn method for=VideoEncoder>encode(frame, options)</dfn></dt> + <dt><dfn method for=VideoEncoder>encode(|frame|, |options|)</dfn></dt> <dd> [=Enqueues a control message=] to encode the given |frame|. @@ -2021,11 +2037,15 @@ VideoFrame Interface {#videoframe-interface} -------------------------------------------- +NOTE: {{VideoFrame}} is a {{CanvasImageSource}}. A {{VideoFrame}} may be + passed to any method accepting a {{CanvasImageSource}}, including + {{CanvasDrawImage}}'s {{CanvasDrawImage/drawImage()}}. + <pre class='idl'> <xmp> [Exposed=(Window,DedicatedWorker)] interface VideoFrame { - constructor(ImageBitmap imageBitmap, optional VideoFrameInit init = {}); + constructor(CanvasImageSource image, optional VideoFrameInit init = {}); constructor(sequence<(Plane or PlaneInit)> planes, VideoFramePlaneInit init); @@ -2044,10 +2064,6 @@ VideoFrame clone(); undefined close(); - - Promise<ImageBitmap> createImageBitmap( - optional ImageBitmapOptions options = {}); - }; dictionary VideoFrameInit { @@ -2081,44 +2097,61 @@ ### Constructors ###{#videoframe-constructors} -<dfn constructor for=VideoFrame title="VideoFrame(imageBitmap, init)"> - VideoFrame(imageBitmap, init) +<dfn constructor for=VideoFrame title="VideoFrame(image, init)"> + VideoFrame(image, init) </dfn> -1. If the value of |imageBitmap|'s' {{PlatformObject/[[Detached]]}} internal - slot is `true`, then throw an {{InvalidStateError}} +1. [=Canvas/Check the usability of the image argument=]. If this throws an + exception or returns <var ignore=''>bad</var>, then throw an {{InvalidStateError}} {{DOMException}}. +2. If the [=origin/origin=] of |image|'s image data is not [=same origin=] + with the [=webappapis/entry settings object=]'s + [=origin/origin=], then throw a {{SecurityError}} {{DOMException}}. -2. Let |resource| be the [=frame resource=] containing the bitmap data for - |imageBitmap|. -3. Let |resourceReference| be a reference to |resource|. -4. Let |frame| be a new {{VideoFrame}}, initialized as follows: - 1. Assign |resourceReference| to - {{VideoFrame/[[resource reference]]}}. - 2. If |resource| uses a recognized {{PixelFormat}}: - 1. Assign the {{PixelFormat}} of |resource| to {{VideoFrame/format}}. - 2. Let |planes| be a list of {{Plane}}s describing the - [=frame resource=] in accordance with the {{VideoFrame/format}}. +3. Let |frame| be a new {{VideoFrame}}. +5. Switch on |image|: + - {{HTMLImageElement}} + - {{SVGImageElement}} + 1. If {{VideoFramePlaneInit/timestamp}} does not [=map/exist=] in + |init|, throw a {{TypeError}}. + 2. If image's media data has no [=natural dimensions=] + (e.g., it's a vector graphic with no specified content size), then + throw an {{InvalidStateError}} {{DOMException}}. + 3. Let |resource| be a new [=frame resource=] containing a copy of + |image|'s media data. If this is an animated image, |image|'s + [=bitmap data=] must only be taken from the default image of the + animation (the one that the format defines is to be used when + animation is not supported or is disabled), or, if there is no + such image, the first frame of the animation. + 4. Let |width| and |height| be the [=natural width=] and + [=natural height=] of |image|. + 5. Run the [=VideoFrame/Initialize Frame With Resource and Size=] + algorithm with |init|, |frame|, |resource|, |width|, and |height| + + - {{HTMLVideoElement}} + 1. If |image|'s {{HTMLMediaElement/networkState}} attribute is + {{HTMLMediaElement/NETWORK_EMPTY}}, then throw an + {{InvalidStateError}} {{DOMException}}. + 2. Let |currentPlaybackFrame| be the {{VideoFrame}} at the [=current + playback position=]. + 3. Run the [=VideoFrame/Initialize Frame From Other Frame=] algoirhtm + with |init|, |frame|, and |currentPlaybackFrame|. + + - {{HTMLCanvasElement}} + - {{ImageBitmap}} + - {{OffscreenCanvas}} + 1. If {{VideoFramePlaneInit/timestamp}} does not [=map/exist=] in + |init|, throw a {{TypeError}}. + 2. Let |resource| be a new [=frame resource=] containing a copy of + |image|'s [=bitmap data=]. + 3. Let |width| be `image.width` and |height| be `image.height`. + 4. Run the [=VideoFrame/Initialize Frame With Resource and Size=] + algorithm with |init|, |frame|, |resource|, |width|, and |height|. + + - {{VideoFrame}} + 1. Run the [=VideoFrame/Initialize Frame From Other Frame=] algorithm + with |init|, |frame|, and |image|. - ISSUE: The spec should define explicit rules for each - {{PixelFormat}} and reference them in the step above. See - [#165](https://github.com/w3c/webcodecs/issues/165). +6. Return |frame|. - 3. Assign |planes| to {{VideoFrame/planes}}. - 3. Otherwise (|resource| does not use a recognized {{PixelFormat}}): - 1. Assign `""` to {{VideoFrame/format}}. - 2. Assign `null` to {{VideoFrame/planes}}. - 4. Assign |imageBitmap|.{{ImageBitmap/width}} to {{VideoFrame/codedWidth}}, - {{VideoFrame/cropWidth}}, and {{VideoFrame/displayWidth}}. - 5. Assign |imageBitmap|.{{ImageBitmap/height}} to - {{VideoFrame/codedHeight}}, {{VideoFrame/cropHeight}}, and - {{VideoFrame/displayHeight}}. - 6. Assign `0` to {{VideoFrame/cropTop}} and {{VideoFrame/cropLeft}}. - 7. If {{VideoFrameInit/timestamp}} [=map/exists=] in |init|, assign - `init.timestamp` to {{VideoFrame/timestamp}}. Otherwise, assign - `null` to {{VideoFrame/timestamp}}. - 8. If {{VideoFrameInit/duration}} [=map/exists=] in |init|, assign - `init.duration` to {{VideoFrame/duration}}. Otherwise, assign - `null` to {{VideoFrame/duration}}. -5. Return |frame|. <dfn constructor for=VideoFrame title="VideoFrame(planes, init)"> VideoFrame(planes, init) @@ -2286,28 +2319,6 @@ {{VideoFrame/displayWidth}}, and {{VideoFrame/displayHeight}}. 6. Assign `null` to {{VideoFrame/duration}} and {{VideoFrame/timestamp}}. -: <dfn method for=VideoFrame>createImageBitmap(options)</dfn> -:: Creates an ImageBitmap from this {{VideoFrame}}. - - When invoked, run these steps: - 1. Let |p| be a new Promise. - 2. If either |options|'s {{ImageBitmapOptions/resizeWidth}} or - {{ImageBitmap/resizeHeight}} is present and is 0, then return |p| - rejected with an {{InvalidStateError}} {{DOMException}}. - 3. If the <a>this'</a> {{VideoFrame/[[detached]]}} internal slot is set to - `true`, then return |p| rejected with an {{InvalidStateError}} - {{DOMException}}. - 4. Let |imageBitmap| be a new {{ImageBitmap}} object. - 5. Set |imageBitmap|'s bitmap data to a copy of the {{VideoFrame}} pixel - data, at the frame's intrinsic width and intrinsic height (`i.e`., - after any aspect-ratio correction has been applied), - [=ImageBitmap/cropped to the source rectangle with formatting=]. - 6. If the origin of |imageBitmap|'s image is not same origin with entry - settings object's origin, then set the origin-clean flag of - |imageBitmap|'s bitmap to `false`. - 7. Run this step in parallel: - 1. Resolve p with imageBitmap. - ### Algorithms ###{#videoframe-algorithms} : To check if a {{VideoFramePlaneInit}} is a <dfn>valid VideoFramePlaneInit</dfn>, run these steps: @@ -2325,6 +2336,66 @@ {{VideoFramePlaneInit/displayHeight}} = 0, return `false`. 6. Return `true`. +: <dfn for=VideoFrame>Initialize Frame From Other Frame</dfn> (with |init|, + |frame|, and |otherFrame|) +:: 1. Let |resource| be the [=frame resource=] referenced by |otherFrame|'s + {{VideoFrame/[[resource reference]]}}. + 2. Assign a new reference for |resource| to |frame|'s + {{VideoFrame/[[resource reference]]}}. + 3. Assign the following attributes from |otherFrame| to |frame|: + {{VideoFrame/format}}, {{VideoFrame/codedWidth}}, + {{VideoFrame/codedHeight}}, {{VideoFrame/cropLeft}}, + {{VideoFrame/cropTop}}, {{VideoFrame/cropWidth}}, + {{VideoFrame/cropHeight}}, {{VideoFrame/displayWidth}}, + {{VideoFrame/displayHeight}}. + 4. Let |planes| be a new [=list=]. + 5. For each |otherPlane| in |otherFrame|.{{VideoFrame/planes}}: + 1. Let |plane| be a new {{Plane}}. + 2. Assign a reference for |frame| to |plane|'s + {{Plane/[[parent frame]]}}. + 3. Assign the following attributes from |otherPlane| to |plane|: + {{Plane/stride}}, {{Plane/rows}}, {{Plane/length}}. + 4. Append |plane| to |planes|. + 6. Assign |planes| to |frame|.{{VideoFrame/planes}}. + 7. If {{VideoFrameInit/duration}} [=map/exists=] in |init|, assign it to + |frame|.{{VideoFrame/duration}}. Otherwise, assign + |otherFrame|.{{VideoFrame/duration}} to + |frame|.{{VideoFrame/duration}}. + 8. If {{VideoFrameInit/timestamp}} [=map/exists=] in |init|, assign it to + |frame|.{{VideoFrame/timestamp}}. Otherwise, assign + |otherFrame|.{{VideoFrame/timestamp}} to + |frame|.{{VideoFrame/timestamp}}. + +: <dfn for=VideoFrame>Initialize Frame With Resource and Size</dfn> (with + |init|, |frame|, |resource|, |width| and |height|) +:: 1. Assign a new reference for |resource| to |frame|'s + {{VideoFrame/[[resource reference]]}}. + 2. If |resource| uses a recognized {{PixelFormat}}: + 1. Assign the {{PixelFormat}} of |resource| to {{VideoFrame/format}}. + 2. Let |planes| be a list of {{Plane}}s describing the + [=frame resource=] in accordance with the {{VideoFrame/format}}. + + ISSUE: The spec should define explicit rules for each + {{PixelFormat}} and reference them in the step above. See + [#165](https://github.com/w3c/webcodecs/issues/165). + + 3. Assign |planes| to {{VideoFrame/planes}}. + 3. Otherwise (|resource| does not use a recognized {{PixelFormat}}): + 1. Assign `""` to {{VideoFrame/format}}. + 2. Assign `null` to {{VideoFrame/planes}}. + 4. Assign |width| to the following attributes of |frame|: + {{VideoFrame/codedWidth}}, {{VideoFrame/cropWidth}}, + {{VideoFrame/displayWidth}}. + 5. Assign |height| to the following attributes of |frame|: + {{VideoFrame/codedHeight}}, {{VideoFrame/cropHeight}}, + {{VideoFrame/displayHeight}}. + 6. Assign `0` to frame's {{VideoFrame/cropTop}} and + {{VideoFrame/cropLeft}}. + 7. Assign `init`.{{VideoFrameInit/duration}} to + |frame|.{{VideoFrame/duration}}. + 8. Assign `init`.{{VideoFrameInit/timestamp}} to + |frame|.{{VideoFrame/timestamp}}. + : <dfn>Clone VideoFrame</dfn> (with |frame|) :: 1. Let |clone| be a new {{VideoFrame}} initialized as follows: 1. Assign |frame|.{{VideoFrame/[[resource reference]]}} to From 87fb9b422ccd4de4e5d1a7ded45f5e26c489b361 Mon Sep 17 00:00:00 2001 From: Chris Cunningham <chcunningham@chromium.org> Date: Thu, 29 Apr 2021 22:50:30 -0700 Subject: [PATCH 6/7] Fix nits --- index.src.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/index.src.html b/index.src.html index cccd0a9b..aa8e437e 100644 --- a/index.src.html +++ b/index.src.html @@ -2004,7 +2004,7 @@ ### Methods ###{#audiodata-methods} : <dfn method for=AudioData> - copyFromChannel(destination, channelNumber) + copyFromChannel(|destination|, |channelNumber|) </dfn> :: Copies the samples from the specified channel of the {{AudioData}} to the destination buffer. @@ -2019,7 +2019,7 @@ {{TypeError}}. 4. Let |resource| be the [=media resource=] referenced by {{AudioData/[[resource reference]]}}. - 5. Copy the bytes of |resource| into <var ignore=''>destination</var>. + 5. Copy the bytes of |resource| into |destination|</var>. : <dfn method for=AudioData>clone()</dfn> :: Creates a new AudioData with a reference to the same [=media resource=]. @@ -2450,9 +2450,8 @@ 2. If either |options|'s {{ImageBitmapOptions/resizeWidth}} or {{ImageBitmap/resizeHeight}} is present and is 0, then return |p| rejected with an {{InvalidStateError}} {{DOMException}}. - 3. If the <a>this'</a> {{VideoFrame/[[detached]]}} internal slot is set to - `true`, then return |p| rejected with an {{InvalidStateError}} - {{DOMException}}. + 3. If the <a>this'</a> {{VideoFrame/[[detached]]}} is `true`, then return + |p| rejected with an {{InvalidStateError}} {{DOMException}}. 4. Let |imageBitmap| be a new {{ImageBitmap}} object. 5. Set |imageBitmap|'s bitmap data to a copy of the {{VideoFrame}} pixel data, at the frame's intrinsic width and intrinsic height (`i.e`., @@ -2545,7 +2544,7 @@ </dl> ### Methods ###{#plane-methods} -<dfn method for=Plane>readInto(dst)</dfn> +<dfn method for=Plane>readInto(|dst|)</dfn> Copies the plane data into dst. @@ -2558,7 +2557,7 @@ {{Plane/[[parent frame]]}}'s {{VideoFrame/[[resource reference]]}}. 4. Let |plane bytes| be the region of bytes in [=media resource=] coresponding to this plane. -5. Copy the |plane bytes| into <var ignore>dst</var>. +5. Copy the |plane bytes| into |dst|. Pixel Format{#pixel-format} From 36022512d0e1b3a48861c12ba7992de0ec30dbef Mon Sep 17 00:00:00 2001 From: Chris Cunningham <chcunningham@chromium.org> Date: Mon, 3 May 2021 21:41:34 -0700 Subject: [PATCH 7/7] fix typos, add note: about canvasImageSource 'copy' --- index.src.html | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/index.src.html b/index.src.html index 8e9e25fd..751f7814 100644 --- a/index.src.html +++ b/index.src.html @@ -2264,12 +2264,12 @@ 5. Switch on |image|: - {{HTMLImageElement}} - {{SVGImageElement}} - 1. If {{VideoFramePlaneInit/timestamp}} does not [=map/exist=] in + 1. If {{VideoFrameInit/timestamp}} does not [=map/exist=] in |init|, throw a {{TypeError}}. - 2. If image's media data has no [=natural dimensions=] + 2. If |image|'s media data has no [=natural dimensions=] (e.g., it's a vector graphic with no specified content size), then throw an {{InvalidStateError}} {{DOMException}}. - 3. Let |resource| be a new [=frame resource=] containing a copy of + 3. Let |resource| be a new [=media resource=] containing a copy of |image|'s media data. If this is an animated image, |image|'s [=bitmap data=] must only be taken from the default image of the animation (the one that the format defines is to be used when @@ -2286,16 +2286,20 @@ {{InvalidStateError}} {{DOMException}}. 2. Let |currentPlaybackFrame| be the {{VideoFrame}} at the [=current playback position=]. - 3. Run the [=VideoFrame/Initialize Frame From Other Frame=] algoirhtm + 3. Run the [=VideoFrame/Initialize Frame From Other Frame=] algorithm with |init|, |frame|, and |currentPlaybackFrame|. - {{HTMLCanvasElement}} - {{ImageBitmap}} - {{OffscreenCanvas}} - 1. If {{VideoFramePlaneInit/timestamp}} does not [=map/exist=] in + 1. If {{VideoFrameInit/timestamp}} does not [=map/exist=] in |init|, throw a {{TypeError}}. - 2. Let |resource| be a new [=frame resource=] containing a copy of + 2. Let |resource| be a new [=media resource=] containing a copy of |image|'s [=bitmap data=]. + + NOTE: Implementers are should avoid a deep copy by using reference + coutning where feasible. + 3. Let |width| be `image.width` and |height| be `image.height`. 4. Run the [=VideoFrame/Initialize Frame With Resource and Size=] algorithm with |init|, |frame|, |resource|, |width|, and |height|. @@ -2522,7 +2526,7 @@ : <dfn for=VideoFrame>Initialize Frame From Other Frame</dfn> (with |init|, |frame|, and |otherFrame|) -:: 1. Let |resource| be the [=frame resource=] referenced by |otherFrame|'s +:: 1. Let |resource| be the [=media resource=] referenced by |otherFrame|'s {{VideoFrame/[[resource reference]]}}. 2. Assign a new reference for |resource| to |frame|'s {{VideoFrame/[[resource reference]]}}. @@ -2557,7 +2561,7 @@ 2. If |resource| uses a recognized {{PixelFormat}}: 1. Assign the {{PixelFormat}} of |resource| to {{VideoFrame/format}}. 2. Let |planes| be a list of {{Plane}}s describing the - [=frame resource=] in accordance with the {{VideoFrame/format}}. + [=media resource=] in accordance with the {{VideoFrame/format}}. ISSUE: The spec should define explicit rules for each {{PixelFormat}} and reference them in the step above. See