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头文件。
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 Multimedia::QMediaPlayer框架源码分析