Somewhat comprehensive python implementation of AP2 receiver using some multi-room features. For now it implements:
- HomeKit transient pairing (SRP/Curve25519/ChaCha20-Poly1305) - bit flag 48
- HomeKit non-transient pairing
- Some refinements for HomeKit interaction (e.g. managed/active flags)
- Persist device name and some HomeKit properties across restarts (just use the -m flag again to set the device name anew)
- FairPlay (v3) authentication and decryption of AES keys - the first and only Python implementation. Credit to @systemcrash for implementation.
- Receiving of both REALTIME and BUFFERED Airplay2 audio streams
- Airplay2 Service publication
- Decoding of all Airplay2 supported CODECs: ALAC, AAC, OPUS, PCM. Ref: here and here
- Output latency compensation for sync with other Airplay receivers
- ANNOUNCE and RSA AES for unbuffered streaming from iTunes/Windows
- Spotify (via AirPlay2) and other live media streams with AES keys.
- RTCP
- RFC2198 RTP Redundancy handling (basic); enable bit flag 61
- streamConnections; enable bit flag 59
For now it does not implement:
- FairPlay v2
- Accurate audio sync (with help of PTP and/or NTP)
It may never implement:
- MFi Authentication (requires MFi hardware module)
This code is experimental, yet fully functional. It can act as a real receiver but does not implement all airplay protocols and related pairing/authentication methods.
Next steps:
- PTP (Precision Time Protocol)
- Remove all os specific code (Soft Volume management)
- Sender (branch-sender) - Implementation
- Raspbian package
- DACP/( MRP?) Support
- FairPlay v2 Support
Since multithreading is now enabled, this allows multiple concurrent connections. There are no safeguards built to prevent you playing multiple streams. Python multiprocessing makes this "DJ" mode a possibility but makes stream management and session management (global state data) nigh impossible. So threading is the right approach in the receiver.
HomeKit and other AP senders can now connect concurrently to the receiver and perform operations. This opens the path to Remote Control functionality.
If you encounter strange errors like NonUniqueNameException, or Address already in use, and you run on macOS, you may have noticed that macOS and this app both try to send updates. Here is a possible workaround.
Install docker and then build the image:
docker build -f docker/Dockerfile -t ap2-receiver .
To run the receiver:
docker run -it --rm --device /dev/snd --net host --volume `pwd`/pairings/:/airplay2/pairings/ ap2-receiver
Default network device is wlan0, you can change this with AP2IFACE env variable:
docker run -it --rm --device /dev/snd --env AP2IFACE=eth0 --net host ap2-receiver
Example Docker Compose
docker-compose -f docker/docker-compose.yaml up
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 pyaudio
To run the receiver please use Python 3 and do the following:
- Run the following commands
brew install python3
brew install portaudio
virtualenv -p /usr/local/bin/python3 proto
source proto/bin/activate
pip install -r requirements.txt
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" pyaudio
python ap2-receiver.py -m myap2 --netiface=en0
Note: in recent macOS versions (e.g. Ventura), you must disable AirPlay Receiver: System Settings -> AirDrop & Handoff -> AirPlay Receiver: disable.
To run the receiver please use Python 3 and do the following:
- Run the following commands
cd [WHERE_YOU_CLONED_AIRPLAY2_RECEIVER]
virtualenv ap2env
.\ap2env\Scripts\activate
pip install -r requirements.txt
pip install pipwin pycaw
pipwin install pyaudio
python ap2-receiver.py -m myap2 -n [YOUR_INTERFACE_GUID] (looks like this for instance {02681AC0-AD52-4E15-9BD6-8C6A08C4F836} )
- the AirPlay 2 receiver is announced as myap2.
Tested on Python 3.7.5 / macOS 10.15.2 with iPhone X 13.3 and Raspberry Pi 4