roc-streaming / roc-pulse

Roc Toolkit modules for PulseAudio.
https://roc-streaming.org
GNU Lesser General Public License v2.1
7 stars 2 forks source link

PulseAudio modules for Roc Toolkit

Build GitHub release Matrix chat

What is this?

This repo provides a set of PulseAudio modules enabling it to use Roc Toolkit as a network transport and improve its service quality over an unreliable network such as Wi-Fi.

Advantages over Roc command-line tools:

Advantages over PulseAudio "native" protocol:

Advantages over PulseAudio built-in RTP support:

Interoperability

These modules can communicate with all other Roc Toolkit senders and receivers:

Build instructions

Simple build

When using this method, CMake will automatically download and build dependencies (Roc Toolkit, PulseAudio, libtool). Roc will be statically linked into the modules and there is no need to install it into the system.

First install build tools and PulseAudio:

sudo apt install -y \
    gcc g++ \
    make \
    libtool intltool m4 autoconf automake \
    meson libsndfile-dev \
    cmake \
    scons \
    git \
    wget \
    python3 \
    pulseaudio

Then build PulseAudio modules:

make

And then install modules into the system:

sudo make install

Advanced build

You can disable automatic downloading of dependencies and build them manually.

First, download, build and install Roc Toolkit into the system as described on this page.

Then download and unpack PulseAudio source code from here. There is no need to configure and build it, only source code is needed.

Note: PulseAudio doesn't provide official API for out-of-tree modules. This is the reason why we need full PulseAudio source code to build modules.

Note: PulseAudio source code should have exactly the same version as the actual PulseAudio daemon which will be used to load modules.

Optionally, download and unpack libtool. As with PulseAudio, there is no need to build it. Alternatively, install it into the system using your package manager.

Then you can build and install modules:

mkdir build/x86_64-linux-gnu
cd build/x86_64-linux-gnu
cmake ../.. \
  -DDOWNLOAD_ROC=OFF \
  -DDOWNLOAD_PULSEAUDIO=OFF \
  -DDOWNLOAD_LIBTOOL=OFF \
  -DPULSEAUDIO_DIR=<...> \
  -DPULSEAUDIO_VERSION=<...>
make VERBOSE=1
sudo make install

Don't forget to specify correct PULSEAUDIO_VERSION!

If you've installed Roc Toolkit to non-standard directory, you can use -DROC_INCLUDE_DIR=<...> and -DROC_LIB_DIR=<...>. If you want to use custom libtool instead of the one from system, you can use -DLIBTOOL_DIR=<...>.

Cross-compilation

For simple cases, you can do everything automatically by specifying just two environment variables:

TOOLCHAIN_PREFIX=<...> PULSEAUDIO_VERSION=<...> make

For more granular configuration, you can invoke CMake directly:

mkdir build/<...>
cd build/<...>
cmake ../.. -DTOOLCHAIN_PREFIX=<...> -DPULSEAUDIO_VERSION=<...>
make VERBOSE=1

Commands above will cross-compile PulseAudio modules, as well as download and cross-compile their dependencies. Dependencies will be statically linked into modules. Modules will be installed into ./bin.

In these commands, TOOLCHAIN_PREFIX defines toolchain triple of the target system, e.g. aarch64-linux-gnu. In this case aarch64-linux-gnu-gcc and other tools should be available in PATH.

For more complicated cases, refer to standard instructions for cross-compiling using CMake.

Pre-built toolchains

You can use one of our docker images with prebuilt cross-compilation toolchains. The commands below will cross-compile PulseAudio modules and install them to ./bin.

Raspberry Pi (64-bit):

docker run -t --rm -u "${UID}" -v "${PWD}:${PWD}" -w "${PWD}" \
  rocstreaming/toolchain-aarch64-linux-gnu \
    env TOOLCHAIN_PREFIX=aarch64-linux-gnu PULSEAUDIO_VERSION=<...> make

Raspberry Pi 2 and later (32-bit):

