mediadecode-webcodecs 0.12.0

WebCodecs adapter for the `mediadecode` abstraction layer — implements its `VideoAdapter` / `AudioAdapter` traits on top of the browser's WebCodecs API via `web-sys`, for `wasm32` targets.
# Changelog

All notable changes to the [`mediadecode-webcodecs`](https://crates.io/crates/mediadecode-webcodecs)
crate are documented in this file.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

The backend-agnostic core it adapts has its own log at
[`mediadecode/CHANGELOG.md`](../mediadecode/CHANGELOG.md).

## [Unreleased]

## [0.12.0] - 2026-08-30

### Changed (BREAKING)

- **`mediaframe` 0.7 → 0.9**, tracking the core's own crossing (see
  [`mediadecode`]../mediadecode/CHANGELOG.md#unreleased) — two
  breaking minors in a public dependency whose `PixelFormat`, `color`
  and `audio::ChannelLayoutDescription` types this adapter re-exports
  and names in its own signatures.

  **No adapter source line moved and no behaviour changes.** Upstream
  0.8.0 is additive to the container / audio-container / image roster
  households, which this adapter never names, and 0.9.0 is the `lang`
  family — the retirement of the lossy `lang::Language` triple, its
  `LanguageError` and `audio::Tags`'s language seat, none of which
  appears anywhere in this crate.

  Verified on the real dependency graph (native builds this crate
  empty): `cargo check` and `cargo clippy --all-features -- -D
  warnings` on `--target wasm32-unknown-unknown`, both clean with zero
  source change. The browser `wasm-bindgen-test` lane needing headless
  Chromium is CI-only and was not run locally.

## [0.11.0] - 2026-08-28

Tracks `mediadecode` 0.11.0, which crosses `mediatime` 0.3 → 0.4 and
`mediaframe` 0.6 → 0.7 — two breaking minors in public dependencies
whose `PixelFormat` and `color` types this adapter re-exports. See
[`mediadecode` 0.11.0](../mediadecode/CHANGELOG.md#0110). No adapter
source line moved and no adapter behaviour changes — `mediatime` 0.4 is
additive only, and `mediaframe` 0.7 is itself entirely the `mediatime`
crossing.

Verified on the real dependency graph (native builds this crate
empty): `cargo check` / `cargo clippy -- -D warnings --all-features`
on `--target wasm32-unknown-unknown`, both clean. The browser
`wasm-bindgen-test` lane needing headless Chromium is CI-only and was
not run locally.

## [0.10.0] - 2026-08-27

### Changed

- **BREAKING: `receive_frame` returns `mediadecode::Received` and
  `send_packet` / `send_eof` return `mediadecode::Sent`** on both async
  decoder faces. `VideoDecodeError` and `AudioDecodeError` lose their
  `NoFrameReady`, `Eof` and `OutputFull` arms — three of the four
  conditions they named were never faults.

  This adapter had the family's only complete four-arm vocabulary — and
  that was the problem, not the fix: the FFmpeg backend's video and
  audio decoders named none of the four, so a consumer generic over
  `mediadecode`'s traits observed a different protocol depending on
  which backend it held. The vocabulary now lives one tier up, where
  both backends answer it.

- **`wait_for_frame` no longer takes `eof`**, and answers
  `Ok(None)` for "no frame, and nothing in flight that could produce one
  without the caller acting". Which of the two protocol answers that is
  belongs to the session's own EOF latch, which `receive_frame` owns —
  so there is exactly one place in the adapter that decides between
  "send another packet" and "there will be no more", and it is the place
  that holds the fact.

- **`await_decode_room` answers `Sent`**, and the two kinds of pressure
  part company there. The decoder-side cap drains by itself as the
  browser works, so it is still awaited; the *output* cap drains only
  when the caller calls `receive_frame`, which needs the `&mut self`
  that `send_packet` is holding — so awaiting it would deadlock, and it
  is reported as `Sent::MustDrain` instead. That distinction is why the
  arm survives the move to `async` at all.

- **BREAKING: `AtEof` is renamed `AfterEof`.** It **stays** an error —
  a caller usage fault, not back pressure: draining changes nothing, and
  this decoder refuses every packet until `flush()`, so `MustDrain`
  would be a loop with no exit. What changed is only the word. The
  FFmpeg adapter names the identical condition `AfterEof` on its
  resampler and (as of this release) its subtitle seam, and one
  condition wearing two words across two backends of one trait is the
  disease this release is curing.

- **`VideoDecodeError` and `AudioDecodeError` gain `#[non_exhaustive]`**,
  with the family's other seven. This is what the breaking window buys:
  adding the attribute is itself breaking, so it can only ride a release
  that already is, and afterwards every new fault arm is additive
  forever. The status enums stay exhaustive for the opposite reason —
  closed protocol vocabulary against open fault taxonomy.


## [0.9.0] - 2026-08-26

## [0.9.0] - 2026-08-24

### Changed

- Version bump to track `mediadecode` 0.9. **No adapter source
  changed**, and that is the finding rather than an absence of one:
  0.9's [D-seat amputation contract]../mediadecode/CHANGELOG.md is
  the rule that `WebCodecsBuffer` was already built to — an
  `Arc<Vec<u8>>` view over bytes `copyTo` has already handed to Rust,
  owned, `Send + Sync`, cloning by refcount, with nothing reaching back
  into a JS handle. The FFmpeg adapter had to be rebuilt for that
  release; this one had to be checked, and it passed unchanged.

### Notes

- **Still images: a door, not a gap.** `mediadecode` 0.9 added a
  fourth frame household (`ImageFrame`) and a one-shot `ImageDecoder`
  seam. This crate does not implement them yet, and the absence is a
  schedule rather than a judgement: the browser's answer to "decode
  these bytes into a picture" is `createImageBitmap`, which is not
  part of the WebCodecs surface this crate wraps and carries its own
  async shape and its own pixel-readback problem — an `ImageBitmap`
  has no `copyTo`, so the bytes come back through a canvas or through
  `VideoFrame::new(ImageBitmap)`. Recorded on the crate's module docs
  so the next reader finds the reason where they find the absence.

  The seam this crate will implement when that work happens is
  `mediadecode::future::local::ImageDecoder`, the `async` mirror that
  0.9 also mints — `createImageBitmap` returns a `Promise`, so the
  sync face was never the one on offer here.

## [0.8.0] - 2026-08-24

## [0.7.0] - 2026-08-23

## [0.6.0] - 2026-08-21

### Changed (BREAKING)

- **`AudioAdapter::ChannelLayout` binds
  `mediaframe::audio::ChannelLayoutDescription`.** `mediadecode::channel`
  is gone (see
  [`mediadecode`]../mediadecode/CHANGELOG.md#unreleased) and the
  channel vocabulary lives in `mediaframe::audio` now. The adapter's
  conduct is unchanged: WebCodecs reports a channel count and no layout
  name, so `boundary::empty_audio_frame` and the decode path still
  build `ChannelLayoutDescription::new(N)` — the count with an
  `Unspecified` order — and `default()` is still the zero-channel
  placeholder.

- **`mediaframe` is a direct `wasm32` dependency**, pinned with
  `alloc`: the audio household is compiled only at the alloc-or-std
  tier.

## [0.5.0]

Tracks `mediadecode` 0.5.0, which crosses `mediaframe` 0.4 → 0.5 — a
breaking minor in a public dependency whose `PixelFormat` and `color`
types this adapter re-exports. See
[`mediadecode` 0.5.0](../mediadecode/CHANGELOG.md#050). No adapter
source line moved and no adapter behaviour changes.

## [0.4.0] - 2026-08-19

Tracks `mediadecode` 0.4.0, which crosses `mediatime` 0.1 → 0.3 and
`mediaframe` 0.1 → 0.3 — two breaking minors each. See
[`mediadecode` 0.4.0](../mediadecode/CHANGELOG.md#040). No adapter
behaviour changes.

### Changed (BREAKING)

- **The empty destination frame's pixel format is now
  `PixelFormat::None`.** `boundary::empty_video_frame` filled the
  placeholder with `PixelFormat::Unknown(0)`; mediaframe 0.3 struck
  that variant, and `None` is its named "no format yet" member (and
  the `Default`) — which is what the placeholder always meant. The
  adapter overwrites it on every successful decode, as before.
- **`WebCodecsVideoFrame::format` and `::color` are no longer
  `const`** and return clones. `mediaframe::PixelFormat` and
  `mediaframe::color::Info` lost `Copy` in 0.3. Both signatures are
  otherwise unchanged.
- **`Timebase` construction is signed.** `mediatime` 0.2 made
  `Timebase`'s numerator and denominator `i32` / `NonZeroI32`; the
  crate's two `MICROS` constants and the wasm integration tests move
  from `NonZeroU32` to `NonZeroI32`.

### Changed

- **`mediadecode` dep**: bumped to `0.4`.
- `video::expected_plane_layout` (private) takes `&PixelFormat`.
- README: the install snippet said the crate was unpublished and
  pinned `"0.0"`; it has been on crates.io since 0.3.1. Now `"0.4"`.

[0.4.0]: https://github.com/findit-studio/mediadecode/releases/tag/mediadecode-webcodecs-v0.4.0

## [0.3.1] - 2026-06-14

Version-tracking release
([#10](https://github.com/findit-studio/mediadecode/pull/10)). No adapter
source changed.

### Changed

- **`mediadecode` dep**: bumped to `0.3.1`, which adds `Clone` to
  `AudioFrame` — see
  [`mediadecode` 0.3.1]../mediadecode/CHANGELOG.md#031---2026-06-14.

[0.3.1]: https://github.com/findit-studio/mediadecode/releases/tag/mediadecode-webcodecs-v0.3.1

## [0.3.0] - 2026-06-07

Tracks `mediadecode` 0.3.0, which flips the shared vocabulary crate from
`videoframe` 0.2 to `mediaframe` 0.1
([#7](https://github.com/findit-studio/mediadecode/pull/7),
[#8](https://github.com/findit-studio/mediadecode/pull/8)). No adapter source
changed — but the vocabulary this crate re-exports through its frame
types is a different crate's now, so the release is breaking for its
consumers all the same. See
[`mediadecode` 0.3.0](../mediadecode/CHANGELOG.md#030---2026-06-07).

### Changed (BREAKING)

- **`mediadecode` dep**: bumped to `0.3`.

### Changed

- Version bumped to 0.3.0 with the rest of the workspace.

[0.3.0]: https://github.com/findit-studio/mediadecode/releases/tag/mediadecode-webcodecs-v0.3.0

## [0.2.0] - 2026-05-15

Tracks the `mediadecode` 0.2.0 / `videoframe` 0.2 cutover
([#5](https://github.com/findit-studio/mediadecode/pull/5)).

### Changed

- The `PixelFormat::Unknown` boundary fallback in
  `webcodecs_pixel_format_to_mediadecode` preserves the raw
  WebCodecs identifier via `PixelFormat::Unknown(raw as u32)`
  instead of collapsing to a unit variant.

### Added

- `#[must_use]` added to every `with_*` consuming builder method.
- New `tests/native_stub.rs` — verifies the crate compiles to an
  empty stub on non-wasm32 targets and that no wasm-only names
  leak through. Closes
  [issue #4 — finding 4]https://github.com/findit-studio/mediadecode/issues/4.

[0.2.0]: https://github.com/findit-studio/mediadecode/releases/tag/mediadecode-webcodecs-v0.2.0

## [0.0.0] - 2026-05-15

Placeholder publish — the name is reserved and the shape is scaffolded;
there is no public API yet
([#3](https://github.com/findit-studio/mediadecode/pull/3)).

### Added

- Crate scaffolded: workspace member, `wasm32`-gated `web-sys`
  dependency surface, design spec captured in
  `docs/superpowers/specs/2026-05-09-webcodecs-design.md`.
  Public API lands in a subsequent release.

[0.0.0]: https://github.com/findit-studio/mediadecode/releases/tag/mediadecode-webcodecs-v0.0.0