Low-latency WebRTC
WebRTC player on LiveKit with sub-second latency for "watch live now" and video calls. Ephemeral per-room subscribe tokens.
StreamHub is a management layer on top of LiveKit: sub-second WebRTC, multi-protocol ingest, live HLS, recording to your own S3, and a lightweight drop-in JavaScript SDK to publish and subscribe over WebRTC. Multi-tenant, with signed webhooks and observability.
The open-source (AGPL) alternative to Ant Media Enterprise — 0.19s p50 WebRTC measured in production, signed webhooks and a drop-in SDK to migrate without a rewrite. See the head-to-head →
curl -fsSL https://www.streamhub.studio/install.sh | sudo bash One-liner install · Ubuntu 24.04 / 26.04 LTS · what it provisions →
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.
The same building blocks — multi-protocol ingest, sub-second WebRTC, recording to your S3, plugins — combined into setups already running on StreamHub.
Plug IP cameras and NVRs in over RTSP and watch everything from a multi-camera cockpit.
Sell live at conversation latency, with interaction built in and embedded right in your store.
Video consultations on your own infrastructure: patient data never leaves your S3.
Stations and voice rooms without video: one host publishes, listeners tune in with a public link.
Real screenshots of the StreamHub backoffice. One place for your apps, streams, recordings, tokens and webhooks — running on LiveKit.






Core status at a glance: live CPU, memory and disk, active streams, registered apps and ingress/egress connections. Plus per-app boards, a cluster view and 30-day logs — no SSH needed.
Light or dark, your call — the whole backoffice is themed.


From zero to on-air in three steps.
Each app is an isolated tenant: its own room prefix, S3 bucket, tokens and quotas. Ready in seconds from the backoffice.
Push your feed over RTMP, WHIP, RTSP or straight from the webcam. Copy the URL and stream key into OBS and go live.
Sub-second WebRTC or HLS, recording to your own S3 (VOD + snapshots) and embeddable players via public link or iframe.
We're in beta and it's free. Open source and self-hostable — or 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"}' # One call returns the LiveKit token + wsUrl + play/embed URLs
curl -s -X POST https://streamhub.digitalhub.com.ar/api/v1/apps/live/tokens \
-H "Authorization: Bearer $STREAMHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"room":"demo","identity":"viewer-1","canPublish":false,"ttl":"10m"}'
# → { "data": { "token": "<jwt>", "wsUrl": "wss://media.digitalhub.com.ar",
# "room": "live-demo", "playUrl": "...", "embedUrl": "...", "iframe": "..." } } # RTMP push endpoint (also: whip, or url for RTSP/HLS pull)
curl -s -X POST https://streamhub.digitalhub.com.ar/api/v1/apps/live/ingress \
-H "Authorization: Bearer $STREAMHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"inputType":"rtmp","room":"demo","enableTranscoding":true}'
# → { "data": { "ingressId": "IN_abc123", "streamKey": "sk-9f3c...", "roomName": "live-demo" } }
# then push with any RTMP encoder (OBS, ffmpeg, a drone...)
ffmpeg -re -i input.mp4 -c:v libx264 -c:a aac \
-f flv "rtmp://media.digitalhub.com.ar:1935/live/<streamKey>" # Room-composite MP4 → the app's own bucket (AWS / Wasabi / MinIO) → VOD + snapshot
curl -s -X POST https://streamhub.digitalhub.com.ar/api/v1/apps/live/recording/start \
-H "Authorization: Bearer $STREAMHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"roomName":"live-demo"}'
# → { "data": { "vodId": 12, "egressId": "EG_xyz789", "status": "recording" } }
# stop it (by VOD id or egress id); a vod_ready webhook fires when the upload finishes
curl -s -X POST https://streamhub.digitalhub.com.ar/api/v1/apps/live/recording/12/stop \
-H "Authorization: Bearer $STREAMHUB_TOKEN" # Start a live HLS egress for a stream (video.js-compatible, embeddable)
curl -s -X POST \
https://streamhub.digitalhub.com.ar/api/v1/apps/live/streams/live-demo%2Fcamera-1/hls/start \
-H "Authorization: Bearer $STREAMHUB_TOKEN"
# → { "data": { "playlistUrl":
# "https://streamhub.digitalhub.com.ar/hls/live/live-demo/index.m3u8" } }
# the playlist itself needs no auth — point any HLS player at it
open https://streamhub.digitalhub.com.ar/hls/live/live-demo/index.m3u8 // 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),
}); import { StreamHubAdaptor } from "@streamhub/adaptor";
const viewer = new StreamHubAdaptor({
streamhubApiUrl: "https://streamhub.digitalhub.com.ar/api/v1",
appName: "live",
streamhubApiToken: STREAMHUB_TOKEN,
isPlayMode: true, // subscribe-only: no camera / mic
callback: (info, obj) => {
if (info === "initialized") viewer.joinRoom("demo", "viewer-1");
else if (info === "joinedTheRoom") viewer.play(obj.ATTR_ROOM_NAME);
else if (info === "newStreamAvailable") {
const el = document.createElement("video");
el.autoplay = el.playsInline = true;
el.srcObject = new MediaStream([obj.track]); // obj.track is a MediaStreamTrack
document.getElementById("remote").append(el);
}
},
}); <!-- 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.
The hosted installer provisions the whole stack on a clean Ubuntu Server: Docker, LiveKit, ingress/egress, the StreamHub core, nginx with TLS certificates and a seeded API token. Idempotent — re-running upgrades in place and never rotates your secrets.
$ curl -fsSL https://www.streamhub.studio/install.sh | sudo bash $ curl -fsSL https://www.streamhub.studio/install.sh | sudo bash -s -- \
--join --cluster-token <token> --origin-ip <ip> StreamHub is 100% open source (AGPL) and self-hostable — or, if you'd rather not run it, we host it for you. We're in beta, so you can try it free while we keep polishing.
One command installs a full node on Ubuntu LTS — your own media server on LiveKit. Recording to your S3, your apps, your rules. No lock-in.
Don't want to operate the infra? We hand you StreamHub managed, updated and monitored. You focus on streaming.
Tell us your use case — low-latency WebRTC, RTMP/WHIP/RTSP ingest, recording to your own S3, restreaming to YouTube/Twitch or multi-tenant. We'll help you get going, self-hosted or managed.
Start in minutes
Try the live app, nothing to install.
Your data, your S3
Recordings and VOD in your own bucket.
Open the app
app.streamhub.studio