diff options
Diffstat (limited to 'src/multimedia/platform/gstreamer')
8 files changed, 297 insertions, 525 deletions
diff --git a/src/multimedia/platform/gstreamer/common/common.pri b/src/multimedia/platform/gstreamer/common/common.pri index b57cf4d00..54167b557 100644 --- a/src/multimedia/platform/gstreamer/common/common.pri +++ b/src/multimedia/platform/gstreamer/common/common.pri @@ -7,7 +7,6 @@ HEADERS += \ $$PWD/qgstreamerbufferprobe_p.h \ $$PWD/qgstreamervideorendererinterface_p.h \ $$PWD/qgstreamervideorenderer_p.h \ - $$PWD/qgstcodecsinfo_p.h \ $$PWD/qgstreamervideowindow_p.h \ $$PWD/qgstreamervideooverlay_p.h \ $$PWD/qgstreamerplayersession_p.h \ @@ -24,7 +23,6 @@ SOURCES += \ $$PWD/qgstreamerbufferprobe.cpp \ $$PWD/qgstreamervideorendererinterface.cpp \ $$PWD/qgstreamervideorenderer.cpp \ - $$PWD/qgstcodecsinfo.cpp \ $$PWD/qgstreamervideowindow.cpp \ $$PWD/qgstreamervideooverlay.cpp \ $$PWD/qgstreamerplayersession.cpp \ diff --git a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp deleted file mode 100644 index 06e034198..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo.cpp +++ /dev/null @@ -1,273 +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 "qgstcodecsinfo_p.h" -#include "qgstutils_p.h" -#include <QtCore/qset.h> - -#include <gst/pbutils/pbutils.h> - -static QSet<QString> streamTypes(GstElementFactory *factory, GstPadDirection direction) -{ - QSet<QString> types; - const GList *pads = gst_element_factory_get_static_pad_templates(factory); - for (const GList *pad = pads; pad; pad = g_list_next(pad)) { - GstStaticPadTemplate *templ = reinterpret_cast<GstStaticPadTemplate *>(pad->data); - if (templ->direction == direction) { - GstCaps *caps = gst_static_caps_get(&templ->static_caps); - for (uint i = 0; i < gst_caps_get_size(caps); ++i) { - GstStructure *structure = gst_caps_get_structure(caps, i); - types.insert(QString::fromUtf8(gst_structure_get_name(structure))); - } - gst_caps_unref(caps); - } - } - - return types; -} - -QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType) -{ - updateCodecs(elementType); - for (auto &codec : supportedCodecs()) { - GstElementFactory *factory = gst_element_factory_find(codecElement(codec).constData()); - if (factory) { - GstPadDirection direction = elementType == Muxer ? GST_PAD_SINK : GST_PAD_SRC; - m_streamTypes.insert(codec, streamTypes(factory, direction)); - gst_object_unref(GST_OBJECT(factory)); - } - } -} - -QStringList QGstCodecsInfo::supportedCodecs() const -{ - return m_codecs; -} - -QString QGstCodecsInfo::codecDescription(const QString &codec) const -{ - return m_codecInfo.value(codec).description; -} - -QByteArray QGstCodecsInfo::codecElement(const QString &codec) const - -{ - return m_codecInfo.value(codec).elementName; -} - -QStringList QGstCodecsInfo::codecOptions(const QString &codec) const -{ - QStringList options; - - QByteArray elementName = m_codecInfo.value(codec).elementName; - if (elementName.isEmpty()) - return options; - - GstElement *element = gst_element_factory_make(elementName, nullptr); - if (element) { - guint numProperties; - GParamSpec **properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(element), - &numProperties); - for (guint j = 0; j < numProperties; ++j) { - GParamSpec *property = properties[j]; - // ignore some properties - if (strcmp(property->name, "name") == 0 || strcmp(property->name, "parent") == 0) - continue; - - options.append(QLatin1String(property->name)); - } - g_free(properties); - gst_object_unref(element); - } - - return options; -} - -void QGstCodecsInfo::updateCodecs(ElementType elementType) -{ - m_codecs.clear(); - m_codecInfo.clear(); - - GstPadDirection padDirection = GST_PAD_SRC; - if (elementType == Demuxer || elementType == AudioDecoder || elementType == VideoDecoder) - padDirection = GST_PAD_SINK; - - GList *elements = elementFactories(elementType); - - QSet<QByteArray> fakeEncoderMimeTypes; - fakeEncoderMimeTypes << "unknown/unknown" - << "audio/x-raw-int" << "audio/x-raw-float" - << "video/x-raw-yuv" << "video/x-raw-rgb"; - - QSet<QByteArray> fieldsToAdd; - fieldsToAdd << "mpegversion" << "layer" << "layout" - << "variant"; - - GList *element = elements; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == padDirection) { - GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps); - for (uint i=0; i<gst_caps_get_size(caps); i++) { - const GstStructure *structure = gst_caps_get_structure(caps, i); - - //skip "fake" encoders - if (fakeEncoderMimeTypes.contains(gst_structure_get_name(structure))) - continue; - - GstStructure *newStructure = qt_gst_structure_new_empty(gst_structure_get_name(structure)); - - //add structure fields to distinguish between formats with similar mime types, - //like audio/mpeg - for (int j=0; j<gst_structure_n_fields(structure); j++) { - const gchar* fieldName = gst_structure_nth_field_name(structure, j); - if (fieldsToAdd.contains(fieldName)) { - const GValue *value = gst_structure_get_value(structure, fieldName); - GType valueType = G_VALUE_TYPE(value); - - //don't add values of range type, - //gst_pb_utils_get_codec_description complains about not fixed caps - - if (valueType != GST_TYPE_INT_RANGE && valueType != GST_TYPE_DOUBLE_RANGE && - valueType != GST_TYPE_FRACTION_RANGE && valueType != GST_TYPE_LIST && - valueType != GST_TYPE_ARRAY) - gst_structure_set_value(newStructure, fieldName, value); - } - } - - GstCaps *newCaps = gst_caps_new_full(newStructure, nullptr); - - gchar *capsString = gst_caps_to_string(newCaps); - QString codec = QLatin1String(capsString); - if (capsString) - g_free(capsString); - // skip stuff that's not really a known audio/video codec - if (codec.startsWith("application")) - continue; - GstRank rank = GstRank(gst_plugin_feature_get_rank(GST_PLUGIN_FEATURE(factory))); - - // If two elements provide the same codec, use the highest ranked one - QMap<QString, CodecInfo>::const_iterator it = m_codecInfo.constFind(codec); - if (it == m_codecInfo.constEnd() || it->rank < rank) { - if (it == m_codecInfo.constEnd()) - m_codecs.append(codec); - - CodecInfo info; - info.elementName = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); - - gchar *description = gst_pb_utils_get_codec_description(newCaps); - info.description = QString::fromUtf8(description); - if (description) - g_free(description); - - info.rank = rank; - - m_codecInfo.insert(codec, info); - } - - gst_caps_unref(newCaps); - } - gst_caps_unref(caps); - } - } - } - - gst_plugin_feature_list_free(elements); -} - -GList *QGstCodecsInfo::elementFactories(ElementType elementType) const -{ - GstElementFactoryListType gstElementType = 0; - switch (elementType) { - case AudioEncoder: - gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER; - break; - case VideoEncoder: - // GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER also lists image encoders. We don't want these here. - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); - break; - case Muxer: - gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER; - break; - case AudioDecoder: - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO); - break; - case VideoDecoder: - gstElementType = (GstElementFactoryListType)(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO); - break; - case Demuxer: - gstElementType = GST_ELEMENT_FACTORY_TYPE_DEMUXER; - break; - } - - GList *list = gst_element_factory_list_get_elements(gstElementType, GST_RANK_MARGINAL); - if (elementType == AudioEncoder) { - // Manually add "audioconvert" to the list - // to allow linking with various containers. - auto factory = gst_element_factory_find("audioconvert"); - if (factory) - list = g_list_prepend(list, factory); - } - - return list; -} - -QSet<QString> QGstCodecsInfo::supportedStreamTypes(const QString &codec) const -{ - return m_streamTypes.value(codec); -} - -QStringList QGstCodecsInfo::supportedCodecs(const QSet<QString> &types) const -{ - QStringList result; - for (auto &candidate : supportedCodecs()) { - auto candidateTypes = supportedStreamTypes(candidate); - if (candidateTypes.intersects(types)) - result << candidate; - } - - return result; -} diff --git a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h b/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h deleted file mode 100644 index fb2fb71d9..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstcodecsinfo_p.h +++ /dev/null @@ -1,96 +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$ -** -****************************************************************************/ - -#ifndef QGSTCODECSINFO_H -#define QGSTCODECSINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <QtCore/qmap.h> -#include <QtCore/qstringlist.h> -#include <QSet> - -#include <gst/gst.h> - -QT_BEGIN_NAMESPACE - -class Q_MULTIMEDIA_EXPORT QGstCodecsInfo -{ -public: - enum ElementType { AudioEncoder, VideoEncoder, Muxer, AudioDecoder, VideoDecoder, Demuxer }; - - struct CodecInfo { - QString description; - QByteArray elementName; - GstRank rank; - }; - - QGstCodecsInfo(ElementType elementType); - - QStringList supportedCodecs() const; - QString codecDescription(const QString &codec) const; - QByteArray codecElement(const QString &codec) const; - QStringList codecOptions(const QString &codec) const; - QSet<QString> supportedStreamTypes(const QString &codec) const; - QStringList supportedCodecs(const QSet<QString> &types) const; - -private: - void updateCodecs(ElementType elementType); - GList *elementFactories(ElementType elementType) const; - - QStringList m_codecs; - QMap<QString, CodecInfo> m_codecInfo; - QMap<QString, QSet<QString>> m_streamTypes; -}; - -Q_DECLARE_TYPEINFO(QGstCodecsInfo::CodecInfo, Q_RELOCATABLE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstutils_p.h b/src/multimedia/platform/gstreamer/common/qgstutils_p.h index 7b6b6ded1..5cf5035ea 100644 --- a/src/multimedia/platform/gstreamer/common/qgstutils_p.h +++ b/src/multimedia/platform/gstreamer/common/qgstutils_p.h @@ -195,6 +195,10 @@ class QGstCaps { public: QGstCaps(const GstCaps *c) : caps(c) {} const GstCaps *caps; + void unref() { + gst_caps_unref(const_cast<GstCaps *>(caps)); + caps = nullptr; + } bool isNull() const { return !caps; } diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp index 193ccaa34..131e18e80 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercapturesession.cpp @@ -129,25 +129,17 @@ GstElement *QGstreamerCaptureSession::buildFileSink() static GstEncodingContainerProfile *createContainerProfile(const QMediaEncoderSettings &settings) { - GstCaps *caps = nullptr; - auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableMediaContainers().contains(settings.format())) - return nullptr; - - const char *format = formatInfo->nativeFormat(settings.format()); - Q_ASSERT(format); - - caps = gst_caps_from_string(format); + QGstCaps caps = formatInfo->formatCaps(settings.format()); GstEncodingContainerProfile *profile = (GstEncodingContainerProfile *)gst_encoding_container_profile_new( "container_profile", (gchar *)"custom container profile", - caps, + const_cast<GstCaps *>(caps.caps), NULL); //preset - gst_caps_unref(caps); + caps.unref(); return profile; } @@ -156,23 +148,17 @@ static GstEncodingProfile *createVideoProfile(const QMediaEncoderSettings &setti { auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableVideoCodecs().contains(settings.videoCodec())) - return nullptr; - - const char *codec = formatInfo->nativeFormat(settings.videoCodec()); - Q_ASSERT(codec); - GstCaps *caps = gst_caps_from_string(codec); - - if (!caps) + QGstCaps caps = formatInfo->videoCaps(settings); + if (caps.isNull()) return nullptr; GstEncodingVideoProfile *profile = gst_encoding_video_profile_new( - caps, + const_cast<GstCaps *>(caps.caps), nullptr, NULL, //restriction 0); //presence - gst_caps_unref(caps); + caps.unref(); gst_encoding_video_profile_set_pass(profile, 0); gst_encoding_video_profile_set_variableframerate(profile, TRUE); @@ -184,21 +170,17 @@ static GstEncodingProfile *createAudioProfile(const QMediaEncoderSettings &setti { auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - if (!formatInfo->encodableAudioCodecs().contains(settings.audioCodec())) + QGstCaps caps = formatInfo->audioCaps(settings); + if (caps.isNull()) return nullptr; - const char *codec = formatInfo->nativeFormat(settings.audioCodec()); - Q_ASSERT(codec); - - GstCaps *caps = gst_caps_from_string(codec); - GstEncodingProfile *profile = (GstEncodingProfile *)gst_encoding_audio_profile_new( - caps, + const_cast<GstCaps *>(caps.caps), nullptr, //preset NULL, //restriction 0); //presence - gst_caps_unref(caps); + caps.unref(); return profile; } diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp index 7c60ed4d1..fabf6cf20 100644 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp +++ b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp @@ -42,6 +42,7 @@ #include <QtGui/qdesktopservices.h> #include <QStandardPaths> #include "qaudiodeviceinfo.h" +#include <qmimetype.h> QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session) : QMediaRecorderControl(session), @@ -154,9 +155,9 @@ void QGstreamerRecorderControl::record() m_state = QMediaRecorder::RecordingState; if (m_outputLocation.isEmpty()) { - QString container;// ### = m_session->mediaContainerControl()->containerExtension(); + QString container = resolvedEncoderSettings().mimeType().preferredSuffix(); if (container.isEmpty()) - container = "raw"; + container = QString::fromLatin1("raw"); m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container))); } @@ -232,12 +233,10 @@ void QGstreamerRecorderControl::setEncoderSettings(const QMediaEncoderSettings & QMediaEncoderSettings QGstreamerRecorderControl::resolvedEncoderSettings() const { QMediaEncoderSettings f = m_settings; - f.resolveFormat(m_session->captureMode() & QGstreamerCaptureSession::Video ? - QMediaEncoderSettings::AudioAndVideo : QMediaEncoderSettings::AudioOnly); + f.resolveFormat(); return f; } - bool QGstreamerRecorderControl::isMuted() const { return m_session->isMuted(); diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp index 1e5c41969..b0fd8e53e 100644 --- a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp +++ b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo.cpp @@ -40,135 +40,308 @@ #include "qgstreamerformatsinfo_p.h" #include "private/qgstutils_p.h" -#include "private/qgstcodecsinfo_p.h" QT_BEGIN_NAMESPACE -static struct { - const char *name; - QMediaFormat::FileFormat value; -} videoFormatsMap[] = { - { "video/x-ms-asf", QMediaFormat::FileFormat::ASF }, - { "video/x-msvideo", QMediaFormat::FileFormat::AVI }, - { "video/x-matroska", QMediaFormat::FileFormat::Matroska }, - { "video/mpeg", QMediaFormat::FileFormat::MPEG4 }, - { "video/quicktime", QMediaFormat::FileFormat::QuickTime }, - { "video/ogg", QMediaFormat::FileFormat::Ogg }, - { "video/webm", QMediaFormat::FileFormat::WebM }, - { nullptr, QMediaFormat::FileFormat::UnspecifiedFormat } -}; - -static struct { - const char *name; - QMediaFormat::FileFormat value; -} audioFormatsMap[] = { - { "audio/mpeg, mpegversion=(int)1, layer=(int)3", QMediaFormat::FileFormat::MP3 }, - { "audio/mpeg, mpegversion=(int)4", QMediaFormat::FileFormat::AAC }, - { "audio/x-flac", QMediaFormat::FileFormat::FLAC }, - { "audio/x-alac", QMediaFormat::FileFormat::ALAC }, - { nullptr, QMediaFormat::FileFormat::UnspecifiedFormat }, -}; - -static struct { - const char *name; - QMediaFormat::VideoCodec value; -} videoCodecMap[] = { - { "video/mpeg, mpegversion=(int)1", QMediaFormat::VideoCodec::MPEG1 }, - { "video/mpeg, mpegversion=(int)2", QMediaFormat::VideoCodec::MPEG2 }, - { "video/mpeg, mpegversion=(int)4", QMediaFormat::VideoCodec::MPEG4 }, - { "video/x-h264", QMediaFormat::VideoCodec::H264 }, - { "video/x-h265", QMediaFormat::VideoCodec::H265 }, - { "video/x-vp8", QMediaFormat::VideoCodec::VP8 }, - { "video/x-vp9", QMediaFormat::VideoCodec::VP9 }, - { "video/x-av1", QMediaFormat::VideoCodec::AV1 }, - { "video/x-theora", QMediaFormat::VideoCodec::Theora }, - { "video/x-jpeg", QMediaFormat::VideoCodec::MotionJPEG }, - { nullptr, QMediaFormat::VideoCodec::Unspecified } -}; - -static struct { - const char *name; - QMediaFormat::AudioCodec value; -} audioCodecMap[] = { - { "audio/mpeg, mpegversion=(int)1, layer=(int)3", QMediaFormat::AudioCodec::MP3 }, - { "audio/mpeg, mpegversion=(int)4", QMediaFormat::AudioCodec::AAC }, - { "audio/x-ac3", QMediaFormat::AudioCodec::AC3 }, - { "audio/x-eac3", QMediaFormat::AudioCodec::EAC3 }, - { "audio/x-flac", QMediaFormat::AudioCodec::FLAC }, - { "audio/x-alac", QMediaFormat::AudioCodec::ALAC }, - { "audio/x-true-hd", QMediaFormat::AudioCodec::DolbyTrueHD }, - { "audio/x-vorbis", QMediaFormat::AudioCodec::Vorbis }, - { nullptr, QMediaFormat::AudioCodec::Unspecified }, -}; - -template<typename Map, typename Hash> -static auto getList(QGstCodecsInfo::ElementType type, Map *map, Hash &hash) +static QMediaFormat::AudioCodec audioCodecForCaps(QGstStructure structure) { - using T = decltype(map->value); - QList<T> list; - QGstCodecsInfo info(type); - auto codecs = info.supportedCodecs(); - for (const auto &c : codecs) { - Map *m = map; - while (m->name) { - if (m->name == c.toLatin1()) { - list.append(m->value); - hash.insert(m->value, m->name); - break; - } - ++m; + const char *name = structure.name(); + + if (!name || strncmp(name, "audio/", 6)) + return QMediaFormat::AudioCodec::Unspecified; + name += 6; + if (!strcmp(name, "mpeg")) { + auto version = structure["mpegversion"].toInt(); + if (version == 1) { + auto layer = structure["layer"].toInt(); + if (layer == 3) + return QMediaFormat::AudioCodec::MP3; } + if (version == 4) + return QMediaFormat::AudioCodec::AAC; + } else if (!strcmp(name, "x-ac3")) { + return QMediaFormat::AudioCodec::AC3; + } else if (!strcmp(name, "x-eac3")) { + return QMediaFormat::AudioCodec::EAC3; + } else if (!strcmp(name, "x-flac")) { + return QMediaFormat::AudioCodec::FLAC; + } else if (!strcmp(name, "x-alac")) { + return QMediaFormat::AudioCodec::ALAC; + } else if (!strcmp(name, "x-true-hd")) { + return QMediaFormat::AudioCodec::DolbyTrueHD; + } else if (!strcmp(name, "x-vorbis")) { + return QMediaFormat::AudioCodec::Vorbis; + } else if (!strcmp(name, "x-opus")) { + return QMediaFormat::AudioCodec::Opus; + } else if (!strcmp(name, "x-wav")) { + return QMediaFormat::AudioCodec::Wave; } - return list; + return QMediaFormat::AudioCodec::Unspecified; } -QGstreamerFormatsInfo::QGstreamerFormatsInfo() +static QMediaFormat::VideoCodec videoCodecForCaps(QGstStructure structure) +{ + const char *name = structure.name(); + + if (!name || strncmp(name, "video/", 6)) + return QMediaFormat::VideoCodec::Unspecified; + name += 6; + + if (!strcmp(name, "mpeg")) { + auto version = structure["mpegversion"].toInt(); + if (version == 1) + return QMediaFormat::VideoCodec::MPEG1; + else if (version == 2) + return QMediaFormat::VideoCodec::MPEG2; + else if (version == 4) + return QMediaFormat::VideoCodec::MPEG4; + } else if (!strcmp(name, "x-h264")) { + return QMediaFormat::VideoCodec::H264; + } else if (!strcmp(name, "x-h265")) { + return QMediaFormat::VideoCodec::H265; + } else if (!strcmp(name, "x-vp8")) { + return QMediaFormat::VideoCodec::VP8; + } else if (!strcmp(name, "x-vp9")) { + return QMediaFormat::VideoCodec::VP9; + } else if (!strcmp(name, "x-av1")) { + return QMediaFormat::VideoCodec::AV1; + } else if (!strcmp(name, "x-theora")) { + return QMediaFormat::VideoCodec::Theora; + } else if (!strcmp(name, "x-jpeg")) { + return QMediaFormat::VideoCodec::MotionJPEG; + } + return QMediaFormat::VideoCodec::Unspecified; +} + +static QMediaFormat::FileFormat fileFormatForCaps(QGstStructure structure) { - m_decodableMediaContainers = getList(QGstCodecsInfo::Demuxer, videoFormatsMap, formatToCaps); - m_decodableMediaContainers.append(getList(QGstCodecsInfo::AudioDecoder, audioFormatsMap, formatToCaps)); - m_decodableAudioCodecs = getList(QGstCodecsInfo::AudioDecoder, audioCodecMap, audioToCaps); - m_decodableVideoCodecs = getList(QGstCodecsInfo::VideoDecoder, videoCodecMap, videoToCaps); - - m_encodableMediaContainers = getList(QGstCodecsInfo::Muxer, videoFormatsMap, formatToCaps); - m_encodableMediaContainers.append(getList(QGstCodecsInfo::AudioEncoder, audioFormatsMap, formatToCaps)); - m_encodableAudioCodecs = getList(QGstCodecsInfo::AudioEncoder, audioCodecMap, audioToCaps); - m_encodableVideoCodecs = getList(QGstCodecsInfo::VideoEncoder, videoCodecMap, videoToCaps); + const char *name = structure.name(); + + if (!strcmp(name, "video/x-ms-asf")) { + return QMediaFormat::FileFormat::ASF; + } else if (!strcmp(name, "video/x-msvideo")) { + return QMediaFormat::FileFormat::AVI; + } else if (!strcmp(name, "video/x-matroska")) { + return QMediaFormat::FileFormat::Matroska; + } else if (!strcmp(name, "video/quicktime")) { + auto variant = structure["variant"].toString(); + if (!variant) + return QMediaFormat::FileFormat::QuickTime; + else if (!strcmp(variant, "iso")) + return QMediaFormat::FileFormat::MPEG4; + } else if (!strcmp(name, "video/ogg")) { + return QMediaFormat::FileFormat::Ogg; + } else if (!strcmp(name, "video/webm")) { + return QMediaFormat::FileFormat::WebM; + } + return QMediaFormat::UnspecifiedFormat; } -QGstreamerFormatsInfo::~QGstreamerFormatsInfo() +static QPair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>> getCodecsList(bool decode) { + QList<QMediaFormat::AudioCodec> audio; + QList<QMediaFormat::VideoCodec> video; + GstPadDirection padDirection = decode ? GST_PAD_SINK : GST_PAD_SRC; + + GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER, + GST_RANK_MARGINAL); + + GList *element = elementList; + while (element) { + GstElementFactory *factory = (GstElementFactory *)element->data; + element = element->next; + + const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + if (padTemplate->direction == padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto a = audioCodecForCaps(structure); + if (a != QMediaFormat::AudioCodec::Unspecified) + audio.append(a); + auto v = videoCodecForCaps(structure); + if (v != QMediaFormat::VideoCodec::Unspecified) + video.append(v); + } + caps.unref(); + } + } + } + gst_plugin_feature_list_free(elementList); + return {audio, video}; } -QList<QMediaFormat::FileFormat> QGstreamerFormatsInfo::decodableMediaContainers() const +QList<QGstreamerFormatsInfo::CodecMap> QGstreamerFormatsInfo::getMuxerList(bool demuxer, + QList<QMediaFormat::AudioCodec> supportedAudioCodecs, + QList<QMediaFormat::VideoCodec> supportedVideoCodecs) +{ + QList<QGstreamerFormatsInfo::CodecMap> muxers; + + GstPadDirection padDirection = demuxer ? GST_PAD_SINK : GST_PAD_SRC; + + GList *elementList = gst_element_factory_list_get_elements(demuxer ? GST_ELEMENT_FACTORY_TYPE_DEMUXER : GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_MARGINAL); + GList *element = elementList; + while (element) { + GstElementFactory *factory = (GstElementFactory *)element->data; + element = element->next; + + QList<QMediaFormat::FileFormat> fileFormats; + + const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + if (padTemplate->direction == padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto fmt = fileFormatForCaps(structure); + if (fmt != QMediaFormat::UnspecifiedFormat) + fileFormats.append(fmt); + } + caps.unref(); + } + } + if (fileFormats.isEmpty()) + continue; + + QList<QMediaFormat::AudioCodec> audioCodecs; + QList<QMediaFormat::VideoCodec> videoCodecs; + + padTemplates = gst_element_factory_get_static_pad_templates(factory); + while (padTemplates) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; + padTemplates = padTemplates->next; + + // check the other side for supported inputs/outputs + if (padTemplate->direction != padDirection) { + QGstCaps caps = gst_static_caps_get(&padTemplate->static_caps); + + for (int i = 0; i < caps.size(); i++) { + QGstStructure structure = caps.at(i); + auto audio = audioCodecForCaps(structure); + if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio)) + audioCodecs.append(audio); + auto video = videoCodecForCaps(structure); + if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video)) + videoCodecs.append(video); + } + caps.unref(); + } + } + if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) { + for (auto f : qAsConst(fileFormats)) + muxers.append({f, audioCodecs, videoCodecs}); + } + } + gst_plugin_feature_list_free(elementList); + return muxers; +} + +#if 0 +static void dumpMuxers(const QList<QGstreamerFormatsInfo::CodecMap> &muxerList) { - return m_decodableMediaContainers; + for (const auto &m : muxerList) { + qDebug() << " " << QMediaFormat::fileFormatName(m.format); + qDebug() << " Audio"; + for (const auto &a : m.audio) + qDebug() << " " << QMediaFormat::audioCodecName(a); + qDebug() << " Video"; + for (const auto &v : m.video) + qDebug() << " " << QMediaFormat::videoCodecName(v); + } + } +#endif -QList<QMediaFormat::AudioCodec> QGstreamerFormatsInfo::decodableAudioCodecs() const +QGstreamerFormatsInfo::QGstreamerFormatsInfo() { - return m_decodableAudioCodecs; + auto codecs = getCodecsList(/*decode = */ true); + decoders = getMuxerList(true, codecs.first, codecs.second); + + codecs = getCodecsList(/*decode = */ false); + encoders = getMuxerList(/* demuxer = */false, codecs.first, codecs.second); + //dumpMuxers(encoders); } -QList<QMediaFormat::VideoCodec> QGstreamerFormatsInfo::decodableVideoCodecs() const +QGstreamerFormatsInfo::~QGstreamerFormatsInfo() { - return m_decodableVideoCodecs; } -QList<QMediaFormat::FileFormat> QGstreamerFormatsInfo::encodableMediaContainers() const +QGstCaps QGstreamerFormatsInfo::formatCaps(const QMediaFormat &f) const { - return m_encodableMediaContainers; + auto format = f.format(); + Q_ASSERT(format != QMediaFormat::UnspecifiedFormat); + + const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = { + "video/x-ms-asf", // ASF + "video/x-msvideo", // AVI + "video/x-matroska", // Matroska + "video/quicktime, variant=(string)iso", // MPEG4 + "video/ogg", // Ogg + "video/quicktime", // QuickTime + "video/webm", // WebM + "audio/mpeg, mpegversion=(int)4", // AAC + "audio/x-flac", // FLAC + "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 + "audio/mpeg, mpegversion=(int)4", // Mpeg4Audio + "audio/x-alac", // ALAC + "audio/x-wav" // Wave + + }; + return gst_caps_from_string(capsForFormat[format]); } -QList<QMediaFormat::AudioCodec> QGstreamerFormatsInfo::encodableAudioCodecs() const +QGstCaps QGstreamerFormatsInfo::audioCaps(const QMediaFormat &f) const { - return m_encodableAudioCodecs; + auto codec = f.audioCodec(); + if (codec == QMediaFormat::AudioCodec::Unspecified) + return nullptr; + + const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = { + "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 + "audio/mpeg, mpegversion=(int)4", // AAC + "audio/x-ac3", // AC3 + "audio/x-eac3", // EAC3 + "audio/x-flac", // FLAC + "audio/x-true-hd", // DolbyTrueHD + "audio/x-opus", // Opus + "audio/x-vorbis", // Vorbis + "audio/x-alac", // ALAC + "audio/x-wav", // WAVE + }; + return gst_caps_from_string(capsForCodec[(int)codec]); } -QList<QMediaFormat::VideoCodec> QGstreamerFormatsInfo::encodableVideoCodecs() const +QGstCaps QGstreamerFormatsInfo::videoCaps(const QMediaFormat &f) const { - return m_encodableVideoCodecs; + auto codec = f.videoCodec(); + if (codec == QMediaFormat::VideoCodec::Unspecified) + return nullptr; + + const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = { + "video/mpeg, mpegversion=(int)1", // MPEG1, + "video/mpeg, mpegversion=(int)2", // MPEG2, + "video/mpeg, mpegversion=(int)4", // MPEG4, + "video/x-h264", // H264, + "video/x-h265", // H265, + "video/x-vp8", // VP8, + "video/x-vp9", // VP9, + "video/x-av1", // AV1, + "video/x-theora", // Theora, + "video/x-jpeg", // MotionJPEG, + }; + return gst_caps_from_string(capsForCodec[(int)codec]); } QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h index e81319532..cb9d4d880 100644 --- a/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h +++ b/src/multimedia/platform/gstreamer/qgstreamerformatsinfo_p.h @@ -54,6 +54,7 @@ #include <private/qmediaplatformformatinfo_p.h> #include <qhash.h> #include <qlist.h> +#include <private/qgstutils_p.h> QT_BEGIN_NAMESPACE @@ -63,30 +64,14 @@ public: QGstreamerFormatsInfo(); ~QGstreamerFormatsInfo(); - QList<QMediaFormat::FileFormat> decodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> decodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> decodableVideoCodecs() const override; + QGstCaps formatCaps(const QMediaFormat &f) const; + QGstCaps audioCaps(const QMediaFormat &f) const; + QGstCaps videoCaps(const QMediaFormat &f) const; + // ### +// QGstCaps audioEncoderCaps(const QMediaEncoderSettings &f) const; +// QGstCaps videoEncoderCaps(const QMediaEncoderSettings &f) const; - QList<QMediaFormat::FileFormat> encodableMediaContainers() const override; - QList<QMediaFormat::AudioCodec> encodableAudioCodecs() const override; - QList<QMediaFormat::VideoCodec> encodableVideoCodecs() const override; - - const char *nativeFormat(QMediaFormat::FileFormat f) const { return formatToCaps.value(f); } - const char *nativeFormat(QMediaFormat::AudioCodec c) const { return audioToCaps.value(c); } - const char *nativeFormat(QMediaFormat::VideoCodec c) const { return videoToCaps.value(c); } - -private: - QList<QMediaFormat::FileFormat> m_decodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_decodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_decodableVideoCodecs; - - QList<QMediaFormat::FileFormat> m_encodableMediaContainers; - QList<QMediaFormat::AudioCodec> m_encodableAudioCodecs; - QList<QMediaFormat::VideoCodec> m_encodableVideoCodecs; - - QHash<QMediaFormat::FileFormat, const char *> formatToCaps; - QHash<QMediaFormat::AudioCodec, const char *> audioToCaps; - QHash<QMediaFormat::VideoCodec, const char *> videoToCaps; + QList<CodecMap> getMuxerList(bool demuxer, QList<QMediaFormat::AudioCodec> audioCodecs, QList<QMediaFormat::VideoCodec> videoCodecs); }; QT_END_NAMESPACE |