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 "webrtc/modules/video_capture/linux/device_info_linux.h"
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <sys/ioctl.h>
18#include <sys/stat.h>
19#include <unistd.h>
20//v4l includes
21#include <linux/videodev2.h>
22
23#include "webrtc/system_wrappers/interface/ref_count.h"
24#include "webrtc/system_wrappers/interface/trace.h"
25
26
27namespace webrtc
28{
29namespace videocapturemodule
30{
31VideoCaptureModule::DeviceInfo*
32VideoCaptureImpl::CreateDeviceInfo(const int32_t id)
33{
34    videocapturemodule::DeviceInfoLinux *deviceInfo =
35                    new videocapturemodule::DeviceInfoLinux(id);
36    if (!deviceInfo)
37    {
38        deviceInfo = NULL;
39    }
40
41    return deviceInfo;
42}
43
44DeviceInfoLinux::DeviceInfoLinux(const int32_t id)
45    : DeviceInfoImpl(id)
46{
47}
48
49int32_t DeviceInfoLinux::Init()
50{
51    return 0;
52}
53
54DeviceInfoLinux::~DeviceInfoLinux()
55{
56}
57
58uint32_t DeviceInfoLinux::NumberOfDevices()
59{
60    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__);
61
62    uint32_t count = 0;
63    char device[20];
64    int fd = -1;
65
66    /* detect /dev/video [0-63]VideoCaptureModule entries */
67    for (int n = 0; n < 64; n++)
68    {
69        sprintf(device, "/dev/video%d", n);
70        if ((fd = open(device, O_RDONLY)) != -1)
71        {
72            close(fd);
73            count++;
74        }
75    }
76
77    return count;
78}
79
80int32_t DeviceInfoLinux::GetDeviceName(
81                                         uint32_t deviceNumber,
82                                         char* deviceNameUTF8,
83                                         uint32_t deviceNameLength,
84                                         char* deviceUniqueIdUTF8,
85                                         uint32_t deviceUniqueIdUTF8Length,
86                                         char* /*productUniqueIdUTF8*/,
87                                         uint32_t /*productUniqueIdUTF8Length*/)
88{
89    WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__);
90
91    // Travel through /dev/video [0-63]
92    uint32_t count = 0;
93    char device[20];
94    int fd = -1;
95    bool found = false;
96    for (int n = 0; n < 64; n++)
97    {
98        sprintf(device, "/dev/video%d", n);
99        if ((fd = open(device, O_RDONLY)) != -1)
100        {
101            if (count == deviceNumber) {
102                // Found the device
103                found = true;
104                break;
105            } else {
106                close(fd);
107                count++;
108            }
109        }
110    }
111
112    if (!found)
113        return -1;
114
115    // query device capabilities
116    struct v4l2_capability cap;
117    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
118    {
119        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
120                   "error in querying the device capability for device %s. errno = %d",
121                   device, errno);
122        close(fd);
123        return -1;
124    }
125
126    close(fd);
127
128    char cameraName[64];
129    memset(deviceNameUTF8, 0, deviceNameLength);
130    memcpy(cameraName, cap.card, sizeof(cap.card));
131
132    if (deviceNameLength >= strlen(cameraName))
133    {
134        memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
135    }
136    else
137    {
138        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "buffer passed is too small");
139        return -1;
140    }
141
142    if (cap.bus_info[0] != 0) // may not available in all drivers
143    {
144        // copy device id
145        if (deviceUniqueIdUTF8Length >= strlen((const char*) cap.bus_info))
146        {
147            memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
148            memcpy(deviceUniqueIdUTF8, cap.bus_info,
149                   strlen((const char*) cap.bus_info));
150        }
151        else
152        {
153            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
154                       "buffer passed is too small");
155            return -1;
156        }
157    }
158
159    return 0;
160}
161
162int32_t DeviceInfoLinux::CreateCapabilityMap(
163                                        const char* deviceUniqueIdUTF8)
164{
165    int fd;
166    char device[32];
167    bool found = false;
168
169    const int32_t deviceUniqueIdUTF8Length =
170                            (int32_t) strlen((char*) deviceUniqueIdUTF8);
171    if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
172    {
173        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "Device name too long");
174        return -1;
175    }
176    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
177               "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8);
178
179    /* detect /dev/video [0-63] entries */
180    for (int n = 0; n < 64; ++n)
181    {
182        sprintf(device, "/dev/video%d", n);
183        fd = open(device, O_RDONLY);
184        if (fd == -1)
185          continue;
186
187        // query device capabilities
188        struct v4l2_capability cap;
189        if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0)
190        {
191            if (cap.bus_info[0] != 0)
192            {
193                if (strncmp((const char*) cap.bus_info,
194                            (const char*) deviceUniqueIdUTF8,
195                            strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id
196                {
197                    found = true;
198                    break; // fd matches with device unique id supplied
199                }
200            }
201            else //match for device name
202            {
203                if (IsDeviceNameMatches((const char*) cap.card,
204                                        (const char*) deviceUniqueIdUTF8))
205                {
206                    found = true;
207                    break;
208                }
209            }
210        }
211        close(fd); // close since this is not the matching device
212    }
213
214    if (!found)
215    {
216        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "no matching device found");
217        return -1;
218    }
219
220    // now fd will point to the matching device
221    // reset old capability list.
222    _captureCapabilities.clear();
223
224    int size = FillCapabilities(fd);
225    close(fd);
226
227    // Store the new used device name
228    _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
229    _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
230                                                   _lastUsedDeviceNameLength + 1);
231    memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1);
232
233    WEBRTC_TRACE(webrtc::kTraceInfo,
234                 webrtc::kTraceVideoCapture,
235                 _id,
236                 "CreateCapabilityMap %u",
237                 static_cast<unsigned int>(_captureCapabilities.size()));
238
239    return size;
240}
241
242bool DeviceInfoLinux::IsDeviceNameMatches(const char* name,
243                                          const char* deviceUniqueIdUTF8)
244{
245    if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
246            return true;
247    return false;
248}
249
250int32_t DeviceInfoLinux::FillCapabilities(int fd)
251{
252
253    // set image format
254    struct v4l2_format video_fmt;
255    memset(&video_fmt, 0, sizeof(struct v4l2_format));
256
257    video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
258    video_fmt.fmt.pix.sizeimage = 0;
259
260    int totalFmts = 3;
261    unsigned int videoFormats[] = {
262        V4L2_PIX_FMT_MJPEG,
263        V4L2_PIX_FMT_YUV420,
264        V4L2_PIX_FMT_YUYV };
265
266    int sizes = 13;
267    unsigned int size[][2] = { { 128, 96 }, { 160, 120 }, { 176, 144 },
268                               { 320, 240 }, { 352, 288 }, { 640, 480 },
269                               { 704, 576 }, { 800, 600 }, { 960, 720 },
270                               { 1280, 720 }, { 1024, 768 }, { 1440, 1080 },
271                               { 1920, 1080 } };
272
273    int index = 0;
274    for (int fmts = 0; fmts < totalFmts; fmts++)
275    {
276        for (int i = 0; i < sizes; i++)
277        {
278            video_fmt.fmt.pix.pixelformat = videoFormats[fmts];
279            video_fmt.fmt.pix.width = size[i][0];
280            video_fmt.fmt.pix.height = size[i][1];
281
282            if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0)
283            {
284                if ((video_fmt.fmt.pix.width == size[i][0])
285                    && (video_fmt.fmt.pix.height == size[i][1]))
286                {
287                    VideoCaptureCapability cap;
288                    cap.width = video_fmt.fmt.pix.width;
289                    cap.height = video_fmt.fmt.pix.height;
290                    cap.expectedCaptureDelay = 120;
291                    if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV)
292                    {
293                        cap.rawType = kVideoYUY2;
294                    }
295                    else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420)
296                    {
297                        cap.rawType = kVideoI420;
298                    }
299                    else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG)
300                    {
301                        cap.rawType = kVideoMJPEG;
302                    }
303
304                    // get fps of current camera mode
305                    // V4l2 does not have a stable method of knowing so we just guess.
306                    if(cap.width >= 800 && cap.rawType != kVideoMJPEG)
307                    {
308                        cap.maxFPS = 15;
309                    }
310                    else
311                    {
312                        cap.maxFPS = 30;
313                    }
314
315                    _captureCapabilities.push_back(cap);
316                    index++;
317                    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
318                               "Camera capability, width:%d height:%d type:%d fps:%d",
319                               cap.width, cap.height, cap.rawType, cap.maxFPS);
320                }
321            }
322        }
323    }
324
325    WEBRTC_TRACE(webrtc::kTraceInfo,
326                 webrtc::kTraceVideoCapture,
327                 _id,
328                 "CreateCapabilityMap %u",
329                 static_cast<unsigned int>(_captureCapabilities.size()));
330    return _captureCapabilities.size();
331}
332
333}  // namespace videocapturemodule
334}  // namespace webrtc
335