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