File indexing completed on 2025-01-05 04:29:50

0001 /**
0002  * SPDX-FileCopyrightText: 2022-2023 Bart De Vries <bart@mogwai.be>
0003  * SPDX-FileCopyrightText: 2017 Matthieu Gallien <matthieu_gallien@yahoo.fr>
0004  *
0005  * SPDX-License-Identifier: LGPL-3.0-or-later
0006  */
0007 
0008 #include "vlcmediabackend.h"
0009 #include "vlcmediabackendlogging.h"
0010 #include "vlcsignalslogging.h"
0011 
0012 #include <QAudio>
0013 #include <QDir>
0014 #include <QGuiApplication>
0015 #include <QTimer>
0016 
0017 #include "kmediasession.h"
0018 
0019 #if defined Q_OS_WIN
0020 #include <basetsd.h>
0021 typedef SSIZE_T ssize_t;
0022 #endif
0023 
0024 #include <vlc/vlc.h>
0025 
0026 class VlcMediaBackendPrivate
0027 {
0028 public:
0029     KMediaSession *mKMediaSession = nullptr;
0030 
0031     VlcMediaBackend *mParent = nullptr;
0032 
0033     libvlc_instance_t *mInstance = nullptr;
0034 
0035     libvlc_media_player_t *mPlayer = nullptr;
0036 
0037     libvlc_event_manager_t *mPlayerEventManager = nullptr;
0038     libvlc_event_manager_t *mMediaEventManager = nullptr;
0039 
0040     libvlc_media_t *mMedia = nullptr;
0041 
0042     qint64 mMediaDuration = 0;
0043 
0044     KMediaSession::PlaybackState mPreviousPlayerState = KMediaSession::StoppedState;
0045 
0046     KMediaSession::MediaStatus mPreviousMediaStatus = KMediaSession::NoMedia;
0047 
0048     qreal mPreviousVolume = 100.0;
0049 
0050     qint64 mPreviousPosition = 0;
0051 
0052     KMediaSession::Error mError = KMediaSession::NoError;
0053 
0054     bool mIsMuted = false;
0055 
0056     bool mIsSeekable = false;
0057 
0058     qreal mPlaybackRate = 1.0;
0059 
0060     void vlcEventCallback(const struct libvlc_event_t *p_event);
0061 
0062     void mediaIsEnded();
0063 
0064     bool signalPlaybackChange(KMediaSession::PlaybackState newPlayerState);
0065 
0066     void signalMediaStatusChange(KMediaSession::MediaStatus newMediaStatus);
0067 
0068     void signalVolumeChange(int newVolume);
0069 
0070     void signalMutedChange(bool isMuted);
0071 
0072     void signalDurationChange(libvlc_time_t newDuration);
0073 
0074     void signalPositionChange(float newPosition);
0075 
0076     void signalSeekableChange(bool isSeekable);
0077 
0078     void signalErrorChange(KMediaSession::Error errorCode);
0079 
0080     void parseMetaData();
0081 };
0082 
0083 static void vlc_callback(const struct libvlc_event_t *p_event, void *p_data)
0084 {
0085     reinterpret_cast<VlcMediaBackendPrivate *>(p_data)->vlcEventCallback(p_event);
0086 }
0087 
0088 VlcMediaBackend::VlcMediaBackend(QObject *parent)
0089     : AbstractMediaBackend(parent)
0090     , d(std::make_unique<VlcMediaBackendPrivate>())
0091 {
0092     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::VlcMediaBackend";
0093     d->mKMediaSession = static_cast<KMediaSession *>(parent);
0094     d->mParent = this;
0095 
0096     // TODO: handle video playback
0097     const char *cmdLineOption = "--no-video";
0098     d->mInstance = libvlc_new(1, &cmdLineOption);
0099 
0100     libvlc_set_user_agent(d->mInstance, d->mKMediaSession->playerName().toUtf8().constData(), d->mKMediaSession->playerName().toUtf8().constData());
0101     libvlc_set_app_id(d->mInstance, d->mKMediaSession->desktopEntryName().toUtf8().constData(), "1.0", d->mKMediaSession->playerName().toUtf8().constData());
0102 
0103     connect(d->mKMediaSession, &KMediaSession::playerNameChanged, this, &VlcMediaBackend::setPlayerName);
0104     connect(d->mKMediaSession, &KMediaSession::desktopEntryNameChanged, this, &VlcMediaBackend::setDesktopEntryName);
0105 
0106     d->mPlayer = libvlc_media_player_new(d->mInstance);
0107 
0108     if (!d->mPlayer) {
0109         qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::VlcMediaBackend"
0110                                     << "failed creating player" << libvlc_errmsg();
0111         return;
0112     }
0113 
0114     d->mPlayerEventManager = libvlc_media_player_event_manager(d->mPlayer);
0115 
0116     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerOpening, &vlc_callback, d.get());
0117     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerBuffering, &vlc_callback, d.get());
0118     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerPlaying, &vlc_callback, d.get());
0119     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerPaused, &vlc_callback, d.get());
0120     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerStopped, &vlc_callback, d.get());
0121     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerEndReached, &vlc_callback, d.get());
0122     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerEncounteredError, &vlc_callback, d.get());
0123     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerPositionChanged, &vlc_callback, d.get());
0124     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerSeekableChanged, &vlc_callback, d.get());
0125     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerLengthChanged, &vlc_callback, d.get());
0126     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerMuted, &vlc_callback, d.get());
0127     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerUnmuted, &vlc_callback, d.get());
0128     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerAudioVolume, &vlc_callback, d.get());
0129     libvlc_event_attach(d->mPlayerEventManager, libvlc_MediaPlayerAudioDevice, &vlc_callback, d.get());
0130 }
0131 
0132 VlcMediaBackend::~VlcMediaBackend()
0133 {
0134     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::~VlcMediaBackend";
0135     if (d->mInstance) {
0136         if (d->mPlayer && d->mPreviousPlayerState != KMediaSession::StoppedState) {
0137             libvlc_media_player_stop(d->mPlayer);
0138         }
0139         libvlc_release(d->mInstance);
0140     }
0141 }
0142 
0143 KMediaSession::MediaBackends VlcMediaBackend::backend() const
0144 {
0145     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::backend()";
0146     return KMediaSession::MediaBackends::Vlc;
0147 }
0148 
0149 bool VlcMediaBackend::muted() const
0150 {
0151     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::muted()";
0152     if (!d->mPlayer) {
0153         return false;
0154     }
0155 
0156     qCDebug(VlcMediaBackendLog) << "muted" << d->mIsMuted;
0157     return d->mIsMuted;
0158 }
0159 
0160 qreal VlcMediaBackend::volume() const
0161 {
0162     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::volume()";
0163     if (!d->mPlayer) {
0164         return 100.0;
0165     }
0166 
0167     qCDebug(VlcMediaBackendLog) << "volume" << d->mPreviousVolume;
0168     return d->mPreviousVolume;
0169 }
0170 
0171 QUrl VlcMediaBackend::source() const
0172 {
0173     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::source()";
0174     if (!d->mPlayer) {
0175         return {};
0176     }
0177     if (d->mMedia) {
0178         auto filePath = QString::fromUtf8(libvlc_media_get_mrl(d->mMedia));
0179         return QUrl::fromUserInput(filePath);
0180     }
0181     return {};
0182 }
0183 
0184 KMediaSession::Error VlcMediaBackend::error() const
0185 {
0186     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::error()";
0187     return d->mError;
0188 }
0189 
0190 qint64 VlcMediaBackend::duration() const
0191 {
0192     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::duration()";
0193     return d->mMediaDuration;
0194 }
0195 
0196 qint64 VlcMediaBackend::position() const
0197 {
0198     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::position()";
0199     if (!d->mPlayer) {
0200         return 0;
0201     }
0202 
0203     if (d->mMediaDuration == -1) {
0204         return 0;
0205     }
0206 
0207     qint64 currentPosition = qRound64(libvlc_media_player_get_position(d->mPlayer) * d->mMediaDuration);
0208 
0209     if (currentPosition < 0) {
0210         return 0;
0211     }
0212 
0213     return currentPosition;
0214 }
0215 
0216 qreal VlcMediaBackend::playbackRate() const
0217 {
0218     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playbackRate()";
0219     if (d->mPlayer) {
0220         return libvlc_media_player_get_rate(d->mPlayer);
0221     }
0222     return 1.0;
0223 }
0224 
0225 bool VlcMediaBackend::seekable() const
0226 {
0227     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::seekable()";
0228     return d->mIsSeekable;
0229 }
0230 
0231 KMediaSession::PlaybackState VlcMediaBackend::playbackState() const
0232 {
0233     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playbackState()";
0234     return d->mPreviousPlayerState;
0235 }
0236 
0237 KMediaSession::MediaStatus VlcMediaBackend::mediaStatus() const
0238 {
0239     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::mediaStatus()";
0240     return d->mPreviousMediaStatus;
0241 }
0242 
0243 void VlcMediaBackend::setMuted(bool muted)
0244 {
0245     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setMuted(" << muted << ")";
0246 
0247     if (d->mPlayer) {
0248         libvlc_audio_set_mute(d->mPlayer, muted);
0249     } else {
0250         d->mIsMuted = muted;
0251         Q_EMIT mutedChanged(muted);
0252     }
0253 }
0254 
0255 void VlcMediaBackend::setVolume(qreal volume)
0256 {
0257     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setVolume(" << volume << ")";
0258 
0259     if (d->mPlayer && d->mPreviousPlayerState != KMediaSession::PlaybackState::StoppedState) {
0260         libvlc_audio_set_volume(d->mPlayer, qRound(volume));
0261     }
0262 }
0263 
0264 void VlcMediaBackend::setSource(const QUrl &source)
0265 {
0266     if (playbackState() != KMediaSession::PlaybackState::StoppedState) {
0267         stop();
0268     }
0269 
0270     d->mMediaDuration = 0;
0271     d->mIsSeekable = false;
0272     d->mPlaybackRate = 1.0;
0273     d->mPreviousPosition = 0;
0274     d->mPreviousPlayerState = KMediaSession::PlaybackState::StoppedState;
0275 
0276     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setSource(" << source << ")";
0277     if (source.isLocalFile()) {
0278         qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setSource reading local resource";
0279         d->mMedia = libvlc_media_new_path(d->mInstance, QDir::toNativeSeparators(source.toLocalFile()).toUtf8().constData());
0280     } else {
0281         qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setSource reading remote resource";
0282         d->mMedia = libvlc_media_new_location(d->mInstance, source.url().toUtf8().constData());
0283     }
0284 
0285     if (!d->mMedia) {
0286         qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setSource"
0287                                     << "failed creating media" << libvlc_errmsg() << QDir::toNativeSeparators(source.toLocalFile()).toUtf8().constData();
0288 
0289         d->mMedia = libvlc_media_new_path(d->mInstance, QDir::toNativeSeparators(source.toLocalFile()).toLatin1().constData());
0290         if (!d->mMedia) {
0291             qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setSource"
0292                                         << "failed creating media" << libvlc_errmsg() << QDir::toNativeSeparators(source.toLocalFile()).toLatin1().constData();
0293             return;
0294         }
0295     }
0296 
0297     // By default, libvlc caches only next 1000 (ms, 0..60000) of the playback,
0298     // which is unreasonable given our usecase of sequential playback.
0299     libvlc_media_add_option(d->mMedia, ":file-caching=10000");
0300     libvlc_media_add_option(d->mMedia, ":live-caching=10000");
0301     libvlc_media_add_option(d->mMedia, ":disc-caching=10000");
0302     libvlc_media_add_option(d->mMedia, ":network-caching=10000");
0303 
0304     libvlc_media_player_set_media(d->mPlayer, d->mMedia);
0305 
0306     d->signalMediaStatusChange(KMediaSession::LoadingMedia);
0307     d->signalMediaStatusChange(KMediaSession::LoadedMedia);
0308     d->signalMediaStatusChange(KMediaSession::BufferedMedia);
0309 
0310     d->mMediaEventManager = libvlc_media_event_manager(d->mMedia);
0311 
0312     libvlc_event_attach(d->mMediaEventManager, libvlc_MediaParsedChanged, &vlc_callback, d.get());
0313     libvlc_event_attach(d->mMediaEventManager, libvlc_MediaDurationChanged, &vlc_callback, d.get());
0314 
0315     libvlc_media_parse_with_options(
0316         d->mMedia,
0317         static_cast<libvlc_media_parse_flag_t>(libvlc_media_parse_local | libvlc_media_parse_network | libvlc_media_fetch_local | libvlc_media_fetch_network),
0318         0);
0319 
0320     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::sourceChanged(" << source << ")";
0321     QTimer::singleShot(0, this, [this, source]() {
0322         Q_EMIT sourceChanged(source);
0323     });
0324 }
0325 
0326 void VlcMediaBackend::setPosition(qint64 position)
0327 {
0328     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setPosition(" << position << ")";
0329 
0330     if (!d->mPlayer) {
0331         return;
0332     }
0333 
0334     if (d->mMediaDuration == -1 || d->mMediaDuration == 0) {
0335         return;
0336     }
0337 
0338     libvlc_media_player_set_position(d->mPlayer, static_cast<float>(position) / d->mMediaDuration);
0339 }
0340 
0341 void VlcMediaBackend::setPlaybackRate(qreal rate)
0342 {
0343     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setPlaybackRate(" << rate << ")";
0344     if (d->mPlayer) {
0345         if (libvlc_media_player_set_rate(d->mPlayer, static_cast<float>(rate)) == 0) {
0346             d->mPlaybackRate = rate;
0347             QTimer::singleShot(0, this, [this, rate]() {
0348                 Q_EMIT playbackRateChanged(rate);
0349             });
0350         }
0351     }
0352 }
0353 
0354 void VlcMediaBackend::play()
0355 {
0356     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::play()";
0357     if (!d->mPlayer) {
0358         return;
0359     }
0360 
0361     libvlc_media_player_play(d->mPlayer);
0362 }
0363 
0364 void VlcMediaBackend::pause()
0365 {
0366     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::pause()";
0367     if (!d->mPlayer) {
0368         return;
0369     }
0370 
0371     // need to check playback state first, because the pause function will
0372     // actually toggle pause, i.e. it will start playing when the current track
0373     // has already been paused
0374     if (playbackState() == KMediaSession::PlaybackState::PlayingState) {
0375         libvlc_media_player_pause(d->mPlayer);
0376     }
0377 }
0378 
0379 void VlcMediaBackend::stop()
0380 {
0381     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::stop()";
0382     if (!d->mPlayer) {
0383         return;
0384     }
0385 
0386     d->mIsSeekable = false;
0387     QTimer::singleShot(0, this, [this]() {
0388         Q_EMIT seekableChanged(d->mIsSeekable);
0389     });
0390 
0391     libvlc_media_player_stop(d->mPlayer);
0392 }
0393 
0394 void VlcMediaBackend::playerStateSignalChanges(KMediaSession::PlaybackState newState)
0395 {
0396     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerStateSignalChanges(" << newState << ")";
0397     QTimer::singleShot(0, this, [this, newState]() {
0398         Q_EMIT playbackStateChanged(newState);
0399         if (newState == KMediaSession::PlaybackState::StoppedState) {
0400             Q_EMIT positionChanged(position());
0401         } else {
0402             Q_EMIT mutedChanged(muted());
0403         }
0404     });
0405 }
0406 
0407 void VlcMediaBackend::mediaStatusSignalChanges(KMediaSession::MediaStatus newStatus)
0408 {
0409     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::mediaStatusSignalChanges(" << newStatus << ")";
0410     QTimer::singleShot(0, this, [this, newStatus]() {
0411         Q_EMIT mediaStatusChanged(newStatus);
0412     });
0413 }
0414 
0415 void VlcMediaBackend::playerErrorSignalChanges(KMediaSession::Error error)
0416 {
0417     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerErrorSignalChanges(" << error << ")";
0418     QTimer::singleShot(0, this, [this, error]() {
0419         Q_EMIT errorChanged(error);
0420     });
0421 }
0422 
0423 void VlcMediaBackend::playerDurationSignalChanges(qint64 newDuration)
0424 {
0425     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerDurationSignalChanges(" << newDuration << ")";
0426     d->mMediaDuration = newDuration;
0427     QTimer::singleShot(0, this, [this, newDuration]() {
0428         Q_EMIT durationChanged(newDuration);
0429     });
0430 }
0431 
0432 void VlcMediaBackend::playerPositionSignalChanges(qint64 newPosition)
0433 {
0434     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerPositionSignalChanges(" << newPosition << ")";
0435     QTimer::singleShot(0, this, [this, newPosition]() {
0436         Q_EMIT positionChanged(newPosition);
0437     });
0438 }
0439 
0440 void VlcMediaBackend::playerVolumeSignalChanges(qreal volume)
0441 {
0442     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerVolumeSignalChanges(" << volume << ")";
0443     QTimer::singleShot(0, this, [this, volume]() {
0444         Q_EMIT volumeChanged(volume);
0445     });
0446 }
0447 
0448 void VlcMediaBackend::playerMutedSignalChanges(bool isMuted)
0449 {
0450     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerMutedSignalChanges(" << isMuted << ")";
0451     QTimer::singleShot(0, this, [this, isMuted]() {
0452         Q_EMIT mutedChanged(isMuted);
0453     });
0454 }
0455 
0456 void VlcMediaBackend::playerSeekableSignalChanges(bool isSeekable)
0457 {
0458     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::playerSeekableSignalChanges(" << isSeekable << ")";
0459     QTimer::singleShot(0, this, [this, isSeekable]() {
0460         Q_EMIT seekableChanged(isSeekable);
0461     });
0462 }
0463 
0464 void VlcMediaBackend::setPlayerName(const QString &name)
0465 {
0466     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setPlayerName(" << name << ")";
0467     libvlc_set_user_agent(d->mInstance, name.toUtf8().constData(), name.toUtf8().constData());
0468     libvlc_set_app_id(d->mInstance, d->mKMediaSession->desktopEntryName().toUtf8().constData(), "1.0", name.toUtf8().constData());
0469 }
0470 
0471 void VlcMediaBackend::setDesktopEntryName(const QString &name)
0472 {
0473     qCDebug(VlcMediaBackendLog) << "VlcMediaBackend::setDesktopEntryName(" << name << ")";
0474     libvlc_set_app_id(d->mInstance, name.toUtf8().constData(), "1.0", d->mKMediaSession->playerName().toUtf8().constData());
0475 }
0476 
0477 void VlcMediaBackendPrivate::vlcEventCallback(const struct libvlc_event_t *p_event)
0478 {
0479     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback()";
0480     const auto eventType = static_cast<libvlc_event_e>(p_event->type);
0481 
0482     switch (eventType) {
0483     case libvlc_MediaPlayerOpening:
0484         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0485                                << "libvlc_MediaPlayerOpening";
0486         signalMediaStatusChange(KMediaSession::LoadedMedia);
0487         break;
0488     case libvlc_MediaPlayerBuffering:
0489         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0490                                << "libvlc_MediaPlayerBuffering";
0491         signalMediaStatusChange(KMediaSession::BufferedMedia);
0492         break;
0493     case libvlc_MediaPlayerPlaying:
0494         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0495                                << "libvlc_MediaPlayerPlaying";
0496         signalPlaybackChange(KMediaSession::PlayingState);
0497         break;
0498     case libvlc_MediaPlayerPaused:
0499         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0500                                << "libvlc_MediaPlayerPaused";
0501         signalPlaybackChange(KMediaSession::PausedState);
0502         break;
0503     case libvlc_MediaPlayerStopped:
0504         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0505                                << "libvlc_MediaPlayerStopped";
0506         signalPlaybackChange(KMediaSession::StoppedState);
0507         break;
0508     case libvlc_MediaPlayerEndReached:
0509         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0510                                << "libvlc_MediaPlayerEndReached";
0511         signalMediaStatusChange(KMediaSession::BufferedMedia);
0512         signalMediaStatusChange(KMediaSession::NoMedia);
0513         signalMediaStatusChange(KMediaSession::EndOfMedia);
0514         mediaIsEnded();
0515         break;
0516     case libvlc_MediaPlayerEncounteredError:
0517         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0518                                << "libvlc_MediaPlayerEncounteredError";
0519         signalErrorChange(KMediaSession::ResourceError);
0520         mediaIsEnded();
0521         signalMediaStatusChange(KMediaSession::InvalidMedia);
0522         break;
0523     case libvlc_MediaPlayerPositionChanged:
0524         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0525                                << "libvlc_MediaPlayerPositionChanged";
0526         signalPositionChange(p_event->u.media_player_position_changed.new_position);
0527         break;
0528     case libvlc_MediaPlayerSeekableChanged:
0529         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0530                                << "libvlc_MediaPlayerSeekableChanged";
0531         signalSeekableChange(p_event->u.media_player_seekable_changed.new_seekable);
0532         break;
0533     case libvlc_MediaPlayerLengthChanged:
0534         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0535                                << "libvlc_MediaPlayerLengthChanged";
0536         signalDurationChange(p_event->u.media_player_length_changed.new_length);
0537         break;
0538     case libvlc_MediaPlayerMuted:
0539         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0540                                << "libvlc_MediaPlayerMuted";
0541         signalMutedChange(true);
0542         break;
0543     case libvlc_MediaPlayerUnmuted:
0544         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0545                                << "libvlc_MediaPlayerUnmuted";
0546         signalMutedChange(false);
0547         break;
0548     case libvlc_MediaPlayerAudioVolume:
0549         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0550                                << "libvlc_MediaPlayerAudioVolume";
0551         signalVolumeChange(qRound(p_event->u.media_player_audio_volume.volume * 100));
0552         break;
0553     case libvlc_MediaPlayerAudioDevice:
0554         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0555                                << "libvlc_MediaPlayerAudioDevice";
0556         break;
0557     case libvlc_MediaDurationChanged:
0558         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0559                                << "libvlc_MediaDurationChanged";
0560         signalDurationChange(p_event->u.media_duration_changed.new_duration);
0561         break;
0562     case libvlc_MediaParsedChanged:
0563         if (p_event->u.media_parsed_changed.new_status == libvlc_media_parsed_status_done) {
0564             parseMetaData();
0565         }
0566         break;
0567     default:
0568         qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::vlcEventCallback"
0569                                << "eventType" << eventType;
0570         break;
0571     }
0572 }
0573 
0574 void VlcMediaBackendPrivate::mediaIsEnded()
0575 {
0576     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::mediaIsEnded()";
0577 
0578     mIsSeekable = false;
0579     Q_EMIT mParent->seekableChanged(mIsSeekable);
0580 
0581     libvlc_media_release(mMedia);
0582     mMedia = nullptr;
0583 }
0584 
0585 bool VlcMediaBackendPrivate::signalPlaybackChange(KMediaSession::PlaybackState newPlayerState)
0586 {
0587     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalPlaybackChange(" << newPlayerState << ")";
0588     if (mPreviousPlayerState != newPlayerState) {
0589         mPreviousPlayerState = newPlayerState;
0590 
0591         mParent->playerStateSignalChanges(mPreviousPlayerState);
0592         return true;
0593     }
0594 
0595     return false;
0596 }
0597 
0598 void VlcMediaBackendPrivate::signalMediaStatusChange(KMediaSession::MediaStatus newMediaStatus)
0599 {
0600     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalMediaStatusChange(" << newMediaStatus << ")";
0601     if (mPreviousMediaStatus != newMediaStatus) {
0602         mPreviousMediaStatus = newMediaStatus;
0603 
0604         mParent->mediaStatusSignalChanges(mPreviousMediaStatus);
0605     }
0606 }
0607 
0608 void VlcMediaBackendPrivate::signalVolumeChange(int newVolume)
0609 {
0610     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalVolumeChange(" << newVolume << ")";
0611     if (newVolume == -100) {
0612         return;
0613     }
0614 
0615     if (abs(int(mPreviousVolume - newVolume)) > 0.01) {
0616         mPreviousVolume = newVolume;
0617 
0618         mParent->playerVolumeSignalChanges(newVolume);
0619     }
0620 }
0621 
0622 void VlcMediaBackendPrivate::signalMutedChange(bool isMuted)
0623 {
0624     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalMutedChange(" << isMuted << ")";
0625     if (mIsMuted != isMuted) {
0626         mIsMuted = isMuted;
0627 
0628         mParent->playerMutedSignalChanges(mIsMuted);
0629     }
0630 }
0631 
0632 void VlcMediaBackendPrivate::signalDurationChange(libvlc_time_t newDuration)
0633 {
0634     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalDurationChange(" << newDuration << ")";
0635     if (mMediaDuration != newDuration) {
0636         mMediaDuration = newDuration;
0637 
0638         mParent->playerDurationSignalChanges(mMediaDuration);
0639     }
0640 }
0641 
0642 void VlcMediaBackendPrivate::signalPositionChange(float newPosition)
0643 {
0644     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalPositionChange(" << newPosition << ")";
0645     if (mMediaDuration == -1) {
0646         return;
0647     }
0648 
0649     if (newPosition < 0) {
0650         mPreviousPosition = 0;
0651         mParent->playerPositionSignalChanges(mPreviousPosition);
0652         return;
0653     }
0654 
0655     auto computedPosition = qRound64(newPosition * mMediaDuration);
0656 
0657     if (mPreviousPosition != computedPosition) {
0658         mPreviousPosition = computedPosition;
0659 
0660         mParent->playerPositionSignalChanges(mPreviousPosition);
0661     }
0662 }
0663 
0664 void VlcMediaBackendPrivate::signalSeekableChange(bool isSeekable)
0665 {
0666     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalSeekableChange(" << isSeekable << ")";
0667     if (mIsSeekable != isSeekable) {
0668         mIsSeekable = isSeekable;
0669 
0670         mParent->playerSeekableSignalChanges(isSeekable);
0671     }
0672 }
0673 
0674 void VlcMediaBackendPrivate::signalErrorChange(KMediaSession::Error errorCode)
0675 {
0676     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::signalErrorChange(" << errorCode << ")";
0677     if (mError != errorCode) {
0678         mError = errorCode;
0679 
0680         mParent->playerErrorSignalChanges(errorCode);
0681     }
0682 }
0683 
0684 void VlcMediaBackendPrivate::parseMetaData()
0685 {
0686     qCDebug(VlcSignalsLog) << "VlcMediaBackendPrivate::parseMetaData()";
0687     if (mMedia && mKMediaSession->metaData()->title().isEmpty()) {
0688         mKMediaSession->metaData()->setTitle(QString::fromUtf8(libvlc_media_get_meta(mMedia, libvlc_meta_Title)));
0689     }
0690     if (mMedia && mKMediaSession->metaData()->artist().isEmpty()) {
0691         mKMediaSession->metaData()->setArtist(QString::fromUtf8(libvlc_media_get_meta(mMedia, libvlc_meta_Artist)));
0692     }
0693     if (mMedia && mKMediaSession->metaData()->album().isEmpty()) {
0694         mKMediaSession->metaData()->setAlbum(QString::fromUtf8(libvlc_media_get_meta(mMedia, libvlc_meta_Album)));
0695     }
0696     if (mMedia && mKMediaSession->metaData()->artworkUrl().isEmpty()) {
0697         mKMediaSession->metaData()->setArtworkUrl(QUrl(QString::fromUtf8(libvlc_media_get_meta(mMedia, libvlc_meta_ArtworkURL))));
0698     }
0699 }