mirror of https://github.com/OpenVidu/openvidu.git
310 lines
10 KiB
TypeScript
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);
|
|
}
|
|
}
|