QtHelper.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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. #ifndef MPV_CLIENT_API_QTHELPER_H_
  16. #define MPV_CLIENT_API_QTHELPER_H_
  17. #include <mpv/client.h>
  18. /**
  19. * Note: these helpers are provided for convenience for C++/Qt applications.
  20. * This is based on the public API in client.h, and it does not encode any
  21. * knowledge that is not known or guaranteed outside of the C client API. You
  22. * can even copy and modify this code as you like, or implement similar things
  23. * for other languages.
  24. */
  25. #include <cstring>
  26. #include <QVariant>
  27. #include <QString>
  28. #include <QList>
  29. #include <QHash>
  30. #include <QSharedPointer>
  31. #include <QMetaType>
  32. namespace mpv {
  33. namespace qt {
  34. // Wrapper around mpv_handle. Does refcounting under the hood.
  35. class Handle
  36. {
  37. struct container {
  38. container(mpv_handle *h) : mpv(h) {}
  39. ~container() { mpv_terminate_destroy(mpv); }
  40. mpv_handle *mpv;
  41. };
  42. QSharedPointer<container> sptr;
  43. public:
  44. // Construct a new Handle from a raw mpv_handle with refcount 1. If the
  45. // last Handle goes out of scope, the mpv_handle will be destroyed with
  46. // mpv_terminate_destroy().
  47. // Never destroy the mpv_handle manually when using this wrapper. You
  48. // will create dangling pointers. Just let the wrapper take care of
  49. // destroying the mpv_handle.
  50. // Never create multiple wrappers from the same raw mpv_handle; copy the
  51. // wrapper instead (that's what it's for).
  52. static Handle FromRawHandle(mpv_handle *handle) {
  53. Handle h;
  54. h.sptr = QSharedPointer<container>(new container(handle));
  55. return h;
  56. }
  57. // Return the raw handle; for use with the libmpv C API.
  58. operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; }
  59. };
  60. static inline QVariant node_to_variant(const mpv_node *node)
  61. {
  62. switch (node->format) {
  63. case MPV_FORMAT_STRING:
  64. return QVariant(QString::fromUtf8(node->u.string));
  65. case MPV_FORMAT_FLAG:
  66. return QVariant(static_cast<bool>(node->u.flag));
  67. case MPV_FORMAT_INT64:
  68. return QVariant(static_cast<qlonglong>(node->u.int64));
  69. case MPV_FORMAT_DOUBLE:
  70. return QVariant(node->u.double_);
  71. case MPV_FORMAT_NODE_ARRAY: {
  72. mpv_node_list *list = node->u.list;
  73. QVariantList qlist;
  74. for (int n = 0; n < list->num; n++)
  75. qlist.append(node_to_variant(&list->values[n]));
  76. return QVariant(qlist);
  77. }
  78. case MPV_FORMAT_NODE_MAP: {
  79. mpv_node_list *list = node->u.list;
  80. QVariantMap qmap;
  81. for (int n = 0; n < list->num; n++) {
  82. qmap.insert(QString::fromUtf8(list->keys[n]),
  83. node_to_variant(&list->values[n]));
  84. }
  85. return QVariant(qmap);
  86. }
  87. default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
  88. return QVariant();
  89. }
  90. }
  91. struct node_builder {
  92. node_builder(const QVariant& v) {
  93. set(&node_, v);
  94. }
  95. ~node_builder() {
  96. free_node(&node_);
  97. }
  98. mpv_node *node() { return &node_; }
  99. private:
  100. Q_DISABLE_COPY(node_builder)
  101. mpv_node node_;
  102. mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) {
  103. dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
  104. mpv_node_list *list = new mpv_node_list();
  105. dst->u.list = list;
  106. if (!list)
  107. goto err;
  108. list->values = new mpv_node[num]();
  109. if (!list->values)
  110. goto err;
  111. if (is_map) {
  112. list->keys = new char*[num]();
  113. if (!list->keys)
  114. goto err;
  115. }
  116. return list;
  117. err:
  118. free_node(dst);
  119. return NULL;
  120. }
  121. char *dup_qstring(const QString &s) {
  122. QByteArray b = s.toUtf8();
  123. char *r = new char[b.size() + 1];
  124. if (r)
  125. std::memcpy(r, b.data(), b.size() + 1);
  126. return r;
  127. }
  128. bool test_type(const QVariant &v, QMetaType::Type t) {
  129. // The Qt docs say: "Although this function is declared as returning
  130. // "QVariant::Type(obsolete), the return value should be interpreted
  131. // as QMetaType::Type."
  132. // So a cast really seems to be needed to avoid warnings (urgh).
  133. return static_cast<int>(v.type()) == static_cast<int>(t);
  134. }
  135. void set(mpv_node *dst, const QVariant &src) {
  136. if (test_type(src, QMetaType::QString)) {
  137. dst->format = MPV_FORMAT_STRING;
  138. dst->u.string = dup_qstring(src.toString());
  139. if (!dst->u.string)
  140. goto fail;
  141. } else if (test_type(src, QMetaType::Bool)) {
  142. dst->format = MPV_FORMAT_FLAG;
  143. dst->u.flag = src.toBool() ? 1 : 0;
  144. } else if (test_type(src, QMetaType::Int) ||
  145. test_type(src, QMetaType::LongLong) ||
  146. test_type(src, QMetaType::UInt) ||
  147. test_type(src, QMetaType::ULongLong))
  148. {
  149. dst->format = MPV_FORMAT_INT64;
  150. dst->u.int64 = src.toLongLong();
  151. } else if (test_type(src, QMetaType::Double)) {
  152. dst->format = MPV_FORMAT_DOUBLE;
  153. dst->u.double_ = src.toDouble();
  154. } else if (src.canConvert<QVariantList>()) {
  155. QVariantList qlist = src.toList();
  156. mpv_node_list *list = create_list(dst, false, qlist.size());
  157. if (!list)
  158. goto fail;
  159. list->num = qlist.size();
  160. for (int n = 0; n < qlist.size(); n++)
  161. set(&list->values[n], qlist[n]);
  162. } else if (src.canConvert<QVariantMap>()) {
  163. QVariantMap qmap = src.toMap();
  164. mpv_node_list *list = create_list(dst, true, qmap.size());
  165. if (!list)
  166. goto fail;
  167. list->num = qmap.size();
  168. for (int n = 0; n < qmap.size(); n++) {
  169. list->keys[n] = dup_qstring(qmap.keys()[n]);
  170. if (!list->keys[n]) {
  171. free_node(dst);
  172. goto fail;
  173. }
  174. set(&list->values[n], qmap.values()[n]);
  175. }
  176. } else {
  177. goto fail;
  178. }
  179. return;
  180. fail:
  181. dst->format = MPV_FORMAT_NONE;
  182. }
  183. void free_node(mpv_node *dst) {
  184. switch (dst->format) {
  185. case MPV_FORMAT_STRING:
  186. delete[] dst->u.string;
  187. break;
  188. case MPV_FORMAT_NODE_ARRAY:
  189. case MPV_FORMAT_NODE_MAP: {
  190. mpv_node_list *list = dst->u.list;
  191. if (list) {
  192. for (int n = 0; n < list->num; n++) {
  193. if (list->keys)
  194. delete[] list->keys[n];
  195. if (list->values)
  196. free_node(&list->values[n]);
  197. }
  198. delete[] list->keys;
  199. delete[] list->values;
  200. }
  201. delete list;
  202. break;
  203. }
  204. default: ;
  205. }
  206. dst->format = MPV_FORMAT_NONE;
  207. }
  208. };
  209. /**
  210. * RAII wrapper that calls mpv_free_node_contents() on the pointer.
  211. */
  212. struct node_autofree {
  213. mpv_node *ptr;
  214. node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {}
  215. ~node_autofree() { mpv_free_node_contents(ptr); }
  216. };
  217. #if MPV_ENABLE_DEPRECATED
  218. /**
  219. * Return the given property as mpv_node converted to QVariant, or QVariant()
  220. * on error.
  221. *
  222. * @deprecated use get_property() instead
  223. *
  224. * @param name the property name
  225. */
  226. static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name)
  227. {
  228. mpv_node node;
  229. if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
  230. return QVariant();
  231. node_autofree f(&node);
  232. return node_to_variant(&node);
  233. }
  234. /**
  235. * Set the given property as mpv_node converted from the QVariant argument.
  236. * @deprecated use set_property() instead
  237. */
  238. static inline int set_property_variant(mpv_handle *ctx, const QString &name,
  239. const QVariant &v)
  240. {
  241. node_builder node(v);
  242. return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
  243. }
  244. /**
  245. * Set the given option as mpv_node converted from the QVariant argument.
  246. *
  247. * @deprecated use set_property() instead
  248. */
  249. static inline int set_option_variant(mpv_handle *ctx, const QString &name,
  250. const QVariant &v)
  251. {
  252. node_builder node(v);
  253. return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
  254. }
  255. /**
  256. * mpv_command_node() equivalent. Returns QVariant() on error (and
  257. * unfortunately, the same on success).
  258. *
  259. * @deprecated use command() instead
  260. */
  261. static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
  262. {
  263. node_builder node(args);
  264. mpv_node res;
  265. if (mpv_command_node(ctx, node.node(), &res) < 0)
  266. return QVariant();
  267. node_autofree f(&res);
  268. return node_to_variant(&res);
  269. }
  270. #endif
  271. /**
  272. * This is used to return error codes wrapped in QVariant for functions which
  273. * return QVariant.
  274. *
  275. * You can use get_error() or is_error() to extract the error status from a
  276. * QVariant value.
  277. */
  278. struct ErrorReturn
  279. {
  280. /**
  281. * enum mpv_error value (or a value outside of it if ABI was extended)
  282. */
  283. int error;
  284. ErrorReturn() : error(0) {}
  285. explicit ErrorReturn(int err) : error(err) {}
  286. };
  287. /**
  288. * Return the mpv error code packed into a QVariant, or 0 (success) if it's not
  289. * an error value.
  290. *
  291. * @return error code (<0) or success (>=0)
  292. */
  293. static inline int get_error(const QVariant &v)
  294. {
  295. if (!v.canConvert<ErrorReturn>())
  296. return 0;
  297. return v.value<ErrorReturn>().error;
  298. }
  299. /**
  300. * Return whether the QVariant carries a mpv error code.
  301. */
  302. static inline bool is_error(const QVariant &v)
  303. {
  304. return get_error(v) < 0;
  305. }
  306. /**
  307. * Return the given property as mpv_node converted to QVariant, or QVariant()
  308. * on error.
  309. *
  310. * @param name the property name
  311. * @return the property value, or an ErrorReturn with the error code
  312. */
  313. static inline QVariant get_property(mpv_handle *ctx, const QString &name)
  314. {
  315. mpv_node node;
  316. int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
  317. if (err < 0)
  318. return QVariant::fromValue(ErrorReturn(err));
  319. node_autofree f(&node);
  320. return node_to_variant(&node);
  321. }
  322. /**
  323. * Set the given property as mpv_node converted from the QVariant argument.
  324. *
  325. * @return mpv error code (<0 on error, >= 0 on success)
  326. */
  327. static inline int set_property(mpv_handle *ctx, const QString &name,
  328. const QVariant &v)
  329. {
  330. node_builder node(v);
  331. return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
  332. }
  333. /**
  334. * mpv_command_node() equivalent.
  335. *
  336. * @param args command arguments, with args[0] being the command name as string
  337. * @return the property value, or an ErrorReturn with the error code
  338. */
  339. static inline QVariant command(mpv_handle *ctx, const QVariant &args)
  340. {
  341. node_builder node(args);
  342. mpv_node res;
  343. int err = mpv_command_node(ctx, node.node(), &res);
  344. if (err < 0)
  345. return QVariant::fromValue(ErrorReturn(err));
  346. node_autofree f(&res);
  347. return node_to_variant(&res);
  348. }
  349. }
  350. }
  351. Q_DECLARE_METATYPE(mpv::qt::ErrorReturn)
  352. #endif