|
|
|
import domready from 'domready';
|
|
|
|
import UrlParse from 'url-parse';
|
|
|
|
import React from 'react';
|
|
|
|
import { render } from 'react-dom';
|
|
|
|
import { Provider } from 'react-redux';
|
|
|
|
import {
|
|
|
|
applyMiddleware as applyReduxMiddleware,
|
|
|
|
createStore as createReduxStore
|
|
|
|
} from 'redux';
|
|
|
|
import thunk from 'redux-thunk';
|
|
|
|
// import { createLogger as createReduxLogger } from 'redux-logger';
|
|
|
|
import randomString from 'random-string';
|
|
|
|
import * as faceapi from 'face-api.js';
|
|
|
|
import Logger from './Logger';
|
|
|
|
import * as utils from './utils';
|
|
|
|
import randomName from './randomName';
|
|
|
|
import deviceInfo from './deviceInfo';
|
|
|
|
import RoomClient from './RoomClient';
|
|
|
|
import RoomContext from './RoomContext';
|
|
|
|
import * as cookiesManager from './cookiesManager';
|
|
|
|
import * as stateActions from './redux/stateActions';
|
|
|
|
import reducers from './redux/reducers';
|
|
|
|
import Room from './components/Room';
|
|
|
|
import { log } from 'gulp-util';
|
|
|
|
|
|
|
|
const logger = new Logger();
|
|
|
|
const reduxMiddlewares = [ thunk ];
|
|
|
|
|
|
|
|
// if (process.env.NODE_ENV === 'development')
|
|
|
|
// {
|
|
|
|
// const reduxLogger = createReduxLogger(
|
|
|
|
// {
|
|
|
|
// duration : true,
|
|
|
|
// timestamp : false,
|
|
|
|
// level : 'log',
|
|
|
|
// logErrors : true
|
|
|
|
// });
|
|
|
|
|
|
|
|
// reduxMiddlewares.push(reduxLogger);
|
|
|
|
// }
|
|
|
|
|
|
|
|
let roomClient;
|
|
|
|
const store = createReduxStore(
|
|
|
|
reducers,
|
|
|
|
undefined,
|
|
|
|
applyReduxMiddleware(...reduxMiddlewares)
|
|
|
|
);
|
|
|
|
|
|
|
|
window.STORE = store;
|
|
|
|
|
|
|
|
RoomClient.init({ store });
|
|
|
|
|
|
|
|
domready(async () =>
|
|
|
|
{
|
|
|
|
logger.debug('DOM ready');
|
|
|
|
|
|
|
|
await utils.initialize();
|
|
|
|
|
|
|
|
run();
|
|
|
|
});
|
|
|
|
|
|
|
|
async function run()
|
|
|
|
{
|
|
|
|
logger.debug('run() [environment:%s]', process.env.NODE_ENV);
|
|
|
|
|
|
|
|
const urlParser = new UrlParse(window.location.href, true);
|
|
|
|
const peerId = randomString({ length: 8 }).toLowerCase();
|
|
|
|
|
|
|
|
// 将字符串按 & 分隔
|
|
|
|
const parts = urlParser.href.split('&');
|
|
|
|
// 提取并解析每个部分
|
|
|
|
const params = parts.slice(1).reduce((acc, part) => {
|
|
|
|
const [key, value] = part.split('=');
|
|
|
|
if (key && value) acc[key] = value;
|
|
|
|
return acc;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
let roomId = params['roomId'];
|
|
|
|
let userName = decodeURIComponent(params['userName']);
|
|
|
|
|
|
|
|
|
|
|
|
let displayName = userName || (cookiesManager.getUser() || {}).displayName;
|
|
|
|
const handlerName = urlParser.query.handlerName || urlParser.query.handler;
|
|
|
|
const forceTcp = urlParser.query.forceTcp === 'true';
|
|
|
|
const produce = urlParser.query.produce !== 'false';
|
|
|
|
const consume = urlParser.query.consume !== 'false';
|
|
|
|
const datachannel = urlParser.query.datachannel !== 'false';
|
|
|
|
const forceVP8 = urlParser.query.forceVP8 === 'true';
|
|
|
|
const forceH264 = urlParser.query.forceH264 === 'true';
|
|
|
|
const forceVP9 = urlParser.query.forceVP9 === 'true';
|
|
|
|
const enableWebcamLayers = urlParser.query.enableWebcamLayers !== 'false';
|
|
|
|
const enableSharingLayers = urlParser.query.enableSharingLayers !== 'false';
|
|
|
|
const webcamScalabilityMode = urlParser.query.webcamScalabilityMode;
|
|
|
|
const sharingScalabilityMode = urlParser.query.sharingScalabilityMode;
|
|
|
|
const numSimulcastStreams = urlParser.query.numSimulcastStreams ?
|
|
|
|
Number(urlParser.query.numSimulcastStreams) : 3;
|
|
|
|
const info = urlParser.query.info === 'true';
|
|
|
|
const faceDetection = urlParser.query.faceDetection === 'true';
|
|
|
|
const externalVideo = urlParser.query.externalVideo === 'true';
|
|
|
|
const throttleSecret = urlParser.query.throttleSecret;
|
|
|
|
const e2eKey = urlParser.query.e2eKey;
|
|
|
|
const consumerReplicas = urlParser.query.consumerReplicas;
|
|
|
|
|
|
|
|
// Enable face detection on demand.
|
|
|
|
if (faceDetection)
|
|
|
|
await faceapi.loadTinyFaceDetectorModel('/resources/face-detector-models');
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
{
|
|
|
|
// eslint-disable-next-line require-atomic-updates
|
|
|
|
window.SHOW_INFO = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (throttleSecret)
|
|
|
|
{
|
|
|
|
// eslint-disable-next-line require-atomic-updates
|
|
|
|
window.NETWORK_THROTTLE_SECRET = throttleSecret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!roomId)
|
|
|
|
{
|
|
|
|
roomId = randomString({ length: 8 }).toLowerCase();
|
|
|
|
|
|
|
|
urlParser.query.roomId = roomId;
|
|
|
|
window.history.pushState('', '', urlParser.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the effective/shareable Room URL.
|
|
|
|
const roomUrlParser = new UrlParse(window.location.href, true);
|
|
|
|
|
|
|
|
for (const key of Object.keys(roomUrlParser.query))
|
|
|
|
{
|
|
|
|
// Don't keep some custom params.
|
|
|
|
switch (key)
|
|
|
|
{
|
|
|
|
case 'roomId':
|
|
|
|
case 'handlerName':
|
|
|
|
case 'handler':
|
|
|
|
case 'forceTcp':
|
|
|
|
case 'produce':
|
|
|
|
case 'consume':
|
|
|
|
case 'datachannel':
|
|
|
|
case 'forceVP8':
|
|
|
|
case 'forceH264':
|
|
|
|
case 'forceVP9':
|
|
|
|
case 'enableWebcamLayers':
|
|
|
|
case 'enableSharingLayers':
|
|
|
|
case 'webcamScalabilityMode':
|
|
|
|
case 'sharingScalabilityMode':
|
|
|
|
case 'numSimulcastStreams':
|
|
|
|
case 'info':
|
|
|
|
case 'faceDetection':
|
|
|
|
case 'externalVideo':
|
|
|
|
case 'throttleSecret':
|
|
|
|
case 'e2eKey':
|
|
|
|
case 'consumerReplicas':
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
delete roomUrlParser.query[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete roomUrlParser.hash;
|
|
|
|
|
|
|
|
const roomUrl = roomUrlParser.toString();
|
|
|
|
|
|
|
|
let displayNameSet;
|
|
|
|
|
|
|
|
// If displayName was provided via URL or Cookie, we are done.
|
|
|
|
if (displayName)
|
|
|
|
{
|
|
|
|
displayNameSet = true;
|
|
|
|
}
|
|
|
|
// Otherwise pick a random name and mark as "not set".
|
|
|
|
else
|
|
|
|
{
|
|
|
|
displayNameSet = false;
|
|
|
|
displayName = randomName();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get current device info.
|
|
|
|
const device = deviceInfo();
|
|
|
|
|
|
|
|
store.dispatch(
|
|
|
|
stateActions.setRoomUrl(roomUrl));
|
|
|
|
|
|
|
|
store.dispatch(
|
|
|
|
stateActions.setRoomFaceDetection(faceDetection));
|
|
|
|
|
|
|
|
store.dispatch(
|
|
|
|
stateActions.setMe({ peerId, displayName, displayNameSet, device }));
|
|
|
|
|
|
|
|
roomClient = new RoomClient(
|
|
|
|
{
|
|
|
|
roomId,
|
|
|
|
peerId,
|
|
|
|
displayName,
|
|
|
|
device,
|
|
|
|
handlerName : handlerName,
|
|
|
|
forceTcp,
|
|
|
|
produce,
|
|
|
|
consume,
|
|
|
|
datachannel,
|
|
|
|
forceVP8,
|
|
|
|
forceH264,
|
|
|
|
forceVP9,
|
|
|
|
enableWebcamLayers,
|
|
|
|
enableSharingLayers,
|
|
|
|
webcamScalabilityMode,
|
|
|
|
sharingScalabilityMode,
|
|
|
|
numSimulcastStreams,
|
|
|
|
externalVideo,
|
|
|
|
e2eKey,
|
|
|
|
consumerReplicas
|
|
|
|
});
|
|
|
|
|
|
|
|
// NOTE: For debugging.
|
|
|
|
// eslint-disable-next-line require-atomic-updates
|
|
|
|
window.CLIENT = roomClient;
|
|
|
|
// eslint-disable-next-line require-atomic-updates
|
|
|
|
window.CC = roomClient;
|
|
|
|
|
|
|
|
render(
|
|
|
|
<Provider store={store}>
|
|
|
|
<RoomContext.Provider value={roomClient}>
|
|
|
|
<Room />
|
|
|
|
</RoomContext.Provider>
|
|
|
|
</Provider>,
|
|
|
|
document.getElementById('mediasoup-demo-app-container')
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: Debugging stuff.
|
|
|
|
|
|
|
|
window.__sendSdps = function()
|
|
|
|
{
|
|
|
|
logger.warn('>>> send transport local SDP offer:');
|
|
|
|
logger.warn(
|
|
|
|
roomClient._sendTransport._handler._pc.localDescription.sdp);
|
|
|
|
|
|
|
|
logger.warn('>>> send transport remote SDP answer:');
|
|
|
|
logger.warn(
|
|
|
|
roomClient._sendTransport._handler._pc.remoteDescription.sdp);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.__recvSdps = function()
|
|
|
|
{
|
|
|
|
logger.warn('>>> recv transport remote SDP offer:');
|
|
|
|
logger.warn(
|
|
|
|
roomClient._recvTransport._handler._pc.remoteDescription.sdp);
|
|
|
|
|
|
|
|
logger.warn('>>> recv transport local SDP answer:');
|
|
|
|
logger.warn(
|
|
|
|
roomClient._recvTransport._handler._pc.localDescription.sdp);
|
|
|
|
};
|
|
|
|
|
|
|
|
let dataChannelTestInterval = null;
|
|
|
|
|
|
|
|
window.__startDataChannelTest = function()
|
|
|
|
{
|
|
|
|
let number = 0;
|
|
|
|
|
|
|
|
const buffer = new ArrayBuffer(32);
|
|
|
|
const view = new DataView(buffer);
|
|
|
|
|
|
|
|
dataChannelTestInterval = window.setInterval(() =>
|
|
|
|
{
|
|
|
|
if (window.DP)
|
|
|
|
{
|
|
|
|
view.setUint32(0, number++);
|
|
|
|
roomClient.sendChatMessage(buffer);
|
|
|
|
}
|
|
|
|
}, 100);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.__stopDataChannelTest = function()
|
|
|
|
{
|
|
|
|
window.clearInterval(dataChannelTestInterval);
|
|
|
|
|
|
|
|
const buffer = new ArrayBuffer(32);
|
|
|
|
const view = new DataView(buffer);
|
|
|
|
|
|
|
|
if (window.DP)
|
|
|
|
{
|
|
|
|
view.setUint32(0, Math.pow(2, 32) - 1);
|
|
|
|
window.DP.send(buffer);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
window.__testSctp = async function({ timeout = 100, bot = false } = {})
|
|
|
|
{
|
|
|
|
let dp;
|
|
|
|
|
|
|
|
if (!bot)
|
|
|
|
{
|
|
|
|
await window.CLIENT.enableChatDataProducer();
|
|
|
|
|
|
|
|
dp = window.CLIENT._chatDataProducer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
await window.CLIENT.enableBotDataProducer();
|
|
|
|
|
|
|
|
dp = window.CLIENT._botDataProducer;
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.warn(
|
|
|
|
'<<< testSctp: DataProducer created [bot:%s, streamId:%d, readyState:%s]',
|
|
|
|
bot ? 'true' : 'false',
|
|
|
|
dp.sctpStreamParameters.streamId,
|
|
|
|
dp.readyState);
|
|
|
|
|
|
|
|
function send()
|
|
|
|
{
|
|
|
|
dp.send(`I am streamId ${dp.sctpStreamParameters.streamId}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dp.readyState === 'open')
|
|
|
|
{
|
|
|
|
send();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dp.on('open', () =>
|
|
|
|
{
|
|
|
|
logger.warn(
|
|
|
|
'<<< testSctp: DataChannel open [streamId:%d]',
|
|
|
|
dp.sctpStreamParameters.streamId);
|
|
|
|
|
|
|
|
send();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(() => window.__testSctp({ timeout, bot }), timeout);
|
|
|
|
};
|
|
|
|
|
|
|
|
setInterval(() =>
|
|
|
|
{
|
|
|
|
if (window.CLIENT._sendTransport)
|
|
|
|
{
|
|
|
|
window.H1 = window.CLIENT._sendTransport._handler;
|
|
|
|
window.PC1 = window.CLIENT._sendTransport._handler._pc;
|
|
|
|
window.DP = window.CLIENT._chatDataProducer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete window.PC1;
|
|
|
|
delete window.DP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (window.CLIENT._recvTransport)
|
|
|
|
{
|
|
|
|
window.H2 = window.CLIENT._recvTransport._handler;
|
|
|
|
window.PC2 = window.CLIENT._recvTransport._handler._pc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete window.PC2;
|
|
|
|
}
|
|
|
|
}, 2000);
|