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 "modules/video_capture/linux/device_info_v4l2.h"
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <unistd.h>
20 // v4l includes
21 #include <linux/videodev2.h>
22
23 #include <vector>
24
25 #include "modules/video_capture/video_capture.h"
26 #include "modules/video_capture/video_capture_defines.h"
27 #include "modules/video_capture/video_capture_impl.h"
28 #include "rtc_base/logging.h"
29
30 namespace webrtc {
31 namespace videocapturemodule {
DeviceInfoV4l2()32 DeviceInfoV4l2::DeviceInfoV4l2() : DeviceInfoImpl() {}
33
Init()34 int32_t DeviceInfoV4l2::Init() {
35 return 0;
36 }
37
~DeviceInfoV4l2()38 DeviceInfoV4l2::~DeviceInfoV4l2() {}
39
NumberOfDevices()40 uint32_t DeviceInfoV4l2::NumberOfDevices() {
41 uint32_t count = 0;
42 char device[20];
43 int fd = -1;
44 struct v4l2_capability cap;
45
46 /* detect /dev/video [0-63]VideoCaptureModule entries */
47 for (int n = 0; n < 64; n++) {
48 snprintf(device, sizeof(device), "/dev/video%d", n);
49 if ((fd = open(device, O_RDONLY)) != -1) {
50 // query device capabilities and make sure this is a video capture device
51 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 ||
52 !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
53 close(fd);
54 continue;
55 }
56
57 close(fd);
58 count++;
59 }
60 }
61
62 return count;
63 }
64
GetDeviceName(uint32_t deviceNumber,char * deviceNameUTF8,uint32_t deviceNameLength,char * deviceUniqueIdUTF8,uint32_t deviceUniqueIdUTF8Length,char *,uint32_t)65 int32_t DeviceInfoV4l2::GetDeviceName(uint32_t deviceNumber,
66 char* deviceNameUTF8,
67 uint32_t deviceNameLength,
68 char* deviceUniqueIdUTF8,
69 uint32_t deviceUniqueIdUTF8Length,
70 char* /*productUniqueIdUTF8*/,
71 uint32_t /*productUniqueIdUTF8Length*/) {
72 // Travel through /dev/video [0-63]
73 uint32_t count = 0;
74 char device[20];
75 int fd = -1;
76 bool found = false;
77 struct v4l2_capability cap;
78 for (int n = 0; n < 64; n++) {
79 snprintf(device, sizeof(device), "/dev/video%d", n);
80 if ((fd = open(device, O_RDONLY)) != -1) {
81 // query device capabilities and make sure this is a video capture device
82 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 ||
83 !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
84 close(fd);
85 continue;
86 }
87 if (count == deviceNumber) {
88 // Found the device
89 found = true;
90 break;
91 } else {
92 close(fd);
93 count++;
94 }
95 }
96 }
97
98 if (!found)
99 return -1;
100
101 // query device capabilities
102 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
103 RTC_LOG(LS_INFO) << "error in querying the device capability for device "
104 << device << ". errno = " << errno;
105 close(fd);
106 return -1;
107 }
108
109 close(fd);
110
111 char cameraName[64];
112 memset(deviceNameUTF8, 0, deviceNameLength);
113 memcpy(cameraName, cap.card, sizeof(cap.card));
114
115 if (deviceNameLength > strlen(cameraName)) {
116 memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
117 } else {
118 RTC_LOG(LS_INFO) << "buffer passed is too small";
119 return -1;
120 }
121
122 if (cap.bus_info[0] != 0) { // may not available in all drivers
123 // copy device id
124 size_t len = strlen(reinterpret_cast<const char*>(cap.bus_info));
125 if (deviceUniqueIdUTF8Length > len) {
126 memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
127 memcpy(deviceUniqueIdUTF8, cap.bus_info, len);
128 } else {
129 RTC_LOG(LS_INFO) << "buffer passed is too small";
130 return -1;
131 }
132 }
133
134 return 0;
135 }
136
CreateCapabilityMap(const char * deviceUniqueIdUTF8)137 int32_t DeviceInfoV4l2::CreateCapabilityMap(const char* deviceUniqueIdUTF8) {
138 int fd;
139 char device[32];
140 bool found = false;
141
142 const int32_t deviceUniqueIdUTF8Length = strlen(deviceUniqueIdUTF8);
143 if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {
144 RTC_LOG(LS_INFO) << "Device name too long";
145 return -1;
146 }
147 RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device "
148 << deviceUniqueIdUTF8;
149
150 /* detect /dev/video [0-63] entries */
151 for (int n = 0; n < 64; ++n) {
152 snprintf(device, sizeof(device), "/dev/video%d", n);
153 fd = open(device, O_RDONLY);
154 if (fd == -1)
155 continue;
156
157 // query device capabilities
158 struct v4l2_capability cap;
159 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
160 // skip devices without video capture capability
161 if (!(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
162 continue;
163 }
164
165 if (cap.bus_info[0] != 0) {
166 if (strncmp(reinterpret_cast<const char*>(cap.bus_info),
167 deviceUniqueIdUTF8,
168 strlen(deviceUniqueIdUTF8)) == 0) { // match with device id
169 found = true;
170 break; // fd matches with device unique id supplied
171 }
172 } else { // match for device name
173 if (IsDeviceNameMatches(reinterpret_cast<const char*>(cap.card),
174 deviceUniqueIdUTF8)) {
175 found = true;
176 break;
177 }
178 }
179 }
180 close(fd); // close since this is not the matching device
181 }
182
183 if (!found) {
184 RTC_LOG(LS_INFO) << "no matching device found";
185 return -1;
186 }
187
188 // now fd will point to the matching device
189 // reset old capability list.
190 _captureCapabilities.clear();
191
192 int size = FillCapabilities(fd);
193 close(fd);
194
195 // Store the new used device name
196 _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
197 _lastUsedDeviceName = reinterpret_cast<char*>(
198 realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1));
199 memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8,
200 _lastUsedDeviceNameLength + 1);
201
202 RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
203
204 return size;
205 }
206
DisplayCaptureSettingsDialogBox(const char *,const char *,void *,uint32_t,uint32_t)207 int32_t DeviceInfoV4l2::DisplayCaptureSettingsDialogBox(
208 const char* /*deviceUniqueIdUTF8*/,
209 const char* /*dialogTitleUTF8*/,
210 void* /*parentWindow*/,
211 uint32_t /*positionX*/,
212 uint32_t /*positionY*/) {
213 return -1;
214 }
215
IsDeviceNameMatches(const char * name,const char * deviceUniqueIdUTF8)216 bool DeviceInfoV4l2::IsDeviceNameMatches(const char* name,
217 const char* deviceUniqueIdUTF8) {
218 if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
219 return true;
220 return false;
221 }
222
FillCapabilities(int fd)223 int32_t DeviceInfoV4l2::FillCapabilities(int fd) {
224 // set image format
225 struct v4l2_format video_fmt;
226 memset(&video_fmt, 0, sizeof(struct v4l2_format));
227
228 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
229 video_fmt.fmt.pix.sizeimage = 0;
230
231 int totalFmts = 5;
232 unsigned int videoFormats[] = {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420,
233 V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY,
234 V4L2_PIX_FMT_NV12};
235
236 int sizes = 13;
237 unsigned int size[][2] = {{128, 96}, {160, 120}, {176, 144}, {320, 240},
238 {352, 288}, {640, 480}, {704, 576}, {800, 600},
239 {960, 720}, {1280, 720}, {1024, 768}, {1440, 1080},
240 {1920, 1080}};
241
242 for (int fmts = 0; fmts < totalFmts; fmts++) {
243 for (int i = 0; i < sizes; i++) {
244 video_fmt.fmt.pix.pixelformat = videoFormats[fmts];
245 video_fmt.fmt.pix.width = size[i][0];
246 video_fmt.fmt.pix.height = size[i][1];
247
248 if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) {
249 if ((video_fmt.fmt.pix.width == size[i][0]) &&
250 (video_fmt.fmt.pix.height == size[i][1])) {
251 VideoCaptureCapability cap;
252 cap.width = video_fmt.fmt.pix.width;
253 cap.height = video_fmt.fmt.pix.height;
254 if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) {
255 cap.videoType = VideoType::kYUY2;
256 } else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420) {
257 cap.videoType = VideoType::kI420;
258 } else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG) {
259 cap.videoType = VideoType::kMJPEG;
260 } else if (videoFormats[fmts] == V4L2_PIX_FMT_UYVY) {
261 cap.videoType = VideoType::kUYVY;
262 } else if (videoFormats[fmts] == V4L2_PIX_FMT_NV12) {
263 cap.videoType = VideoType::kNV12;
264 }
265
266 // get fps of current camera mode
267 // V4l2 does not have a stable method of knowing so we just guess.
268 if (cap.width >= 800 && cap.videoType != VideoType::kMJPEG) {
269 cap.maxFPS = 15;
270 } else {
271 cap.maxFPS = 30;
272 }
273
274 _captureCapabilities.push_back(cap);
275 RTC_LOG(LS_VERBOSE) << "Camera capability, width:" << cap.width
276 << " height:" << cap.height
277 << " type:" << static_cast<int32_t>(cap.videoType)
278 << " fps:" << cap.maxFPS;
279 }
280 }
281 }
282 }
283
284 RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
285 return _captureCapabilities.size();
286 }
287
288 } // namespace videocapturemodule
289 } // namespace webrtc
290