Related to #76 — a concrete list of defects in src/moov/trak/mdia/minf/stbl/stsd/mp4a/esds.rs:
1. Descriptor size varint encoded in reverse group order.
Decode (esds.rs:51-58) reads the ISO 14496-1 base-128 length MSB-group-first (correct). Encode (esds.rs:96-104) emits the least-significant 7-bit group first with the continuation bit on it:
let mut size = tmp.len() as u32;
while size > 0 {
let mut b = (size & 0x7F) as u8; // low group first
size >>= 7;
if size > 0 { b |= 0x80; }
b.encode(buf)?;
}
For body size 200 this writes C8 01, which decodes as size 9217 → corrupt stream. Additionally, size 0 writes no length byte at all (while size > 0 never runs), producing a tag with no length field. Currently only reachable via the public Descriptor::Unknown variant or any descriptor ≥ 128 bytes, so it's latent — but any user-constructed descriptor or payload growth hits it.
2. AudioSpecificConfig bit-extraction bugs (esds.rs:262-310).
:269 — extended audioObjectType: 32 + ((byte_a & 7) | (byte_b >> 5)) is missing << 3 on the first term; the two 3-bit halves are OR'd on top of each other. Any AOT ≥ 32 (USAC/xHE-AAC = 42) decodes to a wrong profile.
:286 — extended-profile channel config: (byte_b & 1) | (byte_c & 0xE0) should be ((byte_b & 1) << 3) | (byte_c >> 5); yields values up to 225 for a 4-bit field.
:279-283 — freq_index == 15 escape: the 24-bit explicit sample rate starts 7 bits into the byte stream but is read byte-aligned, the channel config is selected one bit off, the sample rate is then discarded, and encode can't reproduce the escape (existing TODO) — round-trip corrupts.
:306 — encode does self.profile << 3; for extended profiles ≥ 32 (which decode can produce) the shift silently truncates the AOT with no error.
3. EsDescriptor flags byte ignored (esds.rs:154).
streamDependenceFlag / URL_Flag / OCR_streamFlag are read and discarded (// XXX flags must be 0); when set, the following dependsOn_ES_ID / URL string / OCR_ES_ID bytes are misparsed as child descriptors.
4. Size-varint decode desync (esds.rs:52-58).
If the 4th size byte still has the continuation bit set, the loop exits silently with a truncated size instead of erroring, desyncing the parse.
Found during an extensive automated correctness review (Claude Code); verified against the current main sources.
Related to #76 — a concrete list of defects in
src/moov/trak/mdia/minf/stbl/stsd/mp4a/esds.rs:1. Descriptor size varint encoded in reverse group order.
Decode (
esds.rs:51-58) reads the ISO 14496-1 base-128 length MSB-group-first (correct). Encode (esds.rs:96-104) emits the least-significant 7-bit group first with the continuation bit on it:For body size 200 this writes
C8 01, which decodes as size 9217 → corrupt stream. Additionally, size 0 writes no length byte at all (while size > 0never runs), producing a tag with no length field. Currently only reachable via the publicDescriptor::Unknownvariant or any descriptor ≥ 128 bytes, so it's latent — but any user-constructed descriptor or payload growth hits it.2. AudioSpecificConfig bit-extraction bugs (
esds.rs:262-310).:269— extended audioObjectType:32 + ((byte_a & 7) | (byte_b >> 5))is missing<< 3on the first term; the two 3-bit halves are OR'd on top of each other. Any AOT ≥ 32 (USAC/xHE-AAC = 42) decodes to a wrong profile.:286— extended-profile channel config:(byte_b & 1) | (byte_c & 0xE0)should be((byte_b & 1) << 3) | (byte_c >> 5); yields values up to 225 for a 4-bit field.:279-283—freq_index == 15escape: the 24-bit explicit sample rate starts 7 bits into the byte stream but is read byte-aligned, the channel config is selected one bit off, the sample rate is then discarded, and encode can't reproduce the escape (existing TODO) — round-trip corrupts.:306— encode doesself.profile << 3; for extended profiles ≥ 32 (which decode can produce) the shift silently truncates the AOT with no error.3.
EsDescriptorflags byte ignored (esds.rs:154).streamDependenceFlag/URL_Flag/OCR_streamFlagare read and discarded (// XXX flags must be 0); when set, the following dependsOn_ES_ID / URL string / OCR_ES_ID bytes are misparsed as child descriptors.4. Size-varint decode desync (
esds.rs:52-58).If the 4th size byte still has the continuation bit set, the loop exits silently with a truncated size instead of erroring, desyncing the parse.
Found during an extensive automated correctness review (Claude Code); verified against the current
mainsources.