summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@qt.io>2016-05-03 12:47:57 +0200
committerChristian Stromme <christian.stromme@qt.io>2016-06-20 12:41:30 +0000
commit970128036ab6850ba292465806afe5b799851ac1 (patch)
treeb97d401c26b5d3a50ae67367200148663a6292ad /src
parent6d48deed4889f0da3ed0499b051241ff3fe8f1b2 (diff)
PulseAudio: keep device list up to date.
The list of devices was cached on startup and was never updated when a device was plugged, unplugged or if the default device changed. We now use the event subscription mechanism in PulseAudio to get notified of these changes and update the list accordingly. Change-Id: I5fe1c81494702aa9d38b91009621629dc9606323 Reviewed-by: Karim Pinter <karim.pinter@theqtcompany.com> Reviewed-by: Christian Stromme <christian.stromme@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.cpp118
-rw-r--r--src/plugins/pulseaudio/qpulseaudioengine.h9
2 files changed, 103 insertions, 24 deletions
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp
index 9b9da6b62..e9510fd6a 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.cpp
+++ b/src/plugins/pulseaudio/qpulseaudioengine.cpp
@@ -75,8 +75,10 @@ static void serverInfoCallback(pa_context *context, const pa_server_info *info,
#endif
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
+ pulseEngine->m_serverLock.lockForWrite();
pulseEngine->m_defaultSink = info->default_sink_name;
pulseEngine->m_defaultSource = info->default_source_name;
+ pulseEngine->m_serverLock.unlock();
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
}
@@ -84,11 +86,6 @@ static void serverInfoCallback(pa_context *context, const pa_server_info *info,
static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata)
{
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- QMap<pa_sink_state, QString> stateMap;
- stateMap[PA_SINK_INVALID_STATE] = "n/a";
- stateMap[PA_SINK_RUNNING] = "RUNNING";
- stateMap[PA_SINK_IDLE] = "IDLE";
- stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";
if (isLast < 0) {
qWarning() << QString("Failed to get sink information: %s").arg(pa_strerror(pa_context_errno(context)));
@@ -103,6 +100,12 @@ static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int
Q_ASSERT(info);
#ifdef DEBUG_PULSE
+ QMap<pa_sink_state, QString> stateMap;
+ stateMap[PA_SINK_INVALID_STATE] = "n/a";
+ stateMap[PA_SINK_RUNNING] = "RUNNING";
+ stateMap[PA_SINK_IDLE] = "IDLE";
+ stateMap[PA_SINK_SUSPENDED] = "SUSPENDED";
+
qDebug() << QString("Sink #%1\n"
"\tState: %2\n"
"\tName: %3\n"
@@ -114,8 +117,10 @@ static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int
#endif
QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
+
+ QWriteLocker locker(&pulseEngine->m_sinkLock);
pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sinks.append(info->name);
+ pulseEngine->m_sinks.insert(info->index, info->name);
}
static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata)
@@ -123,12 +128,6 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
Q_UNUSED(context)
QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
- QMap<pa_source_state, QString> stateMap;
- stateMap[PA_SOURCE_INVALID_STATE] = "n/a";
- stateMap[PA_SOURCE_RUNNING] = "RUNNING";
- stateMap[PA_SOURCE_IDLE] = "IDLE";
- stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED";
-
if (isLast) {
pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0);
return;
@@ -137,6 +136,12 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
Q_ASSERT(info);
#ifdef DEBUG_PULSE
+ QMap<pa_source_state, QString> stateMap;
+ stateMap[PA_SOURCE_INVALID_STATE] = "n/a";
+ stateMap[PA_SOURCE_RUNNING] = "RUNNING";
+ stateMap[PA_SOURCE_IDLE] = "IDLE";
+ stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED";
+
qDebug() << QString("Source #%1\n"
"\tState: %2\n"
"\tName: %3\n"
@@ -148,8 +153,57 @@ static void sourceInfoCallback(pa_context *context, const pa_source_info *info,
#endif
QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec);
+
+ QWriteLocker locker(&pulseEngine->m_sourceLock);
pulseEngine->m_preferredFormats.insert(info->name, format);
- pulseEngine->m_sources.append(info->name);
+ pulseEngine->m_sources.insert(info->index, info->name);
+}
+
+static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32_t index, void* userdata)
+{
+ QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata);
+
+ int type = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
+ int facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
+
+ switch (type) {
+ 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));
+ break;
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ pa_operation_unref(pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata));
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ pa_operation_unref(pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata));
+ break;
+ default:
+ break;
+ }
+ break;
+ case PA_SUBSCRIPTION_EVENT_REMOVE:
+ switch (facility) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ pulseEngine->m_sinkLock.lockForWrite();
+ pulseEngine->m_preferredFormats.remove(pulseEngine->m_sinks.value(index));
+ pulseEngine->m_sinks.remove(index);
+ pulseEngine->m_sinkLock.unlock();
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ pulseEngine->m_sourceLock.lockForWrite();
+ pulseEngine->m_preferredFormats.remove(pulseEngine->m_sources.value(index));
+ pulseEngine->m_sources.remove(index);
+ pulseEngine->m_sourceLock.unlock();
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
static void contextStateCallbackInit(pa_context *context, void *userdata)
@@ -272,6 +326,13 @@ void QPulseAudioEngine::prepare()
if (ok) {
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_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK |
+ PA_SUBSCRIPTION_MASK_SOURCE |
+ PA_SUBSCRIPTION_MASK_SERVER),
+ NULL, NULL));
} else {
pa_context_unref(m_context);
m_context = 0;
@@ -332,14 +393,6 @@ void QPulseAudioEngine::updateDevices()
pa_operation_unref(operation);
unlock();
-
- // Swap the default output to index 0
- m_sinks.removeOne(m_defaultSink);
- m_sinks.prepend(m_defaultSink);
-
- // Swap the default input to index 0
- m_sources.removeOne(m_defaultSource);
- m_sources.prepend(m_defaultSource);
}
void QPulseAudioEngine::onContextFailed()
@@ -360,7 +413,28 @@ QPulseAudioEngine *QPulseAudioEngine::instance()
QList<QByteArray> QPulseAudioEngine::availableDevices(QAudio::Mode mode) const
{
- return mode == QAudio::AudioOutput ? m_sinks : m_sources;
+ QList<QByteArray> devices;
+ QByteArray defaultDevice;
+
+ m_serverLock.lockForRead();
+
+ if (mode == QAudio::AudioOutput) {
+ QReadLocker locker(&m_sinkLock);
+ devices = m_sinks.values();
+ defaultDevice = m_defaultSink;
+ } else {
+ QReadLocker locker(&m_sourceLock);
+ devices = m_sources.values();
+ defaultDevice = m_defaultSource;
+ }
+
+ m_serverLock.unlock();
+
+ // Swap the default device to index 0
+ devices.removeOne(defaultDevice);
+ devices.prepend(defaultDevice);
+
+ return devices;
}
QT_END_NAMESPACE
diff --git a/src/plugins/pulseaudio/qpulseaudioengine.h b/src/plugins/pulseaudio/qpulseaudioengine.h
index 3374ed778..0143d4065 100644
--- a/src/plugins/pulseaudio/qpulseaudioengine.h
+++ b/src/plugins/pulseaudio/qpulseaudioengine.h
@@ -47,6 +47,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qreadwritelock.h>
#include <qaudiosystemplugin.h>
#include <pulse/pulseaudio.h>
#include "qpulsehelpers.h"
@@ -98,13 +99,17 @@ private:
void release();
public:
- QList<QByteArray> m_sinks;
- QList<QByteArray> m_sources;
+ QMap<int, QByteArray> m_sinks;
+ QMap<int, QByteArray> m_sources;
QMap<QByteArray, QAudioFormat> m_preferredFormats;
QByteArray m_defaultSink;
QByteArray m_defaultSource;
+ mutable QReadWriteLock m_sinkLock;
+ mutable QReadWriteLock m_sourceLock;
+ mutable QReadWriteLock m_serverLock;
+
private:
pa_mainloop_api *m_mainLoopApi;
pa_threaded_mainloop *m_mainLoop;