diff --git a/openvidu-testapp/.angular-cli.json b/openvidu-testapp/.angular-cli.json index abb53e19..6bc4c127 100644 --- a/openvidu-testapp/.angular-cli.json +++ b/openvidu-testapp/.angular-cli.json @@ -20,7 +20,8 @@ "prefix": "app", "styles": [ "styles.css", - "openvidu-theme.scss" + "openvidu-theme.scss", + "material-icons.css" ], "scripts": [], "environmentSource": "environments/environment.ts", diff --git a/openvidu-testapp/src/app/app.module.ts b/openvidu-testapp/src/app/app.module.ts index ed4f1a80..4cad47d0 100644 --- a/openvidu-testapp/src/app/app.module.ts +++ b/openvidu-testapp/src/app/app.module.ts @@ -10,6 +10,8 @@ import { AppComponent } from './app.component'; import { TestSessionsComponent } from './components/test-sessions/test-sessions.component'; import { TestApirestComponent } from './components/test-apirest/test-apirest.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'; @@ -19,16 +21,20 @@ import { TestFeedService } from './services/test-feed.service'; import { MuteSubscribersService } from './services/mute-subscribers.service'; 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'; @NgModule({ declarations: [ AppComponent, OpenviduInstanceComponent, + VideoComponent, + OpenViduVideoComponent, TestSessionsComponent, TestApirestComponent, ExtensionDialogComponent, SessionPropertiesDialogComponent, SessionApiDialogComponent, + EventsDialogComponent, LocalRecordingDialogComponent ], imports: [ @@ -49,6 +55,7 @@ import { SessionApiDialogComponent } from './components/dialogs/session-api-dial ExtensionDialogComponent, SessionPropertiesDialogComponent, SessionApiDialogComponent, + EventsDialogComponent, LocalRecordingDialogComponent ], bootstrap: [AppComponent] diff --git a/openvidu-testapp/src/app/components/dialogs/events-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/events-dialog.component.ts new file mode 100644 index 00000000..24438242 --- /dev/null +++ b/openvidu-testapp/src/app/components/dialogs/events-dialog.component.ts @@ -0,0 +1,47 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; + +@Component({ + selector: 'app-events-dialog', + template: ` +

{{target}} events

+ + ALL + + {{event}} + + + + + + `, + styles: [ + 'mat-dialog-content { display: inline; }', + 'mat-divider { margin-top: 5px; margin-bottom: 5px }' + ] +}) +export class EventsDialogComponent { + + target = ''; + checkAll = true; + eventCollection: any = {}; + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data) { + this.target = data.target; + this.eventCollection = data.eventCollection; + } + + updateAll() { + Object.keys(this.eventCollection).forEach(key => { + this.eventCollection[key] = this.checkAll; + }); + } + + eventNamesArray(): String[] { + return Object.keys(this.eventCollection); + } + +} diff --git a/openvidu-testapp/src/app/components/dialogs/local-recording-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/local-recording-dialog.component.ts index 879c326d..cecfeace 100644 --- a/openvidu-testapp/src/app/components/dialogs/local-recording-dialog.component.ts +++ b/openvidu-testapp/src/app/components/dialogs/local-recording-dialog.component.ts @@ -29,13 +29,13 @@ export class LocalRecordingDialogComponent { public myReference: MatDialogRef; - private recorder: LocalRecorder; + recorder: LocalRecorder; - private uploading = false; - private endpoint = ''; - private uploadIcon: string; - private iconColor: string; - private iconClass = ''; + uploading = false; + endpoint = ''; + uploadIcon: string; + iconColor: string; + iconClass = ''; constructor(@Inject(MAT_DIALOG_DATA) public data: any) { this.recorder = data.recorder; diff --git a/openvidu-testapp/src/app/components/dialogs/session-api-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/session-api-dialog.component.ts index 1ab951d3..6925e9e4 100644 --- a/openvidu-testapp/src/app/components/dialogs/session-api-dialog.component.ts +++ b/openvidu-testapp/src/app/components/dialogs/session-api-dialog.component.ts @@ -5,7 +5,7 @@ import { Session } from 'openvidu-browser'; import { OpenVidu as OpenViduAPI } from 'openvidu-node-client'; @Component({ - selector: 'app-session-properties-dialog', + selector: 'app-session-api-dialog', template: `

API REST

diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.css b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.css index fad4dfe6..97bbf69e 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.css +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.css @@ -34,7 +34,7 @@ mat-card.session-card { .inner-card { border: 1px solid #e1e1e1; - padding: 10px 15px 10px 15px; + padding: 10px 3px 10px 10px; background: #ffffff; margin-top: 5px; } @@ -138,19 +138,25 @@ mat-expansion-panel-header { } .mat-icon-custom { - width: 29px; - height: 29px; - line-height: 18px; + width: 24px; + height: 24px; + line-height: 17px; + margin-bottom: -4px; } .mat-icon-custom-ic { - width: 20px; - height: 20px; - font-size: 20px; - line-height: 20px; + width: 18px; + height: 18px; + font-size: 18px; + line-height: 18px; } .session-btns-div { margin-top: -14px; margin-right: -14px; + margin-left: 5px; +} + +.publisher-btns-div { + margin-top: -7px; } \ No newline at end of file diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html index f9fce22a..ef37cd93 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html @@ -8,20 +8,25 @@
- + - +
- - +
@@ -36,7 +41,7 @@
-
+

Send

@@ -57,7 +62,7 @@
-
+
Video @@ -71,6 +76,19 @@
to remote
+
+
+ + +
+
+
@@ -85,21 +103,9 @@
{{sessionName}}
- - - - @@ -123,6 +129,10 @@
+ + + +
diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts index 3e36ce5c..5ede01b0 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.ts @@ -7,7 +7,7 @@ import { Subscription } from 'rxjs/Subscription'; import { OpenVidu, Session, Subscriber, Publisher, Stream, Connection, LocalRecorder, VideoInsertMode, StreamEvent, ConnectionEvent, - SessionDisconnectedEvent, SignalEvent, RecordingEvent, VideoElementEvent + SessionDisconnectedEvent, SignalEvent, RecordingEvent, VideoElementEvent, PublisherSpeakingEvent, StreamManagerEvent, StreamManager } from 'openvidu-browser'; import { OpenVidu as OpenViduAPI, @@ -22,10 +22,10 @@ import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component' import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component'; import { TestFeedService } from '../../services/test-feed.service'; import { MuteSubscribersService } from '../../services/mute-subscribers.service'; +import { EventsDialogComponent } from '../dialogs/events-dialog.component'; import { SessionPropertiesDialogComponent } from '../dialogs/session-properties-dialog.component'; import { SessionApiDialogComponent } from '../dialogs/session-api-dialog.component'; -declare var $: any; export interface SessionConf { subscribeTo: boolean; @@ -56,6 +56,9 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { @Input() sessionConf: SessionConf; + @Input() + index: number; + // Session join data clientData: string; sessionName: string; @@ -91,7 +94,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { OV: OpenVidu; session: Session; publisher: Publisher; - subscribers = {}; + subscribers: Subscriber[] = []; // OpenVidu Node Client objects sessionProperties: SessionPropertiesAPI = { @@ -102,17 +105,18 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { customSessionId: '' }; - // Session audio and video status - audioMuted = false; - videoMuted = false; - unpublished = false; - publisherChanged = false; - audioIcon = 'mic'; - videoIcon = 'videocam'; - publishIcon = 'stop'; - - sendAudioChange: boolean; - sendVideoChange: boolean; + sessionEvents = { + connectionCreated: true, + connectionDestroyed: true, + sessionDisconnected: true, + streamCreated: true, + streamDestroyed: true, + recordingStarted: true, + recordingStopped: true, + signal: true, + publisherStartSpeaking: false, + publisherStopSpeaking: false + }; events: OpenViduEvent[] = []; @@ -127,8 +131,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { private changeDetector: ChangeDetectorRef, private dialog: MatDialog, private recordDialog: MatDialog, - private testFeedService: TestFeedService, - private muteSubscribersService: MuteSubscribersService, + private testFeedService: TestFeedService ) { this.generateSessionInfo(); } @@ -147,13 +150,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { if (this.sessionConf.startSession) { this.joinSession(); } - - this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe( - muteOrUnmute => { - Object.keys(this.subscribers).forEach((key) => { - this.subscribers[key].videoElement.muted = muteOrUnmute; - }); - }); } ngOnChanges(changes: SimpleChanges) { @@ -166,7 +162,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { } ngOnDestroy() { - if (!!this.muteSubscribersSubscription) { this.muteSubscribersSubscription.unsubscribe(); } this.leaveSession(); } @@ -199,24 +194,24 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { this.session = this.OV.initSession(); - this.addSessionEvents(this.session); + this.updateSessionEvents({ + connectionCreated: false, + connectionDestroyed: false, + sessionDisconnected: false, + streamCreated: false, + streamDestroyed: false, + recordingStarted: false, + recordingStopped: false, + signal: false, + publisherStartSpeaking: true, + publisherStopSpeaking: true + }, true); this.session.connect(token, this.clientData) .then(() => { this.changeDetector.detectChanges(); if (this.publishTo) { - - this.audioMuted = !this.activeAudio; - this.videoMuted = !this.activeVideo; - this.unpublished = false; - this.updateAudioIcon(); - this.updateVideoIcon(); - this.updatePublishIcon(); - - this.sendAudioChange = this.sendAudio; - this.sendVideoChange = this.sendVideo; - // this.asyncInitPublisher(); this.syncInitPublisher(); } @@ -226,113 +221,14 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { }); } - private leaveSession(): void { - if (!!this.publisherRecorder) { - this.restartPublisherRecord(); - } - Object.keys(this.subscribers).forEach((key) => { - if (!!this.subscribers[key].recorder) { - this.restartSubscriberRecord(key); - } - }); if (this.session) { this.session.disconnect(); } - this.session = null; - this.OV = null; - } - - private toggleAudio() { - this.publisher.publishAudio(this.audioMuted); - this.audioMuted = !this.audioMuted; - this.updateAudioIcon(); - } - - private toggleVideo() { - this.publisher.publishVideo(this.videoMuted); - this.videoMuted = !this.videoMuted; - this.updateVideoIcon(); - } - - private updateAudioIcon() { - this.audioMuted ? this.audioIcon = 'mic_off' : this.audioIcon = 'mic'; - } - - private updateVideoIcon() { - this.videoMuted ? this.videoIcon = 'videocam_off' : this.videoIcon = 'videocam'; - } - - private updatePublishIcon() { - this.unpublished ? this.publishIcon = 'play_arrow' : this.publishIcon = 'stop'; - } - - private appendSubscriberData(videoElement: HTMLVideoElement, connection: Connection): void { - const dataNode = document.createElement('div'); - dataNode.className = 'data-node'; - dataNode.id = 'data-' + this.session.connection.connectionId + '-' + connection.connectionId; - dataNode.innerHTML = '

' + connection.data + '

' + - '' + - '' + - '' + - '' + - ''; - videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling); - document.getElementById('sub-btn-' + this.session.connection.connectionId + '-' + connection.connectionId) - .addEventListener('click', this.subUnsubFromSubscriber.bind(this, connection.connectionId)); - document.getElementById('sub-video-btn-' + this.session.connection.connectionId + '-' + connection.connectionId) - .addEventListener('click', this.subUnsubFromSubscriberVideo.bind(this, connection.connectionId)); - document.getElementById('sub-audio-btn-' + this.session.connection.connectionId + '-' + connection.connectionId) - .addEventListener('click', this.subUnsubFromSubscriberAudio.bind(this, connection.connectionId)); - document.getElementById('record-btn-' + this.session.connection.connectionId + '-' + connection.connectionId) - .addEventListener('click', this.recordSubscriber.bind(this, connection.connectionId)); - document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connection.connectionId) - .addEventListener('click', this.pauseSubscriber.bind(this, connection.connectionId)); - } - - private appendPublisherData(videoElement: HTMLVideoElement): void { - const dataNode = document.createElement('div'); - dataNode.className = 'data-node'; - dataNode.id = 'data-' + this.session.connection.connectionId + '-' + this.session.connection.connectionId; - dataNode.innerHTML = - '' + - ''; - videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling); - document.getElementById('local-record-btn-' + this.session.connection.connectionId).addEventListener('click', - this.recordPublisher.bind(this)); - document.getElementById('local-pause-btn-' + this.session.connection.connectionId).addEventListener('click', - this.pausePublisher.bind(this)); - } - - private removeUserData(connectionId: string): void { - $('#remote-vid-' + this.session.connection.connectionId) - .find('#data-' + this.session.connection.connectionId + '-' + connectionId).remove(); + delete this.session; + delete this.OV; + delete this.publisher; + this.subscribers = []; } private updateEventList(event: string, content: string) { @@ -433,432 +329,122 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { // this.initGrayVideo(); } - recordPublisher(): void { - if (!this.publisherRecording) { - this.publisherRecorder = this.OV.initLocalRecorder(this.publisher.stream); - this.publisherRecorder.record(); - this.publisherRecording = true; - document.getElementById('local-record-icon-' + this.session.connection.connectionId).innerHTML = 'stop'; - document.getElementById('local-pause-btn-' + this.session.connection.connectionId).style.display = 'block'; - } else { - this.publisherRecorder.stop() - .then(() => { - let dialogRef: MatDialogRef; - dialogRef = this.recordDialog.open(LocalRecordingDialogComponent, { - disableClose: true, - data: { - recorder: this.publisherRecorder - } - }); - dialogRef.componentInstance.myReference = dialogRef; + updateSessionEvents(oldValues, firstTime) { - dialogRef.afterOpen().subscribe(() => { - this.afterOpenPreview(this.publisherRecorder); - }); - dialogRef.afterClosed().subscribe(() => { - this.afterClosePreview(); - }); - }) - .catch((error) => { - console.error('Error stopping LocalRecorder: ' + error); - }); - } - } - - pausePublisher(): void { - if (!this.publisherPaused) { - this.publisherRecorder.pause(); - document.getElementById('local-pause-icon-' + this.session.connection.connectionId).innerHTML = 'play_arrow'; - } else { - this.publisherRecorder.resume(); - document.getElementById('local-pause-icon-' + this.session.connection.connectionId).innerHTML = 'pause'; - } - this.publisherPaused = !this.publisherPaused; - } - - recordSubscriber(connectionId: string): void { - const subscriber: Subscriber = this.subscribers[connectionId].subscriber; - const recording = this.subscribers[connectionId].recording; - if (!recording) { - this.subscribers[connectionId].recorder = this.OV.initLocalRecorder(subscriber.stream); - this.subscribers[connectionId].recorder.record(); - this.subscribers[connectionId].recording = true; - document.getElementById('record-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'stop'; - document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId).style.display = 'block'; - } else { - this.subscribers[connectionId].recorder.stop() - .then(() => { - let dialogRef: MatDialogRef; - dialogRef = this.recordDialog.open(LocalRecordingDialogComponent, { - disableClose: true, - data: { - recorder: this.subscribers[connectionId].recorder - } - }); - dialogRef.componentInstance.myReference = dialogRef; - - dialogRef.afterOpen().subscribe(() => { - this.afterOpenPreview(this.subscribers[connectionId].recorder); - }); - dialogRef.afterClosed().subscribe(() => { - this.afterClosePreview(connectionId); - }); - }) - .catch((error) => { - console.error('Error stopping LocalRecorder: ' + error); - }); - } - } - - pauseSubscriber(connectionId: string): void { - const subscriber: Subscriber = this.subscribers[connectionId].subscriber; - const subscriberPaused = this.subscribers[connectionId].paused; - if (!subscriberPaused) { - this.subscribers[connectionId].recorder.pause(); - document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'play_arrow'; - } else { - this.subscribers[connectionId].recorder.resume(); - document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'pause'; - } - this.subscribers[connectionId].paused = !this.subscribers[connectionId].paused; - } - - publishUnpublish(): void { - if (this.unpublished) { - this.session.publish(this.publisher) - .then(() => { - console.log(this.publisher); - }) - .catch(e => { - console.error(e); - }); - } else { - this.session.unpublish(this.publisher); - this.removeUserData(this.session.connection.connectionId); - this.restartPublisherRecord(); - } - this.unpublished = !this.unpublished; - this.updatePublishIcon(); - } - - changePublisher() { - let screenChange; - if (!this.publisherChanged) { - if (this.sendAudio && !this.sendVideo) { - this.sendAudioChange = false; - this.sendVideoChange = true; - screenChange = false; - } else if (!this.sendAudio && this.sendVideo) { - this.sendAudioChange = true; - this.sendVideoChange = false; - } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'video') { - this.sendAudioChange = false; - this.sendVideoChange = true; - screenChange = true; - } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'screen') { - this.sendAudioChange = false; - this.sendVideoChange = true; - screenChange = false; - } - } else { - this.sendAudioChange = this.sendAudio; - this.sendVideoChange = this.sendVideo; - screenChange = this.optionsVideo === 'screen' ? true : false; - } - - this.audioMuted = false; - this.videoMuted = false; - this.unpublished = false; - this.updateAudioIcon(); - this.updateVideoIcon(); - this.updatePublishIcon(); - - const otherPublisher = this.OV.initPublisher( - 'local-vid-' + this.session.connection.connectionId, - { - audioSource: this.sendAudioChange ? undefined : false, - videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false, - publishAudio: (!this.publisherChanged) ? true : !this.audioMuted, - publishVideo: (!this.publisherChanged) ? true : !this.videoMuted, - resolution: '640x480', - frameRate: 30, - insertMode: VideoInsertMode.APPEND - }, - (err) => { - if (err) { - console.warn(err); - this.openviduError = err; - if (err.name === 'SCREEN_EXTENSION_NOT_INSTALLED') { - this.dialog.open(ExtensionDialogComponent, { - data: { url: err.message }, - disableClose: true, - width: '250px' - }); + if (this.sessionEvents.streamCreated !== oldValues.streamCreated || firstTime) { + this.session.off('streamCreated'); + if (this.sessionEvents.streamCreated) { + this.session.on('streamCreated', (event: StreamEvent) => { + this.changeDetector.detectChanges(); + if (this.subscribeTo) { + this.syncSubscribe(this.session, event); } - } - }); - this.addPublisherEvents(otherPublisher); - - otherPublisher.once('accessAllowed', () => { - if (!this.unpublished) { - this.session.unpublish(this.publisher); - this.publisher = otherPublisher; - this.removeUserData(this.session.connection.connectionId); - this.restartPublisherRecord(); - } - this.session.publish(otherPublisher); - }); - - this.publisherChanged = !this.publisherChanged; - } - - subUnsubFromSubscriber(connectionId: string) { - let subscriber: Subscriber = this.subscribers[connectionId].subscriber; - if (this.subscribers[connectionId].subbed) { - this.session.unsubscribe(subscriber); - this.restartSubscriberRecord(connectionId); - document.getElementById('data-' + this.session.connection.connectionId + '-' + connectionId).style.marginLeft = '0'; - document.getElementById('sub-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = 'notifications_off'; - document.getElementById('record-btn-' + this.session.connection.connectionId + '-' + connectionId).remove(); - document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId).remove(); - document.getElementById('sub-video-btn-' + this.session.connection.connectionId + '-' + connectionId).remove(); - document.getElementById('sub-audio-btn-' + this.session.connection.connectionId + '-' + connectionId).remove(); - this.subscribers[connectionId].subbedVideo = false; - this.subscribers[connectionId].subbedAudio = false; - } else { - - - this.session.subscribeAsync(subscriber.stream, 'remote-vid-' + this.session.connection.connectionId) - .then(sub => { - subscriber = sub; - this.subscribers[connectionId].subscriber = subscriber; - subscriber.on('videoElementCreated', (e: VideoElementEvent) => { - if (!subscriber.stream.hasVideo) { - $(e.element).css({ 'background-color': '#4d4d4d' }); - $(e.element).attr('poster', 'assets/images/volume.png'); - } - this.subscribers[connectionId].videoElement = e.element; - this.updateEventList('videoElementCreated', e.element.id); - }); - subscriber.on('videoPlaying', (e: VideoElementEvent) => { - this.removeUserData(connectionId); - this.appendSubscriberData(e.element, subscriber.stream.connection); - this.updateEventList('videoPlaying', e.element.id); - }); - }) - .catch(err => { - console.error(err); + this.updateEventList('streamCreated', event.stream.streamId); }); - - - /*subscriber = this.session.subscribe(subscriber.stream, 'remote-vid-' + this.session.connection.connectionId); - this.subscribers[connectionId].subscriber = subscriber; - subscriber.on('videoElementCreated', (e) => { - if (!subscriber.stream.hasVideo) { - $(e.element).css({ 'background-color': '#4d4d4d' }); - $(e.element).attr('poster', 'assets/images/volume.png'); - } - this.subscribers[connectionId].videoElement = e.element; - this.updateEventList('videoElementCreated', e.element.id); - }); - subscriber.on('videoPlaying', (e) => { - this.removeUserData(connectionId); - this.appendSubscriberData(e.element, subscriber.stream.connection); - this.updateEventList('videoPlaying', e.element.id); - });*/ - - - } - this.subscribers[connectionId].subbed = !this.subscribers[connectionId].subbed; - } - - subUnsubFromSubscriberVideo(connectionId: string) { - this.subscribers[connectionId].subscriber.subscribeToVideo(!!this.subscribers[connectionId].subbedVideo); - this.subscribers[connectionId].subbedVideo = !this.subscribers[connectionId].subbedVideo; - document.getElementById('sub-video-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = - this.subscribers[connectionId].subbedVideo ? 'videocam_off' : 'videocam'; - } - - subUnsubFromSubscriberAudio(connectionId: string) { - this.subscribers[connectionId].subscriber.subscribeToAudio(!!this.subscribers[connectionId].subbedAudio); - this.subscribers[connectionId].subbedAudio = !this.subscribers[connectionId].subbedAudio; - document.getElementById('sub-audio-icon-' + this.session.connection.connectionId + '-' + connectionId).innerHTML = - this.subscribers[connectionId].subbedAudio ? 'mic_off' : 'mic'; - } - - addSessionEvents(session: Session) { - session.on('streamCreated', (event: StreamEvent) => { - - this.changeDetector.detectChanges(); - - if (this.subscribeTo) { - // this.syncSubscribe(session, event); - this.asyncSubscribe(session, event); - } - this.updateEventList('streamCreated', event.stream.connection.connectionId); - }); - - session.on('streamDestroyed', (event: StreamEvent) => { - this.removeUserData(event.stream.connection.connectionId); - this.updateEventList('streamDestroyed', event.stream.connection.connectionId); - }); - session.on('connectionCreated', (event: ConnectionEvent) => { - this.updateEventList('connectionCreated', event.connection.connectionId); - }); - session.on('connectionDestroyed', (event: ConnectionEvent) => { - this.updateEventList('connectionDestroyed', event.connection.connectionId); - }); - session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => { - this.updateEventList('sessionDisconnected', 'No data'); - if (event.reason === 'networkDisconnect') { - this.session = null; - this.OV = null; - } - }); - session.on('signal', (event: SignalEvent) => { - this.updateEventList('signal', event.from.connectionId + '-' + event.data); - }); - - session.on('recordingStarted', (event: RecordingEvent) => { - this.updateEventList('recordingStarted', event.id); - }); - - session.on('recordingStopped', (event: RecordingEvent) => { - this.updateEventList('recordingStopped', event.id); - }); - - /*session.on('publisherStartSpeaking', (event) => { - console.log('Publisher start speaking'); - }); - - session.on('publisherStopSpeaking', (event) => { - console.log('Publisher stop speaking'); - });*/ - } - - addPublisherEvents(publisher: Publisher) { - publisher.on('videoElementCreated', (event: VideoElementEvent) => { - if (this.publishTo && - (!this.sendVideoChange || - this.sendVideoChange && - !(this.optionsVideo !== 'screen') && - this.openviduError && - this.openviduError.name === 'NO_VIDEO_DEVICE')) { - $(event.element).css({ 'background-color': '#4d4d4d' }); - $(event.element).attr('poster', 'assets/images/volume.png'); - } - this.updateEventList('videoElementCreated', event.element.id); - }); - - publisher.on('accessAllowed', (e) => { - this.updateEventList('accessAllowed', ''); - }); - - publisher.on('accessDenied', (e) => { - this.updateEventList('accessDenied', ''); - }); - - publisher.on('accessDialogOpened', (e) => { - this.updateEventList('accessDialogOpened', ''); - }); - - publisher.on('accessDialogClosed', (e) => { - this.updateEventList('accessDialogClosed', ''); - }); - - publisher.on('videoPlaying', (e: VideoElementEvent) => { - this.appendPublisherData(e.element); - this.updateEventList('videoPlaying', e.element.id); - }); - - publisher.on('remoteVideoPlaying', (e: VideoElementEvent) => { - this.appendPublisherData(e.element); - this.updateEventList('remoteVideoPlaying', e.element.id); - }); - - publisher.on('streamCreated', (e: StreamEvent) => { - this.updateEventList('streamCreated', e.stream.connection.connectionId); - }); - - publisher.on('streamDestroyed', (e: StreamEvent) => { - this.updateEventList('streamDestroyed', e.stream.connection.connectionId); - }); - - publisher.on('videoElementDestroyed', (e: VideoElementEvent) => { - this.updateEventList('videoElementDestroyed', '(Publisher)'); - }); - } - - private afterOpenPreview(recorder: LocalRecorder): void { - this.muteSubscribersService.updateMuted(true); - recorder.preview('recorder-preview').controls = true; - } - - private afterClosePreview(connectionId?: string): void { - this.muteSubscribersService.updateMuted(false); - if (!!connectionId) { - this.restartSubscriberRecord(connectionId); - } else { - this.restartPublisherRecord(); - } - } - - private restartPublisherRecord(): void { - if (!!this.session) { - let el: HTMLElement = document.getElementById('local-record-icon-' + this.session.connection.connectionId); - if (!!el) { - el.innerHTML = 'fiber_manual_record'; - } - el = document.getElementById('local-pause-icon-' + this.session.connection.connectionId); - if (!!el) { - el.innerHTML = 'pause'; - } - el = document.getElementById('local-pause-btn-' + this.session.connection.connectionId); - if (!!el) { - el.style.display = 'none'; } } - this.publisherPaused = false; - this.publisherRecording = false; - if (!!this.publisherRecorder) { - this.publisherRecorder.clean(); - } - } - private restartSubscriberRecord(connectionId: string): void { - if (!!this.session) { - let el: HTMLElement = document.getElementById('record-icon-' + this.session.connection.connectionId + '-' + connectionId); - if (!!el) { - el.innerHTML = 'fiber_manual_record'; - } - el = document.getElementById('pause-icon-' + this.session.connection.connectionId + '-' + connectionId); - if (!!el) { - el.innerHTML = 'pause'; - } - el = document.getElementById('pause-btn-' + this.session.connection.connectionId + '-' + connectionId); - if (!!el) { - el.style.display = 'none'; + if (this.sessionEvents.streamDestroyed !== oldValues.streamDestroyed || firstTime) { + this.session.off('streamDestroyed'); + if (this.sessionEvents.streamDestroyed) { + this.session.on('streamDestroyed', (event: StreamEvent) => { + const index = this.subscribers.indexOf(event.stream.streamManager); + if (index > -1) { + this.subscribers.splice(index, 1); + } + this.updateEventList('streamDestroyed', event.stream.streamId); + }); } } - this.subscribers[connectionId].recording = false; - this.subscribers[connectionId].paused = false; - if (!!this.subscribers[connectionId].recorder) { - this.subscribers[connectionId].recorder.clean(); + if (this.sessionEvents.connectionCreated !== oldValues.connectionCreated || firstTime) { + this.session.off('connectionCreated'); + if (this.sessionEvents.connectionCreated) { + this.session.on('connectionCreated', (event: ConnectionEvent) => { + this.updateEventList('connectionCreated', event.connection.connectionId); + }); + } + } + + if (this.sessionEvents.connectionDestroyed !== oldValues.connectionDestroyed || firstTime) { + this.session.off('connectionDestroyed'); + if (this.sessionEvents.connectionDestroyed) { + this.session.on('connectionDestroyed', (event: ConnectionEvent) => { + delete this.subscribers[event.connection.connectionId]; + this.updateEventList('connectionDestroyed', event.connection.connectionId); + }); + } + } + + if (this.sessionEvents.sessionDisconnected !== oldValues.sessionDisconnected || firstTime) { + this.session.off('sessionDisconnected'); + if (this.sessionEvents.sessionDisconnected) { + this.session.on('sessionDisconnected', (event: SessionDisconnectedEvent) => { + this.updateEventList('sessionDisconnected', 'No data'); + if (event.reason === 'networkDisconnect') { + this.session = null; + this.OV = null; + } + }); + } + } + + if (this.sessionEvents.signal !== oldValues.signal || firstTime) { + this.session.off('signal'); + if (this.sessionEvents.signal) { + this.session.on('signal', (event: SignalEvent) => { + this.updateEventList('signal', event.from.connectionId + '-' + event.data); + }); + } + } + + if (this.sessionEvents.recordingStarted !== oldValues.recordingStarted || firstTime) { + this.session.off('recordingStarted'); + if (this.sessionEvents.recordingStarted) { + this.session.on('recordingStarted', (event: RecordingEvent) => { + this.updateEventList('recordingStarted', event.id); + }); + } + } + + if (this.sessionEvents.recordingStopped !== oldValues.recordingStopped || firstTime) { + this.session.off('recordingStopped'); + if (this.sessionEvents.recordingStopped) { + this.session.on('recordingStopped', (event: RecordingEvent) => { + this.updateEventList('recordingStopped', event.id); + }); + } + } + + if (this.sessionEvents.publisherStartSpeaking !== oldValues.publisherStartSpeaking || firstTime) { + this.session.off('publisherStartSpeaking'); + if (this.sessionEvents.publisherStartSpeaking) { + this.session.on('publisherStartSpeaking', (event: PublisherSpeakingEvent) => { + this.updateEventList('publisherStartSpeaking', event.connection.connectionId); + }); + } + } + + if (this.sessionEvents.publisherStopSpeaking !== oldValues.publisherStopSpeaking || firstTime) { + this.session.off('publisherStopSpeaking'); + if (this.sessionEvents.publisherStopSpeaking) { + this.session.on('publisherStopSpeaking', (event: PublisherSpeakingEvent) => { + this.updateEventList('publisherStopSpeaking', event.connection.connectionId); + }); + } } } syncInitPublisher() { this.publisher = this.OV.initPublisher( - 'local-vid-' + this.session.connection.connectionId, + undefined, { audioSource: this.sendAudio ? undefined : false, videoSource: this.sendVideo ? (this.optionsVideo === 'screen' ? 'screen' : undefined) : false, publishAudio: this.activeAudio, publishVideo: this.activeVideo, resolution: '640x480', - frameRate: 30, - insertMode: VideoInsertMode.APPEND + frameRate: 30 }, (err) => { if (err) { @@ -874,8 +460,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { } }); - this.addPublisherEvents(this.publisher); - if (this.subscribeToRemote) { this.publisher.subscribeToRemote(); } @@ -897,7 +481,6 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { }) .then(publisher => { this.publisher = publisher; - this.addPublisherEvents(this.publisher); if (this.subscribeToRemote) { this.publisher.subscribeToRemote(); } @@ -925,75 +508,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { } syncSubscribe(session: Session, event) { - const subscriber: Subscriber = session.subscribe(event.stream, 'remote-vid-' + session.connection.connectionId); - this.subscribers[subscriber.stream.connection.connectionId] = { - 'subscriber': subscriber, - 'subbed': true, - 'recorder': undefined, - 'recording': false, - 'paused': false, - 'videoElement': undefined - }; - subscriber.on('videoElementCreated', (e: VideoElementEvent) => { - if (!event.stream.hasVideo) { - $(e.element).css({ 'background-color': '#4d4d4d' }); - $(e.element).attr('poster', 'assets/images/volume.png'); - } - this.subscribers[subscriber.stream.connection.connectionId].videoElement = e.element; - this.updateEventList('videoElementCreated', e.element.id); - }); - subscriber.on('videoPlaying', (e: VideoElementEvent) => { - this.appendSubscriberData(e.element, subscriber.stream.connection); - this.updateEventList('videoPlaying', e.element.id); - }); - subscriber.on('videoElementDestroyed', (e) => { - this.updateEventList('videoElementDestroyed', '(Subscriber)'); - }); - } - - asyncSubscribe(session: Session, event) { - session.subscribeAsync(event.stream, 'remote-vid-' + session.connection.connectionId) - .then(subscriber => { - this.subscribers[subscriber.stream.connection.connectionId] = { - 'subscriber': subscriber, - 'subbed': true, - 'recorder': undefined, - 'recording': false, - 'paused': false, - 'videoElement': undefined - }; - subscriber.on('videoElementCreated', (e: VideoElementEvent) => { - if (!event.stream.hasVideo) { - $(e.element).css({ 'background-color': '#4d4d4d' }); - $(e.element).attr('poster', 'assets/images/volume.png'); - } - this.subscribers[subscriber.stream.connection.connectionId].videoElement = e.element; - this.updateEventList('videoElementCreated', e.element.id); - }); - subscriber.on('videoPlaying', (e: VideoElementEvent) => { - this.appendSubscriberData(e.element, subscriber.stream.connection); - this.updateEventList('videoPlaying', e.element.id); - }); - subscriber.on('videoElementDestroyed', (e) => { - this.updateEventList('videoElementDestroyed', '(Subscriber)'); - }); - }) - .catch(err => { - console.error(err); - }); - } - - enableSpeakingEvents() { - this.session.on('publisherStartSpeaking', (event) => { - }); - - this.session.on('publisherStopSpeaking', (event) => { - }); - } - - disableSpeakingEvents() { - this.session.off('publisherStartSpeaking'); - this.session.off('publisherStopSpeaking'); + this.subscribers.push(session.subscribe(event.stream, undefined)); } initGrayVideo(): void { @@ -1050,7 +565,7 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { this.sessionName = this.sessionProperties.customSessionId; } } - document.getElementById('session-settings-btn').classList.remove('cdk-program-focused'); + document.getElementById('session-settings-btn-' + this.index).classList.remove('cdk-program-focused'); }); } @@ -1064,7 +579,54 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { }); dialogRef.afterClosed().subscribe((result: string) => { - document.getElementById('session-api-btn').classList.remove('cdk-program-focused'); + document.getElementById('session-api-btn-' + this.index).classList.remove('cdk-program-focused'); + }); + } + + openSessionEventsDialog() { + + const oldValues = { + connectionCreated: this.sessionEvents.connectionCreated, + connectionDestroyed: this.sessionEvents.connectionDestroyed, + sessionDisconnected: this.sessionEvents.sessionDisconnected, + streamCreated: this.sessionEvents.streamCreated, + streamDestroyed: this.sessionEvents.streamDestroyed, + recordingStarted: this.sessionEvents.recordingStarted, + recordingStopped: this.sessionEvents.recordingStopped, + signal: this.sessionEvents.signal, + publisherStartSpeaking: this.sessionEvents.publisherStartSpeaking, + publisherStopSpeaking: this.sessionEvents.publisherStopSpeaking + }; + + const dialogRef = this.dialog.open(EventsDialogComponent, { + data: { + eventCollection: this.sessionEvents, + target: 'Session' + }, + width: '280px', + autoFocus: false, + disableClose: true + }); + + dialogRef.afterClosed().subscribe((result) => { + + if (!!this.session && JSON.stringify(this.sessionEvents) !== JSON.stringify(oldValues)) { + this.updateSessionEvents(oldValues, false); + } + + this.sessionEvents = { + connectionCreated: result.connectionCreated, + connectionDestroyed: result.connectionDestroyed, + sessionDisconnected: result.sessionDisconnected, + streamCreated: result.streamCreated, + streamDestroyed: result.streamDestroyed, + recordingStarted: result.recordingStarted, + recordingStopped: result.recordingStopped, + signal: result.signal, + publisherStartSpeaking: result.publisherStartSpeaking, + publisherStopSpeaking: result.publisherStopSpeaking + }; + document.getElementById('session-events-btn-' + this.index).classList.remove('cdk-program-focused'); }); } @@ -1079,4 +641,15 @@ export class OpenviduInstanceComponent implements OnInit, OnChanges, OnDestroy { }); } + udpateEventFromChild(event) { + this.updateEventList(event.event, event.content); + } + + updateSubscriberFromChild(newSubscriber: Subscriber) { + const oldSubscriber = this.subscribers.filter(sub => { + return sub.stream.streamId === newSubscriber.stream.streamId; + })[0]; + this.subscribers[this.subscribers.indexOf(oldSubscriber)] = newSubscriber; + } + } diff --git a/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.html b/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.html index 00ef80a9..f8eacfa5 100644 --- a/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.html +++ b/openvidu-testapp/src/app/components/test-sessions/test-sessions.component.html @@ -16,7 +16,7 @@
- +
diff --git a/openvidu-testapp/src/app/components/video/ov-video.component.ts b/openvidu-testapp/src/app/components/video/ov-video.component.ts new file mode 100644 index 00000000..80b17a50 --- /dev/null +++ b/openvidu-testapp/src/app/components/video/ov-video.component.ts @@ -0,0 +1,28 @@ +import { Component, Input, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { StreamManager } from 'openvidu-browser'; + +@Component({ + selector: 'app-ov-video', + template: '' +}) +export class OpenViduVideoComponent implements AfterViewInit { + + @ViewChild('videoElement') elementRef: ElementRef; + + @Input() poster = ''; + + _streamManager: StreamManager; + + ngAfterViewInit() { + this._streamManager.addVideoElement(this.elementRef.nativeElement); + } + + @Input() + set streamManager(streamManager: StreamManager) { + this._streamManager = streamManager; + if (!!this.elementRef) { + this._streamManager.addVideoElement(this.elementRef.nativeElement); + } + } + +} diff --git a/openvidu-testapp/src/app/components/video/video.component.css b/openvidu-testapp/src/app/components/video/video.component.css new file mode 100644 index 00000000..0eed51fb --- /dev/null +++ b/openvidu-testapp/src/app/components/video/video.component.css @@ -0,0 +1,98 @@ +app-ov-video { + float: left; + height: 90px; +} + +.data-node { + width: 120px; + height: 90px; + float: left; + position: relative; + margin-left: -120px; + margin-top: 0; +} + +p { + display: inline-flex; + vertical-align: top; + margin: 0; + background: #ffffff; + padding-left: 5px; + padding-right: 5px; + color: #797979; + font-weight: 100; + font-size: 14px; +} + +.material-icons { + font-size: 17px; + width: 17px; + height: 17px; + line-height: 20px; +} + +.video-btn { + border: none; + background: rgba(255, 255, 255, 0.75); + cursor: pointer; + padding: 0; + height: 20px; + float: left; +} + +.video-btn:hover { + color: #4d4d4d; +} + +.video-btn.top-row { + margin-top: 0; + display: inline-flex; + float: right; +} + +.rec-btn { + float: right; + color: #ac0000; +} + +.rec-btn:hover { + color: #ac000082; +} + +.events-btn { + float: right; +} + +.top-left-rounded { + border-top-left-radius: 2px; +} + +.top-right-rounded { + border-top-right-radius: 2px; +} + +.bottom-left-rounded { + border-bottom-left-radius: 2px; +} + +.bottom-right-rounded { + border-bottom-right-radius: 2px; +} + +.grey-background { + background-color: #4d4d4d +} + +.top-div { + position: absolute; + top: 0; + right: 0; + width: 100%; +} + +.bottom-div { + position: absolute; + bottom: 0; + height: 20px; + width: 100%; +} \ No newline at end of file diff --git a/openvidu-testapp/src/app/components/video/video.component.html b/openvidu-testapp/src/app/components/video/video.component.html new file mode 100644 index 00000000..60d07f11 --- /dev/null +++ b/openvidu-testapp/src/app/components/video/video.component.html @@ -0,0 +1,58 @@ +
+ +
+
+ +
+
+ + + + + + +
+
+
+
+

{{streamManager.stream.connection.data}}

+ +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/openvidu-testapp/src/app/components/video/video.component.ts b/openvidu-testapp/src/app/components/video/video.component.ts new file mode 100644 index 00000000..c0056ab9 --- /dev/null +++ b/openvidu-testapp/src/app/components/video/video.component.ts @@ -0,0 +1,569 @@ +import { Component, Input, OnInit, ViewChild, ElementRef, Output, EventEmitter, OnDestroy } from '@angular/core'; +import { MatDialog, MatDialogRef } from '@angular/material'; + +import { + StreamManager, + StreamManagerEvent, + VideoElementEvent, + Subscriber, + Session, + LocalRecorder, + OpenVidu, + Publisher, + StreamEvent, + VideoInsertMode +} from 'openvidu-browser'; + +import { EventsDialogComponent } from '../dialogs/events-dialog.component'; +import { MuteSubscribersService } from '../../services/mute-subscribers.service'; +import { Subscription } from 'rxjs/Subscription'; +import { LocalRecordingDialogComponent } from '../dialogs/local-recording-dialog.component'; +import { ExtensionDialogComponent } from '../dialogs/extension-dialog.component'; +import { OpenViduVideoComponent } from './ov-video.component'; + +@Component({ + selector: 'app-video', + templateUrl: './video.component.html', + styleUrls: ['./video.component.css'] +}) +export class VideoComponent implements OnInit, OnDestroy { + + @Input() streamManager: StreamManager; + @Input() OV: OpenVidu; + @Input() eventCollection: any; + + @Output() updateEventListInParent = new EventEmitter(); + @Output() reSubbed = new EventEmitter(); + + subbed = true; + subbedVideo = true; + subbedAudio = true; + recorder = undefined; + recording = false; + recordingPaused = false; + videoElement = undefined; + showButtons = false; + videoClasses = ''; + videoPoster = ''; + + unpublished = false; + publisherChanged = false; + audioMuted = false; + videoMuted = false; + sendAudio = true; + sendVideo = true; + sendAudioChange = false; + sendVideoChange = false; + optionsVideo = ''; + + private muteSubscribersSubscription: Subscription; + + // Icons + pubSubIcon = 'stop'; + pubSubVideoIcon = 'videocam'; + pubSubAudioIcon = 'mic'; + recordIcon = 'fiber_manual_record'; + pauseRecordIcon = ''; + + constructor(private dialog: MatDialog, private muteSubscribersService: MuteSubscribersService + ) { } + + ngOnInit() { + + if (this.streamManager.remote) { + // Init subscriber events + this.eventCollection = { + videoElementCreated: true, + videoElementDestroyed: true, + streamPlaying: true + }; + this.updateSubscriberEvents({ + videoElementCreated: false, + videoElementDestroyed: false, + streamPlaying: false + }); + + } else { + // Init publisher events + this.eventCollection = { + videoElementCreated: true, + videoElementDestroyed: true, + streamPlaying: true, + accessAllowed: true, + accessDenied: true, + accessDialogOpened: true, + accessDialogClosed: true, + streamCreated: true, + streamDestroyed: true + }; + this.updatePublisherEvents( + this.streamManager, + { + videoElementCreated: false, + videoElementDestroyed: false, + streamPlaying: false, + accessAllowed: false, + accessDenied: false, + accessDialogOpened: false, + accessDialogClosed: false, + streamCreated: false, + streamDestroyed: false + }); + this.sendAudio = this.streamManager.stream.hasAudio; + this.sendVideo = this.streamManager.stream.hasVideo; + this.optionsVideo = this.streamManager.stream.typeOfVideo; + } + + this.muteSubscribersSubscription = this.muteSubscribersService.mutedEvent$.subscribe(muteOrUnmute => { + this.streamManager.videos.forEach(v => { + v.video.muted = muteOrUnmute; + }); + }); + } + + ngOnDestroy() { + if (!!this.recorder) { + this.recorder.clean(); + } + if (!!this.muteSubscribersSubscription) { this.muteSubscribersSubscription.unsubscribe(); } + } + + subUnsub() { + const subscriber: Subscriber = this.streamManager; + if (this.subbed) { + this.streamManager.stream.session.unsubscribe(subscriber); + this.restartRecorder(); + + this.pubSubVideoIcon = ''; + this.pubSubAudioIcon = ''; + this.recordIcon = ''; + this.pauseRecordIcon = ''; + this.pubSubIcon = 'play_arrow'; + this.subbedVideo = false; + this.subbedAudio = false; + } else { + const oldValues = { + videoElementCreated: this.eventCollection.videoElementCreated, + videoElementDestroyed: this.eventCollection.videoElementDestroyed, + streamPlaying: this.eventCollection.streamPlaying + }; + this.streamManager = this.streamManager.stream.session.subscribe(subscriber.stream, undefined); + this.reSubbed.emit(this.streamManager); + + this.pubSubVideoIcon = 'videocam'; + this.pubSubAudioIcon = 'mic'; + this.recordIcon = 'fiber_manual_record'; + this.pauseRecordIcon = ''; + this.pubSubIcon = 'stop'; + this.subbedVideo = true; + this.subbedAudio = true; + + this.updateSubscriberEvents(oldValues); + } + this.subbed = !this.subbed; + } + + subUnsubVideo(connectionId: string) { + const subscriber: Subscriber = this.streamManager; + this.subbedVideo = !this.subbedVideo; + subscriber.subscribeToVideo(this.subbedVideo); + this.pubSubVideoIcon = this.subbedVideo ? 'videocam' : 'videocam_off'; + } + + subUnsubAudio(connectionId: string) { + const subscriber: Subscriber = this.streamManager; + this.subbedAudio = !this.subbedAudio; + subscriber.subscribeToAudio(this.subbedAudio); + this.pubSubAudioIcon = this.subbedAudio ? 'mic' : 'mic_off'; + } + + pubUnpub() { + const publisher: Publisher = this.streamManager; + if (this.unpublished) { + this.streamManager.stream.session.publish(publisher) + .then(() => { + console.log(publisher); + }) + .catch(e => { + console.error(e); + }); + } else { + this.streamManager.stream.session.unpublish(publisher); + } + this.unpublished = !this.unpublished; + this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop'; + } + + pubUnpubVideo() { + const publisher: Publisher = this.streamManager; + publisher.publishVideo(this.videoMuted); + this.videoMuted = !this.videoMuted; + this.pubSubVideoIcon = this.videoMuted ? 'videocam_off' : 'videocam'; + } + + pubUnpubAudio() { + const publisher: Publisher = this.streamManager; + publisher.publishAudio(this.audioMuted); + this.audioMuted = !this.audioMuted; + this.pubSubAudioIcon = this.audioMuted ? 'mic_off' : 'mic'; + } + + changePub() { + let screenChange; + if (!this.publisherChanged) { + if (this.sendAudio && !this.sendVideo) { + this.sendAudioChange = false; + this.sendVideoChange = true; + screenChange = false; + } else if (!this.sendAudio && this.sendVideo) { + this.sendAudioChange = true; + this.sendVideoChange = false; + } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'CAMERA') { + this.sendAudioChange = false; + this.sendVideoChange = true; + screenChange = true; + } else if (this.sendAudio && this.sendVideo && this.optionsVideo === 'SCREEN') { + this.sendAudioChange = false; + this.sendVideoChange = true; + screenChange = false; + } + } else { + this.sendAudioChange = this.sendAudio; + this.sendVideoChange = this.sendVideo; + screenChange = this.optionsVideo === 'SCREEN' ? true : false; + } + + this.audioMuted = false; + this.videoMuted = false; + this.unpublished = false; + + const otherPublisher = this.OV.initPublisher( + undefined, + { + audioSource: this.sendAudioChange ? undefined : false, + videoSource: this.sendVideoChange ? (screenChange ? 'screen' : undefined) : false, + publishAudio: (!this.publisherChanged) ? true : !this.audioMuted, + publishVideo: (!this.publisherChanged) ? true : !this.videoMuted, + resolution: '640x480', + frameRate: 30, + insertMode: VideoInsertMode.APPEND + }, + (err) => { + if (err) { + console.warn(err); + if (err.name === 'SCREEN_EXTENSION_NOT_INSTALLED') { + this.dialog.open(ExtensionDialogComponent, { + data: { url: err.message }, + disableClose: true, + width: '250px' + }); + } + } + }); + this.updatePublisherEvents(otherPublisher, { + videoElementCreated: !this.eventCollection.videoElementCreated, + videoElementDestroyed: !this.eventCollection.videoElementDestroyed, + streamPlaying: !this.eventCollection.streamPlaying, + accessAllowed: !this.eventCollection.accessAllowed, + accessDenied: !this.eventCollection.accessDenied, + accessDialogOpened: !this.eventCollection.accessDialogOpened, + accessDialogClosed: !this.eventCollection.accessDialogClosed, + streamCreated: !this.eventCollection.streamCreated, + streamDestroyed: !this.eventCollection.streamDestroyed + }); + + otherPublisher.once('accessAllowed', () => { + if (!this.unpublished) { + this.streamManager.stream.session.unpublish(this.streamManager); + this.streamManager = otherPublisher; + } + this.streamManager.stream.session.publish(otherPublisher).then(() => { + console.log(this.streamManager); + }); + }); + + this.publisherChanged = !this.publisherChanged; + } + + updateSubscriberEvents(oldValues) { + const sub: Subscriber = this.streamManager; + + if (this.eventCollection.videoElementCreated) { + if (!oldValues.videoElementCreated) { + sub.on('videoElementCreated', (event: VideoElementEvent) => { + if (!sub.stream.hasVideo) { + this.videoClasses = 'grey-background'; + this.videoPoster = 'assets/images/volume.png'; + } else { + this.videoClasses = ''; + this.videoPoster = ''; + } + this.updateEventListInParent.emit({ + event: 'videoElementCreated', + content: event.element.id + }); + }); + } + } else { + sub.off('videoElementCreated'); + } + + if (this.eventCollection.videoElementDestroyed) { + if (!oldValues.videoElementDestroyed) { + sub.on('videoElementDestroyed', (event: VideoElementEvent) => { + this.showButtons = false; + this.updateEventListInParent.emit({ + event: 'videoElementDestroyed', + content: event.element.id + }); + }); + } + } else { + sub.off('videoElementDestroyed'); + } + + if (this.eventCollection.streamPlaying) { + if (!oldValues.streamPlaying) { + sub.on('streamPlaying', (event: StreamManagerEvent) => { + this.showButtons = true; + this.updateEventListInParent.emit({ + event: 'streamPlaying', + content: this.streamManager.stream.streamId + }); + }); + } + } else { + sub.off('streamPlaying'); + } + } + + updatePublisherEvents(pub: Publisher, oldValues: any) { + if (this.eventCollection.videoElementCreated) { + if (!oldValues.videoElementCreated) { + pub.on('videoElementCreated', (event: VideoElementEvent) => { + if (!pub.stream.hasVideo) { + this.videoClasses = 'grey-background'; + this.videoPoster = 'assets/images/volume.png'; + } else { + this.videoClasses = ''; + this.videoPoster = ''; + } + this.updateEventListInParent.emit({ + event: 'videoElementCreated', + content: event.element.id + }); + }); + } + } else { + pub.off('videoElementCreated'); + } + + if (this.eventCollection.accessAllowed) { + if (!oldValues.accessAllowed) { + pub.on('accessAllowed', (e) => { + this.updateEventListInParent.emit({ + event: 'accessAllowed', + content: '' + }); + }); + } + } else { + pub.off('accessAllowed'); + } + + if (this.eventCollection.accessDenied) { + if (!oldValues.accessDenied) { + pub.on('accessDenied', (e) => { + this.updateEventListInParent.emit({ + event: 'accessDenied', + content: '' + }); + }); + } + } else { + pub.off('accessDenied'); + } + + if (this.eventCollection.accessDialogOpened) { + if (!oldValues.accessDialogOpened) { + pub.on('accessDialogOpened', (e) => { + this.updateEventListInParent.emit({ + event: 'accessDialogOpened', + content: '' + }); + }); + } + } else { + pub.off('accessDialogOpened'); + } + + if (this.eventCollection.accessDialogClosed) { + if (!oldValues.accessDialogClosed) { + pub.on('accessDialogClosed', (e) => { + this.updateEventListInParent.emit({ + event: 'accessDialogClosed', + content: '' + }); + }); + } + } else { + pub.off('accessDialogClosed'); + } + + if (this.eventCollection.streamCreated) { + if (!oldValues.streamCreated) { + pub.on('streamCreated', (e: StreamEvent) => { + this.updateEventListInParent.emit({ + event: 'streamCreated', + content: e.stream.streamId + }); + }); + } + } else { + pub.off('streamCreated'); + } + + if (this.eventCollection.streamDestroyed) { + if (!oldValues.streamDestroyed) { + pub.on('streamDestroyed', (e: StreamEvent) => { + this.updateEventListInParent.emit({ + event: 'streamDestroyed', + content: e.stream.streamId + }); + }); + } + } else { + pub.off('streamDestroyed'); + } + + if (this.eventCollection.videoElementDestroyed) { + if (!oldValues.videoElementDestroyed) { + pub.on('videoElementDestroyed', (e: VideoElementEvent) => { + this.updateEventListInParent.emit({ + event: 'videoElementDestroyed', + content: '(Publisher)' + }); + }); + } + } else { + pub.off('videoElementDestroyed'); + } + + if (this.eventCollection.streamPlaying) { + if (!oldValues.streamPlaying) { + pub.on('streamPlaying', (event: StreamManagerEvent) => { + this.showButtons = true; + this.updateEventListInParent.emit({ + event: 'streamPlaying', + content: this.streamManager.stream.streamId + }); + }); + } + } else { + pub.off('streamPlaying'); + } + } + + openSubscriberEventsDialog() { + const oldValues = { + videoElementCreated: this.eventCollection.videoElementCreated, + videoElementDestroyed: this.eventCollection.videoElementDestroyed, + streamPlaying: this.eventCollection.streamPlaying + }; + const dialogRef = this.dialog.open(EventsDialogComponent, { + data: { + eventCollection: this.eventCollection, + target: 'Subscriber' + }, + width: '280px', + autoFocus: false, + disableClose: true + }); + dialogRef.afterClosed().subscribe((result) => { + this.updateSubscriberEvents(oldValues); + }); + } + + + openPublisherEventsDialog() { + const oldValues = { + videoElementCreated: this.eventCollection.videoElementCreated, + videoElementDestroyed: this.eventCollection.videoElementDestroyed, + streamPlaying: this.eventCollection.streamPlaying, + accessAllowed: this.eventCollection.accessAllowed, + accessDenied: this.eventCollection.accessDenied, + accessDialogOpened: this.eventCollection.accessDialogOpened, + accessDialogClosed: this.eventCollection.accessDialogClosed, + streamCreated: this.eventCollection.streamCreated, + streamDestroyed: this.eventCollection.streamDestroyed + }; + const dialogRef = this.dialog.open(EventsDialogComponent, { + data: { + eventCollection: this.eventCollection, + target: 'Publisher' + }, + width: '280px', + autoFocus: false, + disableClose: true + }); + dialogRef.afterClosed().subscribe((result) => { + this.updatePublisherEvents(this.streamManager, oldValues); + }); + } + + record(): void { + if (!this.recording) { + this.recorder = this.OV.initLocalRecorder(this.streamManager.stream); + this.recorder.record(); + this.recording = true; + this.recordIcon = 'stop'; + this.pauseRecordIcon = 'pause'; + } else { + this.recorder.stop() + .then(() => { + let dialogRef: MatDialogRef; + dialogRef = this.dialog.open(LocalRecordingDialogComponent, { + disableClose: true, + data: { + recorder: this.recorder + } + }); + dialogRef.componentInstance.myReference = dialogRef; + + dialogRef.afterOpen().subscribe(() => { + this.muteSubscribersService.updateMuted(true); + this.recorder.preview('recorder-preview').controls = true; + }); + dialogRef.afterClosed().subscribe(() => { + this.muteSubscribersService.updateMuted(false); + this.restartRecorder(); + }); + }) + .catch((error) => { + console.error('Error stopping LocalRecorder: ' + error); + }); + } + } + + pauseRecord(): void { + if (!this.recordingPaused) { + this.recorder.pause(); + this.pauseRecordIcon = 'play_arrow'; + } else { + this.recorder.resume(); + this.pauseRecordIcon = 'pause'; + } + this.recordingPaused = !this.recordingPaused; + } + + private restartRecorder() { + this.recording = false; + this.recordingPaused = false; + this.recordIcon = 'fiber_manual_record'; + this.pauseRecordIcon = ''; + if (!!this.recorder) { + this.recorder.clean(); + } + } + +} diff --git a/openvidu-testapp/src/assets/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 b/openvidu-testapp/src/assets/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 new file mode 100644 index 00000000..af37c6a9 Binary files /dev/null and b/openvidu-testapp/src/assets/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 differ diff --git a/openvidu-testapp/src/index.html b/openvidu-testapp/src/index.html index 27abaf4d..4bb6d2cc 100644 --- a/openvidu-testapp/src/index.html +++ b/openvidu-testapp/src/index.html @@ -9,11 +9,6 @@ - - - - diff --git a/openvidu-testapp/src/material-icons.css b/openvidu-testapp/src/material-icons.css new file mode 100644 index 00000000..8cbb0e3d --- /dev/null +++ b/openvidu-testapp/src/material-icons.css @@ -0,0 +1,23 @@ +/* fallback */ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(/assets/fonts/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'); + } + + .material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; + } \ No newline at end of file diff --git a/openvidu-testapp/src/styles.css b/openvidu-testapp/src/styles.css index 74c44f00..46f772ae 100644 --- a/openvidu-testapp/src/styles.css +++ b/openvidu-testapp/src/styles.css @@ -38,67 +38,6 @@ button { line-height: 15px !important; } -.video-container video { - float: left; -} - -.video-container div.data-node { - width: 120px; - height: 90px; - float: left; - position: relative; - margin-left: -120px; - margin-top: 0; -} - -.video-container p { - margin-top: 0; - width: fit-content; - background: #ffffff; - padding-left: 5px; - padding-right: 5px; - color: #797979; - font-weight: 100; - font-size: 14px; - border-bottom-right-radius: 2px; -} - -.video-container div.data-node .sub-btn { - outline: 0; - border: none; - background: rgba(255, 255, 255, 0.75); - cursor: pointer; - padding: 0; - margin-top: 40px; - border-top-right-radius: 2px; -} - -.video-container div.data-node .sub-btn:hover { - color: #4d4d4d; -} - -.video-container div.data-node .rec-btn { - float: right; - border-top-right-radius: 0; - border-top-left-radius: 2px; - color: #ac0000; -} - -.video-container div.data-node .rec-btn:hover { - color: #ac000082; -} - -.video-container div.data-node .rec-btn.publisher-rec-btn { - margin-top: 70px; -} - -.video-container div.data-node .material-icons { - font-size: 17px; - width: 17px; - height: 17px; - line-height: 20px; -} - .mat-expansion-panel-body { font-size: 9.5px !important; padding: 0 9px 0px !important;