- Add cache *keyFrameCache field to Source (nil by default). - Add EnableKeyFrameCache() — call before Start() on video sources to activate IDR burst caching. - readLoop: call cache.push(pkt) after each successful unmarshal, before the subscriber fanout. No lock held at push time — push acquires its own mutex internally. - Subscribe: snapshot the cache outside s.mu to avoid any cross-lock complexity, then pre-fill the new channel with the burst before registering it in the subscriber set. Uses a labeled break to stop pre-filling if the channel is full (bufDepth too small for the burst; the subscriber will wait for the next live keyframe instead). |
||
|---|---|---|
| .forgejo/workflows | ||
| .github | ||
| app | ||
| cmd/webrtc-poc | ||
| config | ||
| core/webrtc | ||
| deploy | ||
| docs | ||
| encoding/json | ||
| ffmpeg | ||
| glob | ||
| http | ||
| internal | ||
| io | ||
| log | ||
| math/rand | ||
| monitor | ||
| net | ||
| playout | ||
| process | ||
| prometheus | ||
| psutil | ||
| restream | ||
| rtmp | ||
| service | ||
| session | ||
| src/misc/Logo | ||
| srt | ||
| test | ||
| update | ||
| vendor | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitignore | ||
| build.sh | ||
| CHANGELOG.md | ||
| CREDITS | ||
| Dockerfile | ||
| Dockerfile.bundle | ||
| Dockerfile.test | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| mime.types | ||
| NOTES.md | ||
| NOTICE | ||
| README.md | ||
| run.sh | ||
| SECURITY.md | ||
Datarhei — Dragon Fork
A fork of datarhei/core that adds a native WebRTC (WHEP) egress path. Everything upstream Datarhei already does — RTMP / SRT / RTSP ingest, FFmpeg process orchestration, HLS / DASH outputs, S3 mounts, the HTTP API and Swagger UI — works unchanged. WebRTC sits alongside as another output type, opt-in per process.
publisher (OBS / FFmpeg / SRT) ──▶ datarhei Core ──▶ WebRTC peers
│ │ (1–5 viewers per stream)
│ ├──▶ HLS / DASH (existing)
│ ├──▶ RTMP relay (existing)
└──▶ ingest (RTMP / SRT / …) └──▶ recording (existing)
Sub-second glass-to-glass on a LAN over WHEP, no SFU dependencies, single binary, single Docker image.
Status: v0.2 in progress (last work 2026-05-06). Full GUI bundled (Restreamer UI + Wild Dragon WebRTC admin). Prometheus + Grafana observability stack shipped. Live deploy running on TrueNAS since 2026-04-17.
What this fork adds
webrtc.*config block alongsidertmp.*andsrt.*, with the sameCORE_*env-var binding pattern.- Per-process
webrtc.enabledtoggle on the existing process config. Once true, Core auto-injects two RTP output legs (video + audio), allocates UDP ports, and the WHEP endpoint is live. POST /api/v3/whep/{processID}— WebRTC-HTTP Egress Protocol subscribe; SDP offer in, SDP answer out. JWT-protected by the existing Core auth.DELETE /api/v3/whep/{processID}/{resourceID}— idempotent teardown.PATCH …/{resourceID}— trickle ICE.- Bundled GUI — the upstream Restreamer React UI is built into the
TrueNAS deploy image with Wild Dragon branding, plus a single-file
Wild Dragon WebRTC admin page for one-click
webrtc.enabledtoggling. - Browser-side smoke player at
whep-player.html— zero-dependency WHEP subscriber, ICE/codec/bitrate stats, JWT field, shareable?url=&token=URLs. - Prometheus observability — eleven
dragonfork_webrtc_*metrics (RED-method counters/histograms + state gauges). Grafana health dashboard with 5 rows and 4 pre-loaded alert rules. - Multi-viewer correctness: per-stream peer cap, ICE-failure auto-cleanup, process-stop broadcast tear-down.
- Error matrix per the design spec:
406on codec mismatch,504on ICE timeout,503on cap,204on idempotent DELETE, CORS preflights on every WHEP route.
The existing upstream Datarhei feature set is intact — see "From upstream Datarhei" below.
Quick start
Docker (TrueNAS / any host with Docker + LAN-reachable IP)
git clone https://forge.wilddragon.net/zgaetano/datarhei-dragonfork-core.git
cd datarhei-dragonfork-core/deploy/truenas/core
cat > .env <<EOF
PUBLIC_IP=10.0.0.25
CORE_HTTP_PORT=8080
API_AUTH_USERNAME=admin
API_AUTH_PASSWORD=$(openssl rand -base64 24)
API_AUTH_JWT_SECRET=$(openssl rand -base64 48)
GRAFANA_ADMIN_PASSWORD=$(openssl rand -base64 24)
GRAFANA_PORT=3000
PROM_PORT=9090
EOF
docker compose up -d --build
Then open in a browser (replace <host> with your PUBLIC_IP):
| URL | What it does |
|---|---|
http://<host>:8080/ |
Restreamer UI — manage processes, ingests, outputs |
http://<host>:8080/wilddragon-webrtc.html |
Wild Dragon WebRTC admin — toggle webrtc.enabled per process, copy WHEP URL |
http://<host>:8080/whep-player.html |
WHEP smoke player — verify the WebRTC stream renders |
http://<host>:3000/ |
Grafana — WebRTC Health dashboard (login with GRAFANA_ADMIN_PASSWORD) |
http://<host>:9090/ |
Prometheus — raw metrics, alert rules |
http://<host>:8080/api/swagger/index.html |
Swagger — full API docs |
The Restreamer UI doesn't yet have a WebRTC checkbox in its process
editor — use /wilddragon-webrtc.html for that. Tracked in issue #15.
Pulling a pre-built image (after first tag is published)
# Update .env, then:
docker compose pull # pulls pre-built multi-arch image
docker compose up -d # no --build needed
Sample process JSON
{
"id": "live",
"input": [
{ "address": "{rtmp,name=live.stream}", "options": [] }
],
"output": [],
"webrtc": { "enabled": true }
}
That's it. No webrtc:// URL scheme to learn — the toggle on
config.webrtc.enabled is the entire surface. The resolver allocates
ports, injects -f rtp udp://… legs into the FFmpeg command, and the
WHEP endpoint at /api/v3/whep/live becomes live the moment the
process starts.
For multi-input pipelines (lavfi test sources, multi-camera switches,
SDI + file audio), use the video_map and audio_map fields:
"webrtc": {
"enabled": true,
"video_map": "0:v:0",
"audio_map": "1:a:0",
"force_transcode": true
}
Documentation
| Topic | Where |
|---|---|
| Design spec | docs/design/2026-04-16-datarhei-dragon-fork-webrtc-design.md |
| M1 (PoC) plan | docs/design/2026-04-16-datarhei-dragon-fork-m1-webrtc-poc.md |
| M2 (Core integration) spec | docs/design/2026-04-17-datarhei-dragon-fork-m2-webrtc-core-integration.md |
| Prometheus metrics design | docs/design/2026-05-03-datarhei-dragon-fork-webrtc-prometheus-metrics-design.md |
| Upstream rebase policy | docs/REBASE.md |
| TrueNAS deploy guide | deploy/truenas/core/README.md |
| Testing | test/TESTING.md |
| Changelog (Dragon Fork) | CHANGELOG.md |
| Upstream Datarhei docs | docs.datarhei.com/core |
Building from source
Go 1.24 required (vendored).
make release # cross-compiles linux/amd64 to ./core/core
make test # full suite, race detector
go test -tags latency -timeout 90s -count=1 \
-run TestLatencyServerHop ./app/webrtc/... # latency p95 gate
Load testing
go run ./test/load/sustained.go \
-url http://<host>:8080 \
-stream <processID> \
-peers 5 \
-duration 10m \
-auth "Bearer <TOKEN>" \
-out test/load/results/
Reports are written to test/load/results/. Observe the Grafana
WebRTC Health dashboard during the run.
From upstream Datarhei
This fork preserves everything upstream Datarhei Core does — Dragon Fork is purely additive. If a feature isn't WebRTC-related, the behaviour is unchanged from upstream and the upstream documentation applies as-is.
| Subsystem | Upstream feature set |
|---|---|
| Process management | API-driven FFmpeg, error detection / recovery, log history, resource limits, statistics, FFprobe input verification, process metadata |
| Media delivery | HTTP/S, RTMP/S, SRT services with Let's Encrypt, configurable file systems (in-memory / disk / S3), HLS/DASH session limits, viewer session API |
| Misc | HTTP REST + GraphQL, Swagger, Prometheus metrics, multi-arch Docker images |
Attribution
Dragon Fork is built on:
- datarhei Core — Apache 2.0, © datarhei. The base repository this
fork tracks. See
NOTICEfor the required attribution. - datarhei Restreamer UI — Apache 2.0, © datarhei. The React frontend bundled into the TrueNAS deploy image with Wild Dragon overlays.
- Pion WebRTC — MIT. The Go WebRTC stack the egress path is built on.
- FFmpeg — LGPL / GPL (build-flag dependent). Used as a subprocess for transcoding and RTP packetisation; Dragon Fork doesn't link against it.
Full third-party credits in CREDITS.
License
Apache License 2.0 — same as upstream. See LICENSE.