Qt 视频播放

一、简介

  Qt对音视频的播放和控制,相机拍照,收音机等多媒体应用提供了强大的支持。Qt5使用了全新的Qt Multimedia模块来实现多媒体应用,而原来Qt4中用于实现多媒体功能的Phonon模块已经被移除。

  新的Qt Multimedia模块提供了丰富的接口,使读者可以轻松地使用平台的多媒体功能,例如,进行多媒体播放,使用相机和收音机等。该模块还分别提供了一组QML类型和一组C++类来处理多媒体内容。

C++ 中多媒体模块可以实现的功能,对应的示例程序以及需要使用的C++类如:

  Qt的多媒体接口建立在底层平台的多媒体框架之上,这就意味着对于各种编解码器的支持依赖于使用的平台。如果要访问一些平台相关的设置,或者将Qt多媒体接口移植到新的平台,则可以参考Qt帮助中的Multimedia Backend Development文档。

  另外,如果要使用多媒体模块中的内容,则需要在.pro项目文件中添加如下代码,引入多媒体模块:

QT += multimedia

二、几个主要类介绍

QMediaPlayer

提供给外部应用程序的主要API,应用程序可以通过调用其成员函数play,setVolume,setPosition等控制视频文件的播放。大部分成员函数都是通过调用QMediaPlayerControl类型指针的方法来实现的。

QMediaControl

控制媒体的抽象类,包含大量控制媒体的成员函数。

QMediaServiceProvider

提供媒体服务的抽象类,主要功能是requestService得到QMediaService对象。

QPluginServiceProvider

QPluginServiceProvider继承QMediaServiceProvider类,通过requestService方法给QMediaPlayer提供QMediaService

QMediaService

媒体服务的抽象类,主要功能是requestControl得到QMediaControl对象。

QMediaServiceProviderPlugin

所有提供媒体服务的plugin都必须继承这个抽象类。create成员函数用来得到实现后的QMediaService派生类实例的指针,key成员函数用来得到一个QStringList,里面包含这个plugin中能提供的所有媒体服务的id。

注:由于一个plugin可能包含几个QMediaServiceProvider的实现,一个QMediaServiceProvider的实现又可能提供几个QMediaService的实现,一个QMediaService的实现也可能提供几个QMediaControl的实现...所以他们的每个派生类都有一个id来识别。

名字

QMediaServiceProviderPlugin

QMediaService

directshow

DSServicePlugin

DSCameraService、DirectShowPlayerService

windowsmediafoundation

WMFServicePlugin

MFPlayerService/MFAudioDecoderService

gstreamer

QGstreamerPlayerServicePlugin

QGstreamerPlayerService

三、QMediaServiceProvider

提供媒体服务的抽象类,主要功能是requestService得到QMediaService对象

class QMediaService;

class Q_MULTIMEDIA_EXPORT QMediaServiceProvider : public QObject
{
    Q_OBJECT

public:
    virtual QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint = QMediaServiceProviderHint()) = 0;
    virtual void releaseService(QMediaService *service) = 0;

    virtual QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const;

    virtual QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
                                             const QString &mimeType,
                                             const QStringList& codecs,
                                             int flags = 0) const;
    virtual QStringList supportedMimeTypes(const QByteArray &serviceType, int flags = 0) const;

    virtual QByteArray defaultDevice(const QByteArray &serviceType) const;
    virtual QList<QByteArray> devices(const QByteArray &serviceType) const;
    virtual QString deviceDescription(const QByteArray &serviceType, const QByteArray &device);

    virtual QCamera::Position cameraPosition(const QByteArray &device) const;
    virtual int cameraOrientation(const QByteArray &device) const;

    static QMediaServiceProvider* defaultServiceProvider();
    static void setDefaultServiceProvider(QMediaServiceProvider *provider);
};

四、QPluginServiceProvider

QPluginServiceProvider继承QMediaServiceProvider类,在requestService方法中相应的加载QMediaServiceProviderPlugin实现类(windows下的DSServicePlugin、WMFServicePlugin、Linux下的QGstreamerPlayerServicePlugin),在QMediaServiceProviderPlugin实现类中会创建对应的QMediaService

class QPluginServiceProvider : public QMediaServiceProvider
{
    struct MediaServiceData {
        QByteArray type;
        QMediaServiceProviderPlugin *plugin;

        MediaServiceData() : plugin(nullptr) { }
    };

