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 += ('
' + 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 { 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 => { } ); }*/ }