From 128dd3cfedf4705a0dd4e77e6a6c1ea60fb4dc96 Mon Sep 17 00:00:00 2001 From: csantosm <4a.santos@gmail.com> Date: Wed, 17 Aug 2022 17:54:39 +0200 Subject: [PATCH] openvidu-components: Fixed bug requesting devices --- .../videoconference.component.ts | 51 +++++++++++------- .../src/lib/services/device/device.service.ts | 53 ++++++++++--------- .../lib/services/openvidu/openvidu.service.ts | 19 ++++--- 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts index ceb9c4f1..d617c781 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.ts @@ -114,7 +114,7 @@ import { TranslateService } from '../../services/translate/translate.service'; styleUrls: ['./videoconference.component.css'], animations: [ trigger('inOutAnimation', [ - transition(':enter', [style({ opacity: 0 }), animate('300ms ease-out', style({ opacity: 1 }))]), + transition(':enter', [style({ opacity: 0 }), animate('300ms ease-out', style({ opacity: 1 }))]) // transition(':leave', [style({ opacity: 1 }), animate('50ms ease-in', style({ opacity: 0.9 }))]) ]) ] @@ -286,7 +286,8 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni } else { this.log.w('No screen token found. Screenshare feature will be disabled'); } - this.loading = false; + + this.start(); } } @@ -434,32 +435,43 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni async ngOnInit() { this.subscribeToVideconferenceDirectives(); + } + + private async start() { await this.deviceSrv.forceInitDevices(); const nickname = this.externalParticipantName || this.storageSrv.getNickname() || `OpenVidu_User${Math.floor(Math.random() * 100)}`; - this.participantService.initLocalParticipant({local: true, nickname}); + this.participantService.initLocalParticipant({ local: true, nickname }); this.openviduService.initialize(); if (this.deviceSrv.hasVideoDeviceAvailable() || this.deviceSrv.hasAudioDeviceAvailable()) { await this.initwebcamPublisher(); } this.isSessionInitialized = true; this.onParticipantCreated.emit(this.participantService.getLocalParticipant()); + this.loading = false; } - private async initwebcamPublisher() { - try { - const publisher = await this.openviduService.initDefaultPublisher(undefined); - if (publisher) { - publisher.once('accessDenied', (e: any) => this.handlePublisherError(e)); - publisher.once('accessAllowed', async () => { - await this.handlePublisherSuccess(); - this.participantReady = true; - }); - // publisher.once('streamPlaying', () => (this.streamPlaying = true)); + private async initwebcamPublisher(): Promise { + return new Promise(async (resolve, reject) => { + try { + const publisher = await this.openviduService.initDefaultPublisher(); + + if (publisher) { + publisher.once('accessDenied', (e: any) => { + this.handlePublisherError(e); + resolve(); + }); + publisher.once('accessAllowed', async () => { + await this.handlePublisherSuccess(); + this.participantReady = true; + resolve(); + }); + } + } catch (error) { + this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true); + this.log.e(error); + reject(); } - } catch (error) { - this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true); - this.log.e(error); - } + }); } async ngOnDestroy() { @@ -649,8 +661,9 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni this.onSessionCreated.emit(session); } - private handlePublisherError(e: any) { - let message: string; + private handlePublisherError(e: any): Promise { + let message: string = ''; + console.log('ERROR!', e); if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) { this.log.w('Video device already in use. Disabling video device...'); // Allow access to the room with only mic if camera device is already in use diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/device/device.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/device/device.service.ts index 01f6c957..86bc014b 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/device/device.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/device/device.service.ts @@ -16,12 +16,12 @@ import { StorageService } from '../storage/storage.service'; providedIn: 'root' }) export class DeviceService { - private OV: OpenVidu = null; + private OV: OpenVidu | null = null; private devices: Device[]; private cameras: CustomDevice[] = []; private microphones: CustomDevice[] = []; - private cameraSelected: CustomDevice; - private microphoneSelected: CustomDevice; + private cameraSelected: CustomDevice | null; + private microphoneSelected: CustomDevice | null; private log: ILogger; private videoDevicesDisabled: boolean; private audioDevicesDisabled: boolean; @@ -49,30 +49,33 @@ export class DeviceService { this.clear(); try { this.OV = new OpenVidu(); - // Forcing media permissions request. - // Sometimes, browser doens't launch the media permissions modal. - const mediaStream = await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined }); - mediaStream?.getAudioTracks().forEach((track) => track.stop()); - mediaStream?.getVideoTracks().forEach((track) => track.stop()); + this.devices = await this.OV.getDevices(); + if(this.devices?.some(device => !device.deviceId || !device.label)){ + // Forcing media permissions request. + // Sometimes, browser doesn't request the media permissions. + await this.OV.getUserMedia({ audioSource: undefined, videoSource: undefined }); + } + + await this.initializeDevices(); + this.updateAudioDeviceSelected(); + this.updateVideoDeviceSelected(); + + this._isVideoMuted = this.storageSrv.isVideoMuted() || this.libSrv.videoMuted.getValue(); + this._isAudioMuted = this.storageSrv.isAudioMuted() || this.libSrv.audioMuted.getValue(); + + this.log.d('Media devices', this.cameras, this.microphones); } catch (error) { this.deviceAccessDeniedError = (error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED; } - - await this.initializeDevices(); - this.updateAudioDeviceSelected(); - this.updateVideoDeviceSelected(); - - this._isVideoMuted = this.storageSrv.isVideoMuted() || this.libSrv.videoMuted.getValue(); - this._isAudioMuted = this.storageSrv.isAudioMuted() || this.libSrv.audioMuted.getValue(); - - this.log.d('Media devices', this.cameras, this.microphones); } /** * Initialize only the media devices available */ async initializeDevices() { - this.devices = await this.OV.getDevices(); + if(this.devices?.some(device => !device.deviceId || !device.label)){ + this.devices = await this.OV?.getDevices() as Device []; + } this.initializeCustomDevices(); } @@ -156,11 +159,11 @@ export class DeviceService { return this.hasAudioDeviceAvailable() && this._isAudioMuted; } - getCameraSelected(): CustomDevice { + getCameraSelected(): CustomDevice | null { return this.cameraSelected; } - getMicrophoneSelected(): CustomDevice { + getMicrophoneSelected(): CustomDevice | null { return this.microphoneSelected; } @@ -175,11 +178,11 @@ export class DeviceService { } needUpdateVideoTrack(newVideoSource: string): boolean { - return this.cameraSelected.device !== newVideoSource; + return this.cameraSelected?.device !== newVideoSource; } needUpdateAudioTrack(newAudioSource: string): boolean { - return this.microphoneSelected.device !== newAudioSource; + return this.microphoneSelected?.device !== newAudioSource; } getCameras(): CustomDevice[] { @@ -226,14 +229,14 @@ export class DeviceService { } private getCameraByDeviceField(deviceField: any): CustomDevice { - return this.cameras.find((opt: CustomDevice) => opt.device === deviceField || opt.label === deviceField); + return this.cameras.find((opt: CustomDevice) => opt.device === deviceField || opt.label === deviceField); } private getMicrophoneByDeviceField(deviceField: any): CustomDevice { - return this.microphones.find((opt: CustomDevice) => opt.device === deviceField || opt.label === deviceField); + return this.microphones.find((opt: CustomDevice) => opt.device === deviceField || opt.label === deviceField); } - private getMicrophoneFromStogare(): CustomDevice { + private getMicrophoneFromStogare(): CustomDevice | undefined { const storageDevice: CustomDevice = this.storageSrv.getAudioDevice(); if (!!storageDevice && this.microphones.some((device) => device.device === storageDevice.device)) { return storageDevice; diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts index e1191711..9afede83 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/openvidu/openvidu.service.ts @@ -19,10 +19,10 @@ import { OpenViduEdition } from '../../models/openvidu.model'; }) export class OpenViduService { private ovEdition: OpenViduEdition; - protected OV: OpenVidu = null; - protected OVScreen: OpenVidu = null; - protected webcamSession: Session = null; - protected screenSession: Session = null; + protected OV: OpenVidu; + protected OVScreen: OpenVidu; + protected webcamSession: Session; + protected screenSession: Session; protected videoSource = undefined; protected audioSource = undefined; protected log: ILogger; @@ -166,14 +166,14 @@ export class OpenViduService { * @internal * Initialize a publisher checking devices saved on storage or if participant have devices available. */ - async initDefaultPublisher(targetElement: string | HTMLElement): Promise { + async initDefaultPublisher(): Promise { const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable(); const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable(); const isVideoActive = !this.deviceService.isVideoMuted(); const isAudioActive = !this.deviceService.isAudioMuted(); - let videoSource = null; - let audioSource = null; + let videoSource: string | boolean = false; + let audioSource: string | boolean = false; if (hasVideoDevices) { // Video is active, assign the device selected @@ -200,7 +200,7 @@ export class OpenViduService { mirror }; if (hasVideoDevices || hasAudioDevices) { - const publisher = await this.initPublisher(targetElement, properties); + const publisher = await this.initPublisher(undefined, properties); this.participantService.setMyCameraPublisher(publisher); this.participantService.updateLocalParticipant(); return publisher; @@ -325,7 +325,7 @@ export class OpenViduService { const properties: PublisherProperties = { videoSource: ScreenType.SCREEN, - audioSource: hasAudioDevicesAvailable ? this.deviceService.getMicrophoneSelected().device : null, + audioSource: hasAudioDevicesAvailable ? this.deviceService.getMicrophoneSelected().device : false, publishVideo: true, publishAudio: hasAudio, mirror: false @@ -450,7 +450,6 @@ export class OpenViduService { } } - //TODO: Uncomment this section when replaceTrack issue is fixed private async createMediaStream(pp: PublisherProperties): Promise { let mediaStream: MediaStream; const isFirefoxPlatform = this.platformService.isFirefox();