mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: IExplorer support
parent
89db47dd37
commit
8f25b937e2
|
@ -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,
|
||||
|
|
|
@ -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<MediaStream>((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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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((<MediaStreamTrack>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((<MediaStreamTrack>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, <VideoInsertMode>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, <VideoInsertMode>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((<MediaStreamTrack>this.properties.videoSource));
|
||||
}
|
||||
if (this.properties.audioSource instanceof MediaStreamTrack) {
|
||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) {
|
||||
mediaStream.addTrack((<MediaStreamTrack>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,
|
||||
|
|
|
@ -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']) {
|
||||
(<any>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) => {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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(<RTCIceCandidate>this.iceCandidateList.shift());
|
||||
if (platform['isInternetExplorer']) {
|
||||
(<any>this.pc).addIceCandidate(<RTCIceCandidate>this.iceCandidateList.shift(), () => {}, () => {});
|
||||
} else {
|
||||
this.pc.addIceCandidate(<RTCIceCandidate>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<string> {
|
||||
|
@ -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');
|
||||
(<any>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;
|
||||
(<any>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<ConstrainDOMString> {
|
||||
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(<string>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<string> {
|
||||
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
|
||||
(<any>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']) {
|
||||
(<any>this.pc).addIceCandidate(iceCandidate, () => resolve(), error => reject(error));
|
||||
} else {
|
||||
this.pc.addIceCandidate(iceCandidate).then(() => resolve()).catch(error => reject(error));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue