diff --git a/openvidu-browser/ts/OpenVidu/OpenVidu.ts b/openvidu-browser/ts/OpenVidu/OpenVidu.ts index 73c573fe..de116f6d 100644 --- a/openvidu-browser/ts/OpenVidu/OpenVidu.ts +++ b/openvidu-browser/ts/OpenVidu/OpenVidu.ts @@ -58,6 +58,7 @@ export class OpenVidu { initPublisher(parentId: string, cameraOptions?: any, callback?: Function): any { if (this.checkSystemRequirements()) { + this.openVidu.storedPublisherOptions = cameraOptions; let publisher: Publisher; if (cameraOptions != null) { @@ -81,8 +82,8 @@ export class OpenVidu { return publisher; } else { + publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true); if (adapter.browserDetails.browser === 'firefox' && adapter.browserDetails.version >= 52) { - publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true); screenSharingAuto.getScreenId((error, sourceId, screenConstraints) => { cameraOptions = { sendAudio: cameraOptions.audio, @@ -156,7 +157,6 @@ export class OpenVidu { console.error('getScreenId error', error); return; }); - publisher = new Publisher(this.openVidu.initPublisherScreen(parentId, callback), parentId, true); console.info("'Publisher' initialized"); return publisher; } else { diff --git a/openvidu-browser/ts/OpenVidu/Publisher.ts b/openvidu-browser/ts/OpenVidu/Publisher.ts index fd398232..6331b905 100644 --- a/openvidu-browser/ts/OpenVidu/Publisher.ts +++ b/openvidu-browser/ts/OpenVidu/Publisher.ts @@ -5,14 +5,14 @@ * * stream.hasAudio(); stream.hasVideo(); stream.hasData(); */ -import { Stream, StreamOptions, VideoOptions } from '../OpenViduInternal/Stream'; +import { Stream, StreamOptions } from '../OpenViduInternal/Stream'; import { Session } from './Session'; import EventEmitter = require('wolfy87-eventemitter'); export class Publisher { - private ee = new EventEmitter(); + ee = new EventEmitter(); accessAllowed = false; element: Element; @@ -25,13 +25,9 @@ export class Publisher { this.stream = stream; this.isScreenRequested = isScreenRequested; - this.stream.addEventListener('camera-access-changed', (event) => { - this.accessAllowed = event.accessAllowed; - if (this.accessAllowed) { - this.ee.emitEvent('accessAllowed'); - } else { - this.ee.emitEvent('accessDenied'); - } + // Listens to the deactivation of the default behaviour upon the deletion of a Stream object + this.ee.addListener('stream-destroyed-default', event => { + event.stream.removeVideo(); }); if (document.getElementById(parentId) != null) { @@ -72,7 +68,6 @@ export class Publisher { this.ee.emitEvent('streamCreated', [{ stream: this.stream }]); } else { this.stream.addEventListener('stream-created-by-publisher', () => { - console.warn('Publisher emitting streamCreated'); this.ee.emitEvent('streamCreated', [{ stream: this.stream }]); }); } @@ -83,7 +78,7 @@ export class Publisher { element: this.stream.getVideoElement() }]); } else { - this.stream.addOnceEventListener('video-element-created-by-stream', (element) => { + this.stream.addEventListener('video-element-created-by-stream', (element) => { this.id = element.id; this.ee.emitEvent('videoElementCreated', [{ element: element.element @@ -102,7 +97,7 @@ export class Publisher { element: this.stream.getVideoElement() }]); } else { - this.stream.addOnceEventListener('video-is-playing', (element) => { + this.stream.addEventListener('video-is-playing', (element) => { this.ee.emitEvent('videoPlaying', [{ element: element.element }]); @@ -120,7 +115,7 @@ export class Publisher { element: this.stream.getVideoElement() }]); } else { - this.stream.addOnceEventListener('remote-video-is-playing', (element) => { + this.stream.addEventListener('remote-video-is-playing', (element) => { this.ee.emitEvent('remoteVideoPlaying', [{ element: element.element }]); diff --git a/openvidu-browser/ts/OpenVidu/Session.ts b/openvidu-browser/ts/OpenVidu/Session.ts index 68a1b884..e146b850 100644 --- a/openvidu-browser/ts/OpenVidu/Session.ts +++ b/openvidu-browser/ts/OpenVidu/Session.ts @@ -76,19 +76,37 @@ export class Session { } publish(publisher: Publisher) { - if (publisher.isScreenRequested && !publisher.stream.isScreenRequestedReady) { - publisher.stream.addOnceEventListener('screen-ready', () => { - publisher.session = this; - publisher.stream.publish(); - }); - } else { - publisher.session = this; - publisher.stream.publish(); + if (!publisher.stream.isPublisherPublished) { // 'Session.unpublish(Publisher)' has NOT been called + if (publisher.isScreenRequested) { // Screen sharing Publisher + if (!publisher.stream.isScreenRequestedReady) { // Screen video stream is not available yet + publisher.stream.addOnceEventListener('screen-ready', () => { + this.streamPublish(publisher); + }); + } else { // // Screen video stream is already available + this.streamPublish(publisher); + } + } else { // Audio-Video Publisher + this.streamPublish(publisher); + } + } else { // 'Session.unpublish(Publisher)' has been called + let mypublisher = this.openVidu.initPublisher(publisher.stream.getParentId(), this.openVidu.openVidu.storedPublisherOptions); + if (mypublisher.isScreenRequested && !mypublisher.stream.isScreenRequestedReady) { // Screen sharing Publisher and video stream not available yet + mypublisher.stream.addOnceEventListener('screen-ready', () => { + this.streamPublish(mypublisher); + }); + } else { // Video stream already available + this.streamPublish(mypublisher); + } } } + + private streamPublish(publisher: Publisher) { + publisher.session = this; + publisher.stream.publish(); + } unpublish(publisher: Publisher) { - this.session.unpublish(publisher.stream); + this.session.unpublish(publisher); } on(eventName: string, callback) { @@ -144,7 +162,7 @@ export class Session { signalMessage['data'] = signal.data ? signal.data : ''; signalMessage['type'] = signal.type ? signal.type : ''; - this.openVidu.openVidu.sendMessage(this.sessionId, this.connection.connectionId, JSON.stringify(signalMessage)); + this.openVidu.openVidu.sendMessage(JSON.stringify(signalMessage)); } } diff --git a/openvidu-browser/ts/OpenVidu/Subscriber.ts b/openvidu-browser/ts/OpenVidu/Subscriber.ts index 0a82bde6..0d219664 100644 --- a/openvidu-browser/ts/OpenVidu/Subscriber.ts +++ b/openvidu-browser/ts/OpenVidu/Subscriber.ts @@ -1,4 +1,4 @@ -import { Stream, StreamOptions, VideoOptions } from '../OpenViduInternal/Stream'; +import { Stream, StreamOptions } from '../OpenViduInternal/Stream'; import EventEmitter = require('wolfy87-eventemitter'); diff --git a/openvidu-browser/ts/OpenViduInternal/Connection.ts b/openvidu-browser/ts/OpenViduInternal/Connection.ts index 0d7923e1..d82e6b59 100644 --- a/openvidu-browser/ts/OpenViduInternal/Connection.ts +++ b/openvidu-browser/ts/OpenViduInternal/Connection.ts @@ -18,7 +18,7 @@ export class Connection { public data: string; public creationTime: number; private streams: ObjMap = {}; - private streamsOpts: StreamOptions[] = []; + private streamsOpts: StreamOptions; constructor( private openVidu: OpenViduInternal, private local: boolean, private room: SessionInternal, private options?: ConnectionOptions ) { @@ -41,6 +41,12 @@ export class Connection { this.room.getStreams()[stream.streamId] = stream; } + removeStream( key: string ) { + delete this.streams[key]; + delete this.room.getStreams()[key]; + delete this.streamsOpts; + } + getStreams() { return this.streams; } @@ -88,7 +94,7 @@ export class Connection { let stream = new Stream(this.openVidu, false, this.room, streamOpts ); this.addStream( stream ); - this.streamsOpts.push( streamOpts ); + this.streamsOpts = streamOpts; } console.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + "] is now configured for receiving Streams with options: ", this.streamsOpts ); diff --git a/openvidu-browser/ts/OpenViduInternal/OpenViduInternal.ts b/openvidu-browser/ts/OpenViduInternal/OpenViduInternal.ts index a9b15b1b..6ca08d08 100644 --- a/openvidu-browser/ts/OpenViduInternal/OpenViduInternal.ts +++ b/openvidu-browser/ts/OpenViduInternal/OpenViduInternal.ts @@ -35,6 +35,7 @@ export class OpenViduInternal { constructor() { }; + storedPublisherOptions: any; /* NEW METHODS */ initSession(sessionId) { @@ -66,7 +67,9 @@ export class OpenViduInternal { } initPublisherScreen(parentId: string, callback?): Stream { - this.camera = new Stream(this, true, this.session, 'screen-options'); + if (!this.camera) { + this.camera = new Stream(this, true, this.session, 'screen-options'); + } this.camera.addOnceEventListener('can-request-screen', () => { this.camera.requestCameraAccess((error, camera) => { if (error) { @@ -121,14 +124,6 @@ export class OpenViduInternal { return videoElement; } - initPublisher(cameraOptions: any, callback) { - this.getCamera(cameraOptions); - this.camera.requestCameraAccess((error, camera) => { - if (error) callback(error); - else callback(undefined); - }); - } - getLocalStream() { return this.camera; } @@ -187,26 +182,18 @@ export class OpenViduInternal { //notifications participantJoined: this.onParticipantJoined.bind(this), participantPublished: this.onParticipantPublished.bind(this), - participantUnpublished: this.onParticipantLeft.bind(this), + participantUnpublished: this.onParticipantUnpublished.bind(this), participantLeft: this.onParticipantLeft.bind(this), participantEvicted: this.onParticipantEvicted.bind(this), sendMessage: this.onNewMessage.bind(this), iceCandidate: this.iceCandidateEvent.bind(this), mediaError: this.onMediaError.bind(this), - custonNotification: this.customNotification.bind(this) } }; this.jsonRpcClient = new RpcBuilder.clients.JsonRpcClient(config); } - - private customNotification(params) { - if (this.isRoomAvailable()) { - this.session.emitEvent("custom-message-received", [{ params: params }]); - } - } - private connectCallback(error) { if (error) { this.callback(error); @@ -258,6 +245,12 @@ export class OpenViduInternal { } } + private onParticipantUnpublished(params) { + if (this.isRoomAvailable()) { + this.session.onParticipantUnpublished(params); + } + } + private onParticipantLeft(params) { if (this.isRoomAvailable()) { this.session.onParticipantLeft(params); @@ -358,11 +351,9 @@ export class OpenViduInternal { }; //CHAT - sendMessage(room, user, message) { + sendMessage(message) { this.sendRequest('sendMessage', { - message: message, - userMessage: user, - roomMessage: room + message: message }, function (error, response) { if (error) { console.error(error); @@ -370,11 +361,6 @@ export class OpenViduInternal { }); }; - sendCustomRequest(params, callback) { - this.sendRequest('customRequest', params, callback); - }; - - toggleLocalVideoTrack(activate: boolean) { diff --git a/openvidu-browser/ts/OpenViduInternal/SessionInternal.ts b/openvidu-browser/ts/OpenViduInternal/SessionInternal.ts index 4b366771..db99285f 100644 --- a/openvidu-browser/ts/OpenViduInternal/SessionInternal.ts +++ b/openvidu-browser/ts/OpenViduInternal/SessionInternal.ts @@ -2,6 +2,7 @@ import { Stream } from './Stream'; import { OpenViduInternal } from './OpenViduInternal'; import { Connection, ConnectionOptions } from './Connection'; import EventEmitter = require('wolfy87-eventemitter'); +import { Publisher } from '../OpenVidu/Publisher'; const SECRET_PARAM = '?secret='; @@ -177,10 +178,6 @@ export class SessionInternal { } }); } - - publish() { - this.openVidu.getCamera().publish(); - } /* NEW METHODS */ @@ -244,18 +241,19 @@ export class SessionInternal { stream.subscribe(); } - unsubscribe(stream) { - console.info("Unsubscribing from " + stream.streamId); + unsubscribe(stream: Stream) { + console.info("Unsubscribing from " + stream.connection.connectionId); this.openVidu.sendRequest('unsubscribeFromVideo', { - sender: stream.streamId + sender: stream.connection.connectionId }, - function (error, response) { - if (error) { - console.error("Error unsubscribing from Subscriber", error); - } else { - console.info("Unsubscribed correctly from " + stream.streamId); - } - }); + (error, response) => { + if (error) { + console.error("Error unsubscribing from Subscriber", error); + } else { + console.info("Unsubscribed correctly from " + stream.connection.connectionId); + } + stream.dispose(); + }); } onParticipantPublished(options) { @@ -293,11 +291,44 @@ export class SessionInternal { stream.subscribe(); } this.ee.emitEvent('streamCreated', [{ stream }]); + // Adding the remote stream to the OpenVidu object this.openVidu.getRemoteStreams().push(stream); } } + onParticipantUnpublished(msg) { + let connection: Connection = this.participants[msg.name]; + + if (connection !== undefined) { + + let streams = connection.getStreams(); + for (let key in streams) { + this.ee.emitEvent('streamDestroyed', [{ + stream: streams[key], + preventDefault: () => { this.ee.removeEvent('stream-destroyed-default'); } + }]); + this.ee.emitEvent('stream-destroyed-default', [{ + stream: streams[key] + }]); + + // Deleting the removed stream from the OpenVidu object + let index = this.openVidu.getRemoteStreams().indexOf(streams[key]); + let stream = this.openVidu.getRemoteStreams()[index]; + + + stream.dispose(); + this.openVidu.getRemoteStreams().splice(index, 1); + delete this.streams[stream.streamId]; + + } + } else { + console.warn("Participant " + msg.name + + " unknown. Participants: " + + JSON.stringify(this.participants)); + } + } + onParticipantJoined(msg) { let connection = new Connection(this.openVidu, false, this, msg); @@ -325,7 +356,7 @@ export class SessionInternal { onParticipantLeft(msg) { - let connection = this.participants[msg.name]; + let connection: Connection = this.participants[msg.name]; if (connection !== undefined) { delete this.participants[msg.name]; @@ -516,21 +547,22 @@ export class SessionInternal { } } - unpublish(stream: Stream) { + unpublish(publisher: Publisher) { - let connection = stream.getParticipant(); - if (!connection) { - console.error("Stream to disconnect has no participant", stream); + let stream = publisher.stream; + + if (!stream.connection) { + console.error("The associated Connection object of this Publisher is null", stream); return; - } + } else if (stream.connection !== this.localParticipant) { + console.error("The associated Connection object of this Publisher is not your local Connection." + + "Only moderators can force unpublish on remote Streams via 'forceUnpublish' method", stream); + return; + } else { + stream.dispose(); - if (connection === this.localParticipant) { + console.info("Unpublishing local media (" + stream.connection.connectionId + ")"); - delete this.participants[connection.connectionId]; - connection.dispose(); - - console.info("Unpublishing my media (I'm " + connection.connectionId + ")"); - delete this.localParticipant; this.openVidu.sendRequest('unpublishVideo', function (error, response) { if (error) { console.error(error); @@ -538,6 +570,17 @@ export class SessionInternal { console.info("Media unpublished correctly"); } }); + + stream.isReadyToPublish = false; + stream.isScreenRequestedReady = false; + + publisher.ee.emitEvent('streamDestroyed', [{ + stream: publisher.stream, + preventDefault: () => { this.ee.removeEvent('stream-destroyed-default'); } + }]); + publisher.ee.emitEvent('stream-destroyed-default', [{ + stream: publisher.stream + }]); } } diff --git a/openvidu-browser/ts/OpenViduInternal/Stream.ts b/openvidu-browser/ts/OpenViduInternal/Stream.ts index 331626e7..5bb4de6b 100644 --- a/openvidu-browser/ts/OpenViduInternal/Stream.ts +++ b/openvidu-browser/ts/OpenViduInternal/Stream.ts @@ -45,11 +45,6 @@ export interface StreamOptions { mediaConstraints: any; } -export interface VideoOptions { - thumb: string; - video: HTMLVideoElement; -} - export class Stream { public connection: Connection; @@ -62,8 +57,6 @@ export class Stream { private wrStream: MediaStream; private wp: any; private video: HTMLVideoElement; - private videoElements: VideoOptions[] = []; - private elements: HTMLDivElement[] = []; private speechEvent: any; private recvVideo: boolean; private recvAudio: boolean; @@ -79,7 +72,7 @@ export class Stream { private activeAudio = true; private activeVideo = true; - private videoSrcObject: MediaStream | null; + private videoSrcObject: MediaStream; private parentId: string; public isReadyToPublish: boolean = false; public isPublisherPublished: boolean = false; @@ -122,15 +115,18 @@ export class Stream { removeVideo(); removeVideo(parentElement?) { - if (typeof parentElement === "string") { - document.getElementById(parentElement)!.removeChild(this.video); - } else if (parentElement instanceof Element) { - parentElement.removeChild(this.video); - } - else if (!parentElement) { - if (document.getElementById(this.parentId)) { - document.getElementById(this.parentId)!.removeChild(this.video); + if (this.video) { + if (typeof parentElement === "string") { + document.getElementById(parentElement)!.removeChild(this.video); + } else if (parentElement instanceof Element) { + parentElement.removeChild(this.video); } + else if (!parentElement) { + if (document.getElementById(this.parentId)) { + document.getElementById(this.parentId)!.removeChild(this.video); + } + } + delete this.video; } } @@ -142,6 +138,10 @@ export class Stream { this.video = video; } + getParentId() { + return this.parentId; + } + getRecvVideo() { return this.recvVideo; } @@ -259,11 +259,6 @@ export class Stream { this.video.controls = false; this.video.srcObject = this.videoSrcObject; - this.videoElements.push({ - thumb: thumbnailId, - video: this.video - }); - if (this.local && !this.displayMyRemote()) { this.video.muted = true; this.video.oncanplay = () => { @@ -307,8 +302,6 @@ export class Stream { thumbnail.appendChild(container); } - this.elements.push(container); - let name = document.createElement('div'); container.appendChild(name); let userName = this.streamId.replace('_webcam', ''); @@ -598,26 +591,23 @@ export class Stream { } } - for (let videoElement of this.videoElements) { - let thumbnailId = videoElement.thumb; - let video = videoElement.video; - video.srcObject = this.wrStream; - video.oncanplay = () => { - if (this.local && this.displayMyRemote()) { - console.info("Your own remote 'Stream' with id [" + this.streamId + "] video is now playing"); - this.ee.emitEvent('remote-video-is-playing', [{ - element: video - }]); - } else if (!this.local && !this.displayMyRemote()) { - console.info("Remote 'Stream' with id [" + this.streamId + "] video is now playing"); - this.ee.emitEvent('video-is-playing', [{ - element: video - }]); - } - //show(thumbnailId); - //this.hideSpinner(this.streamId); - }; - } + //let thumbnailId = this.video.thumb; + this.video.srcObject = this.wrStream; + this.video.oncanplay = () => { + if (this.local && this.displayMyRemote()) { + console.info("Your own remote 'Stream' with id [" + this.streamId + "] video is now playing"); + this.ee.emitEvent('remote-video-is-playing', [{ + element: this.video + }]); + } else if (!this.local && !this.displayMyRemote()) { + console.info("Remote 'Stream' with id [" + this.streamId + "] video is now playing"); + this.ee.emitEvent('video-is-playing', [{ + element: this.video + }]); + } + //show(thumbnailId); + //this.hideSpinner(this.streamId); + }; this.room.emitEvent('stream-subscribed', [{ stream: this }]); @@ -657,10 +647,6 @@ export class Stream { } } - this.elements.forEach(e => disposeElement(e)); - - //this.videoElements.forEach(ve => disposeElement(ve.video)); - disposeElement("progress-" + this.streamId); if (this.wp) {