From ac6b1785b892f06761911f131a7412b859e4812e Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Thu, 9 Sep 2021 09:34:48 +0200
Subject: [PATCH] MPV: Add a button to re-create the MPV context if something
 went wrong during widget init

The race condition between Qt's widget creation and mpv getting the WID
doesn't seem to be fixed for now, will need to investigate further.
---
 rsc/VivyRessources.qrc               |  2 ++
 src/UI/DocumentViews/MpvContainer.cc | 32 ++++++++++++++++++++--------
 src/UI/DocumentViews/MpvContainer.hh | 19 +++++++++--------
 src/UI/DocumentViews/MpvControls.cc  |  4 ++++
 src/UI/DocumentViews/MpvControls.hh  |  1 +
 src/UI/DocumentViews/VideoView.cc    |  2 --
 src/VivyApplication.hh               |  1 +
 7 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/rsc/VivyRessources.qrc b/rsc/VivyRessources.qrc
index 62d7ef14..cf4b9dc7 100644
--- a/rsc/VivyRessources.qrc
+++ b/rsc/VivyRessources.qrc
@@ -49,6 +49,7 @@
     <file alias="media-pause.svg">icons/breeze-dark/media-playback-pause.svg</file>
     <file alias="media-play.svg">icons/breeze-dark/media-playback-start.svg</file>
     <file alias="media-stop.svg">icons/breeze-dark/media-playback-stop.svg</file>
+    <file alias="run.svg">icons/breeze-dark/run-build-file.svg</file>
 </qresource>
 <qresource prefix="icons/light">
     <file alias="document-new.svg">icons/breeze-light/document-new.svg</file>
@@ -62,6 +63,7 @@
     <file alias="media-pause.svg">icons/breeze-light/media-playback-pause.svg</file>
     <file alias="media-play.svg">icons/breeze-light/media-playback-start.svg</file>
     <file alias="media-stop.svg">icons/breeze-light/media-playback-stop.svg</file>
+    <file alias="run.svg">icons/breeze-light/run-build-file.svg</file>
 </qresource>
 
 <!-- QDarkStyle style sheet, MIT Licence -->
diff --git a/src/UI/DocumentViews/MpvContainer.cc b/src/UI/DocumentViews/MpvContainer.cc
index b6f30f4c..31e0dc58 100644
--- a/src/UI/DocumentViews/MpvContainer.cc
+++ b/src/UI/DocumentViews/MpvContainer.cc
@@ -19,10 +19,12 @@ MpvContainer::MpvContainer(QWidget *parent)
 
     setAttribute(Qt::WA_DontCreateNativeAncestors);
     setAttribute(Qt::WA_NativeWindow);
+
+    initializeMpv();
 }
 
 void
-MpvContainer::intializeMpv()
+MpvContainer::initializeMpv()
 {
     if (isMpvAlreadyInitialized)
         throw std::logic_error("MPV is already initialized!");
@@ -42,13 +44,24 @@ MpvContainer::intializeMpv()
     mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE);
     mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE);
 
+    connect(this, &MpvContainer::mpvEvent, this, &MpvContainer::onMpvEvent, Qt::QueuedConnection);
+    mpv_set_wakeup_callback(mpv, &MpvContainer::mpvEventWakeUpCB, this);
+
     if (int rc = mpv_initialize(mpv); rc < 0) {
         printMpvError(rc);
         throw std::runtime_error("Failed to initialize the mpv context");
     }
+}
 
-    connect(this, &MpvContainer::mpvEvent, this, &MpvContainer::onMpvEvent, Qt::QueuedConnection);
-    mpv_set_wakeup_callback(mpv, &MpvContainer::mpvEventWakeUpCB, this);
+void
+MpvContainer::reCreateMpvContext()
+{
+    closeMpv();
+    mpv = mpv_create();
+    if (mpv == nullptr)
+        throw std::runtime_error("Failed to create the MPV context");
+    initializeMpv();
+    loadFile(previousLoadedFile);
 }
 
 void
@@ -75,11 +88,10 @@ MpvContainer::closeMpv() noexcept
         asyncCommand(AsyncCmdType::None, { "quit", nullptr });
         registerMpvTimeCallback(nullptr);
         registerMpvDurationCallback(nullptr);
-        mpv_handle *tmp_mpv = mpv;
-        mpv                 = nullptr; // Stop all other callbacks here
-        mpv_destroy(tmp_mpv);
-        // mpv_wait_async_requests(tmp_mpv);
-        // mpv_terminate_destroy(tmp_mpv);
+        mpv_wait_async_requests(mpv);
+        mpv_terminate_destroy(mpv);
+        mpv                     = nullptr; // Stop all other callbacks here
+        isMpvAlreadyInitialized = false;   // De-init
     }
 }
 
@@ -232,10 +244,12 @@ MpvContainer::onMpvEvent() noexcept
 void
 MpvContainer::loadFile(const QString &filename) noexcept
 {
-    logDebug() << "Loading file" << filename;
     if (filename.size() == 0)
         return;
 
+    qDebug() << "Loading file" << filename;
+    if (previousLoadedFile != filename)
+        previousLoadedFile = filename;
     const QByteArray cFileName = filename.toUtf8();
     asyncCommand(AsyncCmdType::LoadFile, { "loadfile", cFileName.data(), nullptr });
 }
