From c7e5c92ce96180d153890ae582b20e17b5bc9a87 Mon Sep 17 00:00:00 2001 From: APTX Date: Sun, 26 Feb 2017 22:01:11 +0100 Subject: [PATCH] Make InstanceManager manage Player instances InstanceManager's job is to control the configuration of Player instances. Player no longer handles loading backend plugins. --- core/instancemanager.cpp | 82 ++++++++++++++++++++++++++++++---------- core/instancemanager.h | 17 +++++++-- core/main.cpp | 12 ++---- core/player.cpp | 36 +++++------------- core/player.h | 9 ++--- core/pluginmanager.h | 1 - 6 files changed, 93 insertions(+), 64 deletions(-) diff --git a/core/instancemanager.cpp b/core/instancemanager.cpp index 3935346..1cb2df3 100644 --- a/core/instancemanager.cpp +++ b/core/instancemanager.cpp @@ -6,39 +6,54 @@ #include #include "player.h" +#include "pluginmanager.h" #include "timeformatter.h" Q_LOGGING_CATEGORY(imCategory, "InstanceManager") -InstanceManager::InstanceManager(QObject *parent) : QObject(parent) { - parser.addHelpOption(); - parser.addVersionOption(); - parser.addPositionalArgument("files", "Files to play", "[files...]"); - parser.addOption(backendOption); - parser.addOption(uiOption); - parser.addOption(positionOption); +InstanceManager::InstanceManager(QObject *parent) + : QObject(parent), m_backendPluginManager{new PluginManager} { + m_parser.addHelpOption(); + m_parser.addVersionOption(); + m_parser.addPositionalArgument("files", "Files to play", "[files...]"); + m_parser.addOption(backendOption); + m_parser.addOption(uiOption); + m_parser.addOption(positionOption); } -int InstanceManager::runInstance(const QCoreApplication &app) { - parser.process(app); +void InstanceManager::startFirstInstance() { + m_parser.process(*qApp); - const auto positionalArgs = parser.positionalArguments(); + const auto positionalArgs = m_parser.positionalArguments(); - QQmlApplicationEngine engine; + QQmlApplicationEngine *engine = new QQmlApplicationEngine{this}; + Q_CHECK_PTR(engine); - player = new Player{this}; - Q_CHECK_PTR(player); + auto player = createInstance(); qCDebug(imCategory, "Player Created"); + m_instances.insert(player); + if (!positionalArgs.empty()) player->setNextSource(QUrl::fromUserInput(positionalArgs[0])); TimeFormatter timeFormatter; - engine.rootContext()->setContextProperty("player", player); - engine.rootContext()->setContextProperty("timeFormatter", &timeFormatter); + engine->rootContext()->setContextProperty("player", player); + engine->rootContext()->setContextProperty("timeFormatter", &timeFormatter); qCDebug(imCategory, "Player Added"); - engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + engine->load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); qCDebug(imCategory, "QML engine loaded"); - return app.exec(); +} + +void InstanceManager::startInstance() {} + +QString InstanceManager::encodeCommandLine() const { + QByteArray arr; + { + QDataStream stream{&arr, QIODevice::WriteOnly}; + stream << qApp->arguments(); + } + const auto base64 = arr.toBase64(); + return QString::fromLatin1(base64); } void InstanceManager::handleSingleInstanceMessage(const QString &message) { @@ -53,17 +68,44 @@ void InstanceManager::handleSingleInstanceMessage(const QString &message) { << "Failed to read serialized single instance message"; return; } - if (!parser.parse(args)) { + if (!m_parser.parse(args)) { qCWarning(imCategory) << "Failed to parse arguments from single instance message"; return; } - const auto positionalArgs = parser.positionalArguments(); + const auto positionalArgs = m_parser.positionalArguments(); if (positionalArgs.empty()) { qCInfo(imCategory()) << "No new file to open"; return; } - Q_ASSERT(player); + + const auto it = m_instances.cbegin(); + + Q_ASSERT(it != m_instances.cend()); + + auto player = *it; + player->loadAndPlay(QUrl::fromUserInput(positionalArgs[0])); } + +Player *InstanceManager::createInstance() { +#ifdef Q_OS_WIN + static QStringList pluginPaths{"backendplugins"}; +#else + static QStringList pluginPaths{"backendplugins", + "/usr/lib/aniplayer/backendplugins"}; +#endif + m_backendPluginManager->setPluginDirectories(pluginPaths); + m_backendPluginManager->setPluginPrefix("backend"); + m_backendPluginManager->setPreferredPlugins({"mpv"}); + m_backendPluginManager->loadBestPlugin(); + auto instance = m_backendPluginManager->bestInstance(); + if (!instance) + throw std::runtime_error{std::string("Failed to load backend: ") + + qPrintable(m_backendPluginManager->errorString())}; + + auto player = new Player{instance, this}; + Q_CHECK_PTR(player); + return player; +} diff --git a/core/instancemanager.h b/core/instancemanager.h index c1ae55b..ab68382 100644 --- a/core/instancemanager.h +++ b/core/instancemanager.h @@ -3,30 +3,39 @@ #include -#include #include #include +#include +#include class Player; +class PluginManager; class InstanceManager : public QObject { Q_OBJECT public: explicit InstanceManager(QObject *parent = 0); - int runInstance(const QCoreApplication &app); + void startFirstInstance(); + void startInstance(); + public slots: + QString encodeCommandLine() const; void handleSingleInstanceMessage(const QString &message); private: + Player *createInstance(); const QCommandLineOption backendOption{"backend", "Use backend", "name of backend plugin", "default"}; const QCommandLineOption uiOption{"ui", "Use ui", "name of ui", "default"}; const QCommandLineOption positionOption{"position", "Start playback specific position", "position in seconds", "0"}; - QCommandLineParser parser; - Player *player = nullptr; + const QCommandLineOption multiInstanceOption{ + QStringList{"m", "multi-instance"}, "Open more than one player instance"}; + QCommandLineParser m_parser; + QSet m_instances; + PluginManager *m_backendPluginManager; }; #endif // INSTANCEMANAGER_H diff --git a/core/main.cpp b/core/main.cpp index 0ee70f9..e626ae7 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -21,13 +21,8 @@ int main(int argc, char *argv[]) { SLOT(handleSingleInstanceMessage(QString))); if (app.isRunning()) { - QByteArray arr; - { - QDataStream stream{&arr, QIODevice::WriteOnly}; - stream << app.arguments(); - } - const auto base64 = arr.toBase64(); - if (app.sendMessage(QString::fromUtf8(base64))) + + if (app.sendMessage(im.encodeCommandLine())) return 0; return 1; } @@ -36,7 +31,8 @@ int main(int argc, char *argv[]) { Player::reqisterQmlTypes(); try { - return im.runInstance(app); + im.startFirstInstance(); + return app.exec(); } catch (const std::exception &ex) { qDebug("Exception: %s", ex.what()); } diff --git a/core/player.cpp b/core/player.cpp index b20b42c..0be95be 100644 --- a/core/player.cpp +++ b/core/player.cpp @@ -5,15 +5,19 @@ Q_LOGGING_CATEGORY(playerCategory, "Player") -Player::Player(QObject *parent) : QObject(parent) { +Player::Player(BackendPluginBase *backendPlugin, QObject *parent) + : QObject(parent) { + if (!backendPlugin) { + qCCritical(playerCategory) + << "Trying to create a player without a backendPlugin"; + return; + } qCDebug(playerCategory) << "Creating player" << this; - loadBackend(); + m_backend = backendPlugin->createInstance(this); + Q_CHECK_PTR(m_backend); } -Player::~Player() -{ - qCDebug(playerCategory) << "Destroying player" << this; -} +Player::~Player() { qCDebug(playerCategory) << "Destroying player" << this; } BackendInstance *Player::backend() const { return m_backend; } @@ -203,26 +207,6 @@ void Player::reqisterQmlTypes() { qmlRegisterType("org.aptx.aniplayer", 1, 0, "Player"); } -void Player::loadBackend() { -#ifdef Q_OS_WIN - QStringList pluginPaths{"backendplugins"}; -#else - QStringList pluginPaths{"backendplugins", - "/usr/lib/aniplayer/backendplugins"}; -#endif - m_pluginManager.setPluginDirectories(pluginPaths); - m_pluginManager.setPluginPrefix("backend"); - m_pluginManager.setPreferredPlugins({"mpv"}); - m_pluginManager.loadBestPlugin(); - m_plugin = m_pluginManager.bestInstance(); - if (!m_plugin) - throw std::runtime_error{std::string("Failed to load backend: ") + - qPrintable(m_pluginManager.errorString())}; - m_backend = m_plugin->createInstance(this); - Q_CHECK_PTR(m_backend); - qCDebug(playerCategory) << "Loaded backend" << m_backend; -} - bool Player::canLoadVideoNow() const { return m_backendInstanceReady && m_renderer && m_rendererReady; } diff --git a/core/player.h b/core/player.h index ab28b70..c96d582 100644 --- a/core/player.h +++ b/core/player.h @@ -52,7 +52,9 @@ public: static const constexpr Volume MAX_VOLUME = Volume{1.0}; - explicit Player(QObject *parent = 0); + // BackendPluginBase * allowed to be null because this is exposed to QML + // for the enums/types. Player should never be created in QML. + explicit Player(BackendPluginBase * = nullptr, QObject *parent = nullptr); ~Player() override; enum class PlayState { @@ -144,14 +146,12 @@ public: static void reqisterQmlTypes(); private: - void loadBackend(); bool canLoadVideoNow() const; void loadNextFile(); - PluginManager m_pluginManager; - BackendPluginBase *m_plugin = nullptr; BackendInstance *m_backend = nullptr; QUrl m_currentSource; + QUrl m_nextSource; PlayState m_state = PlayState::Stopped; Volume m_volume = MAX_VOLUME; AudioStreams m_availableAudioStreams; @@ -162,7 +162,6 @@ private: SubtitleStreams m_availableSubtitleStreams; Player::TimeStamp m_duration = 0; Player::TimeStamp m_position = 0; - QUrl m_nextSource; VideoUpdateInterface *m_renderer = nullptr; bool m_muted = false; bool m_backendInstanceReady = false; diff --git a/core/pluginmanager.h b/core/pluginmanager.h index 5ba288c..5ae8391 100644 --- a/core/pluginmanager.h +++ b/core/pluginmanager.h @@ -46,7 +46,6 @@ public slots: signals: void pluginDirectoriesChanged(QStringList pluginDirectories); void pluginPrefixChanged(QString pluginPrefix); - void preferredPluginsChanged(QStringList preferredPlugins); private: -- 2.52.0