Resource policy for QAudioOutput.
Change-Id: I561b8b6216d94f56362f52d65482f6e9cd6d7f3a Conflicts: src/plugins/pulseaudio/qaudiooutput_pulse.cpp
This commit is contained in:
committed by
Martin Jones
parent
dc7453c28a
commit
4822715845
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user