[pulseaudio-discuss] [PATCH] echo-cancel: Limit the maximum sink/source latency

Georg Chini georg at chini.tk
Thu Mar 9 10:31:26 UTC 2017


On 09.03.2017 06:53, Arun Raghavan wrote:
> On systems with constrained CPUs, we might run into a situation where
> the master source/sink is configured to have too high a latency.
>
> On the source side, this would cause us to wake up with a large chunk of
> data to process, which might cause us to exhust our RT limit and thus be
> killed.
>
> So it makes sense to limit the overall latency that we request from the
> source (and correspondingly, the sink, so we don't starve for playback
> data on the source side).
>
> The 10 blocks maximum is somewhat arbitrary (I'm assuming the system has
> enough headroom to process 10 chunks through the canceller without
> getting close to the RT limit). This might make sense to make tunable in
> the future.
> ---
>   src/modules/echo-cancel/module-echo-cancel.c | 32 +++++++++++++++++++++-------
>   1 file changed, 24 insertions(+), 8 deletions(-)
>
> diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
> index ed75e0c..0a9f290 100644
> --- a/src/modules/echo-cancel/module-echo-cancel.c
> +++ b/src/modules/echo-cancel/module-echo-cancel.c
> @@ -145,6 +145,8 @@ static const pa_echo_canceller ec_table[] = {
>   
>   #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
>   
> +#define MAX_LATENCY_BLOCKS 10
> +
>   /* Can only be used in main context */
>   #define IS_ACTIVE(u) ((pa_source_get_state((u)->source) == PA_SOURCE_RUNNING) && \
>                         (pa_sink_get_state((u)->sink) == PA_SINK_RUNNING))
> @@ -515,6 +517,7 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
>   /* Called from source I/O thread context */
>   static void source_update_requested_latency_cb(pa_source *s) {
>       struct userdata *u;
> +    pa_usec_t latency;
>   
>       pa_source_assert_ref(s);
>       pa_assert_se(u = s->userdata);
> @@ -525,15 +528,17 @@ static void source_update_requested_latency_cb(pa_source *s) {
>   
>       pa_log_debug("Source update requested latency");
>   
> -    /* Just hand this one over to the master source */
> -    pa_source_output_set_requested_latency_within_thread(
> -            u->source_output,
> -            pa_source_get_requested_latency_within_thread(s));
> +    /* Cap the maximum latency so we don't have to process too large chunks */
> +    latency = PA_MIN(pa_source_get_requested_latency_within_thread(s),
> +                     pa_bytes_to_usec(u->source_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS);
> +
> +    pa_source_output_set_requested_latency_within_thread(u->source_output, latency);
>   }
>   
>   /* Called from sink I/O thread context */
>   static void sink_update_requested_latency_cb(pa_sink *s) {
>       struct userdata *u;
> +    pa_usec_t latency;
>   
>       pa_sink_assert_ref(s);
>       pa_assert_se(u = s->userdata);
> @@ -544,10 +549,11 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
>   
>       pa_log_debug("Sink update requested latency");
>   
> -    /* Just hand this one over to the master sink */
> -    pa_sink_input_set_requested_latency_within_thread(
> -            u->sink_input,
> -            pa_sink_get_requested_latency_within_thread(s));
> +    /* Cap the maximum latency so we don't have to process too large chunks */
> +    latency = PA_MIN(pa_sink_get_requested_latency_within_thread(s),
> +                     pa_bytes_to_usec(u->sink_blocksize, &s->sample_spec) * MAX_LATENCY_BLOCKS);
> +
> +    pa_sink_input_set_requested_latency_within_thread(u->sink_input, latency);
>   }
>   
>   /* Called from sink I/O thread context */
> @@ -1658,6 +1664,7 @@ int pa__init(pa_module*m) {
>       uint32_t temp;
>       uint32_t nframes = 0;
>       bool use_master_format;
> +    pa_usec_t blocksize_usec;
>   
>       pa_assert(m);
>   
> @@ -2020,6 +2027,15 @@ int pa__init(pa_module*m) {
>   
>       u->thread_info.current_volume = u->source->reference_volume;
>   
> +    /* We don't want to deal with too many chunks at a time */
> +    blocksize_usec = pa_bytes_to_usec(u->source_blocksize, &u->source->sample_spec);
> +    pa_source_set_latency_range(u->source, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS);
> +    pa_source_output_set_requested_latency(u->source_output, blocksize_usec * MAX_LATENCY_BLOCKS);
> +
> +    blocksize_usec = pa_bytes_to_usec(u->sink_blocksize, &u->sink->sample_spec);
> +    pa_sink_set_latency_range(u->sink, blocksize_usec, blocksize_usec * MAX_LATENCY_BLOCKS);
> +    pa_sink_input_set_requested_latency(u->sink_input, blocksize_usec * MAX_LATENCY_BLOCKS);
> +
>       pa_sink_put(u->sink);
>       pa_source_put(u->source);
>   

You should save the initial latency ranges and restore them when the 
module is unloaded.



More information about the pulseaudio-discuss mailing list