diff --git a/openvidu-browser/src/OpenVidu/Connection.ts b/openvidu-browser/src/OpenVidu/Connection.ts index 733ba4fc..be4754bc 100644 --- a/openvidu-browser/src/OpenVidu/Connection.ts +++ b/openvidu-browser/src/OpenVidu/Connection.ts @@ -96,8 +96,8 @@ export class Connection { */ sendIceCandidate(candidate: RTCIceCandidate): void { - console.debug((!!this.stream.outboundStreamOpts ? 'Local' : 'Remote'), 'candidate for', - this.connectionId, JSON.stringify(candidate)); + console.debug((!!this.stream.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' + + this.connectionId, candidate); this.session.openvidu.sendRequest('onIceCandidate', { endpointName: this.connectionId, diff --git a/openvidu-browser/src/OpenVidu/OpenVidu.ts b/openvidu-browser/src/OpenVidu/OpenVidu.ts index 01589796..53540a93 100644 --- a/openvidu-browser/src/OpenVidu/OpenVidu.ts +++ b/openvidu-browser/src/OpenVidu/OpenVidu.ts @@ -32,6 +32,9 @@ import * as screenSharing from '../OpenViduInternal/ScreenSharing/Screen-Capturi import RpcBuilder = require('../OpenViduInternal/KurentoUtils/kurento-jsonrpc'); import platform = require('platform'); platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; +platform['isInternetExplorer'] = platform.name === 'IE' && platform.version !== undefined && parseInt(platform.version) >= 11; +platform['isReactNative'] = navigator.product === 'ReactNative'; +declare const AdapterJS: any; /** * @hidden @@ -96,6 +99,10 @@ export class OpenVidu { console.info("'OpenVidu' initialized"); console.info("openvidu-browser version: " + this.libraryVersion); + + if (platform['isInternetExplorer']) { + this.importIEAdapterJS(); + } if (platform.os!!.family === 'iOS' || platform.os!!.family === 'Android') { // Listen to orientationchange only on mobile devices @@ -216,12 +223,12 @@ export class OpenVidu { properties = { audioSource: (typeof properties.audioSource !== 'undefined') ? properties.audioSource : undefined, - frameRate: (properties.videoSource instanceof MediaStreamTrack) ? undefined : ((typeof properties.frameRate !== 'undefined') ? properties.frameRate : undefined), + frameRate: (typeof MediaStreamTrack !== 'undefined' && properties.videoSource instanceof MediaStreamTrack) ? undefined : ((typeof properties.frameRate !== 'undefined') ? properties.frameRate : undefined), insertMode: (typeof properties.insertMode !== 'undefined') ? ((typeof properties.insertMode === 'string') ? VideoInsertMode[properties.insertMode] : properties.insertMode) : VideoInsertMode.APPEND, mirror: (typeof properties.mirror !== 'undefined') ? properties.mirror : true, publishAudio: (typeof properties.publishAudio !== 'undefined') ? properties.publishAudio : true, publishVideo: (typeof properties.publishVideo !== 'undefined') ? properties.publishVideo : true, - resolution: (properties.videoSource instanceof MediaStreamTrack) ? undefined : ((typeof properties.resolution !== 'undefined') ? properties.resolution : '640x480'), + resolution: (typeof MediaStreamTrack !== 'undefined' && properties.videoSource instanceof MediaStreamTrack) ? undefined : ((typeof properties.resolution !== 'undefined') ? properties.resolution : '640x480'), videoSource: (typeof properties.videoSource !== 'undefined') ? properties.videoSource : undefined, filter: properties.filter }; @@ -325,7 +332,8 @@ export class OpenVidu { (browser !== 'Chrome') && (browser !== 'Chrome Mobile') && (browser !== 'Firefox') && (browser !== 'Firefox Mobile') && (browser !== 'Opera') && (browser !== 'Opera Mobile') && - (browser !== 'Android Browser') + (browser !== 'Android Browser') && + (browser !== 'IE' || platform.version !== undefined && parseInt(platform.version) < 11) ) { return 0; } else { @@ -431,7 +439,9 @@ export class OpenVidu { return new Promise((resolve, reject) => { this.generateMediaConstraints(options) .then(constraints => { - navigator.mediaDevices.getUserMedia(constraints) + + let userMediaFunc = () => { + navigator.mediaDevices.getUserMedia(constraints) .then(mediaStream => { resolve(mediaStream); }) @@ -445,6 +455,15 @@ export class OpenVidu { } reject(new OpenViduError(errorName, errorMessage)); }); + } + + if (platform['isInternetExplorer']) { + AdapterJS.webRTCReady(isUsingPlugin => { + userMediaFunc(); + }); + } else { + userMediaFunc(); + } }) .catch((error: OpenViduError) => { reject(error); @@ -742,4 +761,19 @@ export class OpenVidu { } } + private importIEAdapterJS(): void { + const moduleSpecifier = 'https://cdn.temasys.io/adapterjs/0.15.x/adapter.screenshare.min.js'; + //Create a script tag + var script = document.createElement('script'); + // Assign a URL to the script element + script.src = moduleSpecifier; + // Get the first script tag on the page (we'll insert our new one before it) + var ref = document.querySelector('script'); + // Insert the new node before the reference node + if (ref && ref.parentNode) { + ref.parentNode.insertBefore(script, ref); + console.info("Detected IE Explorer " + platform.version + ". IEAdapter imported"); + } + } + } \ No newline at end of file diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index 5ada639c..27ac6668 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -30,6 +30,9 @@ import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; import platform = require('platform'); platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; +platform['isInternetExplorer'] = platform.name === 'IE' && platform.version !== undefined && parseInt(platform.version) >= 11; +platform['isReactNative'] = navigator.product === 'ReactNative'; +declare const AdapterJS: any, attachMediaStream; /** * Packs local media streams. Participants can publish it to a session. Initialized with [[OpenVidu.initPublisher]] method @@ -67,6 +70,10 @@ export class Publisher extends StreamManager { * @hidden */ screenShareResizeInterval: NodeJS.Timer; + /** + * @hidden + */ + IEAdapter: any; /** * @hidden @@ -284,12 +291,12 @@ export class Publisher extends StreamManager { this.accessAllowed = true; this.accessDenied = false; - if (this.properties.audioSource instanceof MediaStreamTrack) { + if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) { mediaStream.removeTrack(mediaStream.getAudioTracks()[0]); mediaStream.addTrack((this.properties.audioSource)); } - if (this.properties.videoSource instanceof MediaStreamTrack) { + if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) { mediaStream.removeTrack(mediaStream.getVideoTracks()[0]); mediaStream.addTrack((this.properties.videoSource)); } @@ -309,19 +316,62 @@ export class Publisher extends StreamManager { if (platform.name === 'Safari') { this.videoReference.setAttribute('playsinline', 'true'); } - - this.videoReference.srcObject = mediaStream; + + if (!!this.firstVideoElement) { + let video = this.createVideoElement(this.firstVideoElement.targetElement, this.properties.insertMode); + if (platform['isInternetExplorer']) { + this.videoReference = video; + } + } this.stream.setMediaStream(mediaStream); + + if (platform['isInternetExplorer']) { + AdapterJS.webRTCReady(isUsingPlugin => { + this.videoReference = this.customAttachMediaStreamIE(this.videoReference, mediaStream); + if (this.stream.isSendVideo()) { + if (!this.stream.isSendScreen()) { + /*this.videoReference.onloadedmetadata = () => { + this.stream.videoDimensions = { + width: this.videoReference.videoWidth, + height: this.videoReference.videoHeight + }; + + // TODO: if screen-share, set this.screenShareResizeInterval + + console.warn(this.stream.videoDimensions); + this.stream.isLocalStreamReadyToPublish = true; + this.stream.ee.emitEvent('stream-ready-to-publish', []); + }*/ + + this.stream.videoDimensions = { + width: this.videoReference.videoWidth, + height: this.videoReference.videoHeight + }; + + // TODO: if screen-share, set this.screenShareResizeInterval + + console.warn(this.stream.videoDimensions); + this.stream.isLocalStreamReadyToPublish = true; + this.stream.ee.emitEvent('stream-ready-to-publish', []); + + this.videoReference.onplaying = () => { + console.warn("PLAYINNNGNGNGNGNGNG!!!"); + } + + } + } + + }); + } else { + this.videoReference.srcObject = mediaStream; + } + if (!this.stream.displayMyRemote()) { // When we are subscribed to our remote we don't still set the MediaStream object in the video elements to // avoid early 'streamPlaying' event this.stream.updateMediaStreamInVideos(); } - - if (!!this.firstVideoElement) { - this.createVideoElement(this.firstVideoElement.targetElement, this.properties.insertMode); - } delete this.firstVideoElement; if (this.stream.isSendVideo()) { @@ -357,8 +407,8 @@ export class Publisher extends StreamManager { videoDimensionsSet(); } }; - } else { - // Rest of platforms + } else if (platform.name !== 'IE') { + // Rest of platforms except IE // With no screen share, video dimension can be set directly from MediaStream (getSettings) // Orientation must be checked for mobile devices (width and height are reversed) const { width, height } = mediaStream.getVideoTracks()[0].getSettings(); @@ -480,14 +530,14 @@ export class Publisher extends StreamManager { // - video track is given and no audio // - audio track is given and no video // - both video and audio tracks are given - if ((this.properties.videoSource instanceof MediaStreamTrack && !this.properties.audioSource) - || (this.properties.audioSource instanceof MediaStreamTrack && !this.properties.videoSource) - || (this.properties.videoSource instanceof MediaStreamTrack && this.properties.audioSource instanceof MediaStreamTrack)) { + if ((typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack && !this.properties.audioSource) + || (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack && !this.properties.videoSource) + || (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack && this.properties.audioSource instanceof MediaStreamTrack)) { const mediaStream = new MediaStream(); - if (this.properties.videoSource instanceof MediaStreamTrack) { + if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) { mediaStream.addTrack((this.properties.videoSource)); } - if (this.properties.audioSource instanceof MediaStreamTrack) { + if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) { mediaStream.addTrack((this.properties.audioSource)); } // MediaStreamTracks are handled within callback - just call callback with new MediaStream() and let it handle the sources @@ -521,11 +571,16 @@ export class Publisher extends StreamManager { afterGetMedia(mediaStream, definedAudioConstraint); }); } else { - navigator.mediaDevices.getUserMedia(constraintsAux) + + let userMediaFunc = () => { + navigator.mediaDevices.getUserMedia(constraintsAux) .then(mediaStream => { afterGetMedia(mediaStream, definedAudioConstraint); }) .catch(error => { + + console.error(error); + this.clearPermissionDialogTimer(startTime, timeForDialogEvent); if (error.name === 'Error') { // Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError' @@ -598,6 +653,16 @@ export class Publisher extends StreamManager { } }); + } + + if (platform['isInternetExplorer']) { + AdapterJS.webRTCReady(isUsingPlugin => { + userMediaFunc(); + }) + } else { + userMediaFunc(); + } + } } else { reject(new OpenViduError(OpenViduErrorName.NO_INPUT_SOURCE_SET, diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index 3f84f08b..e49b3338 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -160,7 +160,7 @@ export class Session implements EventDispatcher { reject(error); }); } else { - reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' for ' + platform.os!!.family + ' is not supported in OpenVidu')); + reject(new OpenViduError(OpenViduErrorName.BROWSER_NOT_SUPPORTED, 'Browser ' + platform.name + ' (version ' + platform.version + ') for ' + platform.os!!.family + ' is not supported in OpenVidu')); } }); } @@ -882,10 +882,17 @@ export class Session implements EventDispatcher { this.getConnection(msg.senderConnectionId, 'Connection not found for connectionId ' + msg.senderConnectionId + ' owning endpoint ' + msg.endpointName + '. Ice candidate will be ignored: ' + candidate) .then(connection => { const stream = connection.stream; - stream.getWebRtcPeer().addIceCandidate(candidate).catch(error => { - console.error('Error adding candidate for ' + stream.streamId - + ' stream of endpoint ' + msg.endpointName + ': ' + error); - }); + if (platform['isInternetExplorer']) { + (stream.getWebRtcPeer()).addIceCandidate(candidate, () => {}, error => { + console.error('Error adding candidate for ' + stream.streamId + + ' stream of endpoint ' + msg.endpointName + ': ' + error); + }); + } else { + stream.getWebRtcPeer().addIceCandidate(candidate).catch(error => { + console.error('Error adding candidate for ' + stream.streamId + + ' stream of endpoint ' + msg.endpointName + ': ' + error); + }); + } }) .catch(openViduError => { console.error(openViduError); @@ -1023,10 +1030,10 @@ export class Session implements EventDispatcher { const joinParams = { token: (!!token) ? token : '', session: this.sessionId, - platform: platform.description, + platform: !!platform.description ? platform.description : 'unknown', metadata: !!this.options.metadata ? this.options.metadata : '', secret: this.openvidu.getSecret(), - recorder: this.openvidu.getRecorder(), + recorder: this.openvidu.getRecorder() }; this.openvidu.sendRequest('joinRoom', joinParams, (error, response) => { diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index 93fd6a33..2ef437cc 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -35,6 +35,9 @@ import EventEmitter = require('wolfy87-eventemitter'); import hark = require('hark'); import platform = require('platform'); platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; +platform['isInternetExplorer'] = platform.name === 'IE' && platform.version !== undefined && parseInt(platform.version) >= 11; +platform['isReactNative'] = navigator.product === 'ReactNative'; +declare const AdapterJS: any; /** @@ -221,7 +224,7 @@ export class Stream implements EventDispatcher { if (this.hasVideo) { this.videoActive = !!this.outboundStreamOpts.publisherProperties.publishVideo; this.frameRate = this.outboundStreamOpts.publisherProperties.frameRate; - if (this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) { + if (typeof MediaStreamTrack !== 'undefined' && this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) { this.typeOfVideo = 'CUSTOM'; } else { this.typeOfVideo = this.isSendScreen() ? 'SCREEN' : 'CAMERA'; @@ -455,7 +458,7 @@ export class Stream implements EventDispatcher { disposeWebRtcPeer(): void { if (this.webRtcPeer) { const isSenderAndCustomTrack: boolean = !!this.outboundStreamOpts && - this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack; + typeof MediaStreamTrack !== 'undefined' && this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack; this.webRtcPeer.dispose(isSenderAndCustomTrack); } if (this.speechEvent) { @@ -693,7 +696,7 @@ export class Stream implements EventDispatcher { let typeOfVideo = ''; if (this.isSendVideo()) { - typeOfVideo = this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack ? 'CUSTOM' : (this.isSendScreen() ? 'SCREEN' : 'CAMERA'); + typeOfVideo = (typeof MediaStreamTrack !== 'undefined' && this.outboundStreamOpts.publisherProperties.videoSource instanceof MediaStreamTrack) ? 'CUSTOM' : (this.isSendScreen() ? 'SCREEN' : 'CAMERA'); } this.session.openvidu.sendRequest('publishVideo', { @@ -795,20 +798,30 @@ export class Stream implements EventDispatcher { }); }; - this.webRtcPeer = new WebRtcPeerRecvonly(options); - this.webRtcPeer.generateOffer() - .then(offer => { - successCallback(offer); - }) - .catch(error => { - reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error))); + const initWebRtcPeer = () => { + this.webRtcPeer = new WebRtcPeerRecvonly(options); + this.webRtcPeer.generateOffer() + .then(offer => { + successCallback(offer); + }) + .catch(error => { + reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error))); + }); + }; + + if (platform['isInternetExplorer']) { + AdapterJS.webRTCReady(isUsingPlugin => { + initWebRtcPeer(); }); + } else { + initWebRtcPeer(); + } }); } private remotePeerSuccessfullyEstablished(): void { - if (platform['isIonicIos']) { - // iOS Ionic. LIMITATION: must use deprecated WebRTC API + if (platform['isIonicIos'] || platform['isInternetExplorer']) { + // iOS Ionic or IExplorer. LIMITATION: must use deprecated WebRTC API const pc1: any = this.webRtcPeer.pc; this.mediaStream = pc1.getRemoteStreams()[0]; } else { @@ -836,7 +849,7 @@ export class Stream implements EventDispatcher { } } - this.ee.emitEvent('mediastream-updated', []); + this.updateMediaStreamInVideos(); if (!this.displayMyRemote() && !!this.mediaStream.getAudioTracks()[0] && this.session.speakingEventsEnabled) { this.enableSpeakingEvents(); } diff --git a/openvidu-browser/src/OpenVidu/StreamManager.ts b/openvidu-browser/src/OpenVidu/StreamManager.ts index a775491e..775d9f4a 100644 --- a/openvidu-browser/src/OpenVidu/StreamManager.ts +++ b/openvidu-browser/src/OpenVidu/StreamManager.ts @@ -26,6 +26,9 @@ import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; import EventEmitter = require('wolfy87-eventemitter'); import platform = require('platform'); platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; +platform['isInternetExplorer'] = platform.name === 'IE' && platform.version !== undefined && parseInt(platform.version) >= 11; +platform['isReactNative'] = navigator.product === 'ReactNative'; +declare const attachMediaStream; /** * Interface in charge of displaying the media streams in the HTML DOM. This wraps any [[Publisher]] and [[Subscriber]] object. @@ -240,6 +243,10 @@ export class StreamManager implements EventDispatcher { video.srcObject = this.stream.getMediaStream(); } + if (platform['isInternetExplorer'] && !!this.stream.getMediaStream()) { + video = this.customAttachMediaStreamIE(video, this.stream.getMediaStream()); + } + // If the video element is already part of this StreamManager do nothing for (const v of this.videos) { if (v.video === video) { @@ -293,7 +300,7 @@ export class StreamManager implements EventDispatcher { throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement); } - const video = document.createElement('video'); + let video = document.createElement('video'); this.initializeVideoProperties(video); let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND; @@ -319,6 +326,10 @@ export class StreamManager implements EventDispatcher { break; } + if (platform['isInternetExplorer'] && !!this.stream.getMediaStream()) { + video = this.customAttachMediaStreamIE(video, this.stream.getMediaStream()); + } + const v: StreamManagerVideo = { targetElement: targEl, video, @@ -327,9 +338,12 @@ export class StreamManager implements EventDispatcher { }; this.pushNewStreamManagerVideo(v); - this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]); - - this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement; + let launchVideoCreatedEvent = !platform['isInternetExplorer']; + if (launchVideoCreatedEvent) { + // For IE the event is called in this.customAttachMediaStreamIE + this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]); + } + this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement && launchVideoCreatedEvent; return video; } @@ -432,6 +446,9 @@ export class StreamManager implements EventDispatcher { vParent!!.replaceChild(newVideo, streamManagerVideo.video); streamManagerVideo.video = newVideo; } + if (platform['isInternetExplorer']) { + this.customAttachMediaStreamIE(streamManagerVideo.video, mediaStream); + } }); } @@ -462,4 +479,21 @@ export class StreamManager implements EventDispatcher { video.style.webkitTransform = 'unset'; } + protected customAttachMediaStreamIE(video: HTMLVideoElement, mediaStream: MediaStream): HTMLVideoElement { + var simVideo = attachMediaStream(video, mediaStream); + + // Replace HTMLVideoElemet (if exists) with new HTMLObjectElement returned by IE plugin + for (let i = 0; i < this.videos.length; i++) { + if (this.videos[i].video === video) { + this.videos[i].video = simVideo; + break; + } + } + + // Always launch videoElementCreated event after IE plugin has inserted simulated video into DOM + this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(simVideo, this, 'videoElementCreated')]); + this.addPlayEventToFirstVideo(); + return simVideo; + } + } \ No newline at end of file diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts index b416c596..52441b13 100644 --- a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts +++ b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts @@ -19,6 +19,8 @@ import freeice = require('freeice'); import uuid = require('uuid'); import platform = require('platform'); platform['isIonicIos'] = (platform.product === 'iPhone' || platform.product === 'iPad') && platform.ua!!.indexOf('Safari') === -1; +platform['isInternetExplorer'] = platform.name === 'IE' && platform.version !== undefined && parseInt(platform.version) >= 11; +platform['isReactNative'] = navigator.product === 'ReactNative'; export interface WebRtcPeerConfiguration { mediaConstraints: { @@ -66,7 +68,11 @@ export class WebRtcPeer { this.pc.onsignalingstatechange = () => { if (this.pc.signalingState === 'stable') { while (this.iceCandidateList.length > 0) { - this.pc.addIceCandidate(this.iceCandidateList.shift()); + if (platform['isInternetExplorer']) { + (this.pc).addIceCandidate(this.iceCandidateList.shift(), () => {}, () => {}); + } else { + this.pc.addIceCandidate(this.iceCandidateList.shift()); + } } } }; @@ -87,8 +93,8 @@ export class WebRtcPeer { reject('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue'); } if (!!this.configuration.mediaStream) { - if (platform['isIonicIos']) { - // iOS Ionic. LIMITATION: must use deprecated WebRTC API + if (platform['isIonicIos'] || platform['isInternetExplorer']) { + // iOS Ionic and IExplorer. LIMITATION: must use deprecated WebRTC API const pc2: any = this.pc; pc2.addStream(this.configuration.mediaStream); } else { @@ -114,8 +120,8 @@ export class WebRtcPeer { this.remoteCandidatesQueue = []; this.localCandidatesQueue = []; - if (platform['isIonicIos']) { - // iOS Ionic. LIMITATION: must use deprecated WebRTC API + if (platform['isIonicIos'] || platform['isInternetExplorer']) { + // iOS Ionic or IExplorer. LIMITATION: must use deprecated WebRTC API // Stop senders deprecated const pc1: any = this.pc; for (const sender of pc1.getLocalStreams()) { @@ -156,7 +162,7 @@ export class WebRtcPeer { } /** - * 1) Function that creates an offer, sets it as local description and returns the offer param + * Function that creates an offer, sets it as local description and returns the offer param * to send to OpenVidu Server (will be the remote description of other peer) */ generateOffer(): Promise { @@ -209,11 +215,43 @@ export class WebRtcPeer { } }) .catch(error => reject(error)); + + } else if (platform['isInternetExplorer']) { + + // IE Explorer cannot use Promise base API + let setLocalDescriptionOnSuccess = () => { + const localDescription = this.pc.localDescription; + if (!!localDescription) { + console.debug('Local description set', localDescription.sdp); + resolve(localDescription.sdp); + } else { + reject('Local description is not defined'); + } + } + let setLocalDescriptionOnError = error => { + reject(error); + } + let createOfferOnSuccess = offer => { + console.debug('Created SDP offer'); + (this.pc).setLocalDescription(offer, setLocalDescriptionOnSuccess, setLocalDescriptionOnError); + }; + let createOfferOnError = error => { + reject(error); + }; + + // FIX: for IExplorer WebRTC peer connections must be negotiated to receive video and audio + constraints.offerToReceiveAudio = true; + constraints.offerToReceiveVideo = true; + (this.pc).createOffer(createOfferOnSuccess, createOfferOnError, constraints); + } else { + + // Rest of platforms this.pc.createOffer(constraints).then(offer => { console.debug('Created SDP offer'); return this.pc.setLocalDescription(offer); - }).then(() => { + }) + .then(() => { const localDescription = this.pc.localDescription; if (!!localDescription) { console.debug('Local description set', localDescription.sdp); @@ -222,58 +260,21 @@ export class WebRtcPeer { reject('Local description is not defined'); } }) - .catch(error => reject(error)); + .catch(error => reject(error)); } }); } /** - * 2) Function to invoke when a SDP offer is received. Sets it as remote description, - * generates and answer and returns it to send it to OpenVidu Server - */ - processOffer(sdpOffer: string): Promise { - return new Promise((resolve, reject) => { - const offer: RTCSessionDescriptionInit = { - type: 'offer', - sdp: sdpOffer - }; - - console.debug('SDP offer received, setting remote description'); - - if (this.pc.signalingState === 'closed') { - reject('PeerConnection is closed'); - } - - this.pc.setRemoteDescription(offer) - .then(() => { - return this.pc.createAnswer(); - }).then(answer => { - console.debug('Created SDP answer'); - return this.pc.setLocalDescription(answer); - }).then(() => { - const localDescription = this.pc.localDescription; - if (!!localDescription) { - console.debug('Local description set', localDescription.sdp); - resolve(localDescription.sdp); - } else { - reject('Local description is not defined'); - } - }).catch(error => reject(error)); - }); - } - - /** - * 3) Function invoked when a SDP answer is received. Final step in SDP negotiation, the peer + * Function invoked when a SDP answer is received. Final step in SDP negotiation, the peer * just needs to set the answer as its remote description */ processAnswer(sdpAnswer: string, needsTimeoutOnProcessAswer: boolean): Promise { return new Promise((resolve, reject) => { - const answer: RTCSessionDescriptionInit = { type: 'answer', sdp: sdpAnswer }; - console.debug('SDP answer received, setting remote description'); if (this.pc.signalingState === 'closed') { @@ -285,7 +286,13 @@ export class WebRtcPeer { this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error)); }, 250); } else { - this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error)); + if (platform['isInternetExplorer']) { + // IE Explorer cannot use Promise base API + (this.pc).setRemoteDescription(answer, resolve(), error => reject(error)); + } else { + // Rest of platforms + this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error)); + } } }); } @@ -303,7 +310,11 @@ export class WebRtcPeer { break; case 'stable': if (!!this.pc.remoteDescription) { - this.pc.addIceCandidate(iceCandidate).then(() => resolve()).catch(error => reject(error)); + if (platform['isInternetExplorer']) { + (this.pc).addIceCandidate(iceCandidate, () => resolve(), error => reject(error)); + } else { + this.pc.addIceCandidate(iceCandidate).then(() => resolve()).catch(error => reject(error)); + } } break; default: