The Debian install steps create a virtualenv inside the clone and then walk into it
AirPlay 2 Receiver - Python implementation
At a glance
- What is it?
- An experimental Python AirPlay 2 receiver that does HomeKit pairing, FairPlay v3 key decryption and four codecs, with four platform install paths that disagree about how to enter the virtualenv and how to name a network interface. No releases, no license file recorded, last commit 2026-06-29.
- Who is it for?
- This is a receiver to try on a spare Raspberry Pi with a Python 3 toolchain already in place, not one to build a product on. The audio path is the strongest part: four codecs, realtime and buffered streams, RTCP, RTP redundancy and a bit flag for streamConnections, with latency compensation against other receivers.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 100 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Debian steps create the virtualenv inside the clone and then cd into it
The Debian section is a single block, and the order of its directory changes decides whether it works:
sudo apt install -y libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev portaudio19-dev python3 python3-pip python3-pyaudio build-essential pkg-config git alsa-utils
git clone https://github.com/openairplay/airplay2-receiver.git
cd airplay2-receiver/
pip3 install virtualenv
virtualenv airplay2-receiver
cd airplay2-receiver/
pip3 install -r requirements.txt
pip3 install pyaudioThe first `cd` enters the clone. `virtualenv airplay2-receiver` then creates a directory with the same name as the repository, inside it. The second `cd airplay2-receiver/` resolves to that new directory rather than to the repository root, so `pip3 install -r requirements.txt` is asking for a file that a virtualenv does not contain.
The block also never activates anything. The macOS and Windows sequences both run a source or activate step; this one installs with a bare `pip3`, which targets whatever interpreter pip3 is bound to rather than the environment just created. Two of the six commands in the block cannot do their job as written.
The macOS line pins portaudio 19.6.0 to an absolute Cellar path
The macOS path is the only one with a build step, and it hardcodes a Homebrew directory version:
pip install --global-option=build_ext --global-option="-I/usr/local/Cellar/portaudio/19.6.0/include" --global-option="-L/usr/local/Cellar/portaudio/19.6.0/lib" pyaudioHomebrew installs each version of a formula into its own numbered Cellar directory. As soon as portaudio is upgraded past 19.6.0 the include and library paths point at directories that are no longer there, and nothing in the command checks. The instruction immediately above it, `brew install portaudio`, does not pin a version, so the two lines disagree about whether 19.6.0 will still exist when you run them.
The section heading says macOS Catalina while the note below it names Ventura, which is the version where Apple's own AirPlay Receiver has to be switched off first through System Settings, AirDrop and Handoff. The environment is created as `proto`, not after the project name, which is the third name in play alongside `ap2env` on Windows.
The interface flag is spelled differently on each platform
There are three different ways to tell the receiver which network interface to announce on, and one of them is also the device name flag. On macOS the launch line is `python ap2-receiver.py -m myap2 --netiface=en0`. On Windows the same two ideas appear as `-m myap2 -n [YOUR_INTERFACE_GUID]`, with the interface given as a GUID such as {02681AC0-AD52-4E15-9BD6-8C6A08C4F836} rather than a name. So the long form is available on one platform and the short form on the other, and the Windows value is not the kind of string a Unix interface name looks like.
`-m` is the only documented way to set the device name. The project persists the device name and some HomeKit properties across restarts, which means the name given once by `-m myap2` is remembered afterwards and the flag has to be passed again to change it. The README states this as the reason to re-run the same flag rather than describing it as a configuration file, and there is no config file in the tree and no license file either. The root holds ap2-receiver.py, ap1_test.py, fp_decrypt.py, requirements.txt, the ap2 package directory and a pairings directory.
In Docker the same choice moves to an environment variable. The default interface is wlan0, so a Pi on Ethernet needs AP2IFACE set explicitly.
Ten requirements, two exact pins, one commented out
requirements.txt is short enough to read in full, and what it pins matters more than what it lists:
netifaces
zeroconf==0.38.3
biplist
pycryptodome
hexdump
srptools
hkdf
# pynacl
cryptography
requests
av==8.1.0Two of the ten carry an exact version, `zeroconf==0.38.3` and `av==8.1.0`, so those two cannot float with the rest. A third, `pynacl`, is present but commented out, leaving `pycryptodome` and `cryptography` as the two crypto providers in play, which is worth knowing when reading code that does pairing and key derivation.
The pinned zeroconf line connects to the one runtime problem the README calls out by name. If you hit NonUniqueNameException or an address already in use on macOS, the cause given is that macOS and the app both try to send mDNS updates, and the fix offered is a link to a comment on a python-zeroconf issue. Pinning zeroconf to one exact version is what makes that behaviour predictable, and it is also what makes the package a candidate for conflict when the host's own discovery stack changes underneath it.
`netifaces` is the other unversioned entry, and it is what the interface selection above depends on.
Multiple streams at once are possible and nothing prevents them
The project is candid about the sharpest edge in its design. Multithreading is enabled, which allows multiple concurrent connections, and the sentence that follows says there are no safeguards built to prevent you playing multiple streams. Nothing in the code arbitrates between two senders pushing audio at the same time.
The reasoning behind the choice is given too. Python multiprocessing would make a DJ mode possible, where you mix between two connected devices, but it makes stream management and session management, described as global state data, close to impossible. Threading was chosen instead, and the same section treats the concurrency as the opening for Remote Control functionality: senders can connect at once and perform operations.
That trade is the right one for a receiver and it is why the pairing state matters. Device name and HomeKit properties persist across restarts, and the pairings directory at the root of the tree is where that state lives, which is why the Docker instructions bind mount the host's pairings directory into /airplay2/pairings. A container without that volume starts from nothing and has to be paired again.
MFi authentication is the one thing it says it will never implement
The feature list is followed by a short list of what is missing, and a third list of what will stay missing. FairPlay v2 is not implemented, and accurate audio sync is not implemented, with PTP or NTP named as the missing pieces. Accurate sync is the more noticeable of the two in practice, and the project compensates in the direction it can: output latency compensation for sync with other AirPlay receivers.
The third list holds a single entry, MFi Authentication, and the reason given is a hardware one: it requires an MFi hardware module. So the boundary the project draws for itself is not about FairPlay, which it does handle at v3, but about the certification path, which needs a chip it cannot put in software. Anyone pairing a sender that insists on MFi authentication will not get past that step.
The FairPlay work is credited in the feature list to @systemcrash and described as the first and only Python implementation. It is worth reading the project's own summary sentence as the summary of the whole thing: the code is experimental yet fully functional, it can act as a real receiver, and it does not implement all AirPlay protocols and related pairing and authentication methods. The last commit is dated 2026-06-29 and the repository has no GitHub releases.
Docker needs the sound device, the host network and a mounted pairings directory
The Raspberry Pi path builds one image and runs it three ways:
docker build -f docker/Dockerfile -t ap2-receiver .docker run -it --rm --device /dev/snd --net host --volume `pwd`/pairings/:/airplay2/pairings/ ap2-receiverdocker run -it --rm --device /dev/snd --env AP2IFACE=eth0 --net host ap2-receiverThree flags carry the whole design. `--device /dev/snd` hands the container the sound card, `--net host` gives it the host's network namespace so mDNS announcements actually reach senders on the LAN, and the volume mount keeps the pairing state on the host disk. The third command drops the volume and switches the interface to eth0 with AP2IFACE, which is what a wired Pi needs, since the default is wlan0.
The volume argument uses backticks for command substitution, which works in zsh and bash but not in a plain POSIX shell, and every block in the README is fenced as zsh including the Debian one that runs sudo apt. Compose is a single line, `docker-compose -f docker/docker-compose.yaml up`, pointing at a file under docker/ alongside the Dockerfile the build reads.
Editorial conclusion
This is a receiver to try on a spare Raspberry Pi with a Python 3 toolchain already in place, not one to build a product on. The audio path is the strongest part: four codecs, realtime and buffered streams, RTCP, RTP redundancy and a bit flag for streamConnections, with latency compensation against other receivers. The install paths are the weak part, and the Debian sequence in particular will not complete as printed, so budget for fixing it yourself. Before using it for anything that matters, check three things: the FairPlay v3 key handling and the credit to @systemcrash, the MFi authentication the project says it will never implement, and the fact that no license is recorded for the code you would be running. The last commit is dated 2026-06-29 and the repository has no GitHub releases.
Frequently asked questions
Can I use my Raspberry Pi as an AirPlay 2 receiver?
That is the platform the Docker instructions are written for. You build the image with docker build -f docker/Dockerfile -t ap2-receiver . and run it with docker run -it --rm --device /dev/snd --net host --volume `pwd`/pairings/:/airplay2/pairings/ ap2-receiver, which needs the sound device, the host network and a writable pairings directory. The README notes the project was tested on a Raspberry Pi 4.
Which audio formats can airplay2-receiver decode?
All the codecs AirPlay 2 supports: ALAC, AAC, OPUS and PCM. It receives both REALTIME and BUFFERED streams, handles RTCP and basic RFC2198 RTP redundancy behind bit flag 61, and handles streamConnections behind bit flag 59. Accurate audio sync with PTP or NTP is not implemented.
Does airplay2-receiver need MFi certification to pair an iPhone?
MFi authentication is the one thing the project lists as something it may never implement, because it requires an MFi hardware module. Pairing itself is implemented, both transient and non-transient HomeKit pairing, and FairPlay v3 authentication and AES key decryption are included, credited to @systemcrash. FairPlay v2 is not implemented.
Why does airplay2-receiver conflict with macOS over mDNS?
On macOS both the system and the app try to send mDNS updates, which shows up as NonUniqueNameException or an address already in use. The README points at a comment on a python-zeroconf issue as a possible workaround. requirements.txt pins zeroconf to 0.38.3, and on recent macOS you also have to turn off Apple's own receiver under System Settings, AirDrop and Handoff.
Can two iPhones send audio to airplay2-receiver at the same time?
They can connect concurrently, because multithreading is enabled. There are no safeguards to prevent you playing multiple streams, and the project explains that multiprocessing would have made stream management and session management too hard. It is not positioned as a DJ tool.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openairplay-airplay2-receiver)