tokens() is split on the symbol counting flag (const generic), the
count tables are indexed once per call instead of per symbol, the scan,
neighbour and band tables are re-sliced to the block's end of block
limit so the loop needs no bounds checks, and the sign bit uses
BoolDecoder::read_bool_half (read_bool(128) with the split computed as
(range + 1) >> 1, same result).
Output is unchanged (framemd5 equal to ffmpeg).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A block whose (h + 7)-row source window crossed a band boundary went
through fetch_window, which copies the window row by row (a division,
an atomic and a memcpy per row). Every 64-high block and about 60 % of
the 32-high ones cross a boundary. predict_two_bands now filters straight
from the two band slices; filter_v takes an array of eight row slices
(filter_v_rows) so the arithmetic is the same. Windows that need edge
clamping or span three bands still use the window copy.
Output is unchanged (framemd5 equal to ffmpeg).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Resolve the bitstream migration conflicts by retaining the craft-media
re-export and running the NAL regression tests against it. Drop the
obsolete proptest dependency and update the WASI testing documentation.
AI assistance: OpenAI Codex.
Check NAL, VP9 superframe, AV1 tile and OBU lengths before slicing or converting. Add hostile-input regression tests and document running the parser tests on 32-bit WASI.
Targeted native and WASI tests pass. Full workspace CI is currently blocked by pre-existing UI Clippy diagnostics; this checkpoint is committed at the contributor’s explicit request before investigating the baseline separately.
Co-authored-by: Codex <noreply@openai.com>
* security(vp9): bound frame allocation
* vp9: raise the frame-size cap to 2^26 luma samples so level 6.x 8K frames decode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* vp9: keep the frame-size cap below the imports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Problem: the post stage of a VP9 frame (band assembly, loop filter,
publication) ran on one thread. Later frames wait per band for their
references, so the serial loop filter (~30 % of the profile) set the
pace of every inter frame; 16 threads were only ~1.8x one thread.
Fix: rows of a frame's loop filter run as a wavefront, row r two
superblocks behind row r - 1 (what the raster order of 8.8.1 allows),
taken by the post thread and helper jobs on the decoder's pool. No lock
is held while a superblock is filtered: each row filters its own band
and rows exchange copies of the 8 rows the filter reaches across a band
boundary. Output stays bit-identical; one thread / wasm run the rows in
order as before. A row is only taken by a running thread, so waits
always make progress, and helpers run under catch_unwind (a failure
publishes placeholder bands as before). New conformance fixture
narrow_tall (2 superblock columns) and a repeated multi-thread 1080p
decode test. 1.2-1.5x faster with 16 threads on a shared machine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Transforms, 8-tap filters, probability tables: fixed-size views through `first_chunk(_mut)`
instead of slice + `try_into().unwrap()/expect()` (same code on the happy path).
- Tile sizes read with `get()`; a short tile header is `Error::Invalid`.
- Buffer pool tolerates a poisoned lock.
- A failed post-processing thread spawn no longer panics: the job reaches the thread over a
channel, so it is still in hand and runs inline.
- Without the `threads` feature an unpublished reference band reads as empty instead of panicking.
- `#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented,
clippy::todo, clippy::unreachable)]` outside tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The loop filter's lane masks are 0 / -1 integers with bitwise selects and min / max clipping
(bool masks and clamp kept it scalar); the 2D inverse transform's column pass runs the generated
butterflies on 4 / 8 columns at once; 8-bit sub-pixel filters accumulate in 16-bit lanes (every
sum of the VP9 filters fits modulo 2^16). Branch-free bool decoder update. Bit-exact; tested
against the per-column transform on random blocks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frames are published one superblock row at a time once loop filtered; a post stage per frame
(own thread) assembles bands from the tile strips, filters them in raster order and builds the
output picture, while the next frame's tile columns decode and wait per band for the reference
rows they read. Bit-exact with 1, 3 and all threads. Draft mode skips the loop filter of frames
that refresh no reference slot. av1dec / hevcdec bench knobs and VP9 / AV1 4K playback scenarios
in the bench.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1080p 8-bit bench: 55 -> 98 fps single-thread CPU time; 45 -> 115 fps wall with 14 threads
(best of 4 on a heavily loaded machine). Still bit-exact against the ffmpeg oracle on the
whole fixture matrix (1 thread and thread pool). Fixture generation uses per-call unique
temporary names (concurrent tests generating the same fixture raced on rename).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>