fix(lotus): screenshare-audio mute survives a re-share — mute via the renderer, not setVolume
"Mute Screenshare Audio" (io.lotus.set_deafen screenshareAudioMuted) used RemoteParticipant.setVolume(0, ScreenShareAudio). EC's own createVolumeControls writes volume 1 through the same setter the moment a new screenshare media item resolves, so when the sharer stopped and re-shared (or a late joiner shared) the audio came back at full volume while the host button still said "Unmute Screenshare Audio". Reproduced on the local calls stack with two headless clients: after a re-share the screen_share_audio element read vol=1. Now the flag is a global behavior (muteScreenshareAudio$) that LivekitRoomAudioRenderer turns into the `muted` prop of every Track.Source.ScreenShareAudio element — the exact mechanism deafen already uses (pub.setEnabled(false): the server stops sending). Verified via the RemoteTrackPublication behind each <audio>: the re-published track (new sid) mounts with enabled=false while muted and re-enables on unmute; deafen + undeafen leaves it muted; teardown resets the flag so the next call starts clean. Unit tests updated; renderer test asserts only ScreenShareAudio elements get muted by the new prop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5
parent
bc0e5ed432
commit
ea579cb998
@@ -73,6 +73,7 @@ import { matrixRTCMode as matrixRTCModeSetting } from "../settings/settings";
|
||||
import { ReactionsReader } from "../reactions/ReactionsReader";
|
||||
import { LivekitRoomAudioRenderer } from "../livekit/MatrixAudioRenderer.tsx";
|
||||
import { muteAllAudio$ } from "../state/MuteAllAudioModel.ts";
|
||||
import { muteScreenshareAudio$ } from "../lotus/lotusScreenshareAudio";
|
||||
import { useMediaDevices } from "../MediaDevicesContext.ts";
|
||||
import { EarpieceOverlay } from "./EarpieceOverlay.tsx";
|
||||
import {
|
||||
@@ -261,6 +262,8 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
const { showControls, header: headerStyle } = useUrlParams();
|
||||
|
||||
const muteAllAudio = useBehavior(muteAllAudio$);
|
||||
// [lotus] host-driven "Mute Screenshare Audio" (io.lotus.set_deafen).
|
||||
const muteScreenshareAudio = useBehavior(muteScreenshareAudio$);
|
||||
const toggleAudio = useBehavior(muteStates.audio.toggle$);
|
||||
const toggleVideo = useBehavior(muteStates.video.toggle$);
|
||||
const setAudioEnabled = useBehavior(muteStates.audio.setEnabled$);
|
||||
@@ -309,7 +312,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
// [lotus] Handle the host's io.lotus.set_deafen action to silence remote
|
||||
// audio (and optionally screenshare audio) at the LiveKit source. No-op
|
||||
// unless the host sends the action.
|
||||
useEffect(() => startLotusDeafen(vm), [vm]);
|
||||
useEffect(() => startLotusDeafen(), []);
|
||||
|
||||
const fatalCallError = useBehavior(vm.fatalError$);
|
||||
// Stop the rendering and throw for the error boundary
|
||||
@@ -657,6 +660,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
livekitRoom={livekitRoom}
|
||||
validIdentities={participants}
|
||||
muted={muteAllAudio}
|
||||
screenshareAudioMuted={muteScreenshareAudio}
|
||||
/>
|
||||
))}
|
||||
{renderContent()}
|
||||
|
||||
Reference in New Issue
Block a user