From 67f9fc8a1d82b55d7ff35ff92e6f8aac02964e03 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 f86514d8..339d6316 100644
--- a/rsc/VivyRessources.qrc
+++ b/rsc/VivyRessources.qrc
@@ -40,6 +40,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>
@@ -53,6 +54,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 b0e712e1..dc0fc9f1 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
@@ -74,11 +87,10 @@ MpvContainer::closeMpv() noexcept
         qDebug() << "Closing the MPV context";
         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
     }
 }
 
@@ -233,10 +245,12 @@ MpvContainer::onMpvEvent() noexcept
 void
 MpvContainer::loadFile(const QString &filename) noexcept
 {
-    qDebug() << "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 f44d2359..58f9ce94 100644
--- a/src/UI/DocumentViews/MpvContainer.hh
+++ b/src/UI/DocumentViews/MpvContainer.hh
@@ -27,17 +27,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;
@@ -52,6 +43,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;
@@ -63,10 +55,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 931239ac..01d4cabe 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);
@@ -55,6 +57,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);
@@ -62,6 +65,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 51b1e127..ab628ffc 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 0bfa830a..a9c78372 100644
--- a/src/VivyApplication.hh
+++ b/src/VivyApplication.hh
@@ -23,6 +23,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