-
Notifications
You must be signed in to change notification settings - Fork 143
VideoFrame.createImageBitmap returns an ImageBitmap with coded resolution rather than display resolution #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is something to be clarified. Currently for hardware decoded video frames, chrome uses coded size for the created ImageBitmap, while it's crop size for soft video frames. |
Presumably, that's a temporary thing? Having an API behavior vary depending on the implementation of the underlying decoder seems like a bad thing. In the meantime, what's the best way of cropping the ImageBitmap? I'm rendering using WebGL, and texSubImage2D doesn't seem to work (the call doesn't include crop dimensions, but fails with an ImageBitmap anyway). |
The WebCodecs spec should further clarify this. A parameter indicating whether callers want the cropped or coded size may be a good choice. @sandersdan @chcunningham For the texSubImage2D problem, probably you need to use WebGL2 context to crop an ImageBitmap. Alternatively you can simply use texImage2D, and set the texture coordinates cropped. For WebGL, there is another option, WebGL_webcodecs_video_frame and Sample. You can skip the |
Thanks for the info. I'd be strongly in favor of having some way of extracting the cropped frame as an imagebitmap. I would imagine >80% of other API consumers would too. WRT skipping the intermediate ImageBitmap - unfortunately, my use case requires me to buffer a second or more of decoded frames. It seems only a handful of undestroyed video frames are allowed before the decoder will stall, which isn't surprising. Obtaining the ImageBitmap seems the best approach for me. |
We last discussed this in December and at that time the preference was to be as user-friendly as possible without sacrificing substantial performance. I believe that means:
This does mean that an ImageBitmap created in this way can't be used reliably without reference to the VideoFrame's display size, color space, and rotation metadata. I would predict that many developers would assume 1:1 PAR, sRGB, and 0deg, at least one of which will usually be wrong. Any feedback on these points would be appreciated! |
Definitely!
I like this reasoning.
Sounds like everyone is in agreement to at least apply the crop. We fix chrome's behavior wrt gpu-backed frames. Happy to also clarify the spec. For now, I lean toward not making it user-configurable and just doing the intuitive thing. LMK if I'm overlooking a use-case for the alternative. |
Sounds good to me! |
We discussed this again today and there was more support for an easy-to-use API now that we have a separate WebGL path. In summary:
|
Quick comment regarding:
This is desirable most of the time, but I believe it may cause issues for WebGL rendering on mobile devices which have a relatively low limit on the max texture size. Ie. For equirectangular 360 video which is 2:1 DAR, a 4k video's display resolution ends up being 4320 which is beyond the limits of most mobile devices. Would it be possible to consider making this scaling to display size a user configurable option? |
Hmm, I forgot that that was a relatively common thing to do. Is there any reason to suspect that such uses won't prefer using WEBGL_webcodecs_video_frame anyway though? Another alternative is to just lie in the VideoDecoderConfig and claim that the display size is exactly the crop size. That seems like it might be a good enough workaround to not need its own API? |
Ok cool. |
I actually have an intermediate use case... Will there be a way of copying the YUV buffers from video frame without calling createImageBitmap? I think the API spec mentions this, but I didn't notice any mechanism currently implemented. |
The As a workaround, it should be possible to use the WEBGL_webcodecs_video_frame extension to read back the planes as well, the same as for other textures.
FYI it's not actually guaranteed that images created by |
Thanks Dan. I have to confess, though, I'm not sure I fully understand the implications of your comment on My use case requires me to buffer about 60 frames (although the resolution is typically quite low). My current implementation uses a wasm decoder and generates YUV planes as ArrayBuffers. These are buffered, and when/if needed will be rendered via WebGL, using a fragment shader to do colour space conversion. I've successfully got this working with web-codecs. My initial thought was to just buffer the video frames that are output from the decoder. This didn't work though, as the decoder stalls after about 10 undestroyed video frames. I've switched to using Is there any advice you can offer in this case, or is my approach already the best strategy? |
Your approach may or may not work, depending on details of the particular WebCodecs implementation you are using. In Chrome's current implementation, you approach may or may not work depending on the platform; in some cases a zero-copy path may be used and the ImageBitmaps will hold a We could specify that There isn't really a way out of copying; in most cases decoded frames are owned by the decoder and the only way to buffer many of them is to make a copy. WebCodecs does not currently have an explicit copy or readback API. I am investigating and may propose one soon. The priority is relatively low given that there are workarounds available. On the point of color space conversions, I've just noticed that |
So, should I be calling |
@chcunningham: This is not the expected behavior of Cloned frames do have their own lifetimes in the sense that Chrome's implementation of |
@jamespearce2006: The workarounds are using ImageBitmap or WebGL_webcodecs_video_frame to get the data into WebGL, then reading back the image data. |
Would it be out of the question to add an option to |
I'm unsure. If it's on If we implement an explicit copy API we resolve the core problem, but |
Thanks. I can think of a lot of cases where getting access to the underlying buffer and/or holding the data for a period of time will be needed, so I'd like to lobby to increase the priority of the copy/readback API. ImageBitmaps are working for me currently, but I'm slightly concerned that the workaround is quite involved, if that approach doesn't work on other platforms. |
Agree, I mixed this up. I'll send a PR to sort it out (clone isn't necessarily deep copy, but its always a new lifetime guarantee). |
Sorry to resurrect this thread again. I just wanted to make a few additional points after talking with colleagues. Firstly, @sandersdan responded to one of my implementation questions above with "Your approach may or may not work, depending on...". I think it's important that the API is predictable. Either it should behave the same way regardless of the underlying decoder implementation, or it should allow me to query (or control) what the behavior will be. If I have a need to hold onto decoded frames for longer than a hardware decoder might allow, or readback the decoded data, not knowing whether I'll get a hardware or software decoder means I don't know whether my code will work. At the very least, there should be a way of querying this. Even better, I should be able to ask to enable this so that my code is guaranteed to work. (Quite possibly this results in a software decoder being used, but as long as I get consistency, that's fine). Secondly, reading between the lines, it seems that linear playback workflows are being prioritized initially for development of the API. I base this on the fact that the readback API is currently a lower priority. I would argue that linear playback is already well served by existing APIs (MSE, HTMLVideoElement, etc). Personally, I think it'd be a better to prioritize more exotic workflows, that are not currently achievable (or perhaps require WebAssembly). Finally, in the above discussion, the proposed workaround for reading back a decoded frame is to render it, via WebGL, then read back the image data. Unfortunately, this isn't really a viable solution from a web-worker. It's not possible to dynamically create a canvas in a worker - one needs to be passed in from the main thread. |
I agree that the behavior should be predictable. I'm personally on the side that ImageBitmaps shouldn't be able to stall a decoder, but it does have performance implications so it's being considered thoroughly.
The priority right now is to ship a working MVP API. We'll cut features if they are not required to demonstrate the core goals of WebCodecs. Don't worry too much about this specific issue though, ImageBitmap integration is a core feature.
You can create an OffscreenCanvas in a worker. |
@jamespearce2006 earlier wrote
The latest thinking on this is to offer an API like: This is mentioned alongside other API changes for VideoFrame readback in #157. We are working now to get them implemented. I've also created a separate #159 to hash out createImageBitmap semantics. Dan has taken the excellent feedback in this issue and made several proposals. Note that, I think everyone in this thread is actually better suited using the newer drawImage or texImage methods for painting (better perf and memory mgmt). The current plan (in #159) for createImageBitmap() is that it be a friendly (does conversions, rotations, probably makes copies) API for rendering the occasional frame. Please see rationale in that bug. With that, I think we've probably split out all the remaining work in this issue. Please re-open if I've missed anything. Thank you for the feedback! |
It seems the createImageBitmap method is returning an ImageBitmap with slightly incorrect dimensions.
For example, in my case, I'm getting video frames that have the following properties:
I'd expect the resulting ImageBitmap to honor the crop data and be the display resolution - 640x360. In reality, I'm getting an ImageBitmap that is 640x368, and the last 8 lines aren't valid data.
The text was updated successfully, but these errors were encountered: