openvidu/openvidu-components-angular/projects/openvidu-angular/src/lib/components/user-settings/user-settings.component.ts

310 lines
10 KiB
TypeScript

import { Component, ElementRef, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { OpenViduErrorName } from 'openvidu-browser/lib/OpenViduInternal/Enums/OpenViduError';
import { Publisher, PublisherProperties } from 'openvidu-browser';
import { ILogger } from '../../models/logger.model';
import { CustomDevice } from '../../models/device.model';
import { Storage } from '../../models/storage.model';
import { ScreenType } from '../../models/video-type.model';
import { NicknameMatcher } from '../../matchers/nickname.matcher';
import { DeviceService } from '../../services/device/device.service';
import { LoggerService } from '../../services/logger/logger.service';
import { StorageService } from '../../services/storage/storage.service';
import { WebrtcService } from '../../services/webrtc/webrtc.service';
import { ActionService } from '../../services/action/action.service';
import { ParticipantService } from '../../services/participant/participant.service';
import { ParticipantAbstractModel } from '../../models/participant.model';
@Component({
selector: 'ov-user-settings',
templateUrl: './user-settings.component.html',
styleUrls: ['./user-settings.component.css']
})
export class UserSettingsComponent implements OnInit, OnDestroy {
@ViewChild('bodyCard') bodyCard: ElementRef;
@Input() sessionId: string;
@Output() onJoinClicked = new EventEmitter<any>();
@Output() onCloseClicked = new EventEmitter<any>();
cameras: CustomDevice[];
microphones: CustomDevice[];
cameraSelected: CustomDevice;
microphoneSelected: CustomDevice;
isVideoActive = true;
isAudioActive = true;
screenShareEnabled: boolean;
localParticipant: ParticipantAbstractModel;
columns: number;
nicknameFormControl = new FormControl('', [Validators.maxLength(25), Validators.required]);
matcher = new NicknameMatcher();
hasVideoDevices: boolean;
hasAudioDevices: boolean;
isLoading = true;
private log: ILogger;
private oVUsersSubscription: Subscription;
private screenShareStateSubscription: Subscription;
constructor(
private actionService: ActionService,
private deviceSrv: DeviceService,
private loggerSrv: LoggerService,
private openViduWebRTCService: WebrtcService,
private participantService: ParticipantService,
private storageSrv: StorageService
) {
this.log = this.loggerSrv.get('UserSettingsComponent');
}
@HostListener('window:beforeunload')
beforeunloadHandler() {
this.close();
}
async ngOnInit() {
this.subscribeToLocalParticipantEvents();
this.openViduWebRTCService.initialize();
await this.deviceSrv.initializeDevices();
const nickname = this.storageSrv.get(Storage.USER_NICKNAME) || this.generateRandomNickname();
this.nicknameFormControl.setValue(nickname);
this.columns = window.innerWidth > 900 ? 2 : 1;
this.setDevicesInfo();
if (this.hasAudioDevices || this.hasVideoDevices) {
await this.initwebcamPublisher();
}
this.isLoading = false;
}
ngOnDestroy() {
if (this.oVUsersSubscription) {
this.oVUsersSubscription.unsubscribe();
}
if (this.screenShareStateSubscription) {
this.screenShareStateSubscription.unsubscribe();
}
this.deviceSrv.clear();
}
async onCameraSelected(event: any) {
const videoSource = event?.value;
if (!!videoSource) {
// Is New deviceId different from the old one?
if (this.deviceSrv.needUpdateVideoTrack(videoSource)) {
const mirror = this.deviceSrv.cameraNeedsMirror(videoSource);
await this.openViduWebRTCService.republishTrack(videoSource, null, mirror);
this.deviceSrv.setCameraSelected(videoSource);
this.cameraSelected = this.deviceSrv.getCameraSelected();
}
// Publish Webcam video
this.openViduWebRTCService.publishVideo(this.participantService.getMyCameraPublisher(), true);
this.isVideoActive = true;
return;
}
// Unpublish webcam video
this.openViduWebRTCService.publishVideo(this.participantService.getMyCameraPublisher(), false);
this.isVideoActive = false;
}
async onMicrophoneSelected(event: any) {
const audioSource = event?.value;
if (!!audioSource) {
// Is New deviceId different than older?
if (this.deviceSrv.needUpdateAudioTrack(audioSource)) {
const mirror = this.deviceSrv.cameraNeedsMirror(this.cameraSelected.device);
await this.openViduWebRTCService.republishTrack(null, audioSource, mirror);
this.deviceSrv.setMicSelected(audioSource);
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
}
// Publish microphone
this.publishAudio(true);
this.isAudioActive = true;
return;
}
// Unpublish microhpone
this.publishAudio(false);
this.isAudioActive = false;
}
toggleCam() {
this.isVideoActive = !this.isVideoActive;
this.openViduWebRTCService.publishVideo(this.participantService.getMyCameraPublisher(), this.isVideoActive);
if (this.participantService.areBothEnabled()) {
this.participantService.disableWebcamUser();
this.openViduWebRTCService.publishAudio(this.participantService.getMyScreenPublisher(), this.isAudioActive);
} else if (this.participantService.isOnlyMyScreenEnabled()) {
this.participantService.enableWebcamUser();
}
}
toggleScreenShare() {
// Disabling screenShare
if (this.participantService.areBothEnabled()) {
this.participantService.disableScreenUser();
return;
}
// Enabling screenShare
if (this.participantService.isOnlyMyCameraEnabled()) {
const willThereBeWebcam = this.participantService.isMyCameraEnabled() && this.participantService.hasCameraVideoActive();
const hasAudio = willThereBeWebcam ? false : this.hasAudioDevices && this.isAudioActive;
const properties: PublisherProperties = {
videoSource: ScreenType.SCREEN,
audioSource: this.hasAudioDevices ? undefined : null,
publishVideo: true,
publishAudio: hasAudio,
mirror: false
};
const screenPublisher = this.openViduWebRTCService.initPublisher(undefined, properties);
screenPublisher.on('accessAllowed', (event) => {
screenPublisher.stream
.getMediaStream()
.getVideoTracks()[0]
.addEventListener('ended', () => {
this.log.d('Clicked native stop button. Stopping screen sharing');
this.toggleScreenShare();
});
this.participantService.enableScreenUser(screenPublisher);
if (!this.participantService.hasCameraVideoActive()) {
this.participantService.disableWebcamUser();
}
});
screenPublisher.on('accessDenied', (error: any) => {
if (error && error.name === 'SCREEN_SHARING_NOT_SUPPORTED') {
this.actionService.openDialog('Error sharing screen', 'Your browser does not support screen sharing');
}
});
return;
}
// Disabling screnShare and enabling webcam
this.participantService.enableWebcamUser();
this.participantService.disableScreenUser();
}
toggleMic() {
this.isAudioActive = !this.isAudioActive;
this.publishAudio(this.isAudioActive);
}
eventKeyPress(event) {
if (event && event.keyCode === 13 && this.nicknameFormControl.valid) {
this.joinSession();
}
}
onResize(event) {
this.columns = event.target.innerWidth > 900 ? 2 : 1;
}
joinSession() {
if (this.nicknameFormControl.valid) {
const nickname = this.nicknameFormControl.value;
this.participantService.setNickname(this.participantService.getMyCameraConnectionId(), nickname);
this.storageSrv.set(Storage.USER_NICKNAME, nickname);
return this.onJoinClicked.emit();
}
this.scrollToBottom();
}
close() {
this.onCloseClicked.emit();
}
private setDevicesInfo() {
this.hasVideoDevices = this.deviceSrv.hasVideoDeviceAvailable();
this.hasAudioDevices = this.deviceSrv.hasAudioDeviceAvailable();
this.microphones = this.deviceSrv.getMicrophones();
this.cameras = this.deviceSrv.getCameras();
this.cameraSelected = this.deviceSrv.getCameraSelected();
this.microphoneSelected = this.deviceSrv.getMicrophoneSelected();
}
private scrollToBottom(): void {
try {
this.bodyCard.nativeElement.scrollTop = this.bodyCard.nativeElement.scrollHeight;
} catch (err) {}
}
private publishAudio(audio: boolean) {
this.participantService.isMyCameraEnabled()
? this.openViduWebRTCService.publishAudio(this.participantService.getMyCameraPublisher(), audio)
: this.openViduWebRTCService.publishAudio(this.participantService.getMyScreenPublisher(), audio);
}
private subscribeToLocalParticipantEvents() {
this.oVUsersSubscription = this.participantService.localParticipantObs.subscribe((p) => {
this.localParticipant = p;
});
this.screenShareStateSubscription = this.participantService.screenShareState.subscribe((enabled) => {
this.screenShareEnabled = enabled;
});
}
private async initwebcamPublisher() {
const publisher = await this.openViduWebRTCService.initDefaultPublisher(undefined);
if (publisher) {
this.handlePublisherSuccess(publisher);
this.handlePublisherError(publisher);
}
}
private handlePublisherSuccess(publisher: Publisher) {
publisher.once('accessAllowed', async () => {
if (this.deviceSrv.areEmptyLabels()) {
await this.deviceSrv.forceUpdate();
if (this.hasAudioDevices) {
const audioLabel = publisher?.stream?.getMediaStream()?.getAudioTracks()[0]?.label;
this.deviceSrv.setMicSelected(audioLabel);
}
if (this.hasVideoDevices) {
const videoLabel = publisher?.stream?.getMediaStream()?.getVideoTracks()[0]?.label;
this.deviceSrv.setCameraSelected(videoLabel);
}
this.setDevicesInfo();
}
});
}
private handlePublisherError(publisher: Publisher) {
publisher.once('accessDenied', (e: any) => {
let message: string;
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
this.hasVideoDevices = false;
this.deviceSrv.disableVideoDevices();
return this.initwebcamPublisher();
}
if (e.name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
message = 'Access to media devices was not allowed.';
this.hasVideoDevices = false;
this.hasAudioDevices = false;
this.deviceSrv.disableVideoDevices();
this.deviceSrv.disableAudioDevices();
return this.initwebcamPublisher();
} else if (e.name === OpenViduErrorName.NO_INPUT_SOURCE_SET) {
message = 'No video or audio devices have been found. Please, connect at least one.';
}
this.actionService.openDialog(e.name.replace(/_/g, ' '), message, true);
this.log.e(e.message);
});
}
private generateRandomNickname(): string {
return 'OpenVidu_User' + Math.floor(Math.random() * 100);
}
}