mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-02 00:17:46 +00:00
Compare commits
1 Commits
4436
...
replace-sa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d490b29b7a |
@@ -20,5 +20,5 @@
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=20.5.0
|
||||
sdkVersion=2.11.0
|
||||
appVersion=20.4.0
|
||||
sdkVersion=2.10.0
|
||||
|
||||
11
config.js
11
config.js
@@ -14,6 +14,9 @@ var config = {
|
||||
// Domain for authenticated users. Defaults to <domain>.
|
||||
// authdomain: 'jitsi-meet.example.com',
|
||||
|
||||
// Jirecon recording component domain.
|
||||
// jirecon: 'jirecon.jitsi-meet.example.com',
|
||||
|
||||
// Call control component (Jigasi).
|
||||
// call_control: 'callcontrol.jitsi-meet.example.com',
|
||||
|
||||
@@ -64,11 +67,6 @@ var config = {
|
||||
// adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
|
||||
// the probability for this to be enabled.
|
||||
// capScreenshareBitrate: 1 // 0 to disable
|
||||
|
||||
// Enable callstats only for a percentage of users.
|
||||
// This takes a value between 0 and 100 which determines the probability for
|
||||
// the callstats to be enabled.
|
||||
// callStatsThreshold: 5 // enable callstats for 5% of the users.
|
||||
},
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
@@ -337,7 +335,6 @@ var config = {
|
||||
// 'datachannel'), undefined (treat it as 'datachannel') and false (don't
|
||||
// open any channel).
|
||||
// openBridgeChannel: true,
|
||||
openBridgeChannel: 'websocket',
|
||||
|
||||
|
||||
// UI
|
||||
@@ -638,6 +635,8 @@ var config = {
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
autoRecord
|
||||
autoRecordToken
|
||||
debug
|
||||
debugAudioLevels
|
||||
deploymentInfo
|
||||
|
||||
8
debian/jitsi-meet-prosody.postrm
vendored
8
debian/jitsi-meet-prosody.postrm
vendored
@@ -45,12 +45,8 @@ case "$1" in
|
||||
rm -rf /var/lib/prosody/$JICOFO_AUTH_DOMAIN.*
|
||||
rm -rf /var/lib/prosody/$JVB_HOSTNAME.*
|
||||
|
||||
# clean created users, replace '.' with '%2e', replace '-' with '%2d'
|
||||
rm -rf /var/lib/prosody/`echo $JICOFO_AUTH_DOMAIN | sed -e "s/\./%2e/g"| sed -e "s/-/%2d/g"`
|
||||
|
||||
# clean the prosody cert from the trust store
|
||||
rm -rf /usr/local/share/ca-certificates/$JICOFO_AUTH_DOMAIN.*
|
||||
update-ca-certificates -f
|
||||
# clean created users
|
||||
rm -rf /var/lib/prosody/`echo $JICOFO_AUTH_DOMAIN | sed -e "s/\./%2e/g"`
|
||||
fi
|
||||
|
||||
# Clear the debconf variable
|
||||
|
||||
50
debian/jitsi-meet-turnserver.postinst
vendored
50
debian/jitsi-meet-turnserver.postinst
vendored
@@ -36,6 +36,26 @@ case "$1" in
|
||||
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
|
||||
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
|
||||
|
||||
NGINX_SITES_ENABLED="/etc/nginx/sites-enabled/"
|
||||
NGINX_CONFIG_ENABLED="${NGINX_SITES_ENABLED}${JVB_HOSTNAME}.conf"
|
||||
NGINX_MULTIPLEXING="true"
|
||||
for site in ${NGINX_SITES_ENABLED}*; do
|
||||
# if it is not a file continue
|
||||
[ -f "${site}" ] || continue
|
||||
# if it is our config skip
|
||||
[ "${site}" != "${NGINX_CONFIG_ENABLED}" ] || continue
|
||||
# check whether other enabled hosts has listen 443
|
||||
if cat ${site} | grep -v "^[[:space:]]*#" | grep listen | grep -q "^.*[[:space:]:]443[;[:space:]].*" ; then
|
||||
# nothing to do
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "turnserver is listening on tcp 5349 as other nginx sites use port 443"
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
NGINX_MULTIPLEXING="false"
|
||||
fi
|
||||
done
|
||||
|
||||
# if there was a turn config backup it so we can configure
|
||||
# we cannot recognize at the moment is this a user config or default config when installing coturn
|
||||
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
|
||||
@@ -113,9 +133,19 @@ denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG
|
||||
TURN_SECRET="$RET"
|
||||
|
||||
# no turn config exists, lt's copy template and fill it in
|
||||
PUBLIC_IP=$(dig -4 +short myip.opendns.com a @resolver1.opendns.com) || true
|
||||
if [ -z "$PUBLIC_IP" ] ; then
|
||||
PUBLIC_IP="127.0.0.1"
|
||||
echo "------------------------------------------------"
|
||||
echo "Warning! Could not resolve your external ip address! Error:^"
|
||||
echo "Your turn server will not work till you edit your $TURN_CONFIG config file."
|
||||
echo "You need to set your external ip address in external-ip and restart coturn service."
|
||||
echo "------------------------------------------------"
|
||||
fi
|
||||
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
|
||||
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
|
||||
sed -i "s/__external_ip_address__/$PUBLIC_IP/g" $TURN_CONFIG
|
||||
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
@@ -140,14 +170,18 @@ denied-peer-ip=240.0.0.0-255.255.255.255" >> $TURN_CONFIG
|
||||
invoke-rc.d coturn restart || true
|
||||
|
||||
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
|
||||
if [ -f $NGINX_STREAM_CONFIG ] ; then
|
||||
echo "------------------------------------------------"
|
||||
echo ""
|
||||
echo "You have multiplexing enabled, it is recommended to disable it and migrate to using websockets for the bridge channel."
|
||||
echo "The support for sctp data channels is deprecated and will be dropped at some point."
|
||||
echo "How to do it at: https://jitsi.org/multiplexing-to-bridge-ws-howto"
|
||||
echo ""
|
||||
echo "------------------------------------------------"
|
||||
if [ $NGINX_MULTIPLEXING = "true" ] && [ ! -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
|
||||
ln -s /usr/share/jitsi-meet-turnserver/jitsi-meet.conf $NGINX_STREAM_CONFIG
|
||||
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
|
||||
sed -i "s/listen \[\:\:\]\:443 ssl/listen \[\:\:\]\:4444 ssl http2/g" $NGINX_CONFIG
|
||||
invoke-rc.d nginx reload || true
|
||||
else
|
||||
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua"
|
||||
if [ -f $PROSODY_HOST_CONFIG ] ; then
|
||||
# If we are not multiplexing we need to change the port in prosody config
|
||||
sed -i 's/"443"/"5349"/g' $PROSODY_HOST_CONFIG
|
||||
invoke-rc.d prosody restart || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enable turn server in config.js
|
||||
|
||||
2
debian/jitsi-meet-turnserver.postrm
vendored
2
debian/jitsi-meet-turnserver.postrm
vendored
@@ -24,6 +24,7 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
remove)
|
||||
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
fi
|
||||
@@ -32,6 +33,7 @@ case "$1" in
|
||||
fi
|
||||
;;
|
||||
purge)
|
||||
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
|
||||
rm -rf /etc/turnserver.conf
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload || true
|
||||
|
||||
@@ -8,7 +8,7 @@ turncredentials_secret = "__turnSecret__";
|
||||
turncredentials = {
|
||||
{ type = "stun", host = "jitmeet.example.com", port = "3478" },
|
||||
{ type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" },
|
||||
{ type = "turns", host = "jitmeet.example.com", port = "5349", transport = "tcp" }
|
||||
{ type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" }
|
||||
};
|
||||
|
||||
cross_domain_bosh = false;
|
||||
|
||||
@@ -12,6 +12,7 @@ no-tcp-relay
|
||||
no-tcp
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
external-ip=__external_ip_address__
|
||||
no-tlsv1
|
||||
no-tlsv1_1
|
||||
# https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
# this is jitsi-meet nginx module configuration
|
||||
# this forward all http traffic to the nginx virtual host port
|
||||
# and the rest to the turn server
|
||||
#
|
||||
# Multiplexing based on ALPN is DEPRECATED. ALPN does not play well with websockets on some browsers and reverse proxies.
|
||||
# To migrate away from using it read: https://jitsi.org/multiplexing-to-bridge-ws-howto
|
||||
# This file will be removed at some point and if deployment is still using it, will break.
|
||||
#
|
||||
|
||||
stream {
|
||||
upstream web {
|
||||
server 127.0.0.1:4444;
|
||||
|
||||
@@ -87,15 +87,6 @@ server {
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
# colibri (JVB) websockets for jvb1
|
||||
location ~ ^/colibri-ws/default-id/(.*) {
|
||||
proxy_pass http://127.0.0.1:9090/colibri-ws/default-id/$1$is_args$args;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
tcp_nodelay on;
|
||||
}
|
||||
|
||||
location ~ ^/([^/?&:'"]+)$ {
|
||||
try_files $uri @root_path;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>20.5.0</string>
|
||||
<string>20.4.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -66,10 +66,10 @@
|
||||
<string>See your scheduled meetings in the app.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Participate in meetings with video.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Participate in meetings with voice.</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Local network is used for establishing Peer-to-Peer connections.</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>org.jitsi.JitsiMeet.ios.conference</string>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>20.5.0</string>
|
||||
<string>20.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>20.5.0</string>
|
||||
<string>20.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.11.0</string>
|
||||
<string>2.10.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
"micPermissionDeniedError": "Die Berechtigung zur Verwendung des Mikrofons wurde nicht erteilt. Sie können trotzdem an der Konferenz teilnehmen, aber die anderen Teilnehmer können Sie nicht hören. Verwenden Sie die Kamera-Schaltfläche in der Adressleiste, um die Berechtigungen zu erteilen.",
|
||||
"micUnknownError": "Das Mikrofon kann aus einem unbekannten Grund nicht verwendet werden.",
|
||||
"muteEveryoneElseDialog": "Einmal stummgeschaltet, können Sie deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.",
|
||||
"muteEveryoneElseTitle": "Alle außer {{whom}} stummschalten?",
|
||||
"muteEveryoneElseTitle": "Alle außer {{whom}} stummschaten?",
|
||||
"muteEveryoneDialog": "Wollen Sie wirklich alle stummschalten? Sie können deren Stummschaltung nicht mehr beenden, aber sie können ihre Stummschaltung jederzeit selbst beenden.",
|
||||
"muteEveryoneTitle": "Alle stummschalten?",
|
||||
"muteEveryoneSelf": "sich selbst",
|
||||
|
||||
@@ -139,9 +139,6 @@
|
||||
"description": "Rien ne s'est passé ? Nous avons essayé de lancer votre réunion dans l'application de bureau {{app}}. Essayez à nouveau ou lancez-la dans l'application web {{app}}.",
|
||||
"descriptionWithoutWeb": "Rien ne s'est passé ? Nous avons essayé de démarrer votre réunion dans l'application bureau {{app}}.",
|
||||
"downloadApp": "Télécharger l'application",
|
||||
"ifDoNotHaveApp": "Si vous n'avez pas encore l'application:",
|
||||
"ifHaveApp": "Si vous avez déjà installé l'application:",
|
||||
"joinInApp": "Rejoindre la réunion en utilisant l'application",
|
||||
"launchWebButton": "Lancer dans le navigateur",
|
||||
"openApp": "Continuer vers l'application",
|
||||
"title": "Lancement de votre réunion dans {{app}} en cours...",
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
"kickParticipantButton": "Verwijderen",
|
||||
"kickParticipantDialog": "Weet u zeker dat u deze deelnemer wilt verwijderen?",
|
||||
"kickParticipantTitle": "Deze deelnemer verwijderen?",
|
||||
"kickTitle": "Oei! {{participantDisplayName}} heeft u uit de vergadering verwijderd",
|
||||
"kickTitle": "Oei! {{ParticipantDisplayName}} heeft u uit de vergadering verwijderd",
|
||||
"liveStreaming": "Livestreamen",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Niet mogelijk tijdens opnemen",
|
||||
"liveStreamingDisabledForGuestTooltip": "Gasten kunnen geen livestream starten.",
|
||||
|
||||
@@ -15,18 +15,13 @@ import {
|
||||
} from '../../react/features/base/conference';
|
||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||
import { JitsiRecordingConstants } from '../../react/features/base/lib-jitsi-meet';
|
||||
import { pinParticipant } from '../../react/features/base/participants';
|
||||
import {
|
||||
processExternalDeviceRequest
|
||||
} from '../../react/features/device-selection/functions';
|
||||
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
|
||||
import { toggleE2EE } from '../../react/features/e2ee/actions';
|
||||
import { invite } from '../../react/features/invite';
|
||||
import {
|
||||
captureLargeVideoScreenshot,
|
||||
resizeLargeVideo,
|
||||
selectParticipantInLargeVideo
|
||||
} from '../../react/features/large-video/actions';
|
||||
import { selectParticipantInLargeVideo } from '../../react/features/large-video/actions';
|
||||
import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
|
||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||
import { getActiveSession } from '../../react/features/recording/functions';
|
||||
@@ -121,19 +116,9 @@ function initCommands() {
|
||||
));
|
||||
}
|
||||
},
|
||||
'pin-participant': id => {
|
||||
logger.debug('Pin participant command received');
|
||||
sendAnalytics(createApiEvent('participant.pinned'));
|
||||
APP.store.dispatch(pinParticipant(id));
|
||||
},
|
||||
'proxy-connection-event': event => {
|
||||
APP.conference.onProxyConnectionEvent(event);
|
||||
},
|
||||
'resize-large-video': (width, height) => {
|
||||
logger.debug('Resize large video command received');
|
||||
sendAnalytics(createApiEvent('largevideo.resized'));
|
||||
APP.store.dispatch(resizeLargeVideo(width, height));
|
||||
},
|
||||
'send-tones': (options = {}) => {
|
||||
const { duration, tones, pause } = options;
|
||||
|
||||
@@ -349,21 +334,6 @@ function initCommands() {
|
||||
const { name } = request;
|
||||
|
||||
switch (name) {
|
||||
case 'capture-largevideo-screenshot' :
|
||||
APP.store.dispatch(captureLargeVideoScreenshot())
|
||||
.then(dataURL => {
|
||||
let error;
|
||||
|
||||
if (!dataURL) {
|
||||
error = new Error('No large video found!');
|
||||
}
|
||||
|
||||
callback({
|
||||
error,
|
||||
dataURL
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'invite': {
|
||||
const { invitees } = request;
|
||||
|
||||
|
||||
40
modules/API/external/external_api.js
vendored
40
modules/API/external/external_api.js
vendored
@@ -35,8 +35,6 @@ const commands = {
|
||||
hangup: 'video-hangup',
|
||||
muteEveryone: 'mute-everyone',
|
||||
password: 'password',
|
||||
pinParticipant: 'pin-participant',
|
||||
resizeLargeVideo: 'resize-large-video',
|
||||
sendEndpointTextMessage: 'send-endpoint-text-message',
|
||||
sendTones: 'send-tones',
|
||||
setLargeVideoParticipant: 'set-large-video-participant',
|
||||
@@ -439,12 +437,10 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
const parsedWidth = parseSizeParam(width);
|
||||
|
||||
if (parsedHeight !== undefined) {
|
||||
this._height = height;
|
||||
this._frame.style.height = parsedHeight;
|
||||
}
|
||||
|
||||
if (parsedWidth !== undefined) {
|
||||
this._width = width;
|
||||
this._frame.style.width = parsedWidth;
|
||||
}
|
||||
}
|
||||
@@ -637,18 +633,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the screenshot of the large video.
|
||||
*
|
||||
* @returns {Promise<string>} - Resolves with a base64 encoded image data of the screenshot
|
||||
* if large video is detected, an error otherwise.
|
||||
*/
|
||||
captureLargeVideoScreenshot() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'capture-largevideo-screenshot'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listeners and removes the Jitsi Meet frame.
|
||||
*
|
||||
@@ -920,17 +904,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins a participant's video on to the stage view.
|
||||
*
|
||||
* @param {string} participantId - Participant id (JID) of the participant
|
||||
* that needs to be pinned on the stage view.
|
||||
* @returns {void}
|
||||
*/
|
||||
pinParticipant(participantId) {
|
||||
this.executeCommand('pinParticipant', participantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes event listener.
|
||||
*
|
||||
@@ -957,19 +930,6 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
eventList.forEach(event => this.removeEventListener(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the large video container as per the dimensions provided.
|
||||
*
|
||||
* @param {number} width - Width that needs to be applied on the large video container.
|
||||
* @param {number} height - Height that needs to be applied on the large video container.
|
||||
* @returns {void}
|
||||
*/
|
||||
resizeLargeVideo(width, height) {
|
||||
if (width <= this._width && height <= this._height) {
|
||||
this.executeCommand('resizeLargeVideo', width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes an event along to the local conference participant to establish
|
||||
* or update a direct peer connection. This is currently used for developing
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
*/
|
||||
const UIUtil = {
|
||||
|
||||
/**
|
||||
* Returns the available video width.
|
||||
*/
|
||||
getAvailableVideoWidth() {
|
||||
return window.innerWidth;
|
||||
},
|
||||
|
||||
/**
|
||||
* Escapes the given text.
|
||||
*/
|
||||
|
||||
@@ -15,12 +15,13 @@ import { VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import { CHAT_SIZE } from '../../../react/features/chat';
|
||||
import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video/actions';
|
||||
} from '../../../react/features/large-video';
|
||||
import { PresenceLabel } from '../../../react/features/presence-status';
|
||||
/* eslint-enable no-unused-vars */
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { createDeferred } from '../../util/helpers';
|
||||
import AudioLevels from '../audio_levels/AudioLevels';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
import { VideoContainer, VIDEO_CONTAINER_TYPE } from './VideoContainer';
|
||||
|
||||
@@ -67,30 +68,7 @@ export default class LargeVideoManager {
|
||||
// use the same video container to handle desktop tracks
|
||||
this.addContainer(DESKTOP_CONTAINER_TYPE, this.videoContainer);
|
||||
|
||||
/**
|
||||
* The preferred width passed as an argument to {@link updateContainerSize}.
|
||||
*
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.preferredWidth = undefined;
|
||||
|
||||
/**
|
||||
* The preferred height passed as an argument to {@link updateContainerSize}.
|
||||
*
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
this.preferredHeight = undefined;
|
||||
|
||||
/**
|
||||
* The calculated width that will be used for the large video.
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = 0;
|
||||
|
||||
/**
|
||||
* The calculated height that will be used for the large video.
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = 0;
|
||||
|
||||
/**
|
||||
@@ -345,32 +323,20 @@ export default class LargeVideoManager {
|
||||
/**
|
||||
* Update container size.
|
||||
*/
|
||||
updateContainerSize(width, height) {
|
||||
if (typeof width === 'number') {
|
||||
this.preferredWidth = width;
|
||||
}
|
||||
if (typeof height === 'number') {
|
||||
this.preferredHeight = height;
|
||||
}
|
||||
|
||||
let widthToUse = this.preferredWidth || window.innerWidth;
|
||||
updateContainerSize() {
|
||||
let widthToUse = UIUtil.getAvailableVideoWidth();
|
||||
const { isOpen } = APP.store.getState()['features/chat'];
|
||||
|
||||
/**
|
||||
* If chat state is open, we re-compute the container width by subtracting the default width of
|
||||
* the chat. We re-compute the width again after the chat window is closed. This is needed when
|
||||
* custom styling is configured on the large video container through the iFrame API.
|
||||
*/
|
||||
if (isOpen && !this.resizedForChat) {
|
||||
if (isOpen) {
|
||||
/**
|
||||
* If chat state is open, we re-compute the container width
|
||||
* by subtracting the default width of the chat.
|
||||
*/
|
||||
widthToUse -= CHAT_SIZE;
|
||||
this.resizedForChat = true;
|
||||
} else if (this.resizedForChat) {
|
||||
this.resizedForChat = false;
|
||||
widthToUse += CHAT_SIZE;
|
||||
}
|
||||
|
||||
this.width = widthToUse;
|
||||
this.height = this.preferredHeight || window.innerHeight;
|
||||
this.height = window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -373,6 +373,7 @@ export default class RemoteVideo extends SmallVideo {
|
||||
|
||||
if (stream === this.videoStream) {
|
||||
this.videoStream = null;
|
||||
this.videoType = undefined;
|
||||
}
|
||||
|
||||
this.updateView();
|
||||
@@ -483,6 +484,7 @@ export default class RemoteVideo extends SmallVideo {
|
||||
|
||||
if (isVideo) {
|
||||
this.videoStream = stream;
|
||||
this.videoType = stream.videoType;
|
||||
} else {
|
||||
this.audioStream = stream;
|
||||
}
|
||||
|
||||
@@ -243,10 +243,6 @@ export default class SmallVideo {
|
||||
* or hidden
|
||||
*/
|
||||
setScreenSharing(isScreenSharing) {
|
||||
if (isScreenSharing === this.isScreenSharing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isScreenSharing = isScreenSharing;
|
||||
this.updateView();
|
||||
this.updateStatusBar();
|
||||
|
||||
@@ -489,7 +489,7 @@ const VideoLayout = {
|
||||
onVideoTypeChanged(id, newVideoType) {
|
||||
const remoteVideo = remoteVideos[id];
|
||||
|
||||
if (!remoteVideo) {
|
||||
if (!remoteVideo || remoteVideo.videoType === newVideoType) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ export default {
|
||||
})
|
||||
.catch(err => {
|
||||
audioTrackError = err;
|
||||
showError && APP.store.dispatch(notifyMicError(err));
|
||||
showError && APP.store.disptach(notifyMicError(err));
|
||||
|
||||
return [];
|
||||
}));
|
||||
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -10500,8 +10500,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#412606485511afc9a2495f6fb58aa76f65d509a9",
|
||||
"from": "github:jitsi/lib-jitsi-meet#412606485511afc9a2495f6fb58aa76f65d509a9",
|
||||
"version": "github:jitsi/lib-jitsi-meet#43e7c853b834dc7ced0f81ee5f4b130444d85e95",
|
||||
"from": "github:jitsi/lib-jitsi-meet#43e7c853b834dc7ced0f81ee5f4b130444d85e95",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.0",
|
||||
"@jitsi/sdp-interop": "1.0.3",
|
||||
@@ -13141,11 +13141,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"q": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
|
||||
@@ -13804,8 +13799,8 @@
|
||||
"integrity": "sha512-00DphusbzXUuJDP+SSRe7FJczFWzrCErr+2TdRxYfbQjpDaRk3rXw8nhlQXI5U6KthquEQOrCRMq2F2hi/HKLg=="
|
||||
},
|
||||
"react-native-calendar-events": {
|
||||
"version": "github:jitsi/react-native-calendar-events#df48ecdc4e1e90c5352f803ddbab1fa7269b74a7",
|
||||
"from": "github:jitsi/react-native-calendar-events#df48ecdc4e1e90c5352f803ddbab1fa7269b74a7"
|
||||
"version": "github:jitsi/react-native-calendar-events#928a80e2ffef0d7e84936d7e7e0acc4f53ee8470",
|
||||
"from": "github:jitsi/react-native-calendar-events#928a80e2ffef0d7e84936d7e7e0acc4f53ee8470"
|
||||
},
|
||||
"react-native-callstats": {
|
||||
"version": "3.61.0",
|
||||
@@ -15239,11 +15234,6 @@
|
||||
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA=="
|
||||
},
|
||||
"stackblur-canvas": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.3.0.tgz",
|
||||
"integrity": "sha512-3ZHJv+43D8YttgumssIxkfs3hBXW7XaMS5Ux65fOBhKDYMjbG5hF8Ey8a90RiiJ58aQnAhWbGilPzZ9rkIlWgQ=="
|
||||
},
|
||||
"stacktrace-parser": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.8.tgz",
|
||||
|
||||
@@ -56,14 +56,13 @@
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#412606485511afc9a2495f6fb58aa76f65d509a9",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#43e7c853b834dc7ced0f81ee5f4b130444d85e95",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.19",
|
||||
"moment": "2.19.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
|
||||
"pixelmatch": "5.1.0",
|
||||
"punycode": "2.1.1",
|
||||
"react": "16.9",
|
||||
"react-dom": "16.9",
|
||||
"react-emoji-render": "1.2.4",
|
||||
@@ -71,7 +70,7 @@
|
||||
"react-linkify": "1.0.0-alpha",
|
||||
"react-native": "github:jitsi/react-native#efd2aff5661d75a230e36406b698cfe0ee545be2",
|
||||
"react-native-background-timer": "2.4.0",
|
||||
"react-native-calendar-events": "github:jitsi/react-native-calendar-events#df48ecdc4e1e90c5352f803ddbab1fa7269b74a7",
|
||||
"react-native-calendar-events": "github:jitsi/react-native-calendar-events#928a80e2ffef0d7e84936d7e7e0acc4f53ee8470",
|
||||
"react-native-callstats": "3.61.0",
|
||||
"react-native-collapsible": "1.5.1",
|
||||
"react-native-default-preference": "1.4.2",
|
||||
@@ -93,7 +92,6 @@
|
||||
"redux-thunk": "2.2.0",
|
||||
"rnnoise-wasm": "github:jitsi/rnnoise-wasm.git#566a16885897704d6e6d67a1d5ac5d39781db2af",
|
||||
"rtcstats": "github:jitsi/rtcstats#v6.2.0",
|
||||
"stackblur-canvas": "2.3.0",
|
||||
"styled-components": "3.4.9",
|
||||
"util": "0.12.1",
|
||||
"uuid": "3.1.0",
|
||||
|
||||
@@ -57,15 +57,12 @@ export function resetAnalytics() {
|
||||
* @param {Store} store - The redux store in which the specified {@code action} is being dispatched.
|
||||
* @returns {Promise} Resolves with the handlers that have been successfully loaded.
|
||||
*/
|
||||
export async function createHandlers({ getState }: { getState: Function }) {
|
||||
export function createHandlers({ getState }: { getState: Function }) {
|
||||
getJitsiMeetGlobalNS().analyticsHandlers = [];
|
||||
window.analyticsHandlers = []; // Legacy support.
|
||||
|
||||
if (!isAnalyticsEnabled(getState)) {
|
||||
// Avoid all analytics processing if there are no handlers, since no event would be sent.
|
||||
analytics.dispose();
|
||||
|
||||
return [];
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
@@ -103,47 +100,43 @@ export async function createHandlers({ getState }: { getState: Function }) {
|
||||
};
|
||||
const handlers = [];
|
||||
|
||||
if (amplitudeAPPKey) {
|
||||
try {
|
||||
const amplitude = new AmplitudeHandler(handlerConstructorOptions);
|
||||
try {
|
||||
const amplitude = new AmplitudeHandler(handlerConstructorOptions);
|
||||
|
||||
analytics.amplitudeIdentityProps = amplitude.getIdentityProps();
|
||||
analytics.amplitudeIdentityProps = amplitude.getIdentityProps();
|
||||
|
||||
handlers.push(amplitude);
|
||||
} catch (e) {
|
||||
logger.error('Failed to initialize Amplitude handler', e);
|
||||
}
|
||||
}
|
||||
handlers.push(amplitude);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
|
||||
if (matomoEndpoint && matomoSiteID) {
|
||||
try {
|
||||
const matomo = new MatomoHandler(handlerConstructorOptions);
|
||||
try {
|
||||
const matomo = new MatomoHandler(handlerConstructorOptions);
|
||||
|
||||
handlers.push(matomo);
|
||||
} catch (e) {
|
||||
logger.error('Failed to initialize Matomo handler', e);
|
||||
}
|
||||
}
|
||||
handlers.push(matomo);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
|
||||
if (Array.isArray(scriptURLs) && scriptURLs.length > 0) {
|
||||
let externalHandlers;
|
||||
return (
|
||||
_loadHandlers(scriptURLs, handlerConstructorOptions)
|
||||
.then(externalHandlers => {
|
||||
handlers.push(...externalHandlers);
|
||||
if (handlers.length === 0) {
|
||||
// Throwing an error in order to dispose the analytics in the catch clause due to the lack of any
|
||||
// analytics handlers.
|
||||
throw new Error('No analytics handlers created!');
|
||||
}
|
||||
|
||||
try {
|
||||
externalHandlers = await _loadHandlers(scriptURLs, handlerConstructorOptions);
|
||||
handlers.push(...externalHandlers);
|
||||
} catch (e) {
|
||||
logger.error('Failed to initialize external analytics handlers', e);
|
||||
}
|
||||
}
|
||||
return handlers;
|
||||
})
|
||||
.catch(e => {
|
||||
analytics.dispose();
|
||||
if (handlers.length !== 0) {
|
||||
logger.error(e);
|
||||
}
|
||||
|
||||
// Avoid all analytics processing if there are no handlers, since no event would be sent.
|
||||
if (handlers.length === 0) {
|
||||
analytics.dispose();
|
||||
}
|
||||
return [];
|
||||
}));
|
||||
|
||||
logger.info(`Initialized ${handlers.length} analytics handlers`);
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,7 +228,7 @@ function _inIframe() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the scripts for the external analytics handlers and creates them.
|
||||
* Tries to load the scripts for the analytics handlers and creates them.
|
||||
*
|
||||
* @param {Array} scriptURLs - The array of script urls to load.
|
||||
* @param {Object} handlerConstructorOptions - The default options to pass when creating handlers.
|
||||
@@ -286,7 +279,7 @@ function _loadHandlers(scriptURLs = [], handlerConstructorOptions) {
|
||||
logger.warn(`Error creating analytics handler: ${error}`);
|
||||
}
|
||||
}
|
||||
logger.debug(`Loaded ${handlers.length} external analytics handlers`);
|
||||
logger.debug(`Loaded ${handlers.length} analytics handlers`);
|
||||
|
||||
return handlers;
|
||||
});
|
||||
|
||||
@@ -72,11 +72,6 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
* @returns {Object}
|
||||
*/
|
||||
getIdentityProps() {
|
||||
// TODO: Remove when web and native Aplitude implementations are unified.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
sessionId: amplitude.getInstance(this._amplitudeOptions).getSessionId(),
|
||||
deviceId: amplitude.getInstance(this._amplitudeOptions).options.deviceId,
|
||||
|
||||
@@ -294,8 +294,6 @@ export function maybeRedirectToWelcomePage(options: Object = {}) {
|
||||
if (enableClosePage) {
|
||||
if (isVpaasMeeting(getState())) {
|
||||
redirectToStaticPage('/');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { isGuest, jwt } = getState()['features/base/jwt'];
|
||||
|
||||
@@ -15,6 +15,8 @@ export default [
|
||||
'abTesting',
|
||||
'analytics.disabled',
|
||||
'audioLevelsInterval',
|
||||
'autoRecord',
|
||||
'autoRecordToken',
|
||||
'apiLogLevels',
|
||||
'avgRtpStatsN',
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import punycode from 'punycode';
|
||||
import React, { Component } from 'react';
|
||||
import ReactLinkify from 'react-linkify';
|
||||
import { Text } from 'react-native';
|
||||
@@ -69,7 +68,7 @@ export default class Linkify extends Component<Props> {
|
||||
key = { key }
|
||||
style = { this.props.linkStyle }
|
||||
url = { decoratedHref }>
|
||||
{ punycode.toASCII(decoratedText) }
|
||||
{decoratedText}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import punycode from 'punycode';
|
||||
import React, { Component } from 'react';
|
||||
import ReactLinkify from 'react-linkify';
|
||||
|
||||
@@ -45,7 +44,7 @@ export default class Linkify extends Component<Props> {
|
||||
key = { key }
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ punycode.toASCII(decoratedText) }
|
||||
{decoratedText}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,19 +24,35 @@ const _RIGHT_WATERMARK_STYLE = {
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The link used to navigate to on logo click.
|
||||
* The user selected url used to navigate to on logo click.
|
||||
*/
|
||||
_logoLink: string,
|
||||
_customLogoLink: string,
|
||||
|
||||
/**
|
||||
* The url for the logo.
|
||||
* The url of the user selected logo.
|
||||
*/
|
||||
_logoUrl: string,
|
||||
_customLogoUrl: string,
|
||||
|
||||
/**
|
||||
* If the Jitsi watermark should be displayed or not.
|
||||
* Whether or not the current user is logged in through a JWT.
|
||||
*/
|
||||
_showJitsiWatermark: boolean,
|
||||
_isGuest: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the current meeting is a vpaas one.
|
||||
*/
|
||||
_isVpaas: boolean,
|
||||
|
||||
/**
|
||||
* Flag used to signal that the logo can be displayed.
|
||||
* It becomes true after the user customization options are fetched.
|
||||
*/
|
||||
_readyToDisplayJitsiWatermark: boolean,
|
||||
|
||||
/**
|
||||
* Returns true if welcome page is visible at the moment.
|
||||
*/
|
||||
_welcomePageIsVisible: boolean,
|
||||
|
||||
/**
|
||||
* The default value for the Jitsi logo URL.
|
||||
@@ -59,11 +75,27 @@ type State = {
|
||||
*/
|
||||
brandWatermarkLink: string,
|
||||
|
||||
/**
|
||||
* The url to open when clicking the Jitsi watermark.
|
||||
*/
|
||||
jitsiWatermarkLink: string,
|
||||
|
||||
/**
|
||||
* Whether or not the brand watermark should be displayed.
|
||||
*/
|
||||
showBrandWatermark: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the Jitsi watermark should be displayed.
|
||||
*/
|
||||
showJitsiWatermark: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the Jitsi watermark should be displayed for users not
|
||||
* logged in through a JWT.
|
||||
*/
|
||||
showJitsiWatermarkForGuests: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the show the "powered by Jitsi.org" link.
|
||||
*/
|
||||
@@ -85,17 +117,29 @@ class Watermarks extends Component<Props, State> {
|
||||
super(props);
|
||||
|
||||
let showBrandWatermark;
|
||||
let showJitsiWatermark;
|
||||
let showJitsiWatermarkForGuests;
|
||||
|
||||
if (interfaceConfig.filmStripOnly) {
|
||||
showBrandWatermark = false;
|
||||
showJitsiWatermark = false;
|
||||
showJitsiWatermarkForGuests = false;
|
||||
} else {
|
||||
showBrandWatermark = interfaceConfig.SHOW_BRAND_WATERMARK;
|
||||
showJitsiWatermark = interfaceConfig.SHOW_JITSI_WATERMARK;
|
||||
showJitsiWatermarkForGuests
|
||||
= interfaceConfig.SHOW_WATERMARK_FOR_GUESTS;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
brandWatermarkLink:
|
||||
showBrandWatermark ? interfaceConfig.BRAND_WATERMARK_LINK : '',
|
||||
jitsiWatermarkLink:
|
||||
showJitsiWatermark || showJitsiWatermarkForGuests
|
||||
? interfaceConfig.JITSI_WATERMARK_LINK : '',
|
||||
showBrandWatermark,
|
||||
showJitsiWatermark,
|
||||
showJitsiWatermarkForGuests,
|
||||
showPoweredBy: interfaceConfig.SHOW_POWERED_BY
|
||||
};
|
||||
}
|
||||
@@ -122,6 +166,55 @@ class Watermarks extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the watermark is ready to be displayed.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_canDisplayJitsiWatermark() {
|
||||
const {
|
||||
showJitsiWatermark,
|
||||
showJitsiWatermarkForGuests
|
||||
} = this.state;
|
||||
const {
|
||||
_isGuest,
|
||||
_readyToDisplayJitsiWatermark,
|
||||
_welcomePageIsVisible
|
||||
} = this.props;
|
||||
|
||||
return (_readyToDisplayJitsiWatermark
|
||||
&& (showJitsiWatermark || (_isGuest && showJitsiWatermarkForGuests)))
|
||||
|| _welcomePageIsVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the background image style.
|
||||
*
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_getBackgroundImageStyle() {
|
||||
const {
|
||||
_customLogoUrl,
|
||||
_isVpaas,
|
||||
defaultJitsiLogoURL
|
||||
} = this.props;
|
||||
let style = 'none';
|
||||
|
||||
if (_isVpaas) {
|
||||
if (_customLogoUrl) {
|
||||
style = `url(${_customLogoUrl})`;
|
||||
}
|
||||
} else {
|
||||
style = `url(${_customLogoUrl
|
||||
|| defaultJitsiLogoURL
|
||||
|| interfaceConfig.DEFAULT_LOGO_URL})`;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a brand watermark if it is enabled.
|
||||
*
|
||||
@@ -161,28 +254,33 @@ class Watermarks extends Component<Props, State> {
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
_renderJitsiWatermark() {
|
||||
const {
|
||||
_logoLink,
|
||||
_logoUrl,
|
||||
_showJitsiWatermark
|
||||
} = this.props;
|
||||
let reactElement = null;
|
||||
|
||||
if (_showJitsiWatermark) {
|
||||
if (this._canDisplayJitsiWatermark()) {
|
||||
const backgroundImage = this._getBackgroundImageStyle();
|
||||
const link = this.props._customLogoLink || this.state.jitsiWatermarkLink;
|
||||
const additionalStyles = {};
|
||||
|
||||
if (backgroundImage === 'none') {
|
||||
additionalStyles.height = 0;
|
||||
additionalStyles.width = 0;
|
||||
}
|
||||
|
||||
const style = {
|
||||
backgroundImage: `url(${_logoUrl})`,
|
||||
backgroundImage,
|
||||
maxWidth: 140,
|
||||
maxHeight: 70
|
||||
maxHeight: 70,
|
||||
...additionalStyles
|
||||
};
|
||||
|
||||
reactElement = (<div
|
||||
className = 'watermark leftwatermark'
|
||||
style = { style } />);
|
||||
|
||||
if (_logoLink) {
|
||||
if (link) {
|
||||
reactElement = (
|
||||
<a
|
||||
href = { _logoLink }
|
||||
href = { link }
|
||||
target = '_new'>
|
||||
{ reactElement }
|
||||
</a>
|
||||
@@ -221,52 +319,27 @@ class Watermarks extends Component<Props, State> {
|
||||
* Maps parts of Redux store to component prop types.
|
||||
*
|
||||
* @param {Object} state - Snapshot of Redux store.
|
||||
* @param {Object} ownProps - Component's own props.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
function _mapStateToProps(state) {
|
||||
const { isGuest } = state['features/base/jwt'];
|
||||
const {
|
||||
customizationReady,
|
||||
customizationFailed,
|
||||
defaultBranding,
|
||||
useDynamicBrandingData,
|
||||
logoClickUrl,
|
||||
logoImageUrl
|
||||
} = state['features/dynamic-branding'];
|
||||
const isValidRoom = state['features/base/conference'].room;
|
||||
const {
|
||||
DEFAULT_LOGO_URL,
|
||||
JITSI_WATERMARK_LINK,
|
||||
SHOW_JITSI_WATERMARK,
|
||||
SHOW_JITSI_WATERMARK_FOR_GUESTS,
|
||||
filmStripOnly
|
||||
} = interfaceConfig;
|
||||
let _showJitsiWatermark = (!filmStripOnly
|
||||
&& (customizationReady && !customizationFailed)
|
||||
&& (SHOW_JITSI_WATERMARK || (isGuest && SHOW_JITSI_WATERMARK_FOR_GUESTS)))
|
||||
|| !isValidRoom;
|
||||
let _logoUrl = logoImageUrl;
|
||||
let _logoLink = logoClickUrl;
|
||||
|
||||
if (useDynamicBrandingData) {
|
||||
if (isVpaasMeeting(state)) {
|
||||
// don't show logo if request fails or no logo set for vpaas meetings
|
||||
_showJitsiWatermark = !customizationFailed && Boolean(logoImageUrl);
|
||||
} else if (defaultBranding) {
|
||||
_logoUrl = DEFAULT_LOGO_URL;
|
||||
_logoLink = JITSI_WATERMARK_LINK;
|
||||
}
|
||||
} else {
|
||||
// When there is no custom branding data use defaults
|
||||
_logoUrl = ownProps.defaultJitsiLogoURL || DEFAULT_LOGO_URL;
|
||||
_logoLink = JITSI_WATERMARK_LINK;
|
||||
}
|
||||
const { customizationReady, logoClickUrl, logoImageUrl } = state['features/dynamic-branding'];
|
||||
const { room } = state['features/base/conference'];
|
||||
|
||||
return {
|
||||
_logoLink,
|
||||
_logoUrl,
|
||||
_showJitsiWatermark
|
||||
/**
|
||||
* The indicator which determines whether the local participant is a
|
||||
* guest in the conference.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
_customLogoLink: logoClickUrl,
|
||||
_customLogoUrl: logoImageUrl,
|
||||
_isGuest: isGuest,
|
||||
_isVpaas: isVpaasMeeting(state),
|
||||
_readyToDisplayJitsiWatermark: customizationReady,
|
||||
_welcomePageIsVisible: !room
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
*/
|
||||
export const SET_DYNAMIC_BRANDING_DATA = 'SET_DYNAMIC_BRANDING_DATA';
|
||||
|
||||
/**
|
||||
* Action used to signal the customization failed.
|
||||
*/
|
||||
export const SET_DYNAMIC_BRANDING_FAILED = 'SET_DYNAMIC_BRANDING_FAILED';
|
||||
|
||||
/**
|
||||
* Action used to signal the branding elements are ready to be displayed
|
||||
*/
|
||||
|
||||
@@ -4,11 +4,7 @@ import { getLogger } from 'jitsi-meet-logger';
|
||||
|
||||
import { doGetJSON } from '../base/util';
|
||||
|
||||
import {
|
||||
SET_DYNAMIC_BRANDING_DATA,
|
||||
SET_DYNAMIC_BRANDING_FAILED,
|
||||
SET_DYNAMIC_BRANDING_READY
|
||||
} from './actionTypes';
|
||||
import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes';
|
||||
import { extractFqnFromPath } from './functions';
|
||||
|
||||
const logger = getLogger(__filename);
|
||||
@@ -36,8 +32,6 @@ export function fetchCustomBrandingData() {
|
||||
return dispatch(setDynamicBrandingData(res));
|
||||
} catch (err) {
|
||||
logger.error('Error fetching branding data', err);
|
||||
|
||||
return dispatch(setDynamicBrandingFailed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,14 +63,3 @@ function setDynamicBrandingReady() {
|
||||
type: SET_DYNAMIC_BRANDING_READY
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to signal the branding request failed.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function setDynamicBrandingFailed() {
|
||||
return {
|
||||
type: SET_DYNAMIC_BRANDING_FAILED
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
SET_DYNAMIC_BRANDING_DATA,
|
||||
SET_DYNAMIC_BRANDING_FAILED,
|
||||
SET_DYNAMIC_BRANDING_READY
|
||||
} from './actionTypes';
|
||||
import { SET_DYNAMIC_BRANDING_DATA, SET_DYNAMIC_BRANDING_READY } from './actionTypes';
|
||||
|
||||
/**
|
||||
* The name of the redux store/state property which is the root of the redux
|
||||
@@ -15,80 +11,12 @@ import {
|
||||
const STORE_NAME = 'features/dynamic-branding';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
/**
|
||||
* The custom background color for the LargeVideo.
|
||||
*
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
backgroundColor: '',
|
||||
|
||||
/**
|
||||
* The custom background image used on the LargeVideo.
|
||||
*
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
backgroundImageUrl: '',
|
||||
|
||||
/**
|
||||
* Flag indicating that the logo (JitsiWatermark) can be displayed.
|
||||
* This is used in order to avoid image flickering.
|
||||
*
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
customizationReady: false,
|
||||
|
||||
/**
|
||||
* Flag indicating that the dynamic branding data request has failed.
|
||||
* When the request fails there is no logo (JitsiWatermark) displayed.
|
||||
*
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
customizationFailed: false,
|
||||
|
||||
/**
|
||||
* Flag indicating that the dynamic branding has not been modified and should use
|
||||
* the default options.
|
||||
*
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
defaultBranding: true,
|
||||
|
||||
/**
|
||||
* The custom invite domain.
|
||||
*
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
inviteDomain: '',
|
||||
|
||||
/**
|
||||
* The custom url used when the user clicks the logo.
|
||||
*
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
logoClickUrl: '',
|
||||
|
||||
/**
|
||||
* The custom logo (JitisWatermark).
|
||||
*
|
||||
* @public
|
||||
* @type {string}
|
||||
*/
|
||||
logoImageUrl: '',
|
||||
|
||||
/**
|
||||
* Flag used to signal if the app should use a custom logo or not
|
||||
*
|
||||
* @public
|
||||
* @type {boolean}
|
||||
*/
|
||||
useDynamicBrandingData: false
|
||||
logoImageUrl: ''
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -97,33 +25,15 @@ const DEFAULT_STATE = {
|
||||
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case SET_DYNAMIC_BRANDING_DATA: {
|
||||
const {
|
||||
backgroundColor,
|
||||
backgroundImageUrl,
|
||||
defaultBranding,
|
||||
inviteDomain,
|
||||
logoClickUrl,
|
||||
logoImageUrl
|
||||
} = action.value;
|
||||
const { backgroundColor, backgroundImageUrl, inviteDomain, logoClickUrl, logoImageUrl } = action.value;
|
||||
|
||||
return {
|
||||
backgroundColor,
|
||||
backgroundImageUrl,
|
||||
defaultBranding,
|
||||
inviteDomain,
|
||||
logoClickUrl,
|
||||
logoImageUrl,
|
||||
customizationFailed: false,
|
||||
customizationReady: true,
|
||||
useDynamicBrandingData: true
|
||||
};
|
||||
}
|
||||
case SET_DYNAMIC_BRANDING_FAILED: {
|
||||
return {
|
||||
...state,
|
||||
customizationReady: true,
|
||||
customizationFailed: true,
|
||||
useDynamicBrandingData: true
|
||||
customizationReady: true
|
||||
};
|
||||
}
|
||||
case SET_DYNAMIC_BRANDING_READY:
|
||||
@@ -131,6 +41,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
...state,
|
||||
customizationReady: true
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
@@ -10,7 +10,6 @@ import { translate } from '../../../../base/i18n';
|
||||
import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet';
|
||||
import { getLocalParticipant } from '../../../../base/participants';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||
import EmbedMeetingTrigger from '../../../../embed-meeting/components/EmbedMeetingTrigger';
|
||||
import { getActiveSession } from '../../../../recording';
|
||||
import { updateDialInNumbers } from '../../../actions';
|
||||
@@ -37,11 +36,6 @@ type Props = {
|
||||
*/
|
||||
_dialIn: Object,
|
||||
|
||||
/**
|
||||
* Whether or not embed meeting should be visible.
|
||||
*/
|
||||
_embedMeetingVisible: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not invite contacts should be visible.
|
||||
*/
|
||||
@@ -86,7 +80,6 @@ type Props = {
|
||||
function AddPeopleDialog({
|
||||
_conferenceName,
|
||||
_dialIn,
|
||||
_embedMeetingVisible,
|
||||
_inviteContactsVisible,
|
||||
_inviteUrl,
|
||||
_liveStreamViewURL,
|
||||
@@ -159,7 +152,7 @@ function AddPeopleDialog({
|
||||
<InviteByEmailSection
|
||||
inviteSubject = { inviteSubject }
|
||||
inviteText = { invite } />
|
||||
{ _embedMeetingVisible && <EmbedMeetingTrigger /> }
|
||||
<EmbedMeetingTrigger />
|
||||
<div className = 'invite-more-dialog separator' />
|
||||
{
|
||||
_liveStreamViewURL
|
||||
@@ -198,7 +191,6 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
_conferenceName: getRoomName(state),
|
||||
_dialIn: state['features/invite'],
|
||||
_embedMeetingVisible: !isVpaasMeeting(state),
|
||||
_inviteContactsVisible: interfaceConfig.ENABLE_DIAL_OUT && !hideInviteContacts,
|
||||
_inviteUrl: getInviteURL(state),
|
||||
_liveStreamViewURL:
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
|
||||
} from './actionTypes';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Signals conference to select a participant.
|
||||
*
|
||||
@@ -48,7 +50,7 @@ export function selectParticipant() {
|
||||
|
||||
/**
|
||||
* Action to select the participant to be displayed in LargeVideo based on the
|
||||
* participant id provided. If a participant id is not provided, the LargeVideo
|
||||
* participant id provided. If a partcipant id is not provided, the LargeVideo
|
||||
* participant will be selected based on a variety of factors: If there is a
|
||||
* dominant or pinned speaker, or if there are remote tracks, etc.
|
||||
*
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export * from './actions.any';
|
||||
@@ -1,75 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
|
||||
import { MEDIA_TYPE } from '../base/media';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
/**
|
||||
* Captures a screenshot of the video displayed on the large video.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function captureLargeVideoScreenshot() {
|
||||
return (dispatch: Dispatch<any>, getState: Function): Promise<string> => {
|
||||
const state = getState();
|
||||
const largeVideo = state['features/large-video'];
|
||||
|
||||
if (!largeVideo) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const tracks = state['features/base/tracks'];
|
||||
const { jitsiTrack } = getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideo.participantId);
|
||||
const videoStream = jitsiTrack.getOriginalStream();
|
||||
|
||||
// Get the video element for the large video, cast HTMLElement to HTMLVideoElement to make flow happy.
|
||||
/* eslint-disable-next-line no-extra-parens*/
|
||||
const videoElement = ((document.getElementById('largeVideo'): any): HTMLVideoElement);
|
||||
|
||||
if (!videoElement) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Create a HTML canvas and draw video on to the canvas.
|
||||
const [ track ] = videoStream.getVideoTracks();
|
||||
const { height, width } = track.getSettings() ?? track.getConstraints();
|
||||
const canvasElement = document.createElement('canvas');
|
||||
const ctx = canvasElement.getContext('2d');
|
||||
|
||||
canvasElement.style.display = 'none';
|
||||
canvasElement.height = parseInt(height, 10);
|
||||
canvasElement.width = parseInt(width, 10);
|
||||
ctx.drawImage(videoElement, 0, 0);
|
||||
const dataURL = canvasElement.toDataURL('image/png', 1.0);
|
||||
|
||||
// Cleanup.
|
||||
ctx.clearRect(0, 0, canvasElement.width, canvasElement.height);
|
||||
canvasElement.remove();
|
||||
|
||||
return Promise.resolve(dataURL);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the large video container based on the dimensions provided.
|
||||
*
|
||||
* @param {number} width - Width that needs to be applied on the large video container.
|
||||
* @param {number} height - Height that needs to be applied on the large video container.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function resizeLargeVideo(width: number, height: number) {
|
||||
return (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const state = getState();
|
||||
const largeVideo = state['features/large-video'];
|
||||
|
||||
if (largeVideo) {
|
||||
const largeVideoContainer = VideoLayout.getLargeVideo();
|
||||
|
||||
largeVideoContainer.updateContainerSize(width, height);
|
||||
largeVideoContainer.resize();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
/**
|
||||
* Checks whether rtcstats is enabled or not.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux store or {@code getState} function.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isRtcstatsEnabled(stateful: Function | Object) {
|
||||
// TODO: Remove when rtcstats is fully cimpatible with mobile.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = toState(stateful);
|
||||
const config = state['features/base/config'];
|
||||
|
||||
return config?.analytics?.rtcstatsEnabled ?? false;
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import { getLocalParticipant } from '../base/participants';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import RTCStats from './RTCStats';
|
||||
import { isRtcstatsEnabled } from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
switch (action.type) {
|
||||
case LIB_WILL_INIT: {
|
||||
if (isRtcstatsEnabled(state)) {
|
||||
if (analytics.rtcstatsEnabled) {
|
||||
// RTCStats "proxies" WebRTC functions such as GUM and RTCPeerConnection by rewriting the global
|
||||
// window functions. Because lib-jitsi-meet uses references to those functions that are taken on
|
||||
// init, we need to add these proxies before it initializes, otherwise lib-jitsi-meet will use the
|
||||
@@ -48,7 +47,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
break;
|
||||
}
|
||||
case CONFERENCE_JOINED: {
|
||||
if (isRtcstatsEnabled(state) && RTCStats.isInitialized()) {
|
||||
if (analytics.rtcstatsEnabled && RTCStats.isInitialized()) {
|
||||
// Once the conference started connect to the rtcstats server and send data.
|
||||
try {
|
||||
RTCStats.connect();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// @flow
|
||||
|
||||
import * as StackBlur from 'stackblur-canvas';
|
||||
import * as bodyPix from '@tensorflow-models/body-pix';
|
||||
|
||||
import {
|
||||
CLEAR_TIMEOUT,
|
||||
TIMEOUT_TICK,
|
||||
SET_TIMEOUT,
|
||||
CLEAR_INTERVAL,
|
||||
INTERVAL_TIMEOUT,
|
||||
SET_INTERVAL,
|
||||
timerWorkerScript
|
||||
} from './TimerWorker';
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
export default class JitsiStreamBlurEffect {
|
||||
_bpModel: Object;
|
||||
_inputVideoElement: HTMLVideoElement;
|
||||
_inputVideoCanvasElement: HTMLCanvasElement;
|
||||
_onMaskFrameTimer: Function;
|
||||
_maskFrameTimerWorker: Worker;
|
||||
_maskInProgress: boolean;
|
||||
@@ -44,7 +43,6 @@ export default class JitsiStreamBlurEffect {
|
||||
this._outputCanvasElement = document.createElement('canvas');
|
||||
this._outputCanvasElement.getContext('2d');
|
||||
this._inputVideoElement = document.createElement('video');
|
||||
this._inputVideoCanvasElement = document.createElement('canvas');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,8 +53,10 @@ export default class JitsiStreamBlurEffect {
|
||||
* @returns {void}
|
||||
*/
|
||||
async _onMaskFrameTimer(response: Object) {
|
||||
if (response.data.id === TIMEOUT_TICK) {
|
||||
await this._renderMask();
|
||||
if (response.data.id === INTERVAL_TIMEOUT) {
|
||||
if (!this._maskInProgress) {
|
||||
await this._renderMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,53 +67,20 @@ export default class JitsiStreamBlurEffect {
|
||||
* @returns {void}
|
||||
*/
|
||||
async _renderMask() {
|
||||
if (!this._maskInProgress) {
|
||||
this._maskInProgress = true;
|
||||
this._bpModel.segmentPerson(this._inputVideoElement, {
|
||||
internalResolution: 'low', // resized to 0.5 times of the original resolution before inference
|
||||
maxDetections: 1, // max. number of person poses to detect per image
|
||||
segmentationThreshold: 0.7, // represents probability that a pixel belongs to a person
|
||||
flipHorizontal: false,
|
||||
scoreThreshold: 0.2
|
||||
}).then(data => {
|
||||
this._segmentationData = data;
|
||||
this._maskInProgress = false;
|
||||
});
|
||||
}
|
||||
const inputCanvasCtx = this._inputVideoCanvasElement.getContext('2d');
|
||||
|
||||
inputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
|
||||
|
||||
const currentFrame = inputCanvasCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
this._inputVideoCanvasElement.width,
|
||||
this._inputVideoCanvasElement.height
|
||||
);
|
||||
|
||||
if (this._segmentationData) {
|
||||
const blurData = new ImageData(currentFrame.data.slice(), currentFrame.width, currentFrame.height);
|
||||
|
||||
StackBlur.imageDataRGB(blurData, 0, 0, currentFrame.width, currentFrame.height, 12);
|
||||
|
||||
for (let x = 0; x < this._outputCanvasElement.width; x++) {
|
||||
for (let y = 0; y < this._outputCanvasElement.height; y++) {
|
||||
const n = (y * this._outputCanvasElement.width) + x;
|
||||
|
||||
if (this._segmentationData.data[n] === 0) {
|
||||
currentFrame.data[n * 4] = blurData.data[n * 4];
|
||||
currentFrame.data[(n * 4) + 1] = blurData.data[(n * 4) + 1];
|
||||
currentFrame.data[(n * 4) + 2] = blurData.data[(n * 4) + 2];
|
||||
currentFrame.data[(n * 4) + 3] = blurData.data[(n * 4) + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._outputCanvasElement.getContext('2d').putImageData(currentFrame, 0, 0);
|
||||
this._maskFrameTimerWorker.postMessage({
|
||||
id: SET_TIMEOUT,
|
||||
timeMs: 1000 / 30
|
||||
this._maskInProgress = true;
|
||||
this._segmentationData = await this._bpModel.segmentPerson(this._inputVideoElement, {
|
||||
internalResolution: 'medium', // resized to 0.5 times of the original resolution before inference
|
||||
maxDetections: 1, // max. number of person poses to detect per image
|
||||
segmentationThreshold: 0.7 // represents probability that a pixel belongs to a person
|
||||
});
|
||||
this._maskInProgress = false;
|
||||
bodyPix.drawBokehEffect(
|
||||
this._outputCanvasElement,
|
||||
this._inputVideoElement,
|
||||
this._segmentationData,
|
||||
12, // Constant for background blur, integer values between 0-20
|
||||
7 // Constant for edge blur, integer values between 0-20
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,16 +110,14 @@ export default class JitsiStreamBlurEffect {
|
||||
|
||||
this._outputCanvasElement.width = parseInt(width, 10);
|
||||
this._outputCanvasElement.height = parseInt(height, 10);
|
||||
this._inputVideoCanvasElement.width = parseInt(width, 10);
|
||||
this._inputVideoCanvasElement.height = parseInt(height, 10);
|
||||
this._inputVideoElement.width = parseInt(width, 10);
|
||||
this._inputVideoElement.height = parseInt(height, 10);
|
||||
this._inputVideoElement.autoplay = true;
|
||||
this._inputVideoElement.srcObject = stream;
|
||||
this._inputVideoElement.onloadeddata = () => {
|
||||
this._maskFrameTimerWorker.postMessage({
|
||||
id: SET_TIMEOUT,
|
||||
timeMs: 1000 / 30
|
||||
id: SET_INTERVAL,
|
||||
timeMs: 1000 / parseInt(frameRate, 10)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -166,7 +131,7 @@ export default class JitsiStreamBlurEffect {
|
||||
*/
|
||||
stopEffect() {
|
||||
this._maskFrameTimerWorker.postMessage({
|
||||
id: CLEAR_TIMEOUT
|
||||
id: CLEAR_INTERVAL
|
||||
});
|
||||
|
||||
this._maskFrameTimerWorker.terminate();
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
|
||||
/**
|
||||
* SET_TIMEOUT constant is used to set interval and it is set in
|
||||
* SET_INTERVAL constant is used to set interval and it is set in
|
||||
* the id property of the request.data property. timeMs property must
|
||||
* also be set. request.data example:
|
||||
*
|
||||
* {
|
||||
* id: SET_TIMEOUT,
|
||||
* id: SET_INTERVAL,
|
||||
* timeMs: 33
|
||||
* }
|
||||
*/
|
||||
export const SET_TIMEOUT = 1;
|
||||
export const SET_INTERVAL = 1;
|
||||
|
||||
/**
|
||||
* CLEAR_TIMEOUT constant is used to clear the interval and it is set in
|
||||
* CLEAR_INTERVAL constant is used to clear the interval and it is set in
|
||||
* the id property of the request.data property.
|
||||
*
|
||||
* {
|
||||
* id: CLEAR_TIMEOUT
|
||||
* id: CLEAR_INTERVAL
|
||||
* }
|
||||
*/
|
||||
export const CLEAR_TIMEOUT = 2;
|
||||
export const CLEAR_INTERVAL = 2;
|
||||
|
||||
/**
|
||||
* TIMEOUT_TICK constant is used as response and it is set in the id property.
|
||||
* INTERVAL_TIMEOUT constant is used as response and it is set in the id property.
|
||||
*
|
||||
* {
|
||||
* id: TIMEOUT_TICK
|
||||
* id: INTERVAL_TIMEOUT
|
||||
* }
|
||||
*/
|
||||
export const TIMEOUT_TICK = 3;
|
||||
export const INTERVAL_TIMEOUT = 3;
|
||||
|
||||
/**
|
||||
* The following code is needed as string to create a URL from a Blob.
|
||||
@@ -40,15 +40,15 @@ const code = `
|
||||
|
||||
onmessage = function(request) {
|
||||
switch (request.data.id) {
|
||||
case ${SET_TIMEOUT}: {
|
||||
timer = setTimeout(() => {
|
||||
postMessage({ id: ${TIMEOUT_TICK} });
|
||||
case ${SET_INTERVAL}: {
|
||||
timer = setInterval(() => {
|
||||
postMessage({ id: ${INTERVAL_TIMEOUT} });
|
||||
}, request.data.timeMs);
|
||||
break;
|
||||
}
|
||||
case ${CLEAR_TIMEOUT}: {
|
||||
case ${CLEAR_INTERVAL}: {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
clearInterval(timer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
import { connect, equals } from '../../../base/redux';
|
||||
import { OverflowMenuItem } from '../../../base/toolbox/components';
|
||||
import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
|
||||
import { isVpaasMeeting } from '../../../billing-counter/functions';
|
||||
import { VideoBlurButton } from '../../../blur';
|
||||
import { CHAT_SIZE, ChatCounter, toggleChat } from '../../../chat';
|
||||
import { EmbedMeetingDialog } from '../../../embed-meeting';
|
||||
@@ -137,11 +136,6 @@ type Props = {
|
||||
*/
|
||||
_isGuest: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the current meeting belongs to a JaaS user.
|
||||
*/
|
||||
_isVpaasMeeting: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
@@ -977,15 +971,6 @@ class Toolbox extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the profile button is visible and false otherwise.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isEmbedMeetingVisible() {
|
||||
return !this.props._isVpaasMeeting && this._shouldShowButton('embedmeeting');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the profile button is visible and false otherwise.
|
||||
*
|
||||
@@ -1062,7 +1047,7 @@ class Toolbox extends Component<Props, State> {
|
||||
key = 'stats'
|
||||
onClick = { this._onToolbarOpenSpeakerStats }
|
||||
text = { t('toolbar.speakerStats') } />,
|
||||
this._isEmbedMeetingVisible()
|
||||
this._shouldShowButton('embedmeeting')
|
||||
&& <OverflowMenuItem
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.embedMeeting') }
|
||||
icon = { IconCodeBlock }
|
||||
@@ -1448,7 +1433,6 @@ function _mapStateToProps(state) {
|
||||
_dialog: Boolean(state['features/base/dialog'].component),
|
||||
_feedbackConfigured: Boolean(callStatsID),
|
||||
_isGuest: state['features/base/jwt'].isGuest,
|
||||
_isVpaasMeeting: isVpaasMeeting(state),
|
||||
_fullScreen: fullScreen,
|
||||
_tileViewEnabled: shouldDisplayTileView(state),
|
||||
_localParticipantID: localParticipant.id,
|
||||
|
||||
@@ -5,7 +5,7 @@ import debounce from 'lodash/debounce';
|
||||
import { pinParticipant, getPinnedParticipant } from '../base/participants';
|
||||
import { StateListenerRegistry, equals } from '../base/redux';
|
||||
import { isFollowMeActive } from '../follow-me';
|
||||
import { selectParticipant } from '../large-video/actions';
|
||||
import { selectParticipant } from '../large-video';
|
||||
|
||||
import { setParticipantsWithScreenShare } from './actions';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
local new_throttle = require "util.throttle".create;
|
||||
local st = require "util.stanza";
|
||||
|
||||
local token_util = module:require "token/util".new(module);
|
||||
@@ -11,19 +10,12 @@ if token_util == nil then
|
||||
return;
|
||||
end
|
||||
|
||||
-- The maximum number of simultaneous calls,
|
||||
-- and also the maximum number of new calls per minute that a session is allowed to create.
|
||||
local limit_outgoing_calls;
|
||||
local function load_config()
|
||||
limit_outgoing_calls = module:get_option_number("max_number_outgoing_calls", -1);
|
||||
end
|
||||
load_config();
|
||||
-- configuration to limit number of outgoing calls
|
||||
local LIMIT_OUTGOING_CALLS = module:get_option_number("max_number_outgoing_calls", -1);
|
||||
|
||||
-- Header names to use to push extra data extracted from token, if any
|
||||
local OUT_INITIATOR_USER_ATTR_NAME = "X-outbound-call-initiator-user";
|
||||
local OUT_INITIATOR_GROUP_ATTR_NAME = "X-outbound-call-initiator-group";
|
||||
local OUTGOING_CALLS_THROTTLE_INTERVAL = 60; -- if max_number_outgoing_calls is enabled it will be
|
||||
-- the max number of outgoing calls a user can try for a minute
|
||||
|
||||
-- filters rayo iq in case of requested from not jwt authenticated sessions
|
||||
-- or if the session has features in user context and it doesn't mention
|
||||
@@ -61,21 +53,15 @@ module:hook("pre-iq/full", function(event)
|
||||
end
|
||||
|
||||
-- now lets check any limits if configured
|
||||
if limit_outgoing_calls > 0 then
|
||||
if not session.dial_out_throttle then
|
||||
module:log("debug", "Enabling dial-out throttle session=%s.", session);
|
||||
session.dial_out_throttle = new_throttle(limit_outgoing_calls, OUTGOING_CALLS_THROTTLE_INTERVAL);
|
||||
end
|
||||
|
||||
if not session.dial_out_throttle:poll(1) -- we first check the throttle so we can mark one incoming dial for the balance
|
||||
or get_concurrent_outgoing_count(session.jitsi_meet_context_user["id"], session.jitsi_meet_context_group)
|
||||
>= limit_outgoing_calls
|
||||
then
|
||||
module:log("warn",
|
||||
"Filtering stanza dial, stanza:%s, outgoing calls limit reached", tostring(stanza));
|
||||
session.send(st.error_reply(stanza, "cancel", "resource-constraint"));
|
||||
return true;
|
||||
end
|
||||
if LIMIT_OUTGOING_CALLS > 0
|
||||
and get_concurrent_outgoing_count(
|
||||
session.jitsi_meet_context_user["id"],
|
||||
session.jitsi_meet_context_group) >= LIMIT_OUTGOING_CALLS
|
||||
then
|
||||
module:log("warn",
|
||||
"Filtering stanza dial, stanza:%s, outgoing calls limit reached", tostring(stanza));
|
||||
session.send(st.error_reply(stanza, "cancel", "resource-constraint"));
|
||||
return true;
|
||||
end
|
||||
|
||||
-- now lets insert token information if any
|
||||
@@ -177,4 +163,3 @@ function get_concurrent_outgoing_count(context_user, context_group)
|
||||
return count;
|
||||
end
|
||||
|
||||
module:hook_global('config-reloaded', load_config);
|
||||
|
||||
@@ -142,8 +142,7 @@ const config = {
|
||||
// value that is a mock (/index.js).
|
||||
__filename: true,
|
||||
|
||||
// Provide some empty Node modules (required by olm).
|
||||
crypto: 'empty',
|
||||
// Provide an empty 'fs' module.
|
||||
fs: 'empty'
|
||||
},
|
||||
optimization: {
|
||||
@@ -191,7 +190,7 @@ module.exports = [
|
||||
entry: {
|
||||
'app.bundle': './app.js'
|
||||
},
|
||||
performance: getPerformanceHints(4 * 1024 * 1024)
|
||||
performance: getPerformanceHints(4.5 * 1024 * 1024)
|
||||
}),
|
||||
Object.assign({}, config, {
|
||||
entry: {
|
||||
|
||||
Reference in New Issue
Block a user