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/device_info_android.cc | 316 +++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 dom/media/systemservices/android_video_capture/device_info_android.cc (limited to 'dom/media/systemservices/android_video_capture/device_info_android.cc') diff --git a/dom/media/systemservices/android_video_capture/device_info_android.cc b/dom/media/systemservices/android_video_capture/device_info_android.cc new file mode 100644 index 0000000000..581040eb94 --- /dev/null +++ b/dom/media/systemservices/android_video_capture/device_info_android.cc @@ -0,0 +1,316 @@ +/* + * 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 "device_info_android.h" + +#include +#include +#include +#include + +#include "rtc_base/logging.h" +#include "modules/utility/include/helpers_android.h" + +#include "mozilla/jni/Utils.h" + +namespace webrtc { + +namespace videocapturemodule { + +// Helper for storing lists of pairs of ints. Used e.g. for resolutions & FPS +// ranges. +typedef std::pair IntPair; +typedef std::vector IntPairs; + +static std::string IntPairsToString(const IntPairs& pairs, char separator) { + std::stringstream stream; + for (size_t i = 0; i < pairs.size(); ++i) { + if (i > 0) { + stream << ", "; + } + stream << "(" << pairs[i].first << separator << pairs[i].second << ")"; + } + return stream.str(); +} + +struct AndroidCameraInfo { + std::string name; + bool front_facing; + int orientation; + IntPairs resolutions; // Pairs are: (width,height). + // Pairs are (min,max) in units of FPS*1000 ("milli-frame-per-second"). + IntPairs mfpsRanges; + + std::string ToString() { + std::stringstream stream; + stream << "Name: [" << name << "], MFPS ranges: [" + << IntPairsToString(mfpsRanges, ':') + << "], front_facing: " << front_facing + << ", orientation: " << orientation << ", resolutions: [" + << IntPairsToString(resolutions, 'x') << "]"; + return stream.str(); + } +}; + +// Camera info; populated during DeviceInfoAndroid::Refresh() +static std::vector* g_camera_info = NULL; + +static JavaVM* g_jvm_dev_info = NULL; + +// Set |*index| to the index of |name| in g_camera_info or return false if no +// match found. +static bool FindCameraIndexByName(const std::string& name, size_t* index) { + for (size_t i = 0; i < g_camera_info->size(); ++i) { + if (g_camera_info->at(i).name == name) { + *index = i; + return true; + } + } + return false; +} + +// Returns a pointer to the named member of g_camera_info, or NULL if no match +// is found. +static AndroidCameraInfo* FindCameraInfoByName(const std::string& name) { + size_t index = 0; + if (FindCameraIndexByName(name, &index)) { + return &g_camera_info->at(index); + } + return NULL; +} + +// static +void DeviceInfoAndroid::Initialize(JavaVM* javaVM) { + // TODO(henrike): this "if" would make a lot more sense as an assert, but + // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetVideoEngine() and + // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Terminate() conspire to + // prevent this. Once that code is made to only + // VideoEngine::SetAndroidObjects() once per process, this can turn into an + // assert. + if (g_camera_info) { + return; + } + + g_jvm_dev_info = javaVM; + BuildDeviceList(); +} + +void DeviceInfoAndroid::BuildDeviceList() { + if (!g_jvm_dev_info) { + return; + } + + AttachThreadScoped ats(g_jvm_dev_info); + JNIEnv* jni = ats.env(); + + g_camera_info = new std::vector(); + jclass j_info_class = mozilla::jni::GetClassRef( + jni, "org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid"); + jclass j_cap_class = mozilla::jni::GetClassRef( + jni, "org/webrtc/videoengine/CaptureCapabilityAndroid"); + assert(j_info_class); + jmethodID j_get_device_info = jni->GetStaticMethodID( + j_info_class, "getDeviceInfo", + "()[Lorg/webrtc/videoengine/CaptureCapabilityAndroid;"); + jarray j_camera_caps = static_cast( + jni->CallStaticObjectMethod(j_info_class, j_get_device_info)); + if (jni->ExceptionCheck()) { + jni->ExceptionClear(); + RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get camera capabilities."; + return; + } + if (j_camera_caps == nullptr) { + RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get camera capabilities."; + return; + } + + const jsize capLength = jni->GetArrayLength(j_camera_caps); + + jfieldID widthField = jni->GetFieldID(j_cap_class, "width", "[I"); + jfieldID heightField = jni->GetFieldID(j_cap_class, "height", "[I"); + jfieldID maxFpsField = jni->GetFieldID(j_cap_class, "maxMilliFPS", "I"); + jfieldID minFpsField = jni->GetFieldID(j_cap_class, "minMilliFPS", "I"); + jfieldID orientationField = jni->GetFieldID(j_cap_class, "orientation", "I"); + jfieldID frontFacingField = jni->GetFieldID(j_cap_class, "frontFacing", "Z"); + jfieldID nameField = + jni->GetFieldID(j_cap_class, "name", "Ljava/lang/String;"); + if (widthField == NULL || heightField == NULL || maxFpsField == NULL || + minFpsField == NULL || orientationField == NULL || + frontFacingField == NULL || nameField == NULL) { + RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get field Id."; + return; + } + + for (jsize i = 0; i < capLength; i++) { + jobject capabilityElement = + jni->GetObjectArrayElement((jobjectArray)j_camera_caps, i); + + AndroidCameraInfo info; + jstring camName = + static_cast(jni->GetObjectField(capabilityElement, nameField)); + const char* camChars = jni->GetStringUTFChars(camName, nullptr); + info.name = std::string(camChars); + jni->ReleaseStringUTFChars(camName, camChars); + + info.orientation = jni->GetIntField(capabilityElement, orientationField); + info.front_facing = + jni->GetBooleanField(capabilityElement, frontFacingField); + jint min_mfps = jni->GetIntField(capabilityElement, minFpsField); + jint max_mfps = jni->GetIntField(capabilityElement, maxFpsField); + + jintArray widthResArray = static_cast( + jni->GetObjectField(capabilityElement, widthField)); + jintArray heightResArray = static_cast( + jni->GetObjectField(capabilityElement, heightField)); + + const jsize numRes = jni->GetArrayLength(widthResArray); + + jint* widths = jni->GetIntArrayElements(widthResArray, nullptr); + jint* heights = jni->GetIntArrayElements(heightResArray, nullptr); + + for (jsize j = 0; j < numRes; ++j) { + info.resolutions.push_back(std::make_pair(widths[j], heights[j])); + } + + info.mfpsRanges.push_back(std::make_pair(min_mfps, max_mfps)); + g_camera_info->push_back(info); + + jni->ReleaseIntArrayElements(widthResArray, widths, JNI_ABORT); + jni->ReleaseIntArrayElements(heightResArray, heights, JNI_ABORT); + } + + jni->DeleteLocalRef(j_info_class); + jni->DeleteLocalRef(j_cap_class); +} + +void DeviceInfoAndroid::DeInitialize() { + if (g_camera_info) { + delete g_camera_info; + g_camera_info = NULL; + } +} + +int32_t DeviceInfoAndroid::Refresh() { + if (!g_camera_info || g_camera_info->size() == 0) { + DeviceInfoAndroid::BuildDeviceList(); +#ifdef DEBUG + int frontFacingIndex = -1; + for (uint32_t i = 0; i < g_camera_info->size(); i++) { + if (g_camera_info->at(i).front_facing) { + frontFacingIndex = i; + } + } + // Either there is a front-facing camera, and it's first in the list, or + // there is no front-facing camera. + MOZ_ASSERT(frontFacingIndex == 0 || frontFacingIndex == -1); +#endif + } + return 0; +} + +VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() { + return new videocapturemodule::DeviceInfoAndroid(); +} + +DeviceInfoAndroid::DeviceInfoAndroid() : DeviceInfoImpl() {} + +DeviceInfoAndroid::~DeviceInfoAndroid() {} + +bool DeviceInfoAndroid::FindCameraIndex(const char* deviceUniqueIdUTF8, + size_t* index) { + return FindCameraIndexByName(deviceUniqueIdUTF8, index); +} + +int32_t DeviceInfoAndroid::Init() { return 0; } + +uint32_t DeviceInfoAndroid::NumberOfDevices() { + Refresh(); + return g_camera_info->size(); +} + +int32_t DeviceInfoAndroid::GetDeviceName( + uint32_t deviceNumber, char* deviceNameUTF8, uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, uint32_t deviceUniqueIdUTF8Length, + char* /*productUniqueIdUTF8*/, uint32_t /*productUniqueIdUTF8Length*/, + pid_t* /*pid*/) { + if (deviceNumber >= g_camera_info->size()) { + return -1; + } + const AndroidCameraInfo& info = g_camera_info->at(deviceNumber); + if (info.name.length() + 1 > deviceNameLength || + info.name.length() + 1 > deviceUniqueIdUTF8Length) { + return -1; + } + memcpy(deviceNameUTF8, info.name.c_str(), info.name.length() + 1); + memcpy(deviceUniqueIdUTF8, info.name.c_str(), info.name.length() + 1); + return 0; +} + +int32_t DeviceInfoAndroid::CreateCapabilityMap(const char* deviceUniqueIdUTF8) { + _captureCapabilities.clear(); + const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); + if (info == NULL) { + return -1; + } + + for (size_t i = 0; i < info->resolutions.size(); ++i) { + for (size_t j = 0; j < info->mfpsRanges.size(); ++j) { + const IntPair& size = info->resolutions[i]; + const IntPair& mfpsRange = info->mfpsRanges[j]; + VideoCaptureCapability cap; + cap.width = size.first; + cap.height = size.second; + cap.maxFPS = mfpsRange.second / 1000; + cap.videoType = VideoType::kNV21; + _captureCapabilities.push_back(cap); + } + } + return _captureCapabilities.size(); +} + +int32_t DeviceInfoAndroid::GetOrientation(const char* deviceUniqueIdUTF8, + VideoRotation& orientation) { + const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); + if (info == NULL || VideoCaptureImpl::RotationFromDegrees( + info->orientation, &orientation) != 0) { + return -1; + } + return 0; +} + +void DeviceInfoAndroid::GetMFpsRange(const char* deviceUniqueIdUTF8, + int max_fps_to_match, int* min_mfps, + int* max_mfps) { + const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); + if (info == NULL) { + return; + } + int desired_mfps = max_fps_to_match * 1000; + int best_diff_mfps = 0; + RTC_LOG(LS_INFO) << "Search for best target mfps " << desired_mfps; + // Search for best fps range with preference shifted to constant fps modes. + for (size_t i = 0; i < info->mfpsRanges.size(); ++i) { + int diff_mfps = + abs(info->mfpsRanges[i].first - desired_mfps) + + abs(info->mfpsRanges[i].second - desired_mfps) + + (info->mfpsRanges[i].second - info->mfpsRanges[i].first) / 2; + RTC_LOG(LS_INFO) << "Fps range " << info->mfpsRanges[i].first << ":" + << info->mfpsRanges[i].second + << ". Distance: " << diff_mfps; + if (i == 0 || diff_mfps < best_diff_mfps) { + best_diff_mfps = diff_mfps; + *min_mfps = info->mfpsRanges[i].first; + *max_mfps = info->mfpsRanges[i].second; + } + } +} + +} // namespace videocapturemodule +} // namespace webrtc -- cgit v1.2.3