mirror of
https://github.com/edumeet/edumeet.git
synced 2026-07-30 06:20:39 +00:00
Merge branch 'develop' of https://github.com/havfo/multiparty-meeting into develop
This commit is contained in:
commit
cffcec4273
48 changed files with 797 additions and 110 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -3,11 +3,12 @@ node_modules/
|
|||
/app/build/
|
||||
/app/public/config/config.js
|
||||
/app/public/images/logo.*
|
||||
!/app/public/images/logo.example.png
|
||||
!/app/public/images/logo.edumeet.svg
|
||||
/server/config/
|
||||
!/server/config/config.example.js
|
||||
/server/public/
|
||||
/server/certs/
|
||||
!/server/certs/mediasoup-demo.localhost.*
|
||||
.vscode
|
||||
/app/public/config/*.pem
|
||||
/app/public/config/*.pem
|
||||
yarn.lock
|
||||
|
|
@ -29,7 +29,7 @@ If you want the ansible approach, you can find ansible role [here](https://githu
|
|||
If you want to install it on the Debian & Ubuntu based operating systems.
|
||||
|
||||
* Prerequisites:
|
||||
edumeet will run on nodejs v10.x and later versions. (v12.x has a know issue for now, please until it will be fixed use the 10.x version)
|
||||
edumeet will run on nodejs v14.x (tested with v14.15.4 version).
|
||||
To install see here [here](https://github.com/nodesource/distributions/blob/master/README.md#debinstall).
|
||||
|
||||
* Download .deb package from [here](https://github.com/edumeet/edumeet/actions?query=workflow%3ADeployer+branch%3Amaster+is%3Asuccess) (job artifact)
|
||||
|
|
@ -61,7 +61,7 @@ $ sudo systemctl start edumeet
|
|||
## Manual installation
|
||||
|
||||
* Prerequisites:
|
||||
Currently edumeet will only run on nodejs v13.x
|
||||
Currently edumeet will run on nodejs v14.x
|
||||
To install see here [here](https://github.com/nodesource/distributions/blob/master/README.md#debinstall).
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"@material-ui/core": "^4.5.1",
|
||||
"@material-ui/icons": "^4.5.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||
"@react-hook/window-size": "^3.0.7",
|
||||
"bowser": "^2.7.0",
|
||||
"classnames": "^2.2.6",
|
||||
"create-torrent": "^4.4.1",
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
"is-electron": "^2.2.0",
|
||||
"marked": "^0.8.0",
|
||||
"material-ui-popup-state": "^1.7.0",
|
||||
"mediasoup-client": "^3.6.5",
|
||||
"mediasoup-client": "^3.6.21",
|
||||
"notistack": "^0.9.5",
|
||||
"prop-types": "^15.7.2",
|
||||
"random-string": "^0.2.0",
|
||||
|
|
@ -37,6 +38,8 @@
|
|||
"react-cookie-consent": "^2.5.0",
|
||||
"react-dom": "^16.10.2",
|
||||
"react-flip-toolkit": "^7.0.9",
|
||||
"react-image-file-resizer": "^0.3.8",
|
||||
"react-images-upload": "^1.2.0",
|
||||
"react-intl": "^3.4.0",
|
||||
"react-intl-redux": "^2.2.0",
|
||||
"react-redux": "^7.1.1",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ var config =
|
|||
loginEnabled : false,
|
||||
developmentPort : 3443,
|
||||
productionPort : 443,
|
||||
// If the server component runs on a different host than the app
|
||||
// you can uncomment the following line and specify the host name.
|
||||
//serverHostname : 'external-server.com',
|
||||
|
||||
/**
|
||||
* Supported browsers version
|
||||
|
|
@ -74,6 +77,11 @@ var config =
|
|||
{ scaleResolutionDownBy: 2 },
|
||||
{ scaleResolutionDownBy: 1 }
|
||||
],
|
||||
// The adaptive spatial layer selection scaling factor (in the range [0.5, 1.0])
|
||||
// example:
|
||||
// with level width=640px, the minimum width required to trigger the
|
||||
// level change will be: 640 * 0.75 = 480px
|
||||
adaptiveScalingFactor: 0.75,
|
||||
|
||||
/**
|
||||
* Alternative simulcast setting:
|
||||
|
|
@ -163,7 +171,7 @@ var config =
|
|||
lockLastN : false,
|
||||
// Show logo if "logo" is not null, else show title
|
||||
// Set logo file name using logo.* pattern like "logo.png" to not track it by git
|
||||
logo : 'images/logo.example.png',
|
||||
logo : 'images/logo.edumeet.svg',
|
||||
title : 'edumeet',
|
||||
// Service & Support URL
|
||||
// if not set then not displayed on the about modals
|
||||
|
|
|
|||
88
app/public/images/logo.edumeet.svg
Normal file
88
app/public/images/logo.edumeet.svg
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="139"
|
||||
height="30"
|
||||
viewBox="0 0 799.66669 172.29333"
|
||||
sodipodi:docname="edumeet_logo.eps"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1014"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.53772404"
|
||||
inkscape:cx="399.83334"
|
||||
inkscape:cy="86.146667"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="ink_ext_XXXXXX"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,172.29333)"><g
|
||||
id="g12"
|
||||
transform="scale(0.1)"><path
|
||||
d="m 1667.64,373.02 c 0,0 -193.65,11.343 -394.11,119.109 72.23,293.047 -69.56,605.841 -353.264,738.851 C 597.211,1382.37 212.637,1243.22 61.2031,920.25 -90.1719,597.238 48.9063,212.57 371.965,61.1602 659.195,-73.4609 995.113,21.8086 1172.46,271.719 c 248.85,5.543 495.18,101.301 495.18,101.301"
|
||||
style="fill:#3871c1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path14"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 2241.9,497.863 c 1.02,45.887 -19.37,121.34 -102.98,121.34 -77.5,0 -110.14,-70.355 -115.23,-121.34 z m -218.21,-87.687 c 3.06,-89.742 73.42,-128.485 152.96,-128.485 58.12,0 99.91,8.161 137.64,22.434 l 18.37,-86.676 c -42.83,-17.34 -101.98,-30.59 -173.35,-30.59 -161.11,0 -255.95,98.922 -255.95,250.844 0,137.652 83.62,267.16 242.68,267.16 161.12,0 214.15,-132.562 214.15,-241.672 0,-23.453 -2.05,-41.8 -4.09,-53.015 h -332.41"
|
||||
style="fill:#004d7b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path16"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 2793.57,486.648 c 0,10.196 -1.01,22.434 -3.06,32.629 -11.2,48.946 -50.97,88.711 -108.08,88.711 -80.55,0 -125.42,-71.375 -125.42,-164.168 0,-90.75 44.87,-157.031 124.41,-157.031 50.98,0 95.85,34.672 108.08,88.715 3.06,11.215 4.07,23.449 4.07,36.703 z m 125.42,434.379 V 336.754 c 0,-50.973 2.05,-106.043 4.09,-139.703 h -112.17 l -5.1,78.527 h -2.05 c -29.55,-55.066 -89.71,-89.738 -162.11,-89.738 -118.29,0 -212.1,100.949 -212.1,253.898 -1.01,166.211 102.98,265.125 222.29,265.125 68.32,0 117.27,-28.554 139.69,-65.261 h 2.04 v 281.425 h 125.42"
|
||||
style="fill:#004d7b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 3498.2,345.93 c 0,-59.141 2.04,-108.078 4.09,-148.879 h -110.13 l -6.12,75.461 h -2.05 c -21.4,-35.684 -70.36,-86.672 -159.06,-86.672 -90.75,0 -173.35,54.051 -173.35,216.172 V 693.641 H 3177 V 423.422 c 0,-82.586 26.52,-135.613 92.79,-135.613 49.98,0 82.6,35.691 95.85,67.3 4.09,11.211 7.14,24.469 7.14,38.746 V 693.641 H 3498.2 V 345.93"
|
||||
style="fill:#004d7b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path20"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 4219.13,477.465 c -4.09,88.715 -9.19,195.781 -8.15,288.57 h -3.06 c -22.44,-83.609 -51,-176.398 -81.58,-263.07 l -99.93,-297.754 h -96.87 l -91.78,293.672 c -26.5,87.687 -50.97,181.5 -69.34,267.152 h -2.02 c -3.08,-89.726 -7.14,-198.836 -12.24,-292.648 L 3738.87,197.051 H 3621.6 l 45.88,687.273 h 165.19 l 89.73,-276.336 c 24.48,-80.558 45.88,-162.129 63.23,-238.609 h 3.04 c 18.37,74.441 41.82,159.07 68.32,239.629 l 94.84,275.316 h 163.14 l 39.78,-687.273 h -122.38 l -13.24,280.414"
|
||||
style="fill:#3871c1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path22"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="M 4866.67,501.941 H 4607.68 V 300.043 h 289.58 V 197.051 h -415 v 687.273 h 399.7 V 781.336 H 4607.68 V 603.91 h 258.99 V 501.941"
|
||||
style="fill:#3871c1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path24"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 5391.83,501.941 h -259 V 300.043 h 289.59 V 197.051 h -415.01 v 687.273 h 399.71 V 781.336 H 5132.83 V 603.91 h 259 V 501.941"
|
||||
style="fill:#3871c1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path26"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 5674.31,779.297 h -195.78 v 105.027 h 519.02 V 779.297 H 5799.73 V 197.051 h -125.42 v 582.246"
|
||||
style="fill:#3871c1;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path28"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 804.438,984.711 c -187.032,87.649 -409.68,7.086 -497.36,-179.902 -55.531,-118.496 -43.469,-251.282 20.344,-355 -108.164,-67.911 -203.207,-92.024 -203.207,-92.024 0,0 143.82,-26.316 302.43,-14.781 18.66,-13.457 38.761,-25.453 60.351,-35.578 186.984,-87.625 409.707,-7.102 497.359,179.906 87.675,187.047 7.114,409.691 -179.917,497.379"
|
||||
style="fill:#f9a72b;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path30"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="M 831.895,558.988 C 783.809,456.395 661.621,412.215 559.043,460.293 456.438,508.387 412.262,630.559 460.344,733.152 c 48.094,102.582 170.242,146.778 272.847,98.696 102.61,-48.106 146.797,-170.246 98.704,-272.86"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path32"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.8 KiB |
|
|
@ -20,6 +20,7 @@ import Spotlights from './Spotlights';
|
|||
import { permissions } from './permissions';
|
||||
import * as locales from './translations/locales';
|
||||
import { createIntl } from 'react-intl';
|
||||
import { parseScalabilityMode } from 'mediasoup-client';
|
||||
|
||||
let createTorrent;
|
||||
|
||||
|
|
@ -102,6 +103,67 @@ const VIDEO_SVC_ENCODINGS =
|
|||
{ scalabilityMode: 'S3T3', dtx: true }
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the simulcast `encodings` array extracting the resolution scalings
|
||||
* array.
|
||||
* ref. https://www.w3.org/TR/webrtc/#rtp-media-api
|
||||
*
|
||||
* @param {*} encodings
|
||||
* @returns the resolution scalings array
|
||||
*/
|
||||
function getResolutionScalings(encodings)
|
||||
{
|
||||
const resolutionScalings = [];
|
||||
|
||||
// SVC encodings
|
||||
if (encodings.length === 1)
|
||||
{
|
||||
const { spatialLayers, temporalLayers } =
|
||||
mediasoupClient.parseScalabilityMode(encodings[0].scalabilityMode);
|
||||
|
||||
for (let i=0; i < spatialLayers; i++)
|
||||
{
|
||||
resolutionScalings.push(2 ** (spatialLayers - i - 1));
|
||||
}
|
||||
|
||||
return resolutionScalings;
|
||||
}
|
||||
|
||||
// Simulcast encodings
|
||||
let scaleResolutionDownByDefined = false;
|
||||
|
||||
encodings.forEach((encoding) =>
|
||||
{
|
||||
if (encoding.scaleResolutionDownBy !== undefined)
|
||||
{
|
||||
// at least one scaleResolutionDownBy is defined
|
||||
scaleResolutionDownByDefined = true;
|
||||
// scaleResolutionDownBy must be >= 1.0
|
||||
resolutionScalings.push(Math.max(1.0, encoding.scaleResolutionDownBy));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If encodings contains any encoding whose scaleResolutionDownBy
|
||||
// attribute is defined, set any undefined scaleResolutionDownBy
|
||||
// of the other encodings to 1.0.
|
||||
resolutionScalings.push(1.0);
|
||||
}
|
||||
});
|
||||
|
||||
// If the scaleResolutionDownBy attribues of sendEncodings are
|
||||
// still undefined, initialize each encoding's scaleResolutionDownBy
|
||||
// to 2^(length of sendEncodings - encoding index - 1).
|
||||
if (!scaleResolutionDownByDefined)
|
||||
{
|
||||
encodings.forEach((encoding, index) =>
|
||||
{
|
||||
resolutionScalings[index] = 2 ** (encodings.length - index - 1);
|
||||
});
|
||||
}
|
||||
|
||||
return resolutionScalings;
|
||||
}
|
||||
|
||||
let store;
|
||||
|
||||
let intl;
|
||||
|
|
@ -259,6 +321,10 @@ export default class RoomClient
|
|||
|
||||
this.setLocale(store.getState().intl.locale);
|
||||
|
||||
if (store.getState().settings.localPicture)
|
||||
{
|
||||
store.dispatch(meActions.setPicture(store.getState().settings.localPicture));
|
||||
}
|
||||
}
|
||||
|
||||
close()
|
||||
|
|
@ -542,6 +608,13 @@ export default class RoomClient
|
|||
|
||||
}
|
||||
|
||||
setPicture(picture)
|
||||
{
|
||||
store.dispatch(settingsActions.setLocalPicture(picture));
|
||||
store.dispatch(meActions.setPicture(picture));
|
||||
this.changePicture(picture);
|
||||
}
|
||||
|
||||
receiveLoginChildWindow(data)
|
||||
{
|
||||
logger.debug('receiveFromChildWindow() | [data:"%o"]', data);
|
||||
|
|
@ -549,7 +622,11 @@ export default class RoomClient
|
|||
const { displayName, picture } = data;
|
||||
|
||||
store.dispatch(settingsActions.setDisplayName(displayName));
|
||||
store.dispatch(meActions.setPicture(picture));
|
||||
|
||||
if (!store.getState().settings.localPicture)
|
||||
{
|
||||
store.dispatch(meActions.setPicture(picture));
|
||||
}
|
||||
|
||||
store.dispatch(meActions.loggedIn(true));
|
||||
|
||||
|
|
@ -566,7 +643,10 @@ export default class RoomClient
|
|||
{
|
||||
logger.debug('receiveLogoutChildWindow()');
|
||||
|
||||
store.dispatch(meActions.setPicture(null));
|
||||
if (!store.getState().settings.localPicture)
|
||||
{
|
||||
store.dispatch(meActions.setPicture(null));
|
||||
}
|
||||
|
||||
store.dispatch(meActions.loggedIn(false));
|
||||
|
||||
|
|
@ -1479,7 +1559,7 @@ export default class RoomClient
|
|||
|
||||
([ track ] = stream.getVideoTracks());
|
||||
|
||||
const { deviceId: trackDeviceId } = track.getSettings();
|
||||
const { deviceId: trackDeviceId, width, height } = track.getSettings();
|
||||
|
||||
store.dispatch(settingsActions.setSelectedWebcamDevice(trackDeviceId));
|
||||
|
||||
|
|
@ -1500,6 +1580,8 @@ export default class RoomClient
|
|||
else
|
||||
encodings = VIDEO_SIMULCAST_ENCODINGS;
|
||||
|
||||
const resolutionScalings = getResolutionScalings(encodings);
|
||||
|
||||
this._webcamProducer = await this._sendTransport.produce(
|
||||
{
|
||||
track,
|
||||
|
|
@ -1510,7 +1592,10 @@ export default class RoomClient
|
|||
},
|
||||
appData :
|
||||
{
|
||||
source : 'webcam'
|
||||
source : 'webcam',
|
||||
width,
|
||||
height,
|
||||
resolutionScalings
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1520,7 +1605,9 @@ export default class RoomClient
|
|||
track,
|
||||
appData :
|
||||
{
|
||||
source : 'webcam'
|
||||
source : 'webcam',
|
||||
width,
|
||||
height
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -2084,6 +2171,130 @@ export default class RoomClient
|
|||
}
|
||||
}
|
||||
|
||||
async restartIce()
|
||||
{
|
||||
logger.debug('restartIce()');
|
||||
|
||||
try
|
||||
{
|
||||
if (this._sendTransport)
|
||||
{
|
||||
const iceParameters = await this.sendRequest(
|
||||
'restartIce',
|
||||
{ transportId: this._sendTransport.id });
|
||||
|
||||
await this._sendTransport.restartIce({ iceParameters });
|
||||
}
|
||||
|
||||
if (this._recvTransport)
|
||||
{
|
||||
const iceParameters = await this.sendRequest(
|
||||
'restartIce',
|
||||
{ transportId: this._recvTransport.id });
|
||||
|
||||
await this._recvTransport.restartIce({ iceParameters });
|
||||
}
|
||||
|
||||
logger.debug('ICE restarted');
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
logger.error('restartIce() | failed:%o', error);
|
||||
}
|
||||
}
|
||||
|
||||
setConsumerPreferredLayersMax(consumer)
|
||||
{
|
||||
if (consumer.type === 'simple')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
'setConsumerPreferredLayersMax() [consumerId:"%s"]', consumer.id);
|
||||
|
||||
if (consumer.preferredSpatialLayer !== consumer.spatialLayers -1 ||
|
||||
consumer.preferredTemporalLayer !== consumer.temporalLayers -1)
|
||||
{
|
||||
return this.setConsumerPreferredLayers(consumer.id,
|
||||
consumer.spatialLayers - 1, consumer.temporalLayers - 1);
|
||||
}
|
||||
}
|
||||
|
||||
adaptConsumerPreferredLayers(consumer, viewportWidth, viewportHeight)
|
||||
{
|
||||
if (consumer.type === 'simple')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!viewportWidth || !viewportHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
id,
|
||||
preferredSpatialLayer,
|
||||
preferredTemporalLayer,
|
||||
width,
|
||||
height,
|
||||
resolutionScalings
|
||||
} = consumer;
|
||||
const adaptiveScalingFactor = Math.min(Math.max(
|
||||
window.config.adaptiveScalingFactor || 0.75, 0.5), 1.0);
|
||||
|
||||
logger.debug(
|
||||
'adaptConsumerPreferredLayers() [consumerId:"%s", width:"%d", height:"%d" resolutionScalings:[%s] viewportWidth:"%d", viewportHeight:"%d"]',
|
||||
consumer.id, width, height, resolutionScalings.join(', '),
|
||||
viewportWidth, viewportHeight);
|
||||
|
||||
let newPreferredSpatialLayer = 0;
|
||||
|
||||
for (let i = 0; i < resolutionScalings.length; i++)
|
||||
{
|
||||
const levelWidth = adaptiveScalingFactor * width / resolutionScalings[i];
|
||||
const levelHeight = adaptiveScalingFactor * height / resolutionScalings[i];
|
||||
|
||||
if (viewportWidth >= levelWidth || viewportHeight >= levelHeight)
|
||||
{
|
||||
newPreferredSpatialLayer = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let newPreferredTemporalLayer = consumer.temporalLayers - 1;
|
||||
|
||||
if (newPreferredSpatialLayer === 0 && newPreferredTemporalLayer > 0)
|
||||
{
|
||||
const lowestLevelWidth = width / resolutionScalings[0];
|
||||
const lowestLevelHeight = height / resolutionScalings[0];
|
||||
|
||||
if (viewportWidth < lowestLevelWidth * 0.5
|
||||
&& viewportHeight < lowestLevelHeight * 0.5)
|
||||
{
|
||||
newPreferredTemporalLayer -= 1;
|
||||
}
|
||||
if (newPreferredTemporalLayer > 0
|
||||
&& viewportWidth < lowestLevelWidth * 0.25
|
||||
&& viewportHeight < lowestLevelHeight * 0.25)
|
||||
{
|
||||
newPreferredTemporalLayer -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (preferredSpatialLayer !== newPreferredSpatialLayer ||
|
||||
preferredTemporalLayer !== newPreferredTemporalLayer)
|
||||
{
|
||||
return this.setConsumerPreferredLayers(id,
|
||||
newPreferredSpatialLayer, newPreferredTemporalLayer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async setConsumerPriority(consumerId, priority)
|
||||
{
|
||||
logger.debug(
|
||||
|
|
@ -2344,6 +2555,9 @@ export default class RoomClient
|
|||
remotelyPaused : producerPaused,
|
||||
rtpParameters : consumer.rtpParameters,
|
||||
source : consumer.appData.source,
|
||||
width : consumer.appData.width,
|
||||
height : consumer.appData.height,
|
||||
resolutionScalings : consumer.appData.resolutionScalings,
|
||||
spatialLayers : spatialLayers,
|
||||
temporalLayers : temporalLayers,
|
||||
preferredSpatialLayer : spatialLayers - 1,
|
||||
|
|
@ -3186,6 +3400,21 @@ export default class RoomClient
|
|||
.catch(errback);
|
||||
});
|
||||
|
||||
this._sendTransport.on(
|
||||
'connectionstatechange', (connectState) =>
|
||||
{
|
||||
switch (connectState)
|
||||
{
|
||||
case 'disconnected':
|
||||
case 'failed':
|
||||
this.restartIce();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this._sendTransport.on(
|
||||
'produce', async ({ kind, rtpParameters, appData }, callback, errback) =>
|
||||
{
|
||||
|
|
@ -3249,6 +3478,21 @@ export default class RoomClient
|
|||
.catch(errback);
|
||||
});
|
||||
|
||||
this._recvTransport.on(
|
||||
'connectionstatechange', (connectState) =>
|
||||
{
|
||||
switch (connectState)
|
||||
{
|
||||
case 'disconnected':
|
||||
case 'failed':
|
||||
this.restartIce();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Set our media capabilities.
|
||||
store.dispatch(meActions.setMediaCapabilities(
|
||||
{
|
||||
|
|
@ -3377,8 +3621,10 @@ export default class RoomClient
|
|||
{
|
||||
autoMuteThreshold = window.config.autoMuteThreshold;
|
||||
}
|
||||
if (autoMuteThreshold && peers.length >= autoMuteThreshold)
|
||||
if (autoMuteThreshold >= 0 && peers.length >= autoMuteThreshold)
|
||||
{
|
||||
this.muteMic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,4 +149,9 @@ export const setVideoMuted = (videoMuted) =>
|
|||
({
|
||||
type : 'SET_VIDEO_MUTED',
|
||||
payload : { videoMuted }
|
||||
});
|
||||
});
|
||||
export const setLocalPicture = (localPicture) =>
|
||||
({
|
||||
type : 'SET_LOCAL_PICTURE',
|
||||
payload : { localPicture }
|
||||
});
|
||||
|
|
|
|||
|
|
@ -316,6 +316,12 @@ const Me = (props) =>
|
|||
'margin' : spacing
|
||||
};
|
||||
|
||||
if (me.picture)
|
||||
{
|
||||
spacingStyle.backgroundImage = `url(${me.picture})`;
|
||||
spacingStyle.backgroundSize = 'auto 100%';
|
||||
}
|
||||
|
||||
let audioScore = null;
|
||||
|
||||
if (micProducer && micProducer.score)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makePeerConsumerSelector } from '../Selectors';
|
||||
import PropTypes from 'prop-types';
|
||||
|
|
@ -110,7 +110,16 @@ const styles = (theme) =>
|
|||
alignItems : 'center',
|
||||
padding : theme.spacing(1),
|
||||
zIndex : 20,
|
||||
'& p' :
|
||||
'&.hide' :
|
||||
{
|
||||
transition : 'opacity 0.1s ease-in-out',
|
||||
opacity : 0
|
||||
},
|
||||
'&.hover' :
|
||||
{
|
||||
opacity : 1
|
||||
},
|
||||
'& p' :
|
||||
{
|
||||
padding : '6px 12px',
|
||||
borderRadius : 6,
|
||||
|
|
@ -146,8 +155,12 @@ const Peer = (props) =>
|
|||
style,
|
||||
smallContainer,
|
||||
windowConsumer,
|
||||
fullScreenConsumer,
|
||||
classes,
|
||||
theme
|
||||
theme,
|
||||
enableLayersSwitch,
|
||||
width,
|
||||
height
|
||||
} = props;
|
||||
|
||||
const micEnabled = (
|
||||
|
|
@ -176,6 +189,40 @@ const Peer = (props) =>
|
|||
...style
|
||||
};
|
||||
|
||||
if (peer.picture)
|
||||
{
|
||||
rootStyle.backgroundImage = `url(${peer.picture})`;
|
||||
rootStyle.backgroundSize = 'auto 100%';
|
||||
}
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const handler = setTimeout(() =>
|
||||
{
|
||||
if (!webcamConsumer)
|
||||
return;
|
||||
|
||||
if (windowConsumer === webcamConsumer.id)
|
||||
{
|
||||
// if playing in external window, set the maximum quality levels
|
||||
roomClient.setConsumerPreferredLayersMax(webcamConsumer);
|
||||
}
|
||||
else if (enableLayersSwitch && webcamConsumer?.type !== 'simple'
|
||||
&& fullScreenConsumer !== webcamConsumer.id)
|
||||
{
|
||||
roomClient.adaptConsumerPreferredLayers(webcamConsumer, width, height);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => { clearTimeout(handler); };
|
||||
}, [
|
||||
enableLayersSwitch,
|
||||
webcamConsumer,
|
||||
windowConsumer,
|
||||
fullScreenConsumer,
|
||||
roomClient, width, height
|
||||
]);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div
|
||||
|
|
@ -210,7 +257,12 @@ const Peer = (props) =>
|
|||
>
|
||||
<div className={classnames(classes.viewContainer)}>
|
||||
{ !videoVisible &&
|
||||
<div className={classes.videoInfo}>
|
||||
<div className={classnames(
|
||||
classes.videoInfo,
|
||||
'hide',
|
||||
hover ? 'hover' : null
|
||||
)}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='room.videoPaused'
|
||||
|
|
@ -421,6 +473,8 @@ const Peer = (props) =>
|
|||
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||
audioScore={micConsumer ? micConsumer.score : null}
|
||||
videoScore={webcamConsumer ? webcamConsumer.score : null}
|
||||
width={width}
|
||||
height={height}
|
||||
>
|
||||
<Volume id={peer.id} />
|
||||
</VideoView>
|
||||
|
|
@ -614,6 +668,8 @@ const Peer = (props) =>
|
|||
videoVisible={videoVisible}
|
||||
videoCodec={consumer && consumer.codec}
|
||||
videoScore={consumer ? consumer.score : null}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -758,6 +814,8 @@ const Peer = (props) =>
|
|||
videoVisible={screenVisible}
|
||||
videoCodec={screenConsumer && screenConsumer.codec}
|
||||
videoScore={screenConsumer ? screenConsumer.score : null}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -776,6 +834,7 @@ Peer.propTypes =
|
|||
screenConsumer : appPropTypes.Consumer,
|
||||
extraVideoConsumers : PropTypes.arrayOf(appPropTypes.Consumer),
|
||||
windowConsumer : PropTypes.string,
|
||||
fullScreenConsumer : PropTypes.string,
|
||||
activeSpeaker : PropTypes.bool,
|
||||
browser : PropTypes.object.isRequired,
|
||||
spacing : PropTypes.number,
|
||||
|
|
@ -784,7 +843,10 @@ Peer.propTypes =
|
|||
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
||||
toggleConsumerWindow : PropTypes.func.isRequired,
|
||||
classes : PropTypes.object.isRequired,
|
||||
theme : PropTypes.object.isRequired
|
||||
theme : PropTypes.object.isRequired,
|
||||
enableLayersSwitch : PropTypes.bool,
|
||||
width : PropTypes.number,
|
||||
height : PropTypes.number
|
||||
};
|
||||
|
||||
const makeMapStateToProps = (initialState, { id }) =>
|
||||
|
|
@ -794,11 +856,12 @@ const makeMapStateToProps = (initialState, { id }) =>
|
|||
const mapStateToProps = (state) =>
|
||||
{
|
||||
return {
|
||||
peer : state.peers[id],
|
||||
peer : state.peers[id],
|
||||
...getPeerConsumers(state, id),
|
||||
windowConsumer : state.room.windowConsumer,
|
||||
activeSpeaker : id === state.room.activeSpeakerId,
|
||||
browser : state.me.browser
|
||||
windowConsumer : state.room.windowConsumer,
|
||||
fullScreenConsumer : state.room.fullScreenConsumer,
|
||||
activeSpeaker : id === state.room.activeSpeakerId,
|
||||
browser : state.me.browser
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -833,7 +896,11 @@ export default withRoomContext(connect(
|
|||
prev.consumers === next.consumers &&
|
||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||
prev.room.windowConsumer === next.room.windowConsumer &&
|
||||
prev.me.browser === next.me.browser
|
||||
prev.room.fullScreenConsumer === next.room.fullScreenConsumer &&
|
||||
prev.me.browser === next.me.browser &&
|
||||
prev.enableLayersSwitch === next.enableLayersSwitch &&
|
||||
prev.width === next.width &&
|
||||
prev.height === next.height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makePeerConsumerSelector } from '../Selectors';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import * as appPropTypes from '../appPropTypes';
|
||||
import { withRoomContext } from '../../RoomContext';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import VideoView from '../VideoContainers/VideoView';
|
||||
|
|
@ -69,14 +70,19 @@ const styles = (theme) =>
|
|||
const SpeakerPeer = (props) =>
|
||||
{
|
||||
const {
|
||||
roomClient,
|
||||
advancedMode,
|
||||
peer,
|
||||
micConsumer,
|
||||
webcamConsumer,
|
||||
screenConsumer,
|
||||
windowConsumer,
|
||||
fullScreenConsumer,
|
||||
spacing,
|
||||
style,
|
||||
classes
|
||||
classes,
|
||||
width,
|
||||
height
|
||||
} = props;
|
||||
|
||||
const videoVisible = (
|
||||
|
|
@ -96,6 +102,33 @@ const SpeakerPeer = (props) =>
|
|||
'margin' : spacing
|
||||
};
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const handler = setTimeout(() =>
|
||||
{
|
||||
if (!webcamConsumer)
|
||||
return;
|
||||
|
||||
if (windowConsumer === webcamConsumer.id)
|
||||
{
|
||||
// if playing in external window, set the maximum quality levels
|
||||
roomClient.setConsumerPreferredLayersMax(webcamConsumer);
|
||||
}
|
||||
else if (webcamConsumer?.type !== 'simple'
|
||||
&& fullScreenConsumer !== webcamConsumer.id)
|
||||
{
|
||||
roomClient.adaptConsumerPreferredLayers(webcamConsumer, width, height);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => { clearTimeout(handler); };
|
||||
}, [
|
||||
webcamConsumer,
|
||||
windowConsumer,
|
||||
fullScreenConsumer,
|
||||
roomClient, width, height
|
||||
]);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div
|
||||
|
|
@ -145,6 +178,8 @@ const SpeakerPeer = (props) =>
|
|||
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||
audioScore={micConsumer ? micConsumer.score : null}
|
||||
videoScore={webcamConsumer ? webcamConsumer.score : null}
|
||||
width={width}
|
||||
height={height}
|
||||
>
|
||||
<Volume id={peer.id} />
|
||||
</VideoView>
|
||||
|
|
@ -205,14 +240,19 @@ const SpeakerPeer = (props) =>
|
|||
|
||||
SpeakerPeer.propTypes =
|
||||
{
|
||||
advancedMode : PropTypes.bool,
|
||||
peer : appPropTypes.Peer,
|
||||
micConsumer : appPropTypes.Consumer,
|
||||
webcamConsumer : appPropTypes.Consumer,
|
||||
screenConsumer : appPropTypes.Consumer,
|
||||
spacing : PropTypes.number,
|
||||
style : PropTypes.object,
|
||||
classes : PropTypes.object.isRequired
|
||||
roomClient : PropTypes.any.isRequired,
|
||||
advancedMode : PropTypes.bool,
|
||||
peer : appPropTypes.Peer,
|
||||
micConsumer : appPropTypes.Consumer,
|
||||
webcamConsumer : appPropTypes.Consumer,
|
||||
screenConsumer : appPropTypes.Consumer,
|
||||
windowConsumer : PropTypes.string,
|
||||
fullScreenConsumer : PropTypes.string,
|
||||
spacing : PropTypes.number,
|
||||
style : PropTypes.object,
|
||||
classes : PropTypes.object.isRequired,
|
||||
width : PropTypes.number,
|
||||
height : PropTypes.number
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, { id }) =>
|
||||
|
|
@ -220,12 +260,14 @@ const mapStateToProps = (state, { id }) =>
|
|||
const getPeerConsumers = makePeerConsumerSelector();
|
||||
|
||||
return {
|
||||
peer : state.peers[id],
|
||||
...getPeerConsumers(state, id)
|
||||
peer : state.peers[id],
|
||||
...getPeerConsumers(state, id),
|
||||
windowConsumer : state.room.windowConsumer,
|
||||
fullScreenConsumer : state.room.fullScreenConsumer
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
export default withRoomContext(connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
null,
|
||||
|
|
@ -235,8 +277,10 @@ export default connect(
|
|||
return (
|
||||
prev.peers === next.peers &&
|
||||
prev.consumers === next.consumers &&
|
||||
prev.room.activeSpeakerId === next.room.activeSpeakerId
|
||||
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||
prev.width === next.width &&
|
||||
prev.height === next.height
|
||||
);
|
||||
}
|
||||
}
|
||||
)(withStyles(styles, { withTheme: true })(SpeakerPeer));
|
||||
)(withStyles(styles, { withTheme: true })(SpeakerPeer)));
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import Popover from '@material-ui/core/Popover';
|
|||
import Typography from '@material-ui/core/Typography';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Badge from '@material-ui/core/Badge';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
|
|
@ -217,7 +216,6 @@ const TopBar = (props) =>
|
|||
drawerOverlayed,
|
||||
toolAreaOpen,
|
||||
isMobile,
|
||||
myPicture,
|
||||
loggedIn,
|
||||
loginEnabled,
|
||||
fullscreenEnabled,
|
||||
|
|
@ -468,11 +466,7 @@ const TopBar = (props) =>
|
|||
loggedIn ? roomClient.logout() : roomClient.login();
|
||||
}}
|
||||
>
|
||||
{ myPicture ?
|
||||
<Avatar src={myPicture} />
|
||||
:
|
||||
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||
}
|
||||
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
|
|
@ -692,11 +686,7 @@ const TopBar = (props) =>
|
|||
loggedIn ? roomClient.logout() : roomClient.login();
|
||||
}}
|
||||
>
|
||||
{ myPicture ?
|
||||
<Avatar src={myPicture} />
|
||||
:
|
||||
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||
}
|
||||
<AccountCircle className={loggedIn ? classes.green : null} />
|
||||
{ loggedIn ?
|
||||
<p className={classes.moreAction}>
|
||||
<FormattedMessage
|
||||
|
|
@ -934,7 +924,6 @@ TopBar.propTypes =
|
|||
permanentTopBar : PropTypes.bool.isRequired,
|
||||
drawerOverlayed : PropTypes.bool.isRequired,
|
||||
toolAreaOpen : PropTypes.bool.isRequired,
|
||||
myPicture : PropTypes.string,
|
||||
loggedIn : PropTypes.bool.isRequired,
|
||||
loginEnabled : PropTypes.bool.isRequired,
|
||||
fullscreenEnabled : PropTypes.bool,
|
||||
|
|
@ -982,7 +971,6 @@ const makeMapStateToProps = () =>
|
|||
toolAreaOpen : state.toolarea.toolAreaOpen,
|
||||
loggedIn : state.me.loggedIn,
|
||||
loginEnabled : state.me.loginEnabled,
|
||||
myPicture : state.me.picture,
|
||||
unread : state.toolarea.unreadMessages +
|
||||
state.toolarea.unreadFiles + raisedHandsSelector(state),
|
||||
canProduceExtraVideo : hasExtraVideoPermission(state),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { useIntl, FormattedMessage } from 'react-intl';
|
|||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormLabel from '@material-ui/core/FormLabel';
|
||||
|
|
@ -169,7 +168,6 @@ const JoinDialog = ({
|
|||
displayName,
|
||||
displayNameInProgress,
|
||||
loggedIn,
|
||||
myPicture,
|
||||
changeDisplayName,
|
||||
setMediaPerms,
|
||||
classes,
|
||||
|
|
@ -413,21 +411,14 @@ const JoinDialog = ({
|
|||
() => roomClient.login(roomId)
|
||||
}
|
||||
>
|
||||
{ myPicture ?
|
||||
<Avatar
|
||||
src={myPicture}
|
||||
className={classes.accountButtonAvatar}
|
||||
/>
|
||||
:
|
||||
<AccountCircle
|
||||
className={
|
||||
classnames(
|
||||
classes.accountButtonAvatar,
|
||||
loggedIn ? classes.green : null
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
<AccountCircle
|
||||
className={
|
||||
classnames(
|
||||
classes.accountButtonAvatar,
|
||||
loggedIn ? classes.green : null
|
||||
)
|
||||
}
|
||||
/>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
|
|
@ -782,7 +773,6 @@ JoinDialog.propTypes =
|
|||
displayNameInProgress : PropTypes.bool.isRequired,
|
||||
loginEnabled : PropTypes.bool.isRequired,
|
||||
loggedIn : PropTypes.bool.isRequired,
|
||||
myPicture : PropTypes.string,
|
||||
changeDisplayName : PropTypes.func.isRequired,
|
||||
setMediaPerms : PropTypes.func.isRequired,
|
||||
classes : PropTypes.object.isRequired,
|
||||
|
|
@ -803,7 +793,6 @@ const mapStateToProps = (state) =>
|
|||
displayNameInProgress : state.me.displayNameInProgress,
|
||||
loginEnabled : state.me.loginEnabled,
|
||||
loggedIn : state.me.loggedIn,
|
||||
myPicture : state.me.picture,
|
||||
locale : state.intl.locale,
|
||||
localesList : state.intl.list
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,9 @@ class Democratic extends React.PureComponent
|
|||
id={peer}
|
||||
spacing={6}
|
||||
style={style}
|
||||
enableLayersSwitch
|
||||
width={this.state.peerWidth}
|
||||
height={this.state.peerHeight}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -276,6 +276,8 @@ class Filmstrip extends React.PureComponent
|
|||
advancedMode={advancedMode}
|
||||
id={activePeerId}
|
||||
style={speakerStyle}
|
||||
width={speakerStyle.width}
|
||||
height={speakerStyle.height}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -317,6 +319,9 @@ class Filmstrip extends React.PureComponent
|
|||
id={peerId}
|
||||
style={peerStyle}
|
||||
smallContainer
|
||||
enableLayersSwitch={activePeerId !== peerId}
|
||||
width={peerStyle.width}
|
||||
height={peerStyle.height}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import ListItemText from '@material-ui/core/ListItemText';
|
|||
import ExpandLess from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import Switch from '@material-ui/core/Switch';
|
||||
import ImageUploader from 'react-images-upload';
|
||||
import Resizer from 'react-image-file-resizer';
|
||||
|
||||
const NoiseSlider = withStyles(
|
||||
{
|
||||
|
|
@ -98,6 +100,30 @@ const MediaSettings = ({
|
|||
const [ audioSettingsOpen, setAudioSettingsOpen ] = React.useState(false);
|
||||
const [ videoSettingsOpen, setVideoSettingsOpen ] = React.useState(false);
|
||||
|
||||
const onDrop = (picture) =>
|
||||
{
|
||||
if (picture.length > 0)
|
||||
{
|
||||
Resizer.imageFileResizer(picture[0], 1280, 720, 'JPEG', 99, 0,
|
||||
(uri) =>
|
||||
{
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.addEventListener('load', () =>
|
||||
{
|
||||
roomClient.setPicture(reader.result);
|
||||
});
|
||||
|
||||
reader.readAsDataURL(uri);
|
||||
},
|
||||
'blob');
|
||||
}
|
||||
else
|
||||
{
|
||||
roomClient.setPicture(null);
|
||||
}
|
||||
};
|
||||
|
||||
const resolutions = [ {
|
||||
value : 'low',
|
||||
label : intl.formatMessage({
|
||||
|
|
@ -159,6 +185,32 @@ const MediaSettings = ({
|
|||
<React.Fragment>
|
||||
<form className={classes.setting} autoComplete='off'>
|
||||
<FormControl className={classes.formControl}>
|
||||
<ImageUploader
|
||||
withIcon
|
||||
onChange={onDrop}
|
||||
imgExtension={[ '.jpg', '.jpeg', '.png' ]}
|
||||
maxFileSize={5242880}
|
||||
singleImage
|
||||
withPreview
|
||||
defaultImages={settings.localPicture?[ settings.localPicture ]:[]}
|
||||
buttonType='button'
|
||||
buttonText={intl.formatMessage({
|
||||
id : 'settings.myPhotoButton',
|
||||
defaultMessage : 'Set my photo'
|
||||
})}
|
||||
label={intl.formatMessage({
|
||||
id : 'settings.myPhotoLabel',
|
||||
defaultMessage : 'Max. file size: 5MB, accepted: jpg, jpeg, png'
|
||||
})}
|
||||
fileSizeError={intl.formatMessage({
|
||||
id : 'settings.myPhotoSizeError',
|
||||
defaultMessage : ' file is too large'
|
||||
})}
|
||||
fileTypeError={intl.formatMessage({
|
||||
id : 'settings.myPhotoTypeError',
|
||||
defaultMessage : ' is not a supported file extension'
|
||||
})}
|
||||
/>
|
||||
<Select
|
||||
value={settings.selectedWebcam || ''}
|
||||
onChange={(event) =>
|
||||
|
|
@ -201,6 +253,34 @@ const MediaSettings = ({
|
|||
}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
<FormControl className={classes.formControl}>
|
||||
<Select
|
||||
value={settings.resolution || ''}
|
||||
onChange={(event) =>
|
||||
{
|
||||
if (event.target.value)
|
||||
roomClient.updateWebcam({ newResolution: event.target.value });
|
||||
}}
|
||||
name='Video resolution'
|
||||
autoWidth
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
{resolutions.map((resolution, index) =>
|
||||
{
|
||||
return (
|
||||
<MenuItem key={index} value={resolution.value}>
|
||||
{resolution.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
<FormattedMessage
|
||||
id='settings.resolution'
|
||||
defaultMessage='Select your video resolution'
|
||||
/>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
<List className={classes.root} component='nav'>
|
||||
<ListItem button onClick={() => setVideoSettingsOpen(!videoSettingsOpen)}>
|
||||
<ListItemText primary={intl.formatMessage({
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React from 'react';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useWindowSize } from '@react-hook/window-size';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import * as appPropTypes from '../appPropTypes';
|
||||
import * as roomActions from '../../actions/roomActions';
|
||||
import { withRoomContext } from '../../RoomContext';
|
||||
import FullScreenExitIcon from '@material-ui/icons/FullscreenExit';
|
||||
import VideoView from './VideoView';
|
||||
|
||||
|
|
@ -79,14 +81,32 @@ const styles = (theme) =>
|
|||
const FullScreenView = (props) =>
|
||||
{
|
||||
const {
|
||||
roomClient,
|
||||
advancedMode,
|
||||
consumer,
|
||||
fullScreenConsumer,
|
||||
toggleConsumerFullscreen,
|
||||
toolbarsVisible,
|
||||
permanentTopBar,
|
||||
classes
|
||||
} = props;
|
||||
|
||||
const elementRef = useRef(null);
|
||||
const size = useWindowSize({
|
||||
wait : 400
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if (!elementRef.current)
|
||||
return;
|
||||
|
||||
if (consumer && consumer.type !== 'simple')
|
||||
{
|
||||
roomClient.adaptConsumerPreferredLayers(consumer, size[0], size[1]);
|
||||
}
|
||||
}, [ size, fullScreenConsumer ]);
|
||||
|
||||
if (!consumer)
|
||||
return null;
|
||||
|
||||
|
|
@ -97,7 +117,7 @@ const FullScreenView = (props) =>
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.root} ref={elementRef}>
|
||||
<div className={classes.controls}>
|
||||
<div
|
||||
className={classnames(classes.button, {
|
||||
|
|
@ -135,6 +155,8 @@ const FullScreenView = (props) =>
|
|||
videoVisible={consumerVisible}
|
||||
videoCodec={consumer && consumer.codec}
|
||||
videoScore={consumer ? consumer.score : null}
|
||||
width={size[0]}
|
||||
height={size[1]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -142,8 +164,10 @@ const FullScreenView = (props) =>
|
|||
|
||||
FullScreenView.propTypes =
|
||||
{
|
||||
roomClient : PropTypes.any.isRequired,
|
||||
advancedMode : PropTypes.bool,
|
||||
consumer : appPropTypes.Consumer,
|
||||
fullScreenConsumer : PropTypes.string,
|
||||
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
||||
toolbarsVisible : PropTypes.bool,
|
||||
permanentTopBar : PropTypes.bool,
|
||||
|
|
@ -152,9 +176,10 @@ FullScreenView.propTypes =
|
|||
|
||||
const mapStateToProps = (state) =>
|
||||
({
|
||||
consumer : state.consumers[state.room.fullScreenConsumer],
|
||||
toolbarsVisible : state.room.toolbarsVisible,
|
||||
permanentTopBar : state.settings.permanentTopBar
|
||||
consumer : state.consumers[state.room.fullScreenConsumer],
|
||||
toolbarsVisible : state.room.toolbarsVisible,
|
||||
permanentTopBar : state.settings.permanentTopBar,
|
||||
fullScreenConsumer : state.room.fullScreenConsumer
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
|
|
@ -166,7 +191,7 @@ const mapDispatchToProps = (dispatch) =>
|
|||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
export default withRoomContext(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
null,
|
||||
|
|
@ -181,4 +206,4 @@ export default connect(
|
|||
);
|
||||
}
|
||||
}
|
||||
)(withStyles(styles)(FullScreenView));
|
||||
)(withStyles(styles)(FullScreenView)));
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ const styles = (theme) =>
|
|||
"AcodL Acod Acod Acod Acod" \
|
||||
"VcodL Vcod Vcod Vcod Vcod" \
|
||||
"ResL Res Res Res Res" \
|
||||
"VPortL VPort VPort VPort VPort" \
|
||||
"RecvL RecvBps RecvBps RecvSum RecvSum" \
|
||||
"SendL SendBps SendBps SendSum SendSum" \
|
||||
"IPlocL IPloc IPloc IPloc IPloc" \
|
||||
|
|
@ -105,6 +106,8 @@ const styles = (theme) =>
|
|||
'& .Vcod' : { gridArea: 'Vcod' },
|
||||
'& .ResL' : { gridArea: 'ResL' },
|
||||
'& .Res' : { gridArea: 'Res' },
|
||||
'& .VPortL' : { gridArea: 'VPortL' },
|
||||
'& .VPort' : { gridArea: 'VPort' },
|
||||
'& .RecvL' : { gridArea: 'RecvL' },
|
||||
'& .RecvBps' : { gridArea: 'RecvBps', justifySelf: 'flex-end' },
|
||||
'& .RecvSum' : { gridArea: 'RecvSum', justifySelf: 'flex-end' },
|
||||
|
|
@ -143,16 +146,19 @@ const styles = (theme) =>
|
|||
color : 'rgba(255, 255, 255, 0.85)',
|
||||
border : 'none',
|
||||
borderBottom : '1px solid #aeff00',
|
||||
backgroundColor : 'transparent'
|
||||
backgroundColor : 'rgba(0, 0, 0, 0.25)',
|
||||
padding : theme.spacing(0.6)
|
||||
},
|
||||
displayNameStatic :
|
||||
{
|
||||
userSelect : 'none',
|
||||
cursor : 'text',
|
||||
fontSize : 14,
|
||||
fontWeight : 400,
|
||||
color : 'rgba(255, 255, 255, 0.85)',
|
||||
'&:hover' :
|
||||
userSelect : 'none',
|
||||
cursor : 'text',
|
||||
fontSize : 14,
|
||||
fontWeight : 400,
|
||||
color : 'rgba(255, 255, 255, 0.85)',
|
||||
backgroundColor : 'rgba(0, 0, 0, 0.25)',
|
||||
padding : theme.spacing(0.6),
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : 'rgb(174, 255, 0, 0.25)'
|
||||
}
|
||||
|
|
@ -204,7 +210,9 @@ class VideoView extends React.PureComponent
|
|||
onChangeDisplayName,
|
||||
children,
|
||||
classes,
|
||||
netInfo
|
||||
netInfo,
|
||||
width,
|
||||
height
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
|
@ -305,6 +313,15 @@ class VideoView extends React.PureComponent
|
|||
</React.Fragment>
|
||||
}
|
||||
|
||||
{ (videoVisible && width && height) &&
|
||||
<React.Fragment>
|
||||
<span className={'VPortL'}>VPort: </span>
|
||||
<span className={'VPort'}>
|
||||
{Math.round(width)}x{Math.round(height)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
{ isMe && !isScreen && !isExtraVideo &&
|
||||
(netInfo.recv && netInfo.send && netInfo.send.iceSelectedTuple) &&
|
||||
<React.Fragment>
|
||||
|
|
@ -525,7 +542,9 @@ VideoView.propTypes =
|
|||
onChangeDisplayName : PropTypes.func,
|
||||
children : PropTypes.object,
|
||||
classes : PropTypes.object.isRequired,
|
||||
netInfo : PropTypes.object
|
||||
netInfo : PropTypes.object,
|
||||
width : PropTypes.number,
|
||||
height : PropTypes.number
|
||||
};
|
||||
|
||||
export default withStyles(styles)(VideoView);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ export const Consumer = PropTypes.shape(
|
|||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen', 'extravideo' ]).isRequired,
|
||||
locallyPaused : PropTypes.bool.isRequired,
|
||||
remotelyPaused : PropTypes.bool.isRequired,
|
||||
width : PropTypes.number,
|
||||
height : PropTypes.number,
|
||||
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
||||
track : PropTypes.any,
|
||||
codec : PropTypes.string
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ const initialState =
|
|||
drawerOverlayed : window.config.drawerOverlayed || true,
|
||||
aspectRatio : window.config.viewAspectRatio || 1.777, // 16 : 9
|
||||
mediaPerms : { audio: true, video: true },
|
||||
localPicture : null,
|
||||
...window.config.defaultAudio
|
||||
};
|
||||
|
||||
|
|
@ -244,6 +245,13 @@ const settings = (state = initialState, action) =>
|
|||
return { ...state, videoMuted };
|
||||
}
|
||||
|
||||
case 'SET_LOCAL_PICTURE':
|
||||
{
|
||||
const { localPicture } = action.payload;
|
||||
|
||||
return { ...state, localPicture };
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "无法保存文件",
|
||||
"filesharing.startingFileShare": "正在尝试共享文件",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Mezní hodnota hluku",
|
||||
"settings.mirrorOwnVideo": "Zrcadlové zobrazení vlastního videa",
|
||||
"settings.language": "Vyberte jazyk",
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Není možné uložit soubor",
|
||||
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Geräuschepegel",
|
||||
"settings.mirrorOwnVideo": "Ansicht des eigenen Videos spiegeln",
|
||||
"settings.language": "Sprache auswählen",
|
||||
"settings.myPhotoButton": "Mein Foto festlegen",
|
||||
"settings.myPhotoLabel": "Maximale Dateigröße: 5MB, akzeptiert: jpg, jpeg, png",
|
||||
"settings.myPhotoSizeError": " Datei ist zu groß",
|
||||
"settings.myPhotoTypeError": " ist keine unterstützte Dateierweiterung",
|
||||
|
||||
"filesharing.saveFileError": "Fehler beim Speichern der Datei",
|
||||
"filesharing.startingFileShare": "Starte Teilen der Datei",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Kan ikke gemme fil",
|
||||
"filesharing.startingFileShare": "Forsøger at dele filen",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Αδυναμία αποθήκευσης του αρχείου",
|
||||
"filesharing.startingFileShare": "Προσπάθεια διαμοιρασμού αρχείου",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Noise threshold",
|
||||
"settings.mirrorOwnVideo": "Mirror view of own video",
|
||||
"settings.language": "Select language",
|
||||
"settings.myPhotoButton": "Set my photo",
|
||||
"settings.myPhotoLabel": "Max. file size: 5MB, accepted: jpg, jpeg, png",
|
||||
"settings.myPhotoSizeError": " file is too large",
|
||||
"settings.myPhotoTypeError": " is not a supported file extension",
|
||||
|
||||
"filesharing.saveFileError": "Unable to save file",
|
||||
"filesharing.startingFileShare": "Attempting to share file",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "No ha sido posible guardar el fichero",
|
||||
"filesharing.startingFileShare": "Intentando compartir el fichero",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Impossible d'enregistrer le fichier",
|
||||
"filesharing.startingFileShare": "Début du transfert de fichier",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "शोर सीमा",
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "फ़ाइल सहेजने में असमर्थ",
|
||||
"filesharing.startingFileShare": "फ़ाइल के आदान-प्रदान का प्रयास है",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Nije moguće spremiti datoteku",
|
||||
"filesharing.startingFileShare": "Pokušaj dijeljenja datoteke",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Zajszint",
|
||||
"settings.mirrorOwnVideo": "Saját videókép tükrözése",
|
||||
"settings.language": "Nyelv",
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "A file-t nem sikerült elmenteni",
|
||||
"filesharing.startingFileShare": "Fájl megosztása",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Soglia di rumore",
|
||||
"settings.mirrorOwnVideo": "Specchiare il proprio video",
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Impossibile salvare file",
|
||||
"filesharing.startingFileShare": "Tentativo di condivisione file",
|
||||
|
|
|
|||
|
|
@ -169,6 +169,10 @@
|
|||
"settings.noiseThreshold": "Шу деңгейі",
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Файлды сақтау мүмкін емес",
|
||||
"filesharing.startingFileShare": "Файлды бөлісу әрекеті",
|
||||
|
|
|
|||
|
|
@ -165,6 +165,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Nav iespējams saglabāt failu",
|
||||
"filesharing.startingFileShare": "Tiek mēģināts kopīgot failu",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Støyterskel",
|
||||
"settings.mirrorOwnVideo": "Vis eget bilde speilet for deg",
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Klarte ikke å lagre fil",
|
||||
"filesharing.startingFileShare": "Starter fildeling",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Próg szumu",
|
||||
"settings.mirrorOwnVideo": "Lustrzany widok własnego wideo",
|
||||
"settings.language": "Wybór języka",
|
||||
"settings.myPhotoButton": "Ustaw moje zdjęcie",
|
||||
"settings.myPhotoLabel": "Maksymalny rozmiar pliku: 5MB, dozwolone: jpg, jpeg, png",
|
||||
"settings.myPhotoSizeError": " plik jest zbyt duży",
|
||||
"settings.myPhotoTypeError": " nie jest obsługiwanym rozszerzeniem pliku",
|
||||
|
||||
"filesharing.saveFileError": "Nie można zapisać pliku",
|
||||
"filesharing.startingFileShare": "Próba udostępnienia pliku",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Impossível de gravar o ficheiro",
|
||||
"filesharing.startingFileShare": "Tentando partilha de ficheiro",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Încercarea de a salva fișierul a eșuat",
|
||||
"filesharing.startingFileShare": "Partajarea fișierului",
|
||||
|
|
|
|||
|
|
@ -169,6 +169,10 @@
|
|||
"settings.noiseThreshold": "Порог шума",
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Невозможно сохранить файл",
|
||||
"filesharing.startingFileShare": "Попытка поделиться файлом",
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
"settings.noiseThreshold": "Gürültü eşiği",
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Dosya kaydedilemiyor",
|
||||
"filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
|
||||
|
|
|
|||
|
|
@ -170,6 +170,10 @@
|
|||
"settings.noiseThreshold": null,
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "無法保存文件",
|
||||
"filesharing.startingFileShare": "開始分享文件",
|
||||
|
|
|
|||
|
|
@ -170,6 +170,10 @@
|
|||
"settings.noiseThreshold": "Поріг шуму",
|
||||
"settings.mirrorOwnVideo": null,
|
||||
"settings.language": null,
|
||||
"settings.myPhotoButton": null,
|
||||
"settings.myPhotoLabel": null,
|
||||
"settings.myPhotoSizeError": null,
|
||||
"settings.myPhotoTypeError": null,
|
||||
|
||||
"filesharing.saveFileError": "Неможливо зберегти файл",
|
||||
"filesharing.startingFileShare": "Спроба поділитися файлом",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
export function getSignalingUrl(peerId, roomId)
|
||||
{
|
||||
const hostname = window.config.serverHostname || window.location.hostname;
|
||||
const port =
|
||||
process.env.NODE_ENV !== 'production' ?
|
||||
window.config.developmentPort
|
||||
:
|
||||
window.config.productionPort;
|
||||
|
||||
const url = `wss://${window.location.hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
|
||||
const url = `wss://${hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -614,6 +614,13 @@ class Room extends EventEmitter
|
|||
this._roomId);
|
||||
this.close();
|
||||
}
|
||||
else if(this.checkEmpty() && !this._lobby.checkEmpty() && this.isLocked()){
|
||||
logger.info(
|
||||
'Room deserted for some time, closing the room [roomId:"%s"] and kick peers from the lobby',
|
||||
this._roomId);
|
||||
|
||||
this.close();
|
||||
}
|
||||
else
|
||||
logger.debug('selfDestructCountdown() aborted; room is not empty!');
|
||||
}, 10000);
|
||||
|
|
@ -847,6 +854,12 @@ class Room extends EventEmitter
|
|||
// lobby is empty, close the room after a while.
|
||||
if (this.checkEmpty() && this._lobby.checkEmpty())
|
||||
this.selfDestructCountdown();
|
||||
// If this is the last Peer in the room,
|
||||
// lobby is not empty and room is locked,
|
||||
// close the room after a while.
|
||||
else if(this.checkEmpty() && !this._lobby.checkEmpty() && this.isLocked()){
|
||||
this.selfDestructCountdown();
|
||||
}
|
||||
}
|
||||
|
||||
async _handleSocketRequest(peer, request, cb)
|
||||
|
|
@ -1372,7 +1385,7 @@ class Room extends EventEmitter
|
|||
break;
|
||||
}
|
||||
|
||||
/* case 'changePicture':
|
||||
case 'changePicture':
|
||||
{
|
||||
// Ensure the Peer is joined.
|
||||
if (!peer.joined)
|
||||
|
|
@ -1392,7 +1405,7 @@ class Room extends EventEmitter
|
|||
cb();
|
||||
|
||||
break;
|
||||
} */
|
||||
}
|
||||
|
||||
case 'chatMessage':
|
||||
{
|
||||
|
|
@ -1884,6 +1897,8 @@ class Room extends EventEmitter
|
|||
{
|
||||
// Remove from its map.
|
||||
consumerPeer.removeConsumer(consumer.id);
|
||||
|
||||
this._notification(consumerPeer.socket, 'consumerClosed', { consumerId: consumer.id });
|
||||
});
|
||||
|
||||
consumer.on('producerclose', () =>
|
||||
|
|
|
|||
|
|
@ -684,6 +684,11 @@ module.exports = async function(rooms, peers)
|
|||
const interactive = new Interactive(socket);
|
||||
|
||||
interactive.openCommandConsole();
|
||||
|
||||
socket.on('error', (error) =>
|
||||
{
|
||||
console.error(`interactiveServer socket error:`, error.message);
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise((resolve) =>
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ module.exports = async function(rooms, peers, config)
|
|||
|
||||
await collect(registry);
|
||||
res.set('Content-Type', registry.contentType);
|
||||
const data = registry.metrics();
|
||||
const data = await registry.metrics();
|
||||
|
||||
res.end(data);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
"helmet": "^3.21.2",
|
||||
"ims-lti": "^3.0.2",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mediasoup": "^3.5.14",
|
||||
"mediasoup": "^3.6.27",
|
||||
"openid-client": "^3.7.3",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "^1.0.0",
|
||||
|
|
|
|||
|
|
@ -620,20 +620,7 @@ async function runHttpsServer()
|
|||
res.redirect(ltiURL);
|
||||
}
|
||||
else
|
||||
{
|
||||
const specialChars = "<>@!^*()[]{}:;|'\"\\,~`";
|
||||
|
||||
for (let i = 0; i < specialChars.length; i++)
|
||||
{
|
||||
if (req.url.substring(1).indexOf(specialChars[i]) > -1)
|
||||
{
|
||||
req.url = `/${encodeURIComponent(encodeURI(req.url.substring(1)))}`;
|
||||
res.redirect(`${req.url}`);
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
else
|
||||
res.redirect(`https://${req.hostname}${req.url}`);
|
||||
|
|
@ -807,14 +794,18 @@ async function runMediasoupWorkers()
|
|||
|
||||
logger.info('running %d mediasoup Workers...', numWorkers);
|
||||
|
||||
for (let i = 0; i < numWorkers; ++i)
|
||||
const { logLevel, logTags, rtcMinPort, rtcMaxPort } = config.mediasoup.worker;
|
||||
const portInterval = Math.floor((rtcMaxPort - rtcMinPort) / numWorkers);
|
||||
|
||||
for (let i = 0; i < numWorkers; i++)
|
||||
{
|
||||
const worker = await mediasoup.createWorker(
|
||||
{
|
||||
logLevel : config.mediasoup.worker.logLevel,
|
||||
logTags : config.mediasoup.worker.logTags,
|
||||
rtcMinPort : config.mediasoup.worker.rtcMinPort,
|
||||
rtcMaxPort : config.mediasoup.worker.rtcMaxPort
|
||||
logLevel,
|
||||
logTags,
|
||||
rtcMinPort : rtcMinPort + i * portInterval,
|
||||
rtcMaxPort : i === numWorkers - 1 ? rtcMaxPort
|
||||
: rtcMinPort + (i + 1) * portInterval - 1
|
||||
});
|
||||
|
||||
worker.on('died', () =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue