From e7d70ccda6d28e9dc1770ceb3d2f6c0dc844aecb Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 6 Apr 2023 08:20:12 +0200 Subject: [PATCH] Group Calls: Fix error when user with big id enables camera (#2904) --- src/lib/secret-sauce/buildSdp.ts | 9 +++++---- src/lib/secret-sauce/p2p.ts | 3 +++ src/lib/secret-sauce/secretsauce.ts | 17 ++++++++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/lib/secret-sauce/buildSdp.ts b/src/lib/secret-sauce/buildSdp.ts index 976bc1b76..f06c7ae29 100644 --- a/src/lib/secret-sauce/buildSdp.ts +++ b/src/lib/secret-sauce/buildSdp.ts @@ -16,6 +16,7 @@ export type Conference = { export type Ssrc = { userId: string; endpoint: string; + mid: string; isMain: boolean; isRemoved?: boolean; isVideo: boolean; @@ -52,7 +53,7 @@ export default (conference: Conference, isAnswer = false, isPresentation = false add('t=0 0'); // time when session is valid add('a=ice-options:trickle'); add('a=msid-semantic:WMS *'); - add(`a=group:BUNDLE ${ssrcs.map((ssrc) => ssrc.endpoint).join(' ')}${isPresentation ? '' : ` ${isP2p ? '3' : '2'}`}`); + add(`a=group:BUNDLE ${ssrcs.map((ssrc) => ssrc.mid).join(' ')}${isPresentation ? '' : ` ${isP2p ? '3' : '2'}`}`); // ice-lite: is a minimal version of the ICE specification, intended only for servers running on a public IP address if (!isP2p) add('a=ice-lite'); @@ -110,7 +111,7 @@ export default (conference: Conference, isAnswer = false, isPresentation = false add(`m=${type} ${entry.isMain ? 1 : 0} RTP/SAVPF ${payloadTypes.map((l) => l.id).join(' ')}`); add('c=IN IP4 0.0.0.0'); add('b=AS:1300'); // 1300000 / 1000 - add(`a=mid:${entry.endpoint}`); + add(`a=mid:${entry.mid}`); add('a=rtcp-mux'); payloadTypes.forEach(addPayloadType); @@ -159,7 +160,7 @@ export default (conference: Conference, isAnswer = false, isPresentation = false }; if (!isP2p) { - ssrcs.filter((ssrc) => ssrc.endpoint === '0' || ssrc.endpoint === '1').map(addSsrcEntry); + ssrcs.filter((ssrc) => ssrc.mid === '0' || ssrc.mid === '1').map(addSsrcEntry); } else { ssrcs.filter(addSsrcEntry); } @@ -175,7 +176,7 @@ export default (conference: Conference, isAnswer = false, isPresentation = false } if (!isP2p) { - ssrcs.filter((ssrc) => ssrc.endpoint !== '0' && ssrc.endpoint !== '1').map(addSsrcEntry); + ssrcs.filter((ssrc) => ssrc.mid !== '0' && ssrc.mid !== '1').map(addSsrcEntry); } return `${lines.join('\n')}\n`; diff --git a/src/lib/secret-sauce/p2p.ts b/src/lib/secret-sauce/p2p.ts index 13f4ba996..d809f1ef3 100644 --- a/src/lib/secret-sauce/p2p.ts +++ b/src/lib/secret-sauce/p2p.ts @@ -416,6 +416,7 @@ export async function processSignalingMessage(message: P2pMessage) { isMain: false, userId: '123', endpoint: '0', + mid: '0', sourceGroups: [{ semantics: 'FID', sources: [message.audio.ssrc], @@ -427,6 +428,7 @@ export async function processSignalingMessage(message: P2pMessage) { isMain: false, userId: '123', endpoint: '1', + mid: '1', sourceGroups: message.video.ssrcGroups.map((l) => ({ semantics: l.semantics, sources: l.ssrcs, @@ -438,6 +440,7 @@ export async function processSignalingMessage(message: P2pMessage) { isMain: false, userId: '123', endpoint: '2', + mid: '2', sourceGroups: message.screencast.ssrcGroups.map((l) => ({ semantics: l.semantics, sources: l.ssrcs, diff --git a/src/lib/secret-sauce/secretsauce.ts b/src/lib/secret-sauce/secretsauce.ts index de640fcbf..adfef22e3 100644 --- a/src/lib/secret-sauce/secretsauce.ts +++ b/src/lib/secret-sauce/secretsauce.ts @@ -13,7 +13,7 @@ import { } from './utils'; export type StreamType = 'audio' | 'video' | 'presentation'; - +const DEFAULT_MID = 3; type GroupCallState = { connection?: RTCPeerConnection; screenshareConnection?: RTCPeerConnection; @@ -45,6 +45,7 @@ type GroupCallState = { destination?: MediaStreamAudioDestinationNode; audioContext?: AudioContext; mediaStream?: MediaStream; + lastMid: number; }; let state: GroupCallState | undefined; @@ -429,6 +430,7 @@ export async function handleUpdateGroupCallParticipants(updatedParticipants: Gro if (!isAudioLeft && !hasAudio) { // console.log('add audio'); + state!.lastMid = state!.lastMid + 1; conference.ssrcs!.push({ userId: participant.id, isMain: false, @@ -438,11 +440,14 @@ export async function handleUpdateGroupCallParticipants(updatedParticipants: Gro semantics: 'FID', sources: [participant.source], }], + mid: state!.lastMid.toString() }); } if (!isVideoLeft && !hasVideo && participant.video) { // console.log('add video', participant.video); + state!.lastMid = state!.lastMid + 1; + newEndpoints.push(participant.video.endpoint); conference.ssrcs!.push({ userId: participant.id, @@ -450,11 +455,13 @@ export async function handleUpdateGroupCallParticipants(updatedParticipants: Gro endpoint: participant.video.endpoint, isVideo: true, sourceGroups: participant.video.sourceGroups, + mid: state!.lastMid.toString() }); } if (!isPresentationLeft && !hasPresentation && participant.presentation) { // console.log('add presentation'); + state!.lastMid = state!.lastMid + 1; conference.ssrcs!.push({ isPresentation: true, userId: participant.id, @@ -462,6 +469,7 @@ export async function handleUpdateGroupCallParticipants(updatedParticipants: Gro endpoint: participant.presentation.endpoint, isVideo: true, sourceGroups: participant.presentation.sourceGroups, + mid: state!.lastMid.toString() }); } }); @@ -722,6 +730,7 @@ function initializeConnection( isVideo: false, isPresentation, endpoint: isPresentation ? '1' : '0', + mid: isPresentation ? '1' : '0' } : undefined; const videoSsrc: Ssrc | undefined = sdp['ssrc-groups'] && { @@ -731,6 +740,7 @@ function initializeConnection( isMain: true, isVideo: true, endpoint: isPresentation ? '0' : '1', + mid: isPresentation ? '0' : '1' }; const conference = isPresentation ? state.screenshareConference : state.conference; @@ -801,7 +811,7 @@ export async function startSharingScreen(): Promise { const { connection, dataChannel } = initializeConnection([stream], resolve, true); state = { - ...state, + ...state!, screenshareConnection: connection, screenshareDataChannel: dataChannel, }; @@ -840,11 +850,12 @@ export function joinGroupCall( // destination, audioContext, mediaStream, + lastMid: DEFAULT_MID, }; return new Promise((resolve) => { state = { - ...state, + ...state!, ...initializeConnection([state!.silence!, state!.black!], resolve), }; });