OBS source plugin for receiving SLM (SLAY Media) streams with sub-frame latency. https://slay.tools/
  • C 63.1%
  • CMake 28.5%
  • Shell 8.4%
Find a file
Slaygon 5c587a3ebc build: use obs_properties_add_button2 for OBS 32 compatibility
OBS 32 marked obs_properties_add_button OBS_DEPRECATED, and the plugin
builds with -Wdeprecated-declarations -Werror, so upgrading the system
obs-studio package (now 32.2.0 here) breaks the Linux build outright. The
headers come from the distro package, not from anything vendored, so this
appeared without any change on our side.

obs_properties_add_button2 has the same signature plus a void *priv and
exists un-deprecated as far back as the OBS 31.1.1 that buildspec.json
pins for the Windows and macOS dependency sets, so this is safe across all
three targets.

Passing src as priv rather than NULL: obs_property_button_clicked uses
priv when set and otherwise falls back to context->data. Both callbacks
want the slm_source_t, which is what context->data already was, so an
explicit priv is behaviour-identical where src is non-NULL and falls back
exactly as before in the get_properties(NULL) path -- and it does not
depend on that fallback existing in a future OBS.

Ref #35
2026-07-27 15:14:48 +02:00
build-aux Initial commit 2026-05-14 16:06:22 +02:00
cmake fix(cmake): seed build number on non-CI fresh clone 2026-07-06 00:57:15 +02:00
data/locale Changed default names 2026-05-20 12:26:29 +02:00
src build: use obs_properties_add_button2 for OBS 32 compatibility 2026-07-27 15:14:48 +02:00
vendor fix(macos): rebuild vendored FFmpeg libs at deployment target 12.0 2026-07-03 22:03:05 +02:00
.clang-format Initial commit 2026-05-14 16:06:22 +02:00
.gersemirc Initial commit 2026-05-14 16:06:22 +02:00
.gitignore Cleaning up gitignore 2026-06-27 17:22:11 +02:00
build-plugin.sh Rename plugin to SLM Wire for OBS Studio 2026-06-24 22:32:48 +02:00
build.sh Build .pkg instead of .zip for macOS 2026-07-06 09:50:56 +02:00
buildspec.json fix(discover): join the multicast group on every interface, not one 2026-07-27 15:03:19 +02:00
CMakeLists.txt fix(macos): use vendored static lz4, not Homebrew dylib 2026-07-06 00:57:15 +02:00
CMakePresets.json build: enable native macOS (Apple Silicon) plugin build 2026-07-03 19:48:34 +02:00
LICENSE Initial commit 2026-05-14 16:06:22 +02:00
publish.sh Build .pkg instead of .zip for macOS 2026-07-06 09:50:56 +02:00
README.md Rename plugin to SLM Wire for OBS Studio 2026-06-24 22:32:48 +02:00

SLM Wire for OBS Studio

An OBS Studio plugin that receives live video and audio from a SLAY Media (SLM) sender and injects them into OBS as low-latency input sources.

What it does

The plugin registers two OBS source types:

  • SLM Source (slay_media_source) - receives video and audio
  • SLM Audio Source (slay_media_audio_source) - audio only

Senders are discovered automatically via multicast (239.255.77.43:9878). Once a sender is found, the plugin connects and begins receiving frames. If the sender is on the same machine, it uses a shared memory ring buffer for zero-copy, sub-millisecond delivery. UDP is used otherwise.

Known senders

Sender Transport Video codec
slay-cart - SLAY Media player SHM (local) / UDP Raw NV12, H.264, H.265
slay-camera - Android camera app UDP H.265 (H.264 fallback)

Protocol

SLM is a custom binary UDP protocol (magic SLM\x01, version 4). The handshake sequence:

  1. Sender broadcasts ANNOUNCE on multicast
  2. Plugin replies with HELLO (unicast to sender's ctrl_port)
  3. Sender sends CAPS (resolution, FPS, pixel format, codec, audio params)
  4. Sender streams video chunks and audio frames

Video frames are split into 1400-byte UDP chunks and reassembled. Audio fits in a single datagram. Optional LZ4 compression is supported for raw NV12 video.

Supported video codecs:

Codec Notes
Raw NV12 Original SLM mode; optional LZ4 per-frame compression
H.264 (Annex B) Software decoded via libavcodec
H.265 / HEVC (Annex B) Software decoded via libavcodec

H.264/H.265 frames arrive as complete Annex B access units, one per logical video frame (reassembled from chunks). The decoder is initialised on CAPS and replaced if the codec changes mid-stream. IDR frames carry inline SPS/PPS so the decoder can recover after packet loss.

Supported audio format: float32 PCM, mono or stereo, 48 kHz

Transport

Transport Platforms Notes
Shared Memory Linux, Windows Zero-copy; used when sender and receiver are on the same machine
UDP All Used for remote senders; always the fallback if SHM becomes stale

The plugin tries SHM first and monitors the sender's heartbeat. If the heartbeat goes stale (no update for 5 seconds), it falls back to UDP automatically.

On Linux, SHM uses POSIX shared memory (shm_open) and named semaphores. On Windows, it uses Named File Mappings (page-file-backed CreateFileMapping) and Win32 named semaphores - the shared data layout is identical on both platforms.

Source settings

Setting Default Description
Sender name (empty) Filter by sender name. Empty accepts any sender. Dropdown auto-populates from discovery.
Port 0 (auto) UDP port to listen on. 0 lets the OS pick a free port.
Force UDP off Skip SHM and use UDP only. Enable this for remote senders on another machine.

Building

Windows

Requires Visual Studio 2022 and CMake 3.30+.

cmake --preset windows-x64
cmake --build --preset windows-x64

Install target places the plugin at %ALLUSERSPROFILE%\obs-studio\plugins\slm-wire-for-obs\bin\64bit\slm-wire-for-obs.dll.

Linux (Ubuntu 24.04)

Requires CMake 3.28+, ninja-build, pkg-config, and build-essential.

cmake --preset ubuntu-x86_64
cmake --build --preset ubuntu-x86_64

Platform notes

  • Windows uses Winsock2; Linux/macOS use POSIX sockets.
  • SHM works on both Linux and Windows. The Force UDP setting disables SHM on all platforms.
  • Multicast requires sender and receiver to be on the same subnet (or multicast routing to be configured for remote senders).

Planned

  • Tally signal (#21): OBS detects when a source enters or leaves the Program/Preview slots and unicasts PKT_TALLY back to the sender so remote operators know they are live.
  • Operator messaging (#21): A text field in the source properties lets the studio operator send a short message to the connected sender (e.g. "zoom in"), displayed as an overlay on the remote device.
  • Dynamic bitrate feedback (#10): Loss-rate reporting from receiver to sender to allow encoder step-down on degraded links.