diff --git a/src/UI/DocumentViews/MpvContainer.hh b/src/UI/DocumentViews/MpvContainer.hh
index 677d945c..37729f04 100644
--- a/src/UI/DocumentViews/MpvContainer.hh
+++ b/src/UI/DocumentViews/MpvContainer.hh
@@ -30,17 +30,8 @@ class MpvContainer final : public QWidget {
         TogglePlayback
     };
 
-private:
-    bool isPlaybackPaused{ true };
-    bool isMpvAlreadyInitialized{ false };
-    mpv_handle *volatile mpv{ nullptr };
-    qint64 sid{ -1 };
-    std::function<void(double)> mpvTimeCallback{ nullptr };
-    std::function<void(double)> mpvDurationCallback{ nullptr };
-
 public:
     explicit MpvContainer(QWidget *parent);
-    void intializeMpv();
     ~MpvContainer() noexcept override;
 
     void loadFile(const QString &) noexcept;
@@ -55,6 +46,7 @@ public:
     void registerMpvDurationCallback(std::function<void(double)>) noexcept;
 
 private:
+    void initializeMpv();
     void handleMpvEvent(const mpv_event *const) noexcept;
     void closeMpv() noexcept;
     void printMpvError(int) const noexcept;
@@ -66,10 +58,19 @@ private:
     // Must be static to be passed as a function ptr
     static void mpvEventWakeUpCB(void *) noexcept;
 
+    bool isPlaybackPaused{ true };
+    bool isMpvAlreadyInitialized{ false };
+    mpv_handle *volatile mpv{ nullptr };
+    qint64 sid{ -1 };
+    QString previousLoadedFile{};
+    std::function<void(double)> mpvTimeCallback{ nullptr };
+    std::function<void(double)> mpvDurationCallback{ nullptr };
+
 public slots:
     void mpvPlay() noexcept;
     void mpvPause() noexcept;
     void mpvTogglePlayback() noexcept;
+    void reCreateMpvContext();
 
 private slots:
     void onMpvEvent() noexcept;
diff --git a/src/UI/DocumentViews/MpvControls.cc b/src/UI/DocumentViews/MpvControls.cc
index 453c87c5..a9d63a8f 100644
--- a/src/UI/DocumentViews/MpvControls.cc
+++ b/src/UI/DocumentViews/MpvControls.cc
@@ -9,6 +9,8 @@ MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcep
 {
     auto *progressBar          = new QSlider(this);
     auto *togglePlaybackButton = new QPushButton(playIcon, "", this); // Be default MPV is paused
+    auto *reCreateMpvButton    = new QPushButton(
+        reCreateMpvIcon, "", this); // Recreate the MPV context if something went wrong
 
     progressBar->setTracking(false);
     progressBar->setOrientation(Qt::Horizontal);
@@ -54,6 +56,7 @@ MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcep
     });
 
     connect(togglePlaybackButton, &QAbstractButton::clicked, mpv, &MpvContainer::mpvTogglePlayback);
+    connect(reCreateMpvButton, &QAbstractButton::clicked, mpv, &MpvContainer::reCreateMpvContext);
     connect(mpv, &MpvContainer::mpvPlaybackToggled, this,
             [this, togglePlaybackButton](bool isPlay) noexcept -> void {
                 togglePlaybackButton->setIcon(isPlay ? pauseIcon : playIcon);
@@ -61,6 +64,7 @@ MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcep
 
     auto *centralLayout = new QHBoxLayout(this);
     centralLayout->addWidget(togglePlaybackButton);
+    centralLayout->addWidget(reCreateMpvButton);
     centralLayout->addWidget(progressBar, 1);
     setLayout(centralLayout);
 }
diff --git a/src/UI/DocumentViews/MpvControls.hh b/src/UI/DocumentViews/MpvControls.hh
index 5c792dc7..84cd143a 100644
--- a/src/UI/DocumentViews/MpvControls.hh
+++ b/src/UI/DocumentViews/MpvControls.hh
@@ -23,6 +23,7 @@ private:
 
     const QIcon playIcon{ VIVY_ICON_PLAY };
     const QIcon pauseIcon{ VIVY_ICON_PAUSE };
+    const QIcon reCreateMpvIcon{ VIVY_ICON_RUN };
 
 public:
     explicit MpvControls(MpvContainer *mpv, QWidget *parent) noexcept;
diff --git a/src/UI/DocumentViews/VideoView.cc b/src/UI/DocumentViews/VideoView.cc
index 2e45fcfd..30eee0e0 100644
--- a/src/UI/DocumentViews/VideoView.cc
+++ b/src/UI/DocumentViews/VideoView.cc
@@ -14,8 +14,6 @@ VideoView::VideoView(QWidget *parent) noexcept
     centralLayout->addWidget(new MpvControls(mpv, this));
 
     setLayout(centralLayout);
-
-    mpv->intializeMpv();
 }
 
 void
diff --git a/src/VivyApplication.hh b/src/VivyApplication.hh
index 15463929..c802a011 100644
--- a/src/VivyApplication.hh
+++ b/src/VivyApplication.hh
@@ -33,6 +33,7 @@
 #define VIVY_ICON_PLAY    ":icons/dark/media-play.svg"
 #define VIVY_ICON_PAUSE   ":icons/dark/media-pause.svg"
 #define VIVY_ICON_STOP    ":icons/dark/media-stop.svg"
+#define VIVY_ICON_RUN     ":icons/dark/run.svg"
 
 // Detect MacOS
 #if defined(Q_OS_DARWIN) || defined(Q_OS_MACOS)
-- 
GitLab