openvidu-testapp: broadcast

pull/780/head
pabloFuente 2023-02-14 17:40:25 +01:00
parent 8083e3b5cb
commit 4d2fa546cf
6 changed files with 105 additions and 25 deletions

View File

@ -1,10 +1,10 @@
<div id="rec-properties-div"> <div id="rec-properties-div">
<label class="label">Recording properties</label> <label class="label">Recording properties</label>
<div> <div>
<mat-form-field class="inner-text-input" [style.fontSize.px]=14> <mat-form-field *ngIf="!isBroadcast" class="inner-text-input" [style.fontSize.px]=14>
<input matInput id="recording-name-field" placeholder="Recording name" [(ngModel)]="recordingProperties.name"> <input matInput id="recording-name-field" placeholder="Recording name" [(ngModel)]="recordingProperties.name">
</mat-form-field> </mat-form-field>
<mat-form-field class="inner-text-input" [style.fontSize.px]=14> <mat-form-field *ngIf="!isBroadcast" class="inner-text-input" [style.fontSize.px]=14>
<mat-select id="rec-outputmode-select" placeholder="Output mode" [(ngModel)]="recordingProperties.outputMode"> <mat-select id="rec-outputmode-select" placeholder="Output mode" [(ngModel)]="recordingProperties.outputMode">
<mat-option *ngFor="let enumerator of enumToArray(recMode)" [value]="enumerator"> <mat-option *ngFor="let enumerator of enumToArray(recMode)" [value]="enumerator">
<span [attr.id]="'option-' + enumerator">{{ enumerator }}</span> <span [attr.id]="'option-' + enumerator">{{ enumerator }}</span>
@ -17,13 +17,14 @@
</mat-form-field> </mat-form-field>
<div class="inner-text-input mat-form-field"> <div class="inner-text-input mat-form-field">
<mat-checkbox id="rec-hasaudio-checkbox" [(ngModel)]="recordingProperties.hasAudio">Has audio</mat-checkbox> <mat-checkbox id="rec-hasaudio-checkbox" [(ngModel)]="recordingProperties.hasAudio">Has audio</mat-checkbox>
<mat-checkbox id="rec-hasvideo-checkbox" [(ngModel)]="recordingProperties.hasVideo">Has video</mat-checkbox> <mat-checkbox *ngIf="!isBroadcast" id="rec-hasvideo-checkbox" [(ngModel)]="recordingProperties.hasVideo">Has
video</mat-checkbox>
<mat-checkbox *ngIf="recordingProperties.outputMode === 'INDIVIDUAL'" id="rec-ignorefailedstreams-checkbox" <mat-checkbox *ngIf="recordingProperties.outputMode === 'INDIVIDUAL'" id="rec-ignorefailedstreams-checkbox"
[(ngModel)]="recordingProperties.ignoreFailedStreams">Ignore failed streams</mat-checkbox> [(ngModel)]="recordingProperties.ignoreFailedStreams">Ignore failed streams</mat-checkbox>
</div> </div>
</div> </div>
<div <div
*ngIf="recordingProperties.hasVideo && (recordingProperties.outputMode === 'COMPOSED' || recordingProperties.outputMode === 'COMPOSED_QUICK_START')"> *ngIf="isBroadcast || (recordingProperties.hasVideo && (recordingProperties.outputMode === 'COMPOSED' || recordingProperties.outputMode === 'COMPOSED_QUICK_START'))">
<mat-form-field class="inner-text-input" [style.fontSize.px]=14> <mat-form-field class="inner-text-input" [style.fontSize.px]=14>
<mat-select placeholder="Recording layout" id="recording-layout-select" <mat-select placeholder="Recording layout" id="recording-layout-select"
[(ngModel)]="recordingProperties.recordingLayout"> [(ngModel)]="recordingProperties.recordingLayout">
@ -50,4 +51,4 @@
[(ngModel)]="recordingProperties.shmSize"> [(ngModel)]="recordingProperties.shmSize">
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>

View File

