Lumi/docs/stream-testing.md
2026-07-26 17:56:38 +02:00

156 lines
7.5 KiB
Markdown

# Private stream testing
Lumi Stream Testing previews the real OBS output without publishing it to the
normal stream destination. Start it from the core **Stream testing** page in
Lumi Companion, then watch it at **Admin > Stream testing**.
## Architecture
- The existing paired-device WebSocket authenticates session creation, OBS
metrics, caption cues, stop requests, and server-ended notifications.
- The existing same-user OBS Bridge snapshots the complete current OBS service,
temporarily installs an expiring custom service, reports real output facts,
and restores the snapshot.
- Companion protects the minimum recovery record with Windows DPAPI and writes
it atomically before OBS is redirected. Normal stop, server failure, expiry,
Companion exit, WebSocket loss, and OBS restart all use that restore path.
- Core lazily starts a Lumi-managed MediaMTX process. MediaMTX accepts the
private RTMP(S) publisher and repackages the original source into low-latency
HLS without transcoding, scaling, a GPU requirement, or an adaptive ladder.
- The MediaMTX API, metrics, and HLS listeners bind only to loopback. The Admin
WebUI accesses HLS through an authenticated, no-store, same-origin streaming
proxy. RTSP, WebRTC, SRT, playback, pprof, and recording are disabled.
- Companion explicitly starts a server-hosted `stream_test` transcription
session before redirecting OBS. It reuses the selected source and model while
keeping normal live-caption delivery disabled. Interim revisions progressively
reveal each caption in the private player; a finalized caption remains for a
length-aware readability window, then ends when stale or when the next
utterance replaces it. The player's Captions control toggles a wrapping,
multiline Lumi caption layer immediately as revisions arrive instead of
delaying display against the HLS media timeline. No second transcription
pipeline and no local speech inference are added.
Media segments stay in bounded MediaMTX memory and roll out of the short live
window. Lumi does not retain a raw stream recording or create temporary HLS
files. Session state, captions, and diagnostic facts disappear when the test
ends or Lumi restarts.
## Managed setup
No operating-system package installation, FFmpeg installation, or executable
path is required. Open **Admin > Stream testing** and choose **Install
MediaMTX**. Lumi then:
1. downloads the pinned official Windows x64 archive only after confirmation;
2. enforces download and extraction size limits;
3. verifies the exact SHA-256 checksum and archive layout;
4. probes the executable and exact expected version; and
5. atomically installs it while retaining the previous installation until the
replacement is known to be valid.
**Repair installation** repeats the same verified workflow. **Run health
check** starts MediaMTX with deny-by-default paths and tests its loopback API,
metrics, and HLS listeners; opening the page alone never downloads or starts a
runtime. `LUMI_MEDIAMTX_PATH` remains an advanced manual override for another
platform or operator-managed binary, but normal Windows installations do not
need it.
The managed release and checksum source are recorded in
`src/services/mediamtx-runtime-manifest.json`. MediaMTX's licence is retained in
the downloaded runtime and documented in
[`third-party/MediaMTX-MIT.txt`](third-party/MediaMTX-MIT.txt).
## Ingest network configuration
Lumi derives the network policy from the authenticated Companion pairing
origin. A Companion paired through `localhost`, `127.0.0.1`, or `::1` always
receives loopback RTMP; production ingest overrides are deliberately ignored
for that local development session. A non-local Companion must have paired
through HTTPS and always receives RTMPS. `LUMI_STREAM_TEST_INGEST_HOST` may
override the advertised hostname only for those non-local sessions.
For the normal production path, Lumi automatically provisions and renews a
publicly trusted certificate for the paired hostname. It first supports ACME
HTTP-01 through the public, narrowly scoped
`/.well-known/acme-challenge/` route. When a reverse proxy owns that reserved
path, configure DNS automation in **Admin > Stream testing** instead. Lumi can
use encrypted Domeneshop credentials to create the short-lived DNS-01 TXT
record, wait for authoritative propagation, issue or renew the certificate,
and remove the record. No reverse-proxy changes, certificate paths, or external
ACME packages are required. All certificate keys stay under Lumi's ignored
data directory. The first production test may take up to two minutes while the
certificate is issued; later tests reuse it.
Every session receives an exact `lumi-test/<uuid>` path and high-entropy
publisher credentials. Only that path can be published, and the generated
MediaMTX configuration contains SHA-256 credential hashes rather than
plaintext credentials.
RTMPS is required for every non-local pairing. A stale
`LUMI_STREAM_TEST_TRANSPORT=rtmp` value is ignored for production rather than
weakening transport or preventing the test from starting. Normal installations
need no certificate environment variables:
```text
LUMI_STREAM_TEST_INGEST_PORT=19350
LUMI_STREAM_TEST_PUBLIC_PORT=19350
```
The public port is the value given to OBS and can differ when a firewall or
port-forward maps it to the MediaMTX listener. Use the existing paired Lumi
hostname; no extra Stream Testing domain is required.
Operators that already manage a matching certificate can override Lumi's
managed certificate by setting both paths:
```text
LUMI_STREAM_TEST_TLS_CERT=/absolute/path/to/fullchain.pem
LUMI_STREAM_TEST_TLS_KEY=/absolute/path/to/private-key.pem
```
Unencrypted RTMP is selected automatically only for an authenticated loopback
pairing. It cannot be enabled for a non-local pairing through an environment
override. Restrict the ingest port at the host firewall in either mode.
Optional session limits remain:
```text
LUMI_STREAM_TEST_MAX_MS=1800000
LUMI_STREAM_TEST_INACTIVITY_MS=45000
```
## Safety and recovery
Companion refuses to start while OBS is streaming or recording. During a test
its navigation status reads **PRIVATE TEST ACTIVE**. The DPAPI recovery file is
removed only after OBS confirms that the saved service type and complete
settings JSON were restored and saved.
If OBS was closed or unavailable during cleanup, open OBS and leave Companion
running. Automatic restoration resumes when the same-user bridge reconnects.
The **Repair restoration** action retries the same operation and does not create
a new session.
The receiver stops on maximum duration, lack of inbound media, process failure,
server shutdown, Companion disconnect, or administrator stop. OBS status
messages do not reset the media-inactivity clock. Only one session is allowed.
MediaMTX is reconfigured to a deny-by-default path set after every session.
## Diagnostics and verification
The Admin page reports real OBS bitrate, dropped frames, congestion, MediaMTX
path readiness, media tracks, inbound/outbound bytes, HLS viewers, discarded
frames, player latency, buffer health, stalls, errors, and caption delay.
Only an allowlist of MediaMTX API and metrics fields is exposed. Publisher
credentials are redacted from status, timeline entries, process logs, and
errors.
The runtime health check replaces the former transcoded synthetic test pattern.
Focused verification uses a fake MediaMTX process with real loopback API,
metrics, and HLS listeners:
```text
npm run verify:stream-testing
dotnet run --project companion/tests/Lumi.Companion.Core.Tests/Lumi.Companion.Core.Tests.csproj
```