Screensharing rework
Split Linux screensharing audio tracks, Rework screensharing functionality and layout This will need some refactoring soon
This commit is contained in:
@@ -35,6 +35,7 @@ import {
|
||||
JoinedServerInfo,
|
||||
VoiceStateSnapshot,
|
||||
LatencyProfile,
|
||||
ScreenShareStartOptions,
|
||||
SIGNALING_TYPE_IDENTIFY,
|
||||
SIGNALING_TYPE_JOIN_SERVER,
|
||||
SIGNALING_TYPE_VIEW_SERVER,
|
||||
@@ -47,6 +48,8 @@ import {
|
||||
SIGNALING_TYPE_USER_JOINED,
|
||||
SIGNALING_TYPE_USER_LEFT,
|
||||
DEFAULT_DISPLAY_NAME,
|
||||
P2P_TYPE_SCREEN_SHARE_REQUEST,
|
||||
P2P_TYPE_SCREEN_SHARE_STOP,
|
||||
P2P_TYPE_VOICE_STATE,
|
||||
P2P_TYPE_SCREEN_STATE
|
||||
} from './webrtc';
|
||||
@@ -69,6 +72,9 @@ export class WebRTCService implements OnDestroy {
|
||||
/** Maps each remote peer ID to the server they were discovered from. */
|
||||
private readonly peerServerMap = new Map<string, string>();
|
||||
private readonly serviceDestroyed$ = new Subject<void>();
|
||||
private remoteScreenShareRequestsEnabled = false;
|
||||
private readonly desiredRemoteScreenSharePeers = new Set<string>();
|
||||
private readonly activeRemoteScreenSharePeers = new Set<string>();
|
||||
|
||||
private readonly _localPeerId = signal<string>(uuidv4());
|
||||
private readonly _isSignalingConnected = signal(false);
|
||||
@@ -204,11 +210,37 @@ export class WebRTCService implements OnDestroy {
|
||||
// Signaling → heartbeat → broadcast states
|
||||
this.signalingManager.heartbeatTick$.subscribe(() => this.peerManager.broadcastCurrentStates());
|
||||
|
||||
// Internal control-plane messages for on-demand screen-share delivery.
|
||||
this.peerManager.messageReceived$.subscribe((event) => this.handlePeerControlMessage(event));
|
||||
|
||||
// Peer manager → connected peers signal
|
||||
this.peerManager.connectedPeersChanged$.subscribe((peers: string[]) =>
|
||||
this._connectedPeers.set(peers)
|
||||
);
|
||||
|
||||
// If we are already sharing when a new peer connection finishes, push the
|
||||
// current screen-share tracks to that peer and renegotiate.
|
||||
this.peerManager.peerConnected$.subscribe((peerId) => {
|
||||
if (!this.screenShareManager.getIsScreenActive()) {
|
||||
if (this.remoteScreenShareRequestsEnabled && this.desiredRemoteScreenSharePeers.has(peerId)) {
|
||||
this.requestRemoteScreenShares([peerId]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.screenShareManager.syncScreenShareToPeer(peerId);
|
||||
|
||||
if (this.remoteScreenShareRequestsEnabled && this.desiredRemoteScreenSharePeers.has(peerId)) {
|
||||
this.requestRemoteScreenShares([peerId]);
|
||||
}
|
||||
});
|
||||
|
||||
this.peerManager.peerDisconnected$.subscribe((peerId) => {
|
||||
this.activeRemoteScreenSharePeers.delete(peerId);
|
||||
this.screenShareManager.clearScreenShareRequest(peerId);
|
||||
});
|
||||
|
||||
// Media manager → voice connected signal
|
||||
this.mediaManager.voiceConnected$.subscribe(() => {
|
||||
this._isVoiceConnected.set(true);
|
||||
@@ -544,6 +576,31 @@ export class WebRTCService implements OnDestroy {
|
||||
this.peerManager.sendToPeer(peerId, event);
|
||||
}
|
||||
|
||||
syncRemoteScreenShareRequests(peerIds: string[], enabled: boolean): void {
|
||||
const nextDesiredPeers = new Set(
|
||||
peerIds.filter((peerId): peerId is string => !!peerId)
|
||||
);
|
||||
|
||||
if (!enabled) {
|
||||
this.remoteScreenShareRequestsEnabled = false;
|
||||
this.desiredRemoteScreenSharePeers.clear();
|
||||
this.stopRemoteScreenShares([...this.activeRemoteScreenSharePeers]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.remoteScreenShareRequestsEnabled = true;
|
||||
|
||||
for (const activePeerId of [...this.activeRemoteScreenSharePeers]) {
|
||||
if (!nextDesiredPeers.has(activePeerId)) {
|
||||
this.stopRemoteScreenShares([activePeerId]);
|
||||
}
|
||||
}
|
||||
|
||||
this.desiredRemoteScreenSharePeers.clear();
|
||||
nextDesiredPeers.forEach((peerId) => this.desiredRemoteScreenSharePeers.add(peerId));
|
||||
this.requestRemoteScreenShares([...nextDesiredPeers]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a {@link ChatEvent} to a peer with back-pressure awareness.
|
||||
*
|
||||
@@ -569,6 +626,29 @@ export class WebRTCService implements OnDestroy {
|
||||
return this.peerManager.remotePeerStreams.get(peerId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote voice-only stream for a connected peer.
|
||||
*
|
||||
* @param peerId - The remote peer whose voice stream to retrieve.
|
||||
* @returns The stream, or `null` if the peer has no active voice audio.
|
||||
*/
|
||||
getRemoteVoiceStream(peerId: string): MediaStream | null {
|
||||
return this.peerManager.remotePeerVoiceStreams.get(peerId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote screen-share stream for a connected peer.
|
||||
*
|
||||
* This contains the screen video track and any audio track that belongs to
|
||||
* the screen share itself, not the peer's normal voice-chat audio.
|
||||
*
|
||||
* @param peerId - The remote peer whose screen-share stream to retrieve.
|
||||
* @returns The stream, or `null` if the peer has no active screen share.
|
||||
*/
|
||||
getRemoteScreenShareStream(peerId: string): MediaStream | null {
|
||||
return this.peerManager.remotePeerScreenShareStreams.get(peerId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current local media stream (microphone audio).
|
||||
*
|
||||
@@ -715,11 +795,11 @@ export class WebRTCService implements OnDestroy {
|
||||
/**
|
||||
* Start sharing the screen (or a window) with all connected peers.
|
||||
*
|
||||
* @param includeAudio - Whether to capture and mix system audio.
|
||||
* @param options - Screen-share capture options.
|
||||
* @returns The screen-capture {@link MediaStream}.
|
||||
*/
|
||||
async startScreenShare(includeAudio = false): Promise<MediaStream> {
|
||||
const stream = await this.screenShareManager.startScreenShare(includeAudio);
|
||||
async startScreenShare(options: ScreenShareStartOptions): Promise<MediaStream> {
|
||||
const stream = await this.screenShareManager.startScreenShare(options);
|
||||
|
||||
this._isScreenSharing.set(true);
|
||||
this._screenStreamSignal.set(stream);
|
||||
@@ -755,6 +835,9 @@ export class WebRTCService implements OnDestroy {
|
||||
private fullCleanup(): void {
|
||||
this.voiceServerId = null;
|
||||
this.peerServerMap.clear();
|
||||
this.remoteScreenShareRequestsEnabled = false;
|
||||
this.desiredRemoteScreenSharePeers.clear();
|
||||
this.activeRemoteScreenSharePeers.clear();
|
||||
this.peerManager.closeAllPeers();
|
||||
this._connectedPeers.set([]);
|
||||
this.mediaManager.disableVoice();
|
||||
@@ -782,6 +865,50 @@ export class WebRTCService implements OnDestroy {
|
||||
return connState === 'connected' && dcState === 'open';
|
||||
}
|
||||
|
||||
private handlePeerControlMessage(event: ChatEvent): void {
|
||||
if (!event.fromPeerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === P2P_TYPE_SCREEN_SHARE_REQUEST) {
|
||||
this.screenShareManager.requestScreenShareForPeer(event.fromPeerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type === P2P_TYPE_SCREEN_SHARE_STOP) {
|
||||
this.screenShareManager.stopScreenShareForPeer(event.fromPeerId);
|
||||
}
|
||||
}
|
||||
|
||||
private requestRemoteScreenShares(peerIds: string[]): void {
|
||||
const connectedPeerIds = new Set(this.peerManager.getConnectedPeerIds());
|
||||
|
||||
for (const peerId of peerIds) {
|
||||
if (!connectedPeerIds.has(peerId) || this.activeRemoteScreenSharePeers.has(peerId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.peerManager.sendToPeer(peerId, { type: P2P_TYPE_SCREEN_SHARE_REQUEST });
|
||||
this.activeRemoteScreenSharePeers.add(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
private stopRemoteScreenShares(peerIds: string[]): void {
|
||||
const connectedPeerIds = new Set(this.peerManager.getConnectedPeerIds());
|
||||
|
||||
for (const peerId of peerIds) {
|
||||
if (!this.activeRemoteScreenSharePeers.has(peerId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connectedPeerIds.has(peerId)) {
|
||||
this.peerManager.sendToPeer(peerId, { type: P2P_TYPE_SCREEN_SHARE_STOP });
|
||||
}
|
||||
|
||||
this.activeRemoteScreenSharePeers.delete(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.disconnect();
|
||||
this.serviceDestroyed$.complete();
|
||||
|
||||
Reference in New Issue
Block a user