openvidu-components: Added network control directives

Allowed frameRate, resolution and simulcast customization
pull/816/head
Carlos Santos 2023-07-03 17:52:26 +02:00
parent 6bf48078a5
commit 9a6a4cbf25
10 changed files with 336 additions and 37 deletions

View File

@ -30,6 +30,8 @@ import { StorageService } from '../../services/storage/storage.service';
* | **displayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} |
* | **displayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} |
* | **settingsButton** | `boolean` | {@link StreamSettingsButtonDirective} |
* | **resolution** | `string` | {@link StreamResolutionDirective} |
* | **frameRate** | `number` | {@link StreamFrameRateDirective} |
*
* <p class="component-link-text">
* <span class="italic">See all {@link ApiDirectiveModule API Directives}</span>
@ -60,7 +62,7 @@ import { StorageService } from '../../services/storage/storage.service';
animations: [
trigger('posterAnimation', [
transition(':enter', [style({ opacity: 0 }), animate('100ms', style({ opacity: 1 }))]),
transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))]),
transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))])
])
]
})
@ -140,6 +142,7 @@ export class StreamComponent implements OnInit {
if (this._stream.participant) {
this.nickname = this._stream.participant.nickname;
}
}
/**
@ -174,6 +177,12 @@ export class StreamComponent implements OnInit {
this.subscribeToStreamDirectives();
}
async ngAfterViewInit() {
if (this._stream.streamManager) {
await this.openviduService.updateVideoEncodingParameters(this._stream.streamManager);
}
}
ngOnDestroy() {
this.cdkSrv.setSelector('body');
if (this.settingsButtonSub) this.settingsButtonSub.unsubscribe();
@ -214,7 +223,7 @@ export class StreamComponent implements OnInit {
* @ignore
*/
toggleMuteForcibly() {
if(this._stream.participant){
if (this._stream.participant) {
this.participantService.setRemoteMutedForcibly(this._stream.participant.id, !this._stream.participant.isMutedForcibly);
}
}

View File

@ -63,6 +63,7 @@ import { LangOption } from '../../models/lang.model';
* | **participantName** | `string` | {@link ParticipantNameDirective} |
* | **videoMuted** | `boolean` | {@link VideoMutedDirective} |
* | **audioMuted** | `boolean` | {@link AudioMutedDirective} |
| **simulcast** | `boolean` | {@link SimulcastDirective} |
* | **toolbarScreenshareButton** | `boolean` | {@link ToolbarScreenshareButtonDirective} |
* | **toolbarFullscreenButton** | `boolean` | {@link ToolbarFullscreenButtonDirective} |
* | **toolbarCaptionsButton** | `boolean` | {@link ToolbarCaptionsButtonDirective} |
@ -75,6 +76,8 @@ import { LangOption } from '../../models/lang.model';
* | **streamDisplayParticipantName** | `boolean` | {@link StreamDisplayParticipantNameDirective} |
* | **streamDisplayAudioDetection** | `boolean` | {@link StreamDisplayAudioDetectionDirective} |
* | **streamSettingsButton** | `boolean` | {@link StreamSettingsButtonDirective} |
* | **streamFrameRate** | `number` | {@link StreamFrameRateDirective} |
* | **streamResolution** | `string` | {@link StreamResolutionDirective} |
* | **participantPanelItemMuteButton** | `boolean` | {@link ParticipantPanelItemMuteButtonDirective} |
* | **recordingActivityRecordingList** | `{@link RecordingInfo}[]` | {@link RecordingActivityRecordingsListDirective} |
* | **recordingActivityRecordingError** | `any` | {@link RecordingActivityRecordingErrorDirective} |
@ -603,7 +606,12 @@ export class VideoconferenceComponent implements OnInit, OnDestroy, AfterViewIni
private async initwebcamPublisher(): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const publisher = await this.openviduService.initDefaultPublisher();
const pp = {
resolution: this.libService.getStreamResolution(),
frameRate: this.libService.getStreamFrameRate(),
videoSimulcast: this.libService.isSimulcastEnabled()
};
const publisher = await this.openviduService.initDefaultPublisher(pp);
if (publisher) {
publisher.once('accessDenied', async (e: any) => {

View File

@ -8,7 +8,9 @@ import { RecordingActivityRecordingErrorDirective, RecordingActivityRecordingsLi
import {
StreamDisplayAudioDetectionDirective,
StreamDisplayParticipantNameDirective,
StreamSettingsButtonDirective
StreamSettingsButtonDirective,
StreamFrameRateDirective,
StreamResolutionDirective
} from './stream.directive';
import {
ToolbarActivitiesPanelButtonDirective,
@ -32,7 +34,8 @@ import {
MinimalDirective,
ParticipantNameDirective,
PrejoinDirective,
VideoMutedDirective
VideoMutedDirective,
SimulcastDirective
} from './videoconference.directive';
@NgModule({
@ -44,6 +47,7 @@ import {
CaptionsLangDirective,
PrejoinDirective,
VideoMutedDirective,
SimulcastDirective,
AudioMutedDirective,
ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective,
@ -61,6 +65,8 @@ import {
StreamDisplayParticipantNameDirective,
StreamDisplayAudioDetectionDirective,
StreamSettingsButtonDirective,
StreamFrameRateDirective,
StreamResolutionDirective,
LogoDirective,
ParticipantPanelItemMuteButtonDirective,
ParticipantNameDirective,
@ -80,6 +86,7 @@ import {
CaptionsLangDirective,
PrejoinDirective,
VideoMutedDirective,
SimulcastDirective,
AudioMutedDirective,
ToolbarScreenshareButtonDirective,
ToolbarFullscreenButtonDirective,
@ -97,6 +104,8 @@ import {
StreamDisplayParticipantNameDirective,
StreamDisplayAudioDetectionDirective,
StreamSettingsButtonDirective,
StreamFrameRateDirective,
StreamResolutionDirective,
LogoDirective,
ParticipantPanelItemMuteButtonDirective,
ParticipantNameDirective,

View File

@ -1,6 +1,108 @@
import { AfterViewInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core';
import { OpenViduAngularConfigService } from '../../services/config/openvidu-angular.config.service';
/**
* The **frameRate** directive allows initialize the publisher with a specific frame rate in stream component.
*
* Default: `30`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component:
*
* @example
* <ov-videoconference [streamFrameRate]="10"></ov-videoconference>
*
* \
* And it also can be used in the {@link StreamComponent}.
* @example
* <ov-stream [frameRate]="10"></ov-stream>
*/
@Directive({
selector: 'ov-videoconference[streamFrameRate], ov-stream[frameRate]'
})
export class StreamFrameRateDirective implements AfterViewInit, OnDestroy {
@Input() set streamFrameRate(value: number) {
this._frameRate = value;
this.update(this._frameRate);
}
@Input() set frameRate(value: number) {
this._frameRate = value;
this.update(this._frameRate);
}
_frameRate: number;
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngOnDestroy(): void {
this.clear();
}
ngAfterViewInit() {
this.update(this._frameRate);
}
update(value: number) {
if (this.libService.streamFrameRate.getValue() !== value) {
this.libService.streamFrameRate.next(value);
}
}
clear() {
this.update(30);
}
}
/**
* The **resolution** directive allows to set a specific participant resolution in stream component.
*
* Default: `640x480`
*
* It can be used in the parent element {@link VideoconferenceComponent} specifying the name of the `stream` component:
*
* @example
* <ov-videoconference [streamResolution]="'320x240'"></ov-videoconference>
*
* \
* And it also can be used in the {@link StreamComponent}.
* @example
* <ov-stream [resolution]="'320x240'"></ov-stream>
*/
@Directive({
selector: 'ov-videoconference[streamResolution], ov-stream[resolution]'
})
export class StreamResolutionDirective implements AfterViewInit, OnDestroy {
@Input() set streamResolution(value: string) {
this._resolution = value;
this.update(this._resolution);
}
@Input() set resolution(value: string) {
this._resolution = value;
this.update(this._resolution);
}
_resolution: string;
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
ngOnDestroy(): void {
this.clear();
}
ngAfterViewInit() {
this.update(this._resolution);
}
update(value: string) {
if (this.libService.streamResolution.getValue() !== value) {
this.libService.streamResolution.next(value);
}
}
clear() {
this.update('640x480');
}
}
/**
* The **displayParticipantName** directive allows show/hide the participants name in stream component.
*

View File

@ -5,7 +5,6 @@ import { OpenViduAngularConfigService } from '../../services/config/openvidu-ang
import { TranslateService } from '../../services/translate/translate.service';
import { LangOption } from '../../models/lang.model';
/**
* The **minimal** directive applies a minimal UI hiding all controls except for cam and mic.
*
@ -79,7 +78,7 @@ export class MinimalDirective implements OnDestroy {
* @example
* <ov-videoconference [lang]="'es'"></ov-videoconference>
*/
@Directive({
@Directive({
selector: 'ov-videoconference[lang]'
})
export class LangDirective implements OnDestroy {
@ -152,7 +151,7 @@ export class LangOptionsDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set langOptions(value: LangOption []) {
@Input() set langOptions(value: LangOption[]) {
this.update(value);
}
@ -178,7 +177,7 @@ export class LangOptionsDirective implements OnDestroy {
/**
* @ignore
*/
update(value: LangOption [] | undefined) {
update(value: LangOption[] | undefined) {
this.translateService.setLanguageOptions(value);
}
}
@ -208,7 +207,7 @@ export class LangOptionsDirective implements OnDestroy {
* @example
* <ov-videoconference [captionsLang]="'es-ES'"></ov-videoconference>
*/
@Directive({
@Directive({
selector: 'ov-videoconference[captionsLang]'
})
export class CaptionsLangDirective implements OnDestroy {
@ -269,14 +268,14 @@ export class CaptionsLangDirective implements OnDestroy {
* @example
* <ov-videoconference [captionsLangOptions]="[{name:'Spanish', lang: 'es-ES'}]"></ov-videoconference>
*/
@Directive({
@Directive({
selector: 'ov-videoconference[captionsLangOptions]'
})
export class CaptionsLangOptionsDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set captionsLangOptions(value: CaptionsLangOption []) {
@Input() set captionsLangOptions(value: CaptionsLangOption[]) {
this.update(value);
}
@ -302,12 +301,11 @@ export class CaptionsLangOptionsDirective implements OnDestroy {
/**
* @ignore
*/
update(value: CaptionsLangOption [] | undefined) {
update(value: CaptionsLangOption[] | undefined) {
this.captionService.setLanguageOptions(value);
}
}
/**
* The **participantName** directive sets the participant name. It can be useful for aplications which doesn't need the prejoin page.
*
@ -477,7 +475,6 @@ export class VideoMutedDirective implements OnDestroy {
selector: 'ov-videoconference[audioMuted]'
})
export class AudioMutedDirective implements OnDestroy {
/**
* @ignore
*/
@ -510,3 +507,56 @@ export class AudioMutedDirective implements OnDestroy {
}
}
}
/**
* The **simulcast** directive allows to enable/disable the Simulcast feature. Simulcast is a technique that allows
* to send multiple versions of the same video stream at different resolutions, framerates and qualities. This way,
* the receiver can subscribe to the most appropriate stream for its current network conditions.
*
* It is only available for {@link VideoconferenceComponent} and **only if OpenVidu Server was configured to use the
* mediasoup media server**. Otherwise, Simulcast will be disabled.
*
* Default: `false`
*
* @example
* <ov-videoconference [simulcast]="true"></ov-videoconference>
*/
@Directive({
selector: 'ov-videoconference[simulcast]'
})
export class SimulcastDirective implements OnDestroy {
/**
* @ignore
*/
@Input() set simulcast(value: boolean) {
this.update(value);
}
/**
* @ignore
*/
constructor(public elementRef: ElementRef, private libService: OpenViduAngularConfigService) {}
/**
* @ignore
*/
ngOnDestroy(): void {
this.clear();
}
/**
* @ignore
*/
clear() {
this.update(false);
}
/**
* @ignore
*/
update(value: boolean) {
if (this.libService.simulcast.getValue() !== value) {
this.libService.simulcast.next(value);
}
}
}

View File

@ -19,6 +19,9 @@ export class OpenViduAngularConfigService {
prejoin = <BehaviorSubject<boolean>>new BehaviorSubject(true);
prejoinObs: Observable<boolean>;
simulcast = <BehaviorSubject<boolean>>new BehaviorSubject(false);
simulcastObs: Observable<boolean>;
videoMuted = <BehaviorSubject<boolean | undefined>>new BehaviorSubject(undefined);
videoMutedObs: Observable<boolean | undefined>;
audioMuted = <BehaviorSubject<boolean | undefined>>new BehaviorSubject(undefined);
@ -50,6 +53,11 @@ export class OpenViduAngularConfigService {
displaySessionName = <BehaviorSubject<boolean>>new BehaviorSubject(true);
displaySessionNameObs: Observable<boolean>;
streamFrameRate = <BehaviorSubject<number>>new BehaviorSubject(30);
streamFrameRateObs: Observable<number>;
streamResolution = <BehaviorSubject<string>>new BehaviorSubject('640x480');
streamResolutionObs: Observable<string>;
displayLogo = <BehaviorSubject<boolean>>new BehaviorSubject(true);
displayLogoObs: Observable<boolean>;
displayParticipantName = <BehaviorSubject<boolean>>new BehaviorSubject(true);
@ -91,6 +99,7 @@ export class OpenViduAngularConfigService {
this.prejoinObs = this.prejoin.asObservable();
this.videoMutedObs = this.videoMuted.asObservable();
this.audioMutedObs = this.audioMuted.asObservable();
this.simulcastObs = this.simulcast.asObservable();
//Toolbar observables
this.screenshareButtonObs = this.screenshareButton.asObservable();
this.fullscreenButtonObs = this.fullscreenButton.asObservable();
@ -106,6 +115,8 @@ export class OpenViduAngularConfigService {
this.toolbarSettingsButtonObs = this.toolbarSettingsButton.asObservable();
this.captionsButtonObs = this.captionsButton.asObservable();
//Stream observables
this.streamFrameRateObs = this.streamFrameRate.asObservable();
this.streamResolutionObs = this.streamResolution.asObservable();
this.displayParticipantNameObs = this.displayParticipantName.asObservable();
this.displayAudioDetectionObs = this.displayAudioDetection.asObservable();
this.streamSettingsButtonObs = this.streamSettingsButton.asObservable();
@ -145,4 +156,16 @@ export class OpenViduAngularConfigService {
isBroadcastingEnabled(): boolean {
return this.broadcastingButton.getValue() && this.broadcastingActivity.getValue();
}
getStreamResolution(): string {
return this.streamResolution.getValue();
}
getStreamFrameRate(): number {
return this.streamFrameRate.getValue();
}
isSimulcastEnabled(): boolean {
return this.simulcast.getValue() || false;
}
}

View File

@ -8,7 +8,8 @@ import {
PublisherProperties,
Session,
SignalOptions,
Stream
Stream,
StreamManager
} from 'openvidu-browser';
import { BehaviorSubject, Observable } from 'rxjs';
@ -49,7 +50,7 @@ export class OpenViduService {
* @internal
*/
constructor(
protected openviduAngularConfigSrv: OpenViduAngularConfigService,
protected libService: OpenViduAngularConfigService,
protected platformService: PlatformService,
protected loggerSrv: LoggerService,
private injector: Injector,
@ -69,13 +70,13 @@ export class OpenViduService {
interval: 50
}
});
if (this.openviduAngularConfigSrv.isProduction()) this.OV.enableProdMode();
if (this.libService.isProduction()) this.OV.enableProdMode();
this.webcamSession = this.OV.initSession();
// Initialize screen session only if it is not mobile platform
if (!this.platformService.isMobile()) {
this.OVScreen = new OpenVidu();
if (this.openviduAngularConfigSrv.isProduction()) this.OVScreen.enableProdMode();
if (this.libService.isProduction()) this.OVScreen.enableProdMode();
this.screenSession = this.OVScreen.initSession();
}
}
@ -244,11 +245,34 @@ export class OpenViduService {
this.disconnectSession(this.screenSession);
}
async updateVideoEncodingParameters(streamManager: StreamManager) {
if (!streamManager) return;
const track = streamManager?.stream.getMediaStream().getVideoTracks()[0];
const videoSender = streamManager?.stream
.getRTCPeerConnection()
.getSenders()
.find((sender) => sender.track === track);
if (!videoSender) return;
const parameters: RTCRtpSendParameters = videoSender.getParameters();
const desiredFrameRate = this.libService.getStreamFrameRate();
const desiredWidth = Number(this.libService.getStreamResolution().split('x')[0]);
const desiredResolution = Number(track?.getConstraints()?.width) / desiredWidth ?? 1.0;
parameters.encodings.forEach((encoding: RTCRtpEncodingParameters) => {
if (desiredFrameRate > 0 && encoding['maxFramerate'] !== desiredFrameRate) encoding['maxFramerate'] = desiredFrameRate;
if (desiredResolution >= 1 && encoding['scaleResolutionDownBy'] !== desiredResolution) {
encoding['scaleResolutionDownBy'] = desiredResolution;
}
});
await videoSender.setParameters(parameters);
}
/**
* @internal
* Initialize a publisher checking devices saved on storage or if participant have devices available.
*/
async initDefaultPublisher(): Promise<Publisher | undefined> {
async initDefaultPublisher(pp?: Partial<PublisherProperties>): Promise<Publisher | undefined> {
const hasVideoDevices = this.deviceService.hasVideoDeviceAvailable();
const hasAudioDevices = this.deviceService.hasAudioDeviceAvailable();
const isVideoActive = !this.deviceService.isVideoMuted();
@ -259,7 +283,7 @@ export class OpenViduService {
if (hasVideoDevices) {
// Video is active, assign the device selected
videoSource = this.deviceService.getCameraSelected().device;
videoSource = this.deviceService.getCameraSelected()?.device ?? false;
} else if (!isVideoActive && hasVideoDevices) {
// Video is muted, assign the default device
// videoSource = undefined;
@ -267,18 +291,21 @@ export class OpenViduService {
if (hasAudioDevices) {
// Audio is active, assign the device selected
audioSource = this.deviceService.getMicrophoneSelected().device;
audioSource = this.deviceService.getMicrophoneSelected()?.device ?? false;
} else if (!isAudioActive && hasAudioDevices) {
// Audio is muted, assign the default device
// audioSource = undefined;
}
const mirror = this.deviceService.getCameraSelected() && this.deviceService.getCameraSelected().type === CameraType.FRONT;
const mirror = this.deviceService.getCameraSelected() && this.deviceService.getCameraSelected()?.type === CameraType.FRONT;
const properties: PublisherProperties = {
videoSource,
audioSource,
publishVideo: isVideoActive,
publishAudio: isAudioActive,
resolution: pp?.resolution ?? '640x480',
frameRate: pp?.frameRate ?? 30,
videoSimulcast: pp?.videoSimulcast ?? false,
mirror
};
if (hasVideoDevices || hasAudioDevices) {
@ -520,7 +547,6 @@ export class OpenViduService {
}
}
/**
* @internal
* @param pp {@link PublisherProperties}
@ -538,7 +564,7 @@ export class OpenViduService {
this.forceStopMediaTracks(participantService.getMyCameraPublisher().stream.getMediaStream(), trackType);
return this.OV.getUserMedia(pp);
} catch (error) {
console.warn('Error creating MediaStream', error);
this.log.w('Error creating MediaStream', error);
if ((<OpenViduError>error).name === OpenViduErrorName.DEVICE_ACCESS_DENIED) {
this.log.w('The device requested is not available. Restoring the older one');
// The track requested is not available so we are getting the old tracks ids for recovering the track
@ -559,7 +585,7 @@ export class OpenViduService {
*/
myNicknameHasBeenChanged(): boolean {
const participantService = this.injector.get(ParticipantService);
let oldNickname: string = "";
let oldNickname: string = '';
try {
const connData = JSON.parse(this.cleanConnectionData(this.webcamSession.connection.data));
oldNickname = connData.clientData;

View File

@ -1,11 +1,18 @@
<ov-videoconference
[simulcast]="true"
[tokens]="tokens"
[minimal]="false"
[lang]="'es'"
[langOptions]="[{ name: 'Spanish', lang: 'es' },{ name: 'custom', lang: 'cus' }]"
[langOptions]="[
{ name: 'Spanish', lang: 'es' },
{ name: 'custom', lang: 'cus' }
]"
[captionsLang]=""
[captionsLangOptions]="[{ name: 'Spanish', lang: 'es-ES' },{ name: 'English', lang: 'en-US' }]"
[prejoin]="true"
[captionsLangOptions]="[
{ name: 'Spanish', lang: 'es-ES' },
{ name: 'English', lang: 'en-US' }
]"
[prejoin]="false"
[participantName]="'Participant'"
[videoMuted]="false"
[audioMuted]="false"
@ -20,6 +27,8 @@
[toolbarParticipantsPanelButton]="true"
[toolbarDisplayLogo]="true"
[toolbarDisplaySessionName]="true"
[streamResolution]="'160x120'"
[streamFrameRate]="2"
[streamDisplayParticipantName]="true"
[streamDisplayAudioDetection]="true"
[streamSettingsButton]="true"
@ -49,9 +58,12 @@
(onNodeCrashed)="onNodeCrashed()"
(onLangChanged)="onLangChanged($event)"
>
<!-- <div *ovActivitiesPanel>HELLO</div> -->
<div *ovActivitiesPanel>{{ 'CUSTOM.BUTTON' | translate }}</div>
<!-- <ov-toolbar *ovToolbar [activitiesPanelButton]="false"
[chatPanelButton]="false"></ov-toolbar> -->
<!-- <div *ovStream="let stream">
<ov-stream [stream]="stream" [displayParticipantName]="false" [frameRate]="2" [resolution]="'160x120'"></ov-stream>
<p>{{ stream.participant.nickname }}</p>
</div> -->
</ov-videoconference>

View File

@ -1,5 +1,6 @@
<ov-videoconference
*ngIf="success"
[simulcast]="_simulcast"
[participantName]="_participantName"
[tokens]="_tokens"
[minimal]="_minimal"
@ -26,6 +27,8 @@
[streamDisplayParticipantName]="_streamDisplayParticipantName"
[streamDisplayAudioDetection]="_streamDisplayAudioDetection"
[streamSettingsButton]="_streamSettingsButton"
[streamResolution]="_streamResolution"
[streamFrameRate]="_streamFrameRate"
[participantPanelItemMuteButton]="_participantPanelItemMuteButton"
[activitiesPanelRecordingActivity]="_activitiesPanelRecordingActivity"
[activitiesPanelBroadcastingActivity]="_activitiesPanelBroadcastingActivity"

View File

@ -60,6 +60,11 @@ export class OpenviduWebComponentComponent implements OnInit {
* @internal
*/
_audioMuted: boolean = false;
/**
* @internal
*/
_simulcast: boolean = false;
/**
* @internal
*/
@ -124,6 +129,14 @@ export class OpenviduWebComponentComponent implements OnInit {
* @internal
*/
_streamSettingsButton: boolean = true;
/**
* @internal
*/
_streamResolution: string = '640x480';
/**
* @internal
*/
_streamFrameRate: number = 30;
/**
* @internal
*/
@ -295,6 +308,20 @@ export class OpenviduWebComponentComponent implements OnInit {
this._audioMuted = this.castToBoolean(value);
}
/**
* The **simulcast** directive allows to enable/disable the Simulcast feature. Simulcast is a technique that allows
* to send multiple versions of the same video stream at different resolutions, framerates and qualities. This way,
* the receiver can subscribe to the most appropriate stream for its current network conditions.
*
* Default: `false`
*
* @example
* <openvidu-webcomponent simulcast="true"></openvidu-webcomponent>
*/
@Input() set simulcast(value: string | boolean) {
this._simulcast = this.castToBoolean(value);
}
/**
* The **toolbarScreenshareButton** attribute allows show/hide the screenshare toolbar button.
*
@ -524,6 +551,36 @@ export class OpenviduWebComponentComponent implements OnInit {
@Input() set streamSettingsButton(value: string | boolean) {
this._streamSettingsButton = this.castToBoolean(value);
}
/**
* The **resolution** directive allows to set a specific participant resolution in stream component.
*
* Default: `640x480`
*
* <div class="warn-container">
* <span>WARNING</span>: If you want to use this parameter to OpenVidu Web Component statically, you have to replace the <strong>camelCase</strong> with a <strong>hyphen between words</strong>.</div>
*
* @example
* <openvidu-webcomponent stream-resolution="'320x240'"></openvidu-webcomponent>
*/
@Input() set streamResolution(value: string) {
this._streamResolution = value;
}
/**
* The **frameRate** directive allows initialize the publisher with a specific frame rate in stream component.
*
* Default: `30`
*
* <div class="warn-container">
* <span>WARNING</span>: If you want to use this parameter to OpenVidu Web Component statically, you have to replace the <strong>camelCase</strong> with a <strong>hyphen between words</strong>.</div>
*
* @example
* <openvidu-webcomponent stream-frame-rate="30"></openvidu-webcomponent>
*/
@Input() set streamFrameRate(value: number) {
this._streamFrameRate = Number(value);
}
/**
* The **participantPanelItemMuteButton** attribute allows show/hide the muted button in participant panel item component.
*