Qt 视频播放2

简介: Qt 视频播放

Qt 视频播放1:https://developer.aliyun.com/article/1597207

1.1.2 QPluginServiceProvider::requestService

class QPluginServiceProvider : public QMediaServiceProvider
{
    struct MediaServiceData {
        QByteArray type;
        QMediaServiceProviderPlugin *plugin;
 
        MediaServiceData() : plugin(nullptr) { }
    };
 
    QMap<const QMediaService*, MediaServiceData> mediaServiceData;
 
public:
    QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint) override
    {
        // key 的值为 "org.qt-project.qt.mediaplayer"
        QString key(QLatin1String(type.constData()));
 
        QList<QMediaServiceProviderPlugin *>plugins;
        // loader()是由Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader,(QMediaServiceProviderFactoryInterface_iid, QLatin1String("mediaservice"), Qt::CaseInsensitive))定义,
        // 其类型是QMediaPluginLoader对象, ==> 1.1.2.1 , 1.1.2.2
        // instances值在linux下为QGstreamerPlayerServicePlugin  
        const auto instances = loader()->instances(key);
        for (QObject *obj : instances) {
            QMediaServiceProviderPlugin *plugin =
                qobject_cast<QMediaServiceProviderPlugin*>(obj);
            if (plugin)
                plugins << plugin;
        }
 
        if (!plugins.isEmpty()) {
            QMediaServiceProviderPlugin *plugin = nullptr;
            // 此处hint.type()值为QMediaServiceProviderHint::Null
            switch (hint.type()) {
            case QMediaServiceProviderHint::Null:
                plugin = plugins[0];
                //special case for media player, if low latency was not asked,
                //prefer services not offering it, since they are likely to support
                //more formats
                if (type == QByteArray(Q_MEDIASERVICE_MEDIAPLAYER)) {
                    for (QMediaServiceProviderPlugin *currentPlugin : qAsConst(plugins)) {
                        QMediaServiceFeaturesInterface *iface =
                                qobject_cast<QMediaServiceFeaturesInterface*>(currentPlugin);
 
                        if (!iface || !(iface->supportedFeatures(type) &
                                        QMediaServiceProviderHint::LowLatencyPlayback)) {
                            plugin = currentPlugin;
                            break;
                        }
 
                    }
                }
                break;
            case QMediaServiceProviderHint::SupportedFeatures:
                // ...
                break;
            case QMediaServiceProviderHint::Device: 
                // ...
                break;
            case QMediaServiceProviderHint::CameraPosition: {
                    // ...
                }
                break;
            case QMediaServiceProviderHint::ContentType: {
                    // ...
                }
                break;
            }
 
            if (plugin != nullptr) {
                // 返回类型为QGstreamerPlayerService ==> 1.1.2.3
                QMediaService *service = plugin->create(key);
                if (service != nullptr) {
                    MediaServiceData d;
                    d.type = type;
                    d.plugin = plugin;
                    mediaServiceData.insert(service, d);
                }
 
                return service;
            }
        }
 
        qWarning() << "defaultServiceProvider::requestService(): no service found for -" << key;
        return nullptr;
    }
    // ...
}


1.1.2.1 loader()->instances(key)
1.1.2.1.1 QMediaPluginLoader
Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader,
        (QMediaServiceProviderFactoryInterface_iid, QLatin1String("mediaservice"), Qt::CaseInsensitive))

相应的宏为:

#  define Q_GLOBAL_STATIC_INTERNAL_DECORATION Q_DECL_HIDDEN inline
 
#define Q_GLOBAL_STATIC_INTERNAL(ARGS)                          \
    Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction()   \
    {                                                           \
        struct HolderBase {                                     \
            ~HolderBase() noexcept                        \
            { if (guard.loadRelaxed() == QtGlobalStatic::Initialized)  \
                  guard.storeRelaxed(QtGlobalStatic::Destroyed); }     \
        };                                                      \
        static struct Holder : public HolderBase {              \
            Type value;                                         \
            Holder()                                            \
                noexcept(noexcept(Type ARGS))       \
                : value ARGS                                    \
            { guard.storeRelaxed(QtGlobalStatic::Initialized); }       \
        } holder;                                               \
        return &holder.value;                                   \
    }
 
