Snapcast (Snapcast v0.4.4)

Copy Markdown View Source

A pure-Elixir snapcast server: speak snapcast's binary protocol directly to snapclients, owning the audio clock and timestamping every chunk, so there is no external snapserver and no ffmpeg/snapserver pacing to fight.

Public entry points + the stream format. The server currently uses PCM as the compatibility transport while encoded Snap transport (opus/flac) is developed behind an explicit per-play/3 :transport_codec option.

Configuration

Settings are read from the :snapcast application environment, e.g.:

config :snapcast,
  enabled: true,
  port: 1704,
  bind_ip: {0, 0, 0, 0},
  format: {48_000, 16, 2},
  listener: MyApp.SnapcastListener

format is the default PCM output format {sample_rate, bits_per_sample, channels}. play/3 can override it with a per-stream :format.

Supervision

Add Snapcast.children() to your supervision tree; it returns the server subtree when enabled: true, or [] otherwise.

Lifecycle events

Configure a Snapcast.Listener (via :listener) to receive client connect/disconnect and playback progress/ended notifications.

Summary

Functions

Whether to advertise the server over mDNS/DNS-SD.

mDNS service name advertised for the server.

TCP address to bind the server to.

End-to-end buffer (ms): clients play each chunk this long after its timestamp.

End-to-end buffer (ms) scaled to a stream's wire data rate.

Clamp a format's sample rate to max_sample_rate/0 by repeated halving.

The server's supervision subtree, gated by enabled?/0.

Chunk duration in milliseconds.

List connected clients.

Path to the DNS-SD publisher executable, if available.

Whether the server is started in the supervision tree.

Path to the ffmpeg executable used to decode sources to PCM.

FLAC compression level used by ffmpeg for Snapcast FLAC transport.

FLAC frame size (samples per frame) for the FLAC transport.

Default PCM stream format {rate, bits, channels}.

The configured Snapcast.Listener module, if any.

Whether to supervise a local snapclient for this machine.

Stable host id for the supervised local snapclient.

snapclient log filter used by the supervised local client.

URL used by the supervised local snapclient.

Upper bound for the data-rate-scaled buffer_ms_for/1.

Maximum Snapcast sample rate; higher-rate streams are halved down to it (see cap_format/1).

Normalize a PCM format to {sample_rate, bits_per_sample, channels}.

Normalize a transport codec name to :pcm | :opus | :flac (or nil).

Default bitrate used when transcoding speech/long-form sources to Opus.

Opus frame duration in milliseconds (sets the per-packet / WireChunk cadence).

Play a source to the given client ids.

TCP port to listen on (snapclients default to 1704).

Whether ffmpeg reads its input at realtime speed (-re).

Path to the local snapclient executable, if available.

Stop the current stream.

Functions

advertise?()

Whether to advertise the server over mDNS/DNS-SD.

bind_ip()

TCP address to bind the server to.

buffer_ms()

End-to-end buffer (ms): clients play each chunk this long after its timestamp.

buffer_ms_for(arg1)

End-to-end buffer (ms) scaled to a stream's wire data rate.

Higher-bitrate streams (hi-res PCM) leave thinner network headroom, so they get a proportionally deeper buffer to ride out jitter — scaled linearly from buffer_ms/0 against a 48kHz/16-bit/stereo reference (192_000 wire bytes/s) and capped at max_buffer_ms/0. A standard CD/48k stream is unchanged at buffer_ms/0.

cap_format(format)

Clamp a format's sample rate to max_sample_rate/0 by repeated halving.

Snapcast carries PCM uncompressed (24-bit is inflated to 32-bit on the wire), so a very high sample rate can exceed what a client link sustains and cause dropouts. Halving (192k→96k, 176.4k→88.2k) is an exact 2:1 decimation rather than a fractional resample; bit depth and channels are left untouched.

children()

The server's supervision subtree, gated by enabled?/0.

Returns the SessionSupervisor + Server (plus the mDNS advertiser and a local snapclient when configured), or [] when not enabled.

chunk_ms()

Chunk duration in milliseconds.

clients()

List connected clients.

dns_sd_path()

Path to the DNS-SD publisher executable, if available.

enabled?()

Whether the server is started in the supervision tree.

ffmpeg_path()

Path to the ffmpeg executable used to decode sources to PCM.

flac_compression_level()

FLAC compression level used by ffmpeg for Snapcast FLAC transport.

flac_frame_size()

FLAC frame size (samples per frame) for the FLAC transport.

Each WireChunk carries one whole FLAC frame, so the frame size sets the chunk cadence and end-to-end latency. Keep it small (snapcast's own server uses 1152, ~26ms at 48k) so chunks stay PCM-sized — large frames make each blocking socket write hold up the session's Time-sync replies, which can trip a client's sync timeout and make it reconnect.

format()

Default PCM stream format {rate, bits, channels}.

listener()

The configured Snapcast.Listener module, if any.

local_client_enabled?()

Whether to supervise a local snapclient for this machine.

local_client_id()

Stable host id for the supervised local snapclient.

local_client_logfilter()

snapclient log filter used by the supervised local client.

local_client_url()

URL used by the supervised local snapclient.

max_buffer_ms()

Upper bound for the data-rate-scaled buffer_ms_for/1.

max_sample_rate()

Maximum Snapcast sample rate; higher-rate streams are halved down to it (see cap_format/1).

normalize_format(format)

Normalize a PCM format to {sample_rate, bits_per_sample, channels}.

normalize_transport_codec(codec)

Normalize a transport codec name to :pcm | :opus | :flac (or nil).

opus_bitrate()

Default bitrate used when transcoding speech/long-form sources to Opus.

opus_frame_ms()

Opus frame duration in milliseconds (sets the per-packet / WireChunk cadence).

Must be a valid Opus frame size — one of 2.5, 5, 10, 20, 40, 60 ms (integer ms here, so 10/20/40/60). Small frames keep WireChunks tiny and latency low; 20ms is the default.

pause()

See Snapcast.Server.pause/0.

play(source, client_ids, opts \\ [])

Play a source to the given client ids.

source is either a binary path/URL (decoded by ffmpeg) or a 0-arity function returning one, resolved lazily when the stream starts (e.g. for short-lived URLs).

opts: :position_ms, :endpoint (opaque term echoed back in listener events), :duration_ms, :transport_codec, :format.

port()

TCP port to listen on (snapclients default to 1704).

realtime_input?()

Whether ffmpeg reads its input at realtime speed (-re).

Default true: ffmpeg is paced to 1× so the stream buffer stays bounded — without it a long source floods the buffer (the buffer <> data accumulation goes O(n²) and can stall the stream process). The trade-off is that frames are produced just-in-time, so an encode/scheduling hiccup can momentarily starve the pacer (more likely at high sample rates). Set false to let ffmpeg race ahead, which keeps the pacer reliably fed at the cost of an unbounded server-side buffer — use only for short sources.

resume()

See Snapcast.Server.resume/0.

seek(position_ms, playback_gen \\ nil)

See Snapcast.Server.seek/2.

set_volume(client_id, volume)

See Snapcast.Server.set_volume/2.

snapclient_path()

Path to the local snapclient executable, if available.

stop_playback()

Stop the current stream.