]> Some of my projects - aniplayer.git/commitdiff
Introduce mpvhelper.h, possible future replacement of qthelper.hpp
authorAPTX <marek321@gmail.com>
Sun, 20 Feb 2022 04:27:43 +0000 (13:27 +0900)
committerAPTX <marek321@gmail.com>
Mon, 21 Feb 2022 15:14:57 +0000 (00:14 +0900)
Use of "max-volume" property was removed as that property doesn't exist.

backendplugins/backend_mpv/CMakeLists.txt
backendplugins/backend_mpv/backendmpv.cpp
backendplugins/backend_mpv/mpvhelper.h [new file with mode: 0644]

index 81d551911dbe4230c1f3334d456f05684a9e44b4..c848299a07bac394676e77c16cb42cbd6ba395ca 100644 (file)
@@ -22,6 +22,7 @@ set(backend_mpv_SOURCES
 
 set(backend_mpv_HEADERS
     backendmpv.h
+    mpvhelper.h
     qthelper.hpp
     backend_mpv_global.h
 )
@@ -35,7 +36,7 @@ add_library(backend_mpv MODULE
     ${backend_mpv_HEADERS}
 )
 
-set_property(TARGET backend_mpv PROPERTY CXX_STANDARD 14)
+set_property(TARGET backend_mpv PROPERTY CXX_STANDARD 17)
 set_property(TARGET backend_mpv PROPERTY OUTPUT_NAME "backend_mpv")
 target_link_libraries(backend_mpv ${backend_mpv_LIBS})
 
index c110179de8135d9aeb5468626dcb6a62fc450fdf..0e1198557243278973803d8d6a21bd6215136be5 100644 (file)
@@ -5,6 +5,7 @@
 #include <QString>
 #include <QUrl>
 
+#include "mpvhelper.h"
 #include "qthelper.hpp"
 
 #include <mpv/render.h>
@@ -105,8 +106,6 @@ MpvInstance::MpvInstance(PlayerPluginInterface *playerInterface,
   }
   {
     double maxVolume = 130.0;
-    mpv_set_property(m_handle, "max-volume", MPV_FORMAT_DOUBLE, &maxVolume);
-    mpv_get_property(m_handle, "max-volume", MPV_FORMAT_DOUBLE, &maxVolume);
     maxVolume /= 100.0;
     m_player->playbackMaxVolumeChanged(maxVolume);
   }
@@ -136,26 +135,24 @@ bool MpvInstance::open(const QUrl &source) {
 
 void MpvInstance::play() {
   qCDebug(mpvBackend) << "Play";
-  int f = 0;
-  mpv_set_property(m_handle, "pause", MPV_FORMAT_FLAG, &f);
+  mpv::SetProperty(m_handle, mpv::property::Pause, false);
 }
 
 void MpvInstance::pause() {
   qCDebug(mpvBackend) << "Pause";
-  int f = 1;
-  mpv_set_property(m_handle, "pause", MPV_FORMAT_FLAG, &f);
+  mpv::SetProperty(m_handle, mpv::property::Pause, true);
 }
 
 void MpvInstance::stop() { qCDebug(mpvBackend) << "Stop"; }
 
 void MpvInstance::seek(TimeStamp pos) {
-  mpv_set_property(m_handle, "playback-time", MPV_FORMAT_DOUBLE, &pos);
+  mpv::SetProperty(m_handle, mpv::property::PlaybackTime, pos);
 }
 
 void MpvInstance::setVolume(Volume volume) {
   double percantageVolume = volume * 100;
   int error =
-      mpv_set_property(m_handle, VOLUME, MPV_FORMAT_DOUBLE, &percantageVolume);
+      mpv::SetProperty(m_handle, mpv::property::Volume, percantageVolume);
   if (error) {
     qCDebug(mpvBackend)
         << "Audio output not yet ready, setting volume at a later time";
@@ -168,27 +165,27 @@ void MpvInstance::setVolume(Volume volume) {
 }
 
 void MpvInstance::setCurrentVideoStream(TrackIndex track) {
-  qint64 tmp = track;
-  if (track < 0)
-    mpv_set_property_string(m_handle, "vid", "no");
-  else
-    mpv_set_property(m_handle, "vid", MPV_FORMAT_INT64, &tmp);
+  if (track < 0) {
+    mpv::SetProperty(m_handle, mpv::property::Vid, "no");
+  } else {
+    mpv::SetProperty(m_handle, mpv::property::Vid, mpv::format::Int64, track);
+  }
 }
 
 void MpvInstance::setCurrentAudioStream(TrackIndex track) {
-  qint64 tmp = track;
-  if (track < 0)
-    mpv_set_property_string(m_handle, "aid", "no");
-  else
-    mpv_set_property(m_handle, "aid", MPV_FORMAT_INT64, &tmp);
+  if (track < 0) {
+    mpv::SetProperty(m_handle, mpv::property::Aid, "no");
+  } else {
+    mpv::SetProperty(m_handle, mpv::property::Aid, mpv::format::Int64, track);
+  }
 }
 
 void MpvInstance::setCurrentSubtitleStream(TrackIndex track) {
-  qint64 tmp = track;
-  if (track < 0)
-    mpv_set_property_string(m_handle, "sid", "no");
-  else
-    mpv_set_property(m_handle, "sid", MPV_FORMAT_INT64, &tmp);
+  if (track < 0) {
+    mpv::SetProperty(m_handle, mpv::property::Sid, "no");
+  } else {
+    mpv::SetProperty(m_handle, mpv::property::Sid, mpv::format::Int64, track);
+  }
 }
 
 template <int type> struct MpvProperty;
@@ -296,7 +293,7 @@ void MpvInstance::processMpvEvents() {
 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";
+    qCDebug(mpvBackend) << "No data in event for property: " << property->name;
     return;
   }
   if (strcmp(property->name, "pause") == 0) {
@@ -397,8 +394,7 @@ void MpvInstance::onLogMessage(mpv_event_log_message *log) {
 }
 
 void MpvInstance::onFileLoaded() {
-  int paused = 0;
-  mpv_get_property(m_handle, "paused", MPV_FORMAT_FLAG, &paused);
+  bool paused = *mpv::GetProperty(m_handle, mpv::property::Paused);
   qCDebug(mpvBackend) << "file-loaded event!" << paused;
   m_loadedFile = true;
   auto state = paused ? PlayerPluginInterface::PlayState::Paused
diff --git a/backendplugins/backend_mpv/mpvhelper.h b/backendplugins/backend_mpv/mpvhelper.h
new file mode 100644 (file)
index 0000000..0d5208c
--- /dev/null
@@ -0,0 +1,237 @@
+#ifndef MPVHELPER_H
+#define MPVHELPER_H
+
+#include <mpv/client.h>
+
+#include <QString>
+#include <QVariant>
+
+#include <memory>
+
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(mpvHelper, "mpv.helper")
+
+namespace mpv {
+
+using Handle = mpv_handle *;
+
+struct Error
+{
+  using ErrorType = int;
+  ErrorType error;
+};
+
+template<typename T>
+class Outcome {
+  Error::ErrorType m_error = MPV_ERROR_SUCCESS;
+  T m_value;
+
+public:
+  Outcome(T value) : m_value{std::move(value)} {}
+  Outcome(Error error) : m_error{error.error} {}
+
+  bool HasError() const { return m_error != MPV_ERROR_SUCCESS; }
+  bool ok() const { return !HasError(); }
+  explicit operator bool() const { return ok(); }
+  const T &value() const {
+    assert(!HasError());
+    return m_value;
+  }
+  const T &operator*() const { return value(); }
+  Error::ErrorType error() const {
+    assert(HasError());
+    return m_error;
+  }
+};
+
+template<typename DataType = void>
+struct Event
+{
+  using type = DataType;
+  const mpv_event_id id;
+  constexpr explicit Event(mpv_event_id id) : id{id} {}
+};
+
+namespace event {
+// TODO add all events?
+constexpr Event<mpv_event_property> PropertyChange{MPV_EVENT_PROPERTY_CHANGE};
+constexpr Event<mpv_event_log_message> LogMessage{MPV_EVENT_LOG_MESSAGE};
+constexpr Event<> FileLoaded{MPV_EVENT_FILE_LOADED};
+constexpr Event<mpv_event_end_file> EndFile{MPV_EVENT_END_FILE};
+constexpr Event<> AudioReconfig{MPV_EVENT_AUDIO_RECONFIG};
+} // namespace event
+
+template<mpv_format FormatValue, typename CppType,
+         typename MpvInternalTypeName = CppType, bool ReadOnly = false>
+struct Format
+{
+  static_assert(!std::is_same_v<MpvInternalTypeName, bool>,
+                "MpvInternalType can not be bool");
+  // The type returned/provided from C++
+  using type = CppType;
+  // The type used internally by mpc
+  using MpvInternalType = MpvInternalTypeName;
+  static constexpr mpv_format format = FormatValue;
+  static constexpr bool readOnly = ReadOnly;
+};
+
+namespace format {
+constexpr Format<MPV_FORMAT_NONE, void> None;
+constexpr Format<MPV_FORMAT_STRING, QString, const char *> String;
+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;
+// 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;
+// TODO figure out correct type
+constexpr Format<MPV_FORMAT_BYTE_ARRAY, uint8_t *> ByteArray;
+
+} // namespace format
+
+template<typename Format, bool ReadOnly>
+struct Property
+{
+  static_assert(!(Format::readOnly == true && ReadOnly == false),
+                "Property with ReadOnly == false can not have a Format with "
+                "ReadOnly == true");
+  const Format format;
+  static constexpr bool readOnly = ReadOnly;
+  const char *name;
+  constexpr explicit Property(Format format, const char *name)
+      : format{format}, name{name} {}
+};
+
+template<bool ReadOnly = false, typename Format>
+constexpr auto MakeProperty(Format, const char *name) {
+  return Property<Format, ReadOnly>(Format{}, name);
+}
+
+namespace property {
+constexpr Property Pause = MakeProperty(format::Flag, "pause");
+constexpr Property Paused = MakeProperty(format::Flag, "paused");
+constexpr Property Duration = MakeProperty(format::Double, "duration");
+constexpr Property PlaybackTime = MakeProperty(format::Double, "playback-time");
+constexpr Property Volume = MakeProperty(format::Double, "volume");
+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 IdleActive = MakeProperty(format::Flag, "idle-active");
+constexpr Property MaxVolume = MakeProperty(format::Double, "max-volume");
+} // namespace property
+
+namespace detail {
+template<typename From, typename To>
+struct TypeConversion
+{
+  static_assert(std::is_convertible_v<From, To>,
+                "Default type conversion not possible, new TypeConversion "
+                "specialization required");
+  static To convert(const From &from) { return To{from}; }
+};
+
+template<typename T>
+struct TypeConversion<T, T>
+{
+  static const T &convert(const T &from) { return from; }
+};
+
+template<>
+struct TypeConversion<QString, const char *>
+{
+  static const char *convert(const QString &from) {
+    return qUtf8Printable(from);
+  }
+};
+
+template<>
+struct TypeConversion<const char *, QString>
+{
+  static QString convert(const char *from) { return QString::fromUtf8(from); }
+};
+
+// Avoids narrwoing conversion warnings
+template<>
+struct TypeConversion<int, bool>
+{
+  static bool convert(const int &from) { return from != 0; }
+};
+
+template<typename Format>
+auto ReadFormat(const Handle &handle, Format format, const char *name)
+    -> Outcome<typename Format::type> {
+  typename Format::MpvInternalType data;
+  int error = mpv_get_property(handle, name, format.format, &data);
+  qCDebug(mpvHelper()).nospace()
+      << "ReadFormat, property: " << name << ", error: " << error
+      << ", original value: " << data;
+  if (error != MPV_ERROR_SUCCESS) {
+    return Error{error};
+  }
+  return TypeConversion<typename Format::MpvInternalType,
+                        typename Format::type>::convert(data);
+}
+
+template<typename Format>
+int WriteFormat(const Handle &handle, Format format, const char *name,
+                const typename Format::type &value) {
+  const auto &data =
+      TypeConversion<typename Format::type,
+                     typename Format::MpvInternalType>::convert(value);
+  int error =
+      mpv_set_property(handle, name, format.format,
+                       const_cast<typename Format::MpvInternalType *>(&data));
+  qCDebug(mpvHelper()).nospace()
+      << "WriteFormat, property: " << name << ", error: " << error
+      << ", original value: " << value << ", converted value: " << data;
+  return error;
+}
+
+} // namespace detail
+
+template<typename Property>
+auto GetProperty(const Handle &handle, const Property &property)
+    -> Outcome<typename decltype(Property::format)::type> {
+  static_assert(decltype(Property::format)::format != MPV_FORMAT_NONE,
+                "Can not read property with format None");
+  return detail::ReadFormat(handle, property.format, property.name);
+}
+
+// Read property, using specific format rather than the properties format
+template<typename Property, typename Format>
+auto GetProperty(const Handle &handle, const Property &property,
+                 const Format &format) -> Outcome<typename Format::type> {
+  static_assert(Format::format != MPV_FORMAT_NONE,
+                "Can not read property with format None");
+  return detail::ReadFormat(handle, format, property.name);
+}
+
+template<typename Property>
+int SetProperty(const Handle &handle, const Property &property,
+                const typename decltype(Property::format)::type &value) {
+  static_assert(decltype(Property::format)::format != MPV_FORMAT_NONE,
+                "Can not write property with format None");
+  static_assert(!decltype(Property::format)::readOnly,
+                "Can not write a readOnly property");
+
+  return detail::WriteFormat(handle, property.format, property.name, value);
+}
+
+template<typename Property, typename Format>
+int SetProperty(const Handle &handle, const Property &property,
+                const Format &format, const typename Format::type &value) {
+  static_assert(Format::format != MPV_FORMAT_NONE,
+                "Can not write property with format None");
+  static_assert(!Format::readOnly, "Can not write a readOnly property");
+
+  return detail::WriteFormat(handle, format, property.name, value);
+}
+
+} // namespace mpv
+
+#endif // MPVHELPER_H