Merge branch 'm5-branding-release' into m2-webrtc-core-integration
Some checks failed
ci / vet + build (push) Successful in 9m49s
ci / vet + build (pull_request) Successful in 9m59s
ci / race tests (push) Failing after 8m1s
ci / WebRTC smoke (5-viewer fanout) (push) Successful in 9m45s
ci / WebRTC latency p95 gate (push) Successful in 10m3s
ci / race tests (pull_request) Failing after 8m6s
ci / WebRTC smoke (5-viewer fanout) (pull_request) Successful in 9m45s
ci / WebRTC latency p95 gate (pull_request) Successful in 10m5s

# Conflicts:
#	docs/docs.go
#	docs/swagger.json
#	docs/swagger.yaml
This commit is contained in:
Zac Gaetano 2026-05-03 12:26:39 +00:00
commit fd391b5ca4
11 changed files with 337 additions and 71 deletions

View file

@ -1,4 +1,72 @@
# Core # Datarhei — Dragon Fork
## v0.1.0-dragonfork (2026-05-03)
The first tagged Dragon Fork release. Forked from upstream datarhei
Core v16.16.0; everything upstream does is preserved unchanged. New:
WebRTC (WHEP) egress, integrated with the existing process supervisor.
### Added
- **WebRTC subsystem** under `app/webrtc/`, mirroring the shape of
upstream's RTMP and SRT servers (Server interface, Echo handlers,
process-graph hooks, admin endpoints).
- **Per-process opt-in** via `config.webrtc.enabled` on every restream
process; resolver auto-injects two RTP output legs and allocates
loopback UDP ports.
- **`POST /api/v3/whep/{id}`** — WebRTC-HTTP Egress Protocol subscribe.
JWT-protected by the existing Core auth.
- **`DELETE /api/v3/whep/{id}/{resource}`** — idempotent teardown
(returns 204 even on unknown resource per WHEP spec).
- **`PATCH /api/v3/whep/{id}/{resource}`** — trickle ICE.
- **CORS preflight** on every WHEP route + `Access-Control-Expose-Headers`
for `Location` and `ETag` so browser-side WHEP players work
cross-origin.
- **Configurable stream maps** via `webrtc.video_map` / `webrtc.audio_map`
on the per-process config — defaults to `0:v:0` / `0:a:0` for
RTMP/SRT publishers, overridable for multi-input pipelines.
- **`webrtc.*` global config block** with `CORE_WEBRTC_*` env-var
bindings parallel to RTMP and SRT.
- **Admin API:** `GET /api/v3/webrtc/streams` + `/streams/{id}/peers`.
- **Browser smoke player** at `test/whep-player.html` with ICE / codec
/ bitrate diagnostics, JWT field, and `?url=&token=` shareable
URLs.
- **Server-hop latency p95 gate** in CI (`-tags latency`), enforced at
50ms on the runner; locally observed p95 ≈ 240µs.
- **TrueNAS deploy bundle** at `deploy/truenas/core/` — host-networked
Docker stack with bundled FFmpeg, env-driven config.
- **Multi-viewer correctness:** per-stream peer cap, ICE-failure
auto-cleanup goroutines, process-stop broadcast tear-down.
- **Error matrix:** 406 codec mismatch, 504 ICE timeout, 503 cap
reached (separate body for total vs per-stream), 204 DELETE
idempotent.
### Fixed
- `Config.Clone()` now preserves the `WebRTC` section. Pre-fix,
`cfg.WebRTC.Enable` was always zero at runtime regardless of
`CORE_WEBRTC_ENABLE`. Caught on the first M2 TrueNAS deploy.
- `http/api.ProcessConfig` Marshal/Unmarshal now carry the per-process
`webrtc` block. Pre-fix, `POST /api/v3/process` silently dropped
`webrtc.enabled=true` on its way to the restream config layer.
### Forking notes
- Module path stays `github.com/datarhei/core/v16` — internal imports
don't churn, the fork is distinguished by repo location and branch
history.
- `cmd/webrtc-poc` from M1 is preserved as a manual-testing harness.
Production deploys use the main `core` binary.
### Acknowledgements
Built on upstream Datarhei Core (Apache 2.0) and Pion WebRTC v4
(MIT). Full attribution in `NOTICE` and `CREDITS`.
---
# Core (upstream)
### Core v16.15.0 > v16.16.0 ### Core v16.15.0 > v16.16.0

