Update all docs to reflect current working configuration
- README: update architecture diagram for RTMP+FFmpeg pipeline, add FFmpeg install step, fix path descriptions - obs-setup: switch from WHIP to RTMP output, add FFmpeg prerequisite, fix script log messages (MediaMTX starts on load not streaming start), add Python setup note, update troubleshooting for game-opus path and audio - npm-setup: remove Custom Locations GUI instructions (must be empty - all locations defined in Advanced tab only), update verify steps to game-opus paths, add troubleshooting for WHEP 400/401 causes - authentik-setup: add section 6 covering both manual account creation and self-service enrollment via invite link; clarify User Write stage group field is what triggers auto-add (not the invitation form) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,13 +12,21 @@ Authentik authentication.
|
||||
## How it works
|
||||
|
||||
```
|
||||
OBS Studio (NVENC, WHIP out)
|
||||
-> MediaMTX (localhost) ---> WHEP / HLS / API
|
||||
-> Frontend HTTP server (localhost:8080)
|
||||
OBS Studio (NVENC H.264, RTMP out)
|
||||
-> MediaMTX (localhost, RTMP ingest)
|
||||
-> FFmpeg (AAC -> Opus transcode, internal RTSP)
|
||||
-> game-opus path (H.264 + Opus)
|
||||
-> WHEP / HLS for viewers
|
||||
-> Frontend HTTP server (localhost:48080)
|
||||
-> NPM (TLS, Authentik forward auth, reverse proxy)
|
||||
-> Friend's browser
|
||||
```
|
||||
|
||||
OBS outputs RTMP to MediaMTX on localhost. MediaMTX spawns FFmpeg via
|
||||
`runOnReady` to transcode audio from AAC to Opus (required for WebRTC) and
|
||||
re-publish to the `game-opus` path. Viewers connect to `game-opus` via WHEP
|
||||
(WebRTC) or HLS.
|
||||
|
||||
Everything on the gaming PC (MediaMTX, HTTP server, Windows Firewall rule for
|
||||
the WebRTC UDP port) is spawned and torn down by an OBS Python script -
|
||||
`obs-script/game_stream.py`. You just click **Start Streaming** in OBS and the
|
||||
@@ -28,7 +36,7 @@ whole pipeline comes up; click **Stop Streaming** and it all goes away.
|
||||
|
||||
| Path | Purpose |
|
||||
|-------------------------------|-------------------------------------------------------------|
|
||||
| `config/mediamtx.yml` | MediaMTX configuration (WHIP in, WHEP/HLS out, locked-down) |
|
||||
| `config/mediamtx.yml` | MediaMTX configuration (RTMP in, WHEP/HLS out, FFmpeg transcode) |
|
||||
| `config/npm-advanced.conf` | Authentik forward-auth snippet for the NPM Advanced tab |
|
||||
| `obs-script/game_stream.py` | OBS script: lifecycle, HTTP server, firewall toggle |
|
||||
| `frontend/index.html` | Viewer page |
|
||||
@@ -36,27 +44,29 @@ whole pipeline comes up; click **Stop Streaming** and it all goes away.
|
||||
| `frontend/js/app.js` | Status polling and DOM glue |
|
||||
| `frontend/css/style.css` | Dark theme |
|
||||
| `scripts/install.ps1` | Downloads MediaMTX, creates the Windows Firewall rule |
|
||||
| `docs/authentik-setup.md` | Authentik proxy provider + group configuration |
|
||||
| `docs/authentik-setup.md` | Authentik proxy provider, group, and invite configuration |
|
||||
| `docs/npm-setup.md` | NPM proxy host + stream (UDP) configuration |
|
||||
| `docs/obs-setup.md` | OBS encoder + WHIP output settings |
|
||||
| `docs/obs-setup.md` | OBS encoder + RTMP output settings |
|
||||
|
||||
## Setup at a glance
|
||||
|
||||
1. **Clone** this repo onto the Windows gaming PC.
|
||||
2. **Install MediaMTX and the firewall rule:** open an elevated PowerShell in
|
||||
2. **Install FFmpeg:** `winget install Gyan.FFmpeg` in an elevated PowerShell,
|
||||
then open a new terminal to verify: `ffmpeg -version`.
|
||||
3. **Install MediaMTX and the firewall rule:** open an elevated PowerShell in
|
||||
the repo root and run `.\scripts\install.ps1`.
|
||||
3. **Configure Authentik** - see `docs/authentik-setup.md`.
|
||||
4. **Configure NPM** - see `docs/npm-setup.md`.
|
||||
5. **Configure OBS** - see `docs/obs-setup.md`, then add
|
||||
4. **Configure Authentik** - see `docs/authentik-setup.md`.
|
||||
5. **Configure NPM** - see `docs/npm-setup.md`.
|
||||
6. **Configure OBS** - see `docs/obs-setup.md`, then add
|
||||
`obs-script/game_stream.py` via Tools -> Scripts.
|
||||
6. **Click Start Streaming in OBS.** Friends can now open
|
||||
7. **Click Start Streaming in OBS.** Friends can now open
|
||||
`https://stream.hetherman.cloud`, log in with Authentik, and watch.
|
||||
|
||||
## Security posture
|
||||
|
||||
- TLS terminates at NPM with Let's Encrypt.
|
||||
- Every request is gated by Authentik forward auth before it reaches the
|
||||
frontend, WHEP signaling, HLS, or the MediaMTX API.
|
||||
frontend, WHEP signaling, or HLS.
|
||||
- MediaMTX only accepts publishers from `127.0.0.1` - nobody on the public
|
||||
internet can hijack the stream.
|
||||
- The UDP port used for WebRTC media is opened on the Windows Firewall only
|
||||
|
||||
+37
-3
@@ -11,8 +11,6 @@ have admin access.
|
||||
1. Authentik admin -> **Directory -> Groups -> Create**
|
||||
2. Name: `stream-viewers`
|
||||
3. Save.
|
||||
4. Add each friend's user account to this group. **Do not** add yourself /
|
||||
your admin account - your access comes from your existing admin group.
|
||||
|
||||
## 2. Create a Proxy Provider
|
||||
|
||||
@@ -68,7 +66,43 @@ curl -I https://auth.hetherman.cloud/outpost.goauthentik.io/ping
|
||||
|
||||
A 200 or 204 means the outpost is up.
|
||||
|
||||
## 6. Verify end-to-end
|
||||
## 6. Adding friends
|
||||
|
||||
You have two options:
|
||||
|
||||
### Option A: Create accounts manually (recommended for small groups)
|
||||
|
||||
1. **Directory -> Users -> Create**
|
||||
- Set username, name, email
|
||||
- Set a temporary password
|
||||
- Enable "User must change password on next login"
|
||||
2. **Directory -> Groups -> stream-viewers -> Users tab -> Add** the new user.
|
||||
3. Send your friend the URL (`https://stream.hetherman.cloud`) and their
|
||||
temporary credentials. They set their own password on first login.
|
||||
|
||||
### Option B: Self-service enrollment via invite link
|
||||
|
||||
This requires a working enrollment flow with an Invitation stage. The key
|
||||
requirement is that the **User Write stage** in the enrollment flow must have
|
||||
`stream-viewers` set in the **Groups** field - this is what auto-adds new
|
||||
users to the group. The invitation form itself has no group field.
|
||||
|
||||
1. **Flows & Stages -> Stages** - find or create a User Write stage and set
|
||||
**Groups** to `stream-viewers`.
|
||||
2. **Flows & Stages -> Flows** - create an enrollment flow with these stages
|
||||
in order: Invitation stage -> prompt (email/password) -> User Write -> Login.
|
||||
3. **Flows & Stages -> Invitations -> Create**:
|
||||
- **Flow**: your enrollment flow
|
||||
- **Single use**: on (one invite per person)
|
||||
- Set an expiry date
|
||||
4. Copy the invite link (shown after saving) and send it to your friend. They
|
||||
click it, fill in username/password, and are redirected to the stream.
|
||||
|
||||
> **Important:** if friends completed enrollment but were not added to
|
||||
> `stream-viewers` (because the User Write stage was not configured), add them
|
||||
> manually: **Directory -> Groups -> stream-viewers -> Users tab -> Add**.
|
||||
|
||||
## 7. Verify end-to-end
|
||||
|
||||
After finishing `docs/npm-setup.md`:
|
||||
|
||||
|
||||
+32
-24
@@ -8,7 +8,7 @@ Configures NPM to:
|
||||
**Stream** (L4 UDP proxy).
|
||||
|
||||
Replace `<PC-LAN-IP>` with the LAN IP of the Windows gaming PC
|
||||
(e.g., `192.168.50.10`).
|
||||
(e.g., `192.168.50.254`).
|
||||
|
||||
## 1. DNS
|
||||
|
||||
@@ -41,17 +41,12 @@ In NPM, **Hosts -> Proxy Hosts -> Add Proxy Host**.
|
||||
| Forward Port | `48080` |
|
||||
| Cache Assets | off |
|
||||
| Block Common Exploits | on |
|
||||
| Websockets Support | **on** (WebRTC signaling works without this, but it costs nothing) |
|
||||
| Websockets Support | **on** |
|
||||
|
||||
**Custom locations tab:** add three entries so WHEP, HLS, and the MediaMTX
|
||||
API are reverse-proxied to the right MediaMTX ports (and inherit the same
|
||||
forward-auth gating).
|
||||
|
||||
| Location | Scheme | Forward Hostname | Forward Port |
|
||||
|----------|--------|------------------|--------------|
|
||||
| `/whep` | `http` | `<PC-LAN-IP>` | `48889` |
|
||||
| `/hls` | `http` | `<PC-LAN-IP>` | `48888` |
|
||||
| `/v3` | `http` | `<PC-LAN-IP>` | `19997` |
|
||||
**Custom Locations tab:** leave this **empty**. Do not add any custom location
|
||||
entries here. The `/whep/`, `/hls/`, and `/v3/` locations are defined in the
|
||||
Advanced tab config below with trailing-slash path stripping. Adding them in
|
||||
the GUI creates duplicate nginx location blocks that cause routing failures.
|
||||
|
||||
**SSL tab:**
|
||||
|
||||
@@ -61,8 +56,12 @@ forward-auth gating).
|
||||
- HSTS Enabled: optional
|
||||
|
||||
**Advanced tab:** paste the entire contents of
|
||||
[`config/npm-advanced.conf`](../config/npm-advanced.conf). This installs the
|
||||
Authentik forward-auth subrequest and the sign-in redirect.
|
||||
[`config/npm-advanced.conf`](../config/npm-advanced.conf). This installs:
|
||||
- Authentik forward-auth subrequest on all requests
|
||||
- `/whep/` -> MediaMTX WHEP port 48889 (prefix stripped)
|
||||
- `/hls/` -> MediaMTX HLS port 48888 (prefix stripped)
|
||||
- `/v3/` -> MediaMTX API port 19997 (prefix stripped, auth bypassed)
|
||||
- `/outpost.goauthentik.io` -> Authentik internal outpost
|
||||
|
||||
Save the proxy host. Wait for the Let's Encrypt certificate to be issued.
|
||||
|
||||
@@ -88,14 +87,23 @@ on the gaming PC. This is the path WebRTC media takes after ICE negotiation.
|
||||
`auth.hetherman.cloud` to log in. Log in as a `stream-viewers` member -
|
||||
you should land back at the stream page (video container + "Stream
|
||||
offline" overlay, assuming you haven't started OBS yet).
|
||||
2. **Certificate:** the padlock icon should show the Let's Encrypt cert you
|
||||
requested.
|
||||
3. **/whep, /hls, /v3:** once you start streaming in OBS, open DevTools on
|
||||
the stream page and confirm requests to `/whep/game/whep`,
|
||||
`/hls/game/index.m3u8`, and `/v3/paths/get/game` all return 200 (and not
|
||||
401/302).
|
||||
4. **UDP stream:** with OBS streaming, tail the NPM container logs - you
|
||||
should see entries from the stream module for UDP connections on 48189.
|
||||
Alternatively, from the NPM host run
|
||||
`tcpdump -n -i any udp port 48189` and confirm packets flow while a
|
||||
viewer is connected.
|
||||
2. **Certificate:** the padlock icon should show the Let's Encrypt cert.
|
||||
3. **With OBS streaming**, open DevTools on the stream page and confirm:
|
||||
- `POST /whep/game-opus/whep` returns 201
|
||||
- `GET /hls/game-opus/index.m3u8` returns 200
|
||||
- `GET /status` returns 200 with `"ready": true`
|
||||
4. **UDP stream:** with OBS streaming and a viewer connected, run
|
||||
`tcpdump -n -i any udp port 48189` on the NPM host and confirm packets flow.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **500 or auth loop on stream.hetherman.cloud:** check the outpost `proxy_pass`
|
||||
in the Advanced config uses the internal Authentik address
|
||||
(`http://192.168.50.224:30140`) not the public HTTPS URL. Using the public
|
||||
URL causes SSL SNI issues when NPM tries to proxy to itself.
|
||||
- **WHEP returns 400:** the Custom Locations tab in the NPM GUI has `/whep`,
|
||||
`/hls`, or `/v3` entries. Remove them - these must only be in the Advanced
|
||||
tab config so the trailing-slash path stripping works correctly.
|
||||
- **WHEP returns 401 / browser shows a Basic Auth dialog:** MediaMTX is
|
||||
challenging the viewer directly. Check `authInternalUsers` in `mediamtx.yml`
|
||||
has a rule allowing `read` from any IP (empty `ips: []`).
|
||||
|
||||
+61
-41
@@ -1,21 +1,28 @@
|
||||
# OBS setup
|
||||
|
||||
Configures OBS Studio on the Windows gaming PC to capture the game, encode
|
||||
with NVENC, and publish via WHIP to the local MediaMTX instance that the OBS
|
||||
script spawns.
|
||||
with NVENC H.264, and publish via RTMP to the local MediaMTX instance that the
|
||||
OBS script spawns.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- OBS Studio 30.0 or newer (WHIP output is built in from 30.x onward).
|
||||
- OBS Studio 30.0 or newer.
|
||||
- FFmpeg installed and on PATH: `winget install Gyan.FFmpeg` (admin PowerShell).
|
||||
MediaMTX uses FFmpeg to transcode audio AAC->Opus for WebRTC compatibility.
|
||||
- You already ran `.\scripts\install.ps1` in an elevated PowerShell, so
|
||||
`bin\mediamtx.exe` exists and the `GameStream-UDP-48189` firewall rule is
|
||||
registered (in the disabled state).
|
||||
- **Run OBS as Administrator** so the script can toggle the Windows Firewall
|
||||
rule with netsh.
|
||||
|
||||
## 1. Load the OBS script
|
||||
|
||||
1. OBS -> **Tools -> Scripts -> +**
|
||||
2. Select `obs-script/game_stream.py` from this repo.
|
||||
3. In the properties panel on the right, set:
|
||||
1. OBS -> **Tools -> Scripts -> Python Settings** tab - point it at your Python
|
||||
3.11 installation (e.g. `C:\Users\<you>\AppData\Local\Programs\Python\Python311`).
|
||||
If OBS shows no properties after loading the script, this is the cause.
|
||||
2. OBS -> **Tools -> Scripts -> +**
|
||||
3. Select `obs-script/game_stream.py` from this repo.
|
||||
4. In the properties panel on the right, set:
|
||||
|
||||
| Setting | Value |
|
||||
|-----------------------|---------------------------------------------------------------|
|
||||
@@ -27,8 +34,13 @@ Prerequisites:
|
||||
| Public URL | `https://stream.hetherman.cloud` |
|
||||
| MediaMTX API URL | `http://127.0.0.1:19997` |
|
||||
|
||||
4. Check the **Script Log** at the bottom - you should see
|
||||
`[game_stream] game_stream.py loaded`.
|
||||
5. Check the **Script Log** at the bottom - you should see:
|
||||
- `[game_stream] MediaMTX started (pid=...)`
|
||||
- `[game_stream] Frontend HTTP server listening on 0.0.0.0:48080`
|
||||
- `[game_stream] game_stream.py loaded`
|
||||
|
||||
MediaMTX and the HTTP server start immediately on script load (not on
|
||||
streaming started), so they are ready before OBS attempts RTMP.
|
||||
|
||||
## 2. OBS output settings
|
||||
|
||||
@@ -38,32 +50,26 @@ Prerequisites:
|
||||
|
||||
| Setting | Value |
|
||||
|------------------|---------------------------------------------------------|
|
||||
| Audio Encoder | Opus (or FFmpeg AAC if Opus is unavailable - Opus is preferred for WebRTC) |
|
||||
| Video Encoder | **NVIDIA NVENC HEVC** or **NVIDIA NVENC H.264** |
|
||||
| Audio Encoder | FFmpeg AAC (only option; MediaMTX+FFmpeg transcodes to Opus for WebRTC) |
|
||||
| Video Encoder | **NVIDIA NVENC H.264** |
|
||||
|
||||
Use H.264 for maximum browser compatibility (all browsers). HEVC works in
|
||||
Safari and recent Chrome but not Firefox - stick with H.264 unless you have a
|
||||
specific reason.
|
||||
Safari and recent Chrome but not Firefox.
|
||||
|
||||
**Encoder settings (H.264):**
|
||||
|
||||
| Setting | Value |
|
||||
|---------------------|-------------------|
|
||||
| Rate Control | CBR |
|
||||
| Bitrate | 8000 Kbps |
|
||||
| Bitrate | 8000-16000 Kbps |
|
||||
| Keyframe Interval | 2 s |
|
||||
| Preset | P5 (Quality) |
|
||||
| Tuning | Ultra Low Latency |
|
||||
| Multipass | Two Passes (Quarter Resolution) |
|
||||
| Profile | high |
|
||||
| Look-ahead | off |
|
||||
| Psycho Visual Tuning | on |
|
||||
| GPU | 0 |
|
||||
| Max B-frames | **0** (required for low-latency WebRTC) |
|
||||
|
||||
With a 600 Mbps upload and up to 6 viewers at 8 Mbps each, 8000 Kbps leaves
|
||||
generous headroom. Push to 12000-15000 Kbps if you want higher quality.
|
||||
|
||||
### Audio tab
|
||||
|
||||
| Setting | Value |
|
||||
@@ -78,9 +84,11 @@ generous headroom. Push to 12000-15000 Kbps if you want higher quality.
|
||||
| Setting | Value |
|
||||
|----------|-----------------------------------------------|
|
||||
| Service | Custom |
|
||||
| Protocol | **WHIP** |
|
||||
| Server | `http://localhost:48889/game/whip` |
|
||||
| Bearer Token | (leave blank) |
|
||||
| Server | `rtmp://127.0.0.1/game` |
|
||||
|
||||
> **Note:** Use RTMP (not WHIP). WHIP uses WebRTC which requires ICE/UDP
|
||||
> negotiation between OBS and MediaMTX - this fails over localhost due to NAT
|
||||
> hairpin issues. RTMP is plain TCP and works reliably on localhost.
|
||||
|
||||
Save.
|
||||
|
||||
@@ -88,21 +96,25 @@ Save.
|
||||
|
||||
1. Click **Start Streaming**.
|
||||
2. Check the OBS Script Log - you should see:
|
||||
- `Firewall rule 'GameStream-UDP-48189' ENABLED`
|
||||
- `MediaMTX started (pid=...)`
|
||||
- `Frontend HTTP server listening on 0.0.0.0:48080`
|
||||
- `Viewers can watch at: https://stream.hetherman.cloud`
|
||||
3. Open `https://stream.hetherman.cloud` from another device, log in with
|
||||
Authentik, and verify video plays.
|
||||
- `[game_stream] Firewall rule 'GameStream-UDP-48189' ENABLED`
|
||||
- `[game_stream] OBS streaming started -> viewers can watch at: https://stream.hetherman.cloud`
|
||||
3. After 2-3 seconds, MediaMTX spawns FFmpeg to transcode the audio. The
|
||||
`game-opus` path becomes ready. The stream page will show **LIVE**.
|
||||
4. Open `https://stream.hetherman.cloud` from another device, log in with
|
||||
Authentik, and verify video and audio play. Click the **"Click to play"**
|
||||
or **"Click to unmute"** button in the status bar if audio is muted.
|
||||
|
||||
## 5. Stopping
|
||||
|
||||
Click **Stop Streaming** in OBS. The script will:
|
||||
|
||||
- Stop the MediaMTX subprocess
|
||||
- Stop the frontend HTTP server
|
||||
- Disable the firewall rule (`GameStream-UDP-48189` -> disabled)
|
||||
|
||||
When OBS exits, the script also:
|
||||
|
||||
- Stops the MediaMTX subprocess (and with it, the FFmpeg transcoder)
|
||||
- Stops the frontend HTTP server
|
||||
|
||||
Verify the firewall state from PowerShell:
|
||||
|
||||
```powershell
|
||||
@@ -113,20 +125,28 @@ Should report `False` while not streaming, `True` while streaming.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No properties panel after loading script:** OBS is using the Microsoft
|
||||
Store Python stub. Install real Python 3.11 via `winget install Python.Python.3.11`
|
||||
and point OBS Python Settings to the install directory.
|
||||
- **"MediaMTX binary not found"** in the script log: the path in the script
|
||||
properties panel is wrong. Re-select it with the file picker.
|
||||
- **OBS cannot connect to WHIP**: MediaMTX did not start. Check the script
|
||||
log for the actual reason; most commonly a port conflict on 48889 or 48189
|
||||
(another process is already using them).
|
||||
- **OBS cannot connect / streaming fails immediately:** MediaMTX did not start.
|
||||
Check the script log for errors. Most commonly a port conflict on 1935, 48889,
|
||||
or 48189 (another process is already using them). Also check `bin\mediamtx.log`.
|
||||
- **Stream page shows "Connecting..." but never goes LIVE:** FFmpeg failed to
|
||||
start the `game-opus` transcoder. Check `bin\mediamtx.log` for FFmpeg errors.
|
||||
Verify `ffmpeg -version` works in a new PowerShell (not the same session as
|
||||
before installing FFmpeg).
|
||||
- **Viewers see "Stream offline"** even after you click Start Streaming:
|
||||
- Check that the MediaMTX API returns `ready: true`:
|
||||
`curl http://localhost:19997/v3/paths/get/game`
|
||||
- Check OBS's own streaming indicator - if it's red, OBS is not actually
|
||||
sending to WHIP. Verify the URL and that the custom service / WHIP
|
||||
protocol is selected.
|
||||
- Check that the MediaMTX API returns `ready: true` for the transcoded path:
|
||||
`curl http://localhost:19997/v3/paths/get/game-opus`
|
||||
- Check OBS's own streaming indicator - if it's red, OBS is not connected.
|
||||
Verify the RTMP server URL is `rtmp://127.0.0.1/game`.
|
||||
- **Viewers connect but playback freezes after a few seconds:** the UDP port
|
||||
path is broken. Verify the firewall rule is enabled (`Get-NetFirewallRule`),
|
||||
the router port-forward to NPM for UDP 48189 is correct, and the NPM Stream
|
||||
entry points at `<PC-LAN-IP>:48189`.
|
||||
- **Autoplay is blocked / no audio:** browsers start the video muted so
|
||||
autoplay works. There is a "Click to unmute" button in the status bar.
|
||||
the router port-forward for UDP 48189 goes to NPM, and the NPM Stream entry
|
||||
points at `<PC-LAN-IP>:48189`.
|
||||
- **No audio:** the "Click to play" or "Click to unmute" button appears in the
|
||||
status bar when the browser blocks autoplay. Click it once. If audio is still
|
||||
absent, check `bin\mediamtx.log` to confirm FFmpeg started successfully and
|
||||
the `game-opus` path shows both H264 and Opus tracks.
|
||||
|
||||
Reference in New Issue
Block a user