openvidu-testapp: upgrade to Angular v20

master
pabloFuente 2025-10-07 22:00:11 +02:00
parent 6a5980e3da
commit 780635dc9a
16 changed files with 4982 additions and 2367 deletions

View File

@ -73,13 +73,13 @@
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular/build:extract-i18n",
"options": { "options": {
"buildTarget": "openvidu-testapp-livekit:build" "buildTarget": "openvidu-testapp-livekit:build"
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:karma",
"options": { "options": {
"polyfills": [ "polyfills": [
"zone.js", "zone.js",
@ -103,5 +103,31 @@
}, },
"cli": { "cli": {
"analytics": false "analytics": false
},
"schematics": {
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,16 +11,16 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^19.2.14", "@angular/animations": "^20.3.3",
"@angular/cdk": "^19.2.18", "@angular/cdk": "^19.2.18",
"@angular/common": "^19.2.14", "@angular/common": "^20.3.3",
"@angular/compiler": "^19.2.14", "@angular/compiler": "^20.3.3",
"@angular/core": "^19.2.14", "@angular/core": "^20.3.3",
"@angular/forms": "^19.2.14", "@angular/forms": "^20.3.3",
"@angular/material": "^19.2.18", "@angular/material": "^19.2.18",
"@angular/platform-browser": "^19.2.14", "@angular/platform-browser": "^20.3.3",
"@angular/platform-browser-dynamic": "^19.2.14", "@angular/platform-browser-dynamic": "^20.3.3",
"@angular/router": "^19.2.14", "@angular/router": "^20.3.3",
"@livekit/protocol": "^1.38.0", "@livekit/protocol": "^1.38.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"crypto-browserify": "^3.12.1", "crypto-browserify": "^3.12.1",
@ -36,9 +36,9 @@
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "^19.0.1", "@angular-builders/custom-webpack": "^19.0.1",
"@angular-devkit/build-angular": "^19.2.15", "@angular/build": "^20.3.4",
"@angular/cli": "~19.2.15", "@angular/cli": "~20.3.4",
"@angular/compiler-cli": "^19.2.14", "@angular/compiler-cli": "^20.3.3",
"@types/events": "^3.0.3", "@types/events": "^3.0.3",
"@types/jasmine": "~5.1.8", "@types/jasmine": "~5.1.8",
"@types/json-stringify-safe": "^5.0.3", "@types/json-stringify-safe": "^5.0.3",
@ -52,4 +52,4 @@
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~2.1.0",
"typescript": "5.8.3" "typescript": "5.8.3"
} }
} }

View File

@ -1,16 +1,20 @@
<div class="parent-div"> <div class="parent-div">
<audio #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></audio> <audio #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></audio>
<span>{{trackPublication.source}}</span> <span>{{trackPublication.source}}</span>
<div class="bottom-div"> <div class="bottom-div">
<button *ngIf="localParticipant" (click)="muteUnmuteAudio()" class="audio-btn" matTooltip="Mute/Unmute audio" @if (localParticipant) {
matTooltipClass="custom-tooltip"> <button (click)="muteUnmuteAudio()" class="audio-btn" matTooltip="Mute/Unmute audio"
<mat-icon aria-label="Mute/Unmute audio" class="mat-icon material-icons" role="img" matTooltipClass="custom-tooltip">
aria-hidden="true">{{muteAudioIcon}}</mat-icon> <mat-icon aria-label="Mute/Unmute audio" class="mat-icon material-icons" role="img"
</button> aria-hidden="true">{{muteAudioIcon}}</mat-icon>
<button *ngIf="localParticipant" (click)="unpublishTrack()" class="audio-btn" matTooltip="Unpublish track" </button>
matTooltipClass="custom-tooltip"> }
<mat-icon aria-label="Unpublish track" class="mat-icon material-icons" role="img" @if (localParticipant) {
aria-hidden="true">stop</mat-icon> <button (click)="unpublishTrack()" class="audio-btn" matTooltip="Unpublish track"
</button> matTooltipClass="custom-tooltip">
</div> <mat-icon aria-label="Unpublish track" class="mat-icon material-icons" role="img"
aria-hidden="true">stop</mat-icon>
</button>
}
</div>
</div> </div>

View File

@ -7,35 +7,39 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
template: ` template: `
<h2 mat-dialog-title>{{target}} events</h2> <h2 mat-dialog-title>{{target}} events</h2>
<mat-dialog-content> <mat-dialog-content>
<mat-slide-toggle [(ngModel)]="checkAll" (change)="updateAll()" [color]="'warn'"><i>ALL</i></mat-slide-toggle> <mat-slide-toggle [(ngModel)]="checkAll" (change)="updateAll()" [color]="'warn'"><i>ALL</i></mat-slide-toggle>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div class="row no-wrap-row"> <div class="row no-wrap-row">
<div class="col-50"> <div class="col-50">
<div *ngFor="let event of eventArray | slice:0:(eventArray.length/2)" class="toggle"> @for (event of eventArray | slice:0:(eventArray.length/2); track event) {
<mat-slide-toggle <div class="toggle">
(change)="toggleEvent($event)" <mat-slide-toggle
[checked]="eventCollection.get(event)" (change)="toggleEvent($event)"
[name]="event" [checked]="eventCollection.get(event)"
color="warn">{{event}} [name]="event"
</mat-slide-toggle> color="warn">{{event}}
</div> </mat-slide-toggle>
</div>
<div class="col-50">
<div *ngFor="let event of eventArray | slice:(eventArray.length/2 + 1):(eventArray.length)" class="toggle">
<mat-slide-toggle
(change)="toggleEvent($event)"
[checked]="eventCollection.get(event)"
[name]="event"
color="warn">{{event}}
</mat-slide-toggle>
</div>
</div> </div>
}
</div> </div>
<div class="col-50">
@for (event of eventArray | slice:(eventArray.length/2 + 1):(eventArray.length); track event) {
<div class="toggle">
<mat-slide-toggle
(change)="toggleEvent($event)"
[checked]="eventCollection.get(event)"
[name]="event"
color="warn">{{event}}
</mat-slide-toggle>
</div>
}
</div>
</div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button id="close-dialog-btn" mat-dialog-close="">CLOSE</button> <button mat-button id="close-dialog-btn" mat-dialog-close="">CLOSE</button>
</mat-dialog-actions> </mat-dialog-actions>
`, `,
styles: [ styles: [
'mat-dialog-content { display: inline; }', 'mat-dialog-content { display: inline; }',
'mat-divider { margin-top: 5px; margin-bottom: 5px; }', 'mat-divider { margin-top: 5px; margin-bottom: 5px; }',

View File

@ -1,6 +1,8 @@
<div> <div>
<h2 mat-dialog-title>{{ title }}</h2> <h2 mat-dialog-title>{{ title }}</h2>
<p *ngIf="subtitle" id="subtitle">{{ subtitle }}</p> @if (subtitle) {
<p id="subtitle">{{ subtitle }}</p>
}
<mat-dialog-content> <mat-dialog-content>
<mat-form-field> <mat-form-field>

View File

@ -1,182 +1,238 @@
<div> <div>
<h2 mat-dialog-title>OPTIONS</h2> <h2 mat-dialog-title>OPTIONS</h2>
<mat-dialog-content> <mat-dialog-content>
<mat-divider *ngIf="roomOptions"></mat-divider> @if (roomOptions) {
<div *ngIf="roomOptions"> <mat-divider></mat-divider>
<label><a href="https://docs.livekit.io/client-sdk-js/interfaces/RoomOptions.html" target="_blank">RoomOptions</a></label> / <label><a href="https://docs.livekit.io/client-sdk-js/interfaces/RoomConnectOptions.html" target="_blank">RoomConnectOptions</a></label><br> }
<mat-checkbox id="room-adaptiveStream" [(ngModel)]="roomOptions.adaptiveStream">adaptiveStream</mat-checkbox> @if (roomOptions) {
<mat-checkbox id="room-dynacast" [(ngModel)]="roomOptions.dynacast">dynacast</mat-checkbox> <div>
<mat-checkbox id="room-disconnectOnPageLeave" [(ngModel)]="roomOptions.disconnectOnPageLeave">disconnectOnPageLeave</mat-checkbox> <label><a href="https://docs.livekit.io/client-sdk-js/interfaces/RoomOptions.html" target="_blank">RoomOptions</a></label> / <label><a href="https://docs.livekit.io/client-sdk-js/interfaces/RoomConnectOptions.html" target="_blank">RoomConnectOptions</a></label><br>
<mat-checkbox id="room-stopLocalTrackOnUnpublish" [(ngModel)]="roomOptions.stopLocalTrackOnUnpublish">stopLocalTrackOnUnpublish</mat-checkbox> <mat-checkbox id="room-adaptiveStream" [(ngModel)]="roomOptions.adaptiveStream">adaptiveStream</mat-checkbox>
<mat-checkbox id="room-webAudioMix" [(ngModel)]="roomOptions.webAudioMix">webAudioMix</mat-checkbox> <mat-checkbox id="room-dynacast" [(ngModel)]="roomOptions.dynacast">dynacast</mat-checkbox>
<mat-checkbox id="room-forceRelay" [(ngModel)]="forceRelay">Force relay candidates</mat-checkbox> <mat-checkbox id="room-disconnectOnPageLeave" [(ngModel)]="roomOptions.disconnectOnPageLeave">disconnectOnPageLeave</mat-checkbox>
</div> <mat-checkbox id="room-stopLocalTrackOnUnpublish" [(ngModel)]="roomOptions.stopLocalTrackOnUnpublish">stopLocalTrackOnUnpublish</mat-checkbox>
<mat-divider *ngIf="createLocalTracksOptions"></mat-divider> <mat-checkbox id="room-webAudioMix" [(ngModel)]="roomOptions.webAudioMix">webAudioMix</mat-checkbox>
<div *ngIf="createLocalTracksOptions"> <mat-checkbox id="room-forceRelay" [(ngModel)]="forceRelay">Force relay candidates</mat-checkbox>
<div *ngIf="createLocalTracksOptions.video !== undefined">
<a href="https://docs.livekit.io/client-sdk-js/interfaces/VideoCaptureOptions.html" target="_blank">VideoCaptureOptions</a>
<mat-radio-group [(ngModel)]="videoOption">
<mat-radio-button [value]="true" id="video-capture-true">True (default)</mat-radio-button>
<mat-radio-button *ngIf="allowDisablingVideo" [value]="false" id="video-capture-false">False (no video)</mat-radio-button>
<mat-radio-button [value]="'custom'" id="video-capture-custom">Custom</mat-radio-button>
</mat-radio-group>
<div *ngIf="videoOption === 'custom'">
<mat-form-field id="video-deviceId">
<mat-label>deviceId</mat-label>
<mat-select [(value)]="auxVideoCaptureOptions.deviceId">
<mat-option *ngFor="let device of inputVideoDevices" [value]="{ exact: device.deviceId }">{{'[' + device.label + '] ' + device.deviceId}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field id="video-facingMode">
<mat-label>facingMode</mat-label>
<mat-select [(value)]="auxVideoCaptureOptions.facingMode">
<mat-option *ngFor="let mode of ['user','environment','left','right']" [value]="mode">{{mode}}</mat-option>
</mat-select>
</mat-form-field>
<app-video-resolution style="display: inline-block;"
[componentId]="'resolution-video-capture-options'"
[width]="auxVideoCaptureOptions.resolution!.width"
[height]="auxVideoCaptureOptions.resolution!.height"
[frameRate]="auxVideoCaptureOptions.resolution!.frameRate"
[aspectRatio]="auxVideoCaptureOptions.resolution!.aspectRatio"
(resolutionChanged)="auxVideoCaptureOptions.resolution!.width = $event.width; auxVideoCaptureOptions.resolution!.height = $event.height; auxVideoCaptureOptions.resolution!.frameRate = $event.frameRate; auxVideoCaptureOptions.resolution!.aspectRatio = $event.aspectRatio">
</app-video-resolution>
</div>
</div> </div>
<div *ngIf="createLocalTracksOptions.audio !== undefined" style="margin-top: 16px"> }
<label class="label"><a href="https://docs.livekit.io/client-sdk-js/interfaces/AudioCaptureOptions.html" target="_blank">AudioCaptureOptions</a></label> @if (createLocalTracksOptions) {
<mat-radio-group [(ngModel)]="audioOption"> <mat-divider></mat-divider>
<mat-radio-button [value]="true" id="audio-capture-true">True (default)</mat-radio-button> }
<mat-radio-button *ngIf="allowDisablingAudio" [value]="false" id="audio-capture-false">False (no audio)</mat-radio-button> @if (createLocalTracksOptions) {
<mat-radio-button [value]="'custom'" id="audio-capture-custom">Custom</mat-radio-button> <div>
</mat-radio-group> @if (createLocalTracksOptions.video !== undefined) {
<div *ngIf="audioOption === 'custom'">
<div> <div>
<mat-checkbox id="audio-autoGainControl" [(ngModel)]="auxAudioCaptureOptions.autoGainControl">autoGainControl</mat-checkbox> <a href="https://docs.livekit.io/client-sdk-js/interfaces/VideoCaptureOptions.html" target="_blank">VideoCaptureOptions</a>
<mat-checkbox id="audio-echoCancellation" [(ngModel)]="auxAudioCaptureOptions.echoCancellation">echoCancellation</mat-checkbox> <mat-radio-group [(ngModel)]="videoOption">
<mat-checkbox id="audio-noiseSuppression" [(ngModel)]="auxAudioCaptureOptions.noiseSuppression">noiseSuppression</mat-checkbox> <mat-radio-button [value]="true" id="video-capture-true">True (default)</mat-radio-button>
@if (allowDisablingVideo) {
<mat-radio-button [value]="false" id="video-capture-false">False (no video)</mat-radio-button>
}
<mat-radio-button [value]="'custom'" id="video-capture-custom">Custom</mat-radio-button>
</mat-radio-group>
@if (videoOption === 'custom') {
<div>
<mat-form-field id="video-deviceId">
<mat-label>deviceId</mat-label>
<mat-select [(value)]="auxVideoCaptureOptions.deviceId">
@for (device of inputVideoDevices; track device) {
<mat-option [value]="{ exact: device.deviceId }">{{'[' + device.label + '] ' + device.deviceId}}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field id="video-facingMode">
<mat-label>facingMode</mat-label>
<mat-select [(value)]="auxVideoCaptureOptions.facingMode">
@for (mode of ['user','environment','left','right']; track mode) {
<mat-option [value]="mode">{{mode}}</mat-option>
}
</mat-select>
</mat-form-field>
<app-video-resolution style="display: inline-block;"
[componentId]="'resolution-video-capture-options'"
[width]="auxVideoCaptureOptions.resolution!.width"
[height]="auxVideoCaptureOptions.resolution!.height"
[frameRate]="auxVideoCaptureOptions.resolution!.frameRate"
[aspectRatio]="auxVideoCaptureOptions.resolution!.aspectRatio"
(resolutionChanged)="auxVideoCaptureOptions.resolution!.width = $event.width; auxVideoCaptureOptions.resolution!.height = $event.height; auxVideoCaptureOptions.resolution!.frameRate = $event.frameRate; auxVideoCaptureOptions.resolution!.aspectRatio = $event.aspectRatio">
</app-video-resolution>
</div>
}
</div> </div>
<mat-form-field class="inner-text-input"> }
<mat-label>deviceId</mat-label> @if (createLocalTracksOptions.audio !== undefined) {
<input matInput id="audio-deviceId" placeholder="deviceId" [(ngModel)]="auxAudioCaptureOptions.deviceId"/> <div style="margin-top: 16px">
</mat-form-field> <label class="label"><a href="https://docs.livekit.io/client-sdk-js/interfaces/AudioCaptureOptions.html" target="_blank">AudioCaptureOptions</a></label>
<mat-form-field class="inner-text-input"> <mat-radio-group [(ngModel)]="audioOption">
<mat-label>channelCount</mat-label> <mat-radio-button [value]="true" id="audio-capture-true">True (default)</mat-radio-button>
<input matInput id="audio-channelCount" type="number" placeholder="channelCount" [(ngModel)]="auxAudioCaptureOptions.channelCount"/> @if (allowDisablingAudio) {
</mat-form-field> <mat-radio-button [value]="false" id="audio-capture-false">False (no audio)</mat-radio-button>
<mat-form-field class="inner-text-input"> }
<mat-label>latency</mat-label> <mat-radio-button [value]="'custom'" id="audio-capture-custom">Custom</mat-radio-button>
<input matInput id="audio-latency" type="number" placeholder="latency" [(ngModel)]="auxAudioCaptureOptions.latency"/> </mat-radio-group>
</mat-form-field> @if (audioOption === 'custom') {
<mat-form-field class="inner-text-input"> <div>
<mat-label>sampleRate</mat-label> <div>
<input matInput id="audio-sampleRate" type="number" placeholder="sampleRate" [(ngModel)]="auxAudioCaptureOptions.sampleRate"/> <mat-checkbox id="audio-autoGainControl" [(ngModel)]="auxAudioCaptureOptions.autoGainControl">autoGainControl</mat-checkbox>
</mat-form-field> <mat-checkbox id="audio-echoCancellation" [(ngModel)]="auxAudioCaptureOptions.echoCancellation">echoCancellation</mat-checkbox>
<mat-form-field class="inner-text-input"> <mat-checkbox id="audio-noiseSuppression" [(ngModel)]="auxAudioCaptureOptions.noiseSuppression">noiseSuppression</mat-checkbox>
<mat-label>sampleSize</mat-label> </div>
<input matInput id="audio-sampleSize" type="number" placeholder="sampleSize" [(ngModel)]="auxAudioCaptureOptions.sampleSize"/> <mat-form-field class="inner-text-input">
</mat-form-field> <mat-label>deviceId</mat-label>
</div> <input matInput id="audio-deviceId" placeholder="deviceId" [(ngModel)]="auxAudioCaptureOptions.deviceId"/>
</mat-form-field>
<mat-form-field class="inner-text-input">
<mat-label>channelCount</mat-label>
<input matInput id="audio-channelCount" type="number" placeholder="channelCount" [(ngModel)]="auxAudioCaptureOptions.channelCount"/>
</mat-form-field>
<mat-form-field class="inner-text-input">
<mat-label>latency</mat-label>
<input matInput id="audio-latency" type="number" placeholder="latency" [(ngModel)]="auxAudioCaptureOptions.latency"/>
</mat-form-field>
<mat-form-field class="inner-text-input">
<mat-label>sampleRate</mat-label>
<input matInput id="audio-sampleRate" type="number" placeholder="sampleRate" [(ngModel)]="auxAudioCaptureOptions.sampleRate"/>
</mat-form-field>
<mat-form-field class="inner-text-input">
<mat-label>sampleSize</mat-label>
<input matInput id="audio-sampleSize" type="number" placeholder="sampleSize" [(ngModel)]="auxAudioCaptureOptions.sampleSize"/>
</mat-form-field>
</div>
}
</div>
}
</div> </div>
</div> }
<mat-divider *ngIf="shareScreen"></mat-divider> @if (shareScreen) {
<div *ngIf="shareScreen"> <mat-divider></mat-divider>
<label><a href="https://docs.livekit.io/client-sdk-js/interfaces/ScreenShareCaptureOptions.html" target="_blank">ScreenShareCaptureOptions</a></label><br> }
<mat-radio-group [(ngModel)]="screenOption" (change)="screenOptionChanged($event)"> @if (shareScreen) {
<mat-radio-button [value]="true">True (default)</mat-radio-button> <div>
<mat-radio-button [value]="false" *ngIf="allowDisablingScreen">False (no screen)</mat-radio-button> <label><a href="https://docs.livekit.io/client-sdk-js/interfaces/ScreenShareCaptureOptions.html" target="_blank">ScreenShareCaptureOptions</a></label><br>
<mat-radio-button [value]="'custom'">Custom</mat-radio-button> <mat-radio-group [(ngModel)]="screenOption" (change)="screenOptionChanged($event)">
</mat-radio-group> <mat-radio-button [value]="true">True (default)</mat-radio-button>
<div *ngIf="screenOption == 'custom'"> @if (allowDisablingScreen) {
<mat-checkbox id="screenShare-video" [(ngModel)]="screenShareCaptureOptions!.video">video</mat-checkbox> <mat-radio-button [value]="false">False (no screen)</mat-radio-button>
<mat-checkbox id="screenShare-audio" [(ngModel)]="screenShareCaptureOptions!.audio">audio</mat-checkbox> }
<mat-checkbox id="screenShare-preferCurrentTab" [(ngModel)]="screenShareCaptureOptions!.preferCurrentTab">preferCurrentTab</mat-checkbox> <mat-radio-button [value]="'custom'">Custom</mat-radio-button>
<mat-checkbox id="screenShare-suppressLocalAudioPlayback" [(ngModel)]="screenShareCaptureOptions!.suppressLocalAudioPlayback">suppressLocalAudioPlayback</mat-checkbox> </mat-radio-group>
<mat-form-field *ngIf="screenShareCaptureOptions!.video" id="screenShare-displaySurface"> @if (screenOption == 'custom') {
<mat-label>displaySurface</mat-label> <div>
<mat-select [(value)]="auxScreenDisplaySurface"> <mat-checkbox id="screenShare-video" [(ngModel)]="screenShareCaptureOptions!.video">video</mat-checkbox>
<mat-option *ngFor="let surface of ['NONE','window','browser','monitor']" [value]="surface">{{surface}}</mat-option> <mat-checkbox id="screenShare-audio" [(ngModel)]="screenShareCaptureOptions!.audio">audio</mat-checkbox>
</mat-select> <mat-checkbox id="screenShare-preferCurrentTab" [(ngModel)]="screenShareCaptureOptions!.preferCurrentTab">preferCurrentTab</mat-checkbox>
</mat-form-field> <mat-checkbox id="screenShare-suppressLocalAudioPlayback" [(ngModel)]="screenShareCaptureOptions!.suppressLocalAudioPlayback">suppressLocalAudioPlayback</mat-checkbox>
<mat-form-field id="screenShare-contentHint"> @if (screenShareCaptureOptions!.video) {
<mat-label>contentHint</mat-label> <mat-form-field id="screenShare-displaySurface">
<mat-select [(value)]="screenShareCaptureOptions!.contentHint"> <mat-label>displaySurface</mat-label>
<mat-option *ngFor="let hint of ['text','detail','motion']" [value]="hint">{{hint}}</mat-option> <mat-select [(value)]="auxScreenDisplaySurface">
</mat-select> @for (surface of ['NONE','window','browser','monitor']; track surface) {
</mat-form-field> <mat-option [value]="surface">{{surface}}</mat-option>
<mat-form-field id="screenShare-selfBrowserSurface"> }
<mat-label>selfBrowserSurface</mat-label> </mat-select>
<mat-select [(value)]="screenShareCaptureOptions!.selfBrowserSurface"> </mat-form-field>
<mat-option *ngFor="let surface of ['include','exclude']" [value]="surface">{{surface}}</mat-option> }
</mat-select> <mat-form-field id="screenShare-contentHint">
</mat-form-field> <mat-label>contentHint</mat-label>
<mat-form-field id="screenShare-surfaceSwitching"> <mat-select [(value)]="screenShareCaptureOptions!.contentHint">
<mat-label>surfaceSwitching</mat-label> @for (hint of ['text','detail','motion']; track hint) {
<mat-select [(value)]="screenShareCaptureOptions!.surfaceSwitching"> <mat-option [value]="hint">{{hint}}</mat-option>
<mat-option *ngFor="let surface of ['include','exclude']" [value]="surface">{{surface}}</mat-option> }
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field id="screenShare-systemAudio"> <mat-form-field id="screenShare-selfBrowserSurface">
<mat-label>systemAudio</mat-label> <mat-label>selfBrowserSurface</mat-label>
<mat-select [(value)]="screenShareCaptureOptions!.systemAudio"> <mat-select [(value)]="screenShareCaptureOptions!.selfBrowserSurface">
<mat-option *ngFor="let audio of ['include','exclude']" [value]="audio">{{audio}}</mat-option> @for (surface of ['include','exclude']; track surface) {
</mat-select> <mat-option [value]="surface">{{surface}}</mat-option>
</mat-form-field> }
<div> </mat-select>
<label>Resolution</label> </mat-form-field>
<mat-radio-group [(ngModel)]="customScreenShareResolution" (change)="handleCustomScreenResolutionChange()"> <mat-form-field id="screenShare-surfaceSwitching">
<mat-radio-button [value]="false">Default</mat-radio-button> <mat-label>surfaceSwitching</mat-label>
<mat-radio-button [value]="true">Custom</mat-radio-button> <mat-select [(value)]="screenShareCaptureOptions!.surfaceSwitching">
</mat-radio-group> @for (surface of ['include','exclude']; track surface) {
<app-video-resolution *ngIf="customScreenShareResolution" <mat-option [value]="surface">{{surface}}</mat-option>
[componentId]="'resolution-screen-capture-options'" }
[showTitle]="false" </mat-select>
[width]="screenShareCaptureOptions!.resolution!.width" </mat-form-field>
[height]="screenShareCaptureOptions!.resolution!.height" <mat-form-field id="screenShare-systemAudio">
[frameRate]="screenShareCaptureOptions!.resolution!.frameRate" <mat-label>systemAudio</mat-label>
[aspectRatio]="screenShareCaptureOptions!.resolution!.aspectRatio" <mat-select [(value)]="screenShareCaptureOptions!.systemAudio">
(resolutionChanged)="screenShareCaptureOptions!.resolution!.width = $event.width; screenShareCaptureOptions!.resolution!.height = $event.height; screenShareCaptureOptions!.resolution!.frameRate = $event.frameRate; screenShareCaptureOptions!.resolution!.aspectRatio = $event.aspectRatio"> @for (audio of ['include','exclude']; track audio) {
</app-video-resolution> <mat-option [value]="audio">{{audio}}</mat-option>
</div> }
</mat-select>
</mat-form-field>
<div>
<label>Resolution</label>
<mat-radio-group [(ngModel)]="customScreenShareResolution" (change)="handleCustomScreenResolutionChange()">
<mat-radio-button [value]="false">Default</mat-radio-button>
<mat-radio-button [value]="true">Custom</mat-radio-button>
</mat-radio-group>
@if (customScreenShareResolution) {
<app-video-resolution
[componentId]="'resolution-screen-capture-options'"
[showTitle]="false"
[width]="screenShareCaptureOptions!.resolution!.width"
[height]="screenShareCaptureOptions!.resolution!.height"
[frameRate]="screenShareCaptureOptions!.resolution!.frameRate"
[aspectRatio]="screenShareCaptureOptions!.resolution!.aspectRatio"
(resolutionChanged)="screenShareCaptureOptions!.resolution!.width = $event.width; screenShareCaptureOptions!.resolution!.height = $event.height; screenShareCaptureOptions!.resolution!.frameRate = $event.frameRate; screenShareCaptureOptions!.resolution!.aspectRatio = $event.aspectRatio">
</app-video-resolution>
}
</div>
</div>
}
</div> </div>
</div> }
<mat-divider *ngIf="trackPublishOptions"></mat-divider> @if (trackPublishOptions) {
<div *ngIf="trackPublishOptions"> <mat-divider></mat-divider>
<label><a href="https://docs.livekit.io/client-sdk-js/interfaces/TrackPublishOptions.html" target="_blank">TrackPublishOptions</a></label><br> }
<mat-checkbox id="trackPublish-simulcast" [(ngModel)]="trackPublishOptions.simulcast">simulcast</mat-checkbox> @if (trackPublishOptions) {
<mat-checkbox id="trackPublish-dtx" [(ngModel)]="trackPublishOptions.dtx">dtx</mat-checkbox> <div>
<mat-checkbox id="trackPublish-red" [(ngModel)]="trackPublishOptions.red">red</mat-checkbox> <label><a href="https://docs.livekit.io/client-sdk-js/interfaces/TrackPublishOptions.html" target="_blank">TrackPublishOptions</a></label><br>
<mat-checkbox id="trackPublish-backupCodec" [(ngModel)]="trackPublishOptions.backupCodec">backupCodec</mat-checkbox> <mat-checkbox id="trackPublish-simulcast" [(ngModel)]="trackPublishOptions.simulcast">simulcast</mat-checkbox>
<mat-checkbox id="trackPublish-forceStereo" [(ngModel)]="trackPublishOptions.forceStereo">forceStereo</mat-checkbox> <mat-checkbox id="trackPublish-dtx" [(ngModel)]="trackPublishOptions.dtx">dtx</mat-checkbox>
<mat-checkbox id="trackPublish-stopMicTrackOnMute" [(ngModel)]="trackPublishOptions.stopMicTrackOnMute">stopMicTrackOnMute</mat-checkbox> <mat-checkbox id="trackPublish-red" [(ngModel)]="trackPublishOptions.red">red</mat-checkbox>
<mat-form-field id="trackPublish-videoCodec"> <mat-checkbox id="trackPublish-backupCodec" [(ngModel)]="trackPublishOptions.backupCodec">backupCodec</mat-checkbox>
<mat-label>videoCodec</mat-label> <mat-checkbox id="trackPublish-forceStereo" [(ngModel)]="trackPublishOptions.forceStereo">forceStereo</mat-checkbox>
<mat-select [(value)]="trackPublishOptions.videoCodec"> <mat-checkbox id="trackPublish-stopMicTrackOnMute" [(ngModel)]="trackPublishOptions.stopMicTrackOnMute">stopMicTrackOnMute</mat-checkbox>
<mat-option *ngFor="let codec of ['vp8','h264','vp9','av1']" [value]="codec" [id]="'mat-option-' + codec">{{codec | uppercase}}</mat-option> <mat-form-field id="trackPublish-videoCodec">
</mat-select> <mat-label>videoCodec</mat-label>
</mat-form-field> <mat-select [(value)]="trackPublishOptions.videoCodec">
<mat-form-field id="trackPublish-scalabilityMode"> @for (codec of ['vp8','h264','vp9','av1']; track codec) {
<mat-label>scalabilityMode</mat-label> <mat-option [value]="codec" [id]="'mat-option-' + codec">{{codec | uppercase}}</mat-option>
<mat-select [(value)]="trackPublishOptions.scalabilityMode"> }
<mat-option *ngFor="let mode of ['L1T1','L1T2','L1T3','L2T1','L2T1h','L2T1_KEY','L2T2','L2T2h','L2T2_KEY','L2T3','L2T3h','L2T3_KEY','L3T1','L3T1h','L3T1_KEY','L3T2','L3T2h','L3T2_KEY','L3T3','L3T3h','L3T3_KEY']" </mat-select>
[value]="mode" [ngClass]="'mode-' + mode">{{mode}}</mat-option> </mat-form-field>
</mat-select> <mat-form-field id="trackPublish-scalabilityMode">
</mat-form-field> <mat-label>scalabilityMode</mat-label>
<mat-form-field class="inner-text"> <mat-select [(value)]="trackPublishOptions.scalabilityMode">
<mat-label>name</mat-label> @for (mode of ['L1T1','L1T2','L1T3','L2T1','L2T1h','L2T1_KEY','L2T2','L2T2h','L2T2_KEY','L2T3','L2T3h','L2T3_KEY','L3T1','L3T1h','L3T1_KEY','L3T2','L3T2h','L3T2_KEY','L3T3','L3T3h','L3T3_KEY']; track mode) {
<input matInput id="trackPublish-name" placeholder="name" [(ngModel)]="trackPublishOptions.name"/> <mat-option
</mat-form-field> [value]="mode" [ngClass]="'mode-' + mode">{{mode}}</mat-option>
<mat-form-field id="trackPublish-source"> }
<mat-label>source</mat-label> </mat-select>
<mat-select [(value)]="trackPublishOptions.source"> </mat-form-field>
<mat-option *ngFor="let source of ENUMERATION_SOURCE" [value]="source">{{source}}</mat-option> <mat-form-field class="inner-text">
</mat-select> <mat-label>name</mat-label>
</mat-form-field> <input matInput id="trackPublish-name" placeholder="name" [(ngModel)]="trackPublishOptions.name"/>
<mat-form-field id="trackPublish-stream"> </mat-form-field>
<mat-label>stream</mat-label> <mat-form-field id="trackPublish-source">
<input matInput id="trackPublish-stream" placeholder="stream" [(ngModel)]="trackPublishOptions.stream"/> <mat-label>source</mat-label>
</mat-form-field> <mat-select [(value)]="trackPublishOptions.source">
</div> @for (source of ENUMERATION_SOURCE; track source) {
<mat-option [value]="source">{{source}}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field id="trackPublish-stream">
<mat-label>stream</mat-label>
<input matInput id="trackPublish-stream" placeholder="stream" [(ngModel)]="trackPublishOptions.stream"/>
</mat-form-field>
</div>
}
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>

View File

@ -1,19 +1,21 @@
<div class="resolution-div"> <div class="resolution-div">
<p *ngIf="showTitle" class="resolution-title">Resolution</p> @if (showTitle) {
<mat-form-field class="inner-text-input"> <p class="resolution-title">Resolution</p>
<mat-label>width</mat-label> }
<input matInput [id]="componentId+'-width'" type="number" placeholder="width" [(ngModel)]="width" (ngModelChange)="emitChanges()"/> <mat-form-field class="inner-text-input">
</mat-form-field> <mat-label>width</mat-label>
<mat-form-field class="inner-text-input"> <input matInput [id]="componentId+'-width'" type="number" placeholder="width" [(ngModel)]="width" (ngModelChange)="emitChanges()"/>
<mat-label>height</mat-label> </mat-form-field>
<input matInput [id]="componentId+'-height'" type="number" placeholder="height" [(ngModel)]="height" (ngModelChange)="emitChanges()"/> <mat-form-field class="inner-text-input">
</mat-form-field> <mat-label>height</mat-label>
<mat-form-field class="inner-text-input"> <input matInput [id]="componentId+'-height'" type="number" placeholder="height" [(ngModel)]="height" (ngModelChange)="emitChanges()"/>
<mat-label>frameRate</mat-label> </mat-form-field>
<input matInput [id]="componentId+'-frameRate'" type="number" placeholder="frameRate" [(ngModel)]="frameRate" (ngModelChange)="emitChanges()"/> <mat-form-field class="inner-text-input">
</mat-form-field> <mat-label>frameRate</mat-label>
<mat-form-field class="inner-text-input"> <input matInput [id]="componentId+'-frameRate'" type="number" placeholder="frameRate" [(ngModel)]="frameRate" (ngModelChange)="emitChanges()"/>
<mat-label>aspectRatio</mat-label> </mat-form-field>
<input matInput [id]="componentId+'-aspectRatio'" type="number" placeholder="aspectRatio" [(ngModel)]="aspectRatio" (ngModelChange)="emitChanges()"/> <mat-form-field class="inner-text-input">
</mat-form-field> <mat-label>aspectRatio</mat-label>
<input matInput [id]="componentId+'-aspectRatio'" type="number" placeholder="aspectRatio" [(ngModel)]="aspectRatio" (ngModelChange)="emitChanges()"/>
</mat-form-field>
</div> </div>

View File

@ -25,30 +25,30 @@
<button mat-button id="list-rooms-api-btn" (click)="listRooms()">List Rooms</button> <button mat-button id="list-rooms-api-btn" (click)="listRooms()">List Rooms</button>
<span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!apiRoomName"> <span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!apiRoomName">
<button mat-button id="delete-room-api-btn" (click)="deleteRoom()" [disabled]="!apiRoomName">Delete <button mat-button id="delete-room-api-btn" (click)="deleteRoom()" [disabled]="!apiRoomName">Delete
Room</button> Room</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!apiRoomName"> <span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!apiRoomName">
<button mat-button id="list-participants-api-btn" (click)="listParticipants()" [disabled]="!apiRoomName">List <button mat-button id="list-participants-api-btn" (click)="listParticipants()" [disabled]="!apiRoomName">List
participants</button> participants</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room" and "Participant" required' <span style="display: inline-block" matTooltip='"Room" and "Participant" required'
[matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity"> [matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity">
<button mat-button id="get-participant-api-btn" (click)="getParticipant()" <button mat-button id="get-participant-api-btn" (click)="getParticipant()"
[disabled]="!apiRoomName || !apiParticipantIdentity">Get participant</button> [disabled]="!apiRoomName || !apiParticipantIdentity">Get participant</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room" and "Participant" required' <span style="display: inline-block" matTooltip='"Room" and "Participant" required'
[matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity"> [matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity">
<button mat-button id="remove-participant-api-btn" (click)="removeParticipant()" <button mat-button id="remove-participant-api-btn" (click)="removeParticipant()"
[disabled]="!apiRoomName || !apiParticipantIdentity">Remove participant</button> [disabled]="!apiRoomName || !apiParticipantIdentity">Remove participant</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room", "Participant" and "Track" required' <span style="display: inline-block" matTooltip='"Room", "Participant" and "Track" required'
[matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity && !!apiTrackSid"> [matTooltipDisabled]="!!apiRoomName && !!apiParticipantIdentity && !!apiTrackSid">
<button mat-button id="force-unpublish-api-btn" (click)="mutePublishedTrack()" <button mat-button id="force-unpublish-api-btn" (click)="mutePublishedTrack()"
[disabled]="!apiRoomName || !apiParticipantIdentity || !apiTrackSid">Mute [disabled]="!apiRoomName || !apiParticipantIdentity || !apiTrackSid">Mute
track</button> track</button>
</span> </span>
<mat-checkbox class="subscriber-checkbox" name="subscriber" [(ngModel)]="muteTrack" <mat-checkbox class="subscriber-checkbox" name="subscriber" [(ngModel)]="muteTrack"
[disabled]="!apiRoomName || !apiParticipantIdentity || !apiTrackSid">Mute</mat-checkbox> [disabled]="!apiRoomName || !apiParticipantIdentity || !apiTrackSid">Mute</mat-checkbox>
<span style="display: inline-block"> <span style="display: inline-block">
<button mat-button id="delete-all-rooms-api-btn" (click)="deleteAllRooms()" style="font-style: italic; font-size: 0.75rem; margin-left: 5px;">Delete all</button> <button mat-button id="delete-all-rooms-api-btn" (click)="deleteAllRooms()" style="font-style: italic; font-size: 0.75rem; margin-left: 5px;">Delete all</button>
</span> </span>
@ -80,18 +80,18 @@
<button mat-button id="list-egress-api-btn" (click)="listEgress()">List Egress</button> <button mat-button id="list-egress-api-btn" (click)="listEgress()">List Egress</button>
<span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!egressRoomName"> <span style="display: inline-block" matTooltip='"Room" required' [matTooltipDisabled]="!!egressRoomName">
<button mat-button id="start-room-composite-egress-api-btn" (click)="startRoomCompositeEgress()" <button mat-button id="start-room-composite-egress-api-btn" (click)="startRoomCompositeEgress()"
[disabled]="!egressRoomName">Start Room Composite Egress</button> [disabled]="!egressRoomName">Start Room Composite Egress</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room", "Audio Track" and "Video Track" required' <span style="display: inline-block" matTooltip='"Room", "Audio Track" and "Video Track" required'
[matTooltipDisabled]="!(!egressRoomName || !audioTrackId || !videoTrackId)"> [matTooltipDisabled]="!(!egressRoomName || !audioTrackId || !videoTrackId)">
<button mat-button id="start-track-composite-egress-api-btn" (click)="startTrackCompositeEgress()" <button mat-button id="start-track-composite-egress-api-btn" (click)="startTrackCompositeEgress()"
[disabled]="!egressRoomName || !audioTrackId || !videoTrackId">Start Track Composite Egress</button> [disabled]="!egressRoomName || !audioTrackId || !videoTrackId">Start Track Composite Egress</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Room" and only one of "Audio Track" or "Video Track" required' <span style="display: inline-block" matTooltip='"Room" and only one of "Audio Track" or "Video Track" required'
[matTooltipDisabled]="!(!egressRoomName || (!!audioTrackId && !!videoTrackId || !audioTrackId && !videoTrackId))"> [matTooltipDisabled]="!(!egressRoomName || (!!audioTrackId && !!videoTrackId || !audioTrackId && !videoTrackId))">
<button mat-button id="start-track-egress-api-btn" (click)="startTrackEgress()" <button mat-button id="start-track-egress-api-btn" (click)="startTrackEgress()"
[disabled]="!egressRoomName || (!!audioTrackId && !!videoTrackId || !audioTrackId && !videoTrackId)">Start [disabled]="!egressRoomName || (!!audioTrackId && !!videoTrackId || !audioTrackId && !videoTrackId)">Start
Track Egress</button> Track Egress</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Egress ID" required' [matTooltipDisabled]="!!egressId"> <span style="display: inline-block" matTooltip='"Egress ID" required' [matTooltipDisabled]="!!egressId">
<button mat-button id="stop-egress-api-btn" (click)="stopEgress()" [disabled]="!egressId">Stop Egress</button> <button mat-button id="stop-egress-api-btn" (click)="stopEgress()" [disabled]="!egressId">Stop Egress</button>
@ -103,9 +103,11 @@
<mat-form-field id="room-composite-layout-select"> <mat-form-field id="room-composite-layout-select">
<mat-label>Layout</mat-label> <mat-label>Layout</mat-label>
<mat-select [(value)]="roomCompositeLayoutSelected"> <mat-select [(value)]="roomCompositeLayoutSelected">
<mat-option *ngFor="let layout of ROOM_COMPOSITE_LAYOUTS" [value]="layout"> @for (layout of ROOM_COMPOSITE_LAYOUTS; track layout) {
{{layout}} <mat-option [value]="layout">
</mat-option> {{layout}}
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-checkbox id="room-composite-audio-only" [(ngModel)]="roomCompositeAudioOnly">Audio only</mat-checkbox> <mat-checkbox id="room-composite-audio-only" [(ngModel)]="roomCompositeAudioOnly">Audio only</mat-checkbox>
@ -116,34 +118,42 @@
<div class="egress-output"> <div class="egress-output">
<div class="egress-output-container"> <div class="egress-output-container">
<mat-checkbox id="file-output-checkbox" [(ngModel)]="fileOutputSelected">MP4 file output</mat-checkbox> <mat-checkbox id="file-output-checkbox" [(ngModel)]="fileOutputSelected">MP4 file output</mat-checkbox>
<mat-form-field id="s3-endpoint" *ngIf="fileOutputSelected"> @if (fileOutputSelected) {
<mat-label>S3 endpoint</mat-label> <mat-form-field id="s3-endpoint">
<input matInput type="url" placeholder="S3 endpoint" [(ngModel)]="s3Endpoint"> <mat-label>S3 endpoint</mat-label>
</mat-form-field> <input matInput type="url" placeholder="S3 endpoint" [(ngModel)]="s3Endpoint">
</mat-form-field>
}
</div> </div>
<div class="egress-output-container"> <div class="egress-output-container">
<mat-checkbox id="stream-output-checkbox" [(ngModel)]="streamOutputSelected">RTMP stream output</mat-checkbox> <mat-checkbox id="stream-output-checkbox" [(ngModel)]="streamOutputSelected">RTMP stream output</mat-checkbox>
<mat-form-field *ngIf="streamOutputSelected" id="rmpt-urls" class="example-chip-list"> @if (streamOutputSelected) {
<mat-label>RTMP urls</mat-label> <mat-form-field id="rmpt-urls" class="example-chip-list">
<mat-chip-grid #chipGrid aria-label="Enter fruits"> <mat-label>RTMP urls</mat-label>
<mat-chip-row *ngFor="let url of rtmpUrls" (removed)="removeRtmpUrl(url)"> <mat-chip-grid #chipGrid aria-label="Enter fruits">
{{url}} @for (url of rtmpUrls; track url) {
<button class="remove-element-btn" matChipRemove aria-label="Remove URL"> <mat-chip-row (removed)="removeRtmpUrl(url)">
<mat-icon class="remove-element-icon">cancel</mat-icon> {{url}}
</button> <button class="remove-element-btn" matChipRemove aria-label="Remove URL">
</mat-chip-row> <mat-icon class="remove-element-icon">cancel</mat-icon>
<input placeholder="New URL" [matChipInputFor]="chipGrid" </button>
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur" </mat-chip-row>
(matChipInputTokenEnd)="addRtmpUrl($event)" /> }
</mat-chip-grid> <input placeholder="New URL" [matChipInputFor]="chipGrid"
</mat-form-field> [matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="addRtmpUrl($event)" />
</mat-chip-grid>
</mat-form-field>
}
</div> </div>
<div class="egress-output-container"> <div class="egress-output-container">
<mat-checkbox id="segment-output-checkbox" [(ngModel)]="segmentOutputSelected">HLS segment output</mat-checkbox> <mat-checkbox id="segment-output-checkbox" [(ngModel)]="segmentOutputSelected">HLS segment output</mat-checkbox>
<mat-form-field id="segment-duration" *ngIf="segmentOutputSelected"> @if (segmentOutputSelected) {
<mat-label>Segment duration (s)</mat-label> <mat-form-field id="segment-duration">
<input matInput type="number" min="1" placeholder="Segment duration" [(ngModel)]="segmentDuration"> <mat-label>Segment duration (s)</mat-label>
</mat-form-field> <input matInput type="number" min="1" placeholder="Segment duration" [(ngModel)]="segmentDuration">
</mat-form-field>
}
</div> </div>
</div> </div>
@ -166,18 +176,20 @@
<span style="display: inline-block" [matTooltip]="!ingressRoomName ? 'Room required' : 'At least one of audio and video required'" <span style="display: inline-block" [matTooltip]="!ingressRoomName ? 'Room required' : 'At least one of audio and video required'"
[matTooltipDisabled]="!!ingressRoomName && (ingressWithAudio || ingressWithVideo)"> [matTooltipDisabled]="!!ingressRoomName && (ingressWithAudio || ingressWithVideo)">
<button mat-button id="create-ingress-api-btn" (click)="createIngress()" <button mat-button id="create-ingress-api-btn" (click)="createIngress()"
[disabled]="!ingressRoomName || (!ingressWithAudio && !ingressWithVideo)">Create Ingress</button> [disabled]="!ingressRoomName || (!ingressWithAudio && !ingressWithVideo)">Create Ingress</button>
</span> </span>
<span style="display: inline-block" matTooltip='"Ingress ID" required' [matTooltipDisabled]="!!ingressId"> <span style="display: inline-block" matTooltip='"Ingress ID" required' [matTooltipDisabled]="!!ingressId">
<button mat-button id="delete-ingress-api-btn" (click)="deleteIngress()" [disabled]="!ingressId">Delete <button mat-button id="delete-ingress-api-btn" (click)="deleteIngress()" [disabled]="!ingressId">Delete
Ingress</button> Ingress</button>
</span> </span>
<mat-form-field id="ingress-input-type-select"> <mat-form-field id="ingress-input-type-select">
<mat-label>Input Type</mat-label> <mat-label>Input Type</mat-label>
<mat-select [(value)]="inputTypeSelected"> <mat-select [(value)]="inputTypeSelected">
<mat-option *ngFor="let inputType of INGRESS_INPUT_TYPES" [value]="inputType.value"> @for (inputType of INGRESS_INPUT_TYPES; track inputType) {
{{inputType.viewValue}} <mat-option [value]="inputType.value">
</mat-option> {{inputType.viewValue}}
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<span style="display: inline-block"> <span style="display: inline-block">
@ -192,7 +204,7 @@
<span style="display: inline-block" [matTooltip]="!ingressWithVideo ? 'Only with video' : 'Preset overrides this value'" <span style="display: inline-block" [matTooltip]="!ingressWithVideo ? 'Only with video' : 'Preset overrides this value'"
[matTooltipDisabled]="!!ingressWithVideo && ingressVideoEncodingPresetSelected == undefined"> [matTooltipDisabled]="!!ingressWithVideo && ingressVideoEncodingPresetSelected == undefined">
<mat-checkbox id="ingress-simulcast" [(ngModel)]="ingressSimulcast" <mat-checkbox id="ingress-simulcast" [(ngModel)]="ingressSimulcast"
[disabled]="!ingressWithVideo || ingressVideoEncodingPresetSelected != undefined">Simulcast</mat-checkbox> [disabled]="!ingressWithVideo || ingressVideoEncodingPresetSelected != undefined">Simulcast</mat-checkbox>
</span> </span>
<span style="display: inline-block" matTooltip="Only for WHIP" [matTooltipDisabled]="inputTypeSelected === 1"> <span style="display: inline-block" matTooltip="Only for WHIP" [matTooltipDisabled]="inputTypeSelected === 1">
<mat-checkbox id="ingress-transcoding" [(ngModel)]="ingressEnableTranscoding" [disabled]="inputTypeSelected !== 1">Transcoding</mat-checkbox> <mat-checkbox id="ingress-transcoding" [(ngModel)]="ingressEnableTranscoding" [disabled]="inputTypeSelected !== 1">Transcoding</mat-checkbox>
@ -201,9 +213,11 @@
<mat-form-field id="ingress-url-type-select"> <mat-form-field id="ingress-url-type-select">
<mat-label>URL type</mat-label> <mat-label>URL type</mat-label>
<mat-select [(value)]="ingressUrlType" [disabled]="inputTypeSelected !== 2"> <mat-select [(value)]="ingressUrlType" [disabled]="inputTypeSelected !== 2">
<mat-option *ngFor="let urlType of INGRESS_URL_TYPES" [value]="urlType" [id]="'mat-option-' + urlType"> @for (urlType of INGRESS_URL_TYPES; track urlType) {
{{urlType}} <mat-option [value]="urlType" [id]="'mat-option-' + urlType">
</mat-option> {{urlType}}
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</span> </span>
@ -212,9 +226,11 @@
<mat-form-field id="ingress-video-codec-select"> <mat-form-field id="ingress-video-codec-select">
<mat-label>Codec</mat-label> <mat-label>Codec</mat-label>
<mat-select [(value)]="ingressVideoCodecSelected" [disabled]="!ingressWithVideo || ingressVideoEncodingPresetSelected != undefined"> <mat-select [(value)]="ingressVideoCodecSelected" [disabled]="!ingressWithVideo || ingressVideoEncodingPresetSelected != undefined">
<mat-option *ngFor="let codec of INGRESS_VIDEO_CODECS" [value]="codec.value" [id]="'mat-option-' + codec.viewValue"> @for (codec of INGRESS_VIDEO_CODECS; track codec) {
{{codec.viewValue}} <mat-option [value]="codec.value" [id]="'mat-option-' + codec.viewValue">
</mat-option> {{codec.viewValue}}
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</span> </span>
@ -222,9 +238,11 @@
<mat-form-field id="ingress-preset-select"> <mat-form-field id="ingress-preset-select">
<mat-label>Preset</mat-label> <mat-label>Preset</mat-label>
<mat-select [(value)]="ingressVideoEncodingPresetSelected" [disabled]="!ingressWithVideo"> <mat-select [(value)]="ingressVideoEncodingPresetSelected" [disabled]="!ingressWithVideo">
<mat-option *ngFor="let preset of INGRESS_VIDEO_ENCODING_PRESETS" [value]="preset.value" [id]="'mat-option-' + preset.viewValue"> @for (preset of INGRESS_VIDEO_ENCODING_PRESETS; track preset) {
{{preset.viewValue}} <mat-option [value]="preset.value" [id]="'mat-option-' + preset.viewValue">
</mat-option> {{preset.viewValue}}
</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</span> </span>

View File

@ -1,99 +1,100 @@
<div class="div-wrapper"> <div class="div-wrapper">
<div class="config-half"> <div class="config-half">
<mat-card class="config-card"> <mat-card class="config-card">
<mat-card-content> <mat-card-content>
<div class="row no-wrap-row"> <div class="row no-wrap-row">
<mat-form-field style="margin-right: 8px" class="custom-mat-form-field"> <mat-form-field style="margin-right: 8px" class="custom-mat-form-field">
<mat-label>Room</mat-label> <mat-label>Room</mat-label>
<input matInput placeholder="Room name" [id]="'room-name-input-' + index" [(ngModel)]="roomName" <input matInput placeholder="Room name" [id]="'room-name-input-' + index" [(ngModel)]="roomName"
[disabled]="!!room"> [disabled]="!!room">
</mat-form-field> </mat-form-field>
<mat-form-field class="custom-mat-form-field"> <mat-form-field class="custom-mat-form-field">
<mat-label>Participant</mat-label> <mat-label>Participant</mat-label>
<input matInput placeholder="Participant name" [id]="'participant-name-input-'+ index" <input matInput placeholder="Participant name" [id]="'participant-name-input-'+ index"
[(ngModel)]="participantName" [disabled]="!!room"> [(ngModel)]="participantName" [disabled]="!!room">
</mat-form-field> </mat-form-field>
<div class="room-btns-div"> <div class="room-btns-div">
<button mat-icon-button title="Room options" [id]="'room-options-btn-' + index" <button mat-icon-button title="Room options" [id]="'room-options-btn-' + index"
class="mat-icon-custom" (click)="openOptionsDialog()" [disabled]="room"> class="mat-icon-custom" (click)="openOptionsDialog()" [disabled]="room">
<mat-icon class="mat-icon-custom-ic" aria-label="Room options button">settings</mat-icon> <mat-icon class="mat-icon-custom-ic" aria-label="Room options button">settings</mat-icon>
</button> </button>
<button mat-icon-button title="Room API" [id]="'room-api-btn-' + index" class="mat-icon-custom" <button mat-icon-button title="Room API" [id]="'room-api-btn-' + index" class="mat-icon-custom"
(click)="openRoomApiDialog()"> (click)="openRoomApiDialog()">
<mat-icon class="mat-icon-custom-ic" aria-label="Room API button">cloud_circle</mat-icon> <mat-icon class="mat-icon-custom-ic" aria-label="Room API button">cloud_circle</mat-icon>
</button> </button>
<button mat-icon-button title="Room events" [id]="'room-events-btn-' + index" <button mat-icon-button title="Room events" [id]="'room-events-btn-' + index"
class="mat-icon-custom" (click)="openRoomEventsDialog()"> class="mat-icon-custom" (click)="openRoomEventsDialog()">
<mat-icon class="mat-icon-custom-ic" <mat-icon class="mat-icon-custom-ic"
aria-label="Room events button">notifications</mat-icon> aria-label="Room events button">notifications</mat-icon>
</button> </button>
</div> </div>
</div> </div>
<div class="row no-wrap-row"> <div class="row no-wrap-row">
<button class="connect-btn" mat-button (click)="createTokenAndConnectRoom()" <button class="connect-btn" mat-button (click)="createTokenAndConnectRoom()"
[disabled]="!!room">CONNECT</button> [disabled]="!!room">CONNECT</button>
<mat-checkbox class="subscriber-checkbox" name="subscriber" [(ngModel)]="roomConf.subscriber" <mat-checkbox class="subscriber-checkbox" name="subscriber" [(ngModel)]="roomConf.subscriber"
[disabled]="!!room">Subscriber</mat-checkbox> [disabled]="!!room">Subscriber</mat-checkbox>
<mat-checkbox class="publisher-checkbox" name="publisher" [(ngModel)]="roomConf.publisher" <mat-checkbox class="publisher-checkbox" name="publisher" [(ngModel)]="roomConf.publisher"
[disabled]="!!room">Publisher</mat-checkbox> [disabled]="!!room">Publisher</mat-checkbox>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card>
</div>
<div *ngIf="room" class="room-half">
<mat-card class="room-card">
<mat-card-header style="margin-bottom: 7px">
<div class="row">
<mat-card-title class="room-mat-card-title">{{room.name}}</mat-card-title>
<div class="room-actions">
<button class="peer-info-btn" (click)="openInfoDialog()" title="PCTransports info">
<mat-icon aria-label="PCTransports info button">info</mat-icon>
</button>
<button class="message-btn" (click)="sendData()" title="Broadcast message to room">
<mat-icon aria-label="Send message button">chat</mat-icon>
</button>
<button class="disconnect-btn" (click)="disconnectRoom()" title="Disconnect room">
<mat-icon aria-label="Disconnect button">clear</mat-icon>
</button>
</div>
</div>
</mat-card-header>
<mat-card-content class="room-card-content">
<div class="event-list-flex">
<div class="event-list scroll-custom">
<mat-accordion [attr.id]="'room-events-' + room.localParticipant.identity">
<mat-expansion-panel *ngFor="let event of events" [ngClass]="event.eventCategory">
<mat-expansion-panel-header
[ngClass]="event.eventType + '-' + room.localParticipant.identity"
[collapsedHeight]="'20px'" [expandedHeight]="'20px'">
{{event.eventType}}
</mat-expansion-panel-header>
<div class="event-content">{{event.eventDescription}}</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
<div>
<app-participant class="local-participant" [participant]="room.localParticipant" [room]="room"
[index]="index"></app-participant>
<app-participant class="remote-participant" *ngFor="let participant of room.remoteParticipants | keyvalue"
[participant]="participant.value" [room]="room" [index]="index" (sendDataToOneParticipant)="sendData($event)"></app-participant>
</div>
</mat-card-content>
</mat-card>
</mat-card>
</div>
@if (room) {
<div class="room-half">
<mat-card class="room-card">
<mat-card-header style="margin-bottom: 7px">
<div class="row">
<mat-card-title class="room-mat-card-title">{{room.name}}</mat-card-title>
<div class="room-actions">
<button class="peer-info-btn" (click)="openInfoDialog()" title="PCTransports info">
<mat-icon aria-label="PCTransports info button">info</mat-icon>
</button>
<button class="message-btn" (click)="sendData()" title="Broadcast message to room">
<mat-icon aria-label="Send message button">chat</mat-icon>
</button>
<button class="disconnect-btn" (click)="disconnectRoom()" title="Disconnect room">
<mat-icon aria-label="Disconnect button">clear</mat-icon>
</button>
</div>
</div>
</mat-card-header>
<mat-card-content class="room-card-content">
<div class="event-list-flex">
<div class="event-list scroll-custom">
<mat-accordion [attr.id]="'room-events-' + room.localParticipant.identity">
@for (event of events; track event) {
<mat-expansion-panel [ngClass]="event.eventCategory">
<mat-expansion-panel-header
[ngClass]="event.eventType + '-' + room.localParticipant.identity"
[collapsedHeight]="'20px'" [expandedHeight]="'20px'">
{{event.eventType}}
</mat-expansion-panel-header>
<div class="event-content">{{event.eventDescription}}</div>
</mat-expansion-panel>
}
</mat-accordion>
</div>
</div>
<div>
<app-participant class="local-participant" [participant]="room.localParticipant" [room]="room"
[index]="index"></app-participant>
@for (participant of room.remoteParticipants | keyvalue; track participant) {
<app-participant class="remote-participant"
[participant]="participant.value" [room]="room" [index]="index" (sendDataToOneParticipant)="sendData($event)"></app-participant>
}
</div>
</mat-card-content>
</mat-card>
</div> </div>
}
</div> </div>

View File

@ -1,61 +1,85 @@
<div class="participant-container"> <div class="participant-container">
<div class="participant-actions"> <div class="participant-actions">
<p class="participant-identity" [ngClass]="{'local-participant-identity' : participant.isLocal}"> <p class="participant-identity" [ngClass]="{'local-participant-identity' : participant.isLocal}">
{{participant.identity}}</p> {{participant.identity}}</p>
<div class="participant-buttons"> <div class="participant-buttons">
<button *ngIf="participant.isLocal" class="add-audio-btn" (click)="addAudioTrack()" title="New audio track" @if (participant.isLocal) {
matTooltip="New audio track" matTooltipClass="custom-tooltip"> <button class="add-audio-btn" (click)="addAudioTrack()" title="New audio track"
<mat-icon aria-label="New audio track">microphone</mat-icon> matTooltip="New audio track" matTooltipClass="custom-tooltip">
</button> <mat-icon aria-label="New audio track">microphone</mat-icon>
<button *ngIf="participant.isLocal" class="options-audio-btn" (click)="openAudioTrackOptionsDialog()" </button>
title="Video track options" matTooltip="Audio track options" matTooltipClass="custom-tooltip"> }
<mat-icon aria-label="Audio options">more_vert</mat-icon> @if (participant.isLocal) {
</button> <button class="options-audio-btn" (click)="openAudioTrackOptionsDialog()"
<button *ngIf="participant.isLocal" class="add-video-btn" (click)="addVideoTrack()" title="New video track" title="Video track options" matTooltip="Audio track options" matTooltipClass="custom-tooltip">
matTooltip="New video track" matTooltipClass="custom-tooltip"> <mat-icon aria-label="Audio options">more_vert</mat-icon>
<mat-icon aria-label="New video track">videocam</mat-icon> </button>
</button> }
<button *ngIf="participant.isLocal" class="options-video-btn" (click)="openVideoTrackOptionsDialog()" @if (participant.isLocal) {
title="Video track options" matTooltip="Video track options" matTooltipClass="custom-tooltip"> <button class="add-video-btn" (click)="addVideoTrack()" title="New video track"
<mat-icon aria-label="Video options">more_vert</mat-icon> matTooltip="New video track" matTooltipClass="custom-tooltip">
</button> <mat-icon aria-label="New video track">videocam</mat-icon>
<button *ngIf="participant.isLocal" class="add-screen-btn" (click)="addScreenTrack()" </button>
title="New screen track" matTooltip="New screen track" matTooltipClass="custom-tooltip"> }
<mat-icon aria-label="New screen track">present_to_all</mat-icon> @if (participant.isLocal) {
</button> <button class="options-video-btn" (click)="openVideoTrackOptionsDialog()"
<button *ngIf="participant.isLocal" class="options-screen-btn" (click)="openScreenTrackOptionsDialog()" title="Video track options" matTooltip="Video track options" matTooltipClass="custom-tooltip">
title="Screen track options" matTooltip="Screen track options" matTooltipClass="custom-tooltip"> <mat-icon aria-label="Video options">more_vert</mat-icon>
<mat-icon aria-label="Screen options">more_vert</mat-icon> </button>
</button> }
<button *ngIf="participant.isLocal" class="options-track-publish-btn" (click)="openTrackPublishOptionsDialog()" @if (participant.isLocal) {
title="Track publish options" matTooltip="Track publish options" matTooltipClass="custom-tooltip"> <button class="add-screen-btn" (click)="addScreenTrack()"
<mat-icon aria-label="Track publish options">settings</mat-icon> title="New screen track" matTooltip="New screen track" matTooltipClass="custom-tooltip">
</button> <mat-icon aria-label="New screen track">present_to_all</mat-icon>
<button *ngIf="!participant.isLocal" class="message-btn" (click)="sendData()" title="Send message to this participant"> </button>
<mat-icon aria-label="Send message button">chat</mat-icon> }
</button> @if (participant.isLocal) {
</div> <button class="options-screen-btn" (click)="openScreenTrackOptionsDialog()"
title="Screen track options" matTooltip="Screen track options" matTooltipClass="custom-tooltip">
<mat-icon aria-label="Screen options">more_vert</mat-icon>
</button>
}
@if (participant.isLocal) {
<button class="options-track-publish-btn" (click)="openTrackPublishOptionsDialog()"
title="Track publish options" matTooltip="Track publish options" matTooltipClass="custom-tooltip">
<mat-icon aria-label="Track publish options">settings</mat-icon>
</button>
}
@if (!participant.isLocal) {
<button class="message-btn" (click)="sendData()" title="Send message to this participant">
<mat-icon aria-label="Send message button">chat</mat-icon>
</button>
}
</div> </div>
<div *ngIf="participant" class="participant-content"> </div>
<div class="event-list scroll-custom"> @if (participant) {
<mat-accordion [attr.id]="index + '-participant-events-' + participant.identity"> <div class="participant-content">
<mat-expansion-panel *ngFor="let event of events" <div class="event-list scroll-custom">
[ngClass]="event.eventCategory"> <mat-accordion [attr.id]="index + '-participant-events-' + participant.identity">
<mat-expansion-panel-header [ngClass]="event.eventType + '-' + participant.identity" @for (event of events; track event) {
[collapsedHeight]="'20px'" [expandedHeight]="'20px'"> <mat-expansion-panel
{{event.eventType}} [ngClass]="event.eventCategory">
</mat-expansion-panel-header> <mat-expansion-panel-header [ngClass]="event.eventType + '-' + participant.identity"
<div class="event-content">{{event.eventDescription}}</div> [collapsedHeight]="'20px'" [expandedHeight]="'20px'">
</mat-expansion-panel> {{event.eventType}}
</mat-accordion> </mat-expansion-panel-header>
</div> <div class="event-content">{{event.eventDescription}}</div>
<div class="audio-tracks-container"> </mat-expansion-panel>
<app-audio-track *ngFor="let trackPublication of participant.audioTrackPublications| keyvalue" }
[index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.audioTrack" </mat-accordion>
[localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-audio-track> </div>
</div> <div class="audio-tracks-container">
<app-video-track *ngFor="let trackPublication of participant.videoTrackPublications | keyvalue" @for (trackPublication of participant.audioTrackPublications| keyvalue; track trackPublication) {
[index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.videoTrack" <app-audio-track
[localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-video-track> [index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.audioTrack"
[localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-audio-track>
}
</div>
@for (trackPublication of participant.videoTrackPublications | keyvalue; track trackPublication) {
<app-video-track
[index]="index" [trackPublication]="trackPublication.value" [track]="trackPublication.value.videoTrack"
[localParticipant]="localParticipant" (newTrackEvent)="events.push($event)"></app-video-track>
}
</div> </div>
}
</div> </div>

View File

@ -14,12 +14,14 @@
</div> </div>
<div> <div>
<button mat-icon-button id="scenario-options-btn" title="Scenario options" class="mat-icon-custom" (click)="openScenarioOptionsDialog()" [disabled]="scenarioPlaying"> <button mat-icon-button id="scenario-options-btn" title="Scenario options" class="mat-icon-custom" (click)="openScenarioOptionsDialog()" [disabled]="scenarioPlaying">
<mat-icon class="mat-icon-custom-ic" aria-label="Room options button">settings</mat-icon> <mat-icon class="mat-icon-custom-ic" aria-label="Room options button">settings</mat-icon>
</button> </button>
<button id="finish-btn" mat-raised-button color="primary" (click)="endScenario()" [disabled]="!scenarioPlaying">FINISH</button> <button id="finish-btn" mat-raised-button color="primary" (click)="endScenario()" [disabled]="!scenarioPlaying">FINISH</button>
</div> </div>
</div> </div>
<div class="instance-div"> <div class="instance-div">
<app-users-table *ngIf="!!users.length" [users]="users"></app-users-table> @if (!!users.length) {
<app-users-table [users]="users"></app-users-table>
}
</div> </div>

View File

@ -1,29 +1,31 @@
<div> <div>
<div class="top-div"> <div class="top-div">
<div class="controls-div"> <div class="controls-div">
<button id="add-user-btn" mat-raised-button color="primary" (click)="addUser()">ADD USER</button> <button id="add-user-btn" mat-raised-button color="primary" (click)="addUser()">ADD USER</button>
<button id="remove-user-btn" mat-raised-button color="primary" (click)="removeUser()" <button id="remove-user-btn" mat-raised-button color="primary" (click)="removeUser()"
[disabled]="!users.length">REMOVE [disabled]="!users.length">REMOVE
USER</button> USER</button>
<button id="remove-all-users-btn" mat-raised-button color="primary" (click)="removeAllUsers()" <button id="remove-all-users-btn" mat-raised-button color="primary" (click)="removeAllUsers()"
[disabled]="!users.length">REMOVE [disabled]="!users.length">REMOVE
ALL</button> ALL</button>
</div>
<div class="scenario-div">
<mat-checkbox id="auto-join-checkbox" class="auto-join-check" [(ngModel)]="autoJoin" name="autoJoin">Auto
join</mat-checkbox>
<button id="one2one-btn" mat-raised-button color="primary" (click)="loadScenario(numberParticipants,0,0)">{{numberParticipants}}:{{numberParticipants}}</button>
<button id="one2many-btn" mat-raised-button color="primary"
(click)="loadScenario(0,1,numberParticipants)">1 PUB : {{numberParticipants}} SUB</button>
<mat-form-field>
<input id="one2many-input" matInput type="number" min=1 max=100 [(ngModel)]="numberParticipants">
</mat-form-field>
</div>
</div> </div>
<div class="scenario-div">
<mat-checkbox id="auto-join-checkbox" class="auto-join-check" [(ngModel)]="autoJoin" name="autoJoin">Auto
join</mat-checkbox>
<button id="one2one-btn" mat-raised-button color="primary" (click)="loadScenario(numberParticipants,0,0)">{{numberParticipants}}:{{numberParticipants}}</button>
<button id="one2many-btn" mat-raised-button color="primary"
(click)="loadScenario(0,1,numberParticipants)">1 PUB : {{numberParticipants}} SUB</button>
<mat-form-field>
<input id="one2many-input" matInput type="number" min=1 max=100 [(ngModel)]="numberParticipants">
</mat-form-field>
</div>
</div>
<div class="instance-div"> <div class="instance-div">
<app-openvidu-instance @fadeAnimation *ngFor="let user of users; let i=index" [attr.id]="'openvidu-instance-' + i" [roomConf]="user" [index]="i"> @for (user of users; track user; let i = $index) {
</app-openvidu-instance> <app-openvidu-instance @fadeAnimation [attr.id]="'openvidu-instance-' + i" [roomConf]="user" [index]="i">
</div> </app-openvidu-instance>
}
</div>
</div> </div>

View File

@ -1,28 +1,38 @@
<table class="mat-elevation-z8"> <table class="mat-elevation-z8">
<tr class="mat-row"> <tr class="mat-row">
<th class="mat-cell"> <th class="mat-cell">
<p id="number-of-streams">STREAMS</p> <p id="number-of-streams">STREAMS</p>
<p> <p>
<span [matBadge]="numberOfStreamsOut()" matBadgeOverlap="false">OUT</span> <span [matBadge]="numberOfStreamsOut()" matBadgeOverlap="false">OUT</span>
</p> </p>
<p> <p>
<span [matBadge]="numberOfStreamsIn()" matBadgeOverlap="false">IN</span> <span [matBadge]="numberOfStreamsIn()" matBadgeOverlap="false">IN</span>
</p> </p>
</th> </th>
<th *ngFor="let publisher of users | callback: filterPublishers" class="mat-cell"> @for (publisher of users | callback: filterPublishers; track publisher) {
<p>{{publisher.room.localParticipant.identity}}</p> <th class="mat-cell">
<app-table-video *ngIf="publisher.localTracks.audio || publisher.localTracks.video" <p>{{publisher.room.localParticipant.identity}}</p>
[tracks]="publisher.localTracks" @if (publisher.localTracks.audio || publisher.localTracks.video) {
[videoId]="'pub-' + publisher.room.localParticipant.identity"> <app-table-video
</app-table-video> [tracks]="publisher.localTracks"
</th> [videoId]="'pub-' + publisher.room.localParticipant.identity">
</tr> </app-table-video>
<tr *ngFor="let subscriber of users | callback: filterSubscribers" class="mat-cell"> }
<td class="mat-cell">{{subscriber.room.localParticipant.identity}}</td> </th>
<td *ngFor="let publisher of users | callback: filterPublishers" class="mat-cell"> }
<app-table-video *ngIf="subscriber.room.localParticipant.identity !== publisher.room.localParticipant.identity && getRemoteTracksForPublisher(subscriber, publisher) as remoteTracks" </tr>
[tracks]="remoteTracks" [videoId]="'sub-' + subscriber.room.localParticipant.identity + '-of-pub-' + publisher.room.localParticipant.identity"> @for (subscriber of users | callback: filterSubscribers; track subscriber) {
<tr class="mat-cell">
<td class="mat-cell">{{subscriber.room.localParticipant.identity}}</td>
@for (publisher of users | callback: filterPublishers; track publisher) {
<td class="mat-cell">
@if (subscriber.room.localParticipant.identity !== publisher.room.localParticipant.identity && getRemoteTracksForPublisher(subscriber, publisher); as remoteTracks) {
<app-table-video
[tracks]="remoteTracks" [videoId]="'sub-' + subscriber.room.localParticipant.identity + '-of-pub-' + publisher.room.localParticipant.identity">
</app-table-video> </app-table-video>
}
</td> </td>
}
</tr> </tr>
}
</table> </table>

View File

@ -1,46 +1,60 @@
<div class="parent-div"> <div class="parent-div">
<video #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></video> <video #mediaElement [id]="finalElementRefId" [ngClass]="getTrackOrigin()"></video>
<div class="bottom-div"> <div class="bottom-div">
<button *ngIf="localParticipant" (click)="muteUnmuteVideo()" class="video-btn mute-unmute-video" matTooltip="Mute/Unmute video" @if (localParticipant) {
matTooltipClass="custom-tooltip"> <button (click)="muteUnmuteVideo()" class="video-btn mute-unmute-video" matTooltip="Mute/Unmute video"
<mat-icon aria-label="Mute/Unmute video" class="mat-icon material-icons" role="img" matTooltipClass="custom-tooltip">
aria-hidden="true">{{muteVideoIcon}}</mat-icon> <mat-icon aria-label="Mute/Unmute video" class="mat-icon material-icons" role="img"
</button> aria-hidden="true">{{muteVideoIcon}}</mat-icon>
<button *ngIf="localParticipant" (click)="unpublishTrack()" class="video-btn publish-unpublish-video" matTooltip="Unpublish track" </button>
matTooltipClass="custom-tooltip"> }
<mat-icon aria-label="Unpublish track" class="mat-icon material-icons" role="img" @if (localParticipant) {
aria-hidden="true">stop</mat-icon> <button (click)="unpublishTrack()" class="video-btn publish-unpublish-video" matTooltip="Unpublish track"
</button> matTooltipClass="custom-tooltip">
<button *ngIf="!localParticipant" (click)="toggleEnableTrack()" class="video-btn toggle-video-enabled" matTooltip="Toggle track enabled" <mat-icon aria-label="Unpublish track" class="mat-icon material-icons" role="img"
matTooltipClass="custom-tooltip"> aria-hidden="true">stop</mat-icon>
<mat-icon aria-label="Toggle track enabled" class="mat-icon material-icons" role="img" </button>
aria-hidden="true">{{trackEnabled ? 'toggle_off' : 'toggle_on'}}</mat-icon> }
</button> @if (!localParticipant) {
<button *ngIf="!localParticipant" (click)="toggleSubscribeTrack()" class="video-btn toggle-video-subscribed" matTooltip="Toggle track subscription" <button (click)="toggleEnableTrack()" class="video-btn toggle-video-enabled" matTooltip="Toggle track enabled"
matTooltipClass="custom-tooltip"> matTooltipClass="custom-tooltip">
<mat-icon aria-label="Toggle track subscription" class="mat-icon material-icons" role="img" <mat-icon aria-label="Toggle track enabled" class="mat-icon material-icons" role="img"
aria-hidden="true">{{trackSubscribed ? 'stop' : 'play_arrow'}}</mat-icon> aria-hidden="true">{{trackEnabled ? 'toggle_off' : 'toggle_on'}}</mat-icon>
</button> </button>
<mat-form-field *ngIf="!localParticipant" id="max-video-quality" class="video-btn quality-option" matTooltip="Set video quality" matTooltipClass="custom-tooltip"> }
<mat-select [(value)]="maxVideoQuality" (selectionChange)="onQualityChange()"> @if (!localParticipant) {
<mat-option *ngFor="let q of ['LOW', 'MEDIUM', 'HIGH']" [value]="q" [ngClass]="'mode-' + q">{{q}}</mat-option> <button (click)="toggleSubscribeTrack()" class="video-btn toggle-video-subscribed" matTooltip="Toggle track subscription"
</mat-select> matTooltipClass="custom-tooltip">
</mat-form-field> <mat-icon aria-label="Toggle track subscription" class="mat-icon material-icons" role="img"
<button (click)="openInfoDialog()" class="video-btn video-track-info" matTooltip="Open info dialog" aria-hidden="true">{{trackSubscribed ? 'stop' : 'play_arrow'}}</mat-icon>
matTooltipClass="custom-tooltip"> </button>
<mat-icon aria-label="Open info dialog" class="mat-icon material-icons" role="img" }
aria-hidden="true">info</mat-icon> @if (!localParticipant) {
</button> <mat-form-field id="max-video-quality" class="video-btn quality-option" matTooltip="Set video quality" matTooltipClass="custom-tooltip">
<!--<button *ngIf="isLocal" (click)="blur()" class="video-btn"> <mat-select [(value)]="maxVideoQuality" (selectionChange)="onQualityChange()">
<mat-icon aria-label="Blur video" class="mat-icon material-icons" role="img" @for (q of ['LOW', 'MEDIUM', 'HIGH']; track q) {
aria-hidden="true">{{blurIcon}}</mat-icon> <mat-option [value]="q" [ngClass]="'mode-' + q">{{q}}</mat-option>
</button>--> }
</div> </mat-select>
<div class="top-right-div"> </mat-form-field>
<button *ngIf="!localParticipant" (click)="toggleVideoZoom()" class="video-btn toggle-video-zoom" matTooltip="Toggle video zoom" }
matTooltipClass="custom-tooltip"> <button (click)="openInfoDialog()" class="video-btn video-track-info" matTooltip="Open info dialog"
<mat-icon aria-label="Toggle video zoom" class="mat-icon material-icons" role="img" matTooltipClass="custom-tooltip">
aria-hidden="true">{{videoZoom ? 'zoom_out' : 'zoom_in'}}</mat-icon> <mat-icon aria-label="Open info dialog" class="mat-icon material-icons" role="img"
</button> aria-hidden="true">info</mat-icon>
</div> </button>
<!--<button *ngIf="isLocal" (click)="blur()" class="video-btn">
<mat-icon aria-label="Blur video" class="mat-icon material-icons" role="img"
aria-hidden="true">{{blurIcon}}</mat-icon>
</button>-->
</div>
<div class="top-right-div">
@if (!localParticipant) {
<button (click)="toggleVideoZoom()" class="video-btn toggle-video-zoom" matTooltip="Toggle video zoom"
matTooltipClass="custom-tooltip">
<mat-icon aria-label="Toggle video zoom" class="mat-icon material-icons" role="img"
aria-hidden="true">{{videoZoom ? 'zoom_out' : 'zoom_in'}}</mat-icon>
</button>
}
</div>
</div> </div>

View File

@ -14,7 +14,7 @@
"sourceMap": true, "sourceMap": true,
"declaration": false, "declaration": false,
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "bundler",
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "ES2022",
@ -25,7 +25,9 @@
"dom" "dom"
], ],
"paths": { "paths": {
"crypto": ["./node_modules/crypto-browserify"], "crypto": [
"./node_modules/crypto-browserify"
],
"stream": [ "stream": [
"node_modules/stream-browserify" "node_modules/stream-browserify"
], ],