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