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.
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
mainresolves toindex.jsand the unminifiedsrc/. Only the browser bundle is affected.Reproduce
Any mapping-family-255 stream does it. To make one:
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:
Same packets, same page, bundled from
src/with esbuild instead: 16 channels, correct order,errorspresent.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):
src/OpusDecoder.jsdist/opus-decoder.min.jsdecodeFrames,decodeFrame,ready,free,resetchannelData,samplesDecoded,sampleRatechannels,streamCount,coupledStreamCountchannelMappingTable,preSkip,forceStereoerrorsSo property mangling is on with a reserved list that covers the methods and most result fields but misses every constructor option, plus
errorson 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.