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