[pulseaudio-discuss] Fwd: Re: [PATCH 07/13] loopback: Refactor latency initialization

Georg Chini georg at chini.tk
Mon Nov 23 23:51:15 PST 2015


Forgot to include the list ...


-------- Forwarded Message --------
Subject: 	Re: [pulseaudio-discuss] [PATCH 07/13] loopback: Refactor 
latency initialization
Date: 	Tue, 24 Nov 2015 08:21:45 +0100
From: 	Georg Chini <georg at chini.tk>
To: 	Tanu Kaskinen <tanuk at iki.fi>



On 24.11.2015 03:50, Tanu Kaskinen wrote:
> On Sun, 2015-11-22 at 13:21 +0100, Georg Chini wrote:
>> On 22.11.2015 00:27, Tanu Kaskinen wrote:
>>> On Sat, 2015-11-21 at 19:42 +0100, Georg Chini wrote:
>>>> On 20.11.2015 16:18, Tanu Kaskinen wrote:
>>>>> On Fri, 2015-11-20 at 08:03 +0100, Georg Chini wrote:
>>>>>> On 20.11.2015 01:03, Tanu Kaskinen wrote:
>>>>>>> On Wed, 2015-02-25 at 19:43 +0100, Georg Chini wrote:
>>>> The point is, that the assumption that source_output and sink_input
>>>> rate are the same is not valid. As long as they are, you will not hit a
>>>> problem.
>>> I did cover also the case where there is clock drift and the rate
>>> hasn't yet been stabilized. (It's the paragraph starting with "In the
>>> cases where...") I argued that the clock drift won't cause big enough
>>> data shortage to warrant a 75% safety margin relative to the sink
>>> latency.
>>>
>>> I now believe that my initial intuition about what the safety margin
>>> should be to cover for rate errors was wrong, though. I now think that
>>> if the sink input is consuming data too fast (i.e. the configured rate
>>> is too low), the error margin has to be big enough to cover for all
>>> excess samples consumed before the rate controller slows down the sink
>>> input data consumption rate to be at or below the rate at which the
>>> source produces data. For example, if it takes one adjustment cycle to
>>> change a too-fast-consuming sink input to not-too-fast-consuming, the
>>> error margin needs to be "rate_error_in_hz * adjust_time" samples. The
>>> sink and source latencies are irrelevant. If it takes more than one
>>> adjustment cycle, it's more complicated, but an upper bound for the
>>> minimum safety margin is "max_expected_rate_error_in_hz *
>>> number_of_cycles * adjust_time" samples.
>> This can't be true. To transform it to the time you have to divide
>> by the sample rate, so your formula (for the one step case) is
>> basically
>> safty_time = relative_rate_error * adjust_time
>> The module keeps the relative rate error for a single step below
>> 0.002, so you end up with 0.002 * adjust_time, which means for
>> 10 s adjust time you would need 20 msec safety margin regardless
>> of the sink latency. This is far more than the minimum possible
>> latency so it does not make any sense to me. If you have a
>> large initial latency error which would require multiple steps your
>> estimate gets even worse.
> Well, if the sink input consumes 20 ms more audio during 10 seconds
> than what the source produces, then that's how much you need to buffer.
> I don't see any way around that. Underrun-proof 4 ms latency is just
> impossible with those parameters. Using smaller adjust_time is an easy
> way to mitigate this. Maybe it would make sense to use frequent
> adjustments in the beginning, and once the controller stabilizes,
> increase the adjustment interval?

Stable 4 ms operation is possible, that is a fact. To be on the safe side,
let's say 5 ms. Using those short latencies with 10 sec adjust time is
however asking for trouble, that is true. I kept 5 ms running over night
with 2 sec adjust time more than once to test stability when I wrote the
patch, so I can say for sure that this works. According to your formula
I would need to keep at least 4 ms in the buffer for that, which is not the
case.
I think 10 sec adjust time is too long anyway if you want to have a
quick convergence to the desired latency and a good stability in the
steady state.
I tested shorter adjust times down to 100 msec and I cannot see
much improvement compared to 1 or 2 seconds. I would recommend
to lower the default to 2 sec.

>
>> The other big problem is that you cannot determine the number
>> of cycles you will need to correct the initial latency error because
>> this error is unknown before the first adjustment cycle.
>>
>> When you calculate that safety margin you also have to consider
>> that the controller might overshoot, so you temporarily could
>> get less latency than you requested.
> Can the overshoot be greater than the initial error? Getting less
> latency than requested is exactly the problem that the safety margin is
> supposed to solve. If the overshoot is less than the initial error, the
> safety margin calculation doesn't need to take the overshoot into
> account.

No, it cannot. It is just a little bit (I explained it in my e-mail to
Alexander
who said the controller could not overshoot). But the initial error is
not really relevant here at all if it is not so large to produce underruns
immediately, because the rate will be adapted to match that error.
I think it is more the error of the adjustment itself which - for reasons
still completely unclear to me - can be quite high during the first cycles
when the deviation from the base rate is high.


>
>> It is however true, that the sink latency in itself is not relevant,
>> but it controls when chunks of audio are transferred and how
>> big those chunks are. So the connection is indirect, maybe
>> max_request is a better indicator than the latency. I'll do some
>> experiments the next days to find out.
> I don't think max_request is any better. In practice it's almost the
> same thing as the configured sink latency.

