Avoid infinite blocking on internalWait() by replacing cond_wait with cond_timedwait
[platform/core/api/audio-io.git] / src / cpp / CAudioIO.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <pthread.h>
19 #include <assert.h>
20 #include "CAudioIODef.h"
21 #include <sound_manager_internal.h>
22 #include <sys/time.h>
23 #include <string.h>
24
25 using namespace std;
26 using namespace tizen_media_audio;
27
28
29 /**
30  * class CAudioIO
31  */
32 //LCOV_EXCL_START
33 CAudioIO::CAudioIO() :
34     mpPulseAudioClient(nullptr),
35     __mMutex(PTHREAD_MUTEX_INITIALIZER),
36     __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
37     __mCond(PTHREAD_COND_INITIALIZER),
38     __mIsInit(false) {
39     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
40     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
41     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
42     mByPolicy = false;
43 }
44 //LCOV_EXCL_STOP
45
46 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
47     mpPulseAudioClient(nullptr),
48     __mMutex(PTHREAD_MUTEX_INITIALIZER),
49     __mCondMutex(PTHREAD_MUTEX_INITIALIZER),
50     __mCond(PTHREAD_COND_INITIALIZER),
51     __mIsInit(false) {
52     mAudioInfo = audioInfo;
53     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_MAX;
54     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
55     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
56     mByPolicy = false;
57 }
58
59 void CAudioIO::setInit(bool flag) {
60     __mIsInit = flag;
61 }
62
63 bool CAudioIO::isInit() {
64     return __mIsInit;
65 }
66
67 bool CAudioIO::IsReady() {
68     return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING ||
69              mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED));
70 }
71
72 void CAudioIO::internalLock() {
73     if (!__mIsInit)
74         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
75
76     if (pthread_mutex_lock(&__mMutex) != 0)
77         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()"); //LCOV_EXCL_LINE
78
79 #ifdef _AUDIO_IO_DEBUG_TIMING_
80     AUDIO_IO_LOGD(COLOR_RED "%p LOCKED" COLOR_END, &__mMutex);
81 #endif
82 }
83
84 void CAudioIO::internalUnlock() {
85     if (!__mIsInit)
86         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
87
88     if (pthread_mutex_unlock(&__mMutex) != 0)
89         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()"); //LCOV_EXCL_LINE
90
91 #ifdef _AUDIO_IO_DEBUG_TIMING_
92     AUDIO_IO_LOGD(COLOR_GREEN "%p UNLOCKED" COLOR_END, &__mMutex);
93 #endif
94 }
95
96 void CAudioIO::internalWait() {
97     if (!__mIsInit)
98         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
99
100 #ifdef _AUDIO_IO_DEBUG_TIMING_
101     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
102 #endif
103     pthread_mutex_lock(&__mCondMutex);
104
105     struct timeval now = { 0, };
106     struct timeval to_wait = { 0, };
107     struct timeval until = { 0, };
108     struct timespec until_ts = { 0, };
109
110     constexpr int COND_TIMEOUT_MS = 200;
111
112     gettimeofday(&now, nullptr);
113     to_wait.tv_sec = COND_TIMEOUT_MS / 1000UL;
114     to_wait.tv_usec = (COND_TIMEOUT_MS % 1000UL) * 1000UL;
115     timeradd(&now, &to_wait, &until);
116     until_ts.tv_sec = until.tv_sec;
117     until_ts.tv_nsec = until.tv_usec * 1000UL;
118
119     if (pthread_cond_timedwait(&__mCond, &__mCondMutex, &until_ts) != 0) {
120         char str_error[256];
121         AUDIO_IO_LOGE("pthread_cond_timedwait error=%s", strerror_r(errno, str_error, sizeof(str_error)));
122     }
123
124     pthread_mutex_unlock(&__mCondMutex);
125 }
126
127 void CAudioIO::internalSignal() {
128     if (!__mIsInit)
129         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
130
131 #ifdef _AUDIO_IO_DEBUG_TIMING_
132     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
133 #endif
134
135     pthread_mutex_lock(&__mCondMutex);
136     pthread_cond_signal(&__mCond);
137     pthread_mutex_unlock(&__mCondMutex);
138 }
139
140 void CAudioIO::initialize() {
141     if (__mIsInit)
142         return;
143
144     AUDIO_IO_LOGD("initialize");
145
146     int ret = pthread_mutex_init(&__mMutex, NULL);
147     if (ret != 0)
148         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()"); //LCOV_EXCL_LINE
149
150     ret = pthread_cond_init(&__mCond, NULL);
151     if (ret != 0)
152         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()"); //LCOV_EXCL_LINE
153
154     __mIsInit = true;
155 }
156
157 void CAudioIO::finalize() {
158     if (!__mIsInit)
159         return;
160
161     AUDIO_IO_LOGD("finalize");
162
163     bool error_occured = false;
164     int ret = pthread_mutex_destroy(&__mMutex);
165     if (ret != 0) {
166 //LCOV_EXCL_START
167         AUDIO_IO_LOGE("Failed pthread_mutex_destroy(%p) errno:%d", &__mMutex, ret);
168         error_occured = true;
169 //LCOV_EXCL_STOP
170     }
171
172     ret = pthread_mutex_destroy(&__mCondMutex);
173     if (ret != 0) {
174 //LCOV_EXCL_START
175         AUDIO_IO_LOGE("Failed cond pthread_mutex_destroy(%p) errno:%d", &__mCondMutex, ret);
176         error_occured = true;
177 //LCOV_EXCL_STOP
178     }
179
180     ret = pthread_cond_destroy(&__mCond);
181     if (ret != 0) {
182 //LCOV_EXCL_START
183         AUDIO_IO_LOGE("Failed pthread_cond_destroy(%p) errno:%d", &__mCond, ret);
184         error_occured = true;
185 //LCOV_EXCL_STOP
186     }
187
188     if (error_occured)
189         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Finalize Failed"); //LCOV_EXCL_LINE
190
191     __mIsInit = false;
192 }
193
194 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
195     assert(__mIsInit);
196     assert(pClient);
197     assert(length > 0);
198
199 #ifdef _AUDIO_IO_DEBUG_TIMING_
200     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
201 #endif
202
203     if (mStreamCallback.onStream)
204         mStreamCallback.onStream(length, mStreamCallback.mUserData);
205 }
206
207 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
208     assert(__mIsInit);
209     assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
210
211     mStatePrev = mState;
212     mState = state;
213     mByPolicy = byPolicy;
214
215     if (mState == mStatePrev)
216         return;
217
218     static const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
219
220     AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
221                   state_string[static_cast<int>(mStatePrev)],
222                   static_cast<int>(mStatePrev),
223                   state_string[static_cast<int>(mState)],
224                   static_cast<int>(mState),
225                   mByPolicy);
226
227     if (mStateChangedCallback.onStateChanged)
228         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
229 }
230
231 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
232     onStateChanged(state, false);
233 }
234
235 CAudioInfo::EAudioIOState CAudioIO::getState() noexcept {
236     return mState;
237 }
238
239 void CAudioIO::prepare() {
240     if (!__mIsInit)
241         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
242 }
243
244 void CAudioIO::unprepare() {
245     if (!__mIsInit)
246         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
247 }
248
249 void CAudioIO::pause() {
250     if (!__mIsInit || !IsReady())
251         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
252
253     try {
254         internalLock();
255         AUDIO_IO_LOGD("pause");
256         mpPulseAudioClient->cork(true);
257         internalUnlock();
258     } catch (const CAudioError& e) {
259         internalUnlock();
260         throw;
261     }
262 }
263
264 void CAudioIO::resume() {
265     if (!__mIsInit || !IsReady())
266         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
267
268     try {
269         internalLock();
270         AUDIO_IO_LOGD("resume");
271         mpPulseAudioClient->cork(false);
272         internalUnlock();
273     } catch (const CAudioError& e) {
274         internalUnlock();
275         throw;
276     }
277 }
278
279 void CAudioIO::drain() {
280     if (!__mIsInit || !IsReady())
281         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
282
283     try {
284         if (mpPulseAudioClient->isInThread()) {
285             mpPulseAudioClient->drain();
286         } else {
287             internalLock();
288             mpPulseAudioClient->drain();
289             internalUnlock();
290         }
291     } catch (const CAudioError& e) {
292         if (!mpPulseAudioClient->isInThread())
293             internalUnlock();
294         throw;
295     }
296 }
297
298 void CAudioIO::flush() {
299     if (!__mIsInit || !IsReady())
300         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO"); //LCOV_EXCL_LINE
301
302     try {
303         if (mpPulseAudioClient->isInThread()) {
304             mpPulseAudioClient->flush();
305         } else {
306             internalLock();
307             mpPulseAudioClient->flush();
308             internalUnlock();
309         }
310     } catch (const CAudioError& e) {
311         if (!mpPulseAudioClient->isInThread())
312             internalUnlock();
313         throw;
314     }
315 }
316
317 CAudioInfo& CAudioIO::getAudioInfo() {
318     if (!__mIsInit)
319         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
320
321     return mAudioInfo;
322 }
323
324 void CAudioIO::setStreamCallback(SStreamCallback callback) {
325     if (!__mIsInit)
326         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
327
328     mStreamCallback = callback;
329 }
330
331 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() {
332     if (!__mIsInit)
333         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
334
335     return mStreamCallback;
336 }
337
338 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) {
339     if (!__mIsInit)
340         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
341
342     mStateChangedCallback = callback;
343 }
344
345 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() {
346     if (!__mIsInit)
347         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
348
349     return mStateChangedCallback;
350 }
351
352 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) {
353     if (!stream_info)
354         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL"); //LCOV_EXCL_LINE
355
356     if (!__mIsInit)
357         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO"); //LCOV_EXCL_LINE
358
359     if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
360         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started"); //LCOV_EXCL_LINE
361
362     int errorCode = SOUND_MANAGER_ERROR_NONE;
363     char *type = nullptr;
364     int index = -1;
365     bool avail = false;
366
367     if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
368         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
369     if (!avail)
370         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported"); //LCOV_EXCL_LINE
371
372     if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
373         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
374     if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
375         getAudioInfo().setAudioTypeByInputStreamType(type);
376     else
377         getAudioInfo().setAudioTypeByOutputStreamType(type);
378
379     if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
380         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode); //LCOV_EXCL_LINE
381     getAudioInfo().setAudioIndex(index);
382 }