1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! WebCodecs adapter for the [`mediadecode`] abstraction layer.
//!
//! Implements [`mediadecode::adapter::VideoAdapter`] and
//! [`mediadecode::adapter::AudioAdapter`] for the [`WebCodecs`]
//! zero-sized type, plus the matching push-style decoder traits
//! from
//! [`mediadecode::future::local`](mediadecode::future::local),
//! on top of the browser's
//! [WebCodecs API](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API)
//! via [`web-sys`](https://crates.io/crates/web-sys).
//!
//! This crate is **`wasm32`-only**. On non-`wasm32` targets it
//! compiles to an empty stub so workspace builds and `cargo check`
//! still succeed — every public item is gated behind
//! `#[cfg(target_arch = "wasm32")]`.
//!
//! See `docs/superpowers/specs/2026-05-09-webcodecs-design.md`
//! for the full design.
//!
//! # Async-only, `!Send`
//!
//! WebCodecs is fundamentally async: `VideoDecoder.decode`
//! returns immediately and decoded frames arrive on the `output`
//! callback registered at construction. Every value held during
//! a decode is a `JsValue` (or `Closure` / `Promise`), which is
//! `!Send` by design — the browser's event loop is
//! single-threaded anyway. This adapter therefore implements
//! only the [`mediadecode::future::local`] trait variants (no
//! `Send` bound, native `async fn`); there is no sync
//! implementation, and no
//! [`mediadecode::future::send`] implementation.
//!
//! Internally the decoder still runs a frame queue: the `output`
//! callback `spawn_local`s a copy task that pulls bytes out of
//! the JS-side `VideoFrame` (only available via async `copyTo`),
//! pushes the result onto the queue, and wakes the receive
//! future. `receive_frame` is a `poll_fn` that drains the queue,
//! answers [`Received::Ended`](mediadecode::Received) once
//! `send_eof` has resolved and the drain is empty, or registers a
//! waker and yields.
//!
//! # Subtitles
//!
//! WebCodecs has no subtitle surface. This crate intentionally
//! does **not** implement
//! [`SubtitleAdapter`](mediadecode::adapter::SubtitleAdapter).
//!
//! # Still images: a door, not a gap
//!
//! `mediadecode` 0.9 added a fourth household —
//! [`ImageFrame`](mediadecode::frame::ImageFrame) and the one-shot
//! [`ImageDecoder`](mediadecode::decoder::ImageDecoder) seam — for
//! cover art and other stills. This crate does **not** implement it
//! yet, and the absence is a schedule rather than a judgement: the
//! browser's answer to "decode these bytes into a picture" is
//! [`createImageBitmap`](https://developer.mozilla.org/en-US/docs/Web/API/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`; the bytes come back through a canvas
//! or through `VideoFrame::new(ImageBitmap)`). That is separate work
//! with separate failure modes, and half of it would have shipped an
//! [`ImageAdapter`](mediadecode::adapter::ImageAdapter) whose seats
//! nothing could fill.
//!
//! The seam this crate will implement when that work happens is
//! [`mediadecode::future::local::ImageDecoder`], the `async` mirror —
//! `createImageBitmap` returns a `Promise`, so the sync face was never
//! the one on offer here.
//!
//! # The carrier
//!
//! [`WebCodecsBuffer`] satisfies `mediadecode` 0.9's
//! [D-seat amputation contract][law], and always did: it is an
//! `Arc<Vec<u8>>` view over bytes `copyTo` has already handed to Rust,
//! so nothing in a delivered frame reaches 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.
//!
//! [law]: mediadecode::adapter#the-d-seat-amputation-contract
// `web-sys` gates the WebCodecs APIs (`VideoDecoder`,
// `AudioDecoder`, `VideoFrame`, `AudioData`, …) behind
// `--cfg web_sys_unstable_apis` because the WebIDL is not yet
// stable across all browsers. Without it, every WebCodecs type
// disappears and this crate fails with a confusing wall of
// "cannot find type X in `web_sys`" errors. Surface a clear
// compile-time message instead.
compile_error!;
pub use WebCodecs;
pub use WebCodecsAudioStreamDecoder;
pub use ;
pub use WebCodecsBuffer;
pub use ;
pub use ;
pub use ;
pub use SampleFormat;
pub use WebCodecsVideoStreamDecoder;