From eaed1b3692b2d53e425cea39e9bcf6309c198161 Mon Sep 17 00:00:00 2001 From: csantosm <4a.santos@gmail.com> Date: Fri, 24 Jun 2022 16:12:36 +0200 Subject: [PATCH] openvidu-components: Fixed replace tracks in session --- .../audio-wave/audio-wave.component.ts | 71 +++++---- .../audio-devices/audio-devices.component.ts | 11 +- .../video-devices/video-devices.component.ts | 11 +- .../lib/services/openvidu/openvidu.service.ts | 144 +++++------------- 4 files changed, 87 insertions(+), 150 deletions(-) diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts index 6951e2b6..ed5e1735 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/audio-wave/audio-wave.component.ts @@ -1,11 +1,9 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; - -import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser'; +import { PublisherSpeakingEvent, StreamManager, StreamPropertyChangedEvent } from 'openvidu-browser'; /** * @internal */ - @Component({ selector: 'ov-audio-wave', templateUrl: './audio-wave.component.html', @@ -13,40 +11,49 @@ import { PublisherSpeakingEvent, StreamManager } from 'openvidu-browser'; }) export class AudioWaveComponent implements OnInit, OnDestroy { isSpeaking: boolean = false; - // audioVolume: number = 0; - private _streamManager: StreamManager; - - @Input() - set streamManager(streamManager: StreamManager) { - this._streamManager = streamManager; - - if (this._streamManager) { - this._streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => { - this.isSpeaking = true; - }); - - this._streamManager.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => { - this.isSpeaking = false; - }); - - // streamManager.on('streamAudioVolumeChange', (event: any) => { - // // The loudest sounds on your system will be at 0dB - // // and silence in webaudio is -100dB. - // this.audioVolume = 100 + event.value.newValue; - // console.log('Publisher audio volume change from ' + event.value.oldValue + ' to' + event.value.newValue); - // console.log('AUDIO VOLUME', this.audioVolume); - // }); - } - } + @Input() streamManager: StreamManager; constructor() {} + + ngOnInit(): void { + this.subscribeSpeakingEvents(); + this.subscribeToStreamPropertyChanged(); + } + ngOnDestroy(): void { - if (this._streamManager) { - this._streamManager.off('publisherStartSpeaking'); - this._streamManager.off('publisherStopSpeaking'); + this.unsubscribeSpeakingEvents(); + this.unsubscribePropertyChangedEvents(); + } + + private subscribeToStreamPropertyChanged() { + if (this.streamManager) { + this.streamManager.on('streamPropertyChanged', (event: StreamPropertyChangedEvent) => { + if (event.reason === 'trackReplaced' && event.changedProperty === 'audioActive') { + // FIXUP: When the audio track is replaced, the startSpeakingEvents is not fired by openvidu-browser + this.unsubscribeSpeakingEvents(); + this.subscribeSpeakingEvents(); + } + }); } } - ngOnInit(): void {} + private subscribeSpeakingEvents() { + if (this.streamManager) { + this.streamManager.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = true)); + this.streamManager.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => (this.isSpeaking = false)); + } + } + + private unsubscribeSpeakingEvents() { + if (this.streamManager) { + this.streamManager.off('publisherStartSpeaking'); + this.streamManager.off('publisherStopSpeaking'); + } + } + private unsubscribePropertyChangedEvents() { + if (this.streamManager) { + this.streamManager.off('streamPropertyChanged'); + } + } } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts index bc2745c2..c60254ee 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/audio-devices/audio-devices.component.ts @@ -7,6 +7,7 @@ import { CustomDevice } from '../../../models/device.model'; import { ParticipantAbstractModel } from '../../../models/participant.model'; import { ParticipantService } from '../../../services/participant/participant.service'; import { Subscription } from 'rxjs'; +import { VideoType } from '../../../models/video-type.model'; /** * @internal @@ -59,14 +60,8 @@ export class AudioDevicesComponent implements OnInit, OnDestroy { async onMicrophoneSelected(event: any) { const audioSource = event?.value; if (this.deviceSrv.needUpdateAudioTrack(audioSource)) { - //TODO: Uncomment this when replaceTrack issue is fixed - // const pp: PublisherProperties = { audioSource, videoSource: false }; - // await this.openviduService.replaceTrack(VideoType.CAMERA, pp); - // TODO: Remove this when replaceTrack issue is fixed - const mirror = this.deviceSrv.cameraNeedsMirror(this.deviceSrv.getCameraSelected().device); - const pp: PublisherProperties = { videoSource: this.deviceSrv.getCameraSelected().device, audioSource, mirror }; - await this.openviduService.republishTrack(pp); - + const pp: PublisherProperties = { audioSource, videoSource: false }; + await this.openviduService.replaceTrack(VideoType.CAMERA, pp); this.deviceSrv.setMicSelected(audioSource); this.microphoneSelected = this.deviceSrv.getMicrophoneSelected(); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts index 7801bb43..87543f25 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/settings/video-devices/video-devices.component.ts @@ -4,6 +4,7 @@ import { Subscription } from 'rxjs'; import { CustomDevice } from '../../../models/device.model'; import { PanelType } from '../../../models/panel.model'; import { ParticipantAbstractModel } from '../../../models/participant.model'; +import { VideoType } from '../../../models/video-type.model'; import { DeviceService } from '../../../services/device/device.service'; import { OpenViduService } from '../../../services/openvidu/openvidu.service'; import { PanelService } from '../../../services/panel/panel.service'; @@ -72,18 +73,14 @@ export class VideoDevicesComponent implements OnInit, OnDestroy { // Is New deviceId different from the old one? if (this.deviceSrv.needUpdateVideoTrack(videoSource)) { const mirror = this.deviceSrv.cameraNeedsMirror(videoSource); - //TODO: Uncomment this when replaceTrack issue is fixed - // const pp: PublisherProperties = { videoSource, audioSource: false, mirror }; - // await this.openviduService.replaceTrack(VideoType.CAMERA, pp); - // TODO: Remove this when replaceTrack issue is fixed - const pp: PublisherProperties = { videoSource, audioSource: this.deviceSrv.getMicrophoneSelected().device, mirror }; - // Reapply Virtual Background to new Publisher if necessary const backgroundSelected = this.backgroundService.backgroundSelected.getValue(); if (this.backgroundService.isBackgroundApplied()) { await this.backgroundService.removeBackground(); } - await this.openviduService.republishTrack(pp); + const pp: PublisherProperties = { videoSource, audioSource: false, mirror }; + await this.openviduService.replaceTrack(VideoType.CAMERA, pp); + if (this.backgroundService.isBackgroundApplied()) { const bgSelected = this.backgroundService.backgrounds.find((b) => b.id === backgroundSelected); if (bgSelected) { 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 cdc7a36d..e1191711 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 @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Connection, OpenVidu, Publisher, PublisherProperties, Session, SignalOptions, Stream } from 'openvidu-browser'; +import { Connection, OpenVidu, OpenViduError, OpenViduErrorName, Publisher, PublisherProperties, Session, SignalOptions, Stream } from 'openvidu-browser'; import { LoggerService } from '../logger/logger.service'; @@ -323,7 +323,6 @@ export class OpenViduService { const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.isMyVideoActive(); const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.isMyAudioActive(); - console.warn('will be audio active', hasAudio); const properties: PublisherProperties = { videoSource: ScreenType.SCREEN, audioSource: hasAudioDevicesAvailable ? this.deviceService.getMicrophoneSelected().device : null, @@ -388,45 +387,6 @@ export class OpenViduService { } } - /** - * TODO: Remove this method when replaceTrack issue is fixed - * https://github.com/OpenVidu/openvidu/pull/700 - * @internal - */ - republishTrack(properties: PublisherProperties): Promise { - const { videoSource, audioSource, mirror } = properties; - return new Promise(async (resolve, reject) => { - if (!!videoSource) { - this.log.d('Replacing video track ' + videoSource); - this.videoSource = videoSource; - } - if (!!audioSource) { - this.log.d('Replacing audio track ' + audioSource); - this.audioSource = audioSource; - } - this.destroyPublisher(this.participantService.getMyCameraPublisher()); - const properties: PublisherProperties = { - videoSource: this.videoSource, - audioSource: this.audioSource, - publishVideo: this.participantService.isMyVideoActive(), - publishAudio: this.participantService.isMyAudioActive(), - mirror - }; - - const publisher = await this.initPublisher(undefined, properties); - this.participantService.setMyCameraPublisher(publisher); - - publisher.once('streamPlaying', () => { - this.participantService.setMyCameraPublisher(publisher); - resolve(); - }); - - publisher.once('accessDenied', () => { - reject(); - }); - }); - } - /** * @internal */ @@ -437,12 +397,6 @@ export class OpenViduService { to: connections && connections.length > 0 ? connections : undefined }; this.webcamSession.signal(signalOptions); - - // TODO: Check if it is necessary - // if (type === Signal.NICKNAME_CHANGED && !!this.getScreenSession().connection) { - // signalOptions.data = JSON.stringify({ clientData: this.participantService.getScreenNickname() }); - // this.getScreenSession()?.signal(signalOptions); - // } } /** @@ -453,40 +407,24 @@ export class OpenViduService { this.log.d(`Replacing ${videoType} track`, props); if (videoType === VideoType.CAMERA) { - //TODO: Uncomment this section when replaceTrack issue is fixed - // https://github.com/OpenVidu/openvidu/pull/700 - throw 'Replace track feature has a bug. We are trying to fix it'; - // let mediaStream: MediaStream; - // const oldMediaStream = this.participantService.getMyCameraPublisher().stream.getMediaStream(); - // const isFirefoxPlatform = this.platformService.isFirefox(); - // const isReplacingAudio = !!props.audioSource; - // const isReplacingVideo = !!props.videoSource; + let mediaStream: MediaStream; + const isReplacingAudio = !!props.audioSource; + const isReplacingVideo = !!props.videoSource; - // if (isReplacingVideo) { - // if (isFirefoxPlatform) { - // // Firefox throw an exception trying to get a new MediaStreamTrack if the older one is not stopped - // // NotReadableError: Concurrent mic process limit. Stopping tracks before call to getUserMedia - // oldMediaStream.getVideoTracks()[0].stop(); - // } - // mediaStream = await this.createMediaStream(props); - // // Replace video track - // const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0]; - // await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack); - // } else if (isReplacingAudio) { - // if (isFirefoxPlatform) { - // // Firefox throw an exception trying to get a new MediaStreamTrack if the older one is not stopped - // // NotReadableError: Concurrent mic process limit. Stopping tracks before call to getUserMedia - // oldMediaStream.getAudioTracks()[0].stop(); - // } - // mediaStream = await this.createMediaStream(props); - // // Replace audio track - // const audioTrack: MediaStreamTrack = mediaStream.getAudioTracks()[0]; - // await this.participantService.getMyCameraPublisher().replaceTrack(audioTrack); - // } + if (isReplacingVideo) { + mediaStream = await this.createMediaStream(props); + // Replace video track + const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0]; + await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack); + } else if (isReplacingAudio) { + mediaStream = await this.createMediaStream(props); + // Replace audio track + const audioTrack: MediaStreamTrack = mediaStream.getAudioTracks()[0]; + await this.participantService.getMyCameraPublisher().replaceTrack(audioTrack); + } } else if (videoType === VideoType.SCREEN) { - let newScreenMediaStream; try { - newScreenMediaStream = await this.OVScreen.getUserMedia(props); + let newScreenMediaStream = await this.OVScreen.getUserMedia(props); this.participantService.getMyScreenPublisher().stream.getMediaStream().getVideoTracks()[0].stop(); await this.participantService.getMyScreenPublisher().replaceTrack(newScreenMediaStream.getVideoTracks()[0]); } catch (error) { @@ -513,32 +451,32 @@ 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(); - // const isReplacingAudio = !!pp.audioSource; - // const isReplacingVideo = !!pp.videoSource; + private async createMediaStream(pp: PublisherProperties): Promise { + let mediaStream: MediaStream; + const isFirefoxPlatform = this.platformService.isFirefox(); + const isReplacingAudio = !!pp.audioSource; + const isReplacingVideo = !!pp.videoSource; - // try { - // mediaStream = await this.OV.getUserMedia(pp); - // } catch (error) { - // if ((error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) { - // if (isFirefoxPlatform) { - // this.log.w('The device requested is not available. Restoring the older one'); - // // The track requested is not available so we are getting the old tracks ids for recovering the track - // if (isReplacingVideo) { - // pp.videoSource = this.deviceService.getCameraSelected().device; - // } else if (isReplacingAudio) { - // pp.audioSource = this.deviceService.getMicrophoneSelected().device; - // } - // mediaStream = await this.OV.getUserMedia(pp); - // // TODO show error alert informing that the new device is not available - // } - // } - // } finally { - // return mediaStream; - // } - // } + try { + mediaStream = await this.OV.getUserMedia(pp); + } catch (error) { + if ((error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) { + if (isFirefoxPlatform) { + this.log.w('The device requested is not available. Restoring the older one'); + // The track requested is not available so we are getting the old tracks ids for recovering the track + if (isReplacingVideo) { + pp.videoSource = this.deviceService.getCameraSelected().device; + } else if (isReplacingAudio) { + pp.audioSource = this.deviceService.getMicrophoneSelected().device; + } + mediaStream = await this.OV.getUserMedia(pp); + // TODO show error alert informing that the new device is not available + } + } + } finally { + return mediaStream; + } + } /** * @internal