diff options
Diffstat (limited to 'third_party/libwebrtc/webrtc/modules/utility')
14 files changed, 1780 insertions, 0 deletions
diff --git a/third_party/libwebrtc/webrtc/modules/utility/BUILD.gn b/third_party/libwebrtc/webrtc/modules/utility/BUILD.gn new file mode 100644 index 0000000000..cba8b17b09 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (c) 2014 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. + +import("../../webrtc.gni") + +rtc_static_library("utility") { + sources = [ + "include/audio_frame_operations.h", + "include/helpers_android.h", + "include/jvm_android.h", + "include/process_thread.h", + "source/helpers_android.cc", + "source/jvm_android.cc", + "source/process_thread_impl.cc", + "source/process_thread_impl.h", + ] + + if (!build_with_chromium && is_clang) { + # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163). + suppressed_configs += [ "//build/config/clang:find_bad_constructs" ] + } + + if (is_ios) { + libs = [ "AVFoundation.framework" ] + } + + deps = [ + "..:module_api", + "../..:webrtc_common", + "../../audio/utility:audio_frame_operations", + "../../common_audio", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_task_queue", + "../../system_wrappers", + "../media_file", + ] +} + +rtc_source_set("mock_process_thread") { + testonly = true + sources = [ + "include/mock/mock_process_thread.h", + ] + deps = [ + ":utility", + "../../rtc_base:rtc_base_approved", + "../../test:test_support", + ] +} + +if (rtc_include_tests) { + rtc_source_set("utility_unittests") { + testonly = true + + sources = [ + "source/process_thread_impl_unittest.cc", + ] + deps = [ + ":utility", + "..:module_api", + "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_task_queue", + "../../test:test_support", + "//testing/gmock", + ] + } +} diff --git a/third_party/libwebrtc/webrtc/modules/utility/DEPS b/third_party/libwebrtc/webrtc/modules/utility/DEPS new file mode 100644 index 0000000000..d693ea6221 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + # TODO(aleloi): remove when clients update. See + # bugs.webrtc.org/6548. + "+audio/utility/audio_frame_operations.h", + "+common_audio", + "+common_video", + "+system_wrappers", +] diff --git a/third_party/libwebrtc/webrtc/modules/utility/OWNERS b/third_party/libwebrtc/webrtc/modules/utility/OWNERS new file mode 100644 index 0000000000..debeaab6d4 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/OWNERS @@ -0,0 +1,6 @@ +perkj@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gn=* +per-file *.gni=* diff --git a/third_party/libwebrtc/webrtc/modules/utility/include/audio_frame_operations.h b/third_party/libwebrtc/webrtc/modules/utility/include/audio_frame_operations.h new file mode 100644 index 0000000000..adaadfeca7 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/include/audio_frame_operations.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_ +#define MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_ +// The contents of this file have moved to +// //audio/utility. This file is deprecated. + +// TODO(aleloi): Remove this file when clients have updated their +// includes. See bugs.webrtc.org/6548. +#include "audio/utility/audio_frame_operations.h" + +#endif // #ifndef MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/include/helpers_android.h b/third_party/libwebrtc/webrtc/modules/utility/include/helpers_android.h new file mode 100644 index 0000000000..c1d9ad87ec --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/include/helpers_android.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_ +#define MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_ + +#include <jni.h> +#include <string> + +// Abort the process if |jni| has a Java exception pending. +// TODO(henrika): merge with CHECK_JNI_EXCEPTION() in jni_helpers.h. +#define CHECK_EXCEPTION(jni) \ + RTC_CHECK(!jni->ExceptionCheck()) \ + << (jni->ExceptionDescribe(), jni->ExceptionClear(), "") + +namespace webrtc { + +// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached. +JNIEnv* GetEnv(JavaVM* jvm); + +// Return a |jlong| that will correctly convert back to |ptr|. This is needed +// because the alternative (of silently passing a 32-bit pointer to a vararg +// function expecting a 64-bit param) picks up garbage in the high 32 bits. +jlong PointerTojlong(void* ptr); + +// JNIEnv-helper methods that wraps the API which uses the JNI interface +// pointer (JNIEnv*). It allows us to RTC_CHECK success and that no Java +// exception is thrown while calling the method. +jmethodID GetMethodID( + JNIEnv* jni, jclass c, const char* name, const char* signature); + +jmethodID GetStaticMethodID( + JNIEnv* jni, jclass c, const char* name, const char* signature); + +jclass FindClass(JNIEnv* jni, const char* name); + +jobject NewGlobalRef(JNIEnv* jni, jobject o); + +void DeleteGlobalRef(JNIEnv* jni, jobject o); + +// Return thread ID as a string. +std::string GetThreadId(); + +// Return thread ID as string suitable for debug logging. +std::string GetThreadInfo(); + +// Attach thread to JVM if necessary and detach at scope end if originally +// attached. +class AttachThreadScoped { + public: + explicit AttachThreadScoped(JavaVM* jvm); + ~AttachThreadScoped(); + JNIEnv* env(); + + private: + bool attached_; + JavaVM* jvm_; + JNIEnv* env_; +}; + +// Scoped holder for global Java refs. +template<class T> // T is jclass, jobject, jintArray, etc. +class ScopedGlobalRef { + public: + ScopedGlobalRef(JNIEnv* jni, T obj) + : jni_(jni), obj_(static_cast<T>(NewGlobalRef(jni, obj))) {} + ~ScopedGlobalRef() { + DeleteGlobalRef(jni_, obj_); + } + T operator*() const { + return obj_; + } + private: + JNIEnv* jni_; + T obj_; +}; + +} // namespace webrtc + +#endif // MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/include/jvm_android.h b/third_party/libwebrtc/webrtc/modules/utility/include/jvm_android.h new file mode 100644 index 0000000000..f899263f76 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/include/jvm_android.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifndef MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_ +#define MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_ + +#include <jni.h> + +#include <memory> +#include <string> + +#include "modules/utility/include/helpers_android.h" +#include "rtc_base/thread_checker.h" + +namespace webrtc { + +// The JNI interface pointer (JNIEnv) is valid only in the current thread. +// Should another thread need to access the Java VM, it must first call +// AttachCurrentThread() to attach itself to the VM and obtain a JNI interface +// pointer. The native thread remains attached to the VM until it calls +// DetachCurrentThread() to detach. +class AttachCurrentThreadIfNeeded { + public: + AttachCurrentThreadIfNeeded(); + ~AttachCurrentThreadIfNeeded(); + + private: + rtc::ThreadChecker thread_checker_; + bool attached_; +}; + +// This class is created by the NativeRegistration class and is used to wrap +// the actual Java object handle (jobject) on which we can call methods from +// C++ in to Java. See example in JVM for more details. +// TODO(henrika): extend support for type of function calls. +class GlobalRef { + public: + GlobalRef(JNIEnv* jni, jobject object); + ~GlobalRef(); + + jboolean CallBooleanMethod(jmethodID methodID, ...); + jint CallIntMethod(jmethodID methodID, ...); + void CallVoidMethod(jmethodID methodID, ...); + + private: + JNIEnv* const jni_; + const jobject j_object_; +}; + +// Wraps the jclass object on which we can call GetMethodId() functions to +// query method IDs. +class JavaClass { + public: + JavaClass(JNIEnv* jni, jclass clazz) : jni_(jni), j_class_(clazz) {} + ~JavaClass() {} + + jmethodID GetMethodId(const char* name, const char* signature); + jmethodID GetStaticMethodId(const char* name, const char* signature); + jobject CallStaticObjectMethod(jmethodID methodID, ...); + jint CallStaticIntMethod(jmethodID methodID, ...); + + protected: + JNIEnv* const jni_; + jclass const j_class_; +}; + +// Adds support of the NewObject factory method to the JavaClass class. +// See example in JVM for more details on how to use it. +class NativeRegistration : public JavaClass { + public: + NativeRegistration(JNIEnv* jni, jclass clazz); + ~NativeRegistration(); + + std::unique_ptr<GlobalRef> NewObject( + const char* name, const char* signature, ...); + + private: + JNIEnv* const jni_; +}; + +// This class is created by the JVM class and is used to expose methods that +// needs the JNI interface pointer but its main purpose is to create a +// NativeRegistration object given name of a Java class and a list of native +// methods. See example in JVM for more details. +class JNIEnvironment { + public: + explicit JNIEnvironment(JNIEnv* jni); + ~JNIEnvironment(); + + // Registers native methods with the Java class specified by |name|. + // Note that the class name must be one of the names in the static + // |loaded_classes| array defined in jvm_android.cc. + // This method must be called on the construction thread. + std::unique_ptr<NativeRegistration> RegisterNatives( + const char* name, const JNINativeMethod *methods, int num_methods); + + // Converts from Java string to std::string. + // This method must be called on the construction thread. + std::string JavaToStdString(const jstring& j_string); + + private: + rtc::ThreadChecker thread_checker_; + JNIEnv* const jni_; +}; + +// Main class for working with Java from C++ using JNI in WebRTC. +// +// Example usage: +// +// // At initialization (e.g. in JNI_OnLoad), call JVM::Initialize. +// JNIEnv* jni = ::base::android::AttachCurrentThread(); +// JavaVM* jvm = NULL; +// jni->GetJavaVM(&jvm); +// webrtc::JVM::Initialize(jvm); +// +// // Header (.h) file of example class called User. +// std::unique_ptr<JNIEnvironment> env; +// std::unique_ptr<NativeRegistration> reg; +// std::unique_ptr<GlobalRef> obj; +// +// // Construction (in .cc file) of User class. +// User::User() { +// // Calling thread must be attached to the JVM. +// env = JVM::GetInstance()->environment(); +// reg = env->RegisterNatives("org/webrtc/WebRtcTest", ,); +// obj = reg->NewObject("<init>", ,); +// } +// +// // Each User method can now use |reg| and |obj| and call Java functions +// // in WebRtcTest.java, e.g. boolean init() {}. +// bool User::Foo() { +// jmethodID id = reg->GetMethodId("init", "()Z"); +// return obj->CallBooleanMethod(id); +// } +// +// // And finally, e.g. in JNI_OnUnLoad, call JVM::Uninitialize. +// JVM::Uninitialize(); +class JVM { + public: + // Stores global handles to the Java VM interface. + // Should be called once on a thread that is attached to the JVM. + static void Initialize(JavaVM* jvm); + // Like the method above but also passes the context to the ContextUtils + // class. This method should be used by pure-C++ Android users that can't call + // ContextUtils.initialize directly. + static void Initialize(JavaVM* jvm, jobject context); + // Clears handles stored in Initialize(). Must be called on same thread as + // Initialize(). + static void Uninitialize(); + // Gives access to the global Java VM interface pointer, which then can be + // used to create a valid JNIEnvironment object or to get a JavaClass object. + static JVM* GetInstance(); + + // Creates a JNIEnvironment object. + // This method returns a NULL pointer if AttachCurrentThread() has not been + // called successfully. Use the AttachCurrentThreadIfNeeded class if needed. + std::unique_ptr<JNIEnvironment> environment(); + + // Returns a JavaClass object given class |name|. + // Note that the class name must be one of the names in the static + // |loaded_classes| array defined in jvm_android.cc. + // This method must be called on the construction thread. + JavaClass GetClass(const char* name); + + // TODO(henrika): can we make these private? + JavaVM* jvm() const { return jvm_; } + + protected: + JVM(JavaVM* jvm); + ~JVM(); + + private: + JNIEnv* jni() const { return GetEnv(jvm_); } + + rtc::ThreadChecker thread_checker_; + JavaVM* const jvm_; +}; + +} // namespace webrtc + +#endif // MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/include/mock/mock_process_thread.h b/third_party/libwebrtc/webrtc/modules/utility/include/mock/mock_process_thread.h new file mode 100644 index 0000000000..fb88e40074 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/include/mock/mock_process_thread.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 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. + */ + +#ifndef MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_ +#define MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_ + +#include <memory> + +#include "modules/utility/include/process_thread.h" +#include "rtc_base/location.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockProcessThread : public ProcessThread { + public: + // TODO(nisse): Valid overrides commented out, because the gmock + // methods don't use any override declarations, and we want to avoid + // warnings from -Winconsistent-missing-override. See + // http://crbug.com/428099. + MOCK_METHOD0(Start, void()); + MOCK_METHOD0(Stop, void()); + MOCK_METHOD1(WakeUp, void(Module* module)); + MOCK_METHOD1(PostTask, void(rtc::QueuedTask* task)); + MOCK_METHOD2(RegisterModule, void(Module* module, const rtc::Location&)); + MOCK_METHOD1(DeRegisterModule, void(Module* module)); + + // MOCK_METHOD1 gets confused with mocking this method, so we work around it + // by overriding the method from the interface and forwarding the call to a + // mocked, simpler method. + void PostTask(std::unique_ptr<rtc::QueuedTask> task) /*override*/ { + PostTask(task.get()); + } +}; + +} // namespace webrtc +#endif // MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/include/process_thread.h b/third_party/libwebrtc/webrtc/modules/utility/include/process_thread.h new file mode 100644 index 0000000000..28e1191085 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/include/process_thread.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011 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. + */ + +#ifndef MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_ +#define MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_ + +#include <memory> + +#include "typedefs.h" // NOLINT(build/include) + +#if defined(WEBRTC_WIN) +// Due to a bug in the std::unique_ptr implementation that ships with MSVS, +// we need the full definition of QueuedTask, on Windows. +#include "rtc_base/task_queue.h" +#else +namespace rtc { +class QueuedTask; +} +#endif + +namespace rtc { +class Location; +} + +namespace webrtc { +class Module; + +// TODO(tommi): ProcessThread probably doesn't need to be a virtual +// interface. There exists one override besides ProcessThreadImpl, +// MockProcessThread, but when looking at how it is used, it seems +// a nullptr might suffice (or simply an actual ProcessThread instance). +class ProcessThread { + public: + virtual ~ProcessThread(); + + static std::unique_ptr<ProcessThread> Create(const char* thread_name); + + // Starts the worker thread. Must be called from the construction thread. + virtual void Start() = 0; + + // Stops the worker thread. Must be called from the construction thread. + virtual void Stop() = 0; + + // Wakes the thread up to give a module a chance to do processing right + // away. This causes the worker thread to wake up and requery the specified + // module for when it should be called back. (Typically the module should + // return 0 from TimeUntilNextProcess on the worker thread at that point). + // Can be called on any thread. + virtual void WakeUp(Module* module) = 0; + + // Queues a task object to run on the worker thread. Ownership of the + // task object is transferred to the ProcessThread and the object will + // either be deleted after running on the worker thread, or on the + // construction thread of the ProcessThread instance, if the task did not + // get a chance to run (e.g. posting the task while shutting down or when + // the thread never runs). + virtual void PostTask(std::unique_ptr<rtc::QueuedTask> task) = 0; + + // Adds a module that will start to receive callbacks on the worker thread. + // Can be called from any thread. + virtual void RegisterModule(Module* module, const rtc::Location& from) = 0; + + // Removes a previously registered module. + // Can be called from any thread. + virtual void DeRegisterModule(Module* module) = 0; +}; + +} // namespace webrtc + +#endif // MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/source/helpers_android.cc b/third_party/libwebrtc/webrtc/modules/utility/source/helpers_android.cc new file mode 100644 index 0000000000..c5a91235e3 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/source/helpers_android.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2013 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 "modules/utility/include/helpers_android.h" +#include "rtc_base/checks.h" + +#include <android/log.h> +#include <assert.h> +#include <pthread.h> +#include <stddef.h> +#include <unistd.h> + +#define TAG "HelpersAndroid" +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) + +namespace webrtc { + +JNIEnv* GetEnv(JavaVM* jvm) { + void* env = NULL; + jint status = jvm->GetEnv(&env, JNI_VERSION_1_6); + RTC_CHECK(((env != NULL) && (status == JNI_OK)) || + ((env == NULL) && (status == JNI_EDETACHED))) + << "Unexpected GetEnv return: " << status << ":" << env; + return reinterpret_cast<JNIEnv*>(env); +} + +// Return a |jlong| that will correctly convert back to |ptr|. This is needed +// because the alternative (of silently passing a 32-bit pointer to a vararg +// function expecting a 64-bit param) picks up garbage in the high 32 bits. +jlong PointerTojlong(void* ptr) { + static_assert(sizeof(intptr_t) <= sizeof(jlong), + "Time to rethink the use of jlongs"); + // Going through intptr_t to be obvious about the definedness of the + // conversion from pointer to integral type. intptr_t to jlong is a standard + // widening by the static_assert above. + jlong ret = reinterpret_cast<intptr_t>(ptr); + RTC_DCHECK(reinterpret_cast<void*>(ret) == ptr); + return ret; +} + +jmethodID GetMethodID ( + JNIEnv* jni, jclass c, const char* name, const char* signature) { + jmethodID m = jni->GetMethodID(c, name, signature); + CHECK_EXCEPTION(jni) << "Error during GetMethodID: " << name << ", " + << signature; + RTC_CHECK(m) << name << ", " << signature; + return m; +} + +jmethodID GetStaticMethodID ( + JNIEnv* jni, jclass c, const char* name, const char* signature) { + jmethodID m = jni->GetStaticMethodID(c, name, signature); + CHECK_EXCEPTION(jni) << "Error during GetStaticMethodID: " << name << ", " + << signature; + RTC_CHECK(m) << name << ", " << signature; + return m; +} + +jclass FindClass(JNIEnv* jni, const char* name) { + jclass c = jni->FindClass(name); + CHECK_EXCEPTION(jni) << "Error during FindClass: " << name; + RTC_CHECK(c) << name; + return c; +} + +jobject NewGlobalRef(JNIEnv* jni, jobject o) { + jobject ret = jni->NewGlobalRef(o); + CHECK_EXCEPTION(jni) << "Error during NewGlobalRef"; + RTC_CHECK(ret); + return ret; +} + +void DeleteGlobalRef(JNIEnv* jni, jobject o) { + jni->DeleteGlobalRef(o); + CHECK_EXCEPTION(jni) << "Error during DeleteGlobalRef"; +} + +std::string GetThreadId() { + char buf[21]; // Big enough to hold a kuint64max plus terminating NULL. + int thread_id = gettid(); + RTC_CHECK_LT(snprintf(buf, sizeof(buf), "%i", thread_id), + static_cast<int>(sizeof(buf))) + << "Thread id is bigger than uint64??"; + return std::string(buf); +} + +std::string GetThreadInfo() { + return "@[tid=" + GetThreadId() + "]"; +} + +AttachThreadScoped::AttachThreadScoped(JavaVM* jvm) + : attached_(false), jvm_(jvm), env_(NULL) { + env_ = GetEnv(jvm); + if (!env_) { + // Adding debug log here so we can track down potential leaks and figure + // out why we sometimes see "Native thread exiting without having called + // DetachCurrentThread" in logcat outputs. + ALOGD("Attaching thread to JVM%s", GetThreadInfo().c_str()); + jint res = jvm->AttachCurrentThread(&env_, NULL); + attached_ = (res == JNI_OK); + RTC_CHECK(attached_) << "AttachCurrentThread failed: " << res; + } +} + +AttachThreadScoped::~AttachThreadScoped() { + if (attached_) { + ALOGD("Detaching thread from JVM%s", GetThreadInfo().c_str()); + jint res = jvm_->DetachCurrentThread(); + RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res; + RTC_CHECK(!GetEnv(jvm_)); + } +} + +JNIEnv* AttachThreadScoped::env() { return env_; } + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/utility/source/jvm_android.cc b/third_party/libwebrtc/webrtc/modules/utility/source/jvm_android.cc new file mode 100644 index 0000000000..f828d218ae --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/source/jvm_android.cc @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2015 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 <android/log.h> + +#include <memory> + +#include "modules/utility/include/jvm_android.h" + +#include "rtc_base/checks.h" + +namespace mozilla { +namespace jni { +jclass GetClassRef(JNIEnv* aEnv, const char* aClassName); +} +} + +#define TAG "JVM" +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) + +namespace webrtc { + +JVM* g_jvm; + +// TODO(henrika): add more clases here if needed. +struct { + const char* name; + jclass clazz; +} loaded_classes[] = { + {"org/webrtc/voiceengine/BuildInfo", nullptr}, + {"org/webrtc/voiceengine/WebRtcAudioManager", nullptr}, + {"org/webrtc/voiceengine/WebRtcAudioRecord", nullptr}, + {"org/webrtc/voiceengine/WebRtcAudioTrack", nullptr}, +}; + +// Android's FindClass() is trickier than usual because the app-specific +// ClassLoader is not consulted when there is no app-specific frame on the +// stack. Consequently, we only look up all classes once in native WebRTC. +// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass +void LoadClasses(JNIEnv* jni) { + ALOGD("LoadClasses"); + for (auto& c : loaded_classes) { + ALOGD("name: %s", c.name); + jclass clsRef = mozilla::jni::GetClassRef(jni, c.name); + RTC_CHECK(clsRef) << c.name; + c.clazz = static_cast<jclass>(jni->NewGlobalRef(clsRef)); + jni->DeleteLocalRef(clsRef); + } +} + +void FreeClassReferences(JNIEnv* jni) { + for (auto& c : loaded_classes) { + jni->DeleteGlobalRef(c.clazz); + c.clazz = nullptr; + } +} + +jclass LookUpClass(const char* name) { + for (auto& c : loaded_classes) { + if (strcmp(c.name, name) == 0) + return c.clazz; + } + RTC_CHECK(false) << "Unable to find class in lookup table"; + return 0; +} + +// AttachCurrentThreadIfNeeded implementation. +AttachCurrentThreadIfNeeded::AttachCurrentThreadIfNeeded() + : attached_(false) { + ALOGD("AttachCurrentThreadIfNeeded::ctor%s", GetThreadInfo().c_str()); + JavaVM* jvm = JVM::GetInstance()->jvm(); + RTC_CHECK(jvm); + JNIEnv* jni = GetEnv(jvm); + if (!jni) { + ALOGD("Attaching thread to JVM"); + JNIEnv* env = nullptr; + jint ret = jvm->AttachCurrentThread(&env, nullptr); + attached_ = (ret == JNI_OK); + } +} + +AttachCurrentThreadIfNeeded::~AttachCurrentThreadIfNeeded() { + ALOGD("AttachCurrentThreadIfNeeded::dtor%s", GetThreadInfo().c_str()); + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + if (attached_) { + ALOGD("Detaching thread from JVM"); + jint res = JVM::GetInstance()->jvm()->DetachCurrentThread(); + RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res; + } +} + +// GlobalRef implementation. +GlobalRef::GlobalRef(JNIEnv* jni, jobject object) + : jni_(jni), j_object_(NewGlobalRef(jni, object)) { + ALOGD("GlobalRef::ctor%s", GetThreadInfo().c_str()); +} + +GlobalRef::~GlobalRef() { + ALOGD("GlobalRef::dtor%s", GetThreadInfo().c_str()); + DeleteGlobalRef(jni_, j_object_); +} + +jboolean GlobalRef::CallBooleanMethod(jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + jboolean res = jni_->CallBooleanMethodV(j_object_, methodID, args); + CHECK_EXCEPTION(jni_) << "Error during CallBooleanMethod"; + va_end(args); + return res; +} + +jint GlobalRef::CallIntMethod(jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + jint res = jni_->CallIntMethodV(j_object_, methodID, args); + CHECK_EXCEPTION(jni_) << "Error during CallIntMethod"; + va_end(args); + return res; +} + +void GlobalRef::CallVoidMethod(jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + jni_->CallVoidMethodV(j_object_, methodID, args); + CHECK_EXCEPTION(jni_) << "Error during CallVoidMethod"; + va_end(args); +} + +// NativeRegistration implementation. +NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz) + : JavaClass(jni, clazz), jni_(jni) { + ALOGD("NativeRegistration::ctor%s", GetThreadInfo().c_str()); +} + +NativeRegistration::~NativeRegistration() { + ALOGD("NativeRegistration::dtor%s", GetThreadInfo().c_str()); + jni_->UnregisterNatives(j_class_); + CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives"; +} + +std::unique_ptr<GlobalRef> NativeRegistration::NewObject( + const char* name, const char* signature, ...) { + ALOGD("NativeRegistration::NewObject%s", GetThreadInfo().c_str()); + va_list args; + va_start(args, signature); + jobject obj = jni_->NewObjectV(j_class_, + GetMethodID(jni_, j_class_, name, signature), + args); + CHECK_EXCEPTION(jni_) << "Error during NewObjectV"; + va_end(args); + return std::unique_ptr<GlobalRef>(new GlobalRef(jni_, obj)); +} + +// JavaClass implementation. +jmethodID JavaClass::GetMethodId( + const char* name, const char* signature) { + return GetMethodID(jni_, j_class_, name, signature); +} + +jmethodID JavaClass::GetStaticMethodId( + const char* name, const char* signature) { + return GetStaticMethodID(jni_, j_class_, name, signature); +} + +jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args); + CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod"; + return res; +} + +jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) { + va_list args; + va_start(args, methodID); + jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args); + CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod"; + return res; +} + +// JNIEnvironment implementation. +JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) { + ALOGD("JNIEnvironment::ctor%s", GetThreadInfo().c_str()); +} + +JNIEnvironment::~JNIEnvironment() { + ALOGD("JNIEnvironment::dtor%s", GetThreadInfo().c_str()); + RTC_DCHECK(thread_checker_.CalledOnValidThread()); +} + +std::unique_ptr<NativeRegistration> JNIEnvironment::RegisterNatives( + const char* name, const JNINativeMethod *methods, int num_methods) { + ALOGD("JNIEnvironment::RegisterNatives(%s)", name); + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + jclass clazz = LookUpClass(name); + jni_->RegisterNatives(clazz, methods, num_methods); + CHECK_EXCEPTION(jni_) << "Error during RegisterNatives"; + return std::unique_ptr<NativeRegistration>( + new NativeRegistration(jni_, clazz)); +} + +std::string JNIEnvironment::JavaToStdString(const jstring& j_string) { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + const char* jchars = jni_->GetStringUTFChars(j_string, nullptr); + CHECK_EXCEPTION(jni_); + const int size = jni_->GetStringUTFLength(j_string); + CHECK_EXCEPTION(jni_); + std::string ret(jchars, size); + jni_->ReleaseStringUTFChars(j_string, jchars); + CHECK_EXCEPTION(jni_); + return ret; +} + +// static +void JVM::Initialize(JavaVM* jvm) { + ALOGD("JVM::Initialize%s", GetThreadInfo().c_str()); + if (g_jvm) { + return; + } + g_jvm = new JVM(jvm); +} + +void JVM::Initialize(JavaVM* jvm, jobject context) { + Initialize(jvm); + + // Pass in the context to the new ContextUtils class. + JNIEnv* jni = g_jvm->jni(); + jclass context_utils = FindClass(jni, "org/webrtc/ContextUtils"); + jmethodID initialize_method = jni->GetStaticMethodID( + context_utils, "initialize", "(Landroid/content/Context;)V"); + jni->CallStaticVoidMethod(context_utils, initialize_method, context); +} + +// static +void JVM::Uninitialize() { + ALOGD("JVM::Uninitialize%s", GetThreadInfo().c_str()); + RTC_DCHECK(g_jvm); + delete g_jvm; + g_jvm = nullptr; +} + +// static +JVM* JVM::GetInstance() { + RTC_DCHECK(g_jvm); + return g_jvm; +} + +JVM::JVM(JavaVM* jvm) : jvm_(jvm) { + ALOGD("JVM::JVM%s", GetThreadInfo().c_str()); + RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread."; + LoadClasses(jni()); +} + +JVM::~JVM() { + ALOGD("JVM::~JVM%s", GetThreadInfo().c_str()); + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + FreeClassReferences(jni()); +} + +std::unique_ptr<JNIEnvironment> JVM::environment() { + ALOGD("JVM::environment%s", GetThreadInfo().c_str()); + // The JNIEnv is used for thread-local storage. For this reason, we cannot + // share a JNIEnv between threads. If a piece of code has no other way to get + // its JNIEnv, we should share the JavaVM, and use GetEnv to discover the + // thread's JNIEnv. (Assuming it has one, if not, use AttachCurrentThread). + // See // http://developer.android.com/training/articles/perf-jni.html. + JNIEnv* jni = GetEnv(jvm_); + if (!jni) { + ALOGE("AttachCurrentThread() has not been called on this thread."); + return std::unique_ptr<JNIEnvironment>(); + } + return std::unique_ptr<JNIEnvironment>(new JNIEnvironment(jni)); +} + +JavaClass JVM::GetClass(const char* name) { + ALOGD("JVM::GetClass(%s)%s", name, GetThreadInfo().c_str()); + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + return JavaClass(jni(), LookUpClass(name)); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.cc b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.cc new file mode 100644 index 0000000000..014624e48c --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.cc @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2012 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 "modules/utility/source/process_thread_impl.h" + +#include "modules/include/module.h" +#include "rtc_base/checks.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/timeutils.h" +#include "rtc_base/trace_event.h" + +namespace webrtc { +namespace { + +// We use this constant internally to signal that a module has requested +// a callback right away. When this is set, no call to TimeUntilNextProcess +// should be made, but Process() should be called directly. +const int64_t kCallProcessImmediately = -1; + +int64_t GetNextCallbackTime(Module* module, int64_t time_now) { + int64_t interval = module->TimeUntilNextProcess(); + if (interval < 0) { + // Falling behind, we should call the callback now. + return time_now; + } + return time_now + interval; +} +} + +ProcessThread::~ProcessThread() {} + +// static +std::unique_ptr<ProcessThread> ProcessThread::Create( + const char* thread_name) { + return std::unique_ptr<ProcessThread>(new ProcessThreadImpl(thread_name)); +} + +ProcessThreadImpl::ProcessThreadImpl(const char* thread_name) + : wake_up_(EventWrapper::Create()), + stop_(false), + thread_name_(thread_name) {} + +ProcessThreadImpl::~ProcessThreadImpl() { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + RTC_DCHECK(!thread_.get()); + RTC_DCHECK(!stop_); + + while (!queue_.empty()) { + delete queue_.front(); + queue_.pop(); + } +} + +void ProcessThreadImpl::Start() { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + RTC_DCHECK(!thread_.get()); + if (thread_.get()) + return; + + RTC_DCHECK(!stop_); + + for (ModuleCallback& m : modules_) + m.module->ProcessThreadAttached(this); + + thread_.reset( + new rtc::PlatformThread(&ProcessThreadImpl::Run, this, thread_name_)); + thread_->Start(); +} + +void ProcessThreadImpl::Stop() { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + if(!thread_.get()) + return; + + { + rtc::CritScope lock(&lock_); + stop_ = true; + } + + wake_up_->Set(); + + thread_->Stop(); + stop_ = false; + + thread_.reset(); + for (ModuleCallback& m : modules_) + m.module->ProcessThreadAttached(nullptr); +} + +void ProcessThreadImpl::WakeUp(Module* module) { + // Allowed to be called on any thread. + { + rtc::CritScope lock(&lock_); + for (ModuleCallback& m : modules_) { + if (m.module == module) + m.next_callback = kCallProcessImmediately; + } + } + wake_up_->Set(); +} + +void ProcessThreadImpl::PostTask(std::unique_ptr<rtc::QueuedTask> task) { + // Allowed to be called on any thread. + { + rtc::CritScope lock(&lock_); + queue_.push(task.release()); + } + wake_up_->Set(); +} + +void ProcessThreadImpl::RegisterModule(Module* module, + const rtc::Location& from) { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + RTC_DCHECK(module) << from.ToString(); + +#if RTC_DCHECK_IS_ON + { + // Catch programmer error. + rtc::CritScope lock(&lock_); + for (const ModuleCallback& mc : modules_) { + RTC_DCHECK(mc.module != module) + << "Already registered here: " << mc.location.ToString() << "\n" + << "Now attempting from here: " << from.ToString(); + } + } +#endif + + // Now that we know the module isn't in the list, we'll call out to notify + // the module that it's attached to the worker thread. We don't hold + // the lock while we make this call. + if (thread_.get()) + module->ProcessThreadAttached(this); + + { + rtc::CritScope lock(&lock_); + modules_.push_back(ModuleCallback(module, from)); + } + + // Wake the thread calling ProcessThreadImpl::Process() to update the + // waiting time. The waiting time for the just registered module may be + // shorter than all other registered modules. + wake_up_->Set(); +} + +void ProcessThreadImpl::DeRegisterModule(Module* module) { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + RTC_DCHECK(module); + + { + rtc::CritScope lock(&lock_); + modules_.remove_if([&module](const ModuleCallback& m) { + return m.module == module; + }); + } + + // Notify the module that it's been detached. + module->ProcessThreadAttached(nullptr); +} + +// static +bool ProcessThreadImpl::Run(void* obj) { + return static_cast<ProcessThreadImpl*>(obj)->Process(); +} + +bool ProcessThreadImpl::Process() { + TRACE_EVENT1("webrtc", "ProcessThreadImpl", "name", thread_name_); + int64_t now = rtc::TimeMillis(); + int64_t next_checkpoint = now + (1000 * 60); + + { + rtc::CritScope lock(&lock_); + if (stop_) + return false; + for (ModuleCallback& m : modules_) { + // TODO(tommi): Would be good to measure the time TimeUntilNextProcess + // takes and dcheck if it takes too long (e.g. >=10ms). Ideally this + // operation should not require taking a lock, so querying all modules + // should run in a matter of nanoseconds. + if (m.next_callback == 0) + m.next_callback = GetNextCallbackTime(m.module, now); + + if (m.next_callback <= now || + m.next_callback == kCallProcessImmediately) { + { + TRACE_EVENT2("webrtc", "ModuleProcess", "function", + m.location.function_name(), "file", + m.location.file_and_line()); + m.module->Process(); + } + // Use a new 'now' reference to calculate when the next callback + // should occur. We'll continue to use 'now' above for the baseline + // of calculating how long we should wait, to reduce variance. + int64_t new_now = rtc::TimeMillis(); + m.next_callback = GetNextCallbackTime(m.module, new_now); + } + + if (m.next_callback < next_checkpoint) + next_checkpoint = m.next_callback; + } + + while (!queue_.empty()) { + rtc::QueuedTask* task = queue_.front(); + queue_.pop(); + lock_.Leave(); + task->Run(); + delete task; + lock_.Enter(); + } + } + + int64_t time_to_wait = next_checkpoint - rtc::TimeMillis(); + if (time_to_wait > 0) + wake_up_->Wait(static_cast<unsigned long>(time_to_wait)); + + return true; +} +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.h b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.h new file mode 100644 index 0000000000..9856fba576 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 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. + */ + +#ifndef MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ +#define MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ + +#include <list> +#include <memory> +#include <queue> + +#include "modules/utility/include/process_thread.h" +#include "rtc_base/criticalsection.h" +#include "rtc_base/location.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/thread_checker.h" +#include "system_wrappers/include/event_wrapper.h" +#include "typedefs.h" // NOLINT(build/include) + +namespace webrtc { + +class ProcessThreadImpl : public ProcessThread { + public: + explicit ProcessThreadImpl(const char* thread_name); + ~ProcessThreadImpl() override; + + void Start() override; + void Stop() override; + + void WakeUp(Module* module) override; + void PostTask(std::unique_ptr<rtc::QueuedTask> task) override; + + void RegisterModule(Module* module, const rtc::Location& from) override; + void DeRegisterModule(Module* module) override; + + protected: + static bool Run(void* obj); + bool Process(); + + private: + struct ModuleCallback { + ModuleCallback() = delete; + ModuleCallback(ModuleCallback&& cb) = default; + ModuleCallback(const ModuleCallback& cb) = default; + ModuleCallback(Module* module, const rtc::Location& location) + : module(module), location(location) {} + bool operator==(const ModuleCallback& cb) const { + return cb.module == module; + } + + Module* const module; + int64_t next_callback = 0; // Absolute timestamp. + const rtc::Location location; + + private: + ModuleCallback& operator=(ModuleCallback&); + }; + + typedef std::list<ModuleCallback> ModuleList; + + // Warning: For some reason, if |lock_| comes immediately before |modules_| + // with the current class layout, we will start to have mysterious crashes + // on Mac 10.9 debug. I (Tommi) suspect we're hitting some obscure alignemnt + // issues, but I haven't figured out what they are, if there are alignment + // requirements for mutexes on Mac or if there's something else to it. + // So be careful with changing the layout. + rtc::CriticalSection lock_; // Used to guard modules_, tasks_ and stop_. + + rtc::ThreadChecker thread_checker_; + const std::unique_ptr<EventWrapper> wake_up_; + // TODO(pbos): Remove unique_ptr and stop recreating the thread. + std::unique_ptr<rtc::PlatformThread> thread_; + + ModuleList modules_; + std::queue<rtc::QueuedTask*> queue_; + bool stop_; + const char* thread_name_; +}; + +} // namespace webrtc + +#endif // MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ diff --git a/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl_unittest.cc b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl_unittest.cc new file mode 100644 index 0000000000..d27896410e --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl_unittest.cc @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2012 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 <memory> +#include <utility> + +#include "modules/include/module.h" +#include "modules/utility/source/process_thread_impl.h" +#include "rtc_base/location.h" +#include "rtc_base/task_queue.h" +#include "rtc_base/timeutils.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; + +// The length of time, in milliseconds, to wait for an event to become signaled. +// Set to a fairly large value as there is quite a bit of variation on some +// Windows bots. +static const int kEventWaitTimeout = 500; + +class MockModule : public Module { + public: + MOCK_METHOD0(TimeUntilNextProcess, int64_t()); + MOCK_METHOD0(Process, void()); + MOCK_METHOD1(ProcessThreadAttached, void(ProcessThread*)); +}; + +class RaiseEventTask : public rtc::QueuedTask { + public: + RaiseEventTask(EventWrapper* event) : event_(event) {} + bool Run() override { + event_->Set(); + return true; + } + + private: + EventWrapper* event_; +}; + +ACTION_P(SetEvent, event) { + event->Set(); +} + +ACTION_P(Increment, counter) { + ++(*counter); +} + +ACTION_P(SetTimestamp, ptr) { + *ptr = rtc::TimeMillis(); +} + +TEST(ProcessThreadImpl, StartStop) { + ProcessThreadImpl thread("ProcessThread"); + thread.Start(); + thread.Stop(); +} + +TEST(ProcessThreadImpl, MultipleStartStop) { + ProcessThreadImpl thread("ProcessThread"); + for (int i = 0; i < 5; ++i) { + thread.Start(); + thread.Stop(); + } +} + +// Verifies that we get at least call back to Process() on the worker thread. +TEST(ProcessThreadImpl, ProcessCall) { + ProcessThreadImpl thread("ProcessThread"); + thread.Start(); + + std::unique_ptr<EventWrapper> event(EventWrapper::Create()); + + MockModule module; + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillOnce(Return(0)) + .WillRepeatedly(Return(1)); + EXPECT_CALL(module, Process()) + .WillOnce(DoAll(SetEvent(event.get()), Return())) + .WillRepeatedly(Return()); + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + + thread.RegisterModule(&module, RTC_FROM_HERE); + EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.Stop(); +} + +// Same as ProcessCall except the module is registered before the +// call to Start(). +TEST(ProcessThreadImpl, ProcessCall2) { + ProcessThreadImpl thread("ProcessThread"); + std::unique_ptr<EventWrapper> event(EventWrapper::Create()); + + MockModule module; + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillOnce(Return(0)) + .WillRepeatedly(Return(1)); + EXPECT_CALL(module, Process()) + .WillOnce(DoAll(SetEvent(event.get()), Return())) + .WillRepeatedly(Return()); + + thread.RegisterModule(&module, RTC_FROM_HERE); + + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + thread.Start(); + EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.Stop(); +} + +// Tests setting up a module for callbacks and then unregister that module. +// After unregistration, we should not receive any further callbacks. +TEST(ProcessThreadImpl, Deregister) { + ProcessThreadImpl thread("ProcessThread"); + std::unique_ptr<EventWrapper> event(EventWrapper::Create()); + + int process_count = 0; + MockModule module; + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillOnce(Return(0)) + .WillRepeatedly(Return(1)); + EXPECT_CALL(module, Process()) + .WillOnce(DoAll(SetEvent(event.get()), + Increment(&process_count), + Return())) + .WillRepeatedly(DoAll(Increment(&process_count), Return())); + + thread.RegisterModule(&module, RTC_FROM_HERE); + + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + thread.Start(); + + EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.DeRegisterModule(&module); + + EXPECT_GE(process_count, 1); + int count_after_deregister = process_count; + + // We shouldn't get any more callbacks. + EXPECT_EQ(kEventTimeout, event->Wait(20)); + EXPECT_EQ(count_after_deregister, process_count); + thread.Stop(); +} + +// Helper function for testing receiving a callback after a certain amount of +// time. There's some variance of timing built into it to reduce chance of +// flakiness on bots. +void ProcessCallAfterAFewMs(int64_t milliseconds) { + ProcessThreadImpl thread("ProcessThread"); + thread.Start(); + + std::unique_ptr<EventWrapper> event(EventWrapper::Create()); + + MockModule module; + int64_t start_time = 0; + int64_t called_time = 0; + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillOnce(DoAll(SetTimestamp(&start_time), + Return(milliseconds))) + .WillRepeatedly(Return(milliseconds)); + EXPECT_CALL(module, Process()) + .WillOnce(DoAll(SetTimestamp(&called_time), + SetEvent(event.get()), + Return())) + .WillRepeatedly(Return()); + + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + thread.RegisterModule(&module, RTC_FROM_HERE); + + // Add a buffer of 50ms due to slowness of some trybots + // (e.g. win_drmemory_light) + EXPECT_EQ(kEventSignaled, event->Wait(milliseconds + 50)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.Stop(); + + ASSERT_GT(start_time, 0); + ASSERT_GT(called_time, 0); + // Use >= instead of > since due to rounding and timer accuracy (or lack + // thereof), can make the test run in "0"ms time. + EXPECT_GE(called_time, start_time); + // Check for an acceptable range. + uint32_t diff = called_time - start_time; + EXPECT_GE(diff, milliseconds - 15); + EXPECT_LT(diff, milliseconds + 15); +} + +// DISABLED for now since the virtual build bots are too slow :( +// TODO(tommi): Fix. +TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter5ms) { + ProcessCallAfterAFewMs(5); +} + +// DISABLED for now since the virtual build bots are too slow :( +// TODO(tommi): Fix. +TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter50ms) { + ProcessCallAfterAFewMs(50); +} + +// DISABLED for now since the virtual build bots are too slow :( +// TODO(tommi): Fix. +TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter200ms) { + ProcessCallAfterAFewMs(200); +} + +// Runs callbacks with the goal of getting up to 50 callbacks within a second +// (on average 1 callback every 20ms). On real hardware, we're usually pretty +// close to that, but the test bots that run on virtual machines, will +// typically be in the range 30-40 callbacks. +// DISABLED for now since this can take up to 2 seconds to run on the slowest +// build bots. +// TODO(tommi): Fix. +TEST(ProcessThreadImpl, DISABLED_Process50Times) { + ProcessThreadImpl thread("ProcessThread"); + thread.Start(); + + std::unique_ptr<EventWrapper> event(EventWrapper::Create()); + + MockModule module; + int callback_count = 0; + // Ask for a callback after 20ms. + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillRepeatedly(Return(20)); + EXPECT_CALL(module, Process()) + .WillRepeatedly(DoAll(Increment(&callback_count), + Return())); + + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + thread.RegisterModule(&module, RTC_FROM_HERE); + + EXPECT_EQ(kEventTimeout, event->Wait(1000)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.Stop(); + + printf("Callback count: %i\n", callback_count); + // Check that we got called back up to 50 times. + // Some of the try bots run on slow virtual machines, so the lower bound + // is much more relaxed to avoid flakiness. + EXPECT_GE(callback_count, 25); + EXPECT_LE(callback_count, 50); +} + +// Tests that we can wake up the worker thread to give us a callback right +// away when we know the thread is sleeping. +TEST(ProcessThreadImpl, WakeUp) { + ProcessThreadImpl thread("ProcessThread"); + thread.Start(); + + std::unique_ptr<EventWrapper> started(EventWrapper::Create()); + std::unique_ptr<EventWrapper> called(EventWrapper::Create()); + + MockModule module; + int64_t start_time; + int64_t called_time; + + // Ask for a callback after 1000ms. + // TimeUntilNextProcess will be called twice. + // The first time we use it to get the thread into a waiting state. + // Then we wake the thread and there should not be another call made to + // TimeUntilNextProcess before Process() is called. + // The second time TimeUntilNextProcess is then called, is after Process + // has been called and we don't expect any more calls. + EXPECT_CALL(module, TimeUntilNextProcess()) + .WillOnce(DoAll(SetTimestamp(&start_time), + SetEvent(started.get()), + Return(1000))) + .WillOnce(Return(1000)); + EXPECT_CALL(module, Process()) + .WillOnce( + DoAll(SetTimestamp(&called_time), SetEvent(called.get()), Return())) + .WillRepeatedly(Return()); + + EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); + thread.RegisterModule(&module, RTC_FROM_HERE); + + EXPECT_EQ(kEventSignaled, started->Wait(kEventWaitTimeout)); + thread.WakeUp(&module); + EXPECT_EQ(kEventSignaled, called->Wait(kEventWaitTimeout)); + + EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); + thread.Stop(); + + EXPECT_GE(called_time, start_time); + uint32_t diff = called_time - start_time; + // We should have been called back much quicker than 1sec. + EXPECT_LE(diff, 100u); +} + +// Tests that we can post a task that gets run straight away on the worker +// thread. +TEST(ProcessThreadImpl, PostTask) { + ProcessThreadImpl thread("ProcessThread"); + std::unique_ptr<EventWrapper> task_ran(EventWrapper::Create()); + std::unique_ptr<RaiseEventTask> task(new RaiseEventTask(task_ran.get())); + thread.Start(); + thread.PostTask(std::move(task)); + EXPECT_EQ(kEventSignaled, task_ran->Wait(kEventWaitTimeout)); + thread.Stop(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/webrtc/modules/utility/utility_gn/moz.build b/third_party/libwebrtc/webrtc/modules/utility/utility_gn/moz.build new file mode 100644 index 0000000000..781f096e41 --- /dev/null +++ b/third_party/libwebrtc/webrtc/modules/utility/utility_gn/moz.build @@ -0,0 +1,233 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["CHROMIUM_BUILD"] = True +DEFINES["V8_DEPRECATION_WARNINGS"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_RESTRICT_LOGGING"] = True + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "/ipc/chromium/src", + "/ipc/glue", + "/third_party/libwebrtc/webrtc/", + "/third_party/libwebrtc/webrtc/common_audio/resampler/include/", + "/third_party/libwebrtc/webrtc/common_audio/signal_processing/include/", + "/third_party/libwebrtc/webrtc/common_audio/vad/include/" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/modules/utility/source/process_thread_impl.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + DEFINES["WTF_USE_DYNAMIC_ANNOTATIONS"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION"] = "r12b" + DEFINES["DISABLE_NACL"] = True + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["USE_OPENSSL_CERTS"] = "1" + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["__GNU_SOURCE"] = "1" + + OS_LIBS += [ + "log" + ] + + UNIFIED_SOURCES += [ + "/third_party/libwebrtc/webrtc/modules/utility/source/helpers_android.cc", + "/third_party/libwebrtc/webrtc/modules/utility/source/jvm_android.cc" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["NO_TCMALLOC"] = True + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0" + + OS_LIBS += [ + "-framework Foundation" + ] + +if CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NO_TCMALLOC"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "1" + DEFINES["UNICODE"] = True + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_CRT_SECURE_NO_WARNINGS"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_USING_V110_SDK71_"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "DragonFly": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "FreeBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if not CONFIG["MOZ_DEBUG"] and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_FORTIFY_SOURCE"] = "2" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0120" + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["CR_XCODE_VERSION"] = "0920" + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "FreeBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["DISABLE_NACL"] = True + DEFINES["NO_TCMALLOC"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "NetBSD": + + CXXFLAGS += [ + "-msse2" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "OpenBSD": + + CXXFLAGS += [ + "-msse2" + ] + +Library("utility_gn") |