Removed unused tutorials
100
openvidu-android/.gitignore
vendored
@ -1,100 +0,0 @@
|
||||
# github/gitignore/Android
|
||||
# ========================
|
||||
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
.idea/jarRepositories.xml
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
|
||||
|
||||
# Custom ignore rules
|
||||
# ===================
|
||||
|
||||
# IntelliJ
|
||||
.idea/deploymentTargetDropDown.xml
|
||||
1
openvidu-android/.idea/.name
generated
@ -1 +0,0 @@
|
||||
OpenVidu Android
|
||||
143
openvidu-android/.idea/codeStyles/Project.xml
generated
@ -1,143 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="5" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="RESOURCE_LIST_WRAP" value="5" />
|
||||
<option name="RESOURCE_LIST_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="5" />
|
||||
<option name="THROWS_LIST_WRAP" value="5" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="5" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="5" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<option name="ENUM_CONSTANTS_WRAP" value="5" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
openvidu-android/.idea/compiler.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
20
openvidu-android/.idea/gradle.xml
generated
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@ -1,7 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Default" />
|
||||
<inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Default" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
35
openvidu-android/.idea/jarRepositories.xml
generated
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
16
openvidu-android/.idea/misc.xml
generated
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.3333333333333333" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
6
openvidu-android/.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1
openvidu-android/app/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/build
|
||||
@ -1,54 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
// NOTES
|
||||
// =====
|
||||
//
|
||||
// * minSdk: API level 21 (Android 5) required by org.webrtc:google-webrtc:1.0.32006 (Google WebRTC).
|
||||
// * targetSdk: API level 31 (Android 12) required by Google Play Store, since August 1, 2022.
|
||||
// See: https://support.google.com/googleplay/android-developer/answer/11926878
|
||||
// * compileSdkVersion: Should match "targetSdk".
|
||||
//
|
||||
// For a complete table of all Android API levels, check https://apilevels.com/
|
||||
|
||||
android {
|
||||
compileSdk 31
|
||||
defaultConfig {
|
||||
applicationId "io.openvidu.openvidu_android"
|
||||
minSdk 21
|
||||
targetSdk 31
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
debuggable true
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
namespace 'io.openvidu.openvidu_android'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
|
||||
implementation 'com.jakewharton:butterknife:10.2.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
|
||||
implementation 'com.neovisionaries:nv-websocket-client:2.9'
|
||||
implementation 'com.github.webrtc-sdk:android:104.+'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
}
|
||||
21
openvidu-android/app/proguard-rules.pro
vendored
@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@ -1,27 +0,0 @@
|
||||
package io.openvidu.openvidu_android;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("io.openvidu.openvidu_android", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="io.openvidu.openvidu_android.activities.SessionActivity"
|
||||
android:label="OpenVidu Android"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
Before Width: | Height: | Size: 19 KiB |
@ -1,322 +0,0 @@
|
||||
package io.openvidu.openvidu_android.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.SurfaceViewRenderer;
|
||||
import org.webrtc.VideoTrack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.openvidu.openvidu_android.R;
|
||||
import io.openvidu.openvidu_android.fragments.PermissionsDialogFragment;
|
||||
import io.openvidu.openvidu_android.openvidu.LocalParticipant;
|
||||
import io.openvidu.openvidu_android.openvidu.RemoteParticipant;
|
||||
import io.openvidu.openvidu_android.openvidu.Session;
|
||||
import io.openvidu.openvidu_android.utils.CustomHttpClient;
|
||||
import io.openvidu.openvidu_android.websocket.CustomWebSocket;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class SessionActivity extends AppCompatActivity {
|
||||
|
||||
private static final int MY_PERMISSIONS_REQUEST_CAMERA = 100;
|
||||
private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 101;
|
||||
private static final int MY_PERMISSIONS_REQUEST = 102;
|
||||
private final String TAG = "SessionActivity";
|
||||
@BindView(R.id.views_container)
|
||||
LinearLayout views_container;
|
||||
@BindView(R.id.start_finish_call)
|
||||
Button start_finish_call;
|
||||
@BindView(R.id.session_name)
|
||||
EditText session_name;
|
||||
@BindView(R.id.participant_name)
|
||||
EditText participant_name;
|
||||
@BindView(R.id.application_server_url)
|
||||
EditText application_server_url;
|
||||
@BindView(R.id.local_gl_surface_view)
|
||||
SurfaceViewRenderer localVideoView;
|
||||
@BindView(R.id.main_participant)
|
||||
TextView main_participant;
|
||||
@BindView(R.id.peer_container)
|
||||
FrameLayout peer_container;
|
||||
|
||||
private String APPLICATION_SERVER_URL;
|
||||
private Session session;
|
||||
private CustomHttpClient httpClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
setContentView(R.layout.activity_main);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
|
||||
askForPermissions();
|
||||
ButterKnife.bind(this);
|
||||
Random random = new Random();
|
||||
int randomIndex = random.nextInt(100);
|
||||
participant_name.setText(participant_name.getText().append(String.valueOf(randomIndex)));
|
||||
}
|
||||
|
||||
public void askForPermissions() {
|
||||
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) &&
|
||||
(ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
|
||||
!= PackageManager.PERMISSION_GRANTED)) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
|
||||
MY_PERMISSIONS_REQUEST);
|
||||
} else if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.RECORD_AUDIO},
|
||||
MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
|
||||
} else if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.CAMERA},
|
||||
MY_PERMISSIONS_REQUEST_CAMERA);
|
||||
}
|
||||
}
|
||||
|
||||
public void buttonPressed(View view) {
|
||||
if (start_finish_call.getText().equals(getResources().getString(R.string.hang_up))) {
|
||||
// Already connected to a session
|
||||
leaveSession();
|
||||
return;
|
||||
}
|
||||
if (arePermissionGranted()) {
|
||||
initViews();
|
||||
viewToConnectingState();
|
||||
|
||||
APPLICATION_SERVER_URL = application_server_url.getText().toString();
|
||||
httpClient = new CustomHttpClient(APPLICATION_SERVER_URL);
|
||||
|
||||
String sessionId = session_name.getText().toString();
|
||||
getToken(sessionId);
|
||||
} else {
|
||||
DialogFragment permissionsFragment = new PermissionsDialogFragment();
|
||||
permissionsFragment.show(getSupportFragmentManager(), "Permissions Fragment");
|
||||
}
|
||||
}
|
||||
|
||||
private void getToken(String sessionId) {
|
||||
try {
|
||||
// Session Request
|
||||
RequestBody sessionBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{\"customSessionId\": \"" + sessionId + "\"}");
|
||||
this.httpClient.httpCall("/api/sessions", "POST", "application/json", sessionBody, new Callback() {
|
||||
|
||||
@Override
|
||||
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
|
||||
Log.d(TAG, "responseString: " + response.body().string());
|
||||
|
||||
// Token Request
|
||||
RequestBody tokenBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{}");
|
||||
httpClient.httpCall("/api/sessions/" + sessionId + "/connections", "POST", "application/json", tokenBody, new Callback() {
|
||||
|
||||
@Override
|
||||
public void onResponse(@NotNull Call call, @NotNull Response response) {
|
||||
String responseString = null;
|
||||
try {
|
||||
responseString = response.body().string();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting body", e);
|
||||
}
|
||||
getTokenSuccess(responseString, sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
Log.e(TAG, "Error POST /api/sessions/SESSION_ID/connections", e);
|
||||
connectionError(APPLICATION_SERVER_URL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
Log.e(TAG, "Error POST /api/sessions", e);
|
||||
connectionError(APPLICATION_SERVER_URL);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error getting token", e);
|
||||
e.printStackTrace();
|
||||
connectionError(APPLICATION_SERVER_URL);
|
||||
}
|
||||
}
|
||||
|
||||
private void getTokenSuccess(String token, String sessionId) {
|
||||
// Initialize our session
|
||||
session = new Session(sessionId, token, views_container, this);
|
||||
|
||||
// Initialize our local participant and start local camera
|
||||
String participantName = participant_name.getText().toString();
|
||||
LocalParticipant localParticipant = new LocalParticipant(participantName, session, this.getApplicationContext(), localVideoView);
|
||||
localParticipant.startCamera();
|
||||
runOnUiThread(() -> {
|
||||
// Update local participant view
|
||||
main_participant.setText(participant_name.getText().toString());
|
||||
main_participant.setPadding(20, 3, 20, 3);
|
||||
});
|
||||
|
||||
// Initialize and connect the websocket to OpenVidu Server
|
||||
startWebSocket();
|
||||
}
|
||||
|
||||
private void startWebSocket() {
|
||||
CustomWebSocket webSocket = new CustomWebSocket(session, this);
|
||||
webSocket.execute();
|
||||
session.setWebSocket(webSocket);
|
||||
}
|
||||
|
||||
private void connectionError(String url) {
|
||||
Runnable myRunnable = () -> {
|
||||
Toast toast = Toast.makeText(this, "Error connecting to " + url, Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
viewToDisconnectedState();
|
||||
};
|
||||
new Handler(this.getMainLooper()).post(myRunnable);
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
EglBase rootEglBase = EglBase.create();
|
||||
localVideoView.init(rootEglBase.getEglBaseContext(), null);
|
||||
localVideoView.setMirror(true);
|
||||
localVideoView.setEnableHardwareScaler(true);
|
||||
localVideoView.setZOrderMediaOverlay(true);
|
||||
}
|
||||
|
||||
public void viewToDisconnectedState() {
|
||||
runOnUiThread(() -> {
|
||||
localVideoView.clearImage();
|
||||
localVideoView.release();
|
||||
start_finish_call.setText(getResources().getString(R.string.start_button));
|
||||
start_finish_call.setEnabled(true);
|
||||
application_server_url.setEnabled(true);
|
||||
application_server_url.setFocusableInTouchMode(true);
|
||||
session_name.setEnabled(true);
|
||||
session_name.setFocusableInTouchMode(true);
|
||||
participant_name.setEnabled(true);
|
||||
participant_name.setFocusableInTouchMode(true);
|
||||
main_participant.setText(null);
|
||||
main_participant.setPadding(0, 0, 0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
public void viewToConnectingState() {
|
||||
runOnUiThread(() -> {
|
||||
start_finish_call.setEnabled(false);
|
||||
application_server_url.setEnabled(false);
|
||||
application_server_url.setFocusable(false);
|
||||
session_name.setEnabled(false);
|
||||
session_name.setFocusable(false);
|
||||
participant_name.setEnabled(false);
|
||||
participant_name.setFocusable(false);
|
||||
});
|
||||
}
|
||||
|
||||
public void viewToConnectedState() {
|
||||
runOnUiThread(() -> {
|
||||
start_finish_call.setText(getResources().getString(R.string.hang_up));
|
||||
start_finish_call.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
public void createRemoteParticipantVideo(final RemoteParticipant remoteParticipant) {
|
||||
Handler mainHandler = new Handler(this.getMainLooper());
|
||||
Runnable myRunnable = () -> {
|
||||
View rowView = this.getLayoutInflater().inflate(R.layout.peer_video, null);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(0, 0, 0, 20);
|
||||
rowView.setLayoutParams(lp);
|
||||
int rowId = View.generateViewId();
|
||||
rowView.setId(rowId);
|
||||
views_container.addView(rowView);
|
||||
SurfaceViewRenderer videoView = (SurfaceViewRenderer) ((ViewGroup) rowView).getChildAt(0);
|
||||
remoteParticipant.setVideoView(videoView);
|
||||
videoView.setMirror(false);
|
||||
EglBase rootEglBase = EglBase.create();
|
||||
videoView.init(rootEglBase.getEglBaseContext(), null);
|
||||
videoView.setZOrderMediaOverlay(true);
|
||||
View textView = ((ViewGroup) rowView).getChildAt(1);
|
||||
remoteParticipant.setParticipantNameText((TextView) textView);
|
||||
remoteParticipant.setView(rowView);
|
||||
|
||||
remoteParticipant.getParticipantNameText().setText(remoteParticipant.getParticipantName());
|
||||
remoteParticipant.getParticipantNameText().setPadding(20, 3, 20, 3);
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
}
|
||||
|
||||
public void setRemoteMediaStream(MediaStream stream, final RemoteParticipant remoteParticipant) {
|
||||
final VideoTrack videoTrack = stream.videoTracks.get(0);
|
||||
videoTrack.addSink(remoteParticipant.getVideoView());
|
||||
runOnUiThread(() -> {
|
||||
remoteParticipant.getVideoView().setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
public void leaveSession() {
|
||||
if(this.session != null) {
|
||||
this.session.leaveSession();
|
||||
}
|
||||
if(this.httpClient != null) {
|
||||
this.httpClient.dispose();
|
||||
}
|
||||
viewToDisconnectedState();
|
||||
}
|
||||
|
||||
private boolean arePermissionGranted() {
|
||||
return (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_DENIED) &&
|
||||
(ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_DENIED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
leaveSession();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
leaveSession();
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
leaveSession();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package io.openvidu.openvidu_android.constants;
|
||||
|
||||
public final class JsonConstants {
|
||||
|
||||
// RPC incoming methods
|
||||
public static final String PARTICIPANT_JOINED = "participantJoined";
|
||||
public static final String PARTICIPANT_LEFT = "participantLeft";
|
||||
public static final String PARTICIPANT_PUBLISHED = "participantPublished";
|
||||
public static final String ICE_CANDIDATE = "iceCandidate";
|
||||
public static final String PARTICIPANT_UNPUBLISHED = "participantUnpublished";
|
||||
public static final String PARTICIPANT_EVICTED = "participantEvicted";
|
||||
public static final String RECORDING_STARTED = "recordingStarted";
|
||||
public static final String RECORDING_STOPPED = "recordingStopped";
|
||||
public static final String SEND_MESSAGE = "sendMessage";
|
||||
public static final String STREAM_PROPERTY_CHANGED = "streamPropertyChanged";
|
||||
public static final String FILTER_EVENT_DISPATCHED = "filterEventDispatched";
|
||||
public static final String MEDIA_ERROR = "mediaError";
|
||||
|
||||
// RPC outgoing methods
|
||||
public static final String PING_METHOD = "ping";
|
||||
public static final String JOINROOM_METHOD = "joinRoom";
|
||||
public static final String LEAVEROOM_METHOD = "leaveRoom";
|
||||
public static final String PUBLISHVIDEO_METHOD = "publishVideo";
|
||||
public static final String ONICECANDIDATE_METHOD = "onIceCandidate";
|
||||
public static final String PREPARERECEIVEVIDEO_METHOD = "prepareReceiveVideoFrom";
|
||||
public static final String RECEIVEVIDEO_METHOD = "receiveVideoFrom";
|
||||
public static final String UNSUBSCRIBEFROMVIDEO_METHOD = "unsubscribeFromVideo";
|
||||
public static final String SENDMESSAGE_ROOM_METHOD = "sendMessage";
|
||||
public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo";
|
||||
public static final String STREAMPROPERTYCHANGED_METHOD = "streamPropertyChanged";
|
||||
public static final String NETWORKQUALITYLEVELCHANGED_METHOD = "networkQualityLevelChanged";
|
||||
public static final String FORCEDISCONNECT_METHOD = "forceDisconnect";
|
||||
public static final String FORCEUNPUBLISH_METHOD = "forceUnpublish";
|
||||
public static final String APPLYFILTER_METHOD = "applyFilter";
|
||||
public static final String EXECFILTERMETHOD_METHOD = "execFilterMethod";
|
||||
public static final String REMOVEFILTER_METHOD = "removeFilter";
|
||||
public static final String ADDFILTEREVENTLISTENER_METHOD = "addFilterEventListener";
|
||||
public static final String REMOVEFILTEREVENTLISTENER_METHOD = "removeFilterEventListener";
|
||||
|
||||
public static final String JSON_RPCVERSION = "2.0";
|
||||
|
||||
public static final String VALUE = "value";
|
||||
public static final String PARAMS = "params";
|
||||
public static final String METHOD = "method";
|
||||
public static final String ID = "id";
|
||||
public static final String RESULT = "result";
|
||||
public static final String ERROR = "error";
|
||||
public static final String MEDIA_SERVER = "mediaServer";
|
||||
|
||||
public static final String SESSION_ID = "sessionId";
|
||||
public static final String SDP_ANSWER = "sdpAnswer";
|
||||
public static final String METADATA = "metadata";
|
||||
|
||||
public static final String ICE_SERVERS = "customIceServers";
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
package io.openvidu.openvidu_android.fragments;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import io.openvidu.openvidu_android.R;
|
||||
import io.openvidu.openvidu_android.activities.SessionActivity;
|
||||
|
||||
public class PermissionsDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String TAG = "PermissionsDialog";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(R.string.permissions_dialog_title);
|
||||
builder.setMessage(R.string.no_permissions_granted)
|
||||
.setPositiveButton(R.string.accept_permissions_dialog, (dialog, id) -> ((SessionActivity) getActivity()).askForPermissions())
|
||||
.setNegativeButton(R.string.cancel_dialog, (dialog, id) -> Log.i(TAG, "User cancelled Permissions Dialog"));
|
||||
return builder.create();
|
||||
}
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
package io.openvidu.openvidu_android.observers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.webrtc.DataChannel;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.RtpReceiver;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CustomPeerConnectionObserver implements PeerConnection.Observer {
|
||||
|
||||
private String TAG = "PeerConnection";
|
||||
|
||||
public CustomPeerConnectionObserver(String id) {
|
||||
this.TAG = this.TAG + "-" + id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
|
||||
Log.d(TAG, "onSignalingChange() called with: signalingState = [" + signalingState + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
|
||||
Log.d(TAG, "onIceConnectionChange() called with: iceConnectionState = [" + iceConnectionState + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIceConnectionReceivingChange(boolean b) {
|
||||
Log.d(TAG, "onIceConnectionReceivingChange() called with: b = [" + b + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
|
||||
Log.d(TAG, "onIceGatheringChange() called with: iceGatheringState = [" + iceGatheringState + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||
Log.d(TAG, "onIceCandidate() called with: iceCandidate = [" + iceCandidate + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
|
||||
Log.d(TAG, "onIceCandidatesRemoved() called with: iceCandidates = [" + Arrays.toString(iceCandidates) + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddStream(MediaStream mediaStream) {
|
||||
Log.d(TAG, "onAddStream() called with: mediaStream = [" + mediaStream + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveStream(MediaStream mediaStream) {
|
||||
Log.d(TAG, "onRemoveStream() called with: mediaStream = [" + mediaStream + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChannel(DataChannel dataChannel) {
|
||||
Log.d(TAG, "onDataChannel() called with: dataChannel = [" + dataChannel + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenegotiationNeeded() {
|
||||
Log.d(TAG, "onRenegotiationNeeded() called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
|
||||
Log.d(TAG, "onAddTrack() called with: mediaStreams = [" + Arrays.toString(mediaStreams) + "]");
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package io.openvidu.openvidu_android.observers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.webrtc.SdpObserver;
|
||||
import org.webrtc.SessionDescription;
|
||||
|
||||
public class CustomSdpObserver implements SdpObserver {
|
||||
|
||||
private final String tag;
|
||||
|
||||
public CustomSdpObserver(String tag) {
|
||||
this.tag = "SdpObserver-" + tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateSuccess(SessionDescription sdp) {
|
||||
Log.d(this.tag, "onCreateSuccess, SDP: " + sdp.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetSuccess() {
|
||||
Log.d(this.tag, "onSetSuccess");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateFailure(String error) {
|
||||
Log.e(this.tag, "onCreateFailure, error: " + error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetFailure(String error) {
|
||||
Log.e(this.tag, "onSetFailure, error: " + error);
|
||||
}
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
package io.openvidu.openvidu_android.openvidu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import org.webrtc.AudioSource;
|
||||
import org.webrtc.Camera1Enumerator;
|
||||
import org.webrtc.Camera2Enumerator;
|
||||
import org.webrtc.CameraEnumerator;
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaConstraints;
|
||||
import org.webrtc.PeerConnectionFactory;
|
||||
import org.webrtc.SessionDescription;
|
||||
import org.webrtc.SurfaceTextureHelper;
|
||||
import org.webrtc.SurfaceViewRenderer;
|
||||
import org.webrtc.VideoCapturer;
|
||||
import org.webrtc.VideoSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class LocalParticipant extends Participant {
|
||||
|
||||
private Context context;
|
||||
private SurfaceViewRenderer localVideoView;
|
||||
private SurfaceTextureHelper surfaceTextureHelper;
|
||||
private VideoCapturer videoCapturer;
|
||||
|
||||
private Collection<IceCandidate> localIceCandidates;
|
||||
private SessionDescription localSessionDescription;
|
||||
|
||||
public LocalParticipant(String participantName, Session session, Context context, SurfaceViewRenderer localVideoView) {
|
||||
super(participantName, session);
|
||||
this.localVideoView = localVideoView;
|
||||
this.context = context;
|
||||
this.localIceCandidates = new ArrayList<>();
|
||||
session.setLocalParticipant(this);
|
||||
}
|
||||
|
||||
public void startCamera() {
|
||||
|
||||
final EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
|
||||
PeerConnectionFactory peerConnectionFactory = this.session.getPeerConnectionFactory();
|
||||
|
||||
// Create AudioSource
|
||||
AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
|
||||
this.audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
|
||||
|
||||
surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
|
||||
|
||||
// Create VideoCapturer
|
||||
VideoCapturer videoCapturer = createCameraCapturer();
|
||||
VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
|
||||
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
|
||||
videoCapturer.startCapture(480, 640, 30);
|
||||
|
||||
// Create VideoTrack
|
||||
this.videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
|
||||
|
||||
// Display in localView
|
||||
this.videoTrack.addSink(localVideoView);
|
||||
}
|
||||
|
||||
private VideoCapturer createCameraCapturer() {
|
||||
CameraEnumerator enumerator;
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
|
||||
enumerator = new Camera2Enumerator(this.context);
|
||||
} else {
|
||||
enumerator = new Camera1Enumerator(false);
|
||||
}
|
||||
final String[] deviceNames = enumerator.getDeviceNames();
|
||||
|
||||
// Try to find front facing camera
|
||||
for (String deviceName : deviceNames) {
|
||||
if (enumerator.isFrontFacing(deviceName)) {
|
||||
videoCapturer = enumerator.createCapturer(deviceName, null);
|
||||
if (videoCapturer != null) {
|
||||
return videoCapturer;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Front facing camera not found, try something else
|
||||
for (String deviceName : deviceNames) {
|
||||
if (!enumerator.isFrontFacing(deviceName)) {
|
||||
videoCapturer = enumerator.createCapturer(deviceName, null);
|
||||
if (videoCapturer != null) {
|
||||
return videoCapturer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void storeIceCandidate(IceCandidate iceCandidate) {
|
||||
localIceCandidates.add(iceCandidate);
|
||||
}
|
||||
|
||||
public Collection<IceCandidate> getLocalIceCandidates() {
|
||||
return this.localIceCandidates;
|
||||
}
|
||||
|
||||
public void storeLocalSessionDescription(SessionDescription sessionDescription) {
|
||||
localSessionDescription = sessionDescription;
|
||||
}
|
||||
|
||||
public SessionDescription getLocalSessionDescription() {
|
||||
return this.localSessionDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (videoTrack != null) {
|
||||
videoTrack.removeSink(localVideoView);
|
||||
videoCapturer.dispose();
|
||||
videoCapturer = null;
|
||||
}
|
||||
if (surfaceTextureHelper != null) {
|
||||
surfaceTextureHelper.dispose();
|
||||
surfaceTextureHelper = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
package io.openvidu.openvidu_android.openvidu;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.webrtc.AudioTrack;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.VideoTrack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Participant {
|
||||
|
||||
protected String connectionId;
|
||||
protected String participantName;
|
||||
protected Session session;
|
||||
protected List<IceCandidate> iceCandidateList = new ArrayList<>();
|
||||
protected PeerConnection peerConnection;
|
||||
protected AudioTrack audioTrack;
|
||||
protected VideoTrack videoTrack;
|
||||
protected MediaStream mediaStream;
|
||||
|
||||
public Participant(String participantName, Session session) {
|
||||
this.participantName = participantName;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public Participant(String connectionId, String participantName, Session session) {
|
||||
this.connectionId = connectionId;
|
||||
this.participantName = participantName;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public String getConnectionId() {
|
||||
return this.connectionId;
|
||||
}
|
||||
|
||||
public void setConnectionId(String connectionId) {
|
||||
this.connectionId = connectionId;
|
||||
}
|
||||
|
||||
public String getParticipantName() {
|
||||
return this.participantName;
|
||||
}
|
||||
|
||||
public List<IceCandidate> getIceCandidateList() {
|
||||
return this.iceCandidateList;
|
||||
}
|
||||
|
||||
public PeerConnection getPeerConnection() {
|
||||
return peerConnection;
|
||||
}
|
||||
|
||||
public void setPeerConnection(PeerConnection peerConnection) {
|
||||
this.peerConnection = peerConnection;
|
||||
}
|
||||
|
||||
public AudioTrack getAudioTrack() {
|
||||
return this.audioTrack;
|
||||
}
|
||||
|
||||
public void setAudioTrack(AudioTrack audioTrack) {
|
||||
this.audioTrack = audioTrack;
|
||||
}
|
||||
|
||||
public VideoTrack getVideoTrack() {
|
||||
return this.videoTrack;
|
||||
}
|
||||
|
||||
public void setVideoTrack(VideoTrack videoTrack) {
|
||||
this.videoTrack = videoTrack;
|
||||
}
|
||||
|
||||
public MediaStream getMediaStream() {
|
||||
return this.mediaStream;
|
||||
}
|
||||
|
||||
public void setMediaStream(MediaStream mediaStream) {
|
||||
this.mediaStream = mediaStream;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (this.peerConnection != null) {
|
||||
try {
|
||||
this.peerConnection.close();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e("Dispose PeerConnection", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package io.openvidu.openvidu_android.openvidu;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.webrtc.SurfaceViewRenderer;
|
||||
|
||||
public class RemoteParticipant extends Participant {
|
||||
|
||||
private View view;
|
||||
private SurfaceViewRenderer videoView;
|
||||
private TextView participantNameText;
|
||||
|
||||
public RemoteParticipant(String connectionId, String participantName, Session session) {
|
||||
super(connectionId, participantName, session);
|
||||
this.session.addRemoteParticipant(this);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
public void setView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public SurfaceViewRenderer getVideoView() {
|
||||
return this.videoView;
|
||||
}
|
||||
|
||||
public void setVideoView(SurfaceViewRenderer videoView) {
|
||||
this.videoView = videoView;
|
||||
}
|
||||
|
||||
public TextView getParticipantNameText() {
|
||||
return this.participantNameText;
|
||||
}
|
||||
|
||||
public void setParticipantNameText(TextView participantNameText) {
|
||||
this.participantNameText = participantNameText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -1,275 +0,0 @@
|
||||
package io.openvidu.openvidu_android.openvidu;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import io.openvidu.openvidu_android.activities.SessionActivity;
|
||||
import io.openvidu.openvidu_android.observers.CustomPeerConnectionObserver;
|
||||
import io.openvidu.openvidu_android.observers.CustomSdpObserver;
|
||||
import io.openvidu.openvidu_android.websocket.CustomWebSocket;
|
||||
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaConstraints;
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.MediaStreamTrack;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.PeerConnection.IceServer;
|
||||
import org.webrtc.PeerConnectionFactory;
|
||||
import org.webrtc.RtpReceiver;
|
||||
import org.webrtc.RtpTransceiver;
|
||||
import org.webrtc.SessionDescription;
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.VideoDecoderFactory;
|
||||
import org.webrtc.VideoEncoderFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Session {
|
||||
|
||||
private LocalParticipant localParticipant;
|
||||
private Map<String, RemoteParticipant> remoteParticipants = new HashMap<>();
|
||||
private String id;
|
||||
private String token;
|
||||
|
||||
private final List<IceServer> iceServersDefault =
|
||||
Arrays.asList(IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
|
||||
private List<IceServer> iceServers = new ArrayList();
|
||||
|
||||
private LinearLayout views_container;
|
||||
private PeerConnectionFactory peerConnectionFactory;
|
||||
private CustomWebSocket websocket;
|
||||
private SessionActivity activity;
|
||||
|
||||
public Session(String id, String token, LinearLayout views_container, SessionActivity activity) {
|
||||
this.id = id;
|
||||
this.token = token;
|
||||
this.views_container = views_container;
|
||||
this.activity = activity;
|
||||
|
||||
// Creating a new PeerConnectionFactory instance
|
||||
PeerConnectionFactory.InitializationOptions.Builder optionsBuilder = PeerConnectionFactory.InitializationOptions.builder(activity.getApplicationContext());
|
||||
optionsBuilder.setEnableInternalTracer(true);
|
||||
PeerConnectionFactory.InitializationOptions opt = optionsBuilder.createInitializationOptions();
|
||||
PeerConnectionFactory.initialize(opt);
|
||||
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
||||
|
||||
// Using software encoder and decoder
|
||||
final VideoEncoderFactory encoderFactory;
|
||||
final VideoDecoderFactory decoderFactory;
|
||||
encoderFactory = new SoftwareVideoEncoderFactory();
|
||||
decoderFactory = new SoftwareVideoDecoderFactory();
|
||||
|
||||
peerConnectionFactory = PeerConnectionFactory.builder()
|
||||
.setVideoEncoderFactory(encoderFactory)
|
||||
.setVideoDecoderFactory(decoderFactory)
|
||||
.setOptions(options)
|
||||
.createPeerConnectionFactory();
|
||||
}
|
||||
|
||||
public void setWebSocket(CustomWebSocket websocket) {
|
||||
this.websocket = websocket;
|
||||
}
|
||||
|
||||
public PeerConnection createLocalPeerConnection() {
|
||||
PeerConnection.RTCConfiguration config =
|
||||
new PeerConnection.RTCConfiguration(iceServers.isEmpty()
|
||||
? iceServersDefault
|
||||
: iceServers);
|
||||
config.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED;
|
||||
config.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
|
||||
config.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
|
||||
config.continualGatheringPolicy =
|
||||
PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
|
||||
config.keyType = PeerConnection.KeyType.ECDSA;
|
||||
config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
|
||||
|
||||
PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(config, new CustomPeerConnectionObserver("local") {
|
||||
@Override
|
||||
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||
super.onIceCandidate(iceCandidate);
|
||||
websocket.onIceCandidate(iceCandidate, localParticipant.getConnectionId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
|
||||
if (PeerConnection.SignalingState.STABLE.equals(signalingState)) {
|
||||
// SDP Offer/Answer finished. Add stored remote candidates.
|
||||
Iterator<IceCandidate> it = localParticipant.getIceCandidateList().iterator();
|
||||
while (it.hasNext()) {
|
||||
IceCandidate candidate = it.next();
|
||||
localParticipant.getPeerConnection().addIceCandidate(candidate);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (localParticipant.getAudioTrack() != null) {
|
||||
peerConnection.addTransceiver(localParticipant.getAudioTrack(),
|
||||
new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
|
||||
}
|
||||
if (localParticipant.getVideoTrack() != null) {
|
||||
peerConnection.addTransceiver(localParticipant.getVideoTrack(),
|
||||
new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
|
||||
}
|
||||
|
||||
return peerConnection;
|
||||
}
|
||||
|
||||
public void createRemotePeerConnection(final String connectionId) {
|
||||
PeerConnection.RTCConfiguration config =
|
||||
new PeerConnection.RTCConfiguration(iceServers.isEmpty()
|
||||
? iceServersDefault
|
||||
: iceServers);
|
||||
config.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED;
|
||||
config.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
|
||||
config.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
|
||||
config.continualGatheringPolicy =
|
||||
PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
|
||||
config.keyType = PeerConnection.KeyType.ECDSA;
|
||||
config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
|
||||
|
||||
PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(config, new CustomPeerConnectionObserver("remotePeerCreation") {
|
||||
@Override
|
||||
public void onIceCandidate(IceCandidate iceCandidate) {
|
||||
super.onIceCandidate(iceCandidate);
|
||||
websocket.onIceCandidate(iceCandidate, connectionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
|
||||
super.onAddTrack(rtpReceiver, mediaStreams);
|
||||
activity.setRemoteMediaStream(mediaStreams[0], remoteParticipants.get(connectionId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
|
||||
if (PeerConnection.SignalingState.STABLE.equals(signalingState)) {
|
||||
// SDP Offer/Answer finished. Add stored remote candidates.
|
||||
final RemoteParticipant remoteParticipant = remoteParticipants.get(connectionId);
|
||||
Iterator<IceCandidate> it = remoteParticipant.getIceCandidateList().iterator();
|
||||
while (it.hasNext()) {
|
||||
IceCandidate candidate = it.next();
|
||||
remoteParticipant.getPeerConnection().addIceCandidate(candidate);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO,
|
||||
new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY));
|
||||
peerConnection.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO,
|
||||
new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY));
|
||||
|
||||
this.remoteParticipants.get(connectionId).setPeerConnection(peerConnection);
|
||||
}
|
||||
|
||||
public void createOfferForPublishing(MediaConstraints constraints) {
|
||||
localParticipant.getPeerConnection().createOffer(new CustomSdpObserver("createOffer") {
|
||||
@Override
|
||||
public void onCreateSuccess(SessionDescription sdp) {
|
||||
super.onCreateSuccess(sdp);
|
||||
Log.i("createOffer SUCCESS", sdp.toString());
|
||||
localParticipant.getPeerConnection().setLocalDescription(new CustomSdpObserver("createOffer_setLocalDescription") {
|
||||
@Override
|
||||
public void onSetSuccess() {
|
||||
super.onSetSuccess();
|
||||
websocket.publishVideo(sdp);
|
||||
}
|
||||
}, sdp);
|
||||
}
|
||||
}, constraints);
|
||||
}
|
||||
|
||||
public void createAnswerForSubscribing(RemoteParticipant remoteParticipant, String streamId, MediaConstraints constraints) {
|
||||
remoteParticipant.getPeerConnection().createAnswer(new CustomSdpObserver("createAnswerSubscribing") {
|
||||
@Override
|
||||
public void onCreateSuccess(SessionDescription sdp) {
|
||||
super.onCreateSuccess(sdp);
|
||||
Log.i("createAnswer SUCCESS", sdp.toString());
|
||||
remoteParticipant.getPeerConnection().setLocalDescription(new CustomSdpObserver("createAnswerSubscribing_setLocalDescription") {
|
||||
@Override
|
||||
public void onSetSuccess() {
|
||||
super.onSetSuccess();
|
||||
websocket.receiveVideoFrom(sdp, remoteParticipant, streamId);
|
||||
}
|
||||
}, sdp);
|
||||
}
|
||||
}, constraints);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public void setIceServers(List<IceServer> iceServers) {
|
||||
this.iceServers = iceServers;
|
||||
}
|
||||
|
||||
public LocalParticipant getLocalParticipant() {
|
||||
return this.localParticipant;
|
||||
}
|
||||
|
||||
public void setLocalParticipant(LocalParticipant localParticipant) {
|
||||
this.localParticipant = localParticipant;
|
||||
}
|
||||
|
||||
public RemoteParticipant getRemoteParticipant(String id) {
|
||||
return this.remoteParticipants.get(id);
|
||||
}
|
||||
|
||||
public PeerConnectionFactory getPeerConnectionFactory() {
|
||||
return this.peerConnectionFactory;
|
||||
}
|
||||
|
||||
public void addRemoteParticipant(RemoteParticipant remoteParticipant) {
|
||||
this.remoteParticipants.put(remoteParticipant.getConnectionId(), remoteParticipant);
|
||||
}
|
||||
|
||||
public RemoteParticipant removeRemoteParticipant(String id) {
|
||||
return this.remoteParticipants.remove(id);
|
||||
}
|
||||
|
||||
public void leaveSession() {
|
||||
AsyncTask.execute(() -> {
|
||||
websocket.setWebsocketCancelled(true);
|
||||
if (websocket != null) {
|
||||
websocket.leaveRoom();
|
||||
websocket.disconnect();
|
||||
}
|
||||
this.localParticipant.dispose();
|
||||
});
|
||||
this.activity.runOnUiThread(() -> {
|
||||
for (RemoteParticipant remoteParticipant : remoteParticipants.values()) {
|
||||
if (remoteParticipant.getPeerConnection() != null) {
|
||||
remoteParticipant.getPeerConnection().close();
|
||||
}
|
||||
views_container.removeView(remoteParticipant.getView());
|
||||
}
|
||||
});
|
||||
AsyncTask.execute(() -> {
|
||||
if (peerConnectionFactory != null) {
|
||||
peerConnectionFactory.dispose();
|
||||
peerConnectionFactory = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removeView(View view) {
|
||||
this.views_container.removeView(view);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
package io.openvidu.openvidu_android.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
public class CustomHttpClient {
|
||||
|
||||
private OkHttpClient client;
|
||||
private String baseUrl;
|
||||
|
||||
public CustomHttpClient(String baseUrl) {
|
||||
this.baseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
|
||||
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
final TrustManager[] trustAllCerts = new TrustManager[]{
|
||||
new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
final SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||
// Create an ssl socket factory with our all-trusting manager
|
||||
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
this.client = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[]{};
|
||||
}
|
||||
}).hostnameVerifier(new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void httpCall(String url, String method, String contentType, RequestBody body, Callback callback) throws IOException {
|
||||
url = url.startsWith("/") ? url.substring(1) : url;
|
||||
Request request = new Request.Builder()
|
||||
.url(this.baseUrl + url)
|
||||
.header("Content-Type", contentType)
|
||||
.method(method, body)
|
||||
.build();
|
||||
Call call = client.newCall(request);
|
||||
call.enqueue(callback);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
this.client.dispatcher().executorService().shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,692 +0,0 @@
|
||||
package io.openvidu.openvidu_android.websocket;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.neovisionaries.ws.client.ThreadType;
|
||||
import com.neovisionaries.ws.client.WebSocket;
|
||||
import com.neovisionaries.ws.client.WebSocketException;
|
||||
import com.neovisionaries.ws.client.WebSocketFactory;
|
||||
import com.neovisionaries.ws.client.WebSocketFrame;
|
||||
import com.neovisionaries.ws.client.WebSocketListener;
|
||||
import com.neovisionaries.ws.client.WebSocketState;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.webrtc.IceCandidate;
|
||||
import org.webrtc.MediaConstraints;
|
||||
import org.webrtc.MediaStream;
|
||||
import org.webrtc.PeerConnection;
|
||||
import org.webrtc.PeerConnection.IceServer;
|
||||
import org.webrtc.RtpTransceiver;
|
||||
import org.webrtc.SessionDescription;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import io.openvidu.openvidu_android.activities.SessionActivity;
|
||||
import io.openvidu.openvidu_android.constants.JsonConstants;
|
||||
import io.openvidu.openvidu_android.observers.CustomSdpObserver;
|
||||
import io.openvidu.openvidu_android.openvidu.LocalParticipant;
|
||||
import io.openvidu.openvidu_android.openvidu.Participant;
|
||||
import io.openvidu.openvidu_android.openvidu.RemoteParticipant;
|
||||
import io.openvidu.openvidu_android.openvidu.Session;
|
||||
|
||||
public class CustomWebSocket extends AsyncTask<SessionActivity, Void, Void> implements WebSocketListener {
|
||||
|
||||
private final String TAG = "CustomWebSocketListener";
|
||||
private final int PING_MESSAGE_INTERVAL = 5;
|
||||
private final TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
|
||||
Log.i(TAG, ": authType: " + authType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
|
||||
Log.i(TAG, ": authType: " + authType);
|
||||
}
|
||||
}};
|
||||
private AtomicInteger RPC_ID = new AtomicInteger(0);
|
||||
private AtomicInteger ID_PING = new AtomicInteger(-1);
|
||||
private AtomicInteger ID_JOINROOM = new AtomicInteger(-1);
|
||||
private AtomicInteger ID_LEAVEROOM = new AtomicInteger(-1);
|
||||
private AtomicInteger ID_PUBLISHVIDEO = new AtomicInteger(-1);
|
||||
private Map<Integer, Pair<String, String>> IDS_PREPARERECEIVEVIDEO = new ConcurrentHashMap<>();
|
||||
private Map<Integer, String> IDS_RECEIVEVIDEO = new ConcurrentHashMap<>();
|
||||
private Set<Integer> IDS_ONICECANDIDATE = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private Session session;
|
||||
private String mediaServer;
|
||||
private SessionActivity activity;
|
||||
private WebSocket websocket;
|
||||
private boolean websocketCancelled = false;
|
||||
|
||||
public CustomWebSocket(Session session, SessionActivity activity) {
|
||||
this.session = session;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextMessage(WebSocket websocket, String text) throws Exception {
|
||||
Log.i(TAG, "Text Message " + text);
|
||||
JSONObject json = new JSONObject(text);
|
||||
if (json.has(JsonConstants.RESULT)) {
|
||||
handleServerResponse(json);
|
||||
} else if (json.has(JsonConstants.ERROR)) {
|
||||
handleServerError(json);
|
||||
} else {
|
||||
handleServerEvent(json);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleServerResponse(JSONObject json) throws
|
||||
JSONException {
|
||||
final int rpcId = json.getInt(JsonConstants.ID);
|
||||
JSONObject result = new JSONObject(json.getString(JsonConstants.RESULT));
|
||||
|
||||
if (result.has("value") && result.getString("value").equals("pong")) {
|
||||
// Response to ping
|
||||
Log.i(TAG, "pong");
|
||||
|
||||
} else if (rpcId == this.ID_JOINROOM.get()) {
|
||||
// Response to joinRoom
|
||||
activity.viewToConnectedState();
|
||||
|
||||
final LocalParticipant localParticipant = this.session.getLocalParticipant();
|
||||
final String localConnectionId = result.getString(JsonConstants.ID);
|
||||
localParticipant.setConnectionId(localConnectionId);
|
||||
|
||||
this.mediaServer = result.getString(JsonConstants.MEDIA_SERVER);
|
||||
|
||||
if (result.has(JsonConstants.ICE_SERVERS)) {
|
||||
final JSONArray jsonIceServers = result.getJSONArray(JsonConstants.ICE_SERVERS);
|
||||
List<IceServer> iceServers = new ArrayList();
|
||||
|
||||
for (int i = 0; i < jsonIceServers.length(); i++) {
|
||||
JSONObject jsonIceServer = jsonIceServers.getJSONObject(i);
|
||||
List<String> urls = new ArrayList();
|
||||
if (jsonIceServer.has("urls")) {
|
||||
final JSONArray jsonUrls = jsonIceServer.getJSONArray("urls");
|
||||
for (int j = 0; j < jsonUrls.length(); j++) {
|
||||
urls.add(jsonUrls.getString(j));
|
||||
}
|
||||
}
|
||||
if (jsonIceServer.has("url")) {
|
||||
urls.add(jsonIceServer.getString("url"));
|
||||
}
|
||||
|
||||
IceServer.Builder iceServerBuilder;
|
||||
try {
|
||||
iceServerBuilder = IceServer.builder(urls);
|
||||
} catch (IllegalArgumentException e) {
|
||||
continue;
|
||||
}
|
||||
if (jsonIceServer.has("username")) {
|
||||
iceServerBuilder.setUsername(jsonIceServer.getString("username"));
|
||||
}
|
||||
if (jsonIceServer.has("credential")) {
|
||||
iceServerBuilder.setPassword(jsonIceServer.getString("credential"));
|
||||
}
|
||||
iceServers.add(iceServerBuilder.createIceServer());
|
||||
}
|
||||
|
||||
session.setIceServers(iceServers);
|
||||
}
|
||||
|
||||
PeerConnection localPeerConnection = session.createLocalPeerConnection();
|
||||
|
||||
localParticipant.setPeerConnection(localPeerConnection);
|
||||
|
||||
MediaConstraints sdpConstraints = new MediaConstraints();
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "false"));
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "false"));
|
||||
session.createOfferForPublishing(sdpConstraints);
|
||||
|
||||
if (result.getJSONArray(JsonConstants.VALUE).length() > 0) {
|
||||
// There were users already connected to the session
|
||||
addRemoteParticipantsAlreadyInRoom(result);
|
||||
}
|
||||
|
||||
} else if (rpcId == this.ID_LEAVEROOM.get()) {
|
||||
// Response to leaveRoom
|
||||
if (websocket.isOpen()) {
|
||||
websocket.disconnect();
|
||||
}
|
||||
} else if (rpcId == this.ID_PUBLISHVIDEO.get()) {
|
||||
// Response to publishVideo
|
||||
LocalParticipant localParticipant = this.session.getLocalParticipant();
|
||||
SessionDescription remoteSdpAnswer = new SessionDescription(SessionDescription.Type.ANSWER, result.getString("sdpAnswer"));
|
||||
localParticipant.getPeerConnection().setRemoteDescription(new CustomSdpObserver("publishVideo_setRemoteDescription"), remoteSdpAnswer);
|
||||
} else if (this.IDS_PREPARERECEIVEVIDEO.containsKey(rpcId)) {
|
||||
// Response to prepareReceiveVideoFrom
|
||||
Pair<String, String> participantAndStream = IDS_PREPARERECEIVEVIDEO.remove(rpcId);
|
||||
RemoteParticipant remoteParticipant = session.getRemoteParticipant(participantAndStream.first);
|
||||
String streamId = participantAndStream.second;
|
||||
SessionDescription remoteSdpOffer = new SessionDescription(SessionDescription.Type.OFFER, result.getString("sdpOffer"));
|
||||
remoteParticipant.getPeerConnection().setRemoteDescription(new CustomSdpObserver("prepareReceiveVideoFrom_setRemoteDescription") {
|
||||
@Override
|
||||
public void onSetSuccess() {
|
||||
super.onSetSuccess();
|
||||
subscriptionInitiatedFromServer(remoteParticipant, streamId);
|
||||
}
|
||||
}, remoteSdpOffer);
|
||||
} else if (this.IDS_RECEIVEVIDEO.containsKey(rpcId)) {
|
||||
// Response to receiveVideoFrom
|
||||
String id = IDS_RECEIVEVIDEO.remove(rpcId);
|
||||
if ("kurento".equals(this.mediaServer)) {
|
||||
SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, result.getString("sdpAnswer"));
|
||||
session.getRemoteParticipant(id).getPeerConnection().setRemoteDescription(new CustomSdpObserver("remoteSetRemoteDesc"), sessionDescription);
|
||||
}
|
||||
} else if (this.IDS_ONICECANDIDATE.contains(rpcId)) {
|
||||
// Response to onIceCandidate
|
||||
IDS_ONICECANDIDATE.remove(rpcId);
|
||||
} else {
|
||||
Log.e(TAG, "Unrecognized server response: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleServerError(JSONObject json) throws JSONException {
|
||||
final JSONObject error = new JSONObject(json.getString(JsonConstants.ERROR));
|
||||
|
||||
final int errorCode = error.getInt("code");
|
||||
final String errorMessage = error.getString("message");
|
||||
|
||||
Log.e(TAG, "Server error code " + errorCode + ": " + errorMessage);
|
||||
}
|
||||
|
||||
public void joinRoom() {
|
||||
Map<String, String> joinRoomParams = new HashMap<>();
|
||||
joinRoomParams.put(JsonConstants.METADATA, "{\"clientData\": \"" + this.session.getLocalParticipant().getParticipantName() + "\"}");
|
||||
joinRoomParams.put("secret", "");
|
||||
joinRoomParams.put("session", this.session.getId());
|
||||
joinRoomParams.put("platform", "Android " + android.os.Build.VERSION.SDK_INT);
|
||||
joinRoomParams.put("token", this.session.getToken());
|
||||
joinRoomParams.put("sdkVersion", "2.27.0");
|
||||
this.ID_JOINROOM.set(this.sendJson(JsonConstants.JOINROOM_METHOD, joinRoomParams));
|
||||
}
|
||||
|
||||
public void leaveRoom() {
|
||||
this.ID_LEAVEROOM.set(this.sendJson(JsonConstants.LEAVEROOM_METHOD));
|
||||
}
|
||||
|
||||
public void publishVideo(SessionDescription sessionDescription) {
|
||||
Map<String, String> publishVideoParams = new HashMap<>();
|
||||
publishVideoParams.put("audioActive", "true");
|
||||
publishVideoParams.put("videoActive", "true");
|
||||
publishVideoParams.put("doLoopback", "false");
|
||||
publishVideoParams.put("frameRate", "30");
|
||||
publishVideoParams.put("hasAudio", "true");
|
||||
publishVideoParams.put("hasVideo", "true");
|
||||
publishVideoParams.put("typeOfVideo", "CAMERA");
|
||||
publishVideoParams.put("videoDimensions", "{\"width\":320, \"height\":240}");
|
||||
publishVideoParams.put("sdpOffer", sessionDescription.description);
|
||||
this.ID_PUBLISHVIDEO.set(this.sendJson(JsonConstants.PUBLISHVIDEO_METHOD, publishVideoParams));
|
||||
}
|
||||
|
||||
public void prepareReceiveVideoFrom(RemoteParticipant remoteParticipant, String streamId) {
|
||||
Map<String, String> prepareReceiveVideoFromParams = new HashMap<>();
|
||||
prepareReceiveVideoFromParams.put("sender", streamId);
|
||||
prepareReceiveVideoFromParams.put("reconnect", "false");
|
||||
this.IDS_PREPARERECEIVEVIDEO.put(this.sendJson(JsonConstants.PREPARERECEIVEVIDEO_METHOD, prepareReceiveVideoFromParams), new Pair<>(remoteParticipant.getConnectionId(), streamId));
|
||||
}
|
||||
|
||||
public void receiveVideoFrom(SessionDescription sessionDescription, RemoteParticipant remoteParticipant, String streamId) {
|
||||
Map<String, String> receiveVideoFromParams = new HashMap<>();
|
||||
receiveVideoFromParams.put("sender", streamId);
|
||||
if ("kurento".equals(this.mediaServer)) {
|
||||
receiveVideoFromParams.put("sdpOffer", sessionDescription.description);
|
||||
} else {
|
||||
receiveVideoFromParams.put("sdpAnswer", sessionDescription.description);
|
||||
}
|
||||
this.IDS_RECEIVEVIDEO.put(this.sendJson(JsonConstants.RECEIVEVIDEO_METHOD, receiveVideoFromParams), remoteParticipant.getConnectionId());
|
||||
}
|
||||
|
||||
public void onIceCandidate(IceCandidate iceCandidate, String endpointName) {
|
||||
Map<String, String> onIceCandidateParams = new HashMap<>();
|
||||
if (endpointName != null) {
|
||||
onIceCandidateParams.put("endpointName", endpointName);
|
||||
}
|
||||
onIceCandidateParams.put("candidate", iceCandidate.sdp);
|
||||
onIceCandidateParams.put("sdpMid", iceCandidate.sdpMid);
|
||||
onIceCandidateParams.put("sdpMLineIndex", Integer.toString(iceCandidate.sdpMLineIndex));
|
||||
this.IDS_ONICECANDIDATE.add(this.sendJson(JsonConstants.ONICECANDIDATE_METHOD, onIceCandidateParams));
|
||||
}
|
||||
|
||||
private void handleServerEvent(JSONObject json) throws JSONException {
|
||||
if (!json.has(JsonConstants.METHOD)) {
|
||||
Log.e(TAG, "Server event lacks a field '" + JsonConstants.METHOD + "'; JSON: "
|
||||
+ json.toString());
|
||||
return;
|
||||
}
|
||||
final String method = json.getString(JsonConstants.METHOD);
|
||||
|
||||
if (!json.has(JsonConstants.PARAMS)) {
|
||||
Log.e(TAG, "Server event '" + method + "' lacks a field '" + JsonConstants.PARAMS
|
||||
+ "'; JSON: " + json.toString());
|
||||
return;
|
||||
}
|
||||
final JSONObject params = new JSONObject(json.getString(JsonConstants.PARAMS));
|
||||
|
||||
switch (method) {
|
||||
case JsonConstants.ICE_CANDIDATE:
|
||||
iceCandidateEvent(params);
|
||||
break;
|
||||
case JsonConstants.PARTICIPANT_JOINED:
|
||||
participantJoinedEvent(params);
|
||||
break;
|
||||
case JsonConstants.PARTICIPANT_PUBLISHED:
|
||||
participantPublishedEvent(params);
|
||||
break;
|
||||
case JsonConstants.PARTICIPANT_LEFT:
|
||||
participantLeftEvent(params);
|
||||
break;
|
||||
default:
|
||||
throw new JSONException("Unknown server event '" + method + "'");
|
||||
}
|
||||
}
|
||||
|
||||
public int sendJson(String method) {
|
||||
return this.sendJson(method, new HashMap<>());
|
||||
}
|
||||
|
||||
public synchronized int sendJson(String method, Map<String, String> params) {
|
||||
final int id = RPC_ID.get();
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
try {
|
||||
JSONObject paramsJson = new JSONObject();
|
||||
for (Map.Entry<String, String> param : params.entrySet()) {
|
||||
paramsJson.put(param.getKey(), param.getValue());
|
||||
}
|
||||
jsonObject.put("jsonrpc", JsonConstants.JSON_RPCVERSION);
|
||||
jsonObject.put("method", method);
|
||||
jsonObject.put("id", id);
|
||||
jsonObject.put("params", paramsJson);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "JSONException raised on sendJson", e);
|
||||
return -1;
|
||||
}
|
||||
this.websocket.sendText(jsonObject.toString());
|
||||
RPC_ID.incrementAndGet();
|
||||
return id;
|
||||
}
|
||||
|
||||
private void addRemoteParticipantsAlreadyInRoom(JSONObject result) throws
|
||||
JSONException {
|
||||
for (int i = 0; i < result.getJSONArray(JsonConstants.VALUE).length(); i++) {
|
||||
JSONObject participantJson = result.getJSONArray(JsonConstants.VALUE).getJSONObject(i);
|
||||
RemoteParticipant remoteParticipant = this.newRemoteParticipantAux(participantJson);
|
||||
try {
|
||||
JSONArray streams = participantJson.getJSONArray("streams");
|
||||
for (int j = 0; j < streams.length(); j++) {
|
||||
JSONObject stream = streams.getJSONObject(0);
|
||||
String streamId = stream.getString("id");
|
||||
this.subscribe(remoteParticipant, streamId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//Sometimes when we enter in room the other participants have no stream
|
||||
//We catch that in this way the iteration of participants doesn't stop
|
||||
Log.e(TAG, "Error in addRemoteParticipantsAlreadyInRoom: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void iceCandidateEvent(JSONObject params) throws JSONException {
|
||||
IceCandidate iceCandidate = new IceCandidate(params.getString("sdpMid"), params.getInt("sdpMLineIndex"), params.getString("candidate"));
|
||||
final String connectionId = params.getString("senderConnectionId");
|
||||
boolean isRemote = !session.getLocalParticipant().getConnectionId().equals(connectionId);
|
||||
final Participant participant = isRemote ? session.getRemoteParticipant(connectionId) : session.getLocalParticipant();
|
||||
final PeerConnection pc = participant.getPeerConnection();
|
||||
|
||||
switch (pc.signalingState()) {
|
||||
case CLOSED:
|
||||
Log.e("saveIceCandidate error", "PeerConnection object is closed");
|
||||
break;
|
||||
case STABLE:
|
||||
if (pc.getRemoteDescription() != null) {
|
||||
participant.getPeerConnection().addIceCandidate(iceCandidate);
|
||||
} else {
|
||||
participant.getIceCandidateList().add(iceCandidate);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
participant.getIceCandidateList().add(iceCandidate);
|
||||
}
|
||||
}
|
||||
|
||||
private void participantJoinedEvent(JSONObject params) throws JSONException {
|
||||
this.newRemoteParticipantAux(params);
|
||||
}
|
||||
|
||||
private void participantPublishedEvent(JSONObject params) throws
|
||||
JSONException {
|
||||
String remoteParticipantId = params.getString(JsonConstants.ID);
|
||||
final RemoteParticipant remoteParticipant = this.session.getRemoteParticipant(remoteParticipantId);
|
||||
final String streamId = params.getJSONArray("streams").getJSONObject(0).getString("id");
|
||||
this.subscribe(remoteParticipant, streamId);
|
||||
}
|
||||
|
||||
private void participantLeftEvent(JSONObject params) throws JSONException {
|
||||
final RemoteParticipant remoteParticipant = this.session.removeRemoteParticipant(params.getString("connectionId"));
|
||||
remoteParticipant.dispose();
|
||||
Handler mainHandler = new Handler(activity.getMainLooper());
|
||||
Runnable myRunnable = () -> session.removeView(remoteParticipant.getView());
|
||||
mainHandler.post(myRunnable);
|
||||
}
|
||||
|
||||
private RemoteParticipant newRemoteParticipantAux(JSONObject participantJson) throws JSONException {
|
||||
final String connectionId = participantJson.getString(JsonConstants.ID);
|
||||
String participantName = "";
|
||||
if (participantJson.getString(JsonConstants.METADATA) != null) {
|
||||
String jsonStringified = participantJson.getString(JsonConstants.METADATA);
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonStringified);
|
||||
String clientData = json.getString("clientData");
|
||||
if (clientData != null) {
|
||||
participantName = clientData;
|
||||
}
|
||||
} catch(JSONException e) {
|
||||
participantName = jsonStringified;
|
||||
}
|
||||
}
|
||||
final RemoteParticipant remoteParticipant = new RemoteParticipant(connectionId, participantName, this.session);
|
||||
this.activity.createRemoteParticipantVideo(remoteParticipant);
|
||||
this.session.createRemotePeerConnection(remoteParticipant.getConnectionId());
|
||||
return remoteParticipant;
|
||||
}
|
||||
|
||||
private void subscribe(RemoteParticipant remoteParticipant, String streamId) {
|
||||
if ("kurento".equals(this.mediaServer)) {
|
||||
this.subscriptionInitiatedFromClient(remoteParticipant, streamId);
|
||||
} else {
|
||||
this.prepareReceiveVideoFrom(remoteParticipant, streamId);
|
||||
}
|
||||
}
|
||||
|
||||
private void subscriptionInitiatedFromClient(RemoteParticipant remoteParticipant, String streamId) {
|
||||
MediaConstraints sdpConstraints = new MediaConstraints();
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
|
||||
|
||||
remoteParticipant.getPeerConnection().createOffer(new CustomSdpObserver("remote offer sdp") {
|
||||
@Override
|
||||
public void onCreateSuccess(SessionDescription sdp) {
|
||||
super.onCreateSuccess(sdp);
|
||||
remoteParticipant.getPeerConnection().setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc") {
|
||||
@Override
|
||||
public void onSetSuccess() {
|
||||
super.onSetSuccess();
|
||||
receiveVideoFrom(sdp, remoteParticipant, streamId);
|
||||
}
|
||||
}, sdp);
|
||||
}
|
||||
}, sdpConstraints);
|
||||
}
|
||||
|
||||
private void subscriptionInitiatedFromServer(RemoteParticipant remoteParticipant, String streamId) {
|
||||
MediaConstraints sdpConstraints = new MediaConstraints();
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
|
||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
|
||||
this.session.createAnswerForSubscribing(remoteParticipant, streamId, sdpConstraints);
|
||||
}
|
||||
|
||||
public void setWebsocketCancelled(boolean websocketCancelled) {
|
||||
this.websocketCancelled = websocketCancelled;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
this.websocket.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(WebSocket websocket, WebSocketState newState) throws Exception {
|
||||
Log.i(TAG, "State changed: " + newState.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(WebSocket ws, Map<String, List<String>> headers) throws
|
||||
Exception {
|
||||
Log.i(TAG, "Connected");
|
||||
pingMessageHandler();
|
||||
this.joinRoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectError(WebSocket websocket, WebSocketException cause) throws Exception {
|
||||
Log.e(TAG, "Connect error: " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(WebSocket websocket, WebSocketFrame
|
||||
serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception {
|
||||
Log.e(TAG, "Disconnected " + serverCloseFrame.getCloseReason() + " " + clientCloseFrame.getCloseReason() + " " + closedByServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContinuationFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Continuation Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Text Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBinaryFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Binary Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Close Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Ping Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPongFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Pong Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextMessage(WebSocket websocket, byte[] data) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBinaryMessage(WebSocket websocket, byte[] binary) throws Exception {
|
||||
Log.i(TAG, "Binary Message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendingFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Sending Frame");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Frame sent");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameUnsent(WebSocket websocket, WebSocketFrame frame) throws Exception {
|
||||
Log.i(TAG, "Frame unsent");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThreadCreated(WebSocket websocket, ThreadType threadType, Thread thread) throws
|
||||
Exception {
|
||||
Log.i(TAG, "Thread created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThreadStarted(WebSocket websocket, ThreadType threadType, Thread thread) throws
|
||||
Exception {
|
||||
Log.i(TAG, "Thread started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThreadStopping(WebSocket websocket, ThreadType threadType, Thread thread) throws
|
||||
Exception {
|
||||
Log.i(TAG, "Thread stopping");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
|
||||
Log.e(TAG, "Error!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameError(WebSocket websocket, WebSocketException cause, WebSocketFrame
|
||||
frame) throws Exception {
|
||||
Log.e(TAG, "Frame error!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageError(WebSocket websocket, WebSocketException
|
||||
cause, List<WebSocketFrame> frames) throws Exception {
|
||||
Log.e(TAG, "Message error! " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageDecompressionError(WebSocket websocket, WebSocketException cause,
|
||||
byte[] compressed) throws Exception {
|
||||
Log.e(TAG, "Message decompression error!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextMessageError(WebSocket websocket, WebSocketException cause, byte[] data) throws
|
||||
Exception {
|
||||
Log.e(TAG, "Text message error! " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendError(WebSocket websocket, WebSocketException cause, WebSocketFrame frame) throws
|
||||
Exception {
|
||||
Log.e(TAG, "Send error! " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnexpectedError(WebSocket websocket, WebSocketException cause) throws
|
||||
Exception {
|
||||
Log.e(TAG, "Unexpected error! " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
|
||||
Log.e(TAG, "Handle callback error! " + cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendingHandshake(WebSocket websocket, String requestLine, List<String[]>
|
||||
headers) throws Exception {
|
||||
Log.i(TAG, "Sending Handshake! Hello!");
|
||||
}
|
||||
|
||||
private void pingMessageHandler() {
|
||||
long initialDelay = 0L;
|
||||
ScheduledThreadPoolExecutor executor =
|
||||
new ScheduledThreadPoolExecutor(1);
|
||||
executor.scheduleWithFixedDelay(() -> {
|
||||
Map<String, String> pingParams = new HashMap<>();
|
||||
if (ID_PING.get() == -1) {
|
||||
// First ping call
|
||||
pingParams.put("interval", "5000");
|
||||
}
|
||||
ID_PING.set(sendJson(JsonConstants.PING_METHOD, pingParams));
|
||||
}, initialDelay, PING_MESSAGE_INTERVAL, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private String getWebSocketAddress() {
|
||||
String wsUri;
|
||||
try {
|
||||
URI url = new URI(this.session.getToken());
|
||||
if (url.getPort() > -1) {
|
||||
wsUri = url.getScheme() + "://" + url.getHost() + ":" + url.getPort() + "/openvidu";
|
||||
} else {
|
||||
wsUri = url.getScheme() + "://" + url.getHost() + "/openvidu";
|
||||
}
|
||||
return wsUri;
|
||||
} catch (URISyntaxException e) {
|
||||
Log.e(TAG, "Wrong URL", e);
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(SessionActivity... sessionActivities) {
|
||||
try {
|
||||
WebSocketFactory factory = new WebSocketFactory();
|
||||
|
||||
//Returns a SSLContext object that implements the specified secure socket protocol
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustManagers, new java.security.SecureRandom());
|
||||
factory.setSSLContext(sslContext);
|
||||
|
||||
// Set the flag which indicates whether the hostname in the server's certificate should be verified or not.
|
||||
factory.setVerifyHostname(false);
|
||||
|
||||
// Connecting the websocket to OpenVidu URL
|
||||
websocket = factory.createSocket(getWebSocketAddress());
|
||||
websocket.addListener(this);
|
||||
websocket.connect();
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException | IOException | WebSocketException e) {
|
||||
Log.e("WebSocket error", e.getMessage());
|
||||
Handler mainHandler = new Handler(activity.getMainLooper());
|
||||
Runnable myRunnable = () -> {
|
||||
Toast toast = Toast.makeText(activity, e.getMessage(), Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
activity.leaveSession();
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
websocketCancelled = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Void... progress) {
|
||||
Log.i(TAG, "PROGRESS " + Arrays.toString(progress));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@ -1,128 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="io.openvidu.openvidu_android.activities.SessionActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="150dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/views_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/peer_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="212dp"
|
||||
android:layout_marginBottom="5dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_participant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorAccent" />
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
android:id="@+id/local_gl_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="212dp"
|
||||
android:visibility="visible" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/relativeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="#ffffff"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout2"
|
||||
app:layout_constraintVertical_bias="1.0">
|
||||
|
||||
<Button
|
||||
android:id="@+id/start_finish_call"
|
||||
style="@android:style/Widget.Material.Button.Borderless.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:onClick="buttonPressed"
|
||||
android:text="@string/start_button" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:gravity="end"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/application_server_url"
|
||||
style="@android:style/Widget.Material.Button.Borderless.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/application_server_url"
|
||||
android:inputType="text"
|
||||
android:text="@string/application_server_url" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/session_name"
|
||||
style="@android:style/Widget.Material.Button.Borderless.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/session_name"
|
||||
android:inputType="text"
|
||||
android:text="@string/default_session_name" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/participant_name"
|
||||
style="@android:style/Widget.Material.Button.Borderless.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/participant_name"
|
||||
android:inputType="text"
|
||||
android:text="@string/default_participant_name" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/peer_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="212dp">
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
android:id="@+id/local_gl_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="210dp"
|
||||
android:visibility="visible">
|
||||
</org.webrtc.SurfaceViewRenderer>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_participant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorAccent"/>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
||||
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
@ -1,18 +0,0 @@
|
||||
<resources>
|
||||
<!-- WARNING: this application_server_url is not secure and is only meant for a first quick test.
|
||||
Anyone could access your video sessions. You should modify the application_server_url to a custom private one -->
|
||||
<string name="application_server_url">https://demos.openvidu.io/</string>
|
||||
<string name="app_name">OpenVidu Android Sample</string>
|
||||
<string name="start_button">Join</string>
|
||||
<string name="session_name">Session Name</string>
|
||||
<string name="default_session_name">SessionA</string>
|
||||
<string name="participant_name">Participant Name</string>
|
||||
<string name="default_participant_name">Participant</string>
|
||||
<string name="hang_up">Leave session</string>
|
||||
<string name="no_connection">COULD NOT ESTABLISH THE CONNECTION, TRY AGAIN </string>
|
||||
<string name="no_permissions_granted">We can not give you service without your permission</string>
|
||||
<string name="permissions_dialog_title">We need your help</string>
|
||||
<string name="accept_permissions_dialog">GIVE PERMISSIONS!</string>
|
||||
<string name="cancel_dialog">CANCEL</string>
|
||||
<string name="no_internet_connection">NO INTERNET CONNECTION, PLEASE CHECK YOUR CONNECTION</string>
|
||||
</resources>
|
||||
@ -1,11 +0,0 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -1,17 +0,0 @@
|
||||
package io.openvidu.openvidu_android;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
BIN
openvidu-android/gradle/wrapper/gradle-wrapper.jar
vendored
@ -1,6 +0,0 @@
|
||||
#Thu Mar 25 18:51:34 CET 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||
172
openvidu-android/gradlew
vendored
@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
openvidu-android/gradlew.bat
vendored
@ -1,84 +0,0 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@ -1,2 +0,0 @@
|
||||
include ':app'
|
||||
rootProject.name='OpenVidu Android'
|
||||
37
openvidu-basic-dotnet/.gitignore
vendored
@ -1,37 +0,0 @@
|
||||
*.swp
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# Rider
|
||||
.idea
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
|
||||
# Visual Studio 2015
|
||||
.vs/
|
||||
@ -1,85 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
|
||||
|
||||
IConfiguration config = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables().Build();
|
||||
|
||||
// Load env variables
|
||||
var SERVER_PORT = config.GetValue<int>("SERVER_PORT");
|
||||
var OPENVIDU_URL = config.GetValue<string>("OPENVIDU_URL");
|
||||
var OPENVIDU_SECRET = config.GetValue<string>("OPENVIDU_SECRET");
|
||||
|
||||
// Enable CORS support
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(name: MyAllowSpecificOrigins,
|
||||
builder =>
|
||||
{
|
||||
builder.WithOrigins("*").AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
builder.WebHost.UseKestrel(serverOptions => {
|
||||
serverOptions.ListenAnyIP(SERVER_PORT);
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
app.UseCors(MyAllowSpecificOrigins);
|
||||
|
||||
|
||||
// Allow for insecure certificate in OpenVidu deployment
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
HttpClient client = new HttpClient(handler);
|
||||
client.BaseAddress = new System.Uri(OPENVIDU_URL);
|
||||
|
||||
// Set OpenVidu deployment secret
|
||||
var basicAuth = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes($"OPENVIDUAPP:{OPENVIDU_SECRET}"));
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", basicAuth);
|
||||
|
||||
app.MapPost("/api/sessions", async (HttpRequest request) =>
|
||||
{
|
||||
String contentString;
|
||||
HttpContent content;
|
||||
using (var streamContent = new StreamContent(request.Body)) {
|
||||
contentString = await streamContent.ReadAsStringAsync();
|
||||
content = new StringContent(contentString, Encoding.UTF8, "application/json");
|
||||
}
|
||||
HttpResponseMessage response = await client.PostAsync("openvidu/api/sessions", content);
|
||||
if (response.StatusCode == HttpStatusCode.Conflict) {
|
||||
// Session already exists in OpenVidu
|
||||
var bodyRequest = JsonSerializer.Deserialize<Dictionary<string, object>>(contentString);
|
||||
return bodyRequest["customSessionId"];
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<Dictionary<string, object>>();
|
||||
var sessionId = responseBody["sessionId"].ToString().Trim('"');
|
||||
return sessionId;
|
||||
});
|
||||
|
||||
app.MapPost("/api/sessions/{sessionId}/connections", async (HttpRequest request, [FromRoute] string sessionId) =>
|
||||
{
|
||||
HttpContent content;
|
||||
using (var streamContent = new StreamContent(request.Body)) {
|
||||
var contentString = await streamContent.ReadAsStringAsync();
|
||||
content = new StringContent(contentString, Encoding.UTF8, "application/json");
|
||||
}
|
||||
HttpResponseMessage response = await client.PostAsync("openvidu/api/sessions/" + sessionId.Trim('"') + "/connection", content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseBody = await response.Content.ReadFromJsonAsync<Dictionary<string, object>>();
|
||||
var token = responseBody["token"].ToString().Trim('"');
|
||||
return token;
|
||||
});
|
||||
|
||||
app.Run();
|
||||
@ -1,31 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"sslPort": 5000
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"openvidu_basic_dotnet": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://0.0.0.0:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
# openvidu-basic-dotnet
|
||||
|
||||
This is a minimal OpenVidu server application sample built for .NET. Visit [Application server](https://docs.openvidu.io/en/stable/application-server/) documentation for further context.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [.NET 6.0](https://dotnet.microsoft.com/en-us/download)
|
||||
|
||||
## Run
|
||||
|
||||
Download repository
|
||||
|
||||
```
|
||||
git clone git@github.com:OpenVidu/openvidu-tutorials.git
|
||||
cd openvidu-tutorials/openvidu-basic-dotnet
|
||||
```
|
||||
|
||||
Run the application
|
||||
|
||||
```
|
||||
dotnet run
|
||||
```
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"SERVER_PORT": 5000,
|
||||
"OPENVIDU_URL": "http://localhost:4443/",
|
||||
"OPENVIDU_SECRET": "MY_SECRET"
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>openvidu_basic_dotnet</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,3 +0,0 @@
|
||||
# openvidu-basic-php
|
||||
|
||||
Coming soon...
|
||||
28
openvidu-fault-tolerance/.gitignore
vendored
@ -1,28 +0,0 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
target/*
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
@ -1,53 +0,0 @@
|
||||
# openvidu-fault-tolerance
|
||||
|
||||
This project exemplifies the fault tolerance capabilities of an application making use of an OpenVidu cluster, whether it is an OpenVidu Pro cluster or an OpenVidu Enterprise cluster. It demonstrates how to automatically rebuild any Session affected by a node crash, so final users do not have to perform any action to reconnect to a crashed Session.
|
||||
|
||||
## Compile and run the app
|
||||
|
||||
This is a SpringBoot application. Prerequisites:
|
||||
|
||||
| Dependency | Check version | Install |
|
||||
| ------------- | --------------- |---------------------------------------- |
|
||||
| Java 11 JDK | `java -version` | `sudo apt-get install -y openjdk-11-jdk` |
|
||||
| Maven | `mvn -v` | `sudo apt-get install -y maven` |
|
||||
|
||||
To compile and run the app:
|
||||
|
||||
```
|
||||
git clone git@github.com:OpenVidu/openvidu-tutorials.git
|
||||
cd openvidu-tutorials/openvidu-fault-tolerance
|
||||
mvn clean package
|
||||
java -jar target/openvidu-fault-tolerance*.jar --openvidu.url=OPENVIDU_PRO_DOMAIN --openvidu.secret=OPENVIDU_SECRET
|
||||
```
|
||||
|
||||
### Example
|
||||
- `OPENVIDU_PRO_DOMAIN` = `https://example-openvidu.io`
|
||||
- `OPENVIDU_SECRET` = `MY_SECRET`
|
||||
```
|
||||
git clone git@github.com:OpenVidu/openvidu-tutorials.git
|
||||
cd openvidu-tutorials/openvidu-fault-tolerance
|
||||
mvn clean package
|
||||
java -jar target/openvidu-fault-tolerance*.jar --openvidu.url=https://example-openvidu.io --openvidu.secret=MY_SECRET
|
||||
```
|
||||
|
||||
## Test the reconnection capabilities
|
||||
|
||||
### Media Node failure
|
||||
|
||||
A Session hosted in a Media Node suffering a crash will be automatically re-created and re-located in a different Media Node, without intervention of the final user. For this to work, the OpenVidu cluster must have at least 2 running Media Nodes. To test the reconnection capabilities of the application:
|
||||
|
||||
1. Make sure your OpenVidu cluster has at least 2 different Media Nodes.
|
||||
2. Connect 2 different users to the same session. They should both send and receive each other's video.
|
||||
3. Find out in which Media Node the session was located. You can call REST API method [GET Media Nodes](https://docs.openvidu.io/en/stable/reference-docs/REST-API/#get-all-medianodes) to do so.
|
||||
4. Terminate the Media Node hosting the session.
|
||||
5. After 3~4 seconds both users will automatically re-join the same session, successfully re-establishing the video streams.
|
||||
|
||||
### Master Node failure (OpenVidu Enterprise HA)
|
||||
|
||||
A session managed by a Master Node suffering a crash will be automatically re-created and re-located in a different Master Node, without intervention of the final user. For this to work, the OpenVidu Enterprise HA cluster must have at least 2 running Master Nodes. To test the reconnection capabilities of the application:
|
||||
|
||||
1. Make sure your OpenVidu cluster has at least 2 different Master Nodes.
|
||||
2. Connect 2 different users to the same session. They should both send and receive each other's video.
|
||||
3. Find out in which Master Node the session was located. To do so you will need to consume REST API method [GET Sessions](https://docs.openvidu.io/en/stable/reference-docs/REST-API/#reference-docs/REST-API/#get-all-sessions) directly from inside the Master Node machine. Connect to one of them and consume the REST API using directly openvidu-server-pro URI (`http://localhost:5443`), to skip the proxy that unifies the response from every Master Node. For example with cURL: `curl -X GET http://localhost:5443/openvidu/api/sessions -u OPENVIDUAPP:<YOUR_SECRET>`. The Master Node that returns a non-empty response is the one hosting the session.
|
||||
4. Terminate the Master Node hosting the session.
|
||||
5. Users will detect the crash and will rejoin the session automatically, successfully re-establishing the video streams. A very short amount of time will elapse from the detection of the crash and the re-joining to the session.
|
||||
@ -1,51 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-fault-tolerance</artifactId>
|
||||
<version>2.27.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>openvidu-fault-tolerance</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.4</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<start-class>io.openvidu.fault.tolerance.App</start-class>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-java-client</artifactId>
|
||||
<version>2.27.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1,13 +0,0 @@
|
||||
package io.openvidu.fault.tolerance;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class App
|
||||
{
|
||||
public static void main( String[] args )
|
||||
{
|
||||
SpringApplication.run(App.class, args);
|
||||
}
|
||||
}
|
||||
@ -1,126 +0,0 @@
|
||||
package io.openvidu.fault.tolerance;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.java.client.OpenVidu;
|
||||
import io.openvidu.java.client.OpenViduHttpException;
|
||||
import io.openvidu.java.client.OpenViduJavaClientException;
|
||||
import io.openvidu.java.client.Session;
|
||||
import io.openvidu.java.client.SessionProperties;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class MyRestController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MyRestController.class);
|
||||
|
||||
// URL where our OpenVidu server is listening
|
||||
private String OPENVIDU_URL;
|
||||
// Secret shared with our OpenVidu server
|
||||
private String SECRET;
|
||||
|
||||
// OpenVidu object as entrypoint of the SDK
|
||||
private OpenVidu openVidu;
|
||||
|
||||
public MyRestController(@Value("${openvidu.secret}") String secret, @Value("${openvidu.url}") String openviduUrl) {
|
||||
this.SECRET = secret;
|
||||
this.OPENVIDU_URL = openviduUrl;
|
||||
this.openVidu = new OpenVidu(OPENVIDU_URL, SECRET);
|
||||
log.info("Connecting to OpenVidu Pro Multi Master cluster at {}", OPENVIDU_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates a Connection for an existing or new Session, and returns
|
||||
* the Connection's token to the client side. It also handles the petition to
|
||||
* reconnect to a crashed session, as the process is exactly the same
|
||||
*/
|
||||
@RequestMapping(value = "/get-token", method = RequestMethod.POST)
|
||||
public ResponseEntity<?> getToken(@RequestBody Map<String, Object> params) {
|
||||
|
||||
log.info("Getting token | {sessionId}={}", params);
|
||||
|
||||
// The Session to connect
|
||||
String sessionId = (String) params.get("sessionId");
|
||||
|
||||
SessionProperties props = new SessionProperties.Builder().customSessionId(sessionId).build();
|
||||
Session session = null;
|
||||
try {
|
||||
session = this.openVidu.createSession(props);
|
||||
} catch (OpenViduHttpException e) {
|
||||
if (e.getStatus() == 502 || e.getStatus() == 503 || e.getStatus() == 504) {
|
||||
log.warn("The node handling the createSession operation is crashed ({}: {}). Retry", e.getStatus(),
|
||||
e.getMessage());
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e1) {
|
||||
}
|
||||
return getToken(params);
|
||||
} else {
|
||||
log.error("Unexpected error while creating session: {}", e.getMessage());
|
||||
return getErrorResponse(e);
|
||||
}
|
||||
} catch (OpenViduJavaClientException e) {
|
||||
log.error("Unexpected internal error while creating session. {}: {}", e.getClass().getCanonicalName(),
|
||||
e.getMessage());
|
||||
return getErrorResponse(e);
|
||||
}
|
||||
return returnToken(session);
|
||||
}
|
||||
|
||||
private ResponseEntity<?> returnToken(Session session) {
|
||||
try {
|
||||
String token = session.createConnection().getToken();
|
||||
|
||||
// Send the response with the token
|
||||
JsonObject responseJson = new JsonObject();
|
||||
responseJson.addProperty("token", token);
|
||||
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
||||
|
||||
} catch (OpenViduJavaClientException e1) {
|
||||
// If internal error generate an error message and return it to client
|
||||
log.error("Unexpected internal error while creating connection: {}", e1.getMessage());
|
||||
return getErrorResponse(e1);
|
||||
} catch (OpenViduHttpException e2) {
|
||||
if (404 == e2.getStatus()) {
|
||||
// The session wasn't found in OpenVidu Server
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (e2.getStatus() == 502 || e2.getStatus() == 503 || e2.getStatus() == 504) {
|
||||
log.warn("The node handling the createConnection operation is crashed ({}: {}). Retry", e2.getStatus(),
|
||||
e2.getMessage());
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e1) {
|
||||
}
|
||||
return returnToken(session);
|
||||
}
|
||||
return getErrorResponse(e2);
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity<JsonObject> getErrorResponse(Exception e) {
|
||||
JsonObject json = new JsonObject();
|
||||
if (e.getCause() != null) {
|
||||
json.addProperty("cause", e.getCause().toString());
|
||||
}
|
||||
if (e.getStackTrace() != null) {
|
||||
json.addProperty("stacktrace", e.getStackTrace().toString());
|
||||
}
|
||||
json.addProperty("error", e.getMessage());
|
||||
json.addProperty("exception", e.getClass().getCanonicalName());
|
||||
return new ResponseEntity<>(json, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
server.port: 5000
|
||||
server.ssl.enabled: true
|
||||
server.ssl.key-store: classpath:openvidu-selfsigned.jks
|
||||
server.ssl.key-store-password: openvidu
|
||||
server.ssl.key-store-type: JKS
|
||||
server.ssl.key-alias: openvidu-selfsigned
|
||||
spring.mvc.converters.preferred-json-mapper: gson
|
||||
|
||||
openvidu.url: https://localhost:4443/
|
||||
openvidu.secret: MY_SECRET
|
||||
@ -1,245 +0,0 @@
|
||||
var OV;
|
||||
var session;
|
||||
|
||||
var sessionId;
|
||||
var numVideos;
|
||||
|
||||
|
||||
/* OPENVIDU METHODS */
|
||||
|
||||
async function joinSession() {
|
||||
let token = await getToken();
|
||||
await connectToSessionWithToken(token);
|
||||
}
|
||||
|
||||
function leaveSession() {
|
||||
session.disconnect();
|
||||
}
|
||||
|
||||
async function connectToSessionWithToken(token) {
|
||||
|
||||
numVideos = 0;
|
||||
|
||||
OV = new OpenVidu();
|
||||
|
||||
session = OV.initSession();
|
||||
|
||||
session.on('connectionCreated', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
session.on('connectionDestroyed', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
session.on('streamCreated', event => {
|
||||
pushEvent(event);
|
||||
|
||||
// Subscribe to the Stream to receive it
|
||||
// HTML video will be appended to element with 'video-container' id
|
||||
var subscriber = session.subscribe(event.stream, 'video-container');
|
||||
|
||||
// When the HTML video has been appended to DOM...
|
||||
subscriber.on('videoElementCreated', event => {
|
||||
pushEvent(event);
|
||||
// Add a new HTML element for the user's name and nickname over its video
|
||||
updateNumVideos(1);
|
||||
});
|
||||
|
||||
// When the HTML video has been appended to DOM...
|
||||
subscriber.on('videoElementDestroyed', event => {
|
||||
pushEvent(event);
|
||||
// Add a new HTML element for the user's name and nickname over its video
|
||||
updateNumVideos(-1);
|
||||
});
|
||||
|
||||
// When the subscriber stream has started playing media...
|
||||
subscriber.on('streamPlaying', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
});
|
||||
|
||||
session.on('streamDestroyed', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
session.on('sessionDisconnected', event => {
|
||||
pushEvent(event);
|
||||
session = null;
|
||||
numVideos = 0;
|
||||
if (event.reason === 'nodeCrashed') {
|
||||
console.warn('Node has crashed!');
|
||||
$('#reconnectionModal').modal('show');
|
||||
joinSession();
|
||||
} else {
|
||||
$('#join').show();
|
||||
$('#session').hide();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
await session.connect(token);
|
||||
|
||||
// Set page layout for active call
|
||||
|
||||
$('#session-title').text(sessionId);
|
||||
$('#join').hide();
|
||||
$('#session').show();
|
||||
$('#reconnectionModal').modal('hide');
|
||||
|
||||
// Get your own camera stream
|
||||
|
||||
var publisher = OV.initPublisher('video-container', {
|
||||
audioSource: undefined, // The source of audio. If undefined default microphone
|
||||
videoSource: undefined, // The source of video. If undefined default webcam
|
||||
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
|
||||
publishVideo: true, // Whether you want to start publishing with your video enabled or not
|
||||
resolution: '640x480', // The resolution of your video
|
||||
frameRate: 30, // The frame rate of your video
|
||||
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
|
||||
mirror: false // Whether to mirror your local video or not
|
||||
});
|
||||
|
||||
// Specify the actions when events take place in our publisher
|
||||
|
||||
// When the publisher stream has started playing media...
|
||||
publisher.on('accessAllowed', event => {
|
||||
pushEvent({
|
||||
type: 'accessAllowed'
|
||||
});
|
||||
});
|
||||
|
||||
publisher.on('accessDenied', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
publisher.on('accessDialogOpened', event => {
|
||||
pushEvent({
|
||||
type: 'accessDialogOpened'
|
||||
});
|
||||
});
|
||||
|
||||
publisher.on('accessDialogClosed', event => {
|
||||
pushEvent({
|
||||
type: 'accessDialogClosed'
|
||||
});
|
||||
});
|
||||
|
||||
// When the publisher stream has started playing media...
|
||||
publisher.on('streamCreated', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
// When our HTML video has been added to DOM...
|
||||
publisher.on('videoElementCreated', event => {
|
||||
pushEvent(event);
|
||||
updateNumVideos(1);
|
||||
$(event.element).prop('muted', true); // Mute local video
|
||||
});
|
||||
|
||||
// When the HTML video has been appended to DOM...
|
||||
publisher.on('videoElementDestroyed', event => {
|
||||
pushEvent(event);
|
||||
// Add a new HTML element for the user's name and nickname over its video
|
||||
updateNumVideos(-1);
|
||||
});
|
||||
|
||||
// When the publisher stream has started playing media...
|
||||
publisher.on('streamPlaying', event => {
|
||||
pushEvent(event);
|
||||
});
|
||||
|
||||
// Publish your stream
|
||||
|
||||
session.publish(publisher);
|
||||
|
||||
} catch (error) {
|
||||
console.warn('There was an error connecting to the session:', error.code, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* OPENVIDU METHODS */
|
||||
|
||||
|
||||
|
||||
/* APPLICATION REST METHODS */
|
||||
|
||||
async function getToken() {
|
||||
|
||||
sessionId = $("#sessionId").val(); // Video-call chosen by the user
|
||||
var mustRetry = true;
|
||||
|
||||
while (mustRetry) {
|
||||
try {
|
||||
const result = await $.ajax({
|
||||
url: 'api/get-token',
|
||||
type: 'POST',
|
||||
dataType: "json",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ sessionId })
|
||||
});
|
||||
console.log('Request of TOKEN gone WELL (TOKEN:' + result + ')');
|
||||
mustRetry = false;
|
||||
return result.token;
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
console.warn('The session was closed. Try again');
|
||||
} else {
|
||||
mustRetry = false;
|
||||
console.error('Unexpected error', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* APPLICATION REST METHODS */
|
||||
|
||||
|
||||
|
||||
/* APPLICATION BROWSER METHODS */
|
||||
|
||||
events = '';
|
||||
|
||||
window.onbeforeunload = function () { // Gracefully leave session
|
||||
if (session) {
|
||||
leaveSession();
|
||||
}
|
||||
}
|
||||
|
||||
function updateNumVideos(i) {
|
||||
numVideos += i;
|
||||
$('video').removeClass();
|
||||
switch (numVideos) {
|
||||
case 1:
|
||||
$('video').addClass('two');
|
||||
break;
|
||||
case 2:
|
||||
$('video').addClass('two');
|
||||
break;
|
||||
case 3:
|
||||
$('video').addClass('three');
|
||||
break;
|
||||
case 4:
|
||||
$('video').addClass('four');
|
||||
break;
|
||||
case 5:
|
||||
$('video').addClass('five');
|
||||
break;
|
||||
case 6:
|
||||
$('video').addClass('six');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function pushEvent(event) {
|
||||
events += (!events ? '' : '\n') + event.type;
|
||||
$('#textarea-events').text(events);
|
||||
}
|
||||
|
||||
function clearEventsTextarea() {
|
||||
$('#textarea-events').text('');
|
||||
events = '';
|
||||
}
|
||||
|
||||
/* APPLICATION BROWSER METHODS */
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
@ -1,121 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>openvidu-fault-tolerance</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
|
||||
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
|
||||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous">
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<!-- Bootstrap -->
|
||||
|
||||
<link rel="styleSheet" href="style.css" type="text/css" media="screen">
|
||||
<script src="openvidu-browser-2.27.0.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
html: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img class="demo-logo"
|
||||
src="images/openvidu_vert_white_bg_trans_cropped.png" />openvidu-fault-tolerance</a>
|
||||
<a class="navbar-brand nav-icon"
|
||||
href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-fault-tolerance"
|
||||
title="GitHub Repository" target="_blank">
|
||||
<i class="fa fa-github" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="navbar-brand nav-icon"
|
||||
href="https://docs.openvidu.io/en/stable/openvidu-pro/fault-tolerance/" title="Documentation"
|
||||
target="_blank">
|
||||
<i class="fa fa-book" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="main-container" class="container">
|
||||
<div id="join" class="vertical-center">
|
||||
<div id="img-div">
|
||||
<img src="images/openvidu_grey_bg_transp_cropped.png" />
|
||||
</div>
|
||||
<div id="join-dialog" class="jumbotron">
|
||||
<h1>Join a video session</h1>
|
||||
<form class="form-group" onsubmit="return false">
|
||||
<p>
|
||||
<label>Session</label>
|
||||
<input class="form-control" type="text" id="sessionId" value="SessionA" required>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<button class="btn btn-lg btn-success" id="join-btn" onclick="joinSession()">Join!</button>
|
||||
</p>
|
||||
</form>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="session" style="display: none">
|
||||
<div id="session-header">
|
||||
<h1 id="session-title"></h1>
|
||||
<input class="btn btn-sm btn-danger" type="button" id="buttonLeaveSession" onmouseup="leaveSession()"
|
||||
value="Leave session">
|
||||
</div>
|
||||
<div id="video-container" class="col-md-12"></div>
|
||||
<div id="recording-btns">
|
||||
<div class="textarea-container" id="textarea-events-container">
|
||||
<button type="button" class="btn btn-outline-secondary" id="clear-events-btn"
|
||||
onclick="clearEventsTextarea()">Clear</button>
|
||||
<span>OpenVidu events</span>
|
||||
<textarea id="textarea-events" readonly="true" class="form-control"
|
||||
name="textarea-events"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="text-muted">OpenVidu © 2017</div>
|
||||
<a href="http://www.openvidu.io/" target="_blank">
|
||||
<img class="openvidu-logo" src="images/openvidu_globe_bg_transp_cropped.png" />
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="modal fade" id="reconnectionModal" tabindex="-1" role="dialog" aria-labelledby="reconnectionModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="reconnectionModalLabel">Oops! Some problem detected. Reconnecting to the
|
||||
session...</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,483 +0,0 @@
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
nav {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
background-color: #4d4d4d !important;
|
||||
border-color: #4d4d4d !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
padding: 5px 15px 5px 15px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
nav i.fa {
|
||||
font-size: 40px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #a9a9a9 !important;
|
||||
}
|
||||
|
||||
nav i.fa:hover {
|
||||
color: #a9a9a9;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
padding-bottom: 80px;
|
||||
height: 100%;
|
||||
margin-top: -70px;
|
||||
}
|
||||
|
||||
.vertical-center {
|
||||
width: -webkit-fit-content;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.vertical-center#not-logged form {
|
||||
width: -moz-fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.vertical-center#not-logged table {
|
||||
width: -moz-fit-content;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.vertical-center table {
|
||||
margin-top: 3em !important;
|
||||
}
|
||||
|
||||
.horizontal-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
color: #0088aa;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #0088aa;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
|
||||
}
|
||||
|
||||
input.btn {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #06d362 !important;
|
||||
border-color: #06d362;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #1abd61 !important;
|
||||
border-color: #1abd61;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background-color: #0088aa !important;
|
||||
border-color: #0088aa;
|
||||
}
|
||||
|
||||
.btn-info:hover {
|
||||
background-color: #00708c !important;
|
||||
border-color: #00708c;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: #ffcc00 !important;
|
||||
border-color: #ffcc00;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
background-color: #eabb3a !important;
|
||||
border-color: #eabb3a;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.btn-warning:active {
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.btn-warning:focus {
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.btn-warning:active:focus {
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
|
||||
.footer .text-muted {
|
||||
margin: 20px 0;
|
||||
float: left;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.openvidu-logo {
|
||||
height: 35px;
|
||||
float: right;
|
||||
margin: 12px 0;
|
||||
-webkit-transition: all 0.1s ease-in-out;
|
||||
-moz-transition: all 0.1s ease-in-out;
|
||||
-o-transition: all 0.1s ease-in-out;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.openvidu-logo:hover {
|
||||
-webkit-filter: grayscale(0.5);
|
||||
filter: grayscale(0.5);
|
||||
}
|
||||
|
||||
.demo-logo {
|
||||
margin: 0;
|
||||
height: 22px;
|
||||
float: left;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
a:hover .demo-logo {
|
||||
-webkit-filter: brightness(0.7);
|
||||
filter: brightness(0.7);
|
||||
}
|
||||
|
||||
#join {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
#not-logged {
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
#join-dialog h1 {
|
||||
color: #4d4d4d;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#join-dialog label {
|
||||
color: #0088aa;
|
||||
}
|
||||
|
||||
#join-dialog input.btn {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#join-dialog hr {
|
||||
background: #4d4d4d;
|
||||
}
|
||||
|
||||
#img-div {
|
||||
text-align: center;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
#img-div img {
|
||||
height: 15%;
|
||||
}
|
||||
|
||||
#logged {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#join {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
#session-header {
|
||||
margin-bottom: 20px;
|
||||
height: 8%;
|
||||
margin-top: 70px;
|
||||
}
|
||||
|
||||
#session-header form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#session-header input.btn {
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#session-title {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#session-header .form-control {
|
||||
width: initial;
|
||||
float: right;
|
||||
margin: 18px 0px 0px 5px;
|
||||
}
|
||||
|
||||
#video-container {
|
||||
width: 100%;
|
||||
max-height: 42%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#video-container video.two {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
#video-container video.three {
|
||||
max-width: 33.33%;
|
||||
}
|
||||
|
||||
#video-container video.four {
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
#video-container video.five {
|
||||
max-width: 20%;
|
||||
}
|
||||
|
||||
#video-container video.six {
|
||||
max-width: 16.66%;
|
||||
}
|
||||
|
||||
#video-container div {
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
margin-left: calc(-50% + 15px);
|
||||
}
|
||||
|
||||
#video-container p {
|
||||
display: inline-block;
|
||||
background: #f8f8f8;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
color: #777777;
|
||||
font-weight: bold;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
#video-container p.userName {
|
||||
float: right;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 0px;
|
||||
font-weight: lighter;
|
||||
font-size: 12px;
|
||||
background: #777777;
|
||||
color: #f8f8f8;
|
||||
}
|
||||
|
||||
video {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
object-fit: scale-down;
|
||||
}
|
||||
|
||||
#session {
|
||||
height: 100%;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
#session img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
object-fit: contain;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
#session #video-container img {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 50%;
|
||||
cursor: pointer;
|
||||
object-fit: cover;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
table i {
|
||||
cursor: pointer;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
#tooltip-div {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#tooltip-div hr {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#login-info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#login-info form {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#login-info div {
|
||||
display: inline;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
#name-user {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#recording-btns {
|
||||
display: inline-block;
|
||||
padding-left: 15px;
|
||||
padding-top: 20px;
|
||||
width: 100%;
|
||||
height: 40%;
|
||||
}
|
||||
|
||||
#recording-btns .btns {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#recording-btns .btns .form-control {
|
||||
width: initial;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#recording-btns .btns form {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#recording-btns textarea {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.textarea-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 85%;
|
||||
margin-top: 20px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size: 13px !important;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#textarea-events-container {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.textarea-container button {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.textarea-container span {
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
right: 1px;
|
||||
padding: 3px;
|
||||
border-bottom-right-radius: 4px;
|
||||
z-index: 1;
|
||||
color: #a5a5a5;
|
||||
background-color: #ededee;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.textarea-container textarea {
|
||||
height: 100%;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.vertical-separator-bottom {
|
||||
width: 2px;
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
background-color: #cbcbcb;
|
||||
margin: 0 8px 0 8px;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
|
||||
.vertical-separator-top {
|
||||
width: 2px;
|
||||
height: 30px;
|
||||
background-color: #cbcbcb;
|
||||
margin: 20px 8px 0 15px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* xs ans md screen resolutions*/
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
#join {
|
||||
padding-top: inherit;
|
||||
}
|
||||
|
||||
#not-logged {
|
||||
padding-top: inherit;
|
||||
}
|
||||
|
||||
.container .navbar-header {
|
||||
margin-right: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
padding: 9px 8px 9px 8px;
|
||||
}
|
||||
|
||||
nav i.fa {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.vertical-center {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#img-div {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
#img-div img {
|
||||
height: 10%;
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
|
||||
[](https://docs.openvidu.io/en/stable/?badge=stable)
|
||||
[](https://hub.docker.com/r/openvidu/openvidu-server-kms)
|
||||
[](https://openvidu.discourse.group/)
|
||||
|
||||
[![][OpenViduLogo]](http://openvidu.io)
|
||||
|
||||
openvidu-filters
|
||||
===
|
||||
|
||||
Visit [docs.openvidu.io/en/stable/advanced-features/filters](http://docs.openvidu.io/en/stable/advanced-features/filters/)
|
||||
|
||||
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120
|
||||
|
||||
## Run this application
|
||||
|
||||
```bash
|
||||
# Launch OpenVidu Server
|
||||
docker run -p 4443:4443 --rm -e OPENVIDU_SECRET=MY_SECRET openvidu/openvidu-dev:2.22.0
|
||||
|
||||
# Clone and serve openvidu-filters application
|
||||
git clone https://github.com/OpenVidu/openvidu-tutorials.git
|
||||
cd openvidu-tutorials/openvidu-filters
|
||||
http-server web/
|
||||
```
|
||||
|
||||
You will need `http-server` npm package (`sudo npm install -g http-server`), and you will need to accept the insecure certificate at [https://localhost:4443](https://localhost:4443) once you launch openvidu-server-kms docker container.
|
||||
@ -1,17 +0,0 @@
|
||||
FROM node:16-alpine3.16
|
||||
|
||||
# Copy openvidu-basic-node
|
||||
COPY ./openvidu-basic-node /opt/openvidu-basic-node
|
||||
|
||||
# Install openvidu-basic-node dependencies
|
||||
RUN npm --prefix /opt/openvidu-basic-node install
|
||||
|
||||
# Copy static files to openvidu-basic-node
|
||||
RUN mkdir -p /opt/openvidu-basic-node/public
|
||||
COPY ./web /opt/openvidu-basic-node/public
|
||||
|
||||
WORKDIR /opt/openvidu-basic-node
|
||||
|
||||
COPY docker/entrypoint.sh .
|
||||
|
||||
ENTRYPOINT [ "./entrypoint.sh" ]
|
||||
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "No version argument provided. Usage: \"./create_image.sh <IMAGE_NAME>\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pushd ../
|
||||
|
||||
cp -r ../openvidu-basic-node .
|
||||
|
||||
trap 'rm -rf ./openvidu-basic-node' ERR
|
||||
|
||||
docker build --pull --no-cache --rm=true -f docker/Dockerfile -t "$1" .
|
||||
|
||||
rm -rf ./openvidu-basic-node
|
||||
@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -n "${OPENVIDU_APPLICATION_SERVER_URL}" ]; then
|
||||
# Replace OPENVIDU_APPLICATION_SERVER_URL at frontend app
|
||||
sed -i \
|
||||
"s|var APPLICATION_SERVER_URL = \"http://localhost:5000/\";|var APPLICATION_SERVER_URL = \"${OPENVIDU_APPLICATION_SERVER_URL}/\";|" \
|
||||
public/app.js
|
||||
else
|
||||
sed -i \
|
||||
"s|var APPLICATION_SERVER_URL = \"http://localhost:5000/\";|var APPLICATION_SERVER_URL = \"\";|" \
|
||||
public/app.js
|
||||
fi
|
||||
|
||||
exec node index.js "$*"
|
||||
@ -1,52 +0,0 @@
|
||||
events {
|
||||
worker_connections 512;
|
||||
}
|
||||
http {
|
||||
upstream openvidu-deployment {
|
||||
server host.docker.internal:4443;
|
||||
}
|
||||
upstream server-application {
|
||||
server host.docker.internal:5000;
|
||||
}
|
||||
upstream client-application {
|
||||
server host.docker.internal:8080;
|
||||
}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
ssl_certificate /etc/nginx/certs/cert.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/key.pem;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_headers_hash_bucket_size 512;
|
||||
proxy_redirect off;
|
||||
|
||||
# Websockets
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# OpenVidu deployment API
|
||||
location /openvidu/api {
|
||||
proxy_pass http://openvidu-deployment;
|
||||
}
|
||||
|
||||
# OpenVidu WebSocket
|
||||
location ~ /openvidu$ {
|
||||
proxy_pass http://openvidu-deployment;
|
||||
}
|
||||
|
||||
# Server application requests
|
||||
location /api/ {
|
||||
proxy_pass http://server-application;
|
||||
}
|
||||
|
||||
# Client application requests
|
||||
location / {
|
||||
proxy_pass http://client-application;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,419 +0,0 @@
|
||||
var OV;
|
||||
var session;
|
||||
var publisher;
|
||||
var role = 'PUBLISHER'; // ['SUBSCRIBER', 'PUBLISHER', 'MODERATOR']
|
||||
var selectedStreamManager; // Our Publisher or any Subscriber (see https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/StreamManager.html)
|
||||
|
||||
|
||||
/* OPENVIDU METHODS */
|
||||
|
||||
function joinSession() {
|
||||
|
||||
var mySessionId = $("#sessionId").val();
|
||||
var myUserName = $("#userName").val();
|
||||
var startWithFilterEnabled = $('#start-filter-enabled').prop('checked');
|
||||
|
||||
// --- 1) Get an OpenVidu object ---
|
||||
|
||||
OV = new OpenVidu();
|
||||
|
||||
// --- 2) Init a session ---
|
||||
|
||||
session = OV.initSession();
|
||||
|
||||
// --- 3) Specify the actions when events take place in the session ---
|
||||
|
||||
// On every new Stream received...
|
||||
session.on('streamCreated', event => {
|
||||
|
||||
// Subscribe to the Stream to receive it. HTML video will be appended to element with 'video-container' id
|
||||
var subscriber = session.subscribe(event.stream, 'video-container');
|
||||
|
||||
// When the HTML video has been appended to DOM...
|
||||
subscriber.on('videoElementCreated', event => {
|
||||
// Add a new <p> element for the user's nickname just below its video
|
||||
appendUserData(event.element, subscriber);
|
||||
});
|
||||
|
||||
// When the video starts playing remove the spinner
|
||||
subscriber.on('streamPlaying', function (event) {
|
||||
$('#spinner-' + subscriber.stream.connection.connectionId).remove();
|
||||
});
|
||||
|
||||
// Listen to any subscriber filter applied or removed to update the filter control buttons
|
||||
subscriber.on('streamPropertyChanged', function (event) {
|
||||
// If the changed property is the filter and the current selected streamManager is this subscriber's one
|
||||
if (subscriber === selectedStreamManager && event.changedProperty === 'filter') {
|
||||
if (!!event.newValue) {
|
||||
showRemoveFilterButtons();
|
||||
} else {
|
||||
showApplyFilterButtons();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// On every Stream destroyed...
|
||||
session.on('streamDestroyed', event => {
|
||||
// Delete the HTML element with the user's nickname. HTML videos are automatically removed from DOM
|
||||
removeUserData(event.stream.connection);
|
||||
});
|
||||
|
||||
// On every asynchronous exception...
|
||||
session.on('exception', (exception) => {
|
||||
console.warn(exception);
|
||||
});
|
||||
|
||||
// --- 4) Connect to the session with a valid user token ---
|
||||
|
||||
// Get a token from the OpenVidu deployment
|
||||
getToken(mySessionId, role).then(token => {
|
||||
|
||||
// First param is the token got from the OpenVidu deployment. Second param can be retrieved by every user on event
|
||||
// 'streamCreated' (property Stream.connection.data), and will be appended to DOM as the user's nickname
|
||||
session.connect(token, { clientData: myUserName }).then(() => {
|
||||
|
||||
// --- 5) Set page layout for active call ---
|
||||
|
||||
$('#session-title').text(mySessionId);
|
||||
$('#join').hide();
|
||||
$('#session').show();
|
||||
|
||||
// --- 6) Get your own camera stream with the desired properties ---
|
||||
|
||||
if (role !== 'SUBSCRIBER') {
|
||||
var publisherProperties = {
|
||||
audioSource: undefined, // The source of audio. If undefined default microphone
|
||||
videoSource: undefined, // The source of video. If undefined default webcam
|
||||
publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
|
||||
publishVideo: true, // Whether you want to start publishing with your video enabled or not
|
||||
resolution: '1280x720', // The resolution of your video
|
||||
frameRate: 30, // The frame rate of your video
|
||||
insertMode: 'APPEND', // How the video is inserted in the target element 'video-container'
|
||||
mirror: false // Whether to mirror your local video or not
|
||||
};
|
||||
|
||||
// If the filter should be enabled from the beginning of the publishing
|
||||
if (startWithFilterEnabled) {
|
||||
publisherProperties.filter = {
|
||||
type: 'GStreamerFilter',
|
||||
options: { "command": "videobalance saturation=0.0" }
|
||||
}
|
||||
}
|
||||
|
||||
publisher = OV.initPublisher('video-container', publisherProperties);
|
||||
|
||||
// --- 7) Specify the actions when events take place in our publisher ---
|
||||
|
||||
// When our HTML video has been added to DOM...
|
||||
publisher.on('videoElementCreated', function (event) {
|
||||
appendUserData(event.element, publisher);
|
||||
initMainVideo(publisher, myUserName);
|
||||
});
|
||||
// When our video has started playing...
|
||||
publisher.on('streamPlaying', function (event) {
|
||||
$('#spinner-' + publisher.stream.connection.connectionId).remove();
|
||||
$('#filter-btns').show();
|
||||
$('#buttonApplyFilter').prop('value', 'Apply filter to your stream');
|
||||
$('#buttonRemoveFilter').prop('value', 'Remove filter of your stream');
|
||||
$('#buttonApplyFilter').prop('disabled', false);
|
||||
$('#buttonRemoveFilter').prop('disabled', false);
|
||||
if (startWithFilterEnabled) {
|
||||
showRemoveFilterButtons();
|
||||
} else {
|
||||
showApplyFilterButtons();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen to your filter being applied or removed to update the filter control buttons
|
||||
publisher.on('streamPropertyChanged', function (event) {
|
||||
// If the changed property is the filter and the current selected streamManager is our publisher
|
||||
if (publisher === selectedStreamManager && event.changedProperty === 'filter') {
|
||||
if (!!event.newValue) {
|
||||
showRemoveFilterButtons();
|
||||
} else {
|
||||
showApplyFilterButtons();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// --- 8) Publish your stream, indicating you want to receive your remote stream to see the filters ---
|
||||
publisher.subscribeToRemote();
|
||||
session.publish(publisher);
|
||||
|
||||
} else {
|
||||
// Show a message warning the subscriber cannot publish
|
||||
$('#main-video video').css("background", "url('resources/images/subscriber-msg.jpg') round");
|
||||
$('#filter-btns').hide();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('There was an error connecting to the session:', error.code, error.message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function leaveSession() {
|
||||
|
||||
// --- 9) Leave the session by calling 'disconnect' method over the Session object ---
|
||||
|
||||
session.disconnect();
|
||||
|
||||
// Removing all HTML elements with user's nicknames.
|
||||
// HTML videos are automatically removed when leaving a Session
|
||||
removeAllUserData();
|
||||
|
||||
// Back to 'Join session' page
|
||||
$('#join').show();
|
||||
$('#filter-btns').hide();
|
||||
$('#session').hide();
|
||||
}
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
if (session) session.disconnect();
|
||||
};
|
||||
|
||||
|
||||
// --- Filter related methods ---
|
||||
|
||||
function applyFilter() {
|
||||
var filter = { type: '', options: {} };
|
||||
var type = $('input[name=filter]:checked').val();
|
||||
switch (type) {
|
||||
case 'Grayscale':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "videobalance saturation=0.0" };
|
||||
break;
|
||||
case 'Rotation':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "videoflip method=vertical-flip" };
|
||||
break;
|
||||
case 'Faceoverlay':
|
||||
filter.type = 'FaceOverlayFilter';
|
||||
filter.options = {};
|
||||
break;
|
||||
case 'Audioecho':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "audioecho delay=40000000 intensity=0.7 feedback=0.4" };
|
||||
break;
|
||||
case 'Amplify':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "audioamplify amplification=1.7" };
|
||||
break;
|
||||
case 'Pitch':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "pitch pitch=1.2" };
|
||||
break;
|
||||
case 'Videobox':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": "videobox fill=black top=-30 bottom=-30 left=-30 right=-30" };
|
||||
break;
|
||||
case 'Text':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": 'textoverlay text="Embedded text!" valignment=top halignment=right font-desc="Cantarell 25" draw-shadow=false' };
|
||||
break;
|
||||
case 'Time':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": 'timeoverlay valignment=bottom halignment=right font-desc="Sans, 20"' };
|
||||
break;
|
||||
case 'Clock':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": 'clockoverlay valignment=bottom halignment=right shaded-background=true font-desc="Sans, 20"' };
|
||||
break;
|
||||
case 'Chroma':
|
||||
filter.type = 'GStreamerFilter';
|
||||
filter.options = { "command": 'chromahold target-r=0 target-g=0 target-b=255 tolerance=90' };
|
||||
break;
|
||||
}
|
||||
selectedStreamManager.stream.applyFilter(filter.type, filter.options)
|
||||
.then(f => {
|
||||
if (f.type === 'FaceOverlayFilter') {
|
||||
f.execMethod(
|
||||
"setOverlayedImage",
|
||||
{
|
||||
"uri": "https://cdn.pixabay.com/photo/2017/09/30/09/29/cowboy-hat-2801582_960_720.png",
|
||||
"offsetXPercent": "-0.1F",
|
||||
"offsetYPercent": "-0.8F",
|
||||
"widthPercent": "1.5F",
|
||||
"heightPercent": "1.0F"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeFilter() {
|
||||
selectedStreamManager.stream.removeFilter();
|
||||
}
|
||||
|
||||
// --- End filter related methods ---
|
||||
|
||||
|
||||
|
||||
/* APPLICATION SPECIFIC METHODS */
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
generateParticipantInfo();
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body', trigger: 'hover' });
|
||||
});
|
||||
|
||||
function generateParticipantInfo() {
|
||||
$('#sessionId').val("SessionA");
|
||||
$('#userName').val("Participant" + Math.floor(Math.random() * 100));
|
||||
}
|
||||
|
||||
function handleRadioBtnClick(myRadio) {
|
||||
this.role = myRadio.value;
|
||||
if (this.role !== 'SUBSCRIBER') {
|
||||
$('#filter-enabled').show();
|
||||
} else {
|
||||
$('#filter-enabled').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function showApplyFilterButtons() {
|
||||
$('#filter-applied-opts').show();
|
||||
$('#filter-removed-opts').hide();
|
||||
}
|
||||
|
||||
function showRemoveFilterButtons() {
|
||||
$('#filter-applied-opts').hide();
|
||||
$('#filter-removed-opts').show();
|
||||
}
|
||||
|
||||
var spinnerNodeHtml =
|
||||
'<div class="spinner"><div class="sk-circle1 sk-child"></div><div class="sk-circle2 sk-child"></div><div class="sk-circle3 sk-child"></div>' +
|
||||
'<div class="sk-circle4 sk-child"></div><div class="sk-circle5 sk-child"></div><div class="sk-circle6 sk-child"></div><div class="sk-circle7 sk-child"></div>' +
|
||||
'<div class="sk-circle8 sk-child"></div><div class="sk-circle9 sk-child"></div><div class="sk-circle10 sk-child"></div><div class="sk-circle11 sk-child"></div>' +
|
||||
'<div class="sk-circle12 sk-child"></div></div>';
|
||||
|
||||
function appendUserData(videoElement, streamManager) {
|
||||
var userData = JSON.parse(streamManager.stream.connection.data).clientData;
|
||||
var nodeId = streamManager.stream.connection.connectionId;
|
||||
// Insert user nickname
|
||||
var dataNode = $('<div id="data-' + nodeId + '" class="data-node"><p>' + userData + '</p></div>');
|
||||
dataNode.insertAfter($(videoElement));
|
||||
// Insert spinner loader
|
||||
var spinnerNode = $(spinnerNodeHtml).attr('id', 'spinner-' + nodeId)
|
||||
dataNode.append(spinnerNode);
|
||||
addClickListener(videoElement, streamManager);
|
||||
}
|
||||
|
||||
function removeUserData(connection) {
|
||||
$("#data-" + connection.connectionId).remove();
|
||||
}
|
||||
|
||||
function removeAllUserData() {
|
||||
$(".data-node").remove();
|
||||
$('#main-video div p').html('');
|
||||
}
|
||||
|
||||
function addClickListener(videoElement, streamManager) {
|
||||
videoElement.addEventListener('click', function () {
|
||||
var mainVideo = $('#main-video video').get(0);
|
||||
// Only apply all these changes if not clicked on the same video again
|
||||
if (!streamManager.videos.map(v => v.video).includes(mainVideo)) {
|
||||
selectedStreamManager = streamManager;
|
||||
$('#main-video').fadeOut("fast", () => {
|
||||
// Put the nickname of the clicked user in the main video view
|
||||
var nickname = JSON.parse(streamManager.stream.connection.data).clientData;
|
||||
$('#main-video div p').html(nickname);
|
||||
// Change the ownership of the main video to the clicked StreamManager (Publisher or Subscriber)
|
||||
streamManager.addVideoElement(mainVideo);
|
||||
// Show the required filter buttons depending on whether the filter is applied or not for the selected StreamManager
|
||||
if (!!streamManager.stream.filter) {
|
||||
showRemoveFilterButtons();
|
||||
} else {
|
||||
showApplyFilterButtons();
|
||||
}
|
||||
// Change the text of the filter buttons depending on whether the selected StreamManager is your Publisher or a Subscriber
|
||||
if (streamManager !== publisher) {
|
||||
$('#buttonApplyFilter').prop('value', 'Apply filter to ' + nickname);
|
||||
$('#buttonRemoveFilter').prop('value', 'Remove filter of ' + nickname);
|
||||
if (role === 'PUBLISHER') {
|
||||
// Publishers cannot manage other user's filters
|
||||
$('#buttonApplyFilter').prop('disabled', true);
|
||||
$('#buttonRemoveFilter').prop('disabled', true);
|
||||
}
|
||||
} else {
|
||||
$('#buttonApplyFilter').prop('value', 'Apply filter to your stream');
|
||||
$('#buttonRemoveFilter').prop('value', 'Remove filter of your stream');
|
||||
$('#buttonApplyFilter').prop('disabled', false);
|
||||
$('#buttonRemoveFilter').prop('disabled', false);
|
||||
}
|
||||
$('#main-video').fadeIn("fast");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initMainVideo(streamManager, userData) {
|
||||
var videoEl = $('#main-video video').get(0);
|
||||
videoEl.onplaying = () => {
|
||||
$('#main-video div .spinner').remove();
|
||||
};
|
||||
streamManager.addVideoElement(videoEl);
|
||||
$('#main-video div p').html(userData);
|
||||
$('#main-video div').append($(spinnerNodeHtml));
|
||||
$('#main-video video').prop('muted', true);
|
||||
selectedStreamManager = streamManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* GETTING A TOKEN FROM YOUR APPLICATION SERVER
|
||||
* --------------------------------------------
|
||||
* The methods below request the creation of a Session and a Token to
|
||||
* your application server. This keeps your OpenVidu deployment secure.
|
||||
*
|
||||
* In this sample code, there is no user control at all. Anybody could
|
||||
* access your application server endpoints! In a real production
|
||||
* environment, your application server must identify the user to allow
|
||||
* access to the endpoints.
|
||||
*
|
||||
* Visit https://docs.openvidu.io/en/stable/application-server to learn
|
||||
* more about the integration of OpenVidu in your application server.
|
||||
*/
|
||||
|
||||
var APPLICATION_SERVER_URL = "http://localhost:5000/";
|
||||
|
||||
function getToken(mySessionId) {
|
||||
return createSession(mySessionId).then(sessionId => createToken(sessionId));
|
||||
}
|
||||
|
||||
function createSession(sessionId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: APPLICATION_SERVER_URL + "api/sessions",
|
||||
data: JSON.stringify({ customSessionId: sessionId }),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
success: response => resolve(response), // The sessionId
|
||||
error: (error) => reject(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createToken(sessionId, role) {
|
||||
var openviduRole;
|
||||
var jsonBody = {
|
||||
role: role,
|
||||
kurentoOptions: {}
|
||||
};
|
||||
if (openviduRole !== 'SUBSCRIBER') {
|
||||
// Only the PUBLISHERS and MODERATORS need to configure the ability of applying filters
|
||||
jsonBody.kurentoOptions = {
|
||||
allowedFilters: ['FaceOverlayFilter', 'ChromaFilter', 'GStreamerFilter']
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: APPLICATION_SERVER_URL + 'api/sessions/' + sessionId + '/connections',
|
||||
data: JSON.stringify(jsonBody),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
success: (response) => resolve(response), // The token
|
||||
error: (error) => reject(error)
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,136 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>openvidu-filters</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
|
||||
<link rel="shortcut icon" href="resources/images/favicon.ico" type="image/x-icon">
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<!-- Bootstrap -->
|
||||
|
||||
<link rel="stylesheet" href="style.css" type="text/css" media="screen">
|
||||
<script src="openvidu-browser-2.27.0.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/"><img class="demo-logo" src="resources/images/openvidu_vert_white_bg_trans_cropped.png"/> Filters</a>
|
||||
<a class="navbar-brand nav-icon" href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-filters" title="GitHub Repository" target="_blank"><i class="fa fa-github" aria-hidden="true"></i></a>
|
||||
<a class="navbar-brand nav-icon" href="http://www.docs.openvidu.io/en/stable/advanced-features/filters/" title="Documentation" target="_blank"><i class="fa fa-book" aria-hidden="true"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="main-container" class="container">
|
||||
<div id="join">
|
||||
<div id="img-div"><img src="resources/images/openvidu_grey_bg_transp_cropped.png" /></div>
|
||||
<div id="join-dialog" class="jumbotron vertical-center">
|
||||
<h1>Join a video session</h1>
|
||||
<form class="form-group" onsubmit="joinSession(); return false">
|
||||
<p>
|
||||
<label class="label-title">Session</label>
|
||||
<input class="form-control" type="text" id="sessionId" required>
|
||||
</p>
|
||||
<p>
|
||||
<label class="label-title">Participant</label>
|
||||
<input class="form-control" type="text" id="userName" required>
|
||||
</p>
|
||||
<p>
|
||||
<label class="label-title">Role</label>
|
||||
<div id="radio-btns">
|
||||
<label class="radio-inline" data-toggle="tooltip" data-placement="top" title="Subscribers cannot publish video and therefore cannot apply filters">
|
||||
<input type="radio" onclick="handleRadioBtnClick(this)" name="role" value="SUBSCRIBER">Subscriber
|
||||
</label>
|
||||
<label class="radio-inline" data-toggle="tooltip" data-placement="top" title="Publishers can apply filters to their own streams">
|
||||
<input type="radio" onclick="handleRadioBtnClick(this)" name="role" value="PUBLISHER" checked>Publisher
|
||||
</label>
|
||||
<label class="radio-inline" data-toggle="tooltip" data-placement="top" title="Moderators can apply filters to their own streams and other users streams">
|
||||
<input type="radio" onclick="handleRadioBtnClick(this)" name="role" value="MODERATOR">Moderator
|
||||
</label>
|
||||
</div>
|
||||
<div id="filter-enabled" class="checkbox">
|
||||
<label><input type="checkbox" id="start-filter-enabled" name="startFilterEnabled" value="false">Start with filter enabled?</label>
|
||||
</div>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<input class="btn btn-lg btn-success" type="submit" name="commit" value="Join!">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="session" style="display: none;">
|
||||
<div id="session-header">
|
||||
<h1 id="session-title"></h1>
|
||||
<div id="div-btns">
|
||||
<input class="btn btn-large btn-danger" type="button" id="buttonLeaveSession" onmouseup="leaveSession()" value="Leave session">
|
||||
<div id="filter-btns">
|
||||
<div id="filter-applied-opts">
|
||||
<input class="btn btn-large btn-info" type="button" id="buttonApplyFilter" onmouseup="applyFilter()" value="Apply filter">
|
||||
<div id="filter-radio-btns">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Grayscale" checked>Grayscale
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Rotation">Rotation
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Faceoverlay">Faceoverlay
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Videobox">Video box
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Audioecho">Audio echo
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Amplify">Audio amplify
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Pitch">Pitch
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Text">Text
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Time">Time
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Clock">Clock
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" name="filter" value="Chroma">Chroma (blue)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filter-removed-opts">
|
||||
<input class="btn btn-large btn-info" type="button" id="buttonRemoveFilter" onmouseup="removeFilter()" value="Remove filter">
|
||||
</div>
|
||||
<div id="info-msg">This are just some examples of available filters. There are a lot more. Visit <a href="https://docs.openvidu.io/en/stable/advanced-features/filters" target="_blank">openvidu.io</a> docs for further information</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-video" class="col-md-6"><div id="main-video-data"><p></p></div><video autoplay playsinline="true"></video></div>
|
||||
<div id="video-container" class="col-md-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="text-muted">OpenVidu © 2022</div>
|
||||
<a href="http://www.openvidu.io/" target="_blank"><img class="openvidu-logo" src="resources/images/openvidu_globe_bg_transp_cropped.png"/></a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 81 KiB |
@ -1,488 +0,0 @@
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
nav {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
background-color: #4d4d4d !important;
|
||||
border-color: #4d4d4d !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
}
|
||||
|
||||
.navbar-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
padding: 5px 15px 5px 15px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
nav i.fa {
|
||||
font-size: 40px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #a9a9a9 !important;
|
||||
}
|
||||
|
||||
nav i.fa:hover {
|
||||
color: #a9a9a9;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.horizontal-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
color: #0088aa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #0088aa;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
|
||||
}
|
||||
|
||||
input.btn {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #06d362 !important;
|
||||
border-color: #06d362;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #1abd61 !important;
|
||||
border-color: #1abd61;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
|
||||
.footer .text-muted {
|
||||
margin: 20px 0;
|
||||
float: left;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.openvidu-logo {
|
||||
height: 35px;
|
||||
float: right;
|
||||
margin: 12px 0;
|
||||
-webkit-transition: all 0.1s ease-in-out;
|
||||
-moz-transition: all 0.1s ease-in-out;
|
||||
-o-transition: all 0.1s ease-in-out;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.openvidu-logo:hover {
|
||||
-webkit-filter: grayscale(0.5);
|
||||
filter: grayscale(0.5);
|
||||
}
|
||||
|
||||
.demo-logo {
|
||||
margin: 0;
|
||||
height: 22px;
|
||||
float: left;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
a:hover .demo-logo {
|
||||
-webkit-filter: brightness(0.7);
|
||||
filter: brightness(0.7);
|
||||
}
|
||||
|
||||
#join-dialog {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
#join-dialog h1 {
|
||||
color: #4d4d4d;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#img-div {
|
||||
text-align: center;
|
||||
margin-top: 3em;
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
|
||||
#img-div img {
|
||||
height: 15%;
|
||||
}
|
||||
|
||||
#join-dialog .label-title {
|
||||
color: #0088aa;
|
||||
}
|
||||
|
||||
#join-dialog input.btn {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#session-header {
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#session-title {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#buttonLeaveSession {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#video-container video {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#video-container div.data-node {
|
||||
float: left;
|
||||
width: 50%;
|
||||
position: relative;
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
#video-container div.data-node p {
|
||||
display: inline-block;
|
||||
background: #f8f8f8;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
color: #777777;
|
||||
font-weight: bold;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
video {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#main-video-data {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#main-video p {
|
||||
display: inline-block;
|
||||
background: #f8f8f8;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
font-size: 22px;
|
||||
color: #777777;
|
||||
font-weight: bold;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
#session img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
object-fit: contain;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
#session #video-container img {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 50%;
|
||||
cursor: pointer;
|
||||
object-fit: cover;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
#filter-enabled {
|
||||
display: inline-block;
|
||||
margin: 0 0 0 20px;
|
||||
}
|
||||
|
||||
#radio-btns {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#filter-btns {
|
||||
display: none;
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#filter-radio-btns {
|
||||
margin-top: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#filter-applied-opts {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#filter-removed-opts {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#div-btns {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin: 10px 0 30px 0;
|
||||
}
|
||||
|
||||
#info-msg {
|
||||
margin-top: 20px;
|
||||
color: #7b7b7b;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* xs ans md screen resolutions*/
|
||||
|
||||
@media screen and (max-width: 991px) and (orientation: portrait) {
|
||||
#join-dialog {
|
||||
max-width: inherit;
|
||||
}
|
||||
|
||||
#img-div img {
|
||||
height: 10%;
|
||||
}
|
||||
|
||||
#img-div {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.container-fluid>.navbar-collapse,
|
||||
.container-fluid>.navbar-header,
|
||||
.container>.navbar-collapse,
|
||||
.container>.navbar-header {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.navbar-header i.fa {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.navbar-header a.nav-icon {
|
||||
padding: 7px 3px 7px 3px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 767px) and (orientation: landscape) {
|
||||
#img-div {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#join-dialog {
|
||||
max-width: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
/* loader */
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 10%;
|
||||
}
|
||||
|
||||
.spinner .sk-child {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.spinner .sk-child:before {
|
||||
content: '';
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 15%;
|
||||
height: 15%;
|
||||
background-color: #777777;
|
||||
border-radius: 100%;
|
||||
-webkit-animation: sk-circleBounceDelay 1.2s infinite ease-in-out both;
|
||||
animation: sk-circleBounceDelay 1.2s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner .sk-circle2 {
|
||||
-webkit-transform: rotate(30deg);
|
||||
-ms-transform: rotate(30deg);
|
||||
transform: rotate(30deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle3 {
|
||||
-webkit-transform: rotate(60deg);
|
||||
-ms-transform: rotate(60deg);
|
||||
transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle4 {
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle5 {
|
||||
-webkit-transform: rotate(120deg);
|
||||
-ms-transform: rotate(120deg);
|
||||
transform: rotate(120deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle6 {
|
||||
-webkit-transform: rotate(150deg);
|
||||
-ms-transform: rotate(150deg);
|
||||
transform: rotate(150deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle7 {
|
||||
-webkit-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle8 {
|
||||
-webkit-transform: rotate(210deg);
|
||||
-ms-transform: rotate(210deg);
|
||||
transform: rotate(210deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle9 {
|
||||
-webkit-transform: rotate(240deg);
|
||||
-ms-transform: rotate(240deg);
|
||||
transform: rotate(240deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle10 {
|
||||
-webkit-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle11 {
|
||||
-webkit-transform: rotate(300deg);
|
||||
-ms-transform: rotate(300deg);
|
||||
transform: rotate(300deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle12 {
|
||||
-webkit-transform: rotate(330deg);
|
||||
-ms-transform: rotate(330deg);
|
||||
transform: rotate(330deg);
|
||||
}
|
||||
|
||||
.spinner .sk-circle2:before {
|
||||
-webkit-animation-delay: -1.1s;
|
||||
animation-delay: -1.1s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle3:before {
|
||||
-webkit-animation-delay: -1s;
|
||||
animation-delay: -1s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle4:before {
|
||||
-webkit-animation-delay: -0.9s;
|
||||
animation-delay: -0.9s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle5:before {
|
||||
-webkit-animation-delay: -0.8s;
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle6:before {
|
||||
-webkit-animation-delay: -0.7s;
|
||||
animation-delay: -0.7s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle7:before {
|
||||
-webkit-animation-delay: -0.6s;
|
||||
animation-delay: -0.6s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle8:before {
|
||||
-webkit-animation-delay: -0.5s;
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle9:before {
|
||||
-webkit-animation-delay: -0.4s;
|
||||
animation-delay: -0.4s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle10:before {
|
||||
-webkit-animation-delay: -0.3s;
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle11:before {
|
||||
-webkit-animation-delay: -0.2s;
|
||||
animation-delay: -0.2s;
|
||||
}
|
||||
|
||||
.spinner .sk-circle12:before {
|
||||
-webkit-animation-delay: -0.1s;
|
||||
animation-delay: -0.1s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-circleBounceDelay {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-circleBounceDelay {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@ -1,208 +0,0 @@
|
||||
/*
|
||||
👋 Hi! This file was autogenerated by tslint-to-eslint-config.
|
||||
https://github.com/typescript-eslint/tslint-to-eslint-config
|
||||
|
||||
It represents the closest reasonable ESLint configuration to this
|
||||
project's original TSLint configuration.
|
||||
|
||||
We recommend eventually switching this configuration to extend from
|
||||
the recommended rulesets in typescript-eslint.
|
||||
https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md
|
||||
|
||||
Happy linting! 💖
|
||||
*/
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"eslint-plugin-import",
|
||||
"@angular-eslint/eslint-plugin",
|
||||
"@typescript-eslint",
|
||||
"@typescript-eslint/tslint"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "page"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-class-suffix": "error",
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-output-on-prefix": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"off",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/indent": "error",
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/naming-convention": "error",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"error",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"brace-style": [
|
||||
"error",
|
||||
"1tbs"
|
||||
],
|
||||
"constructor-super": "error",
|
||||
"curly": "error",
|
||||
"dot-notation": "off",
|
||||
"eol-last": "error",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"smart"
|
||||
],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"import/no-deprecated": "warn",
|
||||
"indent": "error",
|
||||
"max-len": [
|
||||
"error",
|
||||
{
|
||||
"code": 140
|
||||
}
|
||||
],
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"warn",
|
||||
"dir",
|
||||
"timeLog",
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"group",
|
||||
"groupEnd",
|
||||
"table",
|
||||
"dirxml",
|
||||
"error",
|
||||
"groupCollapsed",
|
||||
"Console",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"timeStamp",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-empty-function": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-shadow": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"quotes": "error",
|
||||
"radix": "error",
|
||||
"semi": "error",
|
||||
"spaced-comment": [
|
||||
"error",
|
||||
"always",
|
||||
{
|
||||
"markers": [
|
||||
"/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/tslint/config": [
|
||||
"error",
|
||||
{
|
||||
"rules": {
|
||||
"import-spacing": true,
|
||||
"use-host-property-decorator": true,
|
||||
"use-input-property-decorator": true,
|
||||
"use-life-cycle-interface": true,
|
||||
"use-output-property-decorator": true,
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
34
openvidu-ionic-cordova/.gitignore
vendored
@ -1,34 +0,0 @@
|
||||
# Specifies intentionally untracked files to ignore when using Git
|
||||
# http://git-scm.com/docs/gitignore
|
||||
|
||||
*~
|
||||
*.sw[mnpcod]
|
||||
*.log
|
||||
*.tmp
|
||||
*.tmp.*
|
||||
log.txt
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.vscode/
|
||||
npm-debug.log*
|
||||
|
||||
.idea/
|
||||
.ionic/
|
||||
.sourcemaps/
|
||||
.sass-cache/
|
||||
.tmp/
|
||||
.versions/
|
||||
coverage/
|
||||
www/
|
||||
node_modules/
|
||||
tmp/
|
||||
temp/
|
||||
platforms/
|
||||
plugins/
|
||||
plugins/android.json
|
||||
plugins/ios.json
|
||||
$RECYCLE.BIN/
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
UserInterfaceState.xcuserstate
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"ios": {
|
||||
"build": {
|
||||
"buildFlag": ["-UseModernBuildSystem=0"]
|
||||
},
|
||||
"run": {
|
||||
"buildFlag": ["-UseModernBuildSystem=0"]
|
||||
},
|
||||
"debug": {
|
||||
"buildFlag": ["-UseModernBuildSystem=0"]
|
||||
},
|
||||
"release": {
|
||||
"buildFlag": ["-UseModernBuildSystem=0"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="io.openvidu.sampleios" version="2.6.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>OpenVidu Ionic</name>
|
||||
<description>OpenVidu Ionic app compatible with Android and iOS</description>
|
||||
<author email="openvidu@gmail.com" href="https://openvidu.io/">OpenVidu</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<preference name="ScrollEnabled" value="false" />
|
||||
<preference name="android-minSdkVersion" value="22" />
|
||||
<preference name="BackupWebStorage" value="none" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<preference name="FadeSplashScreenDuration" value="300" />
|
||||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<preference name="SplashScreenDelay" value="3000" />
|
||||
<preference name="WKWebViewOnly" value="true" />
|
||||
<preference name="AllowInlineMediaPlayback" value="true" />
|
||||
<platform name="android">
|
||||
<preference name="android-minSdkVersion" value="23" />
|
||||
<preference name="android-targetSdkVersion" value="31" />
|
||||
<allow-intent href="market:*" />
|
||||
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
|
||||
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
|
||||
<config-file mode="merge" parent="/*" target="AndroidManifest.xml">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
</config-file>
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
<config-file parent="NSCameraUsageDescription" target="*-Info.plist">
|
||||
<string>OpenVidu needs access to your camera</string>
|
||||
</config-file>
|
||||
<config-file parent="NSContactsUsageDescription" target="*-Info.plist">
|
||||
<string>OpenVidu needs access to contacts</string>
|
||||
</config-file>
|
||||
<config-file parent="NSMicrophoneUsageDescription" target="*-Info.plist">
|
||||
<string>OpenVidu needs access to your microphone</string>
|
||||
</config-file>
|
||||
<icon height="57" src="resources/ios/icon/icon.png" width="57" />
|
||||
<icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
|
||||
<icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
|
||||
<icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
|
||||
<icon height="120" src="resources/ios/icon/icon-40@3x.png" width="120" />
|
||||
<icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
|
||||
<icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
|
||||
<icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
|
||||
<icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
|
||||
<icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
|
||||
<icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
|
||||
<icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
|
||||
<icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
|
||||
<icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
|
||||
<icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
|
||||
<icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
|
||||
<icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
|
||||
<splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
|
||||
<splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
|
||||
<splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
|
||||
<splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
|
||||
<splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Landscape@~ipadpro.png" width="2732" />
|
||||
<splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
|
||||
<splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
|
||||
<splash height="2732" src="resources/ios/splash/Default-Portrait@~ipadpro.png" width="2048" />
|
||||
<splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
|
||||
<splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
|
||||
<splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
|
||||
<splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
|
||||
<icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
|
||||
<icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
|
||||
<icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
|
||||
<icon height="29" src="resources/ios/icon/icon-29.png" width="29" />
|
||||
<icon height="58" src="resources/ios/icon/icon-29@2x.png" width="58" />
|
||||
<icon height="87" src="resources/ios/icon/icon-29@3x.png" width="87" />
|
||||
<icon height="48" src="resources/ios/icon/icon-24@2x.png" width="48" />
|
||||
<icon height="55" src="resources/ios/icon/icon-27.5@2x.png" width="55" />
|
||||
<icon height="88" src="resources/ios/icon/icon-44@2x.png" width="88" />
|
||||
<icon height="172" src="resources/ios/icon/icon-86@2x.png" width="172" />
|
||||
<icon height="196" src="resources/ios/icon/icon-98@2x.png" width="196" />
|
||||
<icon height="216" src="resources/ios/icon/icon-108@2x.png" width="216" />
|
||||
<splash height="2688" src="resources/ios/splash/Default-2688h~iphone.png" width="1242" />
|
||||
<splash height="1242" src="resources/ios/splash/Default-Landscape-2688h~iphone.png" width="2688" />
|
||||
<splash height="1792" src="resources/ios/splash/Default-1792h~iphone.png" width="828" />
|
||||
<splash height="828" src="resources/ios/splash/Default-Landscape-1792h~iphone.png" width="1792" />
|
||||
<splash height="2436" src="resources/ios/splash/Default-2436h.png" width="1125" />
|
||||
<splash height="1125" src="resources/ios/splash/Default-Landscape-2436h.png" width="2436" />
|
||||
</platform>
|
||||
<plugin name="cordova-plugin-statusbar" spec="2.4.2" />
|
||||
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
||||
<plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
|
||||
<plugin name="cordova-plugin-ionic-webview" spec="2.0.0" />
|
||||
<plugin name="cordova-plugin-ionic-keyboard" spec="2.0.5" />
|
||||
<plugin name="cordova-plugin-android-permissions" spec="1.0.0" />
|
||||
<engine name="android" spec="7.0.0" />
|
||||
</widget>
|
||||
@ -1,28 +0,0 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: 'e2e/tsconfig.e2e.json'
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||