fix(publish): honor requested audio channel count on macOS#1616
Conversation
The audio encoder trusted the platform's channel-count reporting when
building the WebAudio capture graph. On macOS getSettings().channelCount
is undefined and MediaStreamAudioSourceNode.channelCount defaults to 2,
so a mono mic was carried (and Opus-encoded) as duplicated stereo,
wasting bitrate. The AudioWorkletNode used the default channelCountMode
"max", which just follows the input, so the requested channel count
never took effect.
Add a channelCount override prop on the encoder (mirroring sampleRate),
recover the requested count from the track's applied getUserMedia
constraint via getConstraints() (which survives the macOS misreport,
unlike getSettings()), and force channelCountMode "explicit" when a
count is requested so WebAudio deterministically downmixes before
capture. Non-mic sources and callers that don't request a count keep
the existing "max" behavior, so there's no regression.
Source.Microphone already forwarded constraints.channelCount to
getUserMedia, so `new Source.Microphone({ constraints: { channelCount: 1 } })`
now produces correct mono end-to-end with no extra plumbing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThis PR adds optional 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
The audio encoder produced stereo Opus from a mono microphone on macOS, wasting bitrate. This forces the WebAudio capture graph to honor the requested channel count instead of trusting the platform's (unreliable) reporting.
Root cause
Source.Microphonealready forwardsconstraints.channelCounttogetUserMedia, so the constraint itself wasn't being dropped. The problem is downstream in the encoder:track.getSettings().channelCountisundefinedandMediaStreamAudioSourceNode.channelCountdefaults to2, soencoder.tsfell back to2channels.AudioWorkletNodeused the defaultchannelCountMode: "max", which just follows the input graph rather than thechannelCountpassed to it. So even a correctly-requested mono mic was carried (and encoded) as duplicated stereo.Because the platform misreports the track after
getUserMedia, the{ channelCount: 1 }constraint alone can't fix it at the source.Changes
channelCountoverride prop onAudio.Encoder(mirrors the existingsampleRateprop). Reachable throughBroadcastviaaudio: { channelCount: 1 }.getConstraints().channelCount, which echoes what was requested and survives the macOS misreport (unlikegetSettings()).channelCountMode: "explicit"when a channel count is requested, so WebAudio deterministically downmixes before the capture worklet sees the frames. The worklet then reports the correct count, so the Opus config andAudioDataframes both come out mono automatically."max"behavior, so there's no regression and no default change.With this,
new Source.Microphone({ constraints: { channelCount: 1 } })produces correct mono end-to-end. Consuming apps can drop both the JSgetUserMediabypass and any server-side downmix workaround.Cross-package sync
No paired updates needed. This is browser-only WebAudio capture (the Rust
moq-audiopath handles PCM channels natively and separately), anddemo/webdoesn't set a channel count, so the additive prop needs no demo change.Test plan
tsc --noEmitpasses forjs/publish🤖 Generated with Claude Code
(Written by Claude)