File indexing completed on 2024-05-19 04:45:38
0001 /* 0002 * SPDX-FileCopyrightText: 2023 George Florea Bănuș <georgefb899@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "mpvcontroller.h" 0008 #include "mpvcontroller_p.h" 0009 0010 #include <QVariant> 0011 #include <QDebug> 0012 0013 #include <clocale> 0014 0015 MpvControllerPrivate::MpvControllerPrivate(MpvController *q) 0016 : q_ptr(q) 0017 { 0018 } 0019 0020 mpv_node_list *MpvControllerPrivate::createList(mpv_node *dst, bool is_map, int num) 0021 { 0022 dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; 0023 mpv_node_list *list = new mpv_node_list(); 0024 dst->u.list = list; 0025 if (!list) { 0026 freeNode(dst); 0027 return nullptr; 0028 } 0029 list->values = new mpv_node[num](); 0030 if (!list->values) { 0031 freeNode(dst); 0032 return nullptr; 0033 } 0034 if (is_map) { 0035 list->keys = new char *[num](); 0036 if (!list->keys) { 0037 freeNode(dst); 0038 return nullptr; 0039 } 0040 } 0041 return list; 0042 } 0043 0044 void MpvControllerPrivate::setNode(mpv_node *dst, const QVariant &src) 0045 { 0046 if (testType(src, QMetaType::QString)) { 0047 dst->format = MPV_FORMAT_STRING; 0048 dst->u.string = qstrdup(src.toString().toUtf8().data()); 0049 if (!dst->u.string) { 0050 dst->format = MPV_FORMAT_NONE; 0051 } 0052 } else if (testType(src, QMetaType::Bool)) { 0053 dst->format = MPV_FORMAT_FLAG; 0054 dst->u.flag = src.toBool() ? 1 : 0; 0055 } else if (testType(src, QMetaType::Int) || testType(src, QMetaType::LongLong) || testType(src, QMetaType::UInt) || testType(src, QMetaType::ULongLong)) { 0056 dst->format = MPV_FORMAT_INT64; 0057 dst->u.int64 = src.toLongLong(); 0058 } else if (testType(src, QMetaType::Double)) { 0059 dst->format = MPV_FORMAT_DOUBLE; 0060 dst->u.double_ = src.toDouble(); 0061 } else if (src.canConvert<QVariantList>()) { 0062 QVariantList qlist = src.toList(); 0063 mpv_node_list *list = createList(dst, false, qlist.size()); 0064 if (!list) { 0065 dst->format = MPV_FORMAT_NONE; 0066 return; 0067 } 0068 list->num = qlist.size(); 0069 for (int n = 0; n < qlist.size(); ++n) { 0070 setNode(&list->values[n], qlist[n]); 0071 } 0072 } else if (src.canConvert<QVariantMap>()) { 0073 QVariantMap qmap = src.toMap(); 0074 mpv_node_list *list = createList(dst, true, qmap.size()); 0075 if (!list) { 0076 dst->format = MPV_FORMAT_NONE; 0077 return; 0078 } 0079 list->num = qmap.size(); 0080 int n = 0; 0081 for (auto it = qmap.constKeyValueBegin(); it != qmap.constKeyValueEnd(); ++it) { 0082 list->keys[n] = qstrdup(it.operator*().first.toUtf8().data()); 0083 if (!list->keys[n]) { 0084 freeNode(dst); 0085 dst->format = MPV_FORMAT_NONE; 0086 return; 0087 } 0088 setNode(&list->values[n], it.operator*().second); 0089 ++n; 0090 } 0091 } else { 0092 dst->format = MPV_FORMAT_NONE; 0093 } 0094 return; 0095 } 0096 0097 bool MpvControllerPrivate::testType(const QVariant &v, QMetaType::Type t) 0098 { 0099 // The Qt docs say: "Although this function is declared as returning 0100 // QVariant::Type(obsolete), the return value should be interpreted 0101 // as QMetaType::Type." So a cast is needed to avoid warnings. 0102 return v.typeId() == t; 0103 } 0104 0105 void MpvControllerPrivate::freeNode(mpv_node *dst) 0106 { 0107 switch (dst->format) { 0108 case MPV_FORMAT_STRING: 0109 delete[] dst->u.string; 0110 break; 0111 case MPV_FORMAT_NODE_ARRAY: 0112 case MPV_FORMAT_NODE_MAP: { 0113 mpv_node_list *list = dst->u.list; 0114 if (list) { 0115 for (int n = 0; n < list->num; ++n) { 0116 if (list->keys) { 0117 delete[] list->keys[n]; 0118 } 0119 if (list->values) { 0120 freeNode(&list->values[n]); 0121 } 0122 } 0123 delete[] list->keys; 0124 delete[] list->values; 0125 } 0126 delete list; 0127 break; 0128 } 0129 default:; 0130 } 0131 dst->format = MPV_FORMAT_NONE; 0132 } 0133 0134 inline QVariant MpvControllerPrivate::nodeToVariant(const mpv_node *node) 0135 { 0136 switch (node->format) { 0137 case MPV_FORMAT_STRING: 0138 return QVariant(QString::fromUtf8(node->u.string)); 0139 case MPV_FORMAT_FLAG: 0140 return QVariant(static_cast<bool>(node->u.flag)); 0141 case MPV_FORMAT_INT64: 0142 return QVariant(static_cast<qlonglong>(node->u.int64)); 0143 case MPV_FORMAT_DOUBLE: 0144 return QVariant(node->u.double_); 0145 case MPV_FORMAT_NODE_ARRAY: { 0146 mpv_node_list *list = node->u.list; 0147 QVariantList qlist; 0148 for (int n = 0; n < list->num; ++n) { 0149 qlist.append(nodeToVariant(&list->values[n])); 0150 } 0151 return QVariant(qlist); 0152 } 0153 case MPV_FORMAT_NODE_MAP: { 0154 mpv_node_list *list = node->u.list; 0155 QVariantMap qmap; 0156 for (int n = 0; n < list->num; ++n) { 0157 qmap.insert(QString::fromUtf8(list->keys[n]), nodeToVariant(&list->values[n])); 0158 } 0159 return QVariant(qmap); 0160 } 0161 default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) 0162 return QVariant(); 0163 } 0164 } 0165 0166 MpvController::MpvController(QObject *parent) 0167 : QObject(parent) 0168 { 0169 } 0170 0171 void MpvController::init() 0172 { 0173 d_ptr = std::make_unique<MpvControllerPrivate>(this); 0174 // Qt sets the locale in the QGuiApplication constructor, but libmpv 0175 // requires the LC_NUMERIC category to be set to "C", so change it back. 0176 std::setlocale(LC_NUMERIC, "C"); 0177 0178 d_ptr->m_mpv = mpv_create(); 0179 if (!d_ptr->m_mpv) { 0180 qFatal("could not create mpv context"); 0181 } 0182 if (mpv_initialize(d_ptr->m_mpv) < 0) { 0183 qFatal("could not initialize mpv context"); 0184 } 0185 mpv_set_wakeup_callback(d_ptr->m_mpv, MpvController::mpvEvents, this); 0186 } 0187 0188 void MpvController::mpvEvents(void *ctx) 0189 { 0190 QMetaObject::invokeMethod(static_cast<MpvController *>(ctx), &MpvController::eventHandler, Qt::QueuedConnection); 0191 } 0192 0193 void MpvController::eventHandler() 0194 { 0195 while (d_ptr->m_mpv) { 0196 mpv_event *event = mpv_wait_event(d_ptr->m_mpv, 0); 0197 if (event->event_id == MPV_EVENT_NONE) { 0198 break; 0199 } 0200 switch (event->event_id) { 0201 case MPV_EVENT_START_FILE: { 0202 Q_EMIT fileStarted(); 0203 break; 0204 } 0205 0206 case MPV_EVENT_FILE_LOADED: { 0207 Q_EMIT fileLoaded(); 0208 break; 0209 } 0210 0211 case MPV_EVENT_END_FILE: { 0212 auto prop = static_cast<mpv_event_end_file *>(event->data); 0213 if (prop->reason == MPV_END_FILE_REASON_EOF) { 0214 Q_EMIT endFile(QStringLiteral("eof")); 0215 } else if (prop->reason == MPV_END_FILE_REASON_ERROR) { 0216 Q_EMIT endFile(QStringLiteral("error")); 0217 } 0218 break; 0219 } 0220 0221 case MPV_EVENT_VIDEO_RECONFIG: { 0222 Q_EMIT videoReconfig(); 0223 break; 0224 } 0225 0226 case MPV_EVENT_GET_PROPERTY_REPLY: { 0227 mpv_event_property *prop = static_cast<mpv_event_property *>(event->data); 0228 auto data = d_ptr->nodeToVariant(reinterpret_cast<mpv_node *>(prop->data)); 0229 Q_EMIT asyncReply(data, {*event}); 0230 break; 0231 } 0232 0233 case MPV_EVENT_SET_PROPERTY_REPLY: { 0234 Q_EMIT asyncReply(QVariant(), {*event}); 0235 break; 0236 } 0237 0238 case MPV_EVENT_COMMAND_REPLY: { 0239 mpv_event_property *prop = static_cast<mpv_event_property *>(event->data); 0240 auto data = d_ptr->nodeToVariant(reinterpret_cast<mpv_node *>(prop)); 0241 Q_EMIT asyncReply(data, {*event}); 0242 break; 0243 } 0244 0245 case MPV_EVENT_PROPERTY_CHANGE: { 0246 mpv_event_property *prop = static_cast<mpv_event_property *>(event->data); 0247 QVariant data; 0248 switch (prop->format) { 0249 case MPV_FORMAT_DOUBLE: 0250 data = *reinterpret_cast<double *>(prop->data); 0251 break; 0252 case MPV_FORMAT_STRING: 0253 data = QString::fromStdString(*reinterpret_cast<char **>(prop->data)); 0254 break; 0255 case MPV_FORMAT_INT64: 0256 data = qlonglong(*reinterpret_cast<int64_t *>(prop->data)); 0257 break; 0258 case MPV_FORMAT_FLAG: 0259 data = *reinterpret_cast<bool *>(prop->data); 0260 break; 0261 case MPV_FORMAT_NODE: 0262 data = d_ptr->nodeToVariant(reinterpret_cast<mpv_node *>(prop->data)); 0263 break; 0264 case MPV_FORMAT_NONE: 0265 case MPV_FORMAT_OSD_STRING: 0266 case MPV_FORMAT_NODE_ARRAY: 0267 case MPV_FORMAT_NODE_MAP: 0268 case MPV_FORMAT_BYTE_ARRAY: 0269 break; 0270 } 0271 Q_EMIT propertyChanged(QString::fromStdString(prop->name), data); 0272 break; 0273 } 0274 case MPV_EVENT_NONE: 0275 case MPV_EVENT_SHUTDOWN: 0276 case MPV_EVENT_LOG_MESSAGE: 0277 case MPV_EVENT_CLIENT_MESSAGE: 0278 case MPV_EVENT_AUDIO_RECONFIG: 0279 case MPV_EVENT_SEEK: 0280 case MPV_EVENT_PLAYBACK_RESTART: 0281 case MPV_EVENT_QUEUE_OVERFLOW: 0282 case MPV_EVENT_HOOK: 0283 #if MPV_ENABLE_DEPRECATED 0284 case MPV_EVENT_IDLE: 0285 case MPV_EVENT_TICK: 0286 #endif 0287 break; 0288 } 0289 } 0290 } 0291 0292 mpv_handle *MpvController::mpv() const 0293 { 0294 return d_ptr->m_mpv; 0295 } 0296 0297 void MpvController::observeProperty(const QString &property, mpv_format format) 0298 { 0299 mpv_observe_property(mpv(), 0, property.toUtf8().data(), format); 0300 } 0301 0302 int MpvController::setProperty(const QString &property, const QVariant &value) 0303 { 0304 mpv_node node; 0305 d_ptr->setNode(&node, value); 0306 return mpv_set_property(d_ptr->m_mpv, property.toUtf8().constData(), MPV_FORMAT_NODE, &node); 0307 } 0308 0309 int MpvController::setPropertyAsync(const QString &property, const QVariant &value, int id) 0310 { 0311 mpv_node node; 0312 d_ptr->setNode(&node, value); 0313 int err = mpv_set_property_async(d_ptr->m_mpv, id, property.toUtf8().constData(), MPV_FORMAT_NODE, &node); 0314 return err; 0315 } 0316 0317 QVariant MpvController::getProperty(const QString &property) 0318 { 0319 mpv_node node; 0320 int err = mpv_get_property(d_ptr->m_mpv, property.toUtf8().constData(), MPV_FORMAT_NODE, &node); 0321 if (err < 0) { 0322 return QVariant::fromValue(ErrorReturn(err)); 0323 } 0324 node_autofree f(&node); 0325 return d_ptr->nodeToVariant(&node); 0326 } 0327 0328 int MpvController::getPropertyAsync(const QString &property, int id) 0329 { 0330 int err = mpv_get_property_async(d_ptr->m_mpv, id, property.toUtf8().constData(), MPV_FORMAT_NODE); 0331 return err; 0332 } 0333 0334 QVariant MpvController::command(const QVariant ¶ms) 0335 { 0336 mpv_node node; 0337 d_ptr->setNode(&node, params); 0338 mpv_node result; 0339 int err = mpv_command_node(d_ptr->m_mpv, &node, &result); 0340 if (err < 0) { 0341 qDebug() << getError(err) << params; 0342 return QVariant::fromValue(ErrorReturn(err)); 0343 } 0344 node_autofree f(&result); 0345 return d_ptr->nodeToVariant(&result); 0346 } 0347 0348 int MpvController::commandAsync(const QVariant ¶ms, int id) 0349 { 0350 mpv_node node; 0351 d_ptr->setNode(&node, params); 0352 return mpv_command_node_async(d_ptr->m_mpv, id, &node); 0353 } 0354 0355 QString MpvController::getError(int error) 0356 { 0357 ErrorReturn err{error}; 0358 switch (err.error) { 0359 case MPV_ERROR_SUCCESS: 0360 return QStringLiteral("MPV_ERROR_SUCCESS"); 0361 case MPV_ERROR_EVENT_QUEUE_FULL: 0362 return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL"); 0363 case MPV_ERROR_NOMEM: 0364 return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL"); 0365 case MPV_ERROR_UNINITIALIZED: 0366 return QStringLiteral("MPV_ERROR_UNINITIALIZED"); 0367 case MPV_ERROR_INVALID_PARAMETER: 0368 return QStringLiteral("MPV_ERROR_INVALID_PARAMETER"); 0369 case MPV_ERROR_OPTION_NOT_FOUND: 0370 return QStringLiteral("MPV_ERROR_OPTION_NOT_FOUND"); 0371 case MPV_ERROR_OPTION_FORMAT: 0372 return QStringLiteral("MPV_ERROR_OPTION_FORMAT"); 0373 case MPV_ERROR_OPTION_ERROR: 0374 return QStringLiteral("MPV_ERROR_OPTION_ERROR"); 0375 case MPV_ERROR_PROPERTY_NOT_FOUND: 0376 return QStringLiteral("MPV_ERROR_PROPERTY_NOT_FOUND"); 0377 case MPV_ERROR_PROPERTY_FORMAT: 0378 return QStringLiteral("MPV_ERROR_PROPERTY_FORMAT"); 0379 case MPV_ERROR_PROPERTY_UNAVAILABLE: 0380 return QStringLiteral("MPV_ERROR_PROPERTY_UNAVAILABLE"); 0381 case MPV_ERROR_PROPERTY_ERROR: 0382 return QStringLiteral("MPV_ERROR_PROPERTY_ERROR"); 0383 case MPV_ERROR_COMMAND: 0384 return QStringLiteral("MPV_ERROR_COMMAND"); 0385 case MPV_ERROR_LOADING_FAILED: 0386 return QStringLiteral("MPV_ERROR_LOADING_FAILED"); 0387 case MPV_ERROR_AO_INIT_FAILED: 0388 return QStringLiteral("MPV_ERROR_AO_INIT_FAILED"); 0389 case MPV_ERROR_VO_INIT_FAILED: 0390 return QStringLiteral("MPV_ERROR_VO_INIT_FAILED"); 0391 case MPV_ERROR_NOTHING_TO_PLAY: 0392 return QStringLiteral("MPV_ERROR_NOTHING_TO_PLAY"); 0393 case MPV_ERROR_UNKNOWN_FORMAT: 0394 return QStringLiteral("MPV_ERROR_UNKNOWN_FORMAT"); 0395 case MPV_ERROR_UNSUPPORTED: 0396 return QStringLiteral("MPV_ERROR_UNSUPPORTED"); 0397 case MPV_ERROR_NOT_IMPLEMENTED: 0398 return QStringLiteral("MPV_ERROR_NOT_IMPLEMENTED"); 0399 case MPV_ERROR_GENERIC: 0400 return QStringLiteral("MPV_ERROR_GENERIC"); 0401 } 0402 return QString(); 0403 } 0404 0405 #include "moc_mpvcontroller.cpp"