arcly-stream 0.1.5

An open-extensible live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, a pluggable multi-protocol ingestion layer (RTMP, RTSP, SRT, WHIP/WHEP shipped), and a feature-gated pure-Rust media plane (MPEG-TS/HLS/fMP4) โ€” runtime, config, and metrics free.
Documentation

๐ŸŽฌ arcly-stream

A high-performance, open-extensible live-media streaming kernel for Rust

Lock-free zero-copy fan-out ยท instant-start GOP replay ยท a pluggable multi-protocol ingestion layer ยท a feature-gated pure-Rust media plane โ€” with zero baked-in config, metrics singleton, or HTTP runtime.

crates.io docs.rs license MSRV unsafe forbidden


๐Ÿ‘€ At a Glance

Every streaming server re-solves the same hard core: fan one publisher's frames out to thousands of late-joining subscribers โ€” without copying payloads, without locks on the hot path, and without silently dropping the slow ones. Everything else โ€” which wire protocol you ingest, how you package egress, where you store, how you authorize and observe โ€” is deployment-specific.

arcly-stream is exactly that core, extracted and hardened into an embeddable kernel. You implement a few small traits for the parts unique to your system; the kernel owns the lock-free pub/sub bus, instant-start replay, back-pressure, lifecycle, and QoS. It ships no opinion about your runtime: no global registry, no TOML schema, no HTTP server.

use arcly_stream::prelude::*;

let engine = Engine::builder()
    .max_publishers(10_000)
    .application(AppSpec::new("live").gop_cache(120)) // instant-start replay
    .build();
assert_eq!(engine.list_apps().len(), 1);

๐Ÿ”„ Coming from a standalone server (xiu, MediaMTX, Ant Media)?

Those are daemons: you run a binary, point a TOML/YAML file at it, and extend it by forking. arcly-stream is the opposite shape โ€” a library you embed in your Rust service, so your application owns the process, the runtime, and the control plane. The mapping is direct:

Standalone server arcly-stream embedding
Edit config.toml, restart Call EngineBuilder methods at startup โ€” config is your code
Built-in HTTP API Wire the engine into your Axum/Hyper handlers; the kernel imposes no framework
Fork to add auth / storage / metrics Implement StreamAuthenticator / StorageBackend / Observer traits
Add a protocol = patch the server Implement InboundProtocol, register with .protocol(...)
One global process Instantiate as many Engines as you like โ€” it's Send + Sync, no singletons

If you want a turnkey server, run a daemon. If you're building a product that happens to stream โ€” and want the lock-free core without inheriting someone's framework โ€” embed this.

๐Ÿ“ฆ Capability Matrix

Capability Status Reach it via
Zero-copy broadcast fan-out โœ… Core StreamHandle::subscribe_resilient
Instant-start GOP replay โœ… Core AppSpec::gop_cache(n) + replay_buffer()
Publish/play authorization โœ… Core StreamAuthenticator (permit-all default)
Live QoS (bitrate / fps) โœ… Core StreamHandle::qos()
Slow-subscriber eviction โœ… Core Subscription::max_lag
Idle-stream reaper โœ… Core EngineBuilder::idle_timeout
Coordinated graceful shutdown โœ… Core Engine::serve_until_signal
Pluggable protocol ingestion โœ… Core InboundProtocol trait
RTMP publish / play โœ… rtmp protocol::rtmp::RtmpHandler
RTSP ingest (IP cameras) โœ… rtsp protocol::rtsp::RtspHandler โ€” client-pull, OPTIONSโ†’PLAY, SDP, TCP-interleaved RTP (H.264)
SRT ingest (broadcast feeds) โœ… srt protocol::srt::SrtHandler โ€” listener handshake + MPEG-TS demux (unencrypted)
WebRTC WHIP ingest / WHEP egress โœ… webrtc protocol::webrtc::{WhipEndpoint, WhepEndpoint} โ€” SDP signaling + RTP/RTCP routing & packetization over a pluggable DtlsSrtpTransport
MPEG-TS muxing (playable) โœ… mpegts packager::MpegTsMuxer
Fragmented-MP4 / CMAF muxing โœ… fmp4 packager::Fmp4Muxer โ€” H.264 avc1 + AV1 av01 (+codec-av1) + VVC vvc1 (+codec-vvc) init, codec-generic fragments (LL-HLS)
Production token auth โœ… auth-token auth::TokenAuthenticator โ€” HMAC-SHA-256, dependency-free
Audit trail / structured telemetry โœ… Core FileAuditSink, StandardTelemetry
H.264/265 ยท AV1 ยท VP9 ยท VVC ยท AAC parsing ๐Ÿงฉ codec* codec::{h264,h265,av1,vp9,vvc,aac}
HLS / LL-HLS packaging ๐Ÿงฉ hls HlsSegmenter + HlsPlaylist
Recording (VOD / DVR) ๐Ÿงฉ record RecordingSink
Filesystem storage ๐Ÿงฉ storage-fs FsStorage
Prometheus metrics ๐Ÿงฉ metrics PrometheusObserver
Hardware transcode / ABR ๐Ÿงญ Seam implement Transcoder
Edge federation ๐Ÿงญ Seam implement ClusterRelay

