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