qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
v4l2.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2000,2001 Fabrice Bellard
3  Copyright © 2006 Luca Abeni
4  Copyright © 2015-2019 by The qTox Project Contributors
5 
6  This file is part of qTox, a Qt-based graphical interface for Tox.
7 
8  qTox is libre software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  qTox is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with qTox. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 
23 #include "v4l2.h"
24 
25 #include <QDebug>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <linux/videodev2.h>
30 #include <map>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33 
40 static std::map<uint32_t, uint8_t> createPixFmtToQuality()
41 {
42  std::map<uint32_t, uint8_t> m;
43  m[V4L2_PIX_FMT_H264] = 3;
44  m[V4L2_PIX_FMT_MJPEG] = 2;
45  m[V4L2_PIX_FMT_YUYV] = 1;
46  m[V4L2_PIX_FMT_UYVY] = 1;
47  return m;
48 }
49 const std::map<uint32_t, uint8_t> pixFmtToQuality = createPixFmtToQuality();
50 
51 static std::map<uint32_t, QString> createPixFmtToName()
52 {
53  std::map<uint32_t, QString> m;
54  m[V4L2_PIX_FMT_H264] = QString("h264");
55  m[V4L2_PIX_FMT_MJPEG] = QString("mjpeg");
56  m[V4L2_PIX_FMT_YUYV] = QString("yuyv422");
57  m[V4L2_PIX_FMT_UYVY] = QString("uyvy422");
58  return m;
59 }
60 const std::map<uint32_t, QString> pixFmtToName = createPixFmtToName();
61 
62 static int deviceOpen(QString devName, int* error)
63 {
64  struct v4l2_capability cap;
65  int fd;
66 
67  const std::string devNameString = devName.toStdString();
68  fd = open(devNameString.c_str(), O_RDWR, 0);
69  if (fd < 0) {
70  *error = errno;
71  return fd;
72  }
73 
74  if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
75  *error = errno;
76  goto fail;
77  }
78 
79  if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
80  *error = ENODEV;
81  goto fail;
82  }
83 
84  if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
85  *error = ENOSYS;
86  goto fail;
87  }
88 
89  return fd;
90 
91 fail:
92  close(fd);
93  return -1;
94 }
95 
96 static QVector<float> getDeviceModeFramerates(int fd, unsigned w, unsigned h,
97  uint32_t pixelFormat)
98 {
99  QVector<float> rates;
100  v4l2_frmivalenum vfve{};
101  vfve.pixel_format = pixelFormat;
102  vfve.height = h;
103  vfve.width = w;
104 
105  while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &vfve)) {
106  float rate;
107  switch (vfve.type) {
108  case V4L2_FRMSIZE_TYPE_DISCRETE:
109  rate = vfve.discrete.denominator / vfve.discrete.numerator;
110  if (!rates.contains(rate))
111  rates.append(rate);
112  break;
113  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
114  case V4L2_FRMSIZE_TYPE_STEPWISE:
115  rate = vfve.stepwise.min.denominator / vfve.stepwise.min.numerator;
116  if (!rates.contains(rate))
117  rates.append(rate);
118  }
119  vfve.index++;
120  }
121 
122  return rates;
123 }
124 
125 QVector<VideoMode> v4l2::getDeviceModes(QString devName)
126 {
127  QVector<VideoMode> modes;
128 
129  int error = 0;
130  int fd = deviceOpen(devName, &error);
131  if (fd < 0 || error != 0) {
132  return modes;
133  }
134 
135  v4l2_fmtdesc vfd{};
136  vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
137 
138  while (!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) {
139  vfd.index++;
140 
141  v4l2_frmsizeenum vfse{};
142  vfse.pixel_format = vfd.pixelformat;
143 
144  while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) {
145  VideoMode mode;
146  mode.pixel_format = vfse.pixel_format;
147  switch (vfse.type) {
148  case V4L2_FRMSIZE_TYPE_DISCRETE:
149  mode.width = vfse.discrete.width;
150  mode.height = vfse.discrete.height;
151  break;
152  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
153  case V4L2_FRMSIZE_TYPE_STEPWISE:
154  mode.width = vfse.stepwise.max_width;
155  mode.height = vfse.stepwise.max_height;
156  break;
157  default:
158  continue;
159  }
160 
161  QVector<float> rates =
162  getDeviceModeFramerates(fd, mode.width, mode.height, vfd.pixelformat);
163 
164  // insert dummy FPS value to have the mode in the list even if we don't know the FPS
165  // this fixes support for some webcams, see #5082
166  if (rates.isEmpty()) {
167  rates.append(0.0f);
168  }
169 
170  for (float rate : rates) {
171  mode.FPS = rate;
172  if (!modes.contains(mode)) {
173  modes.append(std::move(mode));
174  }
175  }
176  vfse.index++;
177  }
178  }
179 
180  close(fd);
181  return modes;
182 }
183 
184 QVector<QPair<QString, QString>> v4l2::getDeviceList()
185 {
186  QVector<QPair<QString, QString>> devices;
187  QStringList deviceFiles;
188 
189  DIR* dir = opendir("/dev");
190  if (!dir)
191  return devices;
192 
193  dirent* e;
194  while ((e = readdir(dir)))
195  if (!strncmp(e->d_name, "video", 5) || !strncmp(e->d_name, "vbi", 3))
196  deviceFiles += QString("/dev/") + e->d_name;
197  closedir(dir);
198 
199  for (QString file : deviceFiles) {
200  const std::string filePath = file.toStdString();
201  int fd = open(filePath.c_str(), O_RDWR);
202  if (fd < 0) {
203  continue;
204  }
205 
206  v4l2_capability caps;
207  ioctl(fd, VIDIOC_QUERYCAP, &caps);
208  close(fd);
209 
210  if (caps.device_caps & V4L2_CAP_VIDEO_CAPTURE)
211  devices += {file, reinterpret_cast<const char*>(caps.card)};
212  }
213  return devices;
214 }
215 
216 QString v4l2::getPixelFormatString(uint32_t pixel_format)
217 {
218  if (pixFmtToName.find(pixel_format) == pixFmtToName.end()) {
219  qWarning() << "Pixel format not found";
220  return QString("invalid");
221  }
222  return pixFmtToName.at(pixel_format);
223 }
224 
225 bool v4l2::betterPixelFormat(uint32_t a, uint32_t b)
226 {
227  if (pixFmtToQuality.find(a) == pixFmtToQuality.end()) {
228  return false;
229  } else if (pixFmtToQuality.find(b) == pixFmtToQuality.end()) {
230  return true;
231  }
232  return pixFmtToQuality.at(a) > pixFmtToQuality.at(b);
233 }
v4l2::getPixelFormatString
QString getPixelFormatString(uint32_t pixel_format)
Definition: v4l2.cpp:216
HistMessageContentType::file
@ file
VideoMode::pixel_format
uint32_t pixel_format
Definition: videomode.h:30
pixFmtToQuality
const std::map< uint32_t, uint8_t > pixFmtToQuality
Definition: v4l2.cpp:49
VideoMode::height
int height
Displayed video resolution (NOT frame resolution).
Definition: videomode.h:27
v4l2.h
VideoMode::width
int width
Definition: videomode.h:27
v4l2::getDeviceList
QVector< QPair< QString, QString > > getDeviceList()
Definition: v4l2.cpp:184
v4l2::getDeviceModes
QVector< VideoMode > getDeviceModes(QString devName)
Definition: v4l2.cpp:125
pixFmtToName
const std::map< uint32_t, QString > pixFmtToName
Definition: v4l2.cpp:60
VideoMode
Describes a video mode supported by a device.
Definition: videomode.h:25
VideoMode::FPS
float FPS
Frames per second supported by the device at this resolution.
Definition: videomode.h:29
v4l2::betterPixelFormat
bool betterPixelFormat(uint32_t a, uint32_t b)
Definition: v4l2.cpp:225