@ -8,6 +8,9 @@ import { Recording, RecordingLayout, RecordingProperties } from 'openvidu-node-c
}) })
export class RecordingPropertiesComponent { export class RecordingPropertiesComponent {
@Input()
isBroadcast = false;
recMode = Recording.OutputMode; recMode = Recording.OutputMode;
recLayouts = RecordingLayout; recLayouts = RecordingLayout;

View File

@ -1,6 +1,7 @@
#response-text-area { #response-text-area {
width: 100%; width: 100%;
color: #808080; color: #808080;
margin-top: 10px;
} }
#response-text-area textarea { #response-text-area textarea {

View File

@ -6,9 +6,10 @@
<button mat-button id="get-session-btn" (click)="fetchActiveConnections()">Fetch</button> <button mat-button id="get-session-btn" (click)="fetchActiveConnections()">Fetch</button>
<button mat-button id="list-sessions-btn" (click)="fetchActiveSessions()">Fetch all</button> <button mat-button id="list-sessions-btn" (click)="fetchActiveSessions()">Fetch all</button>
<button mat-button id="close-session-btn" (click)="closeSession()">Close this session</button> <button mat-button id="close-session-btn" (click)="closeSession()">Close this session</button>
<mat-divider></mat-divider>
</div> </div>
<mat-divider></mat-divider>
<label class="label">Connections/Streams</label> <label class="label">Connections/Streams</label>
<mat-form-field class="inner-text-input" [style.fontSize.px]=14> <mat-form-field class="inner-text-input" [style.fontSize.px]=14>
<input matInput id="connection-id-field" placeholder="connectionId" [(ngModel)]="connectionId"> <input matInput id="connection-id-field" placeholder="connectionId" [(ngModel)]="connectionId">
@ -33,22 +34,25 @@
<div> <div>
<mat-form-field class="inner-text-input" [style.fontSize.px]=14 style="width:33%"> <mat-form-field class="inner-text-input" [style.fontSize.px]=14 style="width:33%">
<mat-label>Custom Ice Servers</mat-label> <mat-label>Custom Ice Servers</mat-label>
<mat-select [(ngModel)]="numCustomIceServers" id="num-ice-servers-select" (selectionChange)="changedNumIceServers(numCustomIceServers)"> <mat-select [(ngModel)]="numCustomIceServers" id="num-ice-servers-select"
(selectionChange)="changedNumIceServers(numCustomIceServers)">
<mat-option *ngFor="let i of [0,1,2,3,4,5,6,7,8,9,10]" [value]="i"> <mat-option *ngFor="let i of [0,1,2,3,4,5,6,7,8,9,10]" [value]="i">
<span [attr.id]="'num-ice-servers-' + i">{{ i }}</span> <span [attr.id]="'num-ice-servers-' + i">{{ i }}</span>
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div id="manual-turn-div" *ngFor="let configuredIce of configuredCustomIceServers; let i = index"> <div id="manual-turn-div" *ngFor="let configuredIce of configuredCustomIceServers; let i = index">
<mat-form-field style="width: 100%"> <mat-form-field style="width: 100%">
<input matInput id="ice-server-url-{{i}}" placeholder="url" type="text" [(ngModel)]="configuredIce.url"> <input matInput id="ice-server-url-{{i}}" placeholder="url" type="text" [(ngModel)]="configuredIce.url">
</mat-form-field> </mat-form-field>
<mat-form-field style="width: 48%; padding-right: 2px"> <mat-form-field style="width: 48%; padding-right: 2px">
<input matInput id="ice-server-username-{{i}}" placeholder="username" type="text" [(ngModel)]="configuredIce.username"> <input matInput id="ice-server-username-{{i}}" placeholder="username" type="text"
</mat-form-field> [(ngModel)]="configuredIce.username">
<mat-form-field style="width: 48%; padding-left: 2px"> </mat-form-field>
<input matInput id="ice-server-credential-{{i}}" placeholder="credential" type="text" [(ngModel)]="configuredIce.credential"> <mat-form-field style="width: 48%; padding-left: 2px">
</mat-form-field> <input matInput id="ice-server-credential-{{i}}" placeholder="credential" type="text"
[(ngModel)]="configuredIce.credential">
</mat-form-field>
</div> </div>
</div> </div>
<div> <div>
@ -59,9 +63,10 @@
[disabled]="!connectionId">Disconnect</button> [disabled]="!connectionId">Disconnect</button>
<button mat-button id="force-unpublish-api-btn" (click)="forceUnpublish()" <button mat-button id="force-unpublish-api-btn" (click)="forceUnpublish()"
[disabled]="!streamId">Unpublish</button> [disabled]="!streamId">Unpublish</button>
<mat-divider></mat-divider>
</div> </div>
<mat-divider></mat-divider>
<label class="label">Recordings</label> <label class="label">Recordings</label>
<div> <div>
<button mat-button id="list-recording-btn" (click)="listRecordings()">List recordings</button> <button mat-button id="list-recording-btn" (click)="listRecordings()">List recordings</button>
@ -86,13 +91,35 @@
<button mat-button id="delete-recording-btn" (click)="deleteRecording()" [disabled]="!recordingId">Delete <button mat-button id="delete-recording-btn" (click)="deleteRecording()" [disabled]="!recordingId">Delete
recording</button> recording</button>
</div> </div>
<mat-divider></mat-divider>
<label class="label">Broadcast</label>
<div>
<mat-form-field class="inner-text-input" [style.fontSize.px]=14>
<input matInput id="broadcasturl-id-field" placeholder="broadcastUrl" [(ngModel)]="broadcastUrl">
</mat-form-field>
<button mat-button id="start-broadcast-btn" (click)="startBroadcast()">Start broadcast</button>
<button mat-button id="stop-broadcast-btn" (click)="stopBroadcast()">Stop broadcast</button>
<button id="broadcast-properties-btn" mat-icon-button style="width: 24px; height: 24px; line-height: 24px;"
title="Broadcast properties" (click)="toggleBroadcastProperties()">
<mat-icon style="font-size: 18px; line-height: 18px; width: 18px; height: 18px"
aria-label="Broadcast properties">{{broadcastPropertiesIcon}}</mat-icon>
</button>
<app-recording-properties *ngIf="showBroadcastProperties" [isBroadcast]="true" [(recordingProperties)]="broadcastProperties">
</app-recording-properties>
</div>
<mat-divider></mat-divider>
<mat-form-field id="response-text-area" appearance="fill"> <mat-form-field id="response-text-area" appearance="fill">
<textarea id="api-response-text-area" [(ngModel)]="response" matInput readonly></textarea> <textarea id="api-response-text-area" [(ngModel)]="response" matInput readonly></textarea>
<button mat-button id="clear-response-text-area-btn" (click)="response=''">Clear</button> <button mat-button id="clear-response-text-area-btn" (click)="response=''">Clear</button>
</mat-form-field> </mat-form-field>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button id="close-dialog-btn" <button mat-button id="close-dialog-btn"
[mat-dialog-close]="{session: session, recordingProperties: recordingProperties}">CLOSE</button> [mat-dialog-close]="{session: session, recordingProperties: recordingProperties}">CLOSE</button>
</mat-dialog-actions> </mat-dialog-actions>
</div> </div>

