Lumi/docs/stream-testing.md

7.2 KiB

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.

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 through ACME HTTP-01. The temporary /.well-known/acme-challenge/ response is public, narrowly scoped, and available before WebUI authentication; all certificate keys stay under Lumi's ignored data directory. The reverse proxy must forward that challenge path to Lumi. 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:

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:

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:

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:

npm run verify:stream-testing
dotnet run --project companion/tests/Lumi.Companion.Core.Tests/Lumi.Companion.Core.Tests.csproj