Skip to content

Commit 7bc86d3

Browse files
committed
fix(capture): 环回重复 Start 根治 0x88890005 永续重连(会话 67/68)
1 parent 4d0f1fd commit 7bc86d3

1 file changed

Lines changed: 25 additions & 26 deletions

File tree

‎app/src-tauri/src/capture/audio_loopback.rs‎

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)