Skip to content
Snippets Groups Projects
Commit c6bee418 authored by Adrien Béraud's avatar Adrien Béraud Committed by Sébastien Blin
Browse files

pulseaudio: start steams when ready


Follow API documentation recommendations: create streams corked,
start them when ready.

Change-Id: I9826f1f77bbe75351d863c29558db412e5dcbda0
Reviewed-by: default avatarSébastien Blin <sebastien.blin@savoirfairelinux.com>
parent 1d71c178
Branches
No related tags found
No related merge requests found
...@@ -31,11 +31,12 @@ namespace ring { ...@@ -31,11 +31,12 @@ namespace ring {
AudioStream::AudioStream(pa_context *c, AudioStream::AudioStream(pa_context *c,
pa_threaded_mainloop *m, pa_threaded_mainloop *m,
const char *desc, const char *desc,
int type, StreamType type,
unsigned samplrate, unsigned samplrate,
const PaDeviceInfos* infos, const PaDeviceInfos* infos,
bool ec) bool ec,
: audiostream_(0), mainloop_(m) OnReady onReady)
: audiostream_(0), mainloop_(m), onReady_(std::move(onReady))
{ {
const pa_channel_map channel_map = infos->channel_map; const pa_channel_map channel_map = infos->channel_map;
...@@ -66,17 +67,24 @@ AudioStream::AudioStream(pa_context *c, ...@@ -66,17 +67,24 @@ AudioStream::AudioStream(pa_context *c,
attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec); attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
attributes.minreq = (uint32_t) -1; attributes.minreq = (uint32_t) -1;
pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void* user_data){
static_cast<AudioStream*>(user_data)->stateChanged(s);
}, this);
pa_stream_set_moved_callback(audiostream_, [](pa_stream* s, void* user_data){
static_cast<AudioStream*>(user_data)->moved(s);
}, this);
{ {
PulseMainLoopLock lock(mainloop_); PulseMainLoopLock lock(mainloop_);
const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED);
if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) { if (type == StreamType::Playback || type == StreamType::Ringtone) {
pa_stream_connect_playback(audiostream_, pa_stream_connect_playback(audiostream_,
infos->name.empty() ? nullptr : infos->name.c_str(), infos->name.empty() ? nullptr : infos->name.c_str(),
&attributes, &attributes,
flags, flags,
nullptr, nullptr); nullptr, nullptr);
} else if (type == CAPTURE_STREAM) { } else if (type == StreamType::Capture) {
pa_stream_connect_record(audiostream_, pa_stream_connect_record(audiostream_,
infos->name.empty() ? nullptr : infos->name.c_str(), infos->name.empty() ? nullptr : infos->name.c_str(),
&attributes, &attributes,
...@@ -84,12 +92,6 @@ AudioStream::AudioStream(pa_context *c, ...@@ -84,12 +92,6 @@ AudioStream::AudioStream(pa_context *c,
} }
} }
pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void* user_data){
static_cast<AudioStream*>(user_data)->stateChanged(s);
}, this);
pa_stream_set_moved_callback(audiostream_, [](pa_stream* s, void* user_data){
static_cast<AudioStream*>(user_data)->moved(s);
}, this);
} }
AudioStream::~AudioStream() AudioStream::~AudioStream()
...@@ -99,16 +101,23 @@ AudioStream::~AudioStream() ...@@ -99,16 +101,23 @@ AudioStream::~AudioStream()
pa_stream_disconnect(audiostream_); pa_stream_disconnect(audiostream_);
// make sure we don't get any further callback // make sure we don't get any further callback
pa_stream_set_state_callback(audiostream_, NULL, NULL); pa_stream_set_state_callback(audiostream_, nullptr, nullptr);
pa_stream_set_write_callback(audiostream_, NULL, NULL); pa_stream_set_write_callback(audiostream_, nullptr, nullptr);
pa_stream_set_read_callback(audiostream_, NULL, NULL); pa_stream_set_read_callback(audiostream_, nullptr, nullptr);
pa_stream_set_moved_callback(audiostream_, NULL, NULL); pa_stream_set_moved_callback(audiostream_, nullptr, nullptr);
pa_stream_set_underflow_callback(audiostream_, NULL, NULL); pa_stream_set_underflow_callback(audiostream_, nullptr, nullptr);
pa_stream_set_overflow_callback(audiostream_, NULL, NULL); pa_stream_set_overflow_callback(audiostream_, nullptr, nullptr);
pa_stream_set_suspended_callback(audiostream_, nullptr, nullptr);
pa_stream_set_started_callback(audiostream_, nullptr, nullptr);
pa_stream_unref(audiostream_); pa_stream_unref(audiostream_);
} }
void
AudioStream::start() {
pa_stream_cork(audiostream_, 0, nullptr, nullptr);
}
void AudioStream::moved(pa_stream* s) void AudioStream::moved(pa_stream* s)
{ {
audiostream_ = s; audiostream_ = s;
...@@ -137,6 +146,7 @@ AudioStream::stateChanged(pa_stream* s) ...@@ -137,6 +146,7 @@ AudioStream::stateChanged(pa_stream* s)
//RING_DBG("minreq %u", pa_stream_get_buffer_attr(s)->minreq); //RING_DBG("minreq %u", pa_stream_get_buffer_attr(s)->minreq);
//RING_DBG("fragsize %u", pa_stream_get_buffer_attr(s)->fragsize); //RING_DBG("fragsize %u", pa_stream_get_buffer_attr(s)->fragsize);
//RING_DBG("samplespec %s", pa_sample_spec_snprint(str, sizeof(str), pa_stream_get_sample_spec(s))); //RING_DBG("samplespec %s", pa_sample_spec_snprint(str, sizeof(str), pa_stream_get_sample_spec(s)));
onReady_();
break; break;
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
......
...@@ -18,8 +18,7 @@ ...@@ -18,8 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef _AUDIO_STREAM_H #pragma once
#define _AUDIO_STREAM_H
#include "noncopyable.h" #include "noncopyable.h"
#include "pulselayer.h" #include "pulselayer.h"
...@@ -32,12 +31,11 @@ namespace ring { ...@@ -32,12 +31,11 @@ namespace ring {
/** /**
* This data structure contains the different king of audio streams available * This data structure contains the different king of audio streams available
*/ */
enum STREAM_TYPE { enum class StreamType { Playback, Capture, Ringtone };
PLAYBACK_STREAM, CAPTURE_STREAM, RINGTONE_STREAM
};
class AudioStream { class AudioStream {
public: public:
using OnReady = std::function<void()>;
/** /**
* Constructor * Constructor
...@@ -50,10 +48,12 @@ class AudioStream { ...@@ -50,10 +48,12 @@ class AudioStream {
* @param pointer to pa_source_info or pa_sink_info (depending on type). * @param pointer to pa_source_info or pa_sink_info (depending on type).
* @param true if echo cancelling should be used with this stream * @param true if echo cancelling should be used with this stream
*/ */
AudioStream(pa_context *, pa_threaded_mainloop *, const char *, int, unsigned, const PaDeviceInfos*, bool); AudioStream(pa_context *, pa_threaded_mainloop *, const char *, StreamType, unsigned, const PaDeviceInfos*, bool, OnReady onReady);
~AudioStream(); ~AudioStream();
void start();
/** /**
* Accessor: Get the pulseaudio stream object * Accessor: Get the pulseaudio stream object
* @return pa_stream* The stream * @return pa_stream* The stream
...@@ -94,6 +94,8 @@ class AudioStream { ...@@ -94,6 +94,8 @@ class AudioStream {
private: private:
NON_COPYABLE(AudioStream); NON_COPYABLE(AudioStream);
OnReady onReady_;
/** /**
* Mandatory asynchronous callback on the audio stream state * Mandatory asynchronous callback on the audio stream state
*/ */
...@@ -112,5 +114,3 @@ class AudioStream { ...@@ -112,5 +114,3 @@ class AudioStream {
}; };
} }
#endif // _AUDIO_STREAM_H
...@@ -323,9 +323,21 @@ void PulseLayer::createStreams(pa_context* c) ...@@ -323,9 +323,21 @@ void PulseLayer::createStreams(pa_context* c)
{ {
hardwareFormatAvailable(defaultAudioFormat_); hardwareFormatAvailable(defaultAudioFormat_);
auto onReady = [this] {
bool playbackReady = not playback_ or playback_->isReady();
bool ringtoneReady = not ringtone_ or ringtone_->isReady();
bool recordReady = not record_ or record_->isReady();
if (playbackReady and recordReady and ringtoneReady) {
RING_DBG("All streams ready, starting !");
if (playback_) playback_->start();
if (ringtone_) ringtone_->start();
if (record_) record_->start();
}
};
// Create playback stream // Create playback stream
if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) { if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredPlaybackDevice())) {
playback_.reset(new AudioStream(c, mainloop_.get(), "Playback", PLAYBACK_STREAM, audioFormat_.sample_rate, dev_infos, true)); playback_.reset(new AudioStream(c, mainloop_.get(), "Playback", StreamType::Playback, audioFormat_.sample_rate, dev_infos, true, onReady));
pa_stream_set_write_callback(playback_->stream(), [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) { pa_stream_set_write_callback(playback_->stream(), [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) {
static_cast<PulseLayer*>(userdata)->writeToSpeaker(); static_cast<PulseLayer*>(userdata)->writeToSpeaker();
}, this); }, this);
...@@ -334,7 +346,7 @@ void PulseLayer::createStreams(pa_context* c) ...@@ -334,7 +346,7 @@ void PulseLayer::createStreams(pa_context* c)
// Create ringtone stream // Create ringtone stream
// Echo canceling is not enabled for ringtone, because PA can only cancel a single output source with an input source // Echo canceling is not enabled for ringtone, because PA can only cancel a single output source with an input source
if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice())) { if (auto dev_infos = getDeviceInfos(sinkList_, getPreferredRingtoneDevice())) {
ringtone_.reset(new AudioStream(c, mainloop_.get(), "Ringtone", RINGTONE_STREAM, audioFormat_.sample_rate, dev_infos, false)); ringtone_.reset(new AudioStream(c, mainloop_.get(), "Ringtone", StreamType::Ringtone, audioFormat_.sample_rate, dev_infos, false, onReady));
pa_stream_set_write_callback(ringtone_->stream(), [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) { pa_stream_set_write_callback(ringtone_->stream(), [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) {
static_cast<PulseLayer*>(userdata)->ringtoneToSpeaker(); static_cast<PulseLayer*>(userdata)->ringtoneToSpeaker();
}, this); }, this);
...@@ -342,7 +354,7 @@ void PulseLayer::createStreams(pa_context* c) ...@@ -342,7 +354,7 @@ void PulseLayer::createStreams(pa_context* c)
// Create capture stream // Create capture stream
if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) { if (auto dev_infos = getDeviceInfos(sourceList_, getPreferredCaptureDevice())) {
record_.reset(new AudioStream(c, mainloop_.get(), "Capture", CAPTURE_STREAM, audioFormat_.sample_rate, dev_infos, true)); record_.reset(new AudioStream(c, mainloop_.get(), "Capture", StreamType::Capture, audioFormat_.sample_rate, dev_infos, true, onReady));
pa_stream_set_read_callback(record_->stream() , [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) { pa_stream_set_read_callback(record_->stream() , [](pa_stream * /*s*/, size_t /*bytes*/, void* userdata) {
static_cast<PulseLayer*>(userdata)->readFromMic(); static_cast<PulseLayer*>(userdata)->readFromMic();
}, this); }, this);
...@@ -483,10 +495,8 @@ void PulseLayer::ringtoneToSpeaker() ...@@ -483,10 +495,8 @@ void PulseLayer::ringtoneToSpeaker()
} }
std::string stripEchoSufix(std::string deviceName) { std::string stripEchoSufix(const std::string& deviceName) {
return std::regex_replace(deviceName, PA_EC_SUFFIX, ""); return std::regex_replace(deviceName, PA_EC_SUFFIX, "");
} }
void void
...@@ -560,8 +570,15 @@ void PulseLayer::waitForDeviceList() ...@@ -560,8 +570,15 @@ void PulseLayer::waitForDeviceList()
return; return;
// If a current device changed, restart streams // If a current device changed, restart streams
if (!playback_ || stripEchoSufix(playback_->getDeviceName()) != getDeviceInfos(sinkList_, getPreferredPlaybackDevice())->name auto playbackInfo = getDeviceInfos(sinkList_, getPreferredPlaybackDevice());
|| !record_ || stripEchoSufix(record_->getDeviceName()) != getDeviceInfos(sourceList_, getPreferredCaptureDevice())->name) { bool playbackDeviceChanged = !playback_ or (!playbackInfo->name.empty() and
playbackInfo->name != stripEchoSufix(playback_->getDeviceName()));
auto recordInfo = getDeviceInfos(sourceList_, getPreferredCaptureDevice());
bool recordDeviceChanged = !record_ or (!recordInfo->name.empty() and
recordInfo->name != stripEchoSufix(record_->getDeviceName()));
if (playbackDeviceChanged or recordDeviceChanged) {
RING_WARN("Audio devices changed, restarting streams."); RING_WARN("Audio devices changed, restarting streams.");
stopStream(); stopStream();
startStream(); startStream();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment