Resource policy for QAudioOutput.

Change-Id: I561b8b6216d94f56362f52d65482f6e9cd6d7f3a

Conflicts:
	src/plugins/pulseaudio/qaudiooutput_pulse.cpp
This commit is contained in:
Juho Hämäläinen
2014-02-21 10:48:18 +02:00
committed by Martin Jones
parent dc7453c28a
commit 4822715845
2 changed files with 113 additions and 0 deletions

View File

@@ -36,6 +36,9 @@
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <private/qaudiohelpers_p.h> #include <private/qaudiohelpers_p.h>
#include <private/qmediaresourcepolicy_p.h>
#include <private/qmediaresourceset_p.h>
#include "qaudiooutput_pulse.h" #include "qaudiooutput_pulse.h"
#include "qaudiodeviceinfo_pulse.h" #include "qaudiodeviceinfo_pulse.h"
#include "qpulseaudioengine.h" #include "qpulseaudioengine.h"
@@ -51,6 +54,11 @@ const int LowLatencyBufferSizeMs = 40;
#define LOW_LATENCY_CATEGORY_NAME "game" #define LOW_LATENCY_CATEGORY_NAME "game"
// 2 second timeout for releasing resources.
// Value was selected as combination of fair dice roll and personal
// feeling when testing.
#define RELEASE_TIMER_TIMEOUT (1000 * 2)
static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata) static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata)
{ {
Q_UNUSED(stream); Q_UNUSED(stream);
@@ -149,6 +157,7 @@ QPulseAudioOutput::QPulseAudioOutput(const QByteArray &device)
: m_device(device) : m_device(device)
, m_errorState(QAudio::NoError) , m_errorState(QAudio::NoError)
, m_deviceState(QAudio::StoppedState) , m_deviceState(QAudio::StoppedState)
, m_wantedState(QAudio::StoppedState)
, m_pullMode(true) , m_pullMode(true)
, m_opened(false) , m_opened(false)
, m_audioSource(0) , m_audioSource(0)
@@ -164,14 +173,29 @@ QPulseAudioOutput::QPulseAudioOutput(const QByteArray &device)
, m_resuming(false) , m_resuming(false)
, m_volume(1.0) , m_volume(1.0)
{ {
m_resources = QMediaResourcePolicy::createResourceSet<QMediaPlayerResourceSetInterface>();
Q_ASSERT(m_resources);
connect(m_resources, SIGNAL(resourcesGranted()), SLOT(handleResourcesGranted()));
//denied signal should be queued to have correct state update process,
//since in playOrPause, when acquire is call on resource set, it may trigger a resourcesDenied signal immediately,
//so handleResourcesDenied should be processed later, otherwise it will be overwritten by state update later in playOrPause.
connect(m_resources, SIGNAL(resourcesDenied()), this, SLOT(handleResourcesDenied()), Qt::QueuedConnection);
connect(m_resources, SIGNAL(resourcesLost()), SLOT(handleResourcesLost()));
connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed())); connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed()));
m_releaseTimer = new QTimer(this);
m_releaseTimer->setSingleShot(true);
connect(m_releaseTimer, SIGNAL(timeout()), this, SLOT(handleRelease()));
} }
QPulseAudioOutput::~QPulseAudioOutput() QPulseAudioOutput::~QPulseAudioOutput()
{ {
stopReleaseTimer();
close(); close();
m_resources->release();
disconnect(m_tickTimer, SIGNAL(timeout())); disconnect(m_tickTimer, SIGNAL(timeout()));
QCoreApplication::processEvents(); QCoreApplication::processEvents();
QMediaResourcePolicy::destroyResourceSet(m_resources);
} }
void QPulseAudioOutput::setError(QAudio::Error error) void QPulseAudioOutput::setError(QAudio::Error error)
@@ -231,11 +255,14 @@ void QPulseAudioOutput::start(QIODevice *device)
return; return;
} }
m_wantedState = QAudio::ActiveState;
setState(QAudio::ActiveState); setState(QAudio::ActiveState);
} }
QIODevice *QPulseAudioOutput::start() QIODevice *QPulseAudioOutput::start()
{ {
stopReleaseTimer();
setState(QAudio::StoppedState); setState(QAudio::StoppedState);
setError(QAudio::NoError); setError(QAudio::NoError);
@@ -255,6 +282,7 @@ QIODevice *QPulseAudioOutput::start()
m_audioSource = new PulseOutputPrivate(this); m_audioSource = new PulseOutputPrivate(this);
m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
m_wantedState = QAudio::IdleState;
setState(QAudio::IdleState); setState(QAudio::IdleState);
return m_audioSource; return m_audioSource;
@@ -294,6 +322,9 @@ bool QPulseAudioOutput::open()
qDebug() << "Frame size: " << pa_frame_size(&spec); qDebug() << "Frame size: " << pa_frame_size(&spec);
#endif #endif
if (!m_resources->isGranted())
m_resources->acquire();
pulseEngine->lock(); pulseEngine->lock();
qint64 bytesPerSecond = m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8; qint64 bytesPerSecond = m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8;
@@ -325,6 +356,7 @@ bool QPulseAudioOutput::open()
if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, NULL, NULL) < 0) { if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, NULL, NULL) < 0) {
qWarning() << "pa_stream_connect_playback() failed!"; qWarning() << "pa_stream_connect_playback() failed!";
m_resources->release();
pa_stream_unref(m_stream); pa_stream_unref(m_stream);
m_stream = 0; m_stream = 0;
pulseEngine->unlock(); pulseEngine->unlock();
@@ -512,7 +544,9 @@ void QPulseAudioOutput::stop()
return; return;
close(); close();
restartReleaseTimer();
m_wantedState = QAudio::StoppedState;
setError(QAudio::NoError); setError(QAudio::NoError);
setState(QAudio::StoppedState); setState(QAudio::StoppedState);
} }
@@ -566,6 +600,9 @@ qint64 QPulseAudioOutput::processedUSecs() const
void QPulseAudioOutput::resume() void QPulseAudioOutput::resume()
{ {
if (m_deviceState == QAudio::SuspendedState) { if (m_deviceState == QAudio::SuspendedState) {
stopReleaseTimer();
m_resources->acquire();
m_resuming = true; m_resuming = true;
QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance();
@@ -584,6 +621,7 @@ void QPulseAudioOutput::resume()
m_tickTimer->start(m_periodTime); m_tickTimer->start(m_periodTime);
m_wantedState = m_pullMode ? QAudio::ActiveState : QAudio::IdleState;
setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState); setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState);
setError(QAudio::NoError); setError(QAudio::NoError);
} }
@@ -600,6 +638,13 @@ QAudioFormat QPulseAudioOutput::format() const
} }
void QPulseAudioOutput::suspend() void QPulseAudioOutput::suspend()
{
m_wantedState = QAudio::SuspendedState;
restartReleaseTimer();
internalSuspend();
}
void QPulseAudioOutput::internalSuspend()
{ {
if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) { if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) {
setError(QAudio::NoError); setError(QAudio::NoError);
@@ -699,6 +744,60 @@ void QPulseAudioOutput::onPulseContextFailed()
setState(QAudio::StoppedState); setState(QAudio::StoppedState);
} }
void QPulseAudioOutput::handleResourcesGranted()
{
#ifdef DEBUG_RESOURCE
qDebug() << Q_FUNC_INFO << "Resources granted, current state " << m_deviceState << " wanted state " << m_wantedState;
#endif
// If we were playing, but got suspended, restart
if (m_deviceState == QAudio::SuspendedState &&
m_wantedState == QAudio::ActiveState) {
resume();
}
}
void QPulseAudioOutput::handleResourcesLost()
{
#ifdef DEBUG_RESOURCE
qDebug() << Q_FUNC_INFO << "Resources lost, current state " << m_deviceState << " wanted state " << m_wantedState;
#endif
// If we lose resources, suspend
if (m_deviceState != QAudio::StoppedState) {
internalSuspend();
}
}
void QPulseAudioOutput::handleResourcesDenied()
{
#ifdef DEBUG_RESOURCE
qDebug() << Q_FUNC_INFO << "Resources denied, current state " << m_deviceState << " wanted state " << m_wantedState;
#endif
// If we are denied resources, suspend
if (m_deviceState != QAudio::StoppedState)
internalSuspend();
}
void QPulseAudioOutput::restartReleaseTimer()
{
stopReleaseTimer();
m_releaseTimer->start(RELEASE_TIMER_TIMEOUT);
}
void QPulseAudioOutput::stopReleaseTimer()
{
m_releaseTimer->stop();
}
void QPulseAudioOutput::handleRelease()
{
if (m_deviceState != QAudio::ActiveState) {
#ifdef DEBUG_RESOURCE
qDebug() << "handleRelease currentState " << m_deviceState;
#endif
m_resources->release();
}
}
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qaudiooutput_pulse.cpp" #include "moc_qaudiooutput_pulse.cpp"

