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 "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 #include <linux/videodev2.h>
     22 
     23 #include "webrtc/system_wrappers/interface/ref_count.h"
     24 #include "webrtc/system_wrappers/interface/trace.h"
     25 
     26 
     27 namespace webrtc
     28 {
     29 namespace videocapturemodule
     30 {
     31 VideoCaptureModule::DeviceInfo*
     32 VideoCaptureImpl::CreateDeviceInfo(const int32_t id)
     33 {
     34     videocapturemodule::DeviceInfoLinux *deviceInfo =
     35                     new videocapturemodule::DeviceInfoLinux(id);
     36     if (!deviceInfo)
     37     {
     38         deviceInfo = NULL;
     39     }
     40 
     41     return deviceInfo;
     42 }
     43 
     44 DeviceInfoLinux::DeviceInfoLinux(const int32_t id)
     45     : DeviceInfoImpl(id)
     46 {
     47 }
     48 
     49 int32_t DeviceInfoLinux::Init()
     50 {
     51     return 0;
     52 }
     53 
     54 DeviceInfoLinux::~DeviceInfoLinux()
     55 {
     56 }
     57 
     58 uint32_t DeviceInfoLinux::NumberOfDevices()
     59 {
     60     WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__);
     61 
     62     uint32_t count = 0;
     63     char device[20];
     64     int fd = -1;
     65 
     66     /* detect /dev/video [0-63]VideoCaptureModule entries */
     67     for (int n = 0; n < 64; n++)
     68     {
     69         sprintf(device, "/dev/video%d", n);
     70         if ((fd = open(device, O_RDONLY)) != -1)
     71         {
     72             close(fd);
     73             count++;
     74         }
     75     }
     76 
     77     return count;
     78 }
     79 
     80 int32_t DeviceInfoLinux::GetDeviceName(
     81                                          uint32_t deviceNumber,
     82                                          char* deviceNameUTF8,
     83                                          uint32_t deviceNameLength,
     84                                          char* deviceUniqueIdUTF8,
     85                                          uint32_t deviceUniqueIdUTF8Length,
     86                                          char* /*productUniqueIdUTF8*/,
     87                                          uint32_t /*productUniqueIdUTF8Length*/)
     88 {
     89     WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__);
     90 
     91     // Travel through /dev/video [0-63]
     92     uint32_t count = 0;
     93     char device[20];
     94     int fd = -1;
     95     bool found = false;
     96     for (int n = 0; n < 64; n++)
     97     {
     98         sprintf(device, "/dev/video%d", n);
     99         if ((fd = open(device, O_RDONLY)) != -1)
    100         {
    101             if (count == deviceNumber) {
    102                 // Found the device
    103                 found = true;
    104                 break;
    105             } else {
    106                 close(fd);
    107                 count++;
    108             }
    109         }
    110     }
    111 
    112     if (!found)
    113         return -1;
    114 
    115     // query device capabilities
    116     struct v4l2_capability cap;
    117     if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
    118     {
    119         WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
    120                    "error in querying the device capability for device %s. errno = %d",
    121                    device, errno);
    122         close(fd);
    123         return -1;
    124     }
    125 
    126     close(fd);
    127 
    128     char cameraName[64];
    129     memset(deviceNameUTF8, 0, deviceNameLength);
    130     memcpy(cameraName, cap.card, sizeof(cap.card));
    131 
    132     if (deviceNameLength >= strlen(cameraName))
    133     {
    134         memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
    135     }
    136     else
    137     {
    138         WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "buffer passed is too small");
    139         return -1;
    140     }
    141 
    142     if (cap.bus_info[0] != 0) // may not available in all drivers
    143     {
    144         // copy device id
    145         if (deviceUniqueIdUTF8Length >= strlen((const char*) cap.bus_info))
    146         {
    147             memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
    148             memcpy(deviceUniqueIdUTF8, cap.bus_info,
    149                    strlen((const char*) cap.bus_info));
    150         }
    151         else
    152         {
    153             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
    154                        "buffer passed is too small");
    155             return -1;
    156         }
    157     }
    158 
    159     return 0;
    160 }
    161 
    162 int32_t DeviceInfoLinux::CreateCapabilityMap(
    163                                         const char* deviceUniqueIdUTF8)
    164 {
    165     int fd;
    166     char device[32];
    167     bool found = false;
    168 
    169     const int32_t deviceUniqueIdUTF8Length =
    170                             (int32_t) strlen((char*) deviceUniqueIdUTF8);
    171     if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength)
    172     {
    173         WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "Device name too long");
    174         return -1;
    175     }
    176     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
    177                "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8);
    178 
    179     /* detect /dev/video [0-63] entries */
    180     for (int n = 0; n < 64; ++n)
    181     {
    182         sprintf(device, "/dev/video%d", n);
    183         fd = open(device, O_RDONLY);
    184         if (fd == -1)
    185           continue;
    186 
    187         // query device capabilities
    188         struct v4l2_capability cap;
    189         if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0)
    190         {
    191             if (cap.bus_info[0] != 0)
    192             {
    193                 if (strncmp((const char*) cap.bus_info,
    194                             (const char*) deviceUniqueIdUTF8,
    195                             strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id
    196                 {
    197                     found = true;
    198                     break; // fd matches with device unique id supplied
    199                 }
    200             }
    201             else //match for device name
    202             {
    203                 if (IsDeviceNameMatches((const char*) cap.card,
    204                                         (const char*) deviceUniqueIdUTF8))
    205                 {
    206                     found = true;
    207                     break;
    208                 }
    209             }
    210         }
    211         close(fd); // close since this is not the matching device
    212     }
    213 
    214     if (!found)
    215     {
    216         WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "no matching device found");
    217         return -1;
    218     }
    219 
    220     // now fd will point to the matching device
    221     // reset old capability list.
    222     _captureCapabilities.clear();
    223 
    224     int size = FillCapabilities(fd);
    225     close(fd);
    226 
    227     // Store the new used device name
    228     _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
    229     _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName,
    230                                                    _lastUsedDeviceNameLength + 1);
    231     memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1);
    232 
    233     WEBRTC_TRACE(webrtc::kTraceInfo,
    234                  webrtc::kTraceVideoCapture,
    235                  _id,
    236                  "CreateCapabilityMap %u",
    237                  static_cast<unsigned int>(_captureCapabilities.size()));
    238 
    239     return size;
    240 }
    241 
    242 bool DeviceInfoLinux::IsDeviceNameMatches(const char* name,
    243                                           const char* deviceUniqueIdUTF8)
    244 {
    245     if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
    246             return true;
    247     return false;
    248 }
    249 
    250 int32_t DeviceInfoLinux::FillCapabilities(int fd)
    251 {
    252 
    253     // set image format
    254     struct v4l2_format video_fmt;
    255     memset(&video_fmt, 0, sizeof(struct v4l2_format));
    256 
    257     video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    258     video_fmt.fmt.pix.sizeimage = 0;
    259 
    260     int totalFmts = 3;
    261     unsigned int videoFormats[] = {
    262         V4L2_PIX_FMT_MJPEG,
    263         V4L2_PIX_FMT_YUV420,
    264         V4L2_PIX_FMT_YUYV };
    265 
    266     int sizes = 13;
    267     unsigned int size[][2] = { { 128, 96 }, { 160, 120 }, { 176, 144 },
    268                                { 320, 240 }, { 352, 288 }, { 640, 480 },
    269                                { 704, 576 }, { 800, 600 }, { 960, 720 },
    270                                { 1280, 720 }, { 1024, 768 }, { 1440, 1080 },
    271                                { 1920, 1080 } };
    272 
    273     int index = 0;
    274     for (int fmts = 0; fmts < totalFmts; fmts++)
    275     {
    276         for (int i = 0; i < sizes; i++)
    277         {
    278             video_fmt.fmt.pix.pixelformat = videoFormats[fmts];
    279             video_fmt.fmt.pix.width = size[i][0];
    280             video_fmt.fmt.pix.height = size[i][1];
    281 
    282             if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0)
    283             {
    284                 if ((video_fmt.fmt.pix.width == size[i][0])
    285                     && (video_fmt.fmt.pix.height == size[i][1]))
    286                 {
    287                     VideoCaptureCapability cap;
    288                     cap.width = video_fmt.fmt.pix.width;
    289                     cap.height = video_fmt.fmt.pix.height;
    290                     cap.expectedCaptureDelay = 120;
    291                     if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV)
    292                     {
    293                         cap.rawType = kVideoYUY2;
    294                     }
    295                     else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420)
    296                     {
    297                         cap.rawType = kVideoI420;
    298                     }
    299                     else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG)
    300                     {
    301                         cap.rawType = kVideoMJPEG;
    302                     }
    303 
    304                     // get fps of current camera mode
    305                     // V4l2 does not have a stable method of knowing so we just guess.
    306                     if(cap.width >= 800 && cap.rawType != kVideoMJPEG)
    307                     {
    308                         cap.maxFPS = 15;
    309                     }
    310                     else
    311                     {
    312                         cap.maxFPS = 30;
    313                     }
    314 
    315                     _captureCapabilities.push_back(cap);
    316                     index++;
    317                     WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id,
    318                                "Camera capability, width:%d height:%d type:%d fps:%d",
    319                                cap.width, cap.height, cap.rawType, cap.maxFPS);
    320                 }
    321             }
    322         }
    323     }
    324 
    325     WEBRTC_TRACE(webrtc::kTraceInfo,
    326                  webrtc::kTraceVideoCapture,
    327                  _id,
    328                  "CreateCapabilityMap %u",
    329                  static_cast<unsigned int>(_captureCapabilities.size()));
    330     return _captureCapabilities.size();
    331 }
    332 
    333 }  // namespace videocapturemodule
    334 }  // namespace webrtc
    335