Skip to content
Download

AdvancedPage 14 of 14

Native Streamer Architecture

Internals of the in-process NVST streamer, environment overrides, and what research shows about NVIDIA's official client

GitHubOpen in GitHub9 sections
On this page9 sections

OpenNOW's desktop client streams through an in-process native NVST runtime loaded by Qt. This is not an experimental Electron child process, and there is no Chromium/WebRTC fallback. This page covers internals. For backends, settings, and diagnostics as a user sees them, use Native Streamer. Report streaming issues on GitHub Issues or Discord with a diagnostics export when possible.

Shipped design#

text
GFN CloudMatch session ── Rust core ── session context ── Qt shell
                                              │
                                              └─ opennow-streamer-ffi (in-process)
                                                       │
                                                       ├─ RTSPS signaling (RTSP over secure WebSocket)
                                                       ├─ Mjolnir SRTP video socket
                                                       ├─ ICE/DTLS/SCTP bundle: audio, mic, RTCP, input
                                                       └─ platform decode
                                                              │
                                                              └─ GPU texture → Qt scene graph

The Rust core owns authentication and CloudMatch session lifecycle. Qt owns windows, overlays, and presentation. The embedded streamer owns NVST sockets, media, input, recording, and GPU frame publication. Qt does not create a second video window or spawn a streamer child process.

Responsibilities#

ComponentOwns
Rust coreCloudMatch, settings, catalog, updates, diagnostics export helpers
Qt shellUI, overlays, QRhi device lending, desktop/console layouts
Native streamer FFINVST lifecycle, decode/audio/input/recording, GPU frame tokens

The streamer workspace splits into opennow-streamer-protocol (local command DTOs), opennow-streamer-core (NVST lifecycle and recording), opennow-streamer-transport (Mjolnir SRTP, ICE/DTLS/SCTP, RTCP, input), opennow-streamer-platform plus per-OS crates (decode, audio, GPU frames), and opennow-streamer-ffi (the C ABI Qt loads).

Session and transport#

  1. CloudMatch allocates a seat. The request marks WebSocket signaling (wssignaling=1).
  2. The streamer carries RTSP over a secure WebSocket: OPTIONS, DESCRIBE, SETUP, ANNOUNCE, PLAY, keepalive, and TEARDOWN. ANNOUNCE sends the client's stream settings as x-nv-* SDP attributes.
  3. Video arrives on a dedicated Mjolnir SRTP UDP socket. Audio, microphone, RTCP, and input share one ICE/DTLS/SCTP bundle socket.
  4. Lost video packets are requested with Mjolnir NACK-v2 (control command 0x317) on control_channel_partially_reliable. Reed-Solomon FEC repairs what it can first. If a frame still cannot be decoded, the streamer requests an IDR frame (control command 0x302).

The bundle opens these SCTP data channels:

ChannelReliabilityTraffic
control_channel_reliableordered, reliable (SCTP stream 0)Input handshake, keyboard and mouse-button input, Unicode text, control commands, server cursor updates
custom_message_on_sctp_private_reliableordered, reliable (stream 2)Opened with the bundle; OpenNOW sends nothing on it
custom_message_on_sctp_private_partially_reliableordered, at most 2 retransmits (stream 4)Opened with the bundle; OpenNOW sends nothing on it
control_channel_partially_reliableunordered, 300 ms lifetime (stream 6)Mouse motion and partially reliable control, including Mjolnir NACK-v2 requests
control_channel_unreliableunordered, no retransmits (stream 8)Opened with the bundle; OpenNOW sends nothing on it
input_channel_partially_reliableunordered, 300 ms lifetime (stream 10)Gamepad state
cursor_channelordered, reliable (stream 12, when negotiated)Cursor notifications, when a host sends them here instead of on the reliable control channel
rtcp_on_sctp_privateordered, reliable (stream 14, when negotiated)RTCP feedback when video runs on the bundle instead of the Mjolnir socket

