[pulseaudio-discuss] Latency/lag with tcp tunnel

Sean Greenslade sean at seangreenslade.com
Wed Jan 27 08:23:08 UTC 2021


On Tue, Jan 26, 2021 at 03:37:02PM -0600, Matt Garman wrote:
> On Mon, Jan 25, 2021 at 10:58 PM Sean Greenslade
> <sean at seangreenslade.com> wrote:
> > As a data point, I routinely use networked TCP streams with pulse, and
> > on wired ethernet links I tend to see only ~50 ms of delay.
> >
> > One avenue to investigate is to see what pulseaudio thinks the latency
> > is. If you run the command "pactl list sink_inputs" on the device with
> > the sound card, you'll get something like this:
> >
> > [sean at bailey ~]$ pactl list sink-inputs
> > Sink Input #2
> >         Driver: protocol-native.c
> >         Owner Module: 9
> >         Client: 17
> >         Sink: 3
> >         Sample Specification: float32le 2ch 48000Hz
> >         Channel Map: front-left,front-right
> >         Format: pcm, format.sample_format = "\"float32le\""  format.rate = "48000"  format.channels = "2"  format.channel_map = "\"front-left,front-right\""
> >         Corked: no
> >         Mute: no
> >         Volume: front-left: 60293 /  92% / -2.17 dB,   front-right: 60293 /  92% / -2.17 dB
> >                 balance 0.00
> >         Buffer Latency: 226791 usec
> >         Sink Latency: 16780 usec
> >         Resample method: speex-float-1
> >
> > Note the reported "Sink Latency" of ~16 ms (in my setup, I saw it vary
> > from 4 ms to 25 ms). What does your setup report? And what is the
> > reported latency of your audio device itself (pactl list sinks).
> 
> 
> Thank you for the suggestion.  Here are the two sink-inputs on the RPI
> server.  They are the tcp and unix domain sockets, respectively.  In
> both cases, the sink latency is around 10ms, which is roughly the same
> as you.  That's the "Sink Latency", is the "Buffer Latency" important?

Yes, the "Sink Latency" is the actual calculated latency that the sink
input is currently experiencing. The "Buffer Latency" is the size
(converted to time) of the buffer that pulseaudio is maintaining for the
application to write sound data into. Media sources that are able to
look ahead (like video players that are reading local files) can
maintain large buffers of future content so that they don't have to fill
them as frequently.

To answer your question directly, the buffer latency isn't really
important, you don't actually see that latency.

> root at dietpi-ma12070p:~# pactl list sink-inputs
> Sink Input #0
>        Driver: protocol-native.c
>        Owner Module: 7
>        Client: 1
>        Sink: 0
>        Sample Specification: s32le 2ch 44100Hz
>        Channel Map: front-left,front-right
>        Format: pcm, format.sample_format = "\"s32le\""  format.rate =
> "44100"  format.channels = "2"  format.channel_map =
> "\"front-left,front-right\""
>        Corked: yes
>        Mute: no
>        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right:
> 65536 / 100% / 0.00 dB
>                balance 0.00
>        Buffer Latency: 139841 usec
>        Sink Latency: 9718 usec
>        Resample method: n/a
>        Properties:
>                media.name = "Built-in Audio for matt at sewage"
>                media.role = "abstract"
>                application.name = "pulseaudio"
>                native-protocol.peer = "TCP/IP client from 10.18.51.34:46968"
>                native-protocol.version = "34"
>                application.id = "org.PulseAudio.PulseAudio"
>                application.version = "14.0"
>                application.process.id = "947578"
>                application.process.user = "matt"
>                application.process.host = "sewage"
>                application.process.binary = "pulseaudio"
>                application.language = "en_US.UTF-8"
>                window.x11.display = ":0"
>                application.process.machine_id =
> "31d757b445f140b1b50278a35a34a1e5"
>                application.process.session_id = "2"
>                module-stream-restore.id = "sink-input-by-media-role:abstract"
> 
> Sink Input #2
>        Driver: protocol-native.c
>        Owner Module: 6
>        Client: 5
>        Sink: 0
>        Sample Specification: s16le 2ch 44100Hz
>        Channel Map: front-left,front-right
>        Format: pcm, format.sample_format = "\"s16le\""  format.rate =
> "44100"  format.channels = "2"  format.channel_map =
> "\"front-left,front-right\""
>        Corked: no
>        Mute: no
>        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right:
> 65536 / 100% / 0.00 dB
>                balance 0.00
>        Buffer Latency: 446349 usec
>        Sink Latency: 9846 usec
>        Resample method: copy
>        Properties:
>                media.name = "ALSA Playback"
>                application.name = "ALSA plug-in [mpd]"
>                native-protocol.peer = "UNIX socket client"
>                native-protocol.version = "32"
>                application.process.id = "504"
>                application.process.user = "mpd"
>                application.process.host = "dietpi-ma12070p"
>                application.process.binary = "mpd"
>                application.language = "C"
>                application.process.machine_id =
> "1000380a73144486937defb00927c831"
>                module-stream-restore.id =
> "sink-input-by-application-name:ALSA plug-in [mpd]"

OK, so both the local client and the tunneled client are showing ~9 ms
of sink latency. Looks good so far.

> Here's the sink latency, also looks reasonable to my untrained eyes:
> 
> root at dietpi-ma12070p:~# pactl list sinks
> Sink #0
>        State: RUNNING
>        Name: alsa_output.default
>        Description: Built-in Audio
>        Driver: module-alsa-sink.c
>        Sample Specification: s32le 2ch 44100Hz
>        Channel Map: front-left,front-right
>        Owner Module: 5
>        Mute: no
>        Volume: front-left: 8308 /  13% / -53.82 dB,   front-right:
> 8308 /  13% / -53.82 dB
>                balance 0.00
>        Base Volume: 65536 / 100% / 0.00 dB
>        Monitor Source: alsa_output.default.monitor
>        Latency: 9833 usec, configured 9977 usec
>        Flags: HARDWARE HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY
>        Properties:
>                alsa.resolution_bits = "32"
>                device.api = "alsa"
>                device.class = "sound"
>                alsa.class = "generic"
>                alsa.subclass = "generic-mix"
>                alsa.name = "Merus Audio Amp ma120x0p-amp-0"
>                alsa.id = "Merus Audio Amp ma120x0p-amp-0"
>                alsa.subdevice = "0"
>                alsa.subdevice_name = "subdevice #0"
>                alsa.device = "0"
>                alsa.card = "0"
>                alsa.card_name = "snd_rpi_merus_amp"
>                alsa.long_card_name = "snd_rpi_merus_amp"
>                alsa.driver_name = "snd_soc_rpi_simple_soundcard"
>                device.bus_path = "platform-soc:sound"
>                sysfs.path = "/devices/platform/soc/soc:sound/sound/card0"
>                device.form_factor = "internal"
>                device.string = "default"
>                device.buffering.buffer_size = "3520"
>                device.buffering.fragment_size = "1760"
>                device.access_mode = "mmap"
>                device.description = "Built-in Audio"
>                device.icon_name = "audio-card"
>        Formats:
>                pcm

The buffer that the ALSA sound device exposes is reported here as 3520
bytes (with a write fragment size of half the buffer). With 8 bytes per
sample, that buffer is only 440 samples, or 9.97 ms. Which matches
exactly with the "configured" latency number. Still looking good.

> Here's what I see on the client side.  I trimmed this substantially,
> as I actually have a number of sinks on this workstation; showing just
> the tunnel to the RPI server:
> 
> Sink #14
>        State: RUNNING
>        Name: tunnel.dietpi-ma12070p.local.alsa_output.default
>        Description: Built-in Audio on pulse at dietpi-ma12070p
>        Driver: module-tunnel.c
>        Sample Specification: s32le 2ch 44100Hz
>        Channel Map: front-left,front-right
>        Owner Module: 30
>        Mute: no
>        Volume: front-left: 65536 / 100%,   front-right: 65536 / 100%
>                balance 0.00
>        Base Volume: 65536 / 100%
>        Monitor Source: tunnel.dietpi-ma12070p.local.alsa_output.default.monitor
>        Latency: 137764 usec, configured 250000 usec
>        Flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
>        Properties:
>                device.description = "Built-in Audio on pulse at dietpi-ma12070p"
>                tunnel.remote.server = "[10.18.51.66]:4713"
>                tunnel.remote.sink = "alsa_output.default"
>                device.icon_name = "computer"
>                tunnel.remote_version = "32"
>                tunnel.remote.user = "pulse"
>                tunnel.remote.fqdn = "dietpi-ma12070p"
>                tunnel.remote.description = "Built-in Audio"
>        Formats:
>                pcm
> 
> That seems a little high to me, around 140 ms.  Not sure if it's
> possible to reduce that and maybe improve the experience? 

So this is where the limitations of module-tunnel come in. The original
tunnel has the latency modification code commented out, so you're stuck
at the default sink latency setting of 250 ms.

One thing you can try is using module-tunnel-sink-new instead of
module-tunnel-sink, as it seems to have dynamic latency calculations
in its code (though I'm not familiar enough with the PA internals to
know how that code actually works).

Note that the online documentation has this to say about tunnels vs.
direct connection:

>>> https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Network/#index2h2
>>>
>>> A direct connection [as opposed to a tunnel] has the advantage that
>>> the client has more control over buffering parameters.

> I know ping is a poor choice for measuring latency, but I think it's
> useful as a ballpark estimate.  Ping from my workstation to the RPI
> server is pretty consistently around 0.4 ms.  So as you said, wired
> ethernet should be low latency enough for lag-free network audio.

In summary:

If you're experiencing latency using an on-device player, then the
latency is somewhere beyond the I2S output of the Pi.

>From what you've reported above, a direct network client (e.g.
PULSE_SERVER=hostname mplayer /path/to/file ) should not have any more
latency than an on-device player.

Using module-tunnel-sink is likely to have hundreds of ms of latency.
module-tunnel-sink-new may perform better, but no guarantees on that.

Hopefully all of this gave you some new things to try.

--Sean



More information about the pulseaudio-discuss mailing list