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 <assert.h>
12#include <stdlib.h>
13
14#include "webrtc/modules/video_capture/device_info_impl.h"
15#include "webrtc/modules/video_capture/video_capture_config.h"
16#include "webrtc/system_wrappers/include/logging.h"
17
18#ifndef abs
19#define abs(a) (a>=0?a:-a)
20#endif
21
22namespace webrtc
23{
24namespace videocapturemodule
25{
26DeviceInfoImpl::DeviceInfoImpl(const int32_t id)
27    : _id(id), _apiLock(*RWLockWrapper::CreateRWLock()), _lastUsedDeviceName(NULL),
28      _lastUsedDeviceNameLength(0)
29{
30}
31
32DeviceInfoImpl::~DeviceInfoImpl(void)
33{
34    _apiLock.AcquireLockExclusive();
35    free(_lastUsedDeviceName);
36    _apiLock.ReleaseLockExclusive();
37
38    delete &_apiLock;
39}
40int32_t DeviceInfoImpl::NumberOfCapabilities(
41                                        const char* deviceUniqueIdUTF8)
42{
43
44    if (!deviceUniqueIdUTF8)
45        return -1;
46
47    _apiLock.AcquireLockShared();
48
49    if (_lastUsedDeviceNameLength == strlen((char*) deviceUniqueIdUTF8))
50    {
51        // Is it the same device that is asked for again.
52#if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX)
53        if(strncasecmp((char*)_lastUsedDeviceName,
54                       (char*) deviceUniqueIdUTF8,
55                       _lastUsedDeviceNameLength)==0)
56#else
57        if (_strnicmp((char*) _lastUsedDeviceName,
58                      (char*) deviceUniqueIdUTF8,
59                      _lastUsedDeviceNameLength) == 0)
60#endif
61        {
62            //yes
63            _apiLock.ReleaseLockShared();
64            return static_cast<int32_t>(_captureCapabilities.size());
65        }
66    }
67    // Need to get exclusive rights to create the new capability map.
68    _apiLock.ReleaseLockShared();
69    WriteLockScoped cs2(_apiLock);
70
71    int32_t ret = CreateCapabilityMap(deviceUniqueIdUTF8);
72    return ret;
73}
74
75int32_t DeviceInfoImpl::GetCapability(const char* deviceUniqueIdUTF8,
76                                      const uint32_t deviceCapabilityNumber,
77                                      VideoCaptureCapability& capability)
78{
79    assert(deviceUniqueIdUTF8 != NULL);
80
81    ReadLockScoped cs(_apiLock);
82
83    if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8))
84#if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX)
85        || (strncasecmp((char*)_lastUsedDeviceName,
86                        (char*) deviceUniqueIdUTF8,
87                        _lastUsedDeviceNameLength)!=0))
88#else
89        || (_strnicmp((char*) _lastUsedDeviceName,
90                      (char*) deviceUniqueIdUTF8,
91                      _lastUsedDeviceNameLength) != 0))
92#endif
93
94    {
95        _apiLock.ReleaseLockShared();
96        _apiLock.AcquireLockExclusive();
97        if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8))
98        {
99            _apiLock.ReleaseLockExclusive();
100            _apiLock.AcquireLockShared();
101            return -1;
102        }
103        _apiLock.ReleaseLockExclusive();
104        _apiLock.AcquireLockShared();
105    }
106
107    // Make sure the number is valid
108    if (deviceCapabilityNumber >= (unsigned int) _captureCapabilities.size())
109    {
110        LOG(LS_ERROR) << "Invalid deviceCapabilityNumber "
111                      << deviceCapabilityNumber << ">= number of capabilities ("
112                      << _captureCapabilities.size() << ").";
113        return -1;
114    }
115
116    capability = _captureCapabilities[deviceCapabilityNumber];
117    return 0;
118}
119
120int32_t DeviceInfoImpl::GetBestMatchedCapability(
121                                        const char*deviceUniqueIdUTF8,
122                                        const VideoCaptureCapability& requested,
123                                        VideoCaptureCapability& resulting)
124{
125
126
127    if (!deviceUniqueIdUTF8)
128        return -1;
129
130    ReadLockScoped cs(_apiLock);
131    if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8))
132#if defined(WEBRTC_MAC) || defined(WEBRTC_LINUX)
133        || (strncasecmp((char*)_lastUsedDeviceName,
134                        (char*) deviceUniqueIdUTF8,
135                        _lastUsedDeviceNameLength)!=0))
136#else
137        || (_strnicmp((char*) _lastUsedDeviceName,
138                      (char*) deviceUniqueIdUTF8,
139                      _lastUsedDeviceNameLength) != 0))
140#endif
141    {
142        _apiLock.ReleaseLockShared();
143        _apiLock.AcquireLockExclusive();
144        if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8))
145        {
146            return -1;
147        }
148        _apiLock.ReleaseLockExclusive();
149        _apiLock.AcquireLockShared();
150    }
151
152    int32_t bestformatIndex = -1;
153    int32_t bestWidth = 0;
154    int32_t bestHeight = 0;
155    int32_t bestFrameRate = 0;
156    RawVideoType bestRawType = kVideoUnknown;
157    webrtc::VideoCodecType bestCodecType = webrtc::kVideoCodecUnknown;
158
159    const int32_t numberOfCapabilies =
160        static_cast<int32_t>(_captureCapabilities.size());
161
162    for (int32_t tmp = 0; tmp < numberOfCapabilies; ++tmp) // Loop through all capabilities
163    {
164        VideoCaptureCapability& capability = _captureCapabilities[tmp];
165
166        const int32_t diffWidth = capability.width - requested.width;
167        const int32_t diffHeight = capability.height - requested.height;
168        const int32_t diffFrameRate = capability.maxFPS - requested.maxFPS;
169
170        const int32_t currentbestDiffWith = bestWidth - requested.width;
171        const int32_t currentbestDiffHeight = bestHeight - requested.height;
172        const int32_t currentbestDiffFrameRate = bestFrameRate - requested.maxFPS;
173
174        if ((diffHeight >= 0 && diffHeight <= abs(currentbestDiffHeight)) // Height better or equalt that previouse.
175            || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight))
176        {
177
178            if (diffHeight == currentbestDiffHeight) // Found best height. Care about the width)
179            {
180                if ((diffWidth >= 0 && diffWidth <= abs(currentbestDiffWith)) // Width better or equal
181                    || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith))
182                {
183                    if (diffWidth == currentbestDiffWith && diffHeight
184                        == currentbestDiffHeight) // Same size as previously
185                    {
186                        //Also check the best frame rate if the diff is the same as previouse
187                        if (((diffFrameRate >= 0 &&
188                              diffFrameRate <= currentbestDiffFrameRate) // Frame rate to high but better match than previouse and we have not selected IUV
189                            ||
190                            (currentbestDiffFrameRate < 0 &&
191                             diffFrameRate >= currentbestDiffFrameRate)) // Current frame rate is lower than requested. This is better.
192                        )
193                        {
194                            if ((currentbestDiffFrameRate == diffFrameRate) // Same frame rate as previous  or frame rate allready good enough
195                                || (currentbestDiffFrameRate >= 0))
196                            {
197                                if (bestRawType != requested.rawType
198                                    && requested.rawType != kVideoUnknown
199                                    && (capability.rawType == requested.rawType
200                                        || capability.rawType == kVideoI420
201                                        || capability.rawType == kVideoYUY2
202                                        || capability.rawType == kVideoYV12))
203                                {
204                                    bestCodecType = capability.codecType;
205                                    bestRawType = capability.rawType;
206                                    bestformatIndex = tmp;
207                                }
208                                // If width height and frame rate is full filled we can use the camera for encoding if it is supported.
209                                if (capability.height == requested.height
210                                    && capability.width == requested.width
211                                    && capability.maxFPS >= requested.maxFPS)
212                                {
213                                    if (capability.codecType == requested.codecType
214                                        && bestCodecType != requested.codecType)
215                                    {
216                                        bestCodecType = capability.codecType;
217                                        bestformatIndex = tmp;
218                                    }
219                                }
220                            }
221                            else // Better frame rate
222                            {
223                                if (requested.codecType == capability.codecType)
224                                {
225
226                                    bestWidth = capability.width;
227                                    bestHeight = capability.height;
228                                    bestFrameRate = capability.maxFPS;
229                                    bestCodecType = capability.codecType;
230                                    bestRawType = capability.rawType;
231                                    bestformatIndex = tmp;
232                                }
233                            }
234                        }
235                    }
236                    else // Better width than previously
237                    {
238                        if (requested.codecType == capability.codecType)
239                        {
240                            bestWidth = capability.width;
241                            bestHeight = capability.height;
242                            bestFrameRate = capability.maxFPS;
243                            bestCodecType = capability.codecType;
244                            bestRawType = capability.rawType;
245                            bestformatIndex = tmp;
246                        }
247                    }
248                }// else width no good
249            }
250            else // Better height
251            {
252                if (requested.codecType == capability.codecType)
253                {
254                    bestWidth = capability.width;
255                    bestHeight = capability.height;
256                    bestFrameRate = capability.maxFPS;
257                    bestCodecType = capability.codecType;
258                    bestRawType = capability.rawType;
259                    bestformatIndex = tmp;
260                }
261            }
262        }// else height not good
263    }//end for
264
265    LOG(LS_VERBOSE) << "Best camera format: " << bestWidth << "x" << bestHeight
266                    << "@" << bestFrameRate
267                    << "fps, color format: " << bestRawType;
268
269    // Copy the capability
270    if (bestformatIndex < 0)
271        return -1;
272    resulting = _captureCapabilities[bestformatIndex];
273    return bestformatIndex;
274}
275
276/* Returns the expected Capture delay*/
277int32_t DeviceInfoImpl::GetExpectedCaptureDelay(
278                                          const DelayValues delayValues[],
279                                          const uint32_t sizeOfDelayValues,
280                                          const char* productId,
281                                          const uint32_t width,
282                                          const uint32_t height)
283{
284    int32_t bestDelay = kDefaultCaptureDelay;
285
286    for (uint32_t device = 0; device < sizeOfDelayValues; ++device)
287    {
288        if (delayValues[device].productId && strncmp((char*) productId,
289                                                     (char*) delayValues[device].productId,
290                                                     kVideoCaptureProductIdLength) == 0)
291        {
292            // We have found the camera
293
294            int32_t bestWidth = 0;
295            int32_t bestHeight = 0;
296
297            //Loop through all tested sizes and find one that seems fitting
298            for (uint32_t delayIndex = 0; delayIndex < NoOfDelayValues; ++delayIndex)
299            {
300                const DelayValue& currentValue = delayValues[device].delayValues[delayIndex];
301
302                const int32_t diffWidth = currentValue.width - width;
303                const int32_t diffHeight = currentValue.height - height;
304
305                const int32_t currentbestDiffWith = bestWidth - width;
306                const int32_t currentbestDiffHeight = bestHeight - height;
307
308                if ((diffHeight >= 0 && diffHeight <= abs(currentbestDiffHeight)) // Height better or equal than previous.
309                    || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight))
310                {
311
312                    if (diffHeight == currentbestDiffHeight) // Found best height. Care about the width)
313                    {
314                        if ((diffWidth >= 0 && diffWidth <= abs(currentbestDiffWith)) // Width better or equal
315                            || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith))
316                        {
317                            if (diffWidth == currentbestDiffWith && diffHeight
318                                == currentbestDiffHeight) // Same size as previous
319                            {
320                            }
321                            else // Better width than previously
322                            {
323                                bestWidth = currentValue.width;
324                                bestHeight = currentValue.height;
325                                bestDelay = currentValue.delay;
326                            }
327                        }// else width no good
328                    }
329                    else // Better height
330                    {
331                        bestWidth = currentValue.width;
332                        bestHeight = currentValue.height;
333                        bestDelay = currentValue.delay;
334                    }
335                }// else height not good
336            }//end for
337            break;
338        }
339    }
340    if (bestDelay > kMaxCaptureDelay)
341    {
342        LOG(LS_WARNING) << "Expected capture delay (" << bestDelay
343                        << " ms) too high, using " << kMaxCaptureDelay
344                        << " ms.";
345        bestDelay = kMaxCaptureDelay;
346    }
347
348    return bestDelay;
349
350}
351
352//Default implementation. This should be overridden by Mobile implementations.
353int32_t DeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
354                                       VideoRotation& orientation) {
355  orientation = kVideoRotation_0;
356    return -1;
357}
358}  // namespace videocapturemodule
359}  // namespace webrtc
360