ADD_DEFINITIONS("-DDISABLE_SESSION_BACK_COMP")
ENDIF(TIZEN_FEATURE_DISABLE_SESSION_BACKWARD_COMP)
+IF(TIZEN_FEATURE_PRODUCT_TV)
+ ADD_DEFINITIONS("-DDISABLE_MOBILE_BACK_COMP")
+ENDIF(TIZEN_FEATURE_PRODUCT_TV)
+
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
aux_source_directory(src/cpp CPPSOURCES)
void setStreamInfo(sound_stream_info_h stream_info);
void setInternalStreamInfo(); /* this is for session backward compatibility and will be removed later */
+ static int sendInterrupt(void* user_data);
CAudioInfo::EAudioIOState getState();
SStreamCallback mStreamCallback;
SStateChangedCallback mStateChangedCallback;
SInterruptCallback mInterruptCallback;
+ IAudioSessionEventListener::EInterruptCode mInterruptCode;
CAudioInfo::EAudioDirection mDirection;
CAudioInfo::EAudioIOState mState;
CAudioInfo::EAudioIOState mStatePrev;
bool mByPolicy;
+ bool mIsInterrupted;
private:
pthread_mutex_t __mMutex;
+ pthread_mutex_t __mCondMutex;
pthread_cond_t __mCond;
bool __mIsInit;
bool __mForceIgnore;
STREAM_LATENCY_OUTPUT_HIGH,
STREAM_LATENCY_OUTPUT_VOIP,
STREAM_LATENCY_OUTPUT_DEFAULT,
+ STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC,
STREAM_LATENCY_MAX
};
Name: capi-media-audio-io
Summary: An Audio Input & Audio Output library in Tizen Native API
-Version: 0.3.80
+Version: 0.3.81
Release: 0
Group: Multimedia/API
License: Apache-2.0
MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
%cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} \
%if "%{TIZEN_PRODUCT_TV}" == "1"
- -DTIZEN_FEATURE_DISABLE_SESSION_BACKWARD_COMP=On
+ -DTIZEN_FEATURE_DISABLE_SESSION_BACKWARD_COMP=On \
+ -DTIZEN_FEATURE_PRODUCT_TV=On
%endif
make %{?jobs:-j%jobs}
#include <mm.h>
#include <pthread.h>
#include <assert.h>
+#include <glib.h>
#include "CAudioIODef.h"
using namespace std;
mpAudioSessionHandler(NULL),
mpPulseAudioClient(NULL),
__mMutex(PTHREAD_MUTEX_INITIALIZER),
+ __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
__mCond(PTHREAD_COND_INITIALIZER),
__mIsInit(false),
__mForceIgnore(false) {
+ mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
mByPolicy = false;
+ mIsInterrupted = false;
+
}
CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
mpAudioSessionHandler(NULL),
mpPulseAudioClient(NULL),
__mMutex(PTHREAD_MUTEX_INITIALIZER),
+ __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
__mCond(PTHREAD_COND_INITIALIZER),
__mIsInit(false),
__mForceIgnore(false) {
mAudioInfo = audioInfo;
+ mInterruptCode = IAudioSessionEventListener::EInterruptCode::INTERRUPT_MAX;
mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
mByPolicy = false;
+ mIsInterrupted = false;
}
CAudioIO::~CAudioIO() {
if (pthread_mutex_lock(&__mMutex) != 0) {
THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
}
+
#ifdef _AUDIO_IO_DEBUG_TIMING_
- AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
+ AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mMutex);
#endif
}
THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
}
#ifdef _AUDIO_IO_DEBUG_TIMING_
- AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
+ AUDIO_IO_LOGD(COLOR_GREEN "%p UNLOCKED" COLOR_END, &__mMutex);
#endif
}
AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
#endif
- pthread_cond_wait(&__mCond, &__mMutex);
+ pthread_mutex_lock(&__mCondMutex);
+ pthread_cond_wait(&__mCond, &__mCondMutex);
+ pthread_mutex_unlock(&__mCondMutex);
}
void CAudioIO::internalSignal() {
AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
#endif
+ pthread_mutex_lock(&__mCondMutex);
pthread_cond_signal(&__mCond);
+ pthread_mutex_unlock(&__mCondMutex);
}
bool CAudioIO::isForceIgnore() {
AUDIO_IO_LOGD("finalize");
+ bool error_occured = false;
int ret = pthread_mutex_destroy(&__mMutex);
if (ret != 0) {
- THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
+ AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mMutex, ret);
+ error_occured = true;
+
+ }
+ ret = pthread_mutex_destroy(&__mCondMutex);
+ if (ret != 0) {
+ AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mCondMutex, ret);
+ error_occured = true;
}
+
ret = pthread_cond_destroy(&__mCond);
if (ret != 0) {
- THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
+ AUDIO_IO_LOGE("Failed pthread_cond_destroy(%p) errno:%d", &__mCond, ret);
+ error_occured = true;
}
+ if (error_occured)
+ THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Finalize Failed");
+
__mIsInit = false;
}
return mState;
}
-void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
+int CAudioIO::sendInterrupt(void* user_data) {
+ CAudioIO *pCaudioIo = (CAudioIO *)user_data;
+
+ if (pCaudioIo && pCaudioIo->mInterruptCallback.onInterrupt) {
+ AUDIO_IO_LOGD("sending interrupt [%d]", pCaudioIo->mInterruptCode);
+ pCaudioIo->mInterruptCallback.onInterrupt(pCaudioIo->mInterruptCode, pCaudioIo->mInterruptCallback.mUserData);
+ }
+ return 0;
+}
+
+int caudio_gsource_callback(void *user_data) {
+ CAudioIO::sendInterrupt(user_data);
+ return 0;
+}
+
+void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type,
+ mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
assert(pHandler);
int session_option = pHandler->getOptions();
}
if (state == FOCUS_IS_RELEASED) {
- // Focus handle(id) of the other application was released, do resume if possible
- internalLock();
- if (mpPulseAudioClient) {
- mpPulseAudioClient->cork(false);
- onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
- }
- internalUnlock();
-
+ // Focus handle(id) of the other application was released, notify resume
// Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
state = FOCUS_IS_ACQUIRED;
} else if (state == FOCUS_IS_ACQUIRED) {
// Focus handle(id) of the other application was acquired, do pause if possible
internalLock();
if (mpPulseAudioClient) {
- /* FIXME: Skip this codes due to the blocking of drain() function
- if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
- if (mpPulseAudioClient->drain() == false) {
- AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
- }
- }
- */
mpPulseAudioClient->cork(true);
- onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
}
+ mIsInterrupted = true;
internalUnlock();
// Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
// Focus handle(id) was released, do pause here
internalLock();
if (mpPulseAudioClient) {
- /* FIXME: Skip this codes due to the blocking of drain() function
- if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
- if (mpPulseAudioClient->drain() == false) {
- AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
- }
- }
- */
mpPulseAudioClient->cork(true);
- onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
}
+
+ mIsInterrupted = true;
internalUnlock();
} else if (state == FOCUS_IS_ACQUIRED) {
// Focus handle(id) was acquired again,
// check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
// do resume here and call interrupt completed callback to application.
- internalLock();
- if (mpPulseAudioClient) {
- mpPulseAudioClient->cork(false);
- onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
- }
- internalUnlock();
}
}
if (mInterruptCallback.onInterrupt != NULL) {
IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
- mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
+
+ if (EInterruptCode::INTERRUPT_COMPLETED == e) {
+ mInterruptCode = e;
+ g_idle_add(caudio_gsource_callback, this);
+ } else {
+ mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
+ }
}
}
if (__mIsInit == false) {
THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
}
-
+ if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN) {
+ AUDIO_IO_LOGD("Prepare for Audio in");
+ } else if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_OUT) {
+ AUDIO_IO_LOGD("Prepare for Audio Out");
+ }
try {
+ if (mIsInterrupted) {
+ AUDIO_IO_LOGE("This is preparing during interrupted!!!");
+ bool isSkip = mpAudioSessionHandler->isSkipSession();
+ if (__mForceIgnore == false && isSkip == false && mpAudioSessionHandler->getId() >= 0) {
+ AUDIO_IO_LOGE("Session updatePlaying!!!");
+ mpAudioSessionHandler->updatePlaying();
+ }
+
+ if (mpPulseAudioClient && mpPulseAudioClient->isCorked()) {
+ AUDIO_IO_LOGE("Uncork!");
+ mpPulseAudioClient->cork(false);
+ }
+ mIsInterrupted = false;
+ }
+
AUDIO_IO_LOGD("------> prepare done");
/* Do nothing */
} catch (CAudioError& e) {
if (__IsReady() == true) {
AUDIO_IO_LOGD("Already prepared CAudioInput");
+ CAudioIO::prepare();
return;
}
if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
mpAudioSessionHandler->updatePlaying();
+#ifndef DISABLE_MOBILE_BACK_COMP
+ // Uncork stream which is created with CORKED flag */
+ mpPulseAudioClient->cork(false);
+#endif
+
internalUnlock();
// Do Prepare
CAudioIO::prepare();
} catch (CAudioError& e) {
+ SAFE_FINALIZE(mpPulseAudioClient);
+ SAFE_DELETE(mpPulseAudioClient);
internalUnlock();
throw;
} catch (const std::bad_alloc&) {
if (mpAudioSessionHandler->getId() >= 0) {
if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
mpAudioSessionHandler->updateStop();
-
- if (mpAudioSessionHandler->isSkipSession() == false)
- mpAudioSessionHandler->unregisterSound();
}
CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
try {
internalLock();
+ if (mIsInterrupted) {
+ THROW_ERROR_MSG(CAudioError::EError::ERROR_POLICY_BLOCKED, "audio-io is interrupted");
+ }
// If another thread did call unprepare, do not read
if (mpPulseAudioClient == NULL)
if (__IsReady() == true) {
AUDIO_IO_LOGD("Already prepared CAudioOutput");
+ CAudioIO::prepare();
return;
}
CAudioIO::setInternalStreamInfo();
// Init StreamSpec
- AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_DEFAULT");
CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT;
+#ifndef DISABLE_MOBILE_BACK_COMP
+ if (!mStreamCallback.onStream) {
+ AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_DEFAULT");
+ streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT;
+ } else {
+ AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC");
+ streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC;
+ }
+#endif
CPulseStreamSpec spec(streamSpec, mAudioInfo);
// Create PulseAudio Handler
if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
mpAudioSessionHandler->updatePlaying();
+#ifndef DISABLE_MOBILE_BACK_COMP
+ // Uncork stream which is created with CORKED flag */
+ mpPulseAudioClient->cork(false);
+#endif
internalUnlock();
CAudioIO::prepare();
} catch (CAudioError& e) {
+ SAFE_FINALIZE(mpPulseAudioClient);
+ SAFE_DELETE(mpPulseAudioClient);
internalUnlock();
throw;
} catch (const std::bad_alloc&) {
}
try {
+ if (mpAudioSessionHandler->getId() >= 0 && !mIsInterrupted) {
+ if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false) {
+ CPulseStreamSpec::EStreamLatency streamSpec;
+ streamSpec = mpPulseAudioClient->getStreamSpec().getStreamLatency();
+ if (CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC != streamSpec)
+ CAudioIO::drain();
+ }
+ }
CAudioIO::unprepare();
internalLock();
if (mpAudioSessionHandler->getId() >= 0) {
if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
mpAudioSessionHandler->updateStop();
-
- if (mpAudioSessionHandler->isSkipSession() == false)
- mpAudioSessionHandler->unregisterSound();
}
CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
}
void CAudioOutput::drain() {
+ CPulseStreamSpec::EStreamLatency streamSpec;
+
if (__IsInit() == false || __IsReady() == false) {
THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
"Did not initialize or prepare CAudioOutput");
}
+ streamSpec = mpPulseAudioClient->getStreamSpec().getStreamLatency();
+ if (CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC == streamSpec)
+ THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "async type don't support drain");
+
try {
CAudioIO::drain();
} catch (CAudioError& e) {
try {
/* For synchronization */
internalLock();
+ if (mIsInterrupted) {
+ THROW_ERROR_MSG(CAudioError::EError::ERROR_POLICY_BLOCKED, "audio io is interrupted");
+ }
// If another thread did call unprepare, do not write
if (mpPulseAudioClient == NULL)
__pcmCaptureCountDec();
}
+ unregisterSound();
+
if (__mSubscribeId > 0) {
AUDIO_IO_LOGD("Unsubscribed mm_sound signal [id:%d]", __mSubscribeId);
mm_sound_unsubscribe_signal(__mSubscribeId);
pClient->__mpListener->onStream(pClient, length);
}
+#ifndef DISABLE_MOBILE_BACK_COMP
+static void __dummy_write(pa_stream* s, size_t length) {
+ char* dummy = new char[length];
+ memset(dummy, 0, length);
+ pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
+ delete [] dummy;
+}
+#endif
+
void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
assert(s);
assert(user_data);
CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
assert(pClient->__mpListener);
+#ifndef DISABLE_MOBILE_BACK_COMP
+ if (pClient->__mIsInit == false) {
+ AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create : Write dummy, length[%d]", length);
+ __dummy_write(s, length);
+ return;
+ }
+ if (pClient->isCorked()) {
+ AUDIO_IO_LOGD("Occurred this listener when an out stream is CORKED : Write dummy, length[%d]", length);
+ __dummy_write(s, length);
+ return;
+ }
+#endif
+
pClient->__mpListener->onStream(pClient, length);
/* If stream is not written in first callback during prepare,
pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_ADJUST_LATENCY |
+#ifndef DISABLE_MOBILE_BACK_COMP
+ PA_STREAM_START_CORKED |
+#endif
PA_STREAM_AUTO_TIMING_UPDATE);
ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_ADJUST_LATENCY |
+#ifndef DISABLE_MOBILE_BACK_COMP
+ PA_STREAM_START_CORKED |
+#endif
PA_STREAM_AUTO_TIMING_UPDATE);
ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
isCork = pa_stream_is_corked(__mpStream);
}
+#ifdef _AUDIO_IO_DEBUG_TIMING_
AUDIO_IO_LOGD("isCork[%d]", isCork);
+#endif
return static_cast<bool>(isCork);
}
checkRunningState();
- if (__mIsDraining)
+ if (isCorked()) {
+ AUDIO_IO_LOGW("Corked...");
+ return true;
+ }
+
+ if (__mIsDraining) {
AUDIO_IO_LOGW("already draining...");
+ return true;
+ }
if (isInThread() == false) {
AUDIO_IO_LOGD("drain");
static const char* STREAM_NAME_OUTPUT_HIGH_LATENCY = "HIGH LATENCY PLAYBACK";
static const char* STREAM_NAME_OUTPUT_VOIP = "VOIP PLAYBACK";
static const char* STREAM_NAME_OUTPUT_DEFAULT = "DEFAULT PLAYBACK";
+static const char* STREAM_NAME_OUTPUT_DEFAULT_ASYNC = "DEFAULT PLAYBACK ASYNC";
static const char* STREAM_LATENCY_LOW = "low";
static const char* STREAM_LATENCY_MID = "mid";
static const char* STREAM_LATENCY_HIGH = "high";
static const char* STREAM_LATENCY_VOIP = "voip";
static const char* STREAM_LATENCY_DEFAULT = "default";
+static const char* STREAM_LATENCY_DEFAULT_ASYNC = "default-async";
CPulseStreamSpec::CPulseStreamSpec():
__mStreamName = STREAM_NAME_OUTPUT_DEFAULT;
break;
+ case EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC:
+ __mStreamName = STREAM_NAME_OUTPUT_DEFAULT_ASYNC;
+ break;
+
default:
AUDIO_IO_LOGW("Invalid __mLatency[%d]", static_cast<int>(__mLatency));
break;
latency = STREAM_LATENCY_DEFAULT;
break;
+ case EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC:
+ latency = STREAM_LATENCY_DEFAULT_ASYNC;
+ break;
+
default:
AUDIO_IO_LOGW("Invalid __mLatency[%d]", static_cast<int>(__mLatency));
latency = STREAM_LATENCY_DEFAULT;