| /* | 
 |  *  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 <assert.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #include "absl/strings/match.h" | 
 | #include "absl/strings/string_view.h" | 
 | #include "modules/video_capture/device_info_impl.h" | 
 | #include "rtc_base/logging.h" | 
 |  | 
 | #ifndef abs | 
 | #define abs(a) (a >= 0 ? a : -a) | 
 | #endif | 
 |  | 
 | namespace webrtc { | 
 | namespace videocapturemodule { | 
 |  | 
 | DeviceInfoImpl::DeviceInfoImpl() | 
 |     : _apiLock(*RWLockWrapper::CreateRWLock()), | 
 |       _lastUsedDeviceName(NULL), | 
 |       _lastUsedDeviceNameLength(0) {} | 
 |  | 
 | DeviceInfoImpl::~DeviceInfoImpl(void) { | 
 |   _apiLock.AcquireLockExclusive(); | 
 |   free(_lastUsedDeviceName); | 
 |   _apiLock.ReleaseLockExclusive(); | 
 |  | 
 |   delete &_apiLock; | 
 | } | 
 |  | 
 | int32_t DeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) { | 
 |   if (!deviceUniqueIdUTF8) | 
 |     return -1; | 
 |  | 
 |   _apiLock.AcquireLockShared(); | 
 |  | 
 |   // Is it the same device that is asked for again. | 
 |   if (absl::EqualsIgnoreCase( | 
 |           deviceUniqueIdUTF8, | 
 |           absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) { | 
 |     _apiLock.ReleaseLockShared(); | 
 |     return static_cast<int32_t>(_captureCapabilities.size()); | 
 |   } | 
 |   // Need to get exclusive rights to create the new capability map. | 
 |   _apiLock.ReleaseLockShared(); | 
 |   WriteLockScoped cs2(_apiLock); | 
 |  | 
 |   int32_t ret = CreateCapabilityMap(deviceUniqueIdUTF8); | 
 |   return ret; | 
 | } | 
 |  | 
 | int32_t DeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8, | 
 |                                       const uint32_t deviceCapabilityNumber, | 
 |                                       VideoCaptureCapability& capability) { | 
 |   assert(deviceUniqueIdUTF8 != NULL); | 
 |  | 
 |   ReadLockScoped cs(_apiLock); | 
 |  | 
 |   if (!absl::EqualsIgnoreCase( | 
 |           deviceUniqueIdUTF8, | 
 |           absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) { | 
 |     _apiLock.ReleaseLockShared(); | 
 |     _apiLock.AcquireLockExclusive(); | 
 |     if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) { | 
 |       _apiLock.ReleaseLockExclusive(); | 
 |       _apiLock.AcquireLockShared(); | 
 |       return -1; | 
 |     } | 
 |     _apiLock.ReleaseLockExclusive(); | 
 |     _apiLock.AcquireLockShared(); | 
 |   } | 
 |  | 
 |   // Make sure the number is valid | 
 |   if (deviceCapabilityNumber >= (unsigned int)_captureCapabilities.size()) { | 
 |     RTC_LOG(LS_ERROR) << "Invalid deviceCapabilityNumber " | 
 |                       << deviceCapabilityNumber << ">= number of capabilities (" | 
 |                       << _captureCapabilities.size() << ")."; | 
 |     return -1; | 
 |   } | 
 |  | 
 |   capability = _captureCapabilities[deviceCapabilityNumber]; | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t DeviceInfoImpl::GetBestMatchedCapability( | 
 |     const char* deviceUniqueIdUTF8, | 
 |     const VideoCaptureCapability& requested, | 
 |     VideoCaptureCapability& resulting) { | 
 |   if (!deviceUniqueIdUTF8) | 
 |     return -1; | 
 |  | 
 |   ReadLockScoped cs(_apiLock); | 
 |   if (!absl::EqualsIgnoreCase( | 
 |           deviceUniqueIdUTF8, | 
 |           absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) { | 
 |     _apiLock.ReleaseLockShared(); | 
 |     _apiLock.AcquireLockExclusive(); | 
 |     if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) { | 
 |       return -1; | 
 |     } | 
 |     _apiLock.ReleaseLockExclusive(); | 
 |     _apiLock.AcquireLockShared(); | 
 |   } | 
 |  | 
 |   int32_t bestformatIndex = -1; | 
 |   int32_t bestWidth = 0; | 
 |   int32_t bestHeight = 0; | 
 |   int32_t bestFrameRate = 0; | 
 |   VideoType bestVideoType = VideoType::kUnknown; | 
 |  | 
 |   const int32_t numberOfCapabilies = | 
 |       static_cast<int32_t>(_captureCapabilities.size()); | 
 |  | 
 |   for (int32_t tmp = 0; tmp < numberOfCapabilies; | 
 |        ++tmp)  // Loop through all capabilities | 
 |   { | 
 |     VideoCaptureCapability& capability = _captureCapabilities[tmp]; | 
 |  | 
 |     const int32_t diffWidth = capability.width - requested.width; | 
 |     const int32_t diffHeight = capability.height - requested.height; | 
 |     const int32_t diffFrameRate = capability.maxFPS - requested.maxFPS; | 
 |  | 
 |     const int32_t currentbestDiffWith = bestWidth - requested.width; | 
 |     const int32_t currentbestDiffHeight = bestHeight - requested.height; | 
 |     const int32_t currentbestDiffFrameRate = bestFrameRate - requested.maxFPS; | 
 |  | 
 |     if ((diffHeight >= 0 && | 
 |          diffHeight <= abs(currentbestDiffHeight))  // Height better or equalt | 
 |                                                     // that previouse. | 
 |         || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight)) { | 
 |       if (diffHeight == | 
 |           currentbestDiffHeight)  // Found best height. Care about the width) | 
 |       { | 
 |         if ((diffWidth >= 0 && | 
 |              diffWidth <= abs(currentbestDiffWith))  // Width better or equal | 
 |             || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith)) { | 
 |           if (diffWidth == currentbestDiffWith && | 
 |               diffHeight == currentbestDiffHeight)  // Same size as previously | 
 |           { | 
 |             // Also check the best frame rate if the diff is the same as | 
 |             // previouse | 
 |             if (((diffFrameRate >= 0 && | 
 |                   diffFrameRate <= | 
 |                       currentbestDiffFrameRate)  // Frame rate to high but | 
 |                                                  // better match than previouse | 
 |                                                  // and we have not selected IUV | 
 |                  || (currentbestDiffFrameRate < 0 && | 
 |                      diffFrameRate >= | 
 |                          currentbestDiffFrameRate))  // Current frame rate is | 
 |                                                      // lower than requested. | 
 |                                                      // This is better. | 
 |                 ) { | 
 |               if ((currentbestDiffFrameRate == | 
 |                    diffFrameRate)  // Same frame rate as previous  or frame rate | 
 |                                    // allready good enough | 
 |                   || (currentbestDiffFrameRate >= 0)) { | 
 |                 if (bestVideoType != requested.videoType && | 
 |                     requested.videoType != VideoType::kUnknown && | 
 |                     (capability.videoType == requested.videoType || | 
 |                      capability.videoType == VideoType::kI420 || | 
 |                      capability.videoType == VideoType::kYUY2 || | 
 |                      capability.videoType == VideoType::kYV12)) { | 
 |                   bestVideoType = capability.videoType; | 
 |                   bestformatIndex = tmp; | 
 |                 } | 
 |                 // If width height and frame rate is full filled we can use the | 
 |                 // camera for encoding if it is supported. | 
 |                 if (capability.height == requested.height && | 
 |                     capability.width == requested.width && | 
 |                     capability.maxFPS >= requested.maxFPS) { | 
 |                   bestformatIndex = tmp; | 
 |                 } | 
 |               } else  // Better frame rate | 
 |               { | 
 |                 bestWidth = capability.width; | 
 |                 bestHeight = capability.height; | 
 |                 bestFrameRate = capability.maxFPS; | 
 |                 bestVideoType = capability.videoType; | 
 |                 bestformatIndex = tmp; | 
 |               } | 
 |             } | 
 |           } else  // Better width than previously | 
 |           { | 
 |             bestWidth = capability.width; | 
 |             bestHeight = capability.height; | 
 |             bestFrameRate = capability.maxFPS; | 
 |             bestVideoType = capability.videoType; | 
 |             bestformatIndex = tmp; | 
 |           } | 
 |         }     // else width no good | 
 |       } else  // Better height | 
 |       { | 
 |         bestWidth = capability.width; | 
 |         bestHeight = capability.height; | 
 |         bestFrameRate = capability.maxFPS; | 
 |         bestVideoType = capability.videoType; | 
 |         bestformatIndex = tmp; | 
 |       } | 
 |     }  // else height not good | 
 |   }    // end for | 
 |  | 
 |   RTC_LOG(LS_VERBOSE) << "Best camera format: " << bestWidth << "x" | 
 |                       << bestHeight << "@" << bestFrameRate | 
 |                       << "fps, color format: " | 
 |                       << static_cast<int>(bestVideoType); | 
 |  | 
 |   // Copy the capability | 
 |   if (bestformatIndex < 0) | 
 |     return -1; | 
 |   resulting = _captureCapabilities[bestformatIndex]; | 
 |   return bestformatIndex; | 
 | } | 
 |  | 
 | // Default implementation. This should be overridden by Mobile implementations. | 
 | int32_t DeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8, | 
 |                                        VideoRotation& orientation) { | 
 |   orientation = kVideoRotation_0; | 
 |   return -1; | 
 | } | 
 | }  // namespace videocapturemodule | 
 | }  // namespace webrtc |