PulseAudio: make code more robust

Some asynchronous operations return a pa_operation pointer, which can
be null if the operation fails. In some cases we were not checking that
the returned object was non null, leading to some asserts being raised
in pa_operation_unref.

Change-Id: Iff1cc67b7f79b758fa81d79e658debb1d737b29f
Reviewed-by: Christian Stromme <christian.stromme@qt.io>
This commit is contained in:
Yoann Lopes
2016-09-20 19:28:40 +03:00
parent bf5c7ca718
commit 4bae769725
2 changed files with 95 additions and 31 deletions

View File

@@ -437,7 +437,11 @@ void QSoundEffectPrivate::setSource(const QUrl &url)
if (m_pulseStream && !pa_stream_is_corked(m_pulseStream)) {
pa_stream_set_write_callback(m_pulseStream, 0, 0);
pa_stream_set_underflow_callback(m_pulseStream, 0, 0);
pa_operation_unref(pa_stream_cork(m_pulseStream, 1, 0, 0));
pa_operation *op = pa_stream_cork(m_pulseStream, 1, 0, 0);
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to cork stream");
}
setPlaying(false);
@@ -619,7 +623,11 @@ void QSoundEffectPrivate::emptyStream(EmptyStreamOptions options)
m_emptying = true;
pa_stream_set_write_callback(m_pulseStream, 0, 0);
pa_stream_set_underflow_callback(m_pulseStream, 0, 0);
pa_operation_unref(pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef()));
pa_operation *op = pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to flush stream");
}
void QSoundEffectPrivate::emptyComplete(void *stream, bool reload)
@@ -631,8 +639,13 @@ void QSoundEffectPrivate::emptyComplete(void *stream, bool reload)
m_emptying = false;
if ((pa_stream *)stream == m_pulseStream)
pa_operation_unref(pa_stream_cork(m_pulseStream, 1, reload ? stream_cork_callback : 0, m_ref->getRef()));
if ((pa_stream *)stream == m_pulseStream) {
pa_operation *op = pa_stream_cork(m_pulseStream, 1, reload ? stream_cork_callback : 0, m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to cork stream");
}
}
void QSoundEffectPrivate::sampleReady()
@@ -666,7 +679,11 @@ void QSoundEffectPrivate::sampleReady()
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = m_sample->data().size();
pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef()));
pa_operation *op = pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
} else {
streamReady();
}
@@ -679,12 +696,20 @@ void QSoundEffectPrivate::sampleReady()
newBufferAttr.minreq = bufferAttr->tlength / 2;
newBufferAttr.prebuf = -1;
newBufferAttr.fragsize = -1;
pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_reset_buffer_callback, m_ref->getRef()));
pa_operation *op = pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_reset_buffer_callback, m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
} else if (bufferAttr->prebuf > uint32_t(m_sample->data().size())) {
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = m_sample->data().size();
pa_operation_unref(pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef()));
pa_operation *op = pa_stream_set_buffer_attr(m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
} else {
streamReady();
}
@@ -989,7 +1014,11 @@ void QSoundEffectPrivate::stream_state_callback(pa_stream *s, void *userdata)
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = self->m_sample->data().size();
pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, self->m_ref->getRef());
pa_operation *op = pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, self->m_ref->getRef());
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
} else {
QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection);
}
@@ -1026,7 +1055,7 @@ void QSoundEffectPrivate::stream_reset_buffer_callback(pa_stream *s, int success
return;
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to reset buffer attribute");
qWarning("QSoundEffect(pulseaudio): failed to reset buffer attribute");
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_reset_buffer_callback";
#endif
@@ -1036,7 +1065,11 @@ void QSoundEffectPrivate::stream_reset_buffer_callback(pa_stream *s, int success
pa_buffer_attr newBufferAttr;
newBufferAttr = *bufferAttr;
newBufferAttr.prebuf = self->m_sample->data().size();
pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, userdata);
pa_operation *op = pa_stream_set_buffer_attr(self->m_pulseStream, &newBufferAttr, stream_adjust_prebuffer_callback, userdata);
if (op)
pa_operation_unref(op);
else
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
} else {
QMetaObject::invokeMethod(self, "streamReady", Qt::QueuedConnection);
}
@@ -1055,7 +1088,7 @@ void QSoundEffectPrivate::stream_adjust_prebuffer_callback(pa_stream *s, int suc
return;
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to adjust pre-buffer attribute");
qWarning("QSoundEffect(pulseaudio): failed to adjust pre-buffer attribute");
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_adjust_prebuffer_callback";
#endif
@@ -1090,7 +1123,7 @@ void QSoundEffectPrivate::stream_cork_callback(pa_stream *s, int success, void *
return;
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to stop");
qWarning("QSoundEffect(pulseaudio): failed to stop");
#ifdef QT_PA_DEBUG
qDebug() << self << "stream_cork_callback";
#endif
@@ -1110,7 +1143,7 @@ void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void
return;
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to drain");
qWarning("QSoundEffect(pulseaudio): failed to drain");
QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, false));
}
@@ -1128,7 +1161,7 @@ void QSoundEffectPrivate::stream_flush_reload_callback(pa_stream *s, int success
return;
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to drain");
qWarning("QSoundEffect(pulseaudio): failed to drain");
QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, true));
}

