device_info_android.cc (10868B)
1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "device_info_android.h" 12 13 #include <sstream> 14 #include <string> 15 #include <vector> 16 17 #include "modules/utility/include/helpers_android.h" 18 #include "mozilla/jni/Utils.h" 19 #include "rtc_base/logging.h" 20 21 namespace webrtc { 22 23 namespace videocapturemodule { 24 25 // Helper for storing lists of pairs of ints. Used e.g. for resolutions & FPS 26 // ranges. 27 typedef std::pair<int, int> IntPair; 28 typedef std::vector<IntPair> IntPairs; 29 30 static std::string IntPairsToString(const IntPairs& pairs, char separator) { 31 std::stringstream stream; 32 for (size_t i = 0; i < pairs.size(); ++i) { 33 if (i > 0) { 34 stream << ", "; 35 } 36 stream << "(" << pairs[i].first << separator << pairs[i].second << ")"; 37 } 38 return stream.str(); 39 } 40 41 struct AndroidCameraInfo { 42 std::string name; 43 bool front_facing; 44 int orientation; 45 IntPairs resolutions; // Pairs are: (width,height). 46 // Pairs are (min,max) in units of FPS*1000 ("milli-frame-per-second"). 47 IntPairs mfpsRanges; 48 49 std::string ToString() { 50 std::stringstream stream; 51 stream << "Name: [" << name << "], MFPS ranges: [" 52 << IntPairsToString(mfpsRanges, ':') 53 << "], front_facing: " << front_facing 54 << ", orientation: " << orientation << ", resolutions: [" 55 << IntPairsToString(resolutions, 'x') << "]"; 56 return stream.str(); 57 } 58 }; 59 60 // Camera info; populated during DeviceInfoAndroid::Refresh() 61 static std::vector<AndroidCameraInfo>* g_camera_info = NULL; 62 63 static JavaVM* g_jvm_dev_info = NULL; 64 65 // Set |*index| to the index of |name| in g_camera_info or return false if no 66 // match found. 67 static bool FindCameraIndexByName(const std::string& name, size_t* index) { 68 for (size_t i = 0; i < g_camera_info->size(); ++i) { 69 if (g_camera_info->at(i).name == name) { 70 *index = i; 71 return true; 72 } 73 } 74 return false; 75 } 76 77 // Returns a pointer to the named member of g_camera_info, or NULL if no match 78 // is found. 79 static AndroidCameraInfo* FindCameraInfoByName(const std::string& name) { 80 size_t index = 0; 81 if (FindCameraIndexByName(name, &index)) { 82 return &g_camera_info->at(index); 83 } 84 return NULL; 85 } 86 87 // static 88 void DeviceInfoAndroid::Initialize(JavaVM* javaVM) { 89 // TODO(henrike): this "if" would make a lot more sense as an assert, but 90 // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_GetVideoEngine() and 91 // Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_Terminate() conspire to 92 // prevent this. Once that code is made to only 93 // VideoEngine::SetAndroidObjects() once per process, this can turn into an 94 // assert. 95 if (g_camera_info) { 96 return; 97 } 98 99 g_jvm_dev_info = javaVM; 100 BuildDeviceList(); 101 } 102 103 void DeviceInfoAndroid::BuildDeviceList() { 104 if (!g_jvm_dev_info) { 105 return; 106 } 107 108 AttachThreadScoped ats(g_jvm_dev_info); 109 JNIEnv* jni = ats.env(); 110 111 g_camera_info = new std::vector<AndroidCameraInfo>(); 112 jclass j_info_class = mozilla::jni::GetClassRef( 113 jni, "org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid"); 114 jclass j_cap_class = mozilla::jni::GetClassRef( 115 jni, "org/webrtc/videoengine/CaptureCapabilityAndroid"); 116 assert(j_info_class); 117 jmethodID j_get_device_info = jni->GetStaticMethodID( 118 j_info_class, "getDeviceInfo", 119 "()[Lorg/webrtc/videoengine/CaptureCapabilityAndroid;"); 120 jarray j_camera_caps = static_cast<jarray>( 121 jni->CallStaticObjectMethod(j_info_class, j_get_device_info)); 122 if (jni->ExceptionCheck()) { 123 jni->ExceptionClear(); 124 RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get camera capabilities."; 125 return; 126 } 127 if (j_camera_caps == nullptr) { 128 RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get camera capabilities."; 129 return; 130 } 131 132 const jsize capLength = jni->GetArrayLength(j_camera_caps); 133 134 jfieldID widthField = jni->GetFieldID(j_cap_class, "width", "[I"); 135 jfieldID heightField = jni->GetFieldID(j_cap_class, "height", "[I"); 136 jfieldID maxFpsField = jni->GetFieldID(j_cap_class, "maxMilliFPS", "I"); 137 jfieldID minFpsField = jni->GetFieldID(j_cap_class, "minMilliFPS", "I"); 138 jfieldID orientationField = jni->GetFieldID(j_cap_class, "orientation", "I"); 139 jfieldID frontFacingField = jni->GetFieldID(j_cap_class, "frontFacing", "Z"); 140 jfieldID nameField = 141 jni->GetFieldID(j_cap_class, "name", "Ljava/lang/String;"); 142 if (widthField == NULL || heightField == NULL || maxFpsField == NULL || 143 minFpsField == NULL || orientationField == NULL || 144 frontFacingField == NULL || nameField == NULL) { 145 RTC_LOG(LS_INFO) << __FUNCTION__ << ": Failed to get field Id."; 146 return; 147 } 148 149 for (jsize i = 0; i < capLength; i++) { 150 jobject capabilityElement = 151 jni->GetObjectArrayElement((jobjectArray)j_camera_caps, i); 152 153 AndroidCameraInfo info; 154 jstring camName = 155 static_cast<jstring>(jni->GetObjectField(capabilityElement, nameField)); 156 const char* camChars = jni->GetStringUTFChars(camName, nullptr); 157 info.name = std::string(camChars); 158 jni->ReleaseStringUTFChars(camName, camChars); 159 160 info.orientation = jni->GetIntField(capabilityElement, orientationField); 161 info.front_facing = 162 jni->GetBooleanField(capabilityElement, frontFacingField); 163 jint min_mfps = jni->GetIntField(capabilityElement, minFpsField); 164 jint max_mfps = jni->GetIntField(capabilityElement, maxFpsField); 165 166 jintArray widthResArray = static_cast<jintArray>( 167 jni->GetObjectField(capabilityElement, widthField)); 168 jintArray heightResArray = static_cast<jintArray>( 169 jni->GetObjectField(capabilityElement, heightField)); 170 171 const jsize numRes = jni->GetArrayLength(widthResArray); 172 173 jint* widths = jni->GetIntArrayElements(widthResArray, nullptr); 174 jint* heights = jni->GetIntArrayElements(heightResArray, nullptr); 175 176 for (jsize j = 0; j < numRes; ++j) { 177 info.resolutions.push_back(std::make_pair(widths[j], heights[j])); 178 } 179 180 info.mfpsRanges.push_back(std::make_pair(min_mfps, max_mfps)); 181 g_camera_info->push_back(info); 182 183 jni->ReleaseIntArrayElements(widthResArray, widths, JNI_ABORT); 184 jni->ReleaseIntArrayElements(heightResArray, heights, JNI_ABORT); 185 } 186 187 jni->DeleteLocalRef(j_info_class); 188 jni->DeleteLocalRef(j_cap_class); 189 } 190 191 void DeviceInfoAndroid::DeInitialize() { 192 if (g_camera_info) { 193 delete g_camera_info; 194 g_camera_info = NULL; 195 } 196 } 197 198 int32_t DeviceInfoAndroid::Refresh() { 199 if (!g_camera_info || g_camera_info->size() == 0) { 200 DeviceInfoAndroid::BuildDeviceList(); 201 #ifdef DEBUG 202 int frontFacingIndex = -1; 203 for (uint32_t i = 0; i < g_camera_info->size(); i++) { 204 if (g_camera_info->at(i).front_facing) { 205 frontFacingIndex = i; 206 } 207 } 208 // Either there is a front-facing camera, and it's first in the list, or 209 // there is no front-facing camera. 210 MOZ_ASSERT(frontFacingIndex == 0 || frontFacingIndex == -1); 211 #endif 212 } 213 return 0; 214 } 215 216 VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() { 217 return new videocapturemodule::DeviceInfoAndroid(); 218 } 219 220 DeviceInfoAndroid::DeviceInfoAndroid() : DeviceInfoImpl() {} 221 222 DeviceInfoAndroid::~DeviceInfoAndroid() {} 223 224 bool DeviceInfoAndroid::FindCameraIndex(const char* deviceUniqueIdUTF8, 225 size_t* index) { 226 return FindCameraIndexByName(deviceUniqueIdUTF8, index); 227 } 228 229 int32_t DeviceInfoAndroid::Init() { return 0; } 230 231 uint32_t DeviceInfoAndroid::NumberOfDevices() { 232 Refresh(); 233 return g_camera_info->size(); 234 } 235 236 int32_t DeviceInfoAndroid::GetDeviceName( 237 uint32_t deviceNumber, char* deviceNameUTF8, uint32_t deviceNameLength, 238 char* deviceUniqueIdUTF8, uint32_t deviceUniqueIdUTF8Length, 239 char* /*productUniqueIdUTF8*/, uint32_t /*productUniqueIdUTF8Length*/, 240 pid_t* /*pid*/, bool* /*deviceIsPlaceholder*/) { 241 if (deviceNumber >= g_camera_info->size()) { 242 return -1; 243 } 244 const AndroidCameraInfo& info = g_camera_info->at(deviceNumber); 245 if (info.name.length() + 1 > deviceNameLength || 246 info.name.length() + 1 > deviceUniqueIdUTF8Length) { 247 return -1; 248 } 249 memcpy(deviceNameUTF8, info.name.c_str(), info.name.length() + 1); 250 memcpy(deviceUniqueIdUTF8, info.name.c_str(), info.name.length() + 1); 251 return 0; 252 } 253 254 int32_t DeviceInfoAndroid::CreateCapabilityMap(const char* deviceUniqueIdUTF8) { 255 _captureCapabilities.clear(); 256 const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); 257 if (info == NULL) { 258 return -1; 259 } 260 261 for (size_t i = 0; i < info->resolutions.size(); ++i) { 262 for (size_t j = 0; j < info->mfpsRanges.size(); ++j) { 263 const IntPair& size = info->resolutions[i]; 264 const IntPair& mfpsRange = info->mfpsRanges[j]; 265 VideoCaptureCapability cap; 266 cap.width = size.first; 267 cap.height = size.second; 268 cap.maxFPS = mfpsRange.second / 1000; 269 cap.videoType = VideoType::kNV21; 270 _captureCapabilities.push_back(cap); 271 } 272 } 273 return _captureCapabilities.size(); 274 } 275 276 int32_t DeviceInfoAndroid::GetOrientation(const char* deviceUniqueIdUTF8, 277 VideoRotation& orientation) { 278 const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); 279 if (info == NULL || VideoCaptureImpl::RotationFromDegrees( 280 info->orientation, &orientation) != 0) { 281 return -1; 282 } 283 return 0; 284 } 285 286 void DeviceInfoAndroid::GetMFpsRange(const char* deviceUniqueIdUTF8, 287 int max_fps_to_match, int* min_mfps, 288 int* max_mfps) { 289 const AndroidCameraInfo* info = FindCameraInfoByName(deviceUniqueIdUTF8); 290 if (info == NULL) { 291 return; 292 } 293 int desired_mfps = max_fps_to_match * 1000; 294 int best_diff_mfps = 0; 295 RTC_LOG(LS_INFO) << "Search for best target mfps " << desired_mfps; 296 // Search for best fps range with preference shifted to constant fps modes. 297 for (size_t i = 0; i < info->mfpsRanges.size(); ++i) { 298 int diff_mfps = 299 abs(info->mfpsRanges[i].first - desired_mfps) + 300 abs(info->mfpsRanges[i].second - desired_mfps) + 301 (info->mfpsRanges[i].second - info->mfpsRanges[i].first) / 2; 302 RTC_LOG(LS_INFO) << "Fps range " << info->mfpsRanges[i].first << ":" 303 << info->mfpsRanges[i].second 304 << ". Distance: " << diff_mfps; 305 if (i == 0 || diff_mfps < best_diff_mfps) { 306 best_diff_mfps = diff_mfps; 307 *min_mfps = info->mfpsRanges[i].first; 308 *max_mfps = info->mfpsRanges[i].second; 309 } 310 } 311 } 312 313 } // namespace videocapturemodule 314 } // namespace webrtc