Platform decode/render selection#

PlatformPreferred paths
WindowsHardware decode through a Media Foundation transform that has a matching DXVA profile on the GPU Qt renders with. Frames stay on the GPU as D3D11 textures. The picker lists DirectX 12 and Vulkan, but the stream view does not support them. There is no software decode path.
macOS (Apple Silicon)VideoToolbox decode with hardware required, then IOSurface-to-Metal conversion inside the Qt scene graph. HEVC 10-bit, HDR, and 10-bit 4:4:4 are offered only when runtime probes pass. There is no software decode path.
LinuxAuto uses Vulkan Video on the shared Vulkan device when it supports the negotiated profile, otherwise a CUDA, VA-API, or V4L2 decoder that does. Presentation always uses Vulkan. Auto never switches to software: choose Software (CPU) for bundled FFmpeg decode, which is limited to 8-bit 4:2:0 SDR.

nativeVideoBackend defaults to auto. Unsupported forced backends fail explicitly rather than silently selecting a different API. The core checks the codec, bit depth, and chroma against the streamer's capability report before CloudMatch allocates a seat, so most unsupported profiles fail before a session starts. On Linux, the Video backend setting is passed per session; it does not change process environment variables.

Environment overrides#

VariablePurpose
OPENNOW_DATA_DIRReplaces the data directory for settings, accounts, and diagnostics. Default: %APPDATA%\OpenNOW on Windows, ~/Library/Application Support/OpenNOW on macOS, $XDG_CONFIG_HOME/OpenNOW or ~/.config/OpenNOW on Linux.
OPENNOW_PICTURES_DIRReplaces the Pictures root. Screenshots and recordings go to OpenNOW/Screenshots and OpenNOW/Recordings under it. Set but empty disables both folders.
OPENNOW_NATIVE_VIDEO_BACKENDProcess-wide decoder policy: auto, hardware, software, or one backend (d3d11, videotoolbox, vulkan, vaapi, cuda or nvdec, v4l2, ffmpeg). Other backends report as disabled. On Linux it applies when the Video backend setting is Auto.
OPENNOW_VK_DEVICE_INDEXPrefers the Vulkan physical device with this index when the streamer creates the shared Vulkan decode device.
OPENNOW_VK_VENDOR_IDPrefers a Vulkan device by PCI vendor ID, decimal or 0x hex (for example 0x10de). The same scoring also honors __NV_PRIME_RENDER_OFFLOAD=1 and DRI_PRIME=1.
LIBVA_DRIVERS_PATHWhen unset, the AppRun hook searches /usr/lib/dri, /usr/lib64/dri, and the matching Debian multiarch directory for VA-API drivers. An explicit value, including an empty one, is kept.
OPENNOW_NVST_TRACE1, true, yes, or on prints verbose NVST transport lines (sampled datagram counters, input traces) to standard error. Start the app from a terminal to see them; they are not written to native-streamer.log.
OPENNOW_NATIVE_WINDOW_SYSTEMForces x11 or wayland for the standalone opennow-streamer host on Linux. The Qt app does not use it.
OPENNOW_LINUX_NATIVE_WAYLANDLets the standalone host use native Wayland. Without it, the standalone SDL window uses X11/XWayland when DISPLAY is set.
OPENNOW_STREAMER_TRACEEnables stderr trace output in the standalone opennow-streamer host.
OPENNOW_STREAMER_LOG_FILEWrites the standalone host log to this file.
OPENNOW_MEDIACODEC_SOCKET(Nightly) Set by a DroidDeck session that has the MediaCodec bridge enabled. Used with OPENNOW_NATIVE_VIDEO_BACKEND=mediacodec. Do not set it by hand.

The app also sets variables for its own use. Do not set them by hand: OPENNOW_APP_EXECUTABLE, OPENNOW_APP_PID, OPENNOW_UPDATE_PLAN, and OPENNOW_UPDATE_NONCE pass state to the updater, and the OPENNOW_NATIVE_* input and renderer variables configure the legacy standalone streamer process.

