From 87562efbe5ea13fadc7a4c9609eddea20aae38a7 Mon Sep 17 00:00:00 2001 From: hyrious Date: Wed, 1 Nov 2023 17:38:35 +0800 Subject: [PATCH] fix(service-providers): stop using camera when leaving device test - fix: speaker id not saved correctly in device test page - refactor: make device test page load faster --- .../flat-pages/src/DevicesTestPage/index.tsx | 101 +++++++++--------- .../src/services/video-chat/video-chat.ts | 1 + .../src/agora-rtc-electron.ts | 4 + .../agora-rtc-web/src/agora-rtc-web.ts | 16 ++- .../agora-rtc-web/src/rtc-test-avatar.ts | 15 ++- 5 files changed, 81 insertions(+), 56 deletions(-) diff --git a/packages/flat-pages/src/DevicesTestPage/index.tsx b/packages/flat-pages/src/DevicesTestPage/index.tsx index bd222646cdd..de3613bd151 100644 --- a/packages/flat-pages/src/DevicesTestPage/index.tsx +++ b/packages/flat-pages/src/DevicesTestPage/index.tsx @@ -31,9 +31,13 @@ export const DevicesTestPage = observer(function DeviceTestPage() { const [microphoneDevices, setMicrophoneDevices] = useState([]); const [speakerDevices, setSpeakerDevices] = useState([]); - const [cameraDeviceId, setCameraDeviceId] = useState(""); - const [microphoneDeviceId, setMicrophoneDeviceId] = useState(""); - const [speakerDeviceId, setSpeakerDeviceId] = useState(""); + const [cameraDeviceId, setCameraDeviceId] = useState(preferencesStore.cameraId || ""); + const [microphoneDeviceId, setMicrophoneDeviceId] = useState( + preferencesStore.microphoneId || "", + ); + const [speakerDeviceId, setSpeakerDeviceId] = useState( + preferencesStore.speakerId || "", + ); const [isCameraAccessible, setIsCameraAccessible] = useState(true); const [isMicrophoneAccessible, setIsMicrophoneAccessible] = useState(true); @@ -41,11 +45,35 @@ export const DevicesTestPage = observer(function DeviceTestPage() { const [volume, setVolume] = useState(0); + // Make 'setDeviceID' happen first when the track was not created. + useEffect(() => { + if (rtc && cameraDeviceId) { + void rtc.setCameraID(cameraDeviceId).catch(() => { + setIsCameraAccessible(false); + }); + } + }, [rtc, cameraDeviceId]); + + useEffect(() => { + if (rtc && microphoneDeviceId) { + void rtc.setMicID(microphoneDeviceId).catch(() => { + setIsMicrophoneAccessible(false); + }); + } + }, [rtc, microphoneDeviceId]); + + useEffect(() => { + if (rtc && speakerDeviceId) { + void rtc.setSpeakerID(speakerDeviceId).catch(() => { + setIsSpeakerAccessible(false); + }); + } + }, [rtc, speakerDeviceId]); + useEffect(() => { if (!rtc) { return; } - // @FIXME only run once const avatar = rtc.getTestAvatar(); if (avatar) { avatar.enableCamera(true); @@ -62,6 +90,7 @@ export const DevicesTestPage = observer(function DeviceTestPage() { avatar.enableCamera(false); avatar.enableMic(false); avatar.setElement(null); + rtc.stopTesting(); }; } return; @@ -123,66 +152,33 @@ export const DevicesTestPage = observer(function DeviceTestPage() { }; }, [rtc, sp]); - useEffect(() => { - if (rtc && cameraDeviceId) { - void rtc.setCameraID(cameraDeviceId).catch(() => { - setIsCameraAccessible(false); - }); - } - }, [rtc, cameraDeviceId]); - - useEffect(() => { - if (rtc && microphoneDeviceId) { - void rtc.setMicID(microphoneDeviceId).catch(() => { - setIsMicrophoneAccessible(false); - }); - } - }, [rtc, microphoneDeviceId]); - - useEffect(() => { - if (rtc && speakerDeviceId) { - void rtc.setSpeakerID(speakerDeviceId).catch(() => { - setIsSpeakerAccessible(false); - }); - } - }, [rtc, speakerDeviceId]); - useEffect(() => { // check device id on changes - if (cameraDevices.length > 0 && !cameraDeviceId) { - const lastCameraId = preferencesStore.cameraId; - if (lastCameraId && cameraDevices.find(device => device.deviceId === lastCameraId)) { - setCameraDeviceId(lastCameraId); - } else { - setCameraDeviceId(cameraDevices[0].deviceId); - } + if ( + cameraDevices.length > 0 && + !cameraDevices.find(device => device.deviceId === cameraDeviceId) + ) { + setCameraDeviceId(cameraDevices[0].deviceId); } }, [preferencesStore, cameraDeviceId, cameraDevices]); useEffect(() => { // check device id on changes - if (microphoneDevices.length > 0 && !microphoneDeviceId) { - const lastMicrophoneId = preferencesStore.microphoneId; - if ( - lastMicrophoneId && - microphoneDevices.some(device => device.deviceId === lastMicrophoneId) - ) { - setMicrophoneDeviceId(lastMicrophoneId); - } else { - setMicrophoneDeviceId(microphoneDevices[0].deviceId); - } + if ( + microphoneDevices.length > 0 && + !microphoneDevices.some(device => device.deviceId === microphoneDeviceId) + ) { + setMicrophoneDeviceId(microphoneDevices[0].deviceId); } }, [preferencesStore, microphoneDeviceId, microphoneDevices]); useEffect(() => { // check device id on changes - if (speakerDevices.length > 0 && !speakerDeviceId) { - const lastSpeakerId = preferencesStore.speakerId; - if (lastSpeakerId && speakerDevices.some(device => device.deviceId === lastSpeakerId)) { - setSpeakerDeviceId(lastSpeakerId); - } else { - setSpeakerDeviceId(speakerDevices[0].deviceId); - } + if ( + speakerDevices.length > 0 && + !speakerDevices.some(device => device.deviceId === speakerDeviceId) + ) { + setSpeakerDeviceId(speakerDevices[0].deviceId); } }, [preferencesStore, speakerDeviceId, speakerDevices]); @@ -193,6 +189,7 @@ export const DevicesTestPage = observer(function DeviceTestPage() { }, [preferencesStore.mirrorMode, rtc]); const joinRoom = async (): Promise => { + preferencesStore.updateSpeakerId(speakerDeviceId); preferencesStore.updateCameraId(cameraDeviceId); preferencesStore.updateMicrophoneId(microphoneDeviceId); await joinRoomHandler(roomUUID, pushHistory); diff --git a/packages/flat-services/src/services/video-chat/video-chat.ts b/packages/flat-services/src/services/video-chat/video-chat.ts index a997a40919d..78ea444061b 100644 --- a/packages/flat-services/src/services/video-chat/video-chat.ts +++ b/packages/flat-services/src/services/video-chat/video-chat.ts @@ -56,6 +56,7 @@ export abstract class IServiceVideoChat implements IService { public abstract getAvatar(uid?: IServiceVideoChatUID): IServiceVideoChatAvatar | undefined; public abstract getTestAvatar(): IServiceVideoChatAvatar; + public abstract stopTesting(): void; public abstract getVolumeLevel(uid?: IServiceVideoChatUID): number; diff --git a/service-providers/agora-rtc/agora-rtc-electron/src/agora-rtc-electron.ts b/service-providers/agora-rtc/agora-rtc-electron/src/agora-rtc-electron.ts index 07d0082fdbe..999ff1b37ab 100644 --- a/service-providers/agora-rtc/agora-rtc-electron/src/agora-rtc-electron.ts +++ b/service-providers/agora-rtc/agora-rtc-electron/src/agora-rtc-electron.ts @@ -242,6 +242,10 @@ export class AgoraRTCElectron extends IServiceVideoChat { return this.localAvatar; } + public stopTesting(): void { + // do nothing + } + public override getVolumeLevel(uid?: IServiceVideoChatUID): number { return this._volumeLevels.get(uid || "0") || 0; } diff --git a/service-providers/agora-rtc/agora-rtc-web/src/agora-rtc-web.ts b/service-providers/agora-rtc/agora-rtc-web/src/agora-rtc-web.ts index d18bb6a80f3..4028fa60ac3 100644 --- a/service-providers/agora-rtc/agora-rtc-web/src/agora-rtc-web.ts +++ b/service-providers/agora-rtc/agora-rtc-web/src/agora-rtc-web.ts @@ -210,7 +210,15 @@ export class AgoraRTCWeb extends IServiceVideoChat { private _testAvatar?: RTCTestAvatar; public getTestAvatar(): IServiceVideoChatAvatar { - return (this._testAvatar ??= new RTCTestAvatar({ rtc: this })); + this._testAvatar ??= new RTCTestAvatar({ rtc: this }); + this._testAvatar.enabled = true; + return this._testAvatar; + } + + public stopTesting(): void { + if (this._testAvatar) { + this._testAvatar.enabled = false; + } } public getVolumeLevel(uid?: IServiceVideoChatUID): number { @@ -506,6 +514,7 @@ export class AgoraRTCWeb extends IServiceVideoChat { public localCameraTrack?: ICameraVideoTrack; public createLocalCameraTrack = singleRun(async (): Promise => { if (!this.localCameraTrack) { + const prevCameraID = this._cameraID; this.localCameraTrack = await AgoraRTC.createCameraVideoTrack({ encoderConfig: { width: 384, height: 216 }, cameraId: this._cameraID, @@ -517,7 +526,7 @@ export class AgoraRTCWeb extends IServiceVideoChat { // If there is setCameraID() called during the promises above, // the actually used camera ID may be different, so correct it here. - if (this._cameraID) { + if (this._cameraID && this._cameraID !== prevCameraID) { this.localCameraTrack.setDevice(this._cameraID); } } @@ -527,6 +536,7 @@ export class AgoraRTCWeb extends IServiceVideoChat { public localMicTrack?: IMicrophoneAudioTrack; public createLocalMicTrack = singleRun(async (): Promise => { if (!this.localMicTrack) { + const prevMicID = this._micID; this.localMicTrack = await AgoraRTC.createMicrophoneAudioTrack({ microphoneId: this._micID, // AEC: acoustic echo cancellation @@ -541,7 +551,7 @@ export class AgoraRTCWeb extends IServiceVideoChat { // If there is setMicID() called during the promises above, // the actually used microphone ID may be different, so correct it here. - if (this._micID) { + if (this._micID && this._micID !== prevMicID) { this.localMicTrack.setDevice(this._micID); } } diff --git a/service-providers/agora-rtc/agora-rtc-web/src/rtc-test-avatar.ts b/service-providers/agora-rtc/agora-rtc-web/src/rtc-test-avatar.ts index d83b6982ee4..3b8d65bd836 100644 --- a/service-providers/agora-rtc/agora-rtc-web/src/rtc-test-avatar.ts +++ b/service-providers/agora-rtc/agora-rtc-web/src/rtc-test-avatar.ts @@ -11,6 +11,9 @@ export interface RTCAvatarConfig { export class RTCTestAvatar implements IServiceVideoChatAvatar { private static LOW_VOLUME_LEVEL_THRESHOLD = 0.00001; + /** Stop doing anything when it is false */ + public enabled = false; + private readonly _rtc: AgoraRTCWeb; private readonly _sideEffect = new SideEffectManager(); @@ -55,9 +58,16 @@ export class RTCTestAvatar implements IServiceVideoChatAvatar { if (shouldMic && !localMicTrack) { localMicTrack = await this._rtc.createLocalMicTrack(); } + if (!this.enabled) { + return; + } + if (localMicTrack) { await localMicTrack.setEnabled(shouldMic); } + if (!this.enabled) { + return; + } const lowVolumeLevelDisposerID = "local-mic-volume-level"; if (shouldMic) { @@ -109,13 +119,16 @@ export class RTCTestAvatar implements IServiceVideoChatAvatar { let localCameraTrack = this._rtc.localCameraTrack; if (shouldCamera && !localCameraTrack) { localCameraTrack = await this._rtc.createLocalCameraTrack(); + if (!this.enabled) { + return; + } if (this._el$.value) { localCameraTrack.play(this._el$.value, { mirror: this._mirrorMode$.value, }); } } - if (localCameraTrack) { + if (localCameraTrack && !localCameraTrack.enabled) { await localCameraTrack.setEnabled(shouldCamera); } } catch (e) {