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 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..c12e6eb09 --- /dev/null +++ b/internal/e2e-js/tests/callfabric/renegotiation.spec.ts @@ -0,0 +1,185 @@ +import { uuid } from '@signalwire/core' +import { test, expect } from '../../fixtures' +import { + SERVER_URL, + createCFClient, + dialAddress, + expectMCUVisible, + expectMCUVisibleForAudience, + getStats, +} from '../../utils' +import { CallFabricRoomSession } from '@signalwire/js' + +test.describe('CallFabric Renegotiation', () => { + + test('Joining a room with audio channel only and enable sendrecv 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 dialAddress(page, { + address: `/public/${roomName}?channel=audio`, + }) + + 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) +0 + 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) + + await cfRoomSession.enableVideo(); + }); + + 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 ({ + 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 dialAddress(page, { + address: `/public/${roomName}?channel=audio`, + }) + + 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) + + await cfRoomSession.enableVideo({sendOnly: true}); + }); + + await page.waitForTimeout(1000) + + 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) + + await cfRoomSession.enableVideo({video: false, sendOnly: false}); + }); + + await page.waitForTimeout(1000) + + stats = await getStats(page) + + expect(stats.outboundRTP).not.toHaveProperty('video') + expect(stats.inboundRTP).toHaveProperty('video') + + await page.waitForTimeout(1000) + expectMCUVisibleForAudience(page) + }) +}) diff --git a/internal/playground-js/src/fabric/index.html b/internal/playground-js/src/fabric/index.html index 2b6221c10..73b73b491 100644 --- a/internal/playground-js/src/fabric/index.html +++ b/internal/playground-js/src/fabric/index.html @@ -122,6 +122,14 @@
Connect
Controls
+ +