blob: 9ced0ae5cbe6f1ca24edeb855ec450204435cc71 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mallinath@webrtc.org12984f02012-02-16 18:18:21 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_capture/linux/device_info_linux.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
niklase@google.com470e71d2011-07-07 08:21:25 +000013#include <errno.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000014#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000017#include <sys/ioctl.h>
18#include <sys/stat.h>
19#include <unistd.h>
Mirko Bonadei72c42502017-11-09 09:33:23 +010020// v4l includes
niklase@google.com470e71d2011-07-07 08:21:25 +000021#include <linux/videodev2.h>
perkj@webrtc.org0cc68dc2011-09-12 08:53:36 +000022
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/logging.h"
perkj@webrtc.org0cc68dc2011-09-12 08:53:36 +000024
Mirko Bonadei72c42502017-11-09 09:33:23 +010025namespace webrtc {
26namespace videocapturemodule {
27VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() {
28 return new videocapturemodule::DeviceInfoLinux();
niklase@google.com470e71d2011-07-07 08:21:25 +000029}
30
Mirko Bonadei72c42502017-11-09 09:33:23 +010031DeviceInfoLinux::DeviceInfoLinux() : DeviceInfoImpl() {}
32
33int32_t DeviceInfoLinux::Init() {
34 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000035}
36
Mirko Bonadei72c42502017-11-09 09:33:23 +010037DeviceInfoLinux::~DeviceInfoLinux() {}
niklase@google.com470e71d2011-07-07 08:21:25 +000038
Mirko Bonadei72c42502017-11-09 09:33:23 +010039uint32_t DeviceInfoLinux::NumberOfDevices() {
Mirko Bonadei675513b2017-11-09 11:09:25 +010040 RTC_LOG(LS_INFO) << __FUNCTION__;
niklase@google.com470e71d2011-07-07 08:21:25 +000041
Mirko Bonadei72c42502017-11-09 09:33:23 +010042 uint32_t count = 0;
43 char device[20];
44 int fd = -1;
niklase@google.com470e71d2011-07-07 08:21:25 +000045
Mirko Bonadei72c42502017-11-09 09:33:23 +010046 /* detect /dev/video [0-63]VideoCaptureModule entries */
47 for (int n = 0; n < 64; n++) {
48 sprintf(device, "/dev/video%d", n);
49 if ((fd = open(device, O_RDONLY)) != -1) {
50 close(fd);
51 count++;
niklase@google.com470e71d2011-07-07 08:21:25 +000052 }
Mirko Bonadei72c42502017-11-09 09:33:23 +010053 }
niklase@google.com470e71d2011-07-07 08:21:25 +000054
Mirko Bonadei72c42502017-11-09 09:33:23 +010055 return count;
niklase@google.com470e71d2011-07-07 08:21:25 +000056}
57
Mirko Bonadei72c42502017-11-09 09:33:23 +010058int32_t DeviceInfoLinux::GetDeviceName(uint32_t deviceNumber,
59 char* deviceNameUTF8,
60 uint32_t deviceNameLength,
61 char* deviceUniqueIdUTF8,
62 uint32_t deviceUniqueIdUTF8Length,
63 char* /*productUniqueIdUTF8*/,
64 uint32_t /*productUniqueIdUTF8Length*/) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010065 RTC_LOG(LS_INFO) << __FUNCTION__;
niklase@google.com470e71d2011-07-07 08:21:25 +000066
Mirko Bonadei72c42502017-11-09 09:33:23 +010067 // Travel through /dev/video [0-63]
68 uint32_t count = 0;
69 char device[20];
70 int fd = -1;
71 bool found = false;
72 for (int n = 0; n < 64; n++) {
73 sprintf(device, "/dev/video%d", n);
74 if ((fd = open(device, O_RDONLY)) != -1) {
75 if (count == deviceNumber) {
76 // Found the device
77 found = true;
78 break;
79 } else {
80 close(fd);
81 count++;
82 }
niklase@google.com470e71d2011-07-07 08:21:25 +000083 }
Mirko Bonadei72c42502017-11-09 09:33:23 +010084 }
niklase@google.com470e71d2011-07-07 08:21:25 +000085
Mirko Bonadei72c42502017-11-09 09:33:23 +010086 if (!found)
87 return -1;
88
89 // query device capabilities
90 struct v4l2_capability cap;
91 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010092 RTC_LOG(LS_INFO) << "error in querying the device capability for device "
93 << device << ". errno = " << errno;
Mirko Bonadei72c42502017-11-09 09:33:23 +010094 close(fd);
95 return -1;
96 }
97
98 close(fd);
99
100 char cameraName[64];
101 memset(deviceNameUTF8, 0, deviceNameLength);
102 memcpy(cameraName, cap.card, sizeof(cap.card));
103
104 if (deviceNameLength >= strlen(cameraName)) {
105 memcpy(deviceNameUTF8, cameraName, strlen(cameraName));
106 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100107 RTC_LOG(LS_INFO) << "buffer passed is too small";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100108 return -1;
109 }
110
111 if (cap.bus_info[0] != 0) // may not available in all drivers
112 {
113 // copy device id
114 if (deviceUniqueIdUTF8Length >= strlen((const char*)cap.bus_info)) {
115 memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length);
116 memcpy(deviceUniqueIdUTF8, cap.bus_info,
117 strlen((const char*)cap.bus_info));
118 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100119 RTC_LOG(LS_INFO) << "buffer passed is too small";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100120 return -1;
121 }
122 }
123
124 return 0;
125}
126
127int32_t DeviceInfoLinux::CreateCapabilityMap(const char* deviceUniqueIdUTF8) {
128 int fd;
129 char device[32];
130 bool found = false;
131
132 const int32_t deviceUniqueIdUTF8Length =
133 (int32_t)strlen((char*)deviceUniqueIdUTF8);
134 if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100135 RTC_LOG(LS_INFO) << "Device name too long";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100136 return -1;
137 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100138 RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device "
139 << deviceUniqueIdUTF8;
Mirko Bonadei72c42502017-11-09 09:33:23 +0100140
141 /* detect /dev/video [0-63] entries */
142 for (int n = 0; n < 64; ++n) {
143 sprintf(device, "/dev/video%d", n);
144 fd = open(device, O_RDONLY);
145 if (fd == -1)
146 continue;
wu@webrtc.org221b5222011-09-21 16:57:15 +0000147
niklase@google.com470e71d2011-07-07 08:21:25 +0000148 // query device capabilities
149 struct v4l2_capability cap;
Mirko Bonadei72c42502017-11-09 09:33:23 +0100150 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
151 if (cap.bus_info[0] != 0) {
152 if (strncmp((const char*)cap.bus_info, (const char*)deviceUniqueIdUTF8,
153 strlen((const char*)deviceUniqueIdUTF8)) ==
154 0) // match with device id
niklase@google.com470e71d2011-07-07 08:21:25 +0000155 {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100156 found = true;
157 break; // fd matches with device unique id supplied
niklase@google.com470e71d2011-07-07 08:21:25 +0000158 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100159 } else // match for device name
160 {
161 if (IsDeviceNameMatches((const char*)cap.card,
162 (const char*)deviceUniqueIdUTF8)) {
163 found = true;
164 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000165 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100166 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000167 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100168 close(fd); // close since this is not the matching device
169 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000170
Mirko Bonadei72c42502017-11-09 09:33:23 +0100171 if (!found) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100172 RTC_LOG(LS_INFO) << "no matching device found";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100173 return -1;
174 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000175
Mirko Bonadei72c42502017-11-09 09:33:23 +0100176 // now fd will point to the matching device
177 // reset old capability list.
178 _captureCapabilities.clear();
niklase@google.com470e71d2011-07-07 08:21:25 +0000179
Mirko Bonadei72c42502017-11-09 09:33:23 +0100180 int size = FillCapabilities(fd);
181 close(fd);
niklase@google.com470e71d2011-07-07 08:21:25 +0000182
Mirko Bonadei72c42502017-11-09 09:33:23 +0100183 // Store the new used device name
184 _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length;
185 _lastUsedDeviceName =
186 (char*)realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1);
187 memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8,
188 _lastUsedDeviceNameLength + 1);
mallinath@webrtc.org12984f02012-02-16 18:18:21 +0000189
Mirko Bonadei675513b2017-11-09 11:09:25 +0100190 RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
niklase@google.com470e71d2011-07-07 08:21:25 +0000191
Mirko Bonadei72c42502017-11-09 09:33:23 +0100192 return size;
niklase@google.com470e71d2011-07-07 08:21:25 +0000193}
194
195bool DeviceInfoLinux::IsDeviceNameMatches(const char* name,
Mirko Bonadei72c42502017-11-09 09:33:23 +0100196 const char* deviceUniqueIdUTF8) {
197 if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0)
198 return true;
199 return false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000200}
201
Mirko Bonadei72c42502017-11-09 09:33:23 +0100202int32_t DeviceInfoLinux::FillCapabilities(int fd) {
203 // set image format
204 struct v4l2_format video_fmt;
205 memset(&video_fmt, 0, sizeof(struct v4l2_format));
niklase@google.com470e71d2011-07-07 08:21:25 +0000206
Mirko Bonadei72c42502017-11-09 09:33:23 +0100207 video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
208 video_fmt.fmt.pix.sizeimage = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000209
Mirko Bonadei72c42502017-11-09 09:33:23 +0100210 int totalFmts = 4;
211 unsigned int videoFormats[] = {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420,
212 V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY};
niklase@google.com470e71d2011-07-07 08:21:25 +0000213
Mirko Bonadei72c42502017-11-09 09:33:23 +0100214 int sizes = 13;
215 unsigned int size[][2] = {{128, 96}, {160, 120}, {176, 144}, {320, 240},
216 {352, 288}, {640, 480}, {704, 576}, {800, 600},
217 {960, 720}, {1280, 720}, {1024, 768}, {1440, 1080},
218 {1920, 1080}};
niklase@google.com470e71d2011-07-07 08:21:25 +0000219
Mirko Bonadei72c42502017-11-09 09:33:23 +0100220 int index = 0;
221 for (int fmts = 0; fmts < totalFmts; fmts++) {
222 for (int i = 0; i < sizes; i++) {
223 video_fmt.fmt.pix.pixelformat = videoFormats[fmts];
224 video_fmt.fmt.pix.width = size[i][0];
225 video_fmt.fmt.pix.height = size[i][1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000226
Mirko Bonadei72c42502017-11-09 09:33:23 +0100227 if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) {
228 if ((video_fmt.fmt.pix.width == size[i][0]) &&
229 (video_fmt.fmt.pix.height == size[i][1])) {
230 VideoCaptureCapability cap;
231 cap.width = video_fmt.fmt.pix.width;
232 cap.height = video_fmt.fmt.pix.height;
233 if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) {
234 cap.videoType = VideoType::kYUY2;
235 } else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420) {
236 cap.videoType = VideoType::kI420;
237 } else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG) {
238 cap.videoType = VideoType::kMJPEG;
239 } else if (videoFormats[fmts] == V4L2_PIX_FMT_UYVY) {
240 cap.videoType = VideoType::kUYVY;
241 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000242
Mirko Bonadei72c42502017-11-09 09:33:23 +0100243 // get fps of current camera mode
244 // V4l2 does not have a stable method of knowing so we just guess.
245 if (cap.width >= 800 && cap.videoType != VideoType::kMJPEG) {
246 cap.maxFPS = 15;
247 } else {
248 cap.maxFPS = 30;
249 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000250
Mirko Bonadei72c42502017-11-09 09:33:23 +0100251 _captureCapabilities.push_back(cap);
252 index++;
Mirko Bonadei675513b2017-11-09 11:09:25 +0100253 RTC_LOG(LS_VERBOSE) << "Camera capability, width:" << cap.width
254 << " height:" << cap.height
255 << " type:" << static_cast<int32_t>(cap.videoType)
256 << " fps:" << cap.maxFPS;
niklase@google.com470e71d2011-07-07 08:21:25 +0000257 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100258 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000259 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100260 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000261
Mirko Bonadei675513b2017-11-09 11:09:25 +0100262 RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size();
Mirko Bonadei72c42502017-11-09 09:33:23 +0100263 return _captureCapabilities.size();
niklase@google.com470e71d2011-07-07 08:21:25 +0000264}
265
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000266} // namespace videocapturemodule
267} // namespace webrtc