Split peerconnection_jni.cc into separate files.
For now:
java_helpers - JNI convenience functions etc. Can in theory be moved to libjingle / webrtc general one day.
classreferenceholder - app/webrtc specific Java class loader.
androidvideocapturer_jni - the jni part of the video capturer I added.
peerconnection_jni - all the rest.
This also move all jni specifics into ns webrtc_jni to avoid naming collision.
R=glaznev@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/38099004
Cr-Commit-Position: refs/heads/master@{#8363}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8363 4adac7df-926f-26a2-2b94-8c16560cd09d
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 0000000..4f575b1
--- /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_,
+ "<init>",
+ "(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<webrtc::AndroidVideoCapturer*>(
+ 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<webrtc::AndroidVideoCapturer*>(
+ 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 0000000..846905a
--- /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 <string>
+
+#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<jobject> j_capturer_global_;
+ const ScopedGlobalRef<jclass> j_video_capturer_class_;
+ const ScopedGlobalRef<jclass> 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 0000000..25a1101
--- /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<std::string, jclass> 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<std::string, jclass>::const_iterator it = classes_.begin();
+ it != classes_.end(); ++it) {
+ jni->DeleteGlobalRef(it->second);
+ }
+ classes_.clear();
+}
+
+jclass ClassReferenceHolder::GetClass(const std::string& name) {
+ std::map<std::string, jclass>::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<jclass>(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 0000000..46e018b
--- /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 <jni.h>
+#include <string>
+#include <map>
+
+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 0000000..c4e6e67
--- /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 <asm/unistd.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+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<JNIEnv*>(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<void**>(&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<JNIEnv*>(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<intptr_t>(ptr);
+ DCHECK(reinterpret_cast<void*>(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<jstring>(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<jobjectArray>(
+ 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 0000000..bfbde78
--- /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 <jni.h>
+#include <string>
+
+#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<class T> // T is jclass, jobject, jintArray, etc.
+class ScopedGlobalRef {
+ public:
+ ScopedGlobalRef(JNIEnv* jni, T obj)
+ : obj_(static_cast<T>(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 9c8d708..bddd417 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 <asm/unistd.h>
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <unistd.h>
#include <limits>
-#include <map>
+#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 <android/log.h>
#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::VideoCodec;
#endif
-using icu::UnicodeString;
using rtc::Bind;
using rtc::Thread;
using rtc::ThreadManager;
@@ -139,28 +136,7 @@
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 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<JNIEnv*>(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<JNIEnv*>(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<intptr_t>(ptr);
- assert(reinterpret_cast<void*>(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<std::string, jclass>::const_iterator it = classes_.begin();
- it != classes_.end(); ++it) {
- jni->DeleteGlobalRef(it->second);
- }
- classes_.clear();
- }
-
- jclass GetClass(const std::string& name) {
- std::map<std::string, jclass>::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<jclass>(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<std::string, jclass> 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<jstring>(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<void**>(&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<class T> // T is jclass, jobject, jintArray, etc.
-class ScopedGlobalRef {
- public:
- ScopedGlobalRef(JNIEnv* jni, T obj)
- : obj_(static_cast<T>(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<jobjectArray>(
- 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_,
- "<init>",
- "(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<jobject> j_capturer_global_;
- const ScopedGlobalRef<jclass> j_video_capturer_class_;
- const ScopedGlobalRef<jclass> 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<webrtc::AndroidVideoCapturer*>(
- 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<webrtc::AndroidVideoCapturer*>(
- 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 @@
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 @@
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 @@
reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->RemoveRenderer(
reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
}
+
+} // namespace webrtc_jni