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 71e83ef4..0a7cdd27 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 @@ -64,6 +64,8 @@ export class SessionComponent implements OnInit { @Input() usedInPrejoinPage = false; @Output() onSessionCreated = new EventEmitter(); + @Output() onNodeCrashed = new EventEmitter(); + session: Session; sessionScreen: Session; @@ -378,7 +380,14 @@ export class SessionComponent implements OnInit { this.actionService.closeDialog(); }); this.session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => { - if (event.reason === 'networkDisconnect') { + if (event.reason === 'nodeCrashed') { + this.actionService.openDialog( + this.translateService.translate('ERRORS.CONNECTION'), + this.translateService.translate('ERRORS.RECONNECT'), + false + ); + this.onNodeCrashed.emit(); + } else if (event.reason === 'networkDisconnect') { this.actionService.closeDialog(); this.leaveSession(); } 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 15c45b44..dc536b74 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 @@ -68,7 +68,6 @@ export class VideoDevicesComponent implements OnInit, OnDestroy { this.videoMuteChanging = true; const publish = this.isVideoMuted; await this.openviduService.publishVideo(publish); - this.storageSrv.setVideoMuted(this.isVideoMuted); if (this.isVideoMuted && this.panelService.isExternalPanelOpened()) { this.panelService.togglePanel(PanelType.BACKGROUND_EFFECTS); } @@ -107,6 +106,7 @@ export class VideoDevicesComponent implements OnInit, OnDestroy { this.localParticipantSubscription = this.participantService.localParticipantObs.subscribe((p: ParticipantAbstractModel) => { if (p) { this.isVideoMuted = !p.isCameraVideoActive(); + this.storageSrv.setVideoMuted(this.isVideoMuted); } }); } 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 66f702a6..76a72bbf 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 @@ -39,6 +39,7 @@ import { OpenViduService } from '../../services/openvidu/openvidu.service'; import { ParticipantService } from '../../services/participant/participant.service'; import { PlatformService } from '../../services/platform/platform.service'; import { RecordingService } from '../../services/recording/recording.service'; +import { StorageService } from '../../services/storage/storage.service'; import { TranslateService } from '../../services/translate/translate.service'; /** @@ -383,7 +384,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit { private libService: OpenViduAngularConfigService, private platformService: PlatformService, private recordingService: RecordingService, - private translateService: TranslateService + private translateService: TranslateService, + private storageSrv: StorageService ) { this.log = this.loggerSrv.get('ToolbarComponent'); } @@ -627,6 +629,8 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit { this.isAudioActive = p.hasAudioActive(); this.isScreenShareActive = p.isScreenActive(); this.isSessionCreator = p.getRole() === OpenViduRole.MODERATOR; + this.storageSrv.setAudioMuted(!this.isAudioActive); + this.storageSrv.setVideoMuted(!this.isWebcamVideoActive); this.cd.markForCheck(); } }); @@ -637,7 +641,9 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit { .pipe(skip(1)) .subscribe((ev: { info: RecordingInfo; time?: Date }) => { this.recordingStatus = ev.info.status; - this.recordingTime = ev.time; + if (ev.time) { + this.recordingTime = ev.time; + } this.cd.markForCheck(); }); } diff --git a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html index a7981693..fef8d3ff 100644 --- a/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html +++ b/openvidu-components-angular/projects/openvidu-angular/src/lib/components/videoconference/videoconference.component.html @@ -15,7 +15,7 @@
- + 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 ef785e4b..7edafdd5 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 @@ -383,6 +383,13 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni */ @Output() onParticipantCreated: EventEmitter = new EventEmitter(); + /** + * Provides event notifications that fire when Media Node crash OpenVidu Pro from the OpenVidu Pro cluster. + * OpenVidu Pro delegates the recovery of the sessions to the application in the event of a Media Node crash. + * See {@link https://docs.openvidu.io/en/stable/openvidu-pro/fault-tolerance/ OpenVidu Pro Fault tolerance}. + */ + @Output() onNodeCrashed: EventEmitter = new EventEmitter(); + /** * @internal */ @@ -414,6 +421,7 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni * @internal */ loading = true; + private nodeCrashed: boolean = false; private externalParticipantName: string; private prejoinSub: Subscription; private participantNameSub: Subscription; @@ -440,40 +448,6 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni 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.openviduService.initialize(); - if (this.deviceSrv.hasVideoDeviceAvailable() || this.deviceSrv.hasAudioDeviceAvailable()) { - await this.initwebcamPublisher(); - } - this.isSessionInitialized = true; - this.onParticipantCreated.emit(this.participantService.getLocalParticipant()); - this.loading = false; - this.participantReady = true; - } - - private async initwebcamPublisher(): Promise { - return new Promise(async (resolve, reject) => { - try { - const publisher = await this.openviduService.initDefaultPublisher(); - - if (publisher) { - publisher.once('accessDenied', async (e: any) => { - await this.handlePublisherError(e); - resolve(); - }); - publisher.once('accessAllowed', () => resolve()); - } - } catch (error) { - this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true); - this.log.e(error); - reject(); - } - }); - } - async ngOnDestroy() { if (this.prejoinSub) this.prejoinSub.unsubscribe(); if (this.participantNameSub) this.participantNameSub.unsubscribe(); @@ -566,6 +540,44 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni } } + 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.openviduService.initialize(); + if (this.deviceSrv.hasVideoDeviceAvailable() || this.deviceSrv.hasAudioDeviceAvailable()) { + await this.initwebcamPublisher(); + } + this.isSessionInitialized = true; + this.onParticipantCreated.emit(this.participantService.getLocalParticipant()); + this.loading = false; + this.participantReady = true; + if (this.nodeCrashed) { + this.nodeCrashed = false; + this.actionService.closeDialog(); + } + } + + private async initwebcamPublisher(): Promise { + return new Promise(async (resolve, reject) => { + try { + const publisher = await this.openviduService.initDefaultPublisher(); + + if (publisher) { + publisher.once('accessDenied', async (e: any) => { + await this.handlePublisherError(e); + resolve(); + }); + publisher.once('accessAllowed', () => resolve()); + } + } catch (error) { + this.actionService.openDialog(error.name.replace(/_/g, ' '), error.message, true); + this.log.e(error); + reject(); + } + }); + } + /** * @internal */ @@ -661,6 +673,14 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni this.onSessionCreated.emit(session); } + /** + * @internal + */ + _onNodeCrashed() { + this.nodeCrashed = true; + this.onNodeCrashed.emit(); + } + private async handlePublisherError(e: any): Promise { let message: string = ''; if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) { diff --git a/openvidu-components-angular/src/app/openvidu-call/call.component.html b/openvidu-components-angular/src/app/openvidu-call/call.component.html index b05910f9..c3119d92 100644 --- a/openvidu-components-angular/src/app/openvidu-call/call.component.html +++ b/openvidu-components-angular/src/app/openvidu-call/call.component.html @@ -13,10 +13,11 @@ (onActivitiesPanelStartRecordingClicked)="onStartRecordingClicked()" (onActivitiesPanelStopRecordingClicked)="onStopRecordingClicked()" (onActivitiesPanelDeleteRecordingClicked)="onDeleteRecordingClicked($event)" + (onNodeCrashed)="onNodeCrashed()" [minimal]="false" [prejoin]="false" [videoMuted]="false" - [audioMuted]="true" + [audioMuted]="false" [activitiesPanelRecordingActivity]="true" [recordingActivityRecordingsList]="recordingList" [recordingActivityRecordingError]="recordingError" diff --git a/openvidu-components-angular/src/app/openvidu-call/call.component.ts b/openvidu-components-angular/src/app/openvidu-call/call.component.ts index b3496881..f313074e 100644 --- a/openvidu-components-angular/src/app/openvidu-call/call.component.ts +++ b/openvidu-components-angular/src/app/openvidu-call/call.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { RecordingInfo, TokenModel, RecordingService } from 'openvidu-angular'; +import { RecordingInfo, RecordingService, TokenModel } from 'openvidu-angular'; import { RestService } from '../services/rest.service'; @Component({ @@ -21,13 +21,12 @@ export class CallComponent implements OnInit { constructor(private restService: RestService, private recordingService: RecordingService) { } async ngOnInit() { - const response = await this.restService.getTokensFromBackend(this.sessionId); - this.recordingList = response.recordings; - this.tokens = { - webcam: response.cameraToken, - screen: response.screenToken - }; - console.log(this.tokens); + await this.requestForTokens(); + } + + async onNodeCrashed() { + // Request the tokens again for reconnect to the session + await this.requestForTokens(); } onJoinClicked() { @@ -115,4 +114,16 @@ export class CallComponent implements OnInit { } } + private async requestForTokens() { + const response = await this.restService.getTokensFromBackend(this.sessionId); + this.recordingList = response.recordings; + this.tokens = { + webcam: response.cameraToken, + screen: response.screenToken + }; + + console.log('Token requested: ', this.tokens); + + } + }