[pulseaudio-discuss] [Patch] RAOP: fix audio synchronisation, take two

Georg Chini georg at chini.tk
Sat Sep 9 18:44:36 UTC 2017

On 09.09.2017 19:16, Tanu Kaskinen wrote:
> On Sat, 2017-09-09 at 18:07 +0200, Colin Leroy wrote:
>> On 09 September 2017 at 18h00, Tanu Kaskinen wrote:
>> Hi Tanu,
>> Your mail's empty :)
> Well I definitely wrote things, but Evolution did act strange while
> composing the message, and my "sent" folder confirms your
> observation... I supposed I'll have to write it all again :(
>> Anyway, after this patch, there still is a bug left (which was not
>> visible with the previous patch where I did
>> if (latency < 0) {
>>      latency = 0;
>> }
>> in sink_get_latency().
>> The bug is that latency's all wrong (like, -10 to -5 seconds instead of
>> +2.3) after a pause (that is, after switching to PA_SINK_PA_SINK_IDLE
>> and then back to PA_SINK_RUNNING). At the first playback, it's fine.
>> I don't understand why, and if you have some pointers on how pa_rtpoll
>> and pa_smoother work, I guess that'd help.
> pa_rtpoll is just a mechanism for sleeping until the set timeout or
> until something happens (pa_rtpoll users can register fds to listen). I
> don't think it's really relevant for latency.
> pa_smoother is primarily used for translating between a sound card
> clock and the system clock (they usually run at different speeds). I
> think the name comes from the fact that it also helps to smooth out
> jumps in the sound card clock (e.g. the sound card clock may work in
> steps: it "jumps forward" when a buffer if consumed, and between the
> jumps the time appears to not change at all).

The smoother is used to extrapolate the sound card time between
buffer writes. I guess this is what you mean with "smooth out jumps
in the sound card clock".

> I don't quite understand what the "oob" thing is in raop-sink. If I'm
> guessing correctly, the "oob" flag determines whether the sink relies
> solely on the system clock to decide when to write more data, or the
> sink uses the POLLOUT event on the TCP stream to trigger writes. Maybe
> the system clock approach is used with UDP and the POLLOUT approach is
> used with TCP? That would make sense to me.
> If the sink is only using the system clock, then there's no need for
> the smoother. Yet it seems to be used unconditionally?

It will do no harm to use the smoother, in that case both times
are just equal.

> When waiting for POLLOUT events, then the smoother is useful, because
> the rate at which the data is consumed will not exactly match the
> system clock.
> I'm not super confident with my understanding of pa_smoother either,
> but here's how I imagine it's supposed to be used:
> Whenever you get a time update from the sound card (I'll call the raop
> device a sound card here), you tell the smoother the current system
> time and the time of the sound card. In this case the "time update" is
> the POLLOUT event: the TCP buffer has been consumed enough that you can
> write more data. I guess the "time" of the sound card is the total
> amount of data (converted to time units) that has been written to the
> TCP socket before the POLLOUT event.
> Based on the timing events, the smoother creates an estimate of the
> speed difference between the system clock and the sound card clock.
> This allows you to ask the smoother the time of the sound card (i.e.
> the amount of data it has consumed) right now, given the current system
> time.
> When you temporarily stop playing, the smoother has to be told that
> playback has stopped. pa_smoother_pause() and pa_smoother_resume() can
> be used for this. Or maybe pa_smoother_reset()? I really don't know the
> nitty-gritty details here.
> It might be easiest to just totally throw away the old smoother when
> pausing, and recreate the smoother in the same way you do when the sink
> is initially created.

It should be fully sufficient to use pa_smoother_reset(). It does a
complete new initialization.

> Then the behaviour will at least be consistent.
> The downside is that you lose the information in the old smoother: it
> has already worked out the speed difference between the system clock
> and the sound card clock, and the new smoother will have to figure that
> out from scratch, meaning that the initial latency reports after a
> pause might be less accurate.

pa_smoother_pause() and pa_smoother_resume() do not seem to
work as expected from my experience. After a smoother_resume()
the initial error is high again, so using pause()/resume() is no big
advantage over reset().

> It would be great to have some more documentation in pulsecore/time-
> smoother.h... Like what is the "time offset" used for? What does
> pa_smoother_fix_now() do?
The time_offset is the reference point for the system time at
sound_card_time = 0. It is subtracted from the time passed
to smoother_put() or smoother_get().
pa_smoother_fix_now() is only used internally within the
smoother, don't know why it ever was exported. It is used
after a pause to fix up internal variables so that the pause
time does not get counted. Not sure why there is an option
to skip that fix-up.

I have written a patch series that adds an alternate smoother
(not submitted yet, also needs to be re-based). I hope it is
documented somewhat better.

More information about the pulseaudio-discuss mailing list