]> git.sur5r.net Git - minitube/blob - lib/media/src/mpv/qthelper.hpp
New upstream version 3.4.1
[minitube] / lib / media / src / mpv / qthelper.hpp
1 /* Copyright (C) 2017 the mpv developers
2  *
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.
6  *
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.
14  */
15
16 #ifndef MPV_CLIENT_API_QTHELPER_H_
17 #define MPV_CLIENT_API_QTHELPER_H_
18
19 #include <mpv/client.h>
20
21 #if !MPV_ENABLE_DEPRECATED
22 #error "This helper is deprecated. Copy it into your project instead."
23 #else
24
25 /**
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.
31  */
32
33 #include <cstring>
34
35 #include <QVariant>
36 #include <QString>
37 #include <QList>
38 #include <QHash>
39 #include <QSharedPointer>
40 #include <QMetaType>
41
42 namespace mpv {
43 namespace qt {
44
45 // Wrapper around mpv_handle. Does refcounting under the hood.
46 class Handle
47 {
48     struct container {
49         container(mpv_handle *h) : mpv(h) {}
50         ~container() { mpv_terminate_destroy(mpv); }
51         mpv_handle *mpv;
52     };
53     QSharedPointer<container> sptr;
54 public:
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) {
64         Handle h;
65         h.sptr = QSharedPointer<container>(new container(handle));
66         return h;
67     }
68
69     // Return the raw handle; for use with the libmpv C API.
70     operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
71 };
72
73 static inline QVariant node_to_variant(const mpv_node *node)
74 {
75     switch (node->format) {
76     case MPV_FORMAT_STRING:
77         return QVariant(QString::fromUtf8(node->u.string));
78     case MPV_FORMAT_FLAG:
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;
86         QVariantList qlist;
87         for (int n = 0; n < list->num; n++)
88             qlist.append(node_to_variant(&list->values[n]));
89         return QVariant(qlist);
90     }
91     case MPV_FORMAT_NODE_MAP: {
92         mpv_node_list *list = node->u.list;
93         QVariantMap qmap;
94         for (int n = 0; n < list->num; n++) {
95             qmap.insert(QString::fromUtf8(list->keys[n]),
96                         node_to_variant(&list->values[n]));
97         }
98         return QVariant(qmap);
99     }
100     default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
101         return QVariant();
102     }
103 }
104
105 struct node_builder {
106     node_builder(const QVariant& v) {
107         set(&node_, v);
108     }
109     ~node_builder() {
110         free_node(&node_);
111     }
112     mpv_node *node() { return &node_; }
113 private:
114     Q_DISABLE_COPY(node_builder)
115     mpv_node node_;
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();
119         dst->u.list = list;
120         if (!list)
121             goto err;
122         list->values = new mpv_node[num]();
123         if (!list->values)
124             goto err;
125         if (is_map) {
126             list->keys = new char*[num]();
127             if (!list->keys)
128                 goto err;
129         }
130         return list;
131     err:
132         free_node(dst);
133         return NULL;
134     }
135     char *dup_qstring(const QString &s) {
136         QByteArray b = s.toUtf8();
137         char *r = new char[b.size() + 1];
138         if (r)
139             std::memcpy(r, b.data(), b.size() + 1);
140         return r;
141     }
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);
148     }
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());
153             if (!dst->u.string)
154                 goto fail;
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))
162         {
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());
171             if (!list)
172                 goto fail;
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());
179             if (!list)
180                 goto fail;
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]) {
185                     free_node(dst);
186                     goto fail;
187                 }
188                 set(&list->values[n], qmap.values()[n]);
189             }
190         } else {
191             goto fail;
192         }
193         return;
194     fail:
195         dst->format = MPV_FORMAT_NONE;
196     }
197     void free_node(mpv_node *dst) {
198         switch (dst->format) {
199         case MPV_FORMAT_STRING:
200             delete[] dst->u.string;
201             break;
202         case MPV_FORMAT_NODE_ARRAY:
203         case MPV_FORMAT_NODE_MAP: {
204             mpv_node_list *list = dst->u.list;
205             if (list) {
206                 for (int n = 0; n < list->num; n++) {
207                     if (list->keys)
208                         delete[] list->keys[n];
209                     if (list->values)
210                         free_node(&list->values[n]);
211                 }
212                 delete[] list->keys;
213                 delete[] list->values;
214             }
215             delete list;
216             break;
217         }
218         default: ;
219         }
220         dst->format = MPV_FORMAT_NONE;
221     }
222 };
223
224 /**
225  * RAII wrapper that calls mpv_free_node_contents() on the pointer.
226  */
227 struct node_autofree {
228     mpv_node *ptr;
229     node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
230     ~node_autofree() { mpv_free_node_contents(ptr); }
231 };
232
233 #if MPV_ENABLE_DEPRECATED
234
235 /**
236  * Return the given property as mpv_node converted to QVariant, or QVariant()
237  * on error.
238  *
239  * @deprecated use get_property() instead
240  *
241  * @param name the property name
242  */
243 static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
244 {
245     mpv_node node;
246     if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
247         return QVariant();
248     node_autofree f(&node);
249     return node_to_variant(&node);
250 }
251
252 /**
253  * Set the given property as mpv_node converted from the QVariant argument.
254
255  * @deprecated use set_property() instead
256  */
257 static inline int set_property_variant(mpv_handle *ctx, const QString &name,
258                                        const QVariant &v)
259 {
260     node_builder node(v);
261     return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
262 }
263
264 /**
265  * Set the given option as mpv_node converted from the QVariant argument.
266  *
267  * @deprecated use set_property() instead
268  */
269 static inline int set_option_variant(mpv_handle *ctx, const QString &name,
270                                      const QVariant &v)
271 {
272     node_builder node(v);
273     return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
274 }
275
276 /**
277  * mpv_command_node() equivalent. Returns QVariant() on error (and
278  * unfortunately, the same on success).
279  *
280  * @deprecated use command() instead
281  */
282 static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
283 {
284     node_builder node(args);
285     mpv_node res;
286     if (mpv_command_node(ctx, node.node(), &res) < 0)
287         return QVariant();
288     node_autofree f(&res);
289     return node_to_variant(&res);
290 }
291
292 #endif
293
294 /**
295  * This is used to return error codes wrapped in QVariant for functions which
296  * return QVariant.
297  *
298  * You can use get_error() or is_error() to extract the error status from a
299  * QVariant value.
300  */
301 struct ErrorReturn
302 {
303     /**
304      * enum mpv_error value (or a value outside of it if ABI was extended)
305      */
306     int error;
307
308     ErrorReturn() : error(0) {}
309     explicit ErrorReturn(int err) : error(err) {}
310 };
311
312 /**
313  * Return the mpv error code packed into a QVariant, or 0 (success) if it's not
314  * an error value.
315  *
316  * @return error code (<0) or success (>=0)
317  */
318 static inline int get_error(const QVariant &v)
319 {
320     if (!v.canConvert<ErrorReturn>())
321         return 0;
322     return v.value<ErrorReturn>().error;
323 }
324
325 /**
326  * Return whether the QVariant carries a mpv error code.
327  */
328 static inline bool is_error(const QVariant &v)
329 {
330     return get_error(v) < 0;
331 }
332
333 /**
334  * Return the given property as mpv_node converted to QVariant, or QVariant()
335  * on error.
336  *
337  * @param name the property name
338  * @return the property value, or an ErrorReturn with the error code
339  */
340 static inline QVariant get_property(mpv_handle *ctx, const QString &name)
341 {
342     mpv_node node;
343     int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
344     if (err < 0)
345         return QVariant::fromValue(ErrorReturn(err));
346     node_autofree f(&node);
347     return node_to_variant(&node);
348 }
349
350 /**
351  * Set the given property as mpv_node converted from the QVariant argument.
352  *
353  * @return mpv error code (<0 on error, >= 0 on success)
354  */
355 static inline int set_property(mpv_handle *ctx, const QString &name,
356                                        const QVariant &v)
357 {
358     node_builder node(v);
359     return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
360 }
361
362 /**
363  * mpv_command_node() equivalent.
364  *
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
367  */
368 static inline QVariant command(mpv_handle *ctx, const QVariant &args)
369 {
370     node_builder node(args);
371     mpv_node res;
372     int err = mpv_command_node(ctx, node.node(), &res);
373     if (err < 0)
374         return QVariant::fromValue(ErrorReturn(err));
375     node_autofree f(&res);
376     return node_to_variant(&res);
377 }
378
379 }
380 }
381
382 Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
383
384 #endif /* else #if MPV_ENABLE_DEPRECATED */
385
386 #endif