docker run -t --rm -u "${UID}" -v "${PWD}:${PWD}" -w "${PWD}" \
  rocstreaming/toolchain-arm-linux-gnueabihf \
    env TOOLCHAIN_PREFIX=arm-linux-gnueabihf PULSEAUDIO_VERSION=<...> make

Raspberry Pi 1 and Zero (32-bit):

docker run -t --rm -u "${UID}" -v "${PWD}:${PWD}" -w "${PWD}" \
  rocstreaming/toolchain-arm-bcm2708hardfp-linux-gnueabi \
    env TOOLCHAIN_PREFIX=arm-bcm2708hardfp-linux-gnueabi PULSEAUDIO_VERSION=<...> make

Running receiver

For the receiving side, use module-roc-sink-input PulseAudio module. It creates a PulseAudio sink input that receives samples from Roc sender and passes them to the sink it is connected to. You can then connect it to any audio device.

Roc sink input supports several options:

option default description note
local_ip 0.0.0.0 local address to bind to
local_source_port 10001 local port for source (RTP) packets
local_repair_port 10002 local port for repair (FEC) packets
local_control_port 10003 local port for control (RTCP) packets
sink \<default sink> the name of the sink to connect the new sink input to
sink_input_properties empty additional sink input properties
sink_input_rate 44100 local sink input sample rate
sink_input_format f32 local sink input sample format (f32)
sink_input_chans stereo local sink input channel layout (mono, stereo)
packet_encoding_id 10 encoding id for audio packets (any number, but same on sender and receiver) for custom network encoding
packet_encoding_rate 44100 sample rate for audio packets for custom network encoding
packet_encoding_format s16 sample format for audio packets (s16) for custom network encoding
packet_encoding_chans stereo channel layout for audio packets (mono, stereo) for custom network encoding
fec_encoding rs8m encoding for FEC packets (default, disable, rs8m, ldpc)
resampler_backend selected automatically resampler backend (default, builtin, speex, speexdec)
resampler_profile medium resampler profile (default, high, medium, low)
latency_backend selected automatically latency tuner backend (default, niq)
latency_profile selected automatically latency tuner profile (default, intact, responsive, gradual)
target_latency_msec 200 target latency in milliseconds
latency_tolerance_msec selected automatically maximum latency deviation in milliseconds
io_latency_msec 40 playback latency in milliseconds
no_play_timeout_msec selected automatically no playback timeout in milliseconds
choppy_play_timeout_msec selected automatically choppy playback timeout in milliseconds

Here is how you can create a Roc sink input from command line:

pactl load-module module-roc-sink-input

Alternatively, you can add this line to /etc/pulse/default.pa to create a Roc sink input automatically at PulseAudio start:

load-module module-roc-sink-input

You can then connect the Roc sink input to an audio device (i.e. a sink) via command line:

# determine Roc sink-input number
pactl list sink-inputs

# connect Roc sink-input to a sink
pactl move-sink-input <roc_sink_input_number> <sink>

Or via the pavucontrol graphical tool:

image

Running sender

For the sending side, use module-roc-sink PulseAudio module. It creates a PulseAudio sink that sends samples written to it to a preconfigured receiver address. You can then connect an audio stream of any running application to that sink, or make it the default sink.

Roc sink supports several options:

option default description note
remote_ip required parameter remote receiver address
remote_source_port 10001 remote receiver port for source (audio) packets
remote_repair_port 10002 remote receiver port for repair (FEC) packets
remote_control_port 10003 remote receiver port for control (RTCP) packets
sink_name roc_sender the name of the new sink
sink_properties empty additional sink properties
sink_rate 44100 local sink sample rate
sink_format f32 local sink sample format (f32)
sink_chans stereo local sink channel layout (mono, stereo)
packet_encoding_id 10 encoding id for audio packets (any number, but same on sender and receiver) for custom network encoding
packet_encoding_rate 44100 sample rate for audio packets for custom network encoding
packet_encoding_format s16 sample format for audio packets (s16) for custom network encoding
packet_encoding_chans stereo channel layout for audio packets (mono, stereo) for custom network encoding
packet_length_msec 5 audio packet length in milliseconds
fec_encoding rs8m encoding for FEC packets (default, disable, rs8m, ldpc)
fec_block_nbsrc 18 number of source packets in FEC block
fec_block_nbrpr 10 number of repair packets in FEC block
resampler_backend selected automatically resampler backend (default, builtin, speex, speexdec)
resampler_profile medium resampler profile (default, high, medium, low)
latency_backend disabled latency tuner backend (default, niq) for sender-side latency tuner
latency_profile disabled latency tuner profile (default, intact, responsive, gradual) for sender-side latency tuner
target_latency_msec disabled target latency in milliseconds for sender-side latency tuner
latency_tolerance_msec disabled maximum latency deviation in milliseconds for sender-side latency tuner

Here is how you can create a Roc sink from command line:

pactl load-module module-roc-sink remote_ip=<receiver_ip>

Alternatively, you can add this line to /etc/pulse/default.pa to create a Roc sink automatically at PulseAudio start:

load-module module-roc-sink remote_ip=<receiver_ip>

You can then connect an audio stream (i.e. a sink input) to the Roc sink via command line:

pactl move-sink-input <sink_input_number> roc_sender

Or via the pavucontrol graphical tool:

image

Advanced configuration

Custom local encoding

You can use these options to control how Roc sink or sink input presents itself to PulseAudio:

These options may be useful if you want to avoid automatic conversions (such as resampling and channel mapping) performed by PulseAudio when device and application encodings don't match.

Note that if sink/sink input encoding doesn't match packet encoding (see below), Roc will perform conversion by itself, so you probably want to configure that too.

Also note that Roc receiver usually performs resampling even when there is no sample rate mismatch, because it is used to adjust clock speed. Hence it makes sense to avoid resampling in PulseAudio and keep resampling only in Roc.

Custom packet encoding

By default, module-roc-sink-input and module-roc-sink code audio as 44100Hz with 16-bit stereo.

To employ alternative encoding, you need to provide the following options:

All four parameters should be provided on both sender and receiver and have exact same values.

Custom FEC encoding

By default, module-roc-sink-input and module-roc-sink use Reed-Solomon (rs8m) FEC encoding for loss repair.

This can be configured via fec_encoding parameter. Available options are:

This parameter should be provided on both sender and receiver and have exact same value.

Configuring latency

Essential receiver-side (module-roc-sink-input) parameters are:

You can also force specific latency tuner settings (by default they're auto-selected based on target latency):

Note: By default, latency tuner happens on receiver side. It is also possible (though rarely needed) to do it on sender side. For that reason, all parameters above are also available on sender, but are disabled by default. If you enabled them on sender, you likely want to disable them on receiver by using intact latency profile.

There are also sender-side (module-roc-sink) parameters that affect latency:

For lower latency, you may need lower packet length and FEC block size. And vice versa, for higher latency and network jitter, you may need to increase both packet length (for less overhead) and FEC block size (for better repair).

Configuring source or sink name

PulseAudio sinks and sink inputs have name and description. Name is usually used when the sink or sink input is referenced from command-line tools or configuration files, and description is shown in the GUI.

Sink name and description can be configured via sink_name module argument and device.description sink property set by sink_properties module argument:

pactl load-module module-roc-sink remote_ip=192.168.1.38 \
  sink_name=my_name sink_properties=device.description=My-Description

Sink input name and description can be configured via sink_input_name module argument and media.name sink input property set by sink_input_properties module argument:

pactl load-module module-roc-sink-input \
  sink_input_name=my_name sink_input_properties=media.name=My-Description

Troubleshooting

First, run PulseAudio server in verbose mode, both on sending and receiving sides:

pulseaudio -vvv

Among other things, you should find some messages from Roc sink and sink-input there, which may give some idea about what's going wrong.

Second, you can try to replace sender, receiver, or both with Roc command line tools to determine whether the issue is specific to PulseAudio modules or not.

Authors

See here.

License

PulseAudio modules are licensed under LGPL 2.1.

For details on Roc Toolkit licensing, see here.