123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- /* Copyright (C) 2017 the mpv developers
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #ifndef MPV_CLIENT_API_QTHELPER_H_
- #define MPV_CLIENT_API_QTHELPER_H_
- #include <mpv/client.h>
- /**
- * Note: these helpers are provided for convenience for C++/Qt applications.
- * This is based on the public API in client.h, and it does not encode any
- * knowledge that is not known or guaranteed outside of the C client API. You
- * can even copy and modify this code as you like, or implement similar things
- * for other languages.
- */
- #include <cstring>
- #include <QVariant>
- #include <QString>
- #include <QList>
- #include <QHash>
- #include <QSharedPointer>
- #include <QMetaType>
- namespace mpv {
- namespace qt {
- // Wrapper around mpv_handle. Does refcounting under the hood.
- class Handle
- {
- struct container {
- container(mpv_handle *h) : mpv(h) {}
- ~container() { mpv_terminate_destroy(mpv); }
- mpv_handle *mpv;
- };
- QSharedPointer<container> sptr;
- public:
- // Construct a new Handle from a raw mpv_handle with refcount 1. If the
- // last Handle goes out of scope, the mpv_handle will be destroyed with
- // mpv_terminate_destroy().
- // Never destroy the mpv_handle manually when using this wrapper. You
- // will create dangling pointers. Just let the wrapper take care of
- // destroying the mpv_handle.
- // Never create multiple wrappers from the same raw mpv_handle; copy the
- // wrapper instead (that's what it's for).
- static Handle FromRawHandle(mpv_handle *handle) {
- Handle h;
- h.sptr = QSharedPointer<container>(new container(handle));
- return h;
- }
- // Return the raw handle; for use with the libmpv C API.
- operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
- };
- static inline QVariant node_to_variant(const mpv_node *node)
- {
- switch (node->format) {
- case MPV_FORMAT_STRING:
- return QVariant(QString::fromUtf8(node->u.string));
- case MPV_FORMAT_FLAG:
- return QVariant(static_cast<bool>(node->u.flag));
- case MPV_FORMAT_INT64:
- return QVariant(static_cast<qlonglong>(node->u.int64));
- case MPV_FORMAT_DOUBLE:
- return QVariant(node->u.double_);
- case MPV_FORMAT_NODE_ARRAY: {
- mpv_node_list *list = node->u.list;
- QVariantList qlist;
- for (int n = 0; n < list->num; n++)
- qlist.append(node_to_variant(&list->values[n]));
- return QVariant(qlist);
- }
- case MPV_FORMAT_NODE_MAP: {
- mpv_node_list *list = node->u.list;
- QVariantMap qmap;
- for (int n = 0; n < list->num; n++) {
- qmap.insert(QString::fromUtf8(list->keys[n]),
- node_to_variant(&list->values[n]));
- }
- return QVariant(qmap);
- }
- default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
- return QVariant();
- }
- }
- struct node_builder {
- node_builder(const QVariant& v) {
- set(&node_, v);
- }
- ~node_builder() {
- free_node(&node_);
- }
- mpv_node *node() { return &node_; }
- private:
- Q_DISABLE_COPY(node_builder)
- mpv_node node_;
- mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) {
- dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
- mpv_node_list *list = new mpv_node_list();
- dst->u.list = list;
- if (!list)
- goto err;
- list->values = new mpv_node[num]();
- if (!list->values)
- goto err;
- if (is_map) {
- list->keys = new char*[num]();
- if (!list->keys)
- goto err;
- }
- return list;
- err:
- free_node(dst);
- return NULL;
- }
- char *dup_qstring(const QString &s) {
- QByteArray b = s.toUtf8();
- char *r = new char[b.size() + 1];
- if (r)
- std::memcpy(r, b.data(), b.size() + 1);
- return r;
- }
- bool test_type(const QVariant &v, QMetaType::Type t) {
- // The Qt docs say: "Although this function is declared as returning
- // "QVariant::Type(obsolete), the return value should be interpreted
- // as QMetaType::Type."
- // So a cast really seems to be needed to avoid warnings (urgh).
- return static_cast<int>(v.type()) == static_cast<int>(t);
- }
- void set(mpv_node *dst, const QVariant &src) {
- if (test_type(src, QMetaType::QString)) {
- dst->format = MPV_FORMAT_STRING;
- dst->u.string = dup_qstring(src.toString());
- if (!dst->u.string)
- goto fail;
- } else if (test_type(src, QMetaType::Bool)) {
- dst->format = MPV_FORMAT_FLAG;
- dst->u.flag = src.toBool() ? 1 : 0;
- } else if (test_type(src, QMetaType::Int) ||
- test_type(src, QMetaType::LongLong) ||
- test_type(src, QMetaType::UInt) ||
- test_type(src, QMetaType::ULongLong))
- {
- dst->format = MPV_FORMAT_INT64;
- dst->u.int64 = src.toLongLong();
- } else if (test_type(src, QMetaType::Double)) {
- dst->format = MPV_FORMAT_DOUBLE;
- dst->u.double_ = src.toDouble();
- } else if (src.canConvert<QVariantList>()) {
- QVariantList qlist = src.toList();
- mpv_node_list *list = create_list(dst, false, qlist.size());
- if (!list)
- goto fail;
- list->num = qlist.size();
- for (int n = 0; n < qlist.size(); n++)
- set(&list->values[n], qlist[n]);
- } else if (src.canConvert<QVariantMap>()) {
- QVariantMap qmap = src.toMap();
- mpv_node_list *list = create_list(dst, true, qmap.size());
- if (!list)
- goto fail;
- list->num = qmap.size();
- for (int n = 0; n < qmap.size(); n++) {
- list->keys[n] = dup_qstring(qmap.keys()[n]);
- if (!list->keys[n]) {
- free_node(dst);
- goto fail;
- }
- set(&list->values[n], qmap.values()[n]);
- }
- } else {
- goto fail;
- }
- return;
- fail:
- dst->format = MPV_FORMAT_NONE;
- }
- void free_node(mpv_node *dst) {
- switch (dst->format) {
- case MPV_FORMAT_STRING:
- delete[] dst->u.string;
- break;
- case MPV_FORMAT_NODE_ARRAY:
- case MPV_FORMAT_NODE_MAP: {
- mpv_node_list *list = dst->u.list;
- if (list) {
- for (int n = 0; n < list->num; n++) {
- if (list->keys)
- delete[] list->keys[n];
- if (list->values)
- free_node(&list->values[n]);
- }
- delete[] list->keys;
- delete[] list->values;
- }
- delete list;
- break;
- }
- default: ;
- }
- dst->format = MPV_FORMAT_NONE;
- }
- };
- /**
- * RAII wrapper that calls mpv_free_node_contents() on the pointer.
- */
- struct node_autofree {
- mpv_node *ptr;
- node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
- ~node_autofree() { mpv_free_node_contents(ptr); }
- };
- #if MPV_ENABLE_DEPRECATED
- /**
- * Return the given property as mpv_node converted to QVariant, or QVariant()
- * on error.
- *
- * @deprecated use get_property() instead
- *
- * @param name the property name
- */
- static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
- {
- mpv_node node;
- if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
- return QVariant();
- node_autofree f(&node);
- return node_to_variant(&node);
- }
- /**
- * Set the given property as mpv_node converted from the QVariant argument.
- * @deprecated use set_property() instead
- */
- static inline int set_property_variant(mpv_handle *ctx, const QString &name,
- const QVariant &v)
- {
- node_builder node(v);
- return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
- }
- /**
- * Set the given option as mpv_node converted from the QVariant argument.
- *
- * @deprecated use set_property() instead
- */
- static inline int set_option_variant(mpv_handle *ctx, const QString &name,
- const QVariant &v)
- {
- node_builder node(v);
- return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
- }
- /**
- * mpv_command_node() equivalent. Returns QVariant() on error (and
- * unfortunately, the same on success).
- *
- * @deprecated use command() instead
- */
- static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
- {
- node_builder node(args);
- mpv_node res;
- if (mpv_command_node(ctx, node.node(), &res) < 0)
- return QVariant();
- node_autofree f(&res);
- return node_to_variant(&res);
- }
- #endif
- /**
- * This is used to return error codes wrapped in QVariant for functions which
- * return QVariant.
- *
- * You can use get_error() or is_error() to extract the error status from a
- * QVariant value.
- */
- struct ErrorReturn
- {
- /**
- * enum mpv_error value (or a value outside of it if ABI was extended)
- */
- int error;
- ErrorReturn() : error(0) {}
- explicit ErrorReturn(int err) : error(err) {}
- };
- /**
- * Return the mpv error code packed into a QVariant, or 0 (success) if it's not
- * an error value.
- *
- * @return error code (<0) or success (>=0)
- */
- static inline int get_error(const QVariant &v)
- {
- if (!v.canConvert<ErrorReturn>())
- return 0;
- return v.value<ErrorReturn>().error;
- }
- /**
- * Return whether the QVariant carries a mpv error code.
- */
- static inline bool is_error(const QVariant &v)
- {
- return get_error(v) < 0;
- }
- /**
- * Return the given property as mpv_node converted to QVariant, or QVariant()
- * on error.
- *
- * @param name the property name
- * @return the property value, or an ErrorReturn with the error code
- */
- static inline QVariant get_property(mpv_handle *ctx, const QString &name)
- {
- mpv_node node;
- int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
- if (err < 0)
- return QVariant::fromValue(ErrorReturn(err));
- node_autofree f(&node);
- return node_to_variant(&node);
- }
- /**
- * Set the given property as mpv_node converted from the QVariant argument.
- *
- * @return mpv error code (<0 on error, >= 0 on success)
- */
- static inline int set_property(mpv_handle *ctx, const QString &name,
- const QVariant &v)
- {
- node_builder node(v);
- return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
- }
- /**
- * mpv_command_node() equivalent.
- *
- * @param args command arguments, with args[0] being the command name as string
- * @return the property value, or an ErrorReturn with the error code
- */
- static inline QVariant command(mpv_handle *ctx, const QVariant &args)
- {
- node_builder node(args);
- mpv_node res;
- int err = mpv_command_node(ctx, node.node(), &res);
- if (err < 0)
- return QVariant::fromValue(ErrorReturn(err));
- node_autofree f(&res);
- return node_to_variant(&res);
- }
- }
- }
- Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
- #endif
|