/* * 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( context); uint8_t* dataY = reinterpret_cast(env->GetDirectBufferAddress(javaDataY)); uint8_t* dataU = reinterpret_cast(env->GetDirectBufferAddress(javaDataU)); uint8_t* dataV = reinterpret_cast(env->GetDirectBufferAddress(javaDataV)); rtc::scoped_refptr 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(ats.env()->NewGlobalRef(clsRef)); ats.env()->DeleteLocalRef(clsRef); assert(g_java_capturer_class); JNINativeMethod native_methods[] = { {"GetContext", "()Landroid/content/Context;", reinterpret_cast(&GetContext)}, {"ProvideCameraFrame", "(IILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;ILjava/nio/" "ByteBuffer;IIJJ)V", reinterpret_cast(&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(&JniCommon_allocateNativeByteBuffer)}, {"nativeFreeByteBuffer", "(Ljava/nio/ByteBuffer;)V", reinterpret_cast(&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 VideoCaptureImpl::Create( const char* deviceUniqueIdUTF8) { rtc::scoped_refptr implementation( new rtc::RefCountedObject()); if (implementation->Init(deviceUniqueIdUTF8) != 0) { implementation = nullptr; } return implementation; } void VideoCaptureAndroid::OnIncomingFrame(rtc::scoped_refptr 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, "", "(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(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