    QMap<const QMediaService*, MediaServiceData> mediaServiceData;

public:
    // type: "org.qt-project.qt.mediaplayer"
    QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint) override {
 
        // ...
        // 选择合适的QMediaServiceProviderPlugin,有DSServicePlugin、WMFServicePlugin、QGstreamerPlayerServicePlugin
        // 相应的Plugin中创建对应的QMediaService
    }

    void releaseService(QMediaService *service) override {
        // ...
    }

    QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const override {
        // ...
    }

    QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType,
                                     const QString &mimeType,
                                     const QStringList& codecs,
                                     int flags) const override {
        // ...
    }

    QStringList supportedMimeTypes(const QByteArray &serviceType, int flags) const override {
        // ...
    }

    QByteArray defaultDevice(const QByteArray &serviceType) const override {
        // ...
    }

    QList<QByteArray> devices(const QByteArray &serviceType) const override {
        // ...
    }

    QString deviceDescription(const QByteArray &serviceType, const QByteArray &device) override {
        // ...
    }

    QCamera::Position cameraPosition(const QByteArray &device) const override {
        // ...
    }

    int cameraOrientation(const QByteArray &device) const override {
        // ...
    }
};

五、QMediaServiceProviderPlugin

class Q_MULTIMEDIA_EXPORT QMediaServiceProviderPlugin : public QObject, public QMediaServiceProviderFactoryInterface
{
    Q_OBJECT
    Q_INTERFACES(QMediaServiceProviderFactoryInterface)

public:
    QMediaService* create(const QString& key) override = 0;
    void release(QMediaService *service) override = 0;
};

5.1 DSServicePlugin

class DSServicePlugin
    : public QMediaServiceProviderPlugin
    , public QMediaServiceSupportedDevicesInterface
    , public QMediaServiceDefaultDeviceInterface
    , public QMediaServiceFeaturesInterface
{
#if QT_CONFIG(directshow_player)
    Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "directshow.json")
#else
    Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "directshow_camera.json")
#endif
    // ...
}

5.2 WMFServicePlugin

class WMFServicePlugin
    : public QMediaServiceProviderPlugin
    , public QMediaServiceSupportedDevicesInterface
    , public QMediaServiceDefaultDeviceInterface
    , public QMediaServiceFeaturesInterface
{
#if QT_CONFIG(wmf_player)
    Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "wmf.json")
#else
    Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "wmf_audiodecode.json")
#endif
    // ...
}

5.3 QGstreamerPlayerServicePlugin

class QGstreamerPlayerServicePlugin
    : public QMediaServiceProviderPlugin
    , public QMediaServiceFeaturesInterface
    , public QMediaServiceSupportedFormatsInterface
{
    Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediaplayer.json")
    // ...
}

六 gstreamer

gstreamer,其插件类为QGstreamerPlayerServicePlugin,其Service类为QGstreamerPlayerService

#linux下播放插件为libgstmediaplayer.so,其链接到了libgstreamer-1.0.so.0上,最终使用了gstreamer

ldd libgstmediaplayer.so | grep libgstreamer*
libgstreamer-1.0.so.0 => /lib/x86_64-linux-gnu/libgstreamer-1.0.so.0 (0x00007f66d65b6000)

ubuntu下的qt和gstreamer1.0环境报错问题解决

ubuntu16.04 18.04 Qt5.11安装Gstreamer

gstreamer官网

gstreamer(一)入门和概述

gstramer(二) 重要工具

gstreamer(三) 常用命令集锦

ubuntu系统下安装gstreamer的ffmpeg支持

【FFMPEG】gstreamer插件调用ffmpeg 详解

gstreamer源码

Gstreamer编译

Gstreamer安装与编译文档 官网

GStreamer插件编译(Windows平台)

Ubuntu中的PPA源

七 ffmpeg

gstreamer插件调用ffmpeg 详解

Linux下ffmpeg安装教程(亲测有效)

在linux下使用ffmpeg方法

Linux上的ffmpeg完全使用指南

官网源码

FFmpeg官网

编译 ffmpeg 方法

FFMPEG详解(完整版)

八 QMediaPlayer初始化过程

liunx qt编译调试

1、QMediaPlayer实例化

