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 &params)
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 &params, 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"