/* * Copyright 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. */ #include "sdk/android/src/jni/pc/peer_connection_factory.h" #include #include #include "absl/memory/memory.h" #include "api/video_codecs/video_decoder_factory.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/base/media_engine.h" #include "modules/audio_device/include/audio_device.h" #include "modules/utility/include/jvm_android.h" // We don't depend on the audio processing module implementation. // The user may pass in a nullptr. #include "api/call/call_factory_interface.h" #include "api/rtc_event_log/rtc_event_log_factory.h" #include "api/task_queue/default_task_queue_factory.h" #include "api/video_codecs/video_decoder_factory.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/engine/webrtc_media_engine.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "rtc_base/event_tracer.h" #include "rtc_base/system/thread_registry.h" #include "rtc_base/thread.h" #include "sdk/android/generated_peerconnection_jni/PeerConnectionFactory_jni.h" #include "sdk/android/native_api/jni/java_types.h" #include "sdk/android/native_api/stacktrace/stacktrace.h" #include "sdk/android/src/jni/jni_helpers.h" #include "sdk/android/src/jni/logging/log_sink.h" #include "sdk/android/src/jni/pc/android_network_monitor.h" #include "sdk/android/src/jni/pc/audio.h" #include "sdk/android/src/jni/pc/ice_candidate.h" #include "sdk/android/src/jni/pc/owned_factory_and_threads.h" #include "sdk/android/src/jni/pc/peer_connection.h" #include "sdk/android/src/jni/pc/ssl_certificate_verifier_wrapper.h" #include "sdk/android/src/jni/pc/video.h" #include "system_wrappers/include/field_trial.h" namespace webrtc { namespace jni { namespace { // Take ownership of the jlong reference and cast it into an rtc::scoped_refptr. template rtc::scoped_refptr TakeOwnershipOfRefPtr(jlong j_pointer) { T* ptr = reinterpret_cast(j_pointer); rtc::scoped_refptr refptr; refptr.swap(&ptr); return refptr; } // Take ownership of the jlong reference and cast it into a std::unique_ptr. template std::unique_ptr TakeOwnershipOfUniquePtr(jlong native_pointer) { return std::unique_ptr(reinterpret_cast(native_pointer)); } typedef void (*JavaMethodPointer)(JNIEnv*, const JavaRef&); // Post a message on the given thread that will call the Java method on the // given Java object. void PostJavaCallback(JNIEnv* env, rtc::Thread* queue, const rtc::Location& posted_from, const JavaRef& j_object, JavaMethodPointer java_method_pointer) { // One-off message handler that calls the Java method on the specified Java // object before deleting itself. class JavaAsyncCallback : public rtc::MessageHandler { public: JavaAsyncCallback(JNIEnv* env, const JavaRef& j_object, JavaMethodPointer java_method_pointer) : j_object_(env, j_object), java_method_pointer_(java_method_pointer) {} void OnMessage(rtc::Message*) override { java_method_pointer_(AttachCurrentThreadIfNeeded(), j_object_); // The message has been delivered, clean up after ourself. delete this; } private: ScopedJavaGlobalRef j_object_; JavaMethodPointer java_method_pointer_; }; queue->Post(posted_from, new JavaAsyncCallback(env, j_object, java_method_pointer)); } absl::optional JavaToNativePeerConnectionFactoryOptions(JNIEnv* jni, const JavaRef& j_options) { if (j_options.is_null()) return absl::nullopt; PeerConnectionFactoryInterface::Options native_options; // This doesn't necessarily match the c++ version of this struct; feel free // to add more parameters as necessary. native_options.network_ignore_mask = Java_Options_getNetworkIgnoreMask(jni, j_options); native_options.disable_encryption = Java_Options_getDisableEncryption(jni, j_options); native_options.disable_network_monitor = Java_Options_getDisableNetworkMonitor(jni, j_options); return native_options; } // Place static objects into a container that gets leaked so we avoid // non-trivial destructor. struct StaticObjectContainer { // Field trials initialization string std::unique_ptr field_trials_init_string; // Set in PeerConnectionFactory_InjectLoggable(). std::unique_ptr jni_log_sink; }; StaticObjectContainer& GetStaticObjects() { static StaticObjectContainer* static_objects = new StaticObjectContainer(); return *static_objects; } ScopedJavaLocalRef NativeToScopedJavaPeerConnectionFactory( JNIEnv* env, rtc::scoped_refptr pcf, std::unique_ptr network_thread, std::unique_ptr worker_thread, std::unique_ptr signaling_thread) { OwnedFactoryAndThreads* owned_factory = new OwnedFactoryAndThreads( std::move(network_thread), std::move(worker_thread), std::move(signaling_thread), pcf); ScopedJavaLocalRef j_pcf = Java_PeerConnectionFactory_Constructor( env, NativeToJavaPointer(owned_factory)); PostJavaCallback(env, owned_factory->network_thread(), RTC_FROM_HERE, j_pcf, &Java_PeerConnectionFactory_onNetworkThreadReady); PostJavaCallback(env, owned_factory->worker_thread(), RTC_FROM_HERE, j_pcf, &Java_PeerConnectionFactory_onWorkerThreadReady); PostJavaCallback(env, owned_factory->signaling_thread(), RTC_FROM_HERE, j_pcf, &Java_PeerConnectionFactory_onSignalingThreadReady); return j_pcf; } PeerConnectionFactoryInterface* PeerConnectionFactoryFromJava(jlong j_p) { return reinterpret_cast(j_p)->factory(); } } // namespace // Note: Some of the video-specific PeerConnectionFactory methods are // implemented in "video.cc". This is done so that if an application // doesn't need video support, it can just link with "null_video.cc" // instead of "video.cc", which doesn't bring in the video-specific // dependencies. // Set in PeerConnectionFactory_initializeAndroidGlobals(). static bool factory_static_initialized = false; jobject NativeToJavaPeerConnectionFactory( JNIEnv* jni, rtc::scoped_refptr pcf, std::unique_ptr network_thread, std::unique_ptr worker_thread, std::unique_ptr signaling_thread) { return NativeToScopedJavaPeerConnectionFactory( jni, pcf, std::move(network_thread), std::move(worker_thread), std::move(signaling_thread)) .Release(); } static void JNI_PeerConnectionFactory_InitializeAndroidGlobals(JNIEnv* jni) { if (!factory_static_initialized) { JVM::Initialize(GetJVM()); factory_static_initialized = true; } } static void JNI_PeerConnectionFactory_InitializeFieldTrials( JNIEnv* jni, const JavaParamRef& j_trials_init_string) { std::unique_ptr& field_trials_init_string = GetStaticObjects().field_trials_init_string; if (j_trials_init_string.is_null()) { field_trials_init_string = nullptr; field_trial::InitFieldTrialsFromString(nullptr); return; } field_trials_init_string = std::make_unique( JavaToNativeString(jni, j_trials_init_string)); RTC_LOG(LS_INFO) << "initializeFieldTrials: " << *field_trials_init_string; field_trial::InitFieldTrialsFromString(field_trials_init_string->c_str()); } static void JNI_PeerConnectionFactory_InitializeInternalTracer(JNIEnv* jni) { rtc::tracing::SetupInternalTracer(); } static ScopedJavaLocalRef JNI_PeerConnectionFactory_FindFieldTrialsFullName( JNIEnv* jni, const JavaParamRef& j_name) { return NativeToJavaString( jni, field_trial::FindFullName(JavaToStdString(jni, j_name))); } static jboolean JNI_PeerConnectionFactory_StartInternalTracingCapture( JNIEnv* jni, const JavaParamRef& j_event_tracing_filename) { if (j_event_tracing_filename.is_null()) return false; const char* init_string = jni->GetStringUTFChars(j_event_tracing_filename.obj(), NULL); RTC_LOG(LS_INFO) << "Starting internal tracing to: " << init_string; bool ret = rtc::tracing::StartInternalCapture(init_string); jni->ReleaseStringUTFChars(j_event_tracing_filename.obj(), init_string); return ret; } static void JNI_PeerConnectionFactory_StopInternalTracingCapture(JNIEnv* jni) { rtc::tracing::StopInternalCapture(); } static void JNI_PeerConnectionFactory_ShutdownInternalTracer(JNIEnv* jni) { rtc::tracing::ShutdownInternalTracer(); } // Following parameters are optional: // |audio_device_module|, |jencoder_factory|, |jdecoder_factory|, // |audio_processor|, |fec_controller_factory|, // |network_state_predictor_factory|, |neteq_factory|. ScopedJavaLocalRef CreatePeerConnectionFactoryForJava( JNIEnv* jni, const JavaParamRef& jcontext, const JavaParamRef& joptions, rtc::scoped_refptr audio_device_module, rtc::scoped_refptr audio_encoder_factory, rtc::scoped_refptr audio_decoder_factory, const JavaParamRef& jencoder_factory, const JavaParamRef& jdecoder_factory, rtc::scoped_refptr audio_processor, std::unique_ptr fec_controller_factory, std::unique_ptr network_controller_factory, std::unique_ptr network_state_predictor_factory, std::unique_ptr neteq_factory) { // talk/ assumes pretty widely that the current Thread is ThreadManager'd, but // ThreadManager only WrapCurrentThread()s the thread where it is first // created. Since the semantics around when auto-wrapping happens in // webrtc/rtc_base/ are convoluted, we simply wrap here to avoid having to // think about ramifications of auto-wrapping there. rtc::ThreadManager::Instance()->WrapCurrentThread(); std::unique_ptr network_thread = rtc::Thread::CreateWithSocketServer(); network_thread->SetName("network_thread", nullptr); RTC_CHECK(network_thread->Start()) << "Failed to start thread"; std::unique_ptr worker_thread = rtc::Thread::Create(); worker_thread->SetName("worker_thread", nullptr); RTC_CHECK(worker_thread->Start()) << "Failed to start thread"; std::unique_ptr signaling_thread = rtc::Thread::Create(); signaling_thread->SetName("signaling_thread", NULL); RTC_CHECK(signaling_thread->Start()) << "Failed to start thread"; const absl::optional options = JavaToNativePeerConnectionFactoryOptions(jni, joptions); PeerConnectionFactoryDependencies dependencies; dependencies.network_thread = network_thread.get(); dependencies.worker_thread = worker_thread.get(); dependencies.signaling_thread = signaling_thread.get(); dependencies.task_queue_factory = CreateDefaultTaskQueueFactory(); dependencies.call_factory = CreateCallFactory(); dependencies.event_log_factory = std::make_unique( dependencies.task_queue_factory.get()); dependencies.fec_controller_factory = std::move(fec_controller_factory); dependencies.network_controller_factory = std::move(network_controller_factory); dependencies.network_state_predictor_factory = std::move(network_state_predictor_factory); dependencies.neteq_factory = std::move(neteq_factory); if (!(options && options->disable_network_monitor)) { dependencies.network_monitor_factory = std::make_unique(); } cricket::MediaEngineDependencies media_dependencies; media_dependencies.task_queue_factory = dependencies.task_queue_factory.get(); media_dependencies.adm = std::move(audio_device_module); media_dependencies.audio_encoder_factory = std::move(audio_encoder_factory); media_dependencies.audio_decoder_factory = std::move(audio_decoder_factory); media_dependencies.audio_processing = std::move(audio_processor); media_dependencies.video_encoder_factory = absl::WrapUnique(CreateVideoEncoderFactory(jni, jencoder_factory)); media_dependencies.video_decoder_factory = absl::WrapUnique(CreateVideoDecoderFactory(jni, jdecoder_factory)); dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_dependencies)); rtc::scoped_refptr factory = CreateModularPeerConnectionFactory(std::move(dependencies)); RTC_CHECK(factory) << "Failed to create the peer connection factory; " "WebRTC/libjingle init likely failed on this device"; // TODO(honghaiz): Maybe put the options as the argument of // CreatePeerConnectionFactory. if (options) factory->SetOptions(*options); return NativeToScopedJavaPeerConnectionFactory( jni, factory, std::move(network_thread), std::move(worker_thread), std::move(signaling_thread)); } static ScopedJavaLocalRef JNI_PeerConnectionFactory_CreatePeerConnectionFactory( JNIEnv* jni, const JavaParamRef& jcontext, const JavaParamRef& joptions, jlong native_audio_device_module, jlong native_audio_encoder_factory, jlong native_audio_decoder_factory, const JavaParamRef& jencoder_factory, const JavaParamRef& jdecoder_factory, jlong native_audio_processor, jlong native_fec_controller_factory, jlong native_network_controller_factory, jlong native_network_state_predictor_factory, jlong native_neteq_factory) { rtc::scoped_refptr audio_processor = reinterpret_cast(native_audio_processor); return CreatePeerConnectionFactoryForJava( jni, jcontext, joptions, reinterpret_cast(native_audio_device_module), TakeOwnershipOfRefPtr(native_audio_encoder_factory), TakeOwnershipOfRefPtr(native_audio_decoder_factory), jencoder_factory, jdecoder_factory, audio_processor ? audio_processor : CreateAudioProcessing(), TakeOwnershipOfUniquePtr( native_fec_controller_factory), TakeOwnershipOfUniquePtr( native_network_controller_factory), TakeOwnershipOfUniquePtr( native_network_state_predictor_factory), TakeOwnershipOfUniquePtr(native_neteq_factory)); } static void JNI_PeerConnectionFactory_FreeFactory(JNIEnv*, jlong j_p) { delete reinterpret_cast(j_p); field_trial::InitFieldTrialsFromString(nullptr); GetStaticObjects().field_trials_init_string = nullptr; } static jlong JNI_PeerConnectionFactory_CreateLocalMediaStream( JNIEnv* jni, jlong native_factory, const JavaParamRef& label) { rtc::scoped_refptr stream( PeerConnectionFactoryFromJava(native_factory) ->CreateLocalMediaStream(JavaToStdString(jni, label))); return jlongFromPointer(stream.release()); } static jlong JNI_PeerConnectionFactory_CreateAudioSource( JNIEnv* jni, jlong native_factory, const JavaParamRef& j_constraints) { std::unique_ptr constraints = JavaToNativeMediaConstraints(jni, j_constraints); cricket::AudioOptions options; CopyConstraintsIntoAudioOptions(constraints.get(), &options); rtc::scoped_refptr source( PeerConnectionFactoryFromJava(native_factory) ->CreateAudioSource(options)); return jlongFromPointer(source.release()); } jlong JNI_PeerConnectionFactory_CreateAudioTrack( JNIEnv* jni, jlong native_factory, const JavaParamRef& id, jlong native_source) { rtc::scoped_refptr track( PeerConnectionFactoryFromJava(native_factory) ->CreateAudioTrack( JavaToStdString(jni, id), reinterpret_cast(native_source))); return jlongFromPointer(track.release()); } static jboolean JNI_PeerConnectionFactory_StartAecDump( JNIEnv* jni, jlong native_factory, jint file_descriptor, jint filesize_limit_bytes) { FILE* f = fdopen(file_descriptor, "wb"); if (!f) { close(file_descriptor); return false; } return PeerConnectionFactoryFromJava(native_factory) ->StartAecDump(f, filesize_limit_bytes); } static void JNI_PeerConnectionFactory_StopAecDump(JNIEnv* jni, jlong native_factory) { PeerConnectionFactoryFromJava(native_factory)->StopAecDump(); } static jlong JNI_PeerConnectionFactory_CreatePeerConnection( JNIEnv* jni, jlong factory, const JavaParamRef& j_rtc_config, const JavaParamRef& j_constraints, jlong observer_p, const JavaParamRef& j_sslCertificateVerifier) { std::unique_ptr observer( reinterpret_cast(observer_p)); PeerConnectionInterface::RTCConfiguration rtc_config( PeerConnectionInterface::RTCConfigurationType::kAggressive); JavaToNativeRTCConfiguration(jni, j_rtc_config, &rtc_config); if (rtc_config.certificates.empty()) { // Generate non-default certificate. rtc::KeyType key_type = GetRtcConfigKeyType(jni, j_rtc_config); if (key_type != rtc::KT_DEFAULT) { rtc::scoped_refptr certificate = rtc::RTCCertificateGenerator::GenerateCertificate( rtc::KeyParams(key_type), absl::nullopt); if (!certificate) { RTC_LOG(LS_ERROR) << "Failed to generate certificate. KeyType: " << key_type; return 0; } rtc_config.certificates.push_back(certificate); } } std::unique_ptr constraints; if (!j_constraints.is_null()) { constraints = JavaToNativeMediaConstraints(jni, j_constraints); CopyConstraintsIntoRtcConfiguration(constraints.get(), &rtc_config); } PeerConnectionDependencies peer_connection_dependencies(observer.get()); if (!j_sslCertificateVerifier.is_null()) { peer_connection_dependencies.tls_cert_verifier = std::make_unique( jni, j_sslCertificateVerifier); } rtc::scoped_refptr pc = PeerConnectionFactoryFromJava(factory)->CreatePeerConnection( rtc_config, std::move(peer_connection_dependencies)); if (!pc) return 0; return jlongFromPointer( new OwnedPeerConnection(pc, std::move(observer), std::move(constraints))); } static jlong JNI_PeerConnectionFactory_CreateVideoSource( JNIEnv* jni, jlong native_factory, jboolean is_screencast, jboolean align_timestamps) { OwnedFactoryAndThreads* factory = reinterpret_cast(native_factory); return jlongFromPointer(CreateVideoSource(jni, factory->signaling_thread(), factory->worker_thread(), is_screencast, align_timestamps)); } static jlong JNI_PeerConnectionFactory_CreateVideoTrack( JNIEnv* jni, jlong native_factory, const JavaParamRef& id, jlong native_source) { rtc::scoped_refptr track = PeerConnectionFactoryFromJava(native_factory) ->CreateVideoTrack( JavaToStdString(jni, id), reinterpret_cast(native_source)); return jlongFromPointer(track.release()); } static jlong JNI_PeerConnectionFactory_GetNativePeerConnectionFactory( JNIEnv* jni, jlong native_factory) { return jlongFromPointer(PeerConnectionFactoryFromJava(native_factory)); } static void JNI_PeerConnectionFactory_InjectLoggable( JNIEnv* jni, const JavaParamRef& j_logging, jint nativeSeverity) { std::unique_ptr& jni_log_sink = GetStaticObjects().jni_log_sink; // If there is already a LogSink, remove it from LogMessage. if (jni_log_sink) { rtc::LogMessage::RemoveLogToStream(jni_log_sink.get()); } jni_log_sink = std::make_unique(jni, j_logging); rtc::LogMessage::AddLogToStream( jni_log_sink.get(), static_cast(nativeSeverity)); rtc::LogMessage::LogToDebug(rtc::LS_NONE); } static void JNI_PeerConnectionFactory_DeleteLoggable(JNIEnv* jni) { std::unique_ptr& jni_log_sink = GetStaticObjects().jni_log_sink; if (jni_log_sink) { rtc::LogMessage::RemoveLogToStream(jni_log_sink.get()); jni_log_sink.reset(); } } static void JNI_PeerConnectionFactory_PrintStackTrace(JNIEnv* env, jint tid) { RTC_LOG(LS_WARNING) << StackTraceToString(GetStackTrace(tid)); } static void JNI_PeerConnectionFactory_PrintStackTracesOfRegisteredThreads( JNIEnv* env) { PrintStackTracesOfRegisteredThreads(); } } // namespace jni } // namespace webrtc