View File

@@ -58,6 +58,8 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
class QMediaPlayerResourceSetInterface;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QPulseAudioOutput : public QAbstractAudioOutput class QPulseAudioOutput : public QAbstractAudioOutput
@@ -104,10 +106,19 @@ private:
bool open(); bool open();
void close(); void close();
qint64 write(const char *data, qint64 len); qint64 write(const char *data, qint64 len);
void internalSuspend();
void restartReleaseTimer();
void stopReleaseTimer();
private Q_SLOTS: private Q_SLOTS:
void userFeed(); void userFeed();
void onPulseContextFailed(); void onPulseContextFailed();
void handleResourcesGranted();
void handleResourcesLost();
void handleResourcesDenied();
void handleRelease();
private: private:
QByteArray m_device; QByteArray m_device;
@@ -115,6 +126,7 @@ private:
QAudioFormat m_format; QAudioFormat m_format;
QAudio::Error m_errorState; QAudio::Error m_errorState;
QAudio::State m_deviceState; QAudio::State m_deviceState;
QAudio::State m_wantedState;
bool m_pullMode; bool m_pullMode;
bool m_opened; bool m_opened;
QIODevice *m_audioSource; QIODevice *m_audioSource;
@@ -136,6 +148,8 @@ private:
qreal m_volume; qreal m_volume;
pa_sample_spec m_spec; pa_sample_spec m_spec;
QMediaPlayerResourceSetInterface *m_resources;
QTimer *m_releaseTimer;
}; };
class PulseOutputPrivate : public QIODevice class PulseOutputPrivate : public QIODevice