Compare commits

..

No commits in common. "09c91bade9aebc269ae80f9460cd217cfbcce511" and "dea6a2ec1b066c1564198c1ed673408ffe50588d" have entirely different histories.

2 changed files with 41 additions and 203 deletions

View File

@ -1,4 +1,4 @@
# pulseaudio-tcp - Redirect PulseAudio to and from Remote Computer # pulseaudio-tcp - Redirect PulseAudio to Remote Computer
## Requirements ## Requirements
@ -12,10 +12,7 @@
### Installation on Client Playback Host ### Installation on Client Playback Host
* pulseaudio-tcp should be installed on a computer running PulseAudio or Pipewire with PulseAudio compatibility. * pulseaudio-tcp should be installed on a computer running PulseAudio or Pipewire with PulseAudio compatibility.
* The computer pulseaudio-tcp is installed on ("local host") should use use a remote computer ("remote host") for playback. * The computer pulseaudio-tcp is installed on ("local host") should use use a remote computer that has speakers attached ("remote host") for playback.
- This is refered to as "outbound" audio.
* The local host should receive audio from the remote host for recording.
- This is refered to as "inbound" audio.
* The IP address and username of the remote host should be known. * The IP address and username of the remote host should be known.
### SSH to User Session on Remote Host ### SSH to User Session on Remote Host
@ -36,7 +33,7 @@ sudo make install
## Usage ## Usage
### Setup Procedure ### Configuration
As regular user on local host: As regular user on local host:
@ -44,37 +41,19 @@ As regular user on local host:
pulseadio-tcp setup pulseadio-tcp setup
``` ```
The following questions will be asked: ### Start service
* IP address of remote host.
* Name of user on remote host running PulseAudio/Pipewire.
* Enable inbound audio.
* Enable outbound audio.
### Configuration File
The setup procedure writes a configuration file `$HOME/.config/pulseaudio-tcp/config.inc.sh`:
```
remote_ip="192.168.1.166"
remote_user="remoteuser"
inbound=true
outbound=true
```
### Starting the Service
```shell ```shell
pulseadio-tcp start pulseadio-tcp start
``` ```
### Stopping the Service ### Stop service
```shell ```shell
pulseadio-tcp stop pulseadio-tcp stop
``` ```
### Checking if the Service is running ### Check if service is running
```shell ```shell
pulseadio-tcp status pulseadio-tcp status

View File

@ -94,70 +94,6 @@ do_setup() {
fi fi
done done
inbound=${inbound:-true}
while true ; do
if "$inbound" ; then
prompt="Y/n"
else
prompt="y/N"
fi
read -r -p "Enable inbound audio ($prompt): " inbound_in
case "$inbound_in" in
"")
if test -n "$inbound" ; then
inbound_in=$inbound
break 2
fi
;;
y|Y)
inbound_in=true
break 2
;;
n|N)
inbound_in=false
break 2
;;
*)
echo "ERROR: Please type \"y\" or \"n\"." >&2
;;
esac
done
outbound=${outbound:-true}
while true ; do
if "$outbound" ; then
prompt="Y/n"
else
prompt="y/N"
fi
read -r -p "Enable outbound audio ($prompt): " outbound_in
case "$outbound_in" in
"")
if test -n "$outbound" ; then
outbound_in=$outbound
break 2
fi
;;
y|Y)
outbound_in=true
break 2
;;
n|N)
outbound_in=false
break 2
;;
*)
echo "ERROR: Please type \"y\" or \"n\"." >&2
;;
esac
done
cat > "$config" << EOF cat > "$config" << EOF
# Configuration file for pulseaudio-tcp # Configuration file for pulseaudio-tcp
# Generated on $(LC_ALL=C date) by $USER using $0 # Generated on $(LC_ALL=C date) by $USER using $0
@ -167,27 +103,15 @@ remote_ip="$remote_ip_in"
# Username on remote host # Username on remote host
remote_user="$remote_user_in" remote_user="$remote_user_in"
# Enable inbound audio from $remote_user @$remote_ip?
inbound=$inbound_in
# Enable outbound audio to $remote_user @$remote_ip?
outbound=$outbound_in
EOF EOF
} }
# Check if SSH port forwarding is running # Check if SSH port forwarding is running
check_pa_ssh() { check_pa_ssh() {
for pid in $(pidof ssh) ; do for pid in $(pidof ssh) ; do
if grep -Fq $USER-pulseaudio /proc/"$pid"/cmdline ; then grep -Fq $USER-pulseaudio /proc/"$pid"/cmdline && return 0
grep -Fq -e -L /proc/"$pid"/cmdline || {
echo "ERROR: No SSH port forwarding to remote server is established." >&2
return 1 ;
}
fi
done done
echo "ERROR: No SSH is established." >&2
return 1 return 1
} }
@ -195,32 +119,24 @@ check_pa_ssh() {
do_status() { do_status() {
rv=0 rv=0
if ! check_pa_ssh ; then
rv=1
fi
if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
echo "ERROR: PulseAudio module \"module-native-protocol-tcp\" is not loaded on remote_ip=$remote_ip." >&2 echo "ERROR: PulseAudio module \"module-native-protocol-tcp\" is not loaded on remote_ip=$remote_ip." >&2
rv=1 rv=1
fi fi
if "$outbound" ; then if ! check_pa_ssh ; then
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then echo "ERROR: SSH port forwarding to remote_ip=$remote_ip is not running." >&2
echo "ERROR: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2 rv=1
rv=1
fi
if ! pactl get-default-sink | grep -Fq "tunnel-sink.tcp:127.0.0.1" ; then
echo "ERROR: \"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink." >&2
rv=1
fi
fi fi
if "$inbound" ; then if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then echo "ERROR: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
echo "ERROR: PulseAudio module \"module-tunnel-source\" is not loaded." >&2 rv=1
rv=1 fi
fi
if ! pactl get-default-sink | grep -Fq "tunnel-sink.tcp:127.0.0.1" ; then
echo "ERROR: \"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink." >&2
rv=1
fi fi
if test "$rv" -eq 0 ; then if test "$rv" -eq 0 ; then
@ -241,13 +157,8 @@ sync_pa_cookie() {
fi fi
} }
# Establish SSH port forwarding to PulseAudio TCP server on remote host
establish_ssh_portforward() {
_ssh -fNT -L 127.0.0.1:4713:127.0.0.1:4713
}
# Enable PulseAudio TCP tunnel server on remote host # Enable PulseAudio TCP tunnel server on remote host
enable_remote_pa_tunnel_server() { enable_pa_tunnel_server() {
if _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then if _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
echo "INFO: PulseAudio module \"module-native-protocol-tcp\" already loaded on remote_ip=$remote_ip." >&2 echo "INFO: PulseAudio module \"module-native-protocol-tcp\" already loaded on remote_ip=$remote_ip." >&2
return 0 return 0
@ -260,8 +171,13 @@ enable_remote_pa_tunnel_server() {
fi fi
} }
# Establish SSH port forwarding to PulseAudio TCP server on remote host
establish_ssh_portforward() {
_ssh -fNT -L 127.0.0.1:4713:127.0.0.1:4713
}
# Enable tunnel sink on local host # Enable tunnel sink on local host
enable_local_pa_tunnel_sink() { enable_pa_tunnel_sink() {
if pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then if pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
echo "INFO: PulseAudio module \"module-tunnel-sink\" already loaded." >&2 echo "INFO: PulseAudio module \"module-tunnel-sink\" already loaded." >&2
return 0 return 0
@ -274,22 +190,8 @@ enable_local_pa_tunnel_sink() {
fi fi
} }
# Enable tunnel source on local host
enable_local_pa_tunnel_source() {
if pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
echo "INFO: PulseAudio module \"module-tunnel-source\" already loaded." >&2
return 0
elif pactl load-module module-tunnel-source server=tcp:127.0.0.1 ; then
echo "INFO: Loaded PulseAudio module \"module-tunnel-source\"." >&2
return 0
else
echo "ERROR: Unable to load PulseAudio module \"module-tunnel-source\"." >&2
return 1
fi
}
# Set tunnel sink as default sink on local host # Set tunnel sink as default sink on local host
set_local_pa_tunnel_sink_as_default() { set_pa_tunnel_sink_as_default() {
if pactl set-default-sink tunnel-sink.tcp:127.0.0.1 ; then if pactl set-default-sink tunnel-sink.tcp:127.0.0.1 ; then
echo "INFO: Set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink." >&2 echo "INFO: Set \"tunnel-sink.tcp:127.0.0.1\" as default PulseAudio sink." >&2
return 0 return 0
@ -302,23 +204,16 @@ set_local_pa_tunnel_sink_as_default() {
# Perform start operation # Perform start operation
do_start() { do_start() {
sync_pa_cookie || return 1 sync_pa_cookie || return 1
enable_pa_tunnel_server || return 1
establish_ssh_portforward || return 1 establish_ssh_portforward || return 1
enable_remote_pa_tunnel_server || return 1 enable_pa_tunnel_sink || return 1
set_pa_tunnel_sink_as_default || return 1
if "$outbound" ; then
enable_local_pa_tunnel_sink || return 1
set_local_pa_tunnel_sink_as_default || return 1
fi
if "$inbound" ; then
enable_local_pa_tunnel_source || return 1
fi
return 0 return 0
} }
# Remove PulseAudio TCP tunnel sink on local host # Remove PulseAudio TCP tunnel sink on local host
remove_local_pa_tunnel_sink() { remove_pa_tunnel_sink() {
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
echo "INFO: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2 echo "INFO: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
return 0 return 0
@ -341,32 +236,17 @@ remove_local_pa_tunnel_sink() {
fi fi
} }
# Remove PulseAudio TCP tunnel source on local host # Terminate SSH portforwarding session
remove_local_pa_tunnel_source() { terminate_ssh_portforward() {
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then for pid in $(pidof ssh) ; do
echo "INFO: PulseAudio module \"module-tunnel-source\" is not loaded." >&2 if grep -Fq $USER-pulseaudio /proc/"$pid"/cmdline ; then
return 0 kill -TERM "$pid"
elif ! pactl list sources | grep -Fq "tunnel-source.tcp:127.0.0.1" ; then
echo "INFO: No PulseAudio tunnel source from 127.0.0.1 exists." >&2
return 0
else
owner_module=$(
pactl --format json list sources 2>/dev/null | \
jq '.[] | select(.name=="tunnel-source.tcp:127.0.0.1") | .owner_module' -r
)
if ! pactl unload-module "$owner_module" ; then
echo "ERROR: Could not unload owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"." >&2
return 1
else
echo "INFO: Unloaded owner module $owner_module of PulseAudio source \"tunnel-source.tcp:127.0.0.1\"." >&2
return 0
fi fi
fi done
} }
# Stop PulseAudio TCP tunnel server on remote host. # Stop PulseAudio TCP tunnel server on remote host.
disable_remote_pa_tunnel_server() { disable_pa_tunnel_server() {
if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then if ! _ssh pactl list modules | grep -Fq "Name: module-native-protocol-tcp" ; then
echo "INFO: PulseAudio module \"module-native-protocol-tcp\" not loaded on remote_ip=$remote_ip." >&2 echo "INFO: PulseAudio module \"module-native-protocol-tcp\" not loaded on remote_ip=$remote_ip." >&2
return 0 return 0
@ -379,27 +259,11 @@ disable_remote_pa_tunnel_server() {
fi fi
} }
# Terminate SSH portforwarding session
terminate_ssh_portforward() {
for pid in $(pidof ssh) ; do
if grep -Fq $USER-pulseaudio /proc/"$pid"/cmdline ; then
kill -TERM "$pid"
fi
done
}
# Perform stop operation # Perform stop operation
do_stop() { do_stop() {
if "$outbound" ; then remove_pa_tunnel_sink || return 1
remove_local_pa_tunnel_sink || return 1
fi
if "$inbound" ; then
remove_local_pa_tunnel_source || return 1
fi
disable_remote_pa_tunnel_server || return 1
terminate_ssh_portforward || return 1 terminate_ssh_portforward || return 1
disable_pa_tunnel_server || return 1
return 0 return 0
} }
@ -452,7 +316,7 @@ else
""|-h|--help) ""|-h|--help)
cat << EOF cat << EOF
Setup and run encrypted connection to remote PulseAudio/Pipewire server Setup and run encrypted connection to remote PulseAudio/Pipewire server
Usage: $0 restart|setup|start|status|stop Usage: $0 setup|start|stop|status
EOF EOF
rv=0 rv=0
;; ;;
@ -468,17 +332,12 @@ EOF
do_stop do_stop
rv=$? rv=$?
;; ;;
restart)
do_stop
do_start
rv=$?
;;
status) status)
do_status do_status
rv=$? rv=$?
;; ;;
*) *)
echo "ERROR: Usage: $0 restart|setup|start|status|stop" >&2 echo "ERROR: Usage: $0 setup|start|stop|status" >&2
rv=1 rv=1
;; ;;
esac esac