arcly-stream 0.1.6

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
<div align="center">

# ๐ŸŽฌ 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](https://img.shields.io/crates/v/arcly-stream.svg)](https://crates.io/crates/arcly-stream)
[![docs.rs](https://img.shields.io/docsrs/arcly-stream)](https://docs.rs/arcly-stream)
[![license](https://img.shields.io/crates/l/arcly-stream.svg)](#-license)
[![MSRV](https://img.shields.io/badge/MSRV-1.85-blue.svg)](#-minimum-supported-rust-version)
[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](#-guarantees)

</div>

---

## ๐Ÿ‘€ 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.

```rust
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 `Engine`s 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 + H.265) |
| **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; egress packetizes H.264/H.265, over a pluggable `DtlsSrtpTransport` |
| **RTP payload formats** | โœ… `rtsp`/`webrtc` | `protocol::rtp` โ€” H.264 (RFC 6184), H.265 (RFC 7798), VP9 (draft-ietf-payload-vp9), AV1 (AOMedia) packetizers + depacketizers |
| **MPEG-TS muxing (playable)** | โœ… `mpegts` | `packager::MpegTsMuxer` โ€” H.264 + H.265 (`+codec-h265`) video, AAC audio |
| **Fragmented-MP4 / CMAF muxing** | โœ… `fmp4` | `packager::Fmp4Muxer` โ€” H.264 `avc1` + H.265 `hvc1` (`+codec-h265`) + 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**:

```text
                       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                       โ”‚                  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

```text
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:

```bash
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:

```rust,ignore
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.

```rust,ignore
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`](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` (H.264/H.265 + AAC) |
| `fmp4` | Native fragmented-MP4/CMAF muxer (`Fmp4Muxer`) โ€” LL-HLS init + fragments (H.264/H.265/AV1/VVC) |
| `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

```bash
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](https://docs.rs/arcly-stream). See
[`BLUEPRINT.md`](BLUEPRINT.md) for the extraction analysis and the mapping from
the original 25-crate `stream-center` to this single crate.

## ๐Ÿ“„ License

MIT.