Native path changes since v1.0.2#

WebRTC compatibility mode. The core can allocate a session that uses a native WebRTC transport (the str0m library) instead of NVST. The streamer then runs the signaling WebSocket, SDP offer/answer, and ICE itself, and passes compressed frames to the same decoders. See Protocol notes.

  • Reference-frame invalidation. After unrecoverable loss, the streamer sends the lost frame range as control command 0x301, withholds dependent frames, and resumes on the next I, intra-refresh, or reference-picture-invalidation frame. An IDR request is the bounded backstop.
  • Launch fallback. Unsupported codec, chroma, or HDR requests degrade before a seat is allocated: missing HDR output clears HDR, software decode becomes 8-bit 4:2:0 SDR, 4:4:4 selects HEVC when it can decode it and otherwise drops to 4:2:0, and undecodable 10-bit SDR drops to 8-bit. Each step is logged as a stream_profile_fallback diagnostics event.
  • Audio. NVST audio RTP is reordered by timestamp in a jitter buffer that starts at 45 ms and grows by 20 ms per late-arrival burst, up to 200 ms. The microphone sends plain Opus on the bundle's fixed payload type 111 and SSRC 1, and does not send redundant audio.
  • Server timers. Server idle-warning and termination control messages are parsed.

DroidDeck MediaCodec decode (Linux, opt-in). With OPENNOW_NATIVE_VIDEO_BACKEND=mediacodec inside a DroidDeck session that has the bridge enabled, H.264 decodes on Android's hardware decoder. Decoded NV12 pixels are copied into the Vulkan uploader; this path is not zero-copy.

AWDL suppression (macOS, opt-in). An optional root LaunchDaemon keeps awdl0 down while streaming. It is off by default, needs administrator approval to install, and can interrupt AirDrop, AirPlay, Sidecar, and Universal Control while active.

Observed in NVIDIA's official client#

Research notes, not OpenNOW behavior

This section describes NVIDIA's official GeForce NOW client, not OpenNOW. It comes from static analysis of the Windows client 2.0.89.141 and the macOS client 2.0.87.131 (x86_64 code only), plus logs from one user's Windows sessions on NVIDIA-operated seats. No official binary was run for this research and nothing was measured. "Code" means a decompiled code path that can run, "strings" means a symbol or string in the binary, and "log" means the official client's own log showed it. Anything not listed here is unknown.

Processes

  • The streaming engine runs inside the official app process. Two native libraries do the work: Geronimo (decode, render, audio, input) and Bifrost (CloudMatch, signaling, transport). (Windows: strings and log. macOS: strings.)
  • The separate GeForceNOWStreamer executable on Windows relaunches the main app and loads no streaming code. (Code and strings.)

Session and signaling

  • Signaling is RTSP carried over a secure WebSocket: OPTIONS, DESCRIBE, a video-only SETUP, ANNOUNCE, then PLAY. Responses carry header version 14.2. (Windows log; macOS code.)
  • The codec is not part of the CloudMatch request. The client picks it locally after the session is allocated. (Windows log.)
  • ANNOUNCE writes a setting only when its value differs from the server's DESCRIBE offer. (Code.)
  • When several RTSP endpoints are listed, the client moves to the next one only on one specific error code. (Code.)

Transport and recovery

  • The logged Windows session used a dedicated SRTP video socket plus a DTLS bundle socket for audio, microphone, and six SCTP channels. The code also has a unified single-socket mode and an RTC media mode. (Log and code.)
  • DTLS accepts the peer certificate only if its fingerprint matches the one in the SDP, then exports SRTP keys. (Code.)
  • Lost video packets are requested with NACK-v2, control command 0x317, up to 64 records per command. (Code.)
  • Reference-frame invalidation is control command 0x301; an IDR request is 0x302. A pending request is resent after 100 ms until a recovery frame arrives. (Code.)
  • The NACK feature is switched off and back on during high round-trip times. (Windows log.)