47
CREDITS Normal file
View file

@ -0,0 +1,47 @@
# Credits
Datarhei — Dragon Fork stands on the shoulders of the open-source
projects below. Required-attribution notices and the corresponding
licenses live in NOTICE and the per-vendor LICENSE files under
vendor/.
## Direct ancestor
- **datarhei/core** (Apache-2.0) — the base codebase this fork tracks.
https://github.com/datarhei/core
## Major Go dependencies
- **github.com/pion/webrtc/v4** (MIT) — the Go WebRTC stack the egress
path is built on. https://github.com/pion/webrtc
- **github.com/pion/rtp** (MIT) — RTP packet types.
- **github.com/pion/dtls/v2** (MIT) — DTLS for SRTP key exchange.
- **github.com/pion/ice/v3** (MIT) — ICE candidate gathering.
- **github.com/pion/sdp/v3** (MIT) — SDP parsing.
- **github.com/labstack/echo/v4** (MIT) — HTTP routing.
- **github.com/swaggo/echo-swagger** (MIT) — OpenAPI / Swagger UI
middleware.
- **github.com/caddyserver/certmagic** (Apache-2.0) — Let's Encrypt
TLS automation.
- **github.com/datarhei/joy4** (Apache-2.0) — RTMP server primitives
(forked from joy4).
- **github.com/datarhei/gosrt** (Apache-2.0) — pure-Go SRT.
- **go.uber.org/zap** (MIT) — structured logging.
## Subprocess
- **FFmpeg** (LGPL-2.1-or-later / GPL-2.0-or-later, build-flag
dependent) — used as an out-of-process child by the `restream`
subsystem for transcoding and RTP packetisation. Dragon Fork does
not link against the FFmpeg libraries.
## Brand assets
- **"Wild Dragon" mark** — © Wild Dragon, used as the project mark
for Dragon Fork builds.
## Full list
The complete dependency tree, including transitive dependencies and
their licenses, is enumerated in `vendor/modules.txt` and the
per-vendor LICENSE / COPYING files under `vendor/`.

41
NOTICE Normal file
View file

@ -0,0 +1,41 @@
Datarhei — Dragon Fork
Copyright (c) 2026 Wild Dragon
This product includes software developed by datarhei.
datarhei Core
Copyright (c) datarhei
https://github.com/datarhei/core
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. A copy of the
License is in the LICENSE file at the root of this repository, and is
also available at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
This fork additionally bundles or depends on:
Pion WebRTC and related Pion libraries
Copyright (c) The Pion authors
https://github.com/pion
MIT License
Echo HTTP framework
Copyright (c) LabStack
https://github.com/labstack/echo
MIT License
FFmpeg (used as a subprocess by the restream subsystem; not linked)
Copyright (c) The FFmpeg developers
https://ffmpeg.org
LGPL-2.1-or-later / GPL-2.0-or-later (build-flag dependent)
A complete list of dependencies and their licenses lives in the
CREDITS file at the root of this repository.

203
README.md
View file

