Conclusions first: it’s doable but very unstable at the moment.
My OS version: Ubuntu 23.04.
Background
Just searching for “homepod linux" would yield results mostly related to connecting HomePod to Homebridge or Home Assistant, which isn’t what I’m aiming for.
The closest project I could find was BabelPod, but it hadn’t been updated for years.
The openairplay project has an old list of Airplay compatible libraries and programs. I’ve found a few more in addition to that:
I wasn’t aware of the distinction between the different protocols talked by Homepod and Apple TVs. But at some point I came across the Pulseaudio-roap2 project, then I know I’m looking for a ROAP client.
I also found that Pipewire seems to support ROAP. From this issue it seems it might work.
Testing Pipewire 0.3.65, the default version on Ubuntu 23.04
I loaded the roap-discover module, printing debug messages:
PIPEWIRE_DEBUG=3 pw-cli -m load-module libpipewire-module-raop-discover
In my audio panel I immediately see the device detected. (Don’t know why it’s duplicated though.)
I selected the device as output, and started playing some sound. The Homepod seems to detect the stream because it stopped the music that I was originally playing via another Apple device. However, there was no sound from the Homepod!
Tracing the debug message and Wireshark packet capture showed that the server (Homepod) returned a message with timing_port=0
, which makes Pipewire thinks that the response is invalid.
Looking through newer commits to the file, I found one commit that should fix the issue. However the commit is only available after version 0.3.68.
Building the latest Pipewire and copying over the module file
So now the goal becomes to upgrade Pipewire on my system to at least 0.3.68. My first idea is to build the entire project manually and just copy over the module file libpipewire-module-raop-sink.so
to /usr/lib/x86_64-linux-gnu/pipewire-0.3/
.
Result? the older version of Pipewire wouldn’t load the plugin file from a newer version.
So now I have to really upgrade the whole Pipewire.
Upgrading to Pipewire 0.3.71 from Debian experimental
Add the package archive. /etc/apt/sources.list.d/debian-experimental.list :
deb [signed-by=/usr/share/keyrings/debian-archive-buster-automatic.gpg] http://httpredir.debian.org/debian/ experimental main contrib non-free
Install the keyring:
sudo apt install debian-archive-keyring
Next is to identify the packages that needs to be pinned. I also found this excellent tutorial on APT pinning.
The easiest way is to add every binary package built from the pipewire source package to the pin list.
In case there is any unmet dependency requirements when trying to upgrade pipewire, just add those dependencies to the pin list.
Here’s my final pin list, /etc/apt/preferences.d/pin-experimental :
Explanation: use only specific packaeges from debian experimental
Package: *
Pin: release o=Debian,a=experimental,n=rc-buggy,l=Debian
Pin-Priority: -10
Package: gstreamer1.0-pipewire pipewire pipewire-alsa pipewire-audio pipewire-audio-client-libraries pipewire-jack pipewire-libcamera pipewire-tests pipewire-v4l2 pipewire-bin libpipewire-0.3-modules libpipewire-0.3-0 libspa-0.2-bluetooth ilibspa-0.2-dev libspa-0.2-jack libspa-0.2-modules libpipewire-0.3-common pipewire-pulse
Pin: release o=Debian,a=experimental,n=rc-buggy,l=Debian
Pin-Priority: 500
Then simply run apt upgrade
and restart pipewire.
Use PIPEWIRE_DEBUG=3 pw-cli -m load-module libpipewire-module-raop-discover
to load the module again, and select Homepod as the main audio output.
Note: before I realized that I can just get the Debian keys in Ubuntu package debian-archive-keyring
I’m lazy to learn the apt-key transition again.
- The archive signing keys page does not show which key signs the experimental channel package, perhaps there is something that I’ve mistaken.
- Before installing the correct keys,
apt update
would complain key 648ACFD622F3D138 is not found. Looking it up, it is the debian 10 key. - To trust the debian 10 key, one needs to download the key, create a new keyring and add it to the keyring, and finally configure apt to use that keyring with that package source.
Testing Pipewire 0.3.71: it works! (sort of)
Now I’m able to play sound to the Homepod!…
But it will only keep playing for about 40 seconds and then there is no sound. This error was shown:
[E][02299.572523] default | [ rtsp-client.c: 462 on_source_io()] 0x55d067414b50: got connection error -32 (管線損壞)
[E][02299.572567] mod.raop-sink | [module-raop-sink: 1481 rtsp_error()] error -32
[I][02299.572628] mod.raop-sink | [module-raop-sink: 1474 rtsp_disconnected()] disconnected
[I][02303.821931] pw.stream | [ stream.c: 606 impl_send_command()] 0x55d0673eecc0: command Spa:Pod:Object:Command:Node:Suspend
[I][02303.822024] pw.node | [ impl-node.c: 412 node_update_state()] (raop_sink.woshi.local.192.168.212.40.7000-0) running -> suspended
[I][02303.822222] pw.node | [ impl-node.c: 1985 pw_impl_node_destroy()] (raop_sink.woshi.local.192.168.212.40.7000-0) destroy
[I][02303.822444] pw.node | [ impl-node.c: 1985 pw_impl_node_destroy()] (raop_sink.woshi.local.192.168.212.40.7000-0) destroy
[I][02303.822586] default | [ rtsp-client.c: 101 pw_rtsp_client_destroy()] destroy client 0x55d06744f140
[I][02303.822614] default | [ rtsp-client.c: 101 pw_rtsp_client_destroy()] destroy client 0x55d067414b50
Strangely I don’t see any connection error while recording the packets with Wireshark.
I’ll debug this problem some other day…