format: Make pa_format_info_snprint() more parseable
[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 const char *pa_encoding_to_string(pa_encoding_t e) {
45     static const char* const table[]= {
46         [PA_ENCODING_PCM] = "pcm",
47         [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
48         [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
49         [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
50         [PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
51         [PA_ENCODING_ANY] = "any",
52     };
53
54     if (e < 0 || e >= PA_ENCODING_MAX)
55         return NULL;
56
57     return table[e];
58 }
59
60 pa_format_info* pa_format_info_new(void) {
61     pa_format_info *f = pa_xnew(pa_format_info, 1);
62
63     f->encoding = PA_ENCODING_INVALID;
64     f->plist = pa_proplist_new();
65
66     return f;
67 }
68
69 pa_format_info* pa_format_info_copy(const pa_format_info *src) {
70     pa_format_info *dest;
71
72     pa_assert(src);
73
74     dest = pa_xnew(pa_format_info, 1);
75
76     dest->encoding = src->encoding;
77
78     if (src->plist)
79         dest->plist = pa_proplist_copy(src->plist);
80     else
81         dest->plist = NULL;
82
83     return dest;
84 }
85
86 void pa_format_info_free(pa_format_info *f) {
87     pa_assert(f);
88
89     pa_proplist_free(f->plist);
90     pa_xfree(f);
91 }
92
93 void pa_format_info_free2(pa_format_info *f, void *userdata) {
94     pa_format_info_free(f);
95 }
96
97 int pa_format_info_valid(const pa_format_info *f) {
98     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
99 }
100
101 int pa_format_info_is_pcm(const pa_format_info *f) {
102     return f->encoding == PA_ENCODING_PCM;
103 }
104
105 char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
106     char *tmp;
107
108     pa_assert(s);
109     pa_assert(l > 0);
110     pa_assert(f);
111
112     pa_init_i18n();
113
114     if (!pa_format_info_valid(f))
115         pa_snprintf(s, l, _("(invalid)"));
116     else {
117         tmp = pa_proplist_to_string_sep(f->plist, "  ");
118         if (tmp[0])
119             pa_snprintf(s, l, "%s, %s", pa_encoding_to_string(f->encoding), tmp);
120         else
121             pa_snprintf(s, l, "%s", pa_encoding_to_string(f->encoding));
122         pa_xfree(tmp);
123     }
124
125     return s;
126 }
127
128 int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
129     const char *key;
130     void *state = NULL;
131
132     pa_assert(first);
133     pa_assert(second);
134
135     if (first->encoding != second->encoding)
136         return FALSE;
137
138     while ((key = pa_proplist_iterate(first->plist, &state))) {
139         const char *value_one, *value_two;
140
141         value_one = pa_proplist_gets(first->plist, key);
142         value_two = pa_proplist_gets(second->plist, key);
143
144         if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
145             return FALSE;
146     }
147
148     return TRUE;
149 }
150
151 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
152     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
153     pa_format_info *f;
154
155     pa_assert(ss && pa_sample_spec_valid(ss));
156     pa_assert(!map || pa_channel_map_valid(map));
157
158     f = pa_format_info_new();
159     f->encoding = PA_ENCODING_PCM;
160
161     pa_format_info_set_sample_format(f, ss->format);
162     pa_format_info_set_rate(f, ss->rate);
163     pa_format_info_set_channels(f, ss->channels);
164
165     if (map) {
166         pa_channel_map_snprint(cm, sizeof(cm), map);
167         pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
168     }
169
170     return f;
171 }
172
173 /* For PCM streams */
174 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
175     char *sf = NULL, *m = NULL;
176     int rate, channels;
177     pa_bool_t ret = FALSE;
178
179     pa_assert(f);
180     pa_assert(ss);
181     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
182
183     if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
184         goto out;
185     if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
186         goto out;
187     if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
188         goto out;
189
190     if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
191         goto out;
192
193     ss->rate = (uint32_t) rate;
194     ss->channels = (uint8_t) channels;
195
196     if (map) {
197         pa_channel_map_init(map);
198
199         if (pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m))
200             if (pa_channel_map_parse(map, m) == NULL)
201                 goto out;
202     }
203
204     ret = TRUE;
205
206 out:
207     if (sf)
208         pa_xfree(sf);
209     if (m)
210         pa_xfree(m);
211
212     return ret;
213 }
214
215 /* For compressed streams */
216 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
217     int rate;
218
219     pa_assert(f);
220     pa_assert(ss);
221     pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, FALSE);
222
223     ss->format = PA_SAMPLE_S16LE;
224     ss->channels = 2;
225
226     pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE);
227     ss->rate = (uint32_t) rate;
228
229     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
230         ss->rate *= 4;
231
232     return TRUE;
233 }
234
235 void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
236     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
237 }
238
239 void pa_format_info_set_rate(pa_format_info *f, int rate) {
240     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
241 }
242
243 void pa_format_info_set_channels(pa_format_info *f, int channels) {
244     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
245 }
246
247 void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
248     char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
249
250     pa_channel_map_snprint(map_str, sizeof(map_str), map);
251
252     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
253 }
254
255 pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
256     const char *str;
257     json_object *o;
258
259     pa_assert(f);
260     pa_assert(key);
261     pa_assert(v);
262
263     pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
264     o = json_tokener_parse(str);
265     pa_return_val_if_fail(!is_error(o), FALSE);
266     if (json_object_get_type(o) != json_type_int) {
267         json_object_put(o);
268         return FALSE;
269     }
270
271     *v = json_object_get_int(o);
272     json_object_put(o);
273
274     return TRUE;
275 }
276
277 pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
278     const char *str = NULL;
279     json_object *o;
280
281     pa_assert(f);
282     pa_assert(key);
283     pa_assert(v);
284
285     str = pa_proplist_gets(f->plist, key), FALSE;
286     if (!str)
287         return FALSE;
288
289     o = json_tokener_parse(str);
290     pa_return_val_if_fail(!is_error(o), FALSE);
291     if (json_object_get_type(o) != json_type_string) {
292         json_object_put(o);
293         return FALSE;
294     }
295
296     *v = pa_xstrdup(json_object_get_string(o));
297     json_object_put(o);
298
299     return TRUE;
300 }
301
302 void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
303     json_object *o;
304
305     pa_assert(f);
306     pa_assert(key);
307
308     o = json_object_new_int(value);
309
310     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
311
312     json_object_put(o);
313 }
314
315 void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
316     json_object *o;
317     int i;
318
319     pa_assert(f);
320     pa_assert(key);
321
322     o = json_object_new_array();
323
324     for (i = 0; i < n_values; i++)
325         json_object_array_add(o, json_object_new_int(values[i]));
326
327     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
328
329     json_object_put(o);
330 }
331
332 void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
333     json_object *o;
334
335     pa_assert(f);
336     pa_assert(key);
337
338     o = json_object_new_object();
339
340     json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
341     json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
342
343     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
344
345     json_object_put(o);
346 }
347
348 void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
349     json_object *o;
350
351     pa_assert(f);
352     pa_assert(key);
353
354     o = json_object_new_string(value);
355
356     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
357
358     json_object_put(o);
359 }
360
361 void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
362     json_object *o;
363     int i;
364
365     pa_assert(f);
366     pa_assert(key);
367
368     o = json_object_new_array();
369
370     for (i = 0; i < n_values; i++)
371         json_object_array_add(o, json_object_new_string(values[i]));
372
373     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
374
375     json_object_put(o);
376 }
377
378 static pa_bool_t pa_json_is_fixed_type(json_object *o)
379 {
380     switch(json_object_get_type(o)) {
381         case json_type_object:
382         case json_type_array:
383             return FALSE;
384
385         default:
386             return TRUE;
387     }
388 }
389
390 static int pa_json_value_equal(json_object *o1, json_object *o2) {
391     return (json_object_get_type(o1) == json_object_get_type(o2)) &&
392         pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
393 }
394
395 static int pa_format_info_prop_compatible(const char *one, const char *two) {
396     json_object *o1 = NULL, *o2 = NULL;
397     int i, ret = 0;
398
399     o1 = json_tokener_parse(one);
400     if (is_error(o1))
401         goto out;
402
403     o2 = json_tokener_parse(two);
404     if (is_error(o2))
405         goto out;
406
407     /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
408     pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
409
410     if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
411         ret = pa_json_value_equal(o1, o2);
412         goto out;
413     }
414
415     if (pa_json_is_fixed_type(o1)) {
416         json_object *tmp = o2;
417         o2 = o1;
418         o1 = tmp;
419     }
420
421     /* o2 is now a fixed type, and o1 is not */
422
423     if (json_object_get_type(o1) == json_type_array) {
424         for (i = 0; i < json_object_array_length(o1); i++) {
425             if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
426                 ret = 1;
427                 break;
428             }
429         }
430     } else if (json_object_get_type(o1) == json_type_object) {
431         /* o1 should be a range type */
432         int min, max, v;
433         json_object *o_min = NULL, *o_max = NULL;
434
435         if (json_object_get_type(o2) != json_type_int) {
436             /* We don't support non-integer ranges */
437             goto out;
438         }
439
440         o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
441         if (!o_min || json_object_get_type(o_min) != json_type_int)
442             goto out;
443
444         o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
445         if (!o_max || json_object_get_type(o_max) != json_type_int)
446             goto out;
447
448         v = json_object_get_int(o2);
449         min = json_object_get_int(o_min);
450         max = json_object_get_int(o_max);
451
452         ret = v >= min && v <= max;
453     } else {
454         pa_log_warn("Got a format type that we don't support");
455     }
456
457 out:
458     if (o1)
459         json_object_put(o1);
460     if (o2)
461         json_object_put(o2);
462
463     return ret;
464 }