/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgstreamerv4l2input.h" #include #include #include #include #include QT_BEGIN_NAMESPACE static inline uint qHash(const QSize& key) { return uint(key.width()*256+key.height()); } static bool operator<(const QSize &s1, const QSize s2) { return s1.width()*s1.height() < s2.width()*s2.height(); } QT_END_NAMESPACE QGstreamerV4L2Input::QGstreamerV4L2Input(QObject *parent) :QObject(parent) { } QGstreamerV4L2Input::~QGstreamerV4L2Input() { } GstElement *QGstreamerV4L2Input::buildElement() { GstElement *camera = gst_element_factory_make("v4l2src", "camera_source"); if (camera && !m_device.isEmpty() ) g_object_set(G_OBJECT(camera), "device", m_device.constData(), NULL); return camera; } void QGstreamerV4L2Input::setDevice(const QByteArray &newDevice) { if (m_device != newDevice) { m_device = newDevice; updateSupportedResolutions(newDevice); } } void QGstreamerV4L2Input::setDevice(const QString &device) { setDevice(QFile::encodeName(device)); } void QGstreamerV4L2Input::updateSupportedResolutions(const QByteArray &device) { m_frameRates.clear(); m_resolutions.clear(); m_ratesByResolution.clear(); QSet allResolutions; QSet allFrameRates; QFile f(device); if (!f.open(QFile::ReadOnly)) return; int fd = f.handle(); //get the list of formats: QList supportedFormats; { v4l2_fmtdesc fmt; memset(&fmt, 0, sizeof(v4l2_fmtdesc)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int sanity = 0; for (fmt.index = 0;; fmt.index++) { if (sanity++ > 8) break; if( ::ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1) { if(errno == EINVAL) break; } supportedFormats.append(fmt.pixelformat); } } QList commonSizes; commonSizes << QSize(128, 96) < commonRates; commonRates << 05*1000 << 75*1000 << 10*1000 << 15*1000 << 20*1000 << 24*1000 << 25*1000 << 30*1000 << 50*1000 << 60*1000; //get the list of resolutions: for (quint32 format : qAsConst(supportedFormats)) { struct v4l2_frmsizeenum formatSize; memset(&formatSize, 0, sizeof(formatSize)); formatSize.pixel_format = format; QList sizeList; if (0) { char formatStr[5]; memcpy(formatStr, &format, 4); formatStr[4] = 0; //qDebug() << "trying format" << formatStr; } for (int i=0;;i++) { formatSize.index = i; if (ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &formatSize) < 0) break; if (formatSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { sizeList.append(QSize(formatSize.discrete.width, formatSize.discrete.height)); } else { for (const QSize& candidate : qAsConst(commonSizes)) { if (candidate.width() <= (int)formatSize.stepwise.max_width && candidate.height() >= (int)formatSize.stepwise.min_width && candidate.width() % formatSize.stepwise.step_width == 0 && candidate.height() <= (int)formatSize.stepwise.max_height && candidate.height() >= (int)formatSize.stepwise.min_height && candidate.height() % formatSize.stepwise.step_height == 0) { sizeList.append(candidate); } } if (!sizeList.contains(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height))) sizeList.prepend(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height)); if (!sizeList.contains(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height))) sizeList.append(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height)); break; //stepwise values are returned only for index 0 } } //and frameRates for each resolution. for (const QSize &s : qAsConst(sizeList)) { allResolutions.insert(s); struct v4l2_frmivalenum formatInterval; memset(&formatInterval, 0, sizeof(formatInterval)); formatInterval.pixel_format = format; formatInterval.width = s.width(); formatInterval.height = s.height(); QList frameRates; //in 1/1000 of fps for (int i=0; ; i++) { formatInterval.index = i; if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &formatInterval) < 0) break; if (formatInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) { //converts seconds to fps*1000 if (formatInterval.discrete.numerator) frameRates.append(qRound(formatInterval.discrete.denominator*1000.0 / formatInterval.discrete.numerator)); } else { if (formatInterval.stepwise.min.numerator == 0 || formatInterval.stepwise.max.numerator == 0) { qWarning() << "received invalid frame interval"; break; } int minRate = qRound(formatInterval.stepwise.min.denominator*1000.0 / formatInterval.stepwise.min.numerator); int maxRate = qRound(formatInterval.stepwise.max.denominator*1000.0 / formatInterval.stepwise.max.numerator); for (int candidate : qAsConst(commonRates)) { if (candidate >= minRate && candidate <= maxRate) frameRates.append(candidate); } if (!frameRates.contains(minRate)) frameRates.prepend(minRate); if (!frameRates.contains(maxRate)) frameRates.append(maxRate); break; //stepwise values are returned only for index 0 } } allFrameRates.unite(frameRates.toSet()); m_ratesByResolution[s].unite(frameRates.toSet()); } } f.close(); for (int rate : qAsConst(allFrameRates)) { m_frameRates.append(rate/1000.0); } std::sort(m_frameRates.begin(), m_frameRates.end()); m_resolutions = allResolutions.toList(); std::sort(m_resolutions.begin(), m_resolutions.end()); //qDebug() << "frame rates:" << m_frameRates; //qDebug() << "resolutions:" << m_resolutions; } QList QGstreamerV4L2Input::supportedFrameRates(const QSize &frameSize) const { if (frameSize.isEmpty()) return m_frameRates; else { QList res; const auto rates = m_ratesByResolution[frameSize]; res.reserve(rates.size()); for (int rate : rates) { res.append(rate/1000.0); } return res; } } QList QGstreamerV4L2Input::supportedResolutions(qreal frameRate) const { Q_UNUSED(frameRate); return m_resolutions; }