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 <errno.h>
12 : #include <fcntl.h>
13 : #include <stdio.h>
14 : #include <string.h>
15 : #include <sys/ioctl.h>
16 : #include <sys/mman.h>
17 : #include <sys/stat.h>
18 : #include <unistd.h>
19 : //v4l includes
20 : #if defined(__NetBSD__) || defined(__OpenBSD__)
21 : #include <sys/videoio.h>
22 : #elif defined(__sun)
23 : #include <sys/videodev2.h>
24 : #else
25 : #include <linux/videodev2.h>
26 : #endif
27 :
28 : #include <new>
29 :
30 : #include "webrtc/base/refcount.h"
31 : #include "webrtc/base/scoped_ref_ptr.h"
32 : #include "webrtc/modules/video_capture/linux/video_capture_linux.h"
33 : #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
34 : #include "webrtc/system_wrappers/include/trace.h"
35 :
36 : namespace webrtc {
37 : namespace videocapturemodule {
38 0 : rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(
39 : const char* deviceUniqueId) {
40 : rtc::scoped_refptr<VideoCaptureModuleV4L2> implementation(
41 0 : new rtc::RefCountedObject<VideoCaptureModuleV4L2>());
42 :
43 0 : if (implementation->Init(deviceUniqueId) != 0)
44 0 : return nullptr;
45 :
46 0 : return implementation;
47 : }
48 :
49 0 : VideoCaptureModuleV4L2::VideoCaptureModuleV4L2()
50 : : VideoCaptureImpl(),
51 0 : _captureCritSect(CriticalSectionWrapper::CreateCriticalSection()),
52 : _deviceId(-1),
53 : _deviceFd(-1),
54 : _buffersAllocatedByDevice(-1),
55 : _currentWidth(-1),
56 : _currentHeight(-1),
57 : _currentFrameRate(-1),
58 : _captureStarted(false),
59 : _captureVideoType(kVideoI420),
60 0 : _pool(NULL)
61 : {
62 0 : }
63 :
64 0 : int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8)
65 : {
66 0 : int len = strlen((const char*) deviceUniqueIdUTF8);
67 0 : _deviceUniqueId = new (std::nothrow) char[len + 1];
68 0 : if (_deviceUniqueId)
69 : {
70 0 : memcpy(_deviceUniqueId, deviceUniqueIdUTF8, len + 1);
71 : }
72 :
73 : int device_index;
74 0 : if (sscanf(deviceUniqueIdUTF8,"fake_%d", &device_index) == 1)
75 : {
76 0 : _deviceId = device_index;
77 0 : return 0;
78 : }
79 :
80 : int fd;
81 : char device[32];
82 0 : bool found = false;
83 :
84 : /* detect /dev/video [0-63] entries */
85 : int n;
86 0 : for (n = 0; n < 64; n++)
87 : {
88 0 : sprintf(device, "/dev/video%d", n);
89 0 : if ((fd = open(device, O_RDONLY)) != -1)
90 : {
91 : // query device capabilities
92 : struct v4l2_capability cap;
93 0 : if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0)
94 : {
95 0 : if (cap.bus_info[0] != 0)
96 : {
97 0 : if (strncmp((const char*) cap.bus_info,
98 : (const char*) deviceUniqueIdUTF8,
99 : strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id
100 : {
101 0 : close(fd);
102 0 : found = true;
103 0 : break; // fd matches with device unique id supplied
104 : }
105 : }
106 : }
107 0 : close(fd); // close since this is not the matching device
108 : }
109 : }
110 0 : if (!found)
111 : {
112 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
113 : 0, "no matching device found");
114 0 : return -1;
115 : }
116 0 : _deviceId = n; //store the device id
117 0 : return 0;
118 : }
119 :
120 0 : VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2()
121 : {
122 0 : StopCapture();
123 0 : if (_captureCritSect)
124 : {
125 0 : delete _captureCritSect;
126 : }
127 0 : if (_deviceFd != -1)
128 0 : close(_deviceFd);
129 0 : }
130 :
131 0 : int32_t VideoCaptureModuleV4L2::StartCapture(
132 : const VideoCaptureCapability& capability)
133 : {
134 0 : if (_captureStarted)
135 : {
136 0 : if (capability.width == _currentWidth &&
137 0 : capability.height == _currentHeight &&
138 0 : _captureVideoType == capability.rawType)
139 : {
140 0 : return 0;
141 : }
142 : else
143 : {
144 0 : StopCapture();
145 : }
146 : }
147 :
148 0 : CriticalSectionScoped cs(_captureCritSect);
149 : //first open /dev/video device
150 : char device[20];
151 0 : sprintf(device, "/dev/video%d", (int) _deviceId);
152 :
153 0 : if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0)
154 : {
155 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
156 : "error in opening %s errono = %d", device, errno);
157 0 : return -1;
158 : }
159 :
160 : // Supported video formats in preferred order.
161 : // If the requested resolution is larger than VGA, we prefer MJPEG. Go for
162 : // I420 otherwise.
163 0 : const int nFormats = 5;
164 : unsigned int fmts[nFormats];
165 0 : if (capability.width > 640 || capability.height > 480) {
166 0 : fmts[0] = V4L2_PIX_FMT_MJPEG;
167 0 : fmts[1] = V4L2_PIX_FMT_YUV420;
168 0 : fmts[2] = V4L2_PIX_FMT_YUYV;
169 0 : fmts[3] = V4L2_PIX_FMT_UYVY;
170 0 : fmts[4] = V4L2_PIX_FMT_JPEG;
171 : } else {
172 0 : fmts[0] = V4L2_PIX_FMT_YUV420;
173 0 : fmts[1] = V4L2_PIX_FMT_YUYV;
174 0 : fmts[2] = V4L2_PIX_FMT_UYVY;
175 0 : fmts[3] = V4L2_PIX_FMT_MJPEG;
176 0 : fmts[4] = V4L2_PIX_FMT_JPEG;
177 : }
178 :
179 : // Enumerate image formats.
180 : struct v4l2_fmtdesc fmt;
181 0 : int fmtsIdx = nFormats;
182 0 : memset(&fmt, 0, sizeof(fmt));
183 0 : fmt.index = 0;
184 0 : fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
185 : WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0,
186 : "Video Capture enumerats supported image formats:");
187 0 : while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) {
188 : WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0,
189 : " { pixelformat = %c%c%c%c, description = '%s' }",
190 : fmt.pixelformat & 0xFF, (fmt.pixelformat>>8) & 0xFF,
191 : (fmt.pixelformat>>16) & 0xFF, (fmt.pixelformat>>24) & 0xFF,
192 : fmt.description);
193 : // Match the preferred order.
194 0 : for (int i = 0; i < nFormats; i++) {
195 0 : if (fmt.pixelformat == fmts[i] && i < fmtsIdx)
196 0 : fmtsIdx = i;
197 : }
198 : // Keep enumerating.
199 0 : fmt.index++;
200 : }
201 :
202 0 : if (fmtsIdx == nFormats)
203 : {
204 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
205 : "no supporting video formats found");
206 0 : return -1;
207 : } else {
208 : WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0,
209 : "We prefer format %c%c%c%c",
210 : fmts[fmtsIdx] & 0xFF, (fmts[fmtsIdx]>>8) & 0xFF,
211 : (fmts[fmtsIdx]>>16) & 0xFF, (fmts[fmtsIdx]>>24) & 0xFF);
212 : }
213 :
214 : struct v4l2_format video_fmt;
215 0 : memset(&video_fmt, 0, sizeof(struct v4l2_format));
216 0 : video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
217 0 : video_fmt.fmt.pix.sizeimage = 0;
218 0 : video_fmt.fmt.pix.width = capability.width;
219 0 : video_fmt.fmt.pix.height = capability.height;
220 0 : video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx];
221 :
222 0 : if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
223 0 : _captureVideoType = kVideoYUY2;
224 0 : else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
225 0 : _captureVideoType = kVideoI420;
226 0 : else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
227 0 : _captureVideoType = kVideoUYVY;
228 0 : else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG ||
229 0 : video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
230 0 : _captureVideoType = kVideoMJPEG;
231 :
232 : //set format and frame size now
233 0 : if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0)
234 : {
235 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
236 : "error in VIDIOC_S_FMT, errno = %d", errno);
237 0 : return -1;
238 : }
239 :
240 : // initialize current width and height
241 0 : _currentWidth = video_fmt.fmt.pix.width;
242 0 : _currentHeight = video_fmt.fmt.pix.height;
243 0 : _captureDelay = 120;
244 :
245 : // Trying to set frame rate, before check driver capability.
246 0 : bool driver_framerate_support = true;
247 : struct v4l2_streamparm streamparms;
248 0 : memset(&streamparms, 0, sizeof(streamparms));
249 0 : streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
250 0 : if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) {
251 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
252 : "error in VIDIOC_G_PARM errno = %d", errno);
253 0 : driver_framerate_support = false;
254 : // continue
255 : } else {
256 : // check the capability flag is set to V4L2_CAP_TIMEPERFRAME.
257 0 : if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
258 : // driver supports the feature. Set required framerate.
259 0 : memset(&streamparms, 0, sizeof(streamparms));
260 0 : streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
261 0 : streamparms.parm.capture.timeperframe.numerator = 1;
262 0 : streamparms.parm.capture.timeperframe.denominator = capability.maxFPS;
263 0 : if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) {
264 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
265 : "Failed to set the framerate. errno=%d", errno);
266 0 : driver_framerate_support = false;
267 : } else {
268 0 : _currentFrameRate = capability.maxFPS;
269 : }
270 : }
271 : }
272 : // If driver doesn't support framerate control, need to hardcode.
273 : // Hardcoding the value based on the frame size.
274 0 : if (!driver_framerate_support) {
275 0 : if(_currentWidth >= 800 && _captureVideoType != kVideoMJPEG) {
276 0 : _currentFrameRate = 15;
277 : } else {
278 0 : _currentFrameRate = 30;
279 : }
280 : }
281 :
282 0 : if (!AllocateVideoBuffers())
283 : {
284 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
285 : "failed to allocate video capture buffers");
286 0 : return -1;
287 : }
288 :
289 : //start capture thread;
290 0 : if (!_captureThread)
291 : {
292 0 : _captureThread.reset(new rtc::PlatformThread(
293 0 : VideoCaptureModuleV4L2::CaptureThread, this, "CaptureThread"));
294 0 : _captureThread->Start();
295 0 : _captureThread->SetPriority(rtc::kHighPriority);
296 : }
297 :
298 : // Needed to start UVC camera - from the uvcview application
299 : enum v4l2_buf_type type;
300 0 : type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
301 0 : if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1)
302 : {
303 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
304 : "Failed to turn on stream");
305 0 : return -1;
306 : }
307 :
308 0 : _captureStarted = true;
309 0 : return 0;
310 : }
311 :
312 0 : int32_t VideoCaptureModuleV4L2::StopCapture()
313 : {
314 0 : if (_captureThread) {
315 : // Make sure the capture thread stop stop using the critsect.
316 0 : _captureThread->Stop();
317 0 : _captureThread.reset();
318 : }
319 :
320 0 : CriticalSectionScoped cs(_captureCritSect);
321 0 : if (_captureStarted)
322 : {
323 0 : _captureStarted = false;
324 :
325 0 : DeAllocateVideoBuffers();
326 0 : close(_deviceFd);
327 0 : _deviceFd = -1;
328 : }
329 :
330 0 : return 0;
331 : }
332 :
333 : //critical section protected by the caller
334 :
335 0 : bool VideoCaptureModuleV4L2::AllocateVideoBuffers()
336 : {
337 : struct v4l2_requestbuffers rbuffer;
338 0 : memset(&rbuffer, 0, sizeof(v4l2_requestbuffers));
339 :
340 0 : rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
341 0 : rbuffer.memory = V4L2_MEMORY_MMAP;
342 0 : rbuffer.count = kNoOfV4L2Bufffers;
343 :
344 0 : if (ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer) < 0)
345 : {
346 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
347 : "Could not get buffers from device. errno = %d", errno);
348 0 : return false;
349 : }
350 :
351 0 : if (rbuffer.count > kNoOfV4L2Bufffers)
352 0 : rbuffer.count = kNoOfV4L2Bufffers;
353 :
354 0 : _buffersAllocatedByDevice = rbuffer.count;
355 :
356 : //Map the buffers
357 0 : _pool = new Buffer[rbuffer.count];
358 :
359 0 : for (unsigned int i = 0; i < rbuffer.count; i++)
360 : {
361 : struct v4l2_buffer buffer;
362 0 : memset(&buffer, 0, sizeof(v4l2_buffer));
363 0 : buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
364 0 : buffer.memory = V4L2_MEMORY_MMAP;
365 0 : buffer.index = i;
366 :
367 0 : if (ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer) < 0)
368 : {
369 0 : return false;
370 : }
371 :
372 0 : _pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
373 0 : _deviceFd, buffer.m.offset);
374 :
375 0 : if (MAP_FAILED == _pool[i].start)
376 : {
377 0 : for (unsigned int j = 0; j < i; j++)
378 0 : munmap(_pool[j].start, _pool[j].length);
379 0 : return false;
380 : }
381 :
382 0 : _pool[i].length = buffer.length;
383 :
384 0 : if (ioctl(_deviceFd, VIDIOC_QBUF, &buffer) < 0)
385 : {
386 0 : return false;
387 : }
388 : }
389 0 : return true;
390 : }
391 :
392 0 : bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers()
393 : {
394 : // unmap buffers
395 0 : for (int i = 0; i < _buffersAllocatedByDevice; i++)
396 0 : munmap(_pool[i].start, _pool[i].length);
397 :
398 0 : delete[] _pool;
399 :
400 : // turn off stream
401 : enum v4l2_buf_type type;
402 0 : type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
403 0 : if (ioctl(_deviceFd, VIDIOC_STREAMOFF, &type) < 0)
404 : {
405 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
406 : "VIDIOC_STREAMOFF error. errno: %d", errno);
407 : }
408 :
409 0 : return true;
410 : }
411 :
412 0 : bool VideoCaptureModuleV4L2::CaptureStarted()
413 : {
414 0 : return _captureStarted;
415 : }
416 :
417 0 : bool VideoCaptureModuleV4L2::CaptureThread(void* obj)
418 : {
419 0 : return static_cast<VideoCaptureModuleV4L2*> (obj)->CaptureProcess();
420 : }
421 0 : bool VideoCaptureModuleV4L2::CaptureProcess()
422 : {
423 0 : int retVal = 0;
424 : fd_set rSet;
425 : struct timeval timeout;
426 :
427 0 : _captureCritSect->Enter();
428 :
429 0 : FD_ZERO(&rSet);
430 0 : FD_SET(_deviceFd, &rSet);
431 0 : timeout.tv_sec = 1;
432 0 : timeout.tv_usec = 0;
433 :
434 0 : retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout);
435 0 : if (retVal < 0 && errno != EINTR) // continue if interrupted
436 : {
437 : // select failed
438 0 : _captureCritSect->Leave();
439 0 : return false;
440 : }
441 0 : else if (retVal == 0)
442 : {
443 : // select timed out
444 0 : _captureCritSect->Leave();
445 0 : return true;
446 : }
447 0 : else if (!FD_ISSET(_deviceFd, &rSet))
448 : {
449 : // not event on camera handle
450 0 : _captureCritSect->Leave();
451 0 : return true;
452 : }
453 :
454 0 : if (_captureStarted)
455 : {
456 : struct v4l2_buffer buf;
457 0 : memset(&buf, 0, sizeof(struct v4l2_buffer));
458 0 : buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
459 0 : buf.memory = V4L2_MEMORY_MMAP;
460 : // dequeue a buffer - repeat until dequeued properly!
461 0 : while (ioctl(_deviceFd, VIDIOC_DQBUF, &buf) < 0)
462 : {
463 0 : if (errno != EINTR)
464 : {
465 : WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0,
466 : "could not sync on a buffer on device %s", strerror(errno));
467 0 : _captureCritSect->Leave();
468 0 : return true;
469 : }
470 : }
471 0 : VideoCaptureCapability frameInfo;
472 0 : frameInfo.width = _currentWidth;
473 0 : frameInfo.height = _currentHeight;
474 0 : frameInfo.rawType = _captureVideoType;
475 :
476 : // convert to to I420 if needed
477 0 : IncomingFrame((unsigned char*) _pool[buf.index].start,
478 0 : buf.bytesused, frameInfo);
479 : // enqueue the buffer again
480 0 : if (ioctl(_deviceFd, VIDIOC_QBUF, &buf) == -1)
481 : {
482 : WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, 0,
483 : "Failed to enqueue capture buffer");
484 : }
485 : }
486 0 : _captureCritSect->Leave();
487 0 : usleep(0);
488 0 : return true;
489 : }
490 :
491 0 : int32_t VideoCaptureModuleV4L2::CaptureSettings(VideoCaptureCapability& settings)
492 : {
493 0 : settings.width = _currentWidth;
494 0 : settings.height = _currentHeight;
495 0 : settings.maxFPS = _currentFrameRate;
496 0 : settings.rawType=_captureVideoType;
497 :
498 0 : return 0;
499 : }
500 : } // namespace videocapturemodule
501 : } // namespace webrtc
|