summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/android_video_capture/video_capture_android.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/android_video_capture/video_capture_android.cc')
-rw-r--r--dom/media/systemservices/android_video_capture/video_capture_android.cc270
1 files changed, 270 insertions, 0 deletions
diff --git a/dom/media/systemservices/android_video_capture/video_capture_android.cc b/dom/media/systemservices/android_video_capture/video_capture_android.cc
new file mode 100644
index 0000000000..7c9cd72ccb
--- /dev/null
+++ b/dom/media/systemservices/android_video_capture/video_capture_android.cc
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "video_capture_android.h"
+
+#include "device_info_android.h"
+#include "modules/utility/include/helpers_android.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/ref_counted_object.h"
+#include "rtc_base/time_utils.h"
+
+#include "AndroidBridge.h"
+
+static JavaVM* g_jvm_capture = NULL;
+static jclass g_java_capturer_class = NULL; // VideoCaptureAndroid.class.
+static jobject g_context = NULL; // Owned android.content.Context.
+
+namespace webrtc {
+
+jobject JniCommon_allocateNativeByteBuffer(JNIEnv* env, jclass, jint size) {
+ void* new_data = ::operator new(size);
+ jobject byte_buffer = env->NewDirectByteBuffer(new_data, size);
+ return byte_buffer;
+}
+
+void JniCommon_freeNativeByteBuffer(JNIEnv* env, jclass, jobject byte_buffer) {
+ void* data = env->GetDirectBufferAddress(byte_buffer);
+ ::operator delete(data);
+}
+
+// Called by Java to get the global application context.
+jobject JNICALL GetContext(JNIEnv* env, jclass) {
+ assert(g_context);
+ return g_context;
+}
+
+// Called by Java when the camera has a new frame to deliver.
+void JNICALL ProvideCameraFrame(JNIEnv* env, jobject, jint width, jint height,
+ jobject javaDataY, jint strideY,
+ jobject javaDataU, jint strideU,
+ jobject javaDataV, jint strideV, jint rotation,
+ jlong timeStamp, jlong context) {
+ if (!context) {
+ return;
+ }
+
+ webrtc::videocapturemodule::VideoCaptureAndroid* captureModule =
+ reinterpret_cast<webrtc::videocapturemodule::VideoCaptureAndroid*>(
+ context);
+ uint8_t* dataY =
+ reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(javaDataY));
+ uint8_t* dataU =
+ reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(javaDataU));
+ uint8_t* dataV =
+ reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(javaDataV));
+
+ rtc::scoped_refptr<I420Buffer> i420Buffer = I420Buffer::Copy(
+ width, height, dataY, strideY, dataU, strideU, dataV, strideV);
+
+ captureModule->OnIncomingFrame(i420Buffer, rotation, timeStamp);
+}
+
+int32_t SetCaptureAndroidVM(JavaVM* javaVM) {
+ if (g_java_capturer_class) {
+ return 0;
+ }
+
+ if (javaVM) {
+ assert(!g_jvm_capture);
+ g_jvm_capture = javaVM;
+ AttachThreadScoped ats(g_jvm_capture);
+
+ g_context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
+
+ videocapturemodule::DeviceInfoAndroid::Initialize(g_jvm_capture);
+
+ {
+ jclass clsRef = mozilla::jni::GetClassRef(
+ ats.env(), "org/webrtc/videoengine/VideoCaptureAndroid");
+ g_java_capturer_class =
+ static_cast<jclass>(ats.env()->NewGlobalRef(clsRef));
+ ats.env()->DeleteLocalRef(clsRef);
+ assert(g_java_capturer_class);
+
+ JNINativeMethod native_methods[] = {
+ {"GetContext", "()Landroid/content/Context;",
+ reinterpret_cast<void*>(&GetContext)},
+ {"ProvideCameraFrame",
+ "(IILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;ILjava/nio/"
+ "ByteBuffer;IIJJ)V",
+ reinterpret_cast<void*>(&ProvideCameraFrame)}};
+ if (ats.env()->RegisterNatives(g_java_capturer_class, native_methods,
+ 2) != 0)
+ assert(false);
+ }
+
+ {
+ jclass clsRef =
+ mozilla::jni::GetClassRef(ats.env(), "org/webrtc/JniCommon");
+
+ JNINativeMethod native_methods[] = {
+ {"nativeAllocateByteBuffer", "(I)Ljava/nio/ByteBuffer;",
+ reinterpret_cast<void*>(&JniCommon_allocateNativeByteBuffer)},
+ {"nativeFreeByteBuffer", "(Ljava/nio/ByteBuffer;)V",
+ reinterpret_cast<void*>(&JniCommon_freeNativeByteBuffer)}};
+ if (ats.env()->RegisterNatives(clsRef, native_methods, 2) != 0)
+ assert(false);
+ }
+ } else {
+ if (g_jvm_capture) {
+ AttachThreadScoped ats(g_jvm_capture);
+ ats.env()->UnregisterNatives(g_java_capturer_class);
+ ats.env()->DeleteGlobalRef(g_java_capturer_class);
+ g_java_capturer_class = NULL;
+ g_context = NULL;
+ videocapturemodule::DeviceInfoAndroid::DeInitialize();
+ g_jvm_capture = NULL;
+ }
+ }
+
+ return 0;
+}
+
+namespace videocapturemodule {
+
+rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(
+ const char* deviceUniqueIdUTF8) {
+ rtc::scoped_refptr<VideoCaptureAndroid> implementation(
+ new rtc::RefCountedObject<VideoCaptureAndroid>());
+ if (implementation->Init(deviceUniqueIdUTF8) != 0) {
+ implementation = nullptr;
+ }
+ return implementation;
+}
+
+void VideoCaptureAndroid::OnIncomingFrame(rtc::scoped_refptr<I420Buffer> buffer,
+ int32_t degrees,
+ int64_t captureTime) {
+ MutexLock lock(&api_lock_);
+
+ VideoRotation rotation =
+ (degrees <= 45 || degrees > 315) ? kVideoRotation_0
+ : (degrees > 45 && degrees <= 135) ? kVideoRotation_90
+ : (degrees > 135 && degrees <= 225) ? kVideoRotation_180
+ : (degrees > 225 && degrees <= 315) ? kVideoRotation_270
+ : kVideoRotation_0; // Impossible.
+
+ // Historically, we have ignored captureTime. Why?
+ VideoFrame captureFrame(I420Buffer::Rotate(*buffer, rotation), 0,
+ rtc::TimeMillis(), rotation);
+
+ DeliverCapturedFrame(captureFrame);
+}
+
+VideoCaptureAndroid::VideoCaptureAndroid()
+ : VideoCaptureImpl(),
+ _deviceInfo(),
+ _jCapturer(NULL),
+ _captureStarted(false) {}
+
+int32_t VideoCaptureAndroid::Init(const char* deviceUniqueIdUTF8) {
+ const int nameLength = strlen(deviceUniqueIdUTF8);
+ if (nameLength >= kVideoCaptureUniqueNameLength) return -1;
+
+ // Store the device name
+ RTC_LOG(LS_INFO) << "VideoCaptureAndroid::Init: " << deviceUniqueIdUTF8;
+ _deviceUniqueId = new char[nameLength + 1];
+ memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);
+
+ AttachThreadScoped ats(g_jvm_capture);
+ JNIEnv* env = ats.env();
+ jmethodID ctor = env->GetMethodID(g_java_capturer_class, "<init>",
+ "(Ljava/lang/String;)V");
+ assert(ctor);
+ jstring j_deviceName = env->NewStringUTF(_deviceUniqueId);
+ _jCapturer = env->NewGlobalRef(
+ env->NewObject(g_java_capturer_class, ctor, j_deviceName));
+ assert(_jCapturer);
+ return 0;
+}
+
+VideoCaptureAndroid::~VideoCaptureAndroid() {
+ // Ensure Java camera is released even if our caller didn't explicitly Stop.
+ if (_captureStarted) StopCapture();
+ AttachThreadScoped ats(g_jvm_capture);
+ JNIEnv* env = ats.env();
+ env->DeleteGlobalRef(_jCapturer);
+}
+
+int32_t VideoCaptureAndroid::StartCapture(
+ const VideoCaptureCapability& capability) {
+ AttachThreadScoped ats(g_jvm_capture);
+ JNIEnv* env = ats.env();
+ int width = 0;
+ int height = 0;
+ int min_mfps = 0;
+ int max_mfps = 0;
+ {
+ MutexLock lock(&api_lock_);
+
+ if (_deviceInfo.GetBestMatchedCapability(_deviceUniqueId, capability,
+ _captureCapability) < 0) {
+ RTC_LOG(LS_ERROR) << __FUNCTION__
+ << "s: GetBestMatchedCapability failed: "
+ << capability.width << "x" << capability.height;
+ return -1;
+ }
+
+ width = _captureCapability.width;
+ height = _captureCapability.height;
+ _deviceInfo.GetMFpsRange(_deviceUniqueId, _captureCapability.maxFPS,
+ &min_mfps, &max_mfps);
+
+ // Exit critical section to avoid blocking camera thread inside
+ // onIncomingFrame() call.
+ }
+
+ jmethodID j_start =
+ env->GetMethodID(g_java_capturer_class, "startCapture", "(IIIIJ)Z");
+ assert(j_start);
+ jlong j_this = reinterpret_cast<intptr_t>(this);
+ bool started = env->CallBooleanMethod(_jCapturer, j_start, width, height,
+ min_mfps, max_mfps, j_this);
+ if (started) {
+ MutexLock lock(&api_lock_);
+ _requestedCapability = capability;
+ _captureStarted = true;
+ }
+ return started ? 0 : -1;
+}
+
+int32_t VideoCaptureAndroid::StopCapture() {
+ AttachThreadScoped ats(g_jvm_capture);
+ JNIEnv* env = ats.env();
+ {
+ MutexLock lock(&api_lock_);
+
+ memset(&_requestedCapability, 0, sizeof(_requestedCapability));
+ memset(&_captureCapability, 0, sizeof(_captureCapability));
+ _captureStarted = false;
+ // Exit critical section to avoid blocking camera thread inside
+ // onIncomingFrame() call.
+ }
+
+ // try to stop the capturer.
+ jmethodID j_stop =
+ env->GetMethodID(g_java_capturer_class, "stopCapture", "()Z");
+ return env->CallBooleanMethod(_jCapturer, j_stop) ? 0 : -1;
+}
+
+bool VideoCaptureAndroid::CaptureStarted() {
+ MutexLock lock(&api_lock_);
+ return _captureStarted;
+}
+
+int32_t VideoCaptureAndroid::CaptureSettings(VideoCaptureCapability& settings) {
+ MutexLock lock(&api_lock_);
+ settings = _requestedCapability;
+ return 0;
+}
+
+} // namespace videocapturemodule
+} // namespace webrtc