PulseAudio RTP – how to play sound on sender too?

I am streaming music from a single controlling PC (“sender”) to multiple PCs (receivers) on my LAN as described here:

https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Network/#index2h3

How do I also get the same music to play on the controlling PC (sender)?

My current setup is very simple for testing right now:

sender

pactl load-module module-null-sink sink_name=rtpsink1
pactl load-module module-rtp-send source=rtpsink1.monitor

receivers

pactl load-module module-rtp-recv

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

This is my preferred, working solution. It is based on: pulsertp-multiroom
https://github.com/mada3k/pulsertp-multiroom/blob/master/scripts/pulsertpm-start.sh

I am using this to send audio to multiple receivers (different rooms). There are two lines, noted below, which make the sender a receiver too and solve this question. They are:

  • module-rtp-send with the sender’s only IP address
  • module-rtp-recv with the sender’s only IP address

The advantage of this approach is that all receivers (including the one on the sender) stay in sync. That was not the case when combining syncs as in my other answer.

After editing to enter the correct IP addresses, I run this script on the sender when ready to start up RTP unicasting:

#!/bin/bash
#
# PulseRTP-multiroom
#   Loads RTP sender modules to setup multiroom audio at request
#
#   Notes
#     * If you have issues, and have multiple network interfaces, add source_ip= with you prefered local IP
#     * mtu=1408 is good initial value. It makes room for 352 PCM frames in 16/44.1k.
#
pa_rtp_mtu=1408
pa_sink="rtpsink1"

echo "timedatectl status:"
timedatectl status --no-pager | grep 'NTP service: active'
if [[ $? -ne 0 ]]; then
    timedatectl set-ntp true
    sleep 2
    timedatectl status --no-pager | grep 'NTP service: active'
    if [[ $? -ne 0 ]]; then
        echo "WARNING: NTP service not active"
    else
        echo "timedatectl status OK"
    fi
fi

pacmd list-modules | grep module-native-protocol-unix
if [ $? -ne 0 ]; then
    pactl load-module module-native-protocol-unix
fi
pacmd list-modules | grep module-default-device-restore
if [ $? -ne 0 ]; then
    pactl load-module module-default-device-restore
fi
pacmd list-modules | grep module-rescue-streams
if [ $? -ne 0 ]; then
    pactl load-module module-rescue-streams
fi
pacmd list-modules | grep module-always-sink
if [ $? -ne 0 ]; then
    pactl load-module module-always-sink
fi
pacmd list-modules | grep module-intended-roles
if [ $? -ne 0 ]; then
    pactl load-module module-intended-roles
fi
pacmd list-modules | grep module-suspend-on-idle
if [ $? -ne 0 ]; then
    load-module module-suspend-on-idle
fi

#hardcoded addresses of each receiver:
pactl load-module module-rtp-send source=${pa_sink}.monitor mtu=${pa_rtp_mtu} destination_ip=192.168.0.1 #this is the sender's IP address (see below)
pactl load-module module-rtp-send source=${pa_sink}.monitor mtu=${pa_rtp_mtu} destination_ip=192.168.0.2
pactl load-module module-rtp-send source=${pa_sink}.monitor mtu=${pa_rtp_mtu} destination_ip=192.168.0.3
pactl load-module module-rtp-send source=${pa_sink}.monitor mtu=${pa_rtp_mtu} destination_ip=192.168.0.4
pactl load-module module-rtp-send source=${pa_sink}.monitor mtu=${pa_rtp_mtu} destination_ip=192.168.0.5

On each receiver:

pactl load-module module-rtp-recv sap_address=192.168.0.X

Use the receiver’s actual IP address, e.g., 192.168.0.2

Finally, on the sender, make it a receiver too:

pactl load-module module-rtp-recv sap_address=192.168.0.1

You can make the configuration on the receivers permanent by editing /etc/pulse/default.pa

To stop RTP casting to the receivers, run this script on the sender:

#!/bin/bash
#
# PulseRTP-Multiroom
#   Unloads RTP sender modules to avoid unnecessary bandwidth hogging
#

pa_sink="rtpsink1"

IFS=$'\n'
for rtpn in `pactl list modules short|grep ${pa_sink}`; do
    PAM_ID=`echo $rtpn|awk '{print $1}'`
    pactl unload-module $PAM_ID
    echo " * unload-module id $PAM_ID done"
done

For starting and stopping music play, no changes are needed on the receivers. Their config can be made permanent. I did not have the need to make any permanent config changes on the sender with my modified version of the start script above.

Solution 2

Use module-combine-sink.

On the sender:

pacmd list-sinks

Use the values of the of each sink to be combined in slaves list below:

pactl load-module module-combine-sink sink_name=rtp1combined slaves=abcd,wxyz

where abcd and wxyz would be the names of the two sinks to combine. In my case, that would be slaves=rtpsink1,alsa_output.pci-0000_02_00.1.hdmi-stereo

UPDATE: this method apparently has the disadvantage that the sender is not synchronized with the nodes.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply