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