Make PulseAudio implementation of QSoundEffect more robust.

It was crashing when the PulseAudio daemon was not running or was
killed.
When the connection to the daemon fails (or is terminated), it now
tries to reconnect every 30 seconds.
Sounds created before a connection loss will be recreated after
reconnection.

Task-number: QTBUG-32487
Change-Id: Ia63707aa5c70434b834b3079a9950a9b35057b26
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Yoann Lopes
2013-07-25 15:43:41 +02:00
committed by The Qt Project
parent 18d77b2b33
commit cd11c240a6
2 changed files with 47 additions and 8 deletions

View File

@@ -147,9 +147,20 @@ public:
Q_SIGNALS:
void contextReady();
void contextFailed();
void volumeChanged();
private:
private Q_SLOTS:
void onContextFailed()
{
release();
// Try to reconnect later
QTimer::singleShot(30000, this, SLOT(prepare()));
emit contextFailed();
}
void prepare()
{
m_vol = PA_VOLUME_NORM;
@@ -196,12 +207,23 @@ private:
m_prepared = true;
}
private:
void release()
{
if (!m_prepared) return;
if (!m_prepared)
return;
if (m_context) {
pa_context_unref(m_context);
m_context = 0;
}
if (m_mainLoop) {
pa_threaded_mainloop_stop(m_mainLoop);
pa_threaded_mainloop_free(m_mainLoop);
m_mainLoop = 0;
}
m_prepared = false;
}
@@ -221,6 +243,9 @@ private:
#endif
QMetaObject::invokeMethod(self, "contextReady", Qt::QueuedConnection);
break;
case PA_CONTEXT_FAILED:
QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection);
break;
default:
break;
}
@@ -511,6 +536,7 @@ void QSoundEffectPrivate::updateVolume()
PulseDaemonLocker locker;
pa_cvolume volume;
volume.channels = m_pulseSpec.channels;
if (pulseDaemon()->context())
pa_operation_unref(pa_context_set_sink_input_volume(pulseDaemon()->context(), m_sinkInputId, pulseDaemon()->calcVolume(&volume, m_volume), setvolume_callback, m_ref->getRef()));
Q_ASSERT(pa_cvolume_valid(&volume));
#ifdef QT_PA_DEBUG
@@ -535,6 +561,7 @@ void QSoundEffectPrivate::updateMuted()
if (m_sinkInputId < 0)
return;
PulseDaemonLocker locker;
if (pulseDaemon()->context())
pa_operation_unref(pa_context_set_sink_input_mute(pulseDaemon()->context(), m_sinkInputId, m_muted, setmuted_callback, m_ref->getRef()));
#ifdef QT_PA_DEBUG
qDebug() << this << "updateMuted = " << m_muted;
@@ -705,7 +732,7 @@ void QSoundEffectPrivate::sampleReady()
}
#endif
} else {
if (pa_context_get_state(pulseDaemon()->context()) != PA_CONTEXT_READY) {
if (!pulseDaemon()->context() || pa_context_get_state(pulseDaemon()->context()) != PA_CONTEXT_READY) {
connect(pulseDaemon(), SIGNAL(contextReady()), SLOT(contextReady()));
return;
}
@@ -741,6 +768,7 @@ void QSoundEffectPrivate::unloadPulseStream()
pa_stream_disconnect(m_pulseStream);
pa_stream_unref(m_pulseStream);
disconnect(pulseDaemon(), SIGNAL(volumeChanged()), this, SLOT(updateVolume()));
disconnect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed()));
m_pulseStream = 0;
m_reloadCategory = false; // category will be reloaded when we connect anyway
}
@@ -895,6 +923,9 @@ void QSoundEffectPrivate::createPulseStream()
qDebug() << this << "createPulseStream";
#endif
if (!pulseDaemon()->context())
return;
pa_proplist *propList = pa_proplist_new();
if (m_category.isNull()) {
// Meant to be one of the strings "video", "music", "game", "event", "phone", "animation", "production", "a11y", "test"
@@ -906,6 +937,7 @@ void QSoundEffectPrivate::createPulseStream()
pa_proplist_free(propList);
connect(pulseDaemon(), SIGNAL(volumeChanged()), this, SLOT(updateVolume()));
connect(pulseDaemon(), SIGNAL(contextFailed()), this, SLOT(contextFailed()));
if (stream == 0) {
qWarning("QSoundEffect(pulseaudio): Failed to create stream");
@@ -947,6 +979,12 @@ void QSoundEffectPrivate::contextReady()
createPulseStream();
}
void QSoundEffectPrivate::contextFailed()
{
unloadPulseStream();
connect(pulseDaemon(), SIGNAL(contextReady()), this, SLOT(contextReady()));
}
void QSoundEffectPrivate::stream_write_callback(pa_stream *s, size_t length, void *userdata)
{
Q_UNUSED(length);

View File

@@ -111,6 +111,7 @@ private Q_SLOTS:
void sampleReady();
void uploadSample();
void contextReady();
void contextFailed();
void underRun();
void prepare();
void streamReady();