Merge branch 'tizen' into tizen_line_coverage
[platform/core/api/audio-io.git] / src / cpp / CPulseAudioClient.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 "CAudioIODef.h"
19 #include <unistd.h>
20 #include <inttypes.h>
21 #ifdef ENABLE_DPM
22 #include <dpm/restriction.h>
23 #endif
24
25 using namespace std;
26 using namespace tizen_media_audio;
27
28
29 /**
30  * class CPulseAudioClient
31  */
32 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
33 static const unsigned int drain_wait_interval = 10000;
34 static const unsigned int drain_wait_max_count = 30;
35
36 CPulseAudioClient::CPulseAudioClient(
37         EStreamDirection      direction,
38         CPulseStreamSpec&     spec,
39         IPulseStreamListener* listener) :
40     __mDirection(direction),
41     __mSpec(spec),
42     __mpListener(listener),
43     __mpMainloop(NULL),
44     __mpContext(NULL),
45     __mpStream(NULL),
46     __mpPropList(NULL),
47     __mIsInit(false),
48     __mIsOperationSuccess(false),
49     __mpSyncReadDataPtr(NULL),
50     __mSyncReadIndex(0),
51     __mSyncReadLength(0),
52     __mIsUsedSyncRead(false),
53     __mIsFirstStream(false),
54     __mIsDraining(false),
55     __mIsStarted(false) {
56 }
57
58 CPulseAudioClient::~CPulseAudioClient() {
59     finalize();
60 }
61
62 void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) {
63     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
64     assert(pClient);
65     assert(c);
66
67     switch (pa_context_get_state(c)) {
68     case PA_CONTEXT_READY:
69         AUDIO_IO_LOGD("The context is ready");
70         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
71         break;
72
73     case PA_CONTEXT_FAILED:
74     case PA_CONTEXT_TERMINATED:
75         AUDIO_IO_LOGD("The context is lost");
76         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
77         break;
78
79     case PA_CONTEXT_UNCONNECTED:
80     case PA_CONTEXT_CONNECTING:
81     case PA_CONTEXT_AUTHORIZING:
82     case PA_CONTEXT_SETTING_NAME:
83         break;
84     }
85 }
86
87 //LCOV_EXCL_START
88 void CPulseAudioClient::__successContextCb(pa_context* c, int success, void* user_data) {
89     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
90     assert(c);
91     assert(user_data);
92
93     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
94     pClient->__mIsOperationSuccess = static_cast<bool>(success);
95
96     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
97 }
98
99 static bool __is_microphone_restricted(void) {
100     int state = 1;
101 #ifdef ENABLE_DPM
102     device_policy_manager_h dpm_h = NULL;
103     int ret = 0;
104
105     if ((dpm_h = dpm_manager_create())) {
106         /* state: 0(disallowed), 1(allowed) */
107         if ((ret = dpm_restriction_get_microphone_state(dpm_h, &state)))
108             AUDIO_IO_LOGE("Failed to dpm_restriction_get_microphone_state(), ret(0x%x)", ret);
109         dpm_manager_destroy(dpm_h);
110         AUDIO_IO_LOGD("microphone restriction state: %d(1:allowed, 0:disallowed)", state);
111     } else {
112         AUDIO_IO_LOGE("Failed to dpm_manager_create()");
113     }
114 #endif
115     return (state ? false : true);
116 }
117 //LCOV_EXCL_STOP
118
119 void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) {
120     assert(s);
121     assert(user_data);
122
123     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
124
125     switch (pa_stream_get_state(s)) {
126     case PA_STREAM_READY:
127         AUDIO_IO_LOGD("The stream is ready");
128         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
129         if (pClient->__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
130             pClient->__mIsFirstStream = true;
131         pClient->__mIsInit = true;
132         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
133         break;
134
135     case PA_STREAM_FAILED:
136 //LCOV_EXCL_START
137         AUDIO_IO_LOGD("The stream is failed");
138         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE,
139                                               __is_microphone_restricted());
140         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
141         break;
142 //LCOV_EXCL_STOP
143
144     case PA_STREAM_TERMINATED:
145         AUDIO_IO_LOGD("The stream is terminated");
146         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
147         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
148         break;
149
150     case PA_STREAM_UNCONNECTED:
151     case PA_STREAM_CREATING:
152         break;
153     }
154 }
155
156 void CPulseAudioClient::__streamCaptureCb(pa_stream* s, size_t length, void* user_data) {
157     assert(s);
158     assert(user_data);
159
160     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
161     assert(pClient->__mpListener);
162     assert(pClient->__mpMainloop);
163
164     if (pClient->__mIsUsedSyncRead == true) {
165         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
166     }
167
168     pClient->__mpListener->onStream(pClient, length);
169 }
170
171 #ifndef DISABLE_MOBILE_BACK_COMP
172 static void __dummy_write(pa_stream* s, size_t length) {
173     char* dummy = new char[length];
174     memset(dummy, 0, length);
175     pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
176     delete [] dummy;
177 }
178 #endif
179
180 void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
181     assert(s);
182     assert(user_data);
183
184     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
185     assert(pClient->__mpListener);
186
187 #ifndef DISABLE_MOBILE_BACK_COMP
188     if (pClient->__mIsInit == false) {
189         AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create : Write dummy, length[%d]", length);
190         __dummy_write(s, length);
191         return;
192     }
193     if (pClient->isCorked()) {
194         AUDIO_IO_LOGD("Occurred this listener when an out stream is CORKED : Write dummy, length[%d]", length);
195         __dummy_write(s, length);
196         return;
197     }
198 #endif
199
200     pClient->__mpListener->onStream(pClient, length);
201
202 #ifndef DISABLE_MOBILE_BACK_COMP
203     /* If stream is not written in first callback during prepare,
204        then write dummy data to ensure the start */
205     if (pClient->__mSpec.getStreamLatency() == CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC &&
206         pClient->__mIsFirstStream) {
207         AUDIO_IO_LOGW("[async] Write dummy of length[%d] since not written in first callback during prepare", length);
208         __dummy_write(s, length);
209         pClient->__mIsFirstStream = false;
210     }
211 #endif
212 }
213
214 void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) {
215     assert(s);
216     assert(user_data);
217
218     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
219
220     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
221 }
222
223 void CPulseAudioClient::__streamStartedCb(pa_stream* s, void* user_data) {
224     assert(s);
225     assert(user_data);
226
227     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
228
229     AUDIO_IO_LOGD("stream %p started.", pClient);
230
231     pClient->__mIsStarted = true;
232 }
233
234 void CPulseAudioClient::__streamUnderflowCb(pa_stream* s, void* user_data) {
235     assert(s);
236     assert(user_data);
237
238     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
239
240     AUDIO_IO_LOGD("stream %p UnderFlow...", pClient);
241
242     pClient->__mIsStarted = false;
243 }
244
245 //LCOV_EXCL_START
246 void CPulseAudioClient::__streamEventCb(pa_stream* s, const char *name, pa_proplist *pl, void *user_data) {
247     assert(s);
248     assert(user_data);
249
250     AUDIO_IO_LOGE("NAME : %s, Prop : %p", name, pl);
251
252     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
253     if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) {
254         pa_operation_unref(pa_stream_cork(pClient->__mpStream, 1, NULL, NULL));
255     }
256 }
257 //LCOV_EXCL_STOP
258
259 void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
260     AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
261     assert(s);
262     assert(user_data);
263
264     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
265     pClient->__mIsOperationSuccess = static_cast<bool>(success);
266
267     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
268 }
269
270 //LCOV_EXCL_START
271 void CPulseAudioClient::__successDrainCbInThread(pa_stream* s, int success, void* user_data) {
272     AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
273     assert(s);
274     assert(user_data);
275
276     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
277     pClient->__mIsOperationSuccess = static_cast<bool>(success);
278     pClient->__mIsDraining = false;
279 }
280 //LCOV_EXCL_STOP
281
282 void CPulseAudioClient::__successDrainCb(pa_stream* s, int success, void* user_data) {
283     AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
284     assert(s);
285     assert(user_data);
286
287     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
288     pClient->__mIsOperationSuccess = static_cast<bool>(success);
289     pClient->__mIsDraining = false;
290
291     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
292 }
293
294 void CPulseAudioClient::initialize() {
295     if (__mIsInit == true) {
296         return;
297     }
298
299     int ret = 0;
300     int err = 0;
301
302     try {
303         // Allocates PA proplist
304         __mpPropList = pa_proplist_new();
305         if (__mpPropList == NULL) {
306             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()"); //LCOV_EXCL_LINE
307         }
308
309         // Adds values on proplist for delivery to PULSEAUDIO
310         char *streamType = NULL;
311         CAudioInfo::EAudioType audioType = __mSpec.getAudioInfo().getAudioType();
312         __mSpec.getAudioInfo().convertAudioType2StreamType(audioType, &streamType);
313         pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
314
315         int index = __mSpec.getAudioInfo().getAudioIndex();
316         if (index >= 0) {
317             pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
318         }
319
320         // Adds latency on proplist for delivery to PULSEAUDIO
321         AUDIO_IO_LOGD("LATENCY : %s[%d]", __mSpec.getStreamLatencyToString(), static_cast<int>(__mSpec.getStreamLatency()));
322         pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
323
324         // Allocates PA mainloop
325         __mpMainloop = pa_threaded_mainloop_new();
326         if (__mpMainloop == NULL) {
327             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()"); //LCOV_EXCL_LINE
328         }
329
330         // Allocates PA context
331         __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
332         if (__mpContext == NULL) {
333             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()"); //LCOV_EXCL_LINE
334         }
335
336         // Sets context state changed callback
337         pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
338
339         // Connects this client with PA server
340         if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
341             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()"); //LCOV_EXCL_LINE
342         }
343
344         // LOCK for synchronous connection
345         pa_threaded_mainloop_lock(__mpMainloop);
346
347         // Start mainloop
348         if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
349             pa_threaded_mainloop_unlock(__mpMainloop);
350             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()"); //LCOV_EXCL_LINE
351         }
352
353         // Connection process is asynchronously
354         // So, this function will be waited when occurred context state change event
355         // If I got a signal, do next processing
356         while (true) {
357             pa_context_state_t state;
358             state = pa_context_get_state(__mpContext);
359
360             if (state == PA_CONTEXT_READY) {
361                 break;
362             }
363
364             if (!PA_CONTEXT_IS_GOOD(state)) {
365 //LCOV_EXCL_START
366                 err = pa_context_errno(__mpContext);
367                 pa_threaded_mainloop_unlock(__mpMainloop);
368                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err);
369 //LCOV_EXCL_STOP
370             }
371
372             /* Wait until the context is ready */
373             pa_threaded_mainloop_wait(__mpMainloop);
374         }
375
376         // Allocates PA stream
377         pa_sample_spec ss   = __mSpec.getSampleSpec();
378         pa_channel_map map  = __mSpec.getChannelMap();
379
380         __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
381         if (__mpStream == NULL) {
382 //LCOV_EXCL_START
383             pa_threaded_mainloop_unlock(__mpMainloop);
384             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()");
385 //LCOV_EXCL_STOP
386         }
387
388         // Sets stream callbacks
389         pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
390         pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
391         pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
392         pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
393         pa_stream_set_event_callback(__mpStream, __streamEventCb, this);
394         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
395             pa_stream_set_started_callback(__mpStream, __streamStartedCb, this);
396             pa_stream_set_underflow_callback(__mpStream, __streamUnderflowCb, this);
397         }
398
399         // Connect stream with PA Server
400
401         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
402             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
403                     PA_STREAM_INTERPOLATE_TIMING |
404                     PA_STREAM_ADJUST_LATENCY     |
405 #ifndef DISABLE_MOBILE_BACK_COMP
406                     PA_STREAM_START_CORKED       |
407 #endif
408                     PA_STREAM_AUTO_TIMING_UPDATE);
409
410             ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
411         } else {
412             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
413                     PA_STREAM_INTERPOLATE_TIMING |
414                     PA_STREAM_ADJUST_LATENCY     |
415 #ifndef DISABLE_MOBILE_BACK_COMP
416                     PA_STREAM_START_CORKED       |
417 #endif
418                     PA_STREAM_AUTO_TIMING_UPDATE);
419
420             ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
421         }
422
423         if (ret != 0) {
424 //LCOV_EXCL_START
425             err = pa_context_errno(__mpContext);
426             pa_threaded_mainloop_unlock(__mpMainloop);
427             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() : err[%d]", err);
428 //LCOV_EXCL_STOP
429         }
430
431         while (true) {
432             pa_stream_state_t state;
433             state = pa_stream_get_state(__mpStream);
434
435             if (state == PA_STREAM_READY) {
436                 AUDIO_IO_LOGD("STREAM READY");
437                 break;
438             }
439
440             if (!PA_STREAM_IS_GOOD(state)) {
441                 err = pa_context_errno(__mpContext);
442                 pa_threaded_mainloop_unlock(__mpMainloop);
443                 if (err == PA_ERR_ACCESS) {
444                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION, "pa_stream's state is not good : err[%d]", err);
445                 } else {
446                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good : err[%d]", err); //LCOV_EXCL_LINE
447                 }
448             }
449
450             /* Wait until the stream is ready */
451             pa_threaded_mainloop_wait(__mpMainloop);
452         }
453
454         // End of synchronous
455         pa_threaded_mainloop_unlock(__mpMainloop);
456
457         // __mIsInit = true;  // Moved to __streamStateChangeCb()
458     } catch (CAudioError& e) {
459         finalize();
460         throw;
461     }
462 }
463
464 void CPulseAudioClient::finalize() {
465     AUDIO_IO_LOGD("");
466
467     if (__mpMainloop && isInThread() == false)
468         pa_threaded_mainloop_lock(__mpMainloop);
469
470     /* clear callbacks */
471     if (__mpStream) {
472         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
473             pa_stream_set_write_callback(__mpStream, NULL, NULL);
474         else
475             pa_stream_set_read_callback(__mpStream, NULL, NULL);
476         pa_stream_set_latency_update_callback(__mpStream, NULL, NULL);
477         pa_stream_set_event_callback(__mpStream, NULL, NULL);
478     }
479
480     if (__mpMainloop && isInThread() == false)
481         pa_threaded_mainloop_unlock(__mpMainloop);
482
483     /* Wait for drain complete if draining before finalize */
484     if (__mIsDraining) {
485         unsigned int drain_wait_count = 0;
486         while (__mIsDraining && drain_wait_count++ < drain_wait_max_count) {
487             usleep(drain_wait_interval);
488         }
489         AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]",
490                       drain_wait_count, drain_wait_interval);
491     }
492
493     if (__mpMainloop != NULL) {
494         pa_threaded_mainloop_stop(__mpMainloop);
495     }
496
497     if (__mpStream != NULL) {
498         pa_stream_disconnect(__mpStream);
499         pa_stream_unref(__mpStream);
500         __mpStream = NULL;
501     }
502
503     if (__mpContext != NULL) {
504         pa_context_disconnect(__mpContext);
505         pa_context_unref(__mpContext);
506         __mpContext = NULL;
507     }
508
509     if (__mpMainloop != NULL) {
510         pa_threaded_mainloop_free(__mpMainloop);
511         __mpMainloop = NULL;
512     }
513
514     if (__mpPropList != NULL) {
515         pa_proplist_free(__mpPropList);
516         __mpPropList = NULL;
517     }
518
519     __mIsInit = false;
520 }
521
522 int CPulseAudioClient::read(void* buffer, size_t length) {
523     if (__mIsInit == false) {
524         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
525     }
526
527     checkRunningState();
528
529     if (buffer == NULL) {
530         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p]", buffer);
531     }
532
533     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
534         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
535     }
536
537     size_t lengthIter = length;
538     int ret = 0;
539
540     __mIsUsedSyncRead = true;
541
542     try {
543         pa_threaded_mainloop_lock(__mpMainloop);
544
545         while (lengthIter > 0) {
546             size_t l;
547
548             while (__mpSyncReadDataPtr == NULL) {
549                 ret = pa_stream_peek(__mpStream, &__mpSyncReadDataPtr, &__mSyncReadLength);
550                 if (ret != 0) {
551                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_peek() : ret[%d]", ret);
552                 }
553
554                 if (__mSyncReadLength <= 0) {
555 #ifdef _AUDIO_IO_DEBUG_TIMING_
556                     AUDIO_IO_LOGD("readLength(%d byte) is not valid. wait...", __mSyncReadLength);
557 #endif
558                     pa_threaded_mainloop_wait(__mpMainloop);
559                 } else if (__mpSyncReadDataPtr == NULL) {
560                     // Data peeked, but it doesn't have any data
561                     ret = pa_stream_drop(__mpStream);
562                     if (ret != 0) {
563                         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret); //LCOV_EXCL_LINE
564                     }
565                 } else {
566                     __mSyncReadIndex = 0;
567                 }
568             }
569
570             if (__mSyncReadLength < lengthIter) {
571                 l = __mSyncReadLength;
572             } else {
573                 l = lengthIter;
574             }
575
576             // Copy partial pcm data on out parameter
577 #ifdef _AUDIO_IO_DEBUG_TIMING_
578             AUDIO_IO_LOGD("memcpy() that a peeked buffer[0x%x], index[%d], length[%d] on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
579 #endif
580             memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
581
582             // Move next position
583             buffer = (uint8_t*)buffer + l;
584             lengthIter -= l;
585
586             // Adjusts the rest length
587             __mSyncReadIndex  += l;
588             __mSyncReadLength -= l;
589
590             if (__mSyncReadLength == 0) {
591 #ifdef _AUDIO_IO_DEBUG_TIMING_
592                 AUDIO_IO_LOGD("__mSyncReadLength is zero, do drop()");
593 #endif
594                 ret = pa_stream_drop(__mpStream);
595                 if (ret != 0) {
596                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret); //LCOV_EXCL_LINE
597                 }
598
599                 // Reset the internal pointer
600                 __mpSyncReadDataPtr = NULL;
601                 __mSyncReadLength   = 0;
602                 __mSyncReadIndex    = 0;
603             }
604         }  // End of while (lengthIter > 0)
605
606         pa_threaded_mainloop_unlock(__mpMainloop);
607         __mIsUsedSyncRead = false;
608     } catch (CAudioError& e) {
609         pa_threaded_mainloop_unlock(__mpMainloop);
610         __mIsUsedSyncRead = false;
611         throw;
612     }
613
614     return length;
615 }
616
617 int CPulseAudioClient::peek(const void** buffer, size_t* length) {
618     if (__mIsInit == false) {
619         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
620     }
621
622     checkRunningState();
623
624     if (buffer == NULL || length == NULL) {
625         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p], length[%p]", buffer, length);
626     }
627
628     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
629         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
630     }
631
632     int ret = 0;
633
634     if (isInThread() == false) {
635         pa_threaded_mainloop_lock(__mpMainloop);
636         ret = pa_stream_peek(__mpStream, buffer, length);
637         pa_threaded_mainloop_unlock(__mpMainloop);
638     } else {
639         ret = pa_stream_peek(__mpStream, buffer, length);
640     }
641
642 #ifdef _AUDIO_IO_DEBUG_TIMING_
643     AUDIO_IO_LOGD("buffer[%p], length[%d]", *buffer, *length);
644 #endif
645
646     if (ret < 0) {
647         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek() : err[%d]", ret); //LCOV_EXCL_LINE
648     }
649
650     return ret;
651 }
652
653 int CPulseAudioClient::drop() {
654     if (__mIsInit == false) {
655         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
656     }
657
658 #ifdef _AUDIO_IO_DEBUG_TIMING_
659     AUDIO_IO_LOGD("");
660 #endif
661
662     checkRunningState();
663
664     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
665         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
666     }
667
668     int ret = 0;
669
670     if (isInThread() == false) {
671         pa_threaded_mainloop_lock(__mpMainloop);
672         ret = pa_stream_drop(__mpStream);
673         pa_threaded_mainloop_unlock(__mpMainloop);
674     } else {
675         ret = pa_stream_drop(__mpStream);
676     }
677
678     if (ret < 0) {
679         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret); //LCOV_EXCL_LINE
680     }
681
682     return ret;
683 }
684
685 int CPulseAudioClient::write(const void* data, size_t length) {
686     if (data == NULL) {
687         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid");
688     }
689
690     if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) {
691         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
692     }
693
694     int ret = 0;
695
696 #ifdef _AUDIO_IO_DEBUG_TIMING_
697     AUDIO_IO_LOGD("data[%p], length[%d], First[%d]", data, length, __mIsFirstStream);
698 #endif
699     if (pa_stream_is_corked(__mpStream)) {
700         AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
701         pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
702     }
703
704     if (isInThread() == false) {
705         pa_threaded_mainloop_lock(__mpMainloop);
706         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
707         pa_threaded_mainloop_unlock(__mpMainloop);
708     } else {
709         if (__mIsFirstStream) {
710             const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
711             uint32_t prebuf = attr->prebuf;
712             // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
713             // Because a stream will never start when an application wrote less than prebuf at first
714             if (length < prebuf) {
715                 char* dummy = new char[prebuf - length];
716                 memset(dummy, 0, prebuf - length);
717                 pa_stream_write(__mpStream, dummy, prebuf - length, NULL, 0LL, PA_SEEK_RELATIVE);
718                 delete [] dummy;
719             }
720             __mIsFirstStream = false;
721             AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%d], prebuf[%d], dummy[%d]",
722                           length, prebuf, (length < prebuf) ? prebuf - length : 0);
723         }
724         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
725     }
726
727     if (ret < 0) {
728         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret); //LCOV_EXCL_LINE
729     }
730
731     return ret;
732 }
733
734 void CPulseAudioClient::cork(bool cork) {
735     AUDIO_IO_LOGD("cork[%d]", cork);
736
737     if (__mIsInit == false) {
738         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
739     }
740
741     if (isInThread() == true) {
742         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
743     }
744
745     checkRunningState();
746
747     // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
748     if (cork == false)
749         __mIsFirstStream = true;
750
751     if (isInThread() == false) {
752         pa_threaded_mainloop_lock(__mpMainloop);
753         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
754         pa_threaded_mainloop_unlock(__mpMainloop);
755     } else {
756         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
757     }
758
759     return;
760 }
761
762 bool CPulseAudioClient::isCorked() {
763     if (__mIsInit == false) {
764         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
765     }
766
767     checkRunningState();
768
769     int isCork = 0;
770
771     if (isInThread() == false) {
772         pa_threaded_mainloop_lock(__mpMainloop);
773         isCork = pa_stream_is_corked(__mpStream);
774         pa_threaded_mainloop_unlock(__mpMainloop);
775     } else {
776         isCork = pa_stream_is_corked(__mpStream);
777     }
778
779 #ifdef _AUDIO_IO_DEBUG_TIMING_
780     AUDIO_IO_LOGD("isCork[%d]", isCork);
781 #endif
782     return static_cast<bool>(isCork);
783 }
784
785 bool CPulseAudioClient::drain() {
786     if (__mIsInit == false) {
787         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
788     }
789
790     checkRunningState();
791
792     if (isCorked()) {
793         AUDIO_IO_LOGW("Corked...");
794         return true;
795     }
796
797     if (__mIsDraining) {
798         AUDIO_IO_LOGW("already draining...");
799         return true;
800     }
801
802     if (!__mIsStarted) {
803         AUDIO_IO_LOGW("stream not started yet...skip drain");
804         return true;
805     }
806
807     if (isInThread() == false) {
808         AUDIO_IO_LOGD("drain");
809         pa_threaded_mainloop_lock(__mpMainloop);
810         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
811         __mIsDraining = true;
812         while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
813             pa_threaded_mainloop_wait(__mpMainloop);
814         }
815         pa_operation_unref(o);
816         pa_threaded_mainloop_unlock(__mpMainloop);
817         AUDIO_IO_LOGD("drain done");
818     } else {
819         AUDIO_IO_LOGD("drain in thread");
820         pa_operation_unref(pa_stream_drain(__mpStream, __successDrainCbInThread, this));
821         __mIsDraining = true;
822     }
823
824     return true;
825 }
826
827 bool CPulseAudioClient::flush() {
828     if (__mIsInit == false) {
829         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
830     }
831
832     checkRunningState();
833
834     if (isInThread() == false) {
835         AUDIO_IO_LOGD("flush");
836         pa_threaded_mainloop_lock(__mpMainloop);
837         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
838         pa_threaded_mainloop_unlock(__mpMainloop);
839     } else {
840         AUDIO_IO_LOGD("flush in thread");
841         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
842     }
843
844     return true;
845 }
846
847 size_t CPulseAudioClient::getWritableSize() {
848     if (__mIsInit == false) {
849         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
850     }
851
852     checkRunningState();
853
854     if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
855         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
856     }
857
858     size_t ret = 0;
859
860     if (isInThread() == false) {
861         pa_threaded_mainloop_lock(__mpMainloop);
862         ret = pa_stream_writable_size(__mpStream);
863         pa_threaded_mainloop_unlock(__mpMainloop);
864     } else {
865         ret = pa_stream_writable_size(__mpStream);
866     }
867
868     return ret;
869 }
870
871 void CPulseAudioClient::checkRunningState() {
872     if (__mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)) == 0) {
873         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", __mpContext);
874     }
875     if (__mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)) == 0) {
876         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", __mpStream);
877     }
878     if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY) {
879         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream);
880     }
881
882 #ifdef _AUDIO_IO_DEBUG_TIMING_
883     AUDIO_IO_LOGD("This client is running");
884 #endif
885 }
886
887 bool CPulseAudioClient::isInThread() {
888     int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
889
890 #ifdef _AUDIO_IO_DEBUG_TIMING_
891     AUDIO_IO_LOGD("isInThread[%d]", ret);
892 #endif
893     return static_cast<bool>(ret);
894 }
895
896 //LCOV_EXCL_START
897 size_t CPulseAudioClient::getReadableSize() {
898     if (__mIsInit == false) {
899         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
900     }
901
902     checkRunningState();
903
904     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) {
905         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
906     }
907
908     size_t ret = 0;
909
910     if (isInThread() == false) {
911         pa_threaded_mainloop_lock(__mpMainloop);
912         ret = pa_stream_writable_size(__mpStream);
913         pa_threaded_mainloop_unlock(__mpMainloop);
914     } else {
915         ret = pa_stream_writable_size(__mpStream);
916     }
917
918     return ret;
919 }
920
921 size_t CPulseAudioClient::getBufferSize() {
922     if (__mIsInit == false) {
923         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
924     }
925
926     checkRunningState();
927
928     size_t ret = 0;
929
930     try {
931         if (isInThread() == false) {
932             pa_threaded_mainloop_lock(__mpMainloop);
933         }
934
935         const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
936         if (attr == NULL) {
937             int _err = pa_context_errno(__mpContext);
938             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() : err[%d]", _err);
939         }
940
941         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
942             ret = attr->tlength;
943             AUDIO_IO_LOGD("PLAYBACK buffer size[%d]", ret);
944         } else {
945             ret = attr->fragsize;
946             AUDIO_IO_LOGD("RECORD buffer size[%d]", ret);
947         }
948     } catch (CAudioError& e) {
949         if (isInThread() == false) {
950             pa_threaded_mainloop_unlock(__mpMainloop);
951         }
952         throw;
953     }
954
955     if (isInThread() == false) {
956         pa_threaded_mainloop_unlock(__mpMainloop);
957     }
958
959     return ret;
960 }
961
962 pa_usec_t CPulseAudioClient::getLatency() {
963     if (__mIsInit == false) {
964         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
965     }
966
967     checkRunningState();
968
969     pa_usec_t ret = 0;
970     int negative  = 0;
971
972     if (isInThread() == false) {
973         if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) {
974             int _err = pa_context_errno(__mpContext);
975             if (_err != PA_ERR_NODATA) {
976                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
977             }
978         }
979         return negative ? 0 : ret;
980     }
981
982     pa_threaded_mainloop_lock(__mpMainloop);
983
984     try {
985         while (true) {
986             if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) {
987                 break;
988             }
989
990             int _err = pa_context_errno(__mpContext);
991             if (_err != PA_ERR_NODATA) {
992                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
993             }
994
995             /* Wait until latency data is available again */
996             pa_threaded_mainloop_wait(__mpMainloop);
997         }
998     } catch (CAudioError& e) {
999         pa_threaded_mainloop_unlock(__mpMainloop);
1000         throw;
1001     }
1002
1003     pa_threaded_mainloop_unlock(__mpMainloop);
1004
1005     return negative ? 0 : ret;
1006 }
1007
1008 pa_usec_t CPulseAudioClient::getFinalLatency() {
1009     if (__mIsInit == false) {
1010         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
1011     }
1012
1013     checkRunningState();
1014
1015     pa_usec_t ret = 0;
1016     uint32_t  ver = 0;
1017
1018     try {
1019         if (isInThread() == false) {
1020             pa_threaded_mainloop_lock(__mpMainloop);
1021         }
1022
1023         ver = pa_context_get_server_protocol_version(__mpContext);
1024         if (ver >= 13) {
1025             const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
1026             const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
1027             const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
1028
1029             if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
1030                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
1031                         buffer_attr, sample_spec, timing_info);
1032             }
1033
1034             if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
1035                 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
1036                 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", ret);
1037             } else {
1038                 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
1039                 AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", ret);
1040             }
1041         } else {
1042             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
1043         }
1044
1045         if (isInThread() == false) {
1046             pa_threaded_mainloop_unlock(__mpMainloop);
1047         }
1048     } catch (CAudioError& e) {
1049         if (isInThread() == false) {
1050             pa_threaded_mainloop_unlock(__mpMainloop);
1051         }
1052         throw;
1053     }
1054
1055     return ret;
1056 }
1057
1058 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
1059     return __mDirection;
1060 }
1061
1062 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {
1063     return __mSpec;
1064 }
1065 //LCOV_EXCL_STOP