Line data Source code
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 : #if defined(__NetBSD__) || defined(__OpenBSD__)
22 : #include <sys/videoio.h>
23 : #elif defined(__sun)
24 : #include <sys/videodev2.h>
25 : #else
26 : #include <linux/videodev2.h>
27 : #endif
28 :
29 : #include "webrtc/system_wrappers/include/trace.h"
30 :
31 : #ifdef WEBRTC_LINUX
32 : #define EVENT_SIZE ( sizeof (struct inotify_event) )
33 : #define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
34 : #endif
35 :
36 : namespace webrtc
37 : {
38 : namespace videocapturemodule
39 : {
40 : VideoCaptureModule::DeviceInfo*
41 0 : VideoCaptureImpl::CreateDeviceInfo()
42 : {
43 0 : return new videocapturemodule::DeviceInfoLinux();
44 : }
45 :
46 : #ifdef WEBRTC_LINUX
47 0 : void DeviceInfoLinux::HandleEvent(inotify_event* event, int fd)
48 : {
49 0 : if (event->mask & IN_CREATE) {
50 0 : if (fd == _fd_v4l || fd == _fd_snd) {
51 0 : DeviceChange();
52 0 : } else if ((event->mask & IN_ISDIR) && (fd == _fd_dev)) {
53 0 : if (_wd_v4l < 0) {
54 : // Sometimes inotify_add_watch failed if we call it immediately after receiving this event
55 : // Adding 5ms delay to let file system settle down
56 0 : usleep(5*1000);
57 0 : _wd_v4l = inotify_add_watch(_fd_v4l, "/dev/v4l/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
58 0 : if (_wd_v4l >= 0) {
59 0 : DeviceChange();
60 : }
61 : }
62 0 : if (_wd_snd < 0) {
63 0 : usleep(5*1000);
64 0 : _wd_snd = inotify_add_watch(_fd_snd, "/dev/snd/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
65 0 : if (_wd_snd >= 0) {
66 0 : DeviceChange();
67 : }
68 : }
69 : }
70 0 : } else if (event->mask & IN_DELETE) {
71 0 : if (fd == _fd_v4l || fd == _fd_snd) {
72 0 : DeviceChange();
73 : }
74 0 : } else if (event->mask & IN_DELETE_SELF) {
75 0 : if (fd == _fd_v4l) {
76 0 : inotify_rm_watch(_fd_v4l, _wd_v4l);
77 0 : _wd_v4l = -1;
78 0 : } else if (fd == _fd_snd) {
79 0 : inotify_rm_watch(_fd_snd, _wd_snd);
80 0 : _wd_snd = -1;
81 : } else {
82 0 : assert(false);
83 : }
84 : } else {
85 0 : char* cur_event_filename = NULL;
86 0 : int cur_event_wd = event->wd;
87 0 : if (event->len) {
88 0 : cur_event_filename = event->name;
89 : }
90 :
91 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
92 : "UNKNOWN EVENT OCCURRED for file \"%s\" on WD #%i\n",
93 : cur_event_filename, cur_event_wd);
94 : }
95 0 : }
96 :
97 0 : int DeviceInfoLinux::EventCheck(int fd)
98 : {
99 : struct timeval timeout;
100 : fd_set rfds;
101 :
102 0 : timeout.tv_sec = 0;
103 0 : timeout.tv_usec = 100000;
104 :
105 0 : FD_ZERO(&rfds);
106 0 : FD_SET(fd, &rfds);
107 :
108 0 : return select(fd+1, &rfds, NULL, NULL, &timeout);
109 : }
110 :
111 0 : int DeviceInfoLinux::HandleEvents(int fd)
112 : {
113 : char buffer[BUF_LEN];
114 :
115 0 : ssize_t r = read(fd, buffer, BUF_LEN);
116 :
117 0 : if (r <= 0) {
118 0 : return r;
119 : }
120 :
121 0 : ssize_t buffer_i = 0;
122 : inotify_event* pevent;
123 : size_t eventSize;
124 0 : int count = 0;
125 :
126 0 : while (buffer_i < r)
127 : {
128 0 : pevent = (inotify_event *) (&buffer[buffer_i]);
129 0 : eventSize = sizeof(inotify_event) + pevent->len;
130 : char event[sizeof(inotify_event) + FILENAME_MAX + 1] // null-terminated
131 : __attribute__ ((aligned(__alignof__(struct inotify_event))));
132 :
133 0 : memcpy(event, pevent, eventSize);
134 :
135 0 : HandleEvent((inotify_event*)(event), fd);
136 :
137 0 : buffer_i += eventSize;
138 0 : count++;
139 : }
140 :
141 0 : return count;
142 : }
143 :
144 0 : int DeviceInfoLinux::ProcessInotifyEvents()
145 : {
146 0 : while (0 == _isShutdown.Value()) {
147 0 : if (EventCheck(_fd_dev) > 0) {
148 0 : if (HandleEvents(_fd_dev) < 0) {
149 0 : break;
150 : }
151 : }
152 0 : if (EventCheck(_fd_v4l) > 0) {
153 0 : if (HandleEvents(_fd_v4l) < 0) {
154 0 : break;
155 : }
156 : }
157 0 : if (EventCheck(_fd_snd) > 0) {
158 0 : if (HandleEvents(_fd_snd) < 0) {
159 0 : break;
160 : }
161 : }
162 : }
163 0 : return 0;
164 : }
165 :
166 0 : bool DeviceInfoLinux::InotifyEventThread(void* obj)
167 : {
168 0 : return static_cast<DeviceInfoLinux*> (obj)->InotifyProcess();
169 : }
170 :
171 0 : bool DeviceInfoLinux::InotifyProcess()
172 : {
173 0 : _fd_v4l = inotify_init();
174 0 : _fd_snd = inotify_init();
175 0 : _fd_dev = inotify_init();
176 0 : if (_fd_v4l >= 0 && _fd_snd >= 0 && _fd_dev >= 0) {
177 0 : _wd_v4l = inotify_add_watch(_fd_v4l, "/dev/v4l/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
178 0 : _wd_snd = inotify_add_watch(_fd_snd, "/dev/snd/by-path/", IN_CREATE | IN_DELETE | IN_DELETE_SELF);
179 0 : _wd_dev = inotify_add_watch(_fd_dev, "/dev/", IN_CREATE);
180 0 : ProcessInotifyEvents();
181 :
182 0 : if (_wd_v4l >= 0) {
183 0 : inotify_rm_watch(_fd_v4l, _wd_v4l);
184 : }
185 :
186 0 : if (_wd_snd >= 0) {
187 0 : inotify_rm_watch(_fd_snd, _wd_snd);
188 : }
189 :
190 0 : if (_wd_dev >= 0) {
191 0 : inotify_rm_watch(_fd_dev, _wd_dev);
192 : }
193 :
194 0 : close(_fd_v4l);
195 0 : close(_fd_snd);
196 0 : close(_fd_dev);
197 0 : return true;
198 : } else {
199 0 : return false;
200 : }
201 : }
202 : #endif
203 :
204 0 : DeviceInfoLinux::DeviceInfoLinux()
205 : : DeviceInfoImpl()
206 : #ifdef WEBRTC_LINUX
207 : , _inotifyEventThread(new rtc::PlatformThread(
208 0 : InotifyEventThread, this, "InotifyEventThread"))
209 0 : , _isShutdown(0)
210 : #endif
211 : {
212 : #ifdef WEBRTC_LINUX
213 :
214 0 : if (_inotifyEventThread)
215 : {
216 0 : _inotifyEventThread->Start();
217 0 : _inotifyEventThread->SetPriority(rtc::kHighPriority);
218 : }
219 : #endif
220 0 : }
221 :
222 0 : int32_t DeviceInfoLinux::Init()
223 : {
224 0 : return 0;
225 : }
226 :
227 0 : DeviceInfoLinux::~DeviceInfoLinux()
228 : {
229 : #ifdef WEBRTC_LINUX
230 0 : ++_isShutdown;
231 :
232 0 : if (_inotifyEventThread) {
233 0 : _inotifyEventThread->Stop();
234 0 : _inotifyEventThread = nullptr;
235 : }
236 : #endif
237 0 : }
238 :
239 0 : uint32_t DeviceInfoLinux::NumberOfDevices()
240 : {
241 : WEBRTC_TRACE(webrtc::kTraceApiCall,
242 : webrtc::kTraceVideoCapture, 0, "%s", __FUNCTION__);
243 :
244 0 : uint32_t count = 0;
245 : char device[20];
246 0 : int fd = -1;
247 :
248 : /* detect /dev/video [0-63]VideoCaptureModule entries */
249 0 : for (int n = 0; n < 64; n++)
250 : {
251 0 : sprintf(device, "/dev/video%d", n);
252 0 : if ((fd = open(device, O_RDONLY)) != -1)
253 : {
254 0 : close(fd);
255 0 : count++;
256 : }
257 : }
258 :
259 0 : return count;
260 : }
261 :
262 0 : int32_t DeviceInfoLinux::GetDeviceName(
263 : uint32_t deviceNumber,
264 : char* deviceNameUTF8,
265 : uint32_t deviceNameLength,
266 : char* deviceUniqueIdUTF8,
267 : uint32_t deviceUniqueIdUTF8Length,
268 : char* /*productUniqueIdUTF8*/,
269 : uint32_t /*productUniqueIdUTF8Length*/,
270 : pid_t* /*pid*/)
271 : {
272 : WEBRTC_TRACE(webrtc::kTraceApiCall,
273 : webrtc::kTraceVideoCapture, 0, "%s", __FUNCTION__);
274 :
275 : // Travel through /dev/video [0-63]
276 0 : uint32_t count = 0;
277 : char device[20];
278 0 : int fd = -1;
279 0 : bool found = false;
280 : int device_index;
281 0 : for (device_index = 0; device_index < 64; device_index++)
282 : {
283 0 : sprintf(device, "/dev/video%d", device_index);
284 0 : if ((fd = open(device, O_RDONLY)) != -1)
285 : {
286 0 : if (count == deviceNumber) {
287 : // Found the device
288 0 : found = true;
289 0 : break;
290 : } else {
291 0 : close(fd);
292 0 : count++;
293 : }
294 : }
295 : }
296 :
297 0 : if (!found)
298 0 : return -1;
299 :
300 : // query device capabilities
301 : struct v4l2_capability cap;
302 0 : if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
303 : {
304 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
305 : "error in querying the device capability for device %s. errno = %d",
306 : device, errno);
307 0 : close(fd);
308 0 : return -1;
309 : }
310 :
311 0 : close(fd);
312 :
313 : char cameraName[64];
314 0 : memset(deviceNameUTF8, 0, deviceNameLength);
315 0 : memcpy(cameraName, cap.card, sizeof(cap.card));
316 :
317 0 : if (deviceNameLength >= strlen(cameraName))
318 : {
319 0 : memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
320 : }
321 : else
322 : {
323 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
324 : "buffer passed is too small");
325 0 : return -1;
326 : }
327 :
328 0 : if (cap.bus_info[0] != 0) // may not available in all drivers
329 : {
330 : // copy device id
331 0 : if (deviceUniqueIdUTF8Length >= strlen((const char*) cap.bus_info))
332 : {
333 0 : memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
334 0 : memcpy(deviceUniqueIdUTF8, cap.bus_info,
335 0 : strlen((const char*) cap.bus_info));
336 : }
337 : else
338 : {
339 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
340 : "buffer passed is too small");
341 0 : return -1;
342 : }
343 : } else {
344 : // if there's no bus info to use for uniqueId, invent one - and it has to be repeatable
345 0 : if (snprintf(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length, "fake_%u", device_index) >=
346 0 : (int) deviceUniqueIdUTF8Length)
347 : {
348 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
349 : "buffer passed is too small");
350 0 : return -1;
351 : }
352 : }
353 :
354 0 : return 0;
355 : }
356 :
357 0 : int32_t DeviceInfoLinux::CreateCapabilityMap(
358 : const char* deviceUniqueIdUTF8)
359 : {
360 : int fd;
361 : char device[32];
362 0 : bool found = false;
363 : int device_index;
364 :
365 : const int32_t deviceUniqueIdUTF8Length =
366 0 : (int32_t) strlen((char*) deviceUniqueIdUTF8);
367 0 : if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
368 : {
369 : WEBRTC_TRACE(webrtc::kTraceError,
370 : webrtc::kTraceVideoCapture, 0, "Device name too long");
371 0 : return -1;
372 : }
373 : WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0,
374 : "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8);
375 :
376 : /* detect /dev/video [0-63] entries */
377 0 : if (sscanf(deviceUniqueIdUTF8,"fake_%d",&device_index) == 1)
378 : {
379 0 : sprintf(device, "/dev/video%d", device_index);
380 0 : fd = open(device, O_RDONLY);
381 0 : if (fd != -1) {
382 0 : found = true;
383 : }
384 : } else {
385 : /* detect /dev/video [0-63] entries */
386 0 : for (int n = 0; n < 64; ++n)
387 : {
388 0 : sprintf(device, "/dev/video%d", n);
389 0 : fd = open(device, O_RDONLY);
390 0 : if (fd == -1)
391 0 : continue;
392 :
393 : // query device capabilities
394 : struct v4l2_capability cap;
395 0 : if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0)
396 : {
397 0 : if (cap.bus_info[0] != 0)
398 : {
399 0 : if (strncmp((const char*) cap.bus_info,
400 : (const char*) deviceUniqueIdUTF8,
401 : strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id
402 : {
403 0 : found = true;
404 0 : break; // fd matches with device unique id supplied
405 : }
406 : }
407 : // else can't be a match as the test for fake_* above would have matched it
408 : }
409 0 : close(fd); // close since this is not the matching device
410 : }
411 : }
412 0 : if (!found)
413 : {
414 : WEBRTC_TRACE(webrtc::kTraceError,
415 : webrtc::kTraceVideoCapture, 0, "no matching device found");
416 0 : return -1;
417 : }
418 :
419 : // now fd will point to the matching device
420 : // reset old capability list.
421 0 : _captureCapabilities.clear();
422 :
423 0 : int size = FillCapabilities(fd);
424 0 : close(fd);
425 :
426 : // Store the new used device name
427 0 : _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
428 0 : _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
429 0 : _lastUsedDeviceNameLength + 1);
430 0 : memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1);
431 :
432 : WEBRTC_TRACE(webrtc::kTraceInfo,
433 : webrtc::kTraceVideoCapture,
434 : 0,
435 : "CreateCapabilityMap %u",
436 : static_cast<unsigned int>(_captureCapabilities.size()));
437 :
438 0 : return size;
439 : }
440 :
441 0 : bool DeviceInfoLinux::IsDeviceNameMatches(const char* name,
442 : const char* deviceUniqueIdUTF8)
443 : {
444 0 : if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
445 0 : return true;
446 0 : return false;
447 : }
448 :
449 0 : int32_t DeviceInfoLinux::FillCapabilities(int fd)
450 : {
451 : struct v4l2_fmtdesc fmt;
452 : struct v4l2_frmsizeenum frmsize;
453 : struct v4l2_frmivalenum frmival;
454 :
455 0 : fmt.index = 0;
456 0 : fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
457 0 : while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) {
458 0 : frmsize.pixel_format = fmt.pixelformat;
459 0 : frmsize.index = 0;
460 0 : while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
461 0 : if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
462 0 : frmival.index = 0;
463 0 : frmival.pixel_format = fmt.pixelformat;
464 0 : frmival.width = frmsize.discrete.width;
465 0 : frmival.height = frmsize.discrete.height;
466 0 : if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
467 0 : if (fmt.pixelformat == V4L2_PIX_FMT_YUYV ||
468 0 : fmt.pixelformat == V4L2_PIX_FMT_YUV420 ||
469 0 : fmt.pixelformat == V4L2_PIX_FMT_MJPEG) {
470 :
471 0 : VideoCaptureCapability cap;
472 0 : cap.width = frmsize.discrete.width;
473 0 : cap.height = frmsize.discrete.height;
474 0 : cap.expectedCaptureDelay = 120;
475 :
476 0 : if (fmt.pixelformat == V4L2_PIX_FMT_YUYV)
477 : {
478 0 : cap.rawType = kVideoYUY2;
479 : }
480 0 : else if (fmt.pixelformat == V4L2_PIX_FMT_YUV420)
481 : {
482 0 : cap.rawType = kVideoI420;
483 : }
484 0 : else if (fmt.pixelformat == V4L2_PIX_FMT_MJPEG)
485 : {
486 0 : cap.rawType = kVideoMJPEG;
487 : }
488 :
489 0 : cap.maxFPS = frmival.discrete.denominator / frmival.discrete.numerator;
490 0 : _captureCapabilities.push_back(cap);
491 : }
492 : }
493 : }
494 0 : frmsize.index++;
495 : }
496 0 : fmt.index++;
497 : }
498 :
499 : WEBRTC_TRACE(webrtc::kTraceInfo,
500 : webrtc::kTraceVideoCapture,
501 : 0,
502 : "CreateCapabilityMap %u",
503 : static_cast<unsigned int>(_captureCapabilities.size()));
504 0 : return _captureCapabilities.size();
505 : }
506 :
507 : } // namespace videocapturemodule
508 : } // namespace webrtc
|