diff --git a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java index e40a17cd14..caad17125b 100644 --- a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java @@ -378,14 +378,6 @@ public class PeerConnectionClient { } private void createPeerConnectionFactoryInternal(Context context) { - PeerConnectionFactory.initializeInternalTracer(); - if (peerConnectionParameters.tracing) { - PeerConnectionFactory.startInternalTracingCapture( - Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator - + "webrtc-trace.txt"); - } - Log.d(TAG, - "Create peer connection factory. Use video: " + peerConnectionParameters.videoCallEnabled); isError = false; // Initialize field trials. @@ -424,8 +416,21 @@ public class PeerConnectionClient { } } Log.d(TAG, "Preferred video codec: " + preferredVideoCodec); - PeerConnectionFactory.initializeFieldTrials(fieldTrials); - Log.d(TAG, "Field trials: " + fieldTrials); + + // Initialize WebRTC + Log.d(TAG, + "Initialize WebRTC. Field trials: " + fieldTrials + " Enable video HW acceleration: " + + peerConnectionParameters.videoCodecHwAcceleration); + PeerConnectionFactory.initialize( + PeerConnectionFactory.InitializationOptions.builder(context) + .setFieldTrials(fieldTrials) + .setEnableVideoHwAcceleration(peerConnectionParameters.videoCodecHwAcceleration) + .createInitializationOptions()); + if (peerConnectionParameters.tracing) { + PeerConnectionFactory.startInternalTracingCapture( + Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + + "webrtc-trace.txt"); + } // Check if ISAC is used by default. preferIsac = peerConnectionParameters.audioCodec != null @@ -504,8 +509,6 @@ public class PeerConnectionClient { }); // Create peer connection factory. - PeerConnectionFactory.initializeAndroidGlobals( - context, peerConnectionParameters.videoCodecHwAcceleration); if (options != null) { Log.d(TAG, "Factory networkIgnoreMask option: " + options.networkIgnoreMask); } diff --git a/rtc_base/java/src/org/webrtc/ContextUtils.java b/rtc_base/java/src/org/webrtc/ContextUtils.java index ec9af938ca..a53059a9cc 100644 --- a/rtc_base/java/src/org/webrtc/ContextUtils.java +++ b/rtc_base/java/src/org/webrtc/ContextUtils.java @@ -23,17 +23,13 @@ public class ContextUtils { /** * Stores the application context that will be returned by getApplicationContext. This is called - * by PeerConnectionFactory.initializeAndroidGlobals. + * by PeerConnectionFactory.initialize. The application context must be set before creating + * a PeerConnectionFactory and must not be modified while it is alive. */ public static void initialize(Context applicationContext) { - if (ContextUtils.applicationContext != null) { - // TODO(sakal): Re-enable after the migration period. - // throw new RuntimeException("Multiple ContextUtils.initialize calls."); - Logging.e( - TAG, "Calling ContextUtils.initialize multiple times, this will crash in the future!"); - } if (applicationContext == null) { - throw new RuntimeException("Application context cannot be null for ContextUtils.initialize."); + throw new IllegalArgumentException( + "Application context cannot be null for ContextUtils.initialize."); } ContextUtils.applicationContext = applicationContext; } diff --git a/rtc_base/java/src/org/webrtc/Logging.java b/rtc_base/java/src/org/webrtc/Logging.java index 827ecc61da..3c3b7a68b3 100644 --- a/rtc_base/java/src/org/webrtc/Logging.java +++ b/rtc_base/java/src/org/webrtc/Logging.java @@ -30,8 +30,6 @@ public class Logging { private static final Logger fallbackLogger = createFallbackLogger(); private static volatile boolean tracingEnabled; private static volatile boolean loggingEnabled; - private static enum NativeLibStatus { UNINITIALIZED, LOADED, FAILED } - private static volatile NativeLibStatus nativeLibStatus = NativeLibStatus.UNINITIALIZED; private static Logger createFallbackLogger() { final Logger fallbackLogger = Logger.getLogger("org.webrtc.Logging"); @@ -39,19 +37,6 @@ public class Logging { return fallbackLogger; } - private static boolean loadNativeLibrary() { - if (nativeLibStatus == NativeLibStatus.UNINITIALIZED) { - try { - System.loadLibrary("jingle_peerconnection_so"); - nativeLibStatus = NativeLibStatus.LOADED; - } catch (UnsatisfiedLinkError t) { - nativeLibStatus = NativeLibStatus.FAILED; - fallbackLogger.log(Level.WARNING, "Failed to load jingle_peerconnection_so: ", t); - } - } - return nativeLibStatus == NativeLibStatus.LOADED; - } - // Keep in sync with webrtc/common_types.h:TraceLevel. public enum TraceLevel { TRACE_NONE(0x0000), @@ -80,19 +65,10 @@ public class Logging { public enum Severity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, LS_NONE } public static void enableLogThreads() { - if (!loadNativeLibrary()) { - fallbackLogger.log(Level.WARNING, "Cannot enable log thread because native lib not loaded."); - return; - } nativeEnableLogThreads(); } public static void enableLogTimeStamps() { - if (!loadNativeLibrary()) { - fallbackLogger.log( - Level.WARNING, "Cannot enable log timestamps because native lib not loaded."); - return; - } nativeEnableLogTimeStamps(); } @@ -100,11 +76,6 @@ public class Logging { // On Android, use "logcat:" for |path| to send output there. // Note: this function controls the output of the WEBRTC_TRACE() macros. public static synchronized void enableTracing(String path, EnumSet levels) { - if (!loadNativeLibrary()) { - fallbackLogger.log(Level.WARNING, "Cannot enable tracing because native lib not loaded."); - return; - } - if (tracingEnabled) { return; } @@ -120,10 +91,6 @@ public class Logging { // output. On Android, the output will be directed to Logcat. // Note: this function starts collecting the output of the LOG() macros. public static synchronized void enableLogToDebugOutput(Severity severity) { - if (!loadNativeLibrary()) { - fallbackLogger.log(Level.WARNING, "Cannot enable logging because native lib not loaded."); - return; - } nativeEnableLogToDebugOutput(severity.ordinal()); loggingEnabled = true; } diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index d313771e17..211337423c 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -401,6 +401,7 @@ android_library("libjingle_peerconnection_java") { "api/org/webrtc/MediaSource.java", "api/org/webrtc/MediaStream.java", "api/org/webrtc/MediaStreamTrack.java", + "api/org/webrtc/NativeLibraryLoader.java", "api/org/webrtc/NetworkMonitor.java", "api/org/webrtc/NetworkMonitorAutoDetect.java", "api/org/webrtc/PeerConnection.java", @@ -451,6 +452,7 @@ android_library("libjingle_peerconnection_java") { "src/java/org/webrtc/I420BufferImpl.java", "src/java/org/webrtc/JniCommon.java", "src/java/org/webrtc/MediaCodecUtils.java", + "src/java/org/webrtc/NativeLibrary.java", "src/java/org/webrtc/NV12Buffer.java", "src/java/org/webrtc/NV21Buffer.java", "src/java/org/webrtc/TextureBufferImpl.java", diff --git a/sdk/android/api/org/webrtc/NativeLibraryLoader.java b/sdk/android/api/org/webrtc/NativeLibraryLoader.java new file mode 100644 index 0000000000..8bd7b3b250 --- /dev/null +++ b/sdk/android/api/org/webrtc/NativeLibraryLoader.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +/** + * Interface for loading native libraries. A custom loader can be passed to + * PeerConnectionFactory.initialize. + */ +public interface NativeLibraryLoader { + /** + * Loads a native library with the given name. + * + * @return True on success + */ + boolean load(String name); +} diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java index 59ee91cf96..14c367f532 100644 --- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java +++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java @@ -18,15 +18,10 @@ import java.util.List; * the PeerConnection API for clients. */ public class PeerConnectionFactory { - private static volatile boolean nativeLibLoaded; - static { - try { - System.loadLibrary("jingle_peerconnection_so"); - nativeLibLoaded = true; - } catch (UnsatisfiedLinkError t) { - nativeLibLoaded = false; - } + // TODO(sakal): Remove once all dependencies have started using + // PeerConnectionFactory.initialize. + NativeLibrary.initialize(new NativeLibrary.DefaultLoader()); } public static final String TRIAL_ENABLED = "Enabled"; @@ -42,6 +37,65 @@ public class PeerConnectionFactory { private EglBase localEglbase; private EglBase remoteEglbase; + public static class InitializationOptions { + final Context applicationContext; + final String fieldTrials; + final boolean enableInternalTracer; + final boolean enableVideoHwAcceleration; + final NativeLibraryLoader nativeLibraryLoader; + + private InitializationOptions(Context applicationContext, String fieldTrials, + boolean enableInternalTracer, boolean enableVideoHwAcceleration, + NativeLibraryLoader nativeLibraryLoader) { + this.applicationContext = applicationContext; + this.fieldTrials = fieldTrials; + this.enableInternalTracer = enableInternalTracer; + this.enableVideoHwAcceleration = enableVideoHwAcceleration; + this.nativeLibraryLoader = nativeLibraryLoader; + } + + public static Builder builder(Context applicationContext) { + return new Builder(applicationContext); + } + + public static class Builder { + private final Context applicationContext; + private String fieldTrials = ""; + private boolean enableInternalTracer = true; + private boolean enableVideoHwAcceleration = true; + private NativeLibraryLoader nativeLibraryLoader = new NativeLibrary.DefaultLoader(); + + Builder(Context applicationContext) { + this.applicationContext = applicationContext; + } + + public Builder setFieldTrials(String fieldTrials) { + this.fieldTrials = fieldTrials; + return this; + } + + public Builder setEnableInternalTracer(boolean enableInternalTracer) { + this.enableInternalTracer = enableInternalTracer; + return this; + } + + public Builder setEnableVideoHwAcceleration(boolean enableVideoHwAcceleration) { + this.enableVideoHwAcceleration = enableVideoHwAcceleration; + return this; + } + + public Builder setNativeLibraryLoader(NativeLibraryLoader nativeLibraryLoader) { + this.nativeLibraryLoader = nativeLibraryLoader; + return this; + } + + public PeerConnectionFactory.InitializationOptions createInitializationOptions() { + return new PeerConnectionFactory.InitializationOptions(applicationContext, fieldTrials, + enableInternalTracer, enableVideoHwAcceleration, nativeLibraryLoader); + } + } + } + public static class Options { // Keep in sync with webrtc/rtc_base/network.h! static final int ADAPTER_TYPE_UNKNOWN = 0; @@ -56,17 +110,35 @@ public class PeerConnectionFactory { public boolean disableNetworkMonitor; } + /** + * Loads and initializes WebRTC. This must be called at least once before creating a + * PeerConnectionFactory. Replaces all the old initialization methods. Must not be called while + * a PeerConnectionFactory is alive. + */ + public static void initialize(InitializationOptions options) { + ContextUtils.initialize(options.applicationContext); + NativeLibrary.initialize(options.nativeLibraryLoader); + nativeInitializeAndroidGlobals(options.applicationContext, options.enableVideoHwAcceleration); + initializeFieldTrials(options.fieldTrials); + if (options.enableInternalTracer) { + initializeInternalTracer(); + } + } + // Must be called at least once before creating a PeerConnectionFactory // (for example, at application startup time). - public static native void nativeInitializeAndroidGlobals( + private static native void nativeInitializeAndroidGlobals( Context context, boolean videoHwAcceleration); + // Deprecated, use PeerConnectionFactory.initialize instead. + @Deprecated public static void initializeAndroidGlobals(Context context, boolean videoHwAcceleration) { ContextUtils.initialize(context); nativeInitializeAndroidGlobals(context, videoHwAcceleration); } // Older signature of initializeAndroidGlobals. The extra parameters are now meaningless. + // Deprecated, use PeerConnectionFactory.initialize instead. @Deprecated public static boolean initializeAndroidGlobals(Object context, boolean initializeAudio, boolean initializeVideo, boolean videoHwAcceleration) { @@ -76,7 +148,8 @@ public class PeerConnectionFactory { // Field trial initialization. Must be called before PeerConnectionFactory // is created. - public static native void initializeFieldTrials(String fieldTrialsInitString); + // Deprecated, use PeerConnectionFactory.initialize instead. + @Deprecated public static native void initializeFieldTrials(String fieldTrialsInitString); // Wrapper of webrtc::field_trial::FindFullName. Develop the feature with default behaviour off. // Example usage: // if (PeerConnectionFactory.fieldTrialsFindFullName("WebRTCExperiment").equals("Enabled")) { @@ -85,12 +158,13 @@ public class PeerConnectionFactory { // method2(); // } public static String fieldTrialsFindFullName(String name) { - return nativeLibLoaded ? nativeFieldTrialsFindFullName(name) : ""; + return NativeLibrary.isLoaded() ? nativeFieldTrialsFindFullName(name) : ""; } private static native String nativeFieldTrialsFindFullName(String name); // Internal tracing initialization. Must be called before PeerConnectionFactory is created to // prevent racing with tracing code. - public static native void initializeInternalTracer(); + // Deprecated, use PeerConnectionFactory.initialize instead. + @Deprecated public static native void initializeInternalTracer(); // Internal tracing shutdown, called to prevent resource leaks. Must be called after // PeerConnectionFactory is gone to prevent races with code performing tracing. public static native void shutdownInternalTracer(); @@ -111,6 +185,11 @@ public class PeerConnectionFactory { public PeerConnectionFactory( Options options, VideoEncoderFactory encoderFactory, VideoDecoderFactory decoderFactory) { + if (!NativeLibrary.isLoaded() || ContextUtils.getApplicationContext() == null) { + throw new IllegalStateException( + "PeerConnectionFactory.initialize was not called before creating a " + + "PeerConnectionFactory."); + } nativeFactory = nativeCreatePeerConnectionFactory(options, encoderFactory, decoderFactory); if (nativeFactory == 0) { throw new RuntimeException("Failed to initialize PeerConnectionFactory!"); diff --git a/sdk/android/src/java/org/webrtc/NativeLibrary.java b/sdk/android/src/java/org/webrtc/NativeLibrary.java new file mode 100644 index 0000000000..3b88f14b30 --- /dev/null +++ b/sdk/android/src/java/org/webrtc/NativeLibrary.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +class NativeLibrary { + private static String TAG = "NativeLibrary"; + + static class DefaultLoader implements NativeLibraryLoader { + @Override + public boolean load(String name) { + Logging.d(TAG, "Loading library: " + name); + try { + System.loadLibrary(name); + } catch (UnsatisfiedLinkError e) { + Logging.e(TAG, "Failed to load native library: " + name, e); + return false; + } + return true; + } + } + + private static Object lock = new Object(); + private static boolean libraryLoaded = false; + + /** + * Loads the native library. Clients should call PeerConnectionFactory.initialize. It will call + * this method for them. + */ + static void initialize(NativeLibraryLoader loader) { + synchronized (lock) { + if (libraryLoaded) { + Logging.d(TAG, "Native library has already been loaded."); + return; + } + Logging.d(TAG, "Loading native library."); + libraryLoaded = loader.load("jingle_peerconnection_so"); + } + } + + /** Returns true if the library has been loaded successfully. */ + static boolean isLoaded() { + synchronized (lock) { + return libraryLoaded; + } + } +}