Skip to content

opus-decoder: minified dist mangles constructor option names, so multichannel silently decodes as stereo #129

Description

@mormegil6

The published browser bundle (dist/opus-decoder.min.js, opus-decoder 0.7.11) property-mangles the constructor's own option names. Passing a multichannel configuration through it is silently ignored: the decoder falls back to stereo defaults, returns 2 channels for a 16-channel stream, and reports no error.

Node users are unaffected, since main resolves to index.js and the unminified src/. Only the browser bundle is affected.

Reproduce

Any mapping-family-255 stream does it. To make one:

ffmpeg $(for i in $(seq 0 15); do echo -n "-f lavfi -i sine=frequency=$((200+i*100)):duration=2:sample_rate=48000 "; done) \
  -filter_complex "$(for i in $(seq 0 15); do echo -n "[$i]"; done)amerge=inputs=16[a]" -map "[a]" \
  -c:a libopus -mapping_family 255 -b:a 512k out.webm

That gives 16 channels carrying 200 Hz to 1700 Hz, one tone per channel, so a wrong channel count or order is audible and measurable. Take the OpusHead and packets out of the container, then with the dist bundle:

const dec = new OpusDecoder({
  channels: 16, streamCount: 16, coupledStreamCount: 0,
  channelMappingTable: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], preSkip: 312,
});
await dec.ready;
const res = dec.decodeFrames(packets);
res.channelData.length;   // 2, expected 16
res.errors;               // undefined, expected an array

Same packets, same page, bundled from src/ with esbuild instead: 16 channels, correct order, errors present.

Cause

The names are absent from the shipped bundle. Counting occurrences in 0.7.11 (via Python, since the file embeds the WASM binary and contains NUL bytes, so grep treats it as binary and silently matches nothing):

name src/OpusDecoder.js dist/opus-decoder.min.js
decodeFrames, decodeFrame, ready, free, reset present present
channelData, samplesDecoded, sampleRate present present
channels, streamCount, coupledStreamCount present 0
channelMappingTable, preSkip, forceStereo present 0
errors present 0

So property mangling is on with a reserved list that covers the methods and most result fields but misses every constructor option, plus errors on the decode result. Adding those names to the mangler's reserved list should be the whole fix.

I did not find an existing issue for this: #24, #25 and #50 are about multichannel decoding as a feature and are closed, and #92 concerns the wasm packaging rather than name mangling.

Why it is easy to miss

Stereo works, because stereo is what the ignored options would have defaulted to anyway. Nothing throws. The only visible symptom is a channel count quietly smaller than the stream, which is easy to attribute to the browser rather than the library: multichannel Opus is genuinely broken in several browsers right now, so a stereo result looks like the platform's fault.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions