/* * Copyright (c) 2011 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_impl.h" #include "video_capture_config.h" #include "trace.h" #include #ifndef abs #define abs(a) (a>=0?a:-a) #endif namespace webrtc { namespace videocapturemodule { DeviceInfoImpl::DeviceInfoImpl(const WebRtc_Word32 id) : _id(id), _apiLock(*RWLockWrapper::CreateRWLock()), _lastUsedDeviceName(NULL), _lastUsedDeviceNameLength(0) { } DeviceInfoImpl::~DeviceInfoImpl(void) { _apiLock.AcquireLockExclusive(); // Reset old capability list MapItem* item = NULL; while ((item = _captureCapabilities.Last())) { delete (VideoCaptureCapability*) item->GetItem(); _captureCapabilities.Erase(item); } free(_lastUsedDeviceName); _apiLock.ReleaseLockExclusive(); delete &_apiLock; } WebRtc_Word32 DeviceInfoImpl::NumberOfCapabilities( const WebRtc_UWord8* deviceUniqueIdUTF8) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "NumberOfCapabilities, uniqueID %s", 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_INTEL) || 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 _captureCapabilities.Size(); } } // Need to get exclusive rights to create the new capability map. _apiLock.ReleaseLockShared(); WriteLockScoped cs2(_apiLock); WebRtc_Word32 ret = CreateCapabilityMap(deviceUniqueIdUTF8); return ret; } WebRtc_Word32 DeviceInfoImpl::GetCapability(const WebRtc_UWord8* deviceUniqueIdUTF8, const WebRtc_UWord32 deviceCapabilityNumber, VideoCaptureCapability& capability) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "GetCapability capability number %d", deviceCapabilityNumber); if (!deviceUniqueIdUTF8) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "deviceUniqueIdUTF8 parameter not set in call to GetCapability"); return -1; } ReadLockScoped cs(_apiLock); if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) #if defined(WEBRTC_MAC_INTEL) || 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()) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "deviceCapabilityNumber %d is invalid in call to GetCapability", deviceCapabilityNumber); return -1; } MapItem* item = _captureCapabilities.Find(deviceCapabilityNumber); if (!item) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "Failed to find capability number %d of %d possible", deviceCapabilityNumber, _captureCapabilities.Size()); return -1; } VideoCaptureCapability* capPointer = static_cast (item->GetItem()); if (!capPointer) { return -1; } capability = *capPointer; return 0; } WebRtc_Word32 DeviceInfoImpl::GetBestMatchedCapability( const WebRtc_UWord8*deviceUniqueIdUTF8, const VideoCaptureCapability requested, VideoCaptureCapability& resulting) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "GetBestMatchedCapability unique ID %s", deviceUniqueIdUTF8); if (!deviceUniqueIdUTF8) return -1; ReadLockScoped cs(_apiLock); if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) #if defined(WEBRTC_MAC_INTEL) || 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(); } WebRtc_Word32 bestformatIndex = -1; WebRtc_Word32 bestWidth = 0; WebRtc_Word32 bestHeight = 0; WebRtc_Word32 bestFrameRate = 0; RawVideoType bestRawType = kVideoUnknown; webrtc::VideoCodecType bestCodecType = webrtc::kVideoCodecUnknown; const WebRtc_Word32 numberOfCapabilies = _captureCapabilities.Size(); for (WebRtc_Word32 tmp = 0; tmp < numberOfCapabilies; ++tmp) // Loop through all capabilities { MapItem* item = _captureCapabilities.Find(tmp); if (!item) return -1; VideoCaptureCapability& capability = *static_cast (item->GetItem()); const WebRtc_Word32 diffWidth = capability.width - requested.width; const WebRtc_Word32 diffHeight = capability.height - requested.height; const WebRtc_Word32 diffFrameRate = capability.maxFPS - requested.maxFPS; const WebRtc_Word32 currentbestDiffWith = bestWidth - requested.width; const WebRtc_Word32 currentbestDiffHeight = bestHeight - requested.height; const WebRtc_Word32 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 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "Best camera format: Width %d, Height %d, Frame rate %d, Color format %d", bestWidth, bestHeight, bestFrameRate, bestRawType); // Copy the capability MapItem* item = _captureCapabilities.Find(bestformatIndex); if (!item) return -1; VideoCaptureCapability* capPointer = static_cast (item->GetItem()); if (!capPointer) return -1; resulting = *capPointer; return bestformatIndex; } /* Returns the expected Capture delay*/ WebRtc_Word32 DeviceInfoImpl::GetExpectedCaptureDelay( const DelayValues delayValues[], const WebRtc_UWord32 sizeOfDelayValues, const WebRtc_UWord8* productId, const WebRtc_UWord32 width, const WebRtc_UWord32 height) { WebRtc_Word32 bestDelay = kDefaultCaptureDelay; for (WebRtc_UWord32 device = 0; device < sizeOfDelayValues; ++device) { if (delayValues[device].productId && strncmp((char*) productId, (char*) delayValues[device].productId, kVideoCaptureProductIdLength) == 0) { // We have found the camera WebRtc_Word32 bestWidth = 0; WebRtc_Word32 bestHeight = 0; //Loop through all tested sizes and find one that seems fitting for (WebRtc_UWord32 delayIndex = 0; delayIndex < NoOfDelayValues; ++delayIndex) { const DelayValue& currentValue = delayValues[device].delayValues[delayIndex]; const WebRtc_Word32 diffWidth = currentValue.width - width; const WebRtc_Word32 diffHeight = currentValue.height - height; const WebRtc_Word32 currentbestDiffWith = bestWidth - width; const WebRtc_Word32 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) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, "Expected capture delay too high. %dms, will use %d", bestDelay, kMaxCaptureDelay); bestDelay = kMaxCaptureDelay; } return bestDelay; } //Default implementation. This should be overridden by Mobile implementations. WebRtc_Word32 DeviceInfoImpl::GetOrientation(const WebRtc_UWord8* deviceUniqueIdUTF8, VideoCaptureRotation& orientation) { orientation = kCameraRotate0; return -1; } } //namespace videocapturemodule } // namespace webrtc