From 0186daebe4877c231124088b4ad97cb3e2d3d378 Mon Sep 17 00:00:00 2001 From: APTX Date: Sun, 20 Feb 2022 21:01:33 +0900 Subject: [PATCH] Add support for observing properties to mpv::EventHandler Includes a change to mpv_node internal property type. Includes fix for onVolume getting partially converted input. --- backendplugins/backend_mpv/backendmpv.cpp | 120 +++--------------- backendplugins/backend_mpv/backendmpv.h | 1 - backendplugins/backend_mpv/mpvhelper.h | 144 ++++++++++++++++++++-- 3 files changed, 151 insertions(+), 114 deletions(-) diff --git a/backendplugins/backend_mpv/backendmpv.cpp b/backendplugins/backend_mpv/backendmpv.cpp index 3004827..9d73dd0 100644 --- a/backendplugins/backend_mpv/backendmpv.cpp +++ b/backendplugins/backend_mpv/backendmpv.cpp @@ -34,7 +34,8 @@ MpvInstance::MpvInstance(PlayerPluginInterface *playerInterface, : QObject{parent}, m_player{playerInterface}, m_handle{ mpv::Handle::create()} { qCDebug(mpvBackend) << "Initialize"; - m_eventHandler = std::make_unique>(this); + m_eventHandler = + std::make_unique>(m_handle, this); qCDebug(mpvBackend()).nospace() << "Client API version: " << (mpv_client_api_version() >> 16) << '.' @@ -48,57 +49,22 @@ MpvInstance::MpvInstance(PlayerPluginInterface *playerInterface, } mpv_set_wakeup_callback(m_handle.get(), mpvWakeupCb, this); - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "pause", MPV_FORMAT_FLAG); - qCDebug(mpvBackend) << "register pause" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "duration", MPV_FORMAT_DOUBLE); - qCDebug(mpvBackend) << "register duration" << ret; - } - { - const auto ret = mpv_observe_property(m_handle.get(), 0, "playback-time", - MPV_FORMAT_DOUBLE); - qCDebug(mpvBackend) << "register playback-time" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, VOLUME, MPV_FORMAT_DOUBLE); - qCDebug(mpvBackend) << "register" << VOLUME << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "track-list", MPV_FORMAT_NODE); - qCDebug(mpvBackend) << "register track-list" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "vid", MPV_FORMAT_STRING); - qCDebug(mpvBackend) << "register vid" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "aid", MPV_FORMAT_STRING); - qCDebug(mpvBackend) << "register aid" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "sid", MPV_FORMAT_STRING); - qCDebug(mpvBackend) << "register sid" << ret; - } - { - const auto ret = mpv_observe_property(m_handle.get(), 0, "chapter-list", - MPV_FORMAT_NODE); - qCDebug(mpvBackend) << "register chapter-list" << ret; - } - { - const auto ret = - mpv_observe_property(m_handle.get(), 0, "idle-active", MPV_FORMAT_FLAG); - qCDebug(mpvBackend) << "register chapter-list" << ret; - } + m_eventHandler->observe(mpv::property::Pause, &MpvInstance::onPauseChanged); + m_eventHandler->observe(mpv::property::Duration, + &MpvInstance::onDurationChanged); + m_eventHandler->observe(mpv::property::PlaybackTime, + &MpvInstance::onPlaybackTimeChanged); + m_eventHandler->observe(mpv::property::Volume, &MpvInstance::onVolumeChanged); + m_eventHandler->observe(mpv::property::TrackList, + &MpvInstance::onTrackListChanged); + m_eventHandler->observe(mpv::property::Vid, &MpvInstance::onVidChanged); + m_eventHandler->observe(mpv::property::Aid, &MpvInstance::onAidChanged); + m_eventHandler->observe(mpv::property::Sid, &MpvInstance::onSidChanged); + m_eventHandler->observe(mpv::property::ChapterList, + &MpvInstance::onChapterListChanged); + m_eventHandler->observe(mpv::property::IdleActive, + &MpvInstance::onIdleActiveChanged); { const auto ret = mpv_request_log_messages(m_handle.get(), "info"); qCDebug(mpvBackend) << "request log messages" << ret; @@ -109,8 +75,6 @@ MpvInstance::MpvInstance(PlayerPluginInterface *playerInterface, m_player->playbackMaxVolumeChanged(maxVolume); } m_eventHandler->subscribe(mpv::event::LogMessage, &MpvInstance::onLogMessage); - m_eventHandler->subscribe(mpv::event::PropertyChange, - &MpvInstance::onPropertyChange); m_eventHandler->subscribe(mpv::event::FileLoaded, &MpvInstance::onFileLoaded); m_eventHandler->subscribe(mpv::event::EndFile, &MpvInstance::onEndFile); m_eventHandler->subscribe(mpv::event::AudioReconfig, @@ -269,55 +233,6 @@ void MpvInstance::processMpvEvents() { } while (event->event_id != MPV_EVENT_NONE); } -void MpvInstance::onPropertyChange(mpv_event_property *property) { - qCDebug(mpvVerboseBackend) << "Property" << property->name << "changed"; - if (property->format == MPV_FORMAT_NONE) { - qCDebug(mpvBackend) << "No data in event for property: " << property->name; - return; - } - if (strcmp(property->name, "pause") == 0) { - const auto paused = readProperty(property); - onPauseChanged(paused); - } else if (strcmp(property->name, "duration") == 0) { - const auto duration = static_cast( - readProperty(property)); - onDurationChanged(duration); - } else if (strcmp(property->name, "playback-time") == 0) { - const double time = static_cast( - readProperty(property)); - onPlaybackTimeChanged(time); - } else if (strcmp(property->name, VOLUME) == 0) { - const double volume = static_cast( - readProperty(property) / 100.0); - onVolumeChanged(volume); - } else if (strcmp(property->name, "track-list") == 0) { - const auto node = readProperty(property); - const auto variant = mpv::qt::node_to_variant(node); - onTrackListChanged(std::move(variant)); - } else if (strcmp(property->name, "vid") == 0) { - auto str = - QString::fromLocal8Bit(readProperty(property)); - onVidChanged(std::move(str)); - } else if (strcmp(property->name, "aid") == 0) { - auto str = - QString::fromLocal8Bit(readProperty(property)); - onAidChanged(std::move(str)); - } else if (strcmp(property->name, "sid") == 0) { - auto str = - QString::fromLocal8Bit(readProperty(property)); - onSidChanged(std::move(str)); - } else if (strcmp(property->name, "chapter-list") == 0) { - const auto node = readProperty(property); - const auto variant = mpv::qt::node_to_variant(node); - onChapterListChanged(std::move(variant)); - } else if (strcmp(property->name, "idle-active") == 0) { - onIdleActiveChanged(); - } else { - qCWarning(mpvBackend) << "Change notification for not handled property" - << property->name; - } -} - void MpvInstance::onLogMessage(mpv_event_log_message *log) { QMessageLogger l{0, 0, 0, mpvLog().categoryName()}; if (log->log_level <= MPV_LOG_LEVEL_ERROR) @@ -380,6 +295,7 @@ void MpvInstance::onPlaybackTimeChanged(double time) { } void MpvInstance::onVolumeChanged(double volume) { + volume /= 100.0; if (m_volumeToSet > 0) { qCDebug(mpvBackend) << "Requested volume still not set, skipping this update"; diff --git a/backendplugins/backend_mpv/backendmpv.h b/backendplugins/backend_mpv/backendmpv.h index 4a9872c..c400d0e 100644 --- a/backendplugins/backend_mpv/backendmpv.h +++ b/backendplugins/backend_mpv/backendmpv.h @@ -60,7 +60,6 @@ public: private: Q_INVOKABLE void processMpvEvents(); - void onPropertyChange(mpv_event_property *); void onLogMessage(mpv_event_log_message *); void onFileLoaded(); void onEndFile(mpv_event_end_file *); diff --git a/backendplugins/backend_mpv/mpvhelper.h b/backendplugins/backend_mpv/mpvhelper.h index 375ab82..1875400 100644 --- a/backendplugins/backend_mpv/mpvhelper.h +++ b/backendplugins/backend_mpv/mpvhelper.h @@ -2,6 +2,7 @@ #define MPVHELPER_H #include "mpvhandle.h" +#include "qthelper.hpp" #include @@ -85,10 +86,10 @@ constexpr Format OsdString; constexpr Format Flag; constexpr Format Int64; constexpr Format Double; -constexpr Format Node; +constexpr Format Node; // TODO should these be defined -constexpr Format NodeArray; -constexpr Format NodeMap; +constexpr Format NodeArray; +constexpr Format NodeMap; // TODO figure out correct type constexpr Format ByteArray; @@ -122,7 +123,8 @@ constexpr Property TrackList = MakeProperty(format::Node, "track-list"); constexpr Property Vid = MakeProperty(format::String, "vid"); constexpr Property Aid = MakeProperty(format::String, "aid"); constexpr Property Sid = MakeProperty(format::String, "sid"); -constexpr Property ChapterList = MakeProperty(format::Node, "sid"); +constexpr Property ChapterList = + MakeProperty(format::Node, "chapter-list"); constexpr Property IdleActive = MakeProperty(format::Flag, "idle-active"); constexpr Property MaxVolume = MakeProperty(format::Double, "max-volume"); } // namespace property @@ -164,6 +166,14 @@ struct TypeConversion static bool convert(const int &from) { return from != 0; } }; +template<> +struct TypeConversion +{ + static QVariant convert(const mpv_node &from) { + return qt::node_to_variant(&from); + } +}; + template auto ReadFormat(const Handle &handle, Format format, const char *name) -> Outcome { @@ -262,12 +272,72 @@ class EventHandler { } }; + struct PropertyCallback + { + virtual ~PropertyCallback() = default; + virtual void run(Class *, mpv_event_property *) const = 0; + }; + template + struct PropertyCallbackImpl : public PropertyCallback + { + const Property m_property; + CallbackType m_callback; + + PropertyCallbackImpl(Property event, CallbackType callback) + : m_property{event}, m_callback{callback} {} + + void run(Class *obj, mpv_event_property *event) const override { + assert(strcmp(m_property.name, event.name) == 0); + if constexpr (IsVoid) { + (obj->*m_callback)(); + } else { + assert(event->data != nullptr); + if (event->format != m_property.format.format) { + qCWarning(mpvHelper()).nospace() + << "Observed property: " << event->name + << " was registered with format: " << m_property.format.format + << ", got format: " << event->format; + return; + } + const auto &mpvData = *static_cast< + typename decltype(Property::format)::MpvInternalType *>( + event->data); + auto data = detail::TypeConversion< + typename decltype(Property::format)::MpvInternalType, + typename decltype(Property::format)::type>::convert(mpvData); + (obj->*m_callback)(std::move(data)); + } + } + }; + Class *const m_obj; + Handle m_handle; std::unordered_map> m_eventHandlers; + std::unordered_map> + m_propertyHandlers; + + template + bool internalObserve(const Property &property, const It &it) { + const int error = mpv_observe_property(m_handle.get(), 0, property.name, + property.format.format); + if (error != MPV_ERROR_SUCCESS) { + qCWarning(mpvHelper()).nospace() + << "Failed to observe property: " << property.name + << " with format: " << property.format.format << ", error " << error + << ": " << mpv_error_string(error); + m_propertyHandlers.erase(it); + return false; + } + qCDebug(mpvHelper()).nospace() + << "Observing property: " << property.name + << " with format: " << property.format.format; + return true; + } public: - explicit EventHandler(Class *obj) : m_obj{obj} {} + explicit EventHandler(Handle handle, Class *obj) + : m_handle{std::move(handle)}, m_obj{obj} {} template std::enable_if_t, bool> @@ -276,7 +346,7 @@ public: event.id, std::make_unique>( event, handler))); - return !pair.second; + return pair.second; } template @@ -285,16 +355,68 @@ public: event.id, std::make_unique>( event, handler))); - return !pair.second; + return pair.second; + } + + template + std::enable_if_t< + !std::is_same_v && + std::is_convertible_v, + bool> + observe(const Property &property, void (Class::*handler)(T)) { + auto pair = m_propertyHandlers.emplace(std::make_pair( + QLatin1String(property.name), + std::make_unique< + PropertyCallbackImpl>( + property, handler))); + const bool inserted = pair.second; + if (!inserted) { + return false; + } + + return internalObserve(property, pair.first); + } + + template + bool observe(const Property &property, void (Class::*handler)()) { + auto pair = m_propertyHandlers.emplace(std::make_pair( + QLatin1String(property.name), + std::make_unique< + PropertyCallbackImpl>(property, + handler))); + const bool inserted = pair.second; + if (!inserted) { + return false; + } + + return internalObserve(property, pair.first); } void handleEvent(mpv_event *event) { assert(event != nullptr); - const auto it = m_eventHandlers.find(event->event_id); - if (it == std::end(m_eventHandlers)) { - return; + switch (event->event_id) { + case MPV_EVENT_NONE: { + break; + } + case MPV_EVENT_PROPERTY_CHANGE: { + assert(event->data != nullptr); + const auto property = static_cast(event->data); + const auto it = m_propertyHandlers.find(property->name); + if (it == std::end(m_propertyHandlers)) { + return; + } + it->second->run(m_obj, property); + break; + } + default: { + const auto it = m_eventHandlers.find(event->event_id); + if (it == std::end(m_eventHandlers)) { + return; + } + it->second->run(m_obj, event); + break; + } } - it->second->run(m_obj, event); } }; -- 2.52.0