赞
踩
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 |
提供媒体服务的抽象类,主要功能是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继承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 {
- // ...
- }
- };
- 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;
- };
- 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
- // ...
- }
- 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
- // ...
- }
- class QGstreamerPlayerServicePlugin
- : public QMediaServiceProviderPlugin
- , public QMediaServiceFeaturesInterface
- , public QMediaServiceSupportedFormatsInterface
- {
- Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "mediaplayer.json")
- // ...
- }
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
【FFMPEG】gstreamer插件调用ffmpeg 详解
- // 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
- }
- }
- // 通过静态方法获取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);
- }
- 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);
- 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;
- }
- // ...
- }
- 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*对象
- /* 实例化的时,传入的参数
- * 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();
- }
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
}
- 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;
- }
参数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;
- }
- // 此处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;
- }
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;
- }
- 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
- }
其位于:qtmultimedia/src/gsttools/qgstreamerplayersession.cpp
- #include <gst/gst.h>
-
- QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
- : QObject(parent)
- {
- initPlaybin();
- }
从上面可见,其引用了GStreamer头文件。
其位于: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);
- }
其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp
其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp
其位于:qtmultimedia/src/plugins/gstreamer/mediaplayer/qgstreameravailabilitycontrol.cpp
其位于:qtmultimedia/src/gsttools/qgstreamervideorenderer.cpp
其位于:qtmultimedia/src/gsttools/qgstreamervideowindow.cpp
其位于:qtmultimedia/src/gsttools/qgstreamervideowidget.cpp
Qt Multimedia::QMediaPlayer框架源码分析
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。