tagstruct: Allow NULL proplist with pa_tagstruct_get_proplist().
[platform/upstream/pulseaudio.git] / src / pulsecore / tagstruct.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/time.h>
30 #include <stdarg.h>
31
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h>
34 #endif
35
36 #include <pulse/xmalloc.h>
37
38 #include <pulsecore/socket.h>
39 #include <pulsecore/macro.h>
40
41 #include "tagstruct.h"
42
43 #define MAX_TAG_SIZE (64*1024)
44
45 struct pa_tagstruct {
46     uint8_t *data;
47     size_t length, allocated;
48     size_t rindex;
49
50     pa_bool_t dynamic;
51 };
52
53 pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
54     pa_tagstruct*t;
55
56     pa_assert(!data || (data && length));
57
58     t = pa_xnew(pa_tagstruct, 1);
59     t->data = (uint8_t*) data;
60     t->allocated = t->length = data ? length : 0;
61     t->rindex = 0;
62     t->dynamic = !data;
63
64     return t;
65 }
66
67 void pa_tagstruct_free(pa_tagstruct*t) {
68     pa_assert(t);
69
70     if (t->dynamic)
71         pa_xfree(t->data);
72     pa_xfree(t);
73 }
74
75 uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
76     uint8_t *p;
77
78     pa_assert(t);
79     pa_assert(t->dynamic);
80     pa_assert(l);
81
82     p = t->data;
83     *l = t->length;
84     pa_xfree(t);
85     return p;
86 }
87
88 static void extend(pa_tagstruct*t, size_t l) {
89     pa_assert(t);
90     pa_assert(t->dynamic);
91
92     if (t->length+l <= t->allocated)
93         return;
94
95     t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
96 }
97
98 void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
99     size_t l;
100     pa_assert(t);
101
102     if (s) {
103         l = strlen(s)+2;
104         extend(t, l);
105         t->data[t->length] = PA_TAG_STRING;
106         strcpy((char*) (t->data+t->length+1), s);
107         t->length += l;
108     } else {
109         extend(t, 1);
110         t->data[t->length] = PA_TAG_STRING_NULL;
111         t->length += 1;
112     }
113 }
114
115 void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
116     pa_assert(t);
117
118     extend(t, 5);
119     t->data[t->length] = PA_TAG_U32;
120     i = htonl(i);
121     memcpy(t->data+t->length+1, &i, 4);
122     t->length += 5;
123 }
124
125 void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
126     pa_assert(t);
127
128     extend(t, 2);
129     t->data[t->length] = PA_TAG_U8;
130     *(t->data+t->length+1) = c;
131     t->length += 2;
132 }
133
134 void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
135     uint32_t rate;
136
137     pa_assert(t);
138     pa_assert(ss);
139
140     extend(t, 7);
141     t->data[t->length] = PA_TAG_SAMPLE_SPEC;
142     t->data[t->length+1] = (uint8_t) ss->format;
143     t->data[t->length+2] = ss->channels;
144     rate = htonl(ss->rate);
145     memcpy(t->data+t->length+3, &rate, 4);
146     t->length += 7;
147 }
148
149 void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
150     uint32_t tmp;
151
152     pa_assert(t);
153     pa_assert(p);
154
155     extend(t, 5+length);
156     t->data[t->length] = PA_TAG_ARBITRARY;
157     tmp = htonl((uint32_t) length);
158     memcpy(t->data+t->length+1, &tmp, 4);
159     if (length)
160         memcpy(t->data+t->length+5, p, length);
161     t->length += 5+length;
162 }
163
164 void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
165     pa_assert(t);
166
167     extend(t, 1);
168     t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
169     t->length += 1;
170 }
171
172 void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
173     uint32_t tmp;
174     pa_assert(t);
175
176     extend(t, 9);
177     t->data[t->length] = PA_TAG_TIMEVAL;
178     tmp = htonl((uint32_t) tv->tv_sec);
179     memcpy(t->data+t->length+1, &tmp, 4);
180     tmp = htonl((uint32_t) tv->tv_usec);
181     memcpy(t->data+t->length+5, &tmp, 4);
182     t->length += 9;
183 }
184
185 void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
186     uint32_t tmp;
187
188     pa_assert(t);
189
190     extend(t, 9);
191     t->data[t->length] = PA_TAG_USEC;
192     tmp = htonl((uint32_t) (u >> 32));
193     memcpy(t->data+t->length+1, &tmp, 4);
194     tmp = htonl((uint32_t) u);
195     memcpy(t->data+t->length+5, &tmp, 4);
196     t->length += 9;
197 }
198
199 void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
200     uint32_t tmp;
201
202     pa_assert(t);
203
204     extend(t, 9);
205     t->data[t->length] = PA_TAG_U64;
206     tmp = htonl((uint32_t) (u >> 32));
207     memcpy(t->data+t->length+1, &tmp, 4);
208     tmp = htonl((uint32_t) u);
209     memcpy(t->data+t->length+5, &tmp, 4);
210     t->length += 9;
211 }
212
213 void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
214     uint32_t tmp;
215
216     pa_assert(t);
217
218     extend(t, 9);
219     t->data[t->length] = PA_TAG_S64;
220     tmp = htonl((uint32_t) ((uint64_t) u >> 32));
221     memcpy(t->data+t->length+1, &tmp, 4);
222     tmp = htonl((uint32_t) ((uint64_t) u));
223     memcpy(t->data+t->length+5, &tmp, 4);
224     t->length += 9;
225 }
226
227 void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
228     unsigned i;
229
230     pa_assert(t);
231     pa_assert(map);
232     extend(t, 2 + (size_t) map->channels);
233
234     t->data[t->length++] = PA_TAG_CHANNEL_MAP;
235     t->data[t->length++] = map->channels;
236
237     for (i = 0; i < map->channels; i ++)
238         t->data[t->length++] = (uint8_t) map->map[i];
239 }
240
241 void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
242     unsigned i;
243     pa_volume_t vol;
244
245     pa_assert(t);
246     pa_assert(cvolume);
247     extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
248
249     t->data[t->length++] = PA_TAG_CVOLUME;
250     t->data[t->length++] = cvolume->channels;
251
252     for (i = 0; i < cvolume->channels; i ++) {
253         vol = htonl(cvolume->values[i]);
254         memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
255         t->length += sizeof(pa_volume_t);
256     }
257 }
258
259 void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) {
260     uint32_t u;
261     pa_assert(t);
262
263     extend(t, 5);
264     t->data[t->length] = PA_TAG_VOLUME;
265     u = htonl((uint32_t) vol);
266     memcpy(t->data+t->length+1, &u, 4);
267     t->length += 5;
268 }
269
270 void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
271     void *state = NULL;
272     pa_assert(t);
273     pa_assert(p);
274
275     extend(t, 1);
276
277     t->data[t->length++] = PA_TAG_PROPLIST;
278
279     for (;;) {
280         const char *k;
281         const void *d;
282         size_t l;
283
284         if (!(k = pa_proplist_iterate(p, &state)))
285             break;
286
287         pa_tagstruct_puts(t, k);
288         pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
289         pa_tagstruct_putu32(t, (uint32_t) l);
290         pa_tagstruct_put_arbitrary(t, d, l);
291     }
292
293     pa_tagstruct_puts(t, NULL);
294 }
295
296 void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) {
297     pa_assert(t);
298     pa_assert(f);
299
300     extend(t, 1);
301
302     t->data[t->length++] = PA_TAG_FORMAT_INFO;
303     pa_tagstruct_putu8(t, (uint8_t) f->encoding);
304     pa_tagstruct_put_proplist(t, f->plist);
305 }
306
307 int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
308     int error = 0;
309     size_t n;
310     char *c;
311
312     pa_assert(t);
313     pa_assert(s);
314
315     if (t->rindex+1 > t->length)
316         return -1;
317
318     if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
319         t->rindex++;
320         *s = NULL;
321         return 0;
322     }
323
324     if (t->rindex+2 > t->length)
325         return -1;
326
327     if (t->data[t->rindex] != PA_TAG_STRING)
328         return -1;
329
330     error = 1;
331     for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++)
332         if (!*c) {
333             error = 0;
334             break;
335         }
336
337     if (error)
338         return -1;
339
340     *s = (char*) (t->data+t->rindex+1);
341
342     t->rindex += n+2;
343     return 0;
344 }
345
346 int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
347     pa_assert(t);
348     pa_assert(i);
349
350     if (t->rindex+5 > t->length)
351         return -1;
352
353     if (t->data[t->rindex] != PA_TAG_U32)
354         return -1;
355
356     memcpy(i, t->data+t->rindex+1, 4);
357     *i = ntohl(*i);
358     t->rindex += 5;
359     return 0;
360 }
361
362 int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
363     pa_assert(t);
364     pa_assert(c);
365
366     if (t->rindex+2 > t->length)
367         return -1;
368
369     if (t->data[t->rindex] != PA_TAG_U8)
370         return -1;
371
372     *c = t->data[t->rindex+1];
373     t->rindex +=2;
374     return 0;
375 }
376
377 int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
378     pa_assert(t);
379     pa_assert(ss);
380
381     if (t->rindex+7 > t->length)
382         return -1;
383
384     if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
385         return -1;
386
387     ss->format = t->data[t->rindex+1];
388     ss->channels = t->data[t->rindex+2];
389     memcpy(&ss->rate, t->data+t->rindex+3, 4);
390     ss->rate = ntohl(ss->rate);
391
392     t->rindex += 7;
393     return 0;
394 }
395
396 int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
397     uint32_t len;
398
399     pa_assert(t);
400     pa_assert(p);
401
402     if (t->rindex+5+length > t->length)
403         return -1;
404
405     if (t->data[t->rindex] != PA_TAG_ARBITRARY)
406         return -1;
407
408     memcpy(&len, t->data+t->rindex+1, 4);
409     if (ntohl(len) != length)
410         return -1;
411
412     *p = t->data+t->rindex+5;
413     t->rindex += 5+length;
414     return 0;
415 }
416
417 int pa_tagstruct_eof(pa_tagstruct*t) {
418     pa_assert(t);
419
420     return t->rindex >= t->length;
421 }
422
423 const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
424     pa_assert(t);
425     pa_assert(t->dynamic);
426     pa_assert(l);
427
428     *l = t->length;
429     return t->data;
430 }
431
432 int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
433     pa_assert(t);
434     pa_assert(b);
435
436     if (t->rindex+1 > t->length)
437         return -1;
438
439     if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
440         *b = TRUE;
441     else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
442         *b = FALSE;
443     else
444         return -1;
445
446     t->rindex +=1;
447     return 0;
448 }
449
450 int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
451
452     pa_assert(t);
453     pa_assert(tv);
454
455     if (t->rindex+9 > t->length)
456         return -1;
457
458     if (t->data[t->rindex] != PA_TAG_TIMEVAL)
459         return -1;
460
461     memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
462     tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec);
463     memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
464     tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec);
465     t->rindex += 9;
466     return 0;
467 }
468
469 int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
470     uint32_t tmp;
471
472     pa_assert(t);
473     pa_assert(u);
474
475     if (t->rindex+9 > t->length)
476         return -1;
477
478     if (t->data[t->rindex] != PA_TAG_USEC)
479         return -1;
480
481     memcpy(&tmp, t->data+t->rindex+1, 4);
482     *u = (pa_usec_t) ntohl(tmp) << 32;
483     memcpy(&tmp, t->data+t->rindex+5, 4);
484     *u |= (pa_usec_t) ntohl(tmp);
485     t->rindex +=9;
486     return 0;
487 }
488
489 int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
490     uint32_t tmp;
491
492     pa_assert(t);
493     pa_assert(u);
494
495     if (t->rindex+9 > t->length)
496         return -1;
497
498     if (t->data[t->rindex] != PA_TAG_U64)
499         return -1;
500
501     memcpy(&tmp, t->data+t->rindex+1, 4);
502     *u = (uint64_t) ntohl(tmp) << 32;
503     memcpy(&tmp, t->data+t->rindex+5, 4);
504     *u |= (uint64_t) ntohl(tmp);
505     t->rindex +=9;
506     return 0;
507 }
508
509 int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
510     uint32_t tmp;
511
512     pa_assert(t);
513     pa_assert(u);
514
515     if (t->rindex+9 > t->length)
516         return -1;
517
518     if (t->data[t->rindex] != PA_TAG_S64)
519         return -1;
520
521     memcpy(&tmp, t->data+t->rindex+1, 4);
522     *u = (int64_t) ((uint64_t) ntohl(tmp) << 32);
523     memcpy(&tmp, t->data+t->rindex+5, 4);
524     *u |= (int64_t) ntohl(tmp);
525     t->rindex +=9;
526     return 0;
527 }
528
529 int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
530     unsigned i;
531
532     pa_assert(t);
533     pa_assert(map);
534
535     if (t->rindex+2 > t->length)
536         return -1;
537
538     if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP)
539         return -1;
540
541     if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
542         return -1;
543
544     if (t->rindex+2+map->channels > t->length)
545         return -1;
546
547     for (i = 0; i < map->channels; i ++)
548         map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
549
550     t->rindex += 2 + (size_t) map->channels;
551     return 0;
552 }
553
554 int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
555     unsigned i;
556     pa_volume_t vol;
557
558     pa_assert(t);
559     pa_assert(cvolume);
560
561     if (t->rindex+2 > t->length)
562         return -1;
563
564     if (t->data[t->rindex] != PA_TAG_CVOLUME)
565         return -1;
566
567     if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
568         return -1;
569
570     if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
571         return -1;
572
573     for (i = 0; i < cvolume->channels; i ++) {
574         memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
575         cvolume->values[i] = (pa_volume_t) ntohl(vol);
576     }
577
578     t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t);
579     return 0;
580 }
581
582 int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) {
583     uint32_t u;
584
585     pa_assert(t);
586     pa_assert(vol);
587
588     if (t->rindex+5 > t->length)
589         return -1;
590
591     if (t->data[t->rindex] != PA_TAG_VOLUME)
592         return -1;
593
594     memcpy(&u, t->data+t->rindex+1, 4);
595     *vol = (pa_volume_t) ntohl(u);
596
597     t->rindex += 5;
598     return 0;
599 }
600
601 int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
602     size_t saved_rindex;
603
604     pa_assert(t);
605
606     if (t->rindex+1 > t->length)
607         return -1;
608
609     if (t->data[t->rindex] != PA_TAG_PROPLIST)
610         return -1;
611
612     saved_rindex = t->rindex;
613     t->rindex++;
614
615     for (;;) {
616         const char *k;
617         const void *d;
618         uint32_t length;
619
620         if (pa_tagstruct_gets(t, &k) < 0)
621             goto fail;
622
623         if (!k)
624             break;
625
626         if (!pa_proplist_key_valid(k))
627             goto fail;
628
629         if (pa_tagstruct_getu32(t, &length) < 0)
630             goto fail;
631
632         if (length > MAX_TAG_SIZE)
633             goto fail;
634
635         if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
636             goto fail;
637
638         if (p)
639             pa_assert_se(pa_proplist_set(p, k, d, length) >= 0);
640     }
641
642     return 0;
643
644 fail:
645     t->rindex = saved_rindex;
646     return -1;
647 }
648
649 int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) {
650     size_t saved_rindex;
651     uint8_t encoding;
652
653     pa_assert(t);
654     pa_assert(f);
655
656     if (t->rindex+1 > t->length)
657         return -1;
658
659     if (t->data[t->rindex] != PA_TAG_FORMAT_INFO)
660         return -1;
661
662     saved_rindex = t->rindex;
663     t->rindex++;
664
665     if (pa_tagstruct_getu8(t, &encoding) < 0)
666         goto fail;
667
668     f->encoding = encoding;
669
670     if (pa_tagstruct_get_proplist(t, f->plist) < 0)
671         goto fail;
672
673     return 0;
674
675 fail:
676     t->rindex = saved_rindex;
677     return -1;
678 }
679
680 void pa_tagstruct_put(pa_tagstruct *t, ...) {
681     va_list va;
682     pa_assert(t);
683
684     va_start(va, t);
685
686     for (;;) {
687         int tag = va_arg(va, int);
688
689         if (tag == PA_TAG_INVALID)
690             break;
691
692         switch (tag) {
693             case PA_TAG_STRING:
694             case PA_TAG_STRING_NULL:
695                 pa_tagstruct_puts(t, va_arg(va, char*));
696                 break;
697
698             case PA_TAG_U32:
699                 pa_tagstruct_putu32(t, va_arg(va, uint32_t));
700                 break;
701
702             case PA_TAG_U8:
703                 pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
704                 break;
705
706             case PA_TAG_U64:
707                 pa_tagstruct_putu64(t, va_arg(va, uint64_t));
708                 break;
709
710             case PA_TAG_SAMPLE_SPEC:
711                 pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
712                 break;
713
714             case PA_TAG_ARBITRARY: {
715                 void *p = va_arg(va, void*);
716                 size_t size = va_arg(va, size_t);
717                 pa_tagstruct_put_arbitrary(t, p, size);
718                 break;
719             }
720
721             case PA_TAG_BOOLEAN_TRUE:
722             case PA_TAG_BOOLEAN_FALSE:
723                 pa_tagstruct_put_boolean(t, va_arg(va, int));
724                 break;
725
726             case PA_TAG_TIMEVAL:
727                 pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
728                 break;
729
730             case PA_TAG_USEC:
731                 pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
732                 break;
733
734             case PA_TAG_CHANNEL_MAP:
735                 pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
736                 break;
737
738             case PA_TAG_CVOLUME:
739                 pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
740                 break;
741
742             case PA_TAG_VOLUME:
743                 pa_tagstruct_put_volume(t, va_arg(va, pa_volume_t));
744                 break;
745
746             case PA_TAG_PROPLIST:
747                 pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
748                 break;
749
750             default:
751                 pa_assert_not_reached();
752         }
753     }
754
755     va_end(va);
756 }
757
758 int pa_tagstruct_get(pa_tagstruct *t, ...) {
759     va_list va;
760     int ret = 0;
761
762     pa_assert(t);
763
764     va_start(va, t);
765     while (ret == 0) {
766         int tag = va_arg(va, int);
767
768         if (tag == PA_TAG_INVALID)
769             break;
770
771         switch (tag) {
772             case PA_TAG_STRING:
773             case PA_TAG_STRING_NULL:
774                 ret = pa_tagstruct_gets(t, va_arg(va, const char**));
775                 break;
776
777             case PA_TAG_U32:
778                 ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
779                 break;
780
781             case PA_TAG_U8:
782                 ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
783                 break;
784
785             case PA_TAG_U64:
786                 ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
787                 break;
788
789             case PA_TAG_SAMPLE_SPEC:
790                 ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
791                 break;
792
793             case PA_TAG_ARBITRARY: {
794                 const void **p = va_arg(va, const void**);
795                 size_t size = va_arg(va, size_t);
796                 ret = pa_tagstruct_get_arbitrary(t, p, size);
797                 break;
798             }
799
800             case PA_TAG_BOOLEAN_TRUE:
801             case PA_TAG_BOOLEAN_FALSE:
802                 ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
803                 break;
804
805             case PA_TAG_TIMEVAL:
806                 ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
807                 break;
808
809             case PA_TAG_USEC:
810                 ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
811                 break;
812
813             case PA_TAG_CHANNEL_MAP:
814                 ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
815                 break;
816
817             case PA_TAG_CVOLUME:
818                 ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
819                 break;
820
821             case PA_TAG_VOLUME:
822                 ret = pa_tagstruct_get_volume(t, va_arg(va, pa_volume_t *));
823                 break;
824
825             case PA_TAG_PROPLIST:
826                 ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
827                 break;
828
829             default:
830                 pa_assert_not_reached();
831         }
832
833     }
834
835     va_end(va);
836     return ret;
837 }