format: Add some convenience API for setting properties
[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_sample_format(f, ss->format);
159     pa_format_info_set_rate(f, ss->rate);
160     pa_format_info_set_channels(f, 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 void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
235     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
236 }
237
238 void pa_format_info_set_rate(pa_format_info *f, int rate) {
239     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
240 }
241
242 void pa_format_info_set_channels(pa_format_info *f, int channels) {
243     pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
244 }
245
246 void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
247     char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
248
249     pa_channel_map_snprint(map_str, sizeof(map_str), map);
250
251     pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
252 }
253
254 pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
255     const char *str;
256     json_object *o;
257
258     pa_assert(f);
259     pa_assert(key);
260     pa_assert(v);
261
262     pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
263     o = json_tokener_parse(str);
264     pa_return_val_if_fail(!is_error(o), FALSE);
265     if (json_object_get_type(o) != json_type_int) {
266         json_object_put(o);
267         return FALSE;
268     }
269
270     *v = json_object_get_int(o);
271     json_object_put(o);
272
273     return TRUE;
274 }
275
276 pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
277     const char *str = NULL;
278     json_object *o;
279
280     pa_assert(f);
281     pa_assert(key);
282     pa_assert(v);
283
284     pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
285     o = json_tokener_parse(str);
286     pa_return_val_if_fail(!is_error(o), FALSE);
287     if (json_object_get_type(o) != json_type_string) {
288         json_object_put(o);
289         return FALSE;
290     }
291
292     *v = pa_xstrdup(json_object_get_string(o));
293     json_object_put(o);
294
295     return TRUE;
296 }
297
298 void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
299     json_object *o;
300
301     pa_assert(f);
302     pa_assert(key);
303
304     o = json_object_new_int(value);
305
306     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
307
308     json_object_put(o);
309 }
310
311 void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
312     json_object *o;
313     int i;
314
315     pa_assert(f);
316     pa_assert(key);
317
318     o = json_object_new_array();
319
320     for (i = 0; i < n_values; i++)
321         json_object_array_add(o, json_object_new_int(values[i]));
322
323     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
324
325     json_object_put(o);
326 }
327
328 void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
329     json_object *o;
330
331     pa_assert(f);
332     pa_assert(key);
333
334     o = json_object_new_object();
335
336     json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
337     json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
338
339     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
340
341     json_object_put(o);
342 }
343
344 void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
345     json_object *o;
346
347     pa_assert(f);
348     pa_assert(key);
349
350     o = json_object_new_string(value);
351
352     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
353
354     json_object_put(o);
355 }
356
357 void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
358     json_object *o;
359     int i;
360
361     pa_assert(f);
362     pa_assert(key);
363
364     o = json_object_new_array();
365
366     for (i = 0; i < n_values; i++)
367         json_object_array_add(o, json_object_new_string(values[i]));
368
369     pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
370
371     json_object_put(o);
372 }
373
374 static pa_bool_t pa_json_is_fixed_type(json_object *o)
375 {
376     switch(json_object_get_type(o)) {
377         case json_type_object:
378         case json_type_array:
379             return FALSE;
380
381         default:
382             return TRUE;
383     }
384 }
385
386 static int pa_json_value_equal(json_object *o1, json_object *o2) {
387     return (json_object_get_type(o1) == json_object_get_type(o2)) &&
388         pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
389 }
390
391 static int pa_format_info_prop_compatible(const char *one, const char *two) {
392     json_object *o1 = NULL, *o2 = NULL;
393     int i, ret = 0;
394
395     o1 = json_tokener_parse(one);
396     if (is_error(o1))
397         goto out;
398
399     o2 = json_tokener_parse(two);
400     if (is_error(o2))
401         goto out;
402
403     /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
404     pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
405
406     if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
407         ret = pa_json_value_equal(o1, o2);
408         goto out;
409     }
410
411     if (pa_json_is_fixed_type(o1)) {
412         json_object *tmp = o2;
413         o2 = o1;
414         o1 = tmp;
415     }
416
417     /* o2 is now a fixed type, and o1 is not */
418
419     if (json_object_get_type(o1) == json_type_array) {
420         for (i = 0; i < json_object_array_length(o1); i++) {
421             if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
422                 ret = 1;
423                 break;
424             }
425         }
426     } else if (json_object_get_type(o1) == json_type_object) {
427         /* o1 should be a range type */
428         int min, max, v;
429         json_object *o_min = NULL, *o_max = NULL;
430
431         if (json_object_get_type(o2) != json_type_int) {
432             /* We don't support non-integer ranges */
433             goto out;
434         }
435
436         o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
437         if (!o_min || json_object_get_type(o_min) != json_type_int)
438             goto out;
439
440         o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
441         if (!o_max || json_object_get_type(o_max) != json_type_int)
442             goto out;
443
444         v = json_object_get_int(o2);
445         min = json_object_get_int(o_min);
446         max = json_object_get_int(o_max);
447
448         ret = v >= min && v <= max;
449     } else {
450         pa_log_warn("Got a format type that we don't support");
451     }
452
453 out:
454     if (o1)
455         json_object_put(o1);
456     if (o2)
457         json_object_put(o2);
458
459     return ret;
460 }