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[%zu]", 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[%zu]", 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[%zu] 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(%zu 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[%p], index[%zu], length[%zu] 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[%zu]", *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[%zu], First[%d]", data, length, __mIsFirstStream);
698 #endif
699
700     if (isInThread() == false) {
701         pa_threaded_mainloop_lock(__mpMainloop);
702         if (pa_stream_is_corked(__mpStream)) {
703             AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
704             pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
705         }
706
707         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
708         pa_threaded_mainloop_unlock(__mpMainloop);
709     } else {
710         if (pa_stream_is_corked(__mpStream)) {
711             AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
712             pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
713         }
714
715         if (__mIsFirstStream) {
716             const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
717             uint32_t prebuf = attr->prebuf;
718             // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
719             // Because a stream will never start when an application wrote less than prebuf at first
720             if (length < prebuf) {
721                 char* dummy = new char[prebuf - length];
722                 memset(dummy, 0, prebuf - length);
723                 pa_stream_write(__mpStream, dummy, prebuf - length, NULL, 0LL, PA_SEEK_RELATIVE);
724                 delete [] dummy;
725             }
726             __mIsFirstStream = false;
727             AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%zu], prebuf[%d], dummy[%zu]",
728                           length, prebuf, (length < prebuf) ? prebuf - length : 0);
729         }
730         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
731     }
732
733     if (ret < 0) {
734         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret); //LCOV_EXCL_LINE
735     }
736
737     return ret;
738 }
739
740 void CPulseAudioClient::cork(bool cork) {
741     AUDIO_IO_LOGD("cork[%d]", cork);
742
743     if (__mIsInit == false) {
744         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
745     }
746
747     if (isInThread() == true) {
748         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
749     }
750
751     checkRunningState();
752
753     // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
754     if (cork == false)
755         __mIsFirstStream = true;
756
757     if (isInThread() == false) {
758         pa_threaded_mainloop_lock(__mpMainloop);
759         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
760         pa_threaded_mainloop_unlock(__mpMainloop);
761     } else {
762         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
763     }
764
765     return;
766 }
767
768 bool CPulseAudioClient::isCorked() {
769     if (__mIsInit == false) {
770         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
771     }
772
773     checkRunningState();
774
775     int isCork = 0;
776
777     if (isInThread() == false) {
778         pa_threaded_mainloop_lock(__mpMainloop);
779         isCork = pa_stream_is_corked(__mpStream);
780         pa_threaded_mainloop_unlock(__mpMainloop);
781     } else {
782         isCork = pa_stream_is_corked(__mpStream);
783     }
784
785 #ifdef _AUDIO_IO_DEBUG_TIMING_
786     AUDIO_IO_LOGD("isCork[%d]", isCork);
787 #endif
788     return static_cast<bool>(isCork);
789 }
790
791 bool CPulseAudioClient::drain() {
792     if (__mIsInit == false) {
793         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
794     }
795
796     checkRunningState();
797
798     if (isCorked()) {
799         AUDIO_IO_LOGW("Corked...");
800         return true;
801     }
802
803     if (__mIsDraining) {
804         AUDIO_IO_LOGW("already draining...");
805         return true;
806     }
807
808     if (!__mIsStarted) {
809         AUDIO_IO_LOGW("stream not started yet...skip drain");
810         return true;
811     }
812
813     if (isInThread() == false) {
814         AUDIO_IO_LOGD("drain");
815         pa_threaded_mainloop_lock(__mpMainloop);
816         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
817         __mIsDraining = true;
818         while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
819             pa_threaded_mainloop_wait(__mpMainloop);
820         }
821         pa_operation_unref(o);
822         pa_threaded_mainloop_unlock(__mpMainloop);
823         AUDIO_IO_LOGD("drain done");
824     } else {
825         AUDIO_IO_LOGD("drain in thread");
826         pa_operation_unref(pa_stream_drain(__mpStream, __successDrainCbInThread, this));
827         __mIsDraining = true;
828     }
829
830     return true;
831 }
832
833 bool CPulseAudioClient::flush() {
834     if (__mIsInit == false) {
835         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
836     }
837
838     checkRunningState();
839
840     if (isInThread() == false) {
841         AUDIO_IO_LOGD("flush");
842         pa_threaded_mainloop_lock(__mpMainloop);
843         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
844         pa_threaded_mainloop_unlock(__mpMainloop);
845     } else {
846         AUDIO_IO_LOGD("flush in thread");
847         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
848     }
849
850     return true;
851 }
852
853 size_t CPulseAudioClient::getWritableSize() {
854     if (__mIsInit == false) {
855         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
856     }
857
858     checkRunningState();
859
860     if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
861         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
862     }
863
864     size_t ret = 0;
865
866     if (isInThread() == false) {
867         pa_threaded_mainloop_lock(__mpMainloop);
868         ret = pa_stream_writable_size(__mpStream);
869         pa_threaded_mainloop_unlock(__mpMainloop);
870     } else {
871         ret = pa_stream_writable_size(__mpStream);
872     }
873
874     return ret;
875 }
876
877 void CPulseAudioClient::checkRunningState() {
878     if (__mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)) == 0) {
879         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", __mpContext);
880     }
881     if (__mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)) == 0) {
882         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", __mpStream);
883     }
884     if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY) {
885         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream);
886     }
887
888 #ifdef _AUDIO_IO_DEBUG_TIMING_
889     AUDIO_IO_LOGD("This client is running");
890 #endif
891 }
892
893 bool CPulseAudioClient::isInThread() {
894     int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
895
896 #ifdef _AUDIO_IO_DEBUG_TIMING_
897     AUDIO_IO_LOGD("isInThread[%d]", ret);
898 #endif
899     return static_cast<bool>(ret);
900 }
901
902 //LCOV_EXCL_START
903 size_t CPulseAudioClient::getReadableSize() {
904     if (__mIsInit == false) {
905         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
906     }
907
908     checkRunningState();
909
910     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) {
911         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
912     }
913
914     size_t ret = 0;
915
916     if (isInThread() == false) {
917         pa_threaded_mainloop_lock(__mpMainloop);
918         ret = pa_stream_writable_size(__mpStream);
919         pa_threaded_mainloop_unlock(__mpMainloop);
920     } else {
921         ret = pa_stream_writable_size(__mpStream);
922     }
923
924     return ret;
925 }
926
927 size_t CPulseAudioClient::getBufferSize() {
928     if (__mIsInit == false) {
929         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
930     }
931
932     checkRunningState();
933
934     size_t ret = 0;
935
936     try {
937         if (isInThread() == false) {
938             pa_threaded_mainloop_lock(__mpMainloop);
939         }
940
941         const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
942         if (attr == NULL) {
943             int _err = pa_context_errno(__mpContext);
944             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() : err[%d]", _err);
945         }
946
947         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
948             ret = attr->tlength;
949             AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", ret);
950         } else {
951             ret = attr->fragsize;
952             AUDIO_IO_LOGD("RECORD buffer size[%zu]", ret);
953         }
954     } catch (CAudioError& e) {
955         if (isInThread() == false) {
956             pa_threaded_mainloop_unlock(__mpMainloop);
957         }
958         throw;
959     }
960
961     if (isInThread() == false) {
962         pa_threaded_mainloop_unlock(__mpMainloop);
963     }
964
965     return ret;
966 }
967
968 pa_usec_t CPulseAudioClient::getLatency() {
969     if (__mIsInit == false) {
970         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
971     }
972
973     checkRunningState();
974
975     pa_usec_t ret = 0;
976     int negative  = 0;
977
978     if (isInThread() == false) {
979         if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) {
980             int _err = pa_context_errno(__mpContext);
981             if (_err != PA_ERR_NODATA) {
982                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
983             }
984         }
985         return negative ? 0 : ret;
986     }
987
988     pa_threaded_mainloop_lock(__mpMainloop);
989
990     try {
991         while (true) {
992             if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) {
993                 break;
994             }
995
996             int _err = pa_context_errno(__mpContext);
997             if (_err != PA_ERR_NODATA) {
998                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
999             }
1000
1001             /* Wait until latency data is available again */
1002             pa_threaded_mainloop_wait(__mpMainloop);
1003         }
1004     } catch (CAudioError& e) {
1005         pa_threaded_mainloop_unlock(__mpMainloop);
1006         throw;
1007     }
1008
1009     pa_threaded_mainloop_unlock(__mpMainloop);
1010
1011     return negative ? 0 : ret;
1012 }
1013
1014 pa_usec_t CPulseAudioClient::getFinalLatency() {
1015     if (__mIsInit == false) {
1016         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
1017     }
1018
1019     checkRunningState();
1020
1021     pa_usec_t ret = 0;
1022     uint32_t  ver = 0;
1023
1024     try {
1025         if (isInThread() == false) {
1026             pa_threaded_mainloop_lock(__mpMainloop);
1027         }
1028
1029         ver = pa_context_get_server_protocol_version(__mpContext);
1030         if (ver >= 13) {
1031             const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
1032             const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
1033             const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
1034
1035             if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
1036                 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",
1037                         buffer_attr, sample_spec, timing_info);
1038             }
1039
1040             if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
1041                 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
1042                 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", ret);
1043             } else {
1044                 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
1045                 AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", ret);
1046             }
1047         } else {
1048             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
1049         }
1050
1051         if (isInThread() == false) {
1052             pa_threaded_mainloop_unlock(__mpMainloop);
1053         }
1054     } catch (CAudioError& e) {
1055         if (isInThread() == false) {
1056             pa_threaded_mainloop_unlock(__mpMainloop);
1057         }
1058         throw;
1059     }
1060
1061     return ret;
1062 }
1063
1064 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
1065     return __mDirection;
1066 }
1067
1068 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {
1069     return __mSpec;
1070 }
1071 //LCOV_EXCL_STOP