diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4b9740c6d..b60f02a39 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -2461,13 +2461,31 @@ void pa_sink_input_request_rewind( /* Check if rewinding for the maximum is requested, and if so, fix up */ if (nbytes <= 0) { - /* Calculate maximum number of bytes that could be rewound in theory */ - nbytes = i->sink->thread_info.max_rewind + lbq; + /* Calculate maximum number of bytes that could be rewound in theory. + * If the sink has a virtual sink attached, limit rewinding to max_rewind. + * + * The max_rewind value of a virtual sink depends on the rewinding capability + * of its DSP code. The DSP code is rewound in the process_rewind() callback + * of the sink input. Therefore rewinding must be limited to max_rewind here. */ + nbytes = i->sink->thread_info.max_rewind; + if (!pa_sink_has_filter_attached(i->sink) && !pa_sink_is_filter(i->sink)) + nbytes += lbq; /* Transform from sink domain */ nbytes = pa_resampler_request(i->thread_info.resampler, nbytes); } + /* For virtual sinks there are two situations where nbytes may exceed max_rewind: + * 1) If an underrun was detected. + * 2) When the sink input is rewound during a move when it is attached to + * the destination sink. + * Moving a sink input is handled without involving the implementer, so the + * implementer will only be asked to rewind more than max_rewind if an + * underrun occurs. In that case, the DSP code of virtual sinks should be + * reset instead of rewound. Therefore the rewind function of filters should + * check if the requested rewind exceeds the maximum possible rewind of the + * filter. */ + /* Remember how much we actually want to rewrite */ if (i->thread_info.rewrite_nbytes != (size_t) -1) { if (rewrite) { diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 01a94eda0..a5691abef 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1657,6 +1657,27 @@ bool pa_sink_flat_volume_enabled(pa_sink *s) { return false; } +/* Check if the sink has a virtual sink attached. + * Called from the IO thread. */ +bool pa_sink_has_filter_attached(pa_sink *s) { + bool vsink_attached = false; + void *state = NULL; + pa_sink_input *i; + + pa_assert(s); + + if (PA_SINK_IS_LINKED(s->thread_info.state)) { + PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) { + if (!i->origin_sink) + continue; + + vsink_attached = true; + break; + } + } + return vsink_attached; +} + /* Called from the main thread (and also from the IO thread while the main * thread is waiting). */ pa_sink *pa_sink_get_master(pa_sink *s) { @@ -2669,8 +2690,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse } pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)); - pa_sink_invalidate_requested_latency(s, true); pa_sink_request_rewind(s, (size_t) -1); + pa_sink_invalidate_requested_latency(s, true); /* In flat volume mode we need to update the volume as * well */ @@ -2709,11 +2730,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* Let's remove the sink input ...*/ pa_hashmap_remove_and_free(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index)); - pa_sink_invalidate_requested_latency(s, true); - + /* The rewind must be requested before invalidating the latency, otherwise + * the max_rewind value of the sink may change before the rewind. */ pa_log_debug("Requesting rewind due to started move"); pa_sink_request_rewind(s, (size_t) -1); + pa_sink_invalidate_requested_latency(s, true); + /* In flat volume mode we need to update the volume as * well */ return o->process_msg(o, PA_SINK_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 9492b18dc..383edacb5 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -470,6 +470,10 @@ int pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause); /* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */ bool pa_sink_flat_volume_enabled(pa_sink *s); +/* Check if the sink has a virtual sink attached. + * Called from the IO thread. */ +bool pa_sink_has_filter_attached(pa_sink *s); + /* Get the master sink when sharing volumes */ pa_sink *pa_sink_get_master(pa_sink *s);