Video and color

  • Automatic codec choice prefers AV1, then HEVC, then H.264. H.264 is capped at 120 FPS. An explicitly chosen codec that cannot be decoded returns an error instead of switching codec. (Code; the cap also appears in logs.)
  • A 4:4:4 request selects HEVC when HEVC 4:4:4 decodes; otherwise chroma drops to 4:2:0. One logged Windows session asked for H.264 10-bit 4:4:4 and streamed HEVC 10-bit 4:4:4 SDR. (Code and log.)
  • 10-bit SDR also needs a server-side remote-config switch; without it the client uses 8-bit BT.709. (Code.)
  • Windows decodes with D3D11 video decoding (DXVA profiles) and presents through a flip-model swap chain that switches from 8-bit to 10-bit after the first 10-bit frame. (Code and log.)
  • macOS decodes with VideoToolbox and presents with Metal. (Code.)
  • Windows HDR output is PQ BT.2020 in a 10-bit swap chain. No scRGB output path was found. (Code.)

Audio and input

  • Game audio is Opus, 48 kHz, 5 ms frames, with RFC 2198 redundancy level 2. It passes through an adaptive jitter buffer that started at 80 ms, could reach 200 ms, and lowered its floor to 45 ms. (Windows log.)
  • Playback supports 2, 6, and 8 channels. (Code and log.)
  • Microphone capture uses a bundled WebRTC audio module with echo cancellation against the game audio. Noise suppression and gain control were off in the logged session. (Code and log.)
  • The SCTP input channels used stream IDs 0, 2, 4, 6, 8, and 10, and the partially reliable input threshold was 300 ms. (Log.)
  • Clipboard paste uses an NVST custom-message exchange with a 64 KiB limit. (Code and log.)

Where OpenNOW v1.0.3 differs

AreaOfficial clientOpenNOW v1.0.3
Loss recoveryReference-frame invalidation and IDRReference-frame invalidation and IDR
Audio jitterAdaptive timestamp jitter bufferTimestamp jitter buffer, 45 to 200 ms
Audio channelsUp to 8Stereo
MicrophoneEcho cancellationNo echo cancellation
ClipboardCustom-message paste protocolText submitted through the streamer as input
4:4:4 in ANNOUNCEChroma value 1Chroma value 3 (chroma_format_idc) in v1.0.3; dev sends the official 4:4:4 flag (0 for 4:2:0, 1 for 4:4:4)
PLAY responseOnly 200 starts the stream200 or 455
Idle warningParsed from server timer messagesParsed

Still unknown

  • Whether servers accept every codec, bit depth, chroma, and HDR combination, and whether 10-bit sessions start now that the chroma announcement uses the official flag.
  • How alliance-partner seats differ from NVIDIA-operated seats.
  • HDR at runtime: none of the logged sessions used HDR.
  • The FEC algorithm details, the frame capture-time units, and which frame types the server sends in response to invalidation.
  • Whether the official Windows client sent any invalidation or IDR request in the logged sessions.
  • Which nibble of the frame-header byte holds the frame type. The research notes disagree.

Diagnostics#

Native and Qt logs are collected through Settings → About & support → Copy diagnostics → Export. Distinguish an entirely black application window from a stream-only black video surface when filing reports. For transport debugging, start the app from a terminal with OPENNOW_NVST_TRACE=1; the extra lines go to standard error, not to native-streamer.log.

Known limitations#

  • The Qt app is still under active development; platform-specific streaming and GPU issues are expected.
  • Publishing a build does not mean every acceptance checklist item has passed.
  • Nightly packages may be unsigned/not notarized.
  • Separate Android, iOS, Switch, and OpenNOW-Mac projects are not this Qt desktop path.

For the full reference, use Native Streamer. Electron-era WebRTC notes and the WebRTC compatibility mode are on Protocol notes.