diff --git a/agent/src/capture.rs b/agent/src/capture.rs index e6daf81..9489495 100644 --- a/agent/src/capture.rs +++ b/agent/src/capture.rs @@ -236,7 +236,8 @@ impl NvencEncoder { /// 编码当前 GPU 纹理内容,返回 Annex B H.264 码流。 fn encode(&mut self, force_idr: bool, ts_ms: u64) -> Result> { - let ty = if force_idr { + // 首帧强制 IDR:解码端从任意时刻接入,没有 IDR 无法解出后续 P 帧 + let ty = if force_idr || self.frame_count == 0 { NVencPicType::IDR } else { NVencPicType::P @@ -441,12 +442,14 @@ pub fn run(tx: Sender>>, keyreq: Arc, congestion: Arc } Ok(false) => { // 桌面静止:低频重发(丢包后恢复 + 会话保活)。 - // NVENC 重编当前 GPU 帧(输出极小 P 帧);软编重发最后一帧副本。 + // 关键:KeyFrameReq 在桌面静止时也必须响应 —— 此前只在新帧路径 + // 消费该标志,桌面完全静止时解码端请求 IDR 永远得不到响应 → 黑屏。 + let force_key = keyreq.swap(false, Ordering::Relaxed); if last_sent.elapsed() > Duration::from_millis(500) { match &mut encoder { EncoderKind::Nvenc(enc) => { - // 重编当前 GPU 帧(内容未变,输出极小 P 帧) - let r = enc.encode(false, start.elapsed().as_millis() as u64); + // 重编当前 GPU 帧(内容未变,输出极小;force_key 时为 IDR) + let r = enc.encode(force_key, start.elapsed().as_millis() as u64); match r { Ok(payload) => emit!(payload), Err(e) => { diff --git a/viewer/src/main.rs b/viewer/src/main.rs index 36ba386..48873a0 100644 --- a/viewer/src/main.rs +++ b/viewer/src/main.rs @@ -360,6 +360,8 @@ fn session( let push = |tx: &tokio::sync::mpsc::UnboundedSender>, m: ControlMsg| { let _ = tx.send(common::encode_control(&m)); }; + // 入会立即请求 IDR:避免从 GOP 中间接入收到裸 P 帧解码失败导致启动黑屏 + push(&msg_tx, ControlMsg::KeyFrameReq); let mut window = Window::new( "mvp-remote v0.7-LowLatency (Esc 退出)",