- Rust 99.6%
- Shell 0.4%
The crate moved to the public slayradio-public org. Per project convention, source and docs use only keyboard-typable ASCII so terms stay searchable and typeable. Converts em/en dashes (-> --/-), arrows (-> ->), the micro sign (-> us), multiply (-> x), and approx (-> ~=) across comments and README. No behavior change. |
||
|---|---|---|
| src | ||
| .gitignore | ||
| build.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE-APACHE | ||
| LICENSE-MIT | ||
| PROTOCOL.md | ||
| README.md | ||
slay-slm
Rust implementation of the SLM (SLAY Media) protocol sender -- a low-latency protocol for media.
It is an attempt to get as close to real-time interaction between an external "VJ" application, like a soundboard or video clip player, and OBS as possible. Initial attempts were made using NDI, but that added several hundred milliseconds (at best) of latency between hitting "play" and actually getting the content through to OBS, which is why this project exists.
Handles multicast discovery, the HELLO/CAPS handshake, UDP frame delivery, and shared memory transport (POSIX on Unix, Named File Mappings on Windows). Wire format is defined in PROTOCOL.md.
What it does
- Broadcasts multicast ANNOUNCE packets (~1 Hz) so receivers can find the sender
- Accepts HELLO from receivers and replies with CAPS (stream parameters)
- Sends NV12 video frames and f32le audio frames to all registered receivers over UDP
- Writes frames into a shared memory ring buffer for same-machine receivers (no UDP overhead)
- Tracks receiver liveness and expires stale entries automatically
What it does not do
Frame production is the caller's responsibility. This crate accepts raw NV12 bytes and f32le PCM samples; it does not decode video or audio.
Planned protocol extensions
Two new packet types are planned for a receiver->sender feedback channel (#21):
| Value | Name | Direction | Purpose |
|---|---|---|---|
| 0x30 | PKT_TALLY |
receiver -> sender | 1-byte tally state: 0 = off, 1 = preview, 2 = program (on-air) |
| 0x31 | PKT_MSG |
receiver -> sender | UTF-8 freetext message (max 255 bytes) from operator to remote sender |
Both are sent by OBS as UDP unicast to the sender's ctrl_port (already carried in every ANNOUNCE packet), so no new socket is needed on either side. PKT_TALLY is re-sent on every HELLO so a reconnecting sender gets the current state immediately.
This feedback channel also provides the foundation for the dynamic bitrate adaptation design in #10.
Usage
[dependencies]
slay-slm = { git = "https://git.c64.org/slayradio/slay-slm.git", branch = "main" }
use slay_slm::{SlmConfig, SlmSender};
let sender = SlmSender::new(SlmConfig {
name: "My Source".into(),
width: 1920,
height: 1080,
fps_num: 30,
fps_den: 1,
compress: true, // LZ4 on the UDP video path
audio_only: false,
})?;
// In your frame loop (synchronous; use block_in_place from async):
sender.send_video(timestamp_ns, &nv12_bytes);
sender.send_audio(timestamp_ns, &f32le_samples);
// Between clips:
sender.clear_disconnect(); // before starting
sender.send_disconnect(); // after ending, so the receiver flushes its frame queue
sender.update_heartbeat(); // call from idle loops to stay alive
// On shutdown:
sender.send_bye();
SlmSender is Clone and backed by an Arc, so you can share a handle between your feed loop and a status-polling thread without wrapping it yourself.
All send methods are synchronous. When calling from an async tokio task, wrap them in tokio::task::block_in_place.
Platform notes
| Feature | Linux / macOS | Windows |
|---|---|---|
| UDP transport | yes | yes |
| SHM transport | yes (POSIX shm_open) |
yes (Win32 Named File Mapping) |
The shared memory layout is byte-for-byte identical on both platforms; only the OS primitives differ. On Unix the region is a POSIX named SHM object (/slm-<name>); on Windows it is a page-file-backed Named File Mapping (Local\slm-<name>). Both use named semaphores for wake-up signalling.
The Unix path is gated behind #[cfg(unix)] (requires librt on Linux, linked automatically via libc). The Windows path is gated behind #[cfg(windows)] (requires windows-sys). On any other platform the transport is a no-op stub and all frames go over UDP only.
Protocol
See PROTOCOL.md for the full wire format specification, packet layouts, session lifecycle, and SHM region layout. The C header files in the obs-slm-source repo (slm_protocol.h, slm_shm.h) are the normative cross-language reference.