Commit c9fa85bb authored by Adrien Béraud's avatar Adrien Béraud

pulseaudio refactoring

* Use factored audio code from AudioLayer
* Properly refresh device list on device change
* After device change, use preferred device if possible

PulseAudio device selection was broken because only
a single device can use echo cancelling at a time,
so when the second device was opened, the first echo-canc3elled stream
was redirected to it.

Use per-stream instead of per-context echo-cancellation.
Only use echo-cancellation for playback and record (disabled for ringtone).

Tuleap: #207
Change-Id: Ib5fcb3795a079a867e44c743892bb1325d0f4eef
parent 2a4ae6aa
......@@ -458,6 +458,10 @@
</arg>
</method>
<signal name="audioDeviceEvent" tp:name-for-bindings="audioDeviceEvent">
<tp:docstring>Signal triggered by changes in the detected audio devices, e.g. a headset being unplugged.</tp:docstring>
</signal>
<method name="getAudioOutputDeviceList" tp:name-for-bindings="getAudioOutputDeviceList">
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorString"/>
<arg type="as" name="list" direction="out">
......
......@@ -127,6 +127,7 @@ DBusClient::initLibrary(int flags)
using DRing::CallSignal;
using DRing::ConfigurationSignal;
using DRing::PresenceSignal;
using DRing::AudioSignal;
using SharedCallback = std::shared_ptr<DRing::CallbackWrapperBase>;
......@@ -193,6 +194,10 @@ DBusClient::initLibrary(int flags)
exportable_callback<PresenceSignal::SubscriptionStateChanged>(bind(&DBusPresenceManager::subscriptionStateChanged, presM, _1, _2, _3)),
};
const std::map<std::string, SharedCallback> audioEvHandlers = {
exportable_callback<AudioSignal::DeviceEvent>(bind(&DBusConfigurationManager::audioDeviceEvent, confM)),
};
#ifdef RING_VIDEO
// Video event handlers
const std::map<std::string, SharedCallback> videoEvHandlers = {
......@@ -208,6 +213,7 @@ DBusClient::initLibrary(int flags)
registerCallHandlers(callEvHandlers);
registerConfHandlers(configEvHandlers);
registerPresHandlers(presEvHandlers);
registerPresHandlers(audioEvHandlers);
#ifdef RING_VIDEO
registerVideoHandlers(videoEvHandlers);
#endif
......
......@@ -79,6 +79,9 @@ getSignalHandlers()
exported_callback<DRing::PresenceSignal::NewBuddyNotification>(),
exported_callback<DRing::PresenceSignal::SubscriptionStateChanged>(),
/* Audio */
exported_callback<DRing::AudioSignal::DeviceEvent>(),
#ifdef RING_VIDEO
/* Video */
exported_callback<DRing::VideoSignal::DeviceEvent>(),
......
......@@ -149,6 +149,13 @@ bool discardTrustRequest(const std::string& accountId, const std::string& from);
void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload = {});
struct AudioSignal {
struct DeviceEvent {
constexpr static const char* name = "DeviceEvent";
using cb_type = void(void);
};
};
// Configuration signal type definitions
struct ConfigurationSignal {
struct VolumeChanged {
......
......@@ -25,6 +25,7 @@
#include "manager.h"
#include "audio/ringbufferpool.h"
#include "audio/resampler.h"
#include "client/ring_signal.h"
#include <ctime>
......@@ -62,6 +63,11 @@ void AudioLayer::hardwareInputFormatAvailable(AudioFormat capture)
inputResampler_->setFormat(capture);
}
void AudioLayer::devicesChanged()
{
emitSignal<DRing::AudioSignal::DeviceEvent>();
}
void AudioLayer::flushMain()
{
std::lock_guard<std::mutex> lock(mutex_);
......
......@@ -219,6 +219,7 @@ class AudioLayer {
*/
void hardwareInputFormatAvailable(AudioFormat capture);
void devicesChanged();
const AudioBuffer& getToPlay(AudioFormat format, size_t writableSamples);
......@@ -255,7 +256,7 @@ class AudioLayer {
/**
* Whether or not the audio layer stream is started
*/
Status status_ {Status::Idle};
std::atomic<Status> status_ {Status::Idle};
mutable std::condition_variable startedCv_;
/**
......
......@@ -32,7 +32,8 @@ AudioStream::AudioStream(pa_context *c,
const char *desc,
int type,
unsigned samplrate,
const PaDeviceInfos* infos)
const PaDeviceInfos* infos,
bool ec)
: audiostream_(0), mainloop_(m)
{
const pa_channel_map channel_map = infos->channel_map;
......@@ -48,8 +49,10 @@ AudioStream::AudioStream(pa_context *c,
assert(pa_sample_spec_valid(&sample_spec));
assert(pa_channel_map_valid(&channel_map));
audiostream_ = pa_stream_new(c, desc, &sample_spec, &channel_map);
std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl (pa_proplist_new(), pa_proplist_free);
pa_proplist_sets(pl.get(), PA_PROP_FILTER_WANT, "echo-cancel");
audiostream_ = pa_stream_new_with_proplist(c, desc, &sample_spec, &channel_map, ec ? pl.get() : nullptr);
if (!audiostream_) {
RING_ERR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c)));
throw std::runtime_error("Could not create stream\n");
......@@ -108,7 +111,7 @@ AudioStream::~AudioStream()
void AudioStream::moved(pa_stream* s)
{
audiostream_ = s;
RING_DBG("Stream %d to %d", pa_stream_get_index(s), pa_stream_get_device_index(s));
RING_DBG("Stream %d to %s", pa_stream_get_index(s), pa_stream_get_device_name(s));
}
void
......
......@@ -48,8 +48,9 @@ class AudioStream {
* @param types
* @param audio sampling rate
* @param pointer to pa_source_info or pa_sink_info (depending on type).
* @param true if echo cancelling should be used with this stream
*/
AudioStream(pa_context *, pa_threaded_mainloop *, const char *, int, unsigned, const PaDeviceInfos*);
AudioStream(pa_context *, pa_threaded_mainloop *, const char *, int, unsigned, const PaDeviceInfos*, bool);
~AudioStream();
......@@ -81,6 +82,10 @@ class AudioStream {
return AudioFormat(s->rate, s->channels);
}
inline std::string getDeviceName() const {
return pa_stream_get_device_name(audiostream_);
}
bool isReady();
private:
......
This diff is collapsed.
......@@ -34,6 +34,7 @@
#include "logger.h"
#include <memory>
#include <thread>
namespace ring {
......@@ -50,13 +51,15 @@ struct PaDeviceInfos {
std::string description;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
uint32_t monitor_of {PA_INVALID_INDEX};
PaDeviceInfos(const pa_source_info& source) :
index(source.index),
name(source.name),
description(source.description),
sample_spec(source.sample_spec),
channel_map(source.channel_map) {}
channel_map(source.channel_map),
monitor_of(source.monitor_of_sink) {}
PaDeviceInfos(const pa_sink_info& source) :
index(source.index),
......@@ -135,6 +138,9 @@ class PulseLayer : public AudioLayer {
static void context_changed_callback(pa_context* c,
pa_subscription_event_type_t t,
uint32_t idx , void* userdata);
void contextStateChanged(pa_context* c);
void contextChanged(pa_context*, pa_subscription_event_type_t, uint32_t idx);
static void source_input_info_callback(pa_context *c,
const pa_source_info *i,
int eol, void *userdata);
......@@ -151,6 +157,12 @@ class PulseLayer : public AudioLayer {
virtual int getIndexPlayback() const;
virtual int getIndexRingtone() const;
void waitForDeviceList();
std::string getPreferredPlaybackDevice() const;
std::string getPreferredRingtoneDevice() const;
std::string getPreferredCaptureDevice() const;
NON_COPYABLE(PulseLayer);
/**
......@@ -167,7 +179,7 @@ class PulseLayer : public AudioLayer {
/**
* Returns a pointer to the PaEndpointInfos with the given name in sourceList_, or nullptr if not found.
*/
const PaDeviceInfos* getDeviceInfos(const std::vector<PaDeviceInfos>&, const std::string& name) const;
const PaDeviceInfos* getDeviceInfos(const std::vector<PaDeviceInfos>&, const std::string& name, const std::string& defaultName) const;
/**
* A stream object to handle the pulseaudio playback stream
......@@ -217,8 +229,10 @@ class PulseLayer : public AudioLayer {
bool enumeratingSinks_ {false};
bool enumeratingSources_ {false};
bool gettingServerInfo_ {false};
bool waitingDeviceList_ {false};
std::mutex readyMtx_ {};
std::condition_variable readyCv_ {};
std::thread streamStarter_ {};
AudioPreference &preference_;
std::shared_ptr<RingBuffer> mainRingBuffer_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment