mirror of https://github.com/OpenVidu/openvidu.git
openvidu-testapp: scenarios page
parent
263db59bdd
commit
6e682ac123
|
@ -3,14 +3,15 @@
|
|||
<div layout-align='center center' layout='column'>
|
||||
<a id="nav-logo" routerLink="/"><img id="nav-img" src="assets/images/openvidu_vert_white_bg_trans_cropped.png"/> TestApp</a>
|
||||
<a mat-button routerLink="/test-sessions"><span>SESSIONS</span></a>
|
||||
<a mat-button routerLink="/test-scenarios"><span>SCENARIOS</span></a>
|
||||
<a mat-button routerLink="/test-apirest"><span>API REST</span></a>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
<main>
|
||||
<mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<input id="openvidu-url" matInput placeholder="OpenVidu Server URL" [ngModel]="openviduURL" (ngModelChange)="updateUrl($event)">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<input id="openvidu-secret" matInput placeholder="OpenVidu Server Secret" [ngModel]="openviduSecret" (ngModelChange)="updateSecret($event)">
|
||||
</mat-form-field>
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
@ -19,7 +19,8 @@ import {
|
|||
MatExpansionModule,
|
||||
MatSlideToggleModule,
|
||||
MatSidenavModule,
|
||||
MatFormFieldModule
|
||||
MatFormFieldModule,
|
||||
MatBadgeModule
|
||||
} from '@angular/material';
|
||||
|
||||
@NgModule({
|
||||
|
@ -43,7 +44,8 @@ import {
|
|||
MatExpansionModule,
|
||||
MatSlideToggleModule,
|
||||
MatSidenavModule,
|
||||
MatFormFieldModule
|
||||
MatFormFieldModule,
|
||||
MatBadgeModule
|
||||
],
|
||||
})
|
||||
export class AppMaterialModule { }
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { AppMaterialModule } from './app.material.module';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { routing } from './app.routing';
|
||||
import { AppComponent } from './app.component';
|
||||
import { TestSessionsComponent } from './components/test-sessions/test-sessions.component';
|
||||
import { TestApirestComponent } from './components/test-apirest/test-apirest.component';
|
||||
import { TestScenariosComponent } from './components/test-scenarios/test-scenarios.component';
|
||||
import { OpenviduInstanceComponent } from './components/openvidu-instance/openvidu-instance.component';
|
||||
import { VideoComponent } from './components/video/video.component';
|
||||
import { OpenViduVideoComponent } from './components/video/ov-video.component';
|
||||
import { ExtensionDialogComponent } from './components/dialogs/extension-dialog.component';
|
||||
import { LocalRecordingDialogComponent } from './components/dialogs/local-recording-dialog.component';
|
||||
import { SessionPropertiesDialogComponent } from './components/dialogs/session-properties-dialog.component';
|
||||
import { SessionApiDialogComponent } from './components/dialogs/session-api-dialog.component';
|
||||
import { EventsDialogComponent } from './components/dialogs/events-dialog.component';
|
||||
import { PublisherPropertiesDialogComponent } from './components/dialogs/publisher-properties-dialog.component';
|
||||
import { UsersTableComponent } from './components/users-table/users-table.component';
|
||||
import { TableVideoComponent } from './components/users-table/table-video.component';
|
||||
|
||||
import { ExtensionDialogComponent } from './components/dialogs/extension-dialog/extension-dialog.component';
|
||||
import { LocalRecordingDialogComponent } from './components/dialogs/local-recording-dialog/local-recording-dialog.component';
|
||||
import { SessionPropertiesDialogComponent } from './components/dialogs/session-properties-dialog/session-properties-dialog.component';
|
||||
import { SessionApiDialogComponent } from './components/dialogs/session-api-dialog/session-api-dialog.component';
|
||||
import { EventsDialogComponent } from './components/dialogs/events-dialog/events-dialog.component';
|
||||
import { PublisherPropertiesDialogComponent } from './components/dialogs/publisher-properties-dialog/publisher-properties-dialog.component';
|
||||
import { ScenarioPropertiesDialogComponent } from './components/dialogs/scenario-properties-dialog/scenario-properties-dialog.component';
|
||||
|
||||
import { OpenviduRestService } from './services/openvidu-rest.service';
|
||||
import { OpenviduParamsService } from './services/openvidu-params.service';
|
||||
|
@ -32,12 +38,16 @@ import { MuteSubscribersService } from './services/mute-subscribers.service';
|
|||
OpenViduVideoComponent,
|
||||
TestSessionsComponent,
|
||||
TestApirestComponent,
|
||||
TestScenariosComponent,
|
||||
ExtensionDialogComponent,
|
||||
SessionPropertiesDialogComponent,
|
||||
SessionApiDialogComponent,
|
||||
EventsDialogComponent,
|
||||
LocalRecordingDialogComponent,
|
||||
PublisherPropertiesDialogComponent
|
||||
PublisherPropertiesDialogComponent,
|
||||
ScenarioPropertiesDialogComponent,
|
||||
UsersTableComponent,
|
||||
TableVideoComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -45,6 +55,7 @@ import { MuteSubscribersService } from './services/mute-subscribers.service';
|
|||
BrowserAnimationsModule,
|
||||
AppMaterialModule,
|
||||
FlexLayoutModule,
|
||||
HttpClientModule,
|
||||
routing
|
||||
],
|
||||
providers: [
|
||||
|
@ -59,7 +70,8 @@ import { MuteSubscribersService } from './services/mute-subscribers.service';
|
|||
SessionApiDialogComponent,
|
||||
EventsDialogComponent,
|
||||
LocalRecordingDialogComponent,
|
||||
PublisherPropertiesDialogComponent
|
||||
PublisherPropertiesDialogComponent,
|
||||
ScenarioPropertiesDialogComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ModuleWithProviders } from '@angular/core';
|
|||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { TestSessionsComponent } from './components/test-sessions/test-sessions.component';
|
||||
import { TestScenariosComponent } from './components/test-scenarios/test-scenarios.component';
|
||||
import { TestApirestComponent } from './components/test-apirest/test-apirest.component';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
|
@ -12,6 +13,10 @@ const appRoutes: Routes = [
|
|||
path: 'test-sessions',
|
||||
component: TestSessionsComponent
|
||||
},
|
||||
{
|
||||
path: 'test-scenarios',
|
||||
component: TestScenariosComponent
|
||||
},
|
||||
{
|
||||
path: 'test-apirest',
|
||||
component: TestApirestComponent
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialog, MAT_DIALOG_DATA, MatDialogConfig, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||
|
||||
import { SessionProperties, MediaMode, RecordingMode, RecordingLayout } from 'openvidu-node-client';
|
||||
import { PublisherProperties, OpenVidu } from 'openvidu-browser';
|
||||
|
||||
@Component({
|
||||
|
@ -27,10 +26,6 @@ export class PublisherPropertiesDialogComponent {
|
|||
audioDevices = [];
|
||||
videoDevices = [];
|
||||
|
||||
private mediaMode = MediaMode;
|
||||
private recordingMode = RecordingMode;
|
||||
private defaultRecordingLayout = RecordingLayout;
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<PublisherPropertiesDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: PublisherProperties) {
|
||||
this.publisherProperties = data;
|
|
@ -0,0 +1,73 @@
|
|||
mat-chip {
|
||||
margin: 0 0 5px 0 !important;
|
||||
height: 25px;
|
||||
max-width: 180px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin-top: 10px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
button.device-btn {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
button.device-btn mat-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
div.mat-form-field-subscript-wrapper {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
mat-checkbox {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
mat-radio-button {
|
||||
margin-left: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
mat-radio-button:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.not-manual {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.conf-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
font-weight: 400;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.conf-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
font-weight: 400;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#manual-turn-div {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 25px;
|
||||
padding: 5px;
|
||||
border: 1px solid #00000026;
|
||||
border-radius: 3px;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<div>
|
||||
<h2 mat-dialog-title>Scenario properties</h2>
|
||||
<mat-dialog-content>
|
||||
<label class="conf-label">Turn configuration</label>
|
||||
<div [class.not-manual]="turnConf !== 'manual'">
|
||||
<mat-radio-group name="Turn configuration" [(ngModel)]="turnConf">
|
||||
<mat-radio-button value="auto">Auto</mat-radio-button>
|
||||
<mat-radio-button value="freeice">Freeice</mat-radio-button>
|
||||
<mat-radio-button value="manual">Manual</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<div *ngIf="turnConf === 'manual'" id="manual-turn-div">
|
||||
<mat-form-field style="width: 100%">
|
||||
<input matInput placeholder="url" [(ngModel)]="manualTurnConf.urls[0]">
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 49%; padding-right: 2px">
|
||||
<input matInput placeholder="user" [(ngModel)]="manualTurnConf.username">
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 49%; padding-left: 2px">
|
||||
<input matInput placeholder="pass" [(ngModel)]="manualTurnConf.credential">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<label class="conf-label">Publisher configuration</label>
|
||||
<mat-checkbox (click)="toggleAudio()" [checked]="publisherProperties.audioSource !== false">Audio</mat-checkbox>
|
||||
<mat-checkbox (click)="toggleVideo()" [checked]="publisherProperties.videoSource !== false">Video</mat-checkbox>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Audio source" [(ngModel)]="audioSource" [disabled]="(publisherProperties.audioSource === false)">
|
||||
</mat-form-field>
|
||||
<button mat-icon-button class="device-btn" title="List audio devices" (click)="listAudioDevices()">
|
||||
<mat-icon aria-label="List audio devices">add_circle</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="audioDevices.length > 0" mat-icon-button class="device-btn" title="Clear audio devices" (click)="audioDevices = []">
|
||||
<mat-icon aria-label="Clear audio devices">clear</mat-icon>
|
||||
</button>
|
||||
<mat-chip-list *ngIf="audioDevices.length > 0">
|
||||
<mat-chip *ngFor="let audioDevice of audioDevices" (click)="audioSource=audioDevice.deviceId">{{audioDevice.label}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Video source" [(ngModel)]="videoSource" [disabled]="(publisherProperties.videoSource === false)">
|
||||
</mat-form-field>
|
||||
<button mat-icon-button class="device-btn" title="List video devices" (click)="listVideoDevices()">
|
||||
<mat-icon aria-label="List video devices">add_circle</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="videoDevices.length > 0" mat-icon-button class="device-btn" title="Clear video devices" (click)="videoDevices = []">
|
||||
<mat-icon aria-label="Clear video devices">clear</mat-icon>
|
||||
</button>
|
||||
<mat-chip-list *ngIf="videoDevices.length > 0">
|
||||
<mat-chip *ngFor="let videoDevice of videoDevices" (click)="videoSource=videoDevice.deviceId">{{videoDevice.label}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Resolution" [(ngModel)]="publisherProperties.resolution">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput placeholder="Framerate" [(ngModel)]="publisherProperties.frameRate" type="number">
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
<button mat-button [mat-dialog-close]="undefined">CANCEL</button>
|
||||
<button mat-button [mat-dialog-close]="setCloseValue()">SAVE</button>
|
||||
</div>
|
|
@ -0,0 +1,109 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||
|
||||
import { PublisherProperties, OpenVidu } from 'openvidu-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-scenario-properties-dialog',
|
||||
templateUrl: './scenario-properties-dialog.component.html',
|
||||
styleUrls: ['./scenario-properties-dialog.component.css'],
|
||||
providers: [
|
||||
{ provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' }
|
||||
]
|
||||
})
|
||||
export class ScenarioPropertiesDialogComponent {
|
||||
|
||||
OV: OpenVidu;
|
||||
publisherProperties: PublisherProperties;
|
||||
initValue: PublisherProperties;
|
||||
|
||||
audioSource;
|
||||
videoSource;
|
||||
|
||||
audioSourceAux;
|
||||
videoSourceAux;
|
||||
|
||||
audioDevices = [];
|
||||
videoDevices = [];
|
||||
|
||||
turnConf: string;
|
||||
manualTurnConf: RTCIceServer = {};
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<ScenarioPropertiesDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data) {
|
||||
this.publisherProperties = data.publisherProperties;
|
||||
this.OV = new OpenVidu();
|
||||
this.initValue = Object.assign({}, this.publisherProperties);
|
||||
this.audioSource = typeof this.publisherProperties.audioSource === 'string' ? this.publisherProperties.audioSource : undefined;
|
||||
this.videoSource = typeof this.publisherProperties.videoSource === 'string' ? this.publisherProperties.videoSource : undefined;
|
||||
this.turnConf = data.turnConf;
|
||||
this.manualTurnConf = data.manualTurnConf;
|
||||
}
|
||||
|
||||
toggleAudio(): void {
|
||||
if (this.publisherProperties.audioSource === false) {
|
||||
this.publisherProperties.audioSource = this.audioSource;
|
||||
this.audioSource = this.audioSourceAux;
|
||||
} else {
|
||||
this.audioSourceAux = this.audioSource;
|
||||
this.audioSource = undefined;
|
||||
this.publisherProperties.audioSource = false;
|
||||
}
|
||||
}
|
||||
|
||||
toggleVideo(): void {
|
||||
if (this.publisherProperties.videoSource === false) {
|
||||
this.publisherProperties.videoSource = this.videoSource;
|
||||
this.videoSource = this.videoSourceAux;
|
||||
} else {
|
||||
this.videoSourceAux = this.videoSource;
|
||||
this.videoSource = undefined;
|
||||
this.publisherProperties.videoSource = false;
|
||||
}
|
||||
}
|
||||
|
||||
setCloseValue() {
|
||||
if (typeof this.audioSource === 'string') {
|
||||
if (!!this.audioSource) {
|
||||
this.publisherProperties.audioSource = this.audioSource;
|
||||
} else {
|
||||
this.publisherProperties.audioSource = undefined;
|
||||
}
|
||||
}
|
||||
if (typeof this.videoSource === 'string') {
|
||||
if (!!this.videoSource) {
|
||||
this.publisherProperties.videoSource = this.videoSource;
|
||||
} else {
|
||||
this.publisherProperties.videoSource = undefined;
|
||||
}
|
||||
}
|
||||
return {
|
||||
publisherProperties: this.publisherProperties,
|
||||
turnConf: this.turnConf,
|
||||
manualTurnConf: this.manualTurnConf
|
||||
};
|
||||
}
|
||||
|
||||
listAudioDevices() {
|
||||
this.audioDevices = [];
|
||||
this.OV.getDevices().then(devices => {
|
||||
devices.forEach(device => {
|
||||
if (device.kind === 'audioinput') {
|
||||
this.audioDevices.push(device);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
listVideoDevices() {
|
||||
this.videoDevices = [];
|
||||
this.OV.getDevices().then(devices => {
|
||||
devices.forEach(device => {
|
||||
if (device.kind === 'videoinput') {
|
||||
this.videoDevices.push(device);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { Component, Inject } from '@angular/core';
|
||||
import { MatDialog, MAT_DIALOG_DATA, MatDialogConfig, MatDialogRef } from '@angular/material';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
|
||||
import { Session } from 'openvidu-browser';
|
||||
import { OpenVidu as OpenViduAPI } from 'openvidu-node-client';
|
||||
|
||||
@Component({
|
|
@ -1,31 +1,27 @@
|
|||
import {
|
||||
Component, Input, HostListener, ChangeDetectorRef, SimpleChanges, ElementRef, ViewChild,
|
||||
Component, Input, HostListener, ChangeDetectorRef, SimpleChanges,
|
||||
OnInit, OnDestroy, OnChanges
|
||||
} from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import {
|
||||
OpenVidu, Session, Subscriber, Publisher, Stream, Connection,
|
||||
LocalRecorder, VideoInsertMode, StreamEvent, ConnectionEvent,
|
||||
SessionDisconnectedEvent, SignalEvent, RecordingEvent, VideoElementEvent,
|
||||
PublisherSpeakingEvent, StreamManagerEvent, StreamManager, PublisherProperties
|
||||
OpenVidu, Session, Subscriber, Publisher, VideoInsertMode, StreamEvent, ConnectionEvent,
|
||||
SessionDisconnectedEvent, SignalEvent, RecordingEvent,
|
||||
PublisherSpeakingEvent, PublisherProperties
|
||||
} from 'openvidu-browser';
|
||||
import {
|
||||
OpenVidu as OpenViduAPI,
|
||||
Session as SessionAPI,
|
||||
SessionProperties as SessionPropertiesAPI,
|
||||
MediaMode,
|
||||
RecordingMode,
|
||||
RecordingLayout
|
||||
} from 'openvidu-node-client';
|
||||
import { MatDialog, MatDialogRef, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||
import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component';
|
||||
import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component';
|
||||
import { MatDialog, MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
|
||||
import { ExtensionDialogComponent } from '../dialogs/extension-dialog/extension-dialog.component';
|
||||
import { TestFeedService } from '../../services/test-feed.service';
|
||||
import { EventsDialogComponent } from '../dialogs/events-dialog.component';
|
||||
import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog.component';
|
||||
import { SessionApiDialogComponent } from '../dialogs/session-api-dialog.component';
|
||||
import { PublisherPropertiesDialogComponent } from '../dialogs/publisher-properties-dialog.component';
|
||||
import { EventsDialogComponent } from '../dialogs/events-dialog/events-dialog.component';
|
||||
import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog/session-properties-dialog.component';
|
||||
import { SessionApiDialogComponent } from '../dialogs/session-api-dialog/session-api-dialog.component';
|
||||
import { PublisherPropertiesDialogComponent } from '../dialogs/publisher-properties-dialog/publisher-properties-dialog.component';
|
||||
|
||||
|
||||
export interface SessionConf {
|
||||
|
@ -119,15 +115,9 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
openviduError: any;
|
||||
|
||||
private publisherRecorder: LocalRecorder;
|
||||
private publisherRecording = false;
|
||||
private publisherPaused = false;
|
||||
private muteSubscribersSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
private dialog: MatDialog,
|
||||
private recordDialog: MatDialog,
|
||||
private testFeedService: TestFeedService
|
||||
) {
|
||||
this.generateSessionInfo();
|
||||
|
|
|
@ -75,8 +75,8 @@
|
|||
<mat-card [style.background]="getBackgroundColor(ind)">{{sid[0]}}</mat-card>
|
||||
</td>
|
||||
<td>
|
||||
<mat-card *ngIf="sid[1].length > 0" [style.background]="getBackgroundColor(ind)">
|
||||
<p *ngFor="let token of sid[1]">{{token}}</p>
|
||||
<mat-card *ngIf="sid[1].length > 0" [style.background]="getBackgroundColor(ind)" style="max-width: 700px">
|
||||
<p *ngFor="let token of sid[1]" style="word-wrap: break-word">{{token}}</p>
|
||||
</mat-card>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#finish-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
app-openvidu-instance {
|
||||
display: inline-flex;
|
||||
margin: 25px 5px 0px 0px;
|
||||
}
|
||||
|
||||
.scenario-input {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.scenario-input {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.scenario-input:first-of-type {
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.report-div {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.report-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#back-report-btn {
|
||||
float: right;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<div>
|
||||
<button id="one2many-btn" mat-raised-button color="primary" (click)="loadScenario(manyToMany,0,0)" [disabled]="scenarioPlaying">Many To Many</button>
|
||||
<mat-form-field class="scenario-input">
|
||||
<input id="one2many-input" matInput [(ngModel)]="manyToMany" type="number" [disabled]="scenarioPlaying">
|
||||
</mat-form-field>
|
||||
<button id="many2many-btn" mat-raised-button color="primary" (click)="loadScenario(0,1,oneToMany)" [disabled]="scenarioPlaying">One to {{oneToMany}}</button>
|
||||
<mat-form-field class="scenario-input">
|
||||
<input id="one2many-input" matInput [(ngModel)]="oneToMany" type="number" [disabled]="scenarioPlaying">
|
||||
</mat-form-field>
|
||||
<button mat-icon-button title="Scenario properties" class="mat-icon-custom" (click)="openScenarioPropertiesDialog()" [disabled]="scenarioPlaying">
|
||||
<mat-icon class="mat-icon-custom-ic" aria-label="Scenario properties button">settings</mat-icon>
|
||||
</button>
|
||||
<button id="finish-btn" mat-raised-button color="primary" (click)="endScenario()" [disabled]="!scenarioPlaying">FINISH</button>
|
||||
</div>
|
||||
|
||||
<div class="instance-div">
|
||||
<app-users-table *ngIf="!!publishers.length" [connections]="connections" [publishers]="publishers" [subscribers]="subscribers"
|
||||
[numberOfStreams]="{out: publishers.length, in: totalNumberOfStreams - publishers.length}" (reportForStream)="addReportForStream($event)"
|
||||
(focusReport)="focusOnReportForStream($event)"></app-users-table>
|
||||
</div>
|
||||
|
||||
<div class="report-div">
|
||||
<mat-form-field class="report-field" appearance="fill">
|
||||
<mat-label>Report</mat-label>
|
||||
<button *ngIf="isFocusedOnReport" id="back-report-btn" mat-mini-fab color="primary" (click)="$event.stopPropagation(); textAreaValue = stringifyAllReports; isFocusedOnReport = false;">
|
||||
<mat-icon aria-label="Go back to all streams">undo</mat-icon>
|
||||
</button>
|
||||
<textarea matInput readonly [value]="textAreaValue" [mat-autosize]="true" [disabled]="!textAreaValue"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TestScenariosComponent } from './test-scenarios.component';
|
||||
|
||||
describe('TestScenariosComponent', () => {
|
||||
let component: TestScenariosComponent;
|
||||
let fixture: ComponentFixture<TestScenariosComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TestScenariosComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestScenariosComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,470 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { SessionConf } from '../openvidu-instance/openvidu-instance.component';
|
||||
import { OpenviduParamsService } from '../../services/openvidu-params.service';
|
||||
import { TestFeedService } from '../../services/test-feed.service';
|
||||
import { ScenarioPropertiesDialogComponent } from '../dialogs/scenario-properties-dialog/scenario-properties-dialog.component';
|
||||
import { StreamManagerWrapper } from '../users-table/table-video.component';
|
||||
|
||||
import {
|
||||
OpenVidu,
|
||||
Session,
|
||||
StreamEvent,
|
||||
StreamManagerEvent,
|
||||
PublisherProperties,
|
||||
ConnectionEvent
|
||||
} from 'openvidu-browser';
|
||||
import {
|
||||
OpenVidu as OpenViduAPI,
|
||||
SessionProperties as SessionPropertiesAPI,
|
||||
MediaMode,
|
||||
RecordingMode,
|
||||
RecordingLayout
|
||||
} from 'openvidu-node-client';
|
||||
|
||||
@Component({
|
||||
selector: 'app-test-scenarios',
|
||||
templateUrl: './test-scenarios.component.html',
|
||||
styleUrls: ['./test-scenarios.component.css']
|
||||
})
|
||||
export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||
|
||||
fixedSessionId = 'SCENARIO_TEST';
|
||||
|
||||
openviduUrl: string;
|
||||
openviduSecret: string;
|
||||
|
||||
scenarioPlaying = false;
|
||||
|
||||
paramsSubscription: Subscription;
|
||||
eventsInfoSubscription: Subscription;
|
||||
|
||||
// OpenViduInstance collection
|
||||
users: SessionConf[] = [];
|
||||
connections: string[] = [];
|
||||
publishers: StreamManagerWrapper[] = [];
|
||||
subscribers: { connectionId: string, subs: StreamManagerWrapper[] }[] = [];
|
||||
totalNumberOfStreams = 0;
|
||||
manyToMany = 2;
|
||||
oneToMany = 2;
|
||||
|
||||
// OpenVidu Browser objects
|
||||
OVs: OpenVidu[] = [];
|
||||
sessions: Session[] = [];
|
||||
|
||||
// OpenVidu Node Client objects
|
||||
sessionProperties: SessionPropertiesAPI = {
|
||||
mediaMode: MediaMode.ROUTED,
|
||||
recordingMode: RecordingMode.MANUAL,
|
||||
defaultRecordingLayout: RecordingLayout.BEST_FIT,
|
||||
defaultCustomLayout: '',
|
||||
customSessionId: ''
|
||||
};
|
||||
|
||||
turnConf = 'auto';
|
||||
manualTurnConf: RTCIceServer = { urls: [] };
|
||||
|
||||
publisherProperties: PublisherProperties = {
|
||||
audioSource: false,
|
||||
videoSource: undefined,
|
||||
frameRate: 2,
|
||||
resolution: '320x240',
|
||||
mirror: true,
|
||||
publishAudio: true,
|
||||
publishVideo: true
|
||||
};
|
||||
|
||||
report;
|
||||
numberOfReports = 0;
|
||||
stringifyAllReports = '';
|
||||
textAreaValue = '';
|
||||
isFocusedOnReport = false;
|
||||
|
||||
constructor(
|
||||
private openviduParamsService: OpenviduParamsService,
|
||||
private testFeedService: TestFeedService,
|
||||
private dialog: MatDialog,
|
||||
private http: HttpClient
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
const openviduParams = this.openviduParamsService.getParams();
|
||||
this.openviduUrl = openviduParams.openviduUrl;
|
||||
this.openviduSecret = openviduParams.openviduSecret;
|
||||
|
||||
this.paramsSubscription = this.openviduParamsService.newParams$.subscribe(
|
||||
params => {
|
||||
this.openviduUrl = params.openviduUrl;
|
||||
this.openviduSecret = params.openviduSecret;
|
||||
});
|
||||
|
||||
this.eventsInfoSubscription = this.testFeedService.newLastEvent$.subscribe(
|
||||
newEvent => {
|
||||
(window as any).myEvents += ('<br>' + JSON.stringify(newEvent));
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.endScenario();
|
||||
this.paramsSubscription.unsubscribe();
|
||||
this.eventsInfoSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
loadScenario(subsPubs: number, pubs: number, subs: number): void {
|
||||
|
||||
this.totalNumberOfStreams = pubs + subsPubs; // Number of outgoing streams
|
||||
this.totalNumberOfStreams += pubs * (subs + subsPubs); // Publishers only times total number of subscribers
|
||||
if (subsPubs > 0) {
|
||||
// Publihsers/Subscribers times total number of subscribers (minus 1 for not being subscribed to their own publisher)
|
||||
this.totalNumberOfStreams += subsPubs * (subs + (subsPubs - 1));
|
||||
}
|
||||
|
||||
this.users = [];
|
||||
this.report = { 'streamsOut': { 'count': 0, 'items': [] }, 'streamsIn': { 'count': 0, 'items': [] } };
|
||||
this.loadSubsPubs(subsPubs);
|
||||
this.loadPubs(pubs);
|
||||
this.loadSubs(subs);
|
||||
this.startSession();
|
||||
this.scenarioPlaying = true;
|
||||
}
|
||||
|
||||
endScenario() {
|
||||
for (const session of this.sessions) {
|
||||
session.disconnect();
|
||||
}
|
||||
this.publishers = [];
|
||||
this.subscribers = [];
|
||||
this.OVs = [];
|
||||
this.sessions = [];
|
||||
this.connections = [];
|
||||
this.scenarioPlaying = false;
|
||||
delete this.report;
|
||||
this.numberOfReports = 0;
|
||||
this.textAreaValue = '';
|
||||
this.stringifyAllReports = '';
|
||||
}
|
||||
|
||||
private loadSubsPubs(n: number): void {
|
||||
for (let i = 0; i < n; i++) {
|
||||
this.users.push({
|
||||
subscribeTo: true,
|
||||
publishTo: true,
|
||||
startSession: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private loadSubs(n: number): void {
|
||||
for (let i = 0; i < n; i++) {
|
||||
this.users.push({
|
||||
subscribeTo: true,
|
||||
publishTo: false,
|
||||
startSession: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private loadPubs(n: number): void {
|
||||
for (let i = 0; i < n; i++) {
|
||||
this.users.push({
|
||||
subscribeTo: false,
|
||||
publishTo: true,
|
||||
startSession: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private startSession() {
|
||||
for (const user of this.users) {
|
||||
|
||||
this.getToken().then(token => {
|
||||
const OV = new OpenVidu();
|
||||
const session = OV.initSession();
|
||||
|
||||
this.OVs.push(OV);
|
||||
this.sessions.push(session);
|
||||
|
||||
session.on('connectionCreated', (event: ConnectionEvent) => {
|
||||
if (this.connections.indexOf(event.connection.connectionId) === -1) {
|
||||
this.connections.push(event.connection.connectionId);
|
||||
}
|
||||
});
|
||||
|
||||
if (user.subscribeTo) {
|
||||
session.on('streamCreated', (event: StreamEvent) => {
|
||||
const subscriber = session.subscribe(event.stream, undefined, (error) => {
|
||||
const subAux = this.subscribers
|
||||
.find(s => s.connectionId === session.connection.connectionId).subs
|
||||
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId);
|
||||
if (!!error) {
|
||||
subAux.state['errorConnecting'] = Date.now();
|
||||
} else {
|
||||
subAux.state['connected'] = Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
const sub = this.subscribers.find(s => s.connectionId === session.connection.connectionId);
|
||||
if (!sub) {
|
||||
this.subscribers.push({
|
||||
connectionId: session.connection.connectionId,
|
||||
subs: [{
|
||||
connectionId: session.connection.connectionId,
|
||||
streamManager: subscriber,
|
||||
state: { 'connecting': Date.now() }
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
sub.subs.push({
|
||||
connectionId: session.connection.connectionId,
|
||||
streamManager: subscriber,
|
||||
state: { 'connecting': Date.now() }
|
||||
});
|
||||
}
|
||||
|
||||
subscriber.on('streamPlaying', (e: StreamManagerEvent) => {
|
||||
this.subscribers
|
||||
.find(s => s.connectionId === session.connection.connectionId).subs
|
||||
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId)
|
||||
.state['playing'] = Date.now();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
session.connect(token)
|
||||
.then(() => {
|
||||
if (user.publishTo) {
|
||||
|
||||
const publisher = OV.initPublisher(undefined, this.publisherProperties);
|
||||
const publisherWrapper = {
|
||||
connectionId: session.connection.connectionId,
|
||||
streamManager: publisher,
|
||||
state: { 'connecting': Date.now() }
|
||||
};
|
||||
|
||||
publisher.on('streamCreated', () => {
|
||||
publisherWrapper.state['connected'] = Date.now();
|
||||
});
|
||||
publisher.on('streamPlaying', () => {
|
||||
publisherWrapper.state['playing'] = Date.now();
|
||||
});
|
||||
session.publish(publisher).catch(() => {
|
||||
publisherWrapper.state['errorConnecting'] = Date.now();
|
||||
});
|
||||
this.publishers.push(publisherWrapper);
|
||||
|
||||
}
|
||||
})
|
||||
.catch();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getToken(): Promise<string> {
|
||||
const OV_NodeClient = new OpenViduAPI(this.openviduUrl, this.openviduSecret);
|
||||
if (!this.sessionProperties.customSessionId) {
|
||||
this.sessionProperties.customSessionId = this.fixedSessionId;
|
||||
}
|
||||
return OV_NodeClient.createSession(this.sessionProperties)
|
||||
.then(session_NodeClient => {
|
||||
return session_NodeClient.generateToken();
|
||||
});
|
||||
}
|
||||
|
||||
openScenarioPropertiesDialog() {
|
||||
const dialogRef = this.dialog.open(ScenarioPropertiesDialogComponent, {
|
||||
data: {
|
||||
publisherProperties: this.publisherProperties,
|
||||
turnConf: this.turnConf,
|
||||
manualTurnConf: this.manualTurnConf
|
||||
},
|
||||
width: '300px',
|
||||
disableClose: true
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (!!result) {
|
||||
this.publisherProperties = result.publisherProperties;
|
||||
this.turnConf = result.turnConf;
|
||||
this.manualTurnConf = result.manualTurnConf;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addReportForStream(event: StreamManagerWrapper) {
|
||||
|
||||
const getSessionInfo = () => {
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Authorization', 'Basic ' + btoa('OPENVIDUAPP:' + this.openviduSecret));
|
||||
this.http.get(this.openviduUrl + 'api/sessions/' + this.fixedSessionId, { headers }).subscribe(
|
||||
sessionInfo => {
|
||||
|
||||
this.report.streamsOut.items.forEach(report => {
|
||||
const streamOutRemoteInfo = sessionInfo['connections']
|
||||
.find(c => c.connectionId === report.connectionId).publishers
|
||||
.find(p => {
|
||||
report.webrtcTagName = p.webrtcTagName;
|
||||
return p.webrtcTagName === report.streamId;
|
||||
});
|
||||
report.remoteCandidatePair = {
|
||||
localCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.localCandidate),
|
||||
remoteCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.remoteCandidate)
|
||||
};
|
||||
});
|
||||
|
||||
this.report.streamsIn.items.forEach(report => {
|
||||
const streamInRemoteInfo = sessionInfo['connections']
|
||||
.find(c => c.connectionId === report.connectionId).subscribers
|
||||
.find(p => {
|
||||
report.webrtcTagName = p.webrtcTagName;
|
||||
return p.webrtcTagName === report.connectionId + '_' + report.streamId;
|
||||
});
|
||||
report.remoteCandidatePair = {
|
||||
localCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.localCandidate),
|
||||
remoteCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.remoteCandidate)
|
||||
};
|
||||
});
|
||||
|
||||
this.stringifyAllReports = JSON.stringify(this.report, null, '\t');
|
||||
if (!this.textAreaValue) {
|
||||
this.textAreaValue = this.stringifyAllReports;
|
||||
}
|
||||
},
|
||||
error => { }
|
||||
);
|
||||
};
|
||||
|
||||
event.streamManager.stream.getSelectedIceCandidate()
|
||||
.then(localCandidatePair => {
|
||||
|
||||
let newReport;
|
||||
|
||||
if (event.streamManager.remote) {
|
||||
newReport = {
|
||||
connectionId: event.connectionId,
|
||||
streamId: event.streamManager.stream.streamId,
|
||||
state: event.state,
|
||||
localCandidatePair: {
|
||||
localCandidate: localCandidatePair.localCandidate,
|
||||
remoteCandidate: localCandidatePair.remoteCandidate
|
||||
},
|
||||
remoteCandidatePair: {
|
||||
localCandidate: {},
|
||||
remoteCandidate: {}
|
||||
}
|
||||
};
|
||||
|
||||
this.report.streamsIn.count++;
|
||||
this.report.streamsIn.items.push(newReport);
|
||||
} else {
|
||||
newReport = {
|
||||
connectionId: event.connectionId,
|
||||
streamId: event.streamManager.stream.streamId,
|
||||
state: event.state,
|
||||
localCandidatePair: {
|
||||
localCandidate: localCandidatePair.localCandidate,
|
||||
remoteCandidate: localCandidatePair.remoteCandidate
|
||||
},
|
||||
remoteCandidatePair: {
|
||||
localCandidate: {},
|
||||
remoteCandidate: {}
|
||||
}
|
||||
};
|
||||
|
||||
this.report.streamsOut.count++;
|
||||
this.report.streamsOut.items.push(newReport);
|
||||
}
|
||||
|
||||
if (++this.numberOfReports === this.totalNumberOfStreams) {
|
||||
getSessionInfo();
|
||||
}
|
||||
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
private parseRemoteCandidatePair(candidateStr: string) {
|
||||
const array = candidateStr.split(/\s+/);
|
||||
return {
|
||||
portNumber: array[5],
|
||||
ipAddress: array[4],
|
||||
transport: array[2].toLowerCase(),
|
||||
candidateType: array[7],
|
||||
priority: array[3]
|
||||
};
|
||||
}
|
||||
|
||||
focusOnReportForStream(event) {
|
||||
this.isFocusedOnReport = true;
|
||||
let jsonObject = !!event.in ? this.report.streamsIn : this.report.streamsOut;
|
||||
jsonObject = jsonObject.items.find(stream => {
|
||||
const webrtcTagName = !!event.in ? event.connectionId + '_' + event.streamId : event.streamId;
|
||||
return (stream.connectionId === event.connectionId && stream.webrtcTagName === webrtcTagName);
|
||||
});
|
||||
this.textAreaValue = JSON.stringify(jsonObject, null, '\t');
|
||||
}
|
||||
|
||||
/*addReportForStreamConcurrent(event: StreamManagerWrapper) {
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Authorization', 'Basic ' + btoa('OPENVIDUAPP:' + this.openviduSecret));
|
||||
this.http.get(this.openviduUrl + 'api/sessions/' + this.fixedSessionId, { headers }).subscribe(
|
||||
sessionInfo => {
|
||||
|
||||
event.streamManager.stream.getSelectedIceCandidate()
|
||||
.then(localCandidatePair => {
|
||||
let newReport;
|
||||
if (event.streamManager.remote) {
|
||||
const streamInRemoteInfo = sessionInfo['connections']
|
||||
.find(c => c.connectionId === event.connectionId).subscribers
|
||||
.find(p => p.webrtcTagName === event.connectionId + '_' + event.streamManager.stream.streamId);
|
||||
|
||||
newReport = {
|
||||
connectionId: event.connectionId,
|
||||
streamId: event.streamManager.stream.streamId,
|
||||
state: event.state,
|
||||
localCandidatePair: {
|
||||
localCandidate: localCandidatePair.localCandidate,
|
||||
remoteCandidate: localCandidatePair.remoteCandidate
|
||||
},
|
||||
remoteCandidatePair: {
|
||||
localCandidate: streamInRemoteInfo.localCandidate,
|
||||
remoteCandidate: streamInRemoteInfo.remoteCandidate
|
||||
}
|
||||
};
|
||||
|
||||
this.report.streamsIn.count++;
|
||||
this.report.streamsIn.items.push(newReport);
|
||||
this.stringifyReport = JSON.stringify(this.report, null, '\t');
|
||||
} else {
|
||||
const streamOutRemoteInfo = sessionInfo['connections']
|
||||
.find(c => c.connectionId === event.connectionId).publishers
|
||||
.find(p => p.webrtcTagName === event.streamManager.stream.streamId);
|
||||
|
||||
newReport = {
|
||||
connectionId: event.connectionId,
|
||||
streamId: event.streamManager.stream.streamId,
|
||||
state: event.state,
|
||||
localCandidatePair: {
|
||||
localCandidate: localCandidatePair.localCandidate,
|
||||
remoteCandidate: localCandidatePair.remoteCandidate
|
||||
},
|
||||
remoteCandidatePair: {
|
||||
localCandidate: streamOutRemoteInfo.localCandidate,
|
||||
remoteCandidate: streamOutRemoteInfo.remoteCandidate
|
||||
}
|
||||
};
|
||||
|
||||
this.report.streamsOut.count++;
|
||||
this.report.streamsOut.items.push(newReport);
|
||||
this.stringifyReport = JSON.stringify(this.report, null, '\t');
|
||||
}
|
||||
})
|
||||
.catch();
|
||||
},
|
||||
error => { }
|
||||
);
|
||||
}*/
|
||||
|
||||
}
|
|
@ -44,6 +44,7 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
|
|||
|
||||
ngOnDestroy() {
|
||||
this.paramsSubscription.unsubscribe();
|
||||
this.eventsInfoSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
addUser(): void {
|
||||
|
@ -92,7 +93,7 @@ export class TestSessionsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
loadScenario(subsPubs: number, pubs: number, subs: number, ): void {
|
||||
loadScenario(subsPubs: number, pubs: number, subs: number): void {
|
||||
this.users = [];
|
||||
this.loadSubsPubs(subsPubs);
|
||||
this.loadPubs(pubs);
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { Component, Input, AfterViewInit, EventEmitter, Output, DoCheck } from '@angular/core';
|
||||
import { StreamManager } from 'openvidu-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-table-video',
|
||||
template: `
|
||||
<div class="icon-div">
|
||||
<mat-spinner [color]="'warn'"
|
||||
*ngIf="!success() && !fail()" [diameter]="24">
|
||||
</mat-spinner>
|
||||
<mat-icon [color]="'warn'" *ngIf="success() || fail()"
|
||||
matTooltip aria-label="Select report" (click)="emitClickIconEvent($event)">{{success() ? 'done' : 'warning'}}</mat-icon>
|
||||
</div>
|
||||
<app-ov-video [streamManager]="streamManager.streamManager"
|
||||
[attrstyle]="'width: 120px; height: initial'">
|
||||
</app-ov-video>
|
||||
`,
|
||||
styles: ['.icon-div {position: absolute; z-index: 999; left: calc(50% - 60px); padding: 2px 0 0 2px; cursor: pointer}']
|
||||
})
|
||||
export class TableVideoComponent implements AfterViewInit, DoCheck {
|
||||
|
||||
@Input() streamManager: StreamManagerWrapper;
|
||||
@Output() readyForReport = new EventEmitter<StreamManagerWrapper>();
|
||||
@Output() clickIcon = new EventEmitter<any>();
|
||||
|
||||
state = {};
|
||||
playingTimeout;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.playingTimeout = setTimeout(() => {
|
||||
if (!this.state['playing']) {
|
||||
this.state['timeoutPlaying'] = Date.now();
|
||||
this.readyForReport.emit({
|
||||
connectionId: this.streamManager.connectionId,
|
||||
state: this.state,
|
||||
streamManager: this.streamManager.streamManager
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if ((Object.keys(this.state).length !== Object.keys(this.streamManager.state).length) &&
|
||||
(!this.state['timeoutPlaying'])) {
|
||||
this.state = Object.assign({}, this.streamManager.state);
|
||||
if (this.success() || this.fail()) {
|
||||
clearTimeout(this.playingTimeout);
|
||||
this.readyForReport.emit({
|
||||
connectionId: this.streamManager.connectionId,
|
||||
state: this.state,
|
||||
streamManager: this.streamManager.streamManager
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success(): boolean {
|
||||
return (!!this.state['connected'] &&
|
||||
!!this.state['playing']);
|
||||
}
|
||||
|
||||
fail(): boolean {
|
||||
return (!!this.state['errorConnecting'] ||
|
||||
!!this.state['timeoutPlaying']);
|
||||
}
|
||||
|
||||
emitClickIconEvent(event) {
|
||||
event.stopPropagation();
|
||||
this.clickIcon.emit({
|
||||
in: this.streamManager.streamManager.remote,
|
||||
connectionId: this.streamManager.connectionId,
|
||||
streamId: this.streamManager.streamManager.stream.streamId
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface StreamManagerWrapper {
|
||||
connectionId: string;
|
||||
streamManager: StreamManager;
|
||||
state: any;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
tr {
|
||||
min-height: 90px;
|
||||
}
|
||||
|
||||
th {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mat-cell {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
tr:last-child .mat-cell {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#number-of-streams {
|
||||
margin-bottom: 28px;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<table class="mat-elevation-z8">
|
||||
<tr class="mat-row">
|
||||
<th class="mat-cell">
|
||||
<p id="number-of-streams">STREAMS</p>
|
||||
<p>
|
||||
<span [matBadge]="numberOfStreams.out" matBadgeOverlap="false">OUT</span>
|
||||
</p>
|
||||
<p>
|
||||
<span [matBadge]="numberOfStreams.in" matBadgeOverlap="false">IN</span>
|
||||
</p>
|
||||
</th>
|
||||
<th *ngFor="let publisher of publishers" class="mat-cell">
|
||||
<p>{{publisher.streamManager.stream.connection.connectionId}} | {{publisher.streamManager.stream.streamId}}</p>
|
||||
<app-table-video [streamManager]="publisher" (readyForReport)="emitReadyForReport($event)" (clickIcon)="emitFocusOnReport($event)"></app-table-video>
|
||||
</th>
|
||||
</tr>
|
||||
<tr *ngFor="let connectionId of connections" class="mat-row">
|
||||
<td class="mat-cell">{{connectionId}}</td>
|
||||
<td *ngFor="let publisher of publishers" class="mat-cell">
|
||||
<app-table-video *ngIf="connectionId !== publisher.streamManager.stream.connection.connectionId && getSubscriber(connectionId, publisher.streamManager) as sub"
|
||||
[streamManager]="sub" (readyForReport)="emitReadyForReport($event)" (clickIcon)="emitFocusOnReport($event)"></app-table-video>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
|
@ -0,0 +1,37 @@
|
|||
import { Component, Input, EventEmitter, Output } from '@angular/core';
|
||||
import { StreamManager } from 'openvidu-browser';
|
||||
import { StreamManagerWrapper } from './table-video.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-users-table',
|
||||
styleUrls: ['users-table.component.css'],
|
||||
templateUrl: 'users-table.component.html',
|
||||
})
|
||||
export class UsersTableComponent {
|
||||
|
||||
@Input() numberOfStreams = { out: 0, in: 0 };
|
||||
@Input() connections: string[] = [];
|
||||
@Input() publishers: StreamManagerWrapper[] = [];
|
||||
@Input() subscribers: { connectionId: string, subs: StreamManagerWrapper[] }[] = [];
|
||||
|
||||
@Output() reportForStream = new EventEmitter<StreamManagerWrapper>(true);
|
||||
@Output() focusReport = new EventEmitter<any>();
|
||||
|
||||
getSubscriber(connectionId: string, publisher: StreamManager): StreamManagerWrapper {
|
||||
const subscribersForConnection = this.subscribers.find(s => s.connectionId === connectionId);
|
||||
if (!!subscribersForConnection && !!subscribersForConnection.subs) {
|
||||
return subscribersForConnection.subs.find(sub => {
|
||||
return (sub.streamManager.stream.connection.connectionId === publisher.stream.connection.connectionId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
emitReadyForReport(event: StreamManagerWrapper) {
|
||||
this.reportForStream.emit(event);
|
||||
}
|
||||
|
||||
emitFocusOnReport(event) {
|
||||
this.focusReport.emit(event);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +1,28 @@
|
|||
import { Component, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
||||
import { Component, Input, ViewChild, ElementRef, OnInit, AfterViewInit } from '@angular/core';
|
||||
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
|
||||
import { StreamManager } from 'openvidu-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ov-video',
|
||||
template: '<video #videoElement [poster]="poster"></video>'
|
||||
template: '<video #videoElement [poster]="poster" [attr.style]="sanitizedStyle"></video>'
|
||||
})
|
||||
export class OpenViduVideoComponent implements AfterViewInit {
|
||||
export class OpenViduVideoComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@ViewChild('videoElement') elementRef: ElementRef;
|
||||
|
||||
@Input() poster = '';
|
||||
@Input() attrstyle = '';
|
||||
|
||||
sanitizedStyle: SafeStyle;
|
||||
|
||||
_streamManager: StreamManager;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.sanitizedStyle = this.sanitizer.bypassSecurityTrustStyle(this.attrstyle);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this._streamManager.addVideoElement(this.elementRef.nativeElement);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
app-ov-video {
|
||||
float: left;
|
||||
height: 90px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.data-node {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div>
|
||||
<app-ov-video [streamManager]="streamManager" [poster]="videoPoster" class="{{videoClasses}}"></app-ov-video>
|
||||
<app-ov-video [streamManager]="streamManager" [poster]="videoPoster" [attrstyle]="'width: 120px; height: 90px'" class="{{videoClasses}}"></app-ov-video>
|
||||
<div *ngIf="!streamManager.remote && showButtons" class="data-node">
|
||||
<div class="top-div">
|
||||
<button class="video-btn events-btn bottom-left-rounded" title="Publisher events" (click)="openPublisherEventsDialog()">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, Input, OnInit, ViewChild, ElementRef, Output, EventEmitter, OnDestroy } from '@angular/core';
|
||||
import { Component, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material';
|
||||
|
||||
import {
|
||||
|
@ -6,20 +6,17 @@ import {
|
|||
StreamManagerEvent,
|
||||
VideoElementEvent,
|
||||
Subscriber,
|
||||
Session,
|
||||
LocalRecorder,
|
||||
OpenVidu,
|
||||
Publisher,
|
||||
StreamEvent,
|
||||
VideoInsertMode
|
||||
} from 'openvidu-browser';
|
||||
|
||||
import { EventsDialogComponent } from '../dialogs/events-dialog.component';
|
||||
import { EventsDialogComponent } from '../dialogs/events-dialog/events-dialog.component';
|
||||
import { MuteSubscribersService } from '../../services/mute-subscribers.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component';
|
||||
import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component';
|
||||
import { OpenViduVideoComponent } from './ov-video.component';
|
||||
import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog/local-recording-dialog.component';
|
||||
import { ExtensionDialogComponent } from '../dialogs/extension-dialog/extension-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-video',
|
||||
|
|
|
@ -21,11 +21,6 @@ ul {
|
|||
padding-left: 0;
|
||||
}
|
||||
|
||||
video {
|
||||
width: 120px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue