qTox is a chat, voice, video, and file transfer IM client using the encrypted peer-to-peer Tox protocol.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

223 lines
5.8 KiB

/*
Copyright (c) 2000,2001 Fabrice Bellard
Copyright (c) 2006 Luca Abeni
Copyright © 2015 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#include "v4l2.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <dirent.h>
#include <map>
#include <QDebug>
/**
* Most of this file is adapted from libavdevice's v4l2.c,
* which retrieves useful information but only exposes it to
* stdout and is not part of the public API for some reason.
*/
static std::map<uint32_t,uint8_t> createPixFmtToQuality()
{
std::map<uint32_t,uint8_t> m;
m[V4L2_PIX_FMT_H264] = 3;
m[V4L2_PIX_FMT_MJPEG] = 2;
m[V4L2_PIX_FMT_YUYV] = 1;
return m;
}
const std::map<uint32_t,uint8_t> pixFmtToQuality = createPixFmtToQuality();
static std::map<uint32_t,QString> createPixFmtToName()
{
std::map<uint32_t,QString> m;
m[V4L2_PIX_FMT_H264] = QString("h264");
m[V4L2_PIX_FMT_MJPEG] = QString("mjpeg");
m[V4L2_PIX_FMT_YUYV] = QString("yuyv422");
return m;
}
const std::map<uint32_t,QString> pixFmtToName = createPixFmtToName();
static int deviceOpen(QString devName, int* error)
{
struct v4l2_capability cap;
int fd;
fd = open(devName.toStdString().c_str(), O_RDWR, 0);
if (fd < 0) {
*error = errno;
return fd;
}
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
*error = errno;
goto fail;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
*error = ENODEV;
goto fail;
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
*error = ENOSYS;
goto fail;
}
return fd;
fail:
close(fd);
return -1;
}
static QVector<unsigned short> getDeviceModeFramerates(int fd, unsigned w, unsigned h, uint32_t pixelFormat)
{
QVector<unsigned short> rates;
v4l2_frmivalenum vfve{};
vfve.pixel_format = pixelFormat;
vfve.height = h;
vfve.width = w;
while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &vfve)) {
int rate;
switch (vfve.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
rate = vfve.discrete.denominator / vfve.discrete.numerator;
if (!rates.contains(rate))
rates.append(rate);
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
rate = vfve.stepwise.min.denominator / vfve.stepwise.min.numerator;
if (!rates.contains(rate))
rates.append(rate);
}
vfve.index++;
}
return rates;
}
QVector<VideoMode> v4l2::getDeviceModes(QString devName)
{
QVector<VideoMode> modes;
int error = 0;
int fd = deviceOpen(devName, &error);
if (fd < 0 || error != 0)
return modes;
v4l2_fmtdesc vfd{};
vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (!ioctl(fd, VIDIOC_ENUM_FMT, &vfd))
{
vfd.index++;
v4l2_frmsizeenum vfse{};
vfse.pixel_format = vfd.pixelformat;
while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse))
{
VideoMode mode;
mode.pixel_format = vfse.pixel_format;
switch (vfse.type) {
case V4L2_FRMSIZE_TYPE_DISCRETE:
mode.width = vfse.discrete.width;
mode.height = vfse.discrete.height;
break;
case V4L2_FRMSIZE_TYPE_CONTINUOUS:
case V4L2_FRMSIZE_TYPE_STEPWISE:
mode.width = vfse.stepwise.max_width;
mode.height = vfse.stepwise.max_height;
break;
default:
continue;
}
QVector<unsigned short> rates = getDeviceModeFramerates(fd, mode.width, mode.height, vfd.pixelformat);
for (unsigned short rate : rates)
{
mode.FPS = rate;
if (!modes.contains(mode))
modes.append(std::move(mode));
}
vfse.index++;
}
}
return modes;
}
QVector<QPair<QString, QString>> v4l2::getDeviceList()
{
QVector<QPair<QString, QString>> devices;
QVector<QString> deviceFiles;
DIR *dir = opendir("/dev");
if (!dir)
return devices;
dirent* e;
while ((e = readdir(dir)))
if (!strncmp(e->d_name, "video", 5) || !strncmp(e->d_name, "vbi", 3))
deviceFiles += QString("/dev/") + e->d_name;
closedir(dir);
for (QString file : deviceFiles)
{
int fd = open(file.toStdString().c_str(), O_RDWR);
if (fd < 0)
continue;
v4l2_capability caps;
ioctl(fd, VIDIOC_QUERYCAP, &caps);
close(fd);
devices += {file, (const char*)caps.card};
}
return devices;
}
QString v4l2::getPixelFormatString(uint32_t pixel_format)
{
if (pixFmtToName.find(pixel_format) == pixFmtToName.end())
{
qWarning() << "Pixel format not found";
return QString("unknown");
}
return pixFmtToName.at(pixel_format);
}
bool v4l2::betterPixelFormat(uint32_t a, uint32_t b)
{
if (pixFmtToQuality.find(a) == pixFmtToQuality.end())
{
return false;
}
else if (pixFmtToQuality.find(b) == pixFmtToQuality.end())
{
return true;
}
return pixFmtToQuality.at(a) > pixFmtToQuality.at(b);
}