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

Arun Raghavan arun at arunraghavan.net
Thu Mar 9 15:35:18 UTC 2017



On Thu, 9 Mar 2017, at 04:01 PM, Georg Chini wrote:
> 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.

The latency range is set on the echo-cancel sink and source (not the
master), so when the module is unloaded, that sink and source go away as
well.

-- Arun


More information about the pulseaudio-discuss mailing list