VideoFrame.copyTo is manipulating JS objects in a background queue · Issue #955 · w3c/webcodecs · GitHub
Skip to content

VideoFrame.copyTo is manipulating JS objects in a background queue #955

Open

Description

@youennf

As per https://w3c.github.io/webcodecs/#dom-videoframe-copyto, destination which is a JS visible object is written from a background queue task, which is an anti-pattern.
This creates problems (JS observability, destination mutating during the copyTo tasks).

There are various ways to fix this, for instance:

  1. make copyTo algorithm synchronous
  2. Expose a new asynchronous API that would transfer the destination buffer and provide the returned value as part of the promise value.
  3. Extend copyTo API surface to implement 2 within it.
  4. Change the spec to write destination asynchronously but in an event loop task with additional checks, even if UA may do it in a smarter way.

As a side note, copyTo algorithm is also synchronously converting to RGB.
It would be convenient to do this asynchronously, which is feasible by cloning the VideoFrame synchronously and using the cloned VideoFrame in the remainder of the algorithm.

Activity

  1. dalecurtis commented on Oct 1, 2026

    @dalecurtis
    Contributor

    Are any of those actually problems? On the Chrome side we did change to using atomic writes for the copy, so we're not exposing undefined behavior. All of the solutions proposed would hurt WASM interop.

  2. Djuffin commented on Oct 1, 2026

    @Djuffin
    Contributor

    From js observability point of view, It's no different from another worker writing to the same shared buffer. copyTo to non-shared buffers shouldn't have this effect (i.e. it should be synchronous).

  3. youennf commented on Oct 2, 2026

    @youennf
    ContributorAuthor

    Are any of those actually problems?

    A naive implementation of the spec would lead to obvious memory issues.
    Also, the current spec could quickly lead to interop issues (web page relying on copyTo being sync and UAs having different implementations).

    All of the solutions proposed would hurt WASM interop.

    How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?

    copyTo to non-shared buffers shouldn't have this effect (i.e. it should be synchronous).

    Then, the spec should describe this algorithm.
    FWIW, it is surprising to me that a web page gets widely different (and JS observable) behaviour depending on the input buffer flavour.

  4. dalecurtis commented on Oct 5, 2026

    @dalecurtis
    Contributor

    A naive implementation of the spec would lead to obvious memory issues.

    Can you elaborate on the obvious issues? Do you just mean a client who reads w/o waiting for the promise may get partial/corrupted data?

    How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?

    This would result in two copies IIUC?

    Then, the spec should describe this algorithm.

    +1, I didn't realize the spec text didn't already have this clarification. It should be clarified to MUST be synchronous for non-shared type buffers and MAY be asynchronous for SAB.

  5. youennf commented on Oct 6, 2026

    @youennf
    ContributorAuthor

    Can you elaborate on the obvious issues?

    For instance, UA is writing on a buffer in a background queue while web page is resizing it/transferring it on another.

    How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?

    This would result in two copies IIUC?

    At least in WebKit, I am not sure how we can avoid two copies for GPU-backed video frames.
    As part of the copyTo current approach, we do something like:

    • Copy from GPU backed video frames to memory that is shareable with the content process
    • Copy from shareable memory to the JS memory provided by the web page.
      prefetch would trigger the first copy, copyTo the second.
      Can Chromium avoid these two copies?

    Then, the spec should describe this algorithm.

    +1, I didn't realize the spec text didn't already have this clarification. It should be clarified to MUST be synchronous for non-shared type buffers and MAY be asynchronous for SAB.

    If we do this, I would make the algorithm first check whether the buffer is SAB or not.
    Background queue would only be used for SAB.

    I am also not exactly sure how we are supposed to specify the SAB algorithm.
    I would tend to synchronously do a structure clone of the SAB and use the clone in the background queue.
    But maybe we can hook to some SAB internals instead.

  6. dalecurtis commented on Oct 6, 2026

    @dalecurtis
    Contributor

    For instance, UA is writing on a buffer in a background queue while web page is resizing it/transferring it on another.

    Ah you mean for the non-SAB cases? Yes, those do have obvious issues. SAB can't be shrunk and can't be transferred.

    At least in WebKit, I am not sure how we can avoid two copies for GPU-backed video frames. As part of the copyTo current approach, we do something like:

    • Copy from GPU backed video frames to memory that is shareable with the content process
    • Copy from shareable memory to the JS memory provided by the web page.
      prefetch would trigger the first copy, copyTo the second.
      Can Chromium avoid these two copies?

    Does WebKit not pass the IOSurface to the content process? We don't always in Chromium for legacy, non-security reasons. In Chromium some frames are mappable so have 1-copy perf and some require read-back which has 2-copy perf as you note.

    If we do this, I would make the algorithm first check whether the buffer is SAB or not. Background queue would only be used for SAB.

    I am also not exactly sure how we are supposed to specify the SAB algorithm. I would tend to synchronously do a structure clone of the SAB and use the clone in the background queue. But maybe we can hook to some SAB internals instead.

    This seems reasonable; I haven't looked at how other specs handle SAB copies though. In Chromium we just ref the underlying buffer.

  7. youennf commented on Oct 6, 2026

    @youennf
    ContributorAuthor

    In Chromium some frames are mappable so have 1-copy perf and some require read-back which has 2-copy perf as you note.

    I see. AIUI, prefetch would not trigger any additional cost to either strategies.
    And it would allow applications to call copyTo with a consistent behaviour (no blocking IPC call for some frames, no SAB async behaviour).

  8. dalecurtis commented on Oct 6, 2026

    @dalecurtis
    Contributor

    Can you explain prefetch more precisely? I feel like I'm missing something:

    1. Anything that comes back to the main thread adds the cost of doing the memcpy on the main thread even if the readback is async.
    2. Assuming some prefetch promise, why not just handle that internally? I.e., there's no reason the internal API has to have a blocking IPC. It can just post a main thread task to finish the copy.
    3. SAB behavior is always "async" no matter what we do since it's world read/write.

    In any case, copyTo is already shipped by Firefox and Chrome, so at most we want to clarify the spec or make behind the scenes adjustments for better interop. Without a strong reason, adding new methods, attributes, etc seems out of scope.

    @Djuffin can you take a stab at a PR which clarifies the algorithms here?

  9. added
    CR BlockingNeeds to resolved for Candidate Recommendation
    PR existsA PR has been submitted that addresses this issue
    on Oct 7, 2026
  10. added a commit that references this issue on Oct 10, 2026
    442dc7d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

CR BlockingNeeds to resolved for Candidate RecommendationPR existsA PR has been submitted that addresses this issueTPAC2026Agenda topic for TPAC 2026

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions