: 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) << '.'
}
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;
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,
} 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)
}
void MpvInstance::onVolumeChanged(double volume) {
+ volume /= 100.0;
if (m_volumeToSet > 0) {
qCDebug(mpvBackend)
<< "Requested volume still not set, skipping this update";
#define MPVHELPER_H
#include "mpvhandle.h"
+#include "qthelper.hpp"
#include <mpv/client.h>
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;
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
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> {
}
};
+ 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>
event.id,
std::make_unique<EventCallback<Event, decltype(handler), false>>(
event, handler)));
- return !pair.second;
+ return pair.second;
}
template<typename Event>
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);
}
};