Modification for Tizen Coding Rule
[platform/core/multimedia/libmm-sound.git] / mm_sound_pa_client.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Seungbae Shin <seungbae.shin@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <mm_error.h>
27 #include <mm_debug.h>
28
29 #include <mm_sound_pa_client.h>
30
31 #include <glib.h>
32
33 #define MM_SOUND_CHANNEL_MIN                1
34 #define MM_SOUND_CHANNEL_MAX                6
35
36 typedef struct _mm_sound_handle_t {
37         uint32_t handle;
38
39         int mode;
40         int volume_type;
41         int gain_type;
42         int rate;
43         int channels;
44         pa_simple *s;
45
46         int period;     /* open api retrun value. */
47
48         unsigned int stream_idx;
49         int source_type;
50 } mm_sound_handle_t;
51
52 static struct {
53         uint32_t handle_count;  /* use amotic operations */
54         GList *handles;
55         pthread_mutex_t lock;
56
57         int state;
58         pa_threaded_mainloop *mainloop;
59         pa_context *context;
60 } mm_sound_handle_mgr;
61
62 #define CHECK_HANDLE_RANGE(x) \
63         do { \
64                 if (x == 0) { \
65                         debug_msg("invalid handle(%d)", x); \
66                         return MM_ERROR_INVALID_ARGUMENT; \
67                 } \
68         } while (0);
69
70 #define ATOMIC_INC(l, x) \
71         do { \
72                 pthread_mutex_lock(l); \
73                 x = x + 1; \
74                 if (x == 0) \
75                 x = x + 1; \
76                 pthread_mutex_unlock(l); \
77         } while (0);
78
79 // phandle(ret), GList, userdata, coimpare func
80 #define GET_HANDLE_DATA(p, l, u, func) \
81         do { \
82                 GList* list = 0; \
83                 list = g_list_find_custom(l, u, func); \
84                 if (list != 0) \
85                         p = (mm_sound_handle_t*)list->data; \
86                 else \
87                         p = NULL; \
88         } while (0);
89
90 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT                            1536    /* frames */
91 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE                           4
92 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT                            6
93 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP                                       2
94 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK                           8
95 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE                            12
96 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO                                      10
97
98 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC                     20
99 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC                      25
100 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC                      50
101 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC                     75
102 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC                     20
103
104 __attribute__ ((constructor))
105 void __mm_sound_pa_init(void)
106 {
107         memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
108         mm_sound_handle_mgr.state = FALSE;
109
110         mm_sound_handle_mgr.handles = g_list_alloc();
111         mm_sound_handle_mgr.handle_count = 1;
112         pthread_mutex_init(&mm_sound_handle_mgr.lock, NULL);
113 }
114
115 __attribute__ ((destructor))
116 void __mm_sound_pa_deinit(void)
117 {
118         g_list_free(mm_sound_handle_mgr.handles);
119         pthread_mutex_destroy(&mm_sound_handle_mgr.lock);
120         mm_sound_handle_mgr.handle_count = 0;
121
122         if (mm_sound_handle_mgr.state) {
123                 debug_msg("mainloop(%x),  context(%x)", mm_sound_handle_mgr.mainloop, mm_sound_handle_mgr.context);
124                 pa_threaded_mainloop_stop(mm_sound_handle_mgr.mainloop);
125                 pa_context_disconnect(mm_sound_handle_mgr.context);
126                 pa_context_unref(mm_sound_handle_mgr.context);
127                 pa_threaded_mainloop_free(mm_sound_handle_mgr.mainloop);
128         }
129 }
130
131 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
132 {
133         mm_sound_handle_t *phandle = (mm_sound_handle_t *) a;
134         int *handle = (int *)b;
135
136         if (phandle == NULL)
137                 return -1;
138
139         if (phandle->handle == *handle)
140                 return 0;
141         else
142                 return -1;
143 }
144
145 EXPORT_API
146 int mm_sound_pa_open(MMSoundHandleMode mode, int volume_config, pa_sample_spec * ss, pa_channel_map * channel_map,
147                                         int *size, char *stream_type, int stream_index)
148 {
149         pa_simple *s = NULL;
150         pa_channel_map maps;
151         pa_buffer_attr attr;
152
153         int prop_vol_type = 0;
154         int prop_gain_type = VOLUME_GAIN_DEFAULT;
155
156         int err = MM_ERROR_SOUND_INTERNAL;
157         int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
158         int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
159         int periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
160
161         int handle_mode = mode;
162         int sample_size = 0;
163         pa_proplist *proplist = NULL;
164
165         mm_sound_handle_t *handle = NULL;
166
167         if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
168                 return MM_ERROR_INVALID_ARGUMENT;
169
170         proplist = pa_proplist_new();
171
172         if (channel_map == NULL) {
173                 pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
174                 channel_map = &maps;
175         }
176
177         switch (ss->format) {
178         case PA_SAMPLE_U8:
179                 sample_size = 1 * ss->channels;
180                 break;
181         case PA_SAMPLE_S16LE:
182                 sample_size = 2 * ss->channels;
183                 break;
184         default:
185                 sample_size = 0;
186                 debug_error("Invalid sample size (%d)", sample_size);
187                 break;
188         }
189
190         /* Set volume type of stream */
191         if (volume_config > 0) {
192                 debug_log("setting gain type");
193                 prop_vol_type = 0;      /* not used, set it system(0) temporarily */
194
195                 /* Set gain type of stream */
196                 prop_gain_type = (volume_config >> 8) & 0x000000FF;
197
198                 pa_proplist_setf(proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", prop_gain_type);
199         }
200
201         if (stream_index != -1) {
202                 char stream_index_s[11];
203                 debug_msg("Set stream index [%d]", stream_index);
204
205                 snprintf(stream_index_s, sizeof(stream_index_s) - 1, "%d", stream_index);
206                 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
207                 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
208         }
209         /* Set stream type */
210         pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
211
212         memset(&attr, '\0', sizeof(attr));
213
214         switch (handle_mode) {
215         case HANDLE_MODE_INPUT:
216                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
217                 samples_per_period = (ss->rate * period_time) / 1000;
218                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
219                 attr.prebuf = 0;
220                 attr.minreq = -1;
221                 attr.tlength = -1;
222                 attr.maxlength = -1;
223                 attr.fragsize = samples_per_period * pa_sample_size(ss);
224
225                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr,
226                                                                    proplist, &err);
227                 break;
228
229         case HANDLE_MODE_INPUT_LOW_LATENCY:
230                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC;
231                 samples_per_period = (ss->rate * period_time) / 1000;
232                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
233                 attr.prebuf = 0;
234                 attr.minreq = -1;
235                 attr.tlength = -1;
236                 attr.maxlength = -1;
237                 attr.fragsize = samples_per_period * pa_sample_size(ss);
238
239                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map,
240                                                                    &attr, proplist, &err);
241                 break;
242
243         case HANDLE_MODE_INPUT_HIGH_LATENCY:
244                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
245                 samples_per_period = (ss->rate * period_time) / 1000;
246                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE;
247                 attr.prebuf = 0;
248                 attr.minreq = -1;
249                 attr.tlength = -1;
250                 attr.maxlength = -1;
251                 attr.fragsize = samples_per_period * pa_sample_size(ss);
252
253                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map,
254                                                                    &attr, proplist, &err);
255                 break;
256
257         case HANDLE_MODE_OUTPUT:
258                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
259                 samples_per_period = (ss->rate * period_time) / 1000;
260                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
261                 attr.prebuf = -1;
262                 attr.minreq = -1;
263                 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
264                 attr.maxlength = -1;
265                 attr.fragsize = 0;
266
267                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr,
268                                                                    proplist, &err);
269                 break;
270
271         case HANDLE_MODE_OUTPUT_LOW_LATENCY:
272                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
273                 samples_per_period = (ss->rate * period_time) / 1000;
274                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
275                 attr.prebuf = (ss->rate / 100) * pa_sample_size(ss) * ss->channels;
276                 attr.minreq = -1;
277                 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
278                 attr.maxlength = -1;
279                 attr.fragsize = 0;
280                 debug_msg("rate(%d), samplesize(%d), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
281
282                 debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength,
283                                   attr.maxlength, attr.fragsize);
284
285                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
286                                                                    channel_map, &attr, proplist, &err);
287                 break;
288
289         case HANDLE_MODE_OUTPUT_CLOCK:
290                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
291                 samples_per_period = (ss->rate * period_time) / 1000;
292                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK;
293                 attr.prebuf = -1;
294                 attr.minreq = -1;
295                 attr.tlength = (uint32_t) - 1;
296                 attr.maxlength = -1;
297                 attr.fragsize = 0;
298
299                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss,
300                                                                    channel_map, &attr, proplist, &err);
301                 break;
302
303         case HANDLE_MODE_OUTPUT_VIDEO:  /* low latency playback */
304                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
305                 samples_per_period = (ss->rate * period_time) / 1000;
306                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO;
307                 attr.prebuf = 4 * (samples_per_period * pa_sample_size(ss));
308                 attr.minreq = samples_per_period * pa_sample_size(ss);
309                 attr.tlength = periods_per_buffer * samples_per_period * pa_sample_size(ss);
310                 attr.maxlength = -1;
311                 attr.fragsize = 0;
312
313                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
314                                                                    channel_map, &attr, proplist, &err);
315                 break;
316
317         case HANDLE_MODE_OUTPUT_AP_CALL:
318 #if defined(_MMFW_I386_ALL_SIMULATOR)
319                 debug_msg("Does not support AP call mode at i386 simulator");
320                 s = NULL;
321 #else
322                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
323                 samples_per_period = (ss->rate * period_time) / 1000;
324                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
325                 attr.prebuf = -1;
326                 attr.minreq = pa_usec_to_bytes(20 * PA_USEC_PER_MSEC, ss);
327                 attr.tlength = pa_usec_to_bytes(100 * PA_USEC_PER_MSEC, ss);
328                 attr.maxlength = -1;
329                 attr.fragsize = 0;
330
331                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map,
332                                                                    &attr, proplist, &err);
333 #endif
334                 break;
335         case HANDLE_MODE_INPUT_AP_CALL:
336 #if defined(_MMFW_I386_ALL_SIMULATOR)
337                 debug_msg("Does not support AP call mode at i386 simulator");
338                 s = NULL;
339 #else
340                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
341                 samples_per_period = (ss->rate * period_time) / 1000;
342                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
343                 attr.prebuf = 0;
344                 attr.minreq = -1;
345                 attr.tlength = -1;
346                 attr.maxlength = -1;
347                 attr.fragsize = samples_per_period * pa_sample_size(ss);
348
349                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr,
350                                                                    proplist, &err);
351 #endif
352                 break;
353         default:
354                 err = MM_ERROR_SOUND_INTERNAL;
355                 goto fail;
356                 break;
357         }
358
359         if (!s) {
360                 debug_error("Open pulseaudio handle has failed - %s", pa_strerror(err));
361                 if (!strncmp(pa_strerror(err), "Access denied by security check", strlen(pa_strerror(err))))
362                         err = MM_ERROR_SOUND_PERMISSION_DENIED;
363                 else
364                         err = MM_ERROR_SOUND_INTERNAL;
365                 goto fail;
366         }
367
368         handle = (mm_sound_handle_t *) malloc(sizeof(mm_sound_handle_t));
369         memset(handle, 0, sizeof(mm_sound_handle_t));
370         handle->mode = mode;
371         handle->volume_type = prop_vol_type;
372         handle->gain_type = prop_gain_type;
373         handle->rate = ss->rate;
374         handle->channels = ss->channels;
375         handle->s = s;
376         handle->period = samples_per_period * sample_size;
377         *size = handle->period;
378         handle->handle = mm_sound_handle_mgr.handle_count;
379         ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count);        // 0 is not used
380
381         if (0 > pa_simple_get_stream_index(s, &handle->stream_idx, &err)) {
382                 debug_msg("Can not get stream index %s", pa_strerror(err));
383                 err = MM_ERROR_SOUND_INTERNAL;
384                 goto fail;
385         }
386         mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
387
388         if (handle->handle == 0) {
389                 debug_msg("out of range. handle(%d)", handle->handle);
390                 goto fail;
391         }
392
393         debug_msg
394                 ("created handle[%d]. mode(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), stream_idx(%d), s(%x), source_type(%d)",
395                  handle->handle, handle->mode, handle->volume_type, handle->gain_type, handle->rate, handle->channels, ss->format,
396                  handle->stream_idx, handle->s, handle->source_type);
397
398         if (proplist)
399                 pa_proplist_free(proplist);
400
401         return handle->handle;
402
403  fail:
404         if (proplist)
405                 pa_proplist_free(proplist);
406
407         if (handle)
408                 free(handle);
409
410         return err;
411
412 }
413
414 EXPORT_API
415 int mm_sound_pa_write(const int handle, void *buf, const int size)
416 {
417         mm_sound_handle_t *phandle = NULL;
418         int err = MM_ERROR_NONE;
419
420         if (buf == NULL)
421                 return MM_ERROR_INVALID_ARGUMENT;
422
423         if (size < 0)
424                 return MM_ERROR_INVALID_ARGUMENT;
425         else if (size == 0)
426                 return MM_ERROR_NONE;
427
428         CHECK_HANDLE_RANGE(handle);
429         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
430
431 #ifdef __STREAM_DEBUG__
432         debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d), buf(%p), size(%d)",
433                           phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx.buf, size);
434 #endif
435
436         if (phandle == NULL) {
437                 debug_msg("phandle is null");
438                 return MM_ERROR_SOUND_INTERNAL;
439         }
440
441         if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
442                 debug_error("pa_simple_write() failed with %s", pa_strerror(err));
443                 return MM_ERROR_SOUND_INTERNAL;
444         }
445
446         return size;
447
448 }
449
450 EXPORT_API
451 int mm_sound_pa_close(const int handle)
452 {
453         mm_sound_handle_t *phandle = NULL;
454         int err = MM_ERROR_NONE;
455
456         CHECK_HANDLE_RANGE(handle);
457         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
458         if (phandle == NULL) {
459                 debug_msg("phandle is null");
460                 return MM_ERROR_SOUND_INTERNAL;
461         }
462
463         debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d)",
464                           phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx);
465
466         switch (phandle->mode) {
467         case HANDLE_MODE_OUTPUT:
468         case HANDLE_MODE_OUTPUT_CLOCK:
469         case HANDLE_MODE_OUTPUT_LOW_LATENCY:
470         case HANDLE_MODE_OUTPUT_AP_CALL:
471         case HANDLE_MODE_OUTPUT_VIDEO:
472                 if (0 > pa_simple_flush(phandle->s, &err)) {
473                         err = MM_ERROR_SOUND_INTERNAL;
474                         debug_msg("pa_simple_flush() failed with %s", pa_strerror(err));
475                 }
476                 break;
477         default:
478                 break;
479         }
480
481         pa_simple_free(phandle->s);
482         phandle->s = NULL;
483
484         debug_msg("leave: handle[%d] stream_index[%d]", handle, phandle->stream_idx);
485
486         mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
487         if (phandle != NULL) {
488                 free(phandle);
489                 phandle = NULL;
490         }
491
492         return err;
493
494 }
495
496 EXPORT_API
497 int mm_sound_pa_drain(const int handle)
498 {
499         mm_sound_handle_t *phandle = NULL;
500         int err = MM_ERROR_NONE;
501
502         CHECK_HANDLE_RANGE(handle);
503         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
504         if (phandle == NULL)
505                 return MM_ERROR_SOUND_INTERNAL;
506
507         if (0 > pa_simple_drain(phandle->s, &err)) {
508                 debug_error("pa_simple_drain() failed with %s", pa_strerror(err));
509                 err = MM_ERROR_SOUND_INTERNAL;
510         }
511
512         return err;
513 }