AirPlay.Session (AirPlay v0.2.1)

Copy Markdown View Source

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

ports()

@type ports() :: %{
  server: integer(),
  control: integer(),
  timing: integer(),
  session: String.t()
}

Functions

establish(host, opts \\ [])

@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).

keepalive(s)

@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_volume(s, db)

Set playback volume in dB (~ -30 silent .. 0 max; -144 = mute).

teardown(s)