From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../android_video_capture/video_capture_android.cc | 270 +++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 dom/media/systemservices/android_video_capture/video_capture_android.cc (limited to 'dom/media/systemservices/android_video_capture/video_capture_android.cc') 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( + 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 -- cgit v1.2.3