StreamDecoder::conceal(n) and decode_fec(.., n, ..) resized the output
by n * channels for any caller-supplied n that was a multiple of 2.5 ms,
so a huge n (for example from a corrupt container timestamp) allocated
without limit. Both now reject n above 120 ms, the longest Opus packet,
with InvalidArgument before any output is reserved. Internal callers
(packet-loss concealment of the last packet's duration, multistream) never
exceed that. A test covers the boundary and oversized values.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The ~35-tap dot product accumulated strictly sequentially, one 4-cycle
float add after another. It now uses eight independent partial sums over
8-wide chunks, which the compiler vectorises. The resampler filter is
non-normative, so the output moves only at f32 rounding level: max abs
diff 1.5e-7 (about 141 dB SNR) against the previous code, and the SNR
against libopus is unchanged to 0.1 dB (50.3 / 51.5 / 51.7 dB on voip
speech streams).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The SILK output resampler computed j / period, j % period and a 64-bit
cycle*fin*period/fout division for every output sample, and bounds-checked
every tap. The phase and cycle are now tracked incrementally (the cycle
step fin*period/fout is exact), and when the tap window lies inside the
history, which is always the case after the first call, the dot product
runs over one contiguous slice. The accumulation order is unchanged, so
the output is byte-identical.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
At 48 kHz output the CELT de-emphasis filter ran one channel at a time
through a scratch Vec and then copied into the interleaved output. Stereo
now runs both IIR chains in one loop and writes straight into the output,
so the two independent dependency chains hide each other's add/multiply
latency; mono at 48 kHz writes directly too. The arithmetic and its order
are unchanged, the output is byte-identical.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
decode_pulses rebuilt an O(n*k) table of saturating u64 adds for every
coded band partition, about a quarter of CELT decode time. The table is
now built once per process (n <= 176, k <= 128, u32 saturated, 91 KB) and
decoding reads only the O(n+k) entries it needs. Every coded (n, k) has
V(n, k) < 2^32 and only entries <= V(n, k) are read, so the integer
arithmetic is unchanged. The unused pvq_v scratch Vec in BandCtx is gone.
Output is byte-identical on all test inputs; a test checks the table
against rate::pvq_v.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The mixed-radix FFT multiplied every butterfly input by every DFT root
(16 complex multiplies for radix 4) and took a modulo per twiddle index.
Radix 2, 3 and 4 now use dedicated butterflies (radix 4 with the +-j
rotations as swaps, radix 3 with one real constant); radix 5, which
occurs at most once per transform, keeps the generic form with its roots
looked up once. The modulo is dropped, the index never reaches n.
The output moves at f32 rounding level (max abs diff 2.7e-7 against the
previous code on 90 s of music and speech). The previous implementation
is kept under cfg(test) and a test compares it with the new one for all
CELT FFT sizes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Tests: one integration-test binary per crate
Each crate with several tests/*.rs files keeps them as modules of tests/it/,
so cargo links one test executable per crate instead of one per file. Files
that change process-wide state or time a workload stay binaries of their own.
* Profiles: line tables for workspace crates, no debug info for dependencies
* Windows ARM64: nightly and on release pushes instead of every push to main
* xtask ci: clippy and tests in the dev profile
The release profile's thin LTO and single codegen unit made linking the test
binaries take most of the gate. The codec and audio crates stay at opt-level 3
in the dev profile, so their tests run at release speed.
* xtask ci: nextest, doctests, no examples, one wasm check
Tests run with cargo nextest when it is installed (cargo test otherwise) and
build with --lib --bins --tests, so examples aren't linked; doctests run
separately. The wasm check keeps going past the first failing crate. VP8
fixture generation locks across processes, since nextest runs each test in
its own.
* VP9 synthetic fixtures: write aside and rename
A test in another process could read a fixture ffmpeg was still writing.
* Fuzz tests: a third of each sweep by default, all of it with FILMCRAFT_FUZZ_ITERS
Every seed, mutation kind and thread count still runs. Header bytes each get
one overwrite value and two bit flips in rotation, so every value and bit
index still occurs across the region. FILMCRAFT_FUZZ_ITERS=N runs the whole
sweep plus N random rounds per seed, as before.
* Platform fallback tests: one failure point per hybrid path
The failure-anywhere test injected a failure at four points around every
restart point; the hybrid's paths differ only in the first GOP, around the
second and last restart points, at the end and never. The replay test needs
the first two GOPs.
* Export tests: shorter ranges with the same checks
The bitrate test measures 0.5–1.5 s, where each preset reaches its own
target (on 0–3 s the simple demo left two presets at the same rate). The
every-preset test exports three frames per preset.
* Audio tests: effects in parallel, a table-driven FFT for the Opus quality metric
The extreme-settings test keeps both settings and the 40 s tail for every
effect, one thread each. The Opus quality metric's FFT builds its twiddles
once and allocates nothing per transform (same algorithm and summation order).
* CI: dev-profile gate with nextest
xtask ci no longer builds with --release, so the release-profile overrides
go; nextest is installed for the test step.
* CI: apt packages through the cached install-apt-packages action
* Review fixes: fuzz tests with a panic hook in their own binaries, loud fixture lock
The fuzz tests of av1, h264, hevc, mpeg2v, vp8 and vp9 count panics through
a process-wide hook, so a panic in another test of the same process would
fail them; they are binaries of their own again. The VP8 fixture lock fails
the test instead of skipping it silently, and the full-sweep doc comments say
how many truncation points the full run samples.
* Fuzz tests: rotate the default subset per run (FILMCRAFT_FUZZ_SEED replays one)
* platform tests: decode-switch tests in their own binaries, one NVENC lock
Rust/clippy 1.99 added `chunks_exact_to_as_chunks` and tightened
`needless_late_init` and `drain_collect`, failing `clippy -D warnings`
on main.
- `chunks_exact(N)` / `chunks_exact_mut(N)` with a constant N become
`as_chunks::<N>()` / `as_chunks_mut::<N>()` (fixed-size arrays, so
several byte reads drop their per-element indexing). relink's
fingerprint uses the `(chunks, remainder)` split; its hash output is
unchanged.
- Late-initialised `let`s in ac3 bit allocation and opus CELT
bands/rate become `let x = if/match …`.
- `dpb.drain(..).collect()` becomes `std::mem::take(&mut dpb)`.
No behaviour change; `cargo xtask ci` passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ac3: LFE channel taken without `expect`.
- audio-dsp: the channel matrix match is exhaustive (no `unreachable!`); an unknown internal
parameter reads as 0 (debug-asserted) instead of `panic!`.
- opus: dual-stereo bands and SILK frames take the optional buffer / range decoder with `if let`.
- mpeg2v: 64-bit peek through `get()` + `first_chunk`; the literal VLC table parser no longer
panics (debug-asserted).
- prores, dnx: IDCT rows through `as_chunks`; ProRes block store and alpha slices without
unwrap/expect; DNx header timecode through `get()`.
- dnx: `Encoder::new` returns `Result` instead of panicking on a raster the profile can't code
(e.g. 0x0), with a regression test; missing CID table is an error.
- Each crate denies unwrap/expect/panic/unimplemented/todo/unreachable outside tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New dev-dependency-only crate crates/testkit (L0, no FilmCraft deps):
FILMCRAFT_FFMPEG/FILMCRAFT_FFPROBE, then PATH (incl. .exe), then known
dirs; SKIPPED lines and FILMCRAFT_REQUIRE_ORACLES=1 to make skips fail;
require_ffmpeg!/require_ffprobe!/require_oracles!; fixtures_dir() under
<workspace>/target/fixtures with per-thread temp names and atomic
generation. Oracle helpers in aac, opus, h264, hevc, h264enc, isobmff,
matroska, prores and export migrated (codecs left on its local lookup).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Multistream packet-loss concealment conceals the last packet duration on
every stream (conceal(dur)) so all streams stay in step. New robustness
tests: random garbage over every TOC config at all output rates, multistream
garbage, truncated/bit-flipped conformance packets interleaved with losses;
none panic and all output is finite. New oracle test decodes at 8/12/16/24 kHz
against libopus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>