]> code.delx.au - pulseaudio/blob - src/pulse/format.c
format: Extend properties to handle lists/ranges
[pulseaudio] / 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 }