Skip to content

Android: implement ADM SetMicrophoneMute and allow opting out of stop-on-mute (parity with iOS) #268

Description

@max-buster

Summary

Since #206 (624fa1d), WebRtcVoiceSendChannel::MuteStream stops the ADM's recording when all send streams are muted, unless the ADM overrides IsStopOnMuteModeEnabled() to false. The iOS/macOS AudioEngineDevice opts out and mutes internally while keeping capture alive; the Android ADM has no override, so muting an audio track now fully releases AudioRecord.

Per the guidance in #246 — platforms that properly implement the ADM's Mute API can opt out individually — this issue proposes doing exactly that for Android.

Problem

For push-to-talk style apps, re-enabling the mic is a cold start: a new AudioRecord, AGC reconvergence, and on USB audio devices a full interface renegotiation (alternate setting selection, bandwidth reservation, sample rate setup). Speech during that window is lost, heard as a clipped first word on every transmission. Before #206, muting kept capture warm on Android.

Android already has a working warm-mute implementation — WebRtcAudioRecord.setMicrophoneMute() zero-fills the capture buffer on the record thread (sdk/android/src/java/org/webrtc/audio/WebRtcAudioRecord.java) — but it is only reachable from Java. The C++ AndroidAudioDeviceModule::SetMicrophoneMute() returns -1 "Not implemented" (sdk/android/src/jni/audio_device/audio_device_module.cc), so the fallback path in MuteStream is a no-op on Android, and there is no way to opt out of stop-on-mute.

Proposal

Keep stop-on-mute as the Android default; make the mute API real and the opt-out possible:

  1. Implement SetMicrophoneMute on the Android ADM: bridge AndroidAudioDeviceModule::SetMicrophoneMute() through AudioRecordJni to the existing WebRtcAudioRecord.setMicrophoneMute(), so the non-stop mute path zeroes audio at the source — the same "mute inside the engine, capture stays alive" semantics as the iOS AudioEngineDevice.
  2. Builder opt-out: add JavaAudioDeviceModule.Builder.setStopRecordingOnMute(boolean), default true (current behavior unchanged), plumbed through nativeCreateAudioDeviceModule into AndroidAudioDeviceModule, which overrides IsStopOnMuteModeEnabled() accordingly.

This mirrors the existing useStereoInput plumbing and is fully backward compatible. We understand the trade-off the default protects (OS mic-in-use indicator turns off while muted); apps that opt out accept the indicator staying on in exchange for warm capture.

Happy to submit a PR for this if the approach sounds acceptable.

Context

  • Regression surfaced in LiveKit Android (io.github.webrtc-sdk:android builds containing 624fa1d, i.e. 137.7151.05+) for a PTT app using USB handset microphones. LiveKit Android v2.23.0 (webrtc 137.7151.04) is the last unaffected release; v2.23.1 (137.7151.05) the first affected.
  • iOS had the equivalent problem (livekit/client-sdk-swift#422), resolved by the AudioEngineDevice opt-out in AVAudioEngine version AudioDeviceModule #158/Change default to stop recording when muted #206.
  • Current workaround: publish the mic once and gate PTT with the ADM-level setMicrophoneMute from the app, which forfeits track-level mute semantics (server mute state, remote TrackMuted events) and transmits encoded silence for the call duration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions