| /* |
| * 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 |