Add suport for Safari 11 and Microsoft Edge
parent
103be0ece3
commit
b86b6e6957
|
|
@ -133,7 +133,9 @@ export default class Client extends events.EventEmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
videoTrack.stop();
|
videoTrack.stop();
|
||||||
|
stream.removeTrack(videoTrack);
|
||||||
|
|
||||||
|
// New API.
|
||||||
if (this._peerconnection.removeTrack)
|
if (this._peerconnection.removeTrack)
|
||||||
{
|
{
|
||||||
let sender;
|
let sender;
|
||||||
|
|
@ -146,9 +148,11 @@ export default class Client extends events.EventEmitter
|
||||||
|
|
||||||
this._peerconnection.removeTrack(sender);
|
this._peerconnection.removeTrack(sender);
|
||||||
}
|
}
|
||||||
|
// Old API.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stream.removeTrack(videoTrack);
|
this._peerconnection.removeStream(stream);
|
||||||
|
this._peerconnection.addStream(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dontNegotiate)
|
if (!dontNegotiate)
|
||||||
|
|
@ -187,19 +191,33 @@ export default class Client extends events.EventEmitter
|
||||||
|
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
// Fucking hack for adapter.js in Chrome.
|
|
||||||
if (this._peerconnection.removeStream)
|
|
||||||
this._peerconnection.removeStream(stream);
|
|
||||||
|
|
||||||
stream.addTrack(newVideoTrack);
|
stream.addTrack(newVideoTrack);
|
||||||
|
|
||||||
|
// New API.
|
||||||
if (this._peerconnection.addTrack)
|
if (this._peerconnection.addTrack)
|
||||||
|
{
|
||||||
this._peerconnection.addTrack(newVideoTrack, stream);
|
this._peerconnection.addTrack(newVideoTrack, stream);
|
||||||
|
}
|
||||||
|
// Old API.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._peerconnection.addStream(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._localStream = newStream;
|
this._localStream = newStream;
|
||||||
this._peerconnection.addStream(newStream);
|
|
||||||
|
// New API.
|
||||||
|
if (this._peerconnection.addTrack)
|
||||||
|
{
|
||||||
|
this._peerconnection.addTrack(newVideoTrack, stream);
|
||||||
|
}
|
||||||
|
// Old API.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this._peerconnection.addStream(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('localstream', this._localStream, videoResolution);
|
this.emit('localstream', this._localStream, videoResolution);
|
||||||
|
|
@ -803,7 +821,10 @@ export default class Client extends events.EventEmitter
|
||||||
{
|
{
|
||||||
let state = this._peerconnection.iceConnectionState;
|
let state = this._peerconnection.iceConnectionState;
|
||||||
|
|
||||||
logger.debug('peerconnection "iceconnectionstatechange" event [state:%s]', state);
|
if (state === 'failed')
|
||||||
|
logger.warn('peerconnection "iceconnectionstatechange" event [state:failed]');
|
||||||
|
else
|
||||||
|
logger.debug('peerconnection "iceconnectionstatechange" event [state:%s]', state);
|
||||||
|
|
||||||
this.emit('connectionstate', state);
|
this.emit('connectionstate', state);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ export default class RemoteVideo extends React.Component
|
||||||
let videoTrack = this.props.stream.getVideoTracks()[0];
|
let videoTrack = this.props.stream.getVideoTracks()[0];
|
||||||
let videoEnabled = videoTrack && videoTrack.enabled;
|
let videoEnabled = videoTrack && videoTrack.enabled;
|
||||||
let stream = this.props.stream;
|
let stream = this.props.stream;
|
||||||
let msid = stream.id;
|
let msid = stream.jitsiRemoteId || stream.id;
|
||||||
|
|
||||||
if (videoEnabled)
|
if (videoEnabled)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@ export default class Room extends React.Component
|
||||||
this._client = null;
|
this._client = null;
|
||||||
// Timer to retrieve RTC stats.
|
// Timer to retrieve RTC stats.
|
||||||
this._statsTimer = null;
|
this._statsTimer = null;
|
||||||
|
|
||||||
|
// TODO: TMP
|
||||||
|
global.ROOM = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
render()
|
render()
|
||||||
|
|
@ -221,6 +224,13 @@ export default class Room extends React.Component
|
||||||
{
|
{
|
||||||
logger.debug('handleLocalResolutionChange()');
|
logger.debug('handleLocalResolutionChange()');
|
||||||
|
|
||||||
|
if (!utils.canChangeResolution())
|
||||||
|
{
|
||||||
|
logger.warn('changing local resolution not implemented for this browser');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._client.changeVideoResolution();
|
this._client.changeVideoResolution();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,8 +405,23 @@ export default class Room extends React.Component
|
||||||
|
|
||||||
let peers = this.state.peers;
|
let peers = this.state.peers;
|
||||||
|
|
||||||
|
peer = peers[peer.id];
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
|
|
||||||
delete peers[peer.id];
|
delete peers[peer.id];
|
||||||
this.setState({ peers });
|
|
||||||
|
// NOTE: This shouldn't be needed but Safari 11 does not fire pc "removestream"
|
||||||
|
// nor stream "removetrack" nor track "ended", so we need to cleanup remote
|
||||||
|
// streams when a peer leaves.
|
||||||
|
let remoteStreams = this.state.remoteStreams;
|
||||||
|
|
||||||
|
for (let msid of peer.msids)
|
||||||
|
{
|
||||||
|
delete remoteStreams[msid];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ peers, remoteStreams });
|
||||||
});
|
});
|
||||||
|
|
||||||
this._client.on('connectionstate', (state) =>
|
this._client.on('connectionstate', (state) =>
|
||||||
|
|
@ -407,16 +432,18 @@ export default class Room extends React.Component
|
||||||
this._client.on('addstream', (stream) =>
|
this._client.on('addstream', (stream) =>
|
||||||
{
|
{
|
||||||
let remoteStreams = this.state.remoteStreams;
|
let remoteStreams = this.state.remoteStreams;
|
||||||
|
let streamId = stream.jitsiRemoteId || stream.id;
|
||||||
|
|
||||||
remoteStreams[stream.id] = stream;
|
remoteStreams[streamId] = stream;
|
||||||
this.setState({ remoteStreams });
|
this.setState({ remoteStreams });
|
||||||
});
|
});
|
||||||
|
|
||||||
this._client.on('removestream', (stream) =>
|
this._client.on('removestream', (stream) =>
|
||||||
{
|
{
|
||||||
let remoteStreams = this.state.remoteStreams;
|
let remoteStreams = this.state.remoteStreams;
|
||||||
|
let streamId = stream.jitsiRemoteId || stream.id;
|
||||||
|
|
||||||
delete remoteStreams[stream.id];
|
delete remoteStreams[streamId];
|
||||||
this.setState({ remoteStreams });
|
this.setState({ remoteStreams });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ export default class Stats extends React.Component
|
||||||
|
|
||||||
_processStats(stats)
|
_processStats(stats)
|
||||||
{
|
{
|
||||||
|
// global.STATS = stats; // TODO: REMOVE
|
||||||
|
|
||||||
if (browser.check({ chrome: '58' }, true))
|
if (browser.check({ chrome: '58' }, true))
|
||||||
{
|
{
|
||||||
this._processStatsChrome58(stats);
|
this._processStatsChrome58(stats);
|
||||||
|
|
@ -108,6 +110,10 @@ export default class Stats extends React.Component
|
||||||
{
|
{
|
||||||
this._processStatsFirefox(stats);
|
this._processStatsFirefox(stats);
|
||||||
}
|
}
|
||||||
|
else if (browser.check({ safari: '11' }, true))
|
||||||
|
{
|
||||||
|
this._processStatsSafari11(stats);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.warn('_processStats() | unsupported browser [name:"%s", version:%s]',
|
logger.warn('_processStats() | unsupported browser [name:"%s", version:%s]',
|
||||||
|
|
@ -388,9 +394,6 @@ export default class Stats extends React.Component
|
||||||
|
|
||||||
for (let group of stats.values())
|
for (let group of stats.values())
|
||||||
{
|
{
|
||||||
// TODO: REMOVE
|
|
||||||
global.STATS = stats;
|
|
||||||
|
|
||||||
switch (group.type)
|
switch (group.type)
|
||||||
{
|
{
|
||||||
case 'candidate-pair':
|
case 'candidate-pair':
|
||||||
|
|
@ -489,6 +492,87 @@ export default class Stats extends React.Component
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_processStatsSafari11(stats)
|
||||||
|
{
|
||||||
|
let transport = {};
|
||||||
|
let audio = {};
|
||||||
|
let video = {};
|
||||||
|
|
||||||
|
for (let group of stats.values())
|
||||||
|
{
|
||||||
|
switch (group.type)
|
||||||
|
{
|
||||||
|
case 'candidate-pair':
|
||||||
|
{
|
||||||
|
if (!group.writable)
|
||||||
|
break;
|
||||||
|
|
||||||
|
transport['bytes sent'] = group.bytesSent;
|
||||||
|
transport['bytes received'] = group.bytesReceived;
|
||||||
|
transport['available bitrate'] =
|
||||||
|
Math.round(group.availableOutgoingBitrate / 1000) + ' kbps';
|
||||||
|
transport['current RTT'] =
|
||||||
|
Math.round(group.currentRoundTripTime * 1000) + ' ms';
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'outbound-rtp':
|
||||||
|
{
|
||||||
|
if (group.isRemote)
|
||||||
|
break;
|
||||||
|
|
||||||
|
let block;
|
||||||
|
|
||||||
|
switch (group.mediaType)
|
||||||
|
{
|
||||||
|
case 'audio':
|
||||||
|
block = audio;
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
block = video;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!block)
|
||||||
|
break;
|
||||||
|
|
||||||
|
block['ssrc'] = group.ssrc;
|
||||||
|
block['bytes sent'] = group.bytesSent;
|
||||||
|
block['packets sent'] = group.packetsSent;
|
||||||
|
|
||||||
|
if (block === video)
|
||||||
|
block['frames encoded'] = group.framesEncoded;
|
||||||
|
|
||||||
|
block['NACK count'] = group.nackCount;
|
||||||
|
block['PLI count'] = group.pliCount;
|
||||||
|
block['FIR count'] = group.firCount;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post checks.
|
||||||
|
|
||||||
|
if (!video.ssrc)
|
||||||
|
video = {};
|
||||||
|
|
||||||
|
if (!audio.ssrc)
|
||||||
|
audio = {};
|
||||||
|
|
||||||
|
// Set state.
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
stats :
|
||||||
|
{
|
||||||
|
transport,
|
||||||
|
audio,
|
||||||
|
video
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stats.propTypes =
|
Stats.propTypes =
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,9 @@ export default class Video extends React.Component
|
||||||
return stream.getTracks()
|
return stream.getTracks()
|
||||||
.map((track) =>
|
.map((track) =>
|
||||||
{
|
{
|
||||||
return track.id;
|
let trackId = track.jitsiRemoteId || track.id;
|
||||||
|
|
||||||
|
return trackId;
|
||||||
})
|
})
|
||||||
.join('|');
|
.join('|');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import browser from 'bowser';
|
import browser from 'bowser';
|
||||||
import webrtc from 'webrtc-adapter'; // eslint-disable-line no-unused-vars
|
|
||||||
import domready from 'domready';
|
import domready from 'domready';
|
||||||
import UrlParse from 'url-parse';
|
import UrlParse from 'url-parse';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
@ -14,20 +13,29 @@ import edgeRTCPeerConnection from './edge/RTCPeerConnection';
|
||||||
import edgeRTCSessionDescription from './edge/RTCSessionDescription';
|
import edgeRTCSessionDescription from './edge/RTCSessionDescription';
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
|
|
||||||
const REGEXP_FRAGMENT_ROOM_ID = new RegExp('^#room-id=([0-9a-zA-Z_\-]+)$');
|
const REGEXP_FRAGMENT_ROOM_ID = new RegExp('^#room-id=([0-9a-zA-Z_-]+)$');
|
||||||
const logger = new Logger();
|
const logger = new Logger();
|
||||||
|
|
||||||
injectTapEventPlugin();
|
injectTapEventPlugin();
|
||||||
|
|
||||||
logger.debug('detected browser [name:"%s", version:%s]', browser.name, browser.version);
|
logger.debug('detected browser [name:"%s", version:%s]', browser.name, browser.version);
|
||||||
|
|
||||||
|
// If Edge, use the Jitsi RTCPeerConnection shim.
|
||||||
if (browser.msedge)
|
if (browser.msedge)
|
||||||
{
|
{
|
||||||
logger.debug('EDGE detected, overriding WebRTC global classes');
|
logger.debug('Edge detected, overriding RTCPeerConnection and RTCSessionDescription');
|
||||||
|
|
||||||
window.RTCPeerConnection = edgeRTCPeerConnection;
|
window.RTCPeerConnection = edgeRTCPeerConnection;
|
||||||
window.RTCSessionDescription = edgeRTCSessionDescription;
|
window.RTCSessionDescription = edgeRTCSessionDescription;
|
||||||
}
|
}
|
||||||
|
// Otherwise, do almost anything.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window.RTCPeerConnection =
|
||||||
|
window.webkitRTCPeerConnection ||
|
||||||
|
window.mozRTCPeerConnection ||
|
||||||
|
window.RTCPeerConnection;
|
||||||
|
}
|
||||||
|
|
||||||
domready(() =>
|
domready(() =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import browser from 'bowser';
|
||||||
import randomNumberLib from 'random-number';
|
import randomNumberLib from 'random-number';
|
||||||
import Logger from './Logger';
|
import Logger from './Logger';
|
||||||
|
|
||||||
|
global.BROWSER = browser;
|
||||||
|
|
||||||
const logger = new Logger('utils');
|
const logger = new Logger('utils');
|
||||||
const randomNumberGenerator = randomNumberLib.generator(
|
const randomNumberGenerator = randomNumberLib.generator(
|
||||||
{
|
{
|
||||||
|
|
@ -42,6 +44,18 @@ export function isPlanB()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfortunately Edge produces rtpSender.send() to fail when receiving media
|
||||||
|
* from others and removing/adding a local track.
|
||||||
|
*/
|
||||||
|
export function canChangeResolution()
|
||||||
|
{
|
||||||
|
if (browser.msedge)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function randomNumber()
|
export function randomNumber()
|
||||||
{
|
{
|
||||||
return randomNumberGenerator();
|
return randomNumberGenerator();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mediasoup-demo-app",
|
"name": "mediasoup-demo-app",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "mediasoup demo app",
|
"description": "mediasoup demo app",
|
||||||
"author": "Iñaki Baz Castillo <ibc@aliax.net>",
|
"author": "Iñaki Baz Castillo <ibc@aliax.net>",
|
||||||
|
|
@ -8,25 +8,24 @@
|
||||||
"main": "lib/index.jsx",
|
"main": "lib/index.jsx",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-runtime": "^6.23.0",
|
"babel-runtime": "^6.23.0",
|
||||||
"bowser": "^1.6.1",
|
"bowser": "^1.7.0",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"debug": "^2.6.4",
|
"debug": "^2.6.8",
|
||||||
"domready": "^1.0.8",
|
"domready": "^1.0.8",
|
||||||
"hark": "ibc/hark#main-with-raf",
|
"hark": "ibc/hark#main-with-raf",
|
||||||
"material-ui": "^0.18.2",
|
"material-ui": "^0.18.3",
|
||||||
"prop-types": "^15.5.10",
|
"prop-types": "^15.5.10",
|
||||||
"protoo-client": "^1.1.4",
|
"protoo-client": "^1.1.4",
|
||||||
"random-number": "0.0.7",
|
"random-number": "0.0.7",
|
||||||
"random-string": "^0.2.0",
|
"random-string": "^0.2.0",
|
||||||
"react": "^15.5.4",
|
"react": "^15.6.1",
|
||||||
"react-clipboard.js": "^1.0.1",
|
"react-clipboard.js": "^1.1.2",
|
||||||
"react-dom": "^15.5.4",
|
"react-dom": "^15.6.1",
|
||||||
"react-notification-system": "ibc/react-notification-system#master",
|
"react-notification-system": "ibc/react-notification-system#master",
|
||||||
"react-tap-event-plugin": "^2.0.1",
|
"react-tap-event-plugin": "^2.0.1",
|
||||||
"react-transition-group": "^1.1.3",
|
"react-transition-group": "^1.2.0",
|
||||||
"sdp-transform": "^2.3.0",
|
"sdp-transform": "^2.3.0",
|
||||||
"url-parse": "^1.1.8",
|
"url-parse": "^1.1.9",
|
||||||
"webrtc-adapter": "^4.0.0",
|
|
||||||
"yaeti": "^1.0.1"
|
"yaeti": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -35,16 +34,16 @@
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babelify": "^7.3.0",
|
"babelify": "^7.3.0",
|
||||||
"browser-sync": "^2.18.8",
|
"browser-sync": "^2.18.12",
|
||||||
"browserify": "^14.3.0",
|
"browserify": "^14.4.0",
|
||||||
"del": "^2.2.2",
|
"del": "^3.0.0",
|
||||||
"envify": "^4.0.0",
|
"envify": "^4.0.0",
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^4.0.0",
|
||||||
"eslint-plugin-import": "^2.2.0",
|
"eslint-plugin-import": "^2.3.0",
|
||||||
"eslint-plugin-react": "^7.0.1",
|
"eslint-plugin-react": "^7.1.0",
|
||||||
"gulp": "git://github.com/gulpjs/gulp.git#4.0",
|
"gulp": "git://github.com/gulpjs/gulp.git#4.0",
|
||||||
"gulp-css-base64": "^1.3.4",
|
"gulp-css-base64": "^1.3.4",
|
||||||
"gulp-eslint": "^3.0.1",
|
"gulp-eslint": "^4.0.0",
|
||||||
"gulp-header": "^1.8.8",
|
"gulp-header": "^1.8.8",
|
||||||
"gulp-if": "^2.0.2",
|
"gulp-if": "^2.0.2",
|
||||||
"gulp-plumber": "^1.1.0",
|
"gulp-plumber": "^1.1.0",
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ class Room extends EventEmitter
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this._mediaRoom.on('audiolevels', (entries) =>
|
// TODO: FIX
|
||||||
|
this._mediaRoom.on('____audiolevels', (entries) =>
|
||||||
{
|
{
|
||||||
logger.debug('room "audiolevels" event');
|
logger.debug('room "audiolevels" event');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"debug": "^2.6.4",
|
"debug": "^2.6.8",
|
||||||
"express": "^4.15.2",
|
"express": "^4.15.3",
|
||||||
"mediasoup": "^1.2.3",
|
"mediasoup": "^1.2.5",
|
||||||
"protoo-server": "^1.1.4"
|
"protoo-server": "^1.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"gulp": "git://github.com/gulpjs/gulp.git#4.0",
|
"gulp": "git://github.com/gulpjs/gulp.git#4.0",
|
||||||
"gulp-eslint": "^3.0.1",
|
"gulp-eslint": "^4.0.0",
|
||||||
"gulp-plumber": "^1.1.0"
|
"gulp-plumber": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue