Clean up stream audio on ending stream
This commit is contained in:
@@ -623,7 +623,7 @@ class DebugNetworkSnapshotBuilder {
|
||||
node.isMuted = user.voiceState?.isMuted === true;
|
||||
node.isDeafened = user.voiceState?.isDeafened === true;
|
||||
node.isSpeaking = user.voiceState?.isSpeaking === true || node.isSpeaking;
|
||||
node.isStreaming = user.screenShareState?.isSharing === true || node.isStreaming;
|
||||
node.isStreaming = user.screenShareState?.isSharing === true;
|
||||
|
||||
if (user.voiceState?.isConnected !== true)
|
||||
node.streams.audio = 0;
|
||||
|
||||
@@ -924,6 +924,11 @@ export class WebRTCService implements OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === P2P_TYPE_SCREEN_STATE && event.isScreenSharing === false) {
|
||||
this.peerManager.clearRemoteScreenShareStream(event.fromPeerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === P2P_TYPE_SCREEN_SHARE_REQUEST) {
|
||||
this.screenShareManager.requestScreenShareForPeer(event.fromPeerId);
|
||||
return;
|
||||
@@ -951,15 +956,12 @@ export class WebRTCService implements OnDestroy {
|
||||
const connectedPeerIds = new Set(this.peerManager.getConnectedPeerIds());
|
||||
|
||||
for (const peerId of peerIds) {
|
||||
if (!this.activeRemoteScreenSharePeers.has(peerId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connectedPeerIds.has(peerId)) {
|
||||
if (this.activeRemoteScreenSharePeers.has(peerId) && connectedPeerIds.has(peerId)) {
|
||||
this.peerManager.sendToPeer(peerId, { type: P2P_TYPE_SCREEN_SHARE_STOP });
|
||||
}
|
||||
|
||||
this.activeRemoteScreenSharePeers.delete(peerId);
|
||||
this.peerManager.clearRemoteScreenShareStream(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
schedulePeerReconnect,
|
||||
trackDisconnectedPeer
|
||||
} from './recovery/peer-recovery';
|
||||
import { handleRemoteTrack } from './streams/remote-streams';
|
||||
import { clearRemoteScreenShareStream as clearManagedRemoteScreenShareStream, handleRemoteTrack } from './streams/remote-streams';
|
||||
import {
|
||||
ConnectionLifecycleHandlers,
|
||||
createPeerConnectionManagerState,
|
||||
@@ -223,6 +223,11 @@ export class PeerConnectionManager {
|
||||
return getConnectedPeerIds(this.state);
|
||||
}
|
||||
|
||||
/** Remove any cached remote screen-share tracks for a peer. */
|
||||
clearRemoteScreenShareStream(peerId: string): void {
|
||||
clearManagedRemoteScreenShareStream(this.context, peerId);
|
||||
}
|
||||
|
||||
/** Reset the connected peers list to empty and notify subscribers. */
|
||||
resetConnectedPeers(): void {
|
||||
resetConnectedPeers(this.state);
|
||||
|
||||
@@ -53,22 +53,33 @@ export function handleRemoteTrack(
|
||||
state.remotePeerScreenShareStreams.set(remotePeerId, screenShareStream);
|
||||
}
|
||||
|
||||
state.remoteStream$.next({
|
||||
peerId: remotePeerId,
|
||||
stream: compositeStream
|
||||
});
|
||||
publishRemoteStreamUpdate(context, remotePeerId, compositeStream);
|
||||
}
|
||||
|
||||
recordDebugNetworkStreams(remotePeerId, {
|
||||
audio: compositeStream.getAudioTracks().length,
|
||||
video: compositeStream.getVideoTracks().length
|
||||
});
|
||||
export function clearRemoteScreenShareStream(
|
||||
context: PeerConnectionManagerContext,
|
||||
remotePeerId: string
|
||||
): void {
|
||||
const { state } = context;
|
||||
const screenShareStream = state.remotePeerScreenShareStreams.get(remotePeerId);
|
||||
|
||||
logger.info('Remote stream updated', {
|
||||
audioTrackCount: compositeStream.getAudioTracks().length,
|
||||
if (!screenShareStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
const screenShareTrackIds = new Set(
|
||||
screenShareStream.getTracks().map((track) => track.id)
|
||||
);
|
||||
const compositeStream = removeTracksFromStreamMap(
|
||||
state.remotePeerStreams,
|
||||
remotePeerId,
|
||||
trackCount: compositeStream.getTracks().length,
|
||||
videoTrackCount: compositeStream.getVideoTracks().length
|
||||
});
|
||||
screenShareTrackIds
|
||||
);
|
||||
|
||||
removeTracksFromStreamMap(state.remotePeerVoiceStreams, remotePeerId, screenShareTrackIds);
|
||||
state.remotePeerScreenShareStreams.delete(remotePeerId);
|
||||
|
||||
publishRemoteStreamUpdate(context, remotePeerId, compositeStream);
|
||||
}
|
||||
|
||||
function buildCompositeRemoteStream(
|
||||
@@ -140,48 +151,27 @@ function removeRemoteTrack(
|
||||
remotePeerId: string,
|
||||
trackId: string
|
||||
): void {
|
||||
const { logger, state } = context;
|
||||
const { state } = context;
|
||||
const compositeStream = removeTrackFromStreamMap(state.remotePeerStreams, remotePeerId, trackId);
|
||||
|
||||
removeTrackFromStreamMap(state.remotePeerVoiceStreams, remotePeerId, trackId);
|
||||
removeTrackFromStreamMap(state.remotePeerScreenShareStreams, remotePeerId, trackId);
|
||||
|
||||
if (!compositeStream) {
|
||||
recordDebugNetworkStreams(remotePeerId, { audio: 0,
|
||||
video: 0 });
|
||||
|
||||
logger.info('Remote stream updated', {
|
||||
audioTrackCount: 0,
|
||||
remotePeerId,
|
||||
trackCount: 0,
|
||||
videoTrackCount: 0
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
state.remoteStream$.next({
|
||||
peerId: remotePeerId,
|
||||
stream: compositeStream
|
||||
});
|
||||
|
||||
recordDebugNetworkStreams(remotePeerId, {
|
||||
audio: compositeStream.getAudioTracks().length,
|
||||
video: compositeStream.getVideoTracks().length
|
||||
});
|
||||
|
||||
logger.info('Remote stream updated', {
|
||||
audioTrackCount: compositeStream.getAudioTracks().length,
|
||||
remotePeerId,
|
||||
trackCount: compositeStream.getTracks().length,
|
||||
videoTrackCount: compositeStream.getVideoTracks().length
|
||||
});
|
||||
publishRemoteStreamUpdate(context, remotePeerId, compositeStream);
|
||||
}
|
||||
|
||||
function removeTrackFromStreamMap(
|
||||
streamMap: Map<string, MediaStream>,
|
||||
remotePeerId: string,
|
||||
trackId: string
|
||||
): MediaStream | null {
|
||||
return removeTracksFromStreamMap(streamMap, remotePeerId, new Set([trackId]));
|
||||
}
|
||||
|
||||
function removeTracksFromStreamMap(
|
||||
streamMap: Map<string, MediaStream>,
|
||||
remotePeerId: string,
|
||||
trackIds: ReadonlySet<string>
|
||||
): MediaStream | null {
|
||||
const currentStream = streamMap.get(remotePeerId);
|
||||
|
||||
@@ -191,7 +181,7 @@ function removeTrackFromStreamMap(
|
||||
|
||||
const remainingTracks = currentStream
|
||||
.getTracks()
|
||||
.filter((existingTrack) => existingTrack.id !== trackId && existingTrack.readyState === 'live');
|
||||
.filter((existingTrack) => !trackIds.has(existingTrack.id) && existingTrack.readyState === 'live');
|
||||
|
||||
if (remainingTracks.length === currentStream.getTracks().length) {
|
||||
return currentStream;
|
||||
@@ -208,6 +198,32 @@ function removeTrackFromStreamMap(
|
||||
return nextStream;
|
||||
}
|
||||
|
||||
function publishRemoteStreamUpdate(
|
||||
context: PeerConnectionManagerContext,
|
||||
remotePeerId: string,
|
||||
compositeStream: MediaStream | null
|
||||
): void {
|
||||
const { logger, state } = context;
|
||||
const stream = compositeStream ?? new MediaStream();
|
||||
|
||||
state.remoteStream$.next({
|
||||
peerId: remotePeerId,
|
||||
stream
|
||||
});
|
||||
|
||||
recordDebugNetworkStreams(remotePeerId, {
|
||||
audio: stream.getAudioTracks().length,
|
||||
video: stream.getVideoTracks().length
|
||||
});
|
||||
|
||||
logger.info('Remote stream updated', {
|
||||
audioTrackCount: stream.getAudioTracks().length,
|
||||
remotePeerId,
|
||||
trackCount: stream.getTracks().length,
|
||||
videoTrackCount: stream.getVideoTracks().length
|
||||
});
|
||||
}
|
||||
|
||||
function isVoiceAudioTrack(
|
||||
context: PeerConnectionManagerContext,
|
||||
event: RTCTrackEvent,
|
||||
|
||||
@@ -141,9 +141,17 @@ export class ScreenShareViewerComponent implements OnDestroy {
|
||||
// Subscribe to remote streams with video (screen shares)
|
||||
// NOTE: We no longer auto-display remote streams. Users must click "Live" to view.
|
||||
// This subscription is kept for potential future use (e.g., tracking available streams)
|
||||
this.remoteStreamSub = this.webrtcService.onRemoteStream.subscribe(({ peerId, stream }) => {
|
||||
// Do nothing on remote stream - user must explicitly click "Live" to view
|
||||
// The stream is still stored in webrtcService.remoteStreams and can be accessed via getRemoteStream()
|
||||
this.remoteStreamSub = this.webrtcService.onRemoteStream.subscribe(({ peerId }) => {
|
||||
if (peerId !== this.watchingUserId() || this.isLocalShare()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stream = this.webrtcService.getRemoteScreenShareStream(peerId);
|
||||
const hasActiveVideo = stream?.getVideoTracks().some((track) => track.readyState === 'live') ?? false;
|
||||
|
||||
if (!hasActiveVideo) {
|
||||
this.stopWatching();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for focus events dispatched by other components
|
||||
|
||||
Reference in New Issue
Block a user