chore: consolidate parallel-session WIP (HOLD — do not push)

Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:

  - logging rework (~43 files: level re-tiering, structured fields, `?e`,
    hot-path flood latches)
  - conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
    in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
  - standby-sink DWM-stall attribution (windows/display_events.rs + capture/
    vdisplay wiring)

NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 12:42:53 +02:00
parent d466e3e2b2
commit 11045a0f70
62 changed files with 1595 additions and 214 deletions
@@ -262,7 +262,7 @@ fn run(
punktfunk_core::transport::grow_socket_buffers(&sock);
// The client pings the audio port (~every 500ms) so we learn where to send.
sock.set_read_timeout(Some(Duration::from_secs(10)))?;
tracing::info!(port = AUDIO_PORT, "audio: awaiting client ping");
tracing::debug!(port = AUDIO_PORT, "audio: awaiting client ping");
let mut probe = [0u8; 256];
let (_, client) = sock
.recv_from(&mut probe)
@@ -275,7 +275,7 @@ fn run(
&sock,
punktfunk_core::transport::MediaClass::Audio,
);
tracing::info!(%client, "audio: client endpoint learned");
tracing::debug!(%client, "audio: client endpoint learned");
// Reuse the persistent capturer when its channel count still matches (drain stale
// buffered audio); otherwise drop it (clean PipeWire teardown) and open at the new count.
@@ -468,7 +468,7 @@ fn audio_body(
timestamp = timestamp.wrapping_add(frame_ms as u32);
sent += 1;
if sent % 400 == 0 {
tracing::info!(sent, "audio: streaming");
tracing::debug!(sent, "audio: streaming");
}
// Hold each frame to its packet-duration slot (skip if we've fallen behind a burst).
@@ -101,7 +101,7 @@ pub fn spawn(state: Arc<AppState>) -> Result<()> {
},
Ok(None) => break,
Err(e) => {
tracing::warn!(error = %format!("{e:?}"), "control: service error");
tracing::warn!(error = ?e, "control: service error");
break;
}
}
@@ -132,7 +132,7 @@ pub fn spawn(state: Arc<AppState>) -> Result<()> {
for wire in out {
if let Err(e) = host.peer_mut(pid).send(0, &Packet::reliable(&wire[..]))
{
tracing::warn!(error = %format!("{e:?}"), "rumble send failed");
tracing::warn!(error = ?e, "rumble send failed");
}
}
}
@@ -214,12 +214,12 @@ fn on_receive(
if inner == 0x0301 {
if let Some((first, last)) = decode_rfi_range(&pt) {
*state.rfi_range.lock().unwrap() = Some((first, last));
tracing::info!(first, last, "control: RFI request → invalidate ref frames");
tracing::debug!(first, last, "control: RFI request → invalidate ref frames");
} else {
state
.force_idr
.store(true, std::sync::atomic::Ordering::SeqCst);
tracing::info!("control: RFI request (no range) → keyframe");
tracing::debug!("control: RFI request (no range) → keyframe");
}
return;
}
@@ -227,8 +227,8 @@ fn on_receive(
state
.force_idr
.store(true, std::sync::atomic::Ordering::SeqCst);
tracing::info!(
ty = format!("{inner:#06x}"),
tracing::debug!(
ty = %format_args!("{inner:#06x}"),
"control: IDR request → keyframe"
);
return;
@@ -218,6 +218,17 @@ pub fn serve(
gamestream,
"punktfunk host"
);
// Surface a conflicting Moonlight-compatible host (Sunshine/Apollo/…) as early as possible:
// scan once (cached for `/local/summary` → tray + web console) and warn loudly if found.
let conflicts = crate::detect::init();
if !conflicts.is_empty() {
tracing::warn!(
target: "punktfunk::detect",
count = conflicts.len(),
"{}",
crate::detect::render_report(conflicts)
);
}
if gamestream {
tracing::warn!(
"GameStream/Moonlight compat ENABLED (--gamestream): its pairing runs over plain HTTP and \
@@ -265,14 +265,14 @@ impl Pairing {
store.push(s.client_cert_der.clone());
super::save_paired(&store);
}
tracing::info!(uniqueid, "pairing phase 4 — SUCCESS, client cert pinned");
tracing::info!(uniqueid, "pairing phase 4 complete — client cert pinned");
Ok(paired_xml("", true))
} else {
tracing::warn!(
uniqueid,
hash_ok,
sig_ok,
"pairing phase 4 — FAILED (PIN/cert)"
"pairing phase 4 rejected — PIN or cert mismatch"
);
map.remove(uniqueid);
Ok(paired_xml("", false))
+7 -5
View File
@@ -97,10 +97,12 @@ fn handle_conn(mut stream: TcpStream, state: Arc<AppState>) -> Result<()> {
// response until EOF, so we answer one message and close the connection (which signals
// the end of the response). Session state lives in `AppState`, not the connection.
if let Some(req) = read_message(&mut stream, &mut buf)? {
tracing::info!(
method = %req.method, cseq = %req.cseq,
"RTSP {} | {}", req.head.replace("\r\n", " | "),
if req.body.is_empty() { String::new() } else { format!("body: {}", req.body.replace("\r\n", " | ")) }
tracing::debug!(
method = %req.method,
cseq = %req.cseq,
headers = %req.head.replace("\r\n", " | "),
body = %req.body.replace("\r\n", " | "),
"RTSP request"
);
let resp = handle_request(&req, &state, peer);
stream.write_all(resp.as_bytes()).context("RTSP write")?;
@@ -404,7 +406,7 @@ fn stream_config(map: &HashMap<String, String>) -> Option<StreamConfig> {
};
let matches_ours = (hdr && csc >> 1 == 2 || !hdr && csc >> 1 == 1) && csc & 1 == 0;
if matches_ours {
tracing::info!(
tracing::debug!(
csc,
space,
range,
@@ -879,7 +879,7 @@ fn stream_body(
"video: streaming (perf)"
);
} else {
tracing::info!(
tracing::debug!(
fps = fps_count,
sent_batches,
dropped_batches,
+4 -1
View File
@@ -49,7 +49,10 @@ pub(crate) async fn serve_https(
let (tcp, peer) = match listener.accept().await {
Ok(v) => v,
Err(e) => {
tracing::warn!(error = %e, "HTTPS accept failed");
// A persistent accept() error (fd exhaustion / EMFILE) would otherwise hot-spin
// this loop and storm the log; back off so a stuck accept can't burn a core.
tracing::warn!(error = %e, "HTTPS accept failed — backing off 100ms");
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
continue;
}
};