xref: /aosp_15_r20/external/webrtc/modules/video_capture/linux/device_info_v4l2.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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