Exclude more coverage check which can't be covered by this module
[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 <new>
19
20 #include <pulse/pulseaudio.h>
21 #include "CAudioIODef.h"
22 #include <sched.h>
23
24 #define RECORDER_PRIVILEGE "http://tizen.org/privilege/recorder"
25 #define CLIENT_NAME "AUDIO_IO_PA_CLIENT"
26
27 using namespace std;
28 using namespace tizen_media_audio;
29
30 struct PrivilegeData {
31     bool isPrivilegeAllowed;
32     pa_threaded_mainloop *paMainloop;
33 };
34
35 /**
36  * class CAudioInput inherited by CAudioIO
37  */
38 CAudioInput::CAudioInput(CAudioInfo& info) :
39     CAudioIO(info),
40     __mIsUsedSyncRead(true),
41     __mIsInit(false) {
42     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN;
43 }
44
45 //LCOV_EXCL_START
46 CAudioInput::CAudioInput(
47         unsigned int            sampleRate,
48         CAudioInfo::EChannel    channel,
49         CAudioInfo::ESampleType type,
50         CAudioInfo::EAudioType  audioType) :
51     __mIsUsedSyncRead(true),
52     __mIsInit(false) {
53     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN;
54     mAudioInfo = CAudioInfo(sampleRate, channel, type, audioType, -1);
55 }
56 //LCOV_EXCL_STOP
57
58 CAudioInput::~CAudioInput() {
59 }
60
61 void CAudioInput::onStream(CPulseAudioClient* pClient, size_t length) {
62     assert(pClient);
63
64     /*
65      * Does not call CAudioIO::onStream() for synchronization
66      * if a user is using read()
67      */
68     if (__mIsUsedSyncRead == true) {
69 #ifdef _AUDIO_IO_DEBUG_TIMING_
70         AUDIO_IO_LOGD("Sync Read Mode! - pClient:[%p], length:[%d]", pClient, length);
71 #endif
72         return;
73     }
74
75     /*
76      * Accrues callback function
77      */
78 #ifdef _AUDIO_IO_DEBUG_TIMING_
79     AUDIO_IO_LOGD("pClient:[%p], length:[%d]", pClient, length);
80 #endif
81     CAudioIO::onStream(pClient, length);
82 }
83
84 void CAudioInput::__setInit(bool flag) {
85     __mIsInit = flag;
86 }
87
88 bool CAudioInput::__IsInit() {
89     return (CAudioIO::isInit() == true && __mIsInit == true);
90 }
91
92 bool CAudioInput::__IsReady() {
93     return CAudioIO::IsReady();
94 }
95
96 static void __contextStateChangeCb(pa_context* c, void* user_data) {
97     pa_threaded_mainloop *paMainloop = static_cast<pa_threaded_mainloop*>(user_data);
98     assert(paMainloop);
99     assert(c);
100
101     switch (pa_context_get_state(c)) {
102     case PA_CONTEXT_READY:
103         AUDIO_IO_LOGD("The context is ready");
104         pa_threaded_mainloop_signal(paMainloop, 0);
105         break;
106
107     case PA_CONTEXT_FAILED:
108     case PA_CONTEXT_TERMINATED:
109         AUDIO_IO_LOGD("The context is lost");
110         pa_threaded_mainloop_signal(paMainloop, 0);
111         break;
112
113     case PA_CONTEXT_UNCONNECTED:
114     case PA_CONTEXT_CONNECTING:
115     case PA_CONTEXT_AUTHORIZING:
116     case PA_CONTEXT_SETTING_NAME:
117         break;
118     }
119 }
120
121 static void __checkPrivilegeCb(pa_context *c, int success, void *user_data) {
122     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
123     assert(c);
124     assert(user_data);
125
126     PrivilegeData *prData = static_cast<PrivilegeData*>(user_data);
127     prData->isPrivilegeAllowed = success ? true : false;
128
129     pa_threaded_mainloop_signal(prData->paMainloop, 0);
130 }
131
132 static bool __IsPrivilegeAllowed() {
133     pa_operation *o;
134     pa_context *c;
135     int err = 0;
136     PrivilegeData prData;
137
138     prData.paMainloop = pa_threaded_mainloop_new();
139     if (prData.paMainloop == NULL)
140         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()"); //LCOV_EXCL_LINE
141
142     c = pa_context_new(pa_threaded_mainloop_get_api(prData.paMainloop), CLIENT_NAME);
143     if (c == NULL)
144         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()"); //LCOV_EXCL_LINE
145
146     pa_context_set_state_callback(c, __contextStateChangeCb, prData.paMainloop);
147
148     if (pa_context_connect(c, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
149         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()"); //LCOV_EXCL_LINE
150
151     pa_threaded_mainloop_lock(prData.paMainloop);
152
153     if (pa_threaded_mainloop_start(prData.paMainloop) < 0) {
154         pa_threaded_mainloop_unlock(prData.paMainloop);
155         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()"); //LCOV_EXCL_LINE
156     }
157
158     while (true) {
159         pa_context_state_t state;
160         state = pa_context_get_state(c);
161
162         if (state == PA_CONTEXT_READY)
163             break;
164
165         if (!PA_CONTEXT_IS_GOOD(state)) {
166 //LCOV_EXCL_START
167             err = pa_context_errno(c);
168             pa_threaded_mainloop_unlock(prData.paMainloop);
169             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
170                                    "pa_context's state is not good : err[%d]", err);
171 //LCOV_EXCL_STOP
172         }
173
174         /* Wait until the context is ready */
175         pa_threaded_mainloop_wait(prData.paMainloop);
176     }
177
178     o = pa_context_check_privilege(c, RECORDER_PRIVILEGE, __checkPrivilegeCb, &prData);
179     if (!o) {
180 //LCOV_EXCL_START
181         pa_threaded_mainloop_unlock(prData.paMainloop);
182         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed to pa_context_check_privilege()");
183 //LCOV_EXCL_STOP
184     }
185     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
186         pa_threaded_mainloop_wait(prData.paMainloop);
187     pa_operation_unref(o);
188
189     pa_threaded_mainloop_unlock(prData.paMainloop);
190     pa_threaded_mainloop_stop(prData.paMainloop);
191     pa_context_disconnect(c);
192     pa_context_unref(c);
193     pa_threaded_mainloop_free(prData.paMainloop);
194
195     return prData.isPrivilegeAllowed;
196 }
197
198 void CAudioInput::initialize() {
199     if (__IsInit() == true)
200         return;
201
202     if (__IsPrivilegeAllowed() == false)
203         THROW_ERROR_MSG(CAudioError::EError::ERROR_PERMISSION_DENIED, "No privilege for record");
204
205     try {
206         CAudioIO::initialize();
207        __setInit(true);
208     } catch (CAudioError& e) {
209         finalize();
210         throw;
211     }
212
213     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
214 }
215
216 void CAudioInput::finalize() {
217     if (__IsInit() == false) {
218         AUDIO_IO_LOGD("Did not initialize");
219         return;
220     }
221
222     CAudioIO::finalize();
223
224     __setInit(false);
225 }
226
227 void CAudioInput::prepare() {
228     if (__IsInit() == false)
229         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
230
231     if (__IsReady() == true) {
232         AUDIO_IO_LOGD("Already prepared CAudioInput");
233         CAudioIO::prepare();
234         return;
235     }
236
237     /* Check invalid AudioType */
238     CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
239     if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA ||
240         audioType >= CAudioInfo::EAudioType::AUDIO_OUT_TYPE_MEDIA)
241         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
242                                "The audioType is invalid [type:%d]", static_cast<int>(audioType));
243
244     try {
245         /* Init StreamSpec */
246         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_DEFAULT");
247         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_DEFAULT;
248         CPulseStreamSpec spec(streamSpec, mAudioInfo);
249
250         internalLock();
251         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
252         mpPulseAudioClient->initialize();
253 #ifndef DISABLE_MOBILE_BACK_COMP
254         /* Uncork stream which is created with CORKED flag */
255         mpPulseAudioClient->cork(false);
256 #endif
257         internalUnlock();
258
259         CAudioIO::prepare();
260     } catch (CAudioError& e) {
261         SAFE_FINALIZE(mpPulseAudioClient);
262         SAFE_DELETE(mpPulseAudioClient);
263         internalUnlock();
264         throw;
265     } catch (const std::bad_alloc&) {
266 //LCOV_EXCL_START
267         internalUnlock();
268         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
269 //LCOV_EXCL_STOP
270     }
271 }
272
273 void CAudioInput::unprepare() {
274     if (__IsInit() == false)
275         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
276                         "Did not initialize CAudioInput");
277
278     if (__IsReady() == false) {
279         AUDIO_IO_LOGD("Already unprepared");
280         return;
281     }
282
283     CAudioIO::unprepare();
284
285     try {
286         internalLock();
287         if (mpPulseAudioClient && mpPulseAudioClient->isInThread())
288             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread");
289         SAFE_FINALIZE(mpPulseAudioClient);
290         SAFE_DELETE(mpPulseAudioClient);
291         internalUnlock();
292     } catch (CAudioError& e) {
293         internalUnlock();
294         throw;
295     }
296
297     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
298 }
299
300 void CAudioInput::pause() {
301     if (__IsInit() == false || __IsReady() == false)
302         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
303                         "Did not initialize or prepare CAudioInput");
304
305     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
306         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
307                         "Can't pause if not in Running state");
308
309     if (mpPulseAudioClient->isInThread() == true)
310         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread");
311
312     CAudioIO::pause();
313     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
314 }
315
316 void CAudioInput::resume() {
317     if (__IsInit() == false || __IsReady() == false)
318         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
319                         "Did not initialize or prepare CAudioInput");
320
321     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)
322         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
323                         "Can't resume if not in Paused state");
324
325     if (mpPulseAudioClient->isInThread() == true)
326         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
327
328     CAudioIO::resume();
329     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
330 }
331
332 void CAudioInput::drain() {
333     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
334 }
335
336 void CAudioInput::flush() {
337     if (__IsInit() == false || __IsReady() == false)
338         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
339                         "Did not initialize or prepare CAudioInput");
340
341     CAudioIO::flush();
342 }
343
344 int CAudioInput::getBufferSize() {
345     if (__IsInit() == false)
346         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
347
348     /* FIXME : return calculated size here to satisfy backward compatibility */
349     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
350 }
351
352 void CAudioInput::setStreamCallback(SStreamCallback callback) {
353     if (__IsInit() == false)
354         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
355
356     if (callback.onStream == NULL)
357         __mIsUsedSyncRead = true;
358     else
359         __mIsUsedSyncRead = false;
360     AUDIO_IO_LOGD("__mIsUsedSyncRead = %d", __mIsUsedSyncRead);
361
362     CAudioIO::setStreamCallback(callback);
363 }
364
365 size_t CAudioInput::read(void* buffer, size_t length) {
366     if (__IsInit() == false || __IsReady() == false)
367         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
368                         "Did not initialize or prepare CAudioInput");
369
370     if (buffer == NULL)
371         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
372                                "Parameters are NULL buffer:%p", buffer);
373
374     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
375         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
376                         "Can't read if not in Running state");
377
378     /* Checks synchronous flag */
379     if (__mIsUsedSyncRead == false)
380         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
381                         "Invalid operation of read() if receive stream callback");
382
383     int ret = 0;
384
385     try {
386         internalLock();
387
388         // If another thread did call unprepare, do not read
389         if (mpPulseAudioClient == NULL)
390             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
391                             "Did not initialize CPulseAudioClient");
392
393         // Block until read done
394         ret = mpPulseAudioClient->read(buffer, length);
395         internalUnlock();
396
397         sched_yield();
398     } catch (CAudioError& e) {
399         internalUnlock();
400         throw;
401     }
402
403     return ret;
404 }
405
406 int CAudioInput::peek(const void** buffer, size_t* length) {
407     if (__IsInit() == false || __IsReady() == false)
408         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
409                         "Did not initialize or prepare CAudioInput");
410
411     if (buffer == NULL || length == NULL)
412         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
413                                "Parameters are NULL buffer:%p, length:%p", buffer, length);
414
415     /* Checks synchronous flag */
416     if (__mIsUsedSyncRead == true)
417         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
418                         "Invalid operation of peek() if does not receive a stream callback");
419
420     return mpPulseAudioClient->peek(buffer, length);
421 }
422
423 int CAudioInput::drop() {
424     if (__IsInit() == false || __IsReady() == false)
425         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
426                         "Did not initialize or prepare CAudioInput");
427
428     /* Checks synchronous flag */
429     if (__mIsUsedSyncRead == true)
430         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
431                         "Invalid operation of drop() if does not receive a stream callback");
432
433     return mpPulseAudioClient->drop();
434 }