// this class must be POD, unless the compiler supports thread-safe statics
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
struct QGlobalStatic
{
    typedef T Type;
 
    bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
    bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
    operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); }
    Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); }
    Type *operator->()
    {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return innerFunction();
    }
    Type &operator*()
    {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return *innerFunction();
    }
};
 
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)                         \
    namespace { namespace Q_QGS_ ## NAME {                                  \
        typedef TYPE Type;                                                  \
        QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
        Q_GLOBAL_STATIC_INTERNAL(ARGS)                                      \
    } }                                                                     \
    static QGlobalStatic<TYPE,                                              \
                         Q_QGS_ ## NAME::innerFunction,                     \
                         Q_QGS_ ## NAME::guard> NAME;
 
#define Q_GLOBAL_STATIC(TYPE, NAME)                                         \
    Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())

其扩展后变成:

namespace { 
    namespace Q_QGS_loader {
        typedef QMediaPluginLoader Type;
        // 初始化 guard 原子性变量
        QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized);
        //Q_GLOBAL_STATIC_INTERNAL(ARGS)  
        inline QMediaPluginLoader *innerFunction() {                                                           \
            struct HolderBase {
                ~HolderBase() noexcept
                { if (guard.loadRelaxed() == QtGlobalStatic::Initialized)
                      guard.storeRelaxed(QtGlobalStatic::Destroyed); }
            };                                                     
            static struct Holder : public HolderBase {
                QMediaPluginLoader value;                            
                Holder()                              
                    noexcept(noexcept(QMediaPluginLoader(QMediaServiceProviderFactoryInterface_iid, QLatin1String("mediaservice"), Qt::CaseInsensitive))) 
                    : value(QMediaServiceProviderFactoryInterface_iid, QLatin1String("mediaservice"), Qt::CaseInsensitive)                    
                { guard.storeRelaxed(QtGlobalStatic::Initialized); }
            } holder;                                         
            return &holder.value;                      
        }
    } 
}
//static QGlobalStatic<QMediaPluginLoader, Q_QGS_loader::innerFunction,
//                         Q_QGS_loader::guard> loader;
static struct QGlobalStatic<QMediaPluginLoader, Q_QGS_loader::innerFunction,
        Q_QGS_loader::guard> {
    typedef QMediaPluginLoader Type;
 
    bool isDestroyed() const { return Q_QGS_loader::guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
    bool exists() const { return Q_QGS_loader::guard.loadRelaxed() == QtGlobalStatic::Initialized; }
    operator Type *() { if (isDestroyed()) return nullptr; return Q_QGS_loader::innerFunction(); }
    Type *operator()() { if (isDestroyed()) return nullptr; return Q_QGS_loader::innerFunction(); }
    Type *operator->() {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return Q_QGS_loader::innerFunction();
    }
    Type &operator*() {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return *Q_QGS_loader::innerFunction();
    }
} loader;

从上可知loader()operator()()的重载,其返回QMediaPluginLoader*对象

1.1.2.1.2 QMediaPluginLoader实例化
/* 实例化的时,传入的参数
 * iid:             QMediaServiceProviderFactoryInterface_iid, 值为
 *                  "org.qt-project.qt.mediaserviceproviderfactory/5.0"
 * location:        QLatin1String("mediaservice")
 * caseSensitivity: Qt::CaseInsensitive
*/
QMediaPluginLoader::QMediaPluginLoader(const char *iid, const QString &location, Qt::CaseSensitivity caseSensitivity):
    m_iid(iid)
{
#if defined(Q_OS_ANDROID)
    m_location = QString(location).replace(QLatin1Char('/'), QLatin1Char('_'));
#else
    m_location = QString::fromLatin1("/%1").arg(location);
#endif
    m_factoryLoader = new QFactoryLoader(m_iid, m_location, caseSensitivity);
    loadMetadata();
}
1.1.2.1.2.1 QFactoryLoader实例化

QMediaPluginLoader实例化时,会创建QFactoryLoader对象,其里面调用update方法更新Metadata信息,然后通过loadMetadata()中使用。

update方法中会查找Qt plugins/mediaservice下的插件,并加载成QLibraryPrivate对象,

然后读取QLibraryPrivate中元数据(qt plugins 插件框架),通过元数据判断是否是想要的插件,是则放入把QLibraryPrivate放入到QFactoryLoaderPrivate->libraryList中,以供loadMetadata()读取。

QFactoryLoader::QFactoryLoader(const char *iid,
                               const QString &suffix,
                               Qt::CaseSensitivity cs)
    : QObject(*new QFactoryLoaderPrivate)
{
    moveToThread(QCoreApplicationPrivate::mainThread());
    Q_D(QFactoryLoader);
    d->iid = iid;
#if QT_CONFIG(library)
    d->cs = cs;
    d->suffix = suffix;
# ifdef Q_OS_ANDROID
    if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/'))
        d->suffix.remove(0, 1);
# endif
 
    QMutexLocker locker(qt_factoryloader_mutex());
    update();
    qt_factory_loaders()->append(this);
#else
    Q_UNUSED(suffix);
    Q_UNUSED(cs);
#endif
}
 
// 查找Qt plugins/mediaservice下的插件,并加载成QLibraryPrivate对象,
// 读取QLibraryPrivate中元数据,如果是想要的插件,则放入QFactoryLoaderPrivate->libraryList中,
// 以供loadMetadata()读取
void QFactoryLoader::update()
{
#ifdef QT_SHARED
    Q_D(QFactoryLoader);
    // paths的值为:
    // "/home/Qt/5.15.2/gcc_64/plugins"
    // "/home/Qt/Examples/Qt-5.15.2/multimediawidgets/build-player-Desktop_Qt_5_15_2_GCC_64bit-Debug"
    QStringList paths = QCoreApplication::libraryPaths();
    for (int i = 0; i < paths.count(); ++i) {
        const QString &pluginDir = paths.at(i);
        // Already loaded, skip it...
        if (d->loadedPaths.contains(pluginDir))
            continue;
        d->loadedPaths << pluginDir;
        QString path = pluginDir + d->suffix;
        if (!QDir(path).exists(QLatin1String(".")))
            continue;
 
        QStringList plugins = QDir(path).entryList(
#if defined(Q_OS_WIN)
                    QStringList(QStringLiteral("*.dll")),
#elif defined(Q_OS_ANDROID)
                    QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)),
#endif
                    QDir::Files);
        QLibraryPrivate *library = nullptr;
 
        for (int j = 0; j < plugins.count(); ++j) {
            QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j));
 
#ifdef Q_OS_MAC
            const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib"));
            const bool isDebugLibrary =
                #ifdef QT_DEBUG
                    true;
                #else
                    false;
                #endif
 
            // Skip mismatching plugins so that we don't end up loading both debug and release
            // versions of the same Qt libraries (due to the plugin's dependencies).
            if (isDebugPlugin != isDebugLibrary)
                continue;
#elif defined(Q_PROCESSOR_X86)
            if (fileName.endsWith(QLatin1String(".avx2")) || fileName.endsWith(QLatin1String(".avx512"))) {
                // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
                continue;
            }
#endif
            if (qt_debug_component()) {
                qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName;
            }
 
            Q_TRACE(QFactoryLoader_update, fileName);
            
            library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
            // ==> 1.1.2.1.2.1.1
            if (!library->isPlugin()) {
                if (qt_debug_component()) {
                    qDebug() << library->errorString << Qt::endl
                             << "         not a plugin";
                }
                library->release();
                continue;
            }
 
            QStringList keys;
            bool metaDataOk = false;
 
            QString iid = library->metaData.value(QLatin1String("IID")).toString();
            if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
                QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
                metaDataOk = true;
 
                QJsonArray k = object.value(QLatin1String("Keys")).toArray();
                for (int i = 0; i < k.size(); ++i)
                    keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
            }
            if (qt_debug_component())
                qDebug() << "Got keys from plugin meta data" << keys;
 
 
            if (!metaDataOk) {
                library->release();
                continue;
            }
 
            int keyUsageCount = 0;
            for (int k = 0; k < keys.count(); ++k) {
                // first come first serve, unless the first
                // library was built with a future Qt version,
                // whereas the new one has a Qt version that fits
                // better
                const QString &key = keys.at(k);
                QLibraryPrivate *previous = d->keyMap.value(key);
                int prev_qt_version = 0;
                if (previous) {
                    prev_qt_version = (int)previous->metaData.value(QLatin1String("version")).toDouble();
                }
                int qt_version = (int)library->metaData.value(QLatin1String("version")).toDouble();
                if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) {
                    d->keyMap[key] = library;
                    ++keyUsageCount;
                }
            }
            if (keyUsageCount || keys.isEmpty()) {
                library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
                QMutexLocker locker(&d->mutex);
                d->libraryList += library;
            } else {
                library->release();
            }
        }
    }
#else
    Q_D(QFactoryLoader);
    if (qt_debug_component()) {
        qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid
                 << "since plugins are disabled in static builds";
    }
#endif
}

1.1.2.1.2.1.1 library->isPlugin()

bool QLibraryPrivate::isPlugin()
{
    if (pluginState == MightBeAPlugin)
        updatePluginState();
 
    return pluginState == IsAPlugin;
}
 
void QLibraryPrivate::updatePluginState()
{
    QMutexLocker locker(&mutex);
    errorString.clear();
    if (pluginState != MightBeAPlugin)
        return;
 
    bool success = false;
 
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
    if (fileName.endsWith(QLatin1String(".debug"))) {
        // refuse to load a file that ends in .debug
        // these are the debug symbols from the libraries
        // the problem is that they are valid shared library files
        // and dlopen is known to crash while opening them
 
        // pretend we didn't see the file
        errorString = QLibrary::tr("The shared library was not found.");
        pluginState = IsNotAPlugin;
        return;
    }
#endif
 
    if (!pHnd.loadRelaxed()) {
        // scan for the plugin metadata without loading
        // 没有加载,则读入文件内容进行分析 ==> 1.1.2.1.2.1.1.1
        success = findPatternUnloaded(fileName, this);
    } else {
        // library is already loaded (probably via QLibrary)
        // simply get the target function and call it.
        // 有加载则直接调用qt_plugin_query_metadata函数 ==> 1.1.2.1.2.1.1.2
        success = qt_get_metadata(this, &errorString);
    }
 
    if (!success) {
        if (errorString.isEmpty()){
            if (fileName.isEmpty())
                errorString = QLibrary::tr("The shared library was not found.");
            else
                errorString = QLibrary::tr("The file '%1' is not a valid Qt plugin.").arg(fileName);
        }
        pluginState = IsNotAPlugin;
        return;
    }
 
    pluginState = IsNotAPlugin; // be pessimistic
 
    uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
    bool debug = metaData.value(QLatin1String("debug")).toBool();
    if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
        if (qt_debug_component()) {
            qWarning("In %s:\n"
                 "  Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
                 QFile::encodeName(fileName).constData(),
                 (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
                 debug ? "debug" : "release");
        }
        errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
            .arg(fileName)
            .arg((qt_version&0xff0000) >> 16)
            .arg((qt_version&0xff00) >> 8)
            .arg(qt_version&0xff)
            .arg(debug ? QLatin1String("debug") : QLatin1String("release"));
#ifndef QT_NO_DEBUG_PLUGIN_CHECK
    } else if(debug != QLIBRARY_AS_DEBUG) {
        //don't issue a qWarning since we will hopefully find a non-debug? --Sam
        errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
                 " (Cannot mix debug and release libraries.)").arg(fileName);
#endif
    } else {
        pluginState = IsAPlugin;
    }
}

1.1.2.1.2.1.1.1 findPatternUnloaded

没有加载,则读入文件内容进行分析

static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
{
    QFile file(library);
    if (!file.open(QIODevice::ReadOnly)) {
        return false;
    }
 
    // Files can be bigger than the virtual memory size on 32-bit systems, so
    // we limit to 512 MB there. For 64-bit, we allow up to 2^40 bytes.
    constexpr qint64 MaxMemoryMapSize =
            Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29);
 
    QByteArray data;
    qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize);
    const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen));
 
    if (filedata == nullptr) {
        // Try reading the data into memory instead (up to 64 MB).
        data = file.read(64 * 1024 * 1024);
        filedata = data.constData();
        fdlen = data.size();
    }
 
    /*
       ELF and Mach-O binaries with GCC have .qplugin sections.
    */
    bool hasMetaData = false;
    qsizetype pos = 0;
    char pattern[] = "qTMETADATA ";
    pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it.
    const ulong plen = qstrlen(pattern);
#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
    int r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen);
    if (r == QElfParser::Corrupt || r == QElfParser::NotElf) {
            if (lib && qt_debug_component()) {
                qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString));
            }
            return false;
    } else if (r == QElfParser::QtMetaDataSection) {
        qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
        if (rel < 0)
            pos = -1;
        else
            pos += rel;
        hasMetaData = true;
    }
#elif defined (Q_OF_MACH_O)
    {
        QString errorString;
        int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen);
        if (r == QMachOParser::NotSuitable) {
            if (qt_debug_component())
                qWarning("QMachOParser: %ls", qUtf16Printable(errorString));
            if (lib)
                lib->errorString = errorString;
            return false;
        }
        // even if the metadata section was not found, the Mach-O parser will
        // at least return the boundaries of the right architecture
        qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
        if (rel < 0)
            pos = -1;
        else
            pos += rel;
        hasMetaData = true;
    }
#else
    pos = qt_find_pattern(filedata, fdlen, pattern, plen);
    if (pos > 0)
        hasMetaData = true;
#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
 
    bool ret = false;
 
    if (pos >= 0 && hasMetaData) {
        const char *data = filedata + pos;
        QString errMsg;
        QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg);
        if (doc.isNull()) {
            qWarning("Found invalid metadata in lib %ls: %ls",
                     qUtf16Printable(library), qUtf16Printable(errMsg));
        } else {
            lib->metaData = doc.object();
            if (qt_debug_component())
                qWarning("Found metadata in lib %s, metadata=\n%s\n",
                         library.toLocal8Bit().constData(), doc.toJson().constData());
            ret = !doc.isNull();
        }
    }
 
    if (!ret && lib)
        lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1'").arg(library);
    file.close();
    return ret;
}

1.1.2.1.2.1.1.2 qt_get_metadata

有加载则直接调用qt_plugin_query_metadata函数

static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    auto getMetaData = [](QFunctionPointer fptr) {
        auto f = reinterpret_cast<const char * (*)()>(fptr);
        return qMakePair<const char *, size_t>(f(), INT_MAX);
    };
#else
    auto getMetaData = [](QFunctionPointer fptr) {
        auto f = reinterpret_cast<QPair<const char *, size_t> (*)()>(fptr);
        return f();
    };
#endif
 
    QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata");
    if (!pfn)
        return false;
 
    auto metaData = getMetaData(pfn);
    QJsonDocument doc = qJsonFromRawLibraryMetaData(metaData.first, metaData.second, errMsg);
    if (doc.isNull())
        return false;
    priv->metaData = doc.object();
    return true;
}

最终获取到的metaData数据为:

