/*
* Copyright (C) 2013-2016 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Author: Michael Zanetti
*/
#include "mediaplayer.h"
#include
#include
class MetaDataObject: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant title READ title NOTIFY metaDataChanged)
Q_PROPERTY(QVariant resolution READ resolution NOTIFY metaDataChanged)
public:
MetaDataObject(MediaPlayer* parent = nullptr)
: QObject(parent)
, m_source(parent)
{
}
QVariant title() const { return getProperty("title"); }
QVariant resolution() const { return getProperty("resolution", QSize(640, 640)); }
QVariant getProperty(const QString& property, QVariant defaultValue = QVariant()) const {
MediaDataSource* sourceData = MediaPlayerDataController::instance()->dataForSource(m_source->source());
QVariant metaData = sourceData->metaData();
return metaData.toMap().value(property, defaultValue);
}
Q_SIGNALS:
void metaDataChanged();
private:
MediaPlayer* m_source;
};
MediaPlayer::MediaPlayer(QObject* parent)
: QObject(parent)
, m_playbackState(StoppedState)
, m_status(NoMedia)
, m_metaData(new MetaDataObject(this))
, m_playlist(nullptr)
{
qsrand(time(nullptr));
m_timer.setInterval(100);
connect(&m_timer, &QTimer::timeout, this, &MediaPlayer::processTimer);
connect(MediaPlayerDataController::instance(), &MediaPlayerDataController::sourceAboutToBeRemoved,
this, [this](const QUrl& source) {
MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source);
if (!dataSource) return;
disconnect(dataSource, &MediaDataSource::durationChanged, this, &MediaPlayer::durationChanged);
disconnect(dataSource, &MediaDataSource::seekableChanged, this, &MediaPlayer::seekableChanged);
disconnect(dataSource, &MediaDataSource::availabilityChanged, this, &MediaPlayer::availabilityChanged);
disconnect(dataSource, &MediaDataSource::metaDataChanged, m_metaData, &MetaDataObject::metaDataChanged);
});
connect(MediaPlayerDataController::instance(), &MediaPlayerDataController::sourceAdded,
this, [this](const QUrl& source) {
MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source);
if (!dataSource) return;
connect(dataSource, &MediaDataSource::durationChanged, this, &MediaPlayer::durationChanged);
connect(dataSource, &MediaDataSource::seekableChanged,this, &MediaPlayer::seekableChanged);
connect(dataSource, &MediaDataSource::availabilityChanged,this, &MediaPlayer::availabilityChanged);
connect(dataSource, &MediaDataSource::metaDataChanged, m_metaData, &MetaDataObject::metaDataChanged);
});
}
QUrl MediaPlayer::source() const
{
return m_source;
}
void MediaPlayer::setSource(const QUrl &source)
{
if (m_source != source) {
m_source = source;
Q_EMIT sourceChanged(source);
Q_EMIT durationChanged(duration());
Q_EMIT seekableChanged(isSeekable());
Q_EMIT availabilityChanged(availability());
m_position = 0;
Q_EMIT positionChanged(m_position);
Q_EMIT m_metaData->metaDataChanged();
m_status = availability()==Available ? Loaded : InvalidMedia;
Q_EMIT statusChanged();
}
}
DeclarativePlaylist *MediaPlayer::playlist() const
{
return m_playlist;
}
void MediaPlayer::setPlaylist(DeclarativePlaylist *playlist)
{
if (playlist == m_playlist)
return;
m_playlist = playlist;
Q_EMIT playlistChanged();
}
MediaPlayer::PlaybackState MediaPlayer::playbackState() const
{
return m_playbackState;
}
int MediaPlayer::position() const
{
return m_position;
}
int MediaPlayer::duration() const
{
MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
if (dataSource) return dataSource->duration();
return 0;
}
MediaPlayer::Error MediaPlayer::error() const
{
return NoError;
}
QString MediaPlayer::errorString() const
{
return QString();
}
void MediaPlayer::pause()
{
if (m_playbackState == PlayingState) {
m_playbackState = PausedState;
Q_EMIT playbackStateChanged(m_playbackState);
m_timer.stop();
}
}
void MediaPlayer::play()
{
if (m_playbackState != PlayingState) {
m_playbackState = PlayingState;
Q_EMIT playbackStateChanged(m_playbackState);
Q_EMIT playing();
m_timer.start();
}
}
void MediaPlayer::stop()
{
if (m_playbackState != StoppedState) {
m_playbackState = StoppedState;
Q_EMIT playbackStateChanged(m_playbackState);
m_timer.stop();
m_position = 0;
Q_EMIT positionChanged(m_position);
}
}
void MediaPlayer::seek(int position)
{
if (status() != Loaded) return;
int newPosition = qMin(qMax(0, position), duration());
if (newPosition != m_position) {
m_position = newPosition;
Q_EMIT positionChanged(m_position);
}
}
void MediaPlayer::processTimer()
{
if (m_position + m_timer.interval() < duration()) {
m_position += m_timer.interval();
Q_EMIT positionChanged(m_position);
} else {
pause();
}
}
MediaPlayer::AudioRole MediaPlayer::audioRole() const
{
return MediaPlayer::MusicRole;
}
void MediaPlayer::setAudioRole(MediaPlayer::AudioRole audioRole)
{
Q_UNUSED(audioRole);
}
bool MediaPlayer::isSeekable() const
{
MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
if (dataSource) return dataSource->isSeekable();
return true;
}
MediaPlayer::Availability MediaPlayer::availability() const
{
MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
if (dataSource) return dataSource->availability();
return Available;
}
QObject *MediaPlayer::metaData() const
{
return m_metaData;
}
MediaDataSource::MediaDataSource(QObject *parent)
: QObject(parent)
, m_seekable(true)
, m_duration((qrand() % 20000) + 10000)
, m_availability(MediaPlayer::Available)
{
}
MediaDataSource::~MediaDataSource()
{
if (!m_source.isEmpty()) MediaPlayerDataController::instance()->unregisterDataSource(this);
}
void MediaDataSource::setSource(const QUrl &source)
{
if (m_source != source) {
if (!m_source.isEmpty()) MediaPlayerDataController::instance()->unregisterDataSource(this);
m_source = source;
if (!m_source.isEmpty()) MediaPlayerDataController::instance()->registerDataSource(this);
Q_EMIT sourceChanged();
}
}
void MediaDataSource::setSeekable(bool seekable)
{
if (m_seekable != seekable) {
m_seekable = seekable;
Q_EMIT seekableChanged(m_seekable);
}
}
void MediaDataSource::setDuration(int duration)
{
if (m_duration != duration) {
m_duration = duration;
Q_EMIT durationChanged(m_duration);
}
}
void MediaDataSource::setAvailability(MediaPlayer::Availability availability)
{
if (m_availability != availability) {
m_availability = availability;
Q_EMIT availabilityChanged(m_availability);
}
}
void MediaDataSource::setMetaData(const QVariant& metaData)
{
if (m_metaData != metaData) {
m_metaData = metaData;
Q_EMIT metaDataChanged();
}
}
MediaPlayerDataController *MediaPlayerDataController::instance()
{
static MediaPlayerDataController* instance = nullptr;
if (!instance) { instance = new MediaPlayerDataController(); }
return instance;
}
void MediaPlayerDataController::registerDataSource(MediaDataSource *dataSource)
{
m_dataSources[dataSource->source()] = dataSource;
Q_EMIT sourceAdded(dataSource->source());
}
void MediaPlayerDataController::unregisterDataSource(MediaDataSource *dataSource)
{
QList keys = m_dataSources.keys(dataSource);
Q_FOREACH(const QUrl& key, keys) {
Q_EMIT sourceAboutToBeRemoved(key);
m_dataSources.remove(key);
}
}
MediaDataSource *MediaPlayerDataController::dataForSource(const QUrl &source)
{
if (!m_dataSources.contains(source)) {
static MediaDataSource defaultSource;
return &defaultSource;
}
return m_dataSources[source];
}
#include "mediaplayer.moc"