1 /* Copyright (C) 2017 the mpv developers
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #ifndef MPV_CLIENT_API_QTHELPER_H_
17 #define MPV_CLIENT_API_QTHELPER_H_
19 #include <mpv/client.h>
21 #if !MPV_ENABLE_DEPRECATED
22 #error "This helper is deprecated. Copy it into your project instead."
26 * Note: these helpers are provided for convenience for C++/Qt applications.
27 * This is based on the public API in client.h, and it does not encode any
28 * knowledge that is not known or guaranteed outside of the C client API. You
29 * can even copy and modify this code as you like, or implement similar things
30 * for other languages.
39 #include <QSharedPointer>
45 // Wrapper around mpv_handle. Does refcounting under the hood.
49 container(mpv_handle *h) : mpv(h) {}
50 ~container() { mpv_terminate_destroy(mpv); }
53 QSharedPointer<container> sptr;
55 // Construct a new Handle from a raw mpv_handle with refcount 1. If the
56 // last Handle goes out of scope, the mpv_handle will be destroyed with
57 // mpv_terminate_destroy().
58 // Never destroy the mpv_handle manually when using this wrapper. You
59 // will create dangling pointers. Just let the wrapper take care of
60 // destroying the mpv_handle.
61 // Never create multiple wrappers from the same raw mpv_handle; copy the
62 // wrapper instead (that's what it's for).
63 static Handle FromRawHandle(mpv_handle *handle) {
65 h.sptr = QSharedPointer<container>(new container(handle));
69 // Return the raw handle; for use with the libmpv C API.
70 operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
73 static inline QVariant node_to_variant(const mpv_node *node)
75 switch (node->format) {
76 case MPV_FORMAT_STRING:
77 return QVariant(QString::fromUtf8(node->u.string));
79 return QVariant(static_cast<bool>(node->u.flag));
80 case MPV_FORMAT_INT64:
81 return QVariant(static_cast<qlonglong>(node->u.int64));
82 case MPV_FORMAT_DOUBLE:
83 return QVariant(node->u.double_);
84 case MPV_FORMAT_NODE_ARRAY: {
85 mpv_node_list *list = node->u.list;
87 for (int n = 0; n < list->num; n++)
88 qlist.append(node_to_variant(&list->values[n]));
89 return QVariant(qlist);
91 case MPV_FORMAT_NODE_MAP: {
92 mpv_node_list *list = node->u.list;
94 for (int n = 0; n < list->num; n++) {
95 qmap.insert(QString::fromUtf8(list->keys[n]),
96 node_to_variant(&list->values[n]));
98 return QVariant(qmap);
100 default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
105 struct node_builder {
106 node_builder(const QVariant& v) {
112 mpv_node *node() { return &node_; }
114 Q_DISABLE_COPY(node_builder)
116 mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) {
117 dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
118 mpv_node_list *list = new mpv_node_list();
122 list->values = new mpv_node[num]();
126 list->keys = new char*[num]();
135 char *dup_qstring(const QString &s) {
136 QByteArray b = s.toUtf8();
137 char *r = new char[b.size() + 1];
139 std::memcpy(r, b.data(), b.size() + 1);
142 bool test_type(const QVariant &v, QMetaType::Type t) {
143 // The Qt docs say: "Although this function is declared as returning
144 // "QVariant::Type(obsolete), the return value should be interpreted
145 // as QMetaType::Type."
146 // So a cast really seems to be needed to avoid warnings (urgh).
147 return static_cast<int>(v.type()) == static_cast<int>(t);
149 void set(mpv_node *dst, const QVariant &src) {
150 if (test_type(src, QMetaType::QString)) {
151 dst->format = MPV_FORMAT_STRING;
152 dst->u.string = dup_qstring(src.toString());
155 } else if (test_type(src, QMetaType::Bool)) {
156 dst->format = MPV_FORMAT_FLAG;
157 dst->u.flag = src.toBool() ? 1 : 0;
158 } else if (test_type(src, QMetaType::Int) ||
159 test_type(src, QMetaType::LongLong) ||
160 test_type(src, QMetaType::UInt) ||
161 test_type(src, QMetaType::ULongLong))
163 dst->format = MPV_FORMAT_INT64;
164 dst->u.int64 = src.toLongLong();
165 } else if (test_type(src, QMetaType::Double)) {
166 dst->format = MPV_FORMAT_DOUBLE;
167 dst->u.double_ = src.toDouble();
168 } else if (src.canConvert<QVariantList>()) {
169 QVariantList qlist = src.toList();
170 mpv_node_list *list = create_list(dst, false, qlist.size());
173 list->num = qlist.size();
174 for (int n = 0; n < qlist.size(); n++)
175 set(&list->values[n], qlist[n]);
176 } else if (src.canConvert<QVariantMap>()) {
177 QVariantMap qmap = src.toMap();
178 mpv_node_list *list = create_list(dst, true, qmap.size());
181 list->num = qmap.size();
182 for (int n = 0; n < qmap.size(); n++) {
183 list->keys[n] = dup_qstring(qmap.keys()[n]);
184 if (!list->keys[n]) {
188 set(&list->values[n], qmap.values()[n]);
195 dst->format = MPV_FORMAT_NONE;
197 void free_node(mpv_node *dst) {
198 switch (dst->format) {
199 case MPV_FORMAT_STRING:
200 delete[] dst->u.string;
202 case MPV_FORMAT_NODE_ARRAY:
203 case MPV_FORMAT_NODE_MAP: {
204 mpv_node_list *list = dst->u.list;
206 for (int n = 0; n < list->num; n++) {
208 delete[] list->keys[n];
210 free_node(&list->values[n]);
213 delete[] list->values;
220 dst->format = MPV_FORMAT_NONE;
225 * RAII wrapper that calls mpv_free_node_contents() on the pointer.
227 struct node_autofree {
229 node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
230 ~node_autofree() { mpv_free_node_contents(ptr); }
233 #if MPV_ENABLE_DEPRECATED
236 * Return the given property as mpv_node converted to QVariant, or QVariant()
239 * @deprecated use get_property() instead
241 * @param name the property name
243 static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
246 if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
248 node_autofree f(&node);
249 return node_to_variant(&node);
253 * Set the given property as mpv_node converted from the QVariant argument.
255 * @deprecated use set_property() instead
257 static inline int set_property_variant(mpv_handle *ctx, const QString &name,
260 node_builder node(v);
261 return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
265 * Set the given option as mpv_node converted from the QVariant argument.
267 * @deprecated use set_property() instead
269 static inline int set_option_variant(mpv_handle *ctx, const QString &name,
272 node_builder node(v);
273 return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
277 * mpv_command_node() equivalent. Returns QVariant() on error (and
278 * unfortunately, the same on success).
280 * @deprecated use command() instead
282 static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
284 node_builder node(args);
286 if (mpv_command_node(ctx, node.node(), &res) < 0)
288 node_autofree f(&res);
289 return node_to_variant(&res);
295 * This is used to return error codes wrapped in QVariant for functions which
298 * You can use get_error() or is_error() to extract the error status from a
304 * enum mpv_error value (or a value outside of it if ABI was extended)
308 ErrorReturn() : error(0) {}
309 explicit ErrorReturn(int err) : error(err) {}
313 * Return the mpv error code packed into a QVariant, or 0 (success) if it's not
316 * @return error code (<0) or success (>=0)
318 static inline int get_error(const QVariant &v)
320 if (!v.canConvert<ErrorReturn>())
322 return v.value<ErrorReturn>().error;
326 * Return whether the QVariant carries a mpv error code.
328 static inline bool is_error(const QVariant &v)
330 return get_error(v) < 0;
334 * Return the given property as mpv_node converted to QVariant, or QVariant()
337 * @param name the property name
338 * @return the property value, or an ErrorReturn with the error code
340 static inline QVariant get_property(mpv_handle *ctx, const QString &name)
343 int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
345 return QVariant::fromValue(ErrorReturn(err));
346 node_autofree f(&node);
347 return node_to_variant(&node);
351 * Set the given property as mpv_node converted from the QVariant argument.
353 * @return mpv error code (<0 on error, >= 0 on success)
355 static inline int set_property(mpv_handle *ctx, const QString &name,
358 node_builder node(v);
359 return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
363 * mpv_command_node() equivalent.
365 * @param args command arguments, with args[0] being the command name as string
366 * @return the property value, or an ErrorReturn with the error code
368 static inline QVariant command(mpv_handle *ctx, const QVariant &args)
370 node_builder node(args);
372 int err = mpv_command_node(ctx, node.node(), &res);
374 return QVariant::fromValue(ErrorReturn(err));
375 node_autofree f(&res);
376 return node_to_variant(&res);
382 Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
384 #endif /* else #if MPV_ENABLE_DEPRECATED */