7.0 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_testtranscription 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:
- downloads the pinned official Windows x64 archive only after confirmation;
- enforces download and extraction size limits;
- verifies the exact SHA-256 checksum and archive layout;
- probes the executable and exact expected version; and
- 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 direct loopback RTMP on the configured local listener port. A
non-local Companion must have paired through HTTPS and always receives RTMPS
at the same paired Lumi hostname.
In production, Nginx Proxy Manager owns the public certificate and terminates TLS:
OBS → rtmps://<paired Lumi hostname>:1936
→ Nginx Proxy Manager decrypts TLS
→ plain RTMP to Lumi:19350
MediaMTX binds plain RTMP on 0.0.0.0:19350 for a production session. It never
requests, loads, renews, or stores a TLS certificate. Configure an NPM TCP
stream that listens on public port 1936 with TLS enabled and forwards to the
Lumi host on port 19350. The WebUI reverse-proxy settings can change both
ports; values are stored in Lumi's settings database and apply to the next
session.
The collapsed Reverse-proxy ingest section in Admin > Stream testing also provides Check external access. The check connects to the current public Lumi hostname and port with TLS and SNI, validates the public certificate, then completes an RTMP handshake through NPM to MediaMTX. A server-side failure can still mean that the router does not support NAT loopback/hairpinning, so confirm the path from an external network when the NPM and firewall configuration otherwise look correct.
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. Unencrypted RTMP is advertised only to an authenticated loopback pairing. Use the existing paired Lumi hostname; no extra Stream Testing domain is required. Restrict direct access to the local MediaMTX listener with the host firewall so production publishers must enter through NPM.
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