Repository navigation
mxf: faster decode_pcm, AES3 read_pcm no longer decodes the whole element per call - #3276
Conversation
decode_pcm ran a per-sample match and slice for every channel of every frame, and built its outputs with vec![Vec::with_capacity(n); ch], whose clones lose the capacity. It now does one pass per channel with the sample width as a const generic and exact-capacity outputs. Output is bit for bit the same (checked against the previous loop in a test). read_pcm on an AES3 (ST 331) element read and decoded the whole element on every call and then kept the tail, which is O(n^2) for a clip-wrapped element and allocates up to the file size on hostile input. It now reads the 4-byte header, applies the same sample-count bound as before, and reads and decodes only the requested sample frames. The result is unchanged, the allocation is bounded by the request (at most 65535 frames). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
PCM and AES3 frame decoding crates/mxf/src/essence.rs |
PCM decoding uses per-channel vectors with the existing sample-width conversions. AES3 frame decoding is extracted into a shared helper. Tests compare exact output bits and channel capacities. |
Bounded AES3 reads crates/mxf/src/file.rs |
AES3 reads cap usable frames to the lesser of declared and available frames, and read only the requested frames. Tests check decoded results, read sizes, and rejection of tracks with more than eight channels. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Refactor
Possibly related issues
- Issue 3261 — Covers the same
decode_pcmand AES3read_pcmpaths addressed by this change. - Issue 2778 — Covers MXF audio channel handling and whole-file decode allocation; this change bounds AES3 reads but does not cap all header-derived audio values.
Possibly related PRs
- storytold/filmcraft#2948 — Also bounds MXF read sizes; this change bounds AES3 reads to requested frames.
- storytold/filmcraft#3080 — Both changes modify MXF audio read sizing and allocation bounds.
Suggested reviewers: echelon
Merge Risk: 
Minimal · up to 076aa
-
The bounded AES3 read change has no identified issue requiring correction before merge.
Security architecture risk: 
Minimal · up to 076aa
-
The change reduces memory and decoding work for large AES3 elements while preserving existing channel limits and output behavior. No material security risk introduced or worsened by this change was identified.
Retained concerns
No architecture-level concerns identified.Security review details
-
Security Blast Radius
- inferred — The directly supported attack surface is CPU and memory consumption in the process decoding attacker-supplied MXF audio. The changed path narrows work driven by large element lengths; the inspected evidence does not establish tenant, service, or deployment-wide exposure.
Trust Boundaries and Controls
- observed — Untrusted frame counts and channel metadata remain constrained before decoding. The public decoder retains its 256-channel general limit and eight-channel AES3 limit. The file reader now applies the same AES3 channel rejection before reading its header or payload; previously that rejection occurred inside decode_pcm after the full-element read. Existing output-request bounds remain unchanged.
Resilience and Maintainability Implications
- observed — Header and payload reads populate invocation-local buffers through a read-only source interface. I/O errors propagate without returning partial output, and read_pcm does not mutate shared track state or persist a cursor. Repetition therefore introduces no new reservation, cleanup, or recovery lifecycle in the inspected implementation.
-
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed -
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.
|
This PR is one of four from a speed audit of the MXF container code (
Notes:
|
FredApps
left a comment
There was a problem hiding this comment.
Approving. Two MXF audio decode speedups. decode_pcm now de-interleaves one channel per pass with a const-width helper (decode_planes::<B>) instead of a per-sample match, and allocates each channel with its capacity (the old vec![Vec::with_capacity(n); ch] cloned empty vectors). AES3 element reads (read_pcm) now read only the 4-byte header and the requested 32-byte sample frames, bounded by the declared count and the available bytes, instead of reading and decoding the whole element on every call; more than eight channels is refused. The new tests compare decode_pcm with the old loop bit for bit over ten width, channel and padding combinations, and check that AES3 reads match whole-element decoding, never read more than frames * 32 bytes in one go, and reject nine channels. I read the whole diff; the gate and SonarCloud pass at 076aa3c. I read the code and the CI results; I did not run the app.
Part of #3261 (the open window and the caller side follow as separate PRs)
What
decode_pcm(PCM): one pass per channel with the sample width as a const generic, and outputs allocated with exact capacity (vec![Vec::with_capacity(n); ch]clones empty vectors, so all but the first channel reallocated). Output is bit identical.MxfFile::read_pcmfor AES3 (ST 331, D-10) elements: it used to read and decode the whole element on every call and keep the tail. For a clip-wrapped or hostile single element every 1920-frame read allocated and decoded the full element (O(n^2), allocation up to the file size). It now reads the 4-byte header, applies the same bound asaes3_element_samples(header count and bytes in the file), and reads and decodes only the requested frames. No limit is weakened: the allocation per call is now bounded by the request and by 65535 frames. The more-than-8-channels error is kept.Proof
decode_pcm_matches_the_per_sample_reference: the old loop is kept under#[cfg(test)]; 8/16/24/32-bit, 1-6 channels, with and without padding bytes per frame, compared withto_bits; capacities are exact.aes3_reads_decode_only_the_requested_frames: synthetic clip-wrapped AES3 element (header count equal to and smaller than the stored frames), many windows including past the end; compared withdecode_pcmof the whole element, and a counting source asserts no single read is larger than the request.aes3_read_rejects_more_than_eight_channels.cargo test --release -p filmcraft-mxf33 passed, clippy-D warnings,fmt --check,cargo check -p filmcraft-codecs.Measurement
decode_pcmon 60 s of 6-byte and 4-byte frames (24-bit and 16-bit stereo, 20 rounds), one core, 3 alternating rounds:3.7x fewer instructions, about 3x fewer cycles. Not done here: decoding straight into the
read_pcmoutput (one more copy remains).Overlap with open PRs
None of #1794, #1056, #1078 touch
decode_pcmorread_pcm. #1078 edits other hunks ofessence.rs(codec enum,identify_picture, tests near the end); #1794 editsfile.rsnear line 241. The tests added at the end ofessence.rs/file.rsmay need a trivial context merge with #1078'sessence.rstest hunk.🤖 Generated with Claude Code
Summary by CodeRabbit