format: Expose pa_format_info<->pa_sample_spec conversion functions
[platform/upstream/pulseaudio.git] / src / pulse / format.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2011 Intel Corporation
5   Copyright 2011 Collabora Multimedia
6   Copyright 2011 Arun Raghavan <arun.raghavan@collabora.co.uk>
7
8   PulseAudio is free software; you can redistribute it and/or modify
9   it under the terms of the GNU Lesser General Public License as published
10   by the Free Software Foundation; either version 2.1 of the License,
11   or (at your option) any later version.
12
13   PulseAudio is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with PulseAudio; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21   USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <json.h>
29
30 #include <pulse/internal.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/i18n.h>
35 #include <pulsecore/macro.h>
36
37 #include "format.h"
38
39 #define PA_JSON_MIN_KEY "min"
40 #define PA_JSON_MAX_KEY "max"
41
42 static int pa_format_info_prop_compatible(const char *one, const char *two);
43
44 static const char* const _encoding_str_table[]= {
45     [PA_ENCODING_PCM] = "pcm",
46     [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
47     [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
48     [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
49     [PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
50     [PA_ENCODING_ANY] = "any",
51 };
52
53 const char *pa_encoding_to_string(pa_encoding_t e) {
54     if (e < 0 || e >= PA_ENCODING_MAX)
55         return NULL;
56
57     return _encoding_str_table[e];
58 }
59
60 pa_encoding_t pa_encoding_from_string(const char *encoding) {
61     pa_encoding_t e;
62
63     for (e = PA_ENCODING_ANY; e < PA_ENCODING_MAX; e++)
64         if (pa_streq(_encoding_str_table[e], encoding))
65             return e;
66
67     return PA_ENCODING_INVALID;
68 }
69
70 pa_format_info* pa_format_info_new(void) {
71     pa_format_info *f = pa_xnew(pa_format_info, 1);
72
73     f->encoding = PA_ENCODING_INVALID;
74     f->plist = pa_proplist_new();
75
76     return f;
77 }
78
79 pa_format_info* pa_format_info_copy(const pa_format_info *src) {
80     pa_format_info *dest;
81
82     pa_assert(src);
83
84     dest = pa_xnew(pa_format_info, 1);
85
86     dest->encoding = src->encoding;
87
88     if (src->plist)
89         dest->plist = pa_proplist_copy(src->plist);
90     else
91         dest->plist = NULL;
92
93     return dest;
94 }
95
96 void pa_format_info_free(pa_format_info *f) {
97     pa_assert(f);
98
99     pa_proplist_free(f->plist);
100     pa_xfree(f);
101 }
102
103 void pa_format_info_free2(pa_format_info *f, void *userdata) {
104     pa_format_info_free(f);
105 }
106
107 int pa_format_info_valid(const pa_format_info *f) {
108     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
109 }
110
111 int pa_format_info_is_pcm(const pa_format_info *f) {
112     return f->encoding == PA_ENCODING_PCM;
113 }
114
115 char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
116     char *tmp;
117
118     pa_assert(s);
119     pa_assert(l > 0);
120     pa_assert(f);
121
122     pa_init_i18n();
123
124     if (!pa_format_info_valid(f))
125         pa_snprintf(s, l, _("(invalid)"));
126     else {
127         tmp = pa_proplist_to_string_sep(f->plist, "  ");
128         if (tmp[0])
129             pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp);
130         else
131             pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding));
132         pa_xfree(tmp);
133     }
134
135     return s;
136 }
137
138 pa_format_info* pa_format_info_from_string(const char *str) {
139     pa_format_info *f = pa_format_info_new();
140     char *encoding = NULL, *properties = NULL;
141     size_t pos;
142
143     pos = strcspn(str, ",");
144
145     encoding = pa_xstrndup(str, pos);
146     f->encoding = pa_encoding_from_string(pa_strip(encoding));
147     if (f->encoding == PA_ENCODING_INVALID)
148         goto error;
149
150     if (pos != strlen(str)) {
151         pa_proplist *plist;
152
153         properties = pa_xstrdup(&str[pos+1]);
154         plist = pa_proplist_from_string(properties);
155
156         if (!plist)
157             goto error;
158
159         pa_proplist_free(f->plist);
160         f->plist = plist;
161     }
162
163 out:
164     if (encoding)
165         pa_xfree(encoding);
166     if (properties)
167         pa_xfree(properties);
168     return f;
169
170 error:
171     pa_format_info_free(f);
172     f = NULL;
173     goto out;
174 }
175
176 int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
177     const char *key;
178     void *state = NULL;
179
180     pa_assert(first);
181     pa_assert(second);
182
183     if (first->encoding != second->encoding)
184         return FALSE;
185
186     while ((key = pa_proplist_iterate(first->plist, &state))) {
187         const char *value_one, *value_two;
188
189         value_one = pa_proplist_gets(first->plist, key);
190         value_two = pa_proplist_gets(second->plist, key);
191
192         if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
193             return FALSE;
194     }
195
196     return TRUE;
197 }
198
199 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
200     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
201     pa_format_info *f;
202
203     pa_assert(ss && pa_sample_spec_valid(ss));
204     pa_assert(!map || pa_channel_map_valid(map));
205
206     f = pa_format_info_new();
207     f->encoding = PA_ENCODING_PCM;
208
209     pa_format_info_set_sample_format(f, ss->format);
210     pa_format_info_set_rate(f, ss->rate);
211     pa_format_info_set_channels(f, ss->channels);
212
213     if (map) {
214         pa_channel_map_snprint(cm, sizeof(cm), map);
215         pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
216     }
217
218     return f;
219 }
220
221 /* For PCM streams */
222 int pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
223     char *sf = NULL, *m = NULL;
224     int rate, channels;
225     int ret = -PA_ERR_INVALID;
226
227     pa_assert(f);
228     pa_assert(ss);
229     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
230
231     if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
232         goto out;
233     if (pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
234         goto out;
235     if (pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
236         goto out;
237
238     if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
239         goto out;
240
241     ss->rate = (uint32_t) rate;
242     ss->channels = (uint8_t) channels;
243
244     if (map) {
245         pa_channel_map_init(map);
246
247         if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m) == 0)
248             if (pa_channel_map_parse(map, m) == NULL)
249                 goto out;
250     }
251
252     ret = 0;
253
254 out:
255     if (sf)
256         pa_xfree(sf);
257     if (m)
258         pa_xfree(m);
259
260     return ret;
261 }
262
263 /* For compressed streams */
264 int pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
265     int rate;
266
267     pa_assert(f);
268     pa_assert(ss);
269     pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, -PA_ERR_INVALID);
270
271     ss->format = PA_SAMPLE_S16LE;
272     ss->channels = 2;
273
274     pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate) == 0, -PA_ERR_INVALID);
275     ss->rate = (uint32_t) rate;
276
277     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
278         ss->rate *= 4;
279
280     return 0;
281 }
282
283 void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
284     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
285 }
286
287 void pa_format_info_set_rate(pa_format_info *f, int rate) {
288     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
289 }
290
291 void pa_format_info_set_channels(pa_format_info *f, int channels) {
292     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
293 }
294
295 void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
296     char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
297
298     pa_channel_map_snprint(map_str, sizeof(map_str), map);
299
300     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
301 }
302
303 int pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
304     const char *str;
305     json_object *o;
306
307     pa_assert(f);
308     pa_assert(key);
309     pa_assert(v);
310
311     str = pa_proplist_gets(f->plist, key);
312     if (!str)
313         return -PA_ERR_NOENTITY;
314
315     o = json_tokener_parse(str);
316     if (is_error(o))
317         return -PA_ERR_INVALID;
318
319     if (json_object_get_type(o) != json_type_int) {
320         json_object_put(o);
321         return -PA_ERR_INVALID;
322     }
323
324     *v = json_object_get_int(o);
325     json_object_put(o);
326
327     return 0;
328 }
329
330 int pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
331     const char *str = NULL;
332     json_object *o;
333
334     pa_assert(f);
335     pa_assert(key);
336     pa_assert(v);
337
338     str = pa_proplist_gets(f->plist, key);
339     if (!str)
340         return -PA_ERR_NOENTITY;
341
342     o = json_tokener_parse(str);
343     if (is_error(o))
344         return -PA_ERR_INVALID;
345
346     if (json_object_get_type(o) != json_type_string) {
347         json_object_put(o);
348         return -PA_ERR_INVALID;
349     }
350
351     *v = pa_xstrdup(json_object_get_string(o));
352     json_object_put(o);
353
354     return 0;
355 }
356
357 void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
358     json_object *o;
359
360     pa_assert(f);
361     pa_assert(key);
362
363     o = json_object_new_int(value);
364
365     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
366
367     json_object_put(o);
368 }
369
370 void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
371     json_object *o;
372     int i;
373
374     pa_assert(f);
375     pa_assert(key);
376
377     o = json_object_new_array();
378
379     for (i = 0; i < n_values; i++)
380         json_object_array_add(o, json_object_new_int(values[i]));
381
382     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
383
384     json_object_put(o);
385 }
386
387 void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
388     json_object *o;
389
390     pa_assert(f);
391     pa_assert(key);
392
393     o = json_object_new_object();
394
395     json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
396     json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
397
398     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
399
400     json_object_put(o);
401 }
402
403 void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
404     json_object *o;
405
406     pa_assert(f);
407     pa_assert(key);
408
409     o = json_object_new_string(value);
410
411     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
412
413     json_object_put(o);
414 }
415
416 void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
417     json_object *o;
418     int i;
419
420     pa_assert(f);
421     pa_assert(key);
422
423     o = json_object_new_array();
424
425     for (i = 0; i < n_values; i++)
426         json_object_array_add(o, json_object_new_string(values[i]));
427
428     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
429
430     json_object_put(o);
431 }
432
433 static pa_bool_t pa_json_is_fixed_type(json_object *o)
434 {
435     switch(json_object_get_type(o)) {
436         case json_type_object:
437         case json_type_array:
438             return FALSE;
439
440         default:
441             return TRUE;
442     }
443 }
444
445 static int pa_json_value_equal(json_object *o1, json_object *o2) {
446     return (json_object_get_type(o1) == json_object_get_type(o2)) &&
447         pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
448 }
449
450 static int pa_format_info_prop_compatible(const char *one, const char *two) {
451     json_object *o1 = NULL, *o2 = NULL;
452     int i, ret = 0;
453
454     o1 = json_tokener_parse(one);
455     if (is_error(o1))
456         goto out;
457
458     o2 = json_tokener_parse(two);
459     if (is_error(o2))
460         goto out;
461
462     /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
463     pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
464
465     if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
466         ret = pa_json_value_equal(o1, o2);
467         goto out;
468     }
469
470     if (pa_json_is_fixed_type(o1)) {
471         json_object *tmp = o2;
472         o2 = o1;
473         o1 = tmp;
474     }
475
476     /* o2 is now a fixed type, and o1 is not */
477
478     if (json_object_get_type(o1) == json_type_array) {
479         for (i = 0; i < json_object_array_length(o1); i++) {
480             if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
481                 ret = 1;
482                 break;
483             }
484         }
485     } else if (json_object_get_type(o1) == json_type_object) {
486         /* o1 should be a range type */
487         int min, max, v;
488         json_object *o_min = NULL, *o_max = NULL;
489
490         if (json_object_get_type(o2) != json_type_int) {
491             /* We don't support non-integer ranges */
492             goto out;
493         }
494
495         o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
496         if (!o_min || json_object_get_type(o_min) != json_type_int)
497             goto out;
498
499         o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
500         if (!o_max || json_object_get_type(o_max) != json_type_int)
501             goto out;
502
503         v = json_object_get_int(o2);
504         min = json_object_get_int(o_min);
505         max = json_object_get_int(o_max);
506
507         ret = v >= min && v <= max;
508     } else {
509         pa_log_warn("Got a format type that we don't support");
510     }
511
512 out:
513     if (o1)
514         json_object_put(o1);
515     if (o2)
516         json_object_put(o2);
517
518     return ret;
519 }