diff options
author | Jörg Müller <nexyon@gmail.com> | 2021-03-27 14:22:23 +0300 |
---|---|---|
committer | Jörg Müller <nexyon@gmail.com> | 2021-03-29 13:16:01 +0300 |
commit | 35cf34de6d5627e878c88ef8c8a5e89b7c72b3be (patch) | |
tree | 1b1edb78479669752ac91c78ac7ffb1a200e3422 /extern/audaspace/plugins | |
parent | ae9d61e7fea25535803e92298f44b184c9190f76 (diff) |
Fix T86851: PulseAudio randomly asserts in background rendering
Upstream fix from Audaspace with simplified PulseAudio code.
Maniphest Tasks: T86851
Differential Revision: https://developer.blender.org/D10840
Diffstat (limited to 'extern/audaspace/plugins')
-rw-r--r-- | extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp | 62 | ||||
-rw-r--r-- | extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h | 19 | ||||
-rw-r--r-- | extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h | 14 | ||||
-rw-r--r-- | extern/audaspace/plugins/wasapi/WASAPIDevice.cpp | 169 | ||||
-rw-r--r-- | extern/audaspace/plugins/wasapi/WASAPIDevice.h | 29 |
5 files changed, 89 insertions, 204 deletions
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index 0a50d5db2c7..3ffe97661d8 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -27,9 +27,9 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data { PulseAudioDevice* device = (PulseAudioDevice*)data; - device->m_state = AUD_pa_context_get_state(context); + std::lock_guard<ILockable> lock(*device); - AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0); + device->m_state = AUD_pa_context_get_state(context); } void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data) @@ -68,29 +68,40 @@ void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data) } } -void PulseAudioDevice::playing(bool playing) +void PulseAudioDevice::runMixingThread() { - m_playback = playing; + for(;;) + { + { + std::lock_guard<ILockable> lock(*this); + + if(shouldStop()) + { + AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr); + doStop(); + return; + } + } + + if(AUD_pa_stream_is_corked(m_stream)) + AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr); - AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr); + AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); + } } PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) : - m_playback(false), m_state(PA_CONTEXT_UNCONNECTED), m_buffersize(buffersize), m_underflows(0) { - m_mainloop = AUD_pa_threaded_mainloop_new(); + m_mainloop = AUD_pa_mainloop_new(); - AUD_pa_threaded_mainloop_lock(m_mainloop); - - m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str()); + m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str()); if(!m_context) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); } @@ -99,26 +110,21 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); - AUD_pa_threaded_mainloop_start(m_mainloop); - while(m_state != PA_CONTEXT_READY) { switch(m_state) { case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); break; default: - AUD_pa_threaded_mainloop_wait(m_mainloop); + AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); break; } } @@ -166,13 +172,10 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff if(!m_stream) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not create PulseAudio stream."); } @@ -188,32 +191,27 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff buffer_attr.prebuf = -1U; buffer_attr.tlength = buffersize; - if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) + if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect PulseAudio stream."); } - AUD_pa_threaded_mainloop_unlock(m_mainloop); - create(); } PulseAudioDevice::~PulseAudioDevice() { - AUD_pa_threaded_mainloop_stop(m_mainloop); + stopMixingThread(); AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); destroy(); } diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index 9efae5128b1..be34cc9032b 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -26,7 +26,7 @@ * The PulseAudioDevice class. */ -#include "devices/SoftwareDevice.h" +#include "devices/ThreadedDevice.h" #include <pulse/pulseaudio.h> @@ -35,15 +35,10 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through PulseAudio, the simple direct media layer. */ -class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice +class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice { private: - /** - * Whether there is currently playback. - */ - volatile bool m_playback; - - pa_threaded_mainloop* m_mainloop; + pa_mainloop* m_mainloop; pa_context* m_context; pa_stream* m_stream; pa_context_state_t m_state; @@ -74,13 +69,15 @@ private: */ AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data); + /** + * Streaming thread main function. + */ + AUD_LOCAL void runMixingThread(); + // delete copy constructor and operator= PulseAudioDevice(const PulseAudioDevice&) = delete; PulseAudioDevice& operator=(const PulseAudioDevice&) = delete; -protected: - virtual void playing(bool playing); - public: /** * Opens the PulseAudio audio device for playback. diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h index 9cefbc0c7e2..4b9e1ffea2b 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h @@ -24,18 +24,14 @@ PULSEAUDIO_SYMBOL(pa_context_unref); PULSEAUDIO_SYMBOL(pa_stream_begin_write); PULSEAUDIO_SYMBOL(pa_stream_connect_playback); PULSEAUDIO_SYMBOL(pa_stream_cork); +PULSEAUDIO_SYMBOL(pa_stream_is_corked); PULSEAUDIO_SYMBOL(pa_stream_new); PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr); PULSEAUDIO_SYMBOL(pa_stream_set_underflow_callback); PULSEAUDIO_SYMBOL(pa_stream_set_write_callback); PULSEAUDIO_SYMBOL(pa_stream_write); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait); +PULSEAUDIO_SYMBOL(pa_mainloop_free); +PULSEAUDIO_SYMBOL(pa_mainloop_get_api); +PULSEAUDIO_SYMBOL(pa_mainloop_new); +PULSEAUDIO_SYMBOL(pa_mainloop_iterate); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp index 4f213dc8468..b4632ebb83e 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp @@ -31,159 +31,83 @@ template <class T> void SafeRelease(T **ppT) } } -void WASAPIDevice::start() -{ - lock(); - - // thread is still running, we can abort stopping it - if(m_stop) - m_stop = false; - // thread is not running, let's start it - else if(!m_playing) - { - if(m_thread.joinable()) - m_thread.join(); - - m_playing = true; - - m_thread = std::thread(&WASAPIDevice::updateStream, this); - } - - unlock(); -} - -void WASAPIDevice::updateStream() +void WASAPIDevice::runMixingThread() { UINT32 buffer_size; + UINT32 padding; + UINT32 length; data_t* buffer; - lock(); + IAudioRenderClient* render_client = nullptr; - if(FAILED(m_audio_client->GetBufferSize(&buffer_size))) { - m_playing = false; - m_stop = false; - unlock(); - return; - } + std::lock_guard<ILockable> lock(*this); - IAudioRenderClient* render_client = nullptr; - const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); + const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); - if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client)))) - { - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(m_audio_client->GetBufferSize(&buffer_size))) + goto init_error; - UINT32 padding; + if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client)))) + goto init_error; - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(m_audio_client->GetCurrentPadding(&padding))) + goto init_error; - UINT32 length = buffer_size - padding; + length = buffer_size - padding; - if(FAILED(render_client->GetBuffer(length, &buffer))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(render_client->GetBuffer(length, &buffer))) + goto init_error; - mix((data_t*)buffer, length); + mix((data_t*)buffer, length); - if(FAILED(render_client->ReleaseBuffer(length, 0))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; + if(FAILED(render_client->ReleaseBuffer(length, 0))) + { + init_error: + SafeRelease(&render_client); + doStop(); + return; + } } - unlock(); - m_audio_client->Start(); auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); for(;;) { - lock(); - - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + std::lock_guard<ILockable> lock(*this); - length = buffer_size - padding; + if(FAILED(m_audio_client->GetCurrentPadding(&padding))) + goto stop_thread; - if(FAILED(render_client->GetBuffer(length, &buffer))) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + length = buffer_size - padding; - mix((data_t*)buffer, length); + if(FAILED(render_client->GetBuffer(length, &buffer))) + goto stop_thread; - if(FAILED(render_client->ReleaseBuffer(length, 0))) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + mix((data_t*)buffer, length); - // stop thread - if(m_stop) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(render_client->ReleaseBuffer(length, 0))) + goto stop_thread; - unlock(); + // stop thread + if(shouldStop()) + { + stop_thread: + m_audio_client->Stop(); + SafeRelease(&render_client); + doStop(); + return; + } + } std::this_thread::sleep_for(sleepDuration); } } -void WASAPIDevice::playing(bool playing) -{ - if((!m_playing || m_stop) && playing) - start(); - else - m_stop = true; -} - WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : - m_playing(false), - m_stop(false), - m_imm_device_enumerator(nullptr), m_imm_device(nullptr), m_audio_client(nullptr), @@ -361,14 +285,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : WASAPIDevice::~WASAPIDevice() { - lock(); - - stopAll(); - - unlock(); - - if(m_thread.joinable()) - m_thread.join(); + stopMixingThread(); SafeRelease(&m_audio_client); SafeRelease(&m_imm_device); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h index ae25a09c432..375f03bd255 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.h +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h @@ -26,7 +26,7 @@ * The WASAPIDevice class. */ -#include "devices/SoftwareDevice.h" +#include "devices/ThreadedDevice.h" #include <thread> @@ -40,46 +40,23 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through WASAPI, the Windows audio API. */ -class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice +class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice { private: - /** - * Whether there is currently playback. - */ - bool m_playing; - - /** - * Whether the current playback should stop. - */ - bool m_stop; - IMMDeviceEnumerator* m_imm_device_enumerator; IMMDevice* m_imm_device; IAudioClient* m_audio_client; WAVEFORMATEXTENSIBLE m_wave_format_extensible; /** - * The streaming thread. - */ - std::thread m_thread; - - /** - * Starts the streaming thread. - */ - AUD_LOCAL void start(); - - /** * Streaming thread main function. */ - AUD_LOCAL void updateStream(); + AUD_LOCAL void runMixingThread(); // delete copy constructor and operator= WASAPIDevice(const WASAPIDevice&) = delete; WASAPIDevice& operator=(const WASAPIDevice&) = delete; -protected: - virtual void playing(bool playing); - public: /** * Opens the WASAPI audio device for playback. |