From 451667e019f6173aa564a85a2eeaad6891d73b3e Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Wed, 17 Jul 2024 09:05:02 -0300 Subject: [PATCH 01/21] testing --- .../tests/callfabric/renegotiation.spec.ts | 70 +++++++++++++++++++ packages/js/src/fabric/WSClient.ts | 4 +- packages/webrtc/src/RTCPeer.ts | 8 +++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 internal/e2e-js/tests/callfabric/renegotiation.spec.ts diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts new file mode 100644 index 000000000..50c323f96 --- /dev/null +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -0,0 +1,70 @@ +import { uuid } from '@signalwire/core' +import { Video } from '@signalwire/js' +import { test, expect } from '../../fixtures' +import { + SERVER_URL, + createCFClient, + expectLayoutChanged, + expectMCUVisible, + getStats, + setLayoutOnPage, +} from '../../utils' +import { Call } from 'packages/realtime-api/dist/realtime-api/src/voice/Voice' +import { callFabricWorker } from 'packages/js/src/fabric/workers' + +test.describe('CallFabric Renegotiation', () => { + + test('should handle joining a room with audio channel only', async ({ + createCustomPage, + resource, + }) => { + const page = await createCustomPage({ name: '[page]' }) + await page.goto(SERVER_URL) + + const roomName = `e2e-video-room_${uuid()}` + await resource.createVideoRoomResource(roomName) + + await createCFClient(page) + + // Dial an address with audio only channel + const roomSession = await page.evaluate( + async ({ roomName }) => { + return new Promise(async (resolve, _reject) => { + // @ts-expect-error + const client = window._client + + const call = await client.dial({ + to: `/public/${roomName}?channel=audio`, + rootElement: document.getElementById('rootElement'), + }) + + call.on('room.joined', resolve) + call.on('room.updated', () => {}) + + // @ts-expect-error + window._roomObj = call + + await call.start() + }) + }, + { roomName } + ) + + expect(roomSession.room_session).toBeDefined() + expect( + roomSession.room_session.members.some( + (member: any) => member.member_id === roomSession.member_id + ) + ).toBeTruthy() + + const stats = await getStats(page) + + // expect(stats.outboundRTP).not.toHaveProperty('video') + + // expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) + + await page.evaluate(async () => { + window._roomObj. + }); + }) +}) diff --git a/packages/js/src/fabric/WSClient.ts b/packages/js/src/fabric/WSClient.ts index b38430f37..dd2a94c22 100644 --- a/packages/js/src/fabric/WSClient.ts +++ b/packages/js/src/fabric/WSClient.ts @@ -69,8 +69,8 @@ export class WSClient { const call = this.wsClient.makeCallFabricObject({ audio: params.audio ?? true, video, - negotiateAudio: true, - negotiateVideo: true, + negotiateAudio: !!params.audio ?? true, + negotiateVideo: !!video, // iceServers, rootElement: params.rootElement || this.options.rootElement, applyLocalVideoOverlay: true, diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 624dea524..cf4d8cf99 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -423,7 +423,15 @@ export default class RTCPeer { offerOptions.offerToReceiveAudio = this.options.negotiateAudio offerOptions.offerToReceiveVideo = this.options.negotiateVideo } + console.log('##############################') + console.log('##############################') + console.log('##############################') + console.log(JSON.stringify(offerOptions, null, 2)) + console.log('##############################') + console.log('##############################') + console.log('##############################') const offer = await this.instance.createOffer(offerOptions) + console.log(offer.sdp) await this._setLocalDescription(offer) } if (this.isAnswer) { From 56ac7b57b07372941bddaa44f042da2d6ac23c77 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 22 Jul 2024 15:05:23 -0300 Subject: [PATCH 02/21] cleanup --- internal/e2e-js/tests/callfabric/renegotiation.spec.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 50c323f96..4579849e4 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -1,16 +1,10 @@ import { uuid } from '@signalwire/core' -import { Video } from '@signalwire/js' import { test, expect } from '../../fixtures' import { SERVER_URL, createCFClient, - expectLayoutChanged, - expectMCUVisible, getStats, - setLayoutOnPage, } from '../../utils' -import { Call } from 'packages/realtime-api/dist/realtime-api/src/voice/Voice' -import { callFabricWorker } from 'packages/js/src/fabric/workers' test.describe('CallFabric Renegotiation', () => { @@ -64,6 +58,7 @@ test.describe('CallFabric Renegotiation', () => { // expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) await page.evaluate(async () => { + //@ts-ignore window._roomObj. }); }) From d312dc76fa7bb412db7c7e9cc832d4b778e33285 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Wed, 11 Sep 2024 14:44:58 -0300 Subject: [PATCH 03/21] test hacking renegotiation passing --- .../tests/callfabric/renegotiation.spec.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 4579849e4..c520e11e0 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -5,6 +5,8 @@ import { createCFClient, getStats, } from '../../utils' +import { CallFabricRoomSession } from '@signalwire/js' +import RTCPeer from 'packages/webrtc/src/RTCPeer' test.describe('CallFabric Renegotiation', () => { @@ -35,6 +37,8 @@ test.describe('CallFabric Renegotiation', () => { call.on('room.joined', resolve) call.on('room.updated', () => {}) + + // @ts-expect-error window._roomObj = call @@ -51,15 +55,29 @@ test.describe('CallFabric Renegotiation', () => { ) ).toBeTruthy() - const stats = await getStats(page) + await page.waitForTimeout(1000) + + let stats = await getStats(page) - // expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.outboundRTP).not.toHaveProperty('video') - // expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) + expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) await page.evaluate(async () => { - //@ts-ignore - window._roomObj. + // @ts-expect-error + const cfRoomSession = (window._roomObj as CallFabricRoomSession) + // @ts-expect-error + cfRoomSession.updateMediaOptions({video: true, negotiateVideo: true}); + setTimeout(() => { + // @ts-expect-error + (cfRoomSession.peer as RTCPeer).start() + }) }); + + await page.waitForTimeout(1000) + + stats = await getStats(page) + + expect(stats.outboundRTP).toHaveProperty('video') }) }) From f1cfd61c4a22c5d6f315dadbcc438b03ac8797a4 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Wed, 2 Oct 2024 15:26:04 -0300 Subject: [PATCH 04/21] added renegotiateMedia --- .../tests/callfabric/renegotiation.spec.ts | 146 +++++++++++++++++- packages/webrtc/src/BaseConnection.ts | 22 ++- packages/webrtc/src/utils/interfaces.ts | 2 + 3 files changed, 158 insertions(+), 12 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index c520e11e0..8c776bf46 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -3,14 +3,14 @@ import { test, expect } from '../../fixtures' import { SERVER_URL, createCFClient, + expectMCUVisible, getStats, } from '../../utils' import { CallFabricRoomSession } from '@signalwire/js' -import RTCPeer from 'packages/webrtc/src/RTCPeer' test.describe('CallFabric Renegotiation', () => { - test('should handle joining a room with audio channel only', async ({ + test('Joining a room with audio channel only and enable sendrecv video', async ({ createCustomPage, resource, }) => { @@ -60,6 +60,7 @@ test.describe('CallFabric Renegotiation', () => { let stats = await getStats(page) expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.inboundRTP).not.toHaveProperty('video') expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) @@ -67,11 +68,76 @@ test.describe('CallFabric Renegotiation', () => { // @ts-expect-error const cfRoomSession = (window._roomObj as CallFabricRoomSession) // @ts-expect-error - cfRoomSession.updateMediaOptions({video: true, negotiateVideo: true}); - setTimeout(() => { + await cfRoomSession.enableVideo(); + }); + + await page.waitForTimeout(1000) + + stats = await getStats(page) + + expect(stats.outboundRTP).toHaveProperty('video') + expect(stats.inboundRTP).toHaveProperty('video') + }) + + test('Joining a room with audio channel only and enable sendOnly video', async ({ + createCustomPage, + resource, + }) => { + const page = await createCustomPage({ name: '[page]' }) + await page.goto(SERVER_URL) + + const roomName = `e2e-video-room_${uuid()}` + await resource.createVideoRoomResource(roomName) + + await createCFClient(page) + + // Dial an address with audio only channel + const roomSession = await page.evaluate( + async ({ roomName }) => { + return new Promise(async (resolve, _reject) => { + // @ts-expect-error + const client = window._client + + const call = await client.dial({ + to: `/public/${roomName}?channel=audio`, + rootElement: document.getElementById('rootElement'), + }) + + call.on('room.joined', resolve) + call.on('room.updated', () => {}) + + + // @ts-expect-error - (cfRoomSession.peer as RTCPeer).start() + window._roomObj = call + + await call.start() }) + }, + { roomName } + ) + + expect(roomSession.room_session).toBeDefined() + expect( + roomSession.room_session.members.some( + (member: any) => member.member_id === roomSession.member_id + ) + ).toBeTruthy() + + await page.waitForTimeout(1000) + + let stats = await getStats(page) + + expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.inboundRTP).not.toHaveProperty('video') + + expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) + + await page.evaluate(async () => { + // @ts-expect-error + const cfRoomSession = (window._roomObj as CallFabricRoomSession) + // @ts-expect-error + await cfRoomSession.enableVideo({sendOnly: true}); }); await page.waitForTimeout(1000) @@ -79,5 +145,75 @@ test.describe('CallFabric Renegotiation', () => { stats = await getStats(page) expect(stats.outboundRTP).toHaveProperty('video') + expect(stats.inboundRTP).not.toHaveProperty('video') + }) + + test('Joining a room with audio channel only and enable recvOnly video', async ({ + createCustomPage, + resource, + }) => { + const page = await createCustomPage({ name: '[page]' }) + await page.goto(SERVER_URL) + + const roomName = `e2e-video-room_${uuid()}` + await resource.createVideoRoomResource(roomName) + + await createCFClient(page) + + // Dial an address with audio only channel + const roomSession = await page.evaluate( + async ({ roomName }) => { + return new Promise(async (resolve, _reject) => { + // @ts-expect-error + const client = window._client + + const call = await client.dial({ + to: `/public/${roomName}?channel=audio`, + rootElement: document.getElementById('rootElement'), + }) + + call.on('room.joined', resolve) + call.on('room.updated', () => {}) + + + + // @ts-expect-error + window._roomObj = call + + await call.start() + }) + }, + { roomName } + ) + + expect(roomSession.room_session).toBeDefined() + expect( + roomSession.room_session.members.some( + (member: any) => member.member_id === roomSession.member_id + ) + ).toBeTruthy() + + await page.waitForTimeout(1000) + + let stats = await getStats(page) + + expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.inboundRTP).not.toHaveProperty('video') + + expect(stats.inboundRTP.audio.packetsReceived).toBeGreaterThan(0) + + await page.evaluate(async () => { + // @ts-expect-error + const cfRoomSession = (window._roomObj as CallFabricRoomSession) + // @ts-expect-error + await cfRoomSession.enableVideo({video: false, negotiateVideo: true}); + }); + + await page.waitForTimeout(1000) + + stats = await getStats(page) + + expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.inboundRTP).toHaveProperty('video') }) }) diff --git a/packages/webrtc/src/BaseConnection.ts b/packages/webrtc/src/BaseConnection.ts index 3e316f165..9c97a1fa0 100644 --- a/packages/webrtc/src/BaseConnection.ts +++ b/packages/webrtc/src/BaseConnection.ts @@ -21,7 +21,7 @@ import { } from '@signalwire/core' import type { ReduxComponent } from '@signalwire/core' import RTCPeer from './RTCPeer' -import { ConnectionOptions } from './utils/interfaces' +import { ConnectionOptions, UpdateMediaOptions } from './utils/interfaces' import { stopTrack, getUserMedia, streamIsValid } from './utils' import { sdpRemoveLocalCandidates } from './utils/sdpHelpers' import * as workers from './workers' @@ -987,12 +987,7 @@ export class BaseConnection } /** @internal */ - updateMediaOptions(options: { - audio?: boolean - video?: boolean - negotiateAudio?: boolean - negotiateVideo?: boolean - }) { + updateMediaOptions(options: UpdateMediaOptions) { this.logger.debug('updateMediaOptions', { ...options }) this.options = { ...this.options, @@ -1086,4 +1081,17 @@ export class BaseConnection }) this.rtcPeerMap.clear() } + + async renegotiateMedia(renegotiateMediaParams: UpdateMediaOptions): Promise { + this.updateMediaOptions(renegotiateMediaParams) + await this.peer?.start() + } + + async enableVideo(enableVideoParam?: Pick & {sendOnly?: boolean}): Promise { + await this.renegotiateMedia({video: enableVideoParam?.video ?? true, negotiateVideo: !enableVideoParam?.sendOnly}) + } + + async disableVideo(disableVideoParam?: {recvOnly?: boolean}): Promise { + await this.renegotiateMedia({video: false, negotiateVideo: disableVideoParam?.recvOnly}) + } } diff --git a/packages/webrtc/src/utils/interfaces.ts b/packages/webrtc/src/utils/interfaces.ts index 41bb70d2a..8c3db5e23 100644 --- a/packages/webrtc/src/utils/interfaces.ts +++ b/packages/webrtc/src/utils/interfaces.ts @@ -96,3 +96,5 @@ export interface ConnectionOptions { layout?: string positions?: VideoPositions } + +export type UpdateMediaOptions = Pick \ No newline at end of file From 96b0c42291f01235d353703c6803e9a0d08ffe5e Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 14 Oct 2024 13:07:17 -0300 Subject: [PATCH 05/21] fix getMediaConstraints --- packages/webrtc/src/RTCPeer.ts | 9 +++-- packages/webrtc/src/utils/helpers.test.ts | 26 ++++++------ packages/webrtc/src/utils/helpers.ts | 49 +++++++++++++++-------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 641b5f028..b74efa496 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -507,7 +507,11 @@ export default class RTCPeer { } } - async start() { + async start(offering=false) { + if(offering) { // allow callee to send renegotiation offers + this.options.remoteSdp = undefined + this.type = 'offer' + } return new Promise(async (resolve, reject) => { this._resolveStartMethod = resolve this._rejectStartMethod = reject @@ -827,8 +831,7 @@ export default class RTCPeer { if (streamIsValid(this.options.localStream)) { return this.options.localStream } - const remoteSDP = this.remoteSdp ?? this.options.remoteSdp - const constraints = await getMediaConstraints(this.options, remoteSDP) + const constraints = await getMediaConstraints(this.options) return getUserMedia(constraints) } diff --git a/packages/webrtc/src/utils/helpers.test.ts b/packages/webrtc/src/utils/helpers.test.ts index f9fdfa989..70534fe9f 100644 --- a/packages/webrtc/src/utils/helpers.test.ts +++ b/packages/webrtc/src/utils/helpers.test.ts @@ -46,17 +46,17 @@ describe('Helpers functions', () => { 'v=0\r\no=FreeSWITCH 1707233696 1707233697 IN IP4 190.102.98.211\r\ns=FreeSWITCH\r\nc=IN IP4 190.102.98.211\r\nt=0 0\r\na=msid-semantic: WMS xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\nm=audio 19828 RTP/SAVPF 0 8 102\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:102 opus/48000/2\r\na=fmtp:102 useinbandfec=1; maxaveragebitrate=30000; maxplaybackrate=48000; ptime=20; minptime=10; maxptime=40\r\na=fingerprint:sha-256 0F:F7:47:2D:19:38:46:88:E7:42:2A:4B:53:53:F5:19:1B:DC:EF:8E:14:F7:44:79:ED:94:A7:1B:97:92:7F:C5\r\na=setup:actpass\r\na=rtcp-mux\r\na=rtcp:19828 IN IP4 190.102.98.211\r\na=ssrc:4043346828 cname:rhPWOFid3mVMmndP\r\na=ssrc:4043346828 msid:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo a0\r\na=ssrc:4043346828 mslabel:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\na=ssrc:4043346828 label:xXtAEH0vyxeST9BACBkvRkF55amZ0EYoa0\r\na=ice-ufrag:OnbwxGrtGEix86Mq\r\na=ice-pwd:drdSXmVQzHtLVwrAKsW8Yerv\r\na=candidate:1409144412 1 udp 2130706431 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 1 udp 2130706431 172.17.0.2 19828 typ host generation 0\r\na=candidate:1409144412 2 udp 2130706430 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 2 udp 2130706430 172.17.0.2 19828 typ host generation 0\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\n' it('should return audio === true', async () => { - const mediaConstraints = await getMediaConstraints({}, SDP) + const mediaConstraints = await getMediaConstraints({remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(true) }) it('should return audio === {}', async () => { - const mediaConstraints = await getMediaConstraints({audio: {}}, SDP) + const mediaConstraints = await getMediaConstraints({audio: {}, remoteSdp: SDP}) expect(mediaConstraints.audio).toEqual({}) }) it('should return audio === {deviceId: { exact: "abcd" }}', async () => { - const mediaConstraints = await getMediaConstraints({micId: 'abcd'}, SDP) + const mediaConstraints = await getMediaConstraints({micId: 'abcd', remoteSdp: SDP}) expect(mediaConstraints.audio).toEqual({deviceId: { exact: "abcd" }}) }) }) @@ -66,17 +66,17 @@ describe('Helpers functions', () => { 'v=0\r\no=FreeSWITCH 1707233696 1707233697 IN IP4 190.102.98.211\r\ns=FreeSWITCH\r\nc=IN IP4 190.102.98.211\r\nt=0 0\r\na=msid-semantic: WMS xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\nm=video 19828 RTP/SAVPF 0 8 102\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:102 opus/48000/2\r\na=fmtp:102 useinbandfec=1; maxaveragebitrate=30000; maxplaybackrate=48000; ptime=20; minptime=10; maxptime=40\r\na=fingerprint:sha-256 0F:F7:47:2D:19:38:46:88:E7:42:2A:4B:53:53:F5:19:1B:DC:EF:8E:14:F7:44:79:ED:94:A7:1B:97:92:7F:C5\r\na=setup:actpass\r\na=rtcp-mux\r\na=rtcp:19828 IN IP4 190.102.98.211\r\na=ssrc:4043346828 cname:rhPWOFid3mVMmndP\r\na=ssrc:4043346828 msid:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo a0\r\na=ssrc:4043346828 mslabel:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\na=ssrc:4043346828 label:xXtAEH0vyxeST9BACBkvRkF55amZ0EYoa0\r\na=ice-ufrag:OnbwxGrtGEix86Mq\r\na=ice-pwd:drdSXmVQzHtLVwrAKsW8Yerv\r\na=candidate:1409144412 1 udp 2130706431 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 1 udp 2130706431 172.17.0.2 19828 typ host generation 0\r\na=candidate:1409144412 2 udp 2130706430 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 2 udp 2130706430 172.17.0.2 19828 typ host generation 0\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\n' it('should return audio === true', async () => { - const mediaConstraints = await getMediaConstraints({}, SDP) + const mediaConstraints = await getMediaConstraints({remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(false) }) it('should return audio === {}', async () => { - const mediaConstraints = await getMediaConstraints({audio: {}}, SDP) + const mediaConstraints = await getMediaConstraints({audio: {}, remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(false) }) it('should return audio === {deviceId: { exact: "abcd" }}', async () => { - const mediaConstraints = await getMediaConstraints({micId: 'abcd'}, SDP) + const mediaConstraints = await getMediaConstraints({micId: 'abcd', remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(false) }) }) @@ -86,17 +86,17 @@ describe('Helpers functions', () => { 'v=0\r\no=FreeSWITCH 1707233696 1707233697 IN IP4 190.102.98.211\r\ns=FreeSWITCH\r\nc=IN IP4 190.102.98.211\r\nt=0 0\r\na=msid-semantic: WMS xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\nm=audio 19828 RTP/SAVPF 0 8 102\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:102 opus/48000/2\r\na=fmtp:102 useinbandfec=1; maxaveragebitrate=30000; maxplaybackrate=48000; ptime=20; minptime=10; maxptime=40\r\na=fingerprint:sha-256 0F:F7:47:2D:19:38:46:88:E7:42:2A:4B:53:53:F5:19:1B:DC:EF:8E:14:F7:44:79:ED:94:A7:1B:97:92:7F:C5\r\na=setup:actpass\r\na=rtcp-mux\r\na=rtcp:19828 IN IP4 190.102.98.211\r\na=ssrc:4043346828 cname:rhPWOFid3mVMmndP\r\na=ssrc:4043346828 msid:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo a0\r\na=ssrc:4043346828 mslabel:xXtAEH0vyxeST9BACBkvRkF55amZ0EYo\r\na=ssrc:4043346828 label:xXtAEH0vyxeST9BACBkvRkF55amZ0EYoa0\r\na=ice-ufrag:OnbwxGrtGEix86Mq\r\na=ice-pwd:drdSXmVQzHtLVwrAKsW8Yerv\r\na=candidate:1409144412 1 udp 2130706431 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 1 udp 2130706431 172.17.0.2 19828 typ host generation 0\r\na=candidate:1409144412 2 udp 2130706430 190.102.98.211 19828 typ srflx raddr 172.17.0.2 rport 19828 generation 0\r\na=candidate:7363643456 2 udp 2130706430 172.17.0.2 19828 typ host generation 0\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\n' it('should return video === false, case 1', async () => { - const mediaConstraints = await getMediaConstraints({}, SDP) + const mediaConstraints = await getMediaConstraints({remoteSdp: SDP}) expect(mediaConstraints.video).toStrictEqual(false) }) it('should return video === false , case 2', async () => { - const mediaConstraints = await getMediaConstraints({audio: {}}, SDP) + const mediaConstraints = await getMediaConstraints({audio: {}, remoteSdp: SDP}) expect(mediaConstraints.video).toStrictEqual(false) }) it('should return audio === video === false , case 3', async () => { - const mediaConstraints = await getMediaConstraints({camId: 'abcd'}, SDP) + const mediaConstraints = await getMediaConstraints({camId: 'abcd', remoteSdp: SDP}) expect(mediaConstraints.video).toStrictEqual(false) }) }) @@ -106,25 +106,25 @@ describe('Helpers functions', () => { 'v=0\r\no=- 8094323291162995063 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS 45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU\r\nm=audio 51609 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 110 112 113 126\r\nc=IN IP4 172.17.0.5\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=candidate:528442011 1 udp 2122260223 192.168.1.12 52783 typ host generation 0 network-id 1 network-cost 10\r\na=candidate:3788879375 1 tcp 1518280447 192.168.1.12 9 typ host tcptype active generation 0 network-id 1 network-cost 10\r\na=candidate:511643837 1 udp 1686052607 37.118.148.114 52783 typ srflx raddr 192.168.1.12 rport 52783 generation 0 network-id 1 network-cost 10\r\na=candidate:427329035 1 udp 25108479 172.17.0.5 51609 typ relay raddr 172.17.0.6 rport 49152 generation 0 network-id 1 network-cost 10\r\na=ice-ufrag:Yoii\r\na=ice-pwd:uMmennPss4DGhOvNYiKxQT7w\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C4:62:01:34:2C:20:32:37:00:BE:DD:40:E7:03:DA:0E:57:A0:EB:30:DD:BD:98:20:11:3B:1C:00:FD:A6:3D:37\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendrecv\r\na=msid:45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU 29e5d7e5-de01-4058-b202-929b7e454469\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:112 telephone-event/32000\r\na=rtpmap:113 telephone-event/16000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:335962309 cname:7wjKGH97nM78eMmS\r\na=ssrc:335962309 msid:45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU 29e5d7e5-de01-4058-b202-929b7e454469\r\nm=video 52560 UDP/TLS/RTP/SAVPF 96 97 102 122 127 121 125 107 108 109 124 120 39 40 45 46 98 99 100 101 123 119 114 115 116\r\nc=IN IP4 172.17.0.5\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=candidate:528442011 1 udp 2122260223 192.168.1.12 52673 typ host generation 0 network-id 1 network-cost 10\r\na=candidate:3788879375 1 tcp 1518280447 192.168.1.12 9 typ host tcptype active generation 0 network-id 1 network-cost 10\r\na=candidate:511643837 1 udp 1686052607 37.118.148.114 52673 typ srflx raddr 192.168.1.12 rport 52673 generation 0 network-id 1 network-cost 10\r\na=candidate:427329035 1 udp 25108479 172.17.0.5 52560 typ relay raddr 172.17.0.6 rport 49154 generation 0 network-id 1 network-cost 10\r\na=ice-ufrag:Yoii\r\na=ice-pwd:uMmennPss4DGhOvNYiKxQT7w\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C4:62:01:34:2C:20:32:37:00:BE:DD:40:E7:03:DA:0E:57:A0:EB:30:DD:BD:98:20:11:3B:1C:00:FD:A6:3D:37\r\na=setup:actpass\r\na=mid:1\r\na=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:13 urn:3gpp:video-orientation\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\na=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\na=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=sendrecv\r\na=msid:45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU 95f5bd7e-f301-4349-aa8d-c493812cd7b0\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:102 H264/90000\r\na=rtcp-fb:102 goog-remb\r\na=rtcp-fb:102 transport-cc\r\na=rtcp-fb:102 ccm fir\r\na=rtcp-fb:102 nack\r\na=rtcp-fb:102 nack pli\r\na=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\na=rtpmap:122 rtx/90000\r\na=fmtp:122 apt=102\r\na=rtpmap:127 H264/90000\r\na=rtcp-fb:127 goog-remb\r\na=rtcp-fb:127 transport-cc\r\na=rtcp-fb:127 ccm fir\r\na=rtcp-fb:127 nack\r\na=rtcp-fb:127 nack pli\r\na=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f\r\na=rtpmap:121 rtx/90000\r\na=fmtp:121 apt=127\r\na=rtpmap:125 H264/90000\r\na=rtcp-fb:125 goog-remb\r\na=rtcp-fb:125 transport-cc\r\na=rtcp-fb:125 ccm fir\r\na=rtcp-fb:125 nack\r\na=rtcp-fb:125 nack pli\r\na=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:107 rtx/90000\r\na=fmtp:107 apt=125\r\na=rtpmap:108 H264/90000\r\na=rtcp-fb:108 goog-remb\r\na=rtcp-fb:108 transport-cc\r\na=rtcp-fb:108 ccm fir\r\na=rtcp-fb:108 nack\r\na=rtcp-fb:108 nack pli\r\na=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f\r\na=rtpmap:109 rtx/90000\r\na=fmtp:109 apt=108\r\na=rtpmap:124 H264/90000\r\na=rtcp-fb:124 goog-remb\r\na=rtcp-fb:124 transport-cc\r\na=rtcp-fb:124 ccm fir\r\na=rtcp-fb:124 nack\r\na=rtcp-fb:124 nack pli\r\na=fmtp:124 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f\r\na=rtpmap:120 rtx/90000\r\na=fmtp:120 apt=124\r\na=rtpmap:39 H264/90000\r\na=rtcp-fb:39 goog-remb\r\na=rtcp-fb:39 transport-cc\r\na=rtcp-fb:39 ccm fir\r\na=rtcp-fb:39 nack\r\na=rtcp-fb:39 nack pli\r\na=fmtp:39 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f\r\na=rtpmap:40 rtx/90000\r\na=fmtp:40 apt=39\r\na=rtpmap:45 AV1/90000\r\na=rtcp-fb:45 goog-remb\r\na=rtcp-fb:45 transport-cc\r\na=rtcp-fb:45 ccm fir\r\na=rtcp-fb:45 nack\r\na=rtcp-fb:45 nack pli\r\na=rtpmap:46 rtx/90000\r\na=fmtp:46 apt=45\r\na=rtpmap:98 VP9/90000\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-fb:98 transport-cc\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=fmtp:98 profile-id=0\r\na=rtpmap:99 rtx/90000\r\na=fmtp:99 apt=98\r\na=rtpmap:100 VP9/90000\r\na=rtcp-fb:100 goog-remb\r\na=rtcp-fb:100 transport-cc\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 nack pli\r\na=fmtp:100 profile-id=2\r\na=rtpmap:101 rtx/90000\r\na=fmtp:101 apt=100\r\na=rtpmap:123 H264/90000\r\na=rtcp-fb:123 goog-remb\r\na=rtcp-fb:123 transport-cc\r\na=rtcp-fb:123 ccm fir\r\na=rtcp-fb:123 nack\r\na=rtcp-fb:123 nack pli\r\na=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f\r\na=rtpmap:119 rtx/90000\r\na=fmtp:119 apt=123\r\na=rtpmap:114 red/90000\r\na=rtpmap:115 rtx/90000\r\na=fmtp:115 apt=114\r\na=rtpmap:116 ulpfec/90000\r\na=ssrc-group:FID 3973975883 3471156669\r\na=ssrc:3973975883 cname:7wjKGH97nM78eMmS\r\na=ssrc:3973975883 msid:45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU 95f5bd7e-f301-4349-aa8d-c493812cd7b0\r\na=ssrc:3471156669 cname:7wjKGH97nM78eMmS\r\na=ssrc:3471156669 msid:45Xh7kvyxccAi1fP6gpacCd2XY5IPfmp9zkU 95f5bd7e-f301-4349-aa8d-c493812cd7b0\r\n' it('should return audio === true & video === false', async () => { - const mediaConstraints = await getMediaConstraints({}, SDP) + const mediaConstraints = await getMediaConstraints({remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(true) expect(mediaConstraints.video).toStrictEqual(false) }) it('should return audio === true & video === true', async () => { - const mediaConstraints = await getMediaConstraints({video: true}, SDP) + const mediaConstraints = await getMediaConstraints({video: true, remoteSdp: SDP}) expect(mediaConstraints.audio).toStrictEqual(true) expect(mediaConstraints.video).toStrictEqual(true) }) it('should return audio === {} & video === {}', async () => { - const mediaConstraints = await getMediaConstraints({audio: {}, video: {}}, SDP) + const mediaConstraints = await getMediaConstraints({audio: {}, video: {}, remoteSdp: SDP}) expect(mediaConstraints.audio).toEqual({}) expect(mediaConstraints.video).toEqual({}) }) it('should return audio === {deviceId: { exact: "abcd" }} & video === {deviceId: { exact: "abcd" }}' , async () => { - const mediaConstraints = await getMediaConstraints({micId: 'abcd', camId: 'abcd'}, SDP) + const mediaConstraints = await getMediaConstraints({micId: 'abcd', camId: 'abcd', remoteSdp: SDP}) expect(mediaConstraints.audio).toEqual({deviceId: { exact: "abcd" }}) expect(mediaConstraints.video).toEqual({deviceId: { exact: "abcd" }}) }) diff --git a/packages/webrtc/src/utils/helpers.ts b/packages/webrtc/src/utils/helpers.ts index d834da213..8b47fe4dc 100644 --- a/packages/webrtc/src/utils/helpers.ts +++ b/packages/webrtc/src/utils/helpers.ts @@ -15,17 +15,38 @@ export const getUserMedia = (constraints: MediaStreamConstraints) => { return _getUserMedia(constraints) } +const _shouldNegotiateVideo = (options: ConnectionOptions) => { + return ( + (options.negotiateVideo ?? true) && + (!options.remoteSdp || + hasMediaSection(options.remoteSdp, 'video')) + ) +} + +const _shouldNegotiateAudio = (options: ConnectionOptions) => { + return ( + (options.negotiateAudio ?? true) && + (!options.remoteSdp || + hasMediaSection(options.remoteSdp, 'audio')) + ) +} + +const _getVideoConstraints = (options: ConnectionOptions) => { + return _shouldNegotiateVideo(options) ? options.video ?? !!options.camId + : false +} + +const _getAudioConstraints = (options: ConnectionOptions) => { + return _shouldNegotiateAudio(options) ? options.audio ?? true + : false +} + export const getMediaConstraints = async ( - options: ConnectionOptions, - remoteSDP?: string + options: ConnectionOptions ): Promise => { - const { audio: optionsAudio, micId } = options - const { micLabel = '' } = options - let audio: boolean | MediaTrackConstraints = - (remoteSDP && hasMediaSection(remoteSDP, 'audio')) || !remoteSDP - ? optionsAudio ?? true - : false //should not request audio when the remote SDP don't accept audio - + let audio = _getAudioConstraints(options) + const { micLabel = '', micId } = options + if (micId && audio) { const newMicId = await assureDeviceId(micId, micLabel, 'microphone').catch( (_error) => null @@ -38,13 +59,9 @@ export const getMediaConstraints = async ( } } - let { video: optionsVideo, camId } = options - const { camLabel = '' } = options - let video: boolean | MediaTrackConstraints = - (remoteSDP && hasMediaSection(remoteSDP, 'video')) || !remoteSDP - ? optionsVideo ?? !!camId - : false //should not request video when the remote SDP don't accept video - + let video = _getVideoConstraints(options) + const { camLabel = '', camId } = options + if (camId && video) { const newCamId = await assureDeviceId(camId, camLabel, 'camera').catch( (_error) => null From 02b2d5ce1a96f52a0263f17b17b67dc6dcb3edd1 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 14 Oct 2024 13:40:04 -0300 Subject: [PATCH 06/21] start with offering true --- packages/webrtc/src/BaseConnection.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/webrtc/src/BaseConnection.ts b/packages/webrtc/src/BaseConnection.ts index 9c97a1fa0..7222b323c 100644 --- a/packages/webrtc/src/BaseConnection.ts +++ b/packages/webrtc/src/BaseConnection.ts @@ -1084,7 +1084,8 @@ export class BaseConnection async renegotiateMedia(renegotiateMediaParams: UpdateMediaOptions): Promise { this.updateMediaOptions(renegotiateMediaParams) - await this.peer?.start() + //starts with offering = true + await this.peer?.start(true) } async enableVideo(enableVideoParam?: Pick & {sendOnly?: boolean}): Promise { From 12497777cedf5beb704d8c8d69f33f0a65dbae63 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 14 Oct 2024 13:49:33 -0300 Subject: [PATCH 07/21] check MCU --- internal/e2e-js/tests/callfabric/renegotiation.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 8c776bf46..0b50b30d5 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -213,6 +213,8 @@ test.describe('CallFabric Renegotiation', () => { stats = await getStats(page) + expectMCUVisible(page) + expect(stats.outboundRTP).not.toHaveProperty('video') expect(stats.inboundRTP).toHaveProperty('video') }) From b7fcd794c831944db2901129114be28b619fa2fd Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 14 Oct 2024 13:57:44 -0300 Subject: [PATCH 08/21] changeset --- .changeset/sweet-garlics-tease.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/sweet-garlics-tease.md diff --git a/.changeset/sweet-garlics-tease.md b/.changeset/sweet-garlics-tease.md new file mode 100644 index 000000000..da98a9211 --- /dev/null +++ b/.changeset/sweet-garlics-tease.md @@ -0,0 +1,5 @@ +--- +'@signalwire/webrtc': minor +--- + +Added public APIs for media renegotiation From 57089195b9842099551036cd117f4990336ac97a Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Tue, 15 Oct 2024 14:30:06 -0300 Subject: [PATCH 09/21] fixes --- .../e2e-js/tests/callfabric/renegotiation.spec.ts | 15 ++++++++++----- packages/webrtc/src/RTCPeer.ts | 13 ++++++------- packages/webrtc/src/utils/helpers.ts | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 0b50b30d5..7035266ff 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -4,6 +4,7 @@ import { SERVER_URL, createCFClient, expectMCUVisible, + expectMCUVisibleForAudience, getStats, } from '../../utils' import { CallFabricRoomSession } from '@signalwire/js' @@ -58,7 +59,7 @@ test.describe('CallFabric Renegotiation', () => { await page.waitForTimeout(1000) let stats = await getStats(page) - +0 expect(stats.outboundRTP).not.toHaveProperty('video') expect(stats.inboundRTP).not.toHaveProperty('video') @@ -71,12 +72,15 @@ test.describe('CallFabric Renegotiation', () => { await cfRoomSession.enableVideo(); }); - await page.waitForTimeout(1000) + await page.waitForTimeout(2000) stats = await getStats(page) expect(stats.outboundRTP).toHaveProperty('video') expect(stats.inboundRTP).toHaveProperty('video') + + await page.waitForTimeout(2000) + expectMCUVisible(page) }) test('Joining a room with audio channel only and enable sendOnly video', async ({ @@ -206,16 +210,17 @@ test.describe('CallFabric Renegotiation', () => { // @ts-expect-error const cfRoomSession = (window._roomObj as CallFabricRoomSession) // @ts-expect-error - await cfRoomSession.enableVideo({video: false, negotiateVideo: true}); + await cfRoomSession.enableVideo({video: false, sendOnly: false}); }); await page.waitForTimeout(1000) stats = await getStats(page) - expectMCUVisible(page) - expect(stats.outboundRTP).not.toHaveProperty('video') expect(stats.inboundRTP).toHaveProperty('video') + + await page.waitForTimeout(1000) + expectMCUVisibleForAudience(page) }) }) diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 4b8c9e8a5..41dc707e6 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -422,13 +422,7 @@ export default class RTCPeer { offerOptions.offerToReceiveAudio = this.options.negotiateAudio offerOptions.offerToReceiveVideo = this.options.negotiateVideo } - console.log('##############################') - console.log('##############################') - console.log('##############################') - console.log(JSON.stringify(offerOptions, null, 2)) - console.log('##############################') - console.log('##############################') - console.log('##############################') + const offer = await this.instance.createOffer(offerOptions) console.log(offer.sdp) await this._setLocalDescription(offer) @@ -525,6 +519,7 @@ export default class RTCPeer { this._rejectStartMethod = reject try { + this._disposeLocalStream() this._localStream = await this._retrieveLocalStream() } catch (error) { this._rejectStartMethod(error) @@ -628,6 +623,10 @@ export default class RTCPeer { } }) } + _disposeLocalStream() { + this._localStream?.getTracks().forEach((track) => track.stop()) + this._localStream = undefined + } detachAndStop() { if (typeof this.instance?.getTransceivers === 'function') { diff --git a/packages/webrtc/src/utils/helpers.ts b/packages/webrtc/src/utils/helpers.ts index 8b47fe4dc..9f4400cdc 100644 --- a/packages/webrtc/src/utils/helpers.ts +++ b/packages/webrtc/src/utils/helpers.ts @@ -17,7 +17,7 @@ export const getUserMedia = (constraints: MediaStreamConstraints) => { const _shouldNegotiateVideo = (options: ConnectionOptions) => { return ( - (options.negotiateVideo ?? true) && + (options.negotiateVideo || (options.video ?? !!options.camId)) && (!options.remoteSdp || hasMediaSection(options.remoteSdp, 'video')) ) From 46b833c9921982f43f6c15ab236c40debdb75541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Barbosa=20Marques=20dos=20Santos?= Date: Mon, 21 Oct 2024 11:45:54 -0300 Subject: [PATCH 10/21] Update internal/e2e-js/tests/callfabric/renegotiation.spec.ts Co-authored-by: Ammar Ansari --- .../tests/callfabric/renegotiation.spec.ts | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 7035266ff..32774d520 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -24,30 +24,9 @@ test.describe('CallFabric Renegotiation', () => { await createCFClient(page) // Dial an address with audio only channel - const roomSession = await page.evaluate( - async ({ roomName }) => { - return new Promise(async (resolve, _reject) => { - // @ts-expect-error - const client = window._client - - const call = await client.dial({ - to: `/public/${roomName}?channel=audio`, - rootElement: document.getElementById('rootElement'), - }) - - call.on('room.joined', resolve) - call.on('room.updated', () => {}) - - - - // @ts-expect-error - window._roomObj = call - - await call.start() - }) - }, - { roomName } - ) + const roomSession = await dialAddress(page, { + address: `/public/${roomName}?channel=audio`, + }) expect(roomSession.room_session).toBeDefined() expect( From cbdacc0a2259c9f872d6ac41526396f149b95acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Barbosa=20Marques=20dos=20Santos?= Date: Mon, 21 Oct 2024 11:48:11 -0300 Subject: [PATCH 11/21] Update packages/webrtc/src/RTCPeer.ts Co-authored-by: Ammar Ansari --- packages/webrtc/src/RTCPeer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 41dc707e6..2f38cb6f3 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -424,7 +424,6 @@ export default class RTCPeer { } const offer = await this.instance.createOffer(offerOptions) - console.log(offer.sdp) await this._setLocalDescription(offer) } if (this.isAnswer) { From 3a0be5fa496e8c30cdbff3a22df4d99929407f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Barbosa=20Marques=20dos=20Santos?= Date: Mon, 21 Oct 2024 11:48:27 -0300 Subject: [PATCH 12/21] Update packages/webrtc/src/RTCPeer.ts Co-authored-by: Ammar Ansari --- packages/webrtc/src/RTCPeer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 2f38cb6f3..78b91b5a8 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -622,6 +622,7 @@ export default class RTCPeer { } }) } + _disposeLocalStream() { this._localStream?.getTracks().forEach((track) => track.stop()) this._localStream = undefined From dec5f504789f901a9b3e73963cfba0007ab15bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Barbosa=20Marques=20dos=20Santos?= Date: Mon, 21 Oct 2024 11:53:20 -0300 Subject: [PATCH 13/21] Update internal/e2e-js/tests/callfabric/renegotiation.spec.ts Co-authored-by: Ammar Ansari --- .../tests/callfabric/renegotiation.spec.ts | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 32774d520..3ac235eed 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -75,30 +75,9 @@ test.describe('CallFabric Renegotiation', () => { await createCFClient(page) // Dial an address with audio only channel - const roomSession = await page.evaluate( - async ({ roomName }) => { - return new Promise(async (resolve, _reject) => { - // @ts-expect-error - const client = window._client - - const call = await client.dial({ - to: `/public/${roomName}?channel=audio`, - rootElement: document.getElementById('rootElement'), - }) - - call.on('room.joined', resolve) - call.on('room.updated', () => {}) - - - - // @ts-expect-error - window._roomObj = call - - await call.start() - }) - }, - { roomName } - ) + const roomSession = await dialAddress(page, { + address: `/public/${roomName}?channel=audio`, + }) expect(roomSession.room_session).toBeDefined() expect( From e5485e71917012715ee3cc162382438c0b65c21b Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 21 Oct 2024 12:14:15 -0300 Subject: [PATCH 14/21] added to the public contract --- .../tests/callfabric/renegotiation.spec.ts | 7 +++--- .../js/src/utils/interfaces/callFabric.ts | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts index 3ac235eed..c12e6eb09 100644 --- a/internal/e2e-js/tests/callfabric/renegotiation.spec.ts +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -3,6 +3,7 @@ import { test, expect } from '../../fixtures' import { SERVER_URL, createCFClient, + dialAddress, expectMCUVisible, expectMCUVisibleForAudience, getStats, @@ -47,7 +48,7 @@ test.describe('CallFabric Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error const cfRoomSession = (window._roomObj as CallFabricRoomSession) - // @ts-expect-error + await cfRoomSession.enableVideo(); }); @@ -98,7 +99,7 @@ test.describe('CallFabric Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error const cfRoomSession = (window._roomObj as CallFabricRoomSession) - // @ts-expect-error + await cfRoomSession.enableVideo({sendOnly: true}); }); @@ -167,7 +168,7 @@ test.describe('CallFabric Renegotiation', () => { await page.evaluate(async () => { // @ts-expect-error const cfRoomSession = (window._roomObj as CallFabricRoomSession) - // @ts-expect-error + await cfRoomSession.enableVideo({video: false, sendOnly: false}); }); diff --git a/packages/js/src/utils/interfaces/callFabric.ts b/packages/js/src/utils/interfaces/callFabric.ts index d5fa90ea9..a9eddb915 100644 --- a/packages/js/src/utils/interfaces/callFabric.ts +++ b/packages/js/src/utils/interfaces/callFabric.ts @@ -1,4 +1,5 @@ import { VideoLayoutChangedEventParams } from '@signalwire/core' +import { UpdateMediaOptions } from '@signalwire/webrtc' export interface CallFabricRoomSessionConnectionContract { /** @@ -33,4 +34,25 @@ export interface CallFabricRoomSessionConnectionContract { * ``` */ hangup(id?: string): Promise + + /** + * Renegotiate RTC media channels based on the new media constraints + * + * @param renegotiateMediaParams + */ + renegotiateMedia(renegotiateMediaParams: UpdateMediaOptions): Promise + + /** + * Convenience method to enable video in a call + * + * @param enableVideoParam + */ + enableVideo(enableVideoParam?: Pick & {sendOnly?: boolean}): Promise + + /** + * Convenience method to disable video in a call + * + * @param enableVideoParam + */ + disableVideo(disableVideoParam?: {recvOnly?: boolean}): Promise } From 62319301bf8f63050721185e78f4fd2c979e6720 Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 21 Oct 2024 12:20:46 -0300 Subject: [PATCH 15/21] refactor --- packages/webrtc/src/BaseConnection.ts | 3 +-- packages/webrtc/src/RTCPeer.ts | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/webrtc/src/BaseConnection.ts b/packages/webrtc/src/BaseConnection.ts index 7222b323c..e6f9fb652 100644 --- a/packages/webrtc/src/BaseConnection.ts +++ b/packages/webrtc/src/BaseConnection.ts @@ -1084,8 +1084,7 @@ export class BaseConnection async renegotiateMedia(renegotiateMediaParams: UpdateMediaOptions): Promise { this.updateMediaOptions(renegotiateMediaParams) - //starts with offering = true - await this.peer?.start(true) + await this.peer?.start({isRenegotiate: true}) } async enableVideo(enableVideoParam?: Pick & {sendOnly?: boolean}): Promise { diff --git a/packages/webrtc/src/RTCPeer.ts b/packages/webrtc/src/RTCPeer.ts index 78b91b5a8..b5747f3da 100644 --- a/packages/webrtc/src/RTCPeer.ts +++ b/packages/webrtc/src/RTCPeer.ts @@ -508,11 +508,13 @@ export default class RTCPeer { } } - async start(offering=false) { - if(offering) { // allow callee to send renegotiation offers + async start(startParam?:{isRenegotiate:boolean}) { + const isRenegotiate = startParam?.isRenegotiate ?? false + if(isRenegotiate) { // allow callee to send renegotiation offers this.options.remoteSdp = undefined this.type = 'offer' } + return new Promise(async (resolve, reject) => { this._resolveStartMethod = resolve this._rejectStartMethod = reject From 2ca56ad6bbd97f2188e832dac01557983d84f1ef Mon Sep 17 00:00:00 2001 From: Joao Santos Date: Mon, 21 Oct 2024 13:39:23 -0300 Subject: [PATCH 16/21] add to playground --- internal/playground-js/src/fabric/index.html | 8 ++++++++ internal/playground-js/src/fabric/index.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/internal/playground-js/src/fabric/index.html b/internal/playground-js/src/fabric/index.html index eaab80699..8751df857 100644 --- a/internal/playground-js/src/fabric/index.html +++ b/internal/playground-js/src/fabric/index.html @@ -122,6 +122,14 @@
Connect
Controls
+ +