But you were arguing that it is a good indicator for batch cards, or did
I read you wrong? I would assume that the arguments you used there
apply here as well.

>
>>>> Once you are in a steady state you only have to care about jitter.
>>>> I cannot clearly remember how I derived that value, probably
>>>> experiments, but I still believe that 0.75 is a "good" estimate. If
>>>> you look at the 4 msec case, the buffer_latency is slightly lower
>>>> than 3/4 of the sink latency (1.667 versus 1.75 ms) but this is
>>>> also already slightly unstable.
>>>> In a more general case the resulting latency will be
>>>> 1.75 * minimum_sink_latency, which I would consider small enough.
>>> I don't understand where that "1.75 * minimum_sink_latency" comes from.
>>> I'd understand if you said "0.75 * maximum_sink_latency", because
>>> that's what the code seems to do.
>> The 0.75 * sink_latency is just the part that is stored within the
>> module (mostly in the memblockq), so you have to add the
>> sink_latency to it. That's 1.75 * sink_latency then. The source
>> latency does not seem to play any role, whatever you configure,
>> the reported value is most of the time near 0.
> Are you saying that the configured source latency doesn't actually
> affect the real source latency (at least as reported)? That sounds like
> a bug. (One possible explanation would be that since the latency
> queries aren't done with random intervals, the queries might by chance
> be "synchronized" with certain source buffer fill level.)

Yes, the source almost always reports a latency near zero while the
sink reports the configured latency. That has already been the case
with the old module. I do not think that there is some synchronization,
pacmd list-sources shows the same. Occasionally you will see higher
values on the source side but it is rather rare.
I hope it is no bug, otherwise I would have to redo the logic of the
module.

>
>> All calculations assume that when I configure source and sink
>> latency to 1/3 of the requested latency each, I'll end up with
>> having about 1/3 of the latency in source and sink together.
>> I know this is strange but so far I have not seen a case where
>> this assumption fails.
> It doesn't sound strange to me, because if you randomly sample the
> buffer fill level of a sink or a source, on average it will be 50% full
> (assuming that refills happen when the buffer gets empty, which is
> approximately true when using timer-based scheduling). On average,
> then, the sum of the sink and source latencies will be half of the sum
> of the configured latencies.

Should then the reported latency not be half of the configured?
This is not the case, at least on the sink side.

>
>>> Anyway, any formula involving the sink or source latency seems bogus to
>>> me. adjust_time and the rate error (or an estimate of the maximum rate
>>> error, if the real error isn't known) are what matter. Plus of course
>>> some margin for cpu overhead/jitter, which should be constant (I
>>> think). The jitter margin might contribute more than the margin for
>>> covering for rate errors.
>> In the end adjust_time and rate_error don't matter because they are
>> inversely proportional to each other, so that the product is roughly
>> constant.
> Can you elaborate? I don't see why they would be inversely proportional
> to each other.

Mh, I am no longer sure if I understood you correctly. The rate error
I am talking about is the deviation from the base rate that is set by
the controller. This rate deviation is inversely proportional to the
adjust time, because if you have twice the time to correct the same
latency difference, you will only need half of the rate deviation.
If you are talking about the precision of the sample rate, this should
not be relevant because the error should be below 0.1 percent.
(Alexander said that was the worst he could find when he measured it.)

>
>>> I guess none of this matters, if I'm right in saying that the sink and
>>> source latencies are irrelevant for calculating the required safety
>>> margin. Hmm... now it occurred to me that the sink latency has some
>>> (smallish?) relevance after all, because when the sink input rate
>>> changes, the sink will have some data buffered that is resampled using
>>> the old rate. If the old rate was too low, the buffered data will be
>>> played too fast. Therefore, the rate error safety margin formula that I
>>> presented earlier has to be modified to include the sink latency. So
>>> the rate error safety margin should be at least
>>> "max_expected_rate_error_in_hz * (number_of_cycles * adjust_time +
>>> sink_latency)" samples.
>>>
>> Some additional remarks about the importance of buffer_latency and
>> those safeguards:
>>
>> 1) As long as you are not at the lower latency limit both are more
>> or less irrelevant.
> True. To minimize wakeups, it's still good to minimize buffer_latency,
> though, so that the device buffers can be as large as possible.

Yes, patch 13 adds the possibility to specify source/sink latency
and buffer_latency independent from each other, so that you can
minimize buffer_latency for setups that need it. It can save quite
a lot of CPU in certain situations. On my (slow) system I went down
from 12% CPU to 8% CPU for 10 ms latency this way.

>
>> 2) Even if those safeguards are too small, the module will fix up
>> buffer_latency during runtime until there are no longer any underruns.
> I'm not yet familiar with that logic, but even if you don't adapt
> buffer_latency, once the rate gets stabilized, underruns shouldn't
> happen any more due to rate errors, because the errors will be very
> small. Scheduling delays of course don't disappear, so increasing
> buffer_latency on underruns can still be a good idea.

I'm checking for underruns and if there are more that 3 underuns per
hour I increase buffer_latency in 5 msec steps.
This is mainly necessary if you use patch 13 and specify something
that can't be handled. Under normal conditions it should never be
triggered.

>
> --
> Tanu



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20151124/92dbe433/attachment-0001.html>


More information about the pulseaudio-discuss mailing list