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 "modules/video_capture/device_info_impl.h"
12
13 #include <stdlib.h>
14
15 #include "absl/strings/match.h"
16 #include "absl/strings/string_view.h"
17 #include "rtc_base/logging.h"
18
19 #ifndef abs
20 #define abs(a) (a >= 0 ? a : -a)
21 #endif
22
23 namespace webrtc {
24 namespace videocapturemodule {
25
DeviceInfoImpl()26 DeviceInfoImpl::DeviceInfoImpl()
27 : _lastUsedDeviceName(NULL), _lastUsedDeviceNameLength(0) {}
28
~DeviceInfoImpl(void)29 DeviceInfoImpl::~DeviceInfoImpl(void) {
30 MutexLock lock(&_apiLock);
31 free(_lastUsedDeviceName);
32 }
33
NumberOfCapabilities(const char * deviceUniqueIdUTF8)34 int32_t DeviceInfoImpl::NumberOfCapabilities(const char* deviceUniqueIdUTF8) {
35 if (!deviceUniqueIdUTF8)
36 return -1;
37
38 MutexLock lock(&_apiLock);
39
40 // Is it the same device that is asked for again.
41 if (absl::EqualsIgnoreCase(
42 deviceUniqueIdUTF8,
43 absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) {
44 return static_cast<int32_t>(_captureCapabilities.size());
45 }
46
47 int32_t ret = CreateCapabilityMap(deviceUniqueIdUTF8);
48 return ret;
49 }
50
GetCapability(const char * deviceUniqueIdUTF8,const uint32_t deviceCapabilityNumber,VideoCaptureCapability & capability)51 int32_t DeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8,
52 const uint32_t deviceCapabilityNumber,
53 VideoCaptureCapability& capability) {
54 RTC_DCHECK(deviceUniqueIdUTF8);
55
56 MutexLock lock(&_apiLock);
57
58 if (!absl::EqualsIgnoreCase(
59 deviceUniqueIdUTF8,
60 absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) {
61 if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) {
62 return -1;
63 }
64 }
65
66 // Make sure the number is valid
67 if (deviceCapabilityNumber >= (unsigned int)_captureCapabilities.size()) {
68 RTC_LOG(LS_ERROR) << "Invalid deviceCapabilityNumber "
69 << deviceCapabilityNumber << ">= number of capabilities ("
70 << _captureCapabilities.size() << ").";
71 return -1;
72 }
73
74 capability = _captureCapabilities[deviceCapabilityNumber];
75 return 0;
76 }
77
GetBestMatchedCapability(const char * deviceUniqueIdUTF8,const VideoCaptureCapability & requested,VideoCaptureCapability & resulting)78 int32_t DeviceInfoImpl::GetBestMatchedCapability(
79 const char* deviceUniqueIdUTF8,
80 const VideoCaptureCapability& requested,
81 VideoCaptureCapability& resulting) {
82 if (!deviceUniqueIdUTF8)
83 return -1;
84
85 MutexLock lock(&_apiLock);
86 if (!absl::EqualsIgnoreCase(
87 deviceUniqueIdUTF8,
88 absl::string_view(_lastUsedDeviceName, _lastUsedDeviceNameLength))) {
89 if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) {
90 return -1;
91 }
92 }
93
94 int32_t bestformatIndex = -1;
95 int32_t bestWidth = 0;
96 int32_t bestHeight = 0;
97 int32_t bestFrameRate = 0;
98 VideoType bestVideoType = VideoType::kUnknown;
99
100 const int32_t numberOfCapabilies =
101 static_cast<int32_t>(_captureCapabilities.size());
102
103 for (int32_t tmp = 0; tmp < numberOfCapabilies;
104 ++tmp) // Loop through all capabilities
105 {
106 VideoCaptureCapability& capability = _captureCapabilities[tmp];
107
108 const int32_t diffWidth = capability.width - requested.width;
109 const int32_t diffHeight = capability.height - requested.height;
110 const int32_t diffFrameRate = capability.maxFPS - requested.maxFPS;
111
112 const int32_t currentbestDiffWith = bestWidth - requested.width;
113 const int32_t currentbestDiffHeight = bestHeight - requested.height;
114 const int32_t currentbestDiffFrameRate = bestFrameRate - requested.maxFPS;
115
116 if ((diffHeight >= 0 &&
117 diffHeight <= abs(currentbestDiffHeight)) // Height better or equalt
118 // that previouse.
119 || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight)) {
120 if (diffHeight ==
121 currentbestDiffHeight) // Found best height. Care about the width)
122 {
123 if ((diffWidth >= 0 &&
124 diffWidth <= abs(currentbestDiffWith)) // Width better or equal
125 || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith)) {
126 if (diffWidth == currentbestDiffWith &&
127 diffHeight == currentbestDiffHeight) // Same size as previously
128 {
129 // Also check the best frame rate if the diff is the same as
130 // previouse
131 if (((diffFrameRate >= 0 &&
132 diffFrameRate <=
133 currentbestDiffFrameRate) // Frame rate to high but
134 // better match than previouse
135 // and we have not selected IUV
136 || (currentbestDiffFrameRate < 0 &&
137 diffFrameRate >=
138 currentbestDiffFrameRate)) // Current frame rate is
139 // lower than requested.
140 // This is better.
141 ) {
142 if ((currentbestDiffFrameRate ==
143 diffFrameRate) // Same frame rate as previous or frame rate
144 // allready good enough
145 || (currentbestDiffFrameRate >= 0)) {
146 if (bestVideoType != requested.videoType &&
147 requested.videoType != VideoType::kUnknown &&
148 (capability.videoType == requested.videoType ||
149 capability.videoType == VideoType::kI420 ||
150 capability.videoType == VideoType::kYUY2 ||
151 capability.videoType == VideoType::kYV12 ||
152 capability.videoType == VideoType::kNV12)) {
153 bestVideoType = capability.videoType;
154 bestformatIndex = tmp;
155 }
156 // If width height and frame rate is full filled we can use the
157 // camera for encoding if it is supported.
158 if (capability.height == requested.height &&
159 capability.width == requested.width &&
160 capability.maxFPS >= requested.maxFPS) {
161 bestformatIndex = tmp;
162 }
163 } else // Better frame rate
164 {
165 bestWidth = capability.width;
166 bestHeight = capability.height;
167 bestFrameRate = capability.maxFPS;
168 bestVideoType = capability.videoType;
169 bestformatIndex = tmp;
170 }
171 }
172 } else // Better width than previously
173 {
174 bestWidth = capability.width;
175 bestHeight = capability.height;
176 bestFrameRate = capability.maxFPS;
177 bestVideoType = capability.videoType;
178 bestformatIndex = tmp;
179 }
180 } // else width no good
181 } else // Better height
182 {
183 bestWidth = capability.width;
184 bestHeight = capability.height;
185 bestFrameRate = capability.maxFPS;
186 bestVideoType = capability.videoType;
187 bestformatIndex = tmp;
188 }
189 } // else height not good
190 } // end for
191
192 RTC_LOG(LS_VERBOSE) << "Best camera format: " << bestWidth << "x"
193 << bestHeight << "@" << bestFrameRate
194 << "fps, color format: "
195 << static_cast<int>(bestVideoType);
196
197 // Copy the capability
198 if (bestformatIndex < 0)
199 return -1;
200 resulting = _captureCapabilities[bestformatIndex];
201 return bestformatIndex;
202 }
203
204 // Default implementation. This should be overridden by Mobile implementations.
GetOrientation(const char * deviceUniqueIdUTF8,VideoRotation & orientation)205 int32_t DeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
206 VideoRotation& orientation) {
207 orientation = kVideoRotation_0;
208 return -1;
209 }
210 } // namespace videocapturemodule
211 } // namespace webrtc
212