diff options
Diffstat (limited to 'src/pulse/stream.c')
-rw-r--r-- | src/pulse/stream.c | 152 |
1 files changed, 133 insertions, 19 deletions
diff --git a/src/pulse/stream.c b/src/pulse/stream.c index aac18a31..6c055a5c 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,18 @@ 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 { + /* 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; @@ -179,6 +203,38 @@ 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); + + 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 +276,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 +302,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); } @@ -705,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); @@ -970,9 +1039,10 @@ 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))))) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; } @@ -999,6 +1069,22 @@ 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 (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)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; @@ -1039,6 +1125,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 +1166,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 +1234,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 +1309,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); @@ -2374,6 +2478,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); |