Add two-way audio to the native WebRTC player: audio focus handling and push-to-talk#7141
Add two-way audio to the native WebRTC player: audio focus handling and push-to-talk#7141TimoPtr wants to merge 2 commits into
Conversation
Phase 3 of the native WebRTC camera player: sending microphone audio to cameras that support talk-back. - Add AudioController to :webrtc-core, with an AudioManager-backed implementation that holds MODE_IN_COMMUNICATION (enabling the platform echo cancellation path) and transient voice-communication audio focus while a microphone is live. Holds are reference counted so concurrent sessions balance correctly, and the previous audio mode is restored on the last release. - Wire it into WebRtcSession structurally: the audio mode is acquired when the microphone track starts sending and released on every exit path (mic off, stop, failure, release), so consumers cannot leak the communication mode the way manual management did in #6153. - Provide a process-wide AudioController through Hilt and pass it to both WebRtcSession consumers (debug screen, external bus player). - Add a push-to-talk button to the WebRTC developer debug screen: the mic is live only while pressed, and the first press requests the RECORD_AUDIO runtime permission. Unit tests cover the acquire/release balance across mic toggling, reconnection, stop, failure and disposal, plus a Robolectric test for the AudioManager mode and focus handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
The controller created the microphone AudioSource and track eagerly for every session, which initializes the platform audio record pipeline even for sessions that never use talk-back. libwebrtc's audio device module supports only one capture at a time, so any session left running (or leaked capture state from a previous one) broke InitRecording for every following session: the mic toggled to Live but no audio was ever captured or sent, with "InitRecording called twice without StopRecording" in the log. It also engaged the microphone privacy indicator without the microphone being used. The audio transceiver is still negotiated send-and-receive up front, but with no track: on the first setMicrophoneEnabled(true) the capture is created and attached through RtpSender.setTrack, which does not renegotiate, so push-to-talk still needs no session restart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
|
This is a substantial stack—session engine #7137, renderer #7139, external-bus protocol #7140, and audio handling #7141—so I want to raise a concern while the direction is still open. For roughly four years, I have tried to make two-way audio work reliably with Amcrest and Reolink cameras and doorbells. In my deployments, the client has rarely been the limiting layer. Camera firmware fails first: low-power hardware, fragile audio implementations, and codec defects the manufacturer may never fix. Streaming fails next: inexpensive cameras often serve only one or two consumers reliably, so go2rtc must proxy or transcode, and the user must find a codec, resolution, and audio combination that every device in the path accepts. Most users never get past those layers. The WebView can already provide the foreground client experience: a notification opens the app and a two-way-audio camera card answers. A native runtime adds answer-before-unlock, Before merging, please validate end to end with go2rtc and representative Amcrest, Reolink, and similar devices. If the goal is two-way audio that works broadly, coordination with @AlexxIT and camera manufacturers is important than adding another client engine. A native client introduces risk to WebRTC consumers that already work. I run the companion app on always-on wall panels that publish camera and microphone through Once the native stack acquires the microphone—#7141 adds capture, Without an explicit release-and-confirm handoff, existing capture may remain apparently connected while becoming silent or otherwise unstable. As proposed, these PRs risks expanding the Android client without addressing the layers where two-way audio most often fails, while destabilizing WebRTC consumers that already work. I recommend that the ADR require both end-to-end validation across representative cameras and go2rtc configurations, and deterministic cross-stack acquisition, failure, release, and recovery behavior before dashboard talk-back or default enablement. |
These stacked PR are not meant to be merge, it is a PoC before doing anything. Once we agreed at the OHF level to work on it's going to be a cross-repository effort to deliver the feature core, frontend, android and iOS. If you are interested into following the work that we are going to do please go in OpenHomeFoundation/roadmap#201 |
Summary
Phase 3 of the native WebRTC camera player (ADR, stacked on #7140): sending microphone audio to cameras that support talk-back.
AudioControllerin:webrtc-core— a small interface with anAudioManager-backed implementation that, while held, keeps the device inMODE_IN_COMMUNICATION(enabling the platform echo cancellation path used for calls) and takes transient voice-communication audio focus. Holds are reference counted so concurrent sessions balance correctly, and the previous audio mode is restored on the last release.WebRtcSession— the session acquires the audio mode when the microphone track actually starts sending and releases it on every exit path (mic off,stop(), signaling/connection failure,release()). This makes the class of leaks fixed in Microphone remains locked after using two-way audio in Companion App (WebRTC) #6153 (communication mode / audio focus left behind) structurally impossible for consumers: they only togglesetMicEnabledand can't forget the cleanup. The hold is kept across automatic reconnections, so a Wi-Fi roam doesn't glitch the audio route.AudioControllerprovided inWebRtcModule(it must be shared for the reference counting to balance) and passed to bothWebRtcSessionconsumers: the developer debug screen and the external-bus dashboard player.RECORD_AUDIOruntime permission instead. The mic state (Off/Live/Unavailable) is shown next to the player state.The
webrtc/micexternal-bus message for dashboard push-to-talk is intentionally deferred until the frontend has a talk UI to drive it; the dashboard player already benefits from the session-level guarantees.Checklist
Any other notes
WebRtcSessionTestcases covering the acquire/release balance across mic toggling, reconnection, stop, failure and disposal, and 5 RobolectricAndroidAudioControllerTestcases covering theAudioManagermode set/restore, reference counting and idempotent release.RECORD_AUDIOandMODIFY_AUDIO_SETTINGSwere already declared in the app manifest, so no new manifest permissions.🤖 Generated with Claude Code
https://claude.ai/code/session_01TxHn9ySi8aRFvvmfcPRgVu
Generated by Claude Code