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