/**************************************************************************** ** ** 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 #include static QSet streamTypes(GstElementFactory *factory, GstPadDirection direction) { QSet 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(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(); GList *elements = elementFactories(elementType); QSet fakeEncoderMimeTypes; fakeEncoderMimeTypes << "unknown/unknown" << "audio/x-raw-int" << "audio/x-raw-float" << "video/x-raw-yuv" << "video/x-raw-rgb"; QSet fieldsToAdd; fieldsToAdd << "mpegversion" << "layer" << "layout" << "raversion" << "wmaversion" << "wmvversion" << "variant" << "systemstream"; 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 == GST_PAD_SRC) { GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps); for (uint i=0; i::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); } #if !GST_CHECK_VERSION(0, 10, 31) static gboolean element_filter(GstPluginFeature *feature, gpointer user_data) { if (Q_UNLIKELY(!GST_IS_ELEMENT_FACTORY(feature))) return FALSE; const QGstCodecsInfo::ElementType type = *reinterpret_cast(user_data); const gchar *klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); if (type == QGstCodecsInfo::AudioEncoder && !(strstr(klass, "Encoder") && strstr(klass, "Audio"))) return FALSE; if (type == QGstCodecsInfo::VideoEncoder && !(strstr(klass, "Encoder") && strstr(klass, "Video"))) return FALSE; if (type == QGstCodecsInfo::Muxer && !strstr(klass, "Muxer")) return FALSE; guint rank = gst_plugin_feature_get_rank(feature); if (rank < GST_RANK_MARGINAL) return FALSE; return TRUE; } static gint compare_plugin_func(const void *item1, const void *item2) { GstPluginFeature *f1 = reinterpret_cast(const_cast(item1)); GstPluginFeature *f2 = reinterpret_cast(const_cast(item2)); gint 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(f1), gst_plugin_feature_get_name (f2)); } #endif GList *QGstCodecsInfo::elementFactories(ElementType elementType) const { #if GST_CHECK_VERSION(0,10,31) GstElementFactoryListType gstElementType = 0; switch (elementType) { case AudioEncoder: gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER; break; case VideoEncoder: gstElementType = GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER; break; case Muxer: gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER; 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; #else GList *result = gst_registry_feature_filter(gst_registry_get_default(), element_filter, FALSE, &elementType); result = g_list_sort(result, compare_plugin_func); return result; #endif } QSet QGstCodecsInfo::supportedStreamTypes(const QString &codec) const { return m_streamTypes.value(codec); } QStringList QGstCodecsInfo::supportedCodecs(const QSet &types) const { QStringList result; for (auto &candidate : supportedCodecs()) { auto candidateTypes = supportedStreamTypes(candidate); if (candidateTypes.intersects(types)) result << candidate; } return result; }