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
This commit is contained in:
Arun Raghavan 2011-02-28 13:23:23 +05:30
parent 47e0f91aa2
commit 0ac2cfce6d
25 changed files with 347 additions and 82 deletions

View file

@ -213,3 +213,18 @@ Two new flags at the end of sink input introspection data:
bool has_volume bool has_volume
bool volume_writable bool volume_writable
## v21, implemented by >= 1.0
Changes for format negotiation in the extended API.
New fields PA_COMMAND_CREATE_PLAYBACK_STREAM:
uint8_t n_formats
format_info format1
...
format_info formatn
One new field in reply from PA_COMMAND_CREATE_PLAYBACK_STREAM:
format_info format

View file

@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/]) AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 12) AC_SUBST(PA_API_VERSION, 12)
AC_SUBST(PA_PROTOCOL_VERSION, 20) AC_SUBST(PA_PROTOCOL_VERSION, 21)
# The stable ABI for client applications, for the version info x:y:z # The stable ABI for client applications, for the version info x:y:z
# always will hold y=z # always will hold y=z

View file

@ -265,6 +265,7 @@ pa_stream_get_timing_info;
pa_stream_is_corked; pa_stream_is_corked;
pa_stream_is_suspended; pa_stream_is_suspended;
pa_stream_new; pa_stream_new;
pa_stream_new_extended;
pa_stream_new_with_proplist; pa_stream_new_with_proplist;
pa_stream_peek; pa_stream_peek;
pa_stream_prebuf; pa_stream_prebuf;

View file

@ -1549,7 +1549,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = sink_master; pa_sink_input_new_data_set_sink(&sink_input_data, sink_master, FALSE);
sink_input_data.origin_sink = u->sink; sink_input_data.origin_sink = u->sink;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -845,7 +845,7 @@ static int output_create_sink_input(struct output *o) {
return 0; return 0;
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = o->sink; pa_sink_input_new_data_set_sink(&data, o->sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -832,8 +832,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
pa_sink *sink; pa_sink *sink;
if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
new_data->sink = sink; if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
new_data->save_sink = FALSE; pa_log_debug("Not restoring device for stream because no supported format was found");
} }
} }
} }

View file

@ -1212,7 +1212,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = master; pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
sink_input_data.origin_sink = u->sink; sink_input_data.origin_sink = u->sink;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -117,11 +117,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
/* Prefer the default sink over any other sink, just in case... */ /* Prefer the default sink over any other sink, just in case... */
if ((def = pa_namereg_get_default_sink(c))) if ((def = pa_namereg_get_default_sink(c)))
if (role_match(def->proplist, role)) { if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, FALSE))
new_data->sink = def;
new_data->save_sink = FALSE;
return PA_HOOK_OK; return PA_HOOK_OK;
}
/* @todo: favour the highest priority device, not the first one we find? */ /* @todo: favour the highest priority device, not the first one we find? */
PA_IDXSET_FOREACH(s, c->sinks, idx) { PA_IDXSET_FOREACH(s, c->sinks, idx) {
@ -131,11 +128,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
if (!PA_SINK_IS_LINKED(pa_sink_get_state(s))) if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
continue; continue;
if (role_match(s->proplist, role)) { if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, FALSE))
new_data->sink = s;
new_data->save_sink = FALSE;
return PA_HOOK_OK; return PA_HOOK_OK;
}
} }
return PA_HOOK_OK; return PA_HOOK_OK;

View file

@ -907,7 +907,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = master; pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
sink_input_data.origin_sink = u->sink; sink_input_data.origin_sink = u->sink;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -695,7 +695,7 @@ int pa__init(pa_module *m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = sink; pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);
if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL))) if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL)))
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n);

View file

@ -419,7 +419,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = master; pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
sink_input_data.origin_sink = u->sink; sink_input_data.origin_sink = u->sink;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -157,7 +157,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.driver = __FILE__; data.driver = __FILE__;
data.module = m; data.module = m;
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency); pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);

View file

@ -1301,11 +1301,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
/* It might happen that a stream and a sink are set up at the /* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't same time, in which case we want to make sure we don't
interfere with that */ interfere with that */
if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s))) { if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s)))
pa_log_info("Restoring device for stream %s.", name); if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
new_data->sink = s; pa_log_info("Restoring device for stream %s.", name);
new_data->save_sink = TRUE;
}
pa_xfree(e); pa_xfree(e);
} }

View file

