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/sink-input.c') 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 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/sink-input.c | 108 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 3 deletions(-) (limited to 'src/pulsecore/sink-input.c') 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); -- 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 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) (limited to 'src/pulsecore/sink-input.c') 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) -- 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/sink-input.c') 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/sink-input.c') 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/sink-input.c') 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/sink-input.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src/pulsecore/sink-input.c') 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 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 +++++ 1 file changed, 5 insertions(+) (limited to 'src/pulsecore/sink-input.c') 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)) -- 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/sink-input.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/pulsecore/sink-input.c') 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 */ -- 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 +++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) (limited to 'src/pulsecore/sink-input.c') 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); -- 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/sink-input.c') 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/sink-input.c') 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/sink-input.c') 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/sink-input.c') 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