AirPlay 2 gPTP BMCA-yield worker.
This follows the audible airplay2-rs/HomePod flow:
- bind PTP event/general sockets on 319/320
- send Mac-style Sync + Announce + Signaling with priority 250
- yield to the receiver's better clock
- act as a PTP slave and derive the receiver-clock offset
HomePods never answer our Delay_Req, so the classic two-way exchange cannot
complete. Instead the offset is taken one-way from each Sync/Follow_Up
pair (t1 - t2), which is what lets receiver_time_ns/2 track the receiver's
own PTP clock — a HomePod's clock is its uptime, nowhere near the host's
wall clock, so an un-converged (zero) offset stamps audio decades into the
receiver's future and it plays nothing.
Summary
Functions
Block until the one-way offset has converged, or timeout_ms elapses.
Return the last measured clock offset.
Convert local wall-clock time to the receiver's PTP timeline.
Start the BMCA-yield worker for a receiver host.
Stop a BMCA worker.
Types
Functions
@spec await_sync(t(), non_neg_integer()) :: :ok
Block until the one-way offset has converged, or timeout_ms elapses.
Streaming before the offset converges stamps audio in the host's wall clock
rather than the receiver's clock, which the receiver buffers far in its future
and never plays. Always returns :ok (callers proceed either way).
Return the last measured clock offset.
@spec receiver_time_ns(t(), integer()) :: non_neg_integer()
Convert local wall-clock time to the receiver's PTP timeline.
@spec start_link( String.t() | :inet.ip_address(), keyword() ) :: {:ok, t()} | {:error, term()}
Start the BMCA-yield worker for a receiver host.
@spec stop(t()) :: :ok
Stop a BMCA worker.