mirror of https://github.com/OpenVidu/openvidu.git
521 lines
18 KiB
TypeScript
521 lines
18 KiB
TypeScript
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
import { Subscription } from 'rxjs';
|
|
|
|
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 { SessionConf } from '../openvidu-instance/openvidu-instance.component';
|
|
import { StreamManagerWrapper } from '../users-table/table-video.component';
|
|
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import {
|
|
ConnectionEvent, OpenVidu, PublisherProperties, Session,
|
|
SessionDisconnectedEvent,
|
|
StreamEvent,
|
|
StreamManagerEvent
|
|
} from 'openvidu-browser';
|
|
import {
|
|
MediaMode, OpenVidu as OpenViduAPI, RecordingLayout, RecordingMode, SessionProperties as SessionPropertiesAPI
|
|
} 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
|
|
OV_NodeClient: OpenViduAPI;
|
|
sessionProperties: SessionPropertiesAPI = {
|
|
mediaMode: MediaMode.ROUTED,
|
|
recordingMode: RecordingMode.MANUAL,
|
|
defaultRecordingProperties: {
|
|
recordingLayout: RecordingLayout.BEST_FIT,
|
|
customLayout: ''
|
|
},
|
|
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;
|
|
|
|
private API_PATH = 'openvidu/api';
|
|
|
|
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>' + this.testFeedService.stringifyEventNoCircularDependencies(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': { 'numberOfElements': 0, 'content': [] }, 'streamsIn': { 'numberOfElements': 0, 'content': [] } };
|
|
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 startTimeForUser = Date.now();
|
|
|
|
const OV = new OpenVidu();
|
|
|
|
OV.setAdvancedConfiguration({
|
|
noStreamPlayingEventExceptionTimeout: 50000
|
|
});
|
|
|
|
if (this.turnConf === 'freeice') {
|
|
OV.setAdvancedConfiguration({ iceServers: 'freeice' });
|
|
} else if (this.turnConf === 'manual') {
|
|
OV.setAdvancedConfiguration({ iceServers: [this.manualTurnConf] });
|
|
}
|
|
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);
|
|
}
|
|
});
|
|
|
|
session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => {
|
|
this.testFeedService.pushNewEvent({ user: 0, event });
|
|
});
|
|
|
|
if (user.subscribeTo) {
|
|
session.on('streamCreated', (event: StreamEvent) => {
|
|
|
|
this.testFeedService.pushNewEvent({ user: 0, event });
|
|
|
|
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() - startTimeForUser);
|
|
} else {
|
|
subAux.state['connected'] = (Date.now() - startTimeForUser);
|
|
}
|
|
});
|
|
|
|
const sub = this.subscribers.find(s => s.connectionId === session.connection.connectionId);
|
|
if (!sub) {
|
|
this.subscribers.push({
|
|
connectionId: session.connection.connectionId,
|
|
subs: [{
|
|
startTime: startTimeForUser,
|
|
connectionId: session.connection.connectionId,
|
|
streamManager: subscriber,
|
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
|
}]
|
|
});
|
|
} else {
|
|
sub.subs.push({
|
|
startTime: startTimeForUser,
|
|
connectionId: session.connection.connectionId,
|
|
streamManager: subscriber,
|
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
|
});
|
|
}
|
|
|
|
subscriber.on('streamPlaying', (e: StreamManagerEvent) => {
|
|
|
|
this.testFeedService.pushNewEvent({ user: 0, event: e });
|
|
|
|
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() - startTimeForUser);
|
|
});
|
|
});
|
|
}
|
|
|
|
session.connect(token)
|
|
.then(() => {
|
|
if (user.publishTo) {
|
|
|
|
const publisher = OV.initPublisher(undefined, this.publisherProperties);
|
|
const publisherWrapper = {
|
|
startTime: startTimeForUser,
|
|
connectionId: session.connection.connectionId,
|
|
streamManager: publisher,
|
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
|
};
|
|
|
|
publisher.on('streamCreated', event => {
|
|
this.testFeedService.pushNewEvent({ user: 0, event });
|
|
publisherWrapper.state['connected'] = (Date.now() - startTimeForUser);
|
|
});
|
|
publisher.on('streamPlaying', event => {
|
|
this.testFeedService.pushNewEvent({ user: 0, event });
|
|
publisherWrapper.state['playing'] = (Date.now() - startTimeForUser);
|
|
});
|
|
session.publish(publisher).catch(() => {
|
|
publisherWrapper.state['errorConnecting'] = (Date.now() - startTimeForUser);
|
|
});
|
|
this.publishers.push(publisherWrapper);
|
|
|
|
}
|
|
})
|
|
.catch();
|
|
});
|
|
}
|
|
}
|
|
|
|
private getToken(): Promise<string> {
|
|
this.OV_NodeClient = new OpenViduAPI(this.openviduUrl, this.openviduSecret);
|
|
if (!this.sessionProperties.customSessionId) {
|
|
this.sessionProperties.customSessionId = this.fixedSessionId;
|
|
}
|
|
return this.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) {
|
|
event.streamManager.stream.getSelectedIceCandidate()
|
|
.then(localCandidatePair => {
|
|
|
|
let newReport;
|
|
|
|
if (event.streamManager.remote) {
|
|
newReport = {
|
|
connectionId: event.connectionId,
|
|
startTime: event.startTime,
|
|
streamId: event.streamManager.stream.streamId,
|
|
state: event.state,
|
|
candidatePairSelectedByBrowser: {
|
|
localCandidate: localCandidatePair.localCandidate,
|
|
remoteCandidate: localCandidatePair.remoteCandidate
|
|
},
|
|
candidatePairSelectedByKms: {
|
|
localCandidate: {},
|
|
remoteCandidate: {}
|
|
},
|
|
iceCandidatesSentByBrowser:
|
|
event.streamManager.stream.getLocalIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
|
iceCandidatesReceivedByBrowser:
|
|
event.streamManager.stream.getRemoteIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
|
};
|
|
|
|
this.report.streamsIn.numberOfElements++;
|
|
this.report.streamsIn.content.push(newReport);
|
|
} else {
|
|
newReport = {
|
|
connectionId: event.connectionId,
|
|
startTime: event.startTime,
|
|
streamId: event.streamManager.stream.streamId,
|
|
state: event.state,
|
|
candidatePairSelectedByBrowser: {
|
|
localCandidate: localCandidatePair.localCandidate,
|
|
remoteCandidate: localCandidatePair.remoteCandidate
|
|
},
|
|
candidatePairSelectedByKms: {
|
|
localCandidate: {},
|
|
remoteCandidate: {}
|
|
},
|
|
iceCandidatesSentByBrowser:
|
|
event.streamManager.stream.getLocalIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
|
iceCandidatesReceivedByBrowser:
|
|
event.streamManager.stream.getRemoteIceCandidateList().map((c: RTCIceCandidate) => c.candidate)
|
|
};
|
|
|
|
this.report.streamsOut.numberOfElements++;
|
|
this.report.streamsOut.content.push(newReport);
|
|
}
|
|
|
|
if (++this.numberOfReports === this.totalNumberOfStreams) {
|
|
this.updateRemoteStreamsInfo();
|
|
}
|
|
|
|
})
|
|
.catch();
|
|
}
|
|
|
|
private updateRemoteStreamsInfo() {
|
|
let headers = new HttpHeaders();
|
|
headers = headers.append('Authorization', 'Basic ' + btoa('OPENVIDUAPP:' + this.openviduSecret));
|
|
this.http.get(this.openviduUrl + this.API_PATH + '/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
|
|
sessionInfo => {
|
|
|
|
this.report.streamsOut.content.forEach(report => {
|
|
const streamOutRemoteInfo = sessionInfo['connections'].content
|
|
.find(c => c.connectionId === report.connectionId).publishers
|
|
.find(p => {
|
|
report.webrtcEndpointName = p.webrtcEndpointName;
|
|
report.localSdp = p.localSdp;
|
|
report.remoteSdp = p.remoteSdp;
|
|
return p.webrtcEndpointName === report.streamId;
|
|
});
|
|
report.candidatePairSelectedByKms = {
|
|
localCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.localCandidate),
|
|
remoteCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.remoteCandidate)
|
|
};
|
|
report.serverEvents = streamOutRemoteInfo.events;
|
|
for (const ev of report.serverEvents) {
|
|
ev.timestamp = Number(ev.timestamp) - report.startTime;
|
|
}
|
|
});
|
|
|
|
this.report.streamsIn.content.forEach(report => {
|
|
const streamInRemoteInfo = sessionInfo['connections'].content
|
|
.find(c => c.connectionId === report.connectionId).subscribers
|
|
.find(p => {
|
|
report.webrtcEndpointName = p.webrtcEndpointName;
|
|
report.localSdp = p.localSdp;
|
|
report.remoteSdp = p.remoteSdp;
|
|
return p.webrtcEndpointName === report.connectionId + '_' + report.streamId;
|
|
});
|
|
report.candidatePairSelectedByKms = {
|
|
localCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.localCandidate),
|
|
remoteCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.remoteCandidate)
|
|
};
|
|
report.serverEvents = streamInRemoteInfo.events;
|
|
for (const ev of report.serverEvents) {
|
|
ev.timestamp = Number(ev.timestamp) - report.startTime;
|
|
}
|
|
});
|
|
|
|
this.stringifyAllReports = JSON.stringify(this.report, null, '\t');
|
|
console.log('Info has changed: ' + !(this.stringifyAllReports === this.textAreaValue));
|
|
this.textAreaValue = this.stringifyAllReports;
|
|
},
|
|
error => { }
|
|
);
|
|
}
|
|
|
|
private parseRemoteCandidatePair(candidateStr: string) {
|
|
if (!candidateStr) {
|
|
return 'ERROR: No remote candidate available';
|
|
}
|
|
const array = candidateStr.split(/\s+/);
|
|
return {
|
|
portNumber: array[5],
|
|
ipAddress: array[4],
|
|
transport: array[2].toLowerCase(),
|
|
candidateType: array[7],
|
|
priority: array[3],
|
|
raw: candidateStr
|
|
};
|
|
}
|
|
|
|
focusOnReportForStream(event) {
|
|
this.isFocusedOnReport = true;
|
|
let jsonObject = !!event.in ? this.report.streamsIn : this.report.streamsOut;
|
|
jsonObject = jsonObject.content.find(stream => {
|
|
const webrtcEndpointName = !!event.in ? event.connectionId + '_' + event.streamId : event.streamId;
|
|
return (stream.connectionId === event.connectionId && stream.webrtcEndpointName === webrtcEndpointName);
|
|
});
|
|
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 + this.API_PATH + '/sessions/' + this.fixedSessionId + '?webRtcStats=true', { headers }).subscribe(
|
|
sessionInfo => {
|
|
|
|
event.streamManager.stream.getSelectedIceCandidate()
|
|
.then(localCandidatePair => {
|
|
let newReport;
|
|
if (event.streamManager.remote) {
|
|
const streamInRemoteInfo = sessionInfo['connections'].content
|
|
.find(c => c.connectionId === event.connectionId).subscribers
|
|
.find(p => p.webrtcEndpointName === 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.numberOfElements++;
|
|
this.report.streamsIn.content.push(newReport);
|
|
this.stringifyReport = JSON.stringify(this.report, null, '\t');
|
|
} else {
|
|
const streamOutRemoteInfo = sessionInfo['connections'].content
|
|
.find(c => c.connectionId === event.connectionId).publishers
|
|
.find(p => p.webrtcEndpointName === 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.numberOfElements++;
|
|
this.report.streamsOut.content.push(newReport);
|
|
this.stringifyReport = JSON.stringify(this.report, null, '\t');
|
|
}
|
|
})
|
|
.catch();
|
|
},
|
|
error => { }
|
|
);
|
|
}*/
|
|
|
|
}
|