From 881074907e04b23d713815fe11093e9081fe71e8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 8 Mar 2011 15:12:56 +0530 Subject: sink-input: Don't resample passthrough inputs --- src/pulsecore/sink-input.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 1931d994..194ec99e 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -295,18 +295,20 @@ int pa_sink_input_new( !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) || !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) { - if (!(resampler = pa_resampler_new( - core->mempool, - &data->sample_spec, &data->channel_map, - &data->sink->sample_spec, &data->sink->channel_map, - data->resample_method, - ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | - ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | - (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | - (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { - pa_log_warn("Unsupported resampling operation."); - return -PA_ERR_NOTSUPPORTED; - } + /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */ + if (!(data->flags & PA_SINK_INPUT_PASSTHROUGH)) /* no resampler for passthrough content */ + if (!(resampler = pa_resampler_new( + core->mempool, + &data->sample_spec, &data->channel_map, + &data->sink->sample_spec, &data->sink->channel_map, + data->resample_method, + ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | + (core->disable_remixing || (data->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { + pa_log_warn("Unsupported resampling operation."); + return -PA_ERR_NOTSUPPORTED; + } } i = pa_msgobject_new(pa_sink_input); -- cgit From f4d1f2bdcfe5e598e926c190c80ec4c6d38dc9f4 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Fri, 25 Feb 2011 12:44:53 +0530 Subject: sink: Trivial typo fix --- src/pulsecore/sink.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index ef698813..b61ba333 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -161,7 +161,7 @@ struct pa_sink { * s->real_volume and/or s->soft_volume so that they together * match the actual hardware volume that will be set later in the * write_volume callback. */ - void (*set_volume)(pa_sink *s); /* dito */ + void (*set_volume)(pa_sink *s); /* ditto */ /* Sink drivers that set PA_SINK_SYNC_VOLUME must provide this * callback. This callback is not used with sinks that do not set @@ -174,30 +174,30 @@ struct pa_sink { * not called automatically - it is the driver's responsibility to * schedule that function to be called at the right times in the * IO thread. */ - void (*write_volume)(pa_sink *s); /* dito */ + void (*write_volume)(pa_sink *s); /* ditto */ /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME * flag is set otherwise from main loop context. If refresh_mute is FALSE * neither this function is called nor a message is sent.*/ - void (*get_mute)(pa_sink *s); /* dito */ + void (*get_mute)(pa_sink *s); /* ditto */ /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME * flag is set otherwise from main loop context. */ - void (*set_mute)(pa_sink *s); /* dito */ + void (*set_mute)(pa_sink *s); /* ditto */ /* Called when a rewind request is issued. Called from IO thread * context. */ - void (*request_rewind)(pa_sink *s); /* dito */ + void (*request_rewind)(pa_sink *s); /* ditto */ /* Called when a the requested latency is changed. Called from IO * thread context. */ - void (*update_requested_latency)(pa_sink *s); /* dito */ + void (*update_requested_latency)(pa_sink *s); /* ditto */ /* Called whenever the port shall be changed. Called from main * thread. */ - int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */ + int (*set_port)(pa_sink *s, pa_device_port *port); /* ditto */ /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ -- 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/pulsecore/tagstruct.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/tagstruct.h | 6 +++++- 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index 804b9f90..5694a0da 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -291,6 +291,17 @@ void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) { pa_tagstruct_puts(t, NULL); } +void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) { + pa_assert(t); + pa_assert(f); + + extend(t, 1); + + t->data[t->length++] = PA_TAG_FORMAT_INFO; + pa_tagstruct_putu8(t, (uint8_t) f->encoding); + pa_tagstruct_put_proplist(t, f->plist); +} + int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { int error = 0; size_t n; @@ -631,6 +642,37 @@ fail: return -1; } +int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) { + size_t saved_rindex; + uint8_t encoding; + + pa_assert(t); + pa_assert(f); + + if (t->rindex+1 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_FORMAT_INFO) + return -1; + + saved_rindex = t->rindex; + t->rindex++; + + if (pa_tagstruct_getu8(t, &encoding) < 0) + goto fail; + + f->encoding = encoding; + + if (pa_tagstruct_get_proplist(t, f->plist) < 0) + goto fail; + + return 0; + +fail: + t->rindex = saved_rindex; + return -1; +} + void pa_tagstruct_put(pa_tagstruct *t, ...) { va_list va; pa_assert(t); diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index b6553ada..0091eeb9 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -58,7 +59,8 @@ enum { PA_TAG_CHANNEL_MAP = 'm', PA_TAG_CVOLUME = 'v', PA_TAG_PROPLIST = 'P', - PA_TAG_VOLUME = 'V' + PA_TAG_VOLUME = 'V', + PA_TAG_FORMAT_INFO = 'f', }; pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); @@ -84,6 +86,7 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map); void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p); void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume); +void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f); int pa_tagstruct_get(pa_tagstruct *t, ...); @@ -101,5 +104,6 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map); int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p); int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v); +int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f); #endif -- cgit From 47e0f91aa2ca6eb3ea8b7be8aa03cd03a28c3fbe Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Feb 2011 13:00:20 +0530 Subject: sink: Extend API for compressed formats support This adds a get_formats() vfunc for sinks to provide a list of formats they can support. pa_sink_check_formats() can be used during or after routing to determine what formats from a stream the sink can support. --- src/pulsecore/sink.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/sink.h | 7 +++++++ 2 files changed, 58 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 839b7d44..345d090c 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -177,6 +178,7 @@ static void reset_callbacks(pa_sink *s) { s->request_rewind = NULL; s->update_requested_latency = NULL; s->set_port = NULL; + s->get_formats = NULL; } /* Called from main context */ @@ -3258,3 +3260,52 @@ static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) { } pa_sink_volume_change_apply(s, NULL); } + +/* Called from the main thread */ +/* Gets the list of formats supported by the sink. The members and idxset must + * be freed by the caller. */ +pa_idxset* pa_sink_get_formats(pa_sink *s) { + pa_idxset *ret; + + pa_assert(s); + + if (s->get_formats) { + /* Sink supports format query, all is good */ + ret = s->get_formats(s); + } else { + /* Sink doesn't support format query, so assume it does PCM */ + pa_format_info *f = pa_format_info_new(); + f->encoding = PA_ENCODING_PCM; + + ret = pa_idxset_new(NULL, NULL); + pa_idxset_put(ret, f, NULL); + } + + return ret; +} + +/* Called from the main thread */ +/* Calculates the intersection between formats supported by the sink and + * in_formats, and returns these, in the order of the sink's formats. */ +pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) { + pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats; + pa_format_info *f_sink, *f_in; + uint32_t i, j; + + pa_assert(s); + + if (!in_formats || pa_idxset_isempty(in_formats)) + goto done; + + sink_formats = pa_sink_get_formats(s); + + PA_IDXSET_FOREACH(f_sink, sink_formats, i) { + PA_IDXSET_FOREACH(f_in, in_formats, j) { + if (pa_format_info_is_compatible(f_sink, f_in)) + pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL); + } + } + +done: + return out_formats; +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index b61ba333..a96dd90a 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -199,6 +199,10 @@ struct pa_sink { * thread. */ int (*set_port)(pa_sink *s, pa_device_port *port); /* ditto */ + /* Called to get the list of formats supported by the sink, sorted + * in descending order of preference. */ + pa_idxset* (*get_formats)(pa_sink *s); /* ditto */ + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -398,6 +402,9 @@ pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q); void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); void pa_sink_move_all_fail(pa_queue *q); +pa_idxset* pa_sink_get_formats(pa_sink *s); +pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats); + /*** To be called exclusively by the sink driver, from IO context */ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); -- 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/pulsecore/play-memblockq.c | 2 +- src/pulsecore/protocol-esound.c | 2 +- src/pulsecore/protocol-native.c | 107 ++++++++++++++++++++++++++----------- src/pulsecore/protocol-simple.c | 2 +- src/pulsecore/sink-input.c | 108 ++++++++++++++++++++++++++++++++++++-- src/pulsecore/sink-input.h | 7 +++ src/pulsecore/sound-file-stream.c | 2 +- 7 files changed, 193 insertions(+), 37 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 66e47ea4..9455340d 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -202,7 +202,7 @@ pa_sink_input* pa_memblockq_sink_input_new( u->memblockq = NULL; pa_sink_input_new_data_init(&data); - data.sink = sink; + pa_sink_input_new_data_set_sink(&data, sink, FALSE); data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 66fd73c8..c2af77c2 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -426,7 +426,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void sdata.driver = __FILE__; sdata.module = c->options->module; 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(&c->sink_input, c->protocol->core, &sdata); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 4952ee41..83321790 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1014,6 +1014,7 @@ static playback_stream* playback_stream_new( pa_sink *sink, pa_sample_spec *ss, pa_channel_map *map, + pa_idxset *formats, pa_buffer_attr *a, pa_cvolume *volume, pa_bool_t muted, @@ -1067,12 +1068,14 @@ static playback_stream* playback_stream_new( data.driver = __FILE__; data.module = c->options->module; data.client = c->client; - if (sink) { - data.sink = sink; - data.save_sink = TRUE; - } - pa_sink_input_new_data_set_sample_spec(&data, ss); - pa_sink_input_new_data_set_channel_map(&data, map); + if (sink) + pa_sink_input_new_data_set_sink(&data, sink, TRUE); + if (pa_sample_spec_valid(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); + if (formats) + pa_sink_input_new_data_set_formats(&data, formats); if (volume) { pa_sink_input_new_data_set_volume(&data, volume); data.volume_is_absolute = !relative_volume; @@ -1846,6 +1849,10 @@ static pa_tagstruct *reply_new(uint32_t tag) { 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) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); playback_stream *s; @@ -1876,9 +1883,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u passthrough = FALSE; pa_sink_input_flags_t flags = 0; - pa_proplist *p; + pa_proplist *p = NULL; pa_bool_t volume_set = TRUE; 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_assert(t); @@ -1901,17 +1912,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u PA_TAG_INVALID) < 0) { protocol_error(c); - return; + goto error; } 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_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, 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, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); 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) { protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -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 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_proplist(t, p) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -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 || pa_tagstruct_get_boolean(t, &early_requests) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -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 || pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 || pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } if (c->version >= 17) { if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -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 ) { protocol_error(c); - pa_proplist_free(p); - return; + goto error; + } + } + + 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)) { protocol_error(c); - pa_proplist_free(p); - return; + goto error; } if (sink_index != PA_INVALID_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_proplist_free(p); - return; + goto error; } } else if (sink_name) { if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto error; } } @@ -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 */ 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); 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) 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); + 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) { diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 77277e13..c1aaa813 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -538,7 +538,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp data.driver = __FILE__; data.module = o->module; 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_sink_input_new_data_set_sample_spec(&data, &o->sample_spec); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 194ec99e..3df499e4 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -146,9 +147,75 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu data->muted = !!mute; } +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(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); } @@ -189,6 +256,8 @@ int pa_sink_input_new( pa_channel_map original_cm; int r; char *pt; + pa_sample_spec ss; + pa_channel_map map; pa_assert(_i); 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)) 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) return r; pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); - if (!data->sink) { - data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK); - data->save_sink = FALSE; + if (!data->sink) + pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_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); @@ -329,6 +427,7 @@ int pa_sink_input_new( i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; i->sample_spec = data->sample_spec; 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)) { pa_cvolume remapped; @@ -564,6 +663,9 @@ static void sink_input_free(pa_object *o) { if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); + if (i->format) + pa_format_info_free(i->format); + if (i->proplist) pa_proplist_free(i->proplist); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 11f6608f..72a1d5a2 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -28,6 +28,7 @@ typedef struct pa_sink_input pa_sink_input; #include +#include #include #include #include @@ -92,6 +93,7 @@ struct pa_sink_input { pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_format_info *format; pa_sink_input *sync_prev, *sync_next; @@ -279,6 +281,9 @@ typedef struct pa_sink_input_new_data { pa_sample_spec sample_spec; 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_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_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); +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); /* To be called by the implementing module only */ diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 1ec19425..d33eca5a 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -299,7 +299,7 @@ int pa_play_file( u->readf_function = pa_sndfile_readf_function(&ss); pa_sink_input_new_data_init(&data); - data.sink = sink; + pa_sink_input_new_data_set_sink(&data, sink, FALSE); data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_channel_map(&data, &cm); -- 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/pulsecore/sink-input.c | 41 ++++++++++++----------------------------- src/pulsecore/sink.c | 37 ++++++++++++++++++++++--------------- src/pulsecore/sink.h | 4 ++++ 3 files changed, 38 insertions(+), 44 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 3df499e4..b05373f8 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -50,36 +50,19 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject); static void sink_input_free(pa_object *o); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); -static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) { +static int check_passthrough_connection(pa_format_info *format, pa_sink *dest) { - if (dest->flags & PA_SINK_PASSTHROUGH) { - - if (pa_idxset_size(dest->inputs) > 0) { - - pa_sink_input *alt_i; - uint32_t idx; - - alt_i = pa_idxset_first(dest->inputs, &idx); - - /* only need to check the first input is not PASSTHROUGH */ - if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) { - pa_log_warn("Sink is already connected to PASSTHROUGH input"); - return -PA_ERR_BUSY; - } - - /* Current inputs are PCM, check new input is not PASSTHROUGH */ - if (flags & PA_SINK_INPUT_PASSTHROUGH) { - pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT"); - return -PA_ERR_BUSY; - } - } + if (pa_sink_is_passthrough(dest)) { + pa_log_warn("Sink is already connected to PASSTHROUGH input"); + return -PA_ERR_BUSY; + } - } else { - if (flags & PA_SINK_INPUT_PASSTHROUGH) { - pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities"); - return -PA_ERR_INVALID; - } + /* If current input(s) exist, check new input is not PASSTHROUGH */ + if (pa_idxset_size(dest->inputs) > 0 && !pa_format_info_is_pcm(format)) { + pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT"); + return -PA_ERR_BUSY; } + return PA_OK; } @@ -313,7 +296,7 @@ int pa_sink_input_new( pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE); pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID); - r = check_passthrough_connection(data->flags, data->sink); + r = check_passthrough_connection(data->format, data->sink); pa_return_val_if_fail(r == PA_OK, r); if (!data->sample_spec_is_set) @@ -1355,7 +1338,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return FALSE; } - if (check_passthrough_connection(i->flags, dest) < 0) + if (check_passthrough_connection(i->format, dest) < 0) return FALSE; if (i->may_move_to) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 345d090c..33923e1f 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1241,6 +1241,24 @@ pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s) { return (s->flags & PA_SINK_FLAT_VOLUME); } +/* Called from main context */ +pa_bool_t pa_sink_is_passthrough(pa_sink *s) { + pa_sink_input *alt_i; + uint32_t idx; + + pa_sink_assert_ref(s); + + /* one and only one PASSTHROUGH input can possibly be connected */ + if (pa_idxset_size(s->inputs) == 1) { + alt_i = pa_idxset_first(s->inputs, &idx); + + if (!pa_format_info_is_pcm(alt_i->format)) + return TRUE; + } + + return FALSE; +} + /* Called from main context. */ static void compute_reference_ratio(pa_sink_input *i) { unsigned c = 0; @@ -1632,21 +1650,10 @@ void pa_sink_set_volume( pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec)); /* make sure we don't change the volume when a PASSTHROUGH input is connected */ - if (s->flags & PA_SINK_PASSTHROUGH) { - pa_sink_input *alt_i; - uint32_t idx; - - /* one and only one PASSTHROUGH input can possibly be connected */ - if (pa_idxset_size(s->inputs) == 1) { - - alt_i = pa_idxset_first(s->inputs, &idx); - - if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) { - /* FIXME: Need to notify client that volume control is disabled */ - pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input"); - return; - } - } + if (pa_sink_is_passthrough(s)) { + /* FIXME: Need to notify client that volume control is disabled */ + pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input"); + return; } /* In case of volume sharing, the volume is set for the root sink first, diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index a96dd90a..492abf68 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -382,6 +382,10 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) /* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */ pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s); +/* Is the sink in passthrough mode? (that is, is there a passthrough sink input + * connected to this sink? */ +pa_bool_t pa_sink_is_passthrough(pa_sink *s); + void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save); const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh); -- cgit From 5d5523604f03ac7d6b8f67569ed4ac6c06c72463 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 10:53:45 +0530 Subject: sink-input: Minor cleanups Removes a couple of warnings and simplifies the assertion logic that verifies format negotiation was successful. --- src/pulsecore/sink-input.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index b05373f8..92e364f8 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -185,9 +185,6 @@ pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_id } 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) @@ -273,11 +270,14 @@ int pa_sink_input_new( /* 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) + if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats)) data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL)); + + pa_return_val_if_fail(data->format, -PA_ERR_INVALID); + /* 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)) { -- cgit From 8ec0548f5f4c7ce3213e4639722d9cb78fc13b90 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 11:00:49 +0530 Subject: sink-input: Return NOTSUPPORTED if format negotiation fails This is easier for clients to grok than INVALID. --- src/pulsecore/sink-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 92e364f8..1706a7fb 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -276,7 +276,7 @@ int pa_sink_input_new( if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats)) data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL)); - pa_return_val_if_fail(data->format, -PA_ERR_INVALID); + pa_return_val_if_fail(data->format, -PA_ERR_NOTSUPPORTED); /* Now populate the sample spec and format according to the final * format that we've negotiated */ -- 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/pulsecore/sink-input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 1706a7fb..a4659558 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -281,12 +281,12 @@ int pa_sink_input_new( /* 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_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map), -PA_ERR_INVALID); 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_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID); 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; -- 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/pulsecore/protocol-native.c | 7 ++----- src/pulsecore/sink-input.c | 14 +++++--------- 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 83321790..ee2bc4f5 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1849,10 +1850,6 @@ static pa_tagstruct *reply_new(uint32_t tag) { 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) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); playback_stream *s; @@ -2110,7 +2107,7 @@ error: if (p) pa_proplist_free(p); if (formats) - pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL); + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); return; } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index a4659558..0f34e650 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -130,10 +130,6 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu data->muted = !!mute; } -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; @@ -154,12 +150,12 @@ pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink 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); + pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, 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); + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); ret = FALSE; } } @@ -172,7 +168,7 @@ pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_id pa_assert(formats); if (data->req_formats) - pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL); + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); data->req_formats = formats; @@ -188,10 +184,10 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { pa_assert(data); if (data->req_formats) - pa_idxset_free(data->req_formats, (pa_free2_cb_t) free_format_info, NULL); + pa_idxset_free(data->req_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); if (data->nego_formats) - pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL); + pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); if (data->format) pa_format_info_free(data->format); -- cgit From 8b3e68a202084467086af6c83e89c07adb9aa18a Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 11:16:48 +0530 Subject: sink: Fix leak in pa_sink_check_formats() We weren't freeing the sink formats idxset. --- src/pulsecore/sink.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 33923e1f..2b9402ab 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -3295,7 +3295,7 @@ pa_idxset* pa_sink_get_formats(pa_sink *s) { /* Calculates the intersection between formats supported by the sink and * in_formats, and returns these, in the order of the sink's formats. */ pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) { - pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats; + pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL; pa_format_info *f_sink, *f_in; uint32_t i, j; @@ -3314,5 +3314,8 @@ pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) { } done: + if (sink_formats) + pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + return out_formats; } -- cgit From 658a9153f094db9a30ac94428f4e46985e7096eb Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 2 Mar 2011 11:38:01 +0530 Subject: sink-input: Kill passthrough streams if moving to an unsupported sink This will eventually be replaced by a hook to let clients know that the stream has moved so that they can gracefully reconnect and renegotiate a supported format. --- src/pulsecore/sink-input.c | 5 +++++ src/pulsecore/sink.c | 27 +++++++++++++++++++++++++++ src/pulsecore/sink.h | 1 + 3 files changed, 33 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 0f34e650..6e1b81f4 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1552,6 +1552,11 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { if (!pa_sink_input_may_move_to(i, dest)) return -PA_ERR_NOTSUPPORTED; + if (!pa_format_info_is_pcm(i->format) && !pa_sink_check_format(dest, i->format)) { + /* FIXME: Fire a message here so the client can renegotiate */ + return -PA_ERR_NOTSUPPORTED; + } + if (i->thread_info.resampler && pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) && pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map)) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 2b9402ab..626c7278 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -3291,6 +3291,33 @@ pa_idxset* pa_sink_get_formats(pa_sink *s) { return ret; } +/* Called from the main thread */ +/* Checks if the sink can accept this format */ +pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f) +{ + pa_idxset *sink_formats = NULL; + pa_format_info *f_sink; + uint32_t i; + pa_bool_t ret = FALSE; + + pa_assert(s); + pa_assert(f); + + sink_formats = pa_sink_get_formats(s); + + PA_IDXSET_FOREACH(f_sink, sink_formats, i) { + if (pa_format_info_is_compatible(f_sink, f)) { + ret = TRUE; + break; + } + } + + if (sink_formats) + pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + + return ret; +} + /* Called from the main thread */ /* Calculates the intersection between formats supported by the sink and * in_formats, and returns these, in the order of the sink's formats. */ diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 492abf68..cbff5cae 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -407,6 +407,7 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save); void pa_sink_move_all_fail(pa_queue *q); pa_idxset* pa_sink_get_formats(pa_sink *s); +pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f); pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats); /*** To be called exclusively by the sink driver, from IO context */ -- 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/pulsecore/protocol-native.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ee2bc4f5..956670be 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2008,10 +2008,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u } } - 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 (n_formats == 0) { + CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); + } else { + PA_IDXSET_FOREACH(format, formats, i) { + CHECK_VALIDITY(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID); + } + } if (!pa_tagstruct_eof(t)) { protocol_error(c); -- cgit From 4c9d53f3f50162e19ad7934e62c8f35453bdbfbc Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Thu, 3 Mar 2011 18:35:14 +0530 Subject: sink: Trivial typo fix in comment --- src/pulsecore/sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 626c7278..5959a5ce 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -2153,7 +2153,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* If you change anything here, make sure to change the * sink input handling a few lines down at - * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */ + * PA_SINK_MESSAGE_START_MOVE, too. */ if (i->detach) i->detach(i); -- cgit From f94bcae6bd6a3021a9474de5703360817f1ec1a8 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Thu, 3 Mar 2011 19:02:45 +0530 Subject: core: Suspend monitor when a sink enters passthrough mode In most cases it is expected that clients cannot consume compressed data from monitor sources, so we suspend the monitor source when the sink goes into passthrough mode. Eventually, when the extended API includes client notifications for changed formats, we should emit a notification on the monitor so that clients can decide what they want to do when this happens (disconnect or consume the data anyway). --- src/pulsecore/core.h | 1 + src/pulsecore/sink-input.c | 16 ++++++++++++++++ src/pulsecore/source.c | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 358b98d7..6b25fbad 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -35,6 +35,7 @@ typedef enum pa_suspend_cause { PA_SUSPEND_APPLICATION = 2, /* Used by the device reservation logic */ PA_SUSPEND_IDLE = 4, /* Used by module-suspend-on-idle */ PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */ + PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */ PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */ } pa_suspend_cause_t; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 6e1b81f4..d77eb2ca 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -599,6 +599,10 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (i->sink->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); + + /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ + if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); } reset_callbacks(i); @@ -689,6 +693,10 @@ void pa_sink_input_put(pa_sink_input *i) { set_real_ratio(i, &i->volume); } + /* If we're entering passthrough mode, disable the monitor */ + if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + i->thread_info.soft_volume = i->soft_volume; i->thread_info.muted = i->muted; @@ -1380,6 +1388,10 @@ int pa_sink_input_start_move(pa_sink_input *i) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); + /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ + if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); + pa_sink_update_status(i->sink); pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map); i->sink = NULL; @@ -1621,6 +1633,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); + /* If we're entering passthrough mode, disable the monitor */ + if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); + pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); /* Notify everyone */ diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 92fb80e0..15a5b8d9 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -514,7 +514,7 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause) pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(cause != 0); - if (s->monitor_of) + if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH) return -PA_ERR_NOTSUPPORTED; if (suspend) -- 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/pulsecore/protocol-native.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 956670be..f151bd21 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2984,6 +2984,19 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL); } + + if (c->version >= 21) { + uint32_t i; + pa_format_info *f; + pa_idxset *formats = pa_sink_get_formats(sink); + + pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats)); + PA_IDXSET_FOREACH(f, formats, i) { + pa_tagstruct_put_format_info(t, f); + } + + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + } } static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) { -- 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/pulsecore/protocol-native.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index f151bd21..59b87242 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -3153,6 +3153,8 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_put_boolean(t, has_volume); pa_tagstruct_put_boolean(t, s->volume_writable); } + if (c->version >= 21) + pa_tagstruct_put_format_info(t, s->format); } static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) { -- cgit From 4fb68b91acef3cb37c014814d9e9de8ca9f22bf4 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 16 Mar 2011 16:08:23 +0530 Subject: core: Factor out passthrough checks into their own functions Since we currently have two mechanisms to signal a passthrough connection (non-PCM format or PA_SINK_INPUT_PASSTHROUGH flag), we move all the related checks into functions and use those everywhere. This makes things more consistent, and should we decide to get rid of the flag, we only need to change pa_sink_input_*_is_passthrough() accordingly. --- src/pulsecore/sink-input.c | 50 +++++++++++++++++++++++++++++++++------------- src/pulsecore/sink-input.h | 2 ++ src/pulsecore/sink.c | 2 +- 3 files changed, 39 insertions(+), 15 deletions(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d77eb2ca..5e7cfd13 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -50,15 +50,14 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject); static void sink_input_free(pa_object *o); static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v); -static int check_passthrough_connection(pa_format_info *format, pa_sink *dest) { - +static int check_passthrough_connection(pa_bool_t passthrough, pa_sink *dest) { if (pa_sink_is_passthrough(dest)) { pa_log_warn("Sink is already connected to PASSTHROUGH input"); return -PA_ERR_BUSY; } /* If current input(s) exist, check new input is not PASSTHROUGH */ - if (pa_idxset_size(dest->inputs) > 0 && !pa_format_info_is_pcm(format)) { + if (pa_idxset_size(dest->inputs) > 0 && passthrough) { pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT"); return -PA_ERR_BUSY; } @@ -91,6 +90,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } +pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data) { + pa_assert(data); + + if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format))) + return TRUE; + + if (PA_UNLIKELY(data->flags & PA_SINK_INPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); pa_assert(data->volume_writable); @@ -284,15 +295,13 @@ int pa_sink_input_new( } else { pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID); 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(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE); pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID); - r = check_passthrough_connection(data->format, data->sink); + r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink); pa_return_val_if_fail(r == PA_OK, r); if (!data->sample_spec_is_set) @@ -373,7 +382,7 @@ int pa_sink_input_new( !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) { /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */ - if (!(data->flags & PA_SINK_INPUT_PASSTHROUGH)) /* no resampler for passthrough content */ + if (!pa_sink_input_new_data_is_passthrough(data)) /* no resampler for passthrough content */ if (!(resampler = pa_resampler_new( core->mempool, &data->sample_spec, &data->channel_map, @@ -601,7 +610,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ - if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); } @@ -694,7 +703,7 @@ void pa_sink_input_put(pa_sink_input *i) { } /* If we're entering passthrough mode, disable the monitor */ - if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); i->thread_info.soft_volume = i->soft_volume; @@ -1178,12 +1187,25 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { /* We don't copy the data to the thread_info data. That's left for someone else to do */ } +/* Called from main or I/O context */ +pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + + if (PA_UNLIKELY(!pa_format_info_is_pcm(i->format))) + return TRUE; + + if (PA_UNLIKELY(i->flags & PA_SINK_INPUT_PASSTHROUGH)) + return TRUE; + + return FALSE; +} + /* Called from main context */ pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_ctl_context(); - return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); + return !pa_sink_input_is_passthrough(i); } /* Called from main context */ @@ -1342,7 +1364,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return FALSE; } - if (check_passthrough_connection(i->format, dest) < 0) + if (check_passthrough_connection(pa_sink_input_is_passthrough(i), dest) < 0) return FALSE; if (i->may_move_to) @@ -1389,7 +1411,7 @@ int pa_sink_input_start_move(pa_sink_input *i) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */ - if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH); pa_sink_update_status(i->sink); @@ -1564,7 +1586,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { if (!pa_sink_input_may_move_to(i, dest)) return -PA_ERR_NOTSUPPORTED; - if (!pa_format_info_is_pcm(i->format) && !pa_sink_check_format(dest, i->format)) { + if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) { /* FIXME: Fire a message here so the client can renegotiate */ return -PA_ERR_NOTSUPPORTED; } @@ -1634,7 +1656,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); /* If we're entering passthrough mode, disable the monitor */ - if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source) + if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source) pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH); pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 72a1d5a2..0ebb74a9 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -304,6 +304,7 @@ typedef struct pa_sink_input_new_data { pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); +pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); 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); @@ -350,6 +351,7 @@ void pa_sink_input_kill(pa_sink_input*i); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); +pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i); pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute); pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 5959a5ce..bc4ddd21 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1252,7 +1252,7 @@ pa_bool_t pa_sink_is_passthrough(pa_sink *s) { if (pa_idxset_size(s->inputs) == 1) { alt_i = pa_idxset_first(s->inputs, &idx); - if (!pa_format_info_is_pcm(alt_i->format)) + if (pa_sink_input_is_passthrough(alt_i)) return TRUE; } -- cgit From a199bfb765147b3938d268a67f671646a16845c3 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Mar 2011 08:45:31 +0530 Subject: sink-input: Don't print an error if a passthrough connection fails The assertion message is misleading, since the passthrough connection can fail for reasons the client has no control over (like other sink inputs being connected). --- src/pulsecore/sink-input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 5e7cfd13..979dc76e 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -302,7 +302,8 @@ int pa_sink_input_new( pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID); r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink); - pa_return_val_if_fail(r == PA_OK, r); + if (r != PA_OK) + return r; if (!data->sample_spec_is_set) data->sample_spec = data->sink->sample_spec; -- cgit From cb3dcb14f868e10c0fde582e9284561af974fab4 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Mon, 28 Mar 2011 08:46:20 +0530 Subject: sink-input: Don't restore volume for passthrough streams --- src/pulsecore/sink-input.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 979dc76e..196d7712 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -319,6 +319,12 @@ int pa_sink_input_new( pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); + /* Don't restore (or save) stream volume for passthrough streams */ + if (!pa_format_info_is_pcm(data->format)) { + data->volume_is_set = FALSE; + data->volume_factor_is_set = FALSE; + } + if (!data->volume_is_set) { pa_cvolume_reset(&data->volume, data->sample_spec.channels); data->volume_is_absolute = FALSE; -- 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/pulsecore/sink-input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 196d7712..f0558f40 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1594,7 +1594,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { return -PA_ERR_NOTSUPPORTED; if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) { - /* FIXME: Fire a message here so the client can renegotiate */ + pa_log_debug("New sink doesn't support stream format, sending format-changed and killing"); + pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, NULL); return -PA_ERR_NOTSUPPORTED; } -- 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/pulsecore/sink-input.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/pulsecore') diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index f0558f40..b21cfae0 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1594,8 +1594,12 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { return -PA_ERR_NOTSUPPORTED; if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) { + pa_proplist *p = pa_proplist_new(); pa_log_debug("New sink doesn't support stream format, sending format-changed and killing"); - pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, NULL); + /* Tell the client what device we want to be on if it is going to + * reconnect */ + pa_proplist_sets(p, "device", dest->name); + pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p); return -PA_ERR_NOTSUPPORTED; } -- cgit