From 4f95b65f72f76aca3a0766645659de7fcc894a68 Mon Sep 17 00:00:00 2001 From: Kubat <mael.martin31@gmail.com> Date: Tue, 3 Aug 2021 14:50:33 +0200 Subject: [PATCH] UI: Handle MPV events + set locale correctly for MPV --- src/UI/DocumentViews/MpvContainer.cc | 110 +++++++++++++++++++++++---- src/UI/DocumentViews/MpvContainer.hh | 14 +++- src/VivyApplication.cc | 4 + 3 files changed, 111 insertions(+), 17 deletions(-) diff --git a/src/UI/DocumentViews/MpvContainer.cc b/src/UI/DocumentViews/MpvContainer.cc index 9693d75e..2ac832af 100644 --- a/src/UI/DocumentViews/MpvContainer.cc +++ b/src/UI/DocumentViews/MpvContainer.cc @@ -26,22 +26,35 @@ MpvContainer::MpvContainer() 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, "pause", MPV_FORMAT_FLAG); + 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 (mpv_initialize(mpv) < 0) + if (int rc = mpv_initialize(mpv); rc < 0) { + printMpvError(rc); throw std::runtime_error("Failed to initialize the mpv context"); + } } void MpvContainer::registerMpvTimeCallback(void (*callback)(double)) noexcept { - if (mpv_time_callback) - qWarning() << "Override a previous mpv callback, was" << mpv_time_callback + if (mpvTimeCallback) + qWarning() << "Override a previous mpv callback, was" << mpvTimeCallback << "and now will be" << callback; - mpv_time_callback = callback; + mpvTimeCallback = callback; +} + +void +MpvContainer::registerMpvDurationCallback(void (*callback)(double)) noexcept +{ + if (mpvDurationCallback) + qWarning() << "Override a previous mpv callback, was" << mpvDurationCallback + << "and now will be" << callback; + mpvDurationCallback = callback; } void @@ -49,6 +62,7 @@ MpvContainer::closeMpv() noexcept { if (mpv) { registerMpvTimeCallback(nullptr); + registerMpvDurationCallback(nullptr); mpv_handle *tmp_mpv = mpv; mpv = nullptr; // Stop all other callbacks here mpv_destroy(tmp_mpv); @@ -65,17 +79,24 @@ MpvContainer::handleMpvEvent(mpv_event *event) noexcept { // Declare here variables that can be used in the switch-case statements qint64 w, h; + double time; union { mpv_event_log_message *msg; mpv_event_property *prop; }; + auto checkProp = [](mpv_event_property *prop, const std::string &str, + int format) noexcept -> bool { + return (prop->name == str) && (prop->format == format); + }; + switch (event->event_id) { case MPV_EVENT_SHUTDOWN: closeMpv(); break; case MPV_EVENT_VIDEO_RECONFIG: + // TODO: Those are sync calls, prefer async calls 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; @@ -89,14 +110,37 @@ MpvContainer::handleMpvEvent(mpv_event *event) noexcept 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!"; - } + if (checkProp(prop, "time-pos"s, MPV_FORMAT_DOUBLE) && mpvTimeCallback) { + time = *reinterpret_cast<double *>(prop->data); + mpvTimeCallback(time); + } + + else if (checkProp(prop, "duration"s, MPV_FORMAT_DOUBLE) && mpvDurationCallback) { + time = *reinterpret_cast<double *>(prop->data); + mpvDurationCallback(time); } + + else if (checkProp(prop, "pause"s, MPV_FORMAT_FLAG)) { + isPlaybackPaused = *reinterpret_cast<bool *>(prop->data); + emit mpvPlaybackToggled(!isPlaybackPaused); + } + + break; + + case MPV_EVENT_PAUSE: + isPlaybackPaused = true; + break; + + case MPV_EVENT_UNPAUSE: + isPlaybackPaused = false; + break; + + case MPV_EVENT_START_FILE: + qDebug() << "MPV: Begin of file"; + break; + + case MPV_EVENT_END_FILE: + qDebug() << "MPV: Reached end of file!"; break; // Explicitly ignored @@ -104,14 +148,10 @@ MpvContainer::handleMpvEvent(mpv_event *event) noexcept case MPV_EVENT_GET_PROPERTY_REPLY: case MPV_EVENT_SET_PROPERTY_REPLY: case MPV_EVENT_COMMAND_REPLY: - case MPV_EVENT_START_FILE: - case MPV_EVENT_END_FILE: case MPV_EVENT_FILE_LOADED: case MPV_EVENT_TRACKS_CHANGED: case MPV_EVENT_TRACK_SWITCHED: case MPV_EVENT_IDLE: - case MPV_EVENT_PAUSE: - case MPV_EVENT_UNPAUSE: case MPV_EVENT_TICK: case MPV_EVENT_SCRIPT_INPUT_DISPATCH: case MPV_EVENT_CLIENT_MESSAGE: @@ -131,8 +171,46 @@ MpvContainer::onMpvEvent() noexcept { while (mpv) { mpv_event *event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_NONE) + if (event == nullptr || event->event_id == MPV_EVENT_NONE) break; handleMpvEvent(event); } } + +void +MpvContainer::loadFile(const QString &filename) noexcept +{ + const QByteArray c_filename = filename.toUtf8(); + const char *args[] = { "loadfile", c_filename.data(), nullptr }; + printMpvError(mpv_command_async(mpv, 0, args)); +} + +void +MpvContainer::printMpvError(int rc) const noexcept +{ + if (rc == MPV_ERROR_SUCCESS) + return; + + qCritical() << "MPV error:" << mpv_error_string(rc); +} + +void +MpvContainer::mpvPlay() noexcept +{ + if (isPlaybackPaused) + mpvTogglePlayback(); +} + +void +MpvContainer::mpvPause() noexcept +{ + if (!isPlaybackPaused) + mpvTogglePlayback(); +} + +void +MpvContainer::mpvTogglePlayback() noexcept +{ + const char *cmd[] = { "cycle", "pause", "up", nullptr }; + printMpvError(mpv_command_async(mpv, 0, cmd)); +} diff --git a/src/UI/DocumentViews/MpvContainer.hh b/src/UI/DocumentViews/MpvContainer.hh index f56fdc2d..df1fe150 100644 --- a/src/UI/DocumentViews/MpvContainer.hh +++ b/src/UI/DocumentViews/MpvContainer.hh @@ -18,27 +18,39 @@ class MpvContainer : public QWidget { VIVY_UNMOVABLE_OBJECT(MpvContainer) private: + bool isPlaybackPaused{ true }; mpv_handle *mpv{ nullptr }; - void (*mpv_time_callback)(double){ nullptr }; + void (*mpvTimeCallback)(double){ nullptr }; + void (*mpvDurationCallback)(double){ nullptr }; public: explicit MpvContainer(); ~MpvContainer() noexcept override; + void loadFile(const QString &) noexcept; + // Register a callback for time change, don't use Qt's signals for that. void registerMpvTimeCallback(void (*)(double)) noexcept; + void registerMpvDurationCallback(void (*)(double)) noexcept; private: void handleMpvEvent(mpv_event *) noexcept; void closeMpv() noexcept; + void printMpvError(int) const noexcept; // Must be static to be passed as a function ptr static void mpvEventWakeUpCB(void *) noexcept; +public slots: + void mpvPlay() noexcept; + void mpvPause() noexcept; + void mpvTogglePlayback() noexcept; + private slots: void onMpvEvent() noexcept; signals: void mpvEvent(); + void mpvPlaybackToggled(bool isPlaying); }; } diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc index 86ef22ab..70c84396 100644 --- a/src/VivyApplication.cc +++ b/src/VivyApplication.cc @@ -4,6 +4,7 @@ #include <QtGlobal> #include <QIcon> #include <QFontDatabase> +#include <locale> using namespace Vivy; @@ -34,6 +35,9 @@ VivyApplication::setTheme(Theme theme) noexcept int VivyApplication::exec() noexcept { + // For MPV + std::setlocale(LC_NUMERIC, "C"); + // Add fonts fontIdMonospace = QFontDatabase::addApplicationFont(":/fonts/FiraCode-Regular.ttf"); fontIdMonospaceBold = QFontDatabase::addApplicationFont(":/fonts/FiraCode-Bold.ttf"); -- GitLab