/* * 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 #include #include "webrtc/modules/video_capture/device_info_impl.h" #include "webrtc/modules/video_capture/video_capture_config.h" #include "webrtc/system_wrappers/include/logging.h" #ifndef abs #define abs(a) (a>=0?a:-a) #endif namespace webrtc { namespace videocapturemodule { DeviceInfoImpl::DeviceInfoImpl(const int32_t id) : _id(id), _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(); if (_lastUsedDeviceNameLength == strlen((char*) deviceUniqueIdUTF8)) { // Is it the same device that is asked for again. #if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX) if(strncasecmp((char*)_lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength)==0) #else if (_strnicmp((char*) _lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength) == 0) #endif { //yes _apiLock.ReleaseLockShared(); return static_cast(_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 ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) #if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX) || (strncasecmp((char*)_lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength)!=0)) #else || (_strnicmp((char*) _lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength) != 0)) #endif { _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()) { 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 ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) #if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX) || (strncasecmp((char*)_lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength)!=0)) #else || (_strnicmp((char*) _lastUsedDeviceName, (char*) deviceUniqueIdUTF8, _lastUsedDeviceNameLength) != 0)) #endif { _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; RawVideoType bestRawType = kVideoUnknown; webrtc::VideoCodecType bestCodecType = webrtc::kVideoCodecUnknown; const int32_t numberOfCapabilies = static_cast(_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 (bestRawType != requested.rawType && requested.rawType != kVideoUnknown && (capability.rawType == requested.rawType || capability.rawType == kVideoI420 || capability.rawType == kVideoYUY2 || capability.rawType == kVideoYV12)) { bestCodecType = capability.codecType; bestRawType = capability.rawType; 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) { if (capability.codecType == requested.codecType && bestCodecType != requested.codecType) { bestCodecType = capability.codecType; bestformatIndex = tmp; } } } else // Better frame rate { if (requested.codecType == capability.codecType) { bestWidth = capability.width; bestHeight = capability.height; bestFrameRate = capability.maxFPS; bestCodecType = capability.codecType; bestRawType = capability.rawType; bestformatIndex = tmp; } } } } else // Better width than previously { if (requested.codecType == capability.codecType) { bestWidth = capability.width; bestHeight = capability.height; bestFrameRate = capability.maxFPS; bestCodecType = capability.codecType; bestRawType = capability.rawType; bestformatIndex = tmp; } } }// else width no good } else // Better height { if (requested.codecType == capability.codecType) { bestWidth = capability.width; bestHeight = capability.height; bestFrameRate = capability.maxFPS; bestCodecType = capability.codecType; bestRawType = capability.rawType; bestformatIndex = tmp; } } }// else height not good }//end for LOG(LS_VERBOSE) << "Best camera format: " << bestWidth << "x" << bestHeight << "@" << bestFrameRate << "fps, color format: " << bestRawType; // Copy the capability if (bestformatIndex < 0) return -1; resulting = _captureCapabilities[bestformatIndex]; return bestformatIndex; } /* Returns the expected Capture delay*/ int32_t DeviceInfoImpl::GetExpectedCaptureDelay( const DelayValues delayValues[], const uint32_t sizeOfDelayValues, const char* productId, const uint32_t width, const uint32_t height) { int32_t bestDelay = kDefaultCaptureDelay; for (uint32_t device = 0; device < sizeOfDelayValues; ++device) { if (delayValues[device].productId && strncmp((char*) productId, (char*) delayValues[device].productId, kVideoCaptureProductIdLength) == 0) { // We have found the camera int32_t bestWidth = 0; int32_t bestHeight = 0; //Loop through all tested sizes and find one that seems fitting for (uint32_t delayIndex = 0; delayIndex < NoOfDelayValues; ++delayIndex) { const DelayValue& currentValue = delayValues[device].delayValues[delayIndex]; const int32_t diffWidth = currentValue.width - width; const int32_t diffHeight = currentValue.height - height; const int32_t currentbestDiffWith = bestWidth - width; const int32_t currentbestDiffHeight = bestHeight - height; if ((diffHeight >= 0 && diffHeight <= abs(currentbestDiffHeight)) // Height better or equal than previous. || (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 previous { } else // Better width than previously { bestWidth = currentValue.width; bestHeight = currentValue.height; bestDelay = currentValue.delay; } }// else width no good } else // Better height { bestWidth = currentValue.width; bestHeight = currentValue.height; bestDelay = currentValue.delay; } }// else height not good }//end for break; } } if (bestDelay > kMaxCaptureDelay) { LOG(LS_WARNING) << "Expected capture delay (" << bestDelay << " ms) too high, using " << kMaxCaptureDelay << " ms."; bestDelay = kMaxCaptureDelay; } return bestDelay; } //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