LCOV - code coverage report
Current view: top level - media/webrtc/trunk/webrtc/modules/video_capture/linux - device_info_linux.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 0 210 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 16 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 "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

Generated by: LCOV version 1.13