Skip to content

feat(audio): negotiate stereo opus on the subscriber answer - #1007

Open
kirill-jjj wants to merge 3 commits into
livekit:mainfrom
kirill-jjj:stereo-answer-munging
Open

feat(audio): negotiate stereo opus on the subscriber answer#1007
kirill-jjj wants to merge 3 commits into
livekit:mainfrom
kirill-jjj:stereo-answer-munging

Conversation

@kirill-jjj

Copy link
Copy Markdown

Problem

When a remote participant publishes a stereo audio track, Android subscribers receive it as mono. libwebrtc's native Opus decoder downmixes incoming stereo packets to mono unless the local answer negotiates stereo=1 in the fmtp line — even when the server offer correctly announces the track with sprop-stereo=1.

Observed on real hardware (Android 10, Huawei): stereo capture works (setUseStereoInput(true), confirmed via AudioRecord), the track is published with stereo=1 in AddTrackRequest, the server relays sprop-stereo=1 in the subscriber offer — yet the decoded audio plays back mono until stereo=1 is manually added to the answer.

Related issues

Changes

Receive path (the main fix):

  • New StereoSdpMunging.kt: extracts mids carrying sprop-stereo=1 from the server offer and adds stereo=1 to the matching fmtp lines of the answer. Invoked in RTCEngine.onServerOffer before setLocalDescription/sendAnswer. Fully fail-safe on parse failures.

Publish path (API parity with client-sdk-js):

  • LocalAudioTrackOptions.stereo — capture-side flag, surfaced as the TF_STEREO track feature
  • AudioTrackPublishOptions.stereo — sent as AddTrackRequest.stereo, mirroring JS's forceStereo option

Testing

  • 3 unit tests in livekit-android-test, using SDP fixtures captured from a real device (the subscriber offer from a live LiveKit session)
  • Verified on real hardware (Huawei, Android 10) in a two-participant room: stereo-in/stereo-out confirmed after this change; reproduced mono before it
  • ./gradlew test, spotlessCheck, detektRelease pass

Assisted by AI; verified end-to-end on real hardware.

The server advertises a stereo publisher track via sprop-stereo=1 in its
offer, but libwebrtc's Opus decoder only decodes incoming packets as
stereo when the local answer's fmtp line also carries stereo=1. Without
it, stereo tracks published by other participants are received as mono.

- StereoSdpMunging: extract sprop-stereo mids from the server offer and
  add stereo=1 to the matching fmtp lines of the answer (mirrors
  client-sdk-js remoteStereoMids handling), applied in onServerOffer
- LocalAudioTrackOptions.stereo + TF_STEREO track feature so publishers
  can declare a stereo capture
- AudioTrackPublishOptions.stereo sent as AddTrackRequest.stereo,
  mirroring client-sdk-js forceStereo

Includes unit tests built from real device SDP captures and a changeset.
@changeset-bot

changeset-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0212c81

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@CLAassistant

CLAassistant commented Aug 23, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

devin-ai-integration[bot]

This comment was marked as resolved.

…ment stereo option

Addresses Devin review feedback on livekit#1007:
- if the munged answer is rejected by libwebrtc, retry with the original
  answer and send that instead of leaving the subscriber without a local
  description (mirrors PeerConnectionTransport.setMungedSdp)
- add KDoc to LocalParticipant.AudioTrackPublishOptions.stereo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is it possible to add support for Stereo Channel Count in LocalAudioTrackOptions

2 participants