โœ… Core/feature = shipped, native-Rust, unsafe-free. ๐Ÿงญ Seam = a documented public trait you implement in your own crate; the kernel never drags in a native transport or codec you didn't ask for.

WebRTC scope note: the webrtc feature ships the WHIP/WHEP signaling (SDP offer/answer), RTP/RTCP routing, and PLI/FIR feedback natively. The DTLS/SRTP/ICE crypto transport is injected by the host through the DtlsSrtpTransport trait (back it with str0m/webrtc-rs) โ€” so the kernel stays crypto-free and #![forbid(unsafe_code)]. SRT ships unencrypted (handshake + MPEG-TS demux); AES-encrypted SRT is out of scope.

๐Ÿ› Architecture

Three tiers โ€” an always-on Open Kernel, an Injected Multi-Protocol Layer, and a Feature-Gated Media Plane:

                       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                       โ”‚                  OPEN KERNEL                     โ”‚
                       โ”‚                 (always compiled)                โ”‚
   publisher โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚  Engine ยท StreamHandle                          โ”‚ โ”€โ”€โ”€โ”€โ”€โ–ถ subscribers
 (any protocol)        โ”‚   โ€ข broadcast fan-out (zero-copy Arc<Frame>)    โ”‚   (packager / SFU /
                       โ”‚   โ€ข GOP cache โ†’ instant-start replay            โ”‚    recorder / edge)
                       โ”‚   โ€ข live QoS (bitrate / fps)                    โ”‚
                       โ”‚  PublishRegistry ยท PlaybackRegistry ยท EventBus  โ”‚
                       โ”‚  Observer ยท StreamAuthenticator ยท AuditSink     โ”‚
                       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ–ฒ implements                       โ–ฒ feature-gated, pure Rust
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚   INJECTED MULTI-PROTOCOL LAYER      โ”‚  โ”‚           MEDIA PLANE                 โ”‚
        โ”‚        (trait: InboundProtocol)      โ”‚  โ”‚          (opt-in features)            โ”‚
        โ”‚  RtmpHandler   reference impl  โœ…    โ”‚  โ”‚  MpegTsMuxer   playable .ts     โœ…    โ”‚
        โ”‚  IngestContext ยท PublishSession      โ”‚  โ”‚  codec  H.264/265 ยท AV1/VP9/VVC ยท AAC โ”‚
        โ”‚  โ”€โ”€ your crate โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€    โ”‚  โ”‚  hls    Packager + segmenter          โ”‚
        โ”‚  MyRtspHandler ยท MyWhipHandler โ€ฆ     โ”‚  โ”‚  record RecordingSink (VOD/DVR)       โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

A new wire protocol is one InboundProtocol impl in your own crate, registered on the builder โ€” no kernel fork. RtmpHandler is just the first reference implementation of that public trait.

Workspace layout

