From ad022ffcf4b6c45f8bf16502f6dabae6cde55836 Mon Sep 17 00:00:00 2001 From: dev-team Date: Fri, 23 Oct 2026 13:36:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20v0.8.1=20=E9=9D=99=E6=AD=A2=E6=A1=8C?= =?UTF-8?q?=E9=9D=A2=E9=BB=91=E5=B1=8F=20-=20=E7=A7=AF=E5=8E=8B=E8=B7=B3?= =?UTF-8?q?=E5=B8=A7=E9=99=90=E5=AE=9A=E7=AA=81=E5=8F=91=E5=88=B0=E8=BE=BE?= =?UTF-8?q?,=20=E7=A9=BA=E9=97=B2=E9=87=8D=E5=8F=91=E5=B8=A7=E4=B8=8D?= =?UTF-8?q?=E5=88=A4=E9=99=88=E6=97=A7=E4=B8=8D=E8=AE=A1E2E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- viewer/src/main.rs | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/viewer/src/main.rs b/viewer/src/main.rs index 19eaac8..8521538 100644 --- a/viewer/src/main.rs +++ b/viewer/src/main.rs @@ -256,6 +256,7 @@ fn session( let mut rgb_buf: Vec = Vec::new(); let mut last_kfreq = Instant::now(); let mut skip_stale = 0u64; + let mut last_arr: Option = None; tokio::spawn(async move { let mut size_logged = false; loop { @@ -263,6 +264,12 @@ fn session( Ok((hdr, payload)) => { stats.bytes.fetch_add(payload.len() as u64, Ordering::Relaxed); stats.frames.fetch_add(1, Ordering::Relaxed); + // 与上一帧的到达间隔:≤100ms 才算"突发到达"。 + // 桌面静止时 agent 以 2Hz 空闲重发,帧龄天然 ≥500ms, + // 不能按陈旧帧丢弃——否则静止桌面所有帧被跳过 → + // 永久黑屏 + KeyFrameReq/IDR 死循环(v0.8 实测教训) + let burst = matches!(last_arr, Some(t) if t.elapsed() <= Duration::from_millis(100)); + last_arr = Some(Instant::now()); // 端到端延迟 = 接收时刻 - 时钟偏移 - 采集时刻(EMA 平滑) let mut est = None; let mut stale = false; @@ -271,27 +278,30 @@ fn session( now_ms() as i64 - stats.offset.load(Ordering::Relaxed) - hdr.ts_ms as i64; if (0..5000).contains(&e) { - // 积压保护:QUIC 视频流可靠有序,积压必须逐帧读出, - // 但陈旧帧(>250ms)可以不解码——跳过解码让积压以 - // 网络速度排空,否则消化速率≈到达速率永远追不回来 - stale = e > 250; - let old = stats.e2e.load(Ordering::Relaxed) as i64; - let sm = if stats.e2e_valid.load(Ordering::Relaxed) { - (old * 4 + e) / 5 - } else { - e - }; - stats.e2e.store(sm as u64, Ordering::Relaxed); - stats.e2e_valid.store(true, Ordering::Relaxed); - est = Some(sm); + // 积压判定必须同时满足:突发到达(背靠背)+ 帧龄>250ms。 + // QUIC 可靠有序,积压须逐帧读出但可不解码——跳过解码 + // 让队列以网络速度排空 + stale = burst && e > 250; + // 空闲重发帧的帧龄不代表显示延迟,不计入 EMA + if burst || e < 250 { + let old = stats.e2e.load(Ordering::Relaxed) as i64; + let sm = if stats.e2e_valid.load(Ordering::Relaxed) { + (old * 4 + e) / 5 + } else { + e + }; + stats.e2e.store(sm as u64, Ordering::Relaxed); + stats.e2e_valid.store(true, Ordering::Relaxed); + est = Some(sm); + } } } stats.on_frame_arrival(est); if stale { skip_stale += 1; - if skip_stale % 120 == 1 { + if skip_stale % 60 == 1 { eprintln!( - "[viewer] backlog: stale frame skipped (e2e>250ms, total {skip_stale})" + "[viewer] backlog: stale burst frame skipped (total {skip_stale})" ); } // 限频请求关键帧:积压排空后从 IDR 重新衔接画面 @@ -392,7 +402,7 @@ fn session( push(&msg_tx, ControlMsg::KeyFrameReq); let mut window = Window::new( - "mvp-remote v0.8-CatchUp (Esc 退出)", + "mvp-remote v0.8.1-CatchUp (Esc 退出)", w, h, WindowOptions {