mirror of https://github.com/OpenVidu/openvidu.git
openvidu-components: Added fault tolerant support
On media node crashed, openvidu-copmponents emits an event for requesting new tokens to the client.pull/758/head
parent
dd62c033df
commit
fb5c56cd87
|
@ -64,6 +64,8 @@ export class SessionComponent implements OnInit {
|
|||
@Input() usedInPrejoinPage = false;
|
||||
@Output() onSessionCreated = new EventEmitter<any>();
|
||||
|
||||
@Output() onNodeCrashed = new EventEmitter<any>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</div>
|
||||
|
||||
<div [@inOutAnimation] id="vc-container" *ngIf="showVideoconference || (!showPrejoin && !loading && !error)">
|
||||
<ov-session (onSessionCreated)="_onSessionCreated($event)" *ngIf="isSessionInitialized">
|
||||
<ov-session *ngIf="isSessionInitialized && !nodeCrashed" (onSessionCreated)="_onSessionCreated($event)" (onNodeCrashed)="_onNodeCrashed()">
|
||||
<ng-template #toolbar>
|
||||
<ng-container *ngIf="openviduAngularToolbarTemplate">
|
||||
<ng-container *ngTemplateOutlet="openviduAngularToolbarTemplate"></ng-container>
|
||||
|
|
|
@ -383,6 +383,13 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
*/
|
||||
@Output() onParticipantCreated: EventEmitter<ParticipantAbstractModel> = new EventEmitter<ParticipantAbstractModel>();
|
||||
|
||||
/**
|
||||
* 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<void> = new EventEmitter<void>();
|
||||
|
||||
/**
|
||||
* @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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
let message: string = '';
|
||||
if (e.name === OpenViduErrorName.DEVICE_ALREADY_IN_USE) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue