Qt Multimedia::QMediaPlayer框架源码分析

经过分析Qt Multimedia的QMediaPlayer播放器源码,发现了Qt是如何加载那些解码插件的。如果要实现自己的解码插件,让QMediaPlayer自动加载自己开发的解码插件,那么某些音视频文件在没有安装解码器的系统上也能正常播放。


首先,看看QMediaPlayer是如何工作的。以UML序列图表示:

Created with Raphaël 2.1.0 Client Client QMediaPlayer QMediaPlayer QMediaService ProviderPlugin QMediaService ProviderPlugin QMediaService QMediaService QMediaPlayerControl QMediaPlayerControl FFmpeg FFmpeg 传入一个音频文件 或一个播放列表 请求播放服务 factory 创建一个媒体服务 product 请求播放控制 正在播放音频 与解码器通信 进行解码音频文件

QMediaPlayer调用的是QMediaPlayerControl的接口,其中QMediaPlayerControl是一个抽象类,所有接口需要自己实现。

QMediaServiceProviderPlugin是一个Qt插件,派生自QMediaServiceProviderFactoryInterface抽象接口,从它派生可生成一个“.dll”或“.a”类型的插件。然后发现,QMediaServiceProviderPluginQMediaService其实就是一个抽象工厂模式。QMediaService 的工作就是请求一系列不同功能的播放控制插件(解码、声音输出、声音输入等),如QAudioOutputControl、QAodioInputControl、QAudioDecoderControl、QMetaDataReaderControl、QMetaDataWriterControl等等。

  • 而这个插件又是如何被加载并实例化的呢?
Created with Raphaël 2.1.0 QMediaPlayer QMediaPlayer QMediaServiceProvider QMediaServiceProvider QMediaPluginLoader QMediaPluginLoader QFactoryLoader QFactoryLoader QLibrary QLibrary Plugin Plugin call:requestService()=0 implement:QPluginServiceProvider call:instances() check plugin json call:loadPlugin() call:instance()

1.QMediaPlayer
调用了QMediaServiceProvider的抽象接口requestService()。派生QPluginServiceProvider并实现了QMediaServiceProvider的接口。在defaultServiceProvider()函数中实例化一个QPluginServiceProvider对象。

//QMediaPlayer构造函数中调用playerService()
static QMediaService *playerService(QMediaPlayer::Flags flags)
{
    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();
    if (flags) {
        QMediaServiceProviderHint::Features features = 0;
        if (flags & QMediaPlayer::LowLatency)
            features |= QMediaServiceProviderHint::LowLatencyPlayback;

        if (flags & QMediaPlayer::StreamPlayback)
            features |= QMediaServiceProviderHint::StreamPlayback;

        if (flags & QMediaPlayer::VideoSurface)
            features |= QMediaServiceProviderHint::VideoSurface;

        return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER,
                                        QMediaServiceProviderHint(features));
    } else
        return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER);
}

2.QMediaServiceProvider
派生的子类QPluginServiceProvider实现了requestService函数。

class QPluginServiceProvider : public QMediaServiceProvider
{
    //...
    QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint)
    {
        QString key(QLatin1String(type.constData()));

        QListplugins;
        const auto instances = loader()->instances(key);
        for (QObject *obj : instances) {
            QMediaServiceProviderPlugin *plugin =
                qobject_cast(obj);
            if (plugin)
                plugins << plugin;
        }
//...

代码片段中的loader()是:

Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader, (QMediaServiceProviderFactoryInterface_iid, QLatin1String("mediaservice"), Qt::CaseInsensitive)) 

mediaservice是Qt加载多媒体插件的路径,即QT_DIR/plugins/mediaservice

3.QMediaPluginLoader
调用instances()成员函数。

// QFactoryLoader m_factoryLoader;
QList QMediaPluginLoader::instances(QString const &key)
{
    if (!m_metadata.contains(key))
        return QList();

    QList objects;
    const auto list = m_metadata.value(key);
    for (const QJsonObject &jsonobj : list) {
        int idx = jsonobj.value(QStringLiteral("index")).toDouble();
        if (idx < 0)
            continue;

        QObject *object = m_factoryLoader->instance(idx);
        if (!objects.contains(object)) {
            objects.append(object);
        }
    }

    return objects;
}

这里检查重复加载以及验证插件合法性,key是插件接口中的IID:
Q_PLUGIN_METADATA(IID “org.qt-project.qt.mediaserviceproviderfactory/5.0” FILE “wmf.json”)

4.QFactoryLoader
该类是Qt base的类,调用成员函数instance()。QLibraryPrivate是QLibrary的d指针。

QObject *QFactoryLoader::instance(int index) const
{
    Q_D(const QFactoryLoader);
    if (index < 0)
        return 0;

#ifndef QT_NO_LIBRARY
    if (index < d->libraryList.size()) {
        QLibraryPrivate *library = d->libraryList.at(index);
        if (library->instance || library->loadPlugin()) {
            if (!library->inst)
                library->inst = library->instance();
            QObject *obj = library->inst.data();
            if (obj) {
                if (!obj->parent())
                    obj->moveToThread(QCoreApplicationPrivate::mainThread());
                return obj;
            }
        }
        return 0;
    }
    index -= d->libraryList.size();
#endif
// ...
}

5.QLibrary
调用loadPlugin()函数从本地(QT_DIR/plugins/mediaservice)加载dll插件并instance()实例化插件。


分析先到这,下次更新一篇如何实现一个基于FFmpeg实现的Qt插件。

你可能感兴趣的:(Qt)