// playerService
QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags):
    QMediaObject(*new QMediaPlayerPrivate,
                 parent,
                 playerService(flags))    // 详见1.1
{
    Q_D(QMediaPlayer);

    d->provider = QMediaServiceProvider::defaultServiceProvider();
    if (d->service == nullptr) {
        d->error = ServiceMissingError;
    } else {
        // 此处control为QGstreamerPlayerService中创建的QGstreamerPlayerControl
        d->control = qobject_cast<QMediaPlayerControl*>(d->service->requestControl(QMediaPlayerControl_iid));
#ifndef QT_NO_BEARERMANAGEMENT
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
        d->networkAccessControl = qobject_cast<QMediaNetworkAccessControl*>(d->service->requestControl(QMediaNetworkAccessControl_iid));
QT_WARNING_POP
#endif
        if (d->control != nullptr) {
            connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SLOT(_q_handleMediaChanged(QMediaContent)));
            connect(d->control, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(_q_stateChanged(QMediaPlayer::State)));
            connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
                    SLOT(_q_mediaStatusChanged(QMediaPlayer::MediaStatus)));
            connect(d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString)));

            connect(d->control, &QMediaPlayerControl::durationChanged, this, &QMediaPlayer::durationChanged);
            connect(d->control, &QMediaPlayerControl::positionChanged, this, &QMediaPlayer::positionChanged);
            connect(d->control, &QMediaPlayerControl::audioAvailableChanged, this, &QMediaPlayer::audioAvailableChanged);
            connect(d->control, &QMediaPlayerControl::videoAvailableChanged, this, &QMediaPlayer::videoAvailableChanged);
            connect(d->control, &QMediaPlayerControl::volumeChanged, this, &QMediaPlayer::volumeChanged);
            connect(d->control, &QMediaPlayerControl::mutedChanged, this, &QMediaPlayer::mutedChanged);
            connect(d->control, &QMediaPlayerControl::seekableChanged, this, &QMediaPlayer::seekableChanged);
            connect(d->control, &QMediaPlayerControl::playbackRateChanged, this, &QMediaPlayer::playbackRateChanged);
            connect(d->control, &QMediaPlayerControl::bufferStatusChanged, this, &QMediaPlayer::bufferStatusChanged);

            d->state = d->control->state();
            d->status = d->control->mediaStatus();

            if (d->state == PlayingState)
                addPropertyWatch("position");

            if (d->status == StalledMedia || d->status == BufferingMedia)
                addPropertyWatch("bufferStatus");

            d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback);

            d->audioRoleControl = qobject_cast<QAudioRoleControl*>(d->service->requestControl(QAudioRoleControl_iid));
            if (d->audioRoleControl) {
                connect(d->audioRoleControl, &QAudioRoleControl::audioRoleChanged,
                        this, &QMediaPlayer::audioRoleChanged);

                d->customAudioRoleControl = qobject_cast<QCustomAudioRoleControl *>(
                        d->service->requestControl(QCustomAudioRoleControl_iid));
                if (d->customAudioRoleControl) {
                    connect(d->customAudioRoleControl,
                            &QCustomAudioRoleControl::customAudioRoleChanged,
                            this,
                            &QMediaPlayer::customAudioRoleChanged);
                }
            }
        }
#ifndef QT_NO_BEARERMANAGEMENT
        if (d->networkAccessControl != nullptr) {
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
            connect(d->networkAccessControl, &QMediaNetworkAccessControl::configurationChanged,
                    this, &QMediaPlayer::networkConfigurationChanged);
QT_WARNING_POP
        }
#endif
    }
}

1.1 playerService

// 通过静态方法获取QMediaService
static QMediaService *playerService(QMediaPlayer::Flags flags)
{
    // 获取默认的QMediaServiceProvider ==> 1.1.1
    QMediaServiceProvider *provider = QMediaServiceProvider::defaultServiceProvider();
    if (flags) {
        QMediaServiceProviderHint::Features features;
        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));
    }
    //  ==> 1.1.2  QPluginServiceProvider->requestService
    return provider->requestService(Q_MEDIASERVICE_MEDIAPLAYER);
}

1.1.1 defaultServiceProvider

QMediaServiceProvider *QMediaServiceProvider::defaultServiceProvider()
{
    // qt_defaultMediaServiceProvider默认为空,
    // pluginProvider()是由Q_GLOBAL_STATIC(QPluginServiceProvider, pluginProvider)定义,
    // 返回类型为QPluginServiceProvider类型的对象
    return qt_defaultMediaServiceProvider != nullptr
            ? qt_defaultMediaServiceProvider
            : static_cast<QMediaServiceProvider *>(pluginProvider());
}

// 定义命名空间为pluginProvider的QPluginServiceProvider对象 ?
Q_GLOBAL_STATIC(QPluginServiceProvider, pluginProvider);

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.2.1 m_factoryLoader->instance(idx)
// 此处index为3
QObject *QFactoryLoader::instance(int index) const
{
    Q_D(const QFactoryLoader);
    if (index < 0)
        return nullptr;

#if QT_CONFIG(library)
    QMutexLocker lock(&d->mutex);
    if (index < d->libraryList.size()) {
        // library 为 "plugins/mediaservice/libgstmediaplayer.so"
        QLibraryPrivate *library = d->libraryList.at(index);
        // library->pluginInstance() 实例化libgstmediaplayer.so,
        // 其类型为QGstreamerPlayerServicePlugin 
        if (QObject *obj = library->pluginInstance()) {
            if (!obj->parent())
                obj->moveToThread(QCoreApplicationPrivate::mainThread());
            return obj;
        }
        return nullptr;
    }
    index -= d->libraryList.size();
    lock.unlock();
#endif

    QVector<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
    for (int i = 0; i < staticPlugins.count(); ++i) {
        const QJsonObject object = staticPlugins.at(i).metaData();
        if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
            continue;

        if (index == 0)
            return staticPlugins.at(i).instance();
        --index;
    }

    return nullptr;
}
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 插件机制

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值