Low-latency WebRTC
WebRTC player on LiveKit with sub-second latency for "watch live now" and video calls. Ephemeral per-room subscribe tokens.
A management layer over a LiveKit SFU: multi-protocol ingest, sub-second WebRTC, live HLS, recording to your own S3, a REST API and a drop-in SDK. Here is the full technical picture — the head-to-head with Ant Media, every feature, the API and the SDK.
Follow the signal end to end: a device publishes once, the origin runs it through your app and its plugins, replicates it to edge nodes and fans it out to every viewer in sub-second WebRTC or HLS.
StreamHub topology: an ingest device (camera, phone or encoder) publishes a single stream over RTMP, WHIP or SRT to the origin node running StreamHub Core (LiveKit SFU, REST API, multi-tenant apps). Inside the origin the stream flows through a tenant app whose plugins — yolo, deface, scheduled-live, live-chat — process and augment it. The origin then replicates the stream to the edge nodes of the cluster, and each edge fans it out to many viewers at once over sub-second WebRTC and HLS.
One stream in — RTMP, WHIP or SRT from any camera, phone or encoder.
The tenant app and its plugins augment the feed: detection, privacy masking, scheduling, chat.
The origin replicates to edge nodes; every viewer gets sub-second WebRTC or live HLS.
StreamHub covers the feature set you would license Ant Media Server Enterprise or Wowza for in real-world streaming — measured, not promised — and adds the pieces we always missed: signed webhooks, per-app tenancy and a plugin marketplace. AGPL-licensed, free while in beta, and your media never leaves your infra.
WebRTC latency — p50 measured over 601 samples in production
license cost — open source (AGPL). Ant Media EE: ~$109/mo per instance; Wowza: per-instance license or usage-billed SaaS
to install — curl | sudo bash on Ubuntu or Debian
StreamHub · Open source (AGPL). No per-instance license — free while in beta. You only pay if we host it for you.
Ant Media EE · Proprietary. ~$109/mo (~$1,068/yr) per instance.
Wowza · Proprietary. Streaming Engine: per-instance subscription license. Wowza Video: usage-billed SaaS.
StreamHub · Yes — your servers, your data. One-line installer on Ubuntu/Debian, TLS included.
Ant Media EE · Yes — self-hosted or their managed cloud.
Wowza · Streaming Engine: yes (Linux/Windows). Wowza Video: cloud-only SaaS.
StreamHub · 0.19s p50, measured end-to-end over 601 samples in production.
Ant Media EE · ~0.5s per its marketing. Both are genuinely sub-second.
Wowza · WebRTC supported; historically strongest at RTMP/HLS workflows. Sub-second real-time tier in Wowza Video.
StreamHub · RTMP/RTMPS, WHIP, RTSP and SRT in; WebRTC + live HLS out.
Ant Media EE · RTMP, WebRTC, SRT in; HLS/DASH out.
Wowza · Very broad protocol support: RTMP, SRT, RTSP in; HLS/DASH out. A strength.
StreamHub · New: an H.264/AAC RTMP push remuxed straight to HLS (ffmpeg -c copy), bypassing the SFU — no encode, near-zero CPU, no GPU. HLS-only (no WebRTC / ABR / recording), but it now matches the cheap RTMP→HLS case a LiveKit server couldn't do before.
Ant Media EE · A core strength — credit where due: Ant Media natively repackages an RTMP source to HLS/DASH (and WebRTC) without re-encoding. Cheap no-transcode ingest is its whole pitch.
Wowza · Also strong: Streaming Engine transmuxes / repackages RTMP to HLS/DASH without re-encoding — a long-standing Wowza capability.
StreamHub · New: SRT push on the passthrough plane — UDP contribution with retransmission and a ~120 ms default latency budget, remuxed straight to HLS at near-zero CPU; the key travels as the streamid. Honest scope: an SRT push is HLS-only (no WebRTC/ABR from it); a remote srt:// source can still be pulled into the transcode path via URL ingress.
Ant Media EE · Native SRT ingest feeding its full pipeline — WebRTC, HLS/DASH and ABR out of an SRT source. A genuine strength.
Wowza · Native SRT in Streaming Engine — both ingest and SRT stream targets (output); one of the longest-standing SRT implementations in the business.
StreamHub · Audio-only ingest drops the video track → only the trivial AAC→Opus transcode, ~1-2% CPU per channel with no GPU. Radio/podcast without a transcode farm; WebRTC + HLS + room features all stay.
Ant Media EE · Audio-only streaming supported; low-footprint audio channels are well within its wheelhouse.
Wowza · Audio-only streaming supported; efficient audio-only workflows are standard in Streaming Engine.
StreamHub · Every event is POSTed with an HMAC-SHA256 signature you verify in your backend.
Ant Media EE · Webhooks without a signature — you trust whoever calls.
Wowza · Wowza Video offers webhooks; Streaming Engine needs custom Java modules.
StreamHub · MP4 to your own bucket per app + periodic snapshots, indexed VODs with pagination and presigned download.
Ant Media EE · Records MP4/WebM to S3 as well — parity here.
Wowza · Recording and nDVR supported; S3 delivery via modules/integrations.
StreamHub · Each app is a real tenant: isolated rooms, S3, database, tokens, quotas, teams and roles.
Ant Media EE · Applications scope settings and streams.
Wowza · Applications per Streaming Engine instance; tenancy is on you.
StreamHub · @streamhub/adaptor shims Ant Media's WebRTCAdaptor: same callbacks and methods, no rewrite.
Ant Media EE · Its own WebRTCAdaptor SDK.
Wowza · Its own SDKs and player (Flowplayer); no Ant-compatible shim.
StreamHub · Ten full apps with their own database, a full-page dashboard view and one-click export to run standalone: LPR, IN/OUT people counting, perimeter intrusion, workstation presence, PPE monitoring (experimental), YOLO detection, face blur, VOD transcription, CCTV cockpit and agent fleet.
Ant Media EE · A Java plugin SDK plus a TensorFlow object-detection sample — no marketplace of ready-to-run apps; turnkey verticals are yours to build.
Wowza · A Java module SDK + a collection of maintained modules — no app marketplace; you assemble the verticals yourself.
StreamHub · Lightweight, in-process extensions — players, overlays and panels: Vidstack player, timestamp, watermark, quality / stream-health, radio, broadcast studio and scheduled live. Enable them per app.
Ant Media EE · Java plugin SDK — you build your own; the player and overlays are not pluggable server-side slots.
Wowza · Java module SDK + maintained modules; the player is a separate product (Flowplayer).
StreamHub · Yes — any worker-backed app exports as a self-contained bundle (worker, Dockerfile, compose, env template) that runs on your own infra and talks back over the REST API and HMAC-signed webhooks. API-first, not nested.
Ant Media EE · Plugins run in-process on the media server (Java SDK); there is no export-to-standalone concept.
Wowza · Modules run inside Streaming Engine; no marketplace and no app export.
StreamHub · Built-in vision apps on open models — license plates (fast-alpr, MIT), IN/OUT people counting (YOLOv8 + ByteTrack), perimeter intrusion with on-disk evidence, workstation presence, PPE monitoring (experimental, advisory). Plus Vision Edge: a distributed cluster of GPU/CPU edge nodes that dial out to the master (NAT-friendly) for realtime occupancy, counting, crowd and demographics. Framework-managed workers, HMAC-signed events + MQTT, CPU-viable, GPU optional.
Ant Media EE · A Java plugin SDK plus a TensorFlow object-detection sample plugin; turnkey CCTV analytics (LPR, counting, intrusion) are yours to build.
Wowza · No built-in video analytics — Streaming Engine focuses on transport; you integrate external CV services through its module SDK.
StreamHub · Via the StreamHub Agent on the camera LAN: onvif scan discovers every ONVIF camera (WS-Discovery + Profile S) and onvif sync registers each one as an ingest — RTSP pull or near-zero-CPU HLS passthrough — even when the server lives in the cloud. Idempotent re-runs, optional periodic re-scan.
Ant Media EE · ONVIF IP-camera onboarding exists in its panel for cameras the server itself can reach; there is no edge agent to discover a remote customer LAN.
Wowza · No ONVIF discovery — RTSP/SRT sources are configured by URL. Fair note: both are media servers, not VMSs.
StreamHub · Origin + edge; an edge joins with one command and a token. Idempotent one-liner installer, TLS included.
Ant Media EE · Origin + edge cluster (EE feature); script install plus license setup.
Wowza · Origin/edge + load balancing in Streaming Engine; installer plus license key.
StreamHub · Native Prometheus /metrics + Grafana, and logs with 30-day retention right in the panel.
Ant Media EE · Kafka-based monitoring pipeline into Grafana.
Wowza · REST/JMX monitoring in Streaming Engine; dashboards in Wowza Video.
StreamHub · Built-in failover: two RTMP keys into one room, same player URL whichever encoder is live — Dacast/Vimeo-style redundancy, no URL change.
Ant Media EE · Redundancy is cluster-level (origin/edge); no per-stream backup key.
Wowza · Wowza Video exposes backup source URLs; Streaming Engine via stream-name aliases / source failover.
StreamHub · Animated per-app flow diagram (ingest → transcode → delivery → storage → plugins) rendered from the real config.
Ant Media EE · Dashboard metrics and per-stream stats; no per-app topology diagram.
Wowza · Engine Manager incoming/outgoing stats; no live pipeline diagram.
StreamHub · Persistent room (no SID churn when the source drops), auto-HLS watchdog and a bounded DVR manifest + segment janitor.
Ant Media EE · Continuous ingest and stream playlists; DVR window is manual.
Wowza · Strong here: nDVR is built in; linear via SMIL / scheduled playlists.
StreamHub · Ships Prometheus + Grafana dashboards + Loki/Promtail log aggregation across every node, with idempotent installers.
Ant Media EE · Exposes metrics (Kafka/Prometheus); you assemble the Grafana + log stack yourself.
Wowza · REST/JMX metrics; dashboards in Wowza Video. No bundled Loki log stack.
StreamHub · AI/transcode workers dispatch to your own GPU nodes over the cluster (GRID); falls back to local when none match.
Ant Media EE · GPU transcoding on the media node; no external GPU worker pool.
Wowza · GPU acceleration (NVENC/QSV) on the engine host; no external GPU worker offload.
StreamHub · DB-driven standalone/master/edge roles; an edge serves playback but blocks admin login. Convert standalone ↔ cluster in place.
Ant Media EE · Origin/edge cluster roles; no per-node admin login-lock.
Wowza · Origin/edge + load balancing; no per-node portal login-lock.
StreamHub · The HLS + VOD player is a plugin slot: enable Vidstack per app — modern, accessible, HLS/DASH/DRM-capable — and /play + /embed pick it up. video.js stays as the fallback; sub-second WebRTC untouched.
Ant Media EE · Ships its own embedded web player and JS player SDK; the player isn't a pluggable server-side slot — swapping players is wiring you own.
Wowza · The player is a separate product: Flowplayer (Wowza-owned, licensed separately) or bring your own player against HLS/DASH.
StreamHub · App plugins apply to every live stream by default; opt any single stream out of specific plugins from the API or the Streams tab — other streams keep them, players honor it live.
Ant Media EE · Plugins hook in at application scope (Java SDK); per-stream behaviour is up to your plugin code.
Wowza · Modules attach per application in Streaming Engine; per-stream logic means custom Java.
StreamHub · VOD behind any CDN fronting your S3, live HLS behind a CDN with CDN-correct cache headers, or the cluster's own edge nodes as a pull-through CDN. StreamHub can be the media backend of a custom OTT: live + VOD + CDN + player.
Ant Media EE · CDN integration documented (e.g. CloudFront in front of it); VOD from S3; cluster edges distribute streams.
Wowza · Genuinely strong here: Wowza Video includes CDN delivery; Streaming Engine pushes stream targets to third-party CDNs (Akamai, Fastly…).
StreamHub · Optional: we run a dedicated cluster for you — you pay for infra + ops, never a software license.
Ant Media EE · Managed cloud, per-instance pricing.
Wowza · Wowza Video SaaS, billed by usage.
Ant Media EE is a mature product and still ahead on config granularity (~170 per-app settings), native mobile SDKs and multi-destination restreaming — that last one sits at the top of our roadmap. Wowza is a 15+ year veteran with the broadest protocol matrix in the business (SRT, transcoding, nDVR built in) and a fully managed SaaS. If those are your blockers today, we'd rather tell you now.
From Ant Media: swap the SDK import — your callbacks keep working. From Wowza: your RTMP/HLS pipelines map straight onto StreamHub ingest.
WebRTC, RTMP, SRT, WHIP, RTSP and HLS; recording to your S3; realtime, multi-tenant, signed webhooks, AI vision plugins (LPR, people counting, perimeter) and a lightweight WebRTC SDK. Built on LiveKit, self-hosted.
Come in however you want, go out in low-latency WebRTC or embeddable HLS.
WebRTC player on LiveKit with sub-second latency for "watch live now" and video calls. Ephemeral per-room subscribe tokens.
Push from OBS/ffmpeg over RTMP or TLS-encrypted RTMPS to …:1935/live/<key>. Stream key + optional password: StreamHub drops any push that wasn't validated first.
AntMedia-style cheap ingest a LiveKit server normally can't do: take an H.264/AAC RTMP push and remux it straight to HLS (ffmpeg -c copy), bypassing the SFU — no video encode, so CPU stays near-idle without a GPU and it scales wide. The trade is honest: HLS-only (no WebRTC/sub-second), a single rendition (no ABR), source must already be H.264/AAC, and no recording or room features. Env-gated off by default; mutually exclusive with transcoding.
Push over SRT for lossy, long-haul contribution links: UDP with retransmission and a ~120 ms default latency budget, where RTMP-over-TCP struggles. It rides the same passthrough plane — remux straight to HLS (ffmpeg -c copy), near-zero CPU, no GPU — so the same honest trade applies: HLS-only output, source already H.264/AAC. The key travels as the streamid (the key IS the credential, no user/password), with the full create flow in the dashboard. Need WebRTC from an SRT source instead? Pull it with a srt:// URL ingress into the transcode path.
Flag an RTMP ingress audio-only and the ingress drops the incoming video track — no video encode at all, just the trivial AAC→Opus audio transcode. For a radio or podcast channel this collapses ingress CPU from ~85%/channel to ~1-2%, the no-GPU path to very low load. Still WebRTC + HLS out with the full room feature set.
WHIP endpoint to publish over WebRTC straight from the browser or modern encoders, without going through RTMP.
Pull from a remote source (rtsp://camera/stream) and republish it into the room. Ideal for IP cameras and NVRs.
Segmented HLS egress served at /hls/<app>/<room>/index.m3u8 (video.js, open CORS). ~6-15s, embeddable anywhere.
Two RTMP stream keys into the SAME room — primary preferred, backup on standby. Same player URL whichever encoder is live: Dacast/Vimeo-style redundancy without a URL change.
Persistent room that survives a source outage (no SID churn), an auto-HLS watchdog that relaunches a dead egress, and a bounded DVR manifest with a segment janitor. Radio-style always-on delivery.
Pull a broadcast-style MPEG-TS transport stream over UDP — unicast or multicast, including RFC 4607 source-specific multicast — and remux it straight to HLS (ffmpeg -c copy), bypassing LiveKit entirely. Every udp:// source is parsed, allow-listed and rebuilt server-side, so nothing typed into it reaches ffmpeg unescaped. Same honest passthrough trade as RTMP/SRT: HLS-only, source must already be H.264/AAC.
The media is yours: it lands in your bucket. Outputs go to YouTube, Twitch or your CDN.
MP4 egress uploaded to your own S3 bucket (AWS, Wasabi, MinIO). One bucket/prefix per app; keys never live in the YAML.
Every recording lands as a VOD in your S3 with a snapshot, metadata (duration/resolution/codec) and a public or presigned URL.
Cut the recording every N minutes: each part is its own MP4 = its own VOD, indexed and with a recording_part_ready callback.
Capture a JPEG every N seconds during recording, uploaded to your S3. On-demand snapshots of any room too.
Forward a room to an external RTMP/RTMPS (YouTube/Twitch/custom) with the Broadcast widget: browser webcam → restream.
Serve VOD through a CDN in front of your S3 (public base URL), live HLS through a CDN with CDN-correct cache headers, or turn the cluster's edge nodes into a pull-through CDN. Hybrid recipes for building an OTT: live + VOD + CDN + player.
Name recordings and snapshots with placeholders — ${app} ${streamId} ${room} ${ts} ${date:…} ${var.NAME} — applied traversal-safe to local files and the S3 key. Set a per-app retention window (7/30/90/180/360 days, or off) and a janitor deletes the DB row and the S3 object for you when it expires.
Chat, reactions and viewers over data-channels; signed webhooks for everything that happens.
Chat over LiveKit data-channels (with emojis). The backend can inject messages and it fires the chat_message callback.
Floating animated reactions on the reaction topic: real-time hearts and likes, with their reaction callback.
Real subscriber count per live stream, excluding publishers and hidden/QC participants. Exported to Prometheus.
StreamHub posts a signed JSON (HMAC-SHA256) for EVERY event: room, participants, ingress/egress, recording, VOD and HLS.
Public /play and /embed pages that auto-detect the stream: when a live HLS playlist exists they default to HLS — so passthrough channels just play — with a WebRTC | HLS toggle on /play, chat/reactions/viewers panel included. A Share menu on every live stream copies the player URL, the /embed iframe, the public .m3u8 or a self-contained video.js snippet.
The HLS + VOD player is a plugin slot: enable the Vidstack plugin on an app and it replaces the built-in video.js player — modern, modular, accessible, HLS/DASH/DRM-capable. /play and /embed resolve it too; sub-second WebRTC is untouched. The Live view adds a Vidstack | video.js toggle when a provider is active, each with its own embed code.
Sign a play link and get back an expiring, app+room-scoped playUrl/embedUrl and iframe snippet, validated server-side by HMAC on every /embed request. Drop it into any page you do not control — an sk_ token or JWT never has to appear in the snippet.
Five vision apps — of seventeen full Studio Apps across the marketplace — that watch any live stream and emit signed events: open-source models, CPU-viable by default (GPU optional), one managed worker per app with start/stop/logs in the panel, offloadable to your GPU nodes over GRID. Built for CCTV installers and security companies — and like the rest of StreamHub, the apps are open source: no per-camera analytics licensing, ever. Deployment, integration and managed operation are what we sell.
Realtime ALPR on open-source models only (fast-alpr, MIT: YOLOv9-t plate detection + ONNX OCR) — CPU-friendly, CUDA optional. Deduped reads with whitelist/blacklist verdicts (plate.detected / allowed / denied), snapshots and a per-plugin SQLite log, plus a direct callback URL for things like a barrier opener.
Line-crossing counting over the live stream: draw (and rotate) the counting line on a live frame right in the dashboard, get IN/OUT totals, live occupancy and capacity alerts (count.in / count.out / occupancy.limit). The counting logic is ported from a production-validated store-entrance counter.
Draw workstation zones over a live frame and get presence.absent when a zone stays empty past a threshold, presence.returned when someone is back, and presence.after_hours outside the work schedule — with a per-zone occupancy timeline in its dashboard tab.
Watches restricted zones while ARMED — on a daily schedule (IANA timezone, weekend rules) or a manual arm/disarm toggle. Every intrusion stores evidence on disk (JPEG + a no-re-encode clip) and lands in an acknowledgeable alert queue; armed state and alerts are only readable authenticated.
Flags a person inside a work zone, during work hours, missing a required helmet or hi-viz vest — sustained violations only (N consecutive frames + cooldown), each with an evidence JPEG. Honest scope: an open community ONNX model, advisory signals for a human to review — not a certified safety system.
The framework channel the vision wave rides on: workers POST the domain events their plugin manifest declares (ingest-token auth, spoofing rejected) and StreamHub relays them through the app's HMAC-signed webhooks + MQTT; authenticated live-data reads power each plugin's dashboard tab. Any plugin you write gets it for free.
Point the StreamHub Agent at the camera LAN: onvif scan finds every ONVIF camera (WS-Discovery + Profile S, plain Python stdlib) and onvif sync registers each one in StreamHub — RTSP pull or near-zero-CPU HLS passthrough per camera, idempotent re-runs, optional periodic re-scan. One command brings a whole site online, even with the server in the cloud.
The privacy pieces a CCTV deployment answers for already ship: deface masks faces in the player (detection server-side, masking client-side), watermark and timestamp overlays mark the evidence, every admin/API action lands in the audit trail, and playback URLs are signed.
Isolated apps, adaptive transcoding with optional GPU and Prometheus metrics for everything.
Rendition ladder via LiveKit simulcast + multi-layer ingress for an adaptive player per app, with no extra config.
Per-node GPU detection (NVIDIA nvidia-smi / VAAPI /dev/dri) and auto/gpu/cpu hwaccel per app. Never fails when there's no GPU.
Each app is a tenant: namespaced rooms/streams, its own S3, its own VODs/DB, its own tokens and callbacks — fully isolated.
Teams isolated per tenant, roles/permissions and quotas: apps, concurrent streams, recording minutes and GB of egress per month. Per-app config adds its own concurrent-stream and per-stream viewer caps on top, unlimited by default.
Prometheus metrics at /metrics (streams, viewers, VODs, S3 uploads, callbacks, GPU…), health probe, stats and queryable logs.
Global streamhub.db registry + one vods.db per app. No heavy database to operate: SQLite per tenant and media in S3 per app.
Plugin and transcode workers dispatch to your own GPU nodes over the cluster (placement: grid) and fall back to local when no GPU node matches. Bring the GPU, keep the origin light.
Dedicated TURN server (`--turn`) relays WebRTC media when peers sit behind restrictive NATs/firewalls across a multi-node cluster — clients need nothing, ICE servers arrive in the join response.
Everything you used to SSH for, now in the panel: per-app dashboards, logs, VODs, cluster, server settings and backups.
Each app gets its own board: active streams, live viewers, ingress/egress and recording activity, filterable per tenant.
Global and per-app logs with 30-day rotation, queryable and paginated from both the API and the panel. No SSH needed.
Paginated VOD listing per app with snapshots and metadata, plus one-click download via presigned S3 URLs.
Every node with its stats and health, right in the dashboard. An edge joins the cluster with a token and one command.
Server-wide configuration from the backoffice, Ant Media style: tune the server without touching files over SSH.
Trigger, download and restore backups of the SQLite databases and per-app config right from the panel, with a documented rollback path.
Every admin and API action is recorded in an audit trail — who did what, when, from where. Evidence on hand for ISO-style reviews.
One-click OS tuning for media workloads, plus per-node CPU/memory/disk/network history dashboards to see trends, not just snapshots.
The Overview renders each app's real pipeline — ingest → transcode → delivery → storage → plugins — as an animated flow from GET /apps/:app/pipeline, with active-vs-configured pulses. Theme-aware, respects reduced-motion.
Ships a cluster observability overlay: one central Prometheus scraping every node, Grafana dashboards and Loki/Promtail log aggregation across the fleet, with idempotent installers.
DB-driven node roles (standalone / master / edge). An edge serves the public portal and playback but blocks admin login, so a cluster node can't be misused as a standalone. Convert standalone ↔ cluster in place.
App plugins apply to every live stream by default; edit any single stream and switch specific plugins off for that stream only — the rest keep them. Per-stream editor in the Streams tab, and players honor the opt-out live.
Every app version is stored immutably (DATA_DIR/appstore/), sha256-verified before it ever runs. Pin or roll back any app to a prior version from the panel or the `streamhub app` CLI — a tampered bundle falls back to the builtin, never a crash-loop. Twelve of the marketplace apps now live in their own versioned repos and compose into the build pinned by content sha256 — indistinguishable from the built-ins at runtime.
Creating an app opens a purpose-driven wizard (surveillance / radio / events / 1:1 / live shopping / general) that sets up recording, viewer scale and vision plugins for you — always skippable, applies best-effort with deep links to what it configured.
A visual workflow engine triggered by any core, plugin or cluster event: encrypted per-app secrets (AES-256-GCM, write-only, never readable back), JSON export/import, and optional code/SSH/shell/SQL execution nodes — all gated behind a dangerous-nodes flag, default OFF. Runs in-core, no worker to manage.
Take over an already-live stream from the dashboard or the API: pause behind a configured cover (image, looping video or text card) with audio muted, resume back to the live feed, or finish with a closing card. Defaults per app, override per stream, every transition fires a signed callback.
A worker samples every live stream's HLS output and turns anomalies — stalls, dropped/discontinuous segments, A/V desync, audio silence or a missing track, bitrate drops — into signed webhook + MQTT + Streamflow events, with a worst-first status grid in the dashboard. Well under 1% CPU per stream at the default 10s cadence.
Give an app an IANA timezone and every app-scoped timestamp in the dashboard — logs, stream start times, VODs, scheduled-live — renders in that zone, so two admins in two timezones see the same wall-clock. It also becomes the default for any plugin schedule that leaves its own timezone blank.
Override the global SMTP relay per app or per user, resolved user > app > global with best-effort fall-through so a misconfigured override never blocks a magic-link or an alert email. Passwords are encrypted (per-app secrets store, AES-256-GCM for the personal one), with a test-send endpoint that bypasses the enabled gate.
Everything over an API, everything self-hostable. Drop in the SDK and start publishing in a few lines.
A lightweight JavaScript SDK: pull in @streamhub/adaptor (npm or a <script> tag) and StreamHubAdaptor lets you publish and subscribe over WebRTC in a few lines.
API under /api/v1 with an { data, error } envelope. Apps, ingress, recording, VODs, tokens, broadcast and HLS: all over HTTP.
Built-in auth (JWT signup/login, teams, superadmin) + sk_ API tokens for server-to-server. Bearer on every endpoint.
Audio-only rooms (Discord-style voice) + radio mode: one host publishes and listeners join hidden, subscribe-only, with a public listen-token.
Self-hostable end to end (Docker + LiveKit). In beta and free. Don't want to run it? We host it for you.
A complete, self-hostable stack — documented end to end. A REST API (global and per-app), signed webhooks, per-app config, native integrations and a one-command deploy. Every route lives behind a single domain, with a live OpenAPI explorer.
Per-feature deep-dives, from WebRTC to quotas.
Server-wide REST surface, guarded by sk_ tokens.
Everything scoped to a single tenant app.
Incoming LiveKit events and outbound signed POSTs.
The per-app config.yaml, secrets stripped.
Native and device clients, and the protocol each speaks.
One curl | sudo bash — or docker compose up.
How it is built and where it is heading.
Authenticate with a sk_
token. Room names are namespaced under the app prefix (live + demo → live-demo).
# Scaffold an isolated tenant: config, per-app SQLite, S3 prefix, samples
curl -s -X POST https://streamhub.digitalhub.com.ar/api/v1/apps \
-H "Authorization: Bearer $STREAMHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"live","displayName":"Live","roomPrefix":"live"}'
// Every callback is a signed POST. Verify the HMAC-SHA256 over the raw body.
import crypto from 'node:crypto';
app.post('/streamhub', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.header('X-StreamHub-Signature'); // "sha256=<hex>"
const expected =
'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
if (!sig || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
return res.status(401).end();
const evt = JSON.parse(req.body.toString('utf8')); // { event, app, data }
switch (evt.event) {
case 'stream_started': /* a publisher/ingress went live */ break;
case 'vod_ready': /* recording uploaded to your S3 */ break;
case 'recording_failed': /* upload failed, local file kept */ break;
}
res.status(200).end();
});
# Open source — one command provisions a full node (Ubuntu · Debian 12/13):
curl -fsSL https://www.streamhub.studio/install.sh | sudo bash
# docker + LiveKit + ingress/egress + core + nginx/TLS + seeded API token. Idempotent.
# Prefer to wire the pieces yourself? (non-interactive install)
curl -fsSL https://www.streamhub.studio/install.sh | sudo bash -s -- \
--non-interactive --domain media.example.com --email you@example.com
# then edit /opt/streamhub/.env (LiveKit keys, per-app S3, PUBLIC_BASE_URL)
# Swagger / OpenAPI: /api/v1/docs Metrics: /metrics Health: /api/v1/health
This is a tour, not the whole map — the full reference covers architecture, operations, testing and every environment variable. Explore it live in the OpenAPI docs, or read the streamhub-docs in the repo.
@streamhub/adaptor is a lightweight JavaScript SDK for publishing and subscribing over WebRTC, built on livekit-client. It speaks the same language as Ant Media's WebRTCAdaptor — same string callbacks, same methods — so an existing Ant Media front-end migrates by swapping the import, not rewriting it.
- import { WebRTCAdaptor } from "@antmedia/webrtc_adaptor";
+ import { StreamHubAdaptor } from "@streamhub/adaptor";
// Same callbacks, same methods — your Ant Media front-end keeps working.
const adaptor = new StreamHubAdaptor({ /* config below */ });
npm install @streamhub/adaptor livekit-client
import { StreamHubAdaptor } from "@streamhub/adaptor";
const adaptor = new StreamHubAdaptor({
// StreamHub mints the LiveKit token for you (prefer a pre-minted token + wsUrl in prod)
streamhubApiUrl: "https://streamhub.digitalhub.com.ar/api/v1",
appName: "live",
streamhubApiToken: STREAMHUB_TOKEN, // dev only — mint server-side for production
mediaConstraints: { video: true, audio: true },
localVideoId: "localVideo", // <video id="localVideo"> preview element
callback: (info, obj) => {
if (info === "initialized") adaptor.joinRoom("demo", "camera-1");
else if (info === "joinedTheRoom") adaptor.publish(obj.streamId); // send cam + mic
else if (info === "publish_started") console.log("on air");
},
callbackError: (err, msg) => console.warn(err, msg),
});
<!-- No build step? Drop in one script tag and you're ready. -->
<script src="https://streamhub.digitalhub.com.ar/sdk/streamhub-adaptor.global.js"></script>
<script>
const viewer = new StreamHubAdaptor({ isPlayMode: true, /* ...config */ });
</script>
<!-- The mint call returns a ready-to-paste embed. Public player, no SDK needed. -->
<iframe
src="https://streamhub.digitalhub.com.ar/embed/live/live-demo"
width="640" height="360" frameborder="0"
allow="autoplay; fullscreen; camera; microphone"
allowfullscreen>
</iframe>
same callbacks — initialized, publish_started, newStreamAvailable, data_received… — swap the import, keep your code
joinRoom, publish, play, sendData, switchVideoCameraCapture, enableStats…
swap camera or mic on the fly and tune bitrate per sender, live
chat and reactions ride LiveKit DataPackets; canPublishData is granted on every token
LiveKit handles simulcast and adaptive streaming natively. For production, mint tokens server-side and pass token + wsUrl so the management token never reaches the browser.
Install a full node on Ubuntu or Debian with one command, or start a free trial of managed hosting.