File indexing completed on 2024-05-12 16:28:12
0001 // SPDX-FileCopyrightText: 2017 the mpv developers 0002 // 0003 // SPDX-License-Identifier: GPL-2.0-or-later 0004 0005 #ifndef LIBMPV_QTHELPER_H_ 0006 #define LIBMPV_QTHELPER_H_ 0007 0008 #include <mpv/client.h> 0009 0010 #include <cstring> 0011 0012 #include <QHash> 0013 #include <QList> 0014 #include <QMetaType> 0015 #include <QSharedPointer> 0016 #include <QString> 0017 #include <QVariant> 0018 0019 namespace mpv 0020 { 0021 namespace qt 0022 { 0023 0024 // Wrapper around mpv_handle. Does refcounting under the hood. 0025 class Handle 0026 { 0027 struct container { 0028 container(mpv_handle *h) 0029 : mpv(h) 0030 { 0031 } 0032 ~container() 0033 { 0034 mpv_terminate_destroy(mpv); 0035 } 0036 mpv_handle *mpv; 0037 }; 0038 QSharedPointer<container> sptr; 0039 0040 public: 0041 // Construct a new Handle from a raw mpv_handle with refcount 1. If the 0042 // last Handle goes out of scope, the mpv_handle will be destroyed with 0043 // mpv_terminate_destroy(). 0044 // Never destroy the mpv_handle manually when using this wrapper. You 0045 // will create dangling pointers. Just let the wrapper take care of 0046 // destroying the mpv_handle. 0047 // Never create multiple wrappers from the same raw mpv_handle; copy the 0048 // wrapper instead (that's what it's for). 0049 static Handle FromRawHandle(mpv_handle *handle) 0050 { 0051 Handle h; 0052 h.sptr = QSharedPointer<container>(new container(handle)); 0053 return h; 0054 } 0055 0056 // Return the raw handle; for use with the libmpv C API. 0057 operator mpv_handle *() const 0058 { 0059 return sptr ? (*sptr).mpv : 0; 0060 } 0061 }; 0062 0063 static inline QVariant node_to_variant(const mpv_node *node) 0064 { 0065 switch (node->format) { 0066 case MPV_FORMAT_STRING: 0067 return QVariant(QString::fromUtf8(node->u.string)); 0068 case MPV_FORMAT_FLAG: 0069 return QVariant(static_cast<bool>(node->u.flag)); 0070 case MPV_FORMAT_INT64: 0071 return QVariant(static_cast<qlonglong>(node->u.int64)); 0072 case MPV_FORMAT_DOUBLE: 0073 return QVariant(node->u.double_); 0074 case MPV_FORMAT_NODE_ARRAY: { 0075 mpv_node_list *list = node->u.list; 0076 QVariantList qlist; 0077 for (int n = 0; n < list->num; n++) 0078 qlist.append(node_to_variant(&list->values[n])); 0079 return QVariant(qlist); 0080 } 0081 case MPV_FORMAT_NODE_MAP: { 0082 mpv_node_list *list = node->u.list; 0083 QVariantMap qmap; 0084 for (int n = 0; n < list->num; n++) { 0085 qmap.insert(QString::fromUtf8(list->keys[n]), node_to_variant(&list->values[n])); 0086 } 0087 return QVariant(qmap); 0088 } 0089 default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) 0090 return QVariant(); 0091 } 0092 } 0093 0094 struct node_builder { 0095 node_builder(const QVariant &v) 0096 { 0097 set(&node_, v); 0098 } 0099 ~node_builder() 0100 { 0101 free_node(&node_); 0102 } 0103 mpv_node *node() 0104 { 0105 return &node_; 0106 } 0107 0108 private: 0109 Q_DISABLE_COPY(node_builder) 0110 mpv_node node_; 0111 mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) 0112 { 0113 dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; 0114 mpv_node_list *list = new mpv_node_list(); 0115 dst->u.list = list; 0116 if (!list) 0117 goto err; 0118 list->values = new mpv_node[num](); 0119 if (!list->values) 0120 goto err; 0121 if (is_map) { 0122 list->keys = new char *[num](); 0123 if (!list->keys) 0124 goto err; 0125 } 0126 return list; 0127 err: 0128 free_node(dst); 0129 return NULL; 0130 } 0131 char *dup_qstring(const QString &s) 0132 { 0133 QByteArray b = s.toUtf8(); 0134 char *r = new char[b.size() + 1]; 0135 if (r) 0136 std::memcpy(r, b.data(), b.size() + 1); 0137 return r; 0138 } 0139 bool test_type(const QVariant &v, QMetaType::Type t) 0140 { 0141 // The Qt docs say: "Although this function is declared as returning 0142 // "QVariant::Type(obsolete), the return value should be interpreted 0143 // as QMetaType::Type." 0144 // So a cast really seems to be needed to avoid warnings (urgh). 0145 return static_cast<int>(v.type()) == static_cast<int>(t); 0146 } 0147 void set(mpv_node *dst, const QVariant &src) 0148 { 0149 if (test_type(src, QMetaType::QString)) { 0150 dst->format = MPV_FORMAT_STRING; 0151 dst->u.string = dup_qstring(src.toString()); 0152 if (!dst->u.string) 0153 goto fail; 0154 } else if (test_type(src, QMetaType::Bool)) { 0155 dst->format = MPV_FORMAT_FLAG; 0156 dst->u.flag = src.toBool() ? 1 : 0; 0157 } else if (test_type(src, QMetaType::Int) || test_type(src, QMetaType::LongLong) || test_type(src, QMetaType::UInt) 0158 || test_type(src, QMetaType::ULongLong)) { 0159 dst->format = MPV_FORMAT_INT64; 0160 dst->u.int64 = src.toLongLong(); 0161 } else if (test_type(src, QMetaType::Double)) { 0162 dst->format = MPV_FORMAT_DOUBLE; 0163 dst->u.double_ = src.toDouble(); 0164 } else if (src.canConvert<QVariantList>()) { 0165 QVariantList qlist = src.toList(); 0166 mpv_node_list *list = create_list(dst, false, qlist.size()); 0167 if (!list) 0168 goto fail; 0169 list->num = qlist.size(); 0170 for (int n = 0; n < qlist.size(); n++) 0171 set(&list->values[n], qlist[n]); 0172 } else if (src.canConvert<QVariantMap>()) { 0173 QVariantMap qmap = src.toMap(); 0174 mpv_node_list *list = create_list(dst, true, qmap.size()); 0175 if (!list) 0176 goto fail; 0177 list->num = qmap.size(); 0178 for (int n = 0; n < qmap.size(); n++) { 0179 list->keys[n] = dup_qstring(qmap.keys()[n]); 0180 if (!list->keys[n]) { 0181 free_node(dst); 0182 goto fail; 0183 } 0184 set(&list->values[n], qmap.values()[n]); 0185 } 0186 } else { 0187 goto fail; 0188 } 0189 return; 0190 fail: 0191 dst->format = MPV_FORMAT_NONE; 0192 } 0193 void free_node(mpv_node *dst) 0194 { 0195 switch (dst->format) { 0196 case MPV_FORMAT_STRING: 0197 delete[] dst->u.string; 0198 break; 0199 case MPV_FORMAT_NODE_ARRAY: 0200 case MPV_FORMAT_NODE_MAP: { 0201 mpv_node_list *list = dst->u.list; 0202 if (list) { 0203 for (int n = 0; n < list->num; n++) { 0204 if (list->keys) 0205 delete[] list->keys[n]; 0206 if (list->values) 0207 free_node(&list->values[n]); 0208 } 0209 delete[] list->keys; 0210 delete[] list->values; 0211 } 0212 delete list; 0213 break; 0214 } 0215 default:; 0216 } 0217 dst->format = MPV_FORMAT_NONE; 0218 } 0219 }; 0220 0221 /** 0222 * RAII wrapper that calls mpv_free_node_contents() on the pointer. 0223 */ 0224 struct node_autofree { 0225 mpv_node *ptr; 0226 node_autofree(mpv_node *a_ptr) 0227 : ptr(a_ptr) 0228 { 0229 } 0230 ~node_autofree() 0231 { 0232 mpv_free_node_contents(ptr); 0233 } 0234 }; 0235 0236 /** 0237 * Return the given property as mpv_node converted to QVariant, or QVariant() 0238 * on error. 0239 * 0240 * @deprecated use get_property() instead 0241 * 0242 * @param name the property name 0243 */ 0244 static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name) 0245 { 0246 mpv_node node; 0247 if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) 0248 return QVariant(); 0249 node_autofree f(&node); 0250 return node_to_variant(&node); 0251 } 0252 0253 /** 0254 * Set the given property as mpv_node converted from the QVariant argument. 0255 * @deprecated use set_property() instead 0256 */ 0257 static inline int set_property_variant(mpv_handle *ctx, const QString &name, const QVariant &v) 0258 { 0259 node_builder node(v); 0260 return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); 0261 } 0262 0263 /** 0264 * Set the given option as mpv_node converted from the QVariant argument. 0265 * 0266 * @deprecated use set_property() instead 0267 */ 0268 static inline int set_option_variant(mpv_handle *ctx, const QString &name, const QVariant &v) 0269 { 0270 node_builder node(v); 0271 return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); 0272 } 0273 0274 /** 0275 * mpv_command_node() equivalent. Returns QVariant() on error (and 0276 * unfortunately, the same on success). 0277 * 0278 * @deprecated use command() instead 0279 */ 0280 static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args) 0281 { 0282 node_builder node(args); 0283 mpv_node res; 0284 if (mpv_command_node(ctx, node.node(), &res) < 0) 0285 return QVariant(); 0286 node_autofree f(&res); 0287 return node_to_variant(&res); 0288 } 0289 0290 /** 0291 * This is used to return error codes wrapped in QVariant for functions which 0292 * return QVariant. 0293 * 0294 * You can use get_error() or is_error() to extract the error status from a 0295 * QVariant value. 0296 */ 0297 struct ErrorReturn { 0298 /** 0299 * enum mpv_error value (or a value outside of it if ABI was extended) 0300 */ 0301 int error; 0302 0303 ErrorReturn() 0304 : error(0) 0305 { 0306 } 0307 explicit ErrorReturn(int err) 0308 : error(err) 0309 { 0310 } 0311 }; 0312 0313 /** 0314 * Return the mpv error code packed into a QVariant, or 0 (success) if it's not 0315 * an error value. 0316 * 0317 * @return error code (<0) or success (>=0) 0318 */ 0319 static inline int get_error(const QVariant &v) 0320 { 0321 if (!v.canConvert<ErrorReturn>()) 0322 return 0; 0323 return v.value<ErrorReturn>().error; 0324 } 0325 0326 /** 0327 * Return whether the QVariant carries a mpv error code. 0328 */ 0329 static inline bool is_error(const QVariant &v) 0330 { 0331 return get_error(v) < 0; 0332 } 0333 0334 /** 0335 * Return the given property as mpv_node converted to QVariant, or QVariant() 0336 * on error. 0337 * 0338 * @param name the property name 0339 * @return the property value, or an ErrorReturn with the error code 0340 */ 0341 static inline QVariant get_property(mpv_handle *ctx, const QString &name) 0342 { 0343 mpv_node node; 0344 int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node); 0345 if (err < 0) 0346 return QVariant::fromValue(ErrorReturn(err)); 0347 node_autofree f(&node); 0348 return node_to_variant(&node); 0349 } 0350 0351 /** 0352 * Set the given property as mpv_node converted from the QVariant argument. 0353 * 0354 * @return mpv error code (<0 on error, >= 0 on success) 0355 */ 0356 static inline int set_property(mpv_handle *ctx, const QString &name, const QVariant &v) 0357 { 0358 node_builder node(v); 0359 return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); 0360 } 0361 0362 /** 0363 * mpv_command_node() equivalent. 0364 * 0365 * @param args command arguments, with args[0] being the command name as string 0366 * @return the property value, or an ErrorReturn with the error code 0367 */ 0368 static inline QVariant command(mpv_handle *ctx, const QVariant &args) 0369 { 0370 node_builder node(args); 0371 mpv_node res; 0372 int err = mpv_command_node(ctx, node.node(), &res); 0373 if (err < 0) 0374 return QVariant::fromValue(ErrorReturn(err)); 0375 node_autofree f(&res); 0376 return node_to_variant(&res); 0377 } 0378 0379 } 0380 } 0381 0382 Q_DECLARE_METATYPE(mpv::qt::ErrorReturn) 0383 0384 #endif