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