Implementation of isSkipSession() in CAudioSessionHandler.c
[platform/core/api/audio-io.git] / src / cpp / CAudioInput.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 <pulse/pulseaudio.h>
19 #include "CAudioIODef.h"
20
21 #define RECORDER_PRIVILEGE "http://tizen.org/privilege/recorder"
22 #define CLIENT_NAME "AUDIO_IO_PA_CLIENT"
23
24 using namespace std;
25 using namespace tizen_media_audio;
26
27 struct PrivilegeData {
28     bool isPrivilegeAllowed;
29     pa_threaded_mainloop *paMainloop;
30 };
31
32 /**
33  * class CAudioInput inherited by CAudioIO
34  */
35 CAudioInput::CAudioInput(CAudioInfo& info) :
36     CAudioIO(info),
37     __mIsUsedSyncRead(true),
38     __mIsInit(false) {
39 }
40
41 CAudioInput::CAudioInput(
42         unsigned int            sampleRate,
43         CAudioInfo::EChannel    channel,
44         CAudioInfo::ESampleType type,
45         CAudioInfo::EAudioType  audioType) :
46     __mIsUsedSyncRead(true),
47     __mIsInit(false) {
48     mAudioInfo = CAudioInfo(sampleRate, channel, type, audioType, -1);
49 }
50
51 CAudioInput::~CAudioInput() {
52 }
53
54 void CAudioInput::onStream(CPulseAudioClient* pClient, size_t length) {
55     assert(pClient);
56
57     /*
58      * Does not call CAudioIO::onStream() for synchronization
59      * if a user is using read()
60      */
61     if (__mIsUsedSyncRead == true) {
62 #ifdef _AUDIO_IO_DEBUG_TIMING_
63         AUDIO_IO_LOGD("Sync Read Mode! - pClient:[%p], length:[%d]", pClient, length);
64 #endif
65         return;
66     }
67
68     /*
69      * Accrues callback function
70      */
71 #ifdef _AUDIO_IO_DEBUG_TIMING_
72     AUDIO_IO_LOGD("pClient:[%p], length:[%d]", pClient, length);
73 #endif
74     CAudioIO::onStream(pClient, length);
75 }
76
77 void CAudioInput::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type,
78                               mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
79     assert(pHandler);
80     AUDIO_IO_LOGD("[pHandler:0x%x], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s]",
81                    pHandler, focus_type, state, reason_for_change, additional_info);
82     CAudioIO::onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
83 }
84
85 void CAudioInput::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
86     assert(pHandler);
87     AUDIO_IO_LOGD("[pHandler:0x%x], [signal:%d], [value:%d]", pHandler, signal, value);
88     CAudioIO::onSignal(pHandler, signal, value);
89 }
90
91 void CAudioInput::__setInit(bool flag) {
92     __mIsInit = flag;
93 }
94
95 bool CAudioInput::__IsInit() {
96     return (CAudioIO::isInit() == true && __mIsInit == true);
97 }
98
99 bool CAudioInput::__IsReady() {
100     return CAudioIO::IsReady();
101 }
102
103 static void __contextStateChangeCb(pa_context* c, void* user_data) {
104     pa_threaded_mainloop *paMainloop = static_cast<pa_threaded_mainloop*>(user_data);
105     assert(paMainloop);
106     assert(c);
107
108     switch (pa_context_get_state(c)) {
109     case PA_CONTEXT_READY:
110         AUDIO_IO_LOGD("The context is ready");
111         pa_threaded_mainloop_signal(paMainloop, 0);
112         break;
113
114     case PA_CONTEXT_FAILED:
115     case PA_CONTEXT_TERMINATED:
116         AUDIO_IO_LOGD("The context is lost");
117         pa_threaded_mainloop_signal(paMainloop, 0);
118         break;
119
120     case PA_CONTEXT_UNCONNECTED:
121     case PA_CONTEXT_CONNECTING:
122     case PA_CONTEXT_AUTHORIZING:
123     case PA_CONTEXT_SETTING_NAME:
124         break;
125     }
126 }
127
128 static void __checkPrivilegeCb(pa_context *c, int success, void *user_data) {
129     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
130     assert(c);
131     assert(user_data);
132
133     PrivilegeData *prData = static_cast<PrivilegeData*>(user_data);
134     prData->isPrivilegeAllowed = success ? true : false;
135
136     pa_threaded_mainloop_signal(prData->paMainloop, 0);
137 }
138
139 static bool __IsPrivilegeAllowed() {
140     pa_operation *o;
141     pa_context *c;
142     int err = 0;
143     PrivilegeData prData;
144
145     prData.paMainloop = pa_threaded_mainloop_new();
146     if (prData.paMainloop == NULL)
147         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
148     c = pa_context_new(pa_threaded_mainloop_get_api(prData.paMainloop), CLIENT_NAME);
149     if (c == NULL)
150         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
151
152     pa_context_set_state_callback(c, __contextStateChangeCb, prData.paMainloop);
153
154     if (pa_context_connect(c, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
155         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
156
157     pa_threaded_mainloop_lock(prData.paMainloop);
158
159     if (pa_threaded_mainloop_start(prData.paMainloop) < 0) {
160         pa_threaded_mainloop_unlock(prData.paMainloop);
161         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
162     }
163
164     while (true) {
165         pa_context_state_t state;
166         state = pa_context_get_state(c);
167
168         if (state == PA_CONTEXT_READY)
169             break;
170
171         if (!PA_CONTEXT_IS_GOOD(state)) {
172             err = pa_context_errno(c);
173             pa_threaded_mainloop_unlock(prData.paMainloop);
174             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
175                                    "pa_context's state is not good : err[%d]", err);
176         }
177
178         /* Wait until the context is ready */
179         pa_threaded_mainloop_wait(prData.paMainloop);
180     }
181
182     o = pa_context_check_privilege(c, RECORDER_PRIVILEGE, __checkPrivilegeCb, &prData);
183     if (!o) {
184         pa_threaded_mainloop_unlock(prData.paMainloop);
185         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed to pa_context_check_privilege()");
186     }
187     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
188         pa_threaded_mainloop_wait(prData.paMainloop);
189     pa_operation_unref(o);
190
191     pa_threaded_mainloop_unlock(prData.paMainloop);
192     pa_threaded_mainloop_stop(prData.paMainloop);
193     pa_context_disconnect(c);
194     pa_context_unref(c);
195     pa_threaded_mainloop_free(prData.paMainloop);
196
197     return prData.isPrivilegeAllowed;
198 }
199
200 void CAudioInput::initialize() throw(CAudioError) {
201     if (__IsInit() == true) {
202         return;
203     }
204
205     try {
206         CAudioIO::initialize();
207         if (__IsPrivilegeAllowed() == false) {
208             THROW_ERROR_MSG(CAudioError::EError::ERROR_PERMISSION_DENIED, "No privilege for record");
209         }
210
211         // Create ASM Handler
212         mpAudioSessionHandler = new CAudioSessionHandler(CAudioSessionHandler::EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE, mAudioInfo, this);
213         if (mpAudioSessionHandler == NULL) {
214             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CAudioSessionHandler object");
215         }
216
217         // Initialize ASM Handler
218         mpAudioSessionHandler->initialize();
219
220         __setInit(true);
221         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
222     } catch (CAudioError err) {
223         finalize();
224         throw err;
225     }
226 }
227
228 void CAudioInput::finalize() {
229     if (__IsInit() == false) {
230         AUDIO_IO_LOGD("Did not initialize");
231         return;
232     }
233
234     SAFE_FINALIZE(mpAudioSessionHandler);
235     SAFE_DELETE(mpAudioSessionHandler);
236
237     CAudioIO::finalize();
238
239     __setInit(false);
240 }
241
242 void CAudioInput::prepare() throw(CAudioError) {
243     if (__IsInit() == false) {
244         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
245     }
246
247     if (__IsReady() == true) {
248         AUDIO_IO_LOGD("Already prepared CAudioInput");
249         return;
250     }
251
252     try {
253         internalLock();
254
255         // Check to invalid AudioType
256         CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
257         if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA || audioType > CAudioInfo::EAudioType::AUDIO_IN_TYPE_LOOPBACK) {
258             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
259                                    "The audioType is invalid [type:%d]", static_cast<int>(audioType));
260         }
261
262         if (mpAudioSessionHandler->getId() < 0) {  // Did not registerSound()
263             // Check session to skip registration
264             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false) {
265                 // Register ASM Listener
266                 AUDIO_IO_LOGD("Register ASM Listener");
267                 mpAudioSessionHandler->registerSound();
268             }
269         }
270
271         // Init StreamSpec
272         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_DEFAULT");
273         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_DEFAULT;
274         CPulseStreamSpec spec(streamSpec, mAudioInfo);
275
276         // Create PulseAudio Handler
277         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
278         if (mpPulseAudioClient == NULL) {
279             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY,
280                             "Failed to allocate CPulseAudioClient object");
281         }
282
283         // Initialize PulseAudio Handler
284         mpPulseAudioClient->initialize();
285
286         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
287             mpAudioSessionHandler->updatePlaying();
288
289         internalUnlock();
290
291         // Do Prepare
292         CAudioIO::prepare();
293     } catch (CAudioError e) {
294         internalUnlock();
295         throw e;
296     }
297 }
298
299 void CAudioInput::unprepare() throw(CAudioError) {
300     if (__IsInit() == false) {
301         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
302                         "Did not initialize CAudioInput");
303     }
304
305     if (__IsReady() == false) {
306         AUDIO_IO_LOGD("Already unprepared");
307         return;
308     }
309
310     try {
311         CAudioIO::unprepare();
312
313         internalLock();
314
315         SAFE_FINALIZE(mpPulseAudioClient);
316         SAFE_DELETE(mpPulseAudioClient);
317
318         if (mpAudioSessionHandler->getId() >= 0) {
319             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
320                 mpAudioSessionHandler->updateStop();
321
322             if (mpAudioSessionHandler->isSkipSession() ==  false)
323                 mpAudioSessionHandler->unregisterSound();
324         }
325
326         internalUnlock();
327
328         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
329     } catch (CAudioError e) {
330         internalUnlock();
331         throw e;
332     }
333 }
334
335 void CAudioInput::pause() throw(CAudioError) {
336     if (__IsInit() == false || __IsReady() == false) {
337         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
338                         "Did not initialize or prepare CAudioInput");
339     }
340
341     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
342         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
343                         "Can't pause if not in Running state");
344     }
345
346     if (mpPulseAudioClient->isInThread() == true) {
347         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread");
348     }
349
350     try {
351         CAudioIO::pause();
352
353         internalLock();
354
355         /* Updates ASM to STOP */
356         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false) {
357             mpAudioSessionHandler->updateStop();
358         }
359
360         internalUnlock();
361
362         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
363     } catch (CAudioError e) {
364         internalUnlock();
365         throw e;
366     }
367 }
368
369 void CAudioInput::resume() throw(CAudioError) {
370     if (__IsInit() == false || __IsReady() == false) {
371         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
372                         "Did not initialize or prepare CAudioInput");
373     }
374
375     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED) {
376         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
377                         "Can't resume if not in Paused state");
378     }
379
380     if (mpPulseAudioClient->isInThread() == true) {
381         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
382     }
383
384     try {
385         internalLock();
386
387         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSession() == false)
388             mpAudioSessionHandler->updatePlaying();
389
390         internalUnlock();
391
392         CAudioIO::resume();
393
394         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
395     } catch (CAudioError e) {
396         internalUnlock();
397         throw e;
398     }
399 }
400
401 void CAudioInput::drain() throw(CAudioError) {
402     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
403 }
404
405 void CAudioInput::flush() throw(CAudioError) {
406     if (__IsInit() == false || __IsReady() == false) {
407         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
408                         "Did not initialize or prepare CAudioInput");
409     }
410
411     try {
412         CAudioIO::flush();
413     } catch (CAudioError e) {
414         throw e;
415     }
416 }
417
418 int CAudioInput::getBufferSize() throw(CAudioError) {
419     if (__IsInit() == false) {
420         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
421     }
422
423     /* FIXME : return calculated size here to satisfy backward compatibility */
424     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
425 }
426
427 void CAudioInput::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
428     if (__IsInit() == false) {
429         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
430     }
431
432     if (callback.onStream == NULL) {
433         AUDIO_IO_LOGD("__mIsUsedSyncRead = true");
434         __mIsUsedSyncRead = true;
435     } else {
436         AUDIO_IO_LOGD("__mIsUsedSyncRead = false");
437         __mIsUsedSyncRead = false;
438     }
439
440     CAudioIO::setStreamCallback(callback);
441 }
442
443 size_t CAudioInput::read(void* buffer, size_t length) throw(CAudioError) {
444     if (__IsInit() == false || __IsReady() == false) {
445         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
446                         "Did not initialize or prepare CAudioInput");
447     }
448     if (buffer == NULL) {
449         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
450                                "Parameters are NULL buffer:%p", buffer);
451     }
452     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
453         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
454                         "Can't read if not in Running state");
455     }
456     /* Checks synchronous flag */
457     if (__mIsUsedSyncRead == false) {
458         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
459                         "Invalid operation of read() if receive stream callback");
460     }
461
462     int ret = 0;
463
464     try {
465         internalLock();
466
467         // If another thread did call unprepare, do not read
468         if (mpPulseAudioClient == NULL)
469             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
470                             "Did not initialize CPulseAudioClient");
471
472         // Block until read done
473         ret = mpPulseAudioClient->read(buffer, length);
474
475         internalUnlock();
476     } catch (CAudioError e) {
477         internalUnlock();
478         throw e;
479     }
480
481     return ret;
482 }
483
484 int CAudioInput::peek(const void** buffer, size_t* length) throw(CAudioError) {
485     if (__IsInit() == false || __IsReady() == false) {
486         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
487                         "Did not initialize or prepare CAudioInput");
488     }
489
490     if (buffer == NULL || length == NULL) {
491         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
492                                "Parameters are NULL buffer:%p, length:%p", buffer, length);
493     }
494
495     /* Checks synchronous flag */
496     if (__mIsUsedSyncRead == true) {
497         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
498                         "Invalid operation of peek() if does not receive a stream callback");
499     }
500
501     int ret = 0;
502
503     try {
504         ret = mpPulseAudioClient->peek(buffer, length);
505     } catch (CAudioError e) {
506         throw e;
507     }
508
509     return ret;
510 }
511
512 int CAudioInput::drop() throw(CAudioError) {
513     if (__IsInit() == false || __IsReady() == false) {
514         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
515                         "Did not initialize or prepare CAudioInput");
516     }
517
518     /* Checks synchronous flag */
519     if (__mIsUsedSyncRead == true) {
520         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
521                         "Invalid operation of drop() if does not receive a stream callback");
522     }
523
524     int ret = 0;
525
526     try {
527         ret = mpPulseAudioClient->drop();
528     } catch (CAudioError e) {
529         throw e;
530     }
531
532     return ret;
533 }