diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index df7b72461..5e84493e7 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -415,7 +415,7 @@ finish: /* a < b -> return -1 * a == b -> return 0 * a > b -> return 1 */ -static int compare_sinks(pa_sink *a, pa_sink *b) { +static int compare_sinks(pa_sink *a, pa_sink *b, bool ignore_configured_virtual_default) { pa_core *core; core = a->core; @@ -429,23 +429,37 @@ static int compare_sinks(pa_sink *a, pa_sink *b) { return 1; /* The policy default sink is preferred over any other sink. */ - if (pa_safe_streq(b->name, core->policy_default_sink)) - return -1; - if (pa_safe_streq(a->name, core->policy_default_sink)) - return 1; + if (pa_safe_streq(b->name, core->policy_default_sink)) { + if (!ignore_configured_virtual_default || !pa_sink_is_filter(b)) + return -1; + } + if (pa_safe_streq(a->name, core->policy_default_sink)) { + if (!ignore_configured_virtual_default || !pa_sink_is_filter(a)) + return 1; + } /* The configured default sink is preferred over any other sink * except the policy default sink. */ - if (pa_safe_streq(b->name, core->configured_default_sink)) - return -1; - if (pa_safe_streq(a->name, core->configured_default_sink)) - return 1; + if (pa_safe_streq(b->name, core->configured_default_sink)) { + if (!ignore_configured_virtual_default || !pa_sink_is_filter(b)) + return -1; + } + if (pa_safe_streq(a->name, core->configured_default_sink)) { + if (!ignore_configured_virtual_default || !pa_sink_is_filter(a)) + return 1; + } if (a->priority < b->priority) return -1; if (a->priority > b->priority) return 1; + /* Let sinks like pipe sink or null sink win against filter sinks */ + if (a->vsink && !b->vsink) + return -1; + if (!a->vsink && b->vsink) + return 1; + /* It's hard to find any difference between these sinks, but maybe one of * them is already the default sink? If so, it's best to keep it as the * default to avoid changing the routing for no good reason. */ @@ -457,11 +471,10 @@ static int compare_sinks(pa_sink *a, pa_sink *b) { return 0; } -void pa_core_update_default_sink(pa_core *core) { +pa_sink *pa_core_find_best_sink(pa_core *core, bool ignore_configured_virtual_default) { pa_sink *best = NULL; pa_sink *sink; uint32_t idx; - pa_sink *old_default_sink; pa_assert(core); @@ -474,10 +487,21 @@ void pa_core_update_default_sink(pa_core *core) { continue; } - if (compare_sinks(sink, best) > 0) + if (compare_sinks(sink, best, ignore_configured_virtual_default) > 0) best = sink; } + return best; +} + +void pa_core_update_default_sink(pa_core *core) { + pa_sink *best; + pa_sink *old_default_sink; + + pa_assert(core); + + best = pa_core_find_best_sink(core, false); + old_default_sink = core->default_sink; if (best == old_default_sink) @@ -503,7 +527,7 @@ void pa_core_update_default_sink(pa_core *core) { /* a < b -> return -1 * a == b -> return 0 * a > b -> return 1 */ -static int compare_sources(pa_source *a, pa_source *b) { +static int compare_sources(pa_source *a, pa_source *b, bool ignore_configured_virtual_default) { pa_core *core; core = a->core; @@ -517,17 +541,25 @@ static int compare_sources(pa_source *a, pa_source *b) { return 1; /* The policy default source is preferred over any other source. */ - if (pa_safe_streq(b->name, core->policy_default_source)) - return -1; - if (pa_safe_streq(a->name, core->policy_default_source)) - return 1; + if (pa_safe_streq(b->name, core->policy_default_source)) { + if (!ignore_configured_virtual_default || !pa_source_is_filter(b)) + return -1; + } + if (pa_safe_streq(a->name, core->policy_default_source)) { + if (!ignore_configured_virtual_default || !pa_source_is_filter(a)) + return 1; + } /* The configured default source is preferred over any other source * except the policy default source. */ - if (pa_safe_streq(b->name, core->configured_default_source)) - return -1; - if (pa_safe_streq(a->name, core->configured_default_source)) - return 1; + if (pa_safe_streq(b->name, core->configured_default_source)) { + if (!ignore_configured_virtual_default || !pa_source_is_filter(b)) + return -1; + } + if (pa_safe_streq(a->name, core->configured_default_source)) { + if (!ignore_configured_virtual_default || !pa_source_is_filter(a)) + return 1; + } /* Monitor sources lose to non-monitor sources. */ if (a->monitor_of && !b->monitor_of) @@ -540,9 +572,15 @@ static int compare_sources(pa_source *a, pa_source *b) { if (a->priority > b->priority) return 1; + /* Let sources like pipe source or null source win against filter sources */ + if (a->output_from_master && !b->output_from_master) + return -1; + if (!a->output_from_master && b->output_from_master) + return 1; + /* If the sources are monitors, we can compare the monitored sinks. */ if (a->monitor_of) - return compare_sinks(a->monitor_of, b->monitor_of); + return compare_sinks(a->monitor_of, b->monitor_of, false); /* It's hard to find any difference between these sources, but maybe one of * them is already the default source? If so, it's best to keep it as the @@ -555,11 +593,10 @@ static int compare_sources(pa_source *a, pa_source *b) { return 0; } -void pa_core_update_default_source(pa_core *core) { +pa_source *pa_core_find_best_source(pa_core *core, bool ignore_configured_virtual_default) { pa_source *best = NULL; pa_source *source; uint32_t idx; - pa_source *old_default_source; pa_assert(core); @@ -572,10 +609,21 @@ void pa_core_update_default_source(pa_core *core) { continue; } - if (compare_sources(source, best) > 0) + if (compare_sources(source, best, ignore_configured_virtual_default) > 0) best = source; } + return best; +} + +void pa_core_update_default_source(pa_core *core) { + pa_source *best; + pa_source *old_default_source; + + pa_assert(core); + + best = pa_core_find_best_source(core, false); + old_default_source = core->default_source; if (best == old_default_source) @@ -693,11 +741,6 @@ void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink if (!si->sink) continue; - /* Skip this sink input if it is connecting a filter sink to - * the master */ - if (si->origin_sink) - continue; - /* It might happen that a stream and a sink are set up at the same time, in which case we want to make sure we don't interfere with that */ @@ -727,11 +770,6 @@ void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_sou if (!so->source) continue; - /* Skip this source output if it is connecting a filter source to - * the master */ - if (so->destination_source) - continue; - /* It might happen that a stream and a source are set up at the same time, in which case we want to make sure we don't interfere with that */ diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 7ac06f5d7..11011a744 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -271,6 +271,9 @@ void pa_core_set_policy_default_source(pa_core *core, const char *source); void pa_core_update_default_sink(pa_core *core); void pa_core_update_default_source(pa_core *core); +pa_sink *pa_core_find_best_sink(pa_core *core, bool ignore_configured_virtual_default); +pa_source *pa_core_find_best_source(pa_core *core, bool ignore_configured_virtual_default); + void pa_core_set_exit_idle_time(pa_core *core, int time); /* Check whether no one is connected to this core */ diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 9824719c5..1e625a677 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1782,7 +1782,7 @@ bool pa_sink_input_may_move(pa_sink_input *i) { return true; } -static bool find_filter_sink_input(pa_sink_input *target, pa_sink *s) { +bool pa_sink_input_is_filter_loop(pa_sink_input *target, pa_sink *s) { unsigned PA_UNUSED i = 0; while (s && (s->vsink && s->vsink->input_to_master)) { @@ -1827,7 +1827,7 @@ bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) { return false; /* Make sure we're not creating a filter sink cycle */ - if (find_filter_sink_input(i, dest)) { + if (pa_sink_input_is_filter_loop(i, dest)) { pa_log_debug("Can't connect input to %s, as that would create a cycle.", dest->name); return false; } @@ -2240,10 +2240,28 @@ void pa_sink_input_fail_move(pa_sink_input *i) { if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_STOP) return; - /* Can we move the sink input to the default sink? */ - if (i->core->rescue_streams && pa_sink_input_may_move_to(i, i->core->default_sink)) { - if (pa_sink_input_finish_move(i, i->core->default_sink, false) >= 0) - return; + /* Try to rescue stream if configured */ + if (i->core->rescue_streams) { + + /* Can we move the sink input to the default sink? */ + if (pa_sink_input_may_move_to(i, i->core->default_sink)) { + if (pa_sink_input_finish_move(i, i->core->default_sink, false) >= 0) + return; + } + + /* If this is a filter stream and the default sink is set to a filter sink within + * the same filter chain, we would create a loop and therefore have to find another + * sink to move to. */ + if (i->origin_sink && pa_sink_input_is_filter_loop(i, i->core->default_sink)) { + pa_sink *best; + + best = pa_core_find_best_sink(i->core, true); + + if (best && pa_sink_input_may_move_to(i, best)) { + if (pa_sink_input_finish_move(i, best, false) >= 0) + return; + } + } } if (i->moving) diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 7a75c0f09..58b086dbf 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -422,6 +422,8 @@ int pa_sink_input_start_move(pa_sink_input *i); int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save); void pa_sink_input_fail_move(pa_sink_input *i); +bool pa_sink_input_is_filter_loop(pa_sink_input *target, pa_sink *s); + pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i); /* To be used exclusively by the sink driver IO thread */ diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 072f87aeb..21d7dae68 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -4064,9 +4064,32 @@ void pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool if (!i->sink) continue; - /* Don't move sink-inputs which connect filter sinks to their target sinks */ - if (i->origin_sink) + /* If this is a filter stream and the default sink is set to a filter sink within + * the same filter chain, we would create a loop and therefore have to find another + * sink to move to. */ + if (i->origin_sink && pa_sink_input_is_filter_loop(i, core->default_sink)) { + pa_sink *best; + + /* If the default sink changed to our filter chain, lets make the current + * master the preferred sink. */ + if (default_sink_changed) { + pa_xfree(i->preferred_sink); + i->preferred_sink = pa_xstrdup(i->sink->name); + + continue; + } + + best = pa_core_find_best_sink(core, true); + + if (!best || !pa_sink_input_may_move_to(i, best)) + continue; + + pa_log_info("Moving sink input %u \"%s\" to the default sink would create a filter loop, moving to %s instead.", + i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), best->name); + + pa_sink_input_move_to(i, best, false); continue; + } /* If default_sink_changed is false, the old sink became unavailable, so all streams must be moved. */ if (pa_safe_streq(old_sink->name, i->preferred_sink) && default_sink_changed) diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 2e2b7a274..7e0a925fd 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -1304,7 +1304,7 @@ bool pa_source_output_may_move(pa_source_output *o) { return true; } -static bool find_filter_source_output(pa_source_output *target, pa_source *s) { +bool pa_source_output_is_filter_loop(pa_source_output *target, pa_source *s) { unsigned PA_UNUSED i = 0; while (s && s->output_from_master) { if (s->output_from_master == target) @@ -1347,7 +1347,7 @@ bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) { return false; /* Make sure we're not creating a filter source cycle */ - if (find_filter_source_output(o, dest)) { + if (pa_source_output_is_filter_loop(o, dest)) { pa_log_debug("Can't connect output to %s, as that would create a cycle.", dest->name); return false; } @@ -1660,10 +1660,28 @@ void pa_source_output_fail_move(pa_source_output *o) { if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP) return; - /* Can we move the source output to the default source? */ - if (o->core->rescue_streams && pa_source_output_may_move_to(o, o->core->default_source)) { - if (pa_source_output_finish_move(o, o->core->default_source, false) >= 0) - return; + /* Try to rescue stream if configured */ + if (o->core->rescue_streams) { + + /* Can we move the source output to the default source? */ + if (pa_source_output_may_move_to(o, o->core->default_source)) { + if (pa_source_output_finish_move(o, o->core->default_source, false) >= 0) + return; + } + + /* If this is a filter stream and the default source is set to a filter source within + * the same filter chain, we would create a loop and therefore have to find another + * source to move to. */ + if (o->destination_source && pa_source_output_is_filter_loop(o, o->core->default_source)) { + pa_source *best; + + best = pa_core_find_best_source(o->core, true); + + if (best && pa_source_output_may_move_to(o, best)) { + if (pa_source_output_finish_move(o, best, false) >= 0) + return; + } + } } if (o->moving) diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 2bf56820b..0515a080e 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -350,6 +350,8 @@ int pa_source_output_start_move(pa_source_output *o); int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save); void pa_source_output_fail_move(pa_source_output *o); +bool pa_source_output_is_filter_loop(pa_source_output *target, pa_source *s); + pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o); /* To be used exclusively by the source driver thread */ diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 99d8dde6e..226a717ca 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -3033,9 +3033,32 @@ void pa_source_move_streams_to_default_source(pa_core *core, pa_source *old_sour if (!o->source) continue; - /* Don't move source-outputs which connect sources to filter sources */ - if (o->destination_source) + /* If this is a filter stream and the default source is set to a filter source within + * the same filter chain, we would create a loop and therefore have to find another + * source to move to. */ + if (o->destination_source && pa_source_output_is_filter_loop(o, core->default_source)) { + pa_source *best; + + /* If the default source changed to our filter chain, lets make the current + * master the preferred source. */ + if (default_source_changed) { + pa_xfree(o->preferred_source); + o->preferred_source = pa_xstrdup(o->source->name); + + continue; + } + + best = pa_core_find_best_source(core, true); + + if (!best || !pa_source_output_may_move_to(o, best)) + continue; + + pa_log_info("Moving source output %u \"%s\" to the default source would create a filter loop, moving to %s instead.", + o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), best->name); + + pa_source_output_move_to(o, best, false); continue; + } /* If default_source_changed is false, the old source became unavailable, so all streams must be moved. */ if (pa_safe_streq(old_source->name, o->preferred_source) && default_source_changed)