diff --git a/modules/gui/qt/maininterface/mainctx.cpp b/modules/gui/qt/maininterface/mainctx.cpp index e7fc42a38bfdb372837796e89dfbfa17ea687c23..c68ff2848fcf4739f08a114a6644121c002013f4 100644 --- a/modules/gui/qt/maininterface/mainctx.cpp +++ b/modules/gui/qt/maininterface/mainctx.cpp @@ -294,7 +294,7 @@ MainCtx::~MainCtx() var_DelCallback( libvlc, "intf-popupmenu", PopupMenuCB, p_intf ); if (m_medialib) - m_medialib->destroy(); + delete m_medialib; p_intf->p_mi = NULL; } diff --git a/modules/gui/qt/medialibrary/medialib.cpp b/modules/gui/qt/medialibrary/medialib.cpp index 5fb0b8fb21f1865331e8ad5fc4839d7b5c883545..df00cbb57bf36c28cdab8a0165a1463f16b9e6fa 100644 --- a/modules/gui/qt/medialibrary/medialib.cpp +++ b/modules/gui/qt/medialibrary/medialib.cpp @@ -34,42 +34,12 @@ MediaLib::MediaLib(qt_intf_t *_intf, vlc::playlist::PlaylistController* playlist { m_event_cb.reset( vlc_ml_event_register_callback( m_ml, MediaLib::onMediaLibraryEvent, this ) ); - - /* https://xkcd.com/221/ */ - m_mlThreadPool.setMaxThreadCount(4); + m_runner = new MLThreadRunner(m_ml); } MediaLib::~MediaLib() { - assert(m_objectTasks.empty()); - assert(m_runningTasks.empty()); -} - -void MediaLib::destroy() -{ - m_shuttingDown = true; - //try to cancel as many tasks as possible - for (auto taskIt = m_objectTasks.begin(); taskIt != m_objectTasks.end(); /**/) - { - const QObject* object = taskIt.key(); - quint64 key = taskIt.value(); - auto task = m_runningTasks.value(key, nullptr); - if (m_mlThreadPool.tryTake(task)) - { - delete task; - m_runningTasks.remove(key); - taskIt = m_objectTasks.erase(taskIt); - if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); - } - else - ++taskIt; - } - - if (m_runningTasks.empty()) - { - deleteLater(); - } + m_runner->destroy(); } static void convertMLItemToPlaylistMedias(vlc_medialibrary_t* ml, const MLItemId & itemId, const QStringList &options, QVector<vlc::playlist::Media>& medias) @@ -434,13 +404,54 @@ void MediaLib::onMediaLibraryEvent( void* data, const vlc_ml_event_t* event ) } } + + +MLThreadRunner::MLThreadRunner(vlc_medialibrary_t* ml) + : m_ml(ml) +{ + m_mlThreadPool.setMaxThreadCount(4); +} + +MLThreadRunner::~MLThreadRunner() +{ + assert(m_objectTasks.empty()); + assert(m_runningTasks.empty()); +} + +void MLThreadRunner::destroy() +{ + m_shuttingDown = true; + //try to cancel as many tasks as possible + for (auto taskIt = m_objectTasks.begin(); taskIt != m_objectTasks.end(); /**/) + { + const QObject* object = taskIt.key(); + quint64 key = taskIt.value(); + auto task = m_runningTasks.value(key, nullptr); + if (m_mlThreadPool.tryTake(task)) + { + delete task; + m_runningTasks.remove(key); + taskIt = m_objectTasks.erase(taskIt); + if (m_objectTasks.count(object) == 0) + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); + } + else + ++taskIt; + } + + if (m_runningTasks.empty()) + { + deleteLater(); + } +} + quint64 MediaLib::runOnMLThread(const QObject* obj, std::function< void(vlc_medialibrary_t* ml)> mlCb, std::function< void()> uiCb, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -455,7 +466,7 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, std::function< void(quint64)> uiCb, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -470,7 +481,7 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, const char* queue) { struct NoCtx{}; - return runOnMLThread<NoCtx>(obj, + return m_runner->runOnMLThread<NoCtx>(obj, [mlCb](vlc_medialibrary_t* ml, NoCtx&){ mlCb(ml); }, @@ -479,8 +490,12 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, queue); } - void MediaLib::cancelMLTask(const QObject* object, quint64 taskId) +{ + m_runner->cancelMLTask(object, taskId); +} + +void MLThreadRunner::cancelMLTask(const QObject* object, quint64 taskId) { assert(taskId != 0); @@ -494,10 +509,10 @@ void MediaLib::cancelMLTask(const QObject* object, quint64 taskId) m_runningTasks.remove(taskId); m_objectTasks.remove(object, taskId); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } -void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status) +void MLThreadRunner::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status) { if (m_shuttingDown) { @@ -506,7 +521,7 @@ void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target m_runningTasks.remove(target); m_objectTasks.remove(object, target); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } if (m_runningTasks.empty()) deleteLater(); @@ -518,12 +533,12 @@ void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target m_runningTasks.remove(target); m_objectTasks.remove(object, target); if (m_objectTasks.count(object) == 0) - disconnect(object, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + disconnect(object, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); } runner->deleteLater(); } -void MediaLib::runOnMLThreadTargetDestroyed(QObject * object) +void MLThreadRunner::runOnMLThreadTargetDestroyed(QObject * object) { if (m_objectTasks.contains(object)) { diff --git a/modules/gui/qt/medialibrary/medialib.hpp b/modules/gui/qt/medialibrary/medialib.hpp index 68d0b763e124ea26a4e811434bf0d50d2e75607e..0bb30b02cd719997aae15267a10e247063028d85 100644 --- a/modules/gui/qt/medialibrary/medialib.hpp +++ b/modules/gui/qt/medialibrary/medialib.hpp @@ -35,6 +35,7 @@ class Media; struct vlc_medialibrary_t; +class MLThreadRunner; class RunOnMLThreadBaseRunner; class MediaLib : public QObject @@ -47,15 +48,9 @@ public: Q_PROPERTY(QString discoveryEntryPoint READ discoveryEntryPoint NOTIFY discoveryEntryPointChanged FINAL) Q_PROPERTY(bool idle READ idle NOTIFY idleChanged FINAL) - enum MLTaskStatus { - ML_TASK_STATUS_SUCCEED, - ML_TASK_STATUS_CANCELED - }; - public: MediaLib(qt_intf_t* _intf, vlc::playlist::PlaylistController* playlistController, QObject* _parent = nullptr ); - - void destroy(); + ~MediaLib(); Q_INVOKABLE void addToPlaylist(const MLItemId &itemId, const QStringList &options = {}); Q_INVOKABLE void addToPlaylist(const QString& mrl, const QStringList &options = {}); @@ -183,14 +178,8 @@ signals: void idleChanged(); private: - //use the destroy function - ~MediaLib(); static void onMediaLibraryEvent( void* data, const vlc_ml_event_t* event ); -private slots: - void runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status); - void runOnMLThreadTargetDestroyed(QObject * object); - private: qt_intf_t* m_intf; vlc::playlist::PlaylistController* m_playlistController = nullptr; @@ -203,16 +192,46 @@ private: /* Medialibrary */ vlc_medialibrary_t* m_ml; std::unique_ptr<vlc_ml_event_callback_t, std::function<void(vlc_ml_event_callback_t*)>> m_event_cb; + MLThreadRunner* m_runner = nullptr; + + QMap<QVector<MLItemId>, QVector<QJSValue>> m_inputItemQuery; +}; +class MLThreadRunner : public QObject +{ + Q_OBJECT + +public: + enum MLTaskStatus { + ML_TASK_STATUS_SUCCEED, + ML_TASK_STATUS_CANCELED + }; + + MLThreadRunner(vlc_medialibrary_t* ml); + ~MLThreadRunner(); + + void destroy(); + void cancelMLTask(const QObject* object, quint64 taskId); + + template<typename Ctx> + quint64 runOnMLThread(const QObject* obj, + std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, + std::function<void (quint64 taskId, Ctx&)> uiFun, + const char* queue); + +private: + vlc_medialibrary_t* m_ml = nullptr; MLThreadPool m_mlThreadPool; - /* run on ml thread properties */ bool m_shuttingDown = false; quint64 m_taskId = 1; QMap<quint64, RunOnMLThreadBaseRunner*> m_runningTasks; QMultiMap<const QObject*, quint64> m_objectTasks; - QMap<QVector<MLItemId>, QVector<QJSValue>> m_inputItemQuery; + +private slots: + void runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status); + void runOnMLThreadTargetDestroyed(QObject * object); }; class RunOnMLThreadBaseRunner : public QObject, public QRunnable @@ -250,11 +269,11 @@ public: { if (m_canceled) { - emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_CANCELED); + emit done(this, m_taskId, m_obj, MLThreadRunner::ML_TASK_STATUS_CANCELED); return; } m_mlFun(m_ml, m_ctx); - emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_SUCCEED); + emit done(this, m_taskId, m_obj, MLThreadRunner::ML_TASK_STATUS_SUCCEED); } //called from UI thread @@ -278,7 +297,7 @@ private: }; template<typename Ctx> -quint64 MediaLib::runOnMLThread(const QObject* obj, +quint64 MLThreadRunner::runOnMLThread(const QObject* obj, std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, std::function<void (quint64 taskId, Ctx&)> uiFun, const char* queue) @@ -288,10 +307,19 @@ quint64 MediaLib::runOnMLThread(const QObject* obj, auto taskId = m_taskId++; auto runnable = new RunOnMLThreadRunner<Ctx>(taskId, obj, mlFun, uiFun, m_ml); - connect(runnable, &RunOnMLThreadBaseRunner::done, this, &MediaLib::runOnMLThreadDone); - connect(obj, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed); + connect(runnable, &RunOnMLThreadBaseRunner::done, this, &MLThreadRunner::runOnMLThreadDone); + connect(obj, &QObject::destroyed, this, &MLThreadRunner::runOnMLThreadTargetDestroyed); m_runningTasks.insert(taskId, runnable); m_objectTasks.insert(obj, taskId); m_mlThreadPool.start(runnable, queue); return taskId; } + +template<typename Ctx> +quint64 MediaLib::runOnMLThread(const QObject* obj, + std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun, + std::function<void (quint64 taskId, Ctx&)> uiFun, + const char* queue) +{ + return m_runner->runOnMLThread<Ctx>(obj, mlFun, uiFun, queue); +}