View File

@@ -170,15 +170,30 @@ static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32
case PA_SUBSCRIPTION_EVENT_NEW:
case PA_SUBSCRIPTION_EVENT_CHANGE:
switch (facility) {
case PA_SUBSCRIPTION_EVENT_SERVER:
pa_operation_unref(pa_context_get_server_info(context, serverInfoCallback, userdata));
case PA_SUBSCRIPTION_EVENT_SERVER: {
pa_operation *op = pa_context_get_server_info(context, serverInfoCallback, userdata);
if (op)
pa_operation_unref(op);
else
qWarning("PulseAudioService: failed to get server info");
break;
case PA_SUBSCRIPTION_EVENT_SINK:
pa_operation_unref(pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata));
}
case PA_SUBSCRIPTION_EVENT_SINK: {
pa_operation *op = pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata);
if (op)
pa_operation_unref(op);
else
qWarning("PulseAudioService: failed to get sink info");
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
pa_operation_unref(pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata));
}
case PA_SUBSCRIPTION_EVENT_SOURCE: {
pa_operation *op = pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata);
if (op)
pa_operation_unref(op);
else
qWarning("PulseAudioService: failed to get source info");
break;
}
default:
break;
}
@@ -328,11 +343,15 @@ void QPulseAudioEngine::prepare()
pa_context_set_state_callback(m_context, contextStateCallback, this);
pa_context_set_subscribe_callback(m_context, event_cb, this);
pa_operation_unref(pa_context_subscribe(m_context,
pa_operation *op = pa_context_subscribe(m_context,
pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK |
PA_SUBSCRIPTION_MASK_SOURCE |
PA_SUBSCRIPTION_MASK_SERVER),
NULL, NULL));
NULL, NULL);
if (op)
pa_operation_unref(op);
else
qWarning("PulseAudioService: failed to subscribe to context notifications");
} else {
pa_context_unref(m_context);
m_context = 0;
@@ -376,21 +395,33 @@ void QPulseAudioEngine::updateDevices()
// Get default input and output devices
pa_operation *operation = pa_context_get_server_info(m_context, serverInfoCallback, this);
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
if (operation) {
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
} else {
qWarning("PulseAudioService: failed to get server info");
}
// Get output devices
operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this);
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
if (operation) {
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
} else {
qWarning("PulseAudioService: failed to get sink info");
}
// Get input devices
operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this);
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
if (operation) {
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
pa_threaded_mainloop_wait(m_mainLoop);
pa_operation_unref(operation);
} else {
qWarning("PulseAudioService: failed to get source info");
}
unlock();
}