LCOV - code coverage report
Current view: top level - media/webrtc/trunk/webrtc/modules/video_capture/linux - video_capture_linux.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 213 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 13 0.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13