fix(windows): bound ASR final result wait

This commit is contained in:
Cooper-X-Oak committed 2026-05-01 14:00:35 +08:00
1 parent 5650a9a1bc
commit e2774da2c7
1 file changed
+47 -4
@@ -6,7 +6,7 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Instant;
use std::time::{Duration, Instant};
use futures_util::{SinkExt, StreamExt};
use parking_lot::Mutex as ParkingMutex;
@@ -29,6 +29,7 @@ const TARGET_AUDIO_CHUNK_BYTES: usize = 6_400;
/// 16 kHz · 16-bit · mono = 32 000 bytes/sec → 32 bytes/ms.
const BYTES_PER_MS: f64 = 32.0;
const HOTWORD_CAP: usize = 80;
const FINAL_RESULT_TIMEOUT: Duration = Duration::from_secs(12);
#[derive(Clone, Debug)]
pub struct VolcengineCredentials {
@@ -53,6 +54,8 @@ pub enum VolcengineASRError {
AuthenticationFailed,
#[error("no final result")]
NoFinalResult,
#[error("final result timed out")]
FinalResultTimeout,
#[error("decode failed: {0}")]
DecodeFailed(String),
}
@@ -334,13 +337,29 @@ impl VolcengineStreamingASR {
}
pub async fn await_final_result(&self) -> Result<RawTranscript, VolcengineASRError> {
self.await_final_result_with_timeout(FINAL_RESULT_TIMEOUT)
.await
}
pub async fn await_final_result_with_timeout(
&self,
timeout: Duration,
) -> Result<RawTranscript, VolcengineASRError> {
let rx = self.final_rx.lock().take();
let Some(rx) = rx else {
return Err(VolcengineASRError::NoFinalResult);
};
match rx.await {
Ok(result) => result,
Err(_) => Err(VolcengineASRError::NoFinalResult),
match tokio::time::timeout(timeout, rx).await {
Ok(Ok(result)) => result,
Ok(Err(_)) => Err(VolcengineASRError::NoFinalResult),
Err(_) => {
log::error!(
"[asr] final result timed out after {} ms",
timeout.as_millis()
);
self.cancel();
Err(VolcengineASRError::FinalResultTimeout)
}
}
}
@@ -703,4 +722,28 @@ mod tests {
"volc.bigasr.sauc.duration"
);
}
#[tokio::test]
async fn await_final_result_returns_error_when_final_frame_never_arrives() {
let asr = VolcengineStreamingASR::new(
VolcengineCredentials {
app_id: "app".into(),
access_token: "token".into(),
resource_id: VolcengineCredentials::default_resource_id().into(),
},
Vec::new(),
);
let (tx, rx) = oneshot::channel();
asr.state.lock().final_tx = Some(tx);
*asr.final_rx.lock() = Some(rx);
let result = asr
.await_final_result_with_timeout(std::time::Duration::from_millis(10))
.await;
assert!(matches!(
result,
Err(VolcengineASRError::FinalResultTimeout)
));
}
}