audio-io fixed memory corruption issue on 64bit target, C++ conding rule updated
[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 <mm.h>
19 #include "CAudioIODef.h"
20
21
22 using namespace std;
23 using namespace tizen_media_audio;
24
25
26 /**
27  * class CPulseAudioClient
28  */
29 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
30
31 CPulseAudioClient::CPulseAudioClient(EStreamDirection      direction,
32                                      CPulseStreamSpec&     spec,
33                                      IPulseStreamListener* listener) :
34     mDirection(direction),
35     mSpec(spec),
36     mpListener(listener),
37     mpMainloop(NULL),
38     mpContext(NULL),
39     mpStream(NULL),
40     mpPropList(NULL),
41     mIsInit(false),
42     mIsOperationSuccess(false) {
43 }
44
45 CPulseAudioClient::~CPulseAudioClient() {
46     finalize();
47 }
48
49 void CPulseAudioClient::_contextStateChangeCb(pa_context* c, void* user_data) {
50     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
51     assert(pClient);
52     assert(c);
53
54     switch (pa_context_get_state(c)) {
55     case PA_CONTEXT_READY:
56         AUDIO_IO_LOGD("The context is ready!");
57     case PA_CONTEXT_FAILED:
58     case PA_CONTEXT_TERMINATED:
59         pa_threaded_mainloop_signal(pClient->mpMainloop, 0);
60         break;
61
62     case PA_CONTEXT_UNCONNECTED:
63     case PA_CONTEXT_CONNECTING:
64     case PA_CONTEXT_AUTHORIZING:
65     case PA_CONTEXT_SETTING_NAME:
66         break;
67     }
68 }
69
70 void CPulseAudioClient::_successContextCb(pa_context* c, int success, void* user_data) {
71     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
72     assert(c);
73     assert(user_data);
74
75     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
76     pClient->mIsOperationSuccess = static_cast<bool>(success);
77
78     pa_threaded_mainloop_signal(pClient->mpMainloop, 0);
79 }
80
81 void CPulseAudioClient::_streamStateChangeCb(pa_stream* s, void* user_data) {
82     assert(s);
83     assert(user_data);
84
85     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
86
87     switch (pa_stream_get_state(s)) {
88     case PA_STREAM_READY:
89         AUDIO_IO_LOGD("The stream is ready!");
90         pClient->mpListener->onStateChanged(CAudioInfo::AUDIO_IO_STATE_RUNNING);
91     case PA_STREAM_FAILED:
92     case PA_STREAM_TERMINATED:
93         pa_threaded_mainloop_signal(pClient->mpMainloop, 0);
94         break;
95
96     case PA_STREAM_UNCONNECTED:
97         break;
98     case PA_STREAM_CREATING:
99         break;
100     }
101 }
102
103 void CPulseAudioClient::_streamCaptureCb(pa_stream* s, size_t length, void* user_data) {
104     assert(s);
105     assert(user_data);
106
107     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
108     assert(pClient->mpListener);
109
110     pClient->mpListener->onStream(pClient, length);
111 }
112
113 void CPulseAudioClient::_streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
114     //AUDIO_IO_LOGD("_streamPlaybackCb()");
115     assert(s);
116     assert(user_data);
117
118     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
119     assert(pClient->mpListener);
120
121     if (pClient->mIsInit == false) {
122         AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create - Dummy write[length:%d]", length);
123
124         char* dummy = new char[length];
125         memset(dummy, 0, length);
126         pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
127         delete [] dummy;
128
129         return;
130     }
131
132     pClient->mpListener->onStream(pClient, length);
133 }
134
135 void CPulseAudioClient::_streamLatencyUpdateCb(pa_stream* s, void* user_data) {
136     assert(s);
137     assert(user_data);
138
139     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
140
141     pa_threaded_mainloop_signal(pClient->mpMainloop, 0);
142 }
143
144 void CPulseAudioClient::_successStreamCb(pa_stream* s, int success, void* user_data) {
145     AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
146     assert(s);
147     assert(user_data);
148
149     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
150     pClient->mIsOperationSuccess = static_cast<bool>(success);
151
152     pa_threaded_mainloop_signal(pClient->mpMainloop, 0);
153 }
154
155 void CPulseAudioClient::initialize() throw (CAudioError) {
156     AUDIO_IO_LOGD("");
157     if (mIsInit == true) {
158         return;
159     }
160
161     int ret = 0;
162     int err = 0;
163
164     try {
165         // Allocates PA proplist
166         mpPropList = pa_proplist_new();
167         if (mpPropList == NULL) {
168             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()");
169         }
170
171         // Adds values on proplist for delivery to PULSEAUDIO
172         char *streamType = NULL;
173         CAudioInfo::EAudioType audioType = mSpec.getAudioInfo().getAudioType();
174         mSpec.getAudioInfo().convertAudioType2StreamType(audioType, &streamType);
175         pa_proplist_sets(mpPropList, PA_PROP_MEDIA_ROLE, streamType);
176
177         int index = mSpec.getAudioInfo().getAudioIndex();
178         if (index >= 0) {
179             pa_proplist_setf(mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
180         }
181
182         // Adds latency on proplist for delivery to PULSEAUDIO
183         CPulseStreamSpec::EStreamLatency latency = mSpec.getStreamLatency();
184         AUDIO_IO_LOGD("LATENCY : %d", latency);
185
186         pa_proplist_setf(mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%d", latency);
187
188         // Allocates PA mainloop
189         mpMainloop = pa_threaded_mainloop_new();
190         if (mpMainloop == NULL) {
191             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
192         }
193
194         // Allocates PA context
195         mpContext = pa_context_new(pa_threaded_mainloop_get_api(mpMainloop), CLIENT_NAME);
196         if (mpContext == NULL) {
197             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
198         }
199
200         // Sets context state changed callback
201         pa_context_set_state_callback(mpContext, _contextStateChangeCb, this);
202
203         // Connects this client with PA server
204         if (pa_context_connect(mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
205             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
206         }
207
208         // LOCK for synchronous connection
209         pa_threaded_mainloop_lock(mpMainloop);
210
211         // Start mainloop
212         if (pa_threaded_mainloop_start(mpMainloop) < 0) {
213             pa_threaded_mainloop_unlock(mpMainloop);
214             THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
215         }
216
217         // Connection process is asynchronously
218         // So, this function will be waited when occurred context state change event
219         // If I got a signal, do next processing
220         while (true) {
221             pa_context_state_t state;
222             state = pa_context_get_state(mpContext);
223
224             if (state == PA_CONTEXT_READY) {
225                 break;
226             }
227
228             if (!PA_CONTEXT_IS_GOOD(state)) {
229                 err = pa_context_errno(mpContext);
230                 pa_threaded_mainloop_unlock(mpMainloop);
231                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good err:[%d]", err);
232             }
233
234             /* Wait until the context is ready */
235             pa_threaded_mainloop_wait(mpMainloop);
236         }
237
238         // Allocates PA stream
239         pa_sample_spec ss   = mSpec.getSampleSpec();
240         pa_channel_map map  = mSpec.getChannelMap();
241
242         mpStream = pa_stream_new_with_proplist(mpContext, mSpec.getStreamName(), &ss, &map, mpPropList);
243         if (mpStream == NULL) {
244             pa_threaded_mainloop_unlock(mpMainloop);
245             THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()()");
246         }
247
248         // Sets stream callbacks
249         pa_stream_set_state_callback(mpStream, _streamStateChangeCb, this);
250         pa_stream_set_read_callback(mpStream, _streamCaptureCb, this);
251         pa_stream_set_write_callback(mpStream, _streamPlaybackCb, this);
252         pa_stream_set_latency_update_callback(mpStream, _streamLatencyUpdateCb, this);
253
254         // Connect stream with PA Server
255
256         if (mDirection == STREAM_DIRECTION_PLAYBACK) {
257             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
258                     PA_STREAM_INTERPOLATE_TIMING |
259                     PA_STREAM_ADJUST_LATENCY     |
260                     PA_STREAM_AUTO_TIMING_UPDATE);
261
262             ret = pa_stream_connect_playback(mpStream, NULL, NULL, flags, NULL, NULL);
263         } else {
264             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
265                     PA_STREAM_INTERPOLATE_TIMING |
266                     PA_STREAM_ADJUST_LATENCY     |
267                     PA_STREAM_AUTO_TIMING_UPDATE);
268
269             ret = pa_stream_connect_record(mpStream, NULL, NULL, flags);
270         }
271
272         if (ret != 0) {
273             err = pa_context_errno(mpContext);
274             pa_threaded_mainloop_unlock(mpMainloop);
275             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() err:[%d]", err);
276         }
277
278         while (true) {
279             pa_stream_state_t state;
280             state = pa_stream_get_state(mpStream);
281
282             if (state == PA_STREAM_READY) {
283                 AUDIO_IO_LOGD("STREAM READY");
284                 break;
285             }
286
287             if (!PA_STREAM_IS_GOOD(state)) {
288                 err = pa_context_errno(mpContext);
289                 pa_threaded_mainloop_unlock(mpMainloop);
290                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good err:[%d]", err);
291             }
292
293             /* Wait until the stream is ready */
294             pa_threaded_mainloop_wait(mpMainloop);
295         }
296
297         // End of synchronous
298         pa_threaded_mainloop_unlock(mpMainloop);
299
300         mIsInit = true;
301     } catch (CAudioError e) {
302         finalize();
303         throw e;
304     }
305 }
306
307 void CPulseAudioClient::finalize() {
308     AUDIO_IO_LOGD("");
309     if (mIsInit == false) {
310         return;
311     }
312
313     if (mpMainloop != NULL) {
314         pa_threaded_mainloop_stop(mpMainloop);
315     }
316     if (mpStream != NULL) {
317         pa_stream_disconnect(mpStream);
318         mpStream = NULL;
319     }
320
321     if (mpContext != NULL) {
322         pa_context_disconnect(mpContext);
323         pa_context_unref(mpContext);
324         mpContext = NULL;
325     }
326
327     if (mpMainloop != NULL) {
328         pa_threaded_mainloop_free(mpMainloop);
329         mpMainloop = NULL;
330     }
331
332     if (mpPropList != NULL) {
333         pa_proplist_free(mpPropList);
334         mpPropList = NULL;
335     }
336
337     mIsInit = false;
338 }
339
340 int CPulseAudioClient::peek(const void** data, size_t* length) throw (CAudioError) {
341     if (mIsInit == false) {
342         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
343     }
344
345 #ifdef _AUDIO_IO_DEBUG_TIMING_
346     AUDIO_IO_LOGD("data:[%p], length:[%p]", data, length);
347 #endif
348
349     checkRunningState();
350
351     if (data == NULL || length == NULL) {
352         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INVALID_ARGUMENT, "The parameter is invalid - data:%p, length:%p", data, length);
353     }
354
355     if (mDirection == STREAM_DIRECTION_PLAYBACK) {
356         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
357     }
358
359     int ret = 0;
360
361     if (isInThread() == false) {
362         pa_threaded_mainloop_lock(mpMainloop);
363         ret = pa_stream_peek(mpStream, data, length);
364         pa_threaded_mainloop_unlock(mpMainloop);
365     } else {
366         ret = pa_stream_peek(mpStream, data, length);
367     }
368
369     if (ret < 0) {
370         THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek()");
371     }
372
373     return ret;
374 }
375
376 int CPulseAudioClient::drop() throw (CAudioError) {
377     if (mIsInit == false) {
378         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
379     }
380
381 #ifdef _AUDIO_IO_DEBUG_TIMING_
382     AUDIO_IO_LOGD("");
383 #endif
384
385     checkRunningState();
386
387     if (mDirection == STREAM_DIRECTION_PLAYBACK) {
388         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
389     }
390
391     int ret = 0;
392
393     if (isInThread() == false) {
394         pa_threaded_mainloop_lock(mpMainloop);
395         ret = pa_stream_drop(mpStream);
396         pa_threaded_mainloop_unlock(mpMainloop);
397     } else {
398         ret = pa_stream_drop(mpStream);
399     }
400
401     if (ret < 0) {
402         THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop()");
403     }
404
405     return ret;
406 }
407
408 int CPulseAudioClient::write(const void* data, size_t length) throw (CAudioError) {
409     if (mIsInit == false) {
410         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
411     }
412
413 #ifdef _AUDIO_IO_DEBUG_TIMING_
414     AUDIO_IO_LOGD("data[%p], length:[%d]", data, length);
415 #endif
416
417     checkRunningState();
418
419     if (data == NULL || length < 0) {
420         THROW_ERROR_MSG(CAudioError::ERROR_INVALID_ARGUMENT, "The parameter is invalid");
421     }
422
423     if (mDirection == STREAM_DIRECTION_RECORD) {
424         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
425     }
426
427     int ret = 0;
428
429     if (isInThread() == false) {
430         pa_threaded_mainloop_lock(mpMainloop);
431         ret = pa_stream_write(mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
432         pa_threaded_mainloop_unlock(mpMainloop);
433     } else {
434         ret = pa_stream_write(mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
435     }
436
437     if (ret < 0) {
438         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() err:%d", ret);
439     }
440
441     return ret;
442 }
443
444 void CPulseAudioClient::cork(bool cork) throw (CAudioError) {
445     AUDIO_IO_LOGD("bool cork:%d", cork);
446
447     if (mIsInit == false) {
448         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
449     }
450
451     if (isInThread() == true) {
452         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
453     }
454
455     checkRunningState();
456
457     if (isInThread() == false) {
458         pa_threaded_mainloop_lock(mpMainloop);
459         pa_operation_unref(pa_stream_cork(mpStream, static_cast<int>(cork), _successStreamCb, this));
460         pa_threaded_mainloop_unlock(mpMainloop);
461     } else {
462         pa_operation_unref(pa_stream_cork(mpStream, static_cast<int>(cork), _successStreamCb, this));
463     }
464
465     return;
466 }
467
468 bool CPulseAudioClient::isCorked() throw (CAudioError) {
469     if (mIsInit == false) {
470         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
471     }
472
473     checkRunningState();
474
475     int isCork = 0;
476
477     if (isInThread() == false) {
478         pa_threaded_mainloop_lock(mpMainloop);
479         isCork = pa_stream_is_corked(mpStream);
480         pa_threaded_mainloop_unlock(mpMainloop);
481     } else {
482         isCork = pa_stream_is_corked(mpStream);
483     }
484
485     AUDIO_IO_LOGD("isCork:%d", isCork);
486     return static_cast<bool>(isCork);
487 }
488
489 bool CPulseAudioClient::drain() throw (CAudioError) {
490     AUDIO_IO_LOGD("drain");
491
492     if (mIsInit == false) {
493         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
494     }
495
496     checkRunningState();
497
498     if (isInThread() == false) {
499         pa_threaded_mainloop_lock(mpMainloop);
500         pa_operation_unref(pa_stream_drain(mpStream, _successStreamCb, this));
501         pa_threaded_mainloop_unlock(mpMainloop);
502     } else {
503         pa_operation_unref(pa_stream_drain(mpStream, _successStreamCb, this));
504     }
505
506     return true;
507 }
508
509 bool CPulseAudioClient::flush() throw (CAudioError) {
510     AUDIO_IO_LOGD("flush");
511
512     if (mIsInit == false) {
513         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
514     }
515
516     checkRunningState();
517
518     if (isInThread() == false) {
519         pa_threaded_mainloop_lock(mpMainloop);
520         pa_operation_unref(pa_stream_flush(mpStream, _successStreamCb, this));
521         pa_threaded_mainloop_unlock(mpMainloop);
522     } else {
523         pa_operation_unref(pa_stream_flush(mpStream, _successStreamCb, this));
524     }
525
526     return true;
527 }
528
529 size_t CPulseAudioClient::getWritableSize() throw (CAudioError) {
530     if (mIsInit == false) {
531         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
532     }
533
534     checkRunningState();
535
536     if (mDirection != STREAM_DIRECTION_PLAYBACK) {
537         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
538     }
539
540     size_t ret = 0;
541
542     if (isInThread() == false) {
543         pa_threaded_mainloop_lock(mpMainloop);
544         ret = pa_stream_writable_size(mpStream);
545         pa_threaded_mainloop_unlock(mpMainloop);
546     } else {
547         ret = pa_stream_writable_size(mpStream);
548     }
549
550     return ret;
551 }
552
553 void CPulseAudioClient::checkRunningState() throw (CAudioError) {
554     if (mIsInit == false) {
555         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
556     }
557
558     if (mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(mpContext)) == 0) {
559         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", mpContext);
560     }
561     if (mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(mpStream)) == 0) {
562         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", mpStream);
563     }
564     if (pa_context_get_state(mpContext) != PA_CONTEXT_READY || pa_stream_get_state(mpStream)   != PA_STREAM_READY) {
565         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", mpContext, mpStream);
566     }
567
568 #ifdef _AUDIO_IO_DEBUG_TIMING_
569     AUDIO_IO_LOGD("This client is running");
570 #endif
571 }
572
573 bool CPulseAudioClient::isInThread() throw (CAudioError) {
574     if (mIsInit == false) {
575         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
576     }
577
578     int ret = pa_threaded_mainloop_in_thread(mpMainloop);
579
580 #ifdef _AUDIO_IO_DEBUG_TIMING_
581     AUDIO_IO_LOGD("isInThread : [%d][TRUE:1][FALSE:0]", ret);
582 #endif
583     return static_cast<bool>(ret);
584 }
585
586 size_t CPulseAudioClient::getReadableSize() throw (CAudioError) {
587     if (mIsInit == false) {
588         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
589     }
590
591     checkRunningState();
592
593     if (mDirection != STREAM_DIRECTION_RECORD) {
594         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
595     }
596
597     size_t ret = 0;
598
599     if (isInThread() == false) {
600         pa_threaded_mainloop_lock(mpMainloop);
601         ret = pa_stream_writable_size(mpStream);
602         pa_threaded_mainloop_unlock(mpMainloop);
603     } else {
604         ret = pa_stream_writable_size(mpStream);
605     }
606
607     return ret;
608 }
609
610 size_t CPulseAudioClient::getBufferSize() throw (CAudioError) {
611     if (mIsInit == false) {
612         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
613     }
614
615     checkRunningState();
616
617     size_t ret = 0;
618
619     try {
620         if (isInThread() == false) {
621             pa_threaded_mainloop_lock(mpMainloop);
622         }
623
624         const pa_buffer_attr* attr = pa_stream_get_buffer_attr(mpStream);
625         if (attr == NULL) {
626             int _err = pa_context_errno(mpContext);
627             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() err:%d", _err);
628         }
629
630         if (mDirection == STREAM_DIRECTION_PLAYBACK) {
631             ret = attr->tlength;
632             AUDIO_IO_LOGD("PLAYBACK buffer size : %d", ret);
633         } else {
634             ret = attr->fragsize;
635             AUDIO_IO_LOGD("RECORD buffer size : %d", ret);
636         }
637     } catch (CAudioError err) {
638         if (isInThread() == false) {
639             pa_threaded_mainloop_unlock(mpMainloop);
640         }
641         throw err;
642     }
643
644     if (isInThread() == false) {
645         pa_threaded_mainloop_unlock(mpMainloop);
646     }
647
648     return ret;
649 }
650
651 pa_usec_t CPulseAudioClient::getLatency() throw (CAudioError) {
652     if (mIsInit == false) {
653         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
654     }
655
656     checkRunningState();
657
658     pa_usec_t ret = 0;
659     int negative  = 0;
660
661     if (isInThread() == false) {
662         if (pa_stream_get_latency(mpStream, &ret, &negative) < 0) {
663             int _err = pa_context_errno(mpContext);
664             if (_err != PA_ERR_NODATA) {
665                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() err:%d", _err);
666             }
667         }
668         return negative ? 0 : ret;
669     }
670
671         pa_threaded_mainloop_lock(mpMainloop);
672
673     try {
674         while (true) {
675             if (pa_stream_get_latency(mpStream, &ret, &negative) >= 0) {
676                 break;
677             }
678
679             int _err = pa_context_errno(mpContext);
680             if (_err != PA_ERR_NODATA) {
681                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() err:%d", _err);
682             }
683
684             /* Wait until latency data is available again */
685             pa_threaded_mainloop_wait(mpMainloop);
686         }
687     } catch (CAudioError e) {
688         pa_threaded_mainloop_unlock(mpMainloop);
689         throw e;
690     }
691
692     pa_threaded_mainloop_unlock(mpMainloop);
693
694     return negative ? 0 : ret;
695 }
696
697 pa_usec_t CPulseAudioClient::getFinalLatency() throw (CAudioError) {
698     if (mIsInit == false) {
699         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
700     }
701
702     checkRunningState();
703
704     pa_usec_t ret = 0;
705     uint32_t  ver = 0;
706
707     try {
708         if (isInThread() == false) {
709             pa_threaded_mainloop_lock(mpMainloop);
710         }
711
712         ver = pa_context_get_server_protocol_version(mpContext);
713         if (ver >= 13) {
714             const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(mpStream);
715             const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(mpStream);
716             const pa_timing_info* timing_info = pa_stream_get_timing_info(mpStream);
717
718             if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
719                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_OUT_OF_MEMORY, "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
720                         buffer_attr, sample_spec, timing_info);
721             }
722
723             if (mDirection == STREAM_DIRECTION_PLAYBACK) {
724                 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
725                 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY : %d", ret);
726             } else {
727                 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
728                 AUDIO_IO_LOGD("FINAL RECORD LATENCY : %d", ret);
729             }
730         } else {
731             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
732         }
733
734         if (isInThread() == false) {
735             pa_threaded_mainloop_unlock(mpMainloop);
736         }
737     } catch (CAudioError e) {
738         if (isInThread() == false) {
739             pa_threaded_mainloop_unlock(mpMainloop);
740         }
741         throw e;
742     }
743
744     return ret;
745 }
746
747 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
748     return mDirection;
749 }
750
751 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {
752     return mSpec;
753 }