From 7ede27fb2ad5039f60ca1ba961ea61f887299678 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Thu, 10 Sep 2020 16:59:53 +0900 Subject: [PATCH] move __IsReady function into the critical section IsReady() was moved into critical section to support thread-safe. stress testcase was added. [Version] 0.5.30 [Issue Type] DF200907-00219 Change-Id: I45811c183636faabfd66090b96ddfa910f733496 Signed-off-by: Jaechul Lee --- include/CAudioIO.h | 1 + include/CPulseAudioClient.h | 15 +- include/IPulseStreamListener.h | 1 + packaging/capi-media-audio-io.spec | 2 +- src/cpp/CAudioIO.cpp | 12 +- src/cpp/CAudioInput.cpp | 32 ++- src/cpp/CAudioOutput.cpp | 43 ++- src/cpp/CPulseAudioClient.cpp | 413 +++++++++++++---------------- src/cpp/cpp_audio_io.cpp | 10 +- test/CMakeLists.txt | 2 +- test/audio_io_test.c | 242 +++++++++++++++++ 11 files changed, 516 insertions(+), 257 deletions(-) diff --git a/include/CAudioIO.h b/include/CAudioIO.h index 3a84a75..c1073aa 100644 --- a/include/CAudioIO.h +++ b/include/CAudioIO.h @@ -101,6 +101,7 @@ namespace tizen_media_audio { void setStreamInfo(sound_stream_info_h stream_info); + void setState(CAudioInfo::EAudioIOState state); CAudioInfo::EAudioIOState getState() noexcept; protected: diff --git a/include/CPulseAudioClient.h b/include/CPulseAudioClient.h index 01e33fb..c09e499 100644 --- a/include/CPulseAudioClient.h +++ b/include/CPulseAudioClient.h @@ -25,12 +25,11 @@ namespace tizen_media_audio { - - /** - * PULSE Thread + * PULSE ThreadMainloop Locker */ class CPulseStreamSpec; + class CPulseAudioClient { public: enum class EStreamDirection : unsigned int { @@ -64,7 +63,7 @@ namespace tizen_media_audio { bool flush(); void checkRunningState(); - bool isInThread() noexcept; + bool isInThread() const; size_t getWritableSize(); size_t getReadableSize(); @@ -116,6 +115,14 @@ namespace tizen_media_audio { static void __successDrainCbInThread(pa_stream* s, int success, void* user_data); static void __successVolumeCb(pa_context* c, int success, void* user_data); + class CPulseThreadLocker { + public: + explicit CPulseThreadLocker(pa_threaded_mainloop* mainloop); + virtual ~CPulseThreadLocker(); + + private: + const pa_threaded_mainloop* ml{}; + }; }; diff --git a/include/IPulseStreamListener.h b/include/IPulseStreamListener.h index 5f55b33..76b4288 100644 --- a/include/IPulseStreamListener.h +++ b/include/IPulseStreamListener.h @@ -36,6 +36,7 @@ namespace tizen_media_audio { virtual void onStream(CPulseAudioClient* pClient, size_t length) = 0; virtual void onStateChanged(CAudioInfo::EAudioIOState state) = 0; virtual void onStateChanged(CAudioInfo::EAudioIOState state, bool policy) = 0; + virtual void setState(CAudioInfo::EAudioIOState state) = 0; }; diff --git a/packaging/capi-media-audio-io.spec b/packaging/capi-media-audio-io.spec index a300433..d568a5e 100644 --- a/packaging/capi-media-audio-io.spec +++ b/packaging/capi-media-audio-io.spec @@ -1,6 +1,6 @@ Name: capi-media-audio-io Summary: An Audio Input & Audio Output library in Tizen Native API -Version: 0.5.29 +Version: 0.5.30 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/cpp/CAudioIO.cpp b/src/cpp/CAudioIO.cpp index 3115d75..7565f6b 100644 --- a/src/cpp/CAudioIO.cpp +++ b/src/cpp/CAudioIO.cpp @@ -95,8 +95,6 @@ void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) { assert(__mIsInit); assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX); - mStatePrev = mState; - mState = state; mByPolicy = byPolicy; if (mState == mStatePrev) @@ -115,6 +113,11 @@ void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) { mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData); } +void CAudioIO::setState(CAudioInfo::EAudioIOState state) { + mStatePrev = mState; + mState = state; +} + void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) { onStateChanged(state, false); } @@ -137,7 +140,6 @@ void CAudioIO::pause() { if (!__mIsInit || !IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE - std::lock_guard guard(mMutex); AUDIO_IO_LOGD("pause"); mpPulseAudioClient->cork(true); } @@ -146,7 +148,6 @@ void CAudioIO::resume() { if (!__mIsInit || !IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE - std::lock_guard guard(mMutex); mpPulseAudioClient->cork(false); } @@ -154,9 +155,6 @@ void CAudioIO::flush() { if (!__mIsInit || !IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE - std::unique_lock defer_mutex(mMutex, std::defer_lock); - if (!mpPulseAudioClient->isInThread()) - defer_mutex.lock(); mpPulseAudioClient->flush(); } diff --git a/src/cpp/CAudioInput.cpp b/src/cpp/CAudioInput.cpp index cf607d4..e95918d 100644 --- a/src/cpp/CAudioInput.cpp +++ b/src/cpp/CAudioInput.cpp @@ -90,6 +90,7 @@ void CAudioInput::initialize() { //LCOV_EXCL_STOP } + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); } @@ -107,6 +108,8 @@ void CAudioInput::finalize() { } void CAudioInput::prepare() { + std::lock_guard mutex(mMutex); + if (!__IsInit()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE @@ -133,7 +136,6 @@ void CAudioInput::prepare() { CPulseStreamSpec spec(streamSpec, mAudioInfo); - std::unique_lock mutex(mMutex); mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this); mpPulseAudioClient->initialize(); mpPulseAudioClient->applyRecordVolume(__mVolume); @@ -141,8 +143,6 @@ void CAudioInput::prepare() { /* Uncork stream which is created with CORKED flag */ mpPulseAudioClient->cork(false); #endif - mutex.unlock(); - CAudioIO::prepare(); } catch (const CAudioError& e) { //LCOV_EXCL_START @@ -156,6 +156,8 @@ void CAudioInput::prepare() { } void CAudioInput::unprepare() { + std::unique_lock mutex(mMutex); + if (!__IsInit()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize CAudioInput"); //LCOV_EXCL_LINE @@ -167,17 +169,22 @@ void CAudioInput::unprepare() { CAudioIO::unprepare(); - std::unique_lock mutex(mMutex); if (mpPulseAudioClient && mpPulseAudioClient->isInThread()) THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread"); + SAFE_FINALIZE(mpPulseAudioClient); SAFE_DELETE(mpPulseAudioClient); + + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); + mutex.unlock(); CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); } void CAudioInput::pause() { + std::unique_lock mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE @@ -190,10 +197,16 @@ void CAudioInput::pause() { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread"); //LCOV_EXCL_LINE CAudioIO::pause(); + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED); + + mutex.unlock(); + CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED); } void CAudioInput::resume() { + std::unique_lock mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE @@ -206,10 +219,16 @@ void CAudioInput::resume() { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread"); //LCOV_EXCL_LINE CAudioIO::resume(); + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); + + mutex.unlock(); + CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); } void CAudioInput::flush() { + std::lock_guard mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE @@ -237,6 +256,8 @@ void CAudioInput::setStreamCallback(SStreamCallback callback) { } size_t CAudioInput::read(void* buffer, size_t length) { + std::unique_lock mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE @@ -256,7 +277,6 @@ size_t CAudioInput::read(void* buffer, size_t length) { int ret = 0; - std::unique_lock mutex(mMutex); // If another thread did call unprepare, do not read if (!mpPulseAudioClient) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE @@ -321,4 +341,4 @@ double CAudioInput::getVolume() { THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Not initialized"); //LCOV_EXCL_LINE return __mVolume; -} \ No newline at end of file +} diff --git a/src/cpp/CAudioOutput.cpp b/src/cpp/CAudioOutput.cpp index 2297232..4ae30f7 100644 --- a/src/cpp/CAudioOutput.cpp +++ b/src/cpp/CAudioOutput.cpp @@ -49,7 +49,7 @@ void CAudioOutput::onStream(CPulseAudioClient* pClient, size_t length) { #ifdef _AUDIO_IO_DEBUG_TIMING_ AUDIO_IO_LOGD("Sync Write Mode! - signal! - pClient:[%p], length:[%zu]", pClient, length); #endif - { + { /* NOTE: this block needs to unlock right after lock */ std::lock_guard cond_guard(mCondMutex); } mCond.notify_one(); @@ -91,6 +91,7 @@ void CAudioOutput::initialize() { //LCOV_EXCL_STOP } + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); } @@ -105,6 +106,8 @@ void CAudioOutput::finalize() { } void CAudioOutput::prepare() { + std::lock_guard mutex(mMutex); + if (!__IsInit()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput"); //LCOV_EXCL_LINE @@ -143,7 +146,6 @@ void CAudioOutput::prepare() { CPulseStreamSpec spec(streamSpec, mAudioInfo); - std::unique_lock mutex(mMutex); mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK, spec, this); mpPulseAudioClient->initialize(); @@ -151,7 +153,6 @@ void CAudioOutput::prepare() { /* Uncork stream which is created with CORKED flag */ mpPulseAudioClient->cork(false); #endif - mutex.unlock(); CAudioIO::prepare(); } catch (const CAudioError& e) { @@ -166,6 +167,8 @@ void CAudioOutput::prepare() { } void CAudioOutput::unprepare() { + std::unique_lock mutex(mMutex); + if (!__IsInit()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize CAudioOutput"); //LCOV_EXCL_LINE @@ -177,17 +180,22 @@ void CAudioOutput::unprepare() { CAudioIO::unprepare(); - std::unique_lock mutex(mMutex); if (mpPulseAudioClient && mpPulseAudioClient->isInThread()) THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread"); + SAFE_FINALIZE(mpPulseAudioClient); SAFE_DELETE(mpPulseAudioClient); + + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); + mutex.unlock(); CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); } void CAudioOutput::pause() { + std::unique_lock mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE @@ -196,14 +204,20 @@ void CAudioOutput::pause() { THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "Can't pause if not in Running state"); - if (mpPulseAudioClient->isInThread() ) + if (mpPulseAudioClient->isInThread()) THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread"); //LCOV_EXCL_LINE CAudioIO::pause(); + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED); + + mutex.unlock(); + CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED); } void CAudioOutput::resume() { + std::unique_lock mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE @@ -216,10 +230,16 @@ void CAudioOutput::resume() { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread"); //LCOV_EXCL_LINE CAudioIO::resume(); + CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); + + mutex.unlock(); + CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); } void CAudioOutput::drain() { + std::lock_guard mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE @@ -227,14 +247,12 @@ void CAudioOutput::drain() { if (mStreamCallback.onStream) THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "async type don't support drain"); - std::unique_lock defer_mutex(mMutex, std::defer_lock); - if (!mpPulseAudioClient->isInThread()) - defer_mutex.lock(); - mpPulseAudioClient->drain(); } void CAudioOutput::flush() { + std::lock_guard mutex(mMutex); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE @@ -252,6 +270,11 @@ int CAudioOutput::getBufferSize() { } size_t CAudioOutput::write(const void* buffer, size_t length) { + std::unique_lock mutex(mMutex, std::defer_lock); + + if (mpPulseAudioClient && !mpPulseAudioClient->isInThread()) + mutex.lock(); + if (!__IsInit() || !__IsReady()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE @@ -277,8 +300,6 @@ size_t CAudioOutput::write(const void* buffer, size_t length) { } try { - /* For synchronization */ - std::unique_lock mutex(mMutex); // If another thread did call unprepare, do not write if (!mpPulseAudioClient) diff --git a/src/cpp/CPulseAudioClient.cpp b/src/cpp/CPulseAudioClient.cpp index 9b05a55..7367327 100644 --- a/src/cpp/CPulseAudioClient.cpp +++ b/src/cpp/CPulseAudioClient.cpp @@ -30,6 +30,22 @@ using namespace std; using namespace tizen_media_audio; +/** + * class CMainloopLocker + */ +CPulseAudioClient::CPulseThreadLocker::CPulseThreadLocker(pa_threaded_mainloop *mainloop) : ml(nullptr) { + if (!mainloop || pa_threaded_mainloop_in_thread(mainloop)) + return; + + pa_threaded_mainloop_lock(mainloop); + ml = mainloop; +} + +CPulseAudioClient::CPulseThreadLocker::~CPulseThreadLocker() { + if (ml) + pa_threaded_mainloop_unlock(const_cast(ml)); + ml = nullptr; +} /** * class CPulseAudioClient @@ -61,7 +77,11 @@ CPulseAudioClient::CPulseAudioClient( } CPulseAudioClient::~CPulseAudioClient() { - finalize(); + try { + finalize(); + } catch (const CAudioError& e) { + AUDIO_IO_LOGE("%s", e.getErrorMsg()); //LCOV_EXCL_LINE + } } void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) { @@ -71,13 +91,13 @@ void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) { switch (pa_context_get_state(c)) { case PA_CONTEXT_READY: - AUDIO_IO_LOGD("The context is ready"); + AUDIO_IO_LOGD("pa_context[%p] is ready", c); pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); break; case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: - AUDIO_IO_LOGD("The context is lost"); + AUDIO_IO_LOGD("pa_context[%p] is lost", c); pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); break; @@ -118,7 +138,8 @@ void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) { switch (pa_stream_get_state(s)) { case PA_STREAM_READY: - AUDIO_IO_LOGD("The stream is ready"); + AUDIO_IO_LOGD("The pa_stream[%p] is ready", s); + pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING); if (pClient->__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) pClient->__mIsFirstStream = true; @@ -128,7 +149,8 @@ void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) { //LCOV_EXCL_START case PA_STREAM_FAILED: - AUDIO_IO_LOGD("The stream is failed"); + AUDIO_IO_LOGD("The pa_stream[%p] is failed", s); + pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE, __is_microphone_restricted()); pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); @@ -136,7 +158,8 @@ void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) { //LCOV_EXCL_STOP case PA_STREAM_TERMINATED: - AUDIO_IO_LOGD("The stream is terminated"); + AUDIO_IO_LOGD("The pa_stream[%p] is terminated", s); + pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE); pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); break; @@ -174,12 +197,12 @@ void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* us #ifndef DISABLE_MOBILE_BACK_COMP if (!pClient->__mIsInit) { - AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create : Write dummy, length[%zu]", length); + AUDIO_IO_LOGD("pa_stream[%p] is on the way to create : Write dummy, length[%zu]", s, length); __dummy_write(s, length); return; } if (pClient->isCorked()) { - AUDIO_IO_LOGD("Occurred this listener when an out stream is CORKED : Write dummy, length[%zu]", length); + AUDIO_IO_LOGD("pa_stream[%p] is CORKED : Write dummy, length[%zu]", s, length); __dummy_write(s, length); return; } @@ -192,7 +215,8 @@ void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* us then write dummy data to ensure the start */ if (pClient->__mSpec.getStreamLatency() == CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC && pClient->__mIsFirstStream) { - AUDIO_IO_LOGW("[async] Write dummy of length[%zu] since not written in first callback during prepare", length); + AUDIO_IO_LOGW("[async] pa_stream[%p] : not written in the first callback during prepare, write dummy of length[%zu]", + s, length); __dummy_write(s, length); pClient->__mIsFirstStream = false; } @@ -205,6 +229,11 @@ void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) { auto pClient = static_cast(user_data); +#ifdef _AUDIO_IO_DEBUG_TIMING_ + /* FIXME : print some useful latency info */ + AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] latency updated", pClient, s); +#endif + pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); } @@ -214,7 +243,7 @@ void CPulseAudioClient::__streamStartedCb(pa_stream* s, void* user_data) { auto pClient = static_cast(user_data); - AUDIO_IO_LOGD("stream %p started.", pClient); + AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] started", pClient, s); pClient->__mIsStarted = true; } @@ -225,7 +254,7 @@ void CPulseAudioClient::__streamUnderflowCb(pa_stream* s, void* user_data) { auto pClient = static_cast(user_data); - AUDIO_IO_LOGD("stream %p UnderFlow...", pClient); + AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] UnderFlow...", pClient, s); pClient->__mIsStarted = false; } @@ -235,43 +264,51 @@ void CPulseAudioClient::__streamEventCb(pa_stream* s, const char *name, pa_propl assert(s); assert(user_data); - AUDIO_IO_LOGE("NAME : %s, Prop : %p", name, pl); - auto pClient = static_cast(user_data); + + AUDIO_IO_LOGW("pClient[%p] pa_stream[%p] : name[%s], proplist[%p]", pClient, s, name, pl); + if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) pa_operation_unref(pa_stream_cork(pClient->__mpStream, 1, NULL, NULL)); } //LCOV_EXCL_STOP void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) { - AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data); assert(s); assert(user_data); auto pClient = static_cast(user_data); + + AUDIO_IO_LOGD("pClient[%p], pa_stream[%p], success[%d], user_data[%p]", pClient, s, success, user_data); + pClient->__mIsOperationSuccess = static_cast(success); + /* FIXME : verify following action without any waitings */ pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); } //LCOV_EXCL_START void CPulseAudioClient::__successDrainCbInThread(pa_stream* s, int success, void* user_data) { - AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data); assert(s); assert(user_data); auto pClient = static_cast(user_data); + + AUDIO_IO_LOGD("pClient[%p], pa_stream[%p], success[%d], user_data[%p]", pClient, s, success, user_data); + pClient->__mIsOperationSuccess = static_cast(success); pClient->__mIsDraining = false; } //LCOV_EXCL_STOP void CPulseAudioClient::__successDrainCb(pa_stream* s, int success, void* user_data) { - AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data); assert(s); assert(user_data); auto pClient = static_cast(user_data); + + AUDIO_IO_LOGD("pClient[%p], pa_stream[%p], success[%d], user_data[%p]", pClient, s, success, user_data); + pClient->__mIsOperationSuccess = static_cast(success); pClient->__mIsDraining = false; @@ -324,12 +361,11 @@ void CPulseAudioClient::initialize() { THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()"); //LCOV_EXCL_LINE // LOCK for synchronous connection - pa_threaded_mainloop_lock(__mpMainloop); + CPulseThreadLocker locker{__mpMainloop}; // Start mainloop if (pa_threaded_mainloop_start(__mpMainloop) < 0) { //LCOV_EXCL_START - pa_threaded_mainloop_unlock(__mpMainloop); THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()"); //LCOV_EXCL_STOP } @@ -347,8 +383,8 @@ void CPulseAudioClient::initialize() { if (!PA_CONTEXT_IS_GOOD(state)) { //LCOV_EXCL_START err = pa_context_errno(__mpContext); - pa_threaded_mainloop_unlock(__mpMainloop); - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err); + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, + "pa_context's state is not good : err[%d]", err); //LCOV_EXCL_STOP } @@ -363,7 +399,6 @@ void CPulseAudioClient::initialize() { __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList); if (!__mpStream) { //LCOV_EXCL_START - pa_threaded_mainloop_unlock(__mpMainloop); THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()"); //LCOV_EXCL_STOP } @@ -406,8 +441,8 @@ void CPulseAudioClient::initialize() { if (ret != 0) { //LCOV_EXCL_START err = pa_context_errno(__mpContext); - pa_threaded_mainloop_unlock(__mpMainloop); - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() : err[%d]", err); + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, + "Failed pa_stream_connect() : err[%d]", err); //LCOV_EXCL_STOP } @@ -421,20 +456,18 @@ void CPulseAudioClient::initialize() { if (!PA_STREAM_IS_GOOD(state)) { err = pa_context_errno(__mpContext); - pa_threaded_mainloop_unlock(__mpMainloop); if (err == PA_ERR_ACCESS) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION, "pa_stream's state is not good : err[%d]", err); + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION, + "pa_stream's state is not good : err[%d]", err); else - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good : err[%d]", err); //LCOV_EXCL_LINE + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, //LCOV_EXCL_LINE + "pa_stream's state is not good : err[%d]", err); //LCOV_EXCL_LINE } /* Wait until the stream is ready */ pa_threaded_mainloop_wait(__mpMainloop); } - // End of synchronous - pa_threaded_mainloop_unlock(__mpMainloop); - // __mIsInit = true; // Moved to __streamStateChangeCb() } catch (const CAudioError& e) { //LCOV_EXCL_START @@ -442,12 +475,21 @@ void CPulseAudioClient::initialize() { throw; //LCOV_EXCL_END } + + AUDIO_IO_LOGD("Done : pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream); } void CPulseAudioClient::finalize() { - AUDIO_IO_LOGD(""); + AUDIO_IO_LOGD("pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream); + + if (!__mpMainloop) + return; - if (__mpMainloop && !isInThread()) + bool is_in_thread = isInThread(); + + __mIsInit = false; + + if (!is_in_thread) pa_threaded_mainloop_lock(__mpMainloop); /* clear callbacks */ @@ -460,7 +502,7 @@ void CPulseAudioClient::finalize() { pa_stream_set_event_callback(__mpStream, NULL, NULL); } - if (__mpMainloop && !isInThread()) + if (!is_in_thread) pa_threaded_mainloop_unlock(__mpMainloop); /* Wait for drain complete if draining before finalize */ @@ -497,20 +539,20 @@ void CPulseAudioClient::finalize() { __mpPropList = nullptr; } - __mIsInit = false; + AUDIO_IO_LOGD("Done"); } int CPulseAudioClient::read(void* buffer, size_t length) { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); //LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); //LCOV_EXCL_LINE checkRunningState(); if (!buffer) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p]", buffer); //LCOV_EXCL_LINE + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter : buffer[%p]", buffer); //LCOV_EXCL_LINE if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function"); //LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback client can't use this function"); //LCOV_EXCL_LINE size_t lengthIter = length; int ret = 0; @@ -518,7 +560,7 @@ int CPulseAudioClient::read(void* buffer, size_t length) { __mIsUsedSyncRead = true; try { - pa_threaded_mainloop_lock(__mpMainloop); + CPulseThreadLocker locker{__mpMainloop}; while (lengthIter > 0) { size_t l; @@ -567,7 +609,8 @@ int CPulseAudioClient::read(void* buffer, size_t length) { #endif ret = pa_stream_drop(__mpStream); if (ret != 0) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret); //LCOV_EXCL_LINE + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, //LCOV_EXCL_LINE + "Failed pa_stream_drop() : ret[%d]", ret); //LCOV_EXCL_LINE // Reset the internal pointer __mpSyncReadDataPtr = nullptr; @@ -576,11 +619,9 @@ int CPulseAudioClient::read(void* buffer, size_t length) { } } // End of while (lengthIter > 0) - pa_threaded_mainloop_unlock(__mpMainloop); __mIsUsedSyncRead = false; } catch (const CAudioError& e) { // LCOV_EXCL_START - pa_threaded_mainloop_unlock(__mpMainloop); __mIsUsedSyncRead = false; throw; // LCOV_EXCL_STOP @@ -591,26 +632,21 @@ int CPulseAudioClient::read(void* buffer, size_t length) { int CPulseAudioClient::peek(const void** buffer, size_t* length) { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE checkRunningState(); if (!buffer || !length) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, // LCOV_EXCL_LINE - "The parameter is invalid : buffer[%p], length[%p]", buffer, length); // LCOV_EXCL_LINE + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, // LCOV_EXCL_LINE + "invalid parameter : buffer[%p], length[%p]", buffer, length); // LCOV_EXCL_LINE if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE - int ret = 0; + CPulseThreadLocker locker{__mpMainloop}; + + int ret = pa_stream_peek(__mpStream, buffer, length); - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - ret = pa_stream_peek(__mpStream, buffer, length); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - ret = pa_stream_peek(__mpStream, buffer, length); - } #ifdef _AUDIO_IO_DEBUG_TIMING_ AUDIO_IO_LOGD("buffer[%p], length[%zu]", *buffer, *length); @@ -624,7 +660,7 @@ int CPulseAudioClient::peek(const void** buffer, size_t* length) { int CPulseAudioClient::drop() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE #ifdef _AUDIO_IO_DEBUG_TIMING_ AUDIO_IO_LOGD(""); @@ -633,18 +669,11 @@ int CPulseAudioClient::drop() { checkRunningState(); if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function"); // LCOV_EXCL_LINE - - int ret = 0; + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - ret = pa_stream_drop(__mpStream); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - ret = pa_stream_drop(__mpStream); - } + CPulseThreadLocker locker{__mpMainloop}; + int ret = pa_stream_drop(__mpStream); if (ret < 0) THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret); //LCOV_EXCL_LINE @@ -653,10 +682,10 @@ int CPulseAudioClient::drop() { int CPulseAudioClient::write(const void* data, size_t length) { if (!data) - THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter"); // LCOV_EXCL_LINE if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE int ret = 0; @@ -665,14 +694,13 @@ int CPulseAudioClient::write(const void* data, size_t length) { #endif if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); + CPulseThreadLocker locker{__mpMainloop}; if (pa_stream_is_corked(__mpStream)) { AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!"); pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this)); } ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE); - pa_threaded_mainloop_unlock(__mpMainloop); } else { // LCOV_EXCL_START if (pa_stream_is_corked(__mpStream)) { @@ -706,7 +734,7 @@ void CPulseAudioClient::cork(bool cork) { AUDIO_IO_LOGD("cork[%d]", cork); if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE if (isInThread()) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback"); // LCOV_EXCL_LINE @@ -717,40 +745,32 @@ void CPulseAudioClient::cork(bool cork) { if (!cork) __mIsFirstStream = true; - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - pa_operation_unref(pa_stream_cork(__mpStream, static_cast(cork), __successStreamCb, this)); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - pa_operation_unref(pa_stream_cork(__mpStream, static_cast(cork), __successStreamCb, this)); - } + CPulseThreadLocker locker{__mpMainloop}; + + /* FIXME: wait for completion like drain? */ + pa_operation_unref(pa_stream_cork(__mpStream, static_cast(cork), __successStreamCb, this)); + } bool CPulseAudioClient::isCorked() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE checkRunningState(); - int isCork = 0; + CPulseThreadLocker locker{__mpMainloop}; - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - isCork = pa_stream_is_corked(__mpStream); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - isCork = pa_stream_is_corked(__mpStream); - } + int isCorked = pa_stream_is_corked(__mpStream); #ifdef _AUDIO_IO_DEBUG_TIMING_ - AUDIO_IO_LOGD("isCork[%d]", isCork); + AUDIO_IO_LOGD("isCorked[%d]", isCorked); #endif - return static_cast(isCork); + return static_cast(isCorked); } bool CPulseAudioClient::drain() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE checkRunningState(); @@ -771,14 +791,14 @@ bool CPulseAudioClient::drain() { if (!isInThread()) { AUDIO_IO_LOGD("drain"); - pa_threaded_mainloop_lock(__mpMainloop); + CPulseThreadLocker locker{__mpMainloop}; + pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this); __mIsDraining = true; while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) pa_threaded_mainloop_wait(__mpMainloop); - pa_operation_unref(o); - pa_threaded_mainloop_unlock(__mpMainloop); + AUDIO_IO_LOGD("drain done"); } else { AUDIO_IO_LOGD("drain in thread"); @@ -791,43 +811,30 @@ bool CPulseAudioClient::drain() { bool CPulseAudioClient::flush() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE checkRunningState(); - if (!isInThread()) { - AUDIO_IO_LOGD("flush"); - pa_threaded_mainloop_lock(__mpMainloop); - pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this)); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - AUDIO_IO_LOGD("flush in thread"); - pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this)); - } + CPulseThreadLocker locker{__mpMainloop}; + + /* FIXME: wait for completion like drain? */ + pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this)); return true; } size_t CPulseAudioClient::getWritableSize() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); // LCOV_EXCL_LINE + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE checkRunningState(); if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback"); // LCOV_EXCL_LINE - size_t ret = 0; + CPulseThreadLocker locker{__mpMainloop}; - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - ret = pa_stream_writable_size(__mpStream); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - ret = pa_stream_writable_size(__mpStream); - } - - return ret; + return pa_stream_writable_size(__mpStream); } void CPulseAudioClient::checkRunningState() { @@ -848,7 +855,13 @@ void CPulseAudioClient::checkRunningState() { #endif } -bool CPulseAudioClient::isInThread() noexcept { +bool CPulseAudioClient::isInThread() const { + if (!__mIsInit) + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mIsInit is null"); + + if (!__mpMainloop) + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mpMainloop is null"); + int ret = pa_threaded_mainloop_in_thread(__mpMainloop); #ifdef _AUDIO_IO_DEBUG_TIMING_ @@ -860,176 +873,128 @@ bool CPulseAudioClient::isInThread() noexcept { //LCOV_EXCL_START size_t CPulseAudioClient::getReadableSize() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); checkRunningState(); if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture"); - size_t ret = 0; + CPulseThreadLocker locker{__mpMainloop}; - if (!isInThread()) { - pa_threaded_mainloop_lock(__mpMainloop); - ret = pa_stream_readable_size(__mpStream); - pa_threaded_mainloop_unlock(__mpMainloop); - } else { - ret = pa_stream_readable_size(__mpStream); - } - - return ret; + return pa_stream_readable_size(__mpStream); } size_t CPulseAudioClient::getBufferSize() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); checkRunningState(); - size_t ret = 0; + size_t buffer_size = 0; + CPulseThreadLocker locker{__mpMainloop}; - try { - if (!isInThread()) - pa_threaded_mainloop_lock(__mpMainloop); + const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream); + if (!attr) + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, + "Failed pa_stream_get_buffer_attr() : err[%d]", pa_context_errno(__mpContext)); - const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream); - if (!attr) { - int _err = pa_context_errno(__mpContext); - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() : err[%d]", _err); - } - - if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) { - ret = attr->tlength; - AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", ret); - } else { - ret = attr->fragsize; - AUDIO_IO_LOGD("RECORD buffer size[%zu]", ret); - } - } catch (const CAudioError& e) { - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - throw; + if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) { + buffer_size = attr->tlength; + AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", buffer_size); + } else { + buffer_size = attr->fragsize; + AUDIO_IO_LOGD("RECORD buffer size[%zu]", buffer_size); } - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - - return ret; + return buffer_size; } pa_usec_t CPulseAudioClient::getLatency() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); checkRunningState(); - pa_usec_t ret = 0; - int negative = 0; + pa_usec_t latency = 0; + int negative = 0; if (!isInThread()) { - if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) { + if (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) { int _err = pa_context_errno(__mpContext); if (_err != PA_ERR_NODATA) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err); + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, + "Failed pa_stream_get_latency() : err[%d]", _err); } - return negative ? 0 : ret; + return negative ? 0 : latency; } - pa_threaded_mainloop_lock(__mpMainloop); + CPulseThreadLocker locker{__mpMainloop}; - try { - while (true) { - if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) - break; - - int _err = pa_context_errno(__mpContext); - if (_err != PA_ERR_NODATA) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err); - - /* Wait until latency data is available again */ - pa_threaded_mainloop_wait(__mpMainloop); - } - } catch (const CAudioError& e) { - pa_threaded_mainloop_unlock(__mpMainloop); - throw; + while (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) { + int _err = pa_context_errno(__mpContext); + if (_err != PA_ERR_NODATA) + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, + "Failed pa_stream_get_latency() : err[%d]", _err); + /* Wait until latency data is available again */ + pa_threaded_mainloop_wait(__mpMainloop); } - pa_threaded_mainloop_unlock(__mpMainloop); - - return negative ? 0 : ret; + return negative ? 0 : latency; } pa_usec_t CPulseAudioClient::getFinalLatency() { if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); checkRunningState(); - pa_usec_t ret = 0; + pa_usec_t latency = 0; - try { - if (!isInThread()) - pa_threaded_mainloop_lock(__mpMainloop); - - uint32_t ver = pa_context_get_server_protocol_version(__mpContext); - if (ver >= 13) { - const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream); - const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream); - const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream); - - if (!buffer_attr || !sample_spec || !timing_info) - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream", - buffer_attr, sample_spec, timing_info); - - if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) { - ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec); - AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", ret); - } else { - ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec); - AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", ret); - } - } else { - THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver); - } + CPulseThreadLocker locker{__mpMainloop}; - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - } catch (const CAudioError& e) { - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - throw; + uint32_t ver = pa_context_get_server_protocol_version(__mpContext); + if (ver < 13) + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver); + + const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream); + const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream); + const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream); + + if (!buffer_attr || !sample_spec || !timing_info) + THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, + "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream", + buffer_attr, sample_spec, timing_info); + + if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) { + latency = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec); + AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", latency); + } else { + latency = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec); + AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", latency); } - return ret; + return latency; } //LCOV_EXCL_STOP void CPulseAudioClient::applyRecordVolume(double volume) { + if (!__mIsInit) - THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); + THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); checkRunningState(); if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client can't use this function"); //LCOV_EXCL_LINE - try { - if (!isInThread()) - pa_threaded_mainloop_lock(__mpMainloop); - - pa_cvolume cv = { 0, }; - pa_volume_t v = PA_VOLUME_NORM * volume; + CPulseThreadLocker locker{__mpMainloop}; - pa_cvolume_set(&cv, __mSpec.getChannelMap().channels, v); + pa_cvolume cv = { 0, }; + pa_volume_t v = PA_VOLUME_NORM * volume; - pa_operation_unref(pa_context_set_source_output_volume(__mpContext, - pa_stream_get_index(__mpStream), &cv, __successVolumeCb, NULL)); + pa_cvolume_set(&cv, __mSpec.getChannelMap().channels, v); - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - } catch (const CAudioError& e) { - if (!isInThread()) - pa_threaded_mainloop_unlock(__mpMainloop); - throw; - } + pa_operation_unref(pa_context_set_source_output_volume(__mpContext, + pa_stream_get_index(__mpStream), &cv, __successVolumeCb, NULL)); } diff --git a/src/cpp/cpp_audio_io.cpp b/src/cpp/cpp_audio_io.cpp index 283cb84..a2b4722 100644 --- a/src/cpp/cpp_audio_io.cpp +++ b/src/cpp/cpp_audio_io.cpp @@ -374,11 +374,13 @@ int cpp_audio_in_destroy(audio_in_h input) { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL input:%p", input); assert(handle->audioIoHandle); - AUDIO_IO_LOGD("[%p]", handle); + AUDIO_IO_LOGD("unpreparing [%p]", handle); /* Internal unprepare for backward compatibility */ handle->audioIoHandle->unprepare(); + AUDIO_IO_LOGD("try to destroy [%p]", handle); + SAFE_FINALIZE(handle->audioIoHandle); SAFE_DELETE(handle->audioIoHandle); SAFE_DELETE(handle); @@ -885,11 +887,13 @@ int cpp_audio_out_destroy(audio_out_h output) { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter is NULL output:%p", output); assert(handle->audioIoHandle); - AUDIO_IO_LOGD("[%p]", handle); + AUDIO_IO_LOGD("unpreparing [%p]", handle); /* Internal unprepare for backward compatibility */ handle->audioIoHandle->unprepare(); + AUDIO_IO_LOGD("try to destroy [%p]", handle); + SAFE_FINALIZE(handle->audioIoHandle); SAFE_DELETE(handle->audioIoHandle); SAFE_DELETE(handle); @@ -898,7 +902,7 @@ int cpp_audio_out_destroy(audio_out_h output) { return __convert_audio_io_error(e.getError()); } - AUDIO_IO_LOGD("[%p] destroyed", handle); + AUDIO_IO_LOGD("destroyed"); return AUDIO_IO_ERROR_NONE; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 67cedc2..9505dff 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,7 +13,7 @@ FOREACH(src ${sources}) GET_FILENAME_COMPONENT(src_name ${src} NAME_WE) MESSAGE("${src_name}") ADD_EXECUTABLE(${src_name} ${src}) - TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS} -lm) + TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS} -lm -pthread) ENDFOREACH() INSTALL(TARGETS audio_io_test DESTINATION bin) diff --git a/test/audio_io_test.c b/test/audio_io_test.c index f3f5304..5f88826 100644 --- a/test/audio_io_test.c +++ b/test/audio_io_test.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #ifndef M_PI #define M_PI (3.14159265) @@ -719,6 +721,243 @@ EXIT: return 0; } +#define THREAD_MAX 10 +#define TEST_COUNT 500 +static void *thread_stress_test_audio_in(void *data) +{ + int i, j; + int rate; + char *buf; + int buffer_size; + audio_channel_e ch; + audio_sample_type_e type; + + audio_in_h input = (audio_in_h)data; + + srand(time(NULL)); + + audio_in_prepare(input); + audio_in_get_buffer_size(input, &buffer_size); + buf = (char *)malloc(buffer_size); + if (buf == NULL) { + printf("malloc failed\n"); + goto EXIT; + } + + for (i=0; i 8) { @@ -743,6 +984,7 @@ int main(int argc, char **argv) } else { printf("- Usages :\n"); printf("- # audio_io_test loopback\n"); + printf("- # audio_io_test stress\n"); printf("- # audio_io_test [length to read] [number of iteration] [channels]\n"); printf("- # audio_io_test async [write(1) | read(2)]\n"); printf("- # audio_io_test play [filename] [sample rate] [channels] [type(0:U8,1:S16LE,2:S24LE,3:S24_32LE)]\n"); -- 2.34.1