Skip to content

Browser subscribers decode 0 frames: headless Chromium can't decode H.264 (WebCodecs) #24

Description

@kixelated

Summary

The browser subscriber cells (* -> js-vite, * -> js-esbuild, * -> js-jsdelivr) fail the media smoke with decoded 0 frames. This is not a client bug — it's a headless-Chromium codec limitation — plus one latent driver bug that would block the cell from ever passing even once the codec issue is resolved.

Root cause: headless Chromium encodes H.264 but can't decode it

Confirmed by isolating the direction:

  • The browser publishes H.264 fine — js-vite -> go, js-vite -> kotlin, js-vite -> c all pass. So WebCodecs H.264 encode works (Chromium bundles OpenH264 for encode).
  • The browser can't decode — js-vite -> js-vite (browser→browser) also produces decoded 0 frames, and so does python -> js-vite from a known-good publisher.

That asymmetry is the well-known Chromium behavior: WebCodecs H.264 decode needs platform/proprietary decoders that the headless runner lacks, while encode does not. The decode happens entirely inside the published <moq-watch> (@moq/watch) element; the smoke page code (clients/js/jsdelivr/setup.js) is minimal and correct — it just declares <moq-watch url name> with a canvas.

clients/js/driver.ts launches channel: "chromium" (full Chromium), headless: true.

Options to actually make the cell pass (harness decision)

  1. Publish a codec headless Chromium can decode — VP8 / VP9 / AV1 instead of H.264 for the browser-subscriber path. Cleanest if the publishers/importers support it.
  2. Run headed Chromium in CI (e.g. via xvfb), if that gives H.264 decode.
  3. Accept the limitation and exclude browser variants from the subscriber set (keep them as publishers only, which is their reliable role today), documenting why.

Prerequisite bug regardless of the above

clients/js/driver.ts reads the decoded-frame count via w.backend?.video?.stats?.peek()?.frameCount, but @moq/watch 0.4.0 moved the decoder off backend — the element now exposes video: Video.Decoder directly. So the accessor should be:

w?.video?.stats?.peek()?.frameCount ?? 0

Until that's fixed the driver reads 0 even if decode works, so it must land as part of (or before) whatever codec fix is chosen.

Context

Discovered while porting the non-browser clients to the current published APIs (#21, #22, #23 for Go/Kotlin/Swift/Python/native-JS). Those are all fixed and green; the browser-subscriber cell is the remaining red on the media matrix (alongside c-pkgconfig, which is fixed upstream by moq-dev/moq#2502 pending a libmoq release).

(written by Opus 4.8)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions