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 Ant Media Server Enterprise feature set for 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
to install — curl | sudo bash on Ubuntu 24.04 / 26.04 LTS
StreamHub · Open source (AGPL). No per-instance license — free while in beta.
Ant Media EE · Proprietary. ~$109/mo (~$1,068/yr) per instance.
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.
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.
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.
StreamHub · Each app is a real tenant: isolated rooms, S3, database, tokens, quotas, teams and roles.
Ant Media EE · Applications scope settings and streams.
StreamHub · @streamhub/adaptor shims Ant Media's WebRTCAdaptor: same callbacks and methods, no rewrite.
Ant Media EE · Its own WebRTCAdaptor SDK.
StreamHub · Marketplace with working verticals: YOLO detection, CCTV cockpit, radio, watermark, live chat.
Ant Media EE · Java plugin SDK — you build your own.
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.
StreamHub · Native Prometheus /metrics + Grafana, and logs with 30-day retention right in the panel.
Ant Media EE · Kafka-based monitoring pipeline into Grafana.
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. If those are your blockers today, we'd rather tell you now.
Swap the SDK import — your callbacks keep working.
WebRTC, RTMP, WHIP, RTSP and HLS; recording to your S3; realtime, multi-tenant, signed webhooks 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 to rtmp://…:1935/live/<key>. Stream key + optional password: StreamHub drops any push that wasn't validated first.
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.
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.
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 + iframe snippet. WebRTC and HLS players without auth, with a chat/reactions/viewers panel included.
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.
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.
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.
Automated backups of the SQLite databases and per-app config, with a documented restore and rollback path.
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 24.04/26.04 LTS):
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 LTS with one command, or open the managed app.