File indexing completed on 2024-12-15 04:23:41
0001 /* 0002 * SPDX-FileCopyrightText: 2020 George Florea Bănuș <georgefb899@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 //#include "_debug.h" 0008 #include "mpvobject.h" 0009 //#include "application.h" 0010 //#include "playbacksettings.h" 0011 //#include "playlistitem.h" 0012 #include "track.h" 0013 0014 #include <QDir> 0015 #include <QJsonArray> 0016 #include <QJsonDocument> 0017 #include <QJsonObject> 0018 #include <QObject> 0019 #include <QOpenGLContext> 0020 #include <QOpenGLFramebufferObject> 0021 #include <QProcess> 0022 #include <QQuickWindow> 0023 #include <QStandardPaths> 0024 #include <QtGlobal> 0025 #include <cstring> 0026 0027 void on_mpv_redraw(void *ctx) 0028 { 0029 QMetaObject::invokeMethod(static_cast<MpvObject*>(ctx), "update", Qt::QueuedConnection); 0030 } 0031 0032 static void *get_proc_address_mpv(void *ctx, const char *name) 0033 { 0034 Q_UNUSED(ctx) 0035 0036 QOpenGLContext *glctx = QOpenGLContext::currentContext(); 0037 if (!glctx) return nullptr; 0038 0039 return reinterpret_cast<void *>(glctx->getProcAddress(QByteArray(name))); 0040 } 0041 0042 MpvRenderer::MpvRenderer(MpvObject *new_obj) 0043 : obj{new_obj} 0044 {} 0045 0046 void MpvRenderer::render() 0047 { 0048 obj->window()->resetOpenGLState(); 0049 0050 QOpenGLFramebufferObject *fbo = framebufferObject(); 0051 mpv_opengl_fbo mpfbo; 0052 mpfbo.fbo = static_cast<int>(fbo->handle()); 0053 mpfbo.w = fbo->width(); 0054 mpfbo.h = fbo->height(); 0055 mpfbo.internal_format = 0; 0056 0057 mpv_render_param params[] = { 0058 // Specify the default framebuffer (0) as target. This will 0059 // render onto the entire screen. If you want to show the video 0060 // in a smaller rectangle or apply fancy transformations, you'll 0061 // need to render into a separate FBO and draw it manually. 0062 {MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo}, 0063 {MPV_RENDER_PARAM_INVALID, nullptr} 0064 }; 0065 // See render_gl.h on what OpenGL environment mpv expects, and 0066 // other API details. 0067 mpv_render_context_render(obj->mpv_gl, params); 0068 0069 obj->window()->resetOpenGLState(); 0070 } 0071 0072 QOpenGLFramebufferObject * MpvRenderer::createFramebufferObject(const QSize &size) 0073 { 0074 // init mpv_gl: 0075 if (!obj->mpv_gl) 0076 { 0077 mpv_opengl_init_params gl_init_params; 0078 gl_init_params.get_proc_address = get_proc_address_mpv; 0079 gl_init_params.get_proc_address_ctx = nullptr; 0080 mpv_render_param params[]{ 0081 {MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_OPENGL)}, 0082 {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params}, 0083 {MPV_RENDER_PARAM_INVALID, nullptr} 0084 }; 0085 0086 if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0) 0087 throw std::runtime_error("failed to initialize mpv GL context"); 0088 mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj); 0089 Q_EMIT obj->ready(); 0090 } 0091 0092 return QQuickFramebufferObject::Renderer::createFramebufferObject(size); 0093 } 0094 0095 MpvObject::MpvObject(QQuickItem * parent) 0096 : QQuickFramebufferObject(parent) 0097 , mpv{mpv_create()} 0098 , mpv_gl(nullptr) 0099 , m_audioTracksModel(new TracksModel) 0100 , m_subtitleTracksModel(new TracksModel) 0101 // , m_playlistModel(new PlayListModel) 0102 { 0103 if (!mpv) 0104 throw std::runtime_error("could not create mpv context"); 0105 0106 // setProperty("terminal", "yes"); 0107 // setProperty("msg-level", "all=v"); 0108 0109 if (m_hardwareDecoding) { 0110 setProperty("hwdec", "yes"); 0111 } else { 0112 setProperty("hwdec", "no"); 0113 } 0114 0115 setProperty("screenshot-template", "%x/screenshots/%n"); 0116 setProperty("sub-auto", "exact"); 0117 0118 mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING); 0119 mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); 0120 mpv_observe_property(mpv, 0, "time-remaining", MPV_FORMAT_DOUBLE); 0121 mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); 0122 mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_INT64); 0123 mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_FLAG); 0124 mpv_observe_property(mpv, 0, "chapter", MPV_FORMAT_INT64); 0125 mpv_observe_property(mpv, 0, "aid", MPV_FORMAT_INT64); 0126 mpv_observe_property(mpv, 0, "sid", MPV_FORMAT_INT64); 0127 mpv_observe_property(mpv, 0, "secondary-sid", MPV_FORMAT_INT64); 0128 mpv_observe_property(mpv, 0, "contrast", MPV_FORMAT_INT64); 0129 mpv_observe_property(mpv, 0, "brightness", MPV_FORMAT_INT64); 0130 mpv_observe_property(mpv, 0, "gamma", MPV_FORMAT_INT64); 0131 mpv_observe_property(mpv, 0, "saturation", MPV_FORMAT_INT64); 0132 0133 QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); 0134 QString watchLaterPath = configPath.append("/georgefb/watch-later"); 0135 setProperty("watch-later-directory", watchLaterPath); 0136 QDir watchLaterDir(watchLaterPath); 0137 if (!watchLaterDir.exists()) { 0138 QDir().mkdir(watchLaterPath); 0139 } 0140 0141 if (mpv_initialize(mpv) < 0) 0142 throw std::runtime_error("could not initialize mpv context"); 0143 0144 mpv_set_wakeup_callback(mpv, MpvObject::mpvEvents, this); 0145 0146 connect(this, &MpvObject::fileLoaded, 0147 this, &MpvObject::loadTracks); 0148 0149 connect(this, &MpvObject::positionChanged, this, [this]() { 0150 int pos = getProperty("time-pos").toInt(); 0151 double duration = getProperty("duration").toDouble(); 0152 if (!m_secondsWatched.contains(pos)) { 0153 m_secondsWatched << pos; 0154 setWatchPercentage(m_secondsWatched.count() * 100 / duration); 0155 } 0156 }); 0157 0158 connect(this, &MpvObject::paused, [this]() 0159 { 0160 this->setPlaybackState(QMediaPlayer::PausedState); 0161 }); 0162 0163 connect(this, &MpvObject::playing, [this]() 0164 { 0165 this->setPlaybackState(QMediaPlayer::PlayingState); 0166 }); 0167 0168 connect(this, &MpvObject::stopped, [this]() 0169 { 0170 this->setPlaybackState(QMediaPlayer::StoppedState); 0171 }); 0172 } 0173 0174 MpvObject::~MpvObject() 0175 { 0176 // only initialized if something got drawn 0177 if (mpv_gl) { 0178 mpv_render_context_free(mpv_gl); 0179 } 0180 mpv_terminate_destroy(mpv); 0181 } 0182 0183 //PlayListModel *MpvObject::playlistModel() 0184 //{ 0185 // return m_playlistModel; 0186 //} 0187 0188 //void MpvObject::setPlaylistModel(PlayListModel *model) 0189 //{ 0190 // m_playlistModel = model; 0191 //} 0192 0193 QString MpvObject::mediaTitle() 0194 { 0195 return getProperty("media-title").toString(); 0196 } 0197 0198 double MpvObject::position() 0199 { 0200 return getProperty("time-pos").toDouble()*1000; 0201 } 0202 0203 void MpvObject::setPosition(double value) 0204 { 0205 if (value == position()) { 0206 return; 0207 } 0208 setProperty("time-pos", value); 0209 Q_EMIT positionChanged(); 0210 } 0211 0212 double MpvObject::remaining() 0213 { 0214 return getProperty("time-remaining").toDouble(); 0215 } 0216 0217 double MpvObject::duration() 0218 { 0219 return getProperty("duration").toDouble()*1000; 0220 } 0221 0222 int MpvObject::volume() 0223 { 0224 return getProperty("volume").toInt(); 0225 } 0226 0227 void MpvObject::setVolume(int value) 0228 { 0229 if (value == volume()) { 0230 return; 0231 } 0232 setProperty("volume", value); 0233 Q_EMIT volumeChanged(); 0234 } 0235 0236 int MpvObject::chapter() 0237 { 0238 return getProperty("chapter").toInt(); 0239 } 0240 0241 void MpvObject::setChapter(int value) 0242 { 0243 if (value == chapter()) { 0244 return; 0245 } 0246 setProperty("chapter", value); 0247 Q_EMIT chapterChanged(); 0248 } 0249 0250 int MpvObject::audioId() 0251 { 0252 return getProperty("aid").toInt(); 0253 } 0254 0255 void MpvObject::setAudioId(int value) 0256 { 0257 if (value == audioId()) { 0258 return; 0259 } 0260 setProperty("aid", value); 0261 Q_EMIT audioIdChanged(); 0262 } 0263 0264 int MpvObject::subtitleId() 0265 { 0266 return getProperty("sid").toInt(); 0267 } 0268 0269 void MpvObject::setSubtitleId(int value) 0270 { 0271 if (value == subtitleId()) { 0272 return; 0273 } 0274 setProperty("sid", value); 0275 Q_EMIT subtitleIdChanged(); 0276 } 0277 0278 int MpvObject::secondarySubtitleId() 0279 { 0280 return getProperty("secondary-sid").toInt(); 0281 } 0282 0283 void MpvObject::setSecondarySubtitleId(int value) 0284 { 0285 if (value == secondarySubtitleId()) { 0286 return; 0287 } 0288 setProperty("secondary-sid", value); 0289 Q_EMIT secondarySubtitleIdChanged(); 0290 } 0291 0292 int MpvObject::contrast() 0293 { 0294 return getProperty("contrast").toInt(); 0295 } 0296 0297 void MpvObject::setContrast(int value) 0298 { 0299 if (value == contrast()) { 0300 return; 0301 } 0302 setProperty("contrast", value); 0303 Q_EMIT contrastChanged(); 0304 } 0305 0306 int MpvObject::brightness() 0307 { 0308 return getProperty("brightness").toInt(); 0309 } 0310 0311 void MpvObject::setBrightness(int value) 0312 { 0313 if (value == brightness()) { 0314 return; 0315 } 0316 setProperty("brightness", value); 0317 Q_EMIT brightnessChanged(); 0318 } 0319 0320 int MpvObject::gamma() 0321 { 0322 return getProperty("gamma").toInt(); 0323 } 0324 0325 void MpvObject::setGamma(int value) 0326 { 0327 if (value == gamma()) { 0328 return; 0329 } 0330 setProperty("gamma", value); 0331 Q_EMIT gammaChanged(); 0332 } 0333 0334 int MpvObject::saturation() 0335 { 0336 return getProperty("saturation").toInt(); 0337 } 0338 0339 void MpvObject::setSaturation(int value) 0340 { 0341 if (value == saturation()) { 0342 return; 0343 } 0344 setProperty("saturation", value); 0345 Q_EMIT saturationChanged(); 0346 } 0347 0348 double MpvObject::watchPercentage() 0349 { 0350 return m_watchPercentage; 0351 } 0352 0353 void MpvObject::setWatchPercentage(double value) 0354 { 0355 if (m_watchPercentage == value) { 0356 return; 0357 } 0358 m_watchPercentage = value; 0359 Q_EMIT watchPercentageChanged(); 0360 } 0361 0362 bool MpvObject::hwDecoding() 0363 { 0364 if (getProperty("hwdec") == "yes") { 0365 return true; 0366 } else { 0367 return false; 0368 } 0369 } 0370 0371 void MpvObject::setHWDecoding(bool value) 0372 { 0373 if (value) { 0374 setProperty("hwdec", "yes"); 0375 } else { 0376 setProperty("hwdec", "no"); 0377 } 0378 Q_EMIT hwDecodingChanged(); 0379 } 0380 0381 QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const 0382 { 0383 window()->setPersistentOpenGLContext(true); 0384 window()->setPersistentSceneGraph(true); 0385 return new MpvRenderer(const_cast<MpvObject *>(this)); 0386 } 0387 0388 void MpvObject::mpvEvents(void *ctx) 0389 { 0390 QMetaObject::invokeMethod(static_cast<MpvObject*>(ctx), "eventHandler", Qt::QueuedConnection); 0391 } 0392 0393 void MpvObject::eventHandler() 0394 { 0395 while (mpv) { 0396 mpv_event *event = mpv_wait_event(mpv, 0); 0397 if (event->event_id == MPV_EVENT_NONE) { 0398 break; 0399 } 0400 switch (event->event_id) { 0401 0402 case MPV_EVENT_START_FILE: 0403 // clearTrackState(); 0404 // Q_EMIT sourceChanged(); 0405 setStatus(QMediaPlayer::LoadingMedia); 0406 break; 0407 0408 case MPV_EVENT_SEEK: 0409 setStatus(QMediaPlayer::BufferingMedia); 0410 break; 0411 0412 case MPV_EVENT_PLAYBACK_RESTART: { 0413 bool paused = this->getProperty("pause").toBool(); 0414 if (paused) 0415 Q_EMIT this->paused(); 0416 else 0417 Q_EMIT this->playing(); 0418 break; 0419 } 0420 0421 case MPV_EVENT_FILE_LOADED: { 0422 Q_EMIT fileLoaded(); 0423 setStatus(QMediaPlayer::LoadedMedia); 0424 Q_EMIT this->playing(); 0425 break; 0426 } 0427 0428 case MPV_EVENT_END_FILE: { 0429 auto prop = (mpv_event_end_file *)event->data; 0430 if (prop->reason == MPV_END_FILE_REASON_EOF || 0431 prop ->reason == MPV_END_FILE_REASON_ERROR) { 0432 Q_EMIT endOfFile(); 0433 setStatus(QMediaPlayer::EndOfMedia); 0434 Q_EMIT this->stopped(); 0435 } 0436 break; 0437 } 0438 case MPV_EVENT_PROPERTY_CHANGE: { 0439 mpv_event_property *prop = (mpv_event_property *)event->data; 0440 0441 if (strcmp(prop->name, "time-pos") == 0) { 0442 if (prop->format == MPV_FORMAT_DOUBLE) { 0443 Q_EMIT positionChanged(); 0444 } 0445 } else if (strcmp(prop->name, "media-title") == 0) { 0446 if (prop->format == MPV_FORMAT_STRING) { 0447 Q_EMIT mediaTitleChanged(); 0448 } 0449 } else if (strcmp(prop->name, "time-remaining") == 0) { 0450 if (prop->format == MPV_FORMAT_DOUBLE) { 0451 Q_EMIT remainingChanged(); 0452 } 0453 } else if (strcmp(prop->name, "duration") == 0) { 0454 if (prop->format == MPV_FORMAT_DOUBLE) { 0455 Q_EMIT durationChanged(); 0456 } 0457 } else if (strcmp(prop->name, "volume") == 0) { 0458 if (prop->format == MPV_FORMAT_INT64) { 0459 Q_EMIT volumeChanged(); 0460 } 0461 } else if (strcmp(prop->name, "pause") == 0) { 0462 0463 if (prop->format == MPV_FORMAT_FLAG) { 0464 int pause = *(int *)prop->data; 0465 bool paused = pause == 1; 0466 if (paused) 0467 Q_EMIT this->paused(); 0468 else { 0469 if(this->getProperty("core-idle").toBool()) 0470 { 0471 Q_EMIT this->stopped(); 0472 setStatus(QMediaPlayer::NoMedia); 0473 } 0474 else 0475 { 0476 Q_EMIT this->playing(); 0477 Q_EMIT this->stopped(); 0478 setStatus(QMediaPlayer::LoadedMedia); 0479 } 0480 } 0481 } 0482 0483 } else if (strcmp(prop->name, "chapter") == 0) { 0484 if (prop->format == MPV_FORMAT_INT64) { 0485 Q_EMIT chapterChanged(); 0486 } 0487 } else if (strcmp(prop->name, "aid") == 0) { 0488 if (prop->format == MPV_FORMAT_INT64) { 0489 Q_EMIT audioIdChanged(); 0490 } 0491 } else if (strcmp(prop->name, "sid") == 0) { 0492 if (prop->format == MPV_FORMAT_INT64) { 0493 Q_EMIT subtitleIdChanged(); 0494 } else { 0495 Q_EMIT subtitleIdChanged(); 0496 } 0497 } else if (strcmp(prop->name, "secondary-sid") == 0) { 0498 if (prop->format == MPV_FORMAT_INT64) { 0499 Q_EMIT secondarySubtitleIdChanged(); 0500 } else { 0501 Q_EMIT secondarySubtitleIdChanged(); 0502 } 0503 } else if (strcmp(prop->name, "contrast") == 0) { 0504 if (prop->format == MPV_FORMAT_INT64) { 0505 Q_EMIT contrastChanged(); 0506 } 0507 } else if (strcmp(prop->name, "brightness") == 0) { 0508 if (prop->format == MPV_FORMAT_INT64) { 0509 Q_EMIT brightnessChanged(); 0510 } 0511 } else if (strcmp(prop->name, "gamma") == 0) { 0512 if (prop->format == MPV_FORMAT_INT64) { 0513 Q_EMIT gammaChanged(); 0514 } 0515 } else if (strcmp(prop->name, "saturation") == 0) { 0516 if (prop->format == MPV_FORMAT_INT64) { 0517 Q_EMIT saturationChanged(); 0518 } 0519 } 0520 break; 0521 } 0522 0523 case MPV_EVENT_LOG_MESSAGE: { 0524 struct mpv_event_log_message *msg = (struct mpv_event_log_message *)event->data; 0525 qDebug() << "[" << msg->prefix << "] " << msg->level << ": " << msg->text; 0526 0527 if (msg->log_level == MPV_LOG_LEVEL_ERROR) { 0528 // lastErrorString = QString::fromUtf8(msg->text); 0529 Q_EMIT error(QString::fromUtf8(msg->text)); 0530 setStatus(QMediaPlayer::InvalidMedia); 0531 0532 } 0533 0534 break; 0535 } 0536 0537 default: ; 0538 // Ignore uninteresting or unknown events. 0539 } 0540 } 0541 } 0542 0543 void MpvObject::loadTracks() 0544 { 0545 m_subtitleTracks.clear(); 0546 m_audioTracks.clear(); 0547 0548 auto none = new Track(); 0549 none->setId(0); 0550 none->setTitle("None"); 0551 m_subtitleTracks.insert(0, none); 0552 0553 const QList<QVariant> tracks = getProperty("track-list").toList(); 0554 int subIndex = 1; 0555 int audioIndex = 0; 0556 for (const auto &track : tracks) { 0557 const auto t = track.toMap(); 0558 if (track.toMap()["type"] == "sub") { 0559 auto track = new Track(); 0560 track->setCodec(t["codec"].toString()); 0561 track->setType(t["type"].toString()); 0562 track->setDefaut(t["default"].toBool()); 0563 track->setDependent(t["dependent"].toBool()); 0564 track->setForced(t["forced"].toBool()); 0565 track->setId(t["id"].toLongLong()); 0566 track->setSrcId(t["src-id"].toLongLong()); 0567 track->setFfIndex(t["ff-index"].toLongLong()); 0568 track->setLang(t["lang"].toString()); 0569 track->setTitle(t["title"].toString()); 0570 track->setIndex(subIndex); 0571 0572 m_subtitleTracks.insert(subIndex, track); 0573 subIndex++; 0574 } 0575 if (track.toMap()["type"] == "audio") { 0576 auto track = new Track(); 0577 track->setCodec(t["codec"].toString()); 0578 track->setType(t["type"].toString()); 0579 track->setDefaut(t["default"].toBool()); 0580 track->setDependent(t["dependent"].toBool()); 0581 track->setForced(t["forced"].toBool()); 0582 track->setId(t["id"].toLongLong()); 0583 track->setSrcId(t["src-id"].toLongLong()); 0584 track->setFfIndex(t["ff-index"].toLongLong()); 0585 track->setLang(t["lang"].toString()); 0586 track->setTitle(t["title"].toString()); 0587 track->setIndex(audioIndex); 0588 0589 m_audioTracks.insert(audioIndex, track); 0590 audioIndex++; 0591 } 0592 } 0593 m_subtitleTracksModel->setTracks(m_subtitleTracks); 0594 0595 qDebug() << "Audio tracks" << m_audioTracks << m_audioTracks.size(); 0596 0597 m_audioTracksModel->setTracks(m_audioTracks); 0598 0599 Q_EMIT audioTracksModelChanged(); 0600 Q_EMIT subtitleTracksModelChanged(); 0601 } 0602 0603 void MpvObject::play() 0604 { 0605 this->setProperty("pause", false); 0606 } 0607 0608 void MpvObject::stop() 0609 { 0610 this->command(QStringList () << "stop" << ""); 0611 Q_EMIT this->stopped(); 0612 } 0613 0614 void MpvObject::pause() 0615 { 0616 this->setProperty("pause", true); 0617 } 0618 0619 void MpvObject::seek(const double &value) 0620 { 0621 command(QStringList() << "seek" << QString::number(value/1000) << "absolute"); 0622 } 0623 0624 void MpvObject::setHardwareDecoding(bool hardwareDecoding) 0625 { 0626 if (m_hardwareDecoding == hardwareDecoding) 0627 return; 0628 0629 m_hardwareDecoding = hardwareDecoding; 0630 0631 if (m_hardwareDecoding) { 0632 setProperty("hwdec", "yes"); 0633 } else { 0634 setProperty("hwdec", "no"); 0635 } 0636 0637 Q_EMIT hardwareDecodingChanged(m_hardwareDecoding); 0638 } 0639 0640 TracksModel *MpvObject::subtitleTracksModel() const 0641 { 0642 return m_subtitleTracksModel; 0643 } 0644 0645 TracksModel *MpvObject::audioTracksModel() const 0646 { 0647 return m_audioTracksModel; 0648 } 0649 0650 int MpvObject::setProperty(const QString &name, const QVariant &value) 0651 { 0652 return mpv::qt::set_property(mpv, name, value); 0653 } 0654 0655 void MpvObject::setSource(QUrl url) 0656 { 0657 if (m_source == url) 0658 return; 0659 0660 m_source = url; 0661 0662 if(m_autoPlay) 0663 { 0664 this->playUrl(); 0665 } 0666 0667 Q_EMIT sourceChanged(m_source); 0668 } 0669 0670 void MpvObject::setAutoPlay(bool autoPlay) 0671 { 0672 if (m_autoPlay == autoPlay) 0673 return; 0674 0675 m_autoPlay = autoPlay; 0676 Q_EMIT autoPlayChanged(m_autoPlay); 0677 } 0678 0679 QVariant MpvObject::getProperty(const QString &name) 0680 { 0681 auto value = mpv::qt::get_property(mpv, name); 0682 return value; 0683 } 0684 0685 QVariant MpvObject::command(const QVariant ¶ms) 0686 { 0687 return mpv::qt::command(mpv, params); 0688 } 0689 0690 QUrl MpvObject::source() const 0691 { 0692 return m_source; 0693 } 0694 0695 bool MpvObject::autoPlay() const 0696 { 0697 return m_autoPlay; 0698 } 0699 0700 void MpvObject::setPlaybackState(const QMediaPlayer::State &state) 0701 { 0702 m_playbackState = state; 0703 Q_EMIT this->playbackStateChanged(m_playbackState); 0704 } 0705 0706 void MpvObject::setStatus(const QMediaPlayer::MediaStatus &status) 0707 { 0708 m_status = status; 0709 Q_EMIT this->statusChanged(m_status); 0710 } 0711 0712 QMediaPlayer::State MpvObject::getPlaybackState() const 0713 { 0714 return m_playbackState; 0715 } 0716 0717 QMediaPlayer::MediaStatus MpvObject::getStatus() const 0718 { 0719 return m_status; 0720 } 0721 0722 bool MpvObject::hardwareDecoding() const 0723 { 0724 return m_hardwareDecoding; 0725 } 0726 0727 void MpvObject::playUrl() 0728 { 0729 if(!m_source.isEmpty() && m_source.isValid()) 0730 { 0731 qDebug() << "request play file" << m_source; 0732 0733 if(m_source.isLocalFile()) 0734 command(QStringList{"loadfile", m_source.toLocalFile()}); 0735 else 0736 command(QStringList{"loadfile", m_source.toString()}); 0737 } 0738 0739 if (m_playbackState == QMediaPlayer::PausedState) 0740 play(); 0741 }