From 4e41513cd6a097a23b052bf1248e8be64454aef4 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Thu, 15 Aug 2019 15:33:00 +0300 Subject: [PATCH 1/3] osx: drain stream properly The logic for draining a stream was odd. After drain the AU stop was happening on the next callback. That could create race conditions especially when reinit is taking place at the same time. This patch triggers the stop of AU at the end of the same callback and set the shutdown flag in order to exit early any following callback. --- src/cubeb_audiounit.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 25355b439..8fdd0392d 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -611,18 +611,6 @@ audiounit_output_callback(void * user_ptr, return noErr; } - if (stm->draining) { - OSStatus r = AudioOutputUnitStop(stm->output_unit); - assert(r == 0); - if (stm->input_unit) { - r = AudioOutputUnitStop(stm->input_unit); - assert(r == 0); - } - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - audiounit_make_silent(&outBufferList->mBuffers[0]); - return noErr; - } - /* Get output buffer. */ if (stm->mixer) { // If remixing needs to occur, we can't directly work in our final @@ -723,6 +711,12 @@ audiounit_output_callback(void * user_ptr, } } + if (stm->draining) { + audiounit_stream_stop_internal(stm); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); + stm->shutdown = true; + } + return noErr; } From cadcfeeea7e1a5e79f097cb1e4d2369b8c47b981 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Thu, 15 Aug 2019 15:34:08 +0300 Subject: [PATCH 2/3] tool: add an option to test stream drain --- tools/cubeb-test.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tools/cubeb-test.cpp b/tools/cubeb-test.cpp index e16a9bb01..dde034e5d 100644 --- a/tools/cubeb-test.cpp +++ b/tools/cubeb-test.cpp @@ -49,8 +49,8 @@ class cubeb_client final { bool init(char const * backend_name = nullptr); bool init_stream(); - bool start_stream() const; - bool stop_stream() const; + bool start_stream(); + bool stop_stream(); bool destroy_stream() const; bool destroy(); bool activate_log(cubeb_log_level log_level) const; @@ -70,6 +70,8 @@ class cubeb_client final { cubeb_stream_params output_params = {}; cubeb_stream_params input_params = {}; + void force_drain() { _force_drain = true; } + private: bool has_input() { return input_params.rate != 0; } bool has_output() { return output_params.rate != 0; } @@ -85,6 +87,7 @@ class cubeb_client final { std::atomic _channels = {0}; std::atomic _latency_testing = {false}; std::atomic _latency_frames = {0}; // if !0, override. Else, use min. + std::atomic _force_drain = {false}; /* Accessed only from audio thread. */ @@ -159,7 +162,8 @@ bool cubeb_client::init_stream() { return true; } -bool cubeb_client::start_stream() const { +bool cubeb_client::start_stream() { + _force_drain = false; int rv = cubeb_stream_start(stream); if (rv != CUBEB_OK) { fprintf(stderr, "Could not start the stream\n"); @@ -168,7 +172,8 @@ bool cubeb_client::start_stream() const { return true; } -bool cubeb_client::stop_stream() const { +bool cubeb_client::stop_stream() { + _force_drain = false; int rv = cubeb_stream_stop(stream); if (rv != CUBEB_OK) { fprintf(stderr, "Could not stop the stream\n"); @@ -275,6 +280,11 @@ long cubeb_client::user_data_cb(cubeb_stream* stm, void* user, } _total_frames += nframes; + + if (_force_drain) { + return nframes - 1; + } + return nframes; } @@ -333,6 +343,7 @@ void print_help() { "2: change log level to verbose\n" "p: start a initialized stream\n" "s: stop a started stream\n" + "d: force stream to drain\n" "c: get stream position (client thread)\n" "i: change device type to input\n" "o: change device type to output\n" @@ -345,7 +356,7 @@ void print_help() { fprintf(stderr, "%s\n", msg); } -bool choose_action(const cubeb_client& cl, operation_data * op, int c) { +bool choose_action(cubeb_client& cl, operation_data * op, int c) { // Consume "enter" and "space" while (c == 10 || c == 32) { c = getchar(); @@ -398,6 +409,8 @@ bool choose_action(const cubeb_client& cl, operation_data * op, int c) { } else { fprintf(stderr, "stop_stream failed\n"); } + } else if (c == 'd') { + cl.force_drain(); } else if (c == 'c') { uint64_t pos = cl.get_stream_position(); uint64_t latency = cl.get_stream_latency(); From 2b35e5ae0dfa7e66ecd8697e9a10f0f9dfdf086b Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Thu, 15 Aug 2019 16:04:15 +0300 Subject: [PATCH 3/3] osx: exit early the audiocallback during reinit --- src/cubeb_audiounit.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 8fdd0392d..ad443cc59 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -498,8 +498,8 @@ audiounit_input_callback(void * user_ptr, assert(stm->input_unit != NULL); assert(AU_IN_BUS == bus); - if (stm->shutdown) { - ALOG("(%p) input shutdown", stm); + if (stm->shutdown || stm->reinit_pending) { + ALOG("(%p) input shutdown or reinit exit callback early.", stm); return noErr; } @@ -605,8 +605,8 @@ audiounit_output_callback(void * user_ptr, long input_frames = 0; void * output_buffer = NULL, * input_buffer = NULL; - if (stm->shutdown) { - ALOG("(%p) output shutdown.", stm); + if (stm->shutdown || stm->reinit_pending) { + ALOG("(%p) output shutdown or reinit exit callback early.", stm); audiounit_make_silent(&outBufferList->mBuffers[0]); return noErr; }