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