diff --git a/src/UI/DocumentViews/MpvContainer.cc b/src/UI/DocumentViews/MpvContainer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..26890478fca5ca99cacdc16db235b5d48147a5ea
--- /dev/null
+++ b/src/UI/DocumentViews/MpvContainer.cc
@@ -0,0 +1,101 @@
+#include "MpvContainer.hh"
+
+#include <mpv/client.h>
+
+using namespace Vivy;
+using namespace std::string_literals;
+
+void
+MpvContainer::mpvEventWakeUpCB(void *user)
+{
+    MpvContainer *container = reinterpret_cast<MpvContainer *>(user);
+    emit container->mpvEvent();
+}
+
+MpvContainer::MpvContainer()
+    : mpv(mpv_create())
+{
+    if (mpv == nullptr)
+        throw std::runtime_error("Failed to create the MPV context");
+
+    setAttribute(Qt::WA_DontCreateNativeAncestors);
+    setAttribute(Qt::WA_NativeWindow);
+
+    quint64 wid = winId();
+    mpv_set_option(mpv, "wid", MPV_FORMAT_INT64, &wid);
+    mpv_set_option_string(mpv, "input-default-bindings", "no");
+    mpv_set_option_string(mpv, "input-vo-keyboard", "no");
+    mpv_request_log_messages(mpv, "info");
+    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 (mpv_initialize(mpv) < 0)
+        throw std::runtime_error("Failed to initialize the mpv context");
+}
+
+MpvContainer::~MpvContainer() noexcept
+{
+    // TODO: Do that in a private method
+    if (mpv) {
+        mpv_handle *tmp_mpv = mpv;
+        mpv                 = nullptr; // Stop all other callbacks here
+        mpv_destroy(tmp_mpv);
+    }
+}
+
+void
+MpvContainer::handleMpvEvent(mpv_event *event) noexcept
+{
+    // Declare here variables that can be used in the switch-case statements
+    qint64 w, h;
+    union {
+        mpv_event_log_message *msg;
+        mpv_event_property *prop;
+        mpv_handle *tmp_mpv;
+    };
+
+    switch (event->event_id) {
+    case MPV_EVENT_SHUTDOWN:
+        tmp_mpv = mpv;
+        mpv = nullptr;
+        mpv_destroy(mpv);
+        break;
+
+    case MPV_EVENT_VIDEO_RECONFIG:
+        if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
+            mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 && (w > 0 && h > 0)) {
+            qDebug() << "Reconfigure video to" << w << "x" << h;
+        }
+        break;
+
+    case MPV_EVENT_LOG_MESSAGE:
+        msg = reinterpret_cast<mpv_event_log_message *>(event->data);
+        qDebug() << "MPV message: [" << msg->prefix << "]" << msg->level << ":" << msg->text;
+        break;
+
+    case MPV_EVENT_PROPERTY_CHANGE:
+        prop = reinterpret_cast<mpv_event_property *>(event->data);
+        if (prop->name == "time-pos"s) {
+            if (prop->format == MPV_FORMAT_DOUBLE) {
+                double time = *reinterpret_cast<double *>(prop->data);
+                qDebug() << "Playback time:" << time;
+            } else {
+                qCritical() << "Got playback time but not a double!";
+            }
+        }
+        break;
+    }
+}
+
+void
+MpvContainer::onMpvEvent() noexcept
+{
+    while (mpv) {
+        mpv_event *event = mpv_wait_event(mpv, 0);
+        if (event->event_id == MPV_EVENT_NONE)
+            break;
+        handleMpvEvent(event);
+    }
+}
diff --git a/src/UI/DocumentViews/MpvContainer.hh b/src/UI/DocumentViews/MpvContainer.hh
index 063670c93782b45c2050a38c1623eb14b1483eee..bda0f585818e74e0c7d5bc1a528dd7888f04a056 100644
--- a/src/UI/DocumentViews/MpvContainer.hh
+++ b/src/UI/DocumentViews/MpvContainer.hh
@@ -6,21 +6,31 @@
 
 #include "../../Lib/Utils.hh"
 
+extern "C" {
 struct mpv_handle;
+struct mpv_event;
+}
 
 namespace Vivy
 {
 class MpvContainer : public QWidget {
+    Q_OBJECT
     VIVY_UNMOVABLE_OBJECT(MpvContainer)
 
 private:
-    quint64 wid{ 0 };
     mpv_handle *mpv{ nullptr };
 
 public:
-    explicit MpvContainer() noexcept;
+    explicit MpvContainer();
     ~MpvContainer() noexcept override;
 
+private:
+    void handleMpvEvent(mpv_event *) noexcept;
+    static void mpvEventWakeUpCB(void *); // Must be static for function ptr
+
+private slots:
+    void onMpvEvent() noexcept;
+
 signals:
     void mpvEvent();
 };