Home | History | Annotate | Download | only in linux
      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 
     29 namespace webrtc
     30 {
     31 namespace videocapturemodule
     32 {
     33 VideoCaptureModule* 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 
     48 VideoCaptureModuleV4L2::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 
     63 int32_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 
    111 VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2()
    112 {
    113     StopCapture();
    114     if (_captureCritSect)
    115     {
    116         delete _captureCritSect;
    117     }
    118     if (_deviceFd != -1)
    119       close(_deviceFd);
    120 }
    121 
    122 int32_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 
    303 int32_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 
    326 bool 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 
    383 bool 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 
    403 bool VideoCaptureModuleV4L2::CaptureStarted()
    404 {
    405     return _captureStarted;
    406 }
    407 
    408 bool VideoCaptureModuleV4L2::CaptureThread(void* obj)
    409 {
    410     return static_cast<VideoCaptureModuleV4L2*> (obj)->CaptureProcess();
    411 }
    412 bool 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 
    482 int32_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