]> Some of my projects - aniplayer.git/commitdiff
Add support for observing properties to mpv::EventHandler
authorAPTX <marek321@gmail.com>
Sun, 20 Feb 2022 12:01:33 +0000 (21:01 +0900)
committerAPTX <marek321@gmail.com>
Mon, 21 Feb 2022 15:14:57 +0000 (00:14 +0900)
Includes a change to mpv_node internal property type.

Includes fix for onVolume getting partially converted input.

backendplugins/backend_mpv/backendmpv.cpp
backendplugins/backend_mpv/backendmpv.h
backendplugins/backend_mpv/mpvhelper.h

index 30048276386b8031684b944e1d7b80b72742fda9..9d73dd0d38f578403e49d6d2b780079c118e71cf 100644 (file)
@@ -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<mpv::EventHandler<MpvInstance>>(this);
+  m_eventHandler =
+      std::make_unique<mpv::EventHandler<MpvInstance>>(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<MPV_FORMAT_FLAG>(property);
-    onPauseChanged(paused);
-  } else if (strcmp(property->name, "duration") == 0) {
-    const auto duration = static_cast<PlayerPluginInterface::TimeStamp>(
-        readProperty<MPV_FORMAT_DOUBLE>(property));
-    onDurationChanged(duration);
-  } else if (strcmp(property->name, "playback-time") == 0) {
-    const double time = static_cast<PlayerPluginInterface::TimeStamp>(
-        readProperty<MPV_FORMAT_DOUBLE>(property));
-    onPlaybackTimeChanged(time);
-  } else if (strcmp(property->name, VOLUME) == 0) {
-    const double volume = static_cast<PlayerPluginInterface::Volume>(
-        readProperty<MPV_FORMAT_DOUBLE>(property) / 100.0);
-    onVolumeChanged(volume);
-  } else if (strcmp(property->name, "track-list") == 0) {
-    const auto node = readProperty<MPV_FORMAT_NODE>(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<MPV_FORMAT_STRING>(property));
-    onVidChanged(std::move(str));
-  } else if (strcmp(property->name, "aid") == 0) {
-    auto str =
-        QString::fromLocal8Bit(readProperty<MPV_FORMAT_STRING>(property));
-    onAidChanged(std::move(str));
-  } else if (strcmp(property->name, "sid") == 0) {
-    auto str =
-        QString::fromLocal8Bit(readProperty<MPV_FORMAT_STRING>(property));
-    onSidChanged(std::move(str));
-  } else if (strcmp(property->name, "chapter-list") == 0) {
-    const auto node = readProperty<MPV_FORMAT_NODE>(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";
index 4a9872ce84e245c20bae97fc70ff6fe240eee073..c400d0eff7762facb709f2a46eeb5c76f0bbd17b 100644 (file)
@@ -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 *);
index 375ab82215e0697add1dc5f281a70f8f9e0e2a2d..18754007d5b5023583755d2ad9086c9e8c8828bd 100644 (file)
@@ -2,6 +2,7 @@
 #define MPVHELPER_H
 
 #include "mpvhandle.h"
+#include "qthelper.hpp"
 
 #include <mpv/client.h>
 
@@ -85,10 +86,10 @@ constexpr Format<MPV_FORMAT_OSD_STRING, QString, const char *, true> OsdString;
 constexpr Format<MPV_FORMAT_FLAG, bool, int> Flag;
 constexpr Format<MPV_FORMAT_INT64, int64_t> Int64;
 constexpr Format<MPV_FORMAT_DOUBLE, double> Double;
-constexpr Format<MPV_FORMAT_NODE, QVariant, mpv_node *> Node;
+constexpr Format<MPV_FORMAT_NODE, QVariant, mpv_node> Node;
 // TODO should these be defined
-constexpr Format<MPV_FORMAT_NODE_ARRAY, QVariant, mpv_node *> NodeArray;
-constexpr Format<MPV_FORMAT_NODE_MAP, QVariant, mpv_node *> NodeMap;
+constexpr Format<MPV_FORMAT_NODE_ARRAY, QVariant, mpv_node> NodeArray;
+constexpr Format<MPV_FORMAT_NODE_MAP, QVariant, mpv_node> NodeMap;
 // TODO figure out correct type
 constexpr Format<MPV_FORMAT_BYTE_ARRAY, uint8_t *> ByteArray;
 
@@ -122,7 +123,8 @@ constexpr Property TrackList = MakeProperty<true>(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<true>(format::Node, "sid");
+constexpr Property ChapterList =
+    MakeProperty<true>(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<int, bool>
   static bool convert(const int &from) { return from != 0; }
 };
 
+template<>
+struct TypeConversion<mpv_node, QVariant>
+{
+  static QVariant convert(const mpv_node &from) {
+    return qt::node_to_variant(&from);
+  }
+};
+
 template<typename Format>
 auto ReadFormat(const Handle &handle, Format format, const char *name)
     -> Outcome<typename Format::type> {
@@ -262,12 +272,72 @@ class EventHandler {
     }
   };
 
+  struct PropertyCallback
+  {
+    virtual ~PropertyCallback() = default;
+    virtual void run(Class *, mpv_event_property *) const = 0;
+  };
+  template<typename Property, typename CallbackType, bool IsVoid>
+  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<int, std::unique_ptr<Callback>> m_eventHandlers;
+  std::unordered_map<QString, std::unique_ptr<PropertyCallback>>
+      m_propertyHandlers;
+
+  template<typename Property, typename It>
+  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<typename Event>
   std::enable_if_t<!std::is_same_v<typename Event::type, void>, bool>
@@ -276,7 +346,7 @@ public:
         event.id,
         std::make_unique<EventCallback<Event, decltype(handler), false>>(
             event, handler)));
-    return !pair.second;
+    return pair.second;
   }
 
   template<typename Event>
@@ -285,16 +355,68 @@ public:
         event.id,
         std::make_unique<EventCallback<Event, decltype(handler), true>>(
             event, handler)));
-    return !pair.second;
+    return pair.second;
+  }
+
+  template<typename Property, typename T>
+  std::enable_if_t<
+      !std::is_same_v<typename decltype(Property::format)::type, void> &&
+          std::is_convertible_v<T, typename decltype(Property::format)::type>,
+      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, decltype(handler), false>>(
+            property, handler)));
+    const bool inserted = pair.second;
+    if (!inserted) {
+      return false;
+    }
+
+    return internalObserve(property, pair.first);
+  }
+
+  template<typename Property>
+  bool observe(const Property &property, void (Class::*handler)()) {
+    auto pair = m_propertyHandlers.emplace(std::make_pair(
+        QLatin1String(property.name),
+        std::make_unique<
+            PropertyCallbackImpl<Property, decltype(handler), true>>(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<mpv_event_property *>(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);
   }
 };