[android] Allow to configure field trials per PeerConnectionFactory
Provide webrtc::Environment to create AudioProcessing and
AudioDeviceModule through java layer
Reland of https://webrtc-review.googlesource.com/c/src/+/388620
Bug: webrtc:369904700, webrtc:413413572
Change-Id: Id1ac4bb94a4a80d788a409a235c8ea96d568f170
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/390322
Reviewed-by: ZoƩ Lepaul <xalep@webrtc.org>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#44595}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index d9ca1e0..5ce345f 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -117,6 +117,7 @@
":base_jni",
":builtin_audio_codecs_jni",
":default_video_codec_factory_jni",
+ ":environment_jni",
":java_audio_device_module_jni",
":peerconnection_jni",
":video_jni",
@@ -182,6 +183,15 @@
]
}
+ rtc_android_library("environment_java") {
+ visibility = [ "*" ]
+ sources = [ "api/org/webrtc/Environment.java" ]
+ deps = [
+ ":generated_environment_jni_java",
+ "//third_party/androidx:androidx_annotation_annotation_java",
+ ]
+ }
+
rtc_android_library("audio_api_java") {
visibility = [ "*" ]
sources = [
@@ -330,6 +340,7 @@
":base_java",
":builtin_audio_codecs_java",
":default_video_codec_factory_java",
+ ":environment_java",
":generated_base_jni_java",
":generated_peerconnection_jni_java",
@@ -616,6 +627,21 @@
]
}
+ rtc_library("environment_jni") {
+ visibility = [ "*" ]
+ allow_poison = [ "environment_construction" ]
+ sources = [ "src/jni/environment.cc" ]
+ deps = [
+ ":generated_environment_jni",
+ ":native_api_jni",
+ "../../api:field_trials",
+ "../../api:field_trials_view",
+ "../../api/environment",
+ "../../api/environment:environment_factory",
+ "//third_party/jni_zero",
+ ]
+ }
+
rtc_library("builtin_audio_codecs_jni") {
visibility = [ "*" ]
allow_poison = [ "audio_codecs" ]
@@ -778,6 +804,7 @@
deps = [
":base_jni",
+ ":environment_jni",
":generated_external_classes_jni",
":generated_external_classes_jni",
":generated_peerconnection_jni",
@@ -805,6 +832,7 @@
"../../api/audio:builtin_audio_processing_builder",
"../../api/audio_codecs:audio_codecs_api",
"../../api/crypto:options",
+ "../../api/environment",
"../../api/neteq:neteq_api",
"../../api/rtc_event_log:rtc_event_log_factory",
"../../api/task_queue:default_task_queue_factory",
@@ -1319,6 +1347,12 @@
jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
}
+ generate_jni("generated_environment_jni") {
+ sources = [ "api/org/webrtc/Environment.java" ]
+ namespace = "webrtc::jni"
+ jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
+ }
+
generate_jni("generated_metrics_jni") {
sources = [ "api/org/webrtc/Metrics.java" ]
namespace = "webrtc::jni"
diff --git a/sdk/android/api/org/webrtc/AudioProcessingFactory.java b/sdk/android/api/org/webrtc/AudioProcessingFactory.java
index bd8fdb8..e0aa25b 100644
--- a/sdk/android/api/org/webrtc/AudioProcessingFactory.java
+++ b/sdk/android/api/org/webrtc/AudioProcessingFactory.java
@@ -16,5 +16,14 @@
* Dynamically allocates a webrtc::AudioProcessing instance and returns a pointer to it.
* The caller takes ownership of the object.
*/
- public long createNative();
+ // TODO: bugs.webrtc.org/369904700 - Remove when implementations switch to
+ // another variant.
+ @Deprecated
+ public default long createNative() {
+ return 0;
+ }
+
+ public default long createNative(long webrtcEnvRef) {
+ return createNative();
+ }
}
diff --git a/sdk/android/api/org/webrtc/Environment.java b/sdk/android/api/org/webrtc/Environment.java
new file mode 100644
index 0000000..10749ec
--- /dev/null
+++ b/sdk/android/api/org/webrtc/Environment.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2025 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;
+
+import androidx.annotation.Nullable;
+
+/** Wrapper for the webrtc::Environment native class. */
+public final class Environment implements AutoCloseable {
+ private final long webrtcEnv;
+
+ /** Builder for {@link Environment}. */
+ public static class Builder {
+ public Builder setFieldTrials(String fieldTrials) {
+ this.fieldTrials = fieldTrials;
+ return this;
+ }
+
+ public Environment build() {
+ return new Environment(this.fieldTrials);
+ }
+
+ private @Nullable String fieldTrials;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Returns non-owning non-null native pointer to the webrtc::Environment */
+ public long ref() {
+ return webrtcEnv;
+ }
+
+ @Override
+ public void close() {
+ nativeFree(webrtcEnv);
+ }
+
+ private Environment(@Nullable String fieldTrials) {
+ this.webrtcEnv = nativeCreate(fieldTrials);
+ }
+
+ private static native long nativeCreate(@Nullable String fieldTrials);
+
+ private static native void nativeFree(long webrtcEnv);
+}
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
index 2b2ea15..eac3bb0 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
@@ -15,9 +15,6 @@
import androidx.annotation.Nullable;
import java.util.List;
import org.webrtc.Logging.Severity;
-import org.webrtc.MediaStreamTrack;
-import org.webrtc.PeerConnection;
-import org.webrtc.RtpCapabilities;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
@@ -166,6 +163,7 @@
public static class Builder {
@Nullable private Options options;
+ private Environment.Builder envBuilder = Environment.builder();
@Nullable private AudioDeviceModule audioDeviceModule;
private AudioEncoderFactoryFactory audioEncoderFactoryFactory =
new BuiltinAudioEncoderFactoryFactory();
@@ -186,6 +184,11 @@
return this;
}
+ public Builder setFieldTrials(String fieldTrials) {
+ envBuilder.setFieldTrials(fieldTrials);
+ return this;
+ }
+
public Builder setAudioDeviceModule(AudioDeviceModule audioDeviceModule) {
this.audioDeviceModule = audioDeviceModule;
return this;
@@ -261,24 +264,30 @@
public PeerConnectionFactory createPeerConnectionFactory() {
checkInitializeHasBeenCalled();
- if (audioDeviceModule == null) {
- audioDeviceModule = JavaAudioDeviceModule.builder(ContextUtils.getApplicationContext())
- .createAudioDeviceModule();
+ try (Environment env = envBuilder.build()) {
+ if (audioDeviceModule == null) {
+ audioDeviceModule = JavaAudioDeviceModule.builder(ContextUtils.getApplicationContext())
+ .createAudioDeviceModule();
+ }
+ return nativeCreatePeerConnectionFactory(
+ ContextUtils.getApplicationContext(),
+ options,
+ env.ref(),
+ audioDeviceModule.getNative(env.ref()),
+ audioEncoderFactoryFactory.createNativeAudioEncoderFactory(),
+ audioDecoderFactoryFactory.createNativeAudioDecoderFactory(),
+ videoEncoderFactory,
+ videoDecoderFactory,
+ audioProcessingFactory == null ? 0 : audioProcessingFactory.createNative(env.ref()),
+ fecControllerFactoryFactory == null ? 0 : fecControllerFactoryFactory.createNative(),
+ networkControllerFactoryFactory == null
+ ? 0
+ : networkControllerFactoryFactory.createNativeNetworkControllerFactory(),
+ networkStatePredictorFactoryFactory == null
+ ? 0
+ : networkStatePredictorFactoryFactory.createNativeNetworkStatePredictorFactory(),
+ neteqFactoryFactory == null ? 0 : neteqFactoryFactory.createNativeNetEqFactory());
}
- return nativeCreatePeerConnectionFactory(ContextUtils.getApplicationContext(), options,
- audioDeviceModule.getNativeAudioDeviceModulePointer(),
- audioEncoderFactoryFactory.createNativeAudioEncoderFactory(),
- audioDecoderFactoryFactory.createNativeAudioDecoderFactory(), videoEncoderFactory,
- videoDecoderFactory,
- audioProcessingFactory == null ? 0 : audioProcessingFactory.createNative(),
- fecControllerFactoryFactory == null ? 0 : fecControllerFactoryFactory.createNative(),
- networkControllerFactoryFactory == null
- ? 0
- : networkControllerFactoryFactory.createNativeNetworkControllerFactory(),
- networkStatePredictorFactoryFactory == null
- ? 0
- : networkStatePredictorFactoryFactory.createNativeNetworkStatePredictorFactory(),
- neteqFactoryFactory == null ? 0 : neteqFactoryFactory.createNativeNetEqFactory());
}
}
@@ -604,7 +613,7 @@
private static native void nativeStopInternalTracingCapture();
private static native PeerConnectionFactory nativeCreatePeerConnectionFactory(Context context,
- Options options, long nativeAudioDeviceModule, long audioEncoderFactory,
+ Options options, long webrtcEnvRef, long nativeAudioDeviceModule, long audioEncoderFactory,
long audioDecoderFactory, VideoEncoderFactory encoderFactory,
VideoDecoderFactory decoderFactory, long nativeAudioProcessor,
long nativeFecControllerFactory, long nativeNetworkControllerFactory,
diff --git a/sdk/android/api/org/webrtc/audio/AudioDeviceModule.java b/sdk/android/api/org/webrtc/audio/AudioDeviceModule.java
index 5a0bf5c..ba5097d 100644
--- a/sdk/android/api/org/webrtc/audio/AudioDeviceModule.java
+++ b/sdk/android/api/org/webrtc/audio/AudioDeviceModule.java
@@ -22,7 +22,14 @@
* Returns a C++ pointer to a webrtc::AudioDeviceModule. Caller does _not_ take ownership and
* lifetime is handled through the release() call.
*/
- long getNativeAudioDeviceModulePointer();
+ @Deprecated
+ default long getNativeAudioDeviceModulePointer() {
+ return 0;
+ }
+
+ public default long getNative(long webrtcEnvRef) {
+ return getNativeAudioDeviceModulePointer();
+ }
/**
* Release resources for this AudioDeviceModule, including native resources. The object should not
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java
index a28b9c2..1e36fef 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionFactoryTest.java
@@ -62,4 +62,31 @@
.createInitializationOptions());
PeerConnectionFactory.shutdownInternalTracer();
}
+
+ // Tests that the JNI glue between Java and C++ does not crash when creating a
+ // PeerConnectionFactory.
+ @Test
+ @SmallTest
+ public void testCreation() throws Exception {
+ PeerConnectionFactory.initialize(
+ PeerConnectionFactory.InitializationOptions.builder(
+ InstrumentationRegistry.getTargetContext())
+ .setNativeLibraryName(TestConstants.NATIVE_LIBRARY)
+ .createInitializationOptions());
+
+ PeerConnectionFactory factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
+ }
+
+ @Test
+ @SmallTest
+ public void testCreationWithFieldTrials() throws Exception {
+ PeerConnectionFactory.initialize(
+ PeerConnectionFactory.InitializationOptions.builder(
+ InstrumentationRegistry.getTargetContext())
+ .setNativeLibraryName(TestConstants.NATIVE_LIBRARY)
+ .createInitializationOptions());
+
+ PeerConnectionFactory factory =
+ PeerConnectionFactory.builder().setFieldTrials("").createPeerConnectionFactory();
+ }
}
diff --git a/sdk/android/src/jni/environment.cc b/sdk/android/src/jni/environment.cc
new file mode 100644
index 0000000..fab50eb
--- /dev/null
+++ b/sdk/android/src/jni/environment.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2025 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.
+ */
+#include "api/environment/environment.h"
+
+#include <jni.h>
+
+#include <memory>
+#include <utility>
+
+#include "api/environment/environment_factory.h"
+#include "api/field_trials.h"
+#include "api/field_trials_view.h"
+#include "sdk/android/generated_environment_jni/Environment_jni.h"
+#include "sdk/android/native_api/jni/java_types.h"
+#include "third_party/jni_zero/jni_zero.h"
+
+namespace webrtc::jni {
+
+void JNI_Environment_Free(JNIEnv* /*env*/, jlong webrtcEnv) {
+ delete reinterpret_cast<Environment*>(webrtcEnv);
+}
+
+jlong JNI_Environment_Create(
+ JNIEnv* env,
+ const jni_zero::JavaParamRef<jstring>& fieldTrials) {
+ std::unique_ptr<FieldTrialsView> field_trials;
+ if (!IsNull(env, fieldTrials)) {
+ field_trials =
+ std::make_unique<FieldTrials>(JavaToNativeString(env, fieldTrials));
+ }
+ return NativeToJavaPointer(
+ new Environment(CreateEnvironment(std::move(field_trials))));
+}
+
+} // namespace webrtc::jni
diff --git a/sdk/android/src/jni/pc/peer_connection_factory.cc b/sdk/android/src/jni/pc/peer_connection_factory.cc
index a09859a..fbab65f 100644
--- a/sdk/android/src/jni/pc/peer_connection_factory.cc
+++ b/sdk/android/src/jni/pc/peer_connection_factory.cc
@@ -29,6 +29,7 @@
#include "api/audio_codecs/audio_encoder_factory.h"
#include "api/audio_options.h"
#include "api/enable_media.h"
+#include "api/environment/environment.h"
#include "api/fec_controller.h"
#include "api/media_stream_interface.h"
#include "api/neteq/neteq_factory.h"
@@ -256,6 +257,7 @@
JNIEnv* jni,
const jni_zero::JavaParamRef<jobject>& jcontext,
const jni_zero::JavaParamRef<jobject>& joptions,
+ const Environment& env,
scoped_refptr<AudioDeviceModule> audio_device_module,
scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
@@ -292,8 +294,8 @@
JavaToNativePeerConnectionFactoryOptions(jni, joptions);
PeerConnectionFactoryDependencies dependencies;
- // TODO(bugs.webrtc.org/13145): Also add socket_server.get() to the
- // dependencies.
+ dependencies.env = env;
+ dependencies.socket_factory = socket_server.get();
dependencies.network_thread = network_thread.get();
dependencies.worker_thread = worker_thread.get();
dependencies.signaling_thread = signaling_thread.get();
@@ -347,6 +349,7 @@
JNIEnv* jni,
const jni_zero::JavaParamRef<jobject>& jcontext,
const jni_zero::JavaParamRef<jobject>& joptions,
+ jlong webrtc_env_ref,
jlong native_audio_device_module,
jlong native_audio_encoder_factory,
jlong native_audio_decoder_factory,
@@ -357,10 +360,12 @@
jlong native_network_controller_factory,
jlong native_network_state_predictor_factory,
jlong native_neteq_factory) {
+ const Environment* env = reinterpret_cast<Environment*>(webrtc_env_ref);
+ RTC_CHECK(env != nullptr);
scoped_refptr<AudioProcessing> audio_processor(
reinterpret_cast<AudioProcessing*>(native_audio_processor));
return CreatePeerConnectionFactoryForJava(
- jni, jcontext, joptions,
+ jni, jcontext, joptions, *env,
scoped_refptr<AudioDeviceModule>(
reinterpret_cast<AudioDeviceModule*>(native_audio_device_module)),
TakeOwnershipOfRefPtr<AudioEncoderFactory>(native_audio_encoder_factory),