[pulseaudio-discuss] [PATCH] loopback: Calculate and track minimum possible latency

Tanu Kaskinen tanuk at iki.fi
Sun Apr 2 22:11:52 UTC 2017


On Wed, 2017-03-29 at 10:36 +0200, Georg Chini wrote:
> With the current code, the user can request any end-to-end latency. Because there
> is no protection against underruns, setting the latency too small will result in
> repetitive underruns.
> 
> This patch tries to mitigate the problem by calculating the minimum possible latency
> for the current combination of source and sink. The actual calculation has been put
> in a separate function so it can easily be changed. To keep the values up to date,
> changes in the latency ranges have to be tracked.
> 
> The calculated minimum latency is used to limit the configured latency.
> The minimum latency is only a "best guess", so the actual minimum may be much
> larger (for example for USB devices) or much smaller than the calculated value.
> 
> Port latency offsets are not yet handled properly, this will be done in a separate
> patch.

In what way are they not handled properly? Does it make sense to have
any code related to latency offsets in this patch if it needs to be
fixed later?

> ---
>  src/modules/module-loopback.c | 199 ++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 191 insertions(+), 8 deletions(-)
> 
> diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
> index 9b4ea49..e2cd986 100644
> --- a/src/modules/module-loopback.c
> +++ b/src/modules/module-loopback.c
> @@ -65,10 +65,14 @@ PA_MODULE_USAGE(
>  
>  #define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
>  
> +typedef struct loopback_msg loopback_msg;

https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/CodingStyle/

"Exported structs should be typedef'ed so they can be used without
typing "struct" each time. Structs that are used only inside a single
source file should not be typedef'ed that way."

> +enum {
> +    LOOPBACK_MESSAGE_SOURCE_LATENCY_CHANGED,
> +    LOOPBACK_MESSAGE_SINK_LATENCY_CHANGED

I'd name these using "LATENCY_RANGE" instead of just "LATENCY", for
clarity.

> @@ -751,6 +860,12 @@ static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, in
>              u->output_thread_info.effective_source_latency = (pa_usec_t)offset;
>  
>              return 0;
> +
> +        case SINK_INPUT_MESSAGE_UPDATE_MIN_LATENCY:
> +
> +            u->output_thread_info.minimum_latency = (pa_usec_t)offset;

If the minimum latency grows, and the target latency grows too because
of that, I think we should push some silence to the memblockq to
account for the change in the target latency. Otherwise I think there's
 a risk of getting many underruns before the latency controller adjusts
to the new target.

> +/* Called from main context */
> +static int loopback_process_msg_cb(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
> +    struct loopback_msg *msg;
> +    struct userdata *u;
> +    pa_usec_t current_latency;
> +
> +    pa_assert(o);
> +    pa_assert_ctl_context();
> +
> +    msg = LOOPBACK_MSG(o);
> +    pa_assert_se(u = msg->userdata);
> +
> +    switch (code) {
> +
> +        case LOOPBACK_MESSAGE_SOURCE_LATENCY_CHANGED:
> +
> +            update_effective_source_latency(u, u->source_output->source, u->sink_input->sink);
> +            current_latency = pa_source_get_requested_latency(u->source_output->source);
> +            if (current_latency > u->configured_source_latency) {
> +                /* The minimum latency has changed to a value larger than the configured latency. so
> +                 * the source latency has been increased. The case that the minimum latency changes
> +                 * back to a smaller value is not handled because this is currently not implemented */

At the end of the comment, I think "because that never happens with the
current source implementations" would be clearer than "because this is
currently not implemented".

> +                pa_log_warn("Source minimum latency increased to %0.2f ms", (double)current_latency / PA_USEC_PER_MSEC);
> +                u->configured_source_latency = current_latency;
> +                update_latency_boundaries(u, u->source_output->source, u->sink_input->sink, false);
> +            }
> +
> +            return 0;
> +
> +        case LOOPBACK_MESSAGE_SINK_LATENCY_CHANGED:
> +
> +            current_latency = pa_sink_get_requested_latency(u->sink_input->sink);
> +            if (current_latency > u->configured_sink_latency) {
> +                /* The minimum latency has changed to a value larger than the configured latency, so
> +                 * the sink latency has been increased. The case that the minimum latency changes back
> +                 * to a smaller value is not handled because this is currently not implemented */

Same comment as above.

-- 
Tanu

https://www.patreon.com/tanuk


More information about the pulseaudio-discuss mailing list