From 6d3ff99cdf09e8c035ea0f7362ca4caef004f78f Mon Sep 17 00:00:00 2001 From: Seungbae Shin Date: Fri, 20 Jan 2017 18:26:24 +0900 Subject: [PATCH] Fix drain called in thread and wait for drain completion [Version] 0.3.57 [Profile] Common [Issue Type] Bug Change-Id: I308159d4bec56f4b1ccbbab9e5bd929984859955 --- include/CPulseAudioClient.h | 3 ++ packaging/capi-media-audio-io.spec | 2 +- src/cpp/CAudioIO.cpp | 41 ++++++++++++++-------- src/cpp/CAudioOutput.cpp | 6 +++- src/cpp/CPulseAudioClient.cpp | 70 +++++++++++++++++++++++++++++++++----- test/audio_io_test.c | 8 +++-- 6 files changed, 101 insertions(+), 29 deletions(-) diff --git a/include/CPulseAudioClient.h b/include/CPulseAudioClient.h index b2778a3..cab7757 100644 --- a/include/CPulseAudioClient.h +++ b/include/CPulseAudioClient.h @@ -99,6 +99,7 @@ namespace tizen_media_audio { size_t __mSyncReadLength; bool __mIsUsedSyncRead; bool __mIsFirstStream; + bool __mIsDraining; /* Static Methods */ @@ -114,6 +115,8 @@ namespace tizen_media_audio { static void __streamLatencyUpdateCb(pa_stream* s, void* user_data); static void __streamEventCb(pa_stream* s, const char *name, pa_proplist *pl, void *user_data); static void __successStreamCb(pa_stream* s, int success, void* user_data); + static void __successDrainCb(pa_stream* s, int success, void* user_data); + static void __successDrainCbInThread(pa_stream* s, int success, void* user_data); }; diff --git a/packaging/capi-media-audio-io.spec b/packaging/capi-media-audio-io.spec index 5ea5a53..c4ac976 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.3.56 +Version: 0.3.57 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/cpp/CAudioIO.cpp b/src/cpp/CAudioIO.cpp index e7b4095..7fe98a9 100644 --- a/src/cpp/CAudioIO.cpp +++ b/src/cpp/CAudioIO.cpp @@ -20,8 +20,6 @@ #include #include "CAudioIODef.h" -#define AUDIO_IO_DEBUG - using namespace std; using namespace tizen_media_audio; @@ -188,7 +186,10 @@ void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) { if (mState == mStatePrev) return; - AUDIO_IO_LOGD("current(%d), previous(%d), by_policy(%d)", mState, mStatePrev, mByPolicy); + const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" }; + + AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)", + state_string[(int)mStatePrev], mStatePrev, state_string[(int)mState], mState, mByPolicy); if (mStateChangedCallback.onStateChanged != NULL) { mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData); @@ -315,7 +316,7 @@ void CAudioIO::prepare() throw(CAudioError) { } try { - AUDIO_IO_LOGD("prepare"); + AUDIO_IO_LOGD("------> prepare done"); /* Do nothing */ } catch (CAudioError e) { throw e; @@ -328,7 +329,7 @@ void CAudioIO::unprepare() throw(CAudioError) { } try { - AUDIO_IO_LOGD("unprepare"); + AUDIO_IO_LOGD("unprepare ----->"); /* Do nothing */ } catch (CAudioError e) { throw e; @@ -373,12 +374,17 @@ void CAudioIO::drain() throw(CAudioError) { } try { - internalLock(); - AUDIO_IO_LOGD("drain"); - mpPulseAudioClient->drain(); - internalUnlock(); + if (mpPulseAudioClient->isInThread()) { + mpPulseAudioClient->drain(); + } else { + internalLock(); + mpPulseAudioClient->drain(); + internalUnlock(); + } } catch (CAudioError e) { - internalUnlock(); + if (!mpPulseAudioClient->isInThread()) { + internalUnlock(); + } throw e; } } @@ -389,12 +395,17 @@ void CAudioIO::flush() throw(CAudioError) { } try { - internalLock(); - AUDIO_IO_LOGD("flush"); - mpPulseAudioClient->flush(); - internalUnlock(); + if (mpPulseAudioClient->isInThread()) { + mpPulseAudioClient->flush(); + } else { + internalLock(); + mpPulseAudioClient->flush(); + internalUnlock(); + } } catch (CAudioError e) { - internalUnlock(); + if (!mpPulseAudioClient->isInThread()) { + internalUnlock(); + } throw e; } } diff --git a/src/cpp/CAudioOutput.cpp b/src/cpp/CAudioOutput.cpp index 19de90f..5a44bd9 100644 --- a/src/cpp/CAudioOutput.cpp +++ b/src/cpp/CAudioOutput.cpp @@ -21,7 +21,6 @@ using namespace std; using namespace tizen_media_audio; - /** * class CAudioOutput */ @@ -343,6 +342,11 @@ size_t CAudioOutput::write(const void* buffer, size_t length) throw(CAudioError) if (ret < 0) { THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret); } + +#ifdef _AUDIO_IO_DEBUG_TIMING_ + AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%d)", buffer, length); +#endif + return length; } diff --git a/src/cpp/CPulseAudioClient.cpp b/src/cpp/CPulseAudioClient.cpp index d0a0e0d..9733013 100644 --- a/src/cpp/CPulseAudioClient.cpp +++ b/src/cpp/CPulseAudioClient.cpp @@ -17,6 +17,7 @@ #include #include "CAudioIODef.h" +#include #ifdef ENABLE_DPM #include #endif @@ -29,6 +30,8 @@ using namespace tizen_media_audio; * class CPulseAudioClient */ const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT"; +static const unsigned int drain_wait_interval = 10000; +static const unsigned int drain_wait_max_count = 30; CPulseAudioClient::CPulseAudioClient( EStreamDirection direction, @@ -47,7 +50,8 @@ CPulseAudioClient::CPulseAudioClient( __mSyncReadIndex(0), __mSyncReadLength(0), __mIsUsedSyncRead(false), - __mIsFirstStream(false) { + __mIsFirstStream(false), + __mIsDraining(false) { } CPulseAudioClient::~CPulseAudioClient() { @@ -202,8 +206,29 @@ void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_ pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); } +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); + + CPulseAudioClient* pClient = static_cast(user_data); + pClient->__mIsOperationSuccess = static_cast(success); + pClient->__mIsDraining = false; +} + +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); + + CPulseAudioClient* pClient = static_cast(user_data); + pClient->__mIsOperationSuccess = static_cast(success); + pClient->__mIsDraining = false; + + pa_threaded_mainloop_signal(pClient->__mpMainloop, 0); +} + void CPulseAudioClient::initialize() throw(CAudioError) { - AUDIO_IO_LOGD(""); if (__mIsInit == true) { return; } @@ -363,6 +388,24 @@ void CPulseAudioClient::finalize() { return; } + /* clear callbacks */ + if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) + pa_stream_set_write_callback(__mpStream, NULL, NULL); + else + pa_stream_set_read_callback(__mpStream, NULL, NULL); + pa_stream_set_latency_update_callback(__mpStream, NULL, NULL); + pa_stream_set_event_callback(__mpStream, NULL, NULL); + + /* Wait for drain complete if draining before finalize */ + if (__mIsDraining) { + unsigned int drain_wait_count = 0; + while (__mIsDraining && drain_wait_count++ < drain_wait_max_count) { + usleep(drain_wait_interval); + } + AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]", + drain_wait_count, drain_wait_interval); + } + if (__mpMainloop != NULL) { pa_threaded_mainloop_stop(__mpMainloop); } @@ -591,7 +634,8 @@ int CPulseAudioClient::write(const void* data, size_t length) throw(CAudioError) delete [] dummy; } __mIsFirstStream = false; - AUDIO_IO_LOGD("FIRST STREAM CALLBACK : length[%d], prebuf[%d], dummy[%d]", length, prebuf, (length < prebuf) ? prebuf - length : 0); + AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%d], prebuf[%d], dummy[%d]", + length, prebuf, (length < prebuf) ? prebuf - length : 0); } ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE); } @@ -653,28 +697,34 @@ bool CPulseAudioClient::isCorked() throw(CAudioError) { } bool CPulseAudioClient::drain() throw(CAudioError) { - AUDIO_IO_LOGD("drain"); - if (__mIsInit == false) { THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); } checkRunningState(); + if (__mIsDraining) + AUDIO_IO_LOGW("already draining..."); + if (isInThread() == false) { + AUDIO_IO_LOGD("drain"); pa_threaded_mainloop_lock(__mpMainloop); - pa_operation_unref(pa_stream_drain(__mpStream, __successStreamCb, this)); + 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_threaded_mainloop_unlock(__mpMainloop); } else { - pa_operation_unref(pa_stream_drain(__mpStream, __successStreamCb, this)); + AUDIO_IO_LOGD("drain in thread"); + pa_operation_unref(pa_stream_drain(__mpStream, __successDrainCbInThread, this)); + __mIsDraining = true; } return true; } bool CPulseAudioClient::flush() throw(CAudioError) { - AUDIO_IO_LOGD("flush"); - if (__mIsInit == false) { THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient"); } @@ -682,10 +732,12 @@ bool CPulseAudioClient::flush() throw(CAudioError) { checkRunningState(); if (isInThread() == false) { + 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)); } diff --git a/test/audio_io_test.c b/test/audio_io_test.c index b1926a3..02737cf 100644 --- a/test/audio_io_test.c +++ b/test/audio_io_test.c @@ -59,6 +59,7 @@ void play_file(char *file, int length, int ch) audio_out_prepare(output); audio_out_write(output, buf, length); + audio_out_drain(output); audio_out_unprepare(output); audio_out_destroy(output); @@ -109,6 +110,8 @@ void play_file_sample(char *file, int frequency, int ch, int type) return; } + getchar(); + while (file_size > 0) { read_bytes = fread(buf, 1, buffer_size, fp); printf("Read %d Requested - %d\n", read_bytes, buffer_size); @@ -559,10 +562,9 @@ int audio_io_async_test(int mode) } do { - int gotchar; printf("command(q:quit) : "); - gotchar = getchar(); - if (gotchar == EOF) + cmd = getchar(); + if (cmd == EOF) goto EXIT; if (cmd != '\n') getchar(); -- 2.7.4