Compare commits

..

10 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
fbdd1d2f38 settings: fix loading disableCallIntegration 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
53ebab33b3 android: fix selecting the Bluetooth route
Samsung devices (of course) seem to stick with the earpiece if we first select
Bluetooth but then set speaker to false. Reverse the order to make everyone
happy.

This only applies to the generic and legacy handlers.
2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
a36965e908 android: fix initializing audio device handler modules too early
When ConnectionService is used (the default) we were attaching the handlers too
early, and since attaching them requires that the RNConnectionService module is
loaded, it silently failed. Instead, use the initialize() method, which gets
called after all the Catalyst (aka native) modules have been loaded.
2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
9bf859ee50 android: log a warning if listeners could not be attached 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
24ff6f58a5 android: make code a bit more readable 2019-11-08 12:22:41 +01:00
Saúl Ibarra Corretgé
6338624095 android,hack: send custom UA to fool spartan nginx config on meet.jit.si 2019-11-07 15:28:04 +01:00
Saúl Ibarra Corretgé
9634d58d4e Merge branch 'master' into mobile-19.4 2019-11-07 08:40:06 +01:00
Saúl Ibarra Corretgé
95a6f1355f uri: avoid using String.prototype.normalize
It crashes on Android. Well, on the JSC version React Native uses on Android.

While we could use this fallback only on Android, we have decided to use it
on all mobile platforms for consistency.
2019-11-06 15:37:52 +01:00
Saúl Ibarra Corretgé
fd3fb7ef55 Merge branch 'master' into mobile-19.4 2019-10-31 16:45:37 +01:00
Saúl Ibarra Corretgé
6909c6a0f4 android: fix SDK release script for new dependency syntax
Skip the first character, since it's now like ^123456.0.0
2019-10-21 11:13:13 +02:00
464 changed files with 8758 additions and 15767 deletions

View File

@@ -1,15 +0,0 @@
name: Linter
on: [pull_request]
jobs:
run-linter:
name: Run Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install
- run: npm run lint

6
.gitignore vendored
View File

@@ -61,8 +61,14 @@ buck-out/
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Build artifacts
*.jsbundle

View File

@@ -2,7 +2,7 @@
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, [secure](#security) and scalable video conferences. Jitsi Meet in action can be seen at [here at the session #482 of the VoIP Users Conference](http://youtu.be/7vFUVClsNh0).
The Jitsi Meet client runs in your browser, without installing anything else on your computer. You can try it out at https://meet.jit.si .
The Jitsi Meet client runs in your browser, without installing anything on your computer. You can try it out at https://meet.jit.si .
Jitsi Meet allows very efficient collaboration. Users can stream their desktop or only some windows. It also supports shared document editing with Etherpad.
@@ -27,25 +27,10 @@ You can download Debian/Ubuntu binaries:
You can download source archives (produced by ```make source-package```):
* [source builds](https://download.jitsi.org/jitsi-meet/src/)
### Mobile apps
You can get our mobile versions from here:
* [Android](https://play.google.com/store/apps/details?id=org.jitsi.meet)
[<img src="resources/img/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=org.jitsi.meet)
* [Android (F-Droid)](https://f-droid.org/en/packages/org.jitsi.meet/)
[<img src="resources/img/f-droid-badge.png" height="50">](https://f-droid.org/en/packages/org.jitsi.meet/)
* [iOS](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
[<img src="resources/img/appstore-badge.png" height="50">](https://itunes.apple.com/us/app/jitsi-meet/id1165103905)
You can also sign up for our open beta testing here:
* [Android](https://play.google.com/apps/testing/org.jitsi.meet)
* [iOS](https://testflight.apple.com/join/isy6ja7S)
## Development
For web development see [here](doc/development.md), and for mobile see [here](doc/mobile.md).

View File

@@ -32,23 +32,12 @@ android {
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
// Uncomment the following line for singing a test release build.
//signingConfig signingConfigs.debug
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
@@ -80,9 +69,7 @@ repositories {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-5'
implementation 'androidx.appcompat:appcompat:1.0.2'
if (!rootProject.ext.libreBuild) {
implementation 'com.google.android.gms:play-services-auth:16.0.1'
@@ -96,6 +83,9 @@ dependencies {
}
implementation project(':sdk')
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
}
gradle.projectsEvaluated {

Binary file not shown.

View File

@@ -85,8 +85,4 @@
# ^^^ We added the above when we switched minifyEnabled on.
# Rule to avoid build errors related to SVGs.
-keep public class com.horcrux.svg.** {*;}
# Hermes
-keep class com.facebook.hermes.unicode.** { *; }
-keep public class com.horcrux.svg.** {*;}

View File

@@ -5,6 +5,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme">
<activity

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present 8x8, Inc.
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,7 +91,6 @@ public class MainActivity extends JitsiMeetActivity {
= new JitsiMeetConferenceOptions.Builder()
.setWelcomePageEnabled(true)
.setServerURL(buildURL("https://meet.jit.si"))
.setFeatureFlag("call-integration.enabled", false)
.build();
JitsiMeet.setDefaultConferenceOptions(defaultOptions);

View File

@@ -0,0 +1,36 @@
/*
* Copyright @ 2018-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jitsi.meet;
import android.app.Application;
import com.squareup.leakcanary.LeakCanary;
/**
* Simple {@link Application} for hooking up LeakCanary:
* https://github.com/square/leakcanary
*/
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (!LeakCanary.isInAnalyzerProcess(this)) {
LeakCanary.install(this);
}
}
}

View File

@@ -13,8 +13,8 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'io.fabric.tools:gradle:1.28.1'
classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.27.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files.
@@ -27,6 +27,8 @@ allprojects {
jcenter()
// React Native (JS, Obj-C sources, Android binaries) is installed from npm.
maven { url "$rootDir/../node_modules/react-native/android" }
// Android JSC is installed from npm.
maven { url("$rootDir/../node_modules/jsc-android/dist") }
}
// Make sure we use the react-native version in node_modules and not the one
@@ -163,6 +165,50 @@ ext {
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
}
// If Android SDK is not installed, accept its license so that it
// is automatically downloaded.
afterEvaluate { project ->
// Either the environment variable ANDROID_HOME or the property sdk.dir in
// local.properties identifies where Android SDK is installed.
def androidHome = System.env.ANDROID_HOME
if (!androidHome) {
// ANDROID_HOME is not set. Is sdk.dir set?
def file = file("${project.rootDir}/local.properties")
def props = new Properties()
if (file.canRead()) {
file.withInputStream {
props.load(it)
androidHome = props.'sdk.dir'
}
}
if (!androidHome && (!file.exists() || file.canWrite())) {
// Neither ANDROID_HOME nor sdk.dir is set. Set sdk.dir (because
// environment variables cannot be set).
props.'sdk.dir' = "${project.buildDir}/android-sdk".toString()
file.withOutputStream {
props.store(it, null)
androidHome = props.'sdk.dir'
}
}
}
// If the license is not accepted, accept it so that automatic downloading
// kicks in.
// The license hash can be taken from the accepted licenses, by doing this
// on your local machine the file is
// ${androidHome}/licenses/android-sdk-license
if (androidHome) {
def dir = file("${androidHome}/licenses")
dir.mkdirs()
def file = file("${dir.path}/android-sdk-license")
if (!file.exists()) {
file.withWriter {
def hash = 'd56f5187479451eabf01fb78af6dfcb131a6481e'
it.write(hash, 0, hash.length())
}
}
}
}
// Force the version of the Android build tools we have chosen on all
// subprojects. The forcing was introduced for react-native and the third-party
// modules that we utilize such as react-native-background-timer.

View File

@@ -24,7 +24,7 @@ platform :android do
# Upload built artifact to the Closed Beta track
upload_to_play_store(
track: "beta",
track: "Closed Beta",
json_key: ENV["JITSI_JSON_KEY_FILE"],
skip_upload_metadata: true,
skip_upload_images: true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 950 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -20,5 +20,5 @@
android.useAndroidX=true
android.enableJetifier=true
appVersion=20.2.0
sdkVersion=2.8.0
appVersion=19.4.0
sdkVersion=2.4.0

View File

@@ -9,8 +9,8 @@ THE_MVN_REPO=${MVN_REPO:-${1:-$DEFAULT_MVN_REPO}}
MVN_HTTP=0
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
RN_VERSION=$(jq -r '.version' ${THIS_DIR}/../../node_modules/react-native/package.json)
HERMES_VERSION=$(jq -r '.dependencies."hermes-engine"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -c 2-)
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
JSC_VERSION="r"$(jq -r '.dependencies."jsc-android"' ${THIS_DIR}/../../node_modules/react-native/package.json | cut -d . -f 1 | cut -c 2-)
DO_GIT_TAG=${GIT_TAG:-0}
if [[ $THE_MVN_REPO == http* ]]; then
@@ -38,19 +38,17 @@ if [[ $MVN_HTTP == 1 ]]; then
-DgeneratePom=false \
-DpomFile=react-native-${RN_VERSION}.pom || true
popd
# Push Hermes
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
# Push JSC
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-DrepositoryId=${MVN_REPO_ID} \
-Dfile=hermes-release.aar \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgroupId=com.facebook \
-DartifactId=hermes \
-Dversion=${HERMES_VERSION} \
-DgeneratePom=true || true
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom || true
popd
else
# Push React Native, if necessary
@@ -67,19 +65,17 @@ else
popd
fi
# Push Hermes, if necessary
if [[ ! -d ${MVN_REPO}/com/facebook/hermes/${HERMES_VERSION} ]]; then
echo "Pushing Hermes ${HERMES_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/hermes-engine/android/
# Push JSC, if necessary
if [[ ! -d ${MVN_REPO}/org/webkit/android-jsc/${JSC_VERSION} ]]; then
echo "Pushing JSC ${JSC_VERSION} to the Maven repo"
pushd ${THIS_DIR}/../../node_modules/jsc-android/dist/org/webkit/android-jsc/${JSC_VERSION}
mvn \
deploy:deploy-file \
-Durl=${MVN_REPO} \
-Dfile=hermes-release.aar \
-Dfile=android-jsc-${JSC_VERSION}.aar \
-Dpackaging=aar \
-DgroupId=com.facebook \
-DartifactId=hermes \
-Dversion=${HERMES_VERSION} \
-DgeneratePom=true
-DgeneratePom=false \
-DpomFile=android-jsc-${JSC_VERSION}.pom
popd
fi

View File

@@ -1,5 +1,3 @@
import groovy.json.JsonSlurper
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
@@ -33,26 +31,17 @@ android {
}
}
}
packagingOptions {
pickFirst '**/libc++_shared.so'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.fragment:fragment:1.2.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.fragment:fragment:1.0.0'
//noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
// Hermes JS engine
def hermesPath = "../../node_modules/hermes-engine/android/"
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
implementation 'org.webkit:android-jsc:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
implementation 'com.jakewharton.timber:timber:4.7.1'
@@ -224,14 +213,6 @@ publishing {
dependency.appendNode('artifactId', artifactId)
dependency.appendNode('version', it.moduleVersion)
}
// Add Hermes dependency.
def hermesPkg = new File("$rootDir/../node_modules/hermes-engine/package.json")
def hermesVersion = new JsonSlurper().parseText(hermesPkg.text).version
def hermesDependency = dependencies.appendNode('dependency')
hermesDependency.appendNode('groupId', "com.facebook")
hermesDependency.appendNode('artifactId', "hermes")
hermesDependency.appendNode('version', hermesVersion)
}
}

View File

@@ -16,10 +16,6 @@
package org.jitsi.meet.sdk;
import android.annotation.SuppressLint;
import android.provider.Settings;
import android.text.TextUtils;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@@ -53,16 +49,8 @@ class AmplitudeModule
* @param apiKey The API_KEY of the Amplitude project.
*/
@ReactMethod
@SuppressLint("HardwareIds")
public void init(String instanceName, String apiKey) {
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
// Set the device ID to something consistent.
String android_id
= Settings.Secure.getString(getReactApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
if (!TextUtils.isEmpty(android_id)) {
Amplitude.getInstance(instanceName).setDeviceId(android_id);
}
}
/**

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
import android.telecom.CallAudioState;
import androidx.annotation.RequiresApi;
@@ -39,11 +38,6 @@ class AudioDeviceHandlerConnectionService implements
private final static String TAG = AudioDeviceHandlerConnectionService.class.getSimpleName();
/**
* {@link AudioManager} instance used to interact with the Android audio subsystem.
*/
private AudioManager audioManager;
/**
* Reference to the main {@code AudioModeModule}.
*/
@@ -59,7 +53,7 @@ class AudioDeviceHandlerConnectionService implements
*/
private static int audioDeviceToRouteInt(String audioDevice) {
if (audioDevice == null) {
return CallAudioState.ROUTE_SPEAKER;
return CallAudioState.ROUTE_EARPIECE;
}
switch (audioDevice) {
case AudioModeModule.DEVICE_BLUETOOTH:
@@ -72,7 +66,7 @@ class AudioDeviceHandlerConnectionService implements
return CallAudioState.ROUTE_SPEAKER;
default:
JitsiMeetLogger.e(TAG + " Unsupported device name: " + audioDevice);
return CallAudioState.ROUTE_SPEAKER;
return CallAudioState.ROUTE_EARPIECE;
}
}
@@ -108,8 +102,7 @@ class AudioDeviceHandlerConnectionService implements
*/
private int supportedRouteMask = -1;
public AudioDeviceHandlerConnectionService(AudioManager audioManager) {
this.audioManager = audioManager;
public AudioDeviceHandlerConnectionService() {
}
@Override
@@ -137,11 +130,10 @@ class AudioDeviceHandlerConnectionService implements
}
@Override
public void start(AudioModeModule audioModeModule) {
public void start(Context context, AudioModeModule audioModeModule) {
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule;
RNConnectionService rcs = ReactInstanceManagerHolder.getNativeModule(RNConnectionService.class);
if (rcs != null) {
rcs.setCallAudioStateListener(this);
@@ -168,16 +160,6 @@ class AudioDeviceHandlerConnectionService implements
@Override
public boolean setMode(int mode) {
if (mode != AudioModeModule.DEFAULT) {
// This shouldn't be needed when using ConnectionService, but some devices have been
// observed not doing it.
try {
audioManager.setMicrophoneMute(false);
} catch (Throwable tr) {
JitsiMeetLogger.w(tr, TAG + " Failed to unmute the microphone");
}
}
return true;
}
}

View File

@@ -117,8 +117,7 @@ class AudioDeviceHandlerGeneric implements
}
};
public AudioDeviceHandlerGeneric(AudioManager audioManager) {
this.audioManager = audioManager;
public AudioDeviceHandlerGeneric() {
}
/**
@@ -179,10 +178,11 @@ class AudioDeviceHandlerGeneric implements
}
@Override
public void start(AudioModeModule audioModeModule) {
public void start(Context context, AudioModeModule audioModeModule) {
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule;
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
// Setup runtime device change detection.
audioManager.registerAudioDeviceCallback(audioDeviceCallback, null);

View File

@@ -59,8 +59,7 @@ class AudioDeviceHandlerLegacy implements
*/
private BluetoothHeadsetMonitor bluetoothHeadsetMonitor;
public AudioDeviceHandlerLegacy(AudioManager audioManager) {
this.audioManager = audioManager;
public AudioDeviceHandlerLegacy() {
}
/**
@@ -156,11 +155,11 @@ class AudioDeviceHandlerLegacy implements
}
@Override
public void start(AudioModeModule audioModeModule) {
public void start(Context context, AudioModeModule audioModeModule) {
JitsiMeetLogger.i("Using " + TAG + " as the audio device handler");
module = audioModeModule;
Context context = module.getContext();
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
// Setup runtime device change detection.
//

View File

@@ -17,7 +17,6 @@
package org.jitsi.meet.sdk;
import android.content.Context;
import android.media.AudioManager;
import android.os.Build;
import com.facebook.react.bridge.Arguments;
@@ -86,12 +85,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
return supportsConnectionService && useConnectionService_;
}
/**
* {@link AudioManager} instance used to interact with the Android audio
* subsystem.
*/
private AudioManager audioManager;
private AudioDeviceHandlerInterface audioDeviceHandler;
/**
@@ -143,8 +136,6 @@ class AudioModeModule extends ReactContextBaseJavaModule {
*/
public AudioModeModule(ReactApplicationContext reactContext) {
super(reactContext);
audioManager = (AudioManager)reactContext.getSystemService(Context.AUDIO_SERVICE);
}
/**
@@ -207,12 +198,7 @@ class AudioModeModule extends ReactContextBaseJavaModule {
*/
@Override
public void initialize() {
runInAudioThread(new Runnable() {
@Override
public void run() {
setAudioDeviceHandler();
}
});
setAudioDeviceHandler();
}
private void setAudioDeviceHandler() {
@@ -221,14 +207,14 @@ class AudioModeModule extends ReactContextBaseJavaModule {
}
if (useConnectionService()) {
audioDeviceHandler = new AudioDeviceHandlerConnectionService(audioManager);
audioDeviceHandler = new AudioDeviceHandlerConnectionService();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
audioDeviceHandler = new AudioDeviceHandlerGeneric(audioManager);
audioDeviceHandler = new AudioDeviceHandlerGeneric();
} else {
audioDeviceHandler = new AudioDeviceHandlerLegacy(audioManager);
audioDeviceHandler = new AudioDeviceHandlerLegacy();
}
audioDeviceHandler.start(this);
audioDeviceHandler.start(getReactApplicationContext(), this);
}
/**
@@ -306,14 +292,9 @@ class AudioModeModule extends ReactContextBaseJavaModule {
* @param use Boolean indicator of where it should be used or not.
*/
@ReactMethod
public void setUseConnectionService(final boolean use) {
runInAudioThread(new Runnable() {
@Override
public void run() {
useConnectionService_ = use;
setAudioDeviceHandler();
}
});
public void setUseConnectionService(boolean use) {
useConnectionService_ = use;
setAudioDeviceHandler();
}
/**
@@ -427,24 +408,16 @@ class AudioModeModule extends ReactContextBaseJavaModule {
}
}
/**
* Needed on the legacy handler...
*
* @return Context for the application.
*/
Context getContext() {
return getReactApplicationContext();
}
/**
* Interface for the modules implementing the actual audio device management.
*/
interface AudioDeviceHandlerInterface {
/**
* Start detecting audio device changes.
* @param context Android {@link Context} where detection should take place.
* @param audioModeModule Reference to the main {@link AudioModeModule}.
*/
void start(AudioModeModule audioModeModule);
void start(Context context, AudioModeModule audioModeModule);
/**
* Stop audio device detection.

View File

@@ -123,17 +123,14 @@ public class ConnectionService extends android.telecom.ConnectionService {
* {@link android.telecom.Connection#STATE_ACTIVE}.
*
* @param callUUID the call UUID which identifies the connection.
* @return Whether the connection was set as active or not.
*/
static boolean setConnectionActive(String callUUID) {
static void setConnectionActive(String callUUID) {
ConnectionImpl connection = connections.get(callUUID);
if (connection != null) {
connection.setActive();
return true;
} else {
JitsiMeetLogger.w("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
return false;
JitsiMeetLogger.e("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
}
}

View File

@@ -349,7 +349,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
urlProps.putString("jwt", token);
}
if (userInfo != null) {
if (token == null && userInfo != null) {
props.putBundle("userInfo", userInfo.asBundle());
}

View File

@@ -89,17 +89,9 @@ class RNConnectionService extends ReactContextBaseJavaModule {
ReactApplicationContext ctx = getReactApplicationContext();
Uri address = Uri.fromParts(PhoneAccount.SCHEME_SIP, handle, null);
PhoneAccountHandle accountHandle;
try {
accountHandle
= ConnectionService.registerPhoneAccount(getReactApplicationContext(), address, callUUID);
} catch (Throwable tr) {
JitsiMeetLogger.e(tr, TAG + " error in startCall");
promise.reject(tr);
return;
}
PhoneAccountHandle accountHandle
= ConnectionService.registerPhoneAccount(
getReactApplicationContext(), address, callUUID);
Bundle extras = new Bundle();
extras.putParcelable(
@@ -113,23 +105,15 @@ class RNConnectionService extends ReactContextBaseJavaModule {
ConnectionService.registerStartCallPromise(callUUID, promise);
TelecomManager tm = null;
try {
tm = (TelecomManager) ctx.getSystemService(Context.TELECOM_SERVICE);
TelecomManager tm
= (TelecomManager) ctx.getSystemService(
Context.TELECOM_SERVICE);
tm.placeCall(address, extras);
} catch (Throwable tr) {
JitsiMeetLogger.e(tr, TAG + " error in startCall");
if (tm != null) {
try {
tm.unregisterPhoneAccount(accountHandle);
} catch (Throwable tr1) {
// UnsupportedOperationException: System does not support feature android.software.connectionservice
// was observed here. Ignore.
}
}
} catch (Exception e) {
ConnectionService.unregisterStartCallPromise(callUUID);
promise.reject(tr);
promise.reject(e);
}
}
@@ -167,11 +151,8 @@ class RNConnectionService extends ReactContextBaseJavaModule {
@ReactMethod
public void reportConnectedOutgoingCall(String callUUID, Promise promise) {
JitsiMeetLogger.d(TAG + " reportConnectedOutgoingCall " + callUUID);
if (ConnectionService.setConnectionActive(callUUID)) {
promise.resolve(null);
} else {
promise.reject("CONNECTION_NOT_FOUND_ERROR", "Connection wasn't found.");
}
ConnectionService.setConnectionActive(callUUID);
promise.resolve(null);
}
@Override

View File

@@ -1,5 +1,6 @@
/*
* Copyright @ 2017-present 8x8, Inc.
* Copyright @ 2019-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +18,8 @@
package org.jitsi.meet.sdk;
import android.app.Activity;
import androidx.annotation.Nullable;
import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
@@ -28,15 +27,18 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import com.oney.WebRTCModule.RTCVideoViewManager;
import com.oney.WebRTCModule.WebRTCModule;
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
import org.webrtc.SoftwareVideoDecoderFactory;
import org.webrtc.SoftwareVideoEncoderFactory;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
@@ -83,10 +85,12 @@ class ReactInstanceManagerHolder {
AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext)
.createAudioDeviceModule();
options.setAudioDeviceModule(adm);
VideoDecoderFactory videoDecoderFactory = new SoftwareVideoDecoderFactory();
VideoEncoderFactory videoEncoderFactory = new SoftwareVideoEncoderFactory();
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
options.setAudioDeviceModule(adm);
options.setVideoDecoderFactory(videoDecoderFactory);
options.setVideoEncoderFactory(videoEncoderFactory);
nativeModules.add(new WebRTCModule(reactContext, options));
@@ -215,8 +219,9 @@ class ReactInstanceManagerHolder {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
// Use the Hermes JavaScript engine.
HermesExecutorFactory jsFactory = new HermesExecutorFactory();
// Keep on using JSC, the jury is out on Hermes.
JSCExecutorFactory jsFactory
= new JSCExecutorFactory("", "");
reactInstanceManager
= ReactInstanceManager.builder()

View File

@@ -24,24 +24,11 @@ public class JitsiMeetLogger {
}
public static void addHandler(JitsiMeetBaseLogHandler handler) {
if (!Timber.forest().contains(handler)) {
try {
Timber.plant(handler);
} catch (Throwable t) {
Timber.w(t, "Couldn't add log handler");
}
}
Timber.plant(handler);
}
public static void removeHandler(JitsiMeetBaseLogHandler handler) {
if (Timber.forest().contains(handler)) {
try {
Timber.uproot(handler);
} catch (Throwable t) {
Timber.w(t, "Couldn't remove log handler");
}
}
Timber.uproot(handler);
}
public static void v(String message, Object... args) {

View File

@@ -1 +0,0 @@
<base href="/" />

View File

View File

@@ -2,7 +2,6 @@
import { openConnection } from './connection';
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
import AuthHandler from './modules/UI/authentication/AuthHandler';
import Recorder from './modules/recorder/Recorder';
@@ -42,7 +41,6 @@ import {
conferenceJoined,
conferenceLeft,
conferenceSubjectChanged,
conferenceTimestampChanged,
conferenceWillJoin,
conferenceWillLeave,
dataChannelOpened,
@@ -95,15 +93,10 @@ import {
participantRoleChanged,
participantUpdated
} from './react/features/base/participants';
import { updateSettings } from './react/features/base/settings';
import {
getUserSelectedCameraDeviceId,
updateSettings
} from './react/features/base/settings';
import {
createLocalPresenterTrack,
createLocalTracksF,
destroyLocalTracks,
isLocalVideoTrackMuted,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
@@ -120,11 +113,7 @@ import {
import { mediaPermissionPromptVisibilityChanged } from './react/features/overlay';
import { suspendDetected } from './react/features/power-monitor';
import { setSharedVideoStatus } from './react/features/shared-video';
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
import { createPresenterEffect } from './react/features/stream-effects/presenter';
import { endpointMessageReceived } from './react/features/subtitles';
import { createRnnoiseProcessorPromise } from './react/features/rnnoise';
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
const logger = require('jitsi-meet-logger').getLogger(__filename);
@@ -133,15 +122,6 @@ const eventEmitter = new EventEmitter();
let room;
let connection;
/**
* This promise is used for chaining mutePresenterVideo calls in order to avoid calling GUM multiple times if it takes
* a while to finish.
*
* @type {Promise<void>}
* @private
*/
let _prevMutePresenterVideo = Promise.resolve();
/*
* Logic to open a desktop picker put on the window global for
* lib-jitsi-meet to detect and invoke
@@ -437,6 +417,7 @@ export default {
* the tracks won't exist).
*/
_localTracksInitialized: false,
isModerator: false,
isSharingScreen: false,
/**
@@ -456,12 +437,6 @@ export default {
*/
localAudio: null,
/**
* The local presenter video track (if any).
* @type {JitsiLocalTrack|null}
*/
localPresenterVideo: null,
/**
* The local video track (if any).
* FIXME tracks from redux store should be the single source of truth, but
@@ -493,18 +468,14 @@ export default {
audioOnlyError,
screenSharingError,
videoOnlyError;
const initialDevices = [ 'audio' ];
const requestedAudio = true;
const initialDevices = [];
let requestedAudio = false;
let requestedVideo = false;
// Always get a handle on the audio input device so that we have statistics even if the user joins the
// conference muted. Previous implementation would only acquire the handle when the user first unmuted,
// which would results in statistics ( such as "No audio input" or "Are you trying to speak?") being available
// only after that point.
if (options.startWithAudioMuted) {
this.muteAudio(true, true);
if (!options.startWithAudioMuted) {
initialDevices.push('audio');
requestedAudio = true;
}
if (!options.startWithVideoMuted
&& !options.startAudioOnly
&& !options.startScreenSharing) {
@@ -660,10 +631,10 @@ export default {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: config.startWithAudioMuted
|| config.startSilent
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
|| config.startSilent
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
startWithVideoMuted: config.startWithVideoMuted
|| isUserInteractionRequiredForUnmute(APP.store.getState())
|| isUserInteractionRequiredForUnmute(APP.store.getState())
}))
.then(([ tracks, con ]) => {
tracks.forEach(track => {
@@ -751,8 +722,9 @@ export default {
isLocalVideoMuted() {
// If the tracks are not ready, read from base/media state
return this._localTracksInitialized
? isLocalVideoTrackMuted(
APP.store.getState()['features/base/tracks'])
? isLocalTrackMuted(
APP.store.getState()['features/base/tracks'],
MEDIA_TYPE.VIDEO)
: isVideoMutedByUser(APP.store);
},
@@ -826,35 +798,6 @@ export default {
this.muteAudio(!this.isLocalAudioMuted(), showUI);
},
/**
* Simulates toolbar button click for presenter video mute. Used by
* shortcuts and API.
* @param mute true for mute and false for unmute.
* @param {boolean} [showUI] when set to false will not display any error
* dialogs in case of media permissions error.
*/
async mutePresenter(mute, showUI = true) {
const maybeShowErrorDialog = error => {
showUI && APP.store.dispatch(notifyCameraError(error));
};
if (mute) {
try {
await this.localVideo.setEffect(undefined);
} catch (err) {
logger.error('Failed to remove the presenter effect', err);
maybeShowErrorDialog(err);
}
} else {
try {
await this.localVideo.setEffect(await this._createPresenterStreamEffect());
} catch (err) {
logger.error('Failed to apply the presenter effect', err);
maybeShowErrorDialog(err);
}
}
},
/**
* Simulates toolbar button click for video mute. Used by shortcuts and API.
* @param mute true for mute and false for unmute.
@@ -869,13 +812,6 @@ export default {
return;
}
if (this.isSharingScreen) {
// Chain _mutePresenterVideo calls
_prevMutePresenterVideo = _prevMutePresenterVideo.then(() => this._mutePresenterVideo(mute));
return;
}
// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
@@ -927,6 +863,14 @@ export default {
this.muteVideo(!this.isLocalVideoMuted(), showUI);
},
/**
* Retrieve list of conference participants (without local user).
* @returns {JitsiParticipant[]}
*/
listMembers() {
return room.getParticipants();
},
/**
* Retrieve list of ids of conference participants (without local user).
* @returns {string[]}
@@ -946,16 +890,6 @@ export default {
return user && user.isModerator();
},
/**
* Retrieve list of conference participants (without local user).
* @returns {JitsiParticipant[]}
*
* NOTE: Used by jitsi-meet-torture!
*/
listMembers() {
return room.getParticipants();
},
get membersCount() {
return room.getParticipants().length + 1;
},
@@ -1271,29 +1205,17 @@ export default {
_getConferenceOptions() {
const options = config;
const { email, name: nick } = getLocalParticipant(APP.store.getState());
const nick = APP.store.getState()['features/base/settings'].displayName;
const { locationURL } = APP.store.getState()['features/base/connection'];
if (options.enableDisplayNameInStats && nick) {
options.statisticsDisplayName = nick;
}
if (options.enableEmailInStats && email) {
options.statisticsId = email;
if (nick) {
options.displayName = nick;
}
options.applicationName = interfaceConfig.APP_NAME;
options.getWiFiStatsMethod = this._getWiFiStatsMethod;
options.confID = `${locationURL.host}${locationURL.pathname}`;
options.createVADProcessor = createRnnoiseProcessorPromise;
// Disable CallStats, if requessted.
if (options.disableThirdPartyRequests) {
delete options.callStatsID;
delete options.callStatsSecret;
delete options.getWiFiStatsMethod;
}
return options;
},
@@ -1425,7 +1347,7 @@ export default {
* in case it fails.
* @private
*/
async _turnScreenSharingOff(didHaveVideo) {
_turnScreenSharingOff(didHaveVideo, wasVideoMuted) {
this._untoggleScreenSharing = null;
this.videoSwitchInProgress = true;
const { receiver } = APP.remoteControl;
@@ -1435,45 +1357,21 @@ export default {
}
this._stopProxyConnection();
if (config.enableScreenshotCapture) {
APP.store.dispatch(toggleScreenshotCaptureEffect(false));
}
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
// wait for that GUM to be resolved in order to prevent leaking the presenter track(this.localPresenterVideo
// will be null when SS is being turned off, but it will initialize once GUM resolves).
let promise = _prevMutePresenterVideo = _prevMutePresenterVideo.then(() => {
// mute the presenter track if it exists.
if (this.localPresenterVideo) {
APP.store.dispatch(setVideoMuted(true, MEDIA_TYPE.PRESENTER));
return this.localPresenterVideo.dispose().then(() => {
APP.store.dispatch(trackRemoved(this.localPresenterVideo));
this.localPresenterVideo = null;
});
}
});
// If system audio was also shared stop the AudioMixerEffect and dispose of the desktop audio track.
if (this._mixerEffect) {
await this.localAudio.setEffect(undefined);
await this._desktopAudioStream.dispose();
this._mixerEffect = undefined;
this._desktopAudioStream = undefined;
// In case there was no local audio when screen sharing was started the fact that we set the audio stream to
// null will take care of the desktop audio stream cleanup.
} else if (this._desktopAudioStream) {
await this.useAudioStream(null);
this._desktopAudioStream = undefined;
}
let promise = null;
if (didHaveVideo) {
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
promise = createLocalTracksF({ devices: [ 'video' ] })
.then(([ stream ]) => this.useVideoStream(stream))
.then(() => {
sendAnalytics(createScreenSharingEvent('stopped'));
logger.log('Screen sharing stopped.');
logger.log('Screen sharing stopped, switching to video.');
if (!this.localVideo && wasVideoMuted) {
return Promise.reject('No local video to be muted!');
} else if (wasVideoMuted && this.localVideo) {
return this.localVideo.mute();
}
})
.catch(error => {
logger.error('failed to switch back to local video', error);
@@ -1485,7 +1383,7 @@ export default {
);
});
} else {
promise = promise.then(() => this.useVideoStream(null));
promise = this.useVideoStream(null);
}
return promise.then(
@@ -1513,7 +1411,7 @@ export default {
* 'window', etc.).
* @return {Promise.<T>}
*/
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}) {
if (this.videoSwitchInProgress) {
return Promise.reject('Switch in progress.');
}
@@ -1527,15 +1425,7 @@ export default {
}
if (toggle) {
try {
await this._switchToScreenSharing(options);
return;
} catch (err) {
logger.error('Failed to switch to screensharing', err);
return;
}
return this._switchToScreenSharing(options);
}
return this._untoggleScreenSharing
@@ -1560,7 +1450,8 @@ export default {
_createDesktopTrack(options = {}) {
let externalInstallation = false;
let DSExternalInstallationInProgress = false;
const didHaveVideo = !this.isLocalVideoMuted();
const didHaveVideo = Boolean(this.localVideo);
const wasVideoMuted = this.isLocalVideoMuted();
const getDesktopStreamPromise = options.desktopStream
? Promise.resolve([ options.desktopStream ])
@@ -1607,31 +1498,27 @@ export default {
}
});
return getDesktopStreamPromise.then(desktopStreams => {
return getDesktopStreamPromise.then(([ desktopStream ]) => {
// Stores the "untoggle" handler which remembers whether was
// there any video before and whether was it muted.
this._untoggleScreenSharing
= this._turnScreenSharingOff.bind(this, didHaveVideo);
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
if (desktopVideoStream) {
desktopVideoStream.on(
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
() => {
// If the stream was stopped during screen sharing
// session then we should switch back to video.
this.isSharingScreen
&& this._untoggleScreenSharing
&& this._untoggleScreenSharing();
}
);
}
= this._turnScreenSharingOff
.bind(this, didHaveVideo, wasVideoMuted);
desktopStream.on(
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
() => {
// If the stream was stopped during screen sharing
// session then we should switch back to video.
this.isSharingScreen
&& this._untoggleScreenSharing
&& this._untoggleScreenSharing();
}
);
// close external installation dialog on success.
externalInstallation && $.prompt.close();
return desktopStreams;
return desktopStream;
}, error => {
DSExternalInstallationInProgress = false;
@@ -1641,126 +1528,6 @@ export default {
});
},
/**
* Creates a new instance of presenter effect. A new video track is created
* using the new set of constraints that are calculated based on
* the height of the desktop that is being currently shared.
*
* @param {number} height - The height of the desktop stream that is being
* currently shared.
* @param {string} cameraDeviceId - The device id of the camera to be used.
* @return {Promise<JitsiStreamPresenterEffect>} - A promise resolved with
* {@link JitsiStreamPresenterEffect} if it succeeds.
*/
async _createPresenterStreamEffect(height = null, cameraDeviceId = null) {
if (!this.localPresenterVideo) {
try {
this.localPresenterVideo = await createLocalPresenterTrack({ cameraDeviceId }, height);
} catch (err) {
logger.error('Failed to create a camera track for presenter', err);
return;
}
APP.store.dispatch(trackAdded(this.localPresenterVideo));
}
try {
const effect = await createPresenterEffect(this.localPresenterVideo.stream);
return effect;
} catch (err) {
logger.error('Failed to create the presenter effect', err);
}
},
/**
* Tries to turn the presenter video track on or off. If a presenter track
* doesn't exist, a new video track is created.
*
* @param mute - true for mute and false for unmute.
*
* @private
*/
async _mutePresenterVideo(mute) {
const maybeShowErrorDialog = error => {
APP.store.dispatch(notifyCameraError(error));
};
// Check for NO-OP
if (mute && (!this.localPresenterVideo || this.localPresenterVideo.isMuted())) {
return;
} else if (!mute && this.localPresenterVideo && !this.localPresenterVideo.isMuted()) {
return;
}
// Create a new presenter track and apply the presenter effect.
if (!this.localPresenterVideo && !mute) {
let { aspectRatio, height } = this.localVideo.track.getSettings();
const { width } = this.localVideo.track.getSettings();
let desktopResizeConstraints = {};
let resizeDesktopStream = false;
const DESKTOP_STREAM_CAP = 720;
// Determine the constraints if the desktop track needs to be resized.
// Resizing is needed when the resolution cannot be determined or when
// the window is bigger than 720p.
if (height && width) {
aspectRatio = aspectRatio ?? (width / height).toPrecision(4);
const advancedConstraints = [ { aspectRatio } ];
const isPortrait = height >= width;
// Determine which dimension needs resizing and resize only that side
// keeping the aspect ratio same as before.
if (isPortrait && width > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ width: DESKTOP_STREAM_CAP });
} else if (!isPortrait && height > DESKTOP_STREAM_CAP) {
resizeDesktopStream = true;
advancedConstraints.push({ height: DESKTOP_STREAM_CAP });
}
desktopResizeConstraints.advanced = advancedConstraints;
} else {
resizeDesktopStream = true;
desktopResizeConstraints = {
width: 1280,
height: 720
};
}
if (resizeDesktopStream) {
try {
await this.localVideo.track.applyConstraints(desktopResizeConstraints);
} catch (err) {
logger.error('Failed to apply constraints on the desktop stream for presenter mode', err);
return;
}
height = this.localVideo.track.getSettings().height ?? DESKTOP_STREAM_CAP;
}
const defaultCamera = getUserSelectedCameraDeviceId(APP.store.getState());
let effect;
try {
effect = await this._createPresenterStreamEffect(height,
defaultCamera);
} catch (err) {
logger.error('Failed to unmute Presenter Video');
maybeShowErrorDialog(err);
return;
}
try {
await this.localVideo.setEffect(effect);
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
this.setVideoMuteStatus(mute);
} catch (err) {
logger.error('Failed to apply the Presenter effect', err);
}
} else {
APP.store.dispatch(setVideoMuted(mute, MEDIA_TYPE.PRESENTER));
}
},
/**
* Tries to switch to the screensharing mode by disposing camera stream and
* replacing it with a desktop one.
@@ -1782,34 +1549,9 @@ export default {
this.videoSwitchInProgress = true;
return this._createDesktopTrack(options)
.then(async streams => {
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
if (desktopVideoStream) {
this.useVideoStream(desktopVideoStream);
}
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
if (this._desktopAudioStream) {
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
// api.
if (this.localAudio) {
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
await this.localAudio.setEffect(this._mixerEffect);
} else {
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
// stream as we would use a regular stream.
await this.useAudioStream(this._desktopAudioStream);
}
}
})
.then(stream => this.useVideoStream(stream))
.then(() => {
this.videoSwitchInProgress = false;
if (config.enableScreenshotCapture) {
APP.store.dispatch(toggleScreenshotCaptureEffect(true));
}
sendAnalytics(createScreenSharingEvent('started'));
logger.log('Screen sharing started');
})
@@ -1923,10 +1665,7 @@ export default {
room.on(
JitsiConferenceEvents.CONFERENCE_LEFT,
(...args) => {
APP.store.dispatch(conferenceTimestampChanged(0));
APP.store.dispatch(conferenceLeft(room, ...args));
});
(...args) => APP.store.dispatch(conferenceLeft(room, ...args)));
room.on(
JitsiConferenceEvents.AUTH_STATUS_CHANGED,
@@ -1945,6 +1684,9 @@ export default {
logger.log(`USER ${id} connnected:`, user);
APP.UI.addUser(user);
// check the roles for the new user and reflect them
APP.UI.updateUserRole(user);
});
room.on(JitsiConferenceEvents.USER_LEFT, (id, user) => {
@@ -1975,8 +1717,19 @@ export default {
logger.info(`My role changed, new role: ${role}`);
APP.store.dispatch(localParticipantRoleChanged(role));
if (this.isModerator !== room.isModerator()) {
this.isModerator = room.isModerator();
APP.UI.updateLocalRole(room.isModerator());
}
} else {
APP.store.dispatch(participantRoleChanged(id, role));
const user = room.getParticipantById(id);
if (user) {
APP.UI.updateUserRole(user);
}
}
});
@@ -2042,10 +1795,6 @@ export default {
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
room.on(
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
conferenceTimestamp => APP.store.dispatch(conferenceTimestampChanged(conferenceTimestamp)));
room.on(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => {
APP.store.dispatch(localParticipantConnectionStatusChanged(
JitsiParticipantConnectionStatus.INTERRUPTED));
@@ -2091,22 +1840,7 @@ export default {
room.on(
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(...args) => {
APP.store.dispatch(endpointMessageReceived(...args));
if (args && args.length >= 2) {
const [ sender, eventData ] = args;
if (eventData.name === ENDPOINT_TEXT_MESSAGE_NAME) {
APP.API.notifyEndpointTextMessageReceived({
senderInfo: {
jid: sender._jid,
id: sender._id
},
eventData
});
}
}
});
(...args) => APP.store.dispatch(endpointMessageReceived(...args)));
room.on(
JitsiConferenceEvents.LOCK_STATE_CHANGED,
@@ -2254,65 +1988,36 @@ export default {
const videoWasMuted = this.isLocalVideoMuted();
sendAnalytics(createDeviceChangedEvent('video', 'input'));
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId,
micDeviceId: null
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
// If both screenshare and video are in progress, restart the
// presenter mode with the new camera device.
if (this.isSharingScreen && !videoWasMuted) {
const { height } = this.localVideo.track.getSettings();
return stream;
})
.then(stream => {
// if we are screen sharing we do not want to stop it
if (this.isSharingScreen) {
return Promise.resolve();
}
// dispose the existing presenter track and create a new
// camera track.
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
return this._createPresenterStreamEffect(height, cameraDeviceId)
.then(effect => this.localVideo.setEffect(effect))
.then(() => {
this.setVideoMuteStatus(false);
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
// If screenshare is in progress but video is muted, update the default device
// id for video, dispose the existing presenter track and create a new effect
// that can be applied on un-mute.
} else if (this.isSharingScreen && videoWasMuted) {
return this.useVideoStream(stream);
})
.then(() => {
logger.log('switched local video device');
const { height } = this.localVideo.track.getSettings();
this._updateVideoDeviceId();
// FIXME JitsiLocalTrack.dispose is async and should be waited for
this.localPresenterVideo && this.localPresenterVideo.dispose();
this.localPresenterVideo = null;
this._createPresenterStreamEffect(height, cameraDeviceId);
// if there is only video, switch to the new camera stream.
} else {
createLocalTracksF({
devices: [ 'video' ],
cameraDeviceId,
micDeviceId: null
})
.then(([ stream ]) => {
// if we are in audio only mode or video was muted before
// changing device, then mute
if (this.isAudioOnly() || videoWasMuted) {
return stream.mute()
.then(() => stream);
}
return stream;
})
.then(stream => this.useVideoStream(stream))
.then(() => {
logger.log('switched local video device');
this._updateVideoDeviceId();
})
.catch(err => APP.store.dispatch(notifyCameraError(err)));
}
})
.catch(err => {
APP.store.dispatch(notifyCameraError(err));
});
}
);
@@ -2337,19 +2042,9 @@ export default {
return stream;
})
.then(async stream => {
// In case screen sharing audio is also shared we mix it with new input stream. The old _mixerEffect
// will be cleaned up when the existing track is replaced.
if (this._mixerEffect) {
this._mixerEffect = new AudioMixerEffect(this._desktopAudioStream);
await stream.setEffect(this._mixerEffect);
}
return this.useAudioStream(stream);
})
.then(stream => this.useAudioStream(stream))
.then(() => {
logger.log(`switched local audio device: ${this.localAudio?.getDeviceId()}`);
logger.log('switched local audio device');
this._updateAudioDeviceId();
})
@@ -2552,13 +2247,6 @@ export default {
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
// If screenshare is in progress, get the device id from the presenter track.
if (this.localPresenterVideo) {
APP.store.dispatch(updateSettings({
cameraDeviceId: this.localPresenterVideo.getDeviceId()
}));
}
},
/**
@@ -2726,6 +2414,12 @@ export default {
// audio devices detected or if the local audio stream already exists.
const available = audioDeviceCount > 0 || Boolean(this.localAudio);
logger.debug(
`Microphone button enabled: ${available}`,
`local audio: ${this.localAudio}`,
`audio devices: ${audioMediaDevices}`,
`device count: ${audioDeviceCount}`);
APP.store.dispatch(setAudioAvailable(available));
APP.API.notifyAudioAvailabilityChanged(available);
},
@@ -2746,6 +2440,12 @@ export default {
// config).
const available = videoDeviceCount > 0 || Boolean(this.localVideo);
logger.debug(
`Camera button enabled: ${available}`,
`local video: ${this.localVideo}`,
`video devices: ${videoMediaDevices}`,
`device count: ${videoDeviceCount}`);
APP.store.dispatch(setVideoAvailable(available));
APP.API.notifyVideoAvailabilityChanged(available);
},

View File

@@ -30,9 +30,6 @@ var config = {
// BOSH URL. FIXME: use XEP-0156 to discover it.
bosh: '//jitsi-meet.example.com/http-bind',
// Websocket URL
// websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
// The name of client node advertised in XEP-0115 'c' stanza
clientNode: 'http://jitsi.org/jitsimeet',
@@ -75,18 +72,6 @@ var config = {
// Disable measuring of audio levels.
// disableAudioLevels: false,
// audioLevelsInterval: 200,
// Enabling this will run the lib-jitsi-meet no audio detection module which
// will notify the user if the current selected microphone has no audio
// input and will suggest another valid device if one is present.
enableNoAudioDetection: true,
// Enabling this will run the lib-jitsi-meet noise detection module which will
// notify the user if there is noise, other than voice, coming from the current
// selected microphone. The purpose it to let the user know that the input could
// be potentially unpleasant for other meeting participants.
enableNoisyMicDetection: true,
// Start the conference in audio only mode (no video is being received nor
// sent).
@@ -110,7 +95,7 @@ var config = {
// w3c spec-compliant video constraints to use for video capture. Currently
// used by browsers that return true from lib-jitsi-meet's
// util#browser#usesNewGumFlow. The constraints are independent from
// util#browser#usesNewGumFlow. The constraints are independency from
// this config's resolution value. Defaults to requesting an ideal aspect
// ratio of 16:9 with an ideal resolution of 720.
// constraints: {
@@ -302,19 +287,18 @@ var config = {
// estimation tests.
// gatherStats: false,
// The interval at which PeerConnection.getStats() is called. Defaults to 10000
// pcStatsInterval: 10000,
// To enable sending statistics to callstats.io you must provide the
// Application ID and Secret.
// callStatsID: '',
// callStatsSecret: '',
// enables sending participants display name to callstats
// enableDisplayNameInStats: false,
// enables callstatsUsername to be reported as statsId and used
// by callstats as repoted remote id
// enableStatsID: false
// enables sending participants display name to callstats
// enableDisplayNameInStats: false
// enables sending participants email if available to callstats and other analytics
// enableEmailInStats: false,
// Privacy
//
@@ -342,9 +326,9 @@ var config = {
// The STUN servers that will be used in the peer to peer connections
stunServers: [
// { urls: 'stun:jitsi-meet.example.com:443' },
{ urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' }
],
// Sets the ICE transport policy for the p2p connection. At the time
@@ -388,21 +372,7 @@ var config = {
// shard: "shard1",
// region: "europe",
// userRegion: "asia"
},
// Information for the chrome extension banner
// chromeExtensionBanner: {
// // The chrome extension to be installed address
// url: 'https://chrome.google.com/webstore/detail/jitsi-meetings/kglhbbefdnlheedjiejgomgmfplipfeb',
// // Extensions info which allows checking if they are installed or not
// chromeExtensionsInfo: [
// {
// id: 'kglhbbefdnlheedjiejgomgmfplipfeb',
// path: 'jitsi-logo-48x48.png'
// }
// ]
// },
}
// Local Recording
//
@@ -420,7 +390,7 @@ var config = {
// format: 'flac'
//
// },
// }
// Options related to end-to-end (participant to participant) ping.
// e2eping: {
@@ -432,22 +402,22 @@ var config = {
// // with the measured RTT will be sent. Defaults to 60000, set
// // to <= 0 to disable.
// analyticsInterval: 60000,
// },
// }
// If set, will attempt to use the provided video input device label when
// triggering a screenshare, instead of proceeding through the normal flow
// for obtaining a desktop stream.
// NOTE: This option is experimental and is currently intended for internal
// use only.
// _desktopSharingSourceDevice: 'sample-id-or-label',
// _desktopSharingSourceDevice: 'sample-id-or-label'
// If true, any checks to handoff to another application will be prevented
// and instead the app will continue to display in the current browser.
// disableDeepLinking: false,
// disableDeepLinking: false
// A property to disable the right click context menu for localVideo
// the menu has option to flip the locally seen video for local presentations
// disableLocalVideoFlip: false,
// disableLocalVideoFlip: false
// Deployment specific URLs.
// deploymentUrls: {
@@ -457,7 +427,7 @@ var config = {
// // If specified a 'Download our apps' button will be displayed in the overflow menu with a link
// // to the specified URL for an app download page.
// downloadAppsUrl: 'https://docs.example.com/our-apps.html'
// },
// }
// List of undocumented settings used in jitsi-meet
/**
@@ -509,12 +479,6 @@ var config = {
startBitrate
*/
// Allow all above example options to include a trailing comma and
// prevent fear when commenting out the last value.
makeJsonParserHappy: 'even if last key had a trailing comma'
// no configuration value should follow this line.
};
/* eslint-enable no-unused-vars, no-var */

View File

@@ -15,13 +15,6 @@ import {
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* The feature announced so we can distinguish jibri participants.
*
* @type {string}
*/
export const DISCO_JIBRI_FEATURE = 'http://jitsi.org/protocol/jibri';
/**
* Checks if we have data to use attach instead of connect. If we have the data
* executes attach otherwise check if we have to wait for the data. If we have
@@ -82,15 +75,7 @@ function connect(id, password, roomName) {
const connectionConfig = Object.assign({}, config);
const { issuer, jwt } = APP.store.getState()['features/base/jwt'];
// Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
// that this code executes only on web browsers/electron. This needs to be changed when mobile and web are unified.
let serviceUrl = connectionConfig.websocket || connectionConfig.bosh;
serviceUrl += `?room=${roomName}`;
// FIXME Remove deprecated 'bosh' option assignment at some point(LJM will be accepting only 'serviceUrl' option
// in future). It's included for the time being for Jitsi Meet and lib-jitsi-meet versions interoperability.
connectionConfig.serviceUrl = connectionConfig.bosh = serviceUrl;
connectionConfig.bosh += `?room=${roomName}`;
const connection
= new JitsiMeetJS.JitsiConnection(
@@ -98,10 +83,6 @@ function connect(id, password, roomName) {
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
connectionConfig);
if (config.iAmRecorder) {
connection.addFeature(DISCO_JIBRI_FEATURE);
}
return new Promise((resolve, reject) => {
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_ESTABLISHED,

View File

@@ -17,11 +17,10 @@ import parseURLParams from '../react/features/base/config/parseURLParams';
if (typeof createConnectionExternally === 'function') {
// URL params have higher priority than config params.
// Do not use external connect if websocket is enabled.
let url
= parseURLParams(window.location, true, 'hash')[
'config.externalConnectUrl']
|| config.websocket ? undefined : config.externalConnectUrl;
|| config.externalConnectUrl;
const isRecorder
= parseURLParams(window.location, true, 'hash')['config.iAmRecorder'];

View File

@@ -1,23 +1,12 @@
.avatar {
align-items: center;
background-color: #AAA;
display: flex;
border-radius: 50%;
color: rgba(255, 255, 255, 0.6);
font-weight: 100;
justify-content: center;
object-fit: cover;
&.avatar-small {
height: 28px !important;
width: 28px !important;
}
&.avatar-xsmall {
height: 16px !important;
width: 16px !important;
}
.jitsi-icon {
transform: translateY(50%);
}
}
.avatar-foreign {
@@ -39,28 +28,4 @@
.defaultAvatar {
opacity: 0.6
}
.avatar-badge {
position: relative;
&-available::after {
@include avatarBadge;
background-color: $presence-available;
}
&-away::after {
@include avatarBadge;
background-color: $presence-away;
}
&-busy::after {
@include avatarBadge;
background-color: $presence-busy;
}
&-idle::after {
@include avatarBadge;
background-color: $presence-idle;
}
}

View File

@@ -37,11 +37,6 @@ body {
fill: white;
}
.jitsi-icon.gray svg {
fill: #5E6D7A;
cursor: pointer;
}
/**
* AtlasKitThemeProvider sets a background color on an app-wrapping div, thereby
* preventing transparency in filmstrip-only mode. The selector chosen to
@@ -166,7 +161,6 @@ form {
::-webkit-scrollbar {
background: transparent;
width: 7px;
height: $scrollHeight;
}
::-webkit-scrollbar-button {

View File

@@ -205,10 +205,6 @@
border-radius: 6px 0px 6px 6px;
}
.usermessage {
white-space: pre-wrap;
}
&.error {
border-radius: 0px;
@@ -230,8 +226,6 @@
.messagecontent {
margin: 5px 10px;
max-width: 100%;
overflow: hidden;
}
}

View File

@@ -1,93 +0,0 @@
.chrome-extension-banner {
position: fixed;
width: 406px;
height: $chromeExtensionBannerHeight;
background: #FFF;
box-shadow: 0px 2px 48px rgba(0, 0, 0, 0.25);
border-radius: 4px;
z-index: 1000;
float: right;
display: flex;
flex-direction: column;
padding: 20px 20px;
top: $chromeExtensionBannerTop;
right: $chromeExtensionBannerRight;
&__pos_in_meeting {
top: $chromeExtensionBannerTopInMeeting;
right: $chromeExtensionBannerRightInMeeeting;
}
&__container {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
&__button-container {
display: flex;
}
&__checkbox-container {
display: $chromeExtensionBannerDontShowAgainDisplay;
margin-left: 45px;
margin-top: 16px;
}
&__checkbox-label {
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
color: #1C2025;
}
&__icon-container {
display: flex;
background: url('../images/chromeLogo.svg');
background-repeat: no-repeat;
width: 27px;
height: 27px;
}
&__text-container {
font-size: 14px;
line-height: 18px;
display: flex;
align-items: center;
letter-spacing: -0.006em;
color: #151531;
width: 329px;
}
&__close-container {
display: flex;
width: 12px;
height: 12px;
}
&__gray-close-icon {
fill: #5E6D7A;
width: 12px;
height: 12px;
cursor: pointer;
}
&__button-open-url {
background: #0A57EB;
border-radius: 24px;
margin-left: 45px;
width: 236px;
height: 40px;
cursor: pointer;
}
&__button-text {
font-weight: 600;
font-size: 14px;
line-height: 40px;
text-align: center;
letter-spacing: -0.006em;
color: #FFFFFF;
}
}

View File

@@ -192,17 +192,4 @@
*/
@mixin transparentBg($color, $alpha) {
background-color: rgba(red($color), green($color), blue($color), $alpha);
}
/**
* Avatar status badge mixin
*/
@mixin avatarBadge {
border-radius: 50%;
content: '';
display: block;
height: 35%;
position: absolute;
bottom: 0;
width: 35%;
}
}

View File

@@ -23,10 +23,4 @@
&-text {
vertical-align: middle;
}
&-conference-timer {
display: block;
font-size: 15px;
opacity: 0.6;
}
}

View File

@@ -31,6 +31,13 @@
display: inline-block !important;
}
/**
* Shows as a list item
**/
.show-list-item {
display: list-item !important;
}
/**
* Shows a flex element.
*/

View File

@@ -29,14 +29,11 @@ $defaultSideBarFontColor: #44A5FF;
$defaultSemiDarkColor: #ACACAC;
$defaultDarkColor: #2b3d5c;
$defaultWarningColor: rgb(215, 121, 118);
$presence-available: rgb(110, 176, 5);
$presence-away: rgb(250, 201, 20);
$presence-busy: rgb(233, 0, 27);
$presence-idle: rgb(172, 172, 172);
/**
* Toolbar
*/
$defaultToolbarSize: 50px;
$newToolbarBackgroundColor: rgba(22, 38, 55, 0.8);
$newToolbarButtonHoverColor: rgba(255, 255, 255, 0.15);
$newToolbarButtonToggleColor: rgba(255, 255, 255, 0.2);
@@ -105,7 +102,6 @@ $defaultWatermarkLink: '../images/watermark.png';
$popoverMenuPadding: 13px;
$happySoftwareBackground: transparent;
$desktopAppDragBarHeight: 25px;
$scrollHeight: 7px;
/**
* Z-indexes. TODO: Replace this by a function.
@@ -260,14 +256,3 @@ $deepLinkingMobileButtonFontWeight: bold;
$deepLinkingMobileButtonFontSize: inherit;
$primaryDeepLinkingMobileButtonBorderRadius: inherit;
/**
* Chrome extension banner variables.
*/
$chromeExtensionBannerDontShowAgainDisplay: flex;
$chromeExtensionBannerHeight: 128px;
$chromeExtensionBannerTop: 80px;
$chromeExtensionBannerRight: 16px;
$chromeExtensionBannerTopInMeeting: 10px;
$chromeExtensionBannerRightInMeeeting: 10px;

View File

@@ -173,14 +173,18 @@
&__hoverOverlay {
background: rgba(0,0,0,.6);
border-radius: $borderRadius;
position: absolute;
top: 0px;
left: 0px;
position: relative;
width: 100%;
height: 100%;
visibility: hidden;
z-index: $zindex2;
}
&.audio-only {
.videoThumbnailProblemFilter {
filter: none;
}
}
}
#localVideoWrapper {
@@ -485,8 +489,6 @@
height: 300px;
margin: auto;
position: relative;
top: 50%;
transform: translateY(-50%);
}
#mixedstream {
@@ -522,16 +524,13 @@
display: flex;
justify-content: center;
height: 50%;
width: auto;
overflow: hidden;
width: auto;
.userAvatar {
height: 100%;
object-fit: cover;
width: 100%;
top: 0px;
left: 0px;
position: absolute;
}
}
@@ -554,9 +553,6 @@
}
.sharedVideoAvatar {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
object-fit: cover;
@@ -567,6 +563,21 @@
filter: grayscale(.5) opacity(0.8);
}
.remoteVideoProblemFilter {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.videoProblemFilter {
-webkit-filter: blur(10px) grayscale(.5) opacity(0.8);
filter: blur(10px) grayscale(.5) opacity(0.8);
}
.videoThumbnailProblemFilter {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
#remotePresenceMessage,
#remoteConnectionMessage {
position: absolute;
@@ -597,6 +608,24 @@
display: none;
}
#localConnectionMessage {
display: none;
position: absolute;
left: 0;
width: 100%;
top:50%;
z-index: $zindex2;
font-weight: 600;
font-size: 14px;
text-align: center;
color: #FFF;
opacity: .80;
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
0px 1px 1px rgba(0,0,0,0.3),
1px 0px 1px rgba(0,0,0,0.3),
0px 0px 1px rgba(0,0,0,0.3);
}
.display-avatar-with-name {
.avatar-container {
visibility: visible;

View File

@@ -13,24 +13,9 @@
@extend %align-right;
transition: bottom .3s;
z-index: $filmstripVideosZ;
box-sizing: border-box;
width: 100%;
position: fixed;
/*
* Firefox sets flex items to min-height: auto and min-width: auto,
* preventing flex children from shrinking like they do on other browsers.
* Setting min-height and min-width 0 is a workaround for the issue so
* Firefox behaves like other browsers.
* https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
*/
@mixin minHWAutoFix() {
min-height: 0;
min-width: 0;
}
&.reduce-height {
bottom: calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight});
bottom: $newToolbarSizeWithPadding;
}
&__videos {
@@ -44,9 +29,8 @@
&#remoteVideos {
border: $thumbnailsBorder solid transparent;
padding-left: $defaultToolbarSize + 5;
transition: bottom 2s;
flex-grow: 1;
@include minHWAutoFix()
}
/**
@@ -55,7 +39,6 @@
&#filmstripLocalVideo {
align-self: flex-end;
display: block;
margin-bottom: 8px;
}
&.hidden {
@@ -91,52 +74,4 @@
pointer-events: none;
}
}
#filmstripRemoteVideos {
@include minHWAutoFix();
display: flex;
flex: 1;
width: auto;
justify-content: flex-end;
flex-direction: row;
#filmstripRemoteVideosContainer {
flex-direction: row-reverse;
/**
* Add padding as a hack for Firefox not to show scrollbars when
* unnecessary.
*/
padding: 1px 0;
overflow-y: hidden;
overflow-x: scroll;
}
}
.videocontainer {
margin-bottom: 10px;
}
}
/**
* Workarounds for Edge and Firefox not handling scrolling properly with
* flex-direction: row-reverse.
*/
@mixin undoRowReverseVideos() {
.horizontal-filmstrip {
#remoteVideos #filmstripRemoteVideos #filmstripRemoteVideosContainer {
flex-direction: row;
}
}
}
/** Firefox detection hack **/
@-moz-document url-prefix() {
@include undoRowReverseVideos();
}
/** Edge detection hack **/
@supports (-ms-ime-align:auto) {
@include undoRowReverseVideos();
}

View File

@@ -1,5 +1,5 @@
.filmstrip__videos .videocontainer {
display: inline-block;
display: none;
position: relative;
background-size: contain;
border: $thumbnailVideoBorder solid transparent;

View File

@@ -22,6 +22,7 @@
display: none;
}
#localConnectionMessage,
#remoteConnectionMessage,
.watermark {
z-index: $filmstripVideosZ + 1;

View File

@@ -25,7 +25,6 @@
display: flex;
flex-direction: column-reverse;
height: 100%;
width: 100%;
padding: ($desktopAppDragBarHeight - 5px) 5px 10px;
/**
* fixed positioning is necessary for remote menus and tooltips to pop
@@ -49,6 +48,7 @@
.filmstrip__videos {
@extend %align-right;
bottom: 0;
overflow: visible !important;
padding: 0;
position:relative;
right: 0;
@@ -67,7 +67,6 @@
border: $thumbnailsBorder solid transparent;
padding-left: 0;
transition: right 2s;
width: 100%;
}
}
@@ -81,16 +80,6 @@
flex-direction: column-reverse;
height: auto;
justify-content: flex-start;
#filmstripLocalVideoThumbnail {
width: calc(100% - 15px);
.videocontainer {
height: 0px;
width: 100%;
}
}
}
/**
@@ -107,21 +96,18 @@
display: flex;
flex: 1;
flex-direction: column-reverse;
flex-direction: column;
height: auto;
overflow-x: hidden;
overflow-y: scroll;
justify-content: flex-end;
#filmstripRemoteVideosContainer {
@include minHWAutoFix();
flex-direction: column-reverse;
overflow: visible;
width: calc(100% - 8px); // 8px for margin + border of the thumbnails
.videocontainer {
height: 0px;
width: 100%;
}
/**
* Add padding as a hack for Firefox not to show scrollbars when
* unnecessary.
*/
padding: 1px 0;
overflow-x: hidden;
}
}
@@ -136,13 +122,6 @@
display: flex;
transition: opacity 1s;
}
.hide-scrollbar#filmstripRemoteVideos {
margin-right: 7px; // Scrollbar size
&::-webkit-scrollbar {
display: none;
}
}
}
/**
@@ -181,24 +160,9 @@
}
}
/**
* FF does not include the scroll width when calculating the size of the content. That's why we need to include
* ourselves the width of the scroll so that the remote videos are aligned with the local one.
*/
@mixin filmstripSizeWithoutScroll {
.vertical-filmstrip {
#remoteVideos #filmstripRemoteVideos {
#filmstripRemoteVideosContainer {
width: calc(100% - 15px) // 8 px - margins + border of the thumbnails; 7px - for the scroll
}
}
}
}
/** Firefox detection hack **/
@-moz-document url-prefix() {
@include undoColumnReverseVideos();
@include filmstripSizeWithoutScroll();
}
/** Edge detection hack **/

View File

@@ -56,10 +56,6 @@
transform: translate3d(0, 0, 0);
}
.indicator-icon-container {
display: inline-block;
}
.indicator-container {
float: none;
}

View File

@@ -85,6 +85,5 @@ $flagsImagePath: "../images/";
@import 'third-party-branding/microsoft';
@import 'avatar';
@import 'promotional-footer';
@import 'chrome-extension-banner';
/* Modules END */

View File

@@ -140,13 +140,15 @@
margin-top: 20px;
font-size: 12px;
line-height: 24px;
border-collapse: collapse;
border-collapse: separate;
border-spacing: 0 5px;
thead {
text-align: left;
}
tr {
td,
th {
border-bottom: 1px solid #d1dbe8;
}

2
debian/compat vendored
View File

@@ -1 +1 @@
12
8

18
debian/control vendored
View File

@@ -16,17 +16,20 @@ Description: WebRTC JavaScript video conferences
Videobridge to provide high quality, scalable video conferences.
.
It is a web interface to Jitsi Videobridge for audio and video
forwarding and relaying.
forwarding and relaying, configured to work with jetty instance
running embedded into Jitsi Videobridge
Package: jitsi-meet-web-config
Architecture: all
Depends: openssl, nginx | nginx-full | nginx-extras | apache2
Depends: openssl, openjdk-8-jre-headless | nginx | nginx-extras | apache2
Description: Configuration for web serving of Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
.
It is a web interface to Jitsi Videobridge for audio and video
forwarding and relaying, using a webserver Nginx or Apache2.
forwarding and relaying, configured to work with jetty instance
running embedded into Jitsi Videobridge or using a webserver Nginx or
Apache2.
.
This package contains configuration for Nginx to be used with
Jitsi Meet.
@@ -34,13 +37,13 @@ Description: Configuration for web serving of Jitsi Meet
Package: jitsi-meet-prosody
Architecture: all
Depends: openssl, prosody | prosody-trunk | prosody-0.11
Replaces: jitsi-meet-tokens
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.
.
It is a web interface to Jitsi Videobridge for audio and video
forwarding and relaying.
forwarding and relaying, configured to work with jetty instance
running embedded into Jitsi Videobridge
.
This package contains configuration for Prosody to be used with
Jitsi Meet.
@@ -50,8 +53,3 @@ Architecture: all
Depends: ${misc:Depends}, prosody-trunk (>= 1nightly747) | prosody-0.11 | prosody (>= 0.11.2), libssl-dev, luarocks, jitsi-meet-prosody
Description: Prosody token authentication plugin for Jitsi Meet
Package: jitsi-meet-turnserver
Architecture: all
Breaks: apache2
Depends: ${misc:Depends}, nginx (>= 1.13.10) | nginx-full (>= 1.13.10) | nginx-extras (>= 1.13.10), jitsi-meet-prosody, coturn, dnsutils
Description: Configures coturn to be used with Jitsi Meet

View File

@@ -1 +1,2 @@
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example
doc/debian/jitsi-meet-prosody/README

View File

@@ -1,2 +0,0 @@
doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example /usr/share/jitsi-meet-prosody/
resources/prosody-plugins/ /usr/share/jitsi-meet/

View File

@@ -80,15 +80,6 @@ case "$1" in
# stores the hostname so we will reuse it later, like in purge
db_set jitsi-meet-prosody/jvb-hostname "$JVB_HOSTNAME"
db_get jitsi-meet-prosody/turn-secret
if [ -z "$RET" ] ; then
# 8-chars random secret used for the turnserver
TURN_SECRET=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1`
db_set jitsi-meet-prosody/turn-secret "$TURN_SECRET"
else
TURN_SECRET="$RET"
fi
# and we're done with debconf
db_stop
@@ -102,11 +93,11 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
mkdir -p /etc/prosody/conf.avail/
mkdir -p /etc/prosody/conf.d/
cp /usr/share/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
cp /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example $PROSODY_HOST_CONFIG
sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" $PROSODY_HOST_CONFIG
sed -i "s/jitmeetSecret/$JVB_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusSecret/$JICOFO_SECRET/g" $PROSODY_HOST_CONFIG
sed -i "s/focusUser/$JICOFO_AUTH_USER/g" $PROSODY_HOST_CONFIG
sed -i "s/__turnSecret__/$TURN_SECRET/g" $PROSODY_HOST_CONFIG
if [ ! -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua ]; then
ln -s $PROSODY_HOST_CONFIG /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
fi
@@ -125,20 +116,6 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
fi
# Check whether prosody config has the internal muc, if not add it,
# as we are migrating configs
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "internal.auth.$JVB_HOSTNAME" $PROSODY_HOST_CONFIG; then
echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG
echo -e " storage = \"null\"" >> $PROSODY_HOST_CONFIG
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
fi
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
# prosodyctl takes care for the permissions
# echo for using all default values
@@ -202,7 +179,7 @@ case "$1" in
fi
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
;;

View File

@@ -25,7 +25,7 @@ set -e
case "$1" in
remove)
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody reload || true
invoke-rc.d prosody reload
fi
;;
@@ -36,17 +36,13 @@ case "$1" in
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
JICOFO_AUTH_DOMAIN="auth.$JVB_HOSTNAME"
# clean up generated certificates
rm -f /etc/prosody/certs/$JVB_HOSTNAME.crt
rm -f /etc/prosody/certs/$JVB_HOSTNAME.key
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.crt
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.key
rm -rf /var/lib/prosody/$JICOFO_AUTH_DOMAIN.*
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.crt
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.key
rm -rf /var/lib/prosody/auth.$JVB_HOSTNAME.*
rm -rf /var/lib/prosody/$JVB_HOSTNAME.*
# clean created users
rm -rf /var/lib/prosody/`echo $JICOFO_AUTH_DOMAIN | sed -e "s/\./%2e/g"`
fi
# Clear the debconf variable

View File

@@ -28,8 +28,3 @@ Template: jicofo/jicofosecret
Type: password
_Description: Jicofo Component secret:
The secret used to connect to xmpp server as component
Template: jitsi-meet-prosody/turn-secret
Type: string
_Description: The turn server secret
The secret used to connect to turnserver server.

1
debian/jitsi-meet-tokens.install vendored Normal file
View File

@@ -0,0 +1 @@
resources/prosody-plugins/ /usr/share/jitsi-meet/

View File

@@ -78,7 +78,7 @@ case "$1" in
fi
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
fi
else

View File

@@ -41,10 +41,10 @@ case "$1" in
sed -i 's/authentication = "token"/authentication = "anonymous"/g' $PROSODY_HOST_CONFIG
sed -i "s/ app_id=\"$APP_ID\"/ --app_id=\"example_app_id\"/g" $PROSODY_HOST_CONFIG
sed -i "s/ app_secret=\"$APP_SECRET\"/ --app_secret=\"example_app_secret\"/g" $PROSODY_HOST_CONFIG
sed -i 's/ -- "token_verification"/ "token_verification"/g' $PROSODY_HOST_CONFIG
sed -i 's/ modules_enabled = { "token_verification" }/ --modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
if [ -x "/etc/init.d/prosody" ]; then
invoke-rc.d prosody restart || true
invoke-rc.d prosody restart
fi
fi

View File

@@ -1,2 +0,0 @@
doc/debian/jitsi-meet-turn/turnserver.conf /usr/share/jitsi-meet-turnserver/
doc/debian/jitsi-meet/jitsi-meet.conf /usr/share/jitsi-meet-turnserver/

View File

@@ -1 +0,0 @@
/usr/share/jitsi-meet-turnserver/jitsi-meet.conf /etc/nginx/modules-enabled/60-jitsi-meet.conf

View File

@@ -1,128 +0,0 @@
#!/bin/bash
# postinst script for jitsi-meet-turnserver
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
# loading debconf
. /usr/share/debconf/confmodule
# try to get host from jitsi-videobridge
db_get jitsi-videobridge/jvb-hostname
if [ -z "$RET" ] ; then
# server hostname
db_set jitsi-videobridge/jvb-hostname "localhost"
db_input critical jitsi-videobridge/jvb-hostname || true
db_go
fi
JVB_HOSTNAME="$RET"
TURN_CONFIG="/etc/turnserver.conf"
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
# detect dpkg-reconfigure, just delete old links
db_get jitsi-meet-turnserver/jvb-hostname
JVB_HOSTNAME_OLD=$RET
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
rm -f $TURN_CONFIG
fi
# this detect only old installations with no nginx
db_get jitsi-meet/jvb-serve || true
if [ ! -f $NGINX_CONFIG -o "$RET" = "true" ] ; then
# nothing to do
echo ""
echo "turnserver not configured as no nginx found to multiplex traffic"
echo ""
db_stop
exit 0
fi
# stores the hostname so we will reuse it later, like in purge
db_set jitsi-meet-turnserver/jvb-hostname "$JVB_HOSTNAME"
# try to get turnserver password
db_get jitsi-meet-prosody/turn-secret
if [ -z "$RET" ] ; then
db_input critical jitsi-meet-prosody/turn-secret || true
db_go
fi
TURN_SECRET="$RET"
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
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__/$JVB_HOSTNAME/g" $TURN_CONFIG
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
# replace self-signed certificate paths with user provided ones
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
fi
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
invoke-rc.d coturn restart || true
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
if [ -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
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
fi
# Enable turn server in config.js
if [ -f $JITSI_MEET_CONFIG ] ; then
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
fi
fi
# and we're done with debconf
db_stop
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@@ -1,9 +0,0 @@
Template: jitsi-meet-turnserver/jvb-hostname
Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.

View File

@@ -1 +1,4 @@
doc/debian/jitsi-meet/jitsi-meet.example
doc/debian/jitsi-meet/jitsi-meet.example-apache
doc/debian/jitsi-meet/README
config.js

View File

@@ -1,3 +0,0 @@
doc/debian/jitsi-meet/jitsi-meet.example /usr/share/jitsi-meet-web-config/
doc/debian/jitsi-meet/jitsi-meet.example-apache /usr/share/jitsi-meet-web-config/
config.js /usr/share/jitsi-meet-web-config/

View File

@@ -43,8 +43,7 @@ case "$1" in
fi
JVB_SERVE="false"
# this detect only old installations
db_get jitsi-meet/jvb-serve || true
db_get jitsi-meet/jvb-serve
if [ -n "$RET" ] && [ "$RET" = "true" ] ; then
JVB_SERVE="true"
fi
@@ -53,12 +52,9 @@ case "$1" in
db_set jitsi-meet/jvb-hostname $JVB_HOSTNAME
NGINX_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx' 2>/dev/null | awk '{print $3}' || true)"
NGINX_FULL_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-full' 2>/dev/null | awk '{print $3}' || true)"
NGINX_EXTRAS_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'nginx-extras' 2>/dev/null | awk '{print $3}' || true)"
if [ "$NGINX_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_INSTALL_CHECK" = "unpacked" ] \
|| [ "$NGINX_FULL_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_FULL_INSTALL_CHECK" = "unpacked" ] \
|| [ "$NGINX_EXTRAS_INSTALL_CHECK" = "installed" ] \
|| [ "$NGINX_EXTRAS_INSTALL_CHECK" = "unpacked" ] ; then
FORCE_NGINX="true"
@@ -68,11 +64,12 @@ case "$1" in
FORCE_APACHE="true"
fi
UPLOADED_CERT_CHOICE="I want to use my own certificate"
# if first time config ask for certs, or if we are reconfiguring
if [ -z "$JVB_HOSTNAME_OLD" ] || [ "$RECONFIGURING" = "true" ] ; then
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
UPLOADED_CERT_CHOICE="I want to use my own certificate"
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
@@ -101,60 +98,77 @@ case "$1" in
# jitsi meet
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
if [ ! -f $JITSI_MEET_CONFIG ] ; then
cp /usr/share/jitsi-meet-web-config/config.js $JITSI_MEET_CONFIG
# replaces needed config for multidomain as it works only with nginx
if [[ "$FORCE_NGINX" = "true" ]] ; then
sed -i "s/conference.jitsi-meet.example.com/conference.<\!--# echo var=\"subdomain\" default=\"\" -->jitsi-meet.example.com/g" $JITSI_MEET_CONFIG
fi
cp /usr/share/doc/jitsi-meet-web-config/config.js $JITSI_MEET_CONFIG
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG
fi
# getting rid of jetty serving web
if [[ "$JVB_SERVE" = "true" ]] ; then
# this is new install let's configure jvb to serve meet
# no-nginx, no-apache installed on machine, this is new install or reconfiguring old one which have jvb_serve set
if [[ -z "$FORCE_NGINX" && -z "$FORCE_APACHE" && ( -z "$JVB_HOSTNAME_OLD" || ( "$JVB_SERVE" = "true" && "$RECONFIGURING" = "true" )) ]] ; then
JVB_ETC_CONFIG="/etc/jitsi/videobridge/config"
JVB_CONFIG="/etc/jitsi/videobridge/sip-communicator.properties"
# we will write to the file if missing create it
if [ -f $JVB_CONFIG ] ; then
echo ""
echo "------------------------------------------------"
echo ""
echo "You are using jetty to serve jitsi-meet, we are now upgrading you to use nginx!"
echo ""
echo "If you are using Lets Encrypt certificates please re-run the script."
echo ""
echo "------------------------------------------------"
echo ""
sed -i "s/org.jitsi.videobridge.rest.jetty/#org.jitsi.videobridge.rest.jetty/g" $JVB_CONFIG
sed -i "s/org.jitsi.videobridge.TCP_HARVESTER_PORT/#org.jitsi.videobridge.TCP_HARVESTER_PORT/g" $JVB_CONFIG
if [ -d /run/systemd/system ]; then
systemctl restart jitsi-videobridge2.service >/dev/null || true
fi
# Removing this value will force nginx or apache to be locally configured
JVB_HOSTNAME_OLD=""
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
# Fix certs on upgrade from jetty
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
else
# create self-signed certs
CERT_KEY="/etc/jitsi/meet/$JVB_HOSTNAME.key"
CERT_CRT="/etc/jitsi/meet/$JVB_HOSTNAME.crt"
fi
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
rm -f $JVB_CONFIG
fi
db_set jitsi-meet/jvb-serve "false"
fi
# we will write to the file if missing create it
if [ ! -f $JVB_CONFIG ] ; then
touch $JVB_CONFIG
fi
if [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# configure jvb
echo "AUTHBIND=yes" >> $JVB_ETC_CONFIG
sed -i "s/JVB_OPTS=.*/JVB_OPTS=--apis=rest,xmpp/g" $JVB_ETC_CONFIG
echo "org.jitsi.videobridge.rest.jetty.host=::" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.port=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.hostHeader=$JVB_HOSTNAME" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.pathSpec=/http-bind" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.proxyTo=http://localhost:5280/http-bind" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.resourceBase=/usr/share/jitsi-meet" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./config.js=/etc/jitsi/meet/$JVB_HOSTNAME-config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./interface_config.js=/usr/share/jitsi-meet/interface_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./logging_config.js=/usr/share/jitsi-meet/logging_config.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./external_api.js=/usr/share/jitsi-meet/libs/external_api.min.js" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.regex=^/([a-zA-Z0-9]+)$" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.replacement=/" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.SSIResourceHandler.paths=/" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.tls.port=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.TCP_HARVESTER_PORT=443" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePath=/etc/jitsi/videobridge/$JVB_HOSTNAME.jks" >> $JVB_CONFIG
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePassword=changeit" >> $JVB_CONFIG
# configure authbind to allow jvb to bind to privileged ports
OWNER=$(stat -c '%U' /usr/share/jitsi-videobridge)
GROUP=$(stat -c '%G' /usr/share/jitsi-videobridge)
JVB_UID="`id -u $OWNER`"
if [ ! -f "/etc/authbind/byport/443" ] ; then
if [ ! -d "/etc/authbind/byport" ] ; then
mkdir -p /etc/authbind/byport
chmod 755 /etc/authbind
chmod 755 /etc/authbind/byport
fi
touch /etc/authbind/byport/443
chown $OWNER /etc/authbind/byport/443
chmod 755 /etc/authbind/byport/443
fi
CERT_P12="/etc/jitsi/videobridge/$JVB_HOSTNAME.p12"
CERT_JKS="/etc/jitsi/videobridge/$JVB_HOSTNAME.jks"
# create jks from certs
openssl pkcs12 -export \
-in $CERT_CRT -inkey $CERT_KEY -passout pass:changeit > $CERT_P12
keytool -importkeystore -destkeystore $CERT_JKS \
-srckeystore $CERT_P12 -srcstoretype pkcs12 \
-noprompt -storepass changeit -srcstorepass changeit
db_set jitsi-meet/jvb-serve "true"
invoke-rc.d jitsi-videobridge restart
elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
@@ -163,7 +177,7 @@ case "$1" in
# nginx conf
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ] ; then
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
cp /usr/share/doc/jitsi-meet-web-config/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] ; then
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
fi
@@ -182,9 +196,8 @@ case "$1" in
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
fi
invoke-rc.d nginx reload || true
invoke-rc.d nginx reload
elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
# this is a reconfigure, lets just delete old links
if [ "$RECONFIGURING" = "true" ] ; then
a2dissite $JVB_HOSTNAME_OLD.conf
@@ -195,7 +208,7 @@ case "$1" in
if [ ! -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf ] ; then
# when creating new config, make sure all needed modules are enabled
a2enmod rewrite ssl headers proxy_http include
cp /usr/share/jitsi-meet-web-config/jitsi-meet.example-apache /etc/apache2/sites-available/$JVB_HOSTNAME.conf
cp /usr/share/doc/jitsi-meet-web-config/jitsi-meet.example-apache /etc/apache2/sites-available/$JVB_HOSTNAME.conf
a2ensite $JVB_HOSTNAME.conf
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/apache2/sites-available/$JVB_HOSTNAME.conf
fi
@@ -212,7 +225,7 @@ case "$1" in
/etc/apache2/sites-available/$JVB_HOSTNAME.conf
fi
invoke-rc.d apache2 reload || true
invoke-rc.d apache2 reload
fi
echo "----------------"

View File

@@ -25,10 +25,10 @@ set -e
case "$1" in
remove)
if [ -x "/etc/init.d/nginx" ]; then
invoke-rc.d nginx reload || true
invoke-rc.d nginx reload
fi
if [ -x "/etc/init.d/apache2" ]; then
invoke-rc.d apache2 reload || true
invoke-rc.d apache2 reload
fi
;;
purge)
@@ -40,6 +40,8 @@ case "$1" in
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf
rm -f /etc/apache2/sites-enabled/$JVB_HOSTNAME.conf
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.jks
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.key
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.crt
fi

View File

@@ -26,6 +26,12 @@ Type: string
_Description: The hostname of the current installation:
The value for the hostname that is set in Jitsi Videobridge installation.
Template: jitsi-meet/jvb-serve
Type: boolean
Default: false
_Description: for internal use
for internal use.
Template: jitsi-videobridge/jvb-hostname
Type: string
_Description: Hostname:

2
debian/rules vendored
View File

@@ -14,7 +14,7 @@ override_dh_auto_build:
override_dh_install: $(LANGUAGES)
dh_installdirs
dh_install
dh_install -X/config.js -X/package.json
$(LANGUAGES):
LOCALE=$$(echo $@ | cut -c1-2) ; \

View File

@@ -30,7 +30,6 @@ Its constructor gets a number of options:
* **onload**: (optional) handler for the iframe onload event.
* **invitees**: (optional) Array of objects containing information about new participants that will be invited in the call.
* **devices**: (optional) A map containing information about the initial devices that will be used in the call.
* **userInfo**: (optional) JS object containing information about the participant opening the meeting, such as `email`.
Example:
@@ -85,19 +84,6 @@ const options = {
const api = new JitsiMeetExternalAPI(domain, options);
```
You can set the userInfo(email) for the call:
```javascript
var domain = "meet.jit.si";
var options = {
...
userInfo: {
email: 'email@jitsiexamplemail.com'
}
}
var api = new JitsiMeetExternalAPI(domain, options);
```
### Controlling the embedded Jitsi Meet Conference
Device management `JitsiMeetExternalAPI` methods:
@@ -270,11 +256,6 @@ api.executeCommand('email', 'example@example.com');
api.executeCommand('avatarUrl', 'https://avatars0.githubusercontent.com/u/3671647');
```
* **sendEndpointTextMessage** - Sends a text message to another participant through the datachannels.
```javascript
api.executeCommand('receiverParticipantId', 'text');
```
You can also execute multiple commands using the `executeCommands` method:
```javascript
api.executeCommands(commands);
@@ -328,21 +309,6 @@ changes. The listener will receive an object with the following structure:
}
```
* **endpointTextMessageReceived** - event notifications about a text message received through datachannels.
The listener will receive an object with the following structure:
```javascript
{
senderInfo: {
jid: string, // the jid of the sender
id: string // the participant id of the sender
},
eventData: {
name: string // the name of the datachannel event: `endpoint-text-message`
text: string // the received text from the sender
}
}
```
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
```javascript
{

View File

@@ -1,18 +1,5 @@
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
-- domain mapper options, must at least have domain base set to use the mapper
muc_mapper_domain_base = "jitmeet.example.com";
turncredentials_secret = "__turnSecret__";
turncredentials = {
{ type = "stun", host = "jitmeet.example.com", port = "443" },
{ type = "turn", host = "jitmeet.example.com", port = "443", transport = "udp" },
{ type = "turns", host = "jitmeet.example.com", port = "443", transport = "tcp" }
};
cross_domain_bosh = false;
consider_bosh_secure = true;
-- Plugins path gets uncommented during jitsi-meet-tokens package install - that's where token plugin is located
--plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
VirtualHost "jitmeet.example.com"
-- enabled = false -- Remove this line to enable this host
@@ -29,46 +16,25 @@ VirtualHost "jitmeet.example.com"
key = "/etc/prosody/certs/jitmeet.example.com.key";
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
}
speakerstats_component = "speakerstats.jitmeet.example.com"
conference_duration_component = "conferenceduration.jitmeet.example.com"
-- we need bosh
modules_enabled = {
"bosh";
"pubsub";
"ping"; -- Enable mod_ping
"speakerstats";
"turncredentials";
"conference_duration";
}
c2s_require_encryption = false
Component "conference.jitmeet.example.com" "muc"
storage = "null"
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
-- "token_verification";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
--modules_enabled = { "token_verification" }
admins = { "focusUser@auth.jitmeet.example.com" }
-- internal muc component
Component "internal.auth.jitmeet.example.com" "muc"
storage = "null"
modules_enabled = {
"ping";
}
admins = { "focusUser@auth.jitmeet.example.com", "jvb@auth.jitmeet.example.com" }
Component "jitsi-videobridge.jitmeet.example.com"
component_secret = "jitmeetSecret"
VirtualHost "auth.jitmeet.example.com"
authentication = "internal_plain"
Component "focus.jitmeet.example.com"
component_secret = "focusSecret"
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
muc_component = "conference.jitmeet.example.com"
Component "conferenceduration.jitmeet.example.com" "conference_duration_component"
muc_component = "conference.jitmeet.example.com"

View File

@@ -1 +0,0 @@
Coturn configuration for Jitsi Meet

View File

@@ -1,13 +0,0 @@
# jitsi-meet coturn config. Do not modify this line
lt-cred-mech
use-auth-secret
keep-address-family
static-auth-secret=__turnSecret__
realm=jitsi-meet.example.com
cert=/etc/jitsi/meet/jitsi-meet.example.com.crt
pkey=/etc/jitsi/meet/jitsi-meet.example.com.key
no-tcp
listening-port=443
tls-listening-port=4445
external-ip=__external_ip_address__

View File

@@ -1,30 +0,0 @@
# 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
stream {
upstream web {
server 127.0.0.1:4444;
}
upstream turn {
server 127.0.0.1:4445;
}
# since 1.13.10
map $ssl_preread_alpn_protocols $upstream {
"h2" web;
"http/1.1" web;
"h2,http/1.1" web;
default turn;
}
server {
listen 443;
# since 1.11.5
ssl_preread on;
proxy_pass $upstream;
# Increase buffer to serve video
proxy_buffer_size 10m;
}
}

View File

@@ -2,23 +2,11 @@ server_names_hash_bucket_size 64;
server {
listen 80;
listen [::]:80;
server_name jitsi-meet.example.com;
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /usr/share/jitsi-meet;
}
location = /.well-known/acme-challenge/ {
return 404;
}
location / {
return 301 https://$host$request_uri;
}
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name jitsi-meet.example.com;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
@@ -31,11 +19,7 @@ server {
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
root /usr/share/jitsi-meet;
# ssi on with javascript for multidomain variables in config.js
ssi on;
ssi_types application/x-javascript application/javascript;
index index.html index.htm;
error_page 404 /static/404.html;
@@ -61,54 +45,11 @@ server {
proxy_set_header Host $http_host;
}
# xmpp websockets
location = /xmpp-websocket {
proxy_pass http://127.0.0.1:5280/xmpp-websocket?prefix=$prefix&$args;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
tcp_nodelay on;
}
location ~ ^/([^/?&:'"]+)$ {
location ~ ^/([^?&:’“]+)$ {
try_files $uri @root_path;
}
location @root_path {
rewrite ^/(.*)$ / break;
}
location ~ ^/([^/?&:'"]+)/config.js$
{
set $subdomain "$1.";
set $subdir "$1/";
alias /etc/jitsi/meet/jitsi-meet.example.com-config.js;
}
#Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
location ~ ^/([^/?&:'"]+)/(.*)$ {
set $subdomain "$1.";
set $subdir "$1/";
rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
}
# BOSH for subdomains
location ~ ^/([^/?&:'"]+)/http-bind {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /http-bind;
}
# websockets for subdomains
location ~ ^/([^/?&:'"]+)/xmpp-websocket {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /xmpp-websocket;
}
}

View File

@@ -10,6 +10,5 @@ var config = {
focus: 'focus.jitsi.example.com',
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
websocket: 'wss://jitsi.example.com/xmpp-websocket'
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
};

View File

@@ -36,6 +36,10 @@ server {
rewrite ^/(.*)$ / break;
}
location / {
ssi on;
}
location ~ ^/([^/?&:'"]+)/config.js$
{
set $subdomain "$1.";
@@ -60,12 +64,4 @@ server {
rewrite ^/(.*)$ /http-bind;
}
# websockets for subdomains
location ~ ^/([^/?&:'"]+)/xmpp-websocket {
set $subdomain "$1.";
set $subdir "$1/";
set $prefix "$1";
rewrite ^/(.*)$ /xmpp-websocket;
}
}
}

View File

@@ -83,6 +83,8 @@ modules_enabled = {
"adhoc";
"websocket";
"http_altconnect";
-- include domain mapper as global level module
"muc_domain_mapper";
}
-- domain mapper options, must at least have domain base set to use the mapper

View File

@@ -4,4 +4,4 @@ A. In general, if the tool ifconfig (or ipconfig) shows the assigned IP address
**2. Clients could communicate well in room created at meet.jit.si . The same clients still could connect to my self-hosted instance but can neither hear nor see one another. What's wrong?**
A. Most probably, the server is behind NAT. See this [resolved question](https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/). You need to follow the steps detailed [here](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md#Advanced-configuration)
A. Most probably, the server is behind NAT. See this [resolved question](https://community.jitsi.org/t/cannot-see-video-or-hear-audio-on-self-hosted-instance/). You need to follow the steps detailed [here](https://github.com/jitsi/ice4j/blob/master/doc/quick-install.md#Advanced-configuration)

View File

@@ -1,6 +1,9 @@
# Server Installation for Jitsi Meet
:warning: **WARNING:** Manual installation is not recommended. We recommend following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document is sometimes not updated to reflect latest changes.
:warning: **WARNING:** Manual installation is not recommended. We recommend following the [quick-install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md) document. The current document describes the steps that are needed to install a working deployment, but steps are easy to mess up, and the debian packages are more up-to-date, where this document sometimes is not updated to latest changes.
This describes configuring a server `jitsi.example.com` running Debian or a Debian Derivative. You will need to
change references to that to match your host, and generate some passwords for
@@ -19,7 +22,7 @@ This is how the network looks:
443 |
+-------+ |
| | |
| Nginx | |
| NginX | |
| | |
+--+-+--+ |
| | |
@@ -28,7 +31,7 @@ This is how the network looks:
| jitsi-meet +<---+ +--->+ prosody/xmpp | |
| |files 5280 | | |
+------------+ +--------------+ v
5222,5347^ ^5347 4443,10000
5222,5347^ ^5347 4443
+--------+ | | +-------------+
| | | | | |
| jicofo +----^ ^----+ videobridge |
@@ -109,7 +112,7 @@ Restart prosody XMPP server with the new config
prosodyctl restart
```
## Install Nginx
## Install nginx
```sh
apt-get install nginx
```
@@ -119,13 +122,12 @@ Add a new file `jitsi.example.com` in `/etc/nginx/sites-available` (see also the
server_names_hash_bucket_size 64;
server {
listen 0.0.0.0:443 ssl http2;
listen [::]:443 ssl http2;
listen 443;
# tls configuration that is not covered in this guide
# we recommend the use of https://certbot.eff.org/
server_name jitsi.example.com;
# set the root
root /srv/jitsi-meet;
root /srv/jitsi.example.com;
index index.html;
location ~ ^/([a-zA-Z0-9=\?]+)$ {
rewrite ^/(.*)$ / break;
@@ -140,12 +142,6 @@ server {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
}
# external_api.js must be accessible from the root of the
# installation for the electron version of Jitsi Meet to work
# https://github.com/jitsi/jitsi-meet-electron
location /external_api.js {
alias /srv/jitsi-meet/libs/external_api.min.js;
}
}
```
@@ -156,7 +152,6 @@ ln -s ../sites-available/jitsi.example.com jitsi.example.com
```
## Install Jitsi Videobridge
Visit https://download.jitsi.org/jitsi-videobridge/linux to determine the current build number, download and unzip it:
```sh
wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip
unzip jitsi-videobridge-linux-{arch-buildnum}.zip
@@ -169,15 +164,9 @@ apt-get install openjdk-8-jre
_NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7._
Create `~/.sip-communicator/sip-communicator.properties` in the home folder of the user that will be starting Jitsi Videobridge:
```sh
mkdir -p ~/.sip-communicator
cat > ~/.sip-communicator/sip-communicator.properties << EOF
In the user home that will be starting Jitsi Videobridge create `.sip-communicator` folder and add the file `sip-communicator.properties` with one line in it:
```
org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
# The videobridge uses 443 by default with 4443 as a fallback, but since we're already
# running nginx on 443 in this example doc, we specify 4443 manually to avoid a race condition
org.jitsi.videobridge.TCP_HARVESTER_PORT=4443
EOF
```
Start the videobridge with:
@@ -202,7 +191,7 @@ Clone source from Github repo:
```sh
git clone https://github.com/jitsi/jicofo.git
```
Build the package.
Build distribution package. Replace {os-name} with one of: 'lin', 'lin64', 'macosx', 'win', 'win64'.
```sh
cd jicofo
mvn package -DskipTests -Dassembly.skipAssembly=false
@@ -210,8 +199,8 @@ mvn package -DskipTests -Dassembly.skipAssembly=false
Run jicofo:
```sh
=======
unzip target/jicofo-1.1-SNAPSHOT-archive.zip
cd jicofo-1.1-SNAPSHOT-archive'
unzip target/jicofo-{os-name}-1.0-SNAPSHOT.zip
cd jicofo-{os-name}-1.0-SNAPSHOT'
./jicofo.sh --host=localhost --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
```
@@ -220,14 +209,13 @@ Checkout and configure Jitsi Meet:
```sh
cd /srv
git clone https://github.com/jitsi/jitsi-meet.git
cd jitsi-meet
mv jitsi-meet/ jitsi.example.com
cd jitsi.example.com
npm install
make
```
_NOTE: When installing on older distributions keep in mind that you need Node.js >= 10 and npm >= 6._
Edit host names in `/srv/jitsi-meet/config.js` (see also the example config file):
Edit host names in `/srv/jitsi.example.com/config.js` (see also the example config file):
```
var config = {
hosts: {
@@ -243,19 +231,15 @@ var config = {
};
```
Verify that nginx config is valid and reload nginx:
Restart nginx to get the new configuration:
```sh
nginx -t && nginx -s reload
invoke-rc.d nginx restart
```
## Running behind NAT
Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are `TCP/4443` and `UDP/10000`.
Jitsi-Videobridge can run behind a NAT, provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video.
`TCP/443` is required for the webserver which can be running on another machine than the Jitsi Videobrige is running on.
The following extra lines need to be added to the file `~/.sip-communicator/sip-communicator.properties` (in the home directory of the user running the videobridge):
The following extra lines need to be added the file `~/.sip-communicator/sip-communicator.properties` (in the home directory of the user running the videobridge):
```
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
@@ -264,5 +248,6 @@ org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
# Hold your first conference
You are now all set and ready to have your first meet by going to http://jitsi.example.com
## Enabling recording
[Jibri](https://github.com/jitsi/jibri) is a set of tools for recording and/or streaming a Jitsi Meet conference.
[Jibri](https://github.com/jitsi/jibri)is a set of tools for recording and/or streaming a Jitsi Meet conference.

View File

@@ -1,75 +1,68 @@
# Jitsi Meet quick install
This guide helps you ___host your own Jitsi server___. If you want to have a video conference without setting up any infrastructure, use https://meet.jit.si instead.
This document describes the required steps for a quick Jitsi Meet installation on a Debian based GNU/Linux system. Debian 8 (Jessie) or later, and Ubuntu 14.04 or later are supported out-of-the-box.
This document describes the required steps for a quick Jitsi Meet installation on a Debian based GNU/Linux system. Debian 9 (Stretch) or later, and Ubuntu 18.04 (Bionic Beaver) or later are supported out-of-the-box.
Debian Wheezy and other older systems may require additional things to be done. Specifically for Wheezy, [libc needs to be updated](http://lists.jitsi.org/pipermail/users/2015-September/010064.html).
On Ubuntu systems, Jitsi requires dependencies from Ubuntu's `universe` package repository. To ensure this is enabled, run `apt-add-repository universe` at the command-line.
Also note that a recent default Ubuntu installation has only the `main` repository enabled, and Jitsi Meet needs packages from `universe`. Check your `/etc/apt/sources.list` file, and if `universe` is not present refer to [Ubuntu's documentation](https://help.ubuntu.com/community/Repositories/Ubuntu) on how to enable it. (Usually it amounts to copying the `main` lines and changing to `universe`.)
_Note_: Many of the installation steps require elevated privileges. If you are logged in using a regular user account, you may need to temporarily increase your permissions (for example, by using `sudo` for individual commands).
N.B.:
a.) All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands.
b.) You only need to do this if you want to ___host your own Jitsi server___. If you just want to have a video conference with someone, use https://meet.jit.si instead.
## Basic Jitsi Meet install
### Set up the Fully Qualified Domain Name (FQDN) (optional)
If the machine used to host the Jitsi Meet instance has a FQDN (for example `meet.example.org`) already set up in DNS, `/etc/hostname` must contain this FQDN; if this is not the case yet, [change the hostname](https://wiki.debian.org/HowTo/ChangeHostname).
Then add the same FQDN in the `/etc/hosts` file, associating it with the loopback address:
127.0.0.1 localhost meet.example.org
Finally on the same machine test that you can ping the FQDN with: `ping "$(hostname)"`-
### Add the Jitsi package repository
### Add the repository
```sh
echo 'deb https://download.jitsi.org stable/' >> /etc/apt/sources.list.d/jitsi-stable.list
wget -qO - https://download.jitsi.org/jitsi-key.gpg.key | apt-key add -
```
### Install Jitsi Meet
_Note_: The installer will check if [Nginx](https://nginx.org/) or [Apache](https://httpd.apache.org/) is present (in that order) and configure a virtualhost within the web server it finds to serve Jitsi Meet. If none of the above is found it then defaults to Nginx.
If you are already running Nginx on port 443 on the same machine you better skip the turnserver configuration as it will conflict with your current port 443, so use the command `apt install --no-install-recommends jitsi-meet`.
### Update the package lists
```sh
# Ensure support is available for apt repositories served via HTTPS
apt-get install apt-transport-https
# Retrieve the latest package versions across all repositories
apt-get update
```
# Perform jitsi-meet installation
(If you get an error:
E: The method driver /usr/lib/apt/methods/https could not be found.
run:
```sh
apt-get install apt-transport-https
```
)
### Install Jitsi Meet
Note : Something to consider before installation is how you're planning to serve Jitsi Meet. The installer will check if Nginx or Apache is present (with this order) and configure a virtualhost within the web server it finds to serve Jitsi Meet. If none of the above is found it then configures itself to be served via jetty. So if for example you are planning on deploying Jitsi Meet with a web server, you have to make sure to install the server **before** installing jitsi-meet.
```sh
apt-get -y install jitsi-meet
```
During the installation, you will be asked to enter the hostname of the Jitsi Meet instance. If you have a [FQDN](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) for the instance already set up in DNS, enter it there. If you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change).
During the installation, you will be asked to enter the hostname of the Jitsi Meet instance. If you have a FQDN hostname for the instance already set up in DNS, enter it there. If you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change).
This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences.
### Generate a Let's Encrypt certificate (optional, recommended)
### Generate a Let's Encrypt certificate
In order to have encrypted communications, you need a [TLS certificate](https://en.wikipedia.org/wiki/Transport_Layer_Security). The easiest way is to use [Let's Encrypt](https://letsencrypt.org/).
_Note_: Jitsi Meet mobile apps *require* a valid certificate signed by a trusted [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority) and will not be able to connect to your server if you choose a self-signed certificate.
Simply run the following in your shell:
Simply run the following in your shell
```sh
/usr/share/jitsi-meet/scripts/install-letsencrypt-cert.sh
```
Note that this script uses the [HTTP-01 challenge type](https://letsencrypt.org/docs/challenge-types/) and thus your instance needs to be accessible from the public internet. If you want to use a different challenge type, don't use this script and instead choose ___I want to use my own certificate___ during jitsi-meet installation.
#### Advanced configuration
If the installation is on a machine [behind NAT](https://github.com/jitsi/jitsi-meet/blob/master/doc/faq.md) jitsi-videobridge should configure itself automatically on boot. If three way call does not work further configuration of jitsi-videobridge is needed in order for it to be accessible from outside.
Provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP/10000).
The following extra lines need to be added to the file `/etc/jitsi/videobridge/sip-communicator.properties`:
If installation is on a machine [behind NAT](https://github.com/jitsi/jitsi-meet/blob/master/doc/faq.md) further configuration of jitsi-videobridge is needed in order for it to be accessible.
Provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
The following extra lines need to be added the file `/etc/jitsi/videobridge/sip-communicator.properties`:
```
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
```
And comment the existing `org.ice4j.ice.harvest.STUN_MAPPING_HARVESTER_ADDRESSES`.
See [the documentation of ice4j](https://github.com/jitsi/ice4j/blob/master/doc/configuration.md)
See [the documenation of ice4j](https://github.com/jitsi/ice4j/blob/master/doc/configuration.md)
for details.
Default deployments on systems using systemd will have low default values for maximum processes and open files. If the used bridge will expect higher number of participants the default values need to be adjusted (the default values are good for less than 100 participants).
@@ -81,24 +74,20 @@ DefaultTasksMax=65000
```
To load the values and check them look [here](#systemd-details) for details.
By default, anyone who has access to your jitsi instance will be able to start a conference: if your server is open to the world, anyone can have a chat with anyone else. If you want to limit the ability to start a conference to registered users, set up a "secure domain". Follow the instructions at https://github.com/jitsi/jicofo#secure-domain.
By default, anyone who has access to your jitsi instance will be able to start a conferencee: if your server is open to the world, anyone can have a chat with anyone else. If you want to limit the ability to start a conference to registered users, set up a "secure domain". Follow the instructions at https://github.com/jitsi/jicofo#secure-domain.
### Confirm that your installation is working
### Open a conference
Launch a web browser (Chrome, Chromium or latest Opera) and enter the hostname or IP address from the previous step into the address bar.
Launch a web browser (Chrome, Chromium or latest Opera) and enter in the URL bar the hostname (or IP address) you used in the previous step.
If you used a self-signed certificate (as opposed to using Let's Encrypt), your web browser will ask you to confirm that you trust the certificate.
Confirm that you trust the self-signed certificate of the newly installed Jitsi Meet.
You should see a web page prompting you to create a new meeting. Make sure that you can successfully create a meeting and that other participants are able to join the session.
If this all worked, then congratulations! You have an operational Jitsi conference service.
Enjoy!
## Adding sip-gateway to Jitsi Meet
### Install Jigasi
Jigasi is a server-side application acting as a gateway to Jitsi Meet conferences. It allows regular [SIP](https://en.wikipedia.org/wiki/Session_Initiation_Protocol) clients to join meetings and provides transcription capabilities.
```sh
apt-get -y install jigasi
```
@@ -120,7 +109,7 @@ Enjoy!
## Uninstall
```sh
apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jicofo jitsi-videobridge2
apt-get purge jigasi jitsi-meet jitsi-meet-web-config jitsi-meet-prosody jitsi-meet-web jicofo jitsi-videobridge
```
Sometimes the following packages will fail to uninstall properly:
@@ -130,25 +119,13 @@ Sometimes the following packages will fail to uninstall properly:
When this happens, just run the uninstall command a second time and it should be ok.
The reason for the failure is that sometimes the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jitsi-videobridge daemons are already stopped.
The reason for failure is that sometimes, the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jvb daemons are already stopped.
#### Systemd details
To reload the systemd changes on a running system execute `systemctl daemon-reload` and `service jitsi-videobridge2 restart`.
To check the tasks part execute `service jitsi-videobridge2 status` and you should see `Tasks: XX (limit: 65000)`.
To check the files and process part execute ```cat /proc/`cat /var/run/jitsi-videobridge/jitsi-videobridge.pid`/limits``` and you should see:
To reload the systemd changes on a running system execute `systemctl daemon-reload` and `service jitsi-videobridge restart`.
To check the tasks part execute `service jitsi-videobridge status` and you should see `Tasks: XX (limit: 65000)`.
To check the files and process part execute ```cat /proc/`cat /var/run/jitsi-videobridge.pid`/limits``` and you should see:
```
Max processes 65000 65000 processes
Max open files 65000 65000 files
```
## Debugging problems
If you run into problems, one thing to try is using a different web browser. Some versions of some browsers are known to have issues with Jitsi Meet. You can also visit https://test.webrtc.org to test your browser's [WebRTC](https://en.wikipedia.org/wiki/WebRTC) support.
Another place to look is the various log files:
```
/var/log/jitsi/jvb.log
/var/log/jitsi/jicofo.log
/var/log/prosody/prosody.log
```

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
images/avatarprezi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Some files were not shown because too many files have changed in this diff Show More