@ -579,7 +579,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_init(&sink_input_data); pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__; sink_input_data.driver = __FILE__;
sink_input_data.module = m; sink_input_data.module = m;
sink_input_data.sink = master; pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
sink_input_data.origin_sink = u->sink; sink_input_data.origin_sink = u->sink;
pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");

View file

@ -512,7 +512,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail; goto fail;
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream"); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,

View file

@ -122,6 +122,8 @@ typedef struct pa_index_correction {
pa_bool_t corrupt:1; pa_bool_t corrupt:1;
} pa_index_correction; } pa_index_correction;
#define PA_MAX_FORMATS (PA_ENCODING_MAX)
struct pa_stream { struct pa_stream {
PA_REFCNT_DECLARE; PA_REFCNT_DECLARE;
PA_LLIST_FIELDS(pa_stream); PA_LLIST_FIELDS(pa_stream);
@ -137,6 +139,9 @@ struct pa_stream {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
uint8_t n_formats;
pa_format_info *req_formats[PA_MAX_FORMATS];
pa_format_info *format;
pa_proplist *proplist; pa_proplist *proplist;

View file

@ -80,31 +80,24 @@ static void reset_callbacks(pa_stream *s) {
s->buffer_attr_userdata = NULL; s->buffer_attr_userdata = NULL;
} }
pa_stream *pa_stream_new_with_proplist( static pa_stream *pa_stream_new_with_proplist_internal(
pa_context *c, pa_context *c,
const char *name, const char *name,
const pa_sample_spec *ss, const pa_sample_spec *ss,
const pa_channel_map *map, const pa_channel_map *map,
pa_format_info * const *formats,
pa_proplist *p) { pa_proplist *p) {
pa_stream *s; pa_stream *s;
int i; int i;
pa_channel_map tmap;
pa_assert(c); pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1); 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, !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); 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); s = pa_xnew(pa_stream, 1);
PA_REFCNT_INIT(s); PA_REFCNT_INIT(s);
s->context = c; s->context = c;
@ -114,8 +107,28 @@ pa_stream *pa_stream_new_with_proplist(
s->state = PA_STREAM_UNCONNECTED; s->state = PA_STREAM_UNCONNECTED;
s->flags = 0; s->flags = 0;
s->sample_spec = *ss; if (ss)
s->channel_map = *map; 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; s->direct_on_input = PA_INVALID_INDEX;
@ -136,7 +149,10 @@ pa_stream *pa_stream_new_with_proplist(
* what older PA versions provided. */ * what older PA versions provided. */
s->buffer_attr.maxlength = (uint32_t) -1; 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.minreq = (uint32_t) -1;
s->buffer_attr.prebuf = (uint32_t) -1; s->buffer_attr.prebuf = (uint32_t) -1;
s->buffer_attr.fragsize = (uint32_t) -1; s->buffer_attr.fragsize = (uint32_t) -1;
@ -179,6 +195,40 @@ pa_stream *pa_stream_new_with_proplist(
return s; 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) { static void stream_unlink(pa_stream *s) {
pa_operation *o, *n; pa_operation *o, *n;
pa_assert(s); pa_assert(s);
@ -220,6 +270,8 @@ static void stream_unlink(pa_stream *s) {
} }
static void stream_free(pa_stream *s) { static void stream_free(pa_stream *s) {
unsigned int i;
pa_assert(s); pa_assert(s);
stream_unlink(s); stream_unlink(s);
@ -244,6 +296,9 @@ static void stream_free(pa_stream *s) {
if (s->smoother) if (s->smoother)
pa_smoother_free(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->device_name);
pa_xfree(s); 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 || ss.channels != cm.channels ||
!pa_channel_map_valid(&cm) || !pa_channel_map_valid(&cm) ||
!pa_sample_spec_valid(&ss) || !pa_sample_spec_valid(&ss) ||
(!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) || (s->n_formats == 0 && (
(!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) || (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
(!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) { (!(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); pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish; 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; 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)) { if (!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERR_PROTOCOL); pa_context_fail(s->context, PA_ERR_PROTOCOL);
goto finish; goto finish;
@ -1039,6 +1106,7 @@ static int create_stream(
pa_tagstruct *t; pa_tagstruct *t;
uint32_t tag; uint32_t tag;
pa_bool_t volume_set = FALSE; pa_bool_t volume_set = FALSE;
uint32_t i;
pa_assert(s); pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1); 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_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, 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, !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); 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; volume_set = !!volume;
if (!volume) if (!volume) {
volume = pa_cvolume_reset(&cv, s->sample_spec.channels); 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); pa_tagstruct_put_cvolume(t, volume);
} else } else
@ -1214,6 +1290,15 @@ static int create_stream(
pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); 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_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);

View file

@ -26,6 +26,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulse/channelmap.h> #include <pulse/channelmap.h>
#include <pulse/volume.h> #include <pulse/volume.h>
#include <pulse/def.h> #include <pulse/def.h>
@ -356,6 +357,16 @@ pa_stream* pa_stream_new_with_proplist(
const pa_channel_map *map /**< The desired channel map, or NULL for default */, const pa_channel_map *map /**< The desired channel map, or NULL for default */,
pa_proplist *p /**< The initial property list */); 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 */ /** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s); void pa_stream_unref(pa_stream *s);

View file

@ -202,7 +202,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
u->memblockq = NULL; u->memblockq = NULL;
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_channel_map(&data, map);

View file

@ -426,7 +426,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
sdata.driver = __FILE__; sdata.driver = __FILE__;
sdata.module = c->options->module; sdata.module = c->options->module;
sdata.client = c->client; sdata.client = c->client;
sdata.sink = sink; pa_sink_input_new_data_set_sink(&sdata, sink, FALSE);
pa_sink_input_new_data_set_sample_spec(&sdata, &ss); pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata); pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);

View file

@ -1014,6 +1014,7 @@ static playback_stream* playback_stream_new(
pa_sink *sink, pa_sink *sink,
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map *map, pa_channel_map *map,
pa_idxset *formats,
pa_buffer_attr *a, pa_buffer_attr *a,
pa_cvolume *volume, pa_cvolume *volume,
pa_bool_t muted, pa_bool_t muted,
@ -1067,12 +1068,14 @@ static playback_stream* playback_stream_new(
data.driver = __FILE__; data.driver = __FILE__;
data.module = c->options->module; data.module = c->options->module;
data.client = c->client; data.client = c->client;
if (sink) { if (sink)
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, TRUE);
data.save_sink = TRUE; if (pa_sample_spec_valid(ss))
} pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_sample_spec(&data, ss); if (pa_channel_map_valid(map))
pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_channel_map(&data, map);
if (formats)
pa_sink_input_new_data_set_formats(&data, formats);
if (volume) { if (volume) {
pa_sink_input_new_data_set_volume(&data, volume); pa_sink_input_new_data_set_volume(&data, volume);
data.volume_is_absolute = !relative_volume; data.volume_is_absolute = !relative_volume;
@ -1846,6 +1849,10 @@ static pa_tagstruct *reply_new(uint32_t tag) {
return reply; return reply;
} }
static void free_format_info(pa_format_info *f, void *userdata) {
pa_format_info_free(f);
}
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
playback_stream *s; playback_stream *s;
@ -1876,9 +1883,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
passthrough = FALSE; passthrough = FALSE;
pa_sink_input_flags_t flags = 0; pa_sink_input_flags_t flags = 0;
pa_proplist *p; pa_proplist *p = NULL;
pa_bool_t volume_set = TRUE; pa_bool_t volume_set = TRUE;
int ret = PA_ERR_INVALID; int ret = PA_ERR_INVALID;
uint8_t n_formats = 0;
pa_format_info *format;
pa_idxset *formats = NULL;
uint32_t i;
pa_native_connection_assert_ref(c); pa_native_connection_assert_ref(c);
pa_assert(t); pa_assert(t);
@ -1901,17 +1912,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
PA_TAG_INVALID) < 0) { PA_TAG_INVALID) < 0) {
protocol_error(c); protocol_error(c);
return; goto error;
} }
CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
p = pa_proplist_new(); p = pa_proplist_new();
@ -1930,8 +1938,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_get_boolean(t, &variable_rate) < 0) { pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1940,9 +1947,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &muted) < 0 || if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
pa_tagstruct_get_proplist(t, p) < 0) { pa_tagstruct_get_proplist(t, p) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1950,9 +1957,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &volume_set) < 0 || if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
pa_tagstruct_get_boolean(t, &early_requests) < 0) { pa_tagstruct_get_boolean(t, &early_requests) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1961,18 +1968,18 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &muted_set) < 0 || if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 || pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) { pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
if (c->version >= 17) { if (c->version >= 17) {
if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) { if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -1980,31 +1987,52 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) { if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
} }
if (c->version >= 21) {
if (pa_tagstruct_getu8(t, &n_formats) < 0) {
protocol_error(c);
goto error;
}
if (n_formats)
formats = pa_idxset_new(NULL, NULL);
for (i = 0; i < n_formats; i++) {
format = pa_format_info_new();
if (pa_tagstruct_get_format_info(t, format) < 0) {
protocol_error(c);
goto error;
}
pa_idxset_put(formats, format, NULL);
}
}
CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, n_formats > 0 || (map.channels == ss.channels && volume.channels == ss.channels), tag, PA_ERR_INVALID);
CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
/* XXX: add checks on formats. At least inverse checks of the 3 above */
if (!pa_tagstruct_eof(t)) { if (!pa_tagstruct_eof(t)) {
protocol_error(c); protocol_error(c);
pa_proplist_free(p); goto error;
return;
} }
if (sink_index != PA_INVALID_INDEX) { if (sink_index != PA_INVALID_INDEX) {
if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) { if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p); goto error;
return;
} }
} else if (sink_name) { } else if (sink_name) {
if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) { if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
pa_proplist_free(p); goto error;
return;
} }
} }
@ -2025,7 +2053,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
* flag. For older versions we synthesize it here */ * flag. For older versions we synthesize it here */
muted_set = muted_set || muted; muted_set = muted_set || muted;
s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret); s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret);
pa_proplist_free(p); pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret); CHECK_VALIDITY(c->pstream, s, tag, ret);
@ -2064,7 +2092,26 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (c->version >= 13) if (c->version >= 13)
pa_tagstruct_put_usec(reply, s->configured_sink_latency); pa_tagstruct_put_usec(reply, s->configured_sink_latency);
if (c->version >= 21) {
/* Send back the format we negotiated */
if (s->sink_input->format)
pa_tagstruct_put_format_info(reply, s->sink_input->format);
else {
pa_format_info *f = pa_format_info_new();
pa_tagstruct_put_format_info(reply, f);
pa_format_info_free(f);
}
}
pa_pstream_send_tagstruct(c->pstream, reply); pa_pstream_send_tagstruct(c->pstream, reply);
return;
error:
if (p)
pa_proplist_free(p);
if (formats)
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
return;
} }
static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {

View file

@ -538,7 +538,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
data.driver = __FILE__; data.driver = __FILE__;
data.module = o->module; data.module = o->module;
data.client = c->client; data.client = c->client;
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec); pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);

