From b74ae7a6abf50f9c2e68a6c1c96f4ed521322166 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Wed, 1 Mar 2023 10:55:45 +0100 Subject: [PATCH] openvidu-components: Refactored participant and openvidu service - Moved publishVideo from openviduService to participantService - Mark old openviduService methods as deprecated --- .../recording-activity.component.ts | 4 +- .../components/session/session.component.ts | 32 ++- .../video-devices/video-devices.component.ts | 2 +- .../components/toolbar/toolbar.component.ts | 2 +- .../template/openvidu-angular.directive.ts | 37 ++- .../src/lib/services/chat/chat.service.ts | 4 +- .../lib/services/openvidu/openvidu.service.ts | 271 ++++++++++-------- .../participant/participant.service.ts | 62 +++- 8 files changed, 252 insertions(+), 162 deletions(-) diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts index 304e1f74..39e2fa17 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/panel/activities-panel/recording-activity/recording-activity.component.ts @@ -167,10 +167,10 @@ export class RecordingActivityComponent implements OnInit { */ deleteRecording(id: string) { - const succsessCallback = () => { + const succsessCallback = async () => { this.onDeleteRecordingClicked.emit(id); // Sending signal to all participants with the aim of updating their recordings list - this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections()); + await this.openviduService.sendSignal(Signal.RECORDING_DELETED, this.openviduService.getRemoteConnections()); }; this.actionService.openDeleteRecordingDialog(succsessCallback.bind(this)); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts index be576110..340899a3 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/session/session.component.ts @@ -253,20 +253,28 @@ export class SessionComponent implements OnInit, OnDestroy { private async connectToSession(): Promise { try { - const webcamToken = this.openviduService.getWebcamToken(); - const screenToken = this.openviduService.getScreenToken(); + const nickname = this.participantService.getMyNickname(); + const participantId = this.participantService.getLocalParticipant().id; + const screenPublisher = this.participantService.getMyScreenPublisher(); + const cameraPublisher = this.participantService.getMyCameraPublisher(); if (this.participantService.haveICameraAndScreenActive()) { - await this.openviduService.connectSession(this.openviduService.getWebcamSession(), webcamToken); - await this.openviduService.connectSession(this.openviduService.getScreenSession(), screenToken); - await this.openviduService.publish(this.participantService.getMyCameraPublisher()); - await this.openviduService.publish(this.participantService.getMyScreenPublisher()); + + const webcamSessionId = await this.openviduService.connectWebcamSession(participantId, nickname); + if (webcamSessionId) this.participantService.setMyCameraConnectionId(webcamSessionId); + + const screenSessionId = await this.openviduService.connectScreenSession(participantId, nickname); + if (screenSessionId) this.participantService.setMyScreenConnectionId(screenSessionId); + + + await this.openviduService.publishCamera(cameraPublisher); + await this.openviduService.publishScreen(screenPublisher); } else if (this.participantService.isOnlyMyScreenActive()) { - await this.openviduService.connectSession(this.openviduService.getScreenSession(), screenToken); - await this.openviduService.publish(this.participantService.getMyScreenPublisher()); + await this.openviduService.connectScreenSession(participantId, nickname); + await this.openviduService.publishScreen(screenPublisher); } else { - await this.openviduService.connectSession(this.openviduService.getWebcamSession(), webcamToken); - await this.openviduService.publish(this.participantService.getMyCameraPublisher()); + await this.openviduService.connectWebcamSession(participantId, nickname); + await this.openviduService.publishCamera(cameraPublisher); } } catch (error) { // this._error.emit({ error: error.error, messgae: error.message, code: error.code, status: error.status }); @@ -289,7 +297,7 @@ export class SessionComponent implements OnInit, OnDestroy { } private subscribeToConnectionCreatedAndDestroyed() { - this.session.on('connectionCreated', (event: ConnectionEvent) => { + this.session.on('connectionCreated', async (event: ConnectionEvent) => { const connectionId = event.connection?.connectionId; const nickname: string = this.participantService.getNicknameFromConnectionData(event.connection.data); const isRemoteConnection: boolean = !this.openviduService.isMyOwnConnection(connectionId); @@ -303,7 +311,7 @@ export class SessionComponent implements OnInit, OnDestroy { //Sending nicnkanme signal to new participants if (this.openviduService.needSendNicknameSignal()) { const data = { clientData: this.participantService.getMyNickname() }; - this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, [event.connection], data); + await this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, [event.connection], data); } } }); 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 dc536b74..69c73ea6 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 @@ -67,7 +67,7 @@ export class VideoDevicesComponent implements OnInit, OnDestroy { async toggleCam() { this.videoMuteChanging = true; const publish = this.isVideoMuted; - await this.openviduService.publishVideo(publish); + await this.participantService.publishVideo(publish); if (this.isVideoMuted && this.panelService.isExternalPanelOpened()) { this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.ts index bc5a6409..b0e295d4 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/toolbar/toolbar.component.ts @@ -521,7 +521,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit { if (this.panelService.isExternalPanelOpened() && !publishVideo) { this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); } - await this.openviduService.publishVideo(publishVideo); + await this.participantService.publishVideo(publishVideo); } catch (error) { this.log.e('There was an error toggling camera:', error.code, error.message); this.actionService.openDialog(this.translateService.translate('ERRORS.TOGGLE_CAMERA'), error?.error || error?.message || error); diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts index 67d1f48b..57172f08 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/directives/template/openvidu-angular.directive.ts @@ -25,7 +25,7 @@ import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; * publishVideo = true; * publishAudio = true; * - * constructor(private httpClient: HttpClient, private openviduService: OpenViduService) { } + * constructor(private httpClient: HttpClient, private participantService: ParticipantService) { } * * async ngOnInit() { * this.tokens = { @@ -36,18 +36,18 @@ import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; * * toggleVideo() { * this.publishVideo = !this.publishVideo; - * this.openviduService.publishVideo(this.publishVideo); + * this.participantService.publishVideo(this.publishVideo); * } * * toggleAudio() { * this.publishAudio = !this.publishAudio; - * this.openviduService.publishAudio(this.publishAudio); + * this.participantService.publishAudio(this.publishAudio); * } * * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` * @@ -88,13 +88,12 @@ export class ToolbarDirective { sessionId = "panel-directive-example"; tokens!: TokenModel; - + * sessionId = 'toolbar-additionalbtn-directive-example'; * tokens!: TokenModel; * * constructor( * private httpClient: HttpClient, - * private openviduService: OpenViduService, * private participantService: ParticipantService * ) { } * @@ -107,18 +106,18 @@ export class ToolbarDirective { * * toggleVideo() { * const publishVideo = !this.participantService.isMyVideoActive(); - * this.openviduService.publishVideo(publishVideo); + * this.participantService.publishVideo(publishVideo); * } * * toggleAudio() { * const publishAudio = !this.participantService.isMyAudioActive(); - * this.openviduService.publishAudio(publishAudio); + * this.participantService.publishAudio(publishAudio); * } * * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` */ @@ -149,7 +148,7 @@ export class ToolbarAdditionalButtonsDirective { * * ```javascript * export class ToolbarAdditionalPanelButtonsDirectiveComponent { - * + * * sessionId = "toolbar-additionalPanelbtn"; * tokens!: TokenModel; * @@ -169,7 +168,7 @@ export class ToolbarAdditionalButtonsDirective { * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` */ @@ -206,7 +205,7 @@ export class ToolbarAdditionalPanelButtonsDirective { * * ```javascript * export class PanelDirectiveComponent { - * + * * sessionId = "panel-directive-example"; * tokens!: TokenModel; * @@ -222,7 +221,7 @@ export class ToolbarAdditionalPanelButtonsDirective { * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` */ @@ -275,7 +274,7 @@ export class PanelDirective { * * ```javascript * export class AdditionalPanelsDirectiveComponent implements OnInit { - * + * * sessionId = "toolbar-additionalbtn-directive-example"; * tokens!: TokenModel; * @@ -445,7 +444,7 @@ export class BackgroundEffectsPanelDirective { * * ```javascript * export class AppComponent implements OnInit { - * + * * sessionId = "activities-panel-directive-example"; * tokens!: TokenModel; * @@ -585,7 +584,7 @@ export class ParticipantsPanelDirective { * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` * @@ -684,10 +683,10 @@ export class ParticipantPanelItemElementsDirective { * * We need to get the participants in our Session, so we use the {@link ParticipantService} to subscribe to the required Observables. * We'll get the local participant and the remote participants to display their streams in our custom session layout. - * + * * ```javascript * export class LayoutDirectiveComponent implements OnInit, OnDestroy { - * + * * sessionId = 'layout-directive-example'; * tokens!: TokenModel; * @@ -723,7 +722,7 @@ export class ParticipantPanelItemElementsDirective { * async getToken(): Promise { * // Returns an OpeVidu token * } - * + * * } * ``` */ diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts index 76401ef3..7a151335 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/chat/chat.service.ts @@ -64,7 +64,7 @@ export class ChatService { }); } - sendMessage(message: string) { + async sendMessage(message: string) { message = message.replace(/ +(?= )/g, ''); if (message !== '' && message !== ' ') { const data = { @@ -72,7 +72,7 @@ export class ChatService { nickname: this.participantService.getMyNickname() }; - this.openviduService.sendSignal(Signal.CHAT, undefined, data); + await this.openviduService.sendSignal(Signal.CHAT, undefined, data); } } 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 6d3264db..a6b66f02 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,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { Connection, OpenVidu, @@ -53,7 +53,7 @@ export class OpenViduService { protected openviduAngularConfigSrv: OpenViduAngularConfigService, protected platformService: PlatformService, protected loggerSrv: LoggerService, - private participantService: ParticipantService, + private injector: Injector, protected deviceService: DeviceService ) { this.log = this.loggerSrv.get('OpenViduService'); @@ -140,8 +140,6 @@ export class OpenViduService { async clear() { this.videoSource = undefined; this.audioSource = undefined; - // await this.participantService.getMyCameraPublisher()?.stream?.disposeMediaStream(); - // await this.participantService.getMyScreenPublisher()?.stream?.disposeMediaStream(); } /** @@ -201,30 +199,43 @@ export class OpenViduService { /** * @internal + * Connects to webcam session using webcam token. */ - async connectSession(session: Session, token: string): Promise { - if (!!token && session) { - const nickname = this.participantService.getMyNickname(); - const participantId = this.participantService.getLocalParticipant().id; - if (session === this.webcamSession) { - this.log.d('Connecting webcam session'); - await this.webcamSession.connect(token, { - clientData: nickname, - participantId, - type: VideoType.CAMERA - }); - this.participantService.setMyCameraConnectionId(this.webcamSession.connection.connectionId); - } else if (session === this.screenSession) { - this.log.d('Connecting screen session'); - await this.screenSession.connect(token, { - clientData: `${nickname}_${VideoType.SCREEN}`, - participantId, - type: VideoType.SCREEN - }); - - this.participantService.setMyScreenConnectionId(this.screenSession.connection.connectionId); - } + async connectWebcamSession(participantId: string, nickname: string): Promise { + if (this.isWebcamSessionConnected()) { + this.log.d('Webcam session is already connected'); + return undefined; } + + this.log.d('Connecting webcam session'); + await this.webcamSession.connect(this.getWebcamToken(), { + clientData: nickname, + participantId, + type: VideoType.CAMERA + }); + + return this.webcamSession.connection.connectionId; + // this.participantService.setMyCameraConnectionId(this.webcamSession.connection.connectionId); + } + + /** + * @internal + * Connects to screen session using screen token. + */ + async connectScreenSession(participantId: string, nickname: string): Promise { + if (this.isScreenSessionConnected()) { + this.log.d('Screen session is already connected'); + return undefined; + } + + this.log.d('Connecting screen session'); + await this.screenSession.connect(this.getScreenToken(), { + clientData: `${nickname}_${VideoType.SCREEN}`, + participantId, + type: VideoType.SCREEN + }); + + return this.screenSession.connection.connectionId; } /** @@ -286,103 +297,86 @@ export class OpenViduService { } /** - * @internal + * Publishes the publisher to the webcam Session + * @param publisher */ - async publish(publisher: Publisher): Promise { - if (!!publisher) { - if (publisher === this.participantService.getMyCameraPublisher()) { - if (this.webcamSession?.capabilities?.publish) { - return await this.webcamSession.publish(publisher); - } - this.log.e('Webcam publisher cannot be published'); - } else if (publisher === this.participantService.getMyScreenPublisher()) { - if (this.screenSession?.capabilities?.publish) { - return await this.screenSession.publish(publisher); - } - this.log.e('Screen publisher cannot be published'); - } + async publishCamera(publisher: Publisher): Promise { + if (!publisher) return; + if (this.webcamSession?.capabilities?.publish) { + return this.webcamSession.publish(publisher); } + this.log.e('Webcam publisher cannot be published'); } /** - * @internal + * Publishes the publisher to the screen Session + * @param publisher */ - private async unpublish(publisher: Publisher): Promise { - if (!!publisher) { - if (publisher === this.participantService.getMyCameraPublisher()) { - this.publishAudioAux(this.participantService.getMyScreenPublisher(), this.participantService.isMyAudioActive()); - await this.webcamSession.unpublish(publisher); - } else if (publisher === this.participantService.getMyScreenPublisher()) { - await this.screenSession.unpublish(publisher); - } + async publishScreen(publisher: Publisher): Promise { + if (!publisher) return; + + if (this.screenSession?.capabilities?.publish) { + return this.screenSession.publish(publisher); } + this.log.e('Screen publisher cannot be published'); + } + + /** + * Unpublishes the publisher of the webcam Session + * @param publisher + */ + async unpublishCamera(publisher: Publisher): Promise { + if (!publisher) return; + return this.webcamSession.unpublish(publisher); + } + + /** + * Unpublishes the publisher of the screen Session + * @param publisher + */ + async unpublishScreen(publisher: Publisher): Promise { + if (!publisher) return; + return this.screenSession.unpublish(publisher); } /** * Publish or unpublish the video stream (if available). * It hides the camera muted stream if screen is sharing. * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishVideo publishVideo} + * + * @deprecated This method has been moved to ParticipantService + * + * TODO: Remove this method in release 2.28.0 */ async publishVideo(publish: boolean): Promise { - const publishAudio = this.participantService.isMyAudioActive(); - - // Disabling webcam - if (this.participantService.haveICameraAndScreenActive()) { - await this.publishVideoAux(this.participantService.getMyCameraPublisher(), publish); - await this.unpublish(this.participantService.getMyCameraPublisher()); - this.publishAudioAux(this.participantService.getMyScreenPublisher(), publishAudio); - this.participantService.disableWebcamStream(); - } else if (this.participantService.isOnlyMyScreenActive()) { - // Enabling webcam - const hasAudio = this.participantService.hasScreenAudioActive(); - if (!this.isWebcamSessionConnected()) { - await this.connectSession(this.getWebcamSession(), this.getWebcamToken()); - } - await this.publish(this.participantService.getMyCameraPublisher()); - await this.publishVideoAux(this.participantService.getMyCameraPublisher(), true); - this.publishAudioAux(this.participantService.getMyScreenPublisher(), false); - this.publishAudioAux(this.participantService.getMyCameraPublisher(), hasAudio); - this.participantService.enableWebcamStream(); - } else { - // Muting/unmuting webcam - await this.publishVideoAux(this.participantService.getMyCameraPublisher(), publish); - } - } - - /** - * @internal - */ - private async publishVideoAux(publisher: Publisher, publish: boolean): Promise { - if (!!publisher) { - let resource: boolean | MediaStreamTrack = true; - if (publish) { - // Forcing restoration with a custom media stream (the older one instead the default) - const currentDeviceId = this.deviceService.getCameraSelected()?.device; - const mediaStream = await this.createMediaStream({ videoSource: currentDeviceId, audioSource: false }); - resource = mediaStream.getVideoTracks()[0]; - } - - await publisher.publishVideo(publish, resource); - this.participantService.updateLocalParticipant(); - } + const participantService = this.injector.get(ParticipantService); + participantService.publishVideo(publish); } /** * Share or unshare the screen. * Hide the camera muted stream when screen is sharing. + * @deprecated This method has been moved to ParticipantService * - * TODO: This method should be in participant service + * TODO: Remove this method in release 2.28.0 */ async toggleScreenshare() { - if (this.participantService.haveICameraAndScreenActive()) { + const participantService = this.injector.get(ParticipantService); + + const screenPublisher = participantService.getMyScreenPublisher(); + const cameraPublisher = participantService.getMyCameraPublisher(); + const participantNickname = participantService.getMyNickname(); + const participantId = participantService.getLocalParticipant().id; + + if (participantService.haveICameraAndScreenActive()) { // Disabling screenShare - this.participantService.disableScreenStream(); - await this.unpublish(this.participantService.getMyScreenPublisher()); - } else if (this.participantService.isOnlyMyCameraActive()) { + participantService.disableScreenStream(); + this.unpublishScreen(screenPublisher); + } else if (participantService.isOnlyMyCameraActive()) { // I only have the camera published const hasAudioDevicesAvailable = this.deviceService.hasAudioDeviceAvailable(); - const willWebcamBePresent = this.participantService.isMyCameraActive() && this.participantService.isMyVideoActive(); - const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && this.participantService.isMyAudioActive(); + const willWebcamBePresent = participantService.isMyCameraActive() && participantService.isMyVideoActive(); + const hasAudio = willWebcamBePresent ? false : hasAudioDevicesAvailable && participantService.isMyAudioActive(); const properties: PublisherProperties = { videoSource: ScreenType.SCREEN, @@ -404,16 +398,16 @@ export class OpenViduService { }); // Enabling screenShare - this.participantService.activeMyScreenShare(screenPublisher); + participantService.activeMyScreenShare(screenPublisher); if (!this.isScreenSessionConnected()) { - await this.connectSession(this.getScreenSession(), this.getScreenToken()); + await this.connectScreenSession(participantId, participantNickname); } - await this.publish(this.participantService.getMyScreenPublisher()); - if (!this.participantService.isMyVideoActive()) { + await this.publishScreen(screenPublisher); + if (!participantService.isMyVideoActive()) { // Disabling webcam - this.participantService.disableWebcamStream(); - await this.unpublish(this.participantService.getMyCameraPublisher()); + participantService.disableWebcamStream(); + this.unpublishCamera(cameraPublisher); } }); @@ -422,52 +416,71 @@ export class OpenViduService { }); } else { // I only have my screenshare active and I have no camera or it is muted - const hasAudio = this.participantService.hasScreenAudioActive(); + const hasAudio = participantService.hasScreenAudioActive(); // Enable webcam if (!this.isWebcamSessionConnected()) { - await this.connectSession(this.getWebcamSession(), this.getWebcamToken()); + await this.connectWebcamSession(participantId, participantNickname); } - await this.publish(this.participantService.getMyCameraPublisher()); - this.publishAudioAux(this.participantService.getMyCameraPublisher(), hasAudio); - this.participantService.enableWebcamStream(); + await this.publishCamera(cameraPublisher); + this.publishAudioAux(cameraPublisher, hasAudio); + participantService.enableWebcamStream(); // Disabling screenshare - this.participantService.disableScreenStream(); - await this.unpublish(this.participantService.getMyScreenPublisher()); + participantService.disableScreenStream(); + this.unpublishScreen(screenPublisher); } } /** * @internal - * TODO: Remove when it is in participant service + * @deprecated + * + * TODO: Remove this method in release 2.28.0 */ private publishAudioAux(publisher: Publisher, value: boolean): void { + const participantService = this.injector.get(ParticipantService); + if (!!publisher) { publisher.publishAudio(value); - this.participantService.updateLocalParticipant(); + participantService.updateLocalParticipant(); } } + /** + * + * Publish or unpublish the audio stream (if available). + * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishAudio publishAudio}. + * @deprecated This method has been moved to ParticipantService + */ + publishAudio(publish: boolean): void { + const participantService = this.injector.get(ParticipantService); + participantService.publishAudio(publish); + } + /** * @internal * * @param type: type of signal * @param connections: if undefined, the signal will be sent to all participants */ - sendSignal(type: Signal, connections?: Connection[], data?: any): void { + sendSignal(type: Signal, connections?: Connection[], data?: any): Promise { const signalOptions: SignalOptions = { data: JSON.stringify(data), type, to: connections && connections.length > 0 ? connections : undefined }; - this.webcamSession.signal(signalOptions); + return this.webcamSession.signal(signalOptions); } /** * @internal */ async replaceTrack(videoType: VideoType, props: PublisherProperties) { + const participantService = this.injector.get(ParticipantService); + const screenPublisher = participantService.getMyScreenPublisher(); + const cameraPublisher = participantService.getMyCameraPublisher(); + try { this.log.d(`Replacing ${videoType} track`, props); @@ -480,18 +493,18 @@ export class OpenViduService { mediaStream = await this.createMediaStream(props); // Replace video track const videoTrack: MediaStreamTrack = mediaStream.getVideoTracks()[0]; - await this.participantService.getMyCameraPublisher().replaceTrack(videoTrack); + await cameraPublisher.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); + await cameraPublisher.replaceTrack(audioTrack); } } else if (videoType === VideoType.SCREEN) { try { let newScreenMediaStream = await this.OVScreen.getUserMedia(props); - this.participantService.getMyScreenPublisher().stream.getMediaStream().getVideoTracks()[0].stop(); - await this.participantService.getMyScreenPublisher().replaceTrack(newScreenMediaStream.getVideoTracks()[0]); + screenPublisher.stream.getMediaStream().getVideoTracks()[0].stop(); + await screenPublisher.replaceTrack(newScreenMediaStream.getVideoTracks()[0]); } catch (error) { this.log.w('Cannot create the new MediaStream', error); } @@ -509,7 +522,9 @@ export class OpenViduService { * @param lang The language of the Stream's audio track. */ async subscribeRemotesToSTT(lang: string): Promise { - const remoteParticipants = this.participantService.getRemoteParticipants(); + const participantService = this.injector.get(ParticipantService); + + const remoteParticipants = participantService.getRemoteParticipants(); let successNumber = 0; for (const p of remoteParticipants) { @@ -548,9 +563,11 @@ export class OpenViduService { * Unsubscribe to all `CAMERA` stream types to speech-to-text if STT is up(ready) */ async unsubscribeRemotesFromSTT(): Promise { + const participantService = this.injector.get(ParticipantService); + clearTimeout(this.sttReconnectionTimeout); if (this.isSttReady()) { - for (const p of this.participantService.getRemoteParticipants()) { + for (const p of participantService.getRemoteParticipants()) { const stream = p.getCameraConnection().streamManager.stream; if (stream) { try { @@ -563,7 +580,14 @@ export class OpenViduService { } } - private async createMediaStream(pp: PublisherProperties): Promise { + + /** + * @internal + * @param pp {@link PublisherProperties} + * @returns Promise + */ + async createMediaStream(pp: PublisherProperties): Promise { + const participantService = this.injector.get(ParticipantService); const currentCameraSelected = this.deviceService.getCameraSelected(); const currentMicSelected = this.deviceService.getMicrophoneSelected(); const isReplacingAudio = Boolean(pp.audioSource); @@ -571,7 +595,7 @@ export class OpenViduService { try { const trackType = isReplacingAudio ? 'audio' : 'video'; - this.forceStopMediaTracks(this.participantService.getMyCameraPublisher().stream.getMediaStream(), trackType); + this.forceStopMediaTracks(participantService.getMyCameraPublisher().stream.getMediaStream(), trackType); return this.OV.getUserMedia(pp); } catch (error) { console.warn('Error creating MediaStream', error); @@ -594,6 +618,7 @@ export class OpenViduService { * @internal */ needSendNicknameSignal(): boolean { + const participantService = this.injector.get(ParticipantService); let oldNickname: string = ""; try { const connData = JSON.parse(this.cleanConnectionData(this.webcamSession.connection.data)); @@ -601,7 +626,7 @@ export class OpenViduService { } catch (error) { this.log.e(error); } finally { - return oldNickname !== this.participantService.getMyNickname(); + return oldNickname !== participantService.getMyNickname(); } } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts index 164ca2d5..fa8dfc68 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/services/participant/participant.service.ts @@ -11,7 +11,9 @@ import { } from '../../models/participant.model'; import { VideoType } from '../../models/video-type.model'; import { OpenViduAngularConfigService } from '../config/openvidu-angular.config.service'; +import { DeviceService } from '../device/device.service'; import { LoggerService } from '../logger/logger.service'; +import { OpenViduService } from '../openvidu/openvidu.service'; @Injectable({ providedIn: 'root' @@ -39,7 +41,12 @@ export class ParticipantService { /** * @internal */ - constructor(protected openviduAngularConfigSrv: OpenViduAngularConfigService, protected loggerSrv: LoggerService) { + constructor( + protected openviduAngularConfigSrv: OpenViduAngularConfigService, + private openviduService: OpenViduService, + private deviceService: DeviceService, + protected loggerSrv: LoggerService + ) { this.log = this.loggerSrv.get('ParticipantService'); this.localParticipantObs = this._localParticipant.asObservable(); this.remoteParticipantsObs = this._remoteParticipants.asObservable(); @@ -58,7 +65,40 @@ export class ParticipantService { } /** - * Publish or unpublish the audio stream (if available). + * Publish or unpublish the local participant video stream (if available). + * It hides the camera stream (while muted) if screen is sharing. + * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishVideo publishVideo} + * + */ + async publishVideo(publish: boolean): Promise { + const publishAudio = this.isMyAudioActive(); + const cameraPublisher = this.getMyCameraPublisher(); + const screenPublisher = this.getMyScreenPublisher(); + + // Disabling webcam + if (this.haveICameraAndScreenActive()) { + await this.publishVideoAux(cameraPublisher, publish); + this.disableWebcamStream(); + this.openviduService.unpublishCamera(cameraPublisher); + this.publishAudioAux(screenPublisher, publishAudio); + } else if (this.isOnlyMyScreenActive()) { + // Enabling webcam + const hasAudio = this.hasScreenAudioActive(); + const sessionId = await this.openviduService.connectWebcamSession(this.getMyNickname(), this.getLocalParticipant().id); + if (sessionId) this.setMyCameraConnectionId(sessionId); + await this.openviduService.publishCamera(cameraPublisher); + await this.publishVideoAux(cameraPublisher, true); + this.publishAudioAux(screenPublisher, false); + this.publishAudioAux(cameraPublisher, hasAudio); + this.enableWebcamStream(); + } else { + // Muting/unmuting webcam + await this.publishVideoAux(cameraPublisher, publish); + } + } + + /** + * Publish or unpublish the local participant audio stream (if available). * See openvidu-browser {@link https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/Publisher.html#publishAudio publishAudio}. * */ @@ -277,6 +317,24 @@ export class ParticipantService { } } + /** + * @internal + */ + private async publishVideoAux(publisher: Publisher, publish: boolean): Promise { + if (!!publisher) { + let resource: boolean | MediaStreamTrack = true; + if (publish) { + // Forcing restoration with a custom media stream (the older one instead the default) + const currentDeviceId = this.deviceService.getCameraSelected()?.device; + const mediaStream = await this.openviduService.createMediaStream({ videoSource: currentDeviceId, audioSource: false }); + resource = mediaStream.getVideoTracks()[0]; + } + + await publisher.publishVideo(publish, resource); + this.updateLocalParticipant(); + } + } + /** * REMOTE USERS */