arcly-stream/             the published library crate (this README documents it)
arcly-stream-macros/      optional proc-macros (the `macros` feature)
examples/
  minimal_ingest/         MediaSource + Observer, drive the bus
  custom_protocol/        legacy ProtocolHandler + Engine::serve
  custom_protocol_plugin/ custom InboundProtocol registered alongside RTMP
  enterprise_gateway/     RTMP + token auth + FS recording + metrics + audit
  multi_protocol_gateway/ RTMP + RTSP pull + SRT + WHIP under one engine
  codec_inspect/          per-codec random-access + HLS codec strings
  rtmp_to_hls/            real RTMP ingest โ†’ MPEG-TS HLS on disk

Design invariants

  • Zero-copy โ€” one publish clones an Arc<MediaFrame> pointer per subscriber, never the payload (bytes::Bytes).
  • Lock-free hot path โ€” tokio::broadcast fan-out; ArcSwap + atomics for cached config, GOP head, and QoS.
  • No global state โ€” telemetry, auth, and audit are injected traits with safe defaults; instantiate as many engines per process as you like.
  • Contracts are traits โ€” protocols depend on PublishRegistry, not the concrete Engine, so a host can swap the bus.
  • #![forbid(unsafe_code)] + #![deny(missing_docs)] โ€” no unsafe, and every public item documented; both compiler-enforced.

๐Ÿš€ Quick Start

A real RTMP-in โ†’ HLS-out origin

The rtmp handler ingests from OBS or FFmpeg; the mpegts muxer writes playable HLS. Run the bundled, fully-wired version:

cargo run -p rtmp-to-hls
# ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac -f flv rtmp://localhost:1935/live/cam
# โ†’ playable HLS under ./hls/live/cam/index.m3u8

The origin itself is a few lines โ€” register the handler on the builder and serve:

use arcly_stream::prelude::*;
use arcly_stream::protocol::rtmp::RtmpHandler;

#[tokio::main]
async fn main() -> arcly_stream::Result<()> {
    let engine = Engine::builder()
        .application(AppSpec::new("live").gop_cache(120)) // instant-start
        .protocol(RtmpHandler::new("0.0.0.0:1935".parse().unwrap()))
        .build();

    // Publish to rtmp://host/live/<stream>; point an HlsSegmenter + MpegTsMuxer
    // at the same stream for HLS egress.
    engine.serve_registered_until_signal().await
}

Plug in a custom third-party protocol

Teach the engine a brand-new wire protocol from your own crate โ€” implement the public InboundProtocol trait and register it on the builder alongside the native RTMP handler. Both run concurrently under one coordinated shutdown.

use arcly_stream::prelude::*;
use arcly_stream::inbound::{InboundProtocol, IngestContext};
use arcly_stream::protocol::rtmp::RtmpHandler;

struct MyProtocol { /* listener config, handshake state โ€ฆ */ }

#[async_trait]
impl InboundProtocol for MyProtocol {
    fn name(&self) -> &'static str { "my-proto" }

    async fn serve(&self, ctx: IngestContext, shutdown: CancellationToken)
        -> arcly_stream::Result<()>
    {
        // 1. bind your listener ยท 2. accept + handshake ยท 3. resolve a StreamKey
        let session = ctx.open_publish(StreamKey::new("live", "cam")).await?;
        // 4. bridge decoded access units โ†’ MediaFrame and publish:
        //    session.publish_frame(frame)?;   // โ†’ fan-out ยท GOP cache ยท QoS
        shutdown.cancelled().await;            // 5. wind down on shutdown
        session.finish().await                 //    (Drop releases it otherwise)
    }
}

let engine = Engine::builder()
    .application(AppSpec::new("live").gop_cache(120))
    .protocol(RtmpHandler::new("0.0.0.0:1935".parse().unwrap())) // native
    .protocol(MyProtocol { /* โ€ฆ */ })                            // yours
    .build();

A full runnable version lives in examples/custom_protocol_plugin.

Runtime contract: workers are Send + Sync + 'static, must observe shutdown and return promptly, and run on a Tokio runtime. Returning Err trips the engine's coordinated teardown, winding sibling workers down too. Existing ProtocolHandler impls keep working โ€” a blanket bridge makes every one an InboundProtocol automatically.

๐Ÿงฉ Extension points (the traits you implement)

Trait Purpose
InboundProtocol Teach the engine a new wire protocol (RTMP, RTSP, SRT, WHIP all shipped โ€” and your own is one impl away)
DtlsSrtpTransport Plug a WebRTC crypto backend (DTLS/SRTP/ICE) behind the native WHIP/WHEP signaling
MediaSource / MediaSink Produce / consume frames (driven by Engine::pump_source)
StorageBackend Object storage for segments & recordings
Muxer Container byte-format for HLS segments (MPEG-TS shipped; fMP4/CMAF DIY)
Transcoder Decode/scale/encode into an ABR ladder
StreamAuthenticator Authorize publish / play (permit-all by default)
Observer / AuditSink / HealthCheck Telemetry, audit trail, readiness
ClusterRelay Origin/edge discovery & stream federation
PublishRegistry / PlaybackRegistry / EventBus Swap the engine for your own bus

โš™๏ธ Cargo features

Feature Effect
(none) Pure kernel: frame model, bus, auth/audit/health/cluster contracts
codec H.264 NAL/SPS + AAC ADTS parsers
codec-h265 / -av1 / -vp9 / -vvc / codecs-all Next-gen codec parsers
ingest Shared TCP accept loop, keyframe gate, rate limiter, NAL scan
rtmp Working RTMP publish/play (RtmpHandler)
rtsp Native RTSP client-pull ingest (RtspHandler) โ€” IP cameras over TCP-interleaved RTP
srt Native SRT listener ingest (SrtHandler) โ€” handshake + MPEG-TS demux (unencrypted)
webrtc WHIP ingest + WHEP egress signaling, RTP/RTCP routing & packetization (WhipEndpoint/WhepEndpoint) over a pluggable DtlsSrtpTransport
mpegts Native MPEG-TS muxer (MpegTsMuxer) โ€” playable .ts
fmp4 Native fragmented-MP4/CMAF muxer (Fmp4Muxer) โ€” LL-HLS init + fragments
hls HLS/LL-HLS packager + segmenter + playlists
record Segment/recording sink over a StorageBackend
storage-fs Filesystem StorageBackend
metrics Prometheus Observer
auth-token Production HMAC-signed token StreamAuthenticator (dependency-free)
macros #[derive(MediaSink)], #[protocol]
full Everything

๐Ÿงฐ Examples

cargo run -p minimal-ingest          # synthetic publisher + resilient subscriber
cargo run -p custom-protocol         # ProtocolHandler driven by Engine::serve
cargo run -p custom-protocol-plugin  # custom InboundProtocol registered with RTMP
cargo run -p enterprise-gateway      # RTMP + token auth + FS recording + metrics + audit
cargo run -p multi-protocol-gateway  # RTMP + RTSP pull + SRT + WHIP under one engine
cargo run -p codec-inspect           # codec::dispatch over H.264/265/AV1/VP9/VVC
cargo run -p rtmp-to-hls             # real RTMP origin โ†’ playable HLS
cargo bench  -p arcly-stream         # broadcast fan-out throughput (criterion)

๐Ÿ›ก Guarantees

  • #![forbid(unsafe_code)] โ€” no unsafe in the kernel, compiler-enforced.
  • #![deny(missing_docs)] โ€” every public item is documented.
  • Zero-copy payloads (bytes::Bytes), lock-free fan-out.
  • No singletons โ€” many engines per process; ideal for tests.

๐Ÿ“ Minimum Supported Rust Version

arcly-stream supports Rust 1.85 and later. An MSRV bump is a minor-version change.

๐Ÿ“š Documentation

Full API docs are on docs.rs/arcly-stream. See BLUEPRINT.md for the extraction analysis and the mapping from the original 25-crate stream-center to this single crate.

๐Ÿ“„ License

MIT.