@@ -292,34 +292,36 @@ where
292292 . GetService ( )
293293 . map_err ( |e| crate :: error:: AppError :: Io ( format ! ( "获取捕获客户端失败: {}" , e) ) ) ?;
294294
295- audio_client
296- . Start ( )
297- . map_err ( |e| crate :: error:: AppError :: Io ( format ! ( "启动捕获失败: {}" , e) ) ) ?;
295+ // Start 判定收敛到一处(暂停重连循环审查 + 2026-09 修复):暂停期间重连
296+ // (如 Stop 失败重连接管)若不提前判定,会在 Start 之后由内层循环再次检测
297+ // 边沿执行 Stop——Stop 若持续失败则每轮重连都重复 "Start→Stop 失败→重连"
298+ // 的永动循环。已暂停时保持端点停机、直接进入暂停空转,一次性消除该循环。
299+ // 2026-09 修复(会话 67/68 实证):02556c47 为此新增下方分支式 Start 时未
300+ // 删除此前无条件 Start——非暂停进入时同一 IAudioClient 被 Start 两次,第二
301+ // 次返回 AUDCLNT_E_NOT_STOPPED(0x88890005,Start 时流未停止),capture_loop
302+ // 按设备故障 0.5s→10s 永续重连,会话全程无音频。IAudioClient::Start 不允许
303+ // 重复调用已启动的流:进入路径上 Start 恰好一次,别无分号。
304+ let mut capture_paused = matches ! ( pause. as_ref( ) , Some ( p) if p. paused. load( Ordering :: SeqCst ) ) ;
305+ let mut paused_at: Option < std:: time:: Instant > = None ;
306+ if capture_paused {
307+ eprintln ! ( "[AudioLoopback] 端点保持停机(会话已处于暂停——免 Start/Stop 重连循环)" ) ;
308+ // 预检命中暂停时记下空转起点:恢复边沿靠 paused_at 触发 Start + 累计
309+ // 暂停时长(2026-09 修复:此前置 None——恢复边沿取 None 走空分支,
310+ // 端点永不启动、会话静默且暂停时长不补偿)
311+ paused_at = Some ( std:: time:: Instant :: now ( ) ) ;
312+ } else {
313+ audio_client
314+ . Start ( )
315+ . map_err ( |e| crate :: error:: AppError :: Io ( format ! ( "启动捕获失败: {}" , e) ) ) ?;
316+ }
298317
299- // 重连成功后通知退出恢复态(ADR-007:前端徽标恢复)
318+ // 重连成功后通知退出恢复态(ADR-007:前端徽标恢复;暂停进入的会话推迟到
319+ // 恢复边沿 Start 成功——恢复前音频确实未产出,徽标保持"恢复中"更真实)
300320 if * recovering {
301321 * recovering = false ;
302322 on_recovery ( false ) ;
303323 }
304324
305- // 审查修复(暂停重连循环,观察 2026-08-29-2):端点启动前先看暂停标志——
306- // 暂停期间重连(如 Stop 失败重连接管)若不提前判定,会在 Start 之后由
307- // 内层循环再次检测边沿执行 Stop;Stop 若持续失败则每轮重连都重复
308- // "Start→Stop 失败→重连"的永动循环,期间前端"采集中/恢复中"与真实停采
309- // 信息差。已暂停时保持端点停机、直接进入暂停空转——一次性消除循环。
310- let ( capture_paused, started) = match pause. as_ref ( ) {
311- Some ( p) if p. paused . load ( Ordering :: SeqCst ) => ( true , false ) ,
312- Some ( _) => ( false , true ) ,
313- None => ( false , true ) ,
314- } ;
315- if started {
316- audio_client
317- . Start ( )
318- . map_err ( |e| crate :: error:: AppError :: Io ( format ! ( "启动捕获失败: {}" , e) ) ) ?;
319- } else {
320- eprintln ! ( "[AudioLoopback] 端点保持停机(会话已处于暂停——免 Start/Stop 重连循环)" ) ;
321- }
322-
323325 // 归一化管线:字节 → f32 → 混单声道 → 重采样 16k → 200ms 切块
324326 // @ai-context: 时间戳用会话纪元(epoch 由编排层注入,ADR-008 A1:音频/屏幕/
325327 // flush 三处同基准)而非有效音频计数——静默期时间轴也推进,
@@ -330,10 +332,7 @@ where
330332 let mut accumulator = ChunkAccumulator :: new ( 0 ) ;
331333 let block_samples = ( TARGET_SAMPLE_RATE as usize ) / 5 ;
332334 let mut format_error_logged = false ;
333- // 暂停边沿状态(本线程内维护;paused 由命令层置位)
334- // 审查修复:初始化值随上方提前判定(已暂停重连时直接进入空转,边沿状态一致)
335- let mut capture_paused = capture_paused;
336- let mut paused_at: Option < std:: time:: Instant > = None ;
335+ // 暂停边沿状态(本线程内维护;paused 由命令层置位;已在启动预检处完成初始化)
337336
338337 while !stop_flag. load ( Ordering :: SeqCst ) {
339338 // ── 暂停边沿处理(2026-08 A1)──
0 commit comments