diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc new file mode 100644 index 000000000..4f575b1b4 --- /dev/null +++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc @@ -0,0 +1,123 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "talk/app/webrtc/java/jni/androidvideocapturer_jni.h" + +#include "talk/app/webrtc/java/jni/classreferenceholder.h" + +namespace webrtc_jni { + +jobject AndroidVideoCapturerJni::application_context_ = nullptr; + +// static +int AndroidVideoCapturerJni::SetAndroidObjects(JNIEnv* jni, + jobject appliction_context) { + if (application_context_) { + jni->DeleteGlobalRef(application_context_); + } + application_context_ = NewGlobalRef(jni, appliction_context); + + return 0; +} + +AndroidVideoCapturerJni::AndroidVideoCapturerJni(JNIEnv* jni, + jobject j_video_capturer) + : j_capturer_global_(jni, j_video_capturer), + j_video_capturer_class_( + jni, FindClass(jni, "org/webrtc/VideoCapturerAndroid")), + j_frame_observer_class_( + jni, + FindClass(jni, + "org/webrtc/VideoCapturerAndroid$NativeFrameObserver")) { + } + +AndroidVideoCapturerJni::~AndroidVideoCapturerJni() {} + +void AndroidVideoCapturerJni::Start(int width, int height, int framerate, + webrtc::AndroidVideoCapturer* capturer) { + j_frame_observer_ = NewGlobalRef( + jni(), + jni()->NewObject(*j_frame_observer_class_, + GetMethodID(jni(), + *j_frame_observer_class_, + "", + "(J)V"), + jlongFromPointer(capturer))); + CHECK_EXCEPTION(jni()) << "error during NewObject"; + + jmethodID m = GetMethodID( + jni(), *j_video_capturer_class_, "startCapture", + "(IIILandroid/content/Context;" + "Lorg/webrtc/VideoCapturerAndroid$CapturerObserver;)V"); + jni()->CallVoidMethod(*j_capturer_global_, + m, width, height, + framerate, + application_context_, + j_frame_observer_); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.startCapture"; +} + +bool AndroidVideoCapturerJni::Stop() { + jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, + "stopCapture", "()Z"); + jboolean result = jni()->CallBooleanMethod(*j_capturer_global_, m); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.stopCapture"; + DeleteGlobalRef(jni(), j_frame_observer_); + return result; +} + +std::string AndroidVideoCapturerJni::GetSupportedFormats() { + jmethodID m = + GetMethodID(jni(), *j_video_capturer_class_, + "getSupportedFormatsAsJson", "()Ljava/lang/String;"); + jstring j_json_caps = + (jstring) jni()->CallObjectMethod(*j_capturer_global_, m); + CHECK_EXCEPTION(jni()) << "error during supportedFormatsAsJson"; + return JavaToStdString(jni(), j_json_caps); +} + +JNIEnv* AndroidVideoCapturerJni::jni() { return AttachCurrentThreadIfNeeded(); } + +JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeOnFrameCaptured) + (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, + jint rotation, jlong ts) { + jbyte* bytes = jni->GetByteArrayElements(j_frame, NULL); + reinterpret_cast( + j_capturer)->OnIncomingFrame(bytes, jni->GetArrayLength(j_frame), + rotation, ts); + jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT); +} + +JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeCapturerStarted) + (JNIEnv* jni, jclass, jlong j_capturer, jboolean j_success) { + reinterpret_cast( + j_capturer)->OnCapturerStarted(j_success); +} + +} // namespace webrtc_jni + diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.h b/talk/app/webrtc/java/jni/androidvideocapturer_jni.h new file mode 100644 index 000000000..846905a9f --- /dev/null +++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.h @@ -0,0 +1,69 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TALK_APP_WEBRTC_JAVA_JNI_ANDROIDVIDEOCAPTURER_JNI_H_ +#define TALK_APP_WEBRTC_JAVA_JNI_ANDROIDVIDEOCAPTURER_JNI_H_ + +#include + +#include "talk/app/webrtc/androidvideocapturer.h" +#include "talk/app/webrtc/java/jni/jni_helpers.h" + +namespace webrtc_jni { + +// AndroidVideoCapturerJni implements AndroidVideoCapturerDelegate. +// The purpose of the delegate is to hide the JNI specifics from the C++ only +// AndroidVideoCapturer. +class AndroidVideoCapturerJni : public webrtc::AndroidVideoCapturerDelegate { + public: + static int SetAndroidObjects(JNIEnv* jni, jobject appliction_context); + AndroidVideoCapturerJni(JNIEnv* jni, jobject j_video_capturer); + ~AndroidVideoCapturerJni(); + + void Start(int width, int height, int framerate, + webrtc::AndroidVideoCapturer* capturer) override; + bool Stop() override; + + std::string GetSupportedFormats() override; + + private: + JNIEnv* jni(); + + const ScopedGlobalRef j_capturer_global_; + const ScopedGlobalRef j_video_capturer_class_; + const ScopedGlobalRef j_frame_observer_class_; + jobject j_frame_observer_; + + static jobject application_context_; + + DISALLOW_COPY_AND_ASSIGN(AndroidVideoCapturerJni); +}; + +} // namespace webrtc_jni + +#endif // TALK_APP_WEBRTC_JAVA_JNI_ANDROIDVIDEOCAPTURER_JNI_H_ diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc new file mode 100644 index 000000000..25a110101 --- /dev/null +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -0,0 +1,141 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "talk/app/webrtc/java/jni/classreferenceholder.h" + +#include "talk/app/webrtc/java/jni/jni_helpers.h" + +namespace webrtc_jni { + +// ClassReferenceHolder holds global reference to Java classes in app/webrtc. +class ClassReferenceHolder { + public: + explicit ClassReferenceHolder(JNIEnv* jni); + ~ClassReferenceHolder(); + + void FreeReferences(JNIEnv* jni); + jclass GetClass(const std::string& name); + + private: + void LoadClass(JNIEnv* jni, const std::string& name); + + std::map classes_; +}; + +// Allocated in LoadGlobalClassReferenceHolder(), +// freed in FreeGlobalClassReferenceHolder(). +static ClassReferenceHolder* g_class_reference_holder = nullptr; + +void LoadGlobalClassReferenceHolder() { + CHECK(g_class_reference_holder == nullptr); + g_class_reference_holder = new ClassReferenceHolder(GetEnv()); +} + +void FreeGlobalClassReferenceHolder() { + g_class_reference_holder->FreeReferences(AttachCurrentThreadIfNeeded()); + delete g_class_reference_holder; + g_class_reference_holder = nullptr; +} + +ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { + LoadClass(jni, "java/nio/ByteBuffer"); + LoadClass(jni, "org/webrtc/AudioTrack"); + LoadClass(jni, "org/webrtc/DataChannel"); + LoadClass(jni, "org/webrtc/DataChannel$Buffer"); + LoadClass(jni, "org/webrtc/DataChannel$Init"); + LoadClass(jni, "org/webrtc/DataChannel$State"); + LoadClass(jni, "org/webrtc/IceCandidate"); +#if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) + LoadClass(jni, "android/graphics/SurfaceTexture"); + LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); + LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeFrameObserver"); + LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder"); + LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); + LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder"); + LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecoderOutputBufferInfo"); + jclass j_decoder_class = GetClass("org/webrtc/MediaCodecVideoDecoder"); + jmethodID j_is_egl14_supported_method = jni->GetStaticMethodID( + j_decoder_class, "isEGL14Supported", "()Z"); + bool is_egl14_supported = jni->CallStaticBooleanMethod( + j_decoder_class, j_is_egl14_supported_method); + CHECK_EXCEPTION(jni); + if (is_egl14_supported) { + LoadClass(jni, "android/opengl/EGLContext"); + } +#endif + LoadClass(jni, "org/webrtc/MediaSource$State"); + LoadClass(jni, "org/webrtc/MediaStream"); + LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); + LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState"); + LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); + LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); + LoadClass(jni, "org/webrtc/SessionDescription"); + LoadClass(jni, "org/webrtc/SessionDescription$Type"); + LoadClass(jni, "org/webrtc/StatsReport"); + LoadClass(jni, "org/webrtc/StatsReport$Value"); + LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame"); + LoadClass(jni, "org/webrtc/VideoCapturer"); + LoadClass(jni, "org/webrtc/VideoTrack"); +} + +ClassReferenceHolder::~ClassReferenceHolder() { + CHECK(classes_.empty()) << "Must call FreeReferences() before dtor!"; +} + +void ClassReferenceHolder::FreeReferences(JNIEnv* jni) { + for (std::map::const_iterator it = classes_.begin(); + it != classes_.end(); ++it) { + jni->DeleteGlobalRef(it->second); + } + classes_.clear(); +} + +jclass ClassReferenceHolder::GetClass(const std::string& name) { + std::map::iterator it = classes_.find(name); + CHECK(it != classes_.end()) << "Unexpected GetClass() call for: " << name; + return it->second; +} + +void ClassReferenceHolder::LoadClass(JNIEnv* jni, const std::string& name) { + jclass localRef = jni->FindClass(name.c_str()); + CHECK_EXCEPTION(jni) << "error during FindClass: " << name; + CHECK(localRef) << name; + jclass globalRef = reinterpret_cast(jni->NewGlobalRef(localRef)); + CHECK_EXCEPTION(jni) << "error during NewGlobalRef: " << name; + CHECK(globalRef) << name; + bool inserted = classes_.insert(std::make_pair(name, globalRef)).second; + CHECK(inserted) << "Duplicate class name: " << name; +} + +// Returns a global reference guaranteed to be valid for the lifetime of the +// process. +jclass FindClass(JNIEnv* jni, const char* name) { + return g_class_reference_holder->GetClass(name); +} + +} // namespace webrtc_jni + diff --git a/talk/app/webrtc/java/jni/classreferenceholder.h b/talk/app/webrtc/java/jni/classreferenceholder.h new file mode 100644 index 000000000..46e018ba7 --- /dev/null +++ b/talk/app/webrtc/java/jni/classreferenceholder.h @@ -0,0 +1,59 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// 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 app/webrtc. +// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass + +#ifndef TALK_APP_WEBRTC_JAVA_JNI_CLASSREFERENCEHOLDER_H_ +#define TALK_APP_WEBRTC_JAVA_JNI_CLASSREFERENCEHOLDER_H_ + +#include +#include +#include + +namespace webrtc_jni { + +// LoadGlobalClassReferenceHolder must be called in JNI_OnLoad. +void LoadGlobalClassReferenceHolder(); +// FreeGlobalClassReferenceHolder must be called in JNI_UnLoad. +void FreeGlobalClassReferenceHolder(); + +// Returns a global reference guaranteed to be valid for the lifetime of the +// process. +jclass FindClass(JNIEnv* jni, const char* name); + +// Convenience macro defining JNI-accessible methods in the org.webrtc package. +// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter. +#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \ + Java_org_webrtc_##name + +} // namespace webrtc_jni + +#endif // TALK_APP_WEBRTC_JAVA_JNI_CLASSREFERENCEHOLDER_H_ diff --git a/talk/app/webrtc/java/jni/jni_helpers.cc b/talk/app/webrtc/java/jni/jni_helpers.cc new file mode 100644 index 000000000..c4e6e67f9 --- /dev/null +++ b/talk/app/webrtc/java/jni/jni_helpers.cc @@ -0,0 +1,295 @@ + +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "talk/app/webrtc/java/jni/jni_helpers.h" + +#include +#include +#include +#include + +namespace webrtc_jni { + +static JavaVM* g_jvm = nullptr; + +static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT; + +// Key for per-thread JNIEnv* data. Non-NULL in threads attached to |g_jvm| by +// AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that +// were attached by the JVM because of a Java->native call. +static pthread_key_t g_jni_ptr; + +using icu::UnicodeString; + +JavaVM *GetJVM() { + CHECK(g_jvm) << "JNI_OnLoad failed to run?"; + return g_jvm; +} + +// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached. +JNIEnv* GetEnv() { + void* env = NULL; + jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6); + CHECK(((env != NULL) && (status == JNI_OK)) || + ((env == NULL) && (status == JNI_EDETACHED))) + << "Unexpected GetEnv return: " << status << ":" << env; + return reinterpret_cast(env); +} + +static void ThreadDestructor(void* prev_jni_ptr) { + // This function only runs on threads where |g_jni_ptr| is non-NULL, meaning + // we were responsible for originally attaching the thread, so are responsible + // for detaching it now. However, because some JVM implementations (notably + // Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism, + // the JVMs accounting info for this thread may already be wiped out by the + // time this is called. Thus it may appear we are already detached even though + // it was our responsibility to detach! Oh well. + if (!GetEnv()) + return; + + CHECK(GetEnv() == prev_jni_ptr) + << "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv(); + jint status = g_jvm->DetachCurrentThread(); + CHECK(status == JNI_OK) << "Failed to detach thread: " << status; + CHECK(!GetEnv()) << "Detaching was a successful no-op???"; +} + +static void CreateJNIPtrKey() { + CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor)) + << "pthread_key_create"; +} + +jint InitGlobalJniVariables(JavaVM *jvm) { + CHECK(!g_jvm) << "InitGlobalJniVariables!"; + g_jvm = jvm; + CHECK(g_jvm) << "InitGlobalJniVariables handed NULL?"; + + CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once"; + + JNIEnv* jni = nullptr; + if (jvm->GetEnv(reinterpret_cast(&jni), JNI_VERSION_1_6) != JNI_OK) + return -1; + + return JNI_VERSION_1_6; +} + +// Return thread ID as a string. +static std::string GetThreadId() { + char buf[21]; // Big enough to hold a kuint64max plus terminating NULL. + CHECK_LT(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)), + sizeof(buf)) + << "Thread id is bigger than uint64??"; + return std::string(buf); +} + +// Return the current thread's name. +static std::string GetThreadName() { + char name[17]; + CHECK_EQ(0, prctl(PR_GET_NAME, name)) << "prctl(PR_GET_NAME) failed"; + name[16] = '\0'; + return std::string(name); +} + +// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary. +JNIEnv* AttachCurrentThreadIfNeeded() { + JNIEnv* jni = GetEnv(); + if (jni) + return jni; + CHECK(!pthread_getspecific(g_jni_ptr)) + << "TLS has a JNIEnv* but not attached?"; + + std::string name(GetThreadName() + " - " + GetThreadId()); + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_6; + args.name = &name[0]; + args.group = NULL; + // Deal with difference in signatures between Oracle's jni.h and Android's. +#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec! + void* env = NULL; +#else + JNIEnv* env = NULL; +#endif + CHECK(!g_jvm->AttachCurrentThread(&env, &args)) << "Failed to attach thread"; + CHECK(env) << "AttachCurrentThread handed back NULL!"; + jni = reinterpret_cast(env); + CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific"; + return jni; +} + +// 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 jlongFromPointer(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(ptr); + DCHECK(reinterpret_cast(ret) == ptr); + return ret; +} + +// JNIEnv-helper methods that CHECK success: no Java exception thrown and found +// object/class/method/field is non-null. +jmethodID GetMethodID( + JNIEnv* jni, jclass c, const std::string& name, const char* signature) { + jmethodID m = jni->GetMethodID(c, name.c_str(), signature); + CHECK_EXCEPTION(jni) << "error during GetMethodID: " << name << ", " + << signature; + 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; + CHECK(m) << name << ", " << signature; + return m; +} + +jfieldID GetFieldID( + JNIEnv* jni, jclass c, const char* name, const char* signature) { + jfieldID f = jni->GetFieldID(c, name, signature); + CHECK_EXCEPTION(jni) << "error during GetFieldID"; + CHECK(f) << name << ", " << signature; + return f; +} + +jclass GetObjectClass(JNIEnv* jni, jobject object) { + jclass c = jni->GetObjectClass(object); + CHECK_EXCEPTION(jni) << "error during GetObjectClass"; + CHECK(c) << "GetObjectClass returned NULL"; + return c; +} + +jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) { + jobject o = jni->GetObjectField(object, id); + CHECK_EXCEPTION(jni) << "error during GetObjectField"; + CHECK(o) << "GetObjectField returned NULL"; + return o; +} + +jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) { + return static_cast(GetObjectField(jni, object, id)); +} + +jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) { + jlong l = jni->GetLongField(object, id); + CHECK_EXCEPTION(jni) << "error during GetLongField"; + return l; +} + +jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) { + jint i = jni->GetIntField(object, id); + CHECK_EXCEPTION(jni) << "error during GetIntField"; + return i; +} + +bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) { + jboolean b = jni->GetBooleanField(object, id); + CHECK_EXCEPTION(jni) << "error during GetBooleanField"; + return b; +} + +// Java references to "null" can only be distinguished as such in C++ by +// creating a local reference, so this helper wraps that logic. +bool IsNull(JNIEnv* jni, jobject obj) { + ScopedLocalRefFrame local_ref_frame(jni); + return jni->NewLocalRef(obj) == NULL; +} + +// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring. +jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) { + UnicodeString ustr(UnicodeString::fromUTF8(native)); + jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length()); + CHECK_EXCEPTION(jni) << "error during NewString"; + return jstr; +} + +// Given a (UTF-16) jstring return a new UTF-8 native string. +std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) { + const jchar* jchars = jni->GetStringChars(j_string, NULL); + CHECK_EXCEPTION(jni) << "Error during GetStringChars"; + UnicodeString ustr(jchars, jni->GetStringLength(j_string)); + CHECK_EXCEPTION(jni) << "Error during GetStringLength"; + jni->ReleaseStringChars(j_string, jchars); + CHECK_EXCEPTION(jni) << "Error during ReleaseStringChars"; + std::string ret; + return ustr.toUTF8String(ret); +} + +// Return the (singleton) Java Enum object corresponding to |index|; +jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class, + const std::string& state_class_name, int index) { + jmethodID state_values_id = GetStaticMethodID( + jni, state_class, "values", ("()[L" + state_class_name + ";").c_str()); + jobjectArray state_values = static_cast( + jni->CallStaticObjectMethod(state_class, state_values_id)); + CHECK_EXCEPTION(jni) << "error during CallStaticObjectMethod"; + jobject ret = jni->GetObjectArrayElement(state_values, index); + CHECK_EXCEPTION(jni) << "error during GetObjectArrayElement"; + return ret; +} + +jobject NewGlobalRef(JNIEnv* jni, jobject o) { + jobject ret = jni->NewGlobalRef(o); + CHECK_EXCEPTION(jni) << "error during NewGlobalRef"; + CHECK(ret); + return ret; +} + +void DeleteGlobalRef(JNIEnv* jni, jobject o) { + jni->DeleteGlobalRef(o); + CHECK_EXCEPTION(jni) << "error during DeleteGlobalRef"; +} + +WeakRef::WeakRef(JNIEnv* jni, jweak ref) + : jni_(jni), obj_(jni_->NewLocalRef(ref)) { + CHECK_EXCEPTION(jni) << "error during NewLocalRef"; +} +WeakRef::~WeakRef() { + if (obj_) { + jni_->DeleteLocalRef(obj_); + CHECK_EXCEPTION(jni_) << "error during DeleteLocalRef"; + } +} + +// Scope Java local references to the lifetime of this object. Use in all C++ +// callbacks (i.e. entry points that don't originate in a Java callstack +// through a "native" method call). +ScopedLocalRefFrame::ScopedLocalRefFrame(JNIEnv* jni) : jni_(jni) { + CHECK(!jni_->PushLocalFrame(0)) << "Failed to PushLocalFrame"; +} +ScopedLocalRefFrame::~ScopedLocalRefFrame() { + jni_->PopLocalFrame(NULL); +} + +} // namespace webrtc_jni diff --git a/talk/app/webrtc/java/jni/jni_helpers.h b/talk/app/webrtc/java/jni/jni_helpers.h new file mode 100644 index 000000000..bfbde78ef --- /dev/null +++ b/talk/app/webrtc/java/jni/jni_helpers.h @@ -0,0 +1,156 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +// This file contain convenience functions and classes for JNI. +// Before using any of the methods, InitGlobalJniVariables must be called. + +#ifndef TALK_APP_WEBRTC_JAVA_JNI_JNI_HELPERS_H_ +#define TALK_APP_WEBRTC_JAVA_JNI_JNI_HELPERS_H_ + +#include +#include + +#include "webrtc/base/checks.h" +#include "third_party/icu/source/common/unicode/unistr.h" + +// Abort the process if |jni| has a Java exception pending. +// This macros uses the comma operator to execute ExceptionDescribe +// and ExceptionClear ignoring their return values and sending "" +// to the error stream. +#define CHECK_EXCEPTION(jni) \ + CHECK(!jni->ExceptionCheck()) \ + << (jni->ExceptionDescribe(), jni->ExceptionClear(), "") + +// Helper that calls ptr->Release() and aborts the process with a useful +// message if that didn't actually delete *ptr because of extra refcounts. +#define CHECK_RELEASE(ptr) \ + CHECK_EQ(0, (ptr)->Release()) << "Unexpected refcount." + +namespace webrtc_jni { + +jint InitGlobalJniVariables(JavaVM *jvm); + +// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached. +JNIEnv* GetEnv(); + +JavaVM *GetJVM(); + +// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary. +JNIEnv* AttachCurrentThreadIfNeeded(); + +// 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 jlongFromPointer(void* ptr); + +// JNIEnv-helper methods that CHECK success: no Java exception thrown and found +// object/class/method/field is non-null. +jmethodID GetMethodID( + JNIEnv* jni, jclass c, const std::string& name, const char* signature); + +jmethodID GetStaticMethodID( + JNIEnv* jni, jclass c, const char* name, const char* signature); + +jfieldID GetFieldID(JNIEnv* jni, jclass c, const char* name, + const char* signature); + +jclass GetObjectClass(JNIEnv* jni, jobject object); + +jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id); + +jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id); + +jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id); + +jint GetIntField(JNIEnv* jni, jobject object, jfieldID id); + +bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id); + +// Java references to "null" can only be distinguished as such in C++ by +// creating a local reference, so this helper wraps that logic. +bool IsNull(JNIEnv* jni, jobject obj); + +// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring. +jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native); + +// Given a (UTF-16) jstring return a new UTF-8 native string. +std::string JavaToStdString(JNIEnv* jni, const jstring& j_string); + +// Return the (singleton) Java Enum object corresponding to |index|; +jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class, + const std::string& state_class_name, int index); + +jobject NewGlobalRef(JNIEnv* jni, jobject o); + +void DeleteGlobalRef(JNIEnv* jni, jobject o); + +// Given a jweak reference, allocate a (strong) local reference scoped to the +// lifetime of this object if the weak reference is still valid, or NULL +// otherwise. +class WeakRef { + public: + WeakRef(JNIEnv* jni, jweak ref); + ~WeakRef(); + jobject obj() { return obj_; } + + private: + JNIEnv* const jni_; + jobject const obj_; +}; + +// Scope Java local references to the lifetime of this object. Use in all C++ +// callbacks (i.e. entry points that don't originate in a Java callstack +// through a "native" method call). +class ScopedLocalRefFrame { + public: + explicit ScopedLocalRefFrame(JNIEnv* jni); + ~ScopedLocalRefFrame(); + + private: + JNIEnv* jni_; +}; + +// Scoped holder for global Java refs. +template // T is jclass, jobject, jintArray, etc. +class ScopedGlobalRef { + public: + ScopedGlobalRef(JNIEnv* jni, T obj) + : obj_(static_cast(jni->NewGlobalRef(obj))) {} + ~ScopedGlobalRef() { + DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_); + } + T operator*() const { + return obj_; + } + private: + T obj_; +}; + +} // namespace webrtc_jni + +#endif // TALK_APP_WEBRTC_JAVA_JNI_JNI_HELPERS_H_ diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index 9c8d7088d..bddd4171a 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -56,13 +56,10 @@ #undef JNIEXPORT #define JNIEXPORT __attribute__((visibility("default"))) -#include -#include -#include -#include #include -#include +#include "talk/app/webrtc/java/jni/classreferenceholder.h" +#include "talk/app/webrtc/java/jni/jni_helpers.h" #include "talk/app/webrtc/mediaconstraintsinterface.h" #include "talk/app/webrtc/peerconnectioninterface.h" #include "talk/app/webrtc/videosourceinterface.h" @@ -92,6 +89,7 @@ #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) #include #include "talk/app/webrtc/androidvideocapturer.h" +#include "talk/app/webrtc/java/jni/androidvideocapturer_jni.h" #include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/system_wrappers/interface/logcat_trace_context.h" #include "webrtc/system_wrappers/interface/tick_util.h" @@ -106,7 +104,6 @@ using webrtc::TickTime; using webrtc::VideoCodec; #endif -using icu::UnicodeString; using rtc::Bind; using rtc::Thread; using rtc::ThreadManager; @@ -139,28 +136,7 @@ using webrtc::VideoTrackInterface; using webrtc::VideoTrackVector; using webrtc::kVideoCodecVP8; -// Abort the process if |jni| has a Java exception pending. -// This macros uses the comma operator to execute ExceptionDescribe -// and ExceptionClear ignoring their return values and sending "" -// to the error stream. -#define CHECK_EXCEPTION(jni) \ - CHECK(!jni->ExceptionCheck()) \ - << (jni->ExceptionDescribe(), jni->ExceptionClear(), "") - -// Helper that calls ptr->Release() and aborts the process with a useful -// message if that didn't actually delete *ptr because of extra refcounts. -#define CHECK_RELEASE(ptr) \ - CHECK_EQ(0, (ptr)->Release()) << "Unexpected refcount." - -namespace { - -static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad(). - -static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT; -// Key for per-thread JNIEnv* data. Non-NULL in threads attached to |g_jvm| by -// AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that -// were attached by the JVM because of a Java->native call. -static pthread_key_t g_jni_ptr; +namespace webrtc_jni { // Field trials initialization string static char *field_trials_init_string = NULL; @@ -171,388 +147,29 @@ static bool factory_static_initialized = false; static bool vp8_hw_acceleration_enabled = true; #endif - -// Return thread ID as a string. -static std::string GetThreadId() { - char buf[21]; // Big enough to hold a kuint64max plus terminating NULL. - CHECK_LT(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)), - sizeof(buf)) - << "Thread id is bigger than uint64??"; - return std::string(buf); -} - -// Return the current thread's name. -static std::string GetThreadName() { - char name[17]; - CHECK_EQ(0, prctl(PR_GET_NAME, name)) << "prctl(PR_GET_NAME) failed"; - name[16] = '\0'; - return std::string(name); -} - -// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached. -static JNIEnv* GetEnv() { - void* env = NULL; - jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6); - CHECK(((env != NULL) && (status == JNI_OK)) || - ((env == NULL) && (status == JNI_EDETACHED))) - << "Unexpected GetEnv return: " << status << ":" << env; - return reinterpret_cast(env); -} - -static void ThreadDestructor(void* prev_jni_ptr) { - // This function only runs on threads where |g_jni_ptr| is non-NULL, meaning - // we were responsible for originally attaching the thread, so are responsible - // for detaching it now. However, because some JVM implementations (notably - // Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism, - // the JVMs accounting info for this thread may already be wiped out by the - // time this is called. Thus it may appear we are already detached even though - // it was our responsibility to detach! Oh well. - if (!GetEnv()) - return; - - CHECK(GetEnv() == prev_jni_ptr) - << "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv(); - jint status = g_jvm->DetachCurrentThread(); - CHECK(status == JNI_OK) << "Failed to detach thread: " << status; - CHECK(!GetEnv()) << "Detaching was a successful no-op???"; -} - -static void CreateJNIPtrKey() { - CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor)) - << "pthread_key_create"; -} - -// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary. -static JNIEnv* AttachCurrentThreadIfNeeded() { - JNIEnv* jni = GetEnv(); - if (jni) - return jni; - CHECK(!pthread_getspecific(g_jni_ptr)) - << "TLS has a JNIEnv* but not attached?"; - - char* name = strdup((GetThreadName() + " - " + GetThreadId()).c_str()); - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_6; - args.name = name; - args.group = NULL; - // Deal with difference in signatures between Oracle's jni.h and Android's. -#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec! - void* env = NULL; -#else - JNIEnv* env = NULL; -#endif - CHECK(!g_jvm->AttachCurrentThread(&env, &args)) << "Failed to attach thread"; - free(name); - CHECK(env) << "AttachCurrentThread handed back NULL!"; - jni = reinterpret_cast(env); - CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific"; - return jni; -} - -// 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. -static jlong jlongFromPointer(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(ptr); - assert(reinterpret_cast(ret) == ptr); - return ret; -} - -// 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 classes once in JNI_OnLoad. -// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass -class ClassReferenceHolder { - public: - explicit ClassReferenceHolder(JNIEnv* jni) { - LoadClass(jni, "java/nio/ByteBuffer"); - LoadClass(jni, "org/webrtc/AudioTrack"); - LoadClass(jni, "org/webrtc/DataChannel"); - LoadClass(jni, "org/webrtc/DataChannel$Buffer"); - LoadClass(jni, "org/webrtc/DataChannel$Init"); - LoadClass(jni, "org/webrtc/DataChannel$State"); - LoadClass(jni, "org/webrtc/IceCandidate"); -#if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) - LoadClass(jni, "android/graphics/SurfaceTexture"); - LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); - LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeFrameObserver"); - LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder"); - LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); - LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder"); - LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecoderOutputBufferInfo"); - jclass j_decoder_class = GetClass("org/webrtc/MediaCodecVideoDecoder"); - jmethodID j_is_egl14_supported_method = jni->GetStaticMethodID( - j_decoder_class, "isEGL14Supported", "()Z"); - bool is_egl14_supported = jni->CallStaticBooleanMethod( - j_decoder_class, j_is_egl14_supported_method); - CHECK_EXCEPTION(jni); - if (is_egl14_supported) { - LoadClass(jni, "android/opengl/EGLContext"); - } -#endif - LoadClass(jni, "org/webrtc/MediaSource$State"); - LoadClass(jni, "org/webrtc/MediaStream"); - LoadClass(jni, "org/webrtc/MediaStreamTrack$State"); - LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState"); - LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); - LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); - LoadClass(jni, "org/webrtc/SessionDescription"); - LoadClass(jni, "org/webrtc/SessionDescription$Type"); - LoadClass(jni, "org/webrtc/StatsReport"); - LoadClass(jni, "org/webrtc/StatsReport$Value"); - LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame"); - LoadClass(jni, "org/webrtc/VideoCapturer"); - LoadClass(jni, "org/webrtc/VideoTrack"); - } - - ~ClassReferenceHolder() { - CHECK(classes_.empty()) << "Must call FreeReferences() before dtor!"; - } - - void FreeReferences(JNIEnv* jni) { - for (std::map::const_iterator it = classes_.begin(); - it != classes_.end(); ++it) { - jni->DeleteGlobalRef(it->second); - } - classes_.clear(); - } - - jclass GetClass(const std::string& name) { - std::map::iterator it = classes_.find(name); - CHECK(it != classes_.end()) << "Unexpected GetClass() call for: " << name; - return it->second; - } - - private: - void LoadClass(JNIEnv* jni, const std::string& name) { - jclass localRef = jni->FindClass(name.c_str()); - CHECK_EXCEPTION(jni) << "error during FindClass: " << name; - CHECK(localRef) << name; - jclass globalRef = reinterpret_cast(jni->NewGlobalRef(localRef)); - CHECK_EXCEPTION(jni) << "error during NewGlobalRef: " << name; - CHECK(globalRef) << name; - bool inserted = classes_.insert(std::make_pair(name, globalRef)).second; - CHECK(inserted) << "Duplicate class name: " << name; - } - - std::map classes_; -}; - -// Allocated in JNI_OnLoad(), freed in JNI_OnUnLoad(). -static ClassReferenceHolder* g_class_reference_holder = NULL; - -// JNIEnv-helper methods that CHECK success: no Java exception thrown and found -// object/class/method/field is non-null. -jmethodID GetMethodID( - JNIEnv* jni, jclass c, const std::string& name, const char* signature) { - jmethodID m = jni->GetMethodID(c, name.c_str(), signature); - CHECK_EXCEPTION(jni) << "error during GetMethodID: " << name << ", " - << signature; - 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; - CHECK(m) << name << ", " << signature; - return m; -} - -jfieldID GetFieldID( - JNIEnv* jni, jclass c, const char* name, const char* signature) { - jfieldID f = jni->GetFieldID(c, name, signature); - CHECK_EXCEPTION(jni) << "error during GetFieldID"; - CHECK(f) << name << ", " << signature; - return f; -} - -// Returns a global reference guaranteed to be valid for the lifetime of the -// process. -jclass FindClass(JNIEnv* jni, const char* name) { - return g_class_reference_holder->GetClass(name); -} - -jclass GetObjectClass(JNIEnv* jni, jobject object) { - jclass c = jni->GetObjectClass(object); - CHECK_EXCEPTION(jni) << "error during GetObjectClass"; - CHECK(c) << "GetObjectClass returned NULL"; - return c; -} - -jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) { - jobject o = jni->GetObjectField(object, id); - CHECK_EXCEPTION(jni) << "error during GetObjectField"; - CHECK(o) << "GetObjectField returned NULL"; - return o; -} - -jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) { - return static_cast(GetObjectField(jni, object, id)); -} - -jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) { - jlong l = jni->GetLongField(object, id); - CHECK_EXCEPTION(jni) << "error during GetLongField"; - return l; -} - -jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) { - jint i = jni->GetIntField(object, id); - CHECK_EXCEPTION(jni) << "error during GetIntField"; - return i; -} - -bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) { - jboolean b = jni->GetBooleanField(object, id); - CHECK_EXCEPTION(jni) << "error during GetBooleanField"; - return b; -} - -jobject NewGlobalRef(JNIEnv* jni, jobject o) { - jobject ret = jni->NewGlobalRef(o); - CHECK_EXCEPTION(jni) << "error during NewGlobalRef"; - CHECK(ret); - return ret; -} - -void DeleteGlobalRef(JNIEnv* jni, jobject o) { - jni->DeleteGlobalRef(o); - CHECK_EXCEPTION(jni) << "error during DeleteGlobalRef"; -} - -// Convenience macro defining JNI-accessible methods in the org.webrtc package. -// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter. -#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \ - Java_org_webrtc_##name - extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { - CHECK(!g_jvm) << "JNI_OnLoad called more than once!"; - g_jvm = jvm; - CHECK(g_jvm) << "JNI_OnLoad handed NULL?"; - - CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once"; + jint ret = InitGlobalJniVariables(jvm); + if (ret < 0) + return -1; CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()"; + LoadGlobalClassReferenceHolder(); - JNIEnv* jni; - if (jvm->GetEnv(reinterpret_cast(&jni), JNI_VERSION_1_6) != JNI_OK) - return -1; - g_class_reference_holder = new ClassReferenceHolder(jni); - - return JNI_VERSION_1_6; + return ret; } extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) { - g_class_reference_holder->FreeReferences(AttachCurrentThreadIfNeeded()); - delete g_class_reference_holder; - g_class_reference_holder = NULL; + FreeGlobalClassReferenceHolder(); CHECK(rtc::CleanupSSL()) << "Failed to CleanupSSL()"; - g_jvm = NULL; -} - -// Given a jweak reference, allocate a (strong) local reference scoped to the -// lifetime of this object if the weak reference is still valid, or NULL -// otherwise. -class WeakRef { - public: - WeakRef(JNIEnv* jni, jweak ref) - : jni_(jni), obj_(jni_->NewLocalRef(ref)) { - CHECK_EXCEPTION(jni) << "error during NewLocalRef"; - } - ~WeakRef() { - if (obj_) { - jni_->DeleteLocalRef(obj_); - CHECK_EXCEPTION(jni_) << "error during DeleteLocalRef"; - } - } - jobject obj() { return obj_; } - - private: - JNIEnv* const jni_; - jobject const obj_; -}; - -// Scope Java local references to the lifetime of this object. Use in all C++ -// callbacks (i.e. entry points that don't originate in a Java callstack -// through a "native" method call). -class ScopedLocalRefFrame { - public: - explicit ScopedLocalRefFrame(JNIEnv* jni) : jni_(jni) { - CHECK(!jni_->PushLocalFrame(0)) << "Failed to PushLocalFrame"; - } - ~ScopedLocalRefFrame() { - jni_->PopLocalFrame(NULL); - } - - private: - JNIEnv* jni_; -}; - -// Scoped holder for global Java refs. -template // T is jclass, jobject, jintArray, etc. -class ScopedGlobalRef { - public: - ScopedGlobalRef(JNIEnv* jni, T obj) - : obj_(static_cast(jni->NewGlobalRef(obj))) {} - ~ScopedGlobalRef() { - DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_); - } - T operator*() const { - return obj_; - } - private: - T obj_; -}; - -// Java references to "null" can only be distinguished as such in C++ by -// creating a local reference, so this helper wraps that logic. -static bool IsNull(JNIEnv* jni, jobject obj) { - ScopedLocalRefFrame local_ref_frame(jni); - return jni->NewLocalRef(obj) == NULL; } // Return the (singleton) Java Enum object corresponding to |index|; // |state_class_fragment| is something like "MediaSource$State". jobject JavaEnumFromIndex( JNIEnv* jni, const std::string& state_class_fragment, int index) { - std::string state_class_name = "org/webrtc/" + state_class_fragment; - jclass state_class = FindClass(jni, state_class_name.c_str()); - jmethodID state_values_id = GetStaticMethodID( - jni, state_class, "values", ("()[L" + state_class_name + ";").c_str()); - jobjectArray state_values = static_cast( - jni->CallStaticObjectMethod(state_class, state_values_id)); - CHECK_EXCEPTION(jni) << "error during CallStaticObjectMethod"; - jobject ret = jni->GetObjectArrayElement(state_values, index); - CHECK_EXCEPTION(jni) << "error during GetObjectArrayElement"; - return ret; -} - -// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring. -static jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) { - UnicodeString ustr(UnicodeString::fromUTF8(native)); - jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length()); - CHECK_EXCEPTION(jni) << "error during NewString"; - return jstr; -} - -// Given a (UTF-16) jstring return a new UTF-8 native string. -static std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) { - const jchar* jchars = jni->GetStringChars(j_string, NULL); - CHECK_EXCEPTION(jni) << "Error during GetStringChars"; - UnicodeString ustr(jchars, jni->GetStringLength(j_string)); - CHECK_EXCEPTION(jni) << "Error during GetStringLength"; - jni->ReleaseStringChars(j_string, jchars); - CHECK_EXCEPTION(jni) << "Error during ReleaseStringChars"; - std::string ret; - return ustr.toUTF8String(ret); + const std::string state_class = "org/webrtc/" + state_class_fragment; + return JavaEnumFromIndex(jni, FindClass(jni, state_class.c_str()), + state_class, index); } static DataChannelInit JavaDataChannelInitToNative( @@ -1242,7 +859,7 @@ class JavaVideoRendererWrapper : public VideoRendererInterface { // into its own .h/.cc pair, if/when the JNI helper stuff above is extracted // from this file. -//#define TRACK_BUFFER_TIMING +// #define TRACK_BUFFER_TIMING #define TAG "MediaCodecVideo" #ifdef TRACK_BUFFER_TIMING #define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__) @@ -1377,7 +994,7 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder, int height_; // Frame height in pixels. bool inited_; uint16_t picture_id_; - enum libyuv::FourCC encoder_fourcc_; // Encoder color space format. + enum libyuv::FourCC encoder_fourcc_; // Encoder color space format. int last_set_bitrate_kbps_; // Last-requested bitrate in kbps. int last_set_fps_; // Last-requested frame rate. int64_t current_timestamp_us_; // Current frame timestamps in us. @@ -1764,7 +1381,7 @@ int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { CheckOnCodecThread(); JNIEnv* jni = AttachCurrentThreadIfNeeded(); ALOGD("EncoderRelease: Frames received: %d. Frames dropped: %d.", - frames_received_,frames_dropped_); + frames_received_, frames_dropped_); ScopedLocalRefFrame local_ref_frame(jni); for (size_t i = 0; i < input_buffers_.size(); ++i) jni->DeleteGlobalRef(input_buffers_[i]); @@ -1888,7 +1505,7 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { current_encoding_time_ms_ / current_frames_, statistic_time_ms); start_time_ms_ = GetCurrentTimeMs(); current_frames_ = 0; - current_bytes_= 0; + current_bytes_ = 0; current_encoding_time_ms_ = 0; } @@ -2517,7 +2134,8 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( decoded_image_.CreateFrame( stride * slice_height, payload, (stride * slice_height) / 4, payload + (stride * slice_height), - (stride * slice_height) / 4, payload + (5 * stride * slice_height / 4), + (stride * slice_height) / 4, + payload + (5 * stride * slice_height / 4), width, height, stride, stride / 2, stride / 2); } else { @@ -2575,7 +2193,7 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( current_decoding_time_ms_ / current_frames_, statistic_time_ms); start_time_ms_ = GetCurrentTimeMs(); current_frames_ = 0; - current_bytes_= 0; + current_bytes_ = 0; current_decoding_time_ms_ = 0; } @@ -2672,108 +2290,8 @@ void MediaCodecVideoDecoderFactory::DestroyVideoDecoder( delete decoder; } -// AndroidVideoCapturerJni implements AndroidVideoCapturerDelegate. -// The purpose of the delegate is to hide the JNI specifics from the C++ only -// AndroidVideoCapturer. -// TODO(perkj): Refactor this to a separate file once the jni utility functions -// and classes have been moved. -class AndroidVideoCapturerJni : public webrtc::AndroidVideoCapturerDelegate { - public: - static int SetAndroidObjects(JNIEnv* jni, jobject appliction_context) { - if (application_context_) { - jni->DeleteGlobalRef(application_context_); - } - application_context_ = NewGlobalRef(jni, appliction_context); - - return 0; - } - - AndroidVideoCapturerJni(JNIEnv* jni, jobject j_video_capturer) - : j_capturer_global_(jni, j_video_capturer), - j_video_capturer_class_( - jni, FindClass(jni, "org/webrtc/VideoCapturerAndroid")), - j_frame_observer_class_( - jni, - FindClass(jni, - "org/webrtc/VideoCapturerAndroid$NativeFrameObserver")) { - } - - void Start(int width, int height, int framerate, - webrtc::AndroidVideoCapturer* capturer) override { - j_frame_observer_ = NewGlobalRef( - jni(), - jni()->NewObject(*j_frame_observer_class_, - GetMethodID(jni(), - *j_frame_observer_class_, - "", - "(J)V"), - jlongFromPointer(capturer))); - CHECK_EXCEPTION(jni()) << "error during NewObject"; - - jmethodID m = GetMethodID( - jni(), *j_video_capturer_class_, "startCapture", - "(IIILandroid/content/Context;" - "Lorg/webrtc/VideoCapturerAndroid$CapturerObserver;)V"); - jni()->CallVoidMethod(*j_capturer_global_, - m, width, height, - framerate, - application_context_, - j_frame_observer_); - CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.startCapture"; - } - - bool Stop() override { - jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, - "stopCapture", "()Z"); - jboolean result = jni()->CallBooleanMethod(*j_capturer_global_, m); - CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.stopCapture"; - DeleteGlobalRef(jni(), j_frame_observer_); - return result; - } - - std::string GetSupportedFormats() override { - jmethodID m = - GetMethodID(jni(), *j_video_capturer_class_, - "getSupportedFormatsAsJson", "()Ljava/lang/String;"); - jstring j_json_caps = - (jstring) jni()->CallObjectMethod(*j_capturer_global_, m); - CHECK_EXCEPTION(jni()) << "error during supportedFormatsAsJson"; - return JavaToStdString(jni(), j_json_caps); - } - - private: - JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } - - const ScopedGlobalRef j_capturer_global_; - const ScopedGlobalRef j_video_capturer_class_; - const ScopedGlobalRef j_frame_observer_class_; - jobject j_frame_observer_; - - static jobject application_context_; -}; - -jobject AndroidVideoCapturerJni::application_context_ = nullptr; - -JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeOnFrameCaptured) - (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, - jint rotation, jlong ts) { - jbyte* bytes = jni->GetByteArrayElements(j_frame, NULL); - reinterpret_cast( - j_capturer)->OnIncomingFrame(bytes, jni->GetArrayLength(j_frame), - rotation, ts); - jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT); -} - -JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeCapturerStarted) - (JNIEnv* jni, jclass, jlong j_capturer, jboolean j_success) { - reinterpret_cast( - j_capturer)->OnCapturerStarted(j_success); -} - #endif // #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) -} // anonymous namespace - static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) { jfieldID native_dc_id = GetFieldID(jni, GetObjectClass(jni, j_dc), "nativeDataChannel", "J"); @@ -2923,16 +2441,15 @@ JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)( JNIEnv* jni, jclass, jobject context, jboolean initialize_audio, jboolean initialize_video, jboolean vp8_hw_acceleration, jobject render_egl_context) { - CHECK(g_jvm) << "JNI_OnLoad failed to run?"; bool failure = false; vp8_hw_acceleration_enabled = vp8_hw_acceleration; if (!factory_static_initialized) { if (initialize_video) { - failure |= webrtc::SetRenderAndroidVM(g_jvm); + failure |= webrtc::SetRenderAndroidVM(GetJVM()); failure |= AndroidVideoCapturerJni::SetAndroidObjects(jni, context); } if (initialize_audio) - failure |= webrtc::VoiceEngine::SetAndroidObjects(g_jvm, jni, context); + failure |= webrtc::VoiceEngine::SetAndroidObjects(GetJVM(), jni, context); factory_static_initialized = true; } if (initialize_video) { @@ -2953,7 +2470,7 @@ JOW(void, PeerConnectionFactory_initializeFieldTrials)( field_trials_init_string = new char[init_string_length + 1]; rtc::strcpyn(field_trials_init_string, init_string_length + 1, init_string); jni->ReleaseStringUTFChars(j_trials_init_string, init_string); - LOG(LS_INFO) << "initializeFieldTrials: " << field_trials_init_string ; + LOG(LS_INFO) << "initializeFieldTrials: " << field_trials_init_string; } webrtc::field_trial::InitFieldTrialsFromString(field_trials_init_string); } @@ -3505,3 +3022,5 @@ JOW(void, VideoTrack_nativeRemoveRenderer)( reinterpret_cast(j_video_track_pointer)->RemoveRenderer( reinterpret_cast(j_renderer_pointer)); } + +} // namespace webrtc_jni diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 38260fbdd..3f0f6a988 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -50,7 +50,11 @@ '<(DEPTH)/third_party/icu/icu.gyp:icuuc', ], 'sources': [ - 'app/webrtc/java/jni/peerconnection_jni.cc' + 'app/webrtc/java/jni/classreferenceholder.cc', + 'app/webrtc/java/jni/classreferenceholder.h', + 'app/webrtc/java/jni/jni_helpers.cc', + 'app/webrtc/java/jni/jni_helpers.h', + 'app/webrtc/java/jni/peerconnection_jni.cc', ], 'include_dirs': [ '<(DEPTH)/third_party/libyuv/include', @@ -71,6 +75,12 @@ ], }, }], + ['OS=="android"', { + 'sources': [ + 'app/webrtc/java/jni/androidvideocapturer_jni.cc', + 'app/webrtc/java/jni/androidvideocapturer_jni.h', + ] + }], ], }, {