diff options
Diffstat (limited to 'src/gsttools/qgstutils.cpp')
-rw-r--r-- | src/gsttools/qgstutils.cpp | 1746 |
1 files changed, 0 insertions, 1746 deletions
diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp deleted file mode 100644 index 5c8d4c90c..000000000 --- a/src/gsttools/qgstutils.cpp +++ /dev/null @@ -1,1746 +0,0 @@ -/**************************************************************************** -** -** 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 <QtMultimedia/private/qtmultimediaglobal_p.h> -#include "qgstutils_p.h" - -#include <QtCore/qdatetime.h> -#include <QtCore/qdir.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qvariant.h> -#include <QtCore/qregularexpression.h> -#include <QtCore/qsize.h> -#include <QtCore/qset.h> -#include <QtCore/qstringlist.h> -#include <QtGui/qimage.h> -#include <qaudioformat.h> -#include <QtCore/qelapsedtimer.h> -#include <QtMultimedia/qvideosurfaceformat.h> -#include <private/qmultimediautils_p.h> - -#include <gst/audio/audio.h> -#include <gst/video/video.h> - -template<typename T, int N> static int lengthOf(const T (&)[N]) { return N; } - -#if QT_CONFIG(linux_v4l) -# include <private/qcore_unix_p.h> -# include <linux/videodev2.h> -#endif - -#include "qgstreamervideoinputdevicecontrol_p.h" - -QT_BEGIN_NAMESPACE - -//internal -static void addTagToMap(const GstTagList *list, - const gchar *tag, - gpointer user_data) -{ - QMap<QByteArray, QVariant> *map = reinterpret_cast<QMap<QByteArray, QVariant>* >(user_data); - - GValue val; - val.g_type = 0; - gst_tag_list_copy_value(&val,list,tag); - - switch( G_VALUE_TYPE(&val) ) { - case G_TYPE_STRING: - { - const gchar *str_value = g_value_get_string(&val); - map->insert(QByteArray(tag), QString::fromUtf8(str_value)); - break; - } - case G_TYPE_INT: - map->insert(QByteArray(tag), g_value_get_int(&val)); - break; - case G_TYPE_UINT: - map->insert(QByteArray(tag), g_value_get_uint(&val)); - break; - case G_TYPE_LONG: - map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); - break; - case G_TYPE_BOOLEAN: - map->insert(QByteArray(tag), g_value_get_boolean(&val)); - break; - case G_TYPE_CHAR: -#if GLIB_CHECK_VERSION(2,32,0) - map->insert(QByteArray(tag), g_value_get_schar(&val)); -#else - map->insert(QByteArray(tag), g_value_get_char(&val)); -#endif - break; - case G_TYPE_DOUBLE: - map->insert(QByteArray(tag), g_value_get_double(&val)); - break; - default: - // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch -#if GST_CHECK_VERSION(1,0,0) - if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { - const GDate *date = (const GDate *)g_value_get_boxed(&val); -#else - if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { - const GDate *date = gst_value_get_date(&val); -#endif - if (g_date_valid(date)) { - int year = g_date_get_year(date); - int month = g_date_get_month(date); - int day = g_date_get_day(date); - map->insert(QByteArray(tag), QDate(year,month,day)); - if (!map->contains("year")) - map->insert("year", year); - } -#if GST_CHECK_VERSION(1,0,0) - } else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME) { - const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val); - int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0; - int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0; - int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0; - if (gst_date_time_has_time(dateTime)) { - int hour = gst_date_time_get_hour(dateTime); - int minute = gst_date_time_get_minute(dateTime); - int second = gst_date_time_get_second(dateTime); - float tz = gst_date_time_get_time_zone_offset(dateTime); - QDateTime dateTime(QDate(year, month, day), QTime(hour, minute, second), - Qt::OffsetFromUTC, tz * 60 * 60); - map->insert(QByteArray(tag), dateTime); - } else if (year > 0 && month > 0 && day > 0) { - map->insert(QByteArray(tag), QDate(year,month,day)); - } - if (!map->contains("year") && year > 0) - map->insert("year", year); - } else if (G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE) { - GstSample *sample = (GstSample *)g_value_get_boxed(&val); - GstCaps* caps = gst_sample_get_caps(sample); - if (caps && !gst_caps_is_empty(caps)) { - GstStructure *structure = gst_caps_get_structure(caps, 0); - const gchar *name = gst_structure_get_name(structure); - if (QByteArray(name).startsWith("image/")) { - GstBuffer *buffer = gst_sample_get_buffer(sample); - if (buffer) { - GstMapInfo info; - gst_buffer_map(buffer, &info, GST_MAP_READ); - map->insert(QByteArray(tag), QImage::fromData(info.data, info.size, name)); - gst_buffer_unmap(buffer, &info); - } - } - } -#endif - } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { - int nom = gst_value_get_fraction_numerator(&val); - int denom = gst_value_get_fraction_denominator(&val); - - if (denom > 0) { - map->insert(QByteArray(tag), double(nom)/denom); - } - } - break; - } - - g_value_unset(&val); -} - -/*! - \class QGstUtils - \internal -*/ - -/*! - Convert GstTagList structure to QMap<QByteArray, QVariant>. - - Mapping to int, bool, char, string, fractions and date are supported. - Fraction values are converted to doubles. -*/ -QMap<QByteArray, QVariant> QGstUtils::gstTagListToMap(const GstTagList *tags) -{ - QMap<QByteArray, QVariant> res; - gst_tag_list_foreach(tags, addTagToMap, &res); - - return res; -} - -/*! - Returns resolution of \a caps. - If caps doesn't have a valid size, an empty QSize is returned. -*/ -QSize QGstUtils::capsResolution(const GstCaps *caps) -{ - if (gst_caps_get_size(caps) == 0) - return QSize(); - - return structureResolution(gst_caps_get_structure(caps, 0)); -} - -/*! - Returns aspect ratio corrected resolution of \a caps. - If caps doesn't have a valid size, an empty QSize is returned. -*/ -QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) -{ - QSize size; - - if (caps) { - size = capsResolution(caps); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (!size.isEmpty() && gst_structure_get_fraction( - gst_caps_get_structure(caps, 0), "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) - size.setWidth(size.width()*aspectNum/aspectDenum); - } - } - - return size; -} - - -#if GST_CHECK_VERSION(1,0,0) -namespace { - -struct AudioFormat -{ - GstAudioFormat format; - QAudioFormat::SampleType sampleType; - QAudioFormat::Endian byteOrder; - int sampleSize; -}; -static const AudioFormat qt_audioLookup[] = -{ - { GST_AUDIO_FORMAT_S8 , QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 8 }, - { GST_AUDIO_FORMAT_U8 , QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 8 }, - { GST_AUDIO_FORMAT_S16LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 16 }, - { GST_AUDIO_FORMAT_S16BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 16 }, - { GST_AUDIO_FORMAT_U16LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 16 }, - { GST_AUDIO_FORMAT_U16BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 16 }, - { GST_AUDIO_FORMAT_S32LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_S32BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_U32LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_U32BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_S24LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 24 }, - { GST_AUDIO_FORMAT_S24BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 24 }, - { GST_AUDIO_FORMAT_U24LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 24 }, - { GST_AUDIO_FORMAT_U24BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 24 }, - { GST_AUDIO_FORMAT_F32LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 32 }, - { GST_AUDIO_FORMAT_F32BE, QAudioFormat::Float , QAudioFormat::BigEndian , 32 }, - { GST_AUDIO_FORMAT_F64LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 64 }, - { GST_AUDIO_FORMAT_F64BE, QAudioFormat::Float , QAudioFormat::BigEndian , 64 } -}; - -} -#endif - -/*! - Returns audio format for caps. - If caps doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ - -QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps) -{ - QAudioFormat format; -#if GST_CHECK_VERSION(1,0,0) - GstAudioInfo info; - if (gst_audio_info_from_caps(&info, caps)) { - for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { - if (qt_audioLookup[i].format != info.finfo->format) - continue; - - format.setSampleType(qt_audioLookup[i].sampleType); - format.setByteOrder(qt_audioLookup[i].byteOrder); - format.setSampleSize(qt_audioLookup[i].sampleSize); - format.setSampleRate(info.rate); - format.setChannelCount(info.channels); - format.setCodec(QStringLiteral("audio/pcm")); - - return format; - } - } -#else - const GstStructure *structure = gst_caps_get_structure(caps, 0); - - if (qstrcmp(gst_structure_get_name(structure), "audio/x-raw-int") == 0) { - - format.setCodec("audio/pcm"); - - int endianness = 0; - gst_structure_get_int(structure, "endianness", &endianness); - if (endianness == 1234) - format.setByteOrder(QAudioFormat::LittleEndian); - else if (endianness == 4321) - format.setByteOrder(QAudioFormat::BigEndian); - - gboolean isSigned = FALSE; - gst_structure_get_boolean(structure, "signed", &isSigned); - if (isSigned) - format.setSampleType(QAudioFormat::SignedInt); - else - format.setSampleType(QAudioFormat::UnSignedInt); - - // Number of bits allocated per sample. - int width = 0; - gst_structure_get_int(structure, "width", &width); - - // The number of bits used per sample. This must be less than or equal to the width. - int depth = 0; - gst_structure_get_int(structure, "depth", &depth); - - if (width != depth) { - // Unsupported sample layout. - return QAudioFormat(); - } - format.setSampleSize(width); - - int rate = 0; - gst_structure_get_int(structure, "rate", &rate); - format.setSampleRate(rate); - - int channels = 0; - gst_structure_get_int(structure, "channels", &channels); - format.setChannelCount(channels); - - } else if (qstrcmp(gst_structure_get_name(structure), "audio/x-raw-float") == 0) { - - format.setCodec("audio/pcm"); - - int endianness = 0; - gst_structure_get_int(structure, "endianness", &endianness); - if (endianness == 1234) - format.setByteOrder(QAudioFormat::LittleEndian); - else if (endianness == 4321) - format.setByteOrder(QAudioFormat::BigEndian); - - format.setSampleType(QAudioFormat::Float); - - int width = 0; - gst_structure_get_int(structure, "width", &width); - - format.setSampleSize(width); - - int rate = 0; - gst_structure_get_int(structure, "rate", &rate); - format.setSampleRate(rate); - - int channels = 0; - gst_structure_get_int(structure, "channels", &channels); - format.setChannelCount(channels); - - } else { - return QAudioFormat(); - } -#endif - return format; -} - -#if GST_CHECK_VERSION(1,0,0) -/* - Returns audio format for a sample. - If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ -QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) -{ - GstCaps* caps = gst_sample_get_caps(sample); - if (!caps) - return QAudioFormat(); - - return QGstUtils::audioFormatForCaps(caps); -} -#else -/*! - Returns audio format for a buffer. - If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ -QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer) -{ - GstCaps* caps = gst_buffer_get_caps(buffer); - if (!caps) - return QAudioFormat(); - - QAudioFormat format = QGstUtils::audioFormatForCaps(caps); - gst_caps_unref(caps); - return format; -} -#endif - -/*! - Builds GstCaps for an audio format. - Returns 0 if the audio format is not valid. - Caller must unref GstCaps. -*/ - -GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format) -{ - if (!format.isValid()) - return 0; - -#if GST_CHECK_VERSION(1,0,0) - const QAudioFormat::SampleType sampleType = format.sampleType(); - const QAudioFormat::Endian byteOrder = format.byteOrder(); - const int sampleSize = format.sampleSize(); - - for (int i = 0; i < lengthOf(qt_audioLookup); ++i) { - if (qt_audioLookup[i].sampleType != sampleType - || qt_audioLookup[i].byteOrder != byteOrder - || qt_audioLookup[i].sampleSize != sampleSize) { - continue; - } - - return gst_caps_new_simple( - "audio/x-raw", - "format" , G_TYPE_STRING, gst_audio_format_to_string(qt_audioLookup[i].format), - "rate" , G_TYPE_INT , format.sampleRate(), - "channels", G_TYPE_INT , format.channelCount(), - nullptr); - } - return 0; -#else - GstStructure *structure = 0; - - if (format.isValid()) { - if (format.sampleType() == QAudioFormat::SignedInt || format.sampleType() == QAudioFormat::UnSignedInt) { - structure = gst_structure_new("audio/x-raw-int", nullptr); - } else if (format.sampleType() == QAudioFormat::Float) { - structure = gst_structure_new("audio/x-raw-float", nullptr); - } - } - - GstCaps *caps = 0; - - if (structure) { - gst_structure_set(structure, "rate", G_TYPE_INT, format.sampleRate(), nullptr); - gst_structure_set(structure, "channels", G_TYPE_INT, format.channelCount(), nullptr); - gst_structure_set(structure, "width", G_TYPE_INT, format.sampleSize(), nullptr); - gst_structure_set(structure, "depth", G_TYPE_INT, format.sampleSize(), nullptr); - - if (format.byteOrder() == QAudioFormat::LittleEndian) - gst_structure_set(structure, "endianness", G_TYPE_INT, 1234, nullptr); - else if (format.byteOrder() == QAudioFormat::BigEndian) - gst_structure_set(structure, "endianness", G_TYPE_INT, 4321, nullptr); - - if (format.sampleType() == QAudioFormat::SignedInt) - gst_structure_set(structure, "signed", G_TYPE_BOOLEAN, TRUE, nullptr); - else if (format.sampleType() == QAudioFormat::UnSignedInt) - gst_structure_set(structure, "signed", G_TYPE_BOOLEAN, FALSE, nullptr); - - caps = gst_caps_new_empty(); - Q_ASSERT(caps); - gst_caps_append_structure(caps, structure); - } - - return caps; -#endif -} - -void QGstUtils::initializeGst() -{ - static bool initialized = false; - if (!initialized) { - initialized = true; - gst_init(nullptr, nullptr); - } -} - -namespace { - const char* getCodecAlias(const QString &codec) - { - if (codec.startsWith(QLatin1String("avc1."))) - return "video/x-h264"; - - if (codec.startsWith(QLatin1String("mp4a."))) - return "audio/mpeg4"; - - if (codec.startsWith(QLatin1String("mp4v.20."))) - return "video/mpeg4"; - - if (codec == QLatin1String("samr")) - return "audio/amr"; - - return 0; - } - - const char* getMimeTypeAlias(const QString &mimeType) - { - if (mimeType == QLatin1String("video/mp4")) - return "video/mpeg4"; - - if (mimeType == QLatin1String("audio/mp4")) - return "audio/mpeg4"; - - if (mimeType == QLatin1String("video/ogg") - || mimeType == QLatin1String("audio/ogg")) - return "application/ogg"; - - return 0; - } -} - -QMultimedia::SupportEstimate QGstUtils::hasSupport(const QString &mimeType, - const QStringList &codecs, - const QSet<QString> &supportedMimeTypeSet) -{ - if (supportedMimeTypeSet.isEmpty()) - return QMultimedia::NotSupported; - - QString mimeTypeLowcase = mimeType.toLower(); - bool containsMimeType = supportedMimeTypeSet.contains(mimeTypeLowcase); - if (!containsMimeType) { - const char* mimeTypeAlias = getMimeTypeAlias(mimeTypeLowcase); - containsMimeType = supportedMimeTypeSet.contains(QLatin1String(mimeTypeAlias)); - if (!containsMimeType) { - containsMimeType = supportedMimeTypeSet.contains(QLatin1String("video/") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("video/x-") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/") + mimeTypeLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + mimeTypeLowcase); - } - } - - int supportedCodecCount = 0; - for (const QString &codec : codecs) { - QString codecLowcase = codec.toLower(); - const char* codecAlias = getCodecAlias(codecLowcase); - if (codecAlias) { - if (supportedMimeTypeSet.contains(QLatin1String(codecAlias))) - supportedCodecCount++; - } else if (supportedMimeTypeSet.contains(QLatin1String("video/") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("video/x-") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/") + codecLowcase) - || supportedMimeTypeSet.contains(QLatin1String("audio/x-") + codecLowcase)) { - supportedCodecCount++; - } - } - if (supportedCodecCount > 0 && supportedCodecCount == codecs.size()) - return QMultimedia::ProbablySupported; - - if (supportedCodecCount == 0 && !containsMimeType) - return QMultimedia::NotSupported; - - return QMultimedia::MaybeSupported; -} - -namespace { - -typedef QHash<GstElementFactory *, QList<QGstUtils::CameraInfo>> FactoryCameraInfoMap; - -Q_GLOBAL_STATIC(FactoryCameraInfoMap, qt_camera_device_info); - -} - -QList<QGstUtils::CameraInfo> QGstUtils::enumerateCameras(GstElementFactory *factory) -{ - static QElapsedTimer camerasCacheAgeTimer; - if (camerasCacheAgeTimer.isValid() && camerasCacheAgeTimer.elapsed() > 500) // ms - qt_camera_device_info()->clear(); - - FactoryCameraInfoMap::const_iterator it = qt_camera_device_info()->constFind(factory); - if (it != qt_camera_device_info()->constEnd()) - return *it; - - QList<CameraInfo> &devices = (*qt_camera_device_info())[factory]; - - if (factory) { - bool hasVideoSource = false; - - const GType type = gst_element_factory_get_element_type(factory); - GObjectClass * const objectClass = type - ? static_cast<GObjectClass *>(g_type_class_ref(type)) - : 0; - if (objectClass) { - if (g_object_class_find_property(objectClass, "camera-device")) { - const CameraInfo primary = { - QStringLiteral("primary"), - QGstreamerVideoInputDeviceControl::primaryCamera(), - 0, - QCamera::BackFace, - QByteArray() - }; - const CameraInfo secondary = { - QStringLiteral("secondary"), - QGstreamerVideoInputDeviceControl::secondaryCamera(), - 0, - QCamera::FrontFace, - QByteArray() - }; - - devices.append(primary); - devices.append(secondary); - - GstElement *camera = g_object_class_find_property(objectClass, "sensor-mount-angle") - ? gst_element_factory_create(factory, 0) - : 0; - if (camera) { - if (gst_element_set_state(camera, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { - // no-op - } else for (int i = 0; i < 2; ++i) { - gint orientation = 0; - g_object_set(G_OBJECT(camera), "camera-device", i, nullptr); - g_object_get(G_OBJECT(camera), "sensor-mount-angle", &orientation, nullptr); - - devices[i].orientation = (720 - orientation) % 360; - } - gst_element_set_state(camera, GST_STATE_NULL); - gst_object_unref(GST_OBJECT(camera)); - - } - } else if (g_object_class_find_property(objectClass, "video-source")) { - hasVideoSource = true; - } - - g_type_class_unref(objectClass); - } - - if (!devices.isEmpty() || !hasVideoSource) { - camerasCacheAgeTimer.restart(); - return devices; - } - } - -#if QT_CONFIG(linux_v4l) - QDir devDir(QStringLiteral("/dev")); - devDir.setFilter(QDir::System); - - const QFileInfoList entries = devDir.entryInfoList(QStringList() - << QStringLiteral("video*")); - - for (const QFileInfo &entryInfo : entries) { - //qDebug() << "Try" << entryInfo.filePath(); - - int fd = qt_safe_open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); - if (fd == -1) - continue; - - bool isCamera = false; - - v4l2_input input; - memset(&input, 0, sizeof(input)); - for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { - if (input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { - const int ret = ::ioctl(fd, VIDIOC_S_INPUT, &input.index); - isCamera = (ret == 0 || errno == ENOTTY || errno == EBUSY); - break; - } - } - - if (isCamera) { - // find out its driver "name" - QByteArray driver; - QString name; - struct v4l2_capability vcap; - memset(&vcap, 0, sizeof(struct v4l2_capability)); - - if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) { - name = entryInfo.fileName(); - } else { - driver = QByteArray((const char*)vcap.driver); - name = QString::fromUtf8((const char*)vcap.card); - if (name.isEmpty()) - name = entryInfo.fileName(); - } - //qDebug() << "found camera: " << name; - - - CameraInfo device = { - entryInfo.absoluteFilePath(), - name, - 0, - QCamera::UnspecifiedPosition, - driver - }; - devices.append(device); - } - qt_safe_close(fd); - } - camerasCacheAgeTimer.restart(); -#endif // linux_v4l - -#if GST_CHECK_VERSION(1,4,0) && (defined(Q_OS_WIN) || defined(Q_OS_MACOS)) - if (!devices.isEmpty()) - return devices; - -#if defined(Q_OS_WIN) - const char *propName = "device-path"; - auto deviceDesc = [](GValue *value) { - gchar *desc = g_value_dup_string(value); - const QString id = QLatin1String(desc); - g_free(desc); - return id; - }; -#elif defined(Q_OS_MACOS) - const char *propName = "device-index"; - auto deviceDesc = [](GValue *value) { - return QString::number(g_value_get_int(value)); - }; -#endif - - QGstUtils::initializeGst(); - GstDeviceMonitor *monitor = gst_device_monitor_new(); - auto caps = gst_caps_new_empty_simple("video/x-raw"); - gst_device_monitor_add_filter(monitor, "Video/Source", caps); - gst_caps_unref(caps); - - GList *devs = gst_device_monitor_get_devices(monitor); - while (devs) { - GstDevice *dev = reinterpret_cast<GstDevice*>(devs->data); - GstElement *element = gst_device_create_element(dev, nullptr); - if (element) { - gchar *name = gst_device_get_display_name(dev); - const QString deviceName = QLatin1String(name); - g_free(name); - GParamSpec *prop = g_object_class_find_property(G_OBJECT_GET_CLASS(element), propName); - if (prop) { - GValue value = G_VALUE_INIT; - g_value_init(&value, prop->value_type); - g_object_get_property(G_OBJECT(element), prop->name, &value); - const QString deviceId = deviceDesc(&value); - g_value_unset(&value); - - CameraInfo device = { - deviceId, - deviceName, - 0, - QCamera::UnspecifiedPosition, - QByteArray() - }; - - devices.append(device); - } - - gst_object_unref(element); - } - - gst_object_unref(dev); - devs = g_list_delete_link(devs, devs); - } - gst_object_unref(monitor); -#endif // GST_CHECK_VERSION(1,4,0) && (defined(Q_OS_WIN) || defined(Q_OS_MACOS)) - - return devices; -} - -QList<QByteArray> QGstUtils::cameraDevices(GstElementFactory * factory) -{ - QList<QByteArray> devices; - - const auto cameras = enumerateCameras(factory); - devices.reserve(cameras.size()); - for (const CameraInfo &camera : cameras) - devices.append(camera.name.toUtf8()); - - return devices; -} - -QString QGstUtils::cameraDescription(const QString &device, GstElementFactory * factory) -{ - const auto cameras = enumerateCameras(factory); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.description; - } - return QString(); -} - -QCamera::Position QGstUtils::cameraPosition(const QString &device, GstElementFactory * factory) -{ - const auto cameras = enumerateCameras(factory); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.position; - } - return QCamera::UnspecifiedPosition; -} - -int QGstUtils::cameraOrientation(const QString &device, GstElementFactory * factory) -{ - const auto cameras = enumerateCameras(factory); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.orientation; - } - return 0; -} - -QByteArray QGstUtils::cameraDriver(const QString &device, GstElementFactory *factory) -{ - const auto cameras = enumerateCameras(factory); - for (const CameraInfo &camera : cameras) { - if (camera.name == device) - return camera.driver; - } - return QByteArray(); -} - -QSet<QString> QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory)) -{ - QSet<QString> supportedMimeTypes; - - //enumerate supported mime types - gst_init(nullptr, nullptr); - -#if GST_CHECK_VERSION(1,0,0) - GstRegistry *registry = gst_registry_get(); - GList *orig_plugins = gst_registry_get_plugin_list(registry); -#else - GstRegistry *registry = gst_registry_get_default(); - GList *orig_plugins = gst_default_registry_get_plugin_list (); -#endif - for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) { - GstPlugin *plugin = (GstPlugin *) (plugins->data); -#if GST_CHECK_VERSION(1,0,0) - if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED)) - continue; -#else - if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED - continue; -#endif - - GList *orig_features = gst_registry_get_feature_list_by_plugin( - registry, gst_plugin_get_name(plugin)); - for (GList *features = orig_features; features; features = g_list_next(features)) { - if (G_UNLIKELY(features->data == nullptr)) - continue; - - GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); - GstElementFactory *factory; - - if (GST_IS_TYPE_FIND_FACTORY(feature)) { - QString name(QLatin1String(gst_plugin_feature_get_name(feature))); - if (name.contains(QLatin1Char('/'))) //filter out any string without '/' which is obviously not a mime type - supportedMimeTypes.insert(name.toLower()); - continue; - } else if (!GST_IS_ELEMENT_FACTORY (feature) - || !(factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)))) { - continue; - } else if (!isValidFactory(factory)) { - // Do nothing - } else for (const GList *pads = gst_element_factory_get_static_pad_templates(factory); - pads; - pads = g_list_next(pads)) { - GstStaticPadTemplate *padtemplate = static_cast<GstStaticPadTemplate *>(pads->data); - - if (padtemplate->direction == GST_PAD_SINK && padtemplate->static_caps.string) { - GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); - if (gst_caps_is_any(caps) || gst_caps_is_empty(caps)) { - } else for (guint i = 0; i < gst_caps_get_size(caps); i++) { - GstStructure *structure = gst_caps_get_structure(caps, i); - QString nameLowcase = QString::fromLatin1(gst_structure_get_name(structure)).toLower(); - - supportedMimeTypes.insert(nameLowcase); - if (nameLowcase.contains(QLatin1String("mpeg"))) { - //Because mpeg version number is only included in the detail - //description, it is necessary to manually extract this information - //in order to match the mime type of mpeg4. - const GValue *value = gst_structure_get_value(structure, "mpegversion"); - if (value) { - gchar *str = gst_value_serialize(value); - QString versions = QLatin1String(str); - const QStringList elements = versions.split(QRegularExpression(QLatin1String("\\D+")), Qt::SkipEmptyParts); - for (const QString &e : elements) - supportedMimeTypes.insert(nameLowcase + e); - g_free(str); - } - } - } - } - } - gst_object_unref(factory); - } - gst_plugin_feature_list_free(orig_features); - } - gst_plugin_list_free (orig_plugins); - -#if defined QT_SUPPORTEDMIMETYPES_DEBUG - QStringList list = supportedMimeTypes.toList(); - list.sort(); - if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { - for (const QString &type : qAsConst(list)) - qDebug() << type; - } -#endif - return supportedMimeTypes; -} - -#if GST_CHECK_VERSION(1, 0, 0) -namespace { - -struct ColorFormat { QImage::Format imageFormat; GstVideoFormat gstFormat; }; -static const ColorFormat qt_colorLookup[] = -{ - { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx }, - { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, - { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB }, - { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 } -}; - -} -#endif - -#if GST_CHECK_VERSION(1,0,0) -QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo) -#else -QImage QGstUtils::bufferToImage(GstBuffer *buffer) -#endif -{ - QImage img; - -#if GST_CHECK_VERSION(1,0,0) - GstVideoInfo info = videoInfo; - GstVideoFrame frame; - if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ)) - return img; -#else - GstCaps *caps = gst_buffer_get_caps(buffer); - if (!caps) - return img; - - GstStructure *structure = gst_caps_get_structure (caps, 0); - gint width = 0; - gint height = 0; - - if (!structure - || !gst_structure_get_int(structure, "width", &width) - || !gst_structure_get_int(structure, "height", &height) - || width <= 0 - || height <= 0) { - gst_caps_unref(caps); - return img; - } - gst_caps_unref(caps); -#endif - -#if GST_CHECK_VERSION(1,0,0) - if (videoInfo.finfo->format == GST_VIDEO_FORMAT_I420) { - const int width = videoInfo.width; - const int height = videoInfo.height; - - const int stride[] = { frame.info.stride[0], frame.info.stride[1], frame.info.stride[2] }; - const uchar *data[] = { - static_cast<const uchar *>(frame.data[0]), - static_cast<const uchar *>(frame.data[1]), - static_cast<const uchar *>(frame.data[2]) - }; -#else - if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { - const int stride[] = { width, width / 2, width / 2 }; - const uchar *data[] = { - (const uchar *)buffer->data, - (const uchar *)buffer->data + width * height, - (const uchar *)buffer->data + width * height * 5 / 4 - }; -#endif - img = QImage(width/2, height/2, QImage::Format_RGB32); - - for (int y=0; y<height; y+=2) { - const uchar *yLine = data[0] + (y * stride[0]); - const uchar *uLine = data[1] + (y * stride[1] / 2); - const uchar *vLine = data[2] + (y * stride[2] / 2); - - for (int x=0; x<width; x+=2) { - const qreal Y = 1.164*(yLine[x]-16); - const int U = uLine[x/2]-128; - const int V = vLine[x/2]-128; - - int b = qBound(0, int(Y + 2.018*U), 255); - int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255); - int r = qBound(0, int(Y + 1.596*V), 255); - - img.setPixel(x/2,y/2,qRgb(r,g,b)); - } - } -#if GST_CHECK_VERSION(1,0,0) - } else for (int i = 0; i < lengthOf(qt_colorLookup); ++i) { - if (qt_colorLookup[i].gstFormat != videoInfo.finfo->format) - continue; - - const QImage image( - static_cast<const uchar *>(frame.data[0]), - videoInfo.width, - videoInfo.height, - frame.info.stride[0], - qt_colorLookup[i].imageFormat); - img = image; - img.detach(); - - break; - } - - gst_video_frame_unmap(&frame); -#else - } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { - QImage::Format format = QImage::Format_Invalid; - int bpp = 0; - gst_structure_get_int(structure, "bpp", &bpp); - - if (bpp == 24) - format = QImage::Format_RGB888; - else if (bpp == 32) - format = QImage::Format_RGB32; - - if (format != QImage::Format_Invalid) { - img = QImage((const uchar *)buffer->data, - width, - height, - format); - img.bits(); //detach - } - } -#endif - return img; -} - - -namespace { - -#if GST_CHECK_VERSION(1,0,0) - -struct VideoFormat -{ - QVideoFrame::PixelFormat pixelFormat; - GstVideoFormat gstFormat; -}; - -static const VideoFormat qt_videoFormatLookup[] = -{ - { QVideoFrame::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, - { QVideoFrame::Format_YUV422P, GST_VIDEO_FORMAT_Y42B }, - { QVideoFrame::Format_YV12 , GST_VIDEO_FORMAT_YV12 }, - { QVideoFrame::Format_UYVY , GST_VIDEO_FORMAT_UYVY }, - { QVideoFrame::Format_YUYV , GST_VIDEO_FORMAT_YUY2 }, - { QVideoFrame::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, - { QVideoFrame::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, - { QVideoFrame::Format_AYUV444, GST_VIDEO_FORMAT_AYUV }, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_BGRx }, - { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_RGBx }, - { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_BGRA }, - { QVideoFrame::Format_ABGR32, GST_VIDEO_FORMAT_RGBA }, - { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_ARGB }, -#else - { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_xRGB }, - { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_xBGR }, - { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_ARGB }, - { QVideoFrame::Format_ABGR32, GST_VIDEO_FORMAT_ABGR }, - { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_BGRA }, -#endif - { QVideoFrame::Format_RGB24 , GST_VIDEO_FORMAT_RGB }, - { QVideoFrame::Format_BGR24 , GST_VIDEO_FORMAT_BGR }, - { QVideoFrame::Format_RGB565, GST_VIDEO_FORMAT_RGB16 } -}; - -static int indexOfVideoFormat(QVideoFrame::PixelFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].pixelFormat == format) - return i; - - return -1; -} - -static int indexOfVideoFormat(GstVideoFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].gstFormat == format) - return i; - - return -1; -} - -#else - -struct YuvFormat -{ - QVideoFrame::PixelFormat pixelFormat; - guint32 fourcc; - int bitsPerPixel; -}; - -static const YuvFormat qt_yuvColorLookup[] = -{ - { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, - { QVideoFrame::Format_YUV422P, GST_MAKE_FOURCC('Y','4','2','B'), 8 }, - { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, - { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, - { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, - { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, - { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, - { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } -}; - -static int indexOfYuvColor(QVideoFrame::PixelFormat format) -{ - const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); - - for (int i = 0; i < count; ++i) - if (qt_yuvColorLookup[i].pixelFormat == format) - return i; - - return -1; -} - -static int indexOfYuvColor(guint32 fourcc) -{ - const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); - - for (int i = 0; i < count; ++i) - if (qt_yuvColorLookup[i].fourcc == fourcc) - return i; - - return -1; -} - -struct RgbFormat -{ - QVideoFrame::PixelFormat pixelFormat; - int bitsPerPixel; - int depth; - int endianness; - int red; - int green; - int blue; - int alpha; -}; - -static const RgbFormat qt_rgbColorLookup[] = -{ - { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 }, - { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, - { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 }, - { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, - { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF }, - { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) }, - { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, - { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, - { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } -}; - -static int indexOfRgbColor( - int bits, int depth, int endianness, int red, int green, int blue, int alpha) -{ - const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); - - for (int i = 0; i < count; ++i) { - if (qt_rgbColorLookup[i].bitsPerPixel == bits - && qt_rgbColorLookup[i].depth == depth - && qt_rgbColorLookup[i].endianness == endianness - && qt_rgbColorLookup[i].red == red - && qt_rgbColorLookup[i].green == green - && qt_rgbColorLookup[i].blue == blue - && qt_rgbColorLookup[i].alpha == alpha) { - return i; - } - } - return -1; -} -#endif - -} - -#if GST_CHECK_VERSION(1,0,0) - -QVideoSurfaceFormat QGstUtils::formatForCaps( - GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType) -{ - GstVideoInfo vidInfo; - GstVideoInfo *infoPtr = info ? info : &vidInfo; - - if (gst_video_info_from_caps(infoPtr, caps)) { - int index = indexOfVideoFormat(infoPtr->finfo->format); - - if (index != -1) { - QVideoSurfaceFormat format( - QSize(infoPtr->width, infoPtr->height), - qt_videoFormatLookup[index].pixelFormat, - handleType); - - if (infoPtr->fps_d > 0) - format.setFrameRate(qreal(infoPtr->fps_n) / infoPtr->fps_d); - - if (infoPtr->par_d > 0) - format.setPixelAspectRatio(infoPtr->par_n, infoPtr->par_d); - - return format; - } - } - return QVideoSurfaceFormat(); -} - -#else - -QVideoSurfaceFormat QGstUtils::formatForCaps( - GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType) -{ - const GstStructure *structure = gst_caps_get_structure(caps, 0); - - int bitsPerPixel = 0; - QSize size = structureResolution(structure); - QVideoFrame::PixelFormat pixelFormat = structurePixelFormat(structure, &bitsPerPixel); - - if (pixelFormat != QVideoFrame::Format_Invalid) { - QVideoSurfaceFormat format(size, pixelFormat, handleType); - - QPair<qreal, qreal> rate = structureFrameRateRange(structure); - if (rate.second) - format.setFrameRate(rate.second); - - format.setPixelAspectRatio(structurePixelAspectRatio(structure)); - - if (bytesPerLine) - *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; - - return format; - } - return QVideoSurfaceFormat(); -} - -#endif - -GstCaps *QGstUtils::capsForFormats(const QList<QVideoFrame::PixelFormat> &formats) -{ - GstCaps *caps = gst_caps_new_empty(); - -#if GST_CHECK_VERSION(1,0,0) - for (QVideoFrame::PixelFormat format : formats) { - int index = indexOfVideoFormat(format); - - if (index != -1) { - gst_caps_append_structure(caps, gst_structure_new( - "video/x-raw", - "format" , G_TYPE_STRING, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat), - nullptr)); - } - } -#else - for (QVideoFrame::PixelFormat format : formats) { - int index = indexOfYuvColor(format); - - if (index != -1) { - gst_caps_append_structure(caps, gst_structure_new( - "video/x-raw-yuv", - "format", GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, - nullptr)); - continue; - } - - const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); - - for (int i = 0; i < count; ++i) { - if (qt_rgbColorLookup[i].pixelFormat == format) { - GstStructure *structure = gst_structure_new( - "video/x-raw-rgb", - "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, - "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, - "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, - "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, - "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, - "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, - nullptr); - - if (qt_rgbColorLookup[i].alpha != 0) { - gst_structure_set( - structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, nullptr); - } - gst_caps_append_structure(caps, structure); - } - } - } -#endif - - gst_caps_set_simple( - caps, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - nullptr); - - return caps; -} - -void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) -{ - // GStreamer uses nanoseconds, Qt uses microseconds - qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); - if (startTime >= 0) { - frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); - - qint64 duration = GST_BUFFER_DURATION(buffer); - if (duration >= 0) - frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); - } -} - -void QGstUtils::setMetaData(GstElement *element, const QMap<QByteArray, QVariant> &data) -{ - if (!GST_IS_TAG_SETTER(element)) - return; - - gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); - - for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { - const QString tagName = QString::fromLatin1(it.key()); - const QVariant &tagValue = it.value(); - - switch (tagValue.type()) { - case QVariant::String: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toString().toUtf8().constData(), - nullptr); - break; - case QVariant::Int: - case QVariant::LongLong: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toInt(), - nullptr); - break; - case QVariant::Double: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - tagValue.toDouble(), - nullptr); - break; -#if GST_CHECK_VERSION(0, 10, 31) - case QVariant::DateTime: { - QDateTime date = tagValue.toDateTime().toLocalTime(); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName.toUtf8().constData(), - gst_date_time_new_local_time( - date.date().year(), date.date().month(), date.date().day(), - date.time().hour(), date.time().minute(), date.time().second()), - nullptr); - break; - } -#endif - default: - break; - } - } -} - -void QGstUtils::setMetaData(GstBin *bin, const QMap<QByteArray, QVariant> &data) -{ - GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); -#if GST_CHECK_VERSION(1,0,0) - GValue item = G_VALUE_INIT; - while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { - GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); -#else - GstElement *element = 0; - while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { -#endif - setMetaData(element, data); - } - gst_iterator_free(elements); -} - - -GstCaps *QGstUtils::videoFilterCaps() -{ - const char *caps = -#if GST_CHECK_VERSION(1,2,0) - "video/x-raw(ANY);" -#elif GST_CHECK_VERSION(1,0,0) - "video/x-raw;" -#else - "video/x-raw-yuv;" - "video/x-raw-rgb;" - "video/x-raw-data;" - "video/x-android-buffer;" -#endif - "image/jpeg;" - "video/x-h264"; - static GstStaticCaps staticCaps = GST_STATIC_CAPS(caps); - - return gst_caps_make_writable(gst_static_caps_get(&staticCaps)); -} - -QSize QGstUtils::structureResolution(const GstStructure *s) -{ - QSize size; - - int w, h; - if (s && gst_structure_get_int(s, "width", &w) && gst_structure_get_int(s, "height", &h)) { - size.rwidth() = w; - size.rheight() = h; - } - - return size; -} - -QVideoFrame::PixelFormat QGstUtils::structurePixelFormat(const GstStructure *structure, int *bpp) -{ - QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; - - if (!structure) - return pixelFormat; - -#if GST_CHECK_VERSION(1,0,0) - Q_UNUSED(bpp); - - if (gst_structure_has_name(structure, "video/x-raw")) { - const gchar *s = gst_structure_get_string(structure, "format"); - if (s) { - GstVideoFormat format = gst_video_format_from_string(s); - int index = indexOfVideoFormat(format); - - if (index != -1) - pixelFormat = qt_videoFormatLookup[index].pixelFormat; - } - } -#else - if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { - guint32 fourcc = 0; - gst_structure_get_fourcc(structure, "format", &fourcc); - - int index = indexOfYuvColor(fourcc); - if (index != -1) { - pixelFormat = qt_yuvColorLookup[index].pixelFormat; - if (bpp) - *bpp = qt_yuvColorLookup[index].bitsPerPixel; - } - } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { - int bitsPerPixel = 0; - int depth = 0; - int endianness = 0; - int red = 0; - int green = 0; - int blue = 0; - int alpha = 0; - - gst_structure_get_int(structure, "bpp", &bitsPerPixel); - gst_structure_get_int(structure, "depth", &depth); - gst_structure_get_int(structure, "endianness", &endianness); - gst_structure_get_int(structure, "red_mask", &red); - gst_structure_get_int(structure, "green_mask", &green); - gst_structure_get_int(structure, "blue_mask", &blue); - gst_structure_get_int(structure, "alpha_mask", &alpha); - - int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); - - if (index != -1) { - pixelFormat = qt_rgbColorLookup[index].pixelFormat; - if (bpp) - *bpp = qt_rgbColorLookup[index].bitsPerPixel; - } - } -#endif - - return pixelFormat; -} - -QSize QGstUtils::structurePixelAspectRatio(const GstStructure *s) -{ - QSize ratio(1, 1); - - gint aspectNum = 0; - gint aspectDenum = 0; - if (s && gst_structure_get_fraction(s, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { - if (aspectDenum > 0) { - ratio.rwidth() = aspectNum; - ratio.rheight() = aspectDenum; - } - } - - return ratio; -} - -QPair<qreal, qreal> QGstUtils::structureFrameRateRange(const GstStructure *s) -{ - QPair<qreal, qreal> rate; - - if (!s) - return rate; - - int n, d; - if (gst_structure_get_fraction(s, "framerate", &n, &d)) { - rate.second = qreal(n) / d; - rate.first = rate.second; - } else if (gst_structure_get_fraction(s, "max-framerate", &n, &d)) { - rate.second = qreal(n) / d; - if (gst_structure_get_fraction(s, "min-framerate", &n, &d)) - rate.first = qreal(n) / d; - else - rate.first = qreal(1); - } - - return rate; -} - -typedef QMap<QString, QString> FileExtensionMap; -Q_GLOBAL_STATIC(FileExtensionMap, fileExtensionMap) - -QString QGstUtils::fileExtensionForMimeType(const QString &mimeType) -{ - if (fileExtensionMap->isEmpty()) { - //extension for containers hard to guess from mimetype - fileExtensionMap->insert(QStringLiteral("video/x-matroska"), QLatin1String("mkv")); - fileExtensionMap->insert(QStringLiteral("video/quicktime"), QLatin1String("mov")); - fileExtensionMap->insert(QStringLiteral("video/x-msvideo"), QLatin1String("avi")); - fileExtensionMap->insert(QStringLiteral("video/msvideo"), QLatin1String("avi")); - fileExtensionMap->insert(QStringLiteral("audio/mpeg"), QLatin1String("mp3")); - fileExtensionMap->insert(QStringLiteral("application/x-shockwave-flash"), QLatin1String("swf")); - fileExtensionMap->insert(QStringLiteral("application/x-pn-realmedia"), QLatin1String("rm")); - } - - //for container names like avi instead of video/x-msvideo, use it as extension - if (!mimeType.contains(QLatin1Char('/'))) - return mimeType; - - QString format = mimeType.left(mimeType.indexOf(QLatin1Char(','))); - QString extension = fileExtensionMap->value(format); - - if (!extension.isEmpty() || format.isEmpty()) - return extension; - - QRegularExpression rx(QStringLiteral("[-/]([\\w]+)$")); - QRegularExpressionMatch match = rx.match(format); - - if (match.hasMatch()) - extension = match.captured(1); - - return extension; -} - -#if GST_CHECK_VERSION(0,10,30) -QVariant QGstUtils::fromGStreamerOrientation(const QVariant &value) -{ - // Note gstreamer tokens either describe the counter clockwise rotation of the - // image or the clockwise transform to apply to correct the image. The orientation - // value returned is the clockwise rotation of the image. - const QString token = value.toString(); - if (token == QStringLiteral("rotate-90")) - return 270; - if (token == QStringLiteral("rotate-180")) - return 180; - if (token == QStringLiteral("rotate-270")) - return 90; - return 0; -} - -QVariant QGstUtils::toGStreamerOrientation(const QVariant &value) -{ - switch (value.toInt()) { - case 90: - return QStringLiteral("rotate-270"); - case 180: - return QStringLiteral("rotate-180"); - case 270: - return QStringLiteral("rotate-90"); - default: - return QStringLiteral("rotate-0"); - } -} -#endif - -bool QGstUtils::useOpenGL() -{ - static bool result = qEnvironmentVariableIntValue("QT_GSTREAMER_USE_OPENGL_PLUGIN"); - return result; -} - -void qt_gst_object_ref_sink(gpointer object) -{ -#if GST_CHECK_VERSION(0,10,24) - gst_object_ref_sink(object); -#else - g_return_if_fail (GST_IS_OBJECT(object)); - - GST_OBJECT_LOCK(object); - if (G_LIKELY(GST_OBJECT_IS_FLOATING(object))) { - GST_OBJECT_FLAG_UNSET(object, GST_OBJECT_FLOATING); - GST_OBJECT_UNLOCK(object); - } else { - GST_OBJECT_UNLOCK(object); - gst_object_ref(object); - } -#endif -} - -GstCaps *qt_gst_pad_get_current_caps(GstPad *pad) -{ -#if GST_CHECK_VERSION(1,0,0) - return gst_pad_get_current_caps(pad); -#else - return gst_pad_get_negotiated_caps(pad); -#endif -} - -GstCaps *qt_gst_pad_get_caps(GstPad *pad) -{ -#if GST_CHECK_VERSION(1,0,0) - return gst_pad_query_caps(pad, nullptr); -#elif GST_CHECK_VERSION(0, 10, 26) - return gst_pad_get_caps_reffed(pad); -#else - return gst_pad_get_caps(pad); -#endif -} - -GstStructure *qt_gst_structure_new_empty(const char *name) -{ -#if GST_CHECK_VERSION(1,0,0) - return gst_structure_new_empty(name); -#else - return gst_structure_new(name, nullptr); -#endif -} - -gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur) -{ -#if GST_CHECK_VERSION(1,0,0) - return gst_element_query_position(element, format, cur); -#else - return gst_element_query_position(element, &format, cur); -#endif -} - -gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur) -{ -#if GST_CHECK_VERSION(1,0,0) - return gst_element_query_duration(element, format, cur); -#else - return gst_element_query_duration(element, &format, cur); -#endif -} - -GstCaps *qt_gst_caps_normalize(GstCaps *caps) -{ -#if GST_CHECK_VERSION(1,0,0) - // gst_caps_normalize() takes ownership of the argument in 1.0 - return gst_caps_normalize(caps); -#else - // in 0.10, it doesn't. Unref the argument to mimic the 1.0 behavior - GstCaps *res = gst_caps_normalize(caps); - gst_caps_unref(caps); - return res; -#endif -} - -const gchar *qt_gst_element_get_factory_name(GstElement *element) -{ - const gchar *name = 0; - const GstElementFactory *factory = 0; - - if (element && (factory = gst_element_get_factory(element))) - name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - - return name; -} - -gboolean qt_gst_caps_can_intersect(const GstCaps * caps1, const GstCaps * caps2) -{ -#if GST_CHECK_VERSION(0, 10, 25) - return gst_caps_can_intersect(caps1, caps2); -#else - GstCaps *intersection = gst_caps_intersect(caps1, caps2); - gboolean res = !gst_caps_is_empty(intersection); - gst_caps_unref(intersection); - return res; -#endif -} - -#if !GST_CHECK_VERSION(0, 10, 31) -static gboolean qt_gst_videosink_factory_filter(GstPluginFeature *feature, gpointer) -{ - guint rank; - const gchar *klass; - - if (!GST_IS_ELEMENT_FACTORY(feature)) - return FALSE; - - klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); - if (!(strstr(klass, "Sink") && strstr(klass, "Video"))) - return FALSE; - - rank = gst_plugin_feature_get_rank(feature); - if (rank < GST_RANK_MARGINAL) - return FALSE; - - return TRUE; -} - -static gint qt_gst_compare_ranks(GstPluginFeature *f1, GstPluginFeature *f2) -{ - gint diff; - - diff = gst_plugin_feature_get_rank(f2) - gst_plugin_feature_get_rank(f1); - if (diff != 0) - return diff; - - return strcmp(gst_plugin_feature_get_name(f2), gst_plugin_feature_get_name (f1)); -} -#endif - -GList *qt_gst_video_sinks() -{ - GList *list = nullptr; - -#if GST_CHECK_VERSION(0, 10, 31) - list = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, - GST_RANK_MARGINAL); -#else - list = gst_registry_feature_filter(gst_registry_get_default(), - (GstPluginFeatureFilter)qt_gst_videosink_factory_filter, - FALSE, nullptr); - list = g_list_sort(list, (GCompareFunc)qt_gst_compare_ranks); -#endif - - return list; -} - -void qt_gst_util_double_to_fraction(gdouble src, gint *dest_n, gint *dest_d) -{ -#if GST_CHECK_VERSION(0, 10, 26) - gst_util_double_to_fraction(src, dest_n, dest_d); -#else - qt_real_to_fraction(src, dest_n, dest_d); -#endif -} - -QDebug operator <<(QDebug debug, GstCaps *caps) -{ - if (caps) { - gchar *string = gst_caps_to_string(caps); - debug = debug << string; - g_free(string); - } - return debug; -} - -QT_END_NAMESPACE |