From 802c8b6fcc9349505d00c1f6f6132dd513904b68 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Thu, 24 Feb 2011 14:46:01 +0530 Subject: sample: Use PA_SAMPLE_INVALID instead of numeric value --- src/pulse/sample.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 9698d8a5..50d55210 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -242,7 +242,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) { else if (strcasecmp(format, "s24-32re") == 0) return PA_SAMPLE_S24_32RE; - return -1; + return PA_SAMPLE_INVALID; } int pa_sample_format_is_le(pa_sample_format_t f) { -- cgit From c3839c76377ea7662f29364ae66718e8ac6233af Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 25 Feb 2011 12:35:14 +0530 Subject: core: Add a pa_format_info structure This will be used to represent the format of data provided by the client for both compressed and PCM formats in a new extended API. --- src/pulse/format.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/pulse/format.h | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/pulseaudio.h | 1 + 3 files changed, 151 insertions(+) create mode 100644 src/pulse/format.c create mode 100644 src/pulse/format.h (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c new file mode 100644 index 00000000..372ae1c5 --- /dev/null +++ b/src/pulse/format.c @@ -0,0 +1,71 @@ +/*** + This file is part of PulseAudio. + + Copyright 2011 Intel Corporation + Copyright 2011 Collabora Multimedia + Copyright 2011 Arun Raghavan + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "format.h" + +pa_format_info* pa_format_info_new(void) { + pa_format_info *f = pa_xnew(pa_format_info, 1); + + f->encoding = PA_ENCODING_INVALID; + f->plist = pa_proplist_new(); + + return f; +} + +pa_format_info* pa_format_info_copy(const pa_format_info *src) { + pa_format_info *dest; + + pa_assert(src); + + dest = pa_xnew(pa_format_info, 1); + + dest->encoding = src->encoding; + + if (src->plist) + dest->plist = pa_proplist_copy(src->plist); + else + dest->plist = NULL; + + return dest; +} + +void pa_format_info_free(pa_format_info *f) { + pa_assert(f); + + pa_proplist_free(f->plist); + pa_xfree(f); +} + +int pa_format_info_valid(pa_format_info *f) { + return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); +} diff --git a/src/pulse/format.h b/src/pulse/format.h new file mode 100644 index 00000000..fdf4f377 --- /dev/null +++ b/src/pulse/format.h @@ -0,0 +1,79 @@ +#ifndef fooformathfoo +#define fooformathfoo + +/*** + This file is part of PulseAudio. + + Copyright 2011 Intel Corporation + Copyright 2011 Collabora Multimedia + Copyright 2011 Arun Raghavan + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +PA_C_DECL_BEGIN + +/**< Represents the type of encoding used in a stream or accepted by a sink. \since 1.0 */ +typedef enum pa_encoding { + PA_ENCODING_ANY, + /**< Any encoding format, PCM or compressed */ + + PA_ENCODING_PCM, + /**< Any PCM format */ + + PA_ENCODING_AC3_IEC61937, + /**< AC3 data encapsulated in IEC 61937 header/padding */ + + PA_ENCODING_EAC3_IEC61937, + /**< EAC3 data encapsulated in IEC 61937 header/padding */ + + PA_ENCODING_MPEG_IEC61937, + /**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */ + + PA_ENCODING_MAX, + /**< Valid encoding types must be less than this value */ + + PA_ENCODING_INVALID = -1, + /**< Represents an invalid encoding */ +} pa_encoding_t; + +/**< Represents the format of data provided in a stream or processed by a sink. \since 1.0 */ +typedef struct pa_format_info { + pa_encoding_t encoding; + /**< The encoding used for the format */ + + pa_proplist *plist; + /**< Additional encoding-specific properties such as sample rate, bitrate, etc. */ +} pa_format_info; + +/**< Allocates a new \a pa_format_info structure. Clients must initialise at least the encoding field themselves. */ +pa_format_info* pa_format_info_new(void); + +/**< Returns a new \a pa_format_info struct and representing the same format as \a src */ +pa_format_info* pa_format_info_copy(const pa_format_info *src); + +/**< Frees a \a pa_format_info structure */ +void pa_format_info_free(pa_format_info *f); + +/** Returns non-zero when the format info structure is valid */ +int pa_format_info_valid(pa_format_info *f); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h index 793ba9b1..a399ed96 100644 --- a/src/pulse/pulseaudio.h +++ b/src/pulse/pulseaudio.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include -- cgit From 3767c9c4e8107a3e78f387466b45384d64644d94 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Feb 2011 10:53:41 +0530 Subject: format: Add some properties and internal API The properties will be used by clients to set the sample format, sampling rate, etc. The functions will be used internally. --- src/pulse/format.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulse/internal.h | 5 +++ src/pulse/proplist.h | 12 ++++++++ 3 files changed, 104 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 372ae1c5..c3962de9 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -69,3 +69,90 @@ void pa_format_info_free(pa_format_info *f) { int pa_format_info_valid(pa_format_info *f) { return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); } + +pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) { + const char *key; + void *state = NULL; + + pa_assert(first); + pa_assert(second); + + if (first->encoding != second->encoding) + return FALSE; + + while ((key = pa_proplist_iterate(first->plist, &state))) { + const char *value_one, *value_two; + + value_one = pa_proplist_gets(first->plist, key); + value_two = pa_proplist_gets(second->plist, key); + + if (!value_two || !pa_streq(value_one, value_two)) + return FALSE; + } + + return TRUE; +} + +pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) { + char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_format_info *f; + + pa_assert(ss && pa_sample_spec_valid(ss)); + pa_assert(!map || pa_channel_map_valid(map)); + + f = pa_format_info_new(); + f->encoding = PA_ENCODING_PCM; + + pa_proplist_sets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format)); + pa_proplist_setf(f->plist, PA_PROP_FORMAT_RATE, "%u", (unsigned int) ss->rate); + pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNELS, "%u", (unsigned int) ss->channels); + + if (map) { + pa_channel_map_snprint(cm, sizeof(cm), map); + pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNEL_MAP, "%s", cm); + } + + return f; +} + +/* For PCM streams */ +void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { + const char *sf, *r, *ch; + uint32_t channels; + + pa_assert(f); + pa_assert(ss); + pa_assert(f->encoding == PA_ENCODING_PCM); + + pa_assert(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT)); + pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE)); + pa_assert(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS)); + + pa_assert((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID); + pa_assert(pa_atou(r, &ss->rate) == 0); + pa_assert(pa_atou(ch, &channels) == 0); + ss->channels = (uint8_t) channels; + + if (map) { + const char *m = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNEL_MAP); + pa_channel_map_init(map); + + if (m) + pa_assert(pa_channel_map_parse(map, m) != NULL); + } +} + +/* For compressed streams */ +void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) { + const char *r; + + pa_assert(f); + pa_assert(ss); + pa_assert(f->encoding != PA_ENCODING_PCM); + + ss->format = PA_SAMPLE_S16LE; + ss->channels = 2; + + pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE)); + pa_assert(pa_atou(r, &ss->rate) == 0); +} diff --git a/src/pulse/internal.h b/src/pulse/internal.h index ab702b99..53fcca60 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -291,6 +291,11 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); +pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); +pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map); +void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); +void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss); + pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); #endif diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 7d026997..a641f248 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -254,6 +254,18 @@ PA_C_DECL_BEGIN /** For modules: a version string for the module. e.g. "0.9.15" */ #define PA_PROP_MODULE_VERSION "module.version" +/** For PCM formats: the sample format used as returned by pa_sample_format_to_string() \since 1.0 */ +#define PA_PROP_FORMAT_SAMPLE_FORMAT "format.sample_format" + +/** For all formats: the sample rate (unsigned integer) \since 1.0 */ +#define PA_PROP_FORMAT_RATE "format.rate" + +/** For all formats: the number of channels (unsigned integer) \since 1.0 */ +#define PA_PROP_FORMAT_CHANNELS "format.channels" + +/** For PCM formats: the channel map of the stream as returned by pa_channel_map_snprint() \since 1.0 */ +#define PA_PROP_FORMAT_CHANNEL_MAP "format.channel_map" + /** A property list object. Basically a dictionary with ASCII strings * as keys and arbitrary data as values. \since 0.9.11 */ typedef struct pa_proplist pa_proplist; -- cgit From 0ac2cfce6d1a3d7ab5af6aca659e46625c32d3c4 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Feb 2011 13:23:23 +0530 Subject: core: Add extended stream API to support compressed formats This is the beginning of work to support compressed formats natively in PulseAudio. This adds a pa_stream_new_extended() that takes a format structure, sends it to the server (=> protocol extension) and has the server negotiate with the appropropriate sink to figure out what format it should use. This is work in progress, and works only with PCM streams. Actual compressed format support in some sink needs to be implemented, and extensive testing is required. More details on how this is supposed to work is available at: http://pulseaudio.org/wiki/PassthroughSupport --- src/pulse/internal.h | 5 +++ src/pulse/stream.c | 123 +++++++++++++++++++++++++++++++++++++++++++-------- src/pulse/stream.h | 11 +++++ 3 files changed, 120 insertions(+), 19 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 53fcca60..d7151653 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -122,6 +122,8 @@ typedef struct pa_index_correction { pa_bool_t corrupt:1; } pa_index_correction; +#define PA_MAX_FORMATS (PA_ENCODING_MAX) + struct pa_stream { PA_REFCNT_DECLARE; PA_LLIST_FIELDS(pa_stream); @@ -137,6 +139,9 @@ struct pa_stream { pa_sample_spec sample_spec; pa_channel_map channel_map; + uint8_t n_formats; + pa_format_info *req_formats[PA_MAX_FORMATS]; + pa_format_info *format; pa_proplist *proplist; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index aac18a31..f5bf42c9 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -80,31 +80,24 @@ static void reset_callbacks(pa_stream *s) { s->buffer_attr_userdata = NULL; } -pa_stream *pa_stream_new_with_proplist( +static pa_stream *pa_stream_new_with_proplist_internal( pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, + pa_format_info * const *formats, pa_proplist *p) { pa_stream *s; int i; - pa_channel_map tmap; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert((ss == NULL && map == NULL) || formats == NULL); PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); - PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED); - PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID); - if (!map) - PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID); - s = pa_xnew(pa_stream, 1); PA_REFCNT_INIT(s); s->context = c; @@ -114,8 +107,28 @@ pa_stream *pa_stream_new_with_proplist( s->state = PA_STREAM_UNCONNECTED; s->flags = 0; - s->sample_spec = *ss; - s->channel_map = *map; + if (ss) + s->sample_spec = *ss; + else + s->sample_spec.format = PA_SAMPLE_INVALID; + + if (map) + s->channel_map = *map; + else + pa_channel_map_init(&s->channel_map); + + s->n_formats = 0; + if (formats) { + for (i = 0; formats[i] && i < PA_MAX_FORMATS; i++) { + s->n_formats++; + s->req_formats[i] = pa_format_info_copy(formats[i]); + } + /* Make sure the input array was NULL-terminated */ + pa_assert(formats[i] == NULL); + } + + /* We'll get the final negotiated format after connecting */ + s->format = NULL; s->direct_on_input = PA_INVALID_INDEX; @@ -136,7 +149,10 @@ pa_stream *pa_stream_new_with_proplist( * what older PA versions provided. */ s->buffer_attr.maxlength = (uint32_t) -1; - s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ + if (ss) + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ + else + /* XXX: How do we apply worst case conversion here? */ s->buffer_attr.minreq = (uint32_t) -1; s->buffer_attr.prebuf = (uint32_t) -1; s->buffer_attr.fragsize = (uint32_t) -1; @@ -179,6 +195,40 @@ pa_stream *pa_stream_new_with_proplist( return s; } +pa_stream *pa_stream_new_with_proplist( + pa_context *c, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + pa_proplist *p) { + + pa_channel_map tmap; + + PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED); + PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID); + + if (!map) + PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID); + + return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, p); +} + +pa_stream *pa_stream_new_extended( + pa_context *c, + const char *name, + pa_format_info * const *formats, + pa_proplist *p) { + + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED); + + /* XXX: For the single-format PCM case, pass ss/map instead of formats */ + + return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p); +} + static void stream_unlink(pa_stream *s) { pa_operation *o, *n; pa_assert(s); @@ -220,6 +270,8 @@ static void stream_unlink(pa_stream *s) { } static void stream_free(pa_stream *s) { + unsigned int i; + pa_assert(s); stream_unlink(s); @@ -244,6 +296,9 @@ static void stream_free(pa_stream *s) { if (s->smoother) pa_smoother_free(s->smoother); + for (i = 0; i < s->n_formats; i++) + pa_xfree(s->req_formats[i]); + pa_xfree(s->device_name); pa_xfree(s); } @@ -970,9 +1025,11 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, ss.channels != cm.channels || !pa_channel_map_valid(&cm) || !pa_sample_spec_valid(&ss) || - (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || - (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || - (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) { + (s->n_formats == 0 && ( + (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || + (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || + (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) { + /* XXX: checks for the n_formats > 0 case? */ pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; } @@ -999,6 +1056,16 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, s->timing_info.configured_sink_usec = usec; } + if (s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) { + pa_format_info *f = pa_format_info_new(); + pa_tagstruct_get_format_info(t, f); + + if (pa_format_info_valid(f)) + s->format = f; + else + pa_format_info_free(f); + } + if (!pa_tagstruct_eof(t)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; @@ -1039,6 +1106,7 @@ static int create_stream( pa_tagstruct *t; uint32_t tag; pa_bool_t volume_set = FALSE; + uint32_t i; pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -1079,7 +1147,7 @@ static int create_stream( PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); - PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, !volume || (pa_sample_spec_valid(&s->sample_spec) && volume->channels == s->sample_spec.channels), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID); @@ -1147,8 +1215,16 @@ static int create_stream( volume_set = !!volume; - if (!volume) - volume = pa_cvolume_reset(&cv, s->sample_spec.channels); + if (!volume) { + if (pa_sample_spec_valid(&s->sample_spec)) + volume = pa_cvolume_reset(&cv, s->sample_spec.channels); + else { + /* This is not really relevant, since no volume was set, and + * the real number of channels is embedded in the format_info + * structure */ + volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX); + } + } pa_tagstruct_put_cvolume(t, volume); } else @@ -1214,6 +1290,15 @@ static int create_stream( pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); } + if (s->context->version >= 21) { + + if (s->direction == PA_STREAM_PLAYBACK) { + pa_tagstruct_putu8(t, s->n_formats); + for (i = 0; i < s->n_formats; i++) + pa_tagstruct_put_format_info(t, s->req_formats[i]); + } + } + pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index dd67db73..48bf09df 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -356,6 +357,16 @@ pa_stream* pa_stream_new_with_proplist( const pa_channel_map *map /**< The desired channel map, or NULL for default */, pa_proplist *p /**< The initial property list */); +/* Create a new, unconnected stream with the specified name, the set of formats + * this client can provide, and an initial list of properties. While + * connecting, the server will select the most appropriate format which the + * client must then provide. \since 1.0 */ +pa_stream *pa_stream_new_extended( + pa_context *c /**< The context to create this stream in */, + const char *name /**< A name for this stream */, + pa_format_info * const * formats /**< The list of formats that can be provided */, + pa_proplist *p /**< The initial property list */); + /** Decrease the reference counter by one */ void pa_stream_unref(pa_stream *s); -- cgit From 54c391e6db550c5519df0ebb37f2197eed440c92 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 1 Mar 2011 16:34:06 +0530 Subject: format: Add convenience API to check if a format is PCM or not --- src/pulse/format.c | 4 ++++ src/pulse/format.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index c3962de9..4ecd75ee 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -70,6 +70,10 @@ int pa_format_info_valid(pa_format_info *f) { return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); } +int pa_format_info_is_pcm(pa_format_info *f) { + return f->encoding == PA_ENCODING_PCM; +} + pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) { const char *key; void *state = NULL; diff --git a/src/pulse/format.h b/src/pulse/format.h index fdf4f377..7e3ab08d 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -74,6 +74,9 @@ void pa_format_info_free(pa_format_info *f); /** Returns non-zero when the format info structure is valid */ int pa_format_info_valid(pa_format_info *f); +/** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */ +int pa_format_info_is_pcm(pa_format_info *f); + PA_C_DECL_END #endif -- cgit From 71ec9577cf052558cfddb051c7d038c91b397bc5 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 02:06:54 +0530 Subject: sink: Remove PASSTHROUGH flag This removes the passthrough flag from sinks since we will drop exclusively passthrough sinks in favour of providing a list of formats supported by each sink. We can still determine whether a sink is in passthrough mode by checking if any non-PCM streams are attached to it. --- src/pulse/def.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index a3b86223..91a027e3 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -739,12 +739,7 @@ typedef enum pa_sink_flags { /**< The latency can be adjusted dynamically depending on the * needs of the connected streams. \since 0.9.15 */ - PA_SINK_PASSTHROUGH = 0x0100U, - /**< This sink has support for passthrough mode. The data will be left - * as is and not reformatted, resampled, mixed. - * \since 1.0 */ - - PA_SINK_SYNC_VOLUME = 0x0200U, + PA_SINK_SYNC_VOLUME = 0x0100U, /**< The HW volume changes are syncronized with SW volume. * \since 1.0 */ @@ -753,7 +748,7 @@ typedef enum pa_sink_flags { * The server will filter out these flags anyway, so you should never see * these flags in sinks. */ - PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0400U, + PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0200U, /**< This sink shares the volume with the master sink (used by some filter * sinks). */ /** \endcond */ @@ -769,7 +764,6 @@ typedef enum pa_sink_flags { #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME #define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME #define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY -#define PA_SINK_PASSTHROUGH PA_SINK_PASSTHROUGH #define PA_SINK_SYNC_VOLUME PA_SINK_SYNC_VOLUME #define PA_SINK_SHARE_VOLUME_WITH_MASTER PA_SINK_SHARE_VOLUME_WITH_MASTER -- cgit From 13229fb39e7b8cff3f114c98f8236d9123442243 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 11:16:07 +0530 Subject: sink-input: Don't assert on bad formats Handles bad format input more gracefully and returns an error instead. --- src/pulse/format.c | 30 +++++++++++++++++------------- src/pulse/internal.h | 4 ++-- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 4ecd75ee..af24eb2e 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -120,21 +120,21 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m } /* For PCM streams */ -void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { +pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { const char *sf, *r, *ch; uint32_t channels; pa_assert(f); pa_assert(ss); - pa_assert(f->encoding == PA_ENCODING_PCM); + pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE); - pa_assert(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT)); - pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE)); - pa_assert(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS)); + pa_return_val_if_fail(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT), FALSE); + pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE); + pa_return_val_if_fail(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS), FALSE); - pa_assert((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID); - pa_assert(pa_atou(r, &ss->rate) == 0); - pa_assert(pa_atou(ch, &channels) == 0); + pa_return_val_if_fail((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID, FALSE); + pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE); + pa_return_val_if_fail(pa_atou(ch, &channels) == 0, FALSE); ss->channels = (uint8_t) channels; if (map) { @@ -142,21 +142,25 @@ void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_cha pa_channel_map_init(map); if (m) - pa_assert(pa_channel_map_parse(map, m) != NULL); + pa_return_val_if_fail(pa_channel_map_parse(map, m) != NULL, FALSE); } + + return TRUE; } /* For compressed streams */ -void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) { +pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) { const char *r; pa_assert(f); pa_assert(ss); - pa_assert(f->encoding != PA_ENCODING_PCM); + pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, FALSE); ss->format = PA_SAMPLE_S16LE; ss->channels = 2; - pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE)); - pa_assert(pa_atou(r, &ss->rate) == 0); + pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE); + pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE); + + return TRUE; } diff --git a/src/pulse/internal.h b/src/pulse/internal.h index d7151653..228e8dc4 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -298,8 +298,8 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map); -void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); -void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss); +pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); +pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss); pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); -- cgit From e418e49ecbd643f3cac87438d00baaf86275927c Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 11:31:51 +0530 Subject: format: Avoid some code duplication We frequently need to free an idxset containing pa_format_infos, so define an internal free function that can be used directly with this (instead of defining it once-per-file). --- src/pulse/format.c | 4 ++++ src/pulse/internal.h | 1 + 2 files changed, 5 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index af24eb2e..9bb76732 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -66,6 +66,10 @@ void pa_format_info_free(pa_format_info *f) { pa_xfree(f); } +void pa_format_info_free2(pa_format_info *f, void *userdata) { + pa_format_info_free(f); +} + int pa_format_info_valid(pa_format_info *f) { return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); } diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 228e8dc4..351aeec9 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -296,6 +296,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); +void pa_format_info_free2(pa_format_info *f, void *userdata); pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map); pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); -- cgit From e11770b64ffbe0a5e5d172244c175fa0bc4034db Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 12:54:02 +0530 Subject: core: Fix some FIXMEs for the extended API This adds some checks that I'd postponed and adds a "should-be-good-enough" guess for tlength when using a compressed format. --- src/pulse/stream.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index f5bf42c9..10e431cd 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -151,8 +151,16 @@ static pa_stream *pa_stream_new_with_proplist_internal( s->buffer_attr.maxlength = (uint32_t) -1; if (ss) s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */ - else - /* XXX: How do we apply worst case conversion here? */ + else { + /* FIXME: We assume a worst-case compressed format corresponding to + * 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */ + pa_sample_spec tmp_ss = { + .format = PA_SAMPLE_S16NE, + .rate = 48000, + .channels = 2, + }; + s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */ + } s->buffer_attr.minreq = (uint32_t) -1; s->buffer_attr.prebuf = (uint32_t) -1; s->buffer_attr.fragsize = (uint32_t) -1; @@ -224,8 +232,6 @@ pa_stream *pa_stream_new_extended( PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED); - /* XXX: For the single-format PCM case, pass ss/map instead of formats */ - return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p); } @@ -1029,7 +1035,6 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) { - /* XXX: checks for the n_formats > 0 case? */ pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; } @@ -1062,8 +1067,14 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, if (pa_format_info_valid(f)) s->format = f; - else + else { pa_format_info_free(f); + if (s->n_formats > 0) { + /* We used the extended API, so we should have got back a proper format */ + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + } } if (!pa_tagstruct_eof(t)) { -- cgit From a3a004214404c6f91a82c1e2164444e5e08c26cf Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 8 Mar 2011 20:14:46 +0530 Subject: format: Const-ify some parameters --- src/pulse/format.c | 4 ++-- src/pulse/format.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 9bb76732..7ddfa7a1 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -70,11 +70,11 @@ void pa_format_info_free2(pa_format_info *f, void *userdata) { pa_format_info_free(f); } -int pa_format_info_valid(pa_format_info *f) { +int pa_format_info_valid(const pa_format_info *f) { return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL); } -int pa_format_info_is_pcm(pa_format_info *f) { +int pa_format_info_is_pcm(const pa_format_info *f) { return f->encoding == PA_ENCODING_PCM; } diff --git a/src/pulse/format.h b/src/pulse/format.h index 7e3ab08d..03b34059 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -72,10 +72,10 @@ pa_format_info* pa_format_info_copy(const pa_format_info *src); void pa_format_info_free(pa_format_info *f); /** Returns non-zero when the format info structure is valid */ -int pa_format_info_valid(pa_format_info *f); +int pa_format_info_valid(const pa_format_info *f); /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */ -int pa_format_info_is_pcm(pa_format_info *f); +int pa_format_info_is_pcm(const pa_format_info *f); PA_C_DECL_END -- cgit From 8631f4e2c44b47db76795bebdbab54914a1f3ea0 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 8 Mar 2011 20:15:36 +0530 Subject: format: Add some convenience functions for printing --- src/pulse/format.c | 36 ++++++++++++++++++++++++++++++++++++ src/pulse/format.h | 13 +++++++++++++ 2 files changed, 49 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 7ddfa7a1..0c5c24b1 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -27,12 +27,28 @@ #include #include +#include #include #include #include "format.h" +const char *pa_encoding_to_string(pa_encoding_t e) { + static const char* const table[]= { + [PA_ENCODING_PCM] = "pcm", + [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937", + [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937", + [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937", + [PA_ENCODING_ANY] = "any", + }; + + if (e < 0 || e >= PA_ENCODING_MAX) + return NULL; + + return table[e]; +} + pa_format_info* pa_format_info_new(void) { pa_format_info *f = pa_xnew(pa_format_info, 1); @@ -78,6 +94,26 @@ int pa_format_info_is_pcm(const pa_format_info *f) { return f->encoding == PA_ENCODING_PCM; } +char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) { + char *tmp; + + pa_assert(s); + pa_assert(l > 0); + pa_assert(f); + + pa_init_i18n(); + + if (!pa_format_info_valid(f)) + pa_snprintf(s, l, _("(invalid)")); + else { + tmp = pa_proplist_to_string_sep(f->plist, ", "); + pa_snprintf(s, l, _("%s, %s"), pa_encoding_to_string(f->encoding), tmp[0] ? tmp : _("(no properties)")); + pa_xfree(tmp); + } + + return s; +} + pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) { const char *key; void *state = NULL; diff --git a/src/pulse/format.h b/src/pulse/format.h index 03b34059..51c9426c 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -53,6 +53,9 @@ typedef enum pa_encoding { /**< Represents an invalid encoding */ } pa_encoding_t; +/** Returns a printable string representing the given encoding type. \since 1.0 */ +const char *pa_encoding_to_string(pa_encoding_t e) PA_GCC_CONST; + /**< Represents the format of data provided in a stream or processed by a sink. \since 1.0 */ typedef struct pa_format_info { pa_encoding_t encoding; @@ -77,6 +80,16 @@ int pa_format_info_valid(const pa_format_info *f); /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */ int pa_format_info_is_pcm(const pa_format_info *f); +/** Maximum required string length for + * pa_format_info_snprint(). Please note that this value can change + * with any release without warning and without being considered API + * or ABI breakage. You should not use this definition anywhere where + * it might become part of an ABI. \since 1.0 */ +#define PA_FORMAT_INFO_SNPRINT_MAX 256 + +/** Return a human-readable string representing the given format. \since 1.0 */ +char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f); + PA_C_DECL_END #endif -- cgit From dedbc942ab2090320856c36d6673db12026efdc1 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 8 Mar 2011 20:16:00 +0530 Subject: stream: Add API to get a stream's pa_format_info --- src/pulse/stream.c | 10 ++++++++++ src/pulse/stream.h | 3 +++ 2 files changed, 13 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 10e431cd..bd2e6ca1 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -2470,6 +2470,16 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) { return &s->channel_map; } +const pa_format_info* pa_stream_get_format_info(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + /* We don't have the format till routing is done */ + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED); + + return s->format; +} const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 48bf09df..b265fae6 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -709,6 +709,9 @@ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); /** Return a pointer to the stream's channel map. */ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); +/** Return a pointer to the stream's format \since 1.0 */ +const pa_format_info* pa_stream_get_format_info(pa_stream *s); + /** Return the per-stream server-side buffer metrics of the * stream. Only valid after the stream has been connected successfuly * and if the server is at least PulseAudio 0.9. This will return the -- cgit From 322980e2e3844abf837dcc8cc5317406b3d8cb94 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 8 Mar 2011 23:30:24 +0530 Subject: introspect: Get formats for sinks This gets the list of supported formats for a sink in pa_context_get_sink_info*(). Also prints these in 'pactl list'. --- src/pulse/introspect.c | 34 ++++++++++++++++++++++++++++++++++ src/pulse/introspect.h | 3 +++ 2 files changed, 37 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index c93fb062..28a6bf4a 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -240,6 +240,33 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u } } + if (o->context->version >= 21) { + i.formats = NULL; + + if (pa_tagstruct_getu8(t, &i.n_formats)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_proplist_free(i.proplist); + goto finish; + } + + pa_assert(i.n_formats > 0); + i.formats = pa_xnew0(pa_format_info*, i.n_formats); + + for (j = 0; j < i.n_formats; j++) { + i.formats[j] = pa_format_info_new(); + if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0) { + do { + pa_format_info_free(i.formats[j]); + } while (j--); + pa_xfree(i.formats); + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_proplist_free(i.proplist); + goto finish; + } + } + } + i.mute = (int) mute; i.flags = (pa_sink_flags_t) flags; i.state = (pa_sink_state_t) state; @@ -253,6 +280,13 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u pa_xfree(i.ports[0]); pa_xfree(i.ports); } + + if (i.formats) { + for (j = 0; j < i.n_formats; j++) + pa_format_info_free(i.formats[j]); + pa_xfree(i.formats); + } + pa_proplist_free(i.proplist); } } diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 297b4bac..49c1de63 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -32,6 +32,7 @@ #include #include #include +#include #include /** \page introspect Server Query and Control @@ -230,6 +231,8 @@ typedef struct pa_sink_info { uint32_t n_ports; /**< Number of entries in port array \since 0.9.16 */ pa_sink_port_info** ports; /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16 */ pa_sink_port_info* active_port; /**< Pointer to active port in the array, or NULL \since 0.9.16 */ + uint8_t n_formats; /**< Number of formats supported by the sink. \since 1.0 */ + pa_format_info **formats; /**< Array of formats supported by the sink. \since 1.0 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ -- cgit From 7aa84e82089a88a542f15cbf6f38c808b4f04db1 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 8 Mar 2011 23:31:59 +0530 Subject: introspect: Get format of sink input This gets the negotiated format of sink inputs in pa_context_get_sink_input*(). Also prints the format in 'pactl list'. --- src/pulse/introspect.c | 6 +++++- src/pulse/introspect.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/pulse') diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 28a6bf4a..e28a78c4 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -1033,6 +1033,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm pa_zero(i); i.proplist = pa_proplist_new(); + i.format = pa_format_info_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || @@ -1050,10 +1051,12 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) || (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) || (o->context->version >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 || - pa_tagstruct_get_boolean(t, &volume_writable) < 0))) { + pa_tagstruct_get_boolean(t, &volume_writable) < 0)) || + (o->context->version >= 21 && pa_tagstruct_get_format_info(t, i.format) < 0)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); + pa_format_info_free(i.format); goto finish; } @@ -1068,6 +1071,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm } pa_proplist_free(i.proplist); + pa_format_info_free(i.format); } } diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 49c1de63..a6b4a801 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -508,6 +508,7 @@ typedef struct pa_sink_input_info { int corked; /**< Stream corked \since 1.0 */ int has_volume; /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */ int volume_writable; /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */ + pa_format_info *format; /**< Stream format information. \since 1.0 */ } pa_sink_input_info; /** Callback prototype for pa_context_get_sink_input_info() and friends*/ -- cgit From 9a39a3df10c3874bf0b5b091f721a6d8ab8750a5 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 9 Mar 2011 20:50:25 +0530 Subject: format: Add a type for DTS --- src/pulse/format.c | 1 + src/pulse/format.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 0c5c24b1..e601d902 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -40,6 +40,7 @@ const char *pa_encoding_to_string(pa_encoding_t e) { [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937", [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937", [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937", + [PA_ENCODING_DTS_IEC61937] = "dts-iec61937", [PA_ENCODING_ANY] = "any", }; diff --git a/src/pulse/format.h b/src/pulse/format.h index 51c9426c..bd32ba92 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -46,6 +46,9 @@ typedef enum pa_encoding { PA_ENCODING_MPEG_IEC61937, /**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */ + PA_ENCODING_DTS_IEC61937, + /**< DTS data encapsulated in IEC 61937 header/padding */ + PA_ENCODING_MAX, /**< Valid encoding types must be less than this value */ -- cgit From 53091cc5f0a766e0bff36b59600f99cd2771bbd6 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Mar 2011 08:46:40 +0530 Subject: sink-input: Add a format-lost event This event is emitted if the sink-input could not be moved to a new sink because it doesn't support the format of the sink-input. Clients can reconnect their stream with a different format if they wish or gracefully exit. --- src/pulse/def.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/def.h b/src/pulse/def.h index 91a027e3..e01e667f 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -912,6 +912,13 @@ typedef void (*pa_free_cb_t)(void *p); * information, \since 0.9.15 */ #define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork" +/** A stream event notifying that the stream is going to be + * disconnected because the underlying sink changed and no longer + * supports the format that was originally negotiated. Clients need + * to connect a new stream to renegotiate a format and continue + * playback, \since 1.0 */ +#define PA_STREAM_EVENT_FORMAT_LOST "format-lost" + PA_C_DECL_END #endif -- cgit From 13a33abf45f31417076f283ca7da9d9f74892286 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Tue, 29 Mar 2011 17:16:08 +0530 Subject: format: Export pa_format_info_is_compatible in API This allows clients to perform checks between formats as well. --- src/pulse/format.c | 2 +- src/pulse/format.h | 9 +++++++++ src/pulse/internal.h | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index e601d902..a634b27b 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -115,7 +115,7 @@ char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) { return s; } -pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) { +int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) { const char *key; void *state = NULL; diff --git a/src/pulse/format.h b/src/pulse/format.h index bd32ba92..b0efe502 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -83,6 +83,15 @@ int pa_format_info_valid(const pa_format_info *f); /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */ int pa_format_info_is_pcm(const pa_format_info *f); +/** Returns non-zero if the format represented \a first is a subset of + * the format represented by \second. This means that \a second must + * have all the fields that \a first does, but the reverse need not + * be true. This is typically expected to be used to check if a + * stream's format is compatible with a given sink. In such a case, + * \a first would be the sink's format and \a second would be the + * stream's.*/ +int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); + /** Maximum required string length for * pa_format_info_snprint(). Please note that this value can change * with any release without warning and without being considered API diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 351aeec9..a659576a 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -297,7 +297,6 @@ void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_format_info_free2(pa_format_info *f, void *userdata); -pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map); pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss); -- cgit From d1f13fa781ec69e4dbed000902123d255baaba5f Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Sat, 9 Apr 2011 09:36:25 +0530 Subject: format: Add correct sample spec conversion for E-AC3 IEC61937-encapsulated E-AC3 frames contain 6 audio blocks per substream, which corresponds to 1536 samples contained a 24576-byte frame. To cope with this, we maintain the s16le stereo sample spec, but quadruple the sample rate so that the conversion remains accurate. --- src/pulse/format.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index a634b27b..84df76b4 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -203,5 +203,8 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec * pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE); pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE); + if (f->encoding == PA_ENCODING_EAC3_IEC61937) + ss->rate *= 4; + return TRUE; } -- cgit From 62f56a9f6b01c277a8c4f15625473df4b73bd208 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Sun, 10 Apr 2011 16:24:33 +0530 Subject: sink-input: Provide more information to client when format is lost When the sink format changes and we kill the stream, clients need a way to know (a) what device they should reconnect to, and (b) what the stream running time was when the stream got killed (pa_stream_get_time() won't work after the stream has been killed). This adds these two bits of information in the event callback's proplist parameter. --- src/pulse/stream.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/pulse') diff --git a/src/pulse/stream.c b/src/pulse/stream.c index bd2e6ca1..6c055a5c 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -766,6 +766,14 @@ void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, p if (s->state != PA_STREAM_READY) goto finish; + if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) { + /* Let client know what the running time was when the stream had to be + * killed */ + pa_usec_t time; + if (pa_stream_get_time(s, &time) == 0) + pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) time); + } + if (s->event_callback) s->event_callback(s, event, pl, s->event_userdata); -- cgit From 8d076d09902fe618e69f3d71e42299bffe2af437 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 13 Apr 2011 14:05:18 +0530 Subject: format: Extend properties to handle lists/ranges This replaces the simple string used by pa_format_info's proplist with a JSON string (accessed via new API only). This allows us to express lists and ranges more cleanly, and embed type information for future extensibility. We use json-c for JSON parsing. This is a lightweight depdency (32 KB on my system) and avoids the hassle of having to reinvent a JSON parser. Also included is a test which verifies functionality and is valgrind-clean. --- src/pulse/format.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++---- src/pulse/format.h | 11 +++ src/pulse/internal.h | 2 + 3 files changed, 263 insertions(+), 20 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 84df76b4..1cb804ef 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -25,6 +25,8 @@ #include #endif +#include + #include #include #include @@ -34,6 +36,11 @@ #include "format.h" +#define PA_JSON_MIN_KEY "min" +#define PA_JSON_MAX_KEY "max" + +static int pa_format_info_prop_compatible(const char *one, const char *two); + const char *pa_encoding_to_string(pa_encoding_t e) { static const char* const table[]= { [PA_ENCODING_PCM] = "pcm", @@ -131,7 +138,7 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) value_one = pa_proplist_gets(first->plist, key); value_two = pa_proplist_gets(second->plist, key); - if (!value_two || !pa_streq(value_one, value_two)) + if (!value_two || !pa_format_info_prop_compatible(value_one, value_two)) return FALSE; } @@ -148,13 +155,13 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m f = pa_format_info_new(); f->encoding = PA_ENCODING_PCM; - pa_proplist_sets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format)); - pa_proplist_setf(f->plist, PA_PROP_FORMAT_RATE, "%u", (unsigned int) ss->rate); - pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNELS, "%u", (unsigned int) ss->channels); + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format)); + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate); + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels); if (map) { pa_channel_map_snprint(cm, sizeof(cm), map); - pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNEL_MAP, "%s", cm); + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm); } return f; @@ -162,36 +169,51 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m /* For PCM streams */ pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) { - const char *sf, *r, *ch; - uint32_t channels; + char *sf = NULL, *m = NULL; + int rate, channels; + pa_bool_t ret = FALSE; pa_assert(f); pa_assert(ss); pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE); - pa_return_val_if_fail(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT), FALSE); - pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE); - pa_return_val_if_fail(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS), FALSE); + if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf)) + goto out; + if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate)) + goto out; + if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels)) + goto out; - pa_return_val_if_fail((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID, FALSE); - pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE); - pa_return_val_if_fail(pa_atou(ch, &channels) == 0, FALSE); + if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID) + goto out; + + ss->rate = (uint32_t) rate; ss->channels = (uint8_t) channels; if (map) { - const char *m = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNEL_MAP); pa_channel_map_init(map); - if (m) - pa_return_val_if_fail(pa_channel_map_parse(map, m) != NULL, FALSE); + if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m)) + goto out; + + if (m && pa_channel_map_parse(map, m) == NULL) + goto out; } - return TRUE; + ret = TRUE; + +out: + if (sf) + pa_xfree(sf); + if (m) + pa_xfree(m); + + return ret; } /* For compressed streams */ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) { - const char *r; + int rate; pa_assert(f); pa_assert(ss); @@ -200,11 +222,219 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec * ss->format = PA_SAMPLE_S16LE; ss->channels = 2; - pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE); - pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE); + pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE); + ss->rate = (uint32_t) rate; if (f->encoding == PA_ENCODING_EAC3_IEC61937) ss->rate *= 4; return TRUE; } + +pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) { + const char *str; + json_object *o; + + pa_assert(f); + pa_assert(key); + pa_assert(v); + + pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE); + o = json_tokener_parse(str); + pa_return_val_if_fail(!is_error(o), FALSE); + if (json_object_get_type(o) != json_type_int) { + json_object_put(o); + return FALSE; + } + + *v = json_object_get_int(o); + json_object_put(o); + + return TRUE; +} + +pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) { + const char *str = NULL; + json_object *o; + + pa_assert(f); + pa_assert(key); + pa_assert(v); + + pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE); + o = json_tokener_parse(str); + pa_return_val_if_fail(!is_error(o), FALSE); + if (json_object_get_type(o) != json_type_string) { + json_object_put(o); + return FALSE; + } + + *v = pa_xstrdup(json_object_get_string(o)); + json_object_put(o); + + return TRUE; +} + +void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) { + json_object *o; + + pa_assert(f); + pa_assert(key); + + o = json_object_new_int(value); + + pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + + json_object_put(o); +} + +void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) { + json_object *o; + int i; + + pa_assert(f); + pa_assert(key); + + o = json_object_new_array(); + + for (i = 0; i < n_values; i++) + json_object_array_add(o, json_object_new_int(values[i])); + + pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + + json_object_put(o); +} + +void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) { + json_object *o; + + pa_assert(f); + pa_assert(key); + + o = json_object_new_object(); + + json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min)); + json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max)); + + pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + + json_object_put(o); +} + +void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) { + json_object *o; + + pa_assert(f); + pa_assert(key); + + o = json_object_new_string(value); + + pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + + json_object_put(o); +} + +void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) { + json_object *o; + int i; + + pa_assert(f); + pa_assert(key); + + o = json_object_new_array(); + + for (i = 0; i < n_values; i++) + json_object_array_add(o, json_object_new_string(values[i])); + + pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + + json_object_put(o); +} + +static pa_bool_t pa_json_is_fixed_type(json_object *o) +{ + switch(json_object_get_type(o)) { + case json_type_object: + case json_type_array: + return FALSE; + + default: + return TRUE; + } +} + +static int pa_json_value_equal(json_object *o1, json_object *o2) { + return (json_object_get_type(o1) == json_object_get_type(o2)) && + pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2)); +} + +static int pa_format_info_prop_compatible(const char *one, const char *two) { + json_object *o1 = NULL, *o2 = NULL; + int i, ret = 0; + + o1 = json_tokener_parse(one); + if (is_error(o1)) + goto out; + + o2 = json_tokener_parse(two); + if (is_error(o2)) + goto out; + + /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */ + pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE); + + if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) { + ret = pa_json_value_equal(o1, o2); + goto out; + } + + if (pa_json_is_fixed_type(o1)) { + json_object *tmp = o2; + o2 = o1; + o1 = tmp; + } + + /* o2 is now a fixed type, and o1 is not */ + + if (json_object_get_type(o1) == json_type_array) { + for (i = 0; i < json_object_array_length(o1); i++) { + if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) { + ret = 1; + break; + } + } + } else if (json_object_get_type(o1) == json_type_object) { + /* o1 should be a range type */ + int min, max, v; + json_object *o_min = NULL, *o_max = NULL; + + if (json_object_get_type(o2) != json_type_int) { + /* We don't support non-integer ranges */ + goto out; + } + + o_min = json_object_object_get(o1, PA_JSON_MIN_KEY); + if (!o_min || json_object_get_type(o_min) != json_type_int) + goto out; + + o_max = json_object_object_get(o1, PA_JSON_MAX_KEY); + if (!o_max || json_object_get_type(o_max) != json_type_int) + goto out; + + v = json_object_get_int(o2); + min = json_object_get_int(o_min); + max = json_object_get_int(o_max); + + ret = v >= min && v <= max; + } else { + pa_log_warn("Got a format type that we don't support"); + } + +out: + if (o1) + json_object_put(o1); + if (o2) + json_object_put(o2); + + return ret; +} diff --git a/src/pulse/format.h b/src/pulse/format.h index b0efe502..0498e68a 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -102,6 +102,17 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second); /** Return a human-readable string representing the given format. \since 1.0 */ char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f); +/** Sets an integer property on the given format info */ +void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value); +/** Sets a property with a list of integer values on the given format info */ +void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values); +/** Sets a property which can have any value in a given integer range on the given format info */ +void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max); +/** Sets a string property on the given format info */ +void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value); +/** Sets a property with a list of string values on the given format info */ +void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values); + PA_C_DECL_END #endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index a659576a..40f6804a 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -300,6 +300,8 @@ void pa_format_info_free2(pa_format_info *f, void *userdata); pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map); pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map); pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss); +pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v); +pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v); pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); -- cgit From 1404db3d47c77ef3a360feed4ae8932cc8e443a0 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 13 Apr 2011 15:37:25 +0530 Subject: format: Add some convenience API for setting properties Adds functions to set sample format, rate, channels and channel map on a format to make life easier for users of the API. --- src/pulse/format.c | 26 +++++++++++++++++++++++--- src/pulse/format.h | 11 +++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) (limited to 'src/pulse') diff --git a/src/pulse/format.c b/src/pulse/format.c index 1cb804ef..a1a0981b 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -155,9 +155,9 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m f = pa_format_info_new(); f->encoding = PA_ENCODING_PCM; - pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format)); - pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate); - pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels); + pa_format_info_set_sample_format(f, ss->format); + pa_format_info_set_rate(f, ss->rate); + pa_format_info_set_channels(f, ss->channels); if (map) { pa_channel_map_snprint(cm, sizeof(cm), map); @@ -231,6 +231,26 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec * return TRUE; } +void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) { + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf)); +} + +void pa_format_info_set_rate(pa_format_info *f, int rate) { + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate); +} + +void pa_format_info_set_channels(pa_format_info *f, int channels) { + pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels); +} + +void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) { + char map_str[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_channel_map_snprint(map_str, sizeof(map_str), map); + + pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str); +} + pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) { const char *str; json_object *o; diff --git a/src/pulse/format.h b/src/pulse/format.h index 0498e68a..06e1fe62 100644 --- a/src/pulse/format.h +++ b/src/pulse/format.h @@ -26,6 +26,8 @@ #include #include +#include +#include PA_C_DECL_BEGIN @@ -113,6 +115,15 @@ void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const ch /** Sets a property with a list of string values on the given format info */ void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values); +/** Convenience method to set the sample format as a property on the given format */ +void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf); +/** Convenience method to set the sampling rate as a property on the given format */ +void pa_format_info_set_rate(pa_format_info *f, int rate); +/** Convenience method to set the number of channels as a property on the given format */ +void pa_format_info_set_channels(pa_format_info *f, int channels); +/** Convenience method to set the channel map as a property on the given format */ +void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map); + PA_C_DECL_END #endif -- cgit