summaryrefslogtreecommitdiffstats
path: root/src/pulse/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse/stream.c')
-rw-r--r--src/pulse/stream.c152
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);