fix(host): native sessions on the console + GPU-aware codecs + gamestream capability
The web console Dashboard read AppState.{streaming,launch,stream}, which only the
GameStream path writes, so a native punktfunk/1 session (the DEFAULT plane) showed
"Idle / no session" while actively streaming — only the Stats page (shared recorder)
reflected it. Add a plane-neutral per-session registry (session_status.rs) the native
video loop publishes to; /status now merges both planes, reports active_sessions, and
the Stop / Request-IDR buttons reach native sessions too (so surfacing them doesn't
leave dead buttons). LocalSummary (tray) gets the same fix.
Also on the management API:
- /host codecs derive from Codec::host_wire_caps() instead of a hardcoded
[H264,H265,AV1], so codecs the GPU can't encode no longer appear.
- ApiCodec serializes HEVC as "hevc" (matching the wire/SDP/stats label) so the same
codec reads identically across console pages.
- HostInfo.gamestream reports whether the GameStream planes run (--gamestream), so the
console can hide the Moonlight-only pairing UI on the native-only default host.
- StatsStatus.elapsed_ms (host-monotonic) so the capture timer doesn't mix host/browser
clocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3584,7 +3584,7 @@ fn pack_mode(width: u32, height: u32, refresh_hz: u32) -> u64 {
|
||||
}
|
||||
|
||||
/// Unpack a [`pack_mode`] word back into `(width, height, refresh_hz)`.
|
||||
fn unpack_mode(packed: u64) -> (u32, u32, u32) {
|
||||
pub(crate) fn unpack_mode(packed: u64) -> (u32, u32, u32) {
|
||||
(
|
||||
((packed >> 32) & 0xffff) as u32,
|
||||
((packed >> 16) & 0xffff) as u32,
|
||||
@@ -4210,6 +4210,11 @@ fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
mode.height,
|
||||
interval_hz(interval),
|
||||
)));
|
||||
// One-shot force-keyframe flag driven by the management API (`POST /session/idr`, the web-console
|
||||
// Dashboard's "Request IDR" button) — drained in the encode loop below exactly like a client
|
||||
// decode-recovery request. Registered with `session_status` so the mgmt handler can reach THIS
|
||||
// session (the native plane never touches the GameStream `AppState.force_idr`).
|
||||
let force_idr = Arc::new(AtomicBool::new(false));
|
||||
// The send thread emits the web-console stats sample (it owns `session.stats()`); clone the
|
||||
// recorder so the capture loop keeps its own handle for the per-frame `is_armed()` gate.
|
||||
let send_stats = SendStats {
|
||||
@@ -4241,6 +4246,18 @@ fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
})
|
||||
.context("spawn send thread")?;
|
||||
|
||||
// Publish this session to the plane-neutral live-session registry so the web-console Dashboard
|
||||
// (`GET /status`) shows the native stream — resolution/fps/codec/bitrate resolve live from the
|
||||
// same handles a mid-stream mode switch / adaptive-bitrate change updates. The guard clears the
|
||||
// entry when this loop exits (return / `?` / panic), so the Dashboard tracks the session's life.
|
||||
let _live_session = crate::session_status::register(
|
||||
live_mode.clone(),
|
||||
live_bitrate.clone(),
|
||||
plan.codec,
|
||||
stop.clone(),
|
||||
force_idr.clone(),
|
||||
);
|
||||
|
||||
// Mid-stream session-switch watcher (opt-in via PUNKTFUNK_SESSION_WATCH; never under an explicit
|
||||
// PUNKTFUNK_COMPOSITOR pin). It self-baselines and signals the loop below to swap the backend in
|
||||
// place when the box flips Gaming↔Desktop. When not spawned, session_rx just stays empty.
|
||||
@@ -4574,6 +4591,11 @@ fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
while keyframe.try_recv().is_ok() {
|
||||
want_kf = true;
|
||||
}
|
||||
// Management API `POST /session/idr` (web-console Dashboard) targets this session's registry
|
||||
// flag; drain it into the same forced-keyframe path a client decode-recovery request takes.
|
||||
if force_idr.swap(false, Ordering::Relaxed) {
|
||||
want_kf = true;
|
||||
}
|
||||
// Client LTR-RFI recovery: prefer re-referencing a known-good older frame (a clean recovery
|
||||
// P-frame — no 20-40× IDR spike) over a full keyframe when the encoder supports it (native
|
||||
// AMF LTR / Windows NVENC). Drain the backlog (the client re-requests until the recovery
|
||||
|
||||
Reference in New Issue
Block a user