Fix memory leak : valgrind
[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, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
78     assert(pHandler);
79     AUDIO_IO_LOGD("[pHandler:0x%x], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s]", pHandler, focus_type, state, reason_for_change, additional_info);
80     CAudioIO::onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
81 }
82
83 void CAudioInput::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
84     assert(pHandler);
85     AUDIO_IO_LOGD("[pHandler:0x%x], [signal:%d], [value:%d]", pHandler, signal, value);
86     CAudioIO::onSignal(pHandler, signal, value);
87 }
88
89 void CAudioInput::__setInit(bool flag) {
90     __mIsInit = flag;
91 }
92
93 bool CAudioInput::__IsInit() {
94     return (CAudioIO::isInit() == true && __mIsInit == true);
95 }
96
97 bool CAudioInput::__IsReady() {
98     return CAudioIO::IsReady();
99 }
100
101 static void __contextStateChangeCb(pa_context* c, void* user_data) {
102     pa_threaded_mainloop *paMainloop = static_cast<pa_threaded_mainloop*>(user_data);
103     assert(paMainloop);
104     assert(c);
105
106     switch (pa_context_get_state(c)) {
107     case PA_CONTEXT_READY:
108         AUDIO_IO_LOGD("The context is ready");
109         pa_threaded_mainloop_signal(paMainloop, 0);
110         break;
111
112     case PA_CONTEXT_FAILED:
113     case PA_CONTEXT_TERMINATED:
114         AUDIO_IO_LOGD("The context is lost");
115         pa_threaded_mainloop_signal(paMainloop, 0);
116         break;
117
118     case PA_CONTEXT_UNCONNECTED:
119     case PA_CONTEXT_CONNECTING:
120     case PA_CONTEXT_AUTHORIZING:
121     case PA_CONTEXT_SETTING_NAME:
122         break;
123     }
124 }
125
126 static void __checkPrivilegeCb(pa_context *c, int success, void *user_data) {
127     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
128     assert(c);
129     assert(user_data);
130
131     PrivilegeData *prData = static_cast<PrivilegeData*>(user_data);
132     prData->isPrivilegeAllowed = success ? true : false;
133
134     pa_threaded_mainloop_signal(prData->paMainloop, 0);
135 }
136
137 static bool __IsPrivilegeAllowed() {
138     pa_operation *o;
139     pa_context *c;
140     int err = 0;
141     PrivilegeData prData;
142
143     prData.paMainloop = pa_threaded_mainloop_new();
144     if (prData.paMainloop == NULL)
145         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
146     c = pa_context_new(pa_threaded_mainloop_get_api(prData.paMainloop), CLIENT_NAME);
147     if (c == NULL)
148         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
149
150     pa_context_set_state_callback(c, __contextStateChangeCb, prData.paMainloop);
151
152     if (pa_context_connect(c, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
153         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
154
155     pa_threaded_mainloop_lock(prData.paMainloop);
156
157     if (pa_threaded_mainloop_start(prData.paMainloop) < 0) {
158         pa_threaded_mainloop_unlock(prData.paMainloop);
159         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
160     }
161
162     while (true) {
163         pa_context_state_t state;
164         state = pa_context_get_state(c);
165
166         if (state == PA_CONTEXT_READY)
167             break;
168
169         if (!PA_CONTEXT_IS_GOOD(state)) {
170             err = pa_context_errno(c);
171             pa_threaded_mainloop_unlock(prData.paMainloop);
172             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err);
173         }
174
175         /* Wait until the context is ready */
176         pa_threaded_mainloop_wait(prData.paMainloop);
177     }
178
179     o = pa_context_check_privilege(c, RECORDER_PRIVILEGE, __checkPrivilegeCb, &prData);
180     if (!o) {
181         pa_threaded_mainloop_unlock(prData.paMainloop);
182         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed to pa_context_check_privilege()");
183     }
184     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
185         pa_threaded_mainloop_wait(prData.paMainloop);
186     pa_operation_unref(o);
187
188     pa_threaded_mainloop_unlock(prData.paMainloop);
189     pa_threaded_mainloop_stop(prData.paMainloop);
190     pa_context_disconnect(c);
191     pa_context_unref(c);
192     pa_threaded_mainloop_free(prData.paMainloop);
193
194     return prData.isPrivilegeAllowed;
195 }
196
197 void CAudioInput::initialize() throw(CAudioError) {
198     if (__IsInit() == true) {
199         return;
200     }
201
202     try {
203         CAudioIO::initialize();
204         if (__IsPrivilegeAllowed() == false) {
205             THROW_ERROR_MSG(CAudioError::EError::ERROR_PERMISSION_DENIED, "No privilege for record");
206         }
207
208         // Create ASM Handler
209         mpAudioSessionHandler = new CAudioSessionHandler(CAudioSessionHandler::EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE, mAudioInfo, this);
210         if (mpAudioSessionHandler == NULL) {
211             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CAudioSessionHandler object");
212         }
213
214         // Initialize ASM Handler
215         mpAudioSessionHandler->initialize();
216
217         __setInit(true);
218         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
219     } catch (CAudioError err) {
220         finalize();
221         throw err;
222     }
223 }
224
225 void CAudioInput::finalize() {
226     if (__IsInit() == false) {
227         AUDIO_IO_LOGD("Did not initialize");
228         return;
229     }
230
231     SAFE_FINALIZE(mpAudioSessionHandler);
232     SAFE_DELETE(mpAudioSessionHandler);
233
234     CAudioIO::finalize();
235
236     __setInit(false);
237 }
238
239 void CAudioInput::prepare() throw(CAudioError) {
240     if (__IsInit() == false) {
241         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
242     }
243
244     if (__IsReady() == true) {
245         AUDIO_IO_LOGD("Already prepared CAudioInput");
246         return;
247     }
248
249     try {
250         internalLock();
251
252         // Check to invalid AudioType
253         CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
254         if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA || audioType > CAudioInfo::EAudioType::AUDIO_IN_TYPE_LOOPBACK) {
255             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The audioType is invalid [type:%d]", static_cast<int>(audioType));
256         }
257
258         if (mpAudioSessionHandler->getId() < 0) {  // Did not registerSound()
259             // Check session to skip registration
260             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
261                 // Register ASM Listener
262                 AUDIO_IO_LOGD("Register ASM Listener");
263                 mpAudioSessionHandler->registerSound();
264             }
265         }
266
267         // Init StreamSpec
268         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_DEFAULT");
269         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_DEFAULT;
270         CPulseStreamSpec spec(streamSpec, mAudioInfo);
271
272         // Create PulseAudio Handler
273         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
274         if (mpPulseAudioClient == NULL) {
275             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
276         }
277
278         // Initialize PulseAudio Handler
279         mpPulseAudioClient->initialize();
280
281         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
282             /* Updates ASM to PLAYING */
283             mpAudioSessionHandler->updatePlaying();
284         }
285
286         internalUnlock();
287
288         // Do Prepare
289         CAudioIO::prepare();
290     } catch (CAudioError e) {
291         internalUnlock();
292         throw e;
293     }
294 }
295
296 void CAudioInput::unprepare() throw(CAudioError) {
297     if (__IsInit() == false) {
298         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
299     }
300
301     if (__IsReady() == false) {
302         AUDIO_IO_LOGD("Already unprepared");
303         return;
304     }
305
306     try {
307         // Do unprepare
308         CAudioIO::unprepare();
309
310         internalLock();
311
312         SAFE_FINALIZE(mpPulseAudioClient);
313         SAFE_DELETE(mpPulseAudioClient);
314
315         if (mpAudioSessionHandler->getId() >= 0) {
316             /* Updates ASM to STOP */
317             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
318                 mpAudioSessionHandler->updateStop();
319             }
320
321             bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
322             if (isSkip == false) {
323                 mpAudioSessionHandler->unregisterSound();
324             }
325         }
326
327         internalUnlock();
328
329         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
330     } catch (CAudioError e) {
331         internalUnlock();
332         throw e;
333     }
334 }
335
336 void CAudioInput::pause() throw(CAudioError) {
337     if (__IsInit() == false || __IsReady() == false) {
338         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "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->isSkipSessionEvent() == 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, "Did not initialize or prepare CAudioInput");
372     }
373
374     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED) {
375         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
376                         "Can't resume if not in Paused state");
377     }
378
379     if (mpPulseAudioClient->isInThread() == true) {
380         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
381     }
382
383     try {
384         internalLock();
385
386         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
387             /* Updates ASM to PLAYING */
388             mpAudioSessionHandler->updatePlaying();
389         }
390
391         internalUnlock();
392
393         CAudioIO::resume();
394
395         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
396     } catch (CAudioError e) {
397         internalUnlock();
398         throw e;
399     }
400 }
401
402 void CAudioInput::drain() throw(CAudioError) {
403     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
404 }
405
406 void CAudioInput::flush() throw(CAudioError) {
407     if (__IsInit() == false || __IsReady() == false) {
408         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "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, "Did not initialize or prepare CAudioInput");
446     }
447
448     if (buffer == NULL) {
449         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p", buffer);
450     }
451     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
452         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
453                         "Can't read if not in Running state");
454     }
455     /* Checks synchronous flag */
456     if (__mIsUsedSyncRead == false) {
457         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of read() if receive stream callback");
458     }
459
460     int ret = 0;
461
462     try {
463         internalLock();
464
465         // If another thread did call unprepare, do not read
466         if (mpPulseAudioClient == NULL)
467             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
468
469         // Block until read done
470         ret = mpPulseAudioClient->read(buffer, length);
471
472         internalUnlock();
473     } catch (CAudioError e) {
474         internalUnlock();
475         throw e;
476     }
477
478     return ret;
479 }
480
481 int CAudioInput::peek(const void** buffer, size_t* length) throw(CAudioError) {
482     if (__IsInit() == false || __IsReady() == false) {
483         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
484     }
485
486     if (buffer == NULL || length == NULL) {
487         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p, length:%p", buffer, length);
488     }
489
490     /* Checks synchronous flag */
491     if (__mIsUsedSyncRead == true) {
492         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of peek() if does not receive a stream callback");
493     }
494
495     int ret = 0;
496
497     try {
498         ret = mpPulseAudioClient->peek(buffer, length);
499     } catch (CAudioError e) {
500         throw e;
501     }
502
503     return ret;
504 }
505
506 int CAudioInput::drop() throw(CAudioError) {
507     if (__IsInit() == false || __IsReady() == false) {
508         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
509     }
510
511     /* Checks synchronous flag */
512     if (__mIsUsedSyncRead == true) {
513         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of drop() if does not receive a stream callback");
514     }
515
516     int ret = 0;
517
518     try {
519         ret = mpPulseAudioClient->drop();
520     } catch (CAudioError e) {
521         throw e;
522     }
523
524     return ret;
525 }