A pure-Elixir AirPlay (RAOP) audio sender — discover receivers on the LAN and
stream lossless audio to them, with no native dependencies (just :crypto,
:gen_udp and :gen_tcp).
Targets classic AirPlay 1 / RAOP receivers (shairport-sync, AirPort Express, and — verified — Apple HomePods), streaming unencrypted ALAC over RTP with the NTP-style timing/sync the receiver requires.
Quick start
# Find receivers on the network (mDNS browse of _raop._tcp)
AirPlay.discover()
#=> [%{name: "Office", host: "172.16.42.35", port: 7000}, ...]
# Stream a file (decoded via ffmpeg) at 40% volume
{:ok, session} = AirPlay.play("172.16.42.35", "/music/track.flac", volume: 40)
AirPlay.set_volume(session, 25)
AirPlay.stop(session)
# Or stream raw PCM you already have (44.1kHz, s16le, stereo interleaved)
{:ok, session} = AirPlay.play_pcm("172.16.42.35", pcm, volume: 40)play/3 requires ffmpeg on the PATH (used to decode the source file to PCM).
play_pcm/3 has no external dependency.
Experimental AirPlay 2 building blocks (transient pairing, ChaCha20-Poly1305
encrypted control channel, binary plist, SETUP) live under AirPlay.V2; the
control plane is verified against real HomePods but audio rendering is not yet
complete, so use the AirPlay 1 API above for playback.
Summary
Types
A running playback session (the AirPlay.Cast GenServer pid).
Functions
Browse the LAN for AirPlay/RAOP receivers for timeout_ms (default 2500).
Decode the audio file at path (via ffmpeg) and stream it to host.
Stream a raw PCM buffer (44.1 kHz, signed 16-bit little-endian, stereo,
interleaved) to host. Same options as play/3.
Set the playback volume (0–100) on a running session.
Stop a running session and tear down the RTSP connection.
Types
@type session() :: pid()
A running playback session (the AirPlay.Cast GenServer pid).
Functions
@spec discover(non_neg_integer()) :: [map()]
Browse the LAN for AirPlay/RAOP receivers for timeout_ms (default 2500).
Returns a list of %{name: String.t(), host: String.t(), port: pos_integer()}.
Decode the audio file at path (via ffmpeg) and stream it to host.
Options:
:volume— 0–100 (default 25):port— RTSP port (default 7000)
Returns {:ok, session} where session is a pid you pass to set_volume/2
and stop/1; it streams in the background and stops itself when the track ends.
Stream a raw PCM buffer (44.1 kHz, signed 16-bit little-endian, stereo,
interleaved) to host. Same options as play/3.
@spec set_volume(session(), 0..100) :: :ok
Set the playback volume (0–100) on a running session.
@spec stop(session()) :: :ok
Stop a running session and tear down the RTSP connection.