Initial work on Edge
parent
2ea269ea9a
commit
0c63f4cd8c
|
|
@ -5,8 +5,8 @@ import browser from 'bowser';
|
|||
import sdpTransform from 'sdp-transform';
|
||||
import Logger from './Logger';
|
||||
import protooClient from 'protoo-client';
|
||||
import urlFactory from './urlFactory';
|
||||
import utils from './utils';
|
||||
import * as urlFactory from './urlFactory';
|
||||
import * as utils from './utils';
|
||||
|
||||
const logger = new Logger('Client');
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
import React from 'react';
|
||||
import ClipboardButton from 'react-clipboard.js';
|
||||
import browser from 'bowser';
|
||||
import TransitionAppear from './TransitionAppear';
|
||||
import LocalVideo from './LocalVideo';
|
||||
import RemoteVideo from './RemoteVideo';
|
||||
import Stats from './Stats';
|
||||
import Logger from '../Logger';
|
||||
import utils from '../utils';
|
||||
import * as utils from '../utils';
|
||||
import Client from '../Client';
|
||||
|
||||
const logger = new Logger('Room');
|
||||
|
|
@ -285,8 +286,8 @@ export default class Room extends React.Component
|
|||
imageHeight : 80
|
||||
});
|
||||
|
||||
// Start retrieving WebRTC stats (unless mobile)
|
||||
if (utils.isDesktop())
|
||||
// Start retrieving WebRTC stats (unless mobile or Edge).
|
||||
if (utils.isDesktop() && !browser.msedge)
|
||||
{
|
||||
this.setState({ showStats: true });
|
||||
|
||||
|
|
@ -475,10 +476,10 @@ export default class Room extends React.Component
|
|||
|
||||
this.setState({ stats: null });
|
||||
|
||||
this._statsTimer = setTimeout(() =>
|
||||
{
|
||||
getStats.call(this);
|
||||
}, STATS_INTERVAL);
|
||||
// this._statsTimer = setTimeout(() =>
|
||||
// {
|
||||
// getStats.call(this);
|
||||
// }, STATS_INTERVAL);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,105 @@
|
|||
import sdpTransform from 'sdp-transform';
|
||||
|
||||
/**
|
||||
* RTCSessionDescription implementation.
|
||||
*/
|
||||
export default class RTCSessionDescription {
|
||||
/**
|
||||
* RTCSessionDescription constructor.
|
||||
* @param {Object} [data]
|
||||
* @param {String} [data.type] - 'offer' / 'answer'.
|
||||
* @param {String} [data.sdp] - SDP string.
|
||||
* @param {Object} [data._sdpObject] - SDP object generated by the
|
||||
* sdp-transform library.
|
||||
*/
|
||||
constructor(data) {
|
||||
// @type {String}
|
||||
this._sdp = null;
|
||||
|
||||
// @type {Object}
|
||||
this._sdpObject = null;
|
||||
|
||||
// @type {String}
|
||||
this._type = null;
|
||||
|
||||
switch (data.type) {
|
||||
case 'offer':
|
||||
break;
|
||||
case 'answer':
|
||||
break;
|
||||
default:
|
||||
throw new TypeError(`invalid type "${data.type}"`);
|
||||
}
|
||||
|
||||
this._type = data.type;
|
||||
|
||||
if (typeof data.sdp === 'string') {
|
||||
this._sdp = data.sdp;
|
||||
try {
|
||||
this._sdpObject = sdpTransform.parse(data.sdp);
|
||||
} catch (error) {
|
||||
throw new Error(`invalid sdp: ${error}`);
|
||||
}
|
||||
} else if (typeof data._sdpObject === 'object') {
|
||||
this._sdpObject = data._sdpObject;
|
||||
try {
|
||||
this._sdp = sdpTransform.write(data._sdpObject);
|
||||
} catch (error) {
|
||||
throw new Error(`invalid sdp object: ${error}`);
|
||||
}
|
||||
} else {
|
||||
throw new TypeError('invalid sdp or _sdpObject');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sdp field.
|
||||
* @return {String}
|
||||
*/
|
||||
get sdp() {
|
||||
return this._sdp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sdp field.
|
||||
* NOTE: This is not allowed per spec, but lib-jitsi-meet uses it.
|
||||
* @param {String} sdp
|
||||
*/
|
||||
set sdp(sdp) {
|
||||
try {
|
||||
this._sdpObject = sdpTransform.parse(sdp);
|
||||
} catch (error) {
|
||||
throw new Error(`invalid sdp: ${error}`);
|
||||
}
|
||||
|
||||
this._sdp = sdp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal sdp object.
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
get sdpObject() {
|
||||
return this._sdpObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type field.
|
||||
* @return {String}
|
||||
*/
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with type and sdp fields.
|
||||
* @return {Object}
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
sdp: this._sdp,
|
||||
type: this._type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Create a class inheriting from Error.
|
||||
*/
|
||||
function createErrorClass(name) {
|
||||
const klass = class extends Error {
|
||||
/**
|
||||
* Custom error class constructor.
|
||||
* @param {string} message
|
||||
*/
|
||||
constructor(message) {
|
||||
super(message);
|
||||
|
||||
// Override `name` property value and make it non enumerable.
|
||||
Object.defineProperty(this, 'name', { value: name });
|
||||
}
|
||||
};
|
||||
|
||||
return klass;
|
||||
}
|
||||
|
||||
export const InvalidStateError = createErrorClass('InvalidStateError');
|
||||
|
|
@ -0,0 +1,458 @@
|
|||
/* global RTCRtpReceiver */
|
||||
|
||||
import sdpTransform from 'sdp-transform';
|
||||
|
||||
/**
|
||||
* Extract RTP capabilities from remote description.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {RTCRtpCapabilities}
|
||||
*/
|
||||
export function extractCapabilities(sdpObject) {
|
||||
// Map of RtpCodecParameters indexed by payload type.
|
||||
const codecsMap = new Map();
|
||||
|
||||
// Array of RtpHeaderExtensions.
|
||||
const headerExtensions = [];
|
||||
|
||||
for (const m of sdpObject.media) {
|
||||
// Media kind.
|
||||
const kind = m.type;
|
||||
|
||||
if (kind !== 'audio' && kind !== 'video') {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
// Get codecs.
|
||||
for (const rtp of m.rtp) {
|
||||
const codec = {
|
||||
clockRate: rtp.rate,
|
||||
kind,
|
||||
mimeType: `${kind}/${rtp.codec}`,
|
||||
name: rtp.codec,
|
||||
numChannels: rtp.encoding || 1,
|
||||
parameters: {},
|
||||
preferredPayloadType: rtp.payload,
|
||||
rtcpFeedback: []
|
||||
};
|
||||
|
||||
codecsMap.set(codec.preferredPayloadType, codec);
|
||||
}
|
||||
|
||||
// Get codec parameters.
|
||||
for (const fmtp of m.fmtp || []) {
|
||||
const parameters = sdpTransform.parseFmtpConfig(fmtp.config);
|
||||
const codec = codecsMap.get(fmtp.payload);
|
||||
|
||||
if (!codec) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
codec.parameters = parameters;
|
||||
}
|
||||
|
||||
// Get RTCP feedback for each codec.
|
||||
for (const fb of m.rtcpFb || []) {
|
||||
const codec = codecsMap.get(fb.payload);
|
||||
|
||||
if (!codec) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
codec.rtcpFeedback.push({
|
||||
parameter: fb.subtype || '',
|
||||
type: fb.type
|
||||
});
|
||||
}
|
||||
|
||||
// Get RTP header extensions.
|
||||
for (const ext of m.ext || []) {
|
||||
const preferredId = ext.value;
|
||||
const uri = ext.uri;
|
||||
const headerExtension = {
|
||||
kind,
|
||||
uri,
|
||||
preferredId
|
||||
};
|
||||
|
||||
// Check if already present.
|
||||
const duplicated = headerExtensions.find(savedHeaderExtension =>
|
||||
headerExtension.kind === savedHeaderExtension.kind
|
||||
&& headerExtension.uri === savedHeaderExtension.uri
|
||||
);
|
||||
|
||||
if (!duplicated) {
|
||||
headerExtensions.push(headerExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
codecs: Array.from(codecsMap.values()),
|
||||
fecMechanisms: [], // TODO
|
||||
headerExtensions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract DTLS parameters from remote description.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {RTCDtlsParameters}
|
||||
*/
|
||||
export function extractDtlsParameters(sdpObject) {
|
||||
const media = getFirstActiveMediaSection(sdpObject);
|
||||
const fingerprint = media.fingerprint || sdpObject.fingerprint;
|
||||
let role;
|
||||
|
||||
switch (media.setup) {
|
||||
case 'active':
|
||||
role = 'client';
|
||||
break;
|
||||
case 'passive':
|
||||
role = 'server';
|
||||
break;
|
||||
case 'actpass':
|
||||
role = 'auto';
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
role,
|
||||
fingerprints: [
|
||||
{
|
||||
algorithm: fingerprint.type,
|
||||
value: fingerprint.hash
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract ICE candidates from remote description.
|
||||
* NOTE: This implementation assumes a single BUNDLEd transport and rtcp-mux.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {sequence<RTCIceCandidate>}
|
||||
*/
|
||||
export function extractIceCandidates(sdpObject) {
|
||||
const media = getFirstActiveMediaSection(sdpObject);
|
||||
const candidates = [];
|
||||
|
||||
for (const c of media.candidates) {
|
||||
// Ignore RTCP candidates (we assume rtcp-mux).
|
||||
if (c.component !== 1) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
const candidate = {
|
||||
foundation: c.foundation,
|
||||
ip: c.ip,
|
||||
port: c.port,
|
||||
priority: c.priority,
|
||||
protocol: c.transport.toLowerCase(),
|
||||
type: c.type
|
||||
};
|
||||
|
||||
candidates.push(candidate);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract ICE parameters from remote description.
|
||||
* NOTE: This implementation assumes a single BUNDLEd transport.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {RTCIceParameters}
|
||||
*/
|
||||
export function extractIceParameters(sdpObject) {
|
||||
const media = getFirstActiveMediaSection(sdpObject);
|
||||
const usernameFragment = media.iceUfrag;
|
||||
const password = media.icePwd;
|
||||
const icelite = sdpObject.icelite === 'ice-lite';
|
||||
|
||||
return {
|
||||
icelite,
|
||||
password,
|
||||
usernameFragment
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract MID values from remote description.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {map<String, String>} Ordered Map with MID as key and kind as value.
|
||||
*/
|
||||
export function extractMids(sdpObject) {
|
||||
const midToKind = new Map();
|
||||
|
||||
// Ignore disabled media sections.
|
||||
for (const m of sdpObject.media) {
|
||||
midToKind.set(m.mid, m.type);
|
||||
}
|
||||
|
||||
return midToKind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract tracks information.
|
||||
* @param {Object} sdpObject - Remote SDP object generated by sdp-transform.
|
||||
* @return {Map}
|
||||
*/
|
||||
export function extractTrackInfos(sdpObject) {
|
||||
// Map with info about receiving media.
|
||||
// - index: Media SSRC
|
||||
// - value: Object
|
||||
// - kind: 'audio' / 'video'
|
||||
// - ssrc: Media SSRC
|
||||
// - rtxSsrc: RTX SSRC (may be unset)
|
||||
// - streamId: MediaStream.jitsiRemoteId
|
||||
// - trackId: MediaStreamTrack.jitsiRemoteId
|
||||
// - cname: CNAME
|
||||
// @type {map<Number, Object>}
|
||||
const infos = new Map();
|
||||
|
||||
// Map with stream SSRC as index and associated RTX SSRC as value.
|
||||
// @type {map<Number, Number>}
|
||||
const rtxMap = new Map();
|
||||
|
||||
// Set of RTX SSRC values.
|
||||
const rtxSet = new Set();
|
||||
|
||||
for (const m of sdpObject.media) {
|
||||
const kind = m.type;
|
||||
|
||||
if (kind !== 'audio' && kind !== 'video') {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
// Get RTX information.
|
||||
for (const ssrcGroup of m.ssrcGroups || []) {
|
||||
// Just consider FID.
|
||||
if (ssrcGroup.semantics !== 'FID') {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
const ssrcs
|
||||
= ssrcGroup.ssrcs.split(' ').map(ssrc => Number(ssrc));
|
||||
const ssrc = ssrcs[0];
|
||||
const rtxSsrc = ssrcs[1];
|
||||
|
||||
rtxMap.set(ssrc, rtxSsrc);
|
||||
rtxSet.add(rtxSsrc);
|
||||
}
|
||||
|
||||
for (const ssrcObject of m.ssrcs || []) {
|
||||
const ssrc = ssrcObject.id;
|
||||
|
||||
// Ignore RTX.
|
||||
if (rtxSet.has(ssrc)) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
let info = infos.get(ssrc);
|
||||
|
||||
if (!info) {
|
||||
info = {
|
||||
kind,
|
||||
rtxSsrc: rtxMap.get(ssrc),
|
||||
ssrc
|
||||
};
|
||||
|
||||
infos.set(ssrc, info);
|
||||
}
|
||||
|
||||
switch (ssrcObject.attribute) {
|
||||
case 'cname': {
|
||||
info.cname = ssrcObject.value;
|
||||
break;
|
||||
}
|
||||
case 'msid': {
|
||||
const values = ssrcObject.value.split(' ');
|
||||
const streamId = values[0];
|
||||
const trackId = values[1];
|
||||
|
||||
info.streamId = streamId;
|
||||
info.trackId = trackId;
|
||||
break;
|
||||
}
|
||||
case 'mslabel': {
|
||||
const streamId = ssrcObject.value;
|
||||
|
||||
info.streamId = streamId;
|
||||
break;
|
||||
}
|
||||
case 'label': {
|
||||
const trackId = ssrcObject.value;
|
||||
|
||||
info.trackId = trackId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local ORTC RTP capabilities filtered and adapted to the given remote RTP
|
||||
* capabilities.
|
||||
* @param {RTCRtpCapabilities} filterWithCapabilities - RTP capabilities to
|
||||
* filter with.
|
||||
* @return {RTCRtpCapabilities}
|
||||
*/
|
||||
export function getLocalCapabilities(filterWithCapabilities) {
|
||||
const localFullCapabilities = RTCRtpReceiver.getCapabilities();
|
||||
const localCapabilities = {
|
||||
codecs: [],
|
||||
fecMechanisms: [],
|
||||
headerExtensions: []
|
||||
};
|
||||
|
||||
// Map of RTX and codec payloads.
|
||||
// - index: Codec payloadType
|
||||
// - value: Associated RTX payloadType
|
||||
// @type {map<Number, Number>}
|
||||
const remoteRtxMap = new Map();
|
||||
|
||||
// Set codecs.
|
||||
for (const remoteCodec of filterWithCapabilities.codecs) {
|
||||
const remoteCodecName = remoteCodec.name.toLowerCase();
|
||||
|
||||
if (remoteCodecName === 'rtx') {
|
||||
remoteRtxMap.set(
|
||||
remoteCodec.parameters.apt, remoteCodec.preferredPayloadType);
|
||||
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
const localCodec = localFullCapabilities.codecs.find(codec =>
|
||||
codec.name.toLowerCase() === remoteCodecName
|
||||
&& codec.kind === remoteCodec.kind
|
||||
&& codec.clockRate === remoteCodec.clockRate
|
||||
);
|
||||
|
||||
if (!localCodec) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
const codec = {
|
||||
clockRate: localCodec.clockRate,
|
||||
kind: localCodec.kind,
|
||||
mimeType: `${localCodec.kind}/${localCodec.name}`,
|
||||
name: localCodec.name,
|
||||
numChannels: localCodec.numChannels || 1,
|
||||
parameters: {},
|
||||
preferredPayloadType: remoteCodec.preferredPayloadType,
|
||||
rtcpFeedback: []
|
||||
};
|
||||
|
||||
for (const remoteParamName of Object.keys(remoteCodec.parameters)) {
|
||||
const remoteParamValue
|
||||
= remoteCodec.parameters[remoteParamName];
|
||||
|
||||
for (const localParamName of Object.keys(localCodec.parameters)) {
|
||||
const localParamValue
|
||||
= localCodec.parameters[localParamName];
|
||||
|
||||
if (localParamName !== remoteParamName) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
// TODO: We should consider much more cases here, but Edge
|
||||
// does not support many codec parameters.
|
||||
if (localParamValue === remoteParamValue) {
|
||||
// Use this RTP parameter.
|
||||
codec.parameters[localParamName] = localParamValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const remoteFb of remoteCodec.rtcpFeedback) {
|
||||
const localFb = localCodec.rtcpFeedback.find(fb =>
|
||||
fb.type === remoteFb.type
|
||||
&& fb.parameter === remoteFb.parameter
|
||||
);
|
||||
|
||||
if (localFb) {
|
||||
// Use this RTCP feedback.
|
||||
codec.rtcpFeedback.push(localFb);
|
||||
}
|
||||
}
|
||||
|
||||
// Use this codec.
|
||||
localCapabilities.codecs.push(codec);
|
||||
}
|
||||
|
||||
// Add RTX for video codecs.
|
||||
for (const codec of localCapabilities.codecs) {
|
||||
const payloadType = codec.preferredPayloadType;
|
||||
|
||||
if (!remoteRtxMap.has(payloadType)) {
|
||||
continue; // eslint-disable-line no-continue
|
||||
}
|
||||
|
||||
const rtxCodec = {
|
||||
clockRate: codec.clockRate,
|
||||
kind: codec.kind,
|
||||
mimeType: `${codec.kind}/rtx`,
|
||||
name: 'rtx',
|
||||
parameters: {
|
||||
apt: payloadType
|
||||
},
|
||||
preferredPayloadType: remoteRtxMap.get(payloadType),
|
||||
rtcpFeedback: []
|
||||
};
|
||||
|
||||
// Add RTX codec.
|
||||
localCapabilities.codecs.push(rtxCodec);
|
||||
}
|
||||
|
||||
// Add RTP header extensions.
|
||||
for (const remoteExtension of filterWithCapabilities.headerExtensions) {
|
||||
const localExtension
|
||||
= localFullCapabilities.headerExtensions.find(extension =>
|
||||
extension.kind === remoteExtension.kind
|
||||
&& extension.uri === remoteExtension.uri
|
||||
);
|
||||
|
||||
if (localExtension) {
|
||||
const extension = {
|
||||
kind: localExtension.kind,
|
||||
preferredEncrypt: Boolean(remoteExtension.preferredEncrypt),
|
||||
preferredId: remoteExtension.preferredId,
|
||||
uri: localExtension.uri
|
||||
};
|
||||
|
||||
// Use this RTP header extension.
|
||||
localCapabilities.headerExtensions.push(extension);
|
||||
}
|
||||
}
|
||||
|
||||
// Add FEC mechanisms.
|
||||
// NOTE: We don't support FEC yet and, in fact, neither does Edge.
|
||||
for (const remoteFecMechanism of filterWithCapabilities.fecMechanisms) {
|
||||
const localFecMechanism
|
||||
= localFullCapabilities.fecMechanisms.find(fec =>
|
||||
fec === remoteFecMechanism
|
||||
);
|
||||
|
||||
if (localFecMechanism) {
|
||||
// Use this FEC mechanism.
|
||||
localCapabilities.fecMechanisms.push(localFecMechanism);
|
||||
}
|
||||
}
|
||||
|
||||
return localCapabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first acive media section.
|
||||
* @param {Object} sdpObject - SDP object generated by sdp-transform.
|
||||
* @return {Object} SDP media section as parsed by sdp-transform.
|
||||
*/
|
||||
function getFirstActiveMediaSection(sdpObject) {
|
||||
return sdpObject.media.find(m =>
|
||||
m.iceUfrag && m.port !== 0
|
||||
);
|
||||
}
|
||||
|
|
@ -9,13 +9,26 @@ import ReactDOM from 'react-dom';
|
|||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
import randomString from 'random-string';
|
||||
import Logger from './Logger';
|
||||
import utils from './utils';
|
||||
import * as utils from './utils';
|
||||
import edgeRTCPeerConnection from './edge/RTCPeerConnection';
|
||||
import edgeRTCSessionDescription from './edge/RTCSessionDescription';
|
||||
import App from './components/App';
|
||||
|
||||
// TODO: TMP
|
||||
global.BROWSER = browser;
|
||||
|
||||
const REGEXP_FRAGMENT_ROOM_ID = new RegExp('^#room-id=([0-9a-zA-Z_\-]+)$');
|
||||
const logger = new Logger();
|
||||
|
||||
logger.debug('detected browser [name:"%s", version:%s]', browser.name, browser.version);
|
||||
logger.warn('detected browser [name:"%s", version:%s]', browser.name, browser.version);
|
||||
|
||||
if (browser.msedge)
|
||||
{
|
||||
logger.warn('EDGE detected, overriding WebRTC global classes');
|
||||
|
||||
window.RTCPeerConnection = edgeRTCPeerConnection;
|
||||
window.RTCSessionDescription = edgeRTCSessionDescription;
|
||||
}
|
||||
|
||||
injectTapEventPlugin();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,11 @@
|
|||
|
||||
const config = require('../config');
|
||||
|
||||
module.exports =
|
||||
export function getProtooUrl(peerId, roomId)
|
||||
{
|
||||
getProtooUrl(peerId, roomId)
|
||||
{
|
||||
let hostname = window.location.hostname;
|
||||
let port = config.protoo.listenPort;
|
||||
let url = `wss://${hostname}:${port}/?peer-id=${peerId}&room-id=${roomId}`;
|
||||
let hostname = window.location.hostname;
|
||||
let port = config.protoo.listenPort;
|
||||
let url = `wss://${hostname}:${port}/?peer-id=${peerId}&room-id=${roomId}`;
|
||||
|
||||
return url;
|
||||
}
|
||||
};
|
||||
return url;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,61 @@
|
|||
'use strict';
|
||||
|
||||
import browser from 'bowser';
|
||||
import randomNumberLib from 'random-number';
|
||||
import Logger from './Logger';
|
||||
|
||||
const logger = new Logger('utils');
|
||||
const randomNumberGenerator = randomNumberLib.generator(
|
||||
{
|
||||
min : 10000000,
|
||||
max : 99999999,
|
||||
integer : true
|
||||
});
|
||||
|
||||
let mediaQueryDetectorElem;
|
||||
|
||||
module.exports =
|
||||
export function initialize()
|
||||
{
|
||||
initialize()
|
||||
logger.debug('initialize()');
|
||||
|
||||
// Media query detector stuff
|
||||
mediaQueryDetectorElem = document.getElementById('mediasoup-demo-app-media-query-detector');
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function isDesktop()
|
||||
{
|
||||
return !!mediaQueryDetectorElem.offsetParent;
|
||||
}
|
||||
|
||||
export function isMobile()
|
||||
{
|
||||
return !mediaQueryDetectorElem.offsetParent;
|
||||
}
|
||||
|
||||
export function isPlanB()
|
||||
{
|
||||
if (browser.chrome || browser.chromium || browser.opera || browser.msedge)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
export function randomNumber()
|
||||
{
|
||||
return randomNumberGenerator();
|
||||
}
|
||||
|
||||
export function closeMediaStream(stream)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
let tracks = stream.getTracks();
|
||||
|
||||
for (let i=0, len=tracks.length; i < len; i++)
|
||||
{
|
||||
logger.debug('initialize()');
|
||||
|
||||
// Media query detector stuff
|
||||
mediaQueryDetectorElem = document.getElementById('mediasoup-demo-app-media-query-detector');
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
isDesktop()
|
||||
{
|
||||
return !!mediaQueryDetectorElem.offsetParent;
|
||||
},
|
||||
|
||||
isMobile()
|
||||
{
|
||||
return !mediaQueryDetectorElem.offsetParent;
|
||||
},
|
||||
|
||||
isPlanB()
|
||||
{
|
||||
if (browser.chrome || browser.chromium || browser.opera)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
|
||||
closeMediaStream(stream)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
let tracks = stream.getTracks();
|
||||
|
||||
for (let i=0, len=tracks.length; i < len; i++)
|
||||
{
|
||||
tracks[i].stop();
|
||||
}
|
||||
tracks[i].stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"hark": "ibc/hark#main-with-raf",
|
||||
"material-ui": "^0.17.4",
|
||||
"protoo-client": "^1.1.4",
|
||||
"random-number": "0.0.7",
|
||||
"random-string": "^0.2.0",
|
||||
"react": "^15.5.4",
|
||||
"react-addons-css-transition-group": "^15.5.2",
|
||||
|
|
@ -24,7 +25,8 @@
|
|||
"react-tap-event-plugin": "^2.0.1",
|
||||
"sdp-transform": "^2.3.0",
|
||||
"url-parse": "^1.1.8",
|
||||
"webrtc-adapter": "^3.3.3"
|
||||
"webrtc-adapter": "^3.3.3",
|
||||
"yaeti": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-object-assign": "^6.22.0",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"colors": "^1.1.2",
|
||||
"debug": "^2.6.4",
|
||||
"express": "^4.15.2",
|
||||
"mediasoup": "^1.0.1",
|
||||
"mediasoup": "^1.2.3",
|
||||
"protoo-server": "^1.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue