Compare commits
No commits in common. "09c91bade9aebc269ae80f9460cd217cfbcce511" and "dea6a2ec1b066c1564198c1ed673408ffe50588d" have entirely different histories.
09c91bade9
...
dea6a2ec1b
33
README.md
33
README.md
@ -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
|
||||||
|
199
pulseaudio-tcp
199
pulseaudio-tcp
@ -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,16 +119,16 @@ 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
|
||||||
|
echo "ERROR: SSH port forwarding to remote_ip=$remote_ip is not running." >&2
|
||||||
|
rv=1
|
||||||
|
fi
|
||||||
|
|
||||||
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
|
if ! pactl list modules | grep -Fq "Name: module-tunnel-sink" ; then
|
||||||
echo "ERROR: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
|
echo "ERROR: PulseAudio module \"module-tunnel-sink\" is not loaded." >&2
|
||||||
rv=1
|
rv=1
|
||||||
@ -214,14 +138,6 @@ do_status() {
|
|||||||
echo "ERROR: \"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink." >&2
|
echo "ERROR: \"tunnel-sink.tcp:127.0.0.1\" is not the default PulseAudio sink." >&2
|
||||||
rv=1
|
rv=1
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
if "$inbound" ; then
|
|
||||||
if ! pactl list modules | grep -Fq "Name: module-tunnel-source" ; then
|
|
||||||
echo "ERROR: PulseAudio module \"module-tunnel-source\" is not loaded." >&2
|
|
||||||
rv=1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "$rv" -eq 0 ; then
|
if test "$rv" -eq 0 ; then
|
||||||
echo "INFO: All checks passed; pulseaudio-tcp status is okay." >&2
|
echo "INFO: All checks passed; pulseaudio-tcp status is okay." >&2
|
||||||
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user