Drives the AirPlay/RAOP control handshake against a receiver.
establish/2 runs OPTIONS → ANNOUNCE(SDP) → SETUP → RECORD → SET_PARAMETER(volume)
exactly as an AirPlay sender does, returning the negotiated
server/control/timing ports needed to start the RTP audio stream. Targets classic
RAOP with unencrypted ALAC (no rsaaeskey/aesiv), which every receiver supports.
Summary
Functions
Connect + handshake. opts[:volume] is dB (default -20). Returns
{:ok, rtsp_state, ports} with the receiver's RTP ports, or {:error, reason}.
The RTSP connection stays open (keepalive/teardown via the returned state).
Send a no-op OPTIONS * to keep the RTSP control connection alive.
Set playback volume in dB (~ -30 silent .. 0 max; -144 = mute).
Types
Functions
@spec establish( String.t(), keyword() ) :: {:ok, AirPlay.Rtsp.t(), ports()} | {:error, term()}
Connect + handshake. opts[:volume] is dB (default -20). Returns
{:ok, rtsp_state, ports} with the receiver's RTP ports, or {:error, reason}.
The RTSP connection stays open (keepalive/teardown via the returned state).
@spec keepalive(AirPlay.Rtsp.t()) :: {:ok, AirPlay.Rtsp.t()} | {:error, term()}
Send a no-op OPTIONS * to keep the RTSP control connection alive.
RAOP receivers tear down the session (fading the audio out) if the RTSP
channel sits idle past their timeout — ~30s on AirPort Express / HomePods —
even while RTP audio keeps flowing. Real senders poll OPTIONS every couple
of seconds; AirPlay.Cast drives this on a timer. Returns the updated state
so the caller can thread the bumped CSeq.
Set playback volume in dB (~ -30 silent .. 0 max; -144 = mute).