@ -1,92 +1,155 @@
# Core # Datarhei — Dragon Fork
![dsdsds](https://github.com/datarhei/misc/blob/main/img/media-core.png?raw=true) A fork of [datarhei/core](https://github.com/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.
[![License: Apache2](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](<[https://opensource.org/licenses/MI](https://www.apache.org/licenses/LICENSE-2.0)>) ```
[![CodeQL](https://github.com/datarhei/core/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/datarhei/core/actions/workflows/codeql-analysis.yml) publisher (OBS / FFmpeg / SRT) ──▶ datarhei Core ──▶ WebRTC peers
[![tests](https://github.com/datarhei/core/actions/workflows/go-tests.yml/badge.svg)](https://github.com/datarhei/core/actions/workflows/go-tests.yml) │ │ (15 viewers per stream)
[![codecov](https://codecov.io/gh/datarhei/core/branch/main/graph/badge.svg?token=90YMPZRAFK)](https://codecov.io/gh/datarhei/core) │ ├──▶ HLS / DASH (existing)
[![Go Report Card](https://goreportcard.com/badge/github.com/datarhei/core)](https://goreportcard.com/report/github.com/datarhei/core) │ ├──▶ RTMP relay (existing)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/datarhei/core)](https://pkg.go.dev/github.com/datarhei/core) └──▶ ingest (RTMP / SRT / …) └──▶ recording (existing)
[![Gitbook](https://img.shields.io/badge/GitBook-quick%20start-green)](https://docs.datarhei.com/core/guides/beginner) ```
The datarhei Core is a process management solution for FFmpeg that offers a range of interfaces for media content, including HTTP, RTMP, SRT, and storage options. It is optimized for use in virtual environments such as Docker. It has been implemented in various contexts, from small-scale applications like Restreamer to large-scale, multi-instance frameworks spanning multiple locations, such as dedicated servers, cloud instances, and single-board computers. The datarhei Core stands out from traditional media servers by emphasizing FFmpeg and its capabilities rather than focusing on media conversion. Sub-second glass-to-glass on a LAN over WHEP, no SFU dependencies,
single binary, single Docker image.
## Objectives of development > **Status:** M1M4 complete, M5 (release) in flight. Live deploy
> running on TrueNAS since 2026-04-17.
The objectives of development are: ## What this fork adds
- Unhindered use of FFmpeg processes - **`webrtc.*` config block** alongside `rtmp.*` and `srt.*`, with the
- Portability of FFmpeg, including management across development and production environments same `CORE_*` env-var binding pattern.
- Scalability of FFmpeg-based applications through the ability to offload processes to additional instances - **Per-process `webrtc.enabled` toggle** on the existing process
- Streamlining of media product development by focusing on features and design. 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.
- **Browser-side smoke player** at `test/whep-player.html`
zero-dependency WHEP subscriber, ICE/codec/bitrate stats, JWT
field, shareable `?url=&token=` URLs.
- **Multi-viewer correctness:** per-stream peer cap, ICE-failure
auto-cleanup, process-stop broadcast tear-down.
- **Error matrix** per the design spec: `406` on codec mismatch,
`504` on ICE timeout, `503` on cap, `204` on idempotent DELETE,
CORS preflights on every WHEP route.
## What issues have been resolved thus far? The existing upstream Datarhei feature set is intact — see "From
upstream Datarhei" below.
### Process management
- Run multiple processes via API
- Unrestricted FFmpeg commands in process configuration.
- Error detection and recovery (e.g., FFmpeg stalls, dumps)
- Referencing for process chaining (pipelines)
- Placeholders for storage, RTMP, and SRT usage (automatic credentials management and URL resolution)
- Logs (access to current stdout/stderr)
- Log history (configurable log history, e.g., for error analysis)
- Resource limitation (max. CPU and MEMORY usage per process)
- Statistics (like FFmpeg progress per input and output, CPU and MEMORY, state, uptime)
- Input verification (like FFprobe)
- Metadata (option to store additional information like a title)
### Media delivery
- Configurable file systems (in-memory, disk-mount, S3)
- HTTP/S, RTMP/S, and SRT services, including Let's Encrypt
- Bandwidth and session limiting for HLS/MPEG DASH sessions (protects restreams from congestion)
- Viewer session API and logging
### Misc
- HTTP REST and GraphQL API
- Swagger documentation
- Metrics incl. Prometheus support (also detects POSIX and cgroups resources)
- Docker images for fast setup of development environments up to the integration of cloud resources
## Docker images
- datarhei/core:latest (AMD64, ARM64, ARMv7)
- datarhei/core:cuda-latest (Nvidia CUDA 11.7.1, AMD64)
- datarhei/core:rpi-latest (Raspberry Pi / OMX/V4L2-M2M, AMD64/ARMv7)
- datarhei/core:vaapi-latest (Intel VAAPI, AMD64)
## Quick start ## Quick start
1. Run the Docker image ### Docker (TrueNAS / any host with Docker + LAN-reachable IP)
```sh ```sh
docker run --name core -d \ git clone https://forge.wilddragon.net/zgaetano/datarhei-dragonfork-core.git
-e CORE_API_AUTH_USERNAME=admin \ cd datarhei-dragonfork-core/deploy/truenas/core
-e CORE_API_AUTH_PASSWORD=secret \
-p 8080:8080 \ cat > .env <<EOF
-v ${HOME}/core/config:/core/config \ PUBLIC_IP=10.0.0.25
-v ${HOME}/core/data:/core/data \ CORE_HTTP_PORT=8080
datarhei/core:latest API_AUTH_USERNAME=admin
API_AUTH_PASSWORD=$(openssl rand -base64 24)
API_AUTH_JWT_SECRET=$(openssl rand -base64 48)
EOF
docker compose up -d --build
``` ```
2. Open Swagger Then:
http://host-ip:8080/api/swagger/index.html
3. Log in with Swagger - Swagger UI: `http://<host>:8080/api/swagger/index.html`
Authorize > Basic authorization > Username: admin, Password: secret - WHEP smoke player: open `test/whep-player.html` in a browser
### Sample process JSON
```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:
```json
"webrtc": {
"enabled": true,
"video_map": "0:v:0",
"audio_map": "1:a:0",
"force_transcode": true
}
```
## Documentation ## Documentation
Documentation is available on [docs.datarhei.com/core](https://docs.datarhei.com/core). | Topic | Where |
| ----- | ----- |
| Design spec | [`docs/design/2026-04-16-datarhei-dragon-fork-webrtc-design.md`](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`](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`](docs/design/2026-04-17-datarhei-dragon-fork-m2-webrtc-core-integration.md) |
| Testing | [`test/TESTING.md`](test/TESTING.md) |
| Changelog (Dragon Fork) | [`CHANGELOG.md`](CHANGELOG.md) |
| Upstream Datarhei docs | [docs.datarhei.com/core](https://docs.datarhei.com/core) |
- [Quick start](https://docs.datarhei.com/core/guides/beginner) ## Building from source
- [Installation](https://docs.datarhei.com/core/installation)
- [Configuration](https://docs.datarhei.com/core/configuration) Go 1.24 required (vendored).
- [Coding](https://docs.datarhei.com/core/development/coding)
```sh
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
```
## 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 [`NOTICE`](NOTICE) for the required attribution.
- **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`](CREDITS).
## License ## License
datarhei/core is licensed under the Apache License 2.0 Apache License 2.0 — same as upstream. See [`LICENSE`](LICENSE).

View file

@ -219,6 +219,8 @@ func (a *api) Reload() error {
logfields := log.Fields{ logfields := log.Fields{
"application": app.Name, "application": app.Name,
"variant": app.Variant,
"fork": app.Fork,
"version": app.Version.String(), "version": app.Version.String(),
"repository": "https://github.com/datarhei/core", "repository": "https://github.com/datarhei/core",
"license": "Apache License Version 2.0", "license": "Apache License Version 2.0",

View file

@ -8,6 +8,19 @@ import (
// Name of the app // Name of the app
const Name = "datarhei-core" const Name = "datarhei-core"
// Variant distinguishes a Dragon Fork build from upstream Datarhei
// Core in the startup banner and in the /api/v3/about endpoint
// payload. Empty would imply an upstream build; we override the
// linker default with the fork identity.
//
// Kept as a var (not const) so a downstream packager can override it
// at build time via -ldflags="-X github.com/datarhei/core/v16/app.Variant=…"
// without forking the source.
var Variant = "dragonfork"
// Fork carries the human-readable fork name surfaced in logs.
var Fork = "Datarhei — Dragon Fork"
type versionInfo struct { type versionInfo struct {
Major int Major int
Minor int Minor int

View file

@ -2241,6 +2241,10 @@ const docTemplate = `{
"created_at": { "created_at": {
"type": "string" "type": "string"
}, },
"fork": {
"description": "Fork is the human-readable fork name (e.g. \"Datarhei — Dragon Fork\").",
"type": "string"
},
"id": { "id": {
"type": "string" "type": "string"
}, },
@ -2250,6 +2254,10 @@ const docTemplate = `{
"uptime_seconds": { "uptime_seconds": {
"type": "integer" "type": "integer"
}, },
"variant": {
"description": "Variant identifies the build flavor — empty (or \"core\") for an\nupstream Datarhei build, \"dragonfork\" for the Dragon Fork.",
"type": "string"
},
"version": { "version": {
"$ref": "#/definitions/api.Version" "$ref": "#/definitions/api.Version"
} }

View file

@ -2234,6 +2234,10 @@
"created_at": { "created_at": {
"type": "string" "type": "string"
}, },
"fork": {
"description": "Fork is the human-readable fork name (e.g. \"Datarhei — Dragon Fork\").",
"type": "string"
},
"id": { "id": {
"type": "string" "type": "string"
}, },
@ -2243,6 +2247,10 @@
"uptime_seconds": { "uptime_seconds": {
"type": "integer" "type": "integer"
}, },
"variant": {
"description": "Variant identifies the build flavor — empty (or \"core\") for an\nupstream Datarhei build, \"dragonfork\" for the Dragon Fork.",
"type": "string"
},
"version": { "version": {
"$ref": "#/definitions/api.Version" "$ref": "#/definitions/api.Version"
} }

View file

@ -56,12 +56,21 @@ definitions:
type: array type: array
created_at: created_at:
type: string type: string
fork:
description: Fork is the human-readable fork name (e.g. "Datarhei — Dragon
Fork").
type: string
id: id:
type: string type: string
name: name:
type: string type: string
uptime_seconds: uptime_seconds:
type: integer type: integer
variant:
description: |-
Variant identifies the build flavor — empty (or "core") for an
upstream Datarhei build, "dragonfork" for the Dragon Fork.
type: string
version: version:
$ref: '#/definitions/api.Version' $ref: '#/definitions/api.Version'
type: object type: object

View file

@ -3,6 +3,11 @@ package api
// About is some general information about the API // About is some general information about the API
type About struct { type About struct {
App string `json:"app"` App string `json:"app"`
// Variant identifies the build flavor — empty (or "core") for an
// upstream Datarhei build, "dragonfork" for the Dragon Fork.
Variant string `json:"variant,omitempty"`
// Fork is the human-readable fork name (e.g. "Datarhei — Dragon Fork").
Fork string `json:"fork,omitempty"`
Auths []string `json:"auths"` Auths []string `json:"auths"`
Name string `json:"name"` Name string `json:"name"`
ID string `json:"id"` ID string `json:"id"`

View file

@ -39,6 +39,8 @@ func (p *AboutHandler) About(c echo.Context) error {
about := api.About{ about := api.About{
App: app.Name, App: app.Name,
Variant: app.Variant,
Fork: app.Fork,
Name: p.restream.Name(), Name: p.restream.Name(),
Auths: p.auths, Auths: p.auths,
ID: p.restream.ID(), ID: p.restream.ID(),