]> code.delx.au - pulseaudio/blob - src/pulse/format.c
format: Expose pa_format_info<->pa_sample_spec conversion functions
[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
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 }