2022-02-15 15:52:59 +01:00
|
|
|
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
2022-03-07 15:50:27 +01:00
|
|
|
import { Subscription } from 'rxjs';
|
2022-01-19 17:24:11 +01:00
|
|
|
import { MatMenuPanel, MatMenuTrigger } from '@angular/material/menu';
|
|
|
|
import { VideoSizeIcon } from '../../models/icon.model';
|
|
|
|
import { ScreenType, VideoType } from '../../models/video-type.model';
|
|
|
|
import { CdkOverlayService } from '../../services/cdk-overlay/cdk-overlay.service';
|
2022-02-15 15:52:59 +01:00
|
|
|
import { OpenViduService } from '../../services/openvidu/openvidu.service';
|
2022-01-19 17:24:11 +01:00
|
|
|
import { LayoutService } from '../../services/layout/layout.service';
|
|
|
|
import { StorageService } from '../../services/storage/storage.service';
|
|
|
|
import { Signal } from '../../models/signal.model';
|
|
|
|
import { PublisherProperties } from 'openvidu-browser';
|
2022-01-26 11:30:30 +01:00
|
|
|
import { StreamModel } from '../../models/participant.model';
|
2022-01-19 17:24:11 +01:00
|
|
|
import { ParticipantService } from '../../services/participant/participant.service';
|
2022-03-07 15:50:27 +01:00
|
|
|
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
|
2022-01-19 17:24:11 +01:00
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* The **StreamComponent** is hosted inside of the {@link LayoutComponent}.
|
|
|
|
* It is in charge of displaying the participant video stream in the videoconference layout.
|
|
|
|
*
|
|
|
|
* <div class="custom-table-container">
|
|
|
|
* <div>
|
|
|
|
* <h3>API Directives</h3>
|
|
|
|
*
|
|
|
|
* This component allows us to show or hide certain HTML elements with the following {@link https://angular.io/guide/attribute-directives Angular attribute directives}
|
|
|
|
* with the aim of fully customizing the StreamComponent.
|
|
|
|
*
|
|
|
|
* | **Parameter** | **Type** | **Reference** |
|
|
|
|
* | :----------------------------: | :-------: | :---------------------------------------------: |
|
|
|
|
* | **displayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} |
|
|
|
|
* | **displayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} |
|
|
|
|
* | **settingsButton** | `boolean` | {@link StreamSettingsButtonDirective} |
|
|
|
|
*
|
|
|
|
* <p class="component-link-text">
|
|
|
|
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
|
|
|
|
* </p>
|
|
|
|
* </div>
|
|
|
|
*
|
|
|
|
* <div>
|
|
|
|
* <h3>OpenVidu Angular Directives</h3>
|
|
|
|
*
|
|
|
|
* The StreamComponent can be replaced with a custom component. It provides us the following {@link https://angular.io/guide/structural-directives Angular structural directives}
|
|
|
|
* for doing this.
|
|
|
|
*
|
|
|
|
* | **Directive** | **Reference** |
|
|
|
|
* |:----------------------------------:|:---------------------------------------------:|
|
|
|
|
* | ***ovStream** | {@link StreamDirective} |
|
|
|
|
*
|
|
|
|
* <p class="component-link-text">
|
|
|
|
* <span class="italic">See all {@link OpenViduAngularDirectiveModule OpenVidu Angular Directives}</span>
|
|
|
|
* </p>
|
|
|
|
* </div>
|
|
|
|
*
|
|
|
|
* </div>
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
@Component({
|
2022-01-26 11:30:30 +01:00
|
|
|
selector: 'ov-stream',
|
|
|
|
templateUrl: './stream.component.html',
|
|
|
|
styleUrls: ['./stream.component.css']
|
2022-01-19 17:24:11 +01:00
|
|
|
})
|
2022-01-26 11:30:30 +01:00
|
|
|
export class StreamComponent implements OnInit {
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
@ViewChild(MatMenuTrigger) public menuTrigger: MatMenuTrigger;
|
2022-03-23 13:48:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
@ViewChild('menu') menu: MatMenuPanel;
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
videoSizeIconEnum = VideoSizeIcon;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
videoTypeEnum = VideoType;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
videoSizeIcon: VideoSizeIcon = VideoSizeIcon.BIG;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
toggleNickname: boolean;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-02-07 10:43:21 +01:00
|
|
|
_stream: StreamModel;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-02-28 14:37:35 +01:00
|
|
|
nickname: string;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
isMinimal: boolean = false;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
showNickname: boolean = true;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
showAudioDetection: boolean = true;
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-03-07 15:50:27 +01:00
|
|
|
showSettingsButton: boolean = true;
|
2022-07-05 17:48:25 +02:00
|
|
|
showVideo: boolean;
|
2022-03-07 15:50:27 +01:00
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-02-07 15:40:05 +01:00
|
|
|
@ViewChild('streamContainer', { static: false, read: ElementRef })
|
|
|
|
set streamContainer(streamContainer: ElementRef) {
|
|
|
|
setTimeout(() => {
|
|
|
|
if (streamContainer) {
|
|
|
|
this._streamContainer = streamContainer;
|
2022-02-24 13:02:00 +01:00
|
|
|
// This is a workaround for fixing a layout bug which provide a bad UX with each new elements created.
|
2022-07-05 17:48:25 +02:00
|
|
|
setTimeout(() => {
|
|
|
|
this.showVideo = true;
|
|
|
|
}, 100);
|
2022-02-07 15:40:05 +01:00
|
|
|
}
|
|
|
|
}, 0);
|
|
|
|
}
|
2022-01-19 17:24:11 +01:00
|
|
|
|
|
|
|
@Input()
|
2022-02-07 10:43:21 +01:00
|
|
|
set stream(stream: StreamModel) {
|
|
|
|
this._stream = stream;
|
2022-02-07 15:40:05 +01:00
|
|
|
this.checkVideoEnlarged();
|
2022-03-07 15:50:27 +01:00
|
|
|
if (this._stream.participant) {
|
2022-02-28 14:37:35 +01:00
|
|
|
this.nickname = this._stream.participant.nickname;
|
2022-02-28 13:48:58 +01:00
|
|
|
}
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
@ViewChild('nicknameInput')
|
|
|
|
set nicknameInputElement(element: ElementRef) {
|
|
|
|
setTimeout(() => {
|
|
|
|
element?.nativeElement.focus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
private _streamContainer: ElementRef;
|
|
|
|
private minimalSub: Subscription;
|
|
|
|
private displayParticipantNameSub: Subscription;
|
|
|
|
private displayAudioDetectionSub: Subscription;
|
|
|
|
private settingsButtonSub: Subscription;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
|
|
|
constructor(
|
|
|
|
protected openviduService: OpenViduService,
|
|
|
|
protected layoutService: LayoutService,
|
|
|
|
protected participantService: ParticipantService,
|
|
|
|
protected storageService: StorageService,
|
|
|
|
protected cdkSrv: CdkOverlayService,
|
|
|
|
private libService: OpenViduAngularConfigService
|
|
|
|
) {}
|
|
|
|
|
2022-01-19 17:24:11 +01:00
|
|
|
ngOnInit() {
|
2022-03-07 15:50:27 +01:00
|
|
|
this.subscribeToStreamDirectives();
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ngOnDestroy() {
|
|
|
|
this.cdkSrv.setSelector('body');
|
2022-03-07 15:50:27 +01:00
|
|
|
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
|
|
|
|
if (this.displayAudioDetectionSub) this.displayAudioDetectionSub.unsubscribe();
|
|
|
|
if (this.displayParticipantNameSub) this.displayParticipantNameSub.unsubscribe();
|
2022-03-23 13:48:17 +01:00
|
|
|
if (this.minimalSub) this.minimalSub.unsubscribe();
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-02-25 13:53:49 +01:00
|
|
|
toggleVideoEnlarged() {
|
2022-02-07 10:43:21 +01:00
|
|
|
if (!!this._stream.streamManager?.stream?.connection?.connectionId) {
|
2022-02-15 15:52:59 +01:00
|
|
|
if (this.openviduService.isMyOwnConnection(this._stream.streamManager?.stream?.connection?.connectionId)) {
|
2022-02-07 15:40:05 +01:00
|
|
|
this.participantService.toggleMyVideoEnlarged(this._stream.streamManager?.stream?.connection?.connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
} else {
|
2022-02-07 15:40:05 +01:00
|
|
|
this.participantService.toggleRemoteVideoEnlarged(this._stream.streamManager?.stream?.connection?.connectionId);
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.layoutService.update();
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
toggleVideoMenu(event) {
|
|
|
|
if (this.menuTrigger.menuOpen) {
|
|
|
|
this.menuTrigger.closeMenu();
|
|
|
|
return;
|
|
|
|
}
|
2022-02-07 10:43:21 +01:00
|
|
|
this.cdkSrv.setSelector('#container-' + this._stream.streamManager?.stream?.streamId);
|
2022-01-19 17:24:11 +01:00
|
|
|
this.menuTrigger.openMenu();
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
|
|
|
toggleMuteForcibly() {
|
2022-04-01 11:05:25 +02:00
|
|
|
this.participantService.setRemoteMutedForcibly(this._stream.participant.id, !this._stream.participant.isMutedForcibly);
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
toggleNicknameForm() {
|
2022-06-16 14:01:07 +02:00
|
|
|
if (this._stream?.participant?.local) {
|
2022-01-19 17:24:11 +01:00
|
|
|
this.toggleNickname = !this.toggleNickname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-02-28 14:37:35 +01:00
|
|
|
updateNickname(event) {
|
|
|
|
if (event?.keyCode === 13 || event?.type === 'focusout') {
|
2022-03-07 15:50:27 +01:00
|
|
|
if (!!this.nickname) {
|
2022-02-28 14:37:35 +01:00
|
|
|
this.participantService.setMyNickname(this.nickname);
|
|
|
|
this.storageService.setNickname(this.nickname);
|
|
|
|
this.openviduService.sendSignal(Signal.NICKNAME_CHANGED, undefined, { clientData: this.nickname });
|
|
|
|
}
|
2022-01-19 17:24:11 +01:00
|
|
|
this.toggleNicknameForm();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:48:17 +01:00
|
|
|
/**
|
|
|
|
* @ignore
|
|
|
|
*/
|
2022-01-19 17:24:11 +01:00
|
|
|
async replaceScreenTrack() {
|
|
|
|
const properties: PublisherProperties = {
|
|
|
|
videoSource: ScreenType.SCREEN,
|
|
|
|
publishVideo: true,
|
2022-02-25 11:19:21 +01:00
|
|
|
publishAudio: !this.participantService.isMyCameraActive(),
|
2022-01-19 17:24:11 +01:00
|
|
|
mirror: false
|
|
|
|
};
|
2022-02-21 17:33:23 +01:00
|
|
|
await this.openviduService.replaceTrack(VideoType.SCREEN, properties);
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
|
|
|
|
2022-03-07 15:50:27 +01:00
|
|
|
private checkVideoEnlarged() {
|
2022-02-07 15:40:05 +01:00
|
|
|
this.videoSizeIcon = this._stream.videoEnlarged ? VideoSizeIcon.NORMAL : VideoSizeIcon.BIG;
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|
2022-03-07 15:50:27 +01:00
|
|
|
|
|
|
|
private subscribeToStreamDirectives() {
|
|
|
|
this.minimalSub = this.libService.minimalObs.subscribe((value: boolean) => {
|
|
|
|
this.isMinimal = value;
|
|
|
|
});
|
|
|
|
this.displayParticipantNameSub = this.libService.displayParticipantNameObs.subscribe((value: boolean) => {
|
|
|
|
this.showNickname = value;
|
|
|
|
// this.cd.markForCheck();
|
|
|
|
});
|
|
|
|
this.displayAudioDetectionSub = this.libService.displayAudioDetectionObs.subscribe((value: boolean) => {
|
|
|
|
this.showAudioDetection = value;
|
|
|
|
// this.cd.markForCheck();
|
|
|
|
});
|
2022-06-16 14:01:07 +02:00
|
|
|
this.settingsButtonSub = this.libService.streamSettingsButtonObs.subscribe((value: boolean) => {
|
2022-03-07 15:50:27 +01:00
|
|
|
this.showSettingsButton = value;
|
|
|
|
// this.cd.markForCheck();
|
|
|
|
});
|
|
|
|
}
|
2022-01-19 17:24:11 +01:00
|
|
|
}
|