Trigger stream state change callback when stream is failed/terminated and include...
[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 <mm.h>
19 #include <pthread.h>
20 #include <assert.h>
21 #include "CAudioIODef.h"
22
23 #define AUDIO_IO_DEBUG
24
25 using namespace std;
26 using namespace tizen_media_audio;
27
28
29 /**
30  * class CAudioIO
31  */
32 CAudioIO::CAudioIO() :
33     mpAudioSessionHandler(NULL),
34     mpPulseAudioClient(NULL),
35     __mMutex(PTHREAD_MUTEX_INITIALIZER),
36     __mCond(PTHREAD_COND_INITIALIZER),
37     __mIsInit(false),
38     __mForceIgnore(false) {
39     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
40     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
41     mByPolicy = false;
42 }
43
44 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
45     mpAudioSessionHandler(NULL),
46     mpPulseAudioClient(NULL),
47     __mMutex(PTHREAD_MUTEX_INITIALIZER),
48     __mCond(PTHREAD_COND_INITIALIZER),
49     __mIsInit(false),
50     __mForceIgnore(false) {
51     mAudioInfo = audioInfo;
52     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
53     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
54     mByPolicy = false;
55 }
56
57 CAudioIO::~CAudioIO() {
58 }
59
60 void CAudioIO::setInit(bool flag) {
61     __mIsInit = flag;
62 }
63
64 bool CAudioIO::isInit() {
65     return __mIsInit;
66 }
67
68 bool CAudioIO::IsReady() {
69     return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
70 }
71
72 void CAudioIO::internalLock() throw(CAudioError) {
73     if (__mIsInit == false) {
74         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
75     }
76
77     if (pthread_mutex_lock(&__mMutex) != 0) {
78         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
79     }
80 #ifdef _AUDIO_IO_DEBUG_TIMING_
81     AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
82 #endif
83 }
84
85 void CAudioIO::internalUnlock() throw(CAudioError) {
86     if (__mIsInit == false) {
87         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
88     }
89
90     if (pthread_mutex_unlock(&__mMutex) != 0) {
91         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
92     }
93 #ifdef _AUDIO_IO_DEBUG_TIMING_
94     AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
95 #endif
96 }
97
98 void CAudioIO::internalWait() throw(CAudioError) {
99     if (__mIsInit == false) {
100         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
101     }
102
103 #ifdef _AUDIO_IO_DEBUG_TIMING_
104     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
105 #endif
106
107     pthread_cond_wait(&__mCond, &__mMutex);
108 }
109
110 void CAudioIO::internalSignal() throw(CAudioError) {
111     if (__mIsInit == false) {
112         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
113     }
114
115 #ifdef _AUDIO_IO_DEBUG_TIMING_
116     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
117 #endif
118
119     pthread_cond_signal(&__mCond);
120 }
121
122 bool CAudioIO::isForceIgnore() {
123     return __mForceIgnore;
124 }
125
126 void CAudioIO::initialize() throw(CAudioError) {
127     if (__mIsInit == true) {
128         return;
129     }
130
131     AUDIO_IO_LOGD("initialize");
132
133     int ret = pthread_mutex_init(&__mMutex, NULL);
134     if (ret != 0) {
135         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
136     }
137
138     ret = pthread_cond_init(&__mCond, NULL);
139     if (ret != 0) {
140         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
141     }
142
143     __mIsInit = true;
144 }
145
146 void CAudioIO::finalize() {
147     if (__mIsInit == false) {
148         return;
149     }
150
151     AUDIO_IO_LOGD("finalize");
152
153     int ret = pthread_mutex_destroy(&__mMutex);
154     if (ret != 0) {
155         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
156     }
157
158     ret = pthread_cond_destroy(&__mCond);
159     if (ret != 0) {
160         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
161     }
162
163     __mIsInit = false;
164 }
165
166 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
167     assert(__mIsInit == true);
168     assert(pClient != NULL);
169     assert(length > 0);
170
171 #ifdef _AUDIO_IO_DEBUG_TIMING_
172     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
173 #endif
174
175     if (mStreamCallback.onStream != NULL) {
176         mStreamCallback.onStream(length, mStreamCallback.mUserData);
177     }
178 }
179
180 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
181     assert(__mIsInit == true);
182     assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
183
184     mStatePrev = mState;
185     mState     = state;
186     mByPolicy  = byPolicy;
187
188     if (mState == mStatePrev)
189         return;
190
191     AUDIO_IO_LOGD("current(%d), previous(%d), by_policy(%d)", mState, mStatePrev, mByPolicy);
192
193     if (mStateChangedCallback.onStateChanged != NULL) {
194         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
195     }
196 }
197
198 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
199     onStateChanged(state, false);
200 }
201
202 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) {
203     assert(pHandler);
204
205     int session_option = pHandler->getOptions();
206
207     if (id == -1) {
208         ///////////////////////////////////////
209         // Triggered by 'focus watch callback'
210         ///////////////////////////////////////
211
212         if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
213             AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
214             return;
215         }
216
217         if (state == FOCUS_IS_RELEASED) {
218             // Focus handle(id) of the other application was released, do resume if possible
219             internalLock();
220
221             mpPulseAudioClient->cork(false);
222             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
223
224             internalUnlock();
225
226             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
227             state = FOCUS_IS_ACQUIRED;
228         } else if (state == FOCUS_IS_ACQUIRED) {
229             // Focus handle(id) of the other application was acquired, do pause if possible
230             internalLock();
231
232             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
233                 if (mpPulseAudioClient->drain() == false) {
234                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
235                 }
236             }
237
238             mpPulseAudioClient->cork(true);
239             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
240
241             internalUnlock();
242
243             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
244             state = FOCUS_IS_RELEASED;
245         }
246     } else {
247         ///////////////////////////////////////
248         // Triggered by 'focus callback'
249         ///////////////////////////////////////
250
251         if (pHandler->getId() != id) {
252             AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
253         }
254
255         if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
256             AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
257             return;
258         }
259
260         if (state == FOCUS_IS_RELEASED) {
261             // Focus handle(id) was released, do pause here
262             internalLock();
263
264             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
265                 if (mpPulseAudioClient->drain() == false) {
266                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
267                 }
268             }
269
270             mpPulseAudioClient->cork(true);
271             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
272
273             internalUnlock();
274         } else if (state == FOCUS_IS_ACQUIRED) {
275             // Focus handle(id) was acquired again,
276             // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
277             // do resume here and call interrupt completed callback to application.
278             internalLock();
279
280             mpPulseAudioClient->cork(false);
281             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
282
283             internalUnlock();
284         }
285     }
286
287     if (mInterruptCallback.onInterrupt != NULL) {
288         IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
289         e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
290         mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
291     }
292 }
293
294 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
295     assert(pHandler);
296
297     if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
298         if (value == 1 && pHandler->getSubscribeId() >= 0) {
299             // Unregister focus watch callback & disable session handler
300             pHandler->disableSessionHandler();
301             AUDIO_IO_LOGD("Session handler disabled by signal");
302         } else if (value == 0) {
303             // Currently do nothing...
304         }
305     }
306 }
307
308 void CAudioIO::prepare() throw(CAudioError) {
309     if (__mIsInit == false) {
310         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
311     }
312
313     try {
314         AUDIO_IO_LOGD("prepare");
315         /* Do nothing */
316     } catch (CAudioError e) {
317         throw e;
318     }
319 }
320
321 void CAudioIO::unprepare() throw(CAudioError) {
322     if (__mIsInit == false) {
323         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
324     }
325
326     try {
327         AUDIO_IO_LOGD("unprepare");
328         /* Do nothing */
329     } catch (CAudioError e) {
330         throw e;
331     }
332 }
333
334 void CAudioIO::pause() throw(CAudioError) {
335     if (__mIsInit == false || IsReady() == false) {
336         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
337     }
338
339     try {
340         internalLock();
341         AUDIO_IO_LOGD("pause");
342         mpPulseAudioClient->cork(true);
343         internalUnlock();
344     } catch (CAudioError e) {
345         internalUnlock();
346         throw e;
347     }
348 }
349
350 void CAudioIO::resume() throw(CAudioError) {
351     if (__mIsInit == false || IsReady() == false) {
352         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
353     }
354
355     try {
356         internalLock();
357         AUDIO_IO_LOGD("resume");
358         mpPulseAudioClient->cork(false);
359         internalUnlock();
360     } catch (CAudioError e) {
361         internalUnlock();
362         throw e;
363     }
364 }
365
366 void CAudioIO::drain() throw(CAudioError) {
367     if (__mIsInit == false || IsReady() == false) {
368         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
369     }
370
371     try {
372         internalLock();
373         AUDIO_IO_LOGD("drain");
374         mpPulseAudioClient->drain();
375         internalUnlock();
376     } catch (CAudioError e) {
377         internalUnlock();
378         throw e;
379     }
380 }
381
382 void CAudioIO::flush() throw(CAudioError) {
383     if (__mIsInit == false || IsReady() == false) {
384         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
385     }
386
387     try {
388         internalLock();
389         AUDIO_IO_LOGD("flush");
390         mpPulseAudioClient->flush();
391         internalUnlock();
392     } catch (CAudioError e) {
393         internalUnlock();
394         throw e;
395     }
396 }
397
398 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
399     if (__mIsInit == false) {
400         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
401     }
402
403     return mAudioInfo;
404 }
405
406 void CAudioIO::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
407     if (__mIsInit == false) {
408         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
409     }
410
411     mStreamCallback = callback;
412 }
413
414 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
415     if (__mIsInit == false) {
416         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
417     }
418
419     return mStreamCallback;
420 }
421
422 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw(CAudioError) {
423     if (__mIsInit == false) {
424         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
425     }
426
427     mStateChangedCallback = callback;
428 }
429
430 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
431     if (__mIsInit == false) {
432         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
433     }
434
435     return mStateChangedCallback;
436 }
437
438 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw(CAudioError) {
439     if (__mIsInit == false) {
440         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
441     }
442
443     mInterruptCallback = callback;
444 }
445
446 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
447     if (__mIsInit == false) {
448         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
449     }
450
451     return mInterruptCallback;
452 }
453
454
455 void CAudioIO::ignoreSession() throw(CAudioError) {
456     if (__mIsInit == false) {
457         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
458     }
459
460     try {
461         internalLock();
462
463         if (mpPulseAudioClient != NULL && mpPulseAudioClient->isCorked() == false) {
464             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
465         }
466
467         bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
468         if (isSkip == false && mpAudioSessionHandler->getId() >= 0) {
469             mpAudioSessionHandler->unregisterSound();
470             __mForceIgnore = true;
471         }
472
473         internalUnlock();
474     } catch (CAudioError e) {
475         internalUnlock();
476         throw e;
477     }
478 }