feat: Add TURN server support
All checks were successful
Queue Release Build / prepare (push) Successful in 15s
Deploy Web Apps / deploy (push) Successful in 5m35s
Queue Release Build / build-linux (push) Successful in 24m45s
Queue Release Build / build-windows (push) Successful in 13m52s
Queue Release Build / finalize (push) Successful in 23s
All checks were successful
Queue Release Build / prepare (push) Successful in 15s
Deploy Web Apps / deploy (push) Successful in 5m35s
Queue Release Build / build-linux (push) Successful in 24m45s
Queue Release Build / build-windows (push) Successful in 13m52s
Queue Release Build / finalize (push) Successful in 23s
This commit is contained in:
@@ -8,7 +8,8 @@ import {
|
||||
waitForVideoFlow,
|
||||
waitForOutboundVideoFlow,
|
||||
waitForInboundVideoFlow,
|
||||
dumpRtcDiagnostics
|
||||
dumpRtcDiagnostics,
|
||||
installAutoResumeAudioContext
|
||||
} from '../../helpers/webrtc-helpers';
|
||||
import { RegisterPage } from '../../pages/register.page';
|
||||
import { ServerSearchPage } from '../../pages/server-search.page';
|
||||
@@ -38,7 +39,7 @@ async function registerUser(page: import('@playwright/test').Page, user: typeof
|
||||
await expect(page).toHaveURL(/\/search/, { timeout: 15_000 });
|
||||
}
|
||||
|
||||
/** Both users register → Alice creates server → Bob joins. */
|
||||
/** Both users register -> Alice creates server -> Bob joins. */
|
||||
async function setupServerWithBothUsers(
|
||||
alice: { page: import('@playwright/test').Page },
|
||||
bob: { page: import('@playwright/test').Page }
|
||||
@@ -80,19 +81,45 @@ async function joinVoiceTogether(
|
||||
await expect(existingChannel).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
await aliceRoom.joinVoiceChannel(VOICE_CHANNEL);
|
||||
await expect(alice.page.locator('app-voice-controls')).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
const bobRoom = new ChatRoomPage(bob.page);
|
||||
const doJoin = async () => {
|
||||
await aliceRoom.joinVoiceChannel(VOICE_CHANNEL);
|
||||
await expect(alice.page.locator('app-voice-controls')).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
await bobRoom.joinVoiceChannel(VOICE_CHANNEL);
|
||||
await expect(bob.page.locator('app-voice-controls')).toBeVisible({ timeout: 15_000 });
|
||||
await bobRoom.joinVoiceChannel(VOICE_CHANNEL);
|
||||
await expect(bob.page.locator('app-voice-controls')).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
// Wait for WebRTC + audio pipeline
|
||||
await waitForPeerConnected(alice.page, 30_000);
|
||||
await waitForPeerConnected(bob.page, 30_000);
|
||||
await waitForAudioStatsPresent(alice.page, 20_000);
|
||||
await waitForAudioStatsPresent(bob.page, 20_000);
|
||||
// Wait for WebRTC + audio pipeline
|
||||
await waitForPeerConnected(alice.page, 30_000);
|
||||
await waitForPeerConnected(bob.page, 30_000);
|
||||
await waitForAudioStatsPresent(alice.page, 20_000);
|
||||
await waitForAudioStatsPresent(bob.page, 20_000);
|
||||
};
|
||||
|
||||
await doJoin();
|
||||
|
||||
// Chromium's --use-fake-device-for-media-stream can produce a silent
|
||||
// capture track on the very first getUserMedia call. If bidirectional
|
||||
// audio doesn't flow within a short window, leave and rejoin voice to
|
||||
// re-acquire the mic (second getUserMedia on a warm device works).
|
||||
const aliceDelta = await waitForAudioFlow(alice.page, 10_000);
|
||||
const bobDelta = await waitForAudioFlow(bob.page, 10_000);
|
||||
const aliceFlowing =
|
||||
(aliceDelta.outboundBytesDelta > 0 || aliceDelta.outboundPacketsDelta > 0) &&
|
||||
(aliceDelta.inboundBytesDelta > 0 || aliceDelta.inboundPacketsDelta > 0);
|
||||
const bobFlowing =
|
||||
(bobDelta.outboundBytesDelta > 0 || bobDelta.outboundPacketsDelta > 0) &&
|
||||
(bobDelta.inboundBytesDelta > 0 || bobDelta.inboundPacketsDelta > 0);
|
||||
|
||||
if (!aliceFlowing || !bobFlowing) {
|
||||
// Leave voice
|
||||
await aliceRoom.disconnectButton.click();
|
||||
await bobRoom.disconnectButton.click();
|
||||
await alice.page.waitForTimeout(2_000);
|
||||
|
||||
// Rejoin
|
||||
await doJoin();
|
||||
}
|
||||
|
||||
// Expand voice workspace on both clients so the demand-driven screen
|
||||
// share request flow can fire (requires connectRemoteShares = true).
|
||||
@@ -142,6 +169,20 @@ test.describe('Screen sharing', () => {
|
||||
|
||||
await installWebRTCTracking(alice.page);
|
||||
await installWebRTCTracking(bob.page);
|
||||
await installAutoResumeAudioContext(alice.page);
|
||||
await installAutoResumeAudioContext(bob.page);
|
||||
|
||||
// Seed deterministic voice settings so noise reduction doesn't
|
||||
// swallow the fake audio tone.
|
||||
const voiceSettings = JSON.stringify({
|
||||
inputVolume: 100, outputVolume: 100, audioBitrate: 96,
|
||||
latencyProfile: 'balanced', includeSystemAudio: false,
|
||||
noiseReduction: false, screenShareQuality: 'balanced',
|
||||
askScreenShareQuality: false
|
||||
});
|
||||
|
||||
await alice.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
await bob.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
|
||||
alice.page.on('console', msg => console.log('[Alice]', msg.text()));
|
||||
bob.page.on('console', msg => console.log('[Bob]', msg.text()));
|
||||
@@ -251,6 +292,18 @@ test.describe('Screen sharing', () => {
|
||||
|
||||
await installWebRTCTracking(alice.page);
|
||||
await installWebRTCTracking(bob.page);
|
||||
await installAutoResumeAudioContext(alice.page);
|
||||
await installAutoResumeAudioContext(bob.page);
|
||||
|
||||
const voiceSettings = JSON.stringify({
|
||||
inputVolume: 100, outputVolume: 100, audioBitrate: 96,
|
||||
latencyProfile: 'balanced', includeSystemAudio: false,
|
||||
noiseReduction: false, screenShareQuality: 'balanced',
|
||||
askScreenShareQuality: false
|
||||
});
|
||||
|
||||
await alice.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
await bob.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
|
||||
alice.page.on('console', msg => console.log('[Alice]', msg.text()));
|
||||
bob.page.on('console', msg => console.log('[Bob]', msg.text()));
|
||||
@@ -323,6 +376,18 @@ test.describe('Screen sharing', () => {
|
||||
|
||||
await installWebRTCTracking(alice.page);
|
||||
await installWebRTCTracking(bob.page);
|
||||
await installAutoResumeAudioContext(alice.page);
|
||||
await installAutoResumeAudioContext(bob.page);
|
||||
|
||||
const voiceSettings = JSON.stringify({
|
||||
inputVolume: 100, outputVolume: 100, audioBitrate: 96,
|
||||
latencyProfile: 'balanced', includeSystemAudio: false,
|
||||
noiseReduction: false, screenShareQuality: 'balanced',
|
||||
askScreenShareQuality: false
|
||||
});
|
||||
|
||||
await alice.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
await bob.page.addInitScript((settingsValue: string) => localStorage.setItem('metoyou_voice_settings', settingsValue), voiceSettings);
|
||||
|
||||
alice.page.on('console', msg => console.log('[Alice]', msg.text()));
|
||||
bob.page.on('console', msg => console.log('[Bob]', msg.text()));
|
||||
|
||||
Reference in New Issue
Block a user