SHM-only transport prototype for GStreamer, designed to communicate exclusively through a single shared-memory region (for example shm:///dev/shm/gst-shm2-demo on Linux or winshm://Local/gst-shm2-demo on Windows) with no control socket.
Implemented:
- Shared-memory transport core with:
- fixed header
- producer->consumer ready ring
- consumer->producer recycle ring
- producer-side free-list allocator
- OS backend abstraction (
platform) with runtime backend selection fromshm-path. - Linux POSIX shared-file backend.
- Windows named shared-memory backend (
winshm://...). - Two standalone test binaries:
shm2_producershm2_consumer
- Initial GStreamer plugin wiring:
shm2sink(BaseSink)shm2src(PushSrc)
Current plugin status:
shm2src: zero-copy output path implemented (SHM-backedGstMemory+ recycle on memory drop).shm2sink: upstream zero-copy fast path implemented viapropose_allocation+ custom allocator, with copy fallback for non-cooperating upstream memory.- No consumer ownership/heartbeat gating: sink always publishes into SHM and drops/overwrites when full.
src/platform/: OS abstraction + Linux backendsrc/transport.rs: shared protocol, rings, writer/readersrc/allocator.rs: producer allocatorsrc/shm2sink.rs: sink elementsrc/shm2src.rs: source elementsrc/bin/shm2_producer.rs: transport smoke-test producersrc/bin/shm2_consumer.rs: transport smoke-test consumer
cd /home/krusl/code/gst-shm/shm2-rs
nix developcargo fmt
cargo build --bins
cargo build --lib
cargo testshm-path is backend-aware and supports URI-like schemes:
- Linux POSIX file backend:
shm:///dev/shm/gst-shm2-demo- backward-compatible plain path also works:
/dev/shm/gst-shm2-demo
- Windows named shared memory backend:
winshm://Local/gst-shm2-demo(recommended default)winshm://Global/gst-shm2-demo(may require elevated privilege/service context)
- Windows ivshmem backend (guest attach to existing shared BAR2 region):
ivshmem://PCI\VEN_1AF4&DEV_1110&SUBSYS_11001AF4&REV_01\3&11583659&0&88ivshmem://\\?\PCI#VEN_1AF4&DEV_1110&SUBSYS_11001AF4&REV_01#3&11583659&0&88#{df576976-569d-4672-95a0-f57e4ea0b210}
Both producer and consumer must use the exact same shm-path value.
For ivshmem://, the backend is attach-only (reader/open side): use when a host-side daemon already owns and feeds the shared region.
Linux (terminal 1):
cargo run --bin shm2_consumer -- shm:///dev/shm/gst-shm2-demo 2000Linux (terminal 2):
cargo run --bin shm2_producer -- shm:///dev/shm/gst-shm2-demo 2000Windows (PowerShell window 1):
cargo run --bin shm2_consumer -- winshm://Local/gst-shm2-demo 2000Windows (PowerShell window 2):
cargo run --bin shm2_producer -- winshm://Local/gst-shm2-demo 2000shm2_relayd mirrors v4l2-relayd behavior using a TCP/vsock listener instead of V4L2 client usage events.
The output pipeline is explicit and must include appsrc name=appsrc. When no clients are connected, the input pipeline is set to NULL for power efficiency.
Usage:
cargo run --bin shm2_relayd -- \
--listen tcp://0.0.0.0:5555 \
--output "appsrc name=appsrc is-live=true format=time stream-type=stream ! queue max-size-buffers=8 max-size-bytes=0 max-size-time=0 leaky=downstream ! shm2sink shm-path=shm:///dev/shm/gst-shm2-pipe shm-size=67108864" \
--input "videotestsrc is-live=true pattern=ball ! videoconvert"Optional splash (runs only when no clients):
cargo run --bin shm2_relayd -- \
--listen tcp://0.0.0.0:5555 \
--output "appsrc name=appsrc is-live=true format=time stream-type=stream ! queue max-size-buffers=8 max-size-bytes=0 max-size-time=0 leaky=downstream ! shm2sink shm-path=shm:///dev/shm/gst-shm2-pipe shm-size=67108864" \
--input "v4l2src ! videoconvert" \
--splash "videotestsrc is-live=true pattern=black ! videoconvert"Notes:
- Output pipeline is always PLAYING; input pipeline toggles on client connect/disconnect.
- Output pipeline must include
appsrc name=appsrc(similar to v4l2-relayd). - For Linux vsock:
--listen vsock://CID:PORT. - Shallow buffer copy is the default (no payload duplication).
--shm-path/--shm-sizeare deprecated in favor of explicit--output.
After build, plugin is produced as target/debug/libgstshm2.so.
GST_PLUGIN_PATH=$PWD/target/debug gst-inspect-1.0 shm2sink
GST_PLUGIN_PATH=$PWD/target/debug gst-inspect-1.0 shm2srcUse GST_PLUGIN_PATH so GStreamer can find libgstshm2.so:
export GST_PLUGIN_PATH=$PWD/target/debugTerminal 1 (producer pipeline):
gst-launch-1.0 -v \
videotestsrc is-live=true pattern=ball ! \
video/x-raw,format=I420,width=320,height=240,framerate=30/1 ! \
shm2sink shm-path=shm:///dev/shm/gst-shm2-pipeTerminal 2 (consumer pipeline):
gst-launch-1.0 -v \
shm2src shm-path=shm:///dev/shm/gst-shm2-pipe is-live=true ! \
queue ! videoconvert ! autovideosinkshm2sink:
shm-path(string)shm-size(u64)perms(u32)timeline-beacon-ms(u32)
shm2src:
shm-path(string)is-live(bool)live-only(bool): restart output timeline on attach/re-attachlatest-only(bool): low-latency read policy (second-newest when available)
Audio example:
Terminal 1:
gst-launch-1.0 -v \
audiotestsrc is-live=true wave=sine ! \
audio/x-raw,format=S16LE,channels=2,rate=48000 ! \
shm2sink shm-path=shm:///dev/shm/gst-shm2-audioTerminal 2:
gst-launch-1.0 -v \
shm2src shm-path=shm:///dev/shm/gst-shm2-audio is-live=true ! \
queue ! audioconvert ! audioresample ! autoaudiosinkNotes:
- Start producer before consumer (
shm2srcexpects SHM region to exist at start). shm2srcis zero-copy on output.shm2sinkuses zero-copy fast path when upstream adopts the proposed allocator; otherwise it falls back to copy.shm-pathmust resolve to the same shared-memory region on both sides.- Sink never blocks for a consumer; when the arena fills it drops the oldest ready frame, and if the ready ring is empty it resets the arena.
- Multiple readers are undefined behavior (no consumer ownership enforcement).
shm2srccurrently requires producer/SHM region to exist when source starts.- No full stress/fault-recovery automated CI matrix yet.
- ivshmem backend currently targets Windows guest attach/open path; sink/create path is intentionally unsupported.