Fix invalid format string
[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         int source_type;
49 } mm_sound_handle_t;
50
51 static struct {
52         uint32_t handle_count;  /* use amotic operations */
53         GList *handles;
54         pthread_mutex_t lock;
55
56         int state;
57         pa_threaded_mainloop *mainloop;
58         pa_context *context;
59 } mm_sound_handle_mgr;
60
61 #define CHECK_HANDLE_RANGE(x) \
62         do { \
63                 if (x == 0) { \
64                         debug_msg("invalid handle(%d)", x); \
65                         return MM_ERROR_INVALID_ARGUMENT; \
66                 } \
67         } while (0);
68
69 #define ATOMIC_INC(l, x) \
70         do { \
71                 pthread_mutex_lock(l); \
72                 x = x + 1; \
73                 if (x == 0) \
74                 x = x + 1; \
75                 pthread_mutex_unlock(l); \
76         } while (0);
77
78 // phandle(ret), GList, userdata, coimpare func
79 #define GET_HANDLE_DATA(p, l, u, func) \
80         do { \
81                 GList* list = 0; \
82                 list = g_list_find_custom(l, u, func); \
83                 if (list != 0) \
84                         p = (mm_sound_handle_t*)list->data; \
85                 else \
86                         p = NULL; \
87         } while (0);
88
89 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT                            1536    /* frames */
90 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE                           4
91 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT                            6
92 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP                                       2
93 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK                           8
94 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE                            12
95 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO                                      10
96
97 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC                     20
98 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC                      25
99 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC                      50
100 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC                     75
101 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC                     20
102
103 __attribute__ ((constructor))
104 void __mm_sound_pa_init(void)
105 {
106         memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
107         mm_sound_handle_mgr.state = FALSE;
108
109         mm_sound_handle_mgr.handles = g_list_alloc();
110         mm_sound_handle_mgr.handle_count = 1;
111         pthread_mutex_init(&mm_sound_handle_mgr.lock, NULL);
112 }
113
114 __attribute__ ((destructor))
115 void __mm_sound_pa_deinit(void)
116 {
117         g_list_free(mm_sound_handle_mgr.handles);
118         pthread_mutex_destroy(&mm_sound_handle_mgr.lock);
119         mm_sound_handle_mgr.handle_count = 0;
120
121         if (mm_sound_handle_mgr.state) {
122                 debug_msg("mainloop(%p),  context(%p)", mm_sound_handle_mgr.mainloop, mm_sound_handle_mgr.context);
123                 pa_threaded_mainloop_stop(mm_sound_handle_mgr.mainloop);
124                 pa_context_disconnect(mm_sound_handle_mgr.context);
125                 pa_context_unref(mm_sound_handle_mgr.context);
126                 pa_threaded_mainloop_free(mm_sound_handle_mgr.mainloop);
127         }
128 }
129
130 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
131 {
132         mm_sound_handle_t *phandle = (mm_sound_handle_t *) a;
133         int *handle = (int *)b;
134
135         if (phandle == NULL)
136                 return -1;
137
138         if (phandle->handle == *handle)
139                 return 0;
140         else
141                 return -1;
142 }
143
144 EXPORT_API
145 int mm_sound_pa_open(MMSoundHandleMode mode, int volume_config, pa_sample_spec * ss, pa_channel_map * channel_map,
146                                         int *size, char *stream_type, int stream_index)
147 {
148         pa_simple *s = NULL;
149         pa_channel_map maps;
150         pa_buffer_attr attr;
151
152         int prop_vol_type = 0;
153         int prop_gain_type = VOLUME_GAIN_DEFAULT;
154
155         int err = MM_ERROR_SOUND_INTERNAL;
156         int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
157         int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
158         int periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
159
160         int handle_mode = mode;
161         int sample_size = 0;
162         pa_proplist *proplist = NULL;
163
164         mm_sound_handle_t *handle = NULL;
165
166         if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
167                 return MM_ERROR_INVALID_ARGUMENT;
168
169         proplist = pa_proplist_new();
170
171         if (channel_map == NULL) {
172                 pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
173                 channel_map = &maps;
174         }
175
176         switch (ss->format) {
177         case PA_SAMPLE_U8:
178                 sample_size = 1 * ss->channels;
179                 break;
180         case PA_SAMPLE_S16LE:
181                 sample_size = 2 * ss->channels;
182                 break;
183         default:
184                 sample_size = 0;
185                 debug_error("Invalid sample size (%d)", sample_size);
186                 break;
187         }
188
189         if (stream_index != -1) {
190                 char stream_index_s[11];
191                 debug_msg("Set stream index [%d]", stream_index);
192
193                 snprintf(stream_index_s, sizeof(stream_index_s) - 1, "%d", stream_index);
194                 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
195                 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
196         }
197         /* Set stream type */
198         pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
199
200         memset(&attr, '\0', sizeof(attr));
201
202         switch (handle_mode) {
203         case HANDLE_MODE_INPUT:
204                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
205                 samples_per_period = (ss->rate * period_time) / 1000;
206                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
207                 attr.prebuf = 0;
208                 attr.minreq = -1;
209                 attr.tlength = -1;
210                 attr.maxlength = -1;
211                 attr.fragsize = samples_per_period * pa_sample_size(ss);
212
213                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr,
214                                                                    proplist, &err);
215                 break;
216
217         case HANDLE_MODE_INPUT_LOW_LATENCY:
218                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC;
219                 samples_per_period = (ss->rate * period_time) / 1000;
220                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
221                 attr.prebuf = 0;
222                 attr.minreq = -1;
223                 attr.tlength = -1;
224                 attr.maxlength = -1;
225                 attr.fragsize = samples_per_period * pa_sample_size(ss);
226
227                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map,
228                                                                    &attr, proplist, &err);
229                 break;
230
231         case HANDLE_MODE_INPUT_HIGH_LATENCY:
232                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
233                 samples_per_period = (ss->rate * period_time) / 1000;
234                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE;
235                 attr.prebuf = 0;
236                 attr.minreq = -1;
237                 attr.tlength = -1;
238                 attr.maxlength = -1;
239                 attr.fragsize = samples_per_period * pa_sample_size(ss);
240
241                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map,
242                                                                    &attr, proplist, &err);
243                 break;
244
245         case HANDLE_MODE_OUTPUT:
246                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
247                 samples_per_period = (ss->rate * period_time) / 1000;
248                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
249                 attr.prebuf = -1;
250                 attr.minreq = -1;
251                 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
252                 attr.maxlength = -1;
253                 attr.fragsize = 0;
254
255                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr,
256                                                                    proplist, &err);
257                 break;
258
259         case HANDLE_MODE_OUTPUT_LOW_LATENCY:
260                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
261                 samples_per_period = (ss->rate * period_time) / 1000;
262                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
263                 attr.prebuf = (ss->rate / 100) * pa_sample_size(ss) * ss->channels;
264                 attr.minreq = -1;
265                 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
266                 attr.maxlength = -1;
267                 attr.fragsize = 0;
268                 debug_msg("rate(%d), samplesize(%zu), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
269
270                 debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength,
271                                   attr.maxlength, attr.fragsize);
272
273                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
274                                                                    channel_map, &attr, proplist, &err);
275                 break;
276
277         case HANDLE_MODE_OUTPUT_CLOCK:
278                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
279                 samples_per_period = (ss->rate * period_time) / 1000;
280                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK;
281                 attr.prebuf = -1;
282                 attr.minreq = -1;
283                 attr.tlength = (uint32_t) - 1;
284                 attr.maxlength = -1;
285                 attr.fragsize = 0;
286
287                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss,
288                                                                    channel_map, &attr, proplist, &err);
289                 break;
290
291         case HANDLE_MODE_OUTPUT_VIDEO:  /* low latency playback */
292                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
293                 samples_per_period = (ss->rate * period_time) / 1000;
294                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO;
295                 attr.prebuf = 4 * (samples_per_period * pa_sample_size(ss));
296                 attr.minreq = samples_per_period * pa_sample_size(ss);
297                 attr.tlength = periods_per_buffer * samples_per_period * pa_sample_size(ss);
298                 attr.maxlength = -1;
299                 attr.fragsize = 0;
300
301                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
302                                                                    channel_map, &attr, proplist, &err);
303                 break;
304
305         case HANDLE_MODE_OUTPUT_AP_CALL:
306 #if defined(_MMFW_I386_ALL_SIMULATOR)
307                 debug_msg("Does not support AP call mode at i386 simulator");
308                 s = NULL;
309 #else
310                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
311                 samples_per_period = (ss->rate * period_time) / 1000;
312                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
313                 attr.prebuf = -1;
314                 attr.minreq = pa_usec_to_bytes(20 * PA_USEC_PER_MSEC, ss);
315                 attr.tlength = pa_usec_to_bytes(100 * PA_USEC_PER_MSEC, ss);
316                 attr.maxlength = -1;
317                 attr.fragsize = 0;
318
319                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map,
320                                                                    &attr, proplist, &err);
321 #endif
322                 break;
323         case HANDLE_MODE_INPUT_AP_CALL:
324 #if defined(_MMFW_I386_ALL_SIMULATOR)
325                 debug_msg("Does not support AP call mode at i386 simulator");
326                 s = NULL;
327 #else
328                 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
329                 samples_per_period = (ss->rate * period_time) / 1000;
330                 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
331                 attr.prebuf = 0;
332                 attr.minreq = -1;
333                 attr.tlength = -1;
334                 attr.maxlength = -1;
335                 attr.fragsize = samples_per_period * pa_sample_size(ss);
336
337                 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr,
338                                                                    proplist, &err);
339 #endif
340                 break;
341         default:
342                 err = MM_ERROR_SOUND_INTERNAL;
343                 goto fail;
344                 break;
345         }
346
347         if (!s) {
348                 debug_error("Open pulseaudio handle has failed - %s", pa_strerror(err));
349                 if (!strncmp(pa_strerror(err), "Access denied by security check", strlen(pa_strerror(err))))
350                         err = MM_ERROR_SOUND_PERMISSION_DENIED;
351                 else
352                         err = MM_ERROR_SOUND_INTERNAL;
353                 goto fail;
354         }
355
356         handle = (mm_sound_handle_t *) malloc(sizeof(mm_sound_handle_t));
357         memset(handle, 0, sizeof(mm_sound_handle_t));
358         handle->mode = mode;
359         handle->volume_type = prop_vol_type;
360         handle->gain_type = prop_gain_type;
361         handle->rate = ss->rate;
362         handle->channels = ss->channels;
363         handle->s = s;
364         handle->period = samples_per_period * sample_size;
365         *size = handle->period;
366         handle->handle = mm_sound_handle_mgr.handle_count;
367         ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count);        // 0 is not used
368
369         mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
370
371         if (handle->handle == 0) {
372                 debug_msg("out of range. handle(%d)", handle->handle);
373                 goto fail;
374         }
375
376         debug_msg
377                 ("created handle[%d]. mode(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), s(%p), source_type(%d)",
378                  handle->handle, handle->mode, handle->volume_type, handle->gain_type, handle->rate, handle->channels, ss->format,
379                  handle->s, handle->source_type);
380
381         if (proplist)
382                 pa_proplist_free(proplist);
383
384         return handle->handle;
385
386  fail:
387         if (proplist)
388                 pa_proplist_free(proplist);
389
390         if (handle)
391                 free(handle);
392
393         return err;
394
395 }
396
397 EXPORT_API
398 int mm_sound_pa_write(const int handle, void *buf, const int size)
399 {
400         mm_sound_handle_t *phandle = NULL;
401         int err = MM_ERROR_NONE;
402
403         if (buf == NULL)
404                 return MM_ERROR_INVALID_ARGUMENT;
405
406         if (size < 0)
407                 return MM_ERROR_INVALID_ARGUMENT;
408         else if (size == 0)
409                 return MM_ERROR_NONE;
410
411         CHECK_HANDLE_RANGE(handle);
412         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
413
414         if (phandle == NULL) {
415                 debug_msg("phandle is null");
416                 return MM_ERROR_SOUND_INTERNAL;
417         }
418
419         if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
420                 debug_error("pa_simple_write() failed with %s", pa_strerror(err));
421                 return MM_ERROR_SOUND_INTERNAL;
422         }
423
424         return size;
425
426 }
427
428 EXPORT_API
429 int mm_sound_pa_close(const int handle)
430 {
431         mm_sound_handle_t *phandle = NULL;
432         int err = MM_ERROR_NONE;
433
434         CHECK_HANDLE_RANGE(handle);
435         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
436         if (phandle == NULL) {
437                 debug_msg("phandle is null");
438                 return MM_ERROR_SOUND_INTERNAL;
439         }
440
441         debug_msg("phandle(%p) s(%p), handle(%d), rate(%d), ch(%d)",
442                           phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels);
443
444         switch (phandle->mode) {
445         case HANDLE_MODE_OUTPUT:
446         case HANDLE_MODE_OUTPUT_CLOCK:
447         case HANDLE_MODE_OUTPUT_LOW_LATENCY:
448         case HANDLE_MODE_OUTPUT_AP_CALL:
449         case HANDLE_MODE_OUTPUT_VIDEO:
450                 if (0 > pa_simple_flush(phandle->s, &err)) {
451                         err = MM_ERROR_SOUND_INTERNAL;
452                         debug_msg("pa_simple_flush() failed with %s", pa_strerror(err));
453                 }
454                 break;
455         default:
456                 break;
457         }
458
459         pa_simple_free(phandle->s);
460         phandle->s = NULL;
461
462         debug_msg("leave: handle[%d]", handle);
463
464         mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
465         if (phandle != NULL) {
466                 free(phandle);
467                 phandle = NULL;
468         }
469
470         return err;
471
472 }
473
474 EXPORT_API
475 int mm_sound_pa_drain(const int handle)
476 {
477         mm_sound_handle_t *phandle = NULL;
478         int err = MM_ERROR_NONE;
479
480         CHECK_HANDLE_RANGE(handle);
481         GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
482         if (phandle == NULL)
483                 return MM_ERROR_SOUND_INTERNAL;
484
485         if (0 > pa_simple_drain(phandle->s, &err)) {
486                 debug_error("pa_simple_drain() failed with %s", pa_strerror(err));
487                 err = MM_ERROR_SOUND_INTERNAL;
488         }
489
490         return err;
491 }