View file

@ -31,6 +31,7 @@
#include <pulse/utf8.h> #include <pulse/utf8.h>
#include <pulse/xmalloc.h> #include <pulse/xmalloc.h>
#include <pulse/util.h> #include <pulse/util.h>
#include <pulse/internal.h>
#include <pulsecore/sample-util.h> #include <pulsecore/sample-util.h>
#include <pulsecore/core-subscribe.h> #include <pulsecore/core-subscribe.h>
@ -146,8 +147,74 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu
data->muted = !!mute; data->muted = !!mute;
} }
void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { static void free_format_info(pa_format_info *f, void *userdata) {
pa_format_info_free(f);
}
pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save) {
pa_bool_t ret = TRUE;
pa_idxset *formats = NULL;
pa_assert(data); pa_assert(data);
pa_assert(s);
if (!data->req_formats) {
/* We're not working with the extended API */
data->sink = s;
data->save_sink = save;
} else {
/* Extended API: let's see if this sink supports the formats the client can provide */
formats = pa_sink_check_formats(s, data->req_formats);
if (formats && !pa_idxset_isempty(formats)) {
/* Sink supports at least one of the requested formats */
data->sink = s;
data->save_sink = save;
if (data->nego_formats)
pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
data->nego_formats = formats;
} else {
/* Sink doesn't support any of the formats requested by the client */
if (formats)
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
ret = FALSE;
}
}
return ret;
}
pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats) {
pa_assert(data);
pa_assert(formats);
if (data->req_formats)
pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
data->req_formats = formats;
if (data->sink) {
/* Trigger format negotiation */
return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink);
}
return TRUE;
}
void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
pa_format_info *f;
int i;
pa_assert(data);
if (data->req_formats)
pa_idxset_free(data->req_formats, (pa_free2_cb_t) free_format_info, NULL);
if (data->nego_formats)
pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
if (data->format)
pa_format_info_free(data->format);
pa_proplist_free(data->proplist); pa_proplist_free(data->proplist);
} }
@ -189,6 +256,8 @@ int pa_sink_input_new(
pa_channel_map original_cm; pa_channel_map original_cm;
int r; int r;
char *pt; char *pt;
pa_sample_spec ss;
pa_channel_map map;
pa_assert(_i); pa_assert(_i);
pa_assert(core); pa_assert(core);
@ -201,14 +270,43 @@ int pa_sink_input_new(
if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) if (data->origin_sink && (data->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER))
data->volume_writable = FALSE; data->volume_writable = FALSE;
if (!data->req_formats) {
/* From this point on, we want to work only with formats, and get back
* to using the sample spec and channel map after all decisions w.r.t.
* routing are complete. */
pa_idxset *tmp = pa_idxset_new(NULL, NULL);
pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec, &data->channel_map);
pa_idxset_put(tmp, f, NULL);
pa_sink_input_new_data_set_formats(data, tmp);
}
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0) if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
return r; return r;
pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
if (!data->sink) { if (!data->sink)
data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE);
data->save_sink = FALSE;
/* Routing's done, we have a sink. Now let's fix the format and set up the
* sample spec */
pa_return_val_if_fail(data->format || (data->nego_formats && !pa_idxset_isempty(data->nego_formats)), -PA_ERR_INVALID);
/* If something didn't pick a format for us, pick the top-most format since
* we assume this is sorted in priority order */
if (!data->format)
data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
/* Now populate the sample spec and format according to the final
* format that we've negotiated */
if (PA_LIKELY(data->format->encoding == PA_ENCODING_PCM)) {
pa_format_info_to_sample_spec(data->format, &ss, &map);
pa_sink_input_new_data_set_sample_spec(data, &ss);
if (pa_channel_map_valid(&map))
pa_sink_input_new_data_set_channel_map(data, &map);
} else {
pa_format_info_to_sample_spec_fake(data->format, &ss);
pa_sink_input_new_data_set_sample_spec(data, &ss);
/* XXX: this is redundant - we can just check the encoding */
data->flags |= PA_SINK_INPUT_PASSTHROUGH;
} }
pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY); pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
@ -329,6 +427,7 @@ int pa_sink_input_new(
i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
i->sample_spec = data->sample_spec; i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map; i->channel_map = data->channel_map;
i->format = pa_format_info_copy(data->format);
if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) { if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
pa_cvolume remapped; pa_cvolume remapped;
@ -564,6 +663,9 @@ static void sink_input_free(pa_object *o) {
if (i->thread_info.resampler) if (i->thread_info.resampler)
pa_resampler_free(i->thread_info.resampler); pa_resampler_free(i->thread_info.resampler);
if (i->format)
pa_format_info_free(i->format);
if (i->proplist) if (i->proplist)
pa_proplist_free(i->proplist); pa_proplist_free(i->proplist);

View file

@ -28,6 +28,7 @@
typedef struct pa_sink_input pa_sink_input; typedef struct pa_sink_input pa_sink_input;
#include <pulse/sample.h> #include <pulse/sample.h>
#include <pulse/format.h>
#include <pulsecore/hook-list.h> #include <pulsecore/hook-list.h>
#include <pulsecore/memblockq.h> #include <pulsecore/memblockq.h>
#include <pulsecore/resampler.h> #include <pulsecore/resampler.h>
@ -92,6 +93,7 @@ struct pa_sink_input {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_format_info *format;
pa_sink_input *sync_prev, *sync_next; pa_sink_input *sync_prev, *sync_next;
@ -279,6 +281,9 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec; pa_sample_spec sample_spec;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_format_info *format;
pa_idxset *req_formats;
pa_idxset *nego_formats;
pa_cvolume volume, volume_factor, volume_factor_sink; pa_cvolume volume, volume_factor, volume_factor_sink;
pa_bool_t muted:1; pa_bool_t muted:1;
@ -303,6 +308,8 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save);
pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
void pa_sink_input_new_data_done(pa_sink_input_new_data *data); void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
/* To be called by the implementing module only */ /* To be called by the implementing module only */

View file

@ -299,7 +299,7 @@ int pa_play_file(
u->readf_function = pa_sndfile_readf_function(&ss); u->readf_function = pa_sndfile_readf_function(&ss);
pa_sink_input_new_data_init(&data); pa_sink_input_new_data_init(&data);
data.sink = sink; pa_sink_input_new_data_set_sink(&data, sink, FALSE);
data.driver = __FILE__; data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_sample_spec(&data, &ss);
pa_sink_input_new_data_set_channel_map(&data, &cm); pa_sink_input_new_data_set_channel_map(&data, &cm);