QFactoryLoader::QFactoryLoader() looking at "/home/xx/Qt/5.15.2/gcc_64/plugins/mediaservice/libgstmediaplayer.so"
Found metadata in lib /home/xx/Qt/5.15.2/gcc_64/plugins/mediaservice/libgstmediaplayer.so, metadata=
{
"IID": "org.qt-project.qt.mediaserviceproviderfactory/5.0",
"MetaData": {
"Keys": [
"gstreamermediaplayer"
],
"Services": [
"org.qt-project.qt.mediaplayer"
]
},
"archreq": 0,
"className": "QGstreamerPlayerServicePlugin",
"debug": false,
"version": 331520
}
 
1.1.2.1.2.2 loadMetadata() 加载Metadata到m_metadata中
void QMediaPluginLoader::loadMetadata()
{
    if (!m_metadata.isEmpty()) {
        return;
    }
    // QFactoryLoader::metaData() ==> 1.1.2.1.2.2.1
    QList<QJsonObject> meta = m_factoryLoader->metaData();
    for (int i = 0; i < meta.size(); i++) {
        QJsonObject jsonobj = meta.at(i).value(QStringLiteral("MetaData")).toObject();
        jsonobj.insert(QStringLiteral("index"), i);
#if !defined QT_NO_DEBUG
        if (showDebug)
            qDebug() << "QMediaPluginLoader: Inserted index " << i << " into metadata: " << jsonobj;
#endif
 
        QJsonArray arr = jsonobj.value(QStringLiteral("Services")).toArray();
        // Preserve compatibility with older plugins (made before 5.1) in which
        // services were declared in the 'Keys' property
        if (arr.isEmpty())
            arr = jsonobj.value(QStringLiteral("Keys")).toArray();
 
        for (const QJsonValue &value : qAsConst(arr)) {
            QString key = value.toString();
 
            if (!m_metadata.contains(key)) {
#if !defined QT_NO_DEBUG
                if (showDebug)
                    qDebug() << "QMediaPluginLoader: Inserting new list for key: " << key;
#endif
                m_metadata.insert(key, QList<QJsonObject>());
            }
 
            m_metadata[key].append(jsonobj);
        }
    }
}

1.1.2.1.2.2.1 m_factoryLoader->metaData()

QList<QJsonObject> QFactoryLoader::metaData() const
{
    Q_D(const QFactoryLoader);
    QList<QJsonObject> metaData;
#if QT_CONFIG(library)
    QMutexLocker locker(&d->mutex);
    for (int i = 0; i < d->libraryList.size(); ++i)
        metaData.append(d->libraryList.at(i)->metaData);
#endif
 
    const auto staticPlugins = QPluginLoader::staticPlugins();
    for (const QStaticPlugin &plugin : staticPlugins) {
        const QJsonObject object = plugin.metaData();
        if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
            continue;
        metaData.append(object);
    }
    return metaData;
}
1.1.2.2 QMediaPluginLoader::instances(key)

参数key值为"org.qt-project.qt.mediaplayer",m_metadata.value(key)值为下图:

QList<QObject*> QMediaPluginLoader::instances(QString const &key)
{
    if (!m_metadata.contains(key))
        return QList<QObject*>();
 
    QList<QString> keys;
    QList<QObject *> objects;
    const auto list = m_metadata.value(key);
    for (const QJsonObject &jsonobj : list) {
        int idx = jsonobj.value(QStringLiteral("index")).toDouble();
        if (idx < 0)
            continue;
        // 此处idx为3, ==> 1.1.2.2.1
        QObject *object = m_factoryLoader->instance(idx);
        if (!objects.contains(object)) {
            QJsonArray arr = jsonobj.value(QStringLiteral("Keys")).toArray();
            keys.append(!arr.isEmpty() ? arr.at(0).toString() : QStringLiteral(""));
            objects.append(object);
        }
    }
    // 运行到此处 keys 值为"gstreamermediaplayer"
    // objects值为QGstreamerPlayerServicePlugin对象
 
    static const bool showDebug = qEnvironmentVariableIntValue("QT_DEBUG_PLUGINS");
    // preferredPlugins 默认为空
    static const QStringList preferredPlugins =
        qEnvironmentVariable("QT_MULTIMEDIA_PREFERRED_PLUGINS").split(QLatin1Char(','), Qt::SkipEmptyParts);
    for (int i = preferredPlugins.size() - 1; i >= 0; --i) {
        auto name = preferredPlugins[i];
        bool found = false;
        for (int j = 0; j < keys.size(); ++j) {
            if (!keys[j].startsWith(name))
                continue;
 
            auto obj = objects[j];
            objects.removeAt(j);
            objects.prepend(obj);
            auto k = keys[j];
            keys.removeAt(j);
            keys.prepend(k);
            found = true;
            break;
        }
    }
    return objects;
}
1.1.2.3 plugin->create(key)

