/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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 .
*/
#include
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "utils/Utils.hpp"
#include "SoundPlayer.hpp"
// =============================================================================
using namespace std;
namespace {
int ForceCloseTimerInterval = 20;
}
class SoundPlayer::Handlers : public linphone::PlayerListener {
public:
Handlers (SoundPlayer *soundPlayer) {
mSoundPlayer = soundPlayer;
}
private:
void onEofReached (const shared_ptr &) override {
QMutex &mutex = mSoundPlayer->mForceCloseMutex;
// Workaround.
// This callback is called in a standard thread of mediastreamer, not a QThread.
// Signals, connect functions, timers... are unavailable.
mutex.lock();
mSoundPlayer->mForceClose = true;
mutex.unlock();
}
SoundPlayer *mSoundPlayer;
};
// -----------------------------------------------------------------------------
SoundPlayer::SoundPlayer (QObject *parent) : QObject(parent) {
CoreManager *coreManager = CoreManager::getInstance();
SettingsModel *settingsModel = coreManager->getSettingsModel();
mForceCloseTimer = new QTimer(this);
mForceCloseTimer->setInterval(ForceCloseTimerInterval);
QObject::connect(mForceCloseTimer, &QTimer::timeout, this, &SoundPlayer::handleEof);
mHandlers = make_shared(this);
QObject::connect(settingsModel, &SettingsModel::ringerDeviceChanged, this, [this] {
rebuildInternalPlayer();
});
buildInternalPlayer();
}
SoundPlayer::~SoundPlayer () {
mForceCloseTimer->stop();
mInternalPlayer->close();
}
// -----------------------------------------------------------------------------
void SoundPlayer::pause () {
if (mPlaybackState == SoundPlayer::PausedState)
return;
if (mInternalPlayer->pause()) {
setError(QStringLiteral("Unable to pause: `%1`").arg(mSource));
return;
}
mForceCloseTimer->stop();
mPlaybackState = SoundPlayer::PausedState;
emit paused();
emit playbackStateChanged(mPlaybackState);
}
void SoundPlayer::play () {
if (mPlaybackState == SoundPlayer::PlayingState)
return;
if (
(mPlaybackState == SoundPlayer::StoppedState || mPlaybackState == SoundPlayer::ErrorState) &&
mInternalPlayer->open(Utils::appStringToCoreString(mSource))
) {
qWarning() << QStringLiteral("Unable to open: `%1`").arg(mSource);
return;
}
if (mInternalPlayer->start()
) {
setError(QStringLiteral("Unable to play: `%1`").arg(mSource));
return;
}
mForceCloseTimer->start();
mPlaybackState = SoundPlayer::PlayingState;
emit playing();
emit playbackStateChanged(mPlaybackState);
}
void SoundPlayer::stop () {
stop(false);
}
// -----------------------------------------------------------------------------
void SoundPlayer::seek (int offset) {
mInternalPlayer->seek(offset);
}
// -----------------------------------------------------------------------------
int SoundPlayer::getPosition () const {
return mInternalPlayer->getCurrentPosition();
}
// -----------------------------------------------------------------------------
void SoundPlayer::buildInternalPlayer () {
CoreManager *coreManager = CoreManager::getInstance();
SettingsModel *settingsModel = coreManager->getSettingsModel();
mInternalPlayer = coreManager->getCore()->createLocalPlayer(
Utils::appStringToCoreString(settingsModel->getRingerDevice()), "", nullptr
);
mInternalPlayer->addListener(mHandlers);
}
void SoundPlayer::rebuildInternalPlayer () {
stop(true);
buildInternalPlayer();
}
void SoundPlayer::stop (bool force) {
if (mPlaybackState == SoundPlayer::StoppedState && !force)
return;
mForceCloseTimer->stop();
mPlaybackState = SoundPlayer::StoppedState;
mInternalPlayer->close();
emit stopped();
emit playbackStateChanged(mPlaybackState);
}
// -----------------------------------------------------------------------------
void SoundPlayer::handleEof () {
mForceCloseMutex.lock();
if (mForceClose) {
mForceClose = false;
stop();
}
mForceCloseMutex.unlock();
}
// -----------------------------------------------------------------------------
void SoundPlayer::setError (const QString &message) {
qWarning() << message;
mInternalPlayer->close();
if (mPlaybackState != SoundPlayer::ErrorState) {
mPlaybackState = SoundPlayer::ErrorState;
emit playbackStateChanged(mPlaybackState);
}
}
// -----------------------------------------------------------------------------
QString SoundPlayer::getSource () const {
return mSource;
}
void SoundPlayer::setSource (const QString &source) {
if (source == mSource)
return;
stop();
mSource = source;
emit sourceChanged(source);
}
// -----------------------------------------------------------------------------
SoundPlayer::PlaybackState SoundPlayer::getPlaybackState () const {
return mPlaybackState;
}
void SoundPlayer::setPlaybackState (PlaybackState playbackState) {
switch (playbackState) {
case PlayingState:
play();
break;
case PausedState:
pause();
break;
case StoppedState:
stop();
break;
case ErrorState:
break;
}
}
// -----------------------------------------------------------------------------
int SoundPlayer::getDuration () const {
return mInternalPlayer->getDuration();
}