Commit f12595e7 authored by kempe's avatar kempe
Browse files

Added basic support for control via MPRIS

Changed AudioPlayer to extend QMediaplayer directly instead of having a instance of QMediaPlayer
parent 1a457360
......@@ -12,19 +12,23 @@
# The name of your application
TARGET = harbour-received
QT += multimedia
QT += multimedia dbus
CONFIG += sailfishapp
CONFIG += sailfishapp_i18n
HEADERS += \
src/settings.h \
src/audioplayer.h
src/audioplayer.h \
src/mprisbase.h \
src/mprisplayer.h
SOURCES += \
src/harbour-received.cpp \
src/settings.cpp \
src/audioplayer.cpp
src/audioplayer.cpp \
src/mprisbase.cpp \
src/mprisplayer.cpp
DISTFILES += \
qml/components/audioplayer/DockedAudioPlayerForm.ui.qml \
......
#include "audioplayer.h"
#include <QDBusConnection>
#include <QDebug>
#include "audioplayer.h"
#include "mprisbase.h"
#include "mprisplayer.h"
AudioPlayer::AudioPlayer(QObject *parent) : QObject(parent)
AudioPlayer::AudioPlayer(QObject *parent) : QMediaPlayer(parent)
{
m_current_url = "";
m_buffer_progress = 0;
m_state = QMediaPlayer::StoppedState;
m_status = QMediaPlayer::NoMedia;
m_player = new QMediaPlayer(this);
connect(m_player, &QMediaPlayer::stateChanged, this, &AudioPlayer::stateChanged);
connect(m_player, &QMediaPlayer::mediaStatusChanged, this, &AudioPlayer::statusChanged);
connect(m_player, &QMediaPlayer::bufferStatusChanged, this, &AudioPlayer::bufferStatusChanged);
connect(m_player, &QMediaPlayer::seekableChanged, this, &AudioPlayer::seekableChanged);
connect(this, &QMediaPlayer::stateChanged, this, &AudioPlayer::playbackStateChanged);
connect(this, &QMediaPlayer::mediaStatusChanged, this, &AudioPlayer::playbackStatusChanged);
connect(this, &QMediaPlayer::bufferStatusChanged, this, &AudioPlayer::bufferStatusChanged);
connect(
m_player, static_cast<void(QMediaPlayer::*)()>(&QMediaPlayer::metaDataChanged),
this, static_cast<void(QMediaPlayer::*)()>(&QMediaPlayer::metaDataChanged),
this, &AudioPlayer::metaDataChanged
);
new MprisBase(this);
new MprisPlayer(this);
QDBusConnection dbus = QDBusConnection::sessionBus();
if (!dbus.registerObject(QString("/org/mpris/MediaPlayer2"), this, QDBusConnection::ExportAdaptors))
qWarning() << "Failed to register object";
if (!dbus.registerService("org.mpris.MediaPlayer2.Recived"))
qWarning() << "Failed to register service";
}
AudioPlayer::~AudioPlayer()
......@@ -33,7 +41,7 @@ void AudioPlayer::loadUrl(QString url)
void AudioPlayer::togglePlayback()
{
switch(m_player->state()) {
switch(state()) {
case QMediaPlayer::PausedState:
case QMediaPlayer::StoppedState:
startPlayback();
......@@ -47,18 +55,18 @@ void AudioPlayer::togglePlayback()
}
void AudioPlayer::startPlayback() {
if (m_player->mediaStatus() == QMediaPlayer::NoMedia) {
if (mediaStatus() == QMediaPlayer::NoMedia) {
qDebug() << "Loading new media";
m_player->setMedia(m_current_url);
setMedia(m_current_url);
}
m_player->play();
play();
}
void AudioPlayer::pauseOrStopPlayback() {
if (m_player->isSeekable()) {
if (isSeekable()) {
qDebug() << "Pausing playback";
m_player->pause();
pause();
} else {
qDebug() << "Stoping playback";
stopPlayback();
......@@ -67,20 +75,8 @@ void AudioPlayer::pauseOrStopPlayback() {
void AudioPlayer::stopPlayback()
{
m_player->stop();
m_player->setMedia(QMediaContent());
}
void AudioPlayer::stateChanged(QMediaPlayer::State state)
{
m_state = state;
emit playbackStateChanged();
}
void AudioPlayer::statusChanged(QMediaPlayer::MediaStatus status)
{
m_status = status;
emit playbackStatusChanged();
stop();
setMedia(QMediaContent());
}
void AudioPlayer::bufferStatusChanged(const int progress)
......@@ -98,11 +94,17 @@ void AudioPlayer::metaDataChanged()
m_title = tmp_title;
emit titleChanged();
}
QString tmp_genre = getMetaDataValueOrEmptyByKey("Genre").toString();
if (tmp_genre != m_genre) {
qDebug() << "Setting genre to: " << tmp_genre;
m_genre = tmp_genre;
emit genreChanged();
}
}
QVariant AudioPlayer::getMetaDataValueOrEmptyByKey(QString key) {
if (m_player->availableMetaData().contains(key)) {
return m_player->metaData(key);
if (availableMetaData().contains(key)) {
return metaData(key);
}
return "";
......
......@@ -4,29 +4,30 @@
#include <QObject>
#include <QQmlEngine>
#include <QJSEngine>
#include <QMediaPlayer>
class AudioPlayer : public QObject {
class AudioPlayer : public QMediaPlayer {
Q_OBJECT
Q_PROPERTY(bool isPlaying READ isPlaying NOTIFY playbackStateChanged)
Q_PROPERTY(bool isLoading READ isLoading NOTIFY playbackStatusChanged)
Q_PROPERTY(bool isBuffering READ isBuffering NOTIFY playbackStatusChanged)
Q_PROPERTY(bool isSeekable READ isSeekable NOTIFY seekableChanged)
Q_PROPERTY(bool isPlayable READ isPlayable)
Q_PROPERTY(double bufferProgress READ bufferProgress NOTIFY bufferProgressChanged)
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(QString genre READ genre NOTIFY genreChanged)
public:
explicit AudioPlayer(QObject *parent = 0);
~AudioPlayer();
bool isPlaying() const { return m_state == QMediaPlayer::PlayingState; }
bool isLoading() const { return m_status == QMediaPlayer::LoadingMedia; }
bool isBuffering() const { return m_status == QMediaPlayer::BufferingMedia; }
bool isSeekable() const { return m_player->isSeekable(); }
bool isPlaying() const { return state() == QMediaPlayer::PlayingState; }
bool isLoading() const { return mediaStatus() == QMediaPlayer::LoadingMedia; }
bool isBuffering() const { return mediaStatus() == QMediaPlayer::BufferingMedia; }
bool isPlayable() const { return m_current_url.isValid(); }
double bufferProgress() const { return m_buffer_progress; }
QString title() const { return m_title; }
QString genre() const { return m_genre; }
signals:
void playbackStateChanged();
......@@ -34,36 +35,30 @@ signals:
void seekableChanged();
void bufferProgressChanged();
void titleChanged();
void genreChanged();
public slots:
void loadUrl(QString url);
void togglePlayback();
void startPlayback();
void pauseOrStopPlayback();
void stopPlayback();
private slots:
void stateChanged(QMediaPlayer::State);
void statusChanged(QMediaPlayer::MediaStatus);
void bufferStatusChanged(const int);
void metaDataChanged();
private:
QMediaPlayer *m_player;
// States
QUrl m_current_url;
QMediaPlayer::State m_state;
QMediaPlayer::MediaStatus m_status;
double m_buffer_progress;
QString m_title;
// Playback control
void startPlayback();
void pauseOrStopPlayback();
void stopPlayback();
QString m_genre;
QVariant getMetaDataValueOrEmptyByKey(QString);
};
static QObject *audioPlayerProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
inline static QObject *audioPlayerProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
......
#include "mprisbase.h"
#include <QDebug>
MprisBase::MprisBase(QObject *parent) : QDBusAbstractAdaptor(parent)
{
m_dekstopEntry = "harbour-recived";
m_identity = "Recived";
}
QStringList MprisBase::SupportedUriSchemes()
{
return QStringList() << "http" << "https" << "rtsp";
}
QStringList MprisBase::SupportedMimeTypes()
{
return QStringList() << "audio/mpeg" << "audio/mpeg3" << "audio/wav";
}
QString MprisBase::DesktopEntry()
{
return m_dekstopEntry;
}
QString MprisBase::Identity()
{
return m_identity;
}
void MprisBase::setFullScreen(const bool)
{
qDebug() << "MprisBase: SetPosition not supported";
}
void MprisBase::Raise()
{
qDebug() << "MprisBase: Raise not supported";
}
void MprisBase::Quit()
{
qDebug() << "MprisBase: Quit not supported";
}
#ifndef MPRISBASE_H
#define MPRISBASE_H
#include <QObject>
#include <QDBusAbstractAdaptor>
class MprisBase : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2")
Q_PROPERTY(bool CanSetFullscreen READ CanSetFullscreen)
Q_PROPERTY(bool CanQuit READ CanQuit)
Q_PROPERTY(bool CanRaise READ CanRaise)
Q_PROPERTY(bool HasTrackList READ HasTrackList)
Q_PROPERTY(QString Identity READ Identity)
Q_PROPERTY(QString DesktopEntry READ DesktopEntry)
Q_PROPERTY(QStringList SupportedUriSchemes READ SupportedUriSchemes)
Q_PROPERTY(QStringList SupportedMimeTypes READ SupportedMimeTypes)
Q_PROPERTY(bool FullScreen READ FullScreen WRITE setFullScreen)
public:
explicit MprisBase(QObject *parent = 0);
bool FullScreen() const { return false; }
bool CanSetFullscreen() const { return false; }
bool CanQuit() const { return false; }
bool CanRaise() const { return false; }
bool HasTrackList() const { return false; }
QStringList SupportedUriSchemes();
QStringList SupportedMimeTypes();
QString Identity();
QString DesktopEntry();
void setFullScreen(const bool);
private:
QString m_dekstopEntry;
QString m_identity;
public slots:
void Raise();
void Quit();
};
/*
https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html
*/
#endif // MPRISBASE_H
#include "mprisplayer.h"
#include <QDebug>
#include <QDBusConnection>
#include <QDBusMessage>
MprisPlayer::MprisPlayer(AudioPlayer *parent) : QDBusAbstractAdaptor(parent)
{
m_player = parent;
connect(m_player, &QMediaPlayer::stateChanged, this, &MprisPlayer::playbackStatusChanged);
connect(m_player, static_cast<void(QMediaPlayer::*)()>(&QMediaPlayer::metaDataChanged),
this, &MprisPlayer::metaDataChanged);
}
void MprisPlayer::Play()
{
m_player->startPlayback();
}
void MprisPlayer::PlayPause()
{
m_player->togglePlayback();
}
void MprisPlayer::Pause()
{
m_player->pauseOrStopPlayback();
}
void MprisPlayer::Stop()
{
m_player->stopPlayback();
}
QString MprisPlayer::PlaybackStatus()
{
switch(m_player->state()) {
case QMediaPlayer::PlayingState:
return "Playing";
case QMediaPlayer::PausedState:
return "Paused";
default:
return "Stopped";
}
}
qint64 MprisPlayer::Position()
{
return m_player->position();
}
double MprisPlayer::MinimumRate()
{
return 1.0;
}
double MprisPlayer::MaximumRate()
{
return 1.0;
}
double MprisPlayer::Rate()
{
return m_player->playbackRate();
}
double MprisPlayer::Volume()
{
return m_player->volume();
}
QString MprisPlayer::LoopStatus()
{
return "None";
}
QVariantMap MprisPlayer::Metadata()
{
QVariantMap metadata;
metadata.insert("mpris:trackid", QString("/org/mpris/MediaPlayer2/Track/%1").arg(1));
metadata.insert("xesam:title", m_player->title());
metadata.insert("xesam:genre", m_player->genre());
// TODO add more metadata
return metadata;
}
void MprisPlayer::playbackStatusChanged()
{
QVariantMap changedProps;
changedProps.insert("PlaybackStatus", PlaybackStatus());
changedProps.insert("CanPlay", CanPlay());
notifyPropertyChanged(changedProps);
}
void MprisPlayer::metaDataChanged()
{
QVariantMap changedProps;
changedProps.insert("Metadata", Metadata());
notifyPropertyChanged(changedProps);
}
void MprisPlayer::notifyPropertyChanged(const QVariantMap &changedProps)
{
QDBusMessage signal = QDBusMessage::createSignal(
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged");
signal << "org.mpris.MediaPlayer2.Player";
signal << changedProps;
signal << QStringList();
QDBusConnection::sessionBus().send(signal);
}
// Not implemeted function
void MprisPlayer::Next()
{
qDebug() << "MprisBase: Next not supported";
}
void MprisPlayer::Previous()
{
qDebug() << "MprisBase: Previous not supported";
}
void MprisPlayer::setRate(const double rate)
{
qDebug() << "MprisBase: setRate not supported: " << rate;
}
void MprisPlayer::setVolume(const double volume)
{
qDebug() << "MprisBase: setVolume not supported: " << volume;
}
void MprisPlayer::setLoopStatus(QString loop)
{
qDebug() << "MprisBase: setLoopStatus not supported: " << loop;
}
void MprisPlayer::setShuffle(const bool suffle)
{
qDebug() << "MprisBase: setShuffle not supported: " << suffle;
}
void MprisPlayer::OpenUri(QString uri)
{
qDebug() << "MprisBase: OpenUri not supported: " << uri;
}
void MprisPlayer::Seek(qint64 offset)
{
qDebug() << "MprisBase: Seek not supported: " << offset;
}
void MprisPlayer::SetPosition(QString trackId, qint64 position)
{
qDebug() << "MprisBase: SetPosition not supported: " << trackId << " - " << position;
}
#ifndef MPRISPLAYER_H
#define MPRISPLAYER_H
#include <QObject>
#include <QDBusAbstractAdaptor>
#include <QMediaPlayer>
#include "audioplayer.h"
class MprisPlayer : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player")
Q_PROPERTY(bool CanControl READ CanControl)
Q_PROPERTY(bool CanPlay READ CanPlay)
Q_PROPERTY(bool CanPause READ CanPause)
Q_PROPERTY(bool CanGoNext READ CanGoNext)
Q_PROPERTY(bool CanGoPrevious READ CanGoPrevious)
Q_PROPERTY(bool CanSeek READ CanSeek)
Q_PROPERTY(bool Shuffle READ Shuffle WRITE setShuffle)
Q_PROPERTY(qint64 Position READ Position)
Q_PROPERTY(double MinimumRate READ MinimumRate)
Q_PROPERTY(double MaximumRate READ MaximumRate)
Q_PROPERTY(double Rate READ Rate WRITE setRate)
Q_PROPERTY(double Volume READ Volume WRITE setVolume)
Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus)
Q_PROPERTY(QString LoopStatus READ LoopStatus WRITE setLoopStatus)
Q_PROPERTY(QVariantMap Metadata READ Metadata)
public:
explicit MprisPlayer(AudioPlayer *parent);
bool CanControl() { return true; }
bool CanPlay() { return m_player->isPlayable(); }
bool CanPause() { return true; }
bool CanGoNext() { return false; }
bool CanGoPrevious() { return false; }
bool CanSeek() { return false; }
bool Shuffle() const { return false; }
void setShuffle(const bool);
qint64 Position();
double MinimumRate();
double MaximumRate();
double Rate();
double Volume();
void setRate(const double);
void setVolume(const double);
QString PlaybackStatus();
QString LoopStatus();
void setLoopStatus(QString);
QVariantMap Metadata();
private:
AudioPlayer *m_player;
QMediaPlayer *m_qmediaplayer;
public slots:
void Play();
void PlayPause();
void Pause();
void Stop();
void Next();
void Previous();
void OpenUri(QString);
void Seek(qint64);
void SetPosition(QString, qint64);
private slots:
void playbackStatusChanged();
void metaDataChanged();
void notifyPropertyChanged(const QVariantMap &);
};
#endif // MPRISPLAYER_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment