├── sdk ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── ericsson │ │ │ └── research │ │ │ └── owr │ │ │ └── sdk │ │ │ ├── StreamType.java │ │ │ ├── MediaSourceProvider.java │ │ │ ├── MediaSourceListener.java │ │ │ ├── RtcSessions.java │ │ │ ├── InvalidDescriptionException.java │ │ │ ├── RtcPayload.java │ │ │ ├── MicrophoneSource.java │ │ │ ├── CameraSource.java │ │ │ ├── RtcCandidate.java │ │ │ ├── SessionDescription.java │ │ │ ├── StreamDescription.java │ │ │ ├── VideoView.java │ │ │ ├── SessionDescriptionImpl.java │ │ │ ├── RtcPayloadImpl.java │ │ │ ├── StreamMode.java │ │ │ ├── MediaSourceListenerSet.java │ │ │ ├── MicrophoneSourceImpl.java │ │ │ ├── RtcConfig.java │ │ │ ├── RtcSession.java │ │ │ ├── RtcConfigs.java │ │ │ ├── RtcCandidateImpl.java │ │ │ ├── VideoViewImpl.java │ │ │ ├── MutableStreamDescription.java │ │ │ ├── StreamDescriptionImpl.java │ │ │ ├── SimpleStreamSet.java │ │ │ ├── StreamSet.java │ │ │ ├── CameraSourceImpl.java │ │ │ ├── DataStreamHandler.java │ │ │ ├── MediaStreamHandler.java │ │ │ ├── StreamHandler.java │ │ │ ├── RtcSessionImpl.java │ │ │ └── Utils.java │ ├── debug │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── ericsson │ │ └── research │ │ └── owr │ │ └── sdk │ │ ├── OwrTestCase.java │ │ ├── MiscTests.java │ │ ├── OwrActivityTestCase.java │ │ ├── RtcConfigTest.java │ │ ├── SimpleStreamSetTest.java │ │ ├── TestUtils.java │ │ └── MediaSourceTests.java ├── lint.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── gradle.properties ├── LICENSE ├── gradlew.bat └── gradlew /sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sdk' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | .DS_Store 5 | /build 6 | *.iml 7 | -------------------------------------------------------------------------------- /sdk/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OpenWebRTC SDK 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EricssonResearch/openwebrtc-android-sdk/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sdk/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /sdk/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /sdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sdk/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/epatold/Development/Tools/ADT/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [WIP] OpenWebRTC Android SDK 2 | 3 | SDK for adding OpenWebRTC to your Android app 4 | 5 | ## Binaries 6 | 7 | Add `compile 'io.openwebrtc:openwebrtc-android-sdk:0.1.0'` to your gradle dependencies. 8 | 9 | ## Development 10 | 11 | To build the SDK as a part of another project, clone `openwebrtc-android-sdk` to your project's root folder, and add it to your settings.gradle: 12 | ``` 13 | include ':app', ':openwebrtc-android-sdk' 14 | project(':openwebrtc-android-sdk').projectDir = new File('openwebrtc-android-sdk/sdk') 15 | ``` 16 | 17 | Then simply add `compile project(':openwebrtc-android-sdk')` to your module's dependencies. 18 | 19 | Another approach is to clone the SDK to a different directory than your app, and then point directly to that directory. 20 | This is done in the NativeCall example in openwebrtc-examples [link](https://github.com/EricssonResearch/openwebrtc-examples/blob/master/android/NativeCall/settings.gradle) 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | android.useDeprecatedNdk=true 20 | -------------------------------------------------------------------------------- /sdk/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | version = '0.1.0' 4 | 5 | android { 6 | compileSdkVersion 23 7 | buildToolsVersion "23.0.3" 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName version 13 | 14 | ndk { 15 | abiFilter "armeabi-v7a" 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_7 27 | } 28 | } 29 | 30 | dependencies { 31 | // Uncomment to use local version 32 | // compile files(System.getProperty("user.home") + '/cerbero/dist/android_armv7/lib/jni/openwebrtc.jar') 33 | compile 'io.openwebrtc:openwebrtc-android:0.3' 34 | compile 'org.mozilla:rhino:1.7R4' 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Ericsson Research 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public enum StreamType { 29 | AUDIO, VIDEO, DATA 30 | } 31 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MediaSourceProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | interface MediaSourceProvider { 29 | void addMediaSourceListener(MediaSourceListener listener); 30 | } 31 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MediaSourceListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | 29 | import com.ericsson.research.owr.MediaSource; 30 | 31 | public interface MediaSourceListener { 32 | void setMediaSource(MediaSource mediaSource); 33 | } -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcSessions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public class RtcSessions { 29 | private static final String TAG = "RtcSessions"; 30 | 31 | private RtcSessions() {} 32 | 33 | public static RtcSession create(RtcConfig config) { 34 | return new RtcSessionImpl(config); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/InvalidDescriptionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public class InvalidDescriptionException extends Exception { 29 | public InvalidDescriptionException(final String detailMessage) { 30 | super(detailMessage); 31 | } 32 | 33 | public InvalidDescriptionException(final String detailMessage, final Throwable throwable) { 34 | super(detailMessage, throwable); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcPayload.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.Map; 29 | 30 | public interface RtcPayload { 31 | int getPayloadType(); 32 | 33 | String getEncodingName(); 34 | 35 | int getClockRate(); 36 | 37 | Map getParameters(); 38 | 39 | // Audio only 40 | int getChannels(); 41 | 42 | // Video only 43 | boolean isNack(); 44 | 45 | boolean isNackPli(); 46 | 47 | boolean isCcmFir(); 48 | } 49 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MicrophoneSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public abstract class MicrophoneSource implements MediaSourceProvider { 29 | public abstract String getName(); 30 | 31 | private static MicrophoneSourceImpl instance; 32 | 33 | public static synchronized MicrophoneSource getInstance() { 34 | if (instance == null) { 35 | instance = MicrophoneSourceImpl.create(); 36 | } 37 | 38 | return instance; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/OwrTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.test.AndroidTestCase; 29 | import android.util.Log; 30 | 31 | import com.ericsson.research.owr.Owr; 32 | 33 | public class OwrTestCase extends AndroidTestCase { 34 | private static final String TAG = "OwrTestCase"; 35 | 36 | static { 37 | Owr.init(); 38 | } 39 | 40 | @Override 41 | protected void setUp() throws Exception { 42 | super.setUp(); 43 | Log.v(TAG, "running owr main loop in background"); 44 | Owr.runInBackground(); 45 | } 46 | 47 | @Override 48 | protected void tearDown() throws Exception { 49 | super.tearDown(); 50 | Log.v(TAG, "quitting owr main loop"); 51 | Owr.quit(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/CameraSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.List; 29 | 30 | public abstract class CameraSource implements MediaSourceProvider { 31 | public abstract VideoView createVideoView(); 32 | 33 | public abstract String getName(int index); 34 | 35 | public abstract int getCount(); 36 | 37 | public abstract void selectSource(int index); 38 | 39 | public abstract int getActiveSource(); 40 | 41 | public abstract int getSelectedSource(); 42 | 43 | public abstract List dumpPipelineGraphs(); 44 | 45 | private static CameraSourceImpl instance; 46 | 47 | public static synchronized CameraSource getInstance() { 48 | if (instance == null) { 49 | instance = CameraSourceImpl.create(); 50 | } 51 | 52 | return instance; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcCandidate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public interface RtcCandidate { 29 | int getStreamIndex(); 30 | 31 | String getStreamId(); 32 | 33 | String getUfrag(); 34 | 35 | String getPassword(); 36 | 37 | String getFoundation(); 38 | 39 | ComponentType getComponentType(); 40 | 41 | String getAddress(); 42 | 43 | int getPort(); 44 | 45 | int getPriority(); 46 | 47 | TransportType getTransportType(); 48 | 49 | CandidateType getType(); 50 | 51 | String getRelatedAddress(); 52 | 53 | int getRelatedPort(); 54 | 55 | enum ComponentType { 56 | RTP, RTCP 57 | } 58 | 59 | enum TransportType { 60 | UDP, TCP_ACTIVE, TCP_PASSIVE, TCP_SO 61 | } 62 | 63 | enum CandidateType { 64 | HOST, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAY 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/SessionDescription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.List; 29 | 30 | /** 31 | * An interface for an immutable description of RTC session at different stages. 32 | */ 33 | public interface SessionDescription { 34 | /** 35 | * @return the type of the session description 36 | */ 37 | Type getType(); 38 | 39 | /** 40 | * @return an immutable list of stream descriptions 41 | */ 42 | List getStreamDescriptions(); 43 | 44 | /** 45 | * @return the id of the session 46 | */ 47 | String getSessionId(); 48 | 49 | /** 50 | * @param streamType a stream type 51 | * @return true if the session description contains a stream of type streamType, false otherwise 52 | */ 53 | boolean hasStreamType(StreamType streamType); 54 | 55 | enum Type { 56 | OFFER, ANSWER 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamDescription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.List; 29 | 30 | /** 31 | * An interface for describing a stream that is a part of a rtc session 32 | */ 33 | public interface StreamDescription { 34 | public StreamType getType(); 35 | 36 | public StreamMode getMode(); 37 | 38 | public String getUfrag(); 39 | 40 | public String getPassword(); 41 | 42 | public List getCandidates(); 43 | 44 | public String getDtlsSetup(); 45 | 46 | public String getFingerprint(); 47 | 48 | public String getFingerprintHashFunction(); 49 | 50 | // media only 51 | public String getMediaStreamId(); 52 | 53 | public String getMediaStreamTrackId(); 54 | 55 | public String getCname(); 56 | 57 | public boolean isRtcpMux(); 58 | 59 | public List getSsrcs(); 60 | 61 | public List getPayloads(); 62 | 63 | // data only 64 | public int getSctpPort(); 65 | 66 | public int getSctpStreamCount(); 67 | 68 | public String getAppLabel(); 69 | } 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/VideoView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.view.TextureView; 29 | 30 | public interface VideoView { 31 | /** 32 | * Sets the rotation of the video in multiples of 90 degrees 33 | * @param rotation 0, 1, 2, or 3, any other value will throw an IllegalArgumentException 34 | */ 35 | void setRotation(int rotation); 36 | 37 | /** 38 | * @return current rotation in multiples of 90 degrees 39 | */ 40 | int getRotation(); 41 | 42 | /** 43 | * Set whether or not the video should be mirrored. 44 | * @param mirrored true if the rendered video should be mirrored, false otherwise. 45 | */ 46 | void setMirrored(boolean mirrored); 47 | 48 | /** 49 | * @return true if the rendered video will be mirrored, false otherwise. 50 | */ 51 | boolean isMirrored(); 52 | 53 | /** 54 | * Set the view in which the video should be rendered, and starts the view if it was stopped. 55 | * 56 | * @param view The view to render the video in, may not be null. 57 | */ 58 | void setView(TextureView view); 59 | 60 | /** 61 | * Stops the video view and frees all resources. Calling setView again will resume the view. 62 | * Depending on the type of the source it might be required to call this function in order 63 | * to not leak resources. Calling this function will not cause any error, even if view is already stopped or freed. 64 | */ 65 | void stop(); 66 | } 67 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/SessionDescriptionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.Collections; 29 | import java.util.List; 30 | 31 | class SessionDescriptionImpl implements SessionDescription { 32 | public static final String TAG = "SessionDescriptionImpl"; 33 | 34 | private final List mStreamDescriptions; 35 | private final String mSessionId; 36 | private final Type mType; 37 | 38 | SessionDescriptionImpl(Type type, String sessionId, List streamDescriptions) { 39 | mType = type; 40 | mSessionId = sessionId; 41 | if (streamDescriptions == null) { 42 | mStreamDescriptions = Collections.emptyList(); 43 | } else { 44 | mStreamDescriptions = Collections.unmodifiableList(streamDescriptions); 45 | } 46 | } 47 | 48 | @Override 49 | public Type getType() { 50 | return mType; 51 | } 52 | 53 | @Override 54 | public List getStreamDescriptions() { 55 | return mStreamDescriptions; 56 | } 57 | 58 | @Override 59 | public String getSessionId() { 60 | return mSessionId; 61 | } 62 | 63 | @Override 64 | public boolean hasStreamType(StreamType streamType) { 65 | for (StreamDescription streamDescription : mStreamDescriptions) { 66 | if (streamDescription.getType() == streamType) { 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcPayloadImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.Collections; 29 | import java.util.Map; 30 | 31 | class RtcPayloadImpl implements RtcPayload { 32 | private final int mPayloadType; 33 | private final String mEncodingName; 34 | private final int mClockRate; 35 | private final Map mParameters; 36 | 37 | // Audio only 38 | private final int mChannels; 39 | 40 | // Video only 41 | private final boolean mNack; 42 | private final boolean mNackPli; 43 | private final boolean mCcmFir; 44 | 45 | RtcPayloadImpl(int payloadType, String encodingName, int clockRate, Map parameters, int channels, boolean nack, boolean nackPli, boolean ccmFir) { 46 | mPayloadType = payloadType; 47 | mEncodingName = encodingName; 48 | mClockRate = clockRate; 49 | mParameters = parameters == null ? null : Collections.unmodifiableMap(parameters); 50 | mChannels = channels; 51 | mNack = nack; 52 | mNackPli = nackPli; 53 | mCcmFir = ccmFir; 54 | } 55 | 56 | @Override 57 | public int getPayloadType() { 58 | return mPayloadType; 59 | } 60 | 61 | @Override 62 | public String getEncodingName() { 63 | return mEncodingName; 64 | } 65 | 66 | @Override 67 | public int getClockRate() { 68 | return mClockRate; 69 | } 70 | 71 | @Override 72 | public Map getParameters() { 73 | return mParameters; 74 | } 75 | 76 | @Override 77 | public int getChannels() { 78 | return mChannels; 79 | } 80 | 81 | @Override 82 | public boolean isNack() { 83 | return mNack; 84 | } 85 | 86 | @Override 87 | public boolean isNackPli() { 88 | return mNackPli; 89 | } 90 | 91 | @Override 92 | public boolean isCcmFir() { 93 | return mCcmFir; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/MiscTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import junit.framework.TestCase; 29 | 30 | public class MiscTests extends TestCase { 31 | public void testStreamMode() { 32 | assertSame(StreamMode.INACTIVE, StreamMode.get(false, false)); 33 | assertSame(StreamMode.SEND_ONLY, StreamMode.get(true, false)); 34 | assertSame(StreamMode.RECEIVE_ONLY, StreamMode.get(false, true)); 35 | assertSame(StreamMode.SEND_RECEIVE, StreamMode.get(true, true)); 36 | 37 | assertSame(StreamMode.INACTIVE, StreamMode.INACTIVE.reverse(false, false)); 38 | assertSame(StreamMode.INACTIVE, StreamMode.INACTIVE.reverse(true, false)); 39 | assertSame(StreamMode.INACTIVE, StreamMode.INACTIVE.reverse(false, true)); 40 | assertSame(StreamMode.INACTIVE, StreamMode.INACTIVE.reverse(true, true)); 41 | 42 | assertSame(StreamMode.INACTIVE, StreamMode.SEND_ONLY.reverse(false, false)); 43 | assertSame(StreamMode.INACTIVE, StreamMode.SEND_ONLY.reverse(true, false)); 44 | assertSame(StreamMode.RECEIVE_ONLY, StreamMode.SEND_ONLY.reverse(false, true)); 45 | assertSame(StreamMode.RECEIVE_ONLY, StreamMode.SEND_ONLY.reverse(true, true)); 46 | 47 | assertSame(StreamMode.INACTIVE, StreamMode.RECEIVE_ONLY.reverse(false, false)); 48 | assertSame(StreamMode.SEND_ONLY, StreamMode.RECEIVE_ONLY.reverse(true, false)); 49 | assertSame(StreamMode.INACTIVE, StreamMode.RECEIVE_ONLY.reverse(false, true)); 50 | assertSame(StreamMode.SEND_ONLY, StreamMode.RECEIVE_ONLY.reverse(true, true)); 51 | 52 | assertSame(StreamMode.INACTIVE, StreamMode.SEND_RECEIVE.reverse(false, false)); 53 | assertSame(StreamMode.SEND_ONLY, StreamMode.SEND_RECEIVE.reverse(true, false)); 54 | assertSame(StreamMode.RECEIVE_ONLY, StreamMode.SEND_RECEIVE.reverse(false, true)); 55 | assertSame(StreamMode.SEND_RECEIVE, StreamMode.SEND_RECEIVE.reverse(true, true)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/OwrActivityTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.app.Activity; 29 | import android.app.KeyguardManager; 30 | import android.content.Context; 31 | import android.os.Bundle; 32 | import android.test.ActivityInstrumentationTestCase2; 33 | import android.util.Log; 34 | import android.view.TextureView; 35 | import android.view.WindowManager; 36 | 37 | import com.ericsson.research.owr.Owr; 38 | 39 | public class OwrActivityTestCase extends ActivityInstrumentationTestCase2 { 40 | private static final String TAG = "OwrTestCase"; 41 | 42 | static { 43 | Owr.init(); 44 | } 45 | 46 | public OwrActivityTestCase() { 47 | super(TextureViewTestActivity.class); 48 | } 49 | 50 | @Override 51 | protected void setUp() throws Exception { 52 | super.setUp(); 53 | Log.v(TAG, "running owr main loop in background"); 54 | Owr.runInBackground(); 55 | } 56 | 57 | @Override 58 | protected void tearDown() throws Exception { 59 | super.tearDown(); 60 | Log.v(TAG, "quitting owr main loop"); 61 | Owr.quit(); 62 | } 63 | 64 | public static class TextureViewTestActivity extends Activity { 65 | private TextureView mTextureView; 66 | 67 | public TextureViewTestActivity() { 68 | } 69 | 70 | public TextureView getTextureView() { 71 | return mTextureView; 72 | } 73 | 74 | @Override 75 | protected void onCreate(final Bundle savedInstanceState) { 76 | super.onCreate(savedInstanceState); 77 | mTextureView = new TextureView(this); 78 | setContentView(mTextureView); 79 | KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 80 | KeyguardManager.KeyguardLock keyguardLock = km.newKeyguardLock("TAG"); 81 | keyguardLock.disableKeyguard(); 82 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | public enum StreamMode { 29 | SEND_RECEIVE(true, true), SEND_ONLY(true, false), RECEIVE_ONLY(false, true), INACTIVE(false, false); 30 | private final boolean mReceive; 31 | private final boolean mSend; 32 | 33 | StreamMode(boolean send, boolean receive) { 34 | mSend = send; 35 | mReceive = receive; 36 | } 37 | 38 | public boolean wantReceive() { 39 | return mReceive; 40 | } 41 | 42 | public boolean wantSend() { 43 | return mSend; 44 | } 45 | 46 | public static StreamMode get(boolean wantSend, boolean wantReceive) { 47 | if (wantSend && wantReceive) { 48 | return SEND_RECEIVE; 49 | } else if (wantSend) { 50 | return SEND_ONLY; 51 | } else if (wantReceive) { 52 | return RECEIVE_ONLY; 53 | } else { 54 | return INACTIVE; 55 | } 56 | } 57 | 58 | public StreamMode reverse(boolean wantSend, boolean wantReceive) { 59 | switch (this) { 60 | case INACTIVE: 61 | return StreamMode.INACTIVE; 62 | case SEND_ONLY: 63 | if (wantReceive) { 64 | return StreamMode.RECEIVE_ONLY; 65 | } else { 66 | return StreamMode.INACTIVE; 67 | } 68 | case RECEIVE_ONLY: 69 | if (wantSend) { 70 | return StreamMode.SEND_ONLY; 71 | } else { 72 | return StreamMode.INACTIVE; 73 | } 74 | case SEND_RECEIVE: 75 | if (wantSend && wantReceive) { 76 | return StreamMode.SEND_RECEIVE; 77 | } else if (wantSend) { 78 | return StreamMode.SEND_ONLY; 79 | } else if (wantReceive) { 80 | return StreamMode.RECEIVE_ONLY; 81 | } else { 82 | return StreamMode.INACTIVE; 83 | } 84 | default: 85 | return StreamMode.INACTIVE; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MediaSourceListenerSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.os.Handler; 29 | import android.os.Looper; 30 | 31 | import com.ericsson.research.owr.MediaSource; 32 | 33 | import java.lang.ref.WeakReference; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | class MediaSourceListenerSet { 38 | private static final String TAG = "MediaSourceListenerSet"; 39 | 40 | private final List> mListeners = new ArrayList<>(); 41 | private MediaSource mPreviousMediaSource = null; 42 | private static Handler sHandler = new Handler(Looper.getMainLooper()); 43 | 44 | public synchronized void addListener(final MediaSourceListener listener) { 45 | if (listener == null) { 46 | throw new NullPointerException("listener may not be null"); 47 | } 48 | removeListener(listener); 49 | mListeners.add(new WeakReference<>(listener)); 50 | sHandler.post(new Runnable() { 51 | @Override 52 | public void run() { 53 | listener.setMediaSource(mPreviousMediaSource); 54 | } 55 | }); 56 | } 57 | 58 | private void removeListener(MediaSourceListener removedListener) { 59 | List> removed = new ArrayList<>(); 60 | for (WeakReference ref : mListeners) { 61 | MediaSourceListener listener = ref.get(); 62 | if (listener == null || listener == removedListener) { 63 | removed.add(ref); 64 | } 65 | } 66 | mListeners.removeAll(removed); 67 | } 68 | 69 | public synchronized void notifyListeners(final MediaSource mediaSource) { 70 | mPreviousMediaSource = mediaSource; 71 | List> removed = new ArrayList<>(); 72 | for (WeakReference ref : mListeners) { 73 | final MediaSourceListener listener = ref.get(); 74 | if (listener != null) { 75 | sHandler.post(new Runnable() { 76 | @Override 77 | public void run() { 78 | listener.setMediaSource(mediaSource); 79 | } 80 | }); 81 | } else { 82 | removed.add(ref); 83 | } 84 | } 85 | mListeners.removeAll(removed); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MicrophoneSourceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.util.Log; 29 | 30 | import com.ericsson.research.owr.CaptureSourcesCallback; 31 | import com.ericsson.research.owr.MediaSource; 32 | import com.ericsson.research.owr.MediaType; 33 | import com.ericsson.research.owr.Owr; 34 | 35 | import java.util.EnumSet; 36 | import java.util.List; 37 | import java.util.concurrent.CountDownLatch; 38 | import java.util.concurrent.TimeUnit; 39 | 40 | public class MicrophoneSourceImpl extends MicrophoneSource { 41 | private static final String TAG = "MicrophoneSourceImpl"; 42 | 43 | private static final long SOURCE_CALLBACK_TIMEOUT_MS = 1000; 44 | 45 | private final MediaSourceListenerSet mListeners = new MediaSourceListenerSet(); 46 | 47 | private final MediaSource mAudioSource; 48 | 49 | MicrophoneSourceImpl(MediaSource audioSource) { 50 | mAudioSource = audioSource; 51 | mListeners.notifyListeners(audioSource); 52 | } 53 | 54 | public static MicrophoneSourceImpl create() { 55 | final MediaSource[] audioSource = new MediaSource[1]; 56 | 57 | final CountDownLatch latch = new CountDownLatch(1); 58 | Owr.getCaptureSources(EnumSet.of(MediaType.AUDIO), new CaptureSourcesCallback() { 59 | @Override 60 | public void onCaptureSourcesCallback(final List mediaSources) { 61 | if (mediaSources.isEmpty()) { 62 | Log.e(TAG, "no audio source found"); 63 | latch.countDown(); 64 | return; 65 | } else if (mediaSources.size() > 1) { 66 | Log.e(TAG, "multiple audio sources found, using the first one"); 67 | } 68 | audioSource[0] = mediaSources.get(0); 69 | latch.countDown(); 70 | } 71 | }); 72 | 73 | try { 74 | latch.await(SOURCE_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); 75 | } catch (InterruptedException e) { 76 | e.printStackTrace(); 77 | } 78 | 79 | return new MicrophoneSourceImpl(audioSource[0]); 80 | } 81 | 82 | @Override 83 | public String getName() { 84 | return mAudioSource == null ? "(null)" : mAudioSource.getName(); 85 | } 86 | 87 | @Override 88 | public void addMediaSourceListener(final MediaSourceListener delegate) { 89 | mListeners.addListener(delegate); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import com.ericsson.research.owr.HelperServerType; 29 | 30 | import java.util.Collection; 31 | import java.util.List; 32 | 33 | /** 34 | * An interface masquerading as an abstract class that allows configuration of an RtcSession. 35 | * The reason it's an abstract class is to be able to make the abstract methods package-private 36 | */ 37 | public abstract class RtcConfig { 38 | protected RtcConfig() {} 39 | 40 | /** 41 | * Implementations should return a list of payloads, ordered by most-preferred to least-preferred. 42 | * @return a list of payload 43 | */ 44 | protected abstract List getDefaultVideoPayloads(); 45 | 46 | /** 47 | * Implementations should return a list of payloads, ordered by most-preferred to least-preferred. 48 | * @return a list of payload 49 | */ 50 | protected abstract List getDefaultAudioPayloads(); 51 | 52 | /** 53 | * @return true if the send payload should be determined by the order in 54 | * which the payloads are listed in the remote session description. Otherwise the order in the local 55 | * configuration will be used. 56 | */ 57 | protected abstract boolean shouldRespectRemotePayloadOrder(); 58 | 59 | /** 60 | * Implementations should return a list of helper servers that are used for ICE. 61 | * @return a collection of helper servers 62 | */ 63 | protected abstract Collection getHelperServers(); 64 | 65 | public static class HelperServer { 66 | private final HelperServerType mType; 67 | private final String mAddress; 68 | private final int mPort; 69 | private final String mUsername; 70 | private final String mPassword; 71 | 72 | public HelperServer(HelperServerType type, String address, int port, String username, String password) { 73 | mType = type; 74 | mAddress = address; 75 | mPort = port; 76 | mUsername = username; 77 | mPassword = password; 78 | } 79 | 80 | public HelperServerType getType() { 81 | return mType; 82 | } 83 | 84 | public String getAddress() { 85 | return mAddress; 86 | } 87 | 88 | public int getPort() { 89 | return mPort; 90 | } 91 | 92 | public String getUsername() { 93 | return mUsername; 94 | } 95 | 96 | public String getPassword() { 97 | return mPassword; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/RtcConfigTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.test.AndroidTestCase; 29 | 30 | import com.ericsson.research.owr.HelperServerType; 31 | 32 | import java.util.Arrays; 33 | import java.util.List; 34 | 35 | public class RtcConfigTest extends AndroidTestCase { 36 | private static List sValidStunUrls = Arrays.asList( 37 | "stun.example.com", 38 | "stun.example.com:12345" 39 | ); 40 | 41 | private static List sInvalidStunUrls = Arrays.asList( 42 | "stun.example.com:12345/path", 43 | "stun:stun.example.com:12345", 44 | "stun:stun.example.com" 45 | ); 46 | 47 | public void testUrls() { 48 | for (String url : sValidStunUrls) { 49 | RtcConfigs.defaultConfig(url); 50 | } 51 | for (String url : sInvalidStunUrls) { 52 | try { 53 | RtcConfigs.defaultConfig(url); 54 | throw new RuntimeException("should not be reached"); 55 | } catch (IllegalArgumentException ignored) { 56 | } 57 | } 58 | } 59 | 60 | public void testDefaultConfig() { 61 | RtcConfig config = RtcConfigs.defaultConfig("stun.example.com:12345"); 62 | assertEquals(3, config.getDefaultAudioPayloads().size()); 63 | assertEquals(3, config.getDefaultVideoPayloads().size()); 64 | assertEquals(false, config.shouldRespectRemotePayloadOrder()); 65 | assertEquals(1, config.getHelperServers().size()); 66 | RtcConfig.HelperServer helper = config.getHelperServers().iterator().next(); 67 | assertEquals("stun.example.com", helper.getAddress()); 68 | assertEquals(12345, helper.getPort()); 69 | assertEquals("", helper.getUsername()); 70 | assertEquals("", helper.getPassword()); 71 | assertEquals(HelperServerType.STUN, helper.getType()); 72 | RtcConfig config2 = RtcConfigs.defaultConfig(Arrays.asList( 73 | new RtcConfig.HelperServer(HelperServerType.TURN_TCP, "turn.example.com", 1234, "asd", "123") 74 | )); 75 | RtcConfig.HelperServer helper2 = config2.getHelperServers().iterator().next(); 76 | assertEquals("turn.example.com", helper2.getAddress()); 77 | assertEquals(1234, helper2.getPort()); 78 | assertEquals("asd", helper2.getUsername()); 79 | assertEquals("123", helper2.getPassword()); 80 | assertEquals(HelperServerType.TURN_TCP, helper2.getType()); 81 | assertSame(config.getDefaultAudioPayloads(), config2.getDefaultAudioPayloads()); 82 | assertSame(config.getDefaultVideoPayloads(), config2.getDefaultVideoPayloads()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | /** 29 | * An interface representing a RTC session between two peers 30 | */ 31 | public interface RtcSession { 32 | 33 | /** 34 | * Sets the listener that will be called when a local candidate is generated. 35 | * @param listener the listener 36 | */ 37 | void setOnLocalCandidateListener(OnLocalCandidateListener listener); 38 | 39 | /** 40 | * Sets the listener that will be called once a local description is ready to be sent to the peer. 41 | * @param listener the listener 42 | */ 43 | void setOnLocalDescriptionListener(OnLocalDescriptionListener listener); 44 | 45 | /** 46 | * Start the session by getting ready to receive media, and generate a local description for the session. 47 | * If the remote description has already been set then media will sent as well. 48 | * Start can not be called twice in a row without calling stop inbetween. 49 | * @param streamSet the stream set that should be used for the call 50 | */ 51 | void start(StreamSet streamSet); 52 | 53 | /** 54 | * Sets the remote description of the session. This method should only be called once, and only after 55 | * setup has been called for outbound calls, and before setup for inbound calls. 56 | * 57 | * @param remoteDescription the SessionDescription received form the other peer. 58 | */ 59 | void setRemoteDescription(SessionDescription remoteDescription) throws InvalidDescriptionException; 60 | 61 | /** 62 | * Add a RtcCandidate that is received form the other peer. 63 | * 64 | * @param candidate the candidate to add. 65 | */ 66 | void addRemoteCandidate(RtcCandidate candidate); 67 | 68 | /** 69 | * Ends the call, this has no effect if the session isn't active. 70 | */ 71 | void stop(); 72 | 73 | /** 74 | * Dumps the current pipeline graph in dot format. 75 | * @return the pipeline graph in dot format 76 | */ 77 | String dumpPipelineGraph(); 78 | 79 | interface OnLocalCandidateListener { 80 | /** 81 | * Called when a local candidate is generated. 82 | * @param candidate a local RtcCandidate that should be sent to the other peer. 83 | */ 84 | void onLocalCandidate(RtcCandidate candidate); 85 | } 86 | 87 | interface OnLocalDescriptionListener { 88 | /** 89 | * Called once the local description is ready to be sent to the peer. 90 | * @param localDescription a SessionDescription that should be sent to the other peer. 91 | */ 92 | void onLocalDescription(SessionDescription localDescription); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/SimpleStreamSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.view.TextureView; 29 | 30 | import java.util.Collections; 31 | import java.util.concurrent.CountDownLatch; 32 | 33 | public class SimpleStreamSetTest extends OwrActivityTestCase { 34 | private static final String TAG = "SimpleStreamSetTest"; 35 | 36 | public void testSimpleCall() { 37 | RtcConfig config = RtcConfigs.defaultConfig(Collections.emptyList()); 38 | final RtcSession out = RtcSessions.create(config); 39 | final RtcSession in = RtcSessions.create(config); 40 | 41 | final SimpleStreamSet simpleStreamSetOut = SimpleStreamSet.defaultConfig(true, true); 42 | final SimpleStreamSet simpleStreamSetIn = SimpleStreamSet.defaultConfig(true, true); 43 | 44 | final TextureView textureView = getActivity().getTextureView(); 45 | final VideoView videoView = simpleStreamSetOut.createRemoteView(); 46 | videoView.setView(textureView); 47 | 48 | TestUtils.synchronous().timeout(30).run(new TestUtils.SynchronousBlock() { 49 | @Override 50 | public void run(final CountDownLatch latch) { 51 | out.setOnLocalDescriptionListener(new RtcSession.OnLocalDescriptionListener() { 52 | @Override 53 | public void onLocalDescription(final SessionDescription localDescription) { 54 | try { 55 | in.setRemoteDescription(localDescription); 56 | } catch (InvalidDescriptionException e) { 57 | throw new RuntimeException(e); 58 | } 59 | in.start(simpleStreamSetIn); 60 | } 61 | }); 62 | in.setOnLocalDescriptionListener(new RtcSession.OnLocalDescriptionListener() { 63 | @Override 64 | public void onLocalDescription(final SessionDescription localDescription) { 65 | try { 66 | out.setRemoteDescription(localDescription); 67 | } catch (InvalidDescriptionException e) { 68 | throw new RuntimeException(e); 69 | } 70 | latch.countDown(); 71 | } 72 | }); 73 | out.start(simpleStreamSetOut); 74 | } 75 | }) 76 | // FIXME source switch test is disabled, since it's too unstable 77 | /* .delay(5000, new Runnable() { 78 | @Override 79 | public void run() { 80 | TestUtils.waitForNUpdates(textureView, 5); 81 | CameraSource.getInstance().selectSource(1); 82 | } 83 | })*/ 84 | .timeout(20000).delay(5000, new Runnable() { 85 | @Override 86 | public void run() { 87 | TestUtils.waitForNUpdates(textureView, 5); 88 | videoView.stop(); 89 | } 90 | }); 91 | VideoView videoView0 = simpleStreamSetOut.createRemoteView(); 92 | VideoView videoView1 = simpleStreamSetOut.createRemoteView(); 93 | VideoView videoView2 = simpleStreamSetOut.createRemoteView(); 94 | videoView.setView(textureView); 95 | videoView2.stop(); 96 | TestUtils.waitForNUpdates(textureView, 5); 97 | videoView0.stop(); 98 | out.stop(); 99 | in.stop(); 100 | videoView1.stop(); 101 | CameraSource.getInstance().selectSource(0); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcConfigs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import com.ericsson.research.owr.HelperServerType; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Arrays; 32 | import java.util.Collection; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | 36 | public class RtcConfigs { 37 | private static final String TAG = "RtcConfigs"; 38 | 39 | private RtcConfigs() { 40 | } 41 | 42 | public static RtcConfig defaultConfig(String stunServerUrl) { 43 | return new Default(stunServerUrl); 44 | } 45 | 46 | public static RtcConfig defaultConfig(Collection helperServers) { 47 | return new Default(helperServers); 48 | } 49 | 50 | private static class Default extends RtcConfig { 51 | private static final List sDefaultVideoPayloads = new ArrayList<>(3); 52 | static { 53 | sDefaultVideoPayloads.add(new RtcPayloadImpl(103, "H264", 90000, new HashMap(){{ 54 | put("packetization-mode", 1); 55 | }}, 0, false, true, true)); 56 | // FIXME: Enable when Chrome can handle an offer with RTX for H264 57 | /* sDefaultVideoPayloads.add(new PlainRtcPayload(123, "RTX", 90000, new HashMap(){{ 58 | put("apt", 103); 59 | put("rtx-time", 200); 60 | }}, 0, false, false, false));*/ 61 | sDefaultVideoPayloads.add(new RtcPayloadImpl(100, "VP8", 90000, null, 0, true, true, true)); 62 | sDefaultVideoPayloads.add(new RtcPayloadImpl(120, "RTX", 90000, new HashMap(){{ 63 | put("apt", 100); 64 | put("rtx-time", 200); 65 | }}, 0, false, false, false)); 66 | } 67 | 68 | private static final List sDefaultAudioPayloads = new ArrayList<>(3); 69 | static { 70 | sDefaultAudioPayloads.add(new RtcPayloadImpl(111, "OPUS", 48000, null, 2, false, false, false)); 71 | sDefaultAudioPayloads.add(new RtcPayloadImpl(8, "PCMA", 8000, null, 1, false, false, false)); 72 | sDefaultAudioPayloads.add(new RtcPayloadImpl(0, "PCMU", 8000, null, 1, false, false, false)); 73 | } 74 | 75 | private final Collection mHelperServers; 76 | 77 | private Default(Collection helperServers) { 78 | mHelperServers = helperServers; 79 | } 80 | 81 | private Default(String stunServerUrl) { 82 | String[] split = stunServerUrl.split(":"); 83 | if (split.length < 1 || split.length > 2) { 84 | throw new IllegalArgumentException("invalid stun server url: " + stunServerUrl); 85 | } 86 | final int port; 87 | if (split.length == 2) { 88 | port = Integer.parseInt(split[1]); 89 | } else { 90 | port = 3478; 91 | } 92 | mHelperServers = Arrays.asList(new HelperServer(HelperServerType.STUN, split[0], port, "", "")); 93 | } 94 | 95 | @Override 96 | protected List getDefaultVideoPayloads() { 97 | return sDefaultVideoPayloads; 98 | } 99 | 100 | @Override 101 | protected List getDefaultAudioPayloads() { 102 | return sDefaultAudioPayloads; 103 | } 104 | 105 | @Override 106 | protected boolean shouldRespectRemotePayloadOrder() { 107 | return false; 108 | } 109 | 110 | @Override 111 | protected Collection getHelperServers() { 112 | return mHelperServers; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcCandidateImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import com.ericsson.research.owr.Candidate; 29 | 30 | class RtcCandidateImpl implements RtcCandidate { 31 | public static String TAG = "PlainRtcCandidate"; 32 | 33 | private int mStreamIndex; 34 | private String mStreamId; 35 | private String mUfrag; 36 | private String mPassword; 37 | private final String mFoundation; 38 | private final ComponentType mComponentType; 39 | private final TransportType mTransportType; 40 | private final int mPriority; 41 | private final String mAddress; 42 | private final int mPort; 43 | private final CandidateType mType; 44 | private final String mRelatedAddress; 45 | private final int mRelatedPort; 46 | 47 | RtcCandidateImpl(int streamIndex, String streamId, String ufrag, String password, String foundation, ComponentType componentType, TransportType transportType, int priority, String address, int port, CandidateType type, String relatedAddress, int relatedPort) { 48 | mStreamIndex = streamIndex; 49 | mStreamId = streamId; 50 | mUfrag = ufrag; 51 | mPassword = password; 52 | mFoundation = foundation; 53 | mComponentType = componentType; 54 | mTransportType = transportType; 55 | mPriority = priority; 56 | mAddress = address; 57 | mPort = port; 58 | mType = type; 59 | mRelatedAddress = relatedAddress; 60 | mRelatedPort = relatedPort; 61 | } 62 | 63 | RtcCandidateImpl(String foundation, ComponentType componentType, TransportType transportType, int priority, String address, int port, CandidateType type, String relatedAddress, int relatedPort) { 64 | this(-1, null, null, null, foundation, componentType, transportType, priority, address, port, type, relatedAddress, relatedPort); 65 | } 66 | 67 | public void setCredentials(String ufrag, String password) { 68 | mUfrag = ufrag; 69 | mPassword = password; 70 | } 71 | 72 | public void setStreamIndex(final int streamIndex) { 73 | mStreamIndex = streamIndex; 74 | } 75 | 76 | public void setStreamId(final String streamLabel) { 77 | mStreamId = streamLabel; 78 | } 79 | 80 | @Override 81 | public int getStreamIndex() { 82 | return mStreamIndex; 83 | } 84 | 85 | @Override 86 | public String getStreamId() { 87 | return mStreamId; 88 | } 89 | 90 | @Override 91 | public String getUfrag() { 92 | return mUfrag; 93 | } 94 | 95 | @Override 96 | public String getPassword() { 97 | return mPassword; 98 | } 99 | 100 | @Override 101 | public String getFoundation() { 102 | return mFoundation; 103 | } 104 | 105 | @Override 106 | public ComponentType getComponentType() { 107 | return mComponentType; 108 | } 109 | 110 | @Override 111 | public TransportType getTransportType() { 112 | return mTransportType; 113 | } 114 | 115 | @Override 116 | public int getPriority() { 117 | return mPriority; 118 | } 119 | 120 | @Override 121 | public String getAddress() { 122 | return mAddress; 123 | } 124 | 125 | @Override 126 | public int getPort() { 127 | return mPort; 128 | } 129 | 130 | @Override 131 | public CandidateType getType() { 132 | return mType; 133 | } 134 | 135 | @Override 136 | public String getRelatedAddress() { 137 | return mRelatedAddress; 138 | } 139 | 140 | @Override 141 | public int getRelatedPort() { 142 | return mRelatedPort; 143 | } 144 | 145 | static RtcCandidateImpl fromOwrCandidate(Candidate candidate) { 146 | return new RtcCandidateImpl( 147 | -1, null, 148 | candidate.getUfrag(), 149 | candidate.getPassword(), 150 | candidate.getFoundation(), 151 | ComponentType.valueOf(candidate.getComponentType().name()), 152 | TransportType.valueOf(candidate.getTransportType().name()), 153 | candidate.getPriority(), 154 | candidate.getAddress(), 155 | candidate.getPort(), 156 | CandidateType.valueOf(candidate.getType().name()), 157 | candidate.getBaseAddress(), 158 | candidate.getBasePort() 159 | ); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/VideoViewImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.graphics.SurfaceTexture; 29 | import android.view.Surface; 30 | import android.view.TextureView; 31 | 32 | import com.ericsson.research.owr.MediaSource; 33 | import com.ericsson.research.owr.VideoRenderer; 34 | import com.ericsson.research.owr.WindowRegistry; 35 | 36 | public class VideoViewImpl implements VideoView, MediaSourceListener { 37 | private static final String TAG = "VideoViewImpl"; 38 | 39 | private TextureViewTagger mTextureViewTagger; 40 | private VideoRenderer mVideoRenderer; 41 | private MediaSource mVideoSource; 42 | private final String mTag; 43 | 44 | VideoViewImpl(MediaSourceProvider mediaSourceProvider, int width, int height, double framerate) { 45 | mVideoSource = null; 46 | 47 | mTag = Utils.randomString(32); 48 | 49 | mVideoRenderer = new VideoRenderer(mTag); 50 | mVideoRenderer.setRotation(0); 51 | if (width > 0) { 52 | mVideoRenderer.setWidth(width); 53 | } 54 | if (height > 0) { 55 | mVideoRenderer.setHeight(height); 56 | } 57 | if (framerate > 0) { 58 | mVideoRenderer.setMaxFramerate(framerate); 59 | } 60 | mediaSourceProvider.addMediaSourceListener(this); 61 | } 62 | 63 | @Override 64 | public void setRotation(final int rotation) { 65 | if (rotation < 0 || rotation > 3) { 66 | throw new IllegalArgumentException(rotation + " is an invalid rotation, must be between 0 and 3"); 67 | } 68 | mVideoRenderer.setRotation(rotation); 69 | } 70 | 71 | @Override 72 | public int getRotation() { 73 | return mVideoRenderer.getRotation(); 74 | } 75 | 76 | @Override 77 | public void setMirrored(final boolean mirrored) { 78 | mVideoRenderer.setMirror(mirrored); 79 | } 80 | 81 | @Override 82 | public boolean isMirrored() { 83 | return mVideoRenderer.getMirror(); 84 | } 85 | 86 | @Override 87 | public synchronized void setMediaSource(final MediaSource mediaSource) { 88 | mVideoSource = mediaSource; 89 | mVideoRenderer.setSource(mediaSource); 90 | } 91 | 92 | private boolean viewIsActive() { 93 | return mTextureViewTagger != null; 94 | } 95 | 96 | public synchronized void setView(TextureView textureView) { 97 | if (textureView == null) { 98 | throw new NullPointerException("texture view may not be null"); 99 | } 100 | if (!viewIsActive() && mVideoSource != null) { 101 | mVideoRenderer.setSource(mVideoSource); 102 | } 103 | stopViewTagger(); 104 | mTextureViewTagger = new TextureViewTagger(mTag, textureView); 105 | } 106 | 107 | public synchronized void stop() { 108 | stopViewTagger(); 109 | mVideoRenderer.setSource(null); 110 | } 111 | 112 | private void stopViewTagger() { 113 | if (mTextureViewTagger != null) { 114 | mTextureViewTagger.stop(); 115 | mTextureViewTagger = null; 116 | } 117 | } 118 | 119 | private static class TextureViewTagger implements TextureView.SurfaceTextureListener { 120 | private final String mTag; 121 | private TextureView mTextureView; 122 | 123 | private TextureViewTagger(String tag, TextureView textureView) { 124 | mTag = tag; 125 | mTextureView = textureView; 126 | if (textureView.isAvailable()) { 127 | Surface surface = new Surface(textureView.getSurfaceTexture()); 128 | WindowRegistry.get().register(mTag, surface); 129 | } 130 | mTextureView.setSurfaceTextureListener(this); 131 | } 132 | 133 | private synchronized void stop() { 134 | mTextureView.setSurfaceTextureListener(null); 135 | WindowRegistry.get().unregister(mTag); 136 | } 137 | 138 | @Override 139 | public synchronized void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { 140 | Surface surface = new Surface(surfaceTexture); 141 | WindowRegistry.get().register(mTag, surface); 142 | } 143 | 144 | @Override 145 | public synchronized boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 146 | WindowRegistry.get().unregister(mTag); 147 | return true; 148 | } 149 | 150 | @Override 151 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 152 | } 153 | 154 | @Override 155 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MutableStreamDescription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.Collections; 29 | import java.util.LinkedList; 30 | import java.util.List; 31 | 32 | public class MutableStreamDescription implements StreamDescription { 33 | private StreamType mType; 34 | private StreamMode mMode; 35 | private String mUfrag; 36 | private String mPassword; 37 | private List mCandidates = new LinkedList<>(); 38 | private String mDtlsSetup; 39 | private String mFingerprint; 40 | private String mFingerprintHashFunction; 41 | private String mMediaStreamId; 42 | private String mMediaStreamTrackId; 43 | private String mCname; 44 | private boolean mRtcpMux; 45 | private List mSsrcs = new LinkedList<>(); 46 | private List mPayloads = new LinkedList<>(); 47 | private int mSctpPort; 48 | private int mSctpStreamCount; 49 | private String mAppLabel; 50 | 51 | @Override 52 | public StreamType getType() { 53 | return mType; 54 | } 55 | 56 | public void setType(final StreamType type) { 57 | mType = type; 58 | } 59 | 60 | @Override 61 | public StreamMode getMode() { 62 | return mMode; 63 | } 64 | 65 | public void setMode(final StreamMode mode) { 66 | mMode = mode; 67 | } 68 | 69 | @Override 70 | public String getUfrag() { 71 | return mUfrag; 72 | } 73 | 74 | public void setUfrag(final String ufrag) { 75 | mUfrag = ufrag; 76 | } 77 | 78 | @Override 79 | public String getPassword() { 80 | return mPassword; 81 | } 82 | 83 | public void setPassword(final String password) { 84 | mPassword = password; 85 | } 86 | 87 | @Override 88 | public List getCandidates() { 89 | return Collections.unmodifiableList(mCandidates); 90 | } 91 | 92 | public void addCandidate(RtcCandidate candidate) { 93 | mCandidates.add(candidate); 94 | } 95 | 96 | @Override 97 | public String getDtlsSetup() { 98 | return mDtlsSetup; 99 | } 100 | 101 | public void setDtlsSetup(final String dtlsSetup) { 102 | mDtlsSetup = dtlsSetup; 103 | } 104 | 105 | @Override 106 | public String getFingerprint() { 107 | return mFingerprint; 108 | } 109 | 110 | public void setFingerprint(final String fingerprint) { 111 | mFingerprint = fingerprint; 112 | } 113 | 114 | @Override 115 | public String getFingerprintHashFunction() { 116 | return mFingerprintHashFunction; 117 | } 118 | 119 | public void setFingerprintHashFunction(final String fingerprintHashFunction) { 120 | mFingerprintHashFunction = fingerprintHashFunction; 121 | } 122 | 123 | @Override 124 | public String getMediaStreamId() { 125 | return mMediaStreamId; 126 | } 127 | 128 | public void setMediaStreamId(final String mediaStreamId) { 129 | mMediaStreamId = mediaStreamId; 130 | } 131 | 132 | @Override 133 | public String getMediaStreamTrackId() { 134 | return mMediaStreamTrackId; 135 | } 136 | 137 | public void setMediaStreamTrackId(final String mediaStreamTrackId) { 138 | mMediaStreamTrackId = mediaStreamTrackId; 139 | } 140 | 141 | @Override 142 | public String getCname() { 143 | return mCname; 144 | } 145 | 146 | public void setCname(final String cname) { 147 | mCname = cname; 148 | } 149 | 150 | @Override 151 | public boolean isRtcpMux() { 152 | return mRtcpMux; 153 | } 154 | 155 | public void setRtcpMux(final boolean rtcpMux) { 156 | mRtcpMux = rtcpMux; 157 | } 158 | 159 | @Override 160 | public List getSsrcs() { 161 | return Collections.unmodifiableList(mSsrcs); 162 | } 163 | 164 | public void addSsrc(final long ssrc) { 165 | mSsrcs.add(ssrc); 166 | } 167 | 168 | @Override 169 | public List getPayloads() { 170 | return Collections.unmodifiableList(mPayloads); 171 | } 172 | 173 | public void addPayload(RtcPayload payload) { 174 | mPayloads.add(payload); 175 | } 176 | 177 | @Override 178 | public int getSctpPort() { 179 | return mSctpPort; 180 | } 181 | 182 | public void setSctpPort(final int sctpPort) { 183 | mSctpPort = sctpPort; 184 | } 185 | 186 | @Override 187 | public int getSctpStreamCount() { 188 | return mSctpStreamCount; 189 | } 190 | 191 | public void setSctpStreamCount(final int sctpStreamCount) { 192 | mSctpStreamCount = sctpStreamCount; 193 | } 194 | 195 | @Override 196 | public String getAppLabel() { 197 | return mAppLabel; 198 | } 199 | 200 | public void setAppLabel(final String appLabel) { 201 | mAppLabel = appLabel; 202 | } 203 | } -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamDescriptionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import java.util.Collections; 29 | import java.util.List; 30 | 31 | class StreamDescriptionImpl implements StreamDescription { 32 | public static final String TAG = "StreamDescriptionImpl"; 33 | 34 | private final StreamType mType; 35 | private final StreamMode mMode; 36 | private final String mUfrag; 37 | private final String mPassword; 38 | private final List mCandidates; 39 | private final String mDtlsSetup; 40 | private final String mFingerprint; 41 | private final String mFingerprintHashFunction; 42 | // media only 43 | private final String mMediaStreamId; 44 | private final String mMediaStreamTrackId; 45 | private final String mCname; 46 | private final boolean mRtcpMux; 47 | private final List mSsrcs; 48 | private final List mPayloads; 49 | // data only 50 | private final int mSctpPort; 51 | private final int mSctpStreamCount; 52 | private final String mAppLabel; 53 | 54 | private StreamDescriptionImpl(StreamType streamType, StreamMode mode, String ufrag, String password, List candidates, String dtlsSetup, String fingerprint, String fingerprintHashFunction, String mediaStreamId, String mediaStreamTrackId, String cname, boolean rtcpMux, List ssrcs, List payloads, int sctpPort, int sctpStreamCount, String appLabel) { 55 | mType = streamType; 56 | mMode = mode; 57 | mUfrag = ufrag; 58 | mPassword = password; 59 | if (candidates == null) { 60 | mCandidates = Collections.emptyList(); 61 | } else { 62 | mCandidates = Collections.unmodifiableList(candidates); 63 | } 64 | mDtlsSetup = dtlsSetup; 65 | mFingerprint = fingerprint; 66 | mFingerprintHashFunction = fingerprintHashFunction; 67 | mMediaStreamId = mediaStreamId; 68 | mMediaStreamTrackId = mediaStreamTrackId; 69 | mCname = cname; 70 | mRtcpMux = rtcpMux; 71 | if (ssrcs == null) { 72 | mSsrcs = Collections.emptyList(); 73 | } else { 74 | mSsrcs = Collections.unmodifiableList(ssrcs); 75 | } 76 | if (payloads == null) { 77 | mPayloads = Collections.emptyList(); 78 | } else { 79 | mPayloads = Collections.unmodifiableList(payloads); 80 | } 81 | mSctpPort = sctpPort; 82 | mSctpStreamCount = sctpStreamCount; 83 | mAppLabel = appLabel; 84 | } 85 | 86 | StreamDescriptionImpl(StreamType streamType, StreamMode mode, String ufrag, String password, List candidates, String dtlsSetup, String fingerprint, String fingerprintHashFunction, String mediaStreamId, String mediaStreamTrackId, String cname, boolean rtcpMux, List ssrcs, List payloads) { 87 | this(streamType, mode, ufrag, password, candidates, dtlsSetup, fingerprint, fingerprintHashFunction, mediaStreamId, mediaStreamTrackId, cname, rtcpMux, ssrcs, payloads, -1, -1, null); 88 | } 89 | 90 | StreamDescriptionImpl(StreamType streamType, StreamMode mode, String ufrag, String password, List candidates, String dtlsSetup, String fingerprint, String fingerprintHashFunction, int sctpPort, int sctpStreamCount, String appLabel) { 91 | this(streamType, mode, ufrag, password, candidates, dtlsSetup, fingerprint, fingerprintHashFunction, null, null, null, false, null, null, sctpPort, sctpStreamCount, appLabel); 92 | } 93 | 94 | @Override 95 | public StreamType getType() { 96 | return mType; 97 | } 98 | 99 | @Override 100 | public StreamMode getMode() { 101 | return mMode; 102 | } 103 | 104 | @Override 105 | public String getUfrag() { 106 | return mUfrag; 107 | } 108 | 109 | @Override 110 | public String getPassword() { 111 | return mPassword; 112 | } 113 | 114 | @Override 115 | public List getCandidates() { 116 | return mCandidates; 117 | } 118 | 119 | @Override 120 | public String getDtlsSetup() { 121 | return mDtlsSetup; 122 | } 123 | 124 | @Override 125 | public String getFingerprint() { 126 | return mFingerprint; 127 | } 128 | 129 | @Override 130 | public String getFingerprintHashFunction() { 131 | return mFingerprintHashFunction; 132 | } 133 | 134 | @Override 135 | public String getMediaStreamId() { 136 | return mMediaStreamId; 137 | } 138 | 139 | @Override 140 | public String getMediaStreamTrackId() { 141 | return mMediaStreamTrackId; 142 | } 143 | 144 | @Override 145 | public String getCname() { 146 | return mCname; 147 | } 148 | 149 | public boolean isRtcpMux() { 150 | return mRtcpMux; 151 | } 152 | 153 | @Override 154 | public List getSsrcs() { 155 | return mSsrcs; 156 | } 157 | 158 | @Override 159 | public List getPayloads() { 160 | return mPayloads; 161 | } 162 | 163 | @Override 164 | public int getSctpPort() { 165 | return mSctpPort; 166 | } 167 | 168 | @Override 169 | public int getSctpStreamCount() { 170 | return mSctpStreamCount; 171 | } 172 | 173 | @Override 174 | public String getAppLabel() { 175 | return mAppLabel; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/SimpleStreamSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.util.Log; 29 | 30 | import com.ericsson.research.owr.AudioRenderer; 31 | import com.ericsson.research.owr.MediaSource; 32 | import com.ericsson.research.owr.MediaType; 33 | 34 | import java.util.Arrays; 35 | import java.util.List; 36 | 37 | public class SimpleStreamSet extends StreamSet { 38 | private static final String TAG = "SimpleStreamSet"; 39 | 40 | private final boolean mWantVideo; 41 | private final boolean mWantAudio; 42 | 43 | private final AudioRenderer mAudioRenderer; 44 | 45 | private final SimpleMediaStream mAudioStream; 46 | private final SimpleMediaStream mVideoStream; 47 | 48 | private VideoSourceProvider mRemoteVideoSourceProvider = new VideoSourceProvider(); 49 | 50 | private SimpleStreamSet(MediaSourceProvider audioSourceProvider, MediaSourceProvider videoSourceProvider, 51 | boolean sendAudio, boolean sendVideo) { 52 | mWantAudio = sendAudio; 53 | mWantVideo = sendVideo; 54 | 55 | mAudioRenderer = new AudioRenderer(); 56 | 57 | mAudioStream = new SimpleMediaStream(false); 58 | mVideoStream = new SimpleMediaStream(true); 59 | 60 | videoSourceProvider.addMediaSourceListener(new MediaSourceListener() { 61 | @Override 62 | public void setMediaSource(final MediaSource mediaSource) { 63 | mVideoStream.setMediaSource(mediaSource); 64 | } 65 | }); 66 | 67 | audioSourceProvider.addMediaSourceListener(new MediaSourceListener() { 68 | @Override 69 | public void setMediaSource(final MediaSource mediaSource) { 70 | mAudioStream.setMediaSource(mediaSource); 71 | } 72 | }); 73 | } 74 | 75 | private class VideoSourceProvider implements MediaSourceProvider { 76 | private MediaSourceListenerSet set = new MediaSourceListenerSet(); 77 | 78 | public void notifyListeners(MediaSource mediaSource) { 79 | set.notifyListeners(mediaSource); 80 | } 81 | 82 | @Override 83 | public void addMediaSourceListener(final MediaSourceListener listener) { 84 | set.addListener(listener); 85 | } 86 | } 87 | 88 | public VideoView createRemoteView() { 89 | return new VideoViewImpl(mRemoteVideoSourceProvider, 0, 0, 0); 90 | } 91 | 92 | /** 93 | * Creates a configuration for setting up a basic audio/video call. 94 | * 95 | * @param sendAudio true if audio should be sent, audio may still be received 96 | * @param sendVideo true if video should be sent, video may still be received 97 | * @return a new RtcConfig with a simple audio/video call configuration 98 | */ 99 | public static SimpleStreamSet defaultConfig(boolean sendAudio, boolean sendVideo) { 100 | return new SimpleStreamSet(MicrophoneSource.getInstance(), CameraSource.getInstance(), sendAudio, sendVideo); 101 | } 102 | 103 | @Override 104 | protected List getStreams() { 105 | return Arrays.asList(mAudioStream, mVideoStream); 106 | } 107 | 108 | /** 109 | * @return the current audio renderer pipeline graph in dot format. 110 | */ 111 | public String dumpPipelineGraph() { 112 | return mAudioRenderer.getDotData(); 113 | } 114 | 115 | private class SimpleMediaStream extends MediaStream { 116 | private final String mId; 117 | private final boolean mIsVideo; 118 | private MediaSource mMediaSource; 119 | private MediaSourceDelegate mMediaSourceDelegate; 120 | 121 | private SimpleMediaStream(boolean isVideo) { 122 | mIsVideo = isVideo; 123 | mId = Utils.randomString(27); 124 | } 125 | 126 | @Override 127 | protected String getId() { 128 | return mId; 129 | } 130 | 131 | @Override 132 | protected MediaType getMediaType() { 133 | return mIsVideo ? MediaType.VIDEO : MediaType.AUDIO; 134 | } 135 | 136 | @Override 137 | protected boolean wantSend() { 138 | return mIsVideo ? mWantVideo : mWantAudio; 139 | } 140 | 141 | @Override 142 | protected boolean wantReceive() { 143 | return true; 144 | } 145 | 146 | @Override 147 | protected void onRemoteMediaSource(final MediaSource mediaSource) { 148 | if (mIsVideo) { 149 | mRemoteVideoSourceProvider.notifyListeners(mediaSource); 150 | } else { 151 | mAudioRenderer.setSource(mediaSource); 152 | } 153 | } 154 | 155 | @Override 156 | protected synchronized void setMediaSourceDelegate(final MediaSourceDelegate mediaSourceDelegate) { 157 | mMediaSourceDelegate = mediaSourceDelegate; 158 | if (mMediaSource != null && mediaSourceDelegate != null) { 159 | mediaSourceDelegate.setMediaSource(mMediaSource); 160 | } 161 | } 162 | 163 | @Override 164 | public void setStreamMode(final StreamMode mode) { 165 | Log.i(TAG, (mIsVideo ? "video" : "audio") + " stream mode set: " + mode.name()); 166 | } 167 | 168 | public synchronized void setMediaSource(final MediaSource mediaSource) { 169 | mMediaSource = mediaSource; 170 | if (mMediaSourceDelegate != null) { 171 | mMediaSourceDelegate.setMediaSource(mediaSource); 172 | } 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import com.ericsson.research.owr.DataChannel; 29 | import com.ericsson.research.owr.MediaSource; 30 | import com.ericsson.research.owr.MediaType; 31 | 32 | import java.util.List; 33 | 34 | /** 35 | * An interface masquerading as an abstract class that represents a set of stream to attach to a RtcSession 36 | * The reason it's an abstract class is to be able to make the abstract methods package-private 37 | */ 38 | public abstract class StreamSet { 39 | protected StreamSet() {} 40 | 41 | /** 42 | * An interface that represents a single audio, video or data stream. 43 | * The stream interface Stream should not be implemented directly, but through the MediaStream and DataStream interfaces. 44 | */ 45 | protected interface Stream { 46 | /** 47 | * @return the type of the stream 48 | */ 49 | StreamType getType(); 50 | 51 | /** 52 | * Called once the final mode has been determined for the stream 53 | * @param mode of the stream 54 | */ 55 | void setStreamMode(StreamMode mode); 56 | } 57 | 58 | /** 59 | * An interface that represents a single audio or video stream. 60 | */ 61 | protected abstract class MediaStream implements Stream { 62 | @Override 63 | public StreamType getType() { 64 | return getMediaType() == MediaType.AUDIO ? StreamType.AUDIO : StreamType.VIDEO; 65 | } 66 | 67 | /** 68 | * Implementations should return a unique identifier for the stream, or null. 69 | * @return a unique identifier, or null 70 | */ 71 | protected abstract String getId(); 72 | 73 | /** 74 | * Implementations should return the media type of the stream. 75 | * @return the media type of the stream 76 | */ 77 | protected abstract MediaType getMediaType(); 78 | 79 | /** 80 | * If the implementation returns false no local stream will be sent, event if one is requested by the peer. 81 | * @return false if no media should be sent, true otherwise 82 | */ 83 | protected abstract boolean wantSend(); 84 | 85 | /** 86 | * If the implementation return false no remote stream will be requested 87 | * A remote stream might still be received though, in which case it can then be ignored. 88 | * @return true if remote media should be requested, false otherwise 89 | */ 90 | protected abstract boolean wantReceive(); 91 | 92 | /** 93 | * This method is called when the media source for the stream is received 94 | * @param mediaSource a media source matching the media type of the stream 95 | */ 96 | protected abstract void onRemoteMediaSource(MediaSource mediaSource); 97 | 98 | /** 99 | * Implementations should use the media source delegate to set the media source 100 | * for the stream. The delegate can be called at any time, but may be ignored if 101 | * the stream is not active. The most recent stream to be sent to the delegate 102 | * is always the one that will be used. 103 | * The media source type type should be the same as the media type of the stream. 104 | * @param mediaSourceDelegate the delegate to use to set the media source. 105 | */ 106 | protected abstract void setMediaSourceDelegate(MediaSourceDelegate mediaSourceDelegate); 107 | } 108 | 109 | /** 110 | * An interface that represents a data stream. 111 | */ 112 | protected abstract class DataStream implements Stream { 113 | @Override 114 | public StreamType getType() { 115 | return StreamType.DATA; 116 | } 117 | 118 | /** 119 | * This method is called when a data channel is requested from the peer. 120 | * If true is returned the data channel is added to the stream and is ready to be used. 121 | * If false is returned the data channel is ignored. It is possible to return false and then 122 | * add the data channel later using the data channel delegate. 123 | * 124 | * @param dataChannel the data channel that has been requested 125 | * @return true if the data channel should be added to the stream 126 | */ 127 | protected abstract boolean onDataChannelReceived(DataChannel dataChannel); 128 | 129 | /** 130 | * Implementations should use the data channel delegate to add data channels 131 | * to the data stream. The delegate can be called at any time, but may be ignored if 132 | * the stream is not active. 133 | * @param dataChannelDelegate the delegate to use to add data channels. 134 | */ 135 | protected abstract void setDataChannelDelegate(DataChannelDelegate dataChannelDelegate); 136 | } 137 | 138 | /** 139 | * Implementations should return a list of streams that are sent and/or received. 140 | * The streams should be ordered in highest-to-lowest priority. If enough streams of a particular 141 | * type are not received in the remote description, the excess streams will be invalidated, beginning 142 | * at the end of the list. 143 | * @return a list of streams 144 | */ 145 | protected abstract List getStreams(); 146 | 147 | public interface MediaSourceDelegate { 148 | void setMediaSource(MediaSource mediaSource); 149 | } 150 | 151 | public interface DataChannelDelegate { 152 | void addDataChannel(DataChannel dataChannel); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/CameraSourceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.os.Handler; 29 | import android.os.Looper; 30 | import android.util.Log; 31 | 32 | import com.ericsson.research.owr.CaptureSourcesCallback; 33 | import com.ericsson.research.owr.MediaSource; 34 | import com.ericsson.research.owr.MediaType; 35 | import com.ericsson.research.owr.Owr; 36 | 37 | import java.util.ArrayList; 38 | import java.util.EnumSet; 39 | import java.util.List; 40 | import java.util.concurrent.CountDownLatch; 41 | import java.util.concurrent.TimeUnit; 42 | 43 | class CameraSourceImpl extends CameraSource { 44 | private static final String TAG = "LocalMediaSourceImpl"; 45 | 46 | private static final long SOURCE_CALLBACK_TIMEOUT_MS = 1000; 47 | private static final long CAMERA_CLOSE_DURATION_MS = 1000; 48 | private static final long CAMERA_OPEN_DURATION_MS = 8000; 49 | 50 | private final Handler mHandler = new Handler(Looper.getMainLooper()); 51 | private MediaSourceListenerSet mListeners = new MediaSourceListenerSet(); 52 | 53 | private final List mVideoSources; 54 | private int mVideoSourceIndex; 55 | private int mActiveVideoSourceIndex; 56 | 57 | private VideoSourceState mVideoSourceState; 58 | 59 | private CameraSourceImpl(List videoSources) { 60 | mVideoSources = videoSources; 61 | mVideoSourceState = VideoSourceState.READY; 62 | 63 | mVideoSourceIndex = 0; 64 | mActiveVideoSourceIndex = 0; 65 | mListeners.notifyListeners(getSelectedMediaSource()); 66 | } 67 | 68 | @Override 69 | public VideoView createVideoView() { 70 | return new VideoViewImpl(this, 0, 0, 0); 71 | } 72 | 73 | @Override 74 | public int getCount() { 75 | return mVideoSources.size(); 76 | } 77 | 78 | @Override 79 | public String getName(final int index) { 80 | checkIndex(index); 81 | return mVideoSources.get(index).getName(); 82 | } 83 | 84 | private void checkIndex(int index) { 85 | int count = getCount(); 86 | if (index >= count) { 87 | throw new IndexOutOfBoundsException("invalid camera index, " + index + " >= " + count); 88 | } 89 | if (index < 0) { 90 | throw new IndexOutOfBoundsException("invalid camera index, " + index + " < 0 "); 91 | } 92 | } 93 | 94 | @Override 95 | public List dumpPipelineGraphs() { 96 | List result = new ArrayList<>(); 97 | for (MediaSource videoSource : mVideoSources) { 98 | result.add(videoSource.getDotData()); 99 | } 100 | return result; 101 | } 102 | 103 | @Override 104 | public synchronized void selectSource(final int index) { 105 | checkIndex(index); 106 | mVideoSourceIndex = index; 107 | if (mActiveVideoSourceIndex == index) { 108 | return; 109 | } 110 | 111 | if (mVideoSourceState != VideoSourceState.READY) { 112 | Log.d(TAG, "could not set video source index directly in state " + mVideoSourceState + ", queueing switch"); 113 | return; 114 | } 115 | 116 | mListeners.notifyListeners(null); 117 | mVideoSourceState = VideoSourceState.CLOSING; 118 | 119 | final Runnable onCameraOpened = new Runnable() { 120 | @Override 121 | public void run() { 122 | synchronized (CameraSourceImpl.this) { 123 | mVideoSourceState = VideoSourceState.READY; 124 | if (mActiveVideoSourceIndex != mVideoSourceIndex) { 125 | selectSource(mVideoSourceIndex); 126 | } 127 | } 128 | } 129 | }; 130 | 131 | final Runnable onCameraClosed = new Runnable() { 132 | @Override 133 | public void run() { 134 | synchronized (CameraSourceImpl.this) { 135 | mActiveVideoSourceIndex = mVideoSourceIndex; 136 | MediaSource videoSource = getSelectedMediaSource(); 137 | mListeners.notifyListeners(videoSource); 138 | mVideoSourceState = VideoSourceState.OPENING; 139 | mHandler.postDelayed(onCameraOpened, CAMERA_OPEN_DURATION_MS); 140 | } 141 | } 142 | }; 143 | 144 | mHandler.postDelayed(onCameraClosed, CAMERA_CLOSE_DURATION_MS); 145 | } 146 | 147 | @Override 148 | public synchronized void addMediaSourceListener(final MediaSourceListener listener) { 149 | mListeners.addListener(listener); 150 | } 151 | 152 | public enum VideoSourceState { 153 | READY, CLOSING, OPENING 154 | } 155 | 156 | VideoSourceState getVideoSourceState() { 157 | return mVideoSourceState; 158 | } 159 | 160 | public int getActiveSource() { 161 | return mActiveVideoSourceIndex; 162 | } 163 | 164 | public int getSelectedSource() { 165 | return mVideoSourceIndex; 166 | } 167 | 168 | private MediaSource getSelectedMediaSource() { 169 | if (!mVideoSources.isEmpty()) { 170 | return mVideoSources.get(mVideoSourceIndex); 171 | } 172 | return null; 173 | } 174 | 175 | public static CameraSourceImpl create() { 176 | final List videoSources = new ArrayList<>(); 177 | 178 | final CountDownLatch latch = new CountDownLatch(1); 179 | Owr.getCaptureSources(EnumSet.of(MediaType.VIDEO), new CaptureSourcesCallback() { 180 | @Override 181 | public void onCaptureSourcesCallback(final List mediaSources) { 182 | videoSources.addAll(mediaSources); 183 | if (mediaSources.isEmpty()) { 184 | Log.e(TAG, "no video sources found"); 185 | } 186 | latch.countDown(); 187 | } 188 | }); 189 | 190 | try { 191 | latch.await(SOURCE_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); 192 | } catch (InterruptedException e) { 193 | e.printStackTrace(); 194 | } 195 | 196 | return new CameraSourceImpl(videoSources); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/TestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.graphics.SurfaceTexture; 29 | import android.os.Handler; 30 | import android.os.HandlerThread; 31 | import android.view.TextureView; 32 | 33 | import java.util.concurrent.CountDownLatch; 34 | import java.util.concurrent.TimeUnit; 35 | 36 | public class TestUtils { 37 | public static class SynchronousCallBuilder { 38 | private int mTimeout; 39 | private int mLatchCount; 40 | private Handler mAsyncHandler; 41 | 42 | private SynchronousCallBuilder(Handler asyncHandler) { 43 | mTimeout = 5; 44 | mLatchCount = 1; 45 | if (asyncHandler != null) { 46 | mAsyncHandler = asyncHandler; 47 | } else { 48 | HandlerThread thread = new HandlerThread("TestUtils-async-thread"); 49 | thread.start(); 50 | mAsyncHandler = new Handler(thread.getLooper()); 51 | } 52 | } 53 | 54 | public SynchronousCallBuilder timeout(int seconds) { 55 | mTimeout = seconds; 56 | return this; 57 | } 58 | 59 | public SynchronousCallBuilder latchCount(int count) { 60 | mLatchCount = count; 61 | return this; 62 | } 63 | 64 | public SynchronousCallBuilder run(final SynchronousBlock block) { 65 | final CountDownLatch latch = new CountDownLatch(mLatchCount); 66 | block.run(latch); 67 | waitForLatch(latch, 0); 68 | return new SynchronousCallBuilder(mAsyncHandler); 69 | } 70 | 71 | public SynchronousCallBuilder run(final Runnable runnable) { 72 | final CountDownLatch latch = new CountDownLatch(mLatchCount); 73 | mAsyncHandler.post(new Runnable() { 74 | @Override 75 | public void run() { 76 | runnable.run(); 77 | latch.countDown(); 78 | } 79 | }); 80 | waitForLatch(latch, 0); 81 | return new SynchronousCallBuilder(mAsyncHandler); 82 | } 83 | 84 | public SynchronousCallBuilder delay(int delay, final Runnable runnable) { 85 | if (delay <= 0) { 86 | throw new IllegalArgumentException("delay should be >= 0"); 87 | } 88 | final CountDownLatch latch = new CountDownLatch(1); 89 | mAsyncHandler.postDelayed(new Runnable() { 90 | @Override 91 | public void run() { 92 | runnable.run(); 93 | latch.countDown(); 94 | } 95 | }, delay); 96 | waitForLatch(latch, delay); 97 | return new SynchronousCallBuilder(mAsyncHandler); 98 | } 99 | 100 | public SynchronousCallBuilder delay(int delay, final SynchronousBlock block) { 101 | if (delay <= 0) { 102 | throw new IllegalArgumentException("delay should be >= 0"); 103 | } 104 | final CountDownLatch latch = new CountDownLatch(mLatchCount); 105 | mAsyncHandler.postDelayed(new Runnable() { 106 | @Override 107 | public void run() { 108 | block.run(latch); 109 | } 110 | }, delay); 111 | waitForLatch(latch, delay); 112 | return new SynchronousCallBuilder(mAsyncHandler); 113 | } 114 | 115 | private void waitForLatch(CountDownLatch latch, int extraMilliseconds) { 116 | try { 117 | if (!latch.await(extraMilliseconds + mTimeout * 1000, TimeUnit.MILLISECONDS)) { 118 | throw new RuntimeException("synchronous block timed out"); 119 | } 120 | } catch (InterruptedException e) { 121 | throw new RuntimeException(e); 122 | } 123 | } 124 | } 125 | 126 | public static void sleep(int milliseconds) { 127 | try { 128 | Thread.sleep(milliseconds); 129 | } catch (InterruptedException e) { 130 | throw new RuntimeException(e); 131 | } 132 | } 133 | 134 | public static SynchronousCallBuilder synchronous() { 135 | return new SynchronousCallBuilder(null); 136 | } 137 | 138 | public interface SynchronousBlock { 139 | void run(CountDownLatch latch); 140 | } 141 | 142 | public static void waitForNUpdates(final TextureView textureView, int count) { 143 | TextureView.SurfaceTextureListener previousListener = textureView.getSurfaceTextureListener(); 144 | final TextureViewAsserter textureViewAsserter = new TextureViewAsserter(previousListener); 145 | textureView.setSurfaceTextureListener(textureViewAsserter); 146 | TestUtils.synchronous().latchCount(count).timeout(15).run(new TestUtils.SynchronousBlock() { 147 | @Override 148 | public void run(final CountDownLatch latch) { 149 | textureViewAsserter.waitForUpdates(latch); 150 | } 151 | }); 152 | textureView.setSurfaceTextureListener(previousListener); 153 | } 154 | 155 | private static class TextureViewAsserter implements TextureView.SurfaceTextureListener { 156 | private final TextureView.SurfaceTextureListener mListener; 157 | private CountDownLatch mLatch; 158 | 159 | public TextureViewAsserter(final TextureView.SurfaceTextureListener surfaceTextureListener) { 160 | mListener = surfaceTextureListener; 161 | } 162 | 163 | public void waitForUpdates(CountDownLatch latch) { 164 | mLatch = latch; 165 | } 166 | 167 | @Override 168 | public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { 169 | mListener.onSurfaceTextureAvailable(surface, width, height); 170 | } 171 | 172 | @Override 173 | public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { 174 | mListener.onSurfaceTextureSizeChanged(surface, width, height); 175 | } 176 | 177 | @Override 178 | public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { 179 | return mListener.onSurfaceTextureDestroyed(surface); 180 | } 181 | 182 | @Override 183 | public void onSurfaceTextureUpdated(final SurfaceTexture surface) { 184 | if (mLatch != null) { 185 | mLatch.countDown(); 186 | } 187 | mListener.onSurfaceTextureUpdated(surface); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/DataStreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | 29 | import android.util.Log; 30 | 31 | import com.ericsson.research.owr.DataChannel; 32 | import com.ericsson.research.owr.DataSession; 33 | import com.ericsson.research.owr.Session; 34 | 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | class DataStreamHandler extends StreamHandler implements DataSession.OnDataChannelRequestedListener, StreamSet.DataChannelDelegate { 39 | private static final String TAG = "DataStreamHandler"; 40 | 41 | private static final int BASE_PORT = 5000; 42 | 43 | private final List mDataChannels = new ArrayList<>(); 44 | 45 | private Session.DtlsKeyChangeListener mDtlsKeyChangeListener = new Session.DtlsKeyChangeListener() { 46 | @Override 47 | public void onDtlsKeyChanged(final String s) { 48 | getMainHandler().post(new Runnable() { 49 | @Override 50 | public void run() { 51 | getStream().setStreamMode(StreamMode.SEND_RECEIVE); 52 | } 53 | }); 54 | if (getSession() != null) { 55 | getSession().removeDtlsKeyChangeListener(this); 56 | } 57 | mDtlsKeyChangeListener = null; 58 | } 59 | }; 60 | 61 | public DataStreamHandler(int index, StreamDescription streamDescription, StreamSet.DataStream dataStream) { 62 | super(index, streamDescription, dataStream); 63 | if (dataStream == null) { 64 | return; 65 | } 66 | getDataSession().addOnDataChannelRequestedListener(this); 67 | getDataSession().addDtlsKeyChangeListener(mDtlsKeyChangeListener); 68 | getDataStream().setDataChannelDelegate(this); 69 | 70 | boolean haveRemoteDescription = getRemoteStreamDescription() != null; 71 | 72 | StreamMode mode; 73 | String appLabel; 74 | int localPort = BASE_PORT + index; 75 | int streamCount; 76 | 77 | if (!haveRemoteDescription) { 78 | mode = StreamMode.SEND_RECEIVE; 79 | appLabel = "webrtc-datachannel"; 80 | streamCount = 1024; 81 | } else { 82 | if (getRemoteStreamDescription().getMode() != StreamMode.INACTIVE) { 83 | mode = StreamMode.SEND_RECEIVE; 84 | } else { 85 | mode = StreamMode.INACTIVE; 86 | getMainHandler().post(new Runnable() { 87 | @Override 88 | public void run() { 89 | getStream().setStreamMode(StreamMode.INACTIVE); 90 | } 91 | }); 92 | } 93 | appLabel = getRemoteStreamDescription().getAppLabel(); 94 | int remotePort = getRemoteStreamDescription().getSctpPort(); 95 | 96 | streamCount = getRemoteStreamDescription().getSctpStreamCount(); 97 | 98 | getDataSession().setSctpRemotePort(remotePort); 99 | } 100 | 101 | getLocalStreamDescription().setMode(mode); 102 | getLocalStreamDescription().setAppLabel(appLabel); 103 | getLocalStreamDescription().setSctpPort(localPort); 104 | getLocalStreamDescription().setSctpStreamCount(streamCount); 105 | getDataSession().setSctpLocalPort(localPort); 106 | } 107 | 108 | public DataSession getDataSession() { 109 | return (DataSession) getSession(); 110 | } 111 | 112 | public StreamSet.DataStream getDataStream() { 113 | return (StreamSet.DataStream) getStream(); 114 | } 115 | 116 | @Override 117 | Session createSession(final boolean isDtlsClient) { 118 | return new DataSession(isDtlsClient); 119 | } 120 | 121 | @Override 122 | public void setRemoteStreamDescription(StreamDescription streamDescription) { 123 | super.setRemoteStreamDescription(streamDescription); 124 | StreamMode mode; 125 | if (getRemoteStreamDescription().getMode() != StreamMode.SEND_RECEIVE) { 126 | mode = StreamMode.INACTIVE; 127 | } else { 128 | mode = StreamMode.SEND_RECEIVE; 129 | } 130 | getLocalStreamDescription().setMode(mode); 131 | getStream().setStreamMode(mode); 132 | if (mode == StreamMode.INACTIVE) { 133 | return; 134 | } 135 | int remotePort = getRemoteStreamDescription().getSctpPort(); 136 | int streamCount = getRemoteStreamDescription().getSctpStreamCount(); 137 | 138 | if (streamCount > 0) { 139 | getLocalStreamDescription().setSctpStreamCount(streamCount); 140 | } 141 | 142 | getDataSession().setSctpRemotePort(remotePort); 143 | } 144 | 145 | @Override 146 | public void stop() { 147 | if (getDataSession() != null) { 148 | getDataSession().removeOnDataChannelRequestedListener(this); 149 | } 150 | if (getDataStream() != null) { 151 | getDataStream().setDataChannelDelegate(null); 152 | } 153 | for (DataChannel dataChannel : mDataChannels) { 154 | dataChannel.close(); 155 | } 156 | mDataChannels.clear(); 157 | super.stop(); 158 | } 159 | 160 | @Override 161 | public void onDataChannelRequested(boolean ordered, int max_packet_life_time, int max_retransmits, String protocol, boolean negotiated, int id, String label) { 162 | Log.d(TAG, "DATACHANNEL requested:" + 163 | " ordered=" + ordered + 164 | " max_packet_life_time=" + max_packet_life_time + 165 | " max_retransmits=" + max_retransmits + 166 | " protocol=" + protocol + 167 | " negotiated=" + negotiated + 168 | " id=" + id + 169 | " label=" + label); 170 | 171 | final DataChannel dataChannel = new DataChannel(ordered, max_packet_life_time, max_retransmits, protocol, negotiated, (short) id, label); 172 | 173 | getMainHandler().post(new Runnable() { 174 | @Override 175 | public void run() { 176 | boolean keep = false; 177 | 178 | if (getDataStream() != null) { 179 | keep = getDataStream().onDataChannelReceived(dataChannel); 180 | } 181 | 182 | if (keep) { 183 | Log.d(TAG, "adding datachannel to session: " + dataChannel); 184 | getDataSession().addDataChannel(dataChannel); 185 | } 186 | } 187 | }); 188 | } 189 | 190 | @Override 191 | public void addDataChannel(final DataChannel dataChannel) { 192 | if (getDataSession() != null) { 193 | getDataSession().addDataChannel(dataChannel); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/MediaStreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | 29 | import android.util.Log; 30 | 31 | import com.ericsson.research.owr.MediaSession; 32 | import com.ericsson.research.owr.MediaSource; 33 | import com.ericsson.research.owr.MediaType; 34 | import com.ericsson.research.owr.Payload; 35 | import com.ericsson.research.owr.RemoteMediaSource; 36 | import com.ericsson.research.owr.Session; 37 | 38 | import java.util.List; 39 | 40 | class MediaStreamHandler extends StreamHandler implements MediaSession.OnIncomingSourceListener, MediaSession.CnameChangeListener, StreamSet.MediaSourceDelegate, MediaSession.SendSsrcChangeListener { 41 | private static final String TAG = "MediaStreamHandler"; 42 | private boolean mShouldRespectRemotePayloadOrder; 43 | private List mDefaultPayloads; 44 | 45 | private boolean mHaveCname = false; 46 | private boolean mHaveSsrc = false; 47 | 48 | MediaStreamHandler(int index, StreamDescription streamDescription, StreamSet.MediaStream mediaStream, RtcConfig config) { 49 | super(index, streamDescription, mediaStream); 50 | if (mediaStream == null) { 51 | return; 52 | } 53 | getMediaSession().addCnameChangeListener(this); 54 | getMediaSession().addSendSsrcChangeListener(this); 55 | getMediaSession().addOnIncomingSourceListener(this); 56 | getMediaStream().setMediaSourceDelegate(this); 57 | 58 | boolean haveRemoteDescription = getRemoteStreamDescription() != null; 59 | 60 | String mediaStreamId = getMediaStream().getId(); 61 | if (mediaStreamId == null) { 62 | getLocalStreamDescription().setMediaStreamId(Utils.randomString(27)); 63 | } else { 64 | getLocalStreamDescription().setMediaStreamId(mediaStreamId); 65 | } 66 | getLocalStreamDescription().setMediaStreamTrackId(Utils.randomString(27)); 67 | 68 | boolean rtcpMux; 69 | StreamMode mode; 70 | boolean wantSend = getMediaStream().wantSend(); 71 | boolean wantReceive = getMediaStream().wantReceive(); 72 | 73 | if (!haveRemoteDescription) { 74 | mode = StreamMode.get(wantSend, wantReceive); 75 | rtcpMux = true; 76 | } else { 77 | mode = getRemoteStreamDescription().getMode().reverse(wantSend, wantReceive); 78 | rtcpMux = getRemoteStreamDescription().isRtcpMux(); 79 | getStream().setStreamMode(mode); 80 | } 81 | 82 | getLocalStreamDescription().setMode(mode); 83 | 84 | if (mode == StreamMode.INACTIVE) { 85 | getStream().setStreamMode(mode); 86 | return; 87 | } 88 | 89 | getMediaSession().setRtcpMux(rtcpMux); 90 | getLocalStreamDescription().setRtcpMux(rtcpMux); 91 | 92 | if (getMediaStream().getMediaType() == MediaType.VIDEO) { 93 | mDefaultPayloads = config.getDefaultVideoPayloads(); 94 | } else { 95 | mDefaultPayloads = config.getDefaultAudioPayloads(); 96 | } 97 | mShouldRespectRemotePayloadOrder = config.shouldRespectRemotePayloadOrder(); 98 | List payloads = mDefaultPayloads; 99 | if (haveRemoteDescription) { 100 | payloads = Utils.intersectPayloads(getRemoteStreamDescription().getPayloads(), payloads); 101 | if (!mShouldRespectRemotePayloadOrder) { 102 | payloads = Utils.reorderPayloadsByFilter(payloads, mDefaultPayloads); 103 | } 104 | } 105 | for (RtcPayload payload : payloads) { 106 | getLocalStreamDescription().addPayload(payload); 107 | } 108 | List transformedPayloads = Utils.transformPayloads(payloads, getMediaStream().getMediaType()); 109 | if (transformedPayloads.isEmpty()) { 110 | Log.w(TAG, "no suitable payload found for stream: " + getMediaStream().getId()); 111 | getStream().setStreamMode(StreamMode.INACTIVE); 112 | // TODO: stop stream 113 | return; 114 | } 115 | 116 | if (mode.wantReceive()) { 117 | for (Payload payload : transformedPayloads) { 118 | getMediaSession().addReceivePayload(payload); 119 | } 120 | } 121 | if (haveRemoteDescription && mode.wantSend()) { 122 | getMediaSession().setSendPayload(transformedPayloads.get(0)); 123 | } 124 | } 125 | 126 | public MediaSession getMediaSession() { 127 | return (MediaSession) getSession(); 128 | } 129 | 130 | public StreamSet.MediaStream getMediaStream() { 131 | return (StreamSet.MediaStream) getStream(); 132 | } 133 | 134 | @Override 135 | Session createSession(final boolean isDtlsClient) { 136 | return new MediaSession(isDtlsClient); 137 | } 138 | 139 | @Override 140 | public void setRemoteStreamDescription(StreamDescription streamDescription) { 141 | super.setRemoteStreamDescription(streamDescription); 142 | StreamMode mode = getRemoteStreamDescription().getMode().reverse( 143 | getMediaStream().wantSend(), getMediaStream().wantReceive() 144 | ); 145 | getLocalStreamDescription().setMode(mode); 146 | getStream().setStreamMode(mode); 147 | if (mode == StreamMode.INACTIVE) { 148 | return; 149 | } 150 | if (!getRemoteStreamDescription().isRtcpMux()) { 151 | getMediaSession().setRtcpMux(false); 152 | } 153 | 154 | if (mode.wantSend()) { 155 | List payloads = getRemoteStreamDescription().getPayloads(); 156 | if (!mShouldRespectRemotePayloadOrder) { 157 | payloads = Utils.reorderPayloadsByFilter(payloads, mDefaultPayloads); 158 | } 159 | List transformedPayloads = Utils.transformPayloads(payloads, getMediaStream().getMediaType()); 160 | if (transformedPayloads.isEmpty()) { 161 | Log.w(TAG, "no suitable payload found for stream: " + getMediaStream().getId()); 162 | getStream().setStreamMode(StreamMode.INACTIVE); 163 | // TODO: stop stream 164 | return; 165 | } 166 | getMediaSession().setSendPayload(transformedPayloads.get(0)); 167 | } 168 | } 169 | 170 | @Override 171 | public boolean isReady() { 172 | boolean isInactive = getLocalStreamDescription().getMode() == StreamMode.INACTIVE; 173 | return super.isReady() && mHaveSsrc && mHaveCname || isInactive; 174 | } 175 | 176 | @Override 177 | public void stop() { 178 | if (getMediaSession() != null) { 179 | getMediaSession().removeCnameChangeListener(this); 180 | getMediaSession().removeSendSsrcChangeListener(this); 181 | getMediaSession().removeOnIncomingSourceListener(this); 182 | getMediaSession().setSendSource(null); 183 | } 184 | if (getMediaStream() != null) { 185 | getMediaStream().onRemoteMediaSource(null); 186 | getMediaStream().setMediaSourceDelegate(null); 187 | } 188 | super.stop(); 189 | } 190 | 191 | @Override 192 | public void onCnameChanged(String cname) { 193 | getLocalStreamDescription().setCname(cname); 194 | mHaveCname = true; 195 | 196 | signalListenerIfReady(); 197 | } 198 | 199 | @Override 200 | public void onSendSsrcChanged(final int ssrc) { 201 | long unsignedSsrc = ssrc & 0xFFFFFFFFL; 202 | if (unsignedSsrc > 0) { 203 | getLocalStreamDescription().addSsrc(unsignedSsrc); 204 | mHaveSsrc = true; 205 | 206 | signalListenerIfReady(); 207 | } 208 | } 209 | 210 | @Override 211 | public void onIncomingSource(RemoteMediaSource remoteMediaSource) { 212 | if (getSession() != null) { 213 | getMediaStream().onRemoteMediaSource(remoteMediaSource); 214 | } 215 | } 216 | 217 | @Override 218 | public void setMediaSource(MediaSource mediaSource) { 219 | if (getSession() != null) { 220 | getMediaSession().setSendSource(mediaSource); 221 | } 222 | } 223 | } -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/StreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.os.Handler; 29 | import android.os.Looper; 30 | import android.util.Log; 31 | 32 | import com.ericsson.research.owr.Candidate; 33 | import com.ericsson.research.owr.Session; 34 | 35 | import java.lang.ref.WeakReference; 36 | 37 | // TODO: verify peer cert 38 | abstract class StreamHandler implements Session.DtlsCertificateChangeListener, Session.OnNewCandidateListener { 39 | private static final String TAG = "StreamHandler"; 40 | 41 | private static final String DEFAULT_HASH_FUNCTION = "sha-256"; 42 | 43 | private final StreamSet.Stream mStream; 44 | private final MutableStreamDescription mLocalStreamDescription; 45 | private final int mIndex; 46 | private StreamDescription mRemoteStreamDescription; 47 | private Session mSession; 48 | private boolean mHaveCandidate = false; 49 | private boolean mHaveFingerprint = false; 50 | private boolean mLocalDescriptionCreated = false; 51 | private Handler mMainHandler = new Handler(Looper.getMainLooper()); 52 | private WeakReference mRtcSessionDelegateRef = new WeakReference<>(null); 53 | 54 | StreamHandler(int index, StreamDescription streamDescription, StreamSet.Stream stream) { 55 | mLocalStreamDescription = new MutableStreamDescription(); 56 | mRemoteStreamDescription = streamDescription; 57 | mIndex = index; 58 | mStream = stream; 59 | 60 | if (stream == null) { // Inactive stream 61 | mLocalStreamDescription.setMode(StreamMode.INACTIVE); 62 | mLocalStreamDescription.setType(streamDescription.getType()); 63 | return; 64 | } 65 | 66 | mSession = createSession(streamDescription != null); 67 | 68 | if (streamDescription != null) { 69 | for (RtcCandidate rtcCandidate : getRemoteStreamDescription().getCandidates()) { 70 | Candidate candidate = Utils.transformCandidate(rtcCandidate); 71 | candidate.setUfrag(streamDescription.getUfrag()); 72 | candidate.setPassword(streamDescription.getPassword()); 73 | mSession.addRemoteCandidate(candidate); 74 | } 75 | } 76 | 77 | mSession.addDtlsCertificateChangeListener(this); 78 | mSession.addOnNewCandidateListener(this); 79 | 80 | mLocalStreamDescription.setType(getStream().getType()); 81 | 82 | String fingerprintHashFunction; 83 | String dtlsSetup; 84 | 85 | if (streamDescription == null) { 86 | fingerprintHashFunction = DEFAULT_HASH_FUNCTION; 87 | dtlsSetup = "actpass"; 88 | } else { 89 | fingerprintHashFunction = getRemoteStreamDescription().getFingerprintHashFunction(); 90 | dtlsSetup = "active"; 91 | } 92 | 93 | getLocalStreamDescription().setFingerprintHashFunction(fingerprintHashFunction); 94 | getLocalStreamDescription().setDtlsSetup(dtlsSetup); 95 | } 96 | 97 | abstract Session createSession(boolean isDtlsClient); 98 | 99 | public boolean haveRemoteDescription() { 100 | return mRemoteStreamDescription != null; 101 | } 102 | 103 | public int getIndex() { 104 | return mIndex; 105 | } 106 | 107 | public Session getSession() { 108 | return mSession; 109 | } 110 | 111 | public StreamSet.Stream getStream() { 112 | return mStream; 113 | } 114 | 115 | public MutableStreamDescription getLocalStreamDescription() { 116 | return mLocalStreamDescription; 117 | } 118 | 119 | public StreamDescription getRemoteStreamDescription() { 120 | return mRemoteStreamDescription; 121 | } 122 | 123 | public StreamDescription finishLocalStreamDescription() { 124 | mLocalDescriptionCreated = true; 125 | return getLocalStreamDescription(); 126 | } 127 | 128 | public boolean isReady() { 129 | boolean isInactive = getLocalStreamDescription().getMode() == StreamMode.INACTIVE; 130 | return mHaveCandidate && mHaveFingerprint || isInactive; 131 | } 132 | 133 | public void setRemoteStreamDescription(StreamDescription remoteStreamDescription) { 134 | mRemoteStreamDescription = remoteStreamDescription; 135 | 136 | for (RtcCandidate rtcCandidate : getRemoteStreamDescription().getCandidates()) { 137 | Candidate candidate = Utils.transformCandidate(rtcCandidate); 138 | candidate.setUfrag(remoteStreamDescription.getUfrag()); 139 | candidate.setPassword(remoteStreamDescription.getPassword()); 140 | getSession().addRemoteCandidate(candidate); 141 | } 142 | } 143 | 144 | public void stop() { 145 | if (getSession() != null) { 146 | getSession().removeDtlsCertificateChangeListener(this); 147 | getSession().removeOnNewCandidateListener(this); 148 | } 149 | mSession = null; 150 | mRemoteStreamDescription = null; 151 | } 152 | 153 | public void signalListenerIfReady() { 154 | RtcSessionDelegate delegate = mRtcSessionDelegateRef.get(); 155 | if (delegate != null && isReady()) { 156 | delegate.onReady(); 157 | } 158 | } 159 | 160 | @Override 161 | public synchronized void onDtlsCertificateChanged(String pem) { 162 | String fingerprintHashFunction = getLocalStreamDescription().getFingerprintHashFunction(); 163 | String fingerprint = Utils.fingerprintFromPem(pem, fingerprintHashFunction); 164 | getLocalStreamDescription().setFingerprint(fingerprint); 165 | mHaveFingerprint = true; 166 | signalListenerIfReady(); 167 | } 168 | 169 | @Override 170 | public synchronized void onNewCandidate(Candidate candidate) { 171 | if (!mHaveCandidate) { 172 | getLocalStreamDescription().setUfrag(candidate.getUfrag()); 173 | getLocalStreamDescription().setPassword(candidate.getPassword()); 174 | } 175 | 176 | final RtcCandidateImpl rtcCandidate = RtcCandidateImpl.fromOwrCandidate(candidate); 177 | if (mLocalDescriptionCreated) { 178 | Log.d(TAG, "[RtcSession] got local candidate for " + this); 179 | rtcCandidate.setStreamIndex(getIndex()); 180 | mMainHandler.post(new Runnable() { 181 | @Override 182 | public void run() { 183 | RtcSessionDelegate delegate = mRtcSessionDelegateRef.get(); 184 | if (delegate != null) { 185 | delegate.onLocalCandidate(rtcCandidate); 186 | } 187 | } 188 | }); 189 | } else { 190 | getLocalStreamDescription().addCandidate(rtcCandidate); 191 | } 192 | 193 | 194 | if (!mHaveCandidate) { 195 | mHaveCandidate = true; 196 | signalListenerIfReady(); 197 | } 198 | } 199 | 200 | public void onRemoteCandidate(RtcCandidate rtcCandidate) { 201 | if (getStream() == null) { 202 | return; 203 | } 204 | boolean isRtcp = rtcCandidate.getComponentType() == RtcCandidate.ComponentType.RTCP; 205 | if (getLocalStreamDescription().isRtcpMux() && isRtcp) { 206 | return; 207 | } 208 | Candidate candidate = Utils.transformCandidate(rtcCandidate); 209 | candidate.setUfrag(getRemoteStreamDescription().getUfrag()); 210 | candidate.setPassword(getRemoteStreamDescription().getPassword()); 211 | getSession().addRemoteCandidate(candidate); 212 | } 213 | 214 | public void setRtcSessionDelegate(RtcSessionDelegate delegate) { 215 | mRtcSessionDelegateRef = new WeakReference(delegate); 216 | } 217 | 218 | public interface RtcSessionDelegate { 219 | void onReady(); 220 | 221 | void onLocalCandidate(RtcCandidate candidate); 222 | } 223 | 224 | public Handler getMainHandler() { 225 | return mMainHandler; 226 | } 227 | 228 | @Override 229 | public String toString() { 230 | if (getLocalStreamDescription() == null) { 231 | return "Stream{}"; 232 | } 233 | return "Stream{" + 234 | getLocalStreamDescription().getType().toString().charAt(0) + getIndex() + "," + 235 | getLocalStreamDescription().getMode() + "}"; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /sdk/src/androidTest/java/com/ericsson/research/owr/sdk/MediaSourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.test.MoreAsserts; 29 | import android.util.Log; 30 | import android.view.TextureView; 31 | 32 | import com.ericsson.research.owr.MediaSource; 33 | import com.ericsson.research.owr.MediaType; 34 | import com.ericsson.research.owr.SourceType; 35 | 36 | import java.util.List; 37 | import java.util.concurrent.CountDownLatch; 38 | 39 | public class MediaSourceTests extends OwrActivityTestCase { 40 | private static final String TAG = "MediaSourceTests"; 41 | 42 | private int mListenerCallCount = 0; 43 | 44 | public class MockListener implements MediaSourceListener { 45 | private final CountDownLatch mLatch; 46 | 47 | public MockListener(CountDownLatch latch) { 48 | mLatch = latch; 49 | } 50 | 51 | @Override 52 | public void setMediaSource(final MediaSource mediaSource) { 53 | mListenerCallCount += 1; 54 | if (mLatch != null) { 55 | mLatch.countDown(); 56 | } 57 | } 58 | } 59 | 60 | public void testMediaSourceListenerSet() { 61 | final MediaSourceListenerSet set = new MediaSourceListenerSet(); 62 | TestUtils.synchronous().run(new TestUtils.SynchronousBlock() { 63 | @Override 64 | public void run(final CountDownLatch latch) { 65 | set.addListener(new MockListener(latch)); 66 | } 67 | }); 68 | assertEquals(1, mListenerCallCount); 69 | set.notifyListeners(null); 70 | 71 | long freeMemBeforeGc = Runtime.getRuntime().freeMemory(); 72 | System.gc(); 73 | TestUtils.sleep(500); 74 | if (Runtime.getRuntime().freeMemory() < freeMemBeforeGc) { 75 | set.notifyListeners(null); // This should not call the listener, as it was GC'd 76 | assertEquals(1, mListenerCallCount); 77 | TestUtils.synchronous().run(new TestUtils.SynchronousBlock() { 78 | @Override 79 | public void run(final CountDownLatch latch) { 80 | set.addListener(new MockListener(latch)); 81 | } 82 | }); 83 | // The previous assertion would fail here, as we didn't handle that it was asynchronous 84 | assertEquals(2, mListenerCallCount); // should be signaled when it's added 85 | } else { 86 | Log.w(TAG, "Skipping GC test, no memory was free'd"); 87 | } 88 | 89 | try { 90 | set.addListener(null); 91 | throw new RuntimeException("should not be reached"); 92 | } catch (NullPointerException ignored) {} 93 | } 94 | 95 | public void testMicrophoneSource() { 96 | final MicrophoneSource source = MicrophoneSource.getInstance(); 97 | assertEquals("Default audio input", source.getName()); 98 | final MediaSource[] audioSource = new MediaSource[1]; 99 | TestUtils.synchronous().run(new TestUtils.SynchronousBlock() { 100 | @Override 101 | public void run(final CountDownLatch latch) { 102 | source.addMediaSourceListener(new MediaSourceListener() { 103 | @Override 104 | public void setMediaSource(final MediaSource mediaSource) { 105 | assertNotNull(mediaSource); 106 | audioSource[0] = mediaSource; 107 | MoreAsserts.assertContentsInOrder(mediaSource.getMediaType(), MediaType.AUDIO); 108 | assertEquals(source.getName(), mediaSource.getName()); 109 | assertSame(SourceType.CAPTURE, mediaSource.getType()); 110 | latch.countDown(); 111 | } 112 | }); 113 | } 114 | }).run(new TestUtils.SynchronousBlock() { 115 | @Override 116 | public void run(final CountDownLatch latch) { 117 | source.addMediaSourceListener(new MediaSourceListener() { 118 | @Override 119 | public void setMediaSource(final MediaSource mediaSource) { 120 | assertNotNull(mediaSource); 121 | assertSame(audioSource[0], mediaSource); 122 | latch.countDown(); 123 | } 124 | }); 125 | } 126 | }); 127 | } 128 | 129 | public void testSelfView() { 130 | CameraSource cameraSource = CameraSource.getInstance(); 131 | VideoView videoView = cameraSource.createVideoView(); 132 | 133 | assertEquals(2, cameraSource.getCount()); // Test needs to be run on a device with two cameras 134 | assertEquals("Front facing Camera", cameraSource.getName(0)); 135 | assertEquals("Back facing Camera", cameraSource.getName(1)); 136 | List emptyPipelineDump = cameraSource.dumpPipelineGraphs(); 137 | assertEquals(2, emptyPipelineDump.size()); 138 | assertNotNull(emptyPipelineDump.get(0)); 139 | assertNotNull(emptyPipelineDump.get(1)); 140 | 141 | TextureView textureView = getActivity().getTextureView(); 142 | 143 | assertEquals(0, videoView.getRotation()); 144 | assertEquals(false, videoView.isMirrored()); 145 | videoView.setRotation(0); 146 | videoView.setView(textureView); 147 | TestUtils.waitForNUpdates(textureView, 5); 148 | videoView.setRotation(1); 149 | videoView.setView(textureView); 150 | assertEquals(1, videoView.getRotation()); 151 | TestUtils.waitForNUpdates(textureView, 5); 152 | try { 153 | videoView.setView(null); 154 | throw new RuntimeException("should not be reached"); 155 | } catch (NullPointerException ignored) {} 156 | videoView.stop(); 157 | videoView.stop(); 158 | videoView.stop(); 159 | videoView.setView(textureView); 160 | TestUtils.waitForNUpdates(textureView, 5); 161 | videoView.setRotation(1); 162 | assertEquals(1, videoView.getRotation()); 163 | TestUtils.sleep(100); 164 | videoView.setRotation(2); 165 | assertEquals(2, videoView.getRotation()); 166 | TestUtils.sleep(100); 167 | videoView.setMirrored(true); 168 | assertEquals(true, videoView.isMirrored()); 169 | TestUtils.sleep(100); 170 | videoView.setMirrored(false); 171 | assertEquals(false, videoView.isMirrored()); 172 | TestUtils.sleep(100); 173 | videoView.setRotation(3); 174 | assertEquals(3, videoView.getRotation()); 175 | TestUtils.sleep(100); 176 | TestUtils.waitForNUpdates(textureView, 5); 177 | try { 178 | videoView.setRotation(4); 179 | throw new RuntimeException("should not be reached"); 180 | } catch (IllegalArgumentException ignored) {} 181 | try { 182 | videoView.setRotation(-1); 183 | throw new RuntimeException("should not be reached"); 184 | } catch (IllegalArgumentException ignored) {} 185 | try { 186 | videoView.setRotation(90); 187 | throw new RuntimeException("should not be reached"); 188 | } catch (IllegalArgumentException ignored) {} 189 | videoView.stop(); 190 | TestUtils.sleep(1000); 191 | } 192 | 193 | public void testSelfViewSwitching() { 194 | final CameraSourceImpl cameraSource = (CameraSourceImpl) CameraSource.getInstance(); 195 | final VideoView videoView = cameraSource.createVideoView(); 196 | final TextureView textureView = getActivity().getTextureView(); 197 | 198 | videoView.setView(textureView); 199 | TestUtils.waitForNUpdates(textureView, 5); 200 | 201 | TestUtils.synchronous().delay(5000, new Runnable() { 202 | @Override 203 | public void run() { 204 | assertEquals(0, cameraSource.getSelectedSource()); 205 | assertEquals(0, cameraSource.getActiveSource()); 206 | assertSame(CameraSourceImpl.VideoSourceState.READY, cameraSource.getVideoSourceState()); 207 | cameraSource.selectSource(0); 208 | assertEquals(0, cameraSource.getActiveSource()); 209 | assertSame(CameraSourceImpl.VideoSourceState.READY, cameraSource.getVideoSourceState()); 210 | assertEquals(0, cameraSource.getSelectedSource()); 211 | assertEquals(0, cameraSource.getActiveSource()); 212 | cameraSource.selectSource(1); 213 | assertEquals(0, cameraSource.getActiveSource()); 214 | assertSame(CameraSourceImpl.VideoSourceState.CLOSING, cameraSource.getVideoSourceState()); 215 | assertEquals(1, cameraSource.getSelectedSource()); 216 | assertEquals(0, cameraSource.getActiveSource()); 217 | } 218 | }).delay(300, new Runnable() { 219 | @Override 220 | public void run() { // we're closing the old source 221 | cameraSource.selectSource(0); 222 | assertSame(CameraSourceImpl.VideoSourceState.CLOSING, cameraSource.getVideoSourceState()); 223 | assertEquals(0, cameraSource.getSelectedSource()); 224 | assertEquals(0, cameraSource.getActiveSource()); 225 | } 226 | }).delay(300, new Runnable() { 227 | @Override 228 | public void run() { // we're still closing the old source 229 | cameraSource.selectSource(1); 230 | assertSame(CameraSourceImpl.VideoSourceState.CLOSING, cameraSource.getVideoSourceState()); 231 | assertEquals(1, cameraSource.getSelectedSource()); 232 | assertEquals(0, cameraSource.getActiveSource()); 233 | } 234 | }).delay(900, new Runnable() { 235 | @Override 236 | public void run() { // We should now have switched source 237 | assertSame(CameraSourceImpl.VideoSourceState.OPENING, cameraSource.getVideoSourceState()); 238 | assertEquals(1, cameraSource.getSelectedSource()); 239 | assertEquals(1, cameraSource.getActiveSource()); 240 | cameraSource.selectSource(0); 241 | assertEquals(0, cameraSource.getSelectedSource()); 242 | assertEquals(1, cameraSource.getActiveSource()); 243 | } 244 | }).delay(5000, new Runnable() { 245 | @Override 246 | public void run() { // in the opening state 247 | cameraSource.selectSource(1); 248 | assertSame(CameraSourceImpl.VideoSourceState.OPENING, cameraSource.getVideoSourceState()); 249 | assertEquals(1, cameraSource.getSelectedSource()); 250 | assertEquals(1, cameraSource.getActiveSource()); 251 | } 252 | }).delay(2000, new Runnable() { 253 | @Override 254 | public void run() { // switch back to 0 255 | assertSame(CameraSourceImpl.VideoSourceState.OPENING, cameraSource.getVideoSourceState()); 256 | assertEquals(1, cameraSource.getSelectedSource()); 257 | assertEquals(1, cameraSource.getActiveSource()); 258 | } 259 | }).delay(2000, new Runnable() { 260 | @Override 261 | public void run() { // open should now be complete 262 | assertSame(CameraSourceImpl.VideoSourceState.READY, cameraSource.getVideoSourceState()); 263 | assertEquals(1, cameraSource.getSelectedSource()); 264 | assertEquals(1, cameraSource.getActiveSource()); 265 | } 266 | }).delay(2000, new Runnable() { 267 | @Override 268 | public void run() { 269 | assertSame(CameraSourceImpl.VideoSourceState.READY, cameraSource.getVideoSourceState()); 270 | assertEquals(1, cameraSource.getSelectedSource()); 271 | assertEquals(1, cameraSource.getActiveSource()); 272 | cameraSource.selectSource(0); // Switch back to 0 to not break future tests 273 | videoView.stop(); 274 | assertSame(CameraSourceImpl.VideoSourceState.CLOSING, cameraSource.getVideoSourceState()); 275 | assertEquals(0, cameraSource.getSelectedSource()); 276 | assertEquals(1, cameraSource.getActiveSource()); 277 | } 278 | }).delay(10000, new Runnable() { 279 | @Override 280 | public void run() { 281 | assertSame(CameraSourceImpl.VideoSourceState.READY, cameraSource.getVideoSourceState()); 282 | assertEquals(0, cameraSource.getSelectedSource()); 283 | assertEquals(0, cameraSource.getActiveSource()); 284 | } 285 | }); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/RtcSessionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.os.Handler; 29 | import android.os.Looper; 30 | import android.util.Log; 31 | import android.util.Pair; 32 | 33 | import com.ericsson.research.owr.Candidate; 34 | import com.ericsson.research.owr.DataChannel; 35 | import com.ericsson.research.owr.DataSession; 36 | import com.ericsson.research.owr.MediaSession; 37 | import com.ericsson.research.owr.MediaSource; 38 | import com.ericsson.research.owr.MediaType; 39 | import com.ericsson.research.owr.Payload; 40 | import com.ericsson.research.owr.RemoteMediaSource; 41 | import com.ericsson.research.owr.Session; 42 | import com.ericsson.research.owr.TransportAgent; 43 | 44 | import java.util.ArrayList; 45 | import java.util.Arrays; 46 | import java.util.Date; 47 | import java.util.LinkedList; 48 | import java.util.List; 49 | import java.util.Random; 50 | 51 | class RtcSessionImpl implements RtcSession, StreamHandler.RtcSessionDelegate { 52 | private static final String TAG = "RtcSessionImpl"; 53 | 54 | private TransportAgent mTransportAgent; 55 | 56 | private final String mSessionId; 57 | private final RtcConfig mConfig; 58 | 59 | private SessionDescription mRemoteDescription = null; 60 | private final Handler mMainHandler; 61 | 62 | private OnLocalCandidateListener mLocalCandidateListener = null; 63 | private List mStreamHandlers; 64 | private OnLocalDescriptionListener mLocalDescriptionListener; 65 | private List mRemoteCandidateBuffer; 66 | private State mState; 67 | 68 | private static Random sRandom = new Random(); 69 | 70 | RtcSessionImpl(RtcConfig config) { 71 | mSessionId = "" + (sRandom.nextInt() + new Date().getTime()); 72 | mConfig = config; 73 | mState = State.STOPPED; 74 | mMainHandler = new Handler(Looper.getMainLooper()); 75 | } 76 | 77 | @Override 78 | public synchronized void setOnLocalCandidateListener(final OnLocalCandidateListener listener) { 79 | mLocalCandidateListener = listener; 80 | } 81 | 82 | @Override 83 | public void setOnLocalDescriptionListener(final OnLocalDescriptionListener listener) { 84 | mLocalDescriptionListener = listener; 85 | } 86 | 87 | @Override 88 | public void onReady() { 89 | maybeFinishSetup(); 90 | } 91 | 92 | @Override 93 | public void onLocalCandidate(final RtcCandidate candidate) { 94 | if (mLocalCandidateListener != null) { 95 | mLocalCandidateListener.onLocalCandidate(candidate); 96 | } 97 | } 98 | 99 | private void log(String msg) { 100 | String streams; 101 | if (mStreamHandlers == null) { 102 | streams = "[]"; 103 | } else { 104 | streams = Arrays.toString(mStreamHandlers.toArray(new StreamHandler[mStreamHandlers.size()])); 105 | } 106 | Log.d(TAG, "[RtcSession" + 107 | " id=" + mSessionId + 108 | " state=" + mState.name() + 109 | " streams=" + streams + 110 | " candidates=" + (mRemoteCandidateBuffer == null ? 0 : mRemoteCandidateBuffer.size()) + 111 | " ] " + msg); 112 | } 113 | 114 | @Override 115 | public synchronized void start(final StreamSet streamSet) { 116 | if (mState.isStarted()) { 117 | Log.w(TAG, "start called at wrong state: " + mState); 118 | return; 119 | } 120 | if (streamSet == null) { 121 | throw new NullPointerException("streamSet may not be null"); 122 | } 123 | log("setup called"); 124 | 125 | boolean isInitiator = mState == State.STOPPED; 126 | 127 | mTransportAgent = new TransportAgent(isInitiator); 128 | 129 | for (RtcConfig.HelperServer helperServer : mConfig.getHelperServers()) { 130 | mTransportAgent.addHelperServer( 131 | helperServer.getType(), 132 | helperServer.getAddress(), 133 | helperServer.getPort(), 134 | helperServer.getUsername(), 135 | helperServer.getPassword() 136 | ); 137 | } 138 | 139 | mStreamHandlers = new LinkedList<>(); 140 | int index = 0; 141 | if (isInitiator) { 142 | // For outbound calls we initiate all streams without any remote description 143 | for (StreamSet.Stream stream : streamSet.getStreams()) { 144 | mStreamHandlers.add(createStreamHandler(index, null, stream)); 145 | index++; 146 | } 147 | } else { 148 | for (Pair pair : Utils.resolveOfferedStreams(mRemoteDescription, streamSet.getStreams())) { 149 | StreamDescription description = pair.first; 150 | StreamSet.Stream stream = pair.second; 151 | mStreamHandlers.add(createStreamHandler(index, description, stream)); 152 | index++; 153 | } 154 | } 155 | for (StreamHandler handler : mStreamHandlers) { 156 | if (handler.getSession() != null) { 157 | mTransportAgent.addSession(handler.getSession()); 158 | } 159 | } 160 | 161 | if (mState == State.STOPPED) { 162 | mState = State.PENDING_A; 163 | } else { 164 | mState = State.PENDING_B; 165 | } 166 | 167 | if (mRemoteDescription != null && mRemoteCandidateBuffer != null) { 168 | for (RtcCandidate candidate : mRemoteCandidateBuffer) { 169 | addRemoteCandidate(candidate); 170 | } 171 | mRemoteCandidateBuffer = null; 172 | } 173 | log("initial setup complete"); 174 | 175 | mMainHandler.post(new Runnable() { 176 | @Override 177 | public void run() { 178 | // we might be ready straight away if there are no active streams. 179 | // do this check asynchronously to keep code paths consistent 180 | maybeFinishSetup(); 181 | } 182 | }); 183 | } 184 | 185 | private synchronized void maybeFinishSetup() { 186 | final SessionDescription sessionDescription; 187 | 188 | if (!mState.isPending()) { 189 | Log.w(TAG, "maybeFinishSetup called at wrong state: " + mState); 190 | return; 191 | } 192 | for (StreamHandler streamHandler : mStreamHandlers) { 193 | if (!streamHandler.isReady()) { 194 | return; 195 | } 196 | } 197 | log("setup complete"); 198 | 199 | SessionDescription.Type type; 200 | 201 | if (mState == State.PENDING_A) { 202 | type = SessionDescription.Type.OFFER; 203 | mState = State.WAIT_ANSWER; 204 | } else if (mState == State.PENDING_B) { 205 | type = SessionDescription.Type.ANSWER; 206 | mState = State.ACTIVE; 207 | } else { 208 | Log.e(TAG, "invalid state when finishing setup: " + mState); 209 | return; 210 | } 211 | 212 | List streamDescriptions = new ArrayList<>(mStreamHandlers.size()); 213 | 214 | for (StreamHandler streamHandler : mStreamHandlers) { 215 | streamDescriptions.add(streamHandler.finishLocalStreamDescription()); 216 | } 217 | 218 | sessionDescription = new SessionDescriptionImpl(type, mSessionId, streamDescriptions); 219 | 220 | mMainHandler.post(new Runnable() { 221 | @Override 222 | public void run() { 223 | if (mLocalDescriptionListener != null) { 224 | mLocalDescriptionListener.onLocalDescription(sessionDescription); 225 | } 226 | } 227 | }); 228 | } 229 | 230 | private void handleOffer(final SessionDescription remoteDescription) throws InvalidDescriptionException { 231 | if (mState != State.STOPPED) { 232 | Log.w(TAG, "got offer at invalid state: " + mState); 233 | return; 234 | } 235 | 236 | mRemoteDescription = remoteDescription; 237 | mState = State.HAS_OFFER; 238 | log("received offer"); 239 | } 240 | 241 | private void handleAnswer(final SessionDescription remoteDescription) throws InvalidDescriptionException { 242 | if (mState != State.WAIT_ANSWER) { 243 | Log.w(TAG, "got answer at invalid state: " + mState); 244 | return; 245 | } 246 | mRemoteDescription = remoteDescription; 247 | log("received answer"); 248 | 249 | List streamDescriptions = remoteDescription.getStreamDescriptions(); 250 | int numStreamDescriptions = streamDescriptions.size(); 251 | int numStreamHandlers = mStreamHandlers.size(); 252 | 253 | if (numStreamDescriptions != numStreamHandlers) { 254 | throw new InvalidDescriptionException("session description has an invalid number of stream descriptions: " + 255 | numStreamDescriptions + " != " + numStreamHandlers); 256 | } 257 | int size = Math.max(streamDescriptions.size(), mStreamHandlers.size()); 258 | for (int i = 0; i < size; i++) { 259 | StreamDescription streamDescription = streamDescriptions.get(i); 260 | StreamHandler streamHandler = mStreamHandlers.get(i); 261 | if (streamDescription.getType() != streamHandler.getStream().getType()) { 262 | throw new InvalidDescriptionException("stream description types do not match: " + 263 | streamDescription.getType() + " != " + streamHandler.getStream().getType()); 264 | } 265 | streamHandler.setRemoteStreamDescription(streamDescription); 266 | } 267 | 268 | mState = State.ACTIVE; 269 | 270 | if (mRemoteCandidateBuffer != null) { 271 | for (RtcCandidate candidate : mRemoteCandidateBuffer) { 272 | addRemoteCandidate(candidate); 273 | } 274 | mRemoteCandidateBuffer = null; 275 | } 276 | } 277 | 278 | @Override 279 | public synchronized void setRemoteDescription(final SessionDescription remoteDescription) throws InvalidDescriptionException { 280 | if (remoteDescription == null) { 281 | throw new NullPointerException("remote description should not be null"); 282 | } 283 | if (remoteDescription.getType() == SessionDescription.Type.OFFER) { 284 | handleOffer(remoteDescription); 285 | } else if (remoteDescription.getType() == SessionDescription.Type.ANSWER) { 286 | handleAnswer(remoteDescription); 287 | } else { 288 | Log.e(TAG, "Unkown description type: " + remoteDescription.getType()); 289 | } 290 | } 291 | 292 | @Override 293 | public synchronized void addRemoteCandidate(final RtcCandidate candidate) { 294 | if (mState == State.STOPPED) { 295 | return; 296 | } else if (mRemoteDescription == null || mState == State.HAS_OFFER) { 297 | if (mRemoteCandidateBuffer == null) { 298 | mRemoteCandidateBuffer = new LinkedList<>(); 299 | } 300 | mRemoteCandidateBuffer.add(candidate); 301 | Log.d(TAG, "[RtcSession] buffering candidate for stream " + candidate.getStreamIndex()); 302 | return; 303 | } 304 | 305 | StreamHandler streamHandler = null; 306 | int index = candidate.getStreamIndex(); 307 | // String id = candidate.getStreamId(); 308 | 309 | if (index < 0) { 310 | // TODO: use id? 311 | /* if (id != null) { 312 | for (StreamHandler handler : mStreamHandlers) { 313 | if (id.equals(handler.getStream().getId())) { 314 | streamHandler = handler; 315 | break; 316 | } 317 | } 318 | }*/ 319 | } else if (index < mStreamHandlers.size()) { 320 | streamHandler = mStreamHandlers.get(index); 321 | } 322 | 323 | if (streamHandler != null) { 324 | Log.d(TAG, "[RtcSession] got remote candidate for " + streamHandler); 325 | streamHandler.onRemoteCandidate(candidate); 326 | } 327 | } 328 | 329 | @Override 330 | public void stop() { 331 | mState = State.STOPPED; 332 | 333 | if (mStreamHandlers != null) { 334 | for (StreamHandler streamHandler : mStreamHandlers) { 335 | streamHandler.stop(); 336 | } 337 | } 338 | 339 | mRemoteCandidateBuffer = null; 340 | mRemoteDescription = null; 341 | mTransportAgent = null; 342 | mStreamHandlers = null; 343 | } 344 | 345 | @Override 346 | public String dumpPipelineGraph() { 347 | if (mTransportAgent != null) { 348 | return mTransportAgent.getDotData(); 349 | } 350 | return null; 351 | } 352 | 353 | private StreamHandler createStreamHandler(int index, StreamDescription streamDescription, StreamSet.Stream stream) { 354 | StreamHandler streamHandler; 355 | if (stream == null) { 356 | if (streamDescription.getType() == StreamType.DATA) { 357 | streamHandler = new DataStreamHandler(index, streamDescription, null); 358 | } else { 359 | streamHandler = new MediaStreamHandler(index, streamDescription, null, mConfig); 360 | } 361 | } else if (stream.getType() == StreamType.DATA) { 362 | streamHandler = new DataStreamHandler(index, streamDescription, (StreamSet.DataStream) stream); 363 | } else { 364 | streamHandler = new MediaStreamHandler(index, streamDescription, (StreamSet.MediaStream) stream, mConfig); 365 | } 366 | streamHandler.setRtcSessionDelegate(this); 367 | return streamHandler; 368 | } 369 | 370 | private enum State { 371 | STOPPED(false, false), 372 | PENDING_A(true, true), 373 | WAIT_ANSWER(false, true), 374 | HAS_OFFER(false, false), 375 | PENDING_B(true, true), 376 | ACTIVE(false, true); 377 | 378 | private final boolean mPending; 379 | private final boolean mStopped; 380 | 381 | State(boolean pending, boolean stopped) { 382 | mPending = pending; 383 | mStopped = stopped; 384 | } 385 | 386 | public boolean isPending() { 387 | return mPending; 388 | } 389 | 390 | public boolean isStarted() { 391 | return mStopped; 392 | } 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /sdk/src/main/java/com/ericsson/research/owr/sdk/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ericsson AB. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or other 13 | * materials provided with the distribution. 14 | 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 24 | * OF SUCH DAMAGE. 25 | */ 26 | package com.ericsson.research.owr.sdk; 27 | 28 | import android.util.Base64; 29 | import android.util.Log; 30 | import android.util.Pair; 31 | 32 | import com.ericsson.research.owr.AudioPayload; 33 | import com.ericsson.research.owr.Candidate; 34 | import com.ericsson.research.owr.CandidateType; 35 | import com.ericsson.research.owr.CodecType; 36 | import com.ericsson.research.owr.ComponentType; 37 | import com.ericsson.research.owr.MediaType; 38 | import com.ericsson.research.owr.Owr; 39 | import com.ericsson.research.owr.Payload; 40 | import com.ericsson.research.owr.TransportType; 41 | import com.ericsson.research.owr.VideoPayload; 42 | 43 | import java.io.UnsupportedEncodingException; 44 | import java.security.MessageDigest; 45 | import java.security.NoSuchAlgorithmException; 46 | import java.util.LinkedList; 47 | import java.util.List; 48 | import java.util.Map; 49 | import java.util.Queue; 50 | import java.util.Random; 51 | import java.util.regex.Matcher; 52 | import java.util.regex.Pattern; 53 | import java.util.Locale; 54 | 55 | class Utils { 56 | private static final String TAG = "Utils"; 57 | 58 | static Candidate transformCandidate(RtcCandidate rtcCandidate) { 59 | Candidate candidate = new Candidate( 60 | CandidateType.valueOf(rtcCandidate.getType().name()), 61 | ComponentType.valueOf(rtcCandidate.getComponentType().name()) 62 | ); 63 | if (rtcCandidate.getUfrag() != null) { 64 | candidate.setUfrag(rtcCandidate.getUfrag()); 65 | } 66 | if (rtcCandidate.getPassword() != null) { 67 | candidate.setPassword(rtcCandidate.getPassword()); 68 | } 69 | candidate.setFoundation(rtcCandidate.getFoundation()); 70 | candidate.setTransportType(TransportType.valueOf(rtcCandidate.getTransportType().name())); 71 | candidate.setPriority(rtcCandidate.getPriority()); 72 | candidate.setAddress(rtcCandidate.getAddress()); 73 | candidate.setPort(rtcCandidate.getPort()); 74 | if (rtcCandidate.getRelatedAddress() != null) { 75 | candidate.setBaseAddress(rtcCandidate.getRelatedAddress()); 76 | } 77 | if (rtcCandidate.getRelatedPort() >= 0) { 78 | candidate.setBasePort(rtcCandidate.getRelatedPort()); 79 | } 80 | return candidate; 81 | } 82 | 83 | private static boolean encodingNameMatches(String encodingName, RtcPayload payload) { 84 | if (encodingName == null) { 85 | return false; 86 | } 87 | String actualEncodingName = payload.getEncodingName(); 88 | return encodingName.equalsIgnoreCase(actualEncodingName); 89 | } 90 | 91 | static List transformPayloads(List payloads, MediaType mediaType) { 92 | if (payloads == null) { 93 | throw new NullPointerException("payloads should not be null"); 94 | } 95 | if (mediaType == MediaType.UNKNOWN) { 96 | throw new IllegalArgumentException("media type should not be UNKNOWN"); 97 | } 98 | List result = new LinkedList<>(); 99 | for (RtcPayload payload : payloads) { 100 | if (encodingNameMatches("RTX", payload)) { 101 | continue; 102 | } 103 | try { 104 | CodecType codecType = CodecType.valueOf(payload.getEncodingName().toUpperCase(Locale.ENGLISH)); 105 | RtcPayload rtxPayload = findRtxPayloadForPayloadType(payloads, payload.getPayloadType()); 106 | 107 | Payload owrPayload; 108 | if (mediaType == MediaType.AUDIO) { 109 | owrPayload = new AudioPayload(codecType, payload.getPayloadType(), 110 | payload.getClockRate(), payload.getChannels()); 111 | } else { 112 | owrPayload = new VideoPayload(codecType, payload.getPayloadType(), 113 | payload.getClockRate(), payload.isCcmFir(), payload.isNackPli()); 114 | } 115 | if (rtxPayload != null) { 116 | owrPayload.setRtxPayloadType(rtxPayload.getPayloadType()); 117 | Integer rtxTime = (Integer) rtxPayload.getParameters().get("rtx-time"); 118 | if (rtxTime != null) { 119 | owrPayload.setRtxTime(rtxTime); 120 | } 121 | } 122 | result.add(owrPayload); 123 | } catch (IllegalArgumentException e) { 124 | Log.d(TAG, "unknown codec type: " + payload.getEncodingName()); 125 | } 126 | } 127 | return result; 128 | } 129 | 130 | private static RtcPayload findPayloadByPayloadType(List payloads, int payloadType) { 131 | if (payloadType < 0) { 132 | return null; 133 | } 134 | for (RtcPayload payload : payloads) { 135 | if (payload.getPayloadType() == payloadType) { 136 | return payload; 137 | } 138 | } 139 | return null; 140 | } 141 | 142 | private static RtcPayload findRtxPayloadForPayloadType(List payloads, int payloadType) { 143 | if (payloadType < 0) { 144 | return null; 145 | } 146 | for (RtcPayload payload : payloads) { 147 | if (encodingNameMatches("RTX", payload)) { 148 | int apt = getAssociatedPayloadType(payload); 149 | if (apt > 0 && apt == payloadType) { 150 | return payload; 151 | } 152 | } 153 | } 154 | return null; 155 | } 156 | 157 | private static int getAssociatedPayloadType(RtcPayload payload) { 158 | Map parameters = payload.getParameters(); 159 | if (parameters == null) { 160 | return -1; 161 | } 162 | try { 163 | return (int) parameters.get("apt"); 164 | } catch (ClassCastException ignored) { 165 | return -1; 166 | } catch (NullPointerException ignored) { 167 | return -1; 168 | } 169 | } 170 | 171 | private static RtcPayload findMatchingPayload(List payloads, RtcPayload matchingPayload) { 172 | for (RtcPayload payload : payloads) { 173 | if (!encodingNameMatches(payload.getEncodingName(), matchingPayload)) { 174 | continue; 175 | } 176 | try { 177 | if ((int) payload.getParameters().get("packetization-mode") != 178 | (int) matchingPayload.getParameters().get("packetization-mode")) { 179 | continue; 180 | } 181 | } catch (NullPointerException ignored) { 182 | } 183 | return payload; 184 | } 185 | return null; 186 | } 187 | 188 | private static RtcPayload intersectPayload(RtcPayload payload, RtcPayload filter) { 189 | return new RtcPayloadImpl( 190 | payload.getPayloadType(), 191 | payload.getEncodingName(), 192 | payload.getClockRate(), 193 | payload.getParameters(), 194 | payload.getChannels(), 195 | payload.isNack() && filter.isNack(), 196 | payload.isNackPli() && filter.isNackPli(), 197 | payload.isCcmFir() && filter.isCcmFir() 198 | ); 199 | } 200 | 201 | static List intersectPayloads(List payloads, List filterPayloads) { 202 | List result = new LinkedList<>(); 203 | for (RtcPayload payload : payloads) { 204 | if (encodingNameMatches("RTX", payload)) { 205 | RtcPayload associatedPayload = findPayloadByPayloadType(payloads, getAssociatedPayloadType(payload)); 206 | RtcPayload matchingPayload = findMatchingPayload(filterPayloads, associatedPayload); 207 | if (matchingPayload != null) { 208 | if (findRtxPayloadForPayloadType(filterPayloads, matchingPayload.getPayloadType()) != null) { 209 | result.add(payload); 210 | } 211 | } 212 | } else { 213 | RtcPayload matchingPayload = findMatchingPayload(filterPayloads, payload); 214 | if (matchingPayload != null) { 215 | result.add(intersectPayload(payload, matchingPayload)); 216 | } 217 | } 218 | } 219 | return result; 220 | } 221 | 222 | public static List reorderPayloadsByFilter(final List payloads, final List filterPayloads) { 223 | List result = new LinkedList<>(); 224 | List payloadsCopy = new LinkedList<>(); 225 | payloadsCopy.addAll(payloads); 226 | 227 | for (RtcPayload filterPayload : filterPayloads) { 228 | if (encodingNameMatches("RTX", filterPayload)) { 229 | continue; 230 | } 231 | RtcPayload payload = findMatchingPayload(payloadsCopy, filterPayload); 232 | if (payload != null) { 233 | payloadsCopy.remove(payload); 234 | result.add(payload); 235 | } 236 | } 237 | for (RtcPayload payload : payloadsCopy) { 238 | result.add(payload); 239 | } 240 | return result; 241 | } 242 | 243 | public static List selectPreferredPayload(final List payloads) { 244 | List result = new LinkedList<>(); 245 | for (RtcPayload payload : payloads) { 246 | if (!encodingNameMatches("RTX", payload)) { 247 | result.add(payload); 248 | break; 249 | } 250 | } 251 | if (!result.isEmpty()) { 252 | RtcPayload payload = findRtxPayloadForPayloadType(payloads, result.get(0).getPayloadType()); 253 | if (payload != null) { 254 | result.add(payload); 255 | } 256 | } 257 | return result; 258 | } 259 | 260 | private static final Random sRandom = new Random(); 261 | 262 | static String randomString(int length) { 263 | byte[] randomBytes = new byte[(int) Math.ceil(length * 3.0 / 4.0)]; 264 | sRandom.nextBytes(randomBytes); 265 | return new String(Base64.encode(randomBytes, Base64.NO_WRAP | Base64.NO_PADDING)).substring(0, length); 266 | } 267 | 268 | private static final Pattern sPemPattern = Pattern.compile( 269 | ".*-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE-----.*", 270 | Pattern.DOTALL 271 | ); 272 | 273 | static String fingerprintFromPem(String pem, String hashFunction) { 274 | Matcher matcher = sPemPattern.matcher(pem); 275 | if (!matcher.matches()) { 276 | return null; 277 | } 278 | String base64der = matcher.replaceFirst("$1").replaceAll("\r?\n", ""); 279 | try { 280 | byte[] der = Base64.decode(base64der.getBytes("UTF8"), Base64.NO_WRAP | Base64.NO_PADDING | Base64.CRLF); 281 | if (der != null) { 282 | MessageDigest digest = MessageDigest.getInstance(hashFunction.toUpperCase(Locale.ENGLISH)); 283 | byte[] derHash = digest.digest(der); 284 | 285 | StringBuilder fingerprintBuilder = new StringBuilder(derHash.length * 3 - 1); 286 | for (int i = 0; i < derHash.length; i++) { 287 | if (i > 0) { 288 | fingerprintBuilder.append(':'); 289 | } 290 | fingerprintBuilder.append(Character.forDigit((derHash[i] >> 4) & 0xF, 16)); 291 | fingerprintBuilder.append(Character.forDigit(derHash[i] & 0xF, 16)); 292 | } 293 | 294 | return fingerprintBuilder.toString().toUpperCase(Locale.ENGLISH); 295 | } 296 | return null; 297 | } catch (NoSuchAlgorithmException | UnsupportedEncodingException exception) { 298 | Log.e(TAG, "failed to parse pem certificate: " + exception); 299 | throw new RuntimeException(exception); 300 | } 301 | } 302 | 303 | static List> resolveOfferedStreams(SessionDescription remoteDescription, List streams) { 304 | if (remoteDescription.getType() == SessionDescription.Type.ANSWER) { 305 | throw new IllegalArgumentException("remote session description should not be an answer"); 306 | } 307 | List> result = new LinkedList<>(); 308 | // For inbound calls the requested streams are split by type 309 | Queue audioStreams = new LinkedList<>(); 310 | Queue videoStreams = new LinkedList<>(); 311 | Queue dataStreams = new LinkedList<>(); 312 | for (StreamSet.Stream stream : streams) { 313 | switch (stream.getType()) { 314 | case AUDIO: 315 | audioStreams.add(stream); 316 | break; 317 | case VIDEO: 318 | videoStreams.add(stream); 319 | break; 320 | case DATA: 321 | dataStreams.add(stream); 322 | break; 323 | } 324 | } 325 | // Pair each requested stream with a stream from the remote session description 326 | for (StreamDescription streamDescription : remoteDescription.getStreamDescriptions()) { 327 | StreamSet.Stream stream = null; 328 | switch (streamDescription.getType()) { 329 | case AUDIO: 330 | stream = audioStreams.poll(); 331 | break; 332 | case VIDEO: 333 | stream = videoStreams.poll(); 334 | break; 335 | case DATA: 336 | stream = dataStreams.poll(); 337 | break; 338 | } 339 | result.add(new Pair<>(streamDescription, stream)); 340 | } 341 | // Any requested streams that are left are inactivated 342 | for (StreamSet.Stream stream : audioStreams) { 343 | stream.setStreamMode(StreamMode.INACTIVE); 344 | } 345 | for (StreamSet.Stream stream : videoStreams) { 346 | stream.setStreamMode(StreamMode.INACTIVE); 347 | } 348 | for (StreamSet.Stream stream : dataStreams) { 349 | stream.setStreamMode(StreamMode.INACTIVE); 350 | } 351 | return result; 352 | } 353 | } 354 | --------------------------------------------------------------------------------