Revision control

- // Copyright (C) 2023 The Qt Company Ltd.
- // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
- #include "qv4l2cameradevices_p.h"
- #include "qv4l2filedescriptor_p.h"
- #include "qv4l2camera_p.h"
-
- #include <QtMultimedia/private/qmultimediautils_p.h>
- #include <private/qcameradevice_p.h>
- #include <private/qcore_unix_p.h>
-
- #include <qdir.h>
- #include <qfile.h>
- #include <qdebug.h>
- #include <qloggingcategory.h>
-
- #include <linux/videodev2.h>
-
- QT_BEGIN_NAMESPACE
-
- Q_STATIC_LOGGING_CATEGORY(qLcV4L2CameraDevices, "qt.multimedia.ffmpeg.v4l2cameradevices");
-
- static bool areCamerasEqual(QSpan<const QCameraDevice> a, QSpan<const QCameraDevice> b)
- {
- auto areCamerasDataEqual = [](const QCameraDevice &a, const QCameraDevice &b) {
- Q_ASSERT(QCameraDevicePrivate::handle(a));
- Q_ASSERT(QCameraDevicePrivate::handle(b));
- return *QCameraDevicePrivate::handle(a) == *QCameraDevicePrivate::handle(b);
- };
-
- return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend(), areCamerasDataEqual);
- }
-
- QV4L2CameraDevices::QV4L2CameraDevices(QPlatformMediaIntegration *integration)
- : QPlatformVideoDevices(integration)
- {
- m_deviceWatcher.addPath(QLatin1String("/dev"));
- connect(&m_deviceWatcher, &QFileSystemWatcher::directoryChanged, this,
- &QV4L2CameraDevices::checkCameras);
- doCheckCameras();
- }
-
- QList<QCameraDevice> QV4L2CameraDevices::findVideoInputs() const
- {
- return m_cameras;
- }
-
- void QV4L2CameraDevices::checkCameras()
- {
- if (doCheckCameras())
- onVideoInputsChanged();
- }
-
- bool QV4L2CameraDevices::doCheckCameras()
- {
- using namespace QtMultimediaPrivate;
-
- QList<QCameraDevice> newCameras;
-
- QDir dir(QLatin1String("/dev"));
- const auto devices = dir.entryList(QDir::System);
-
- bool first = true;
-
- for (auto device : devices) {
- // qCDebug(qLcV4L2Camera) << "device:" << device;
- if (!device.startsWith(QLatin1String("video")))
- continue;
-
- QByteArray file = QFile::encodeName(dir.filePath(device));
- const int fd = open(file.constData(), O_RDONLY);
- if (fd < 0)
- continue;
-
- auto fileCloseGuard = qScopeGuard([fd]() { close(fd); });
-
- v4l2_fmtdesc formatDesc = {};
-
- struct v4l2_capability cap;
- if (qt_safe_ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
- continue;
-
- if (cap.device_caps & V4L2_CAP_META_CAPTURE)
- continue;
- if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
- continue;
- if (!(cap.capabilities & V4L2_CAP_STREAMING))
- continue;
-
- auto camera = std::make_unique<QCameraDevicePrivate>();
-
- camera->id = file;
- camera->description = QString::fromUtf8((const char *)cap.card);
- qCDebug(qLcV4L2CameraDevices) << "found camera" << camera->id << camera->description;
-
- formatDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- while (!qt_safe_ioctl(fd, VIDIOC_ENUM_FMT, &formatDesc)) {
- auto pixelFmt = formatForV4L2Format(formatDesc.pixelformat);
- qCDebug(qLcV4L2CameraDevices) << " " << pixelFmt;
-
- if (pixelFmt == QVideoFrameFormat::Format_Invalid) {
- ++formatDesc.index;
- continue;
- }
-
- qCDebug(qLcV4L2CameraDevices) << "frame sizes:";
- v4l2_frmsizeenum frameSize = {};
- frameSize.pixel_format = formatDesc.pixelformat;
-
- while (!qt_safe_ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) {
- QList<QSize> resolutions;
- if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
- resolutions.append(QSize(frameSize.discrete.width,
- frameSize.discrete.height));
- } else {
- resolutions.append(QSize(frameSize.stepwise.max_width,
- frameSize.stepwise.max_height));
- resolutions.append(QSize(frameSize.stepwise.min_width,
- frameSize.stepwise.min_height));
- }
-
- for (auto resolution : resolutions) {
- float min = 1e10;
- float max = 0;
- auto updateMaxMinFrameRate = [&max, &min](auto discreteFrameRate) {
- const float rate = float(discreteFrameRate.denominator)
- / float(discreteFrameRate.numerator);
- if (rate > max)
- max = rate;
- if (rate < min)
- min = rate;
- };
-
- v4l2_frmivalenum frameInterval = {};
- frameInterval.pixel_format = formatDesc.pixelformat;
- frameInterval.width = resolution.width();
- frameInterval.height = resolution.height();
-
- while (!qt_safe_ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameInterval)) {
- if (frameInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
- updateMaxMinFrameRate(frameInterval.discrete);
- } else {
- updateMaxMinFrameRate(frameInterval.stepwise.max);
- updateMaxMinFrameRate(frameInterval.stepwise.min);
- }
- ++frameInterval.index;
- }
-
- qCDebug(qLcV4L2CameraDevices) << " " << resolution << min << max;
-
- if (min <= max) {
- auto fmt = std::make_unique<QCameraFormatPrivate>();
- fmt->pixelFormat = pixelFmt;
- fmt->resolution = resolution;
- fmt->minFrameRate = min;
- fmt->maxFrameRate = max;
- camera->videoFormats.append(fmt.release()->create());
- camera->photoResolutions.append(resolution);
- }
- }
- ++frameSize.index;
- }
- ++formatDesc.index;
- }
-
- if (camera->videoFormats.empty())
- continue;
-
- // first camera is default
- camera->isDefault = std::exchange(first, false);
-
- newCameras.append(camera.release()->create());
- }
-
- if (areCamerasEqual(m_cameras, newCameras))
- return false;
-
- m_cameras = std::move(newCameras);
- return true;
- }
-
- QT_END_NAMESPACE
-
- #include "moc_qv4l2cameradevices_p.cpp"