您的位置:首页 > 编程语言 > Qt开发

Qt Multimedia::QMediaPlayer框架源码分析

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

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

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

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

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

而这个插件又是如何被加载并实例化的呢?

Created with Raphaël 2.1.0QMediaPlayerQMediaPlayerQMediaServiceProviderQMediaServiceProviderQMediaPluginLoaderQMediaPluginLoaderQFactoryLoaderQFactoryLoaderQLibraryQLibraryPluginPlugincall:requestService()=0implement:QPluginServiceProvidercall:instances()check plugin jsoncall:loadPlugin()call:instance()

1.QMediaPlayer

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

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

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

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

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


2.QMediaServiceProvider

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

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

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


代码片段中的loader()是:

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


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

3.QMediaPluginLoader

调用instances()成员函数。

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

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;

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

return objects;
}


这里检查重复加载以及验证插件合法性,key是插件接口中的IID:

Q_PLUGIN_METADATA(IID “org.qt-project.qt.mediaserviceproviderfactory/5.0” FILE “wmf.json”)

4.QFactoryLoader

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

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

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


5.QLibrary

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

分析先到这,下次更新一篇如何实现一个基于FFmpeg实现的Qt插件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息