format: Extend properties to handle lists/ranges
[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 #include <pulse/i18n.h>
33
34 #include <pulsecore/core-util.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         pa_snprintf(s, l, _("%s, %s"), pa_encoding_to_string(f->encoding), tmp[0] ? tmp : _("(no properties)"));
119         pa_xfree(tmp);
120     }
121
122     return s;
123 }
124
125 int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
126     const char *key;
127     void *state = NULL;
128
129     pa_assert(first);
130     pa_assert(second);
131
132     if (first->encoding != second->encoding)
133         return FALSE;
134
135     while ((key = pa_proplist_iterate(first->plist, &state))) {
136         const char *value_one, *value_two;
137
138         value_one = pa_proplist_gets(first->plist, key);
139         value_two = pa_proplist_gets(second->plist, key);
140
141         if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
142             return FALSE;
143     }
144
145     return TRUE;
146 }
147
148 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
149     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
150     pa_format_info *f;
151
152     pa_assert(ss && pa_sample_spec_valid(ss));
153     pa_assert(!map || pa_channel_map_valid(map));
154
155     f = pa_format_info_new();
156     f->encoding = PA_ENCODING_PCM;
157
158     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
159     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate);
160     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels);
161
162     if (map) {
163         pa_channel_map_snprint(cm, sizeof(cm), map);
164         pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
165     }
166
167     return f;
168 }
169
170 /* For PCM streams */
171 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
172     char *sf = NULL, *m = NULL;
173     int rate, channels;
174     pa_bool_t ret = FALSE;
175
176     pa_assert(f);
177     pa_assert(ss);
178     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
179
180     if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
181         goto out;
182     if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
183         goto out;
184     if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
185         goto out;
186
187     if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
188         goto out;
189
190     ss->rate = (uint32_t) rate;
191     ss->channels = (uint8_t) channels;
192
193     if (map) {
194         pa_channel_map_init(map);
195
196         if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m))
197             goto out;
198
199         if (m && pa_channel_map_parse(map, m) == NULL)
200             goto out;
201     }
202
203     ret = TRUE;
204
205 out:
206     if (sf)
207         pa_xfree(sf);
208     if (m)
209         pa_xfree(m);
210
211     return ret;
212 }
213
214 /* For compressed streams */
215 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
216     int rate;
217
218     pa_assert(f);
219     pa_assert(ss);
220     pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, FALSE);
221
222     ss->format = PA_SAMPLE_S16LE;
223     ss->channels = 2;
224
225     pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE);
226     ss->rate = (uint32_t) rate;
227
228     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
229         ss->rate *= 4;
230
231     return TRUE;
232 }
233
234 pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
235     const char *str;
236     json_object *o;
237
238     pa_assert(f);
239     pa_assert(key);
240     pa_assert(v);
241
242     pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
243     o = json_tokener_parse(str);
244     pa_return_val_if_fail(!is_error(o), FALSE);
245     if (json_object_get_type(o) != json_type_int) {
246         json_object_put(o);
247         return FALSE;
248     }
249
250     *v = json_object_get_int(o);
251     json_object_put(o);
252
253     return TRUE;
254 }
255
256 pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
257     const char *str = NULL;
258     json_object *o;
259
260     pa_assert(f);
261     pa_assert(key);
262     pa_assert(v);
263
264     pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
265     o = json_tokener_parse(str);
266     pa_return_val_if_fail(!is_error(o), FALSE);
267     if (json_object_get_type(o) != json_type_string) {
268         json_object_put(o);
269         return FALSE;
270     }
271
272     *v = pa_xstrdup(json_object_get_string(o));
273     json_object_put(o);
274
275     return TRUE;
276 }
277
278 void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
279     json_object *o;
280
281     pa_assert(f);
282     pa_assert(key);
283
284     o = json_object_new_int(value);
285
286     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
287
288     json_object_put(o);
289 }
290
291 void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
292     json_object *o;
293     int i;
294
295     pa_assert(f);
296     pa_assert(key);
297
298     o = json_object_new_array();
299
300     for (i = 0; i < n_values; i++)
301         json_object_array_add(o, json_object_new_int(values[i]));
302
303     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
304
305     json_object_put(o);
306 }
307
308 void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
309     json_object *o;
310
311     pa_assert(f);
312     pa_assert(key);
313
314     o = json_object_new_object();
315
316     json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
317     json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
318
319     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
320
321     json_object_put(o);
322 }
323
324 void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
325     json_object *o;
326
327     pa_assert(f);
328     pa_assert(key);
329
330     o = json_object_new_string(value);
331
332     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
333
334     json_object_put(o);
335 }
336
337 void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
338     json_object *o;
339     int i;
340
341     pa_assert(f);
342     pa_assert(key);
343
344     o = json_object_new_array();
345
346     for (i = 0; i < n_values; i++)
347         json_object_array_add(o, json_object_new_string(values[i]));
348
349     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
350
351     json_object_put(o);
352 }
353
354 static pa_bool_t pa_json_is_fixed_type(json_object *o)
355 {
356     switch(json_object_get_type(o)) {
357         case json_type_object:
358         case json_type_array:
359             return FALSE;
360
361         default:
362             return TRUE;
363     }
364 }
365
366 static int pa_json_value_equal(json_object *o1, json_object *o2) {
367     return (json_object_get_type(o1) == json_object_get_type(o2)) &&
368         pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
369 }
370
371 static int pa_format_info_prop_compatible(const char *one, const char *two) {
372     json_object *o1 = NULL, *o2 = NULL;
373     int i, ret = 0;
374
375     o1 = json_tokener_parse(one);
376     if (is_error(o1))
377         goto out;
378
379     o2 = json_tokener_parse(two);
380     if (is_error(o2))
381         goto out;
382
383     /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
384     pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
385
386     if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
387         ret = pa_json_value_equal(o1, o2);
388         goto out;
389     }
390
391     if (pa_json_is_fixed_type(o1)) {
392         json_object *tmp = o2;
393         o2 = o1;
394         o1 = tmp;
395     }
396
397     /* o2 is now a fixed type, and o1 is not */
398
399     if (json_object_get_type(o1) == json_type_array) {
400         for (i = 0; i < json_object_array_length(o1); i++) {
401             if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
402                 ret = 1;
403                 break;
404             }
405         }
406     } else if (json_object_get_type(o1) == json_type_object) {
407         /* o1 should be a range type */
408         int min, max, v;
409         json_object *o_min = NULL, *o_max = NULL;
410
411         if (json_object_get_type(o2) != json_type_int) {
412             /* We don't support non-integer ranges */
413             goto out;
414         }
415
416         o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
417         if (!o_min || json_object_get_type(o_min) != json_type_int)
418             goto out;
419
420         o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
421         if (!o_max || json_object_get_type(o_max) != json_type_int)
422             goto out;
423
424         v = json_object_get_int(o2);
425         min = json_object_get_int(o_min);
426         max = json_object_get_int(o_max);
427
428         ret = v >= min && v <= max;
429     } else {
430         pa_log_warn("Got a format type that we don't support");
431     }
432
433 out:
434     if (o1)
435         json_object_put(o1);
436     if (o2)
437         json_object_put(o2);
438
439     return ret;
440 }