QGstreamerPlayerServicePlugin插件其原信息为:Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediaplayer.json")


mediaplayer.json在Src/qtmultimedia/src/plugins/gstreamer/mediaplayer下,文件内容为:

{
    "Keys": ["gstreamermediaplayer"],
    "Services": ["org.qt-project.qt.mediaplayer"]
}
// Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediaplayer.json")
// key 为 "org.qt-project.qt.mediaplayer"
QMediaService* QGstreamerPlayerServicePlugin::create(const QString &key)
{
    // 初始化gsstreamer库?
    QGstUtils::initializeGst();
 
    if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER))
        return new QGstreamerPlayerService;
 
    qWarning() << "Gstreamer service plugin: unsupported key:" << key;
    return 0;
}
1.1.2.3.1 QGstreamerPlayerService
QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent)
    : QMediaService(parent)
{
    m_session = new QGstreamerPlayerSession(this);
    m_control = new QGstreamerPlayerControl(m_session, this);
    m_metaData = new QGstreamerMetaDataProvider(m_session, this);
    m_streamsControl = new QGstreamerStreamsControl(m_session,this);
    m_availabilityControl = new QGStreamerAvailabilityControl(m_control->resources(), this);
    m_videoRenderer = new QGstreamerVideoRenderer(this);
    m_videoWindow = new QGstreamerVideoWindow(this);
   // If the GStreamer video sink is not available, don't provide the video window control since
    // it won't work anyway.
    if (!m_videoWindow->videoSink()) {
        delete m_videoWindow;
        m_videoWindow = 0;
    }
 
#if defined(HAVE_WIDGETS)
    m_videoWidget = new QGstreamerVideoWidgetControl(this);
 
    // If the GStreamer video sink is not available, don't provide the video widget control since
    // it won't work anyway.
    // QVideoWidget will fall back to QVideoRendererControl in that case.
    if (!m_videoWidget->videoSink()) {
        delete m_videoWidget;
        m_videoWidget = 0;
    }
#endif
}
1.1.2.3.1.1 QGstreamerPlayerSession

其位于:qtmultimedia/src/gsttools/qgstreamerplayersession.cpp

#include <gst/gst.h>
 
QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
    : QObject(parent)
{
    initPlaybin();
}

从上面可见,其引用了GStreamer头文件。

编写一个GStreamer应用

1.1.2.3.1.2 QGstreamerPlayerControl

其位于:qtmultimedia/src/gsttools/qgstreamerplayercontrol.cpp

QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent)
    : QMediaPlayerControl(parent)
    , m_session(session)
{
    m_resources = QMediaResourcePolicy::createResourceSet<QMediaPlayerResourceSetInterface>();
    Q_ASSERT(m_resources);
 
    connect(m_session, &QGstreamerPlayerSession::positionChanged, this, &QGstreamerPlayerControl::positionChanged);
    connect(m_session, &QGstreamerPlayerSession::durationChanged, this, &QGstreamerPlayerControl::durationChanged);
    connect(m_session, &QGstreamerPlayerSession::mutedStateChanged, this, &QGstreamerPlayerControl::mutedChanged);
    connect(m_session, &QGstreamerPlayerSession::volumeChanged, this, &QGstreamerPlayerControl::volumeChanged);
    connect(m_session, &QGstreamerPlayerSession::stateChanged, this, &QGstreamerPlayerControl::updateSessionState);
    connect(m_session, &QGstreamerPlayerSession::bufferingProgressChanged, this, &QGstreamerPlayerControl::setBufferProgress);
    connect(m_session, &QGstreamerPlayerSession::playbackFinished, this, &QGstreamerPlayerControl::processEOS);
    connect(m_session, &QGstreamerPlayerSession::audioAvailableChanged, this, &QGstreamerPlayerControl::audioAvailableChanged);
    connect(m_session, &QGstreamerPlayerSession::videoAvailableChanged, this, &QGstreamerPlayerControl::videoAvailableChanged);
    connect(m_session, &QGstreamerPlayerSession::seekableChanged, this, &QGstreamerPlayerControl::seekableChanged);
    connect(m_session, &QGstreamerPlayerSession::error, this, &QGstreamerPlayerControl::error);
    connect(m_session, &QGstreamerPlayerSession::invalidMedia, this, &QGstreamerPlayerControl::handleInvalidMedia);
    connect(m_session, &QGstreamerPlayerSession::playbackRateChanged, this, &QGstreamerPlayerControl::playbackRateChanged);
 
    connect(m_resources, &QMediaPlayerResourceSetInterface::resourcesGranted, this, &QGstreamerPlayerControl::handleResourcesGranted);
    //denied signal should be queued to have correct state update process,
    //since in playOrPause, when acquire is call on resource set, it may trigger a resourcesDenied signal immediately,
    //so handleResourcesDenied should be processed later, otherwise it will be overwritten by state update later in playOrPause.
    connect(m_resources, &QMediaPlayerResourceSetInterface::resourcesDenied,
            this, &QGstreamerPlayerControl::handleResourcesDenied, Qt::QueuedConnection);
    connect(m_resources, &QMediaPlayerResourceSetInterface::resourcesLost, this, &QGstreamerPlayerControl::handleResourcesLost);
}
1.1.2.3.1.3 QGstreamerMetaDataProvider

其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp

1.1.2.3.1.4 QGstreamerStreamsControl

其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp


1.1.2.3.1.5 QGStreamerAvailabilityControl

其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp


1.1.2.3.1.6 QGstreamerVideoRenderer

其位于:qtmultimedia/src/gsttools/qgstreamervideorenderer.cpp


1.1.2.3.1.7 QGstreamerVideoWindow

其位于:qtmultimedia/src/gsttools/qgstreamervideowindow.cpp


1.1.2.3.1.8 QGstreamerVideoWidgetControl

其位于:qtmultimedia/src/gsttools/qgstreamervideowidget.cpp

参考

Qt Mobility videoplayer 源码剖析

Qt Multimedia::QMediaPlayer框架源码分析

QMediaServiceProviderPlugin 参考手册

qt 插件机制

目录
相关文章
|
23天前
|
Ubuntu Linux API
Qt 视频播放1
Qt 视频播放
37 1
|
监控 定位技术 块存储
Qt编写安防视频监控系统2-视频播放
一、前言 视频播放功能是核心功能之一,为了统一管理接口,统一封装成一个控件,对外提供seturl open close方法即可,不用去管内部的具体处理,这样就可以提供多种接口来实现统一的管理,比如vlc内核+ffmpeg内核+海康sdk内核等,随意切换,在使用各种内核的过程中,对比下来,发现easyplayer的内核是最好的,在国内用ffmpeg做接口做到了极致,CPU占用极低。
1709 0
|
2月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
135 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
1月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
39 0
|
16天前
|
21天前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
57 2
Qt开发网络嗅探器02
Qt开发网络嗅探器02
|
1月前
|
存储 运维 监控
Qt开发网络嗅探器01
Qt开发网络嗅探器01
|
1月前
|
网络协议 容器
Qt开发网络嗅探器03
Qt开发网络嗅探器03
|
1月前
【qt】多窗口开发
【qt】多窗口开发
33 0

推荐镜像

更多