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