View File

@ -19,13 +19,18 @@ export class SessionApiDialogComponent {
response: string; response: string;
recordingProperties: RecordingProperties; recordingProperties: RecordingProperties;
openviduRoles = OpenViduRole;
customLayout = '';
recPropertiesIcon = 'add_circle'; recPropertiesIcon = 'add_circle';
showRecProperties = false; showRecProperties = false;
broadcastProperties: RecordingProperties;
broadcastPropertiesIcon = 'add_circle';
showBroadcastProperties = false;
broadcastUrl: string = 'rtmp://172.17.0.1/live';
numCustomIceServers = 0; numCustomIceServers = 0;
configuredCustomIceServers = [] configuredCustomIceServers = []
openviduRoles = OpenViduRole;
connectionProperties: ConnectionProperties = { connectionProperties: ConnectionProperties = {
record: true, record: true,
role: OpenViduRole.PUBLISHER, role: OpenViduRole.PUBLISHER,
@ -41,6 +46,10 @@ export class SessionApiDialogComponent {
if (!this.recordingProperties.mediaNode) { if (!this.recordingProperties.mediaNode) {
this.recordingProperties.mediaNode = { id: '' }; this.recordingProperties.mediaNode = { id: '' };
} }
this.broadcastProperties = data.broadcastProperties;
if (!this.broadcastProperties.mediaNode) {
this.broadcastProperties.mediaNode = { id: '' };
}
} }
closeSession() { closeSession() {
@ -241,6 +250,11 @@ export class SessionApiDialogComponent {
this.recPropertiesIcon = this.showRecProperties ? 'remove_circle' : 'add_circle'; this.recPropertiesIcon = this.showRecProperties ? 'remove_circle' : 'add_circle';
} }
toggleBroadcastProperties() {
this.showBroadcastProperties = !this.showBroadcastProperties;
this.broadcastPropertiesIcon = this.showBroadcastProperties ? 'remove_circle' : 'add_circle';
}
changedNumIceServers(numIceServers: number) { changedNumIceServers(numIceServers: number) {
// Save Previous Ice Servers // Save Previous Ice Servers
let previousIceServers = []; let previousIceServers = [];
@ -250,14 +264,44 @@ export class SessionApiDialogComponent {
// Fill empty ice servers // Fill empty ice servers
this.configuredCustomIceServers = [] this.configuredCustomIceServers = []
for(let i = 1; i <= numIceServers; i++) { for (let i = 1; i <= numIceServers; i++) {
this.configuredCustomIceServers.push({}); this.configuredCustomIceServers.push({});
} }
// Add previous items // Add previous items
for(let i = 0; i < previousIceServers.length && i < this.configuredCustomIceServers.length; i++) { for (let i = 0; i < previousIceServers.length && i < this.configuredCustomIceServers.length; i++) {
this.configuredCustomIceServers[0] = previousIceServers[0]; this.configuredCustomIceServers[0] = previousIceServers[0];
} }
} }
startBroadcast() {
console.log('Starting broadcast');
const finalBroadcastProperties = {
recordingLayout: this.broadcastProperties.recordingLayout,
customLayout: this.broadcastProperties.customLayout,
resolution: this.broadcastProperties.resolution,
frameRate: this.broadcastProperties.frameRate,
hasAudio: this.broadcastProperties.hasAudio,
shmSize: this.broadcastProperties.shmSize,
mediaNode: !this.broadcastProperties.mediaNode.id ? undefined : this.broadcastProperties.mediaNode
}
this.OV.startBroadcast(this.sessionId, this.broadcastUrl, finalBroadcastProperties)
.then(() => {
this.response = 'Broadcast started';
})
.catch(error => {
this.response = 'Error [' + error.message + ']';
});
}
stopBroadcast() {
this.OV.stopBroadcst(this.sessionId)
.then(() => {
this.response = 'Broadcast stopped';
})
.catch(error => {
this.response = 'Error [' + error.message + ']';
});
}
} }

View File

@ -67,6 +67,9 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
// Recording options // Recording options
recordingProperties: RecordingProperties; recordingProperties: RecordingProperties;
// Broadcast options
broadcastProperties: RecordingProperties;
// OpenVidu Browser objects // OpenVidu Browser objects
OV: OpenVidu; OV: OpenVidu;
session: Session; session: Session;
@ -292,7 +295,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
updateEventList(eventName: string, eventContent: string, event: Event) { updateEventList(eventName: string, eventContent: string, event: Event) {
const eventInterface: OpenViduEvent = { eventName, eventContent, event }; const eventInterface: OpenViduEvent = { eventName, eventContent, event };
this.events.push(eventInterface); this.events.push(eventInterface);
this.testFeedService.pushNewEvent({user: this.index, event}); this.testFeedService.pushNewEvent({ user: this.index, event });
} }
toggleSubscribeTo(): void { toggleSubscribeTo(): void {
@ -698,7 +701,8 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy {
openVidu: !!this.OV_NodeClient ? this.OV_NodeClient : new OpenViduAPI(this.openviduUrl, this.openviduSecret), openVidu: !!this.OV_NodeClient ? this.OV_NodeClient : new OpenViduAPI(this.openviduUrl, this.openviduSecret),
session: this.sessionAPI, session: this.sessionAPI,
sessionId: !!this.session ? this.session.sessionId : this.sessionName, sessionId: !!this.session ? this.session.sessionId : this.sessionName,
recordingProperties: !!this.recordingProperties ? this.recordingProperties : this.sessionProperties.defaultRecordingProperties recordingProperties: !!this.recordingProperties ? this.recordingProperties : this.sessionProperties.defaultRecordingProperties,
broadcastProperties: !!this.broadcastProperties ? this.broadcastProperties : this.sessionProperties.defaultRecordingProperties
}, },
disableClose: true disableClose: true
}); });