diff --git a/examples/stereo/src/app/master.rs b/examples/stereo/src/app/master.rs index 796dd95..f6cd567 100644 --- a/examples/stereo/src/app/master.rs +++ b/examples/stereo/src/app/master.rs @@ -217,6 +217,8 @@ pub async fn run_master(master_role: ChannelRole) -> Result<()> { let wait = target_ts - now; if wait > 1000 { tokio::time::sleep(Duration::from_micros(wait as u64)).await; + } else { + // 小于 1ms,直接播放,让播放时机稍微早一点点 } } } else { diff --git a/examples/stereo/src/app/slave.rs b/examples/stereo/src/app/slave.rs index 98f5c0d..7ab3d6e 100644 --- a/examples/stereo/src/app/slave.rs +++ b/examples/stereo/src/app/slave.rs @@ -170,7 +170,6 @@ async fn handle_connection(role: ChannelRole) -> Result<()> { last_seq = Some(pkt.seq); // 精确等待到播放时间 - // 策略: 距离远时 sleep,距离近时忙等 loop { let now = now_us(); let current_server_time_precise = clock.lock().await.to_server_time(now); @@ -181,14 +180,10 @@ async fn handle_connection(role: ChannelRole) -> Result<()> { let wait_us = (pkt.timestamp - current_server_time_precise) as u64; - if wait_us > 5000 { - // 等待时间 >5ms: sleep 大部分时间 - tokio::time::sleep(Duration::from_micros(wait_us - 3000)).await; - } else if wait_us > 1000 { - // 等待时间 1-5ms: yield 让出 CPU - tokio::task::yield_now().await; + if wait_us > 1000 { + tokio::time::sleep(Duration::from_micros(wait_us as u64)).await; } else { - // 等待时间 <1ms: 直接 break,让播放时机稍微早一点点 + // 小于 1ms,直接播放,让播放时机稍微早一点点 break; } } diff --git a/examples/stereo/src/audio/player.rs b/examples/stereo/src/audio/player.rs index ffb28b9..fe38f5f 100644 --- a/examples/stereo/src/audio/player.rs +++ b/examples/stereo/src/audio/player.rs @@ -52,18 +52,19 @@ fn setup_pcm(pcm: &PCM, sample_rate: u32, channels: u16) -> Result<()> { hwp.set_rate(sample_rate, alsa::ValueOr::Nearest)?; hwp.set_channels(channels as u32)?; - // // 设置较大的缓冲区以减少由于调度抖动导致的断音 - // // 48000Hz * 0.2s = 9600 samples - // let buffer_size = (sample_rate as f64 * 0.2) as u32; - // let period_size = buffer_size / 4; - // hwp.set_buffer_size_near(buffer_size as alsa::pcm::Frames)?; - // hwp.set_period_size_near(period_size as alsa::pcm::Frames, alsa::ValueOr::Nearest)?; + // 设置较大的缓冲区以减少由于调度抖动和设备重初始化导致的断音/卡顿 + // 使用 100ms 缓冲区,既能防止 underrun,又不会引入过大延迟 + let buffer_size = (sample_rate as f64 * 0.1) as u32; // 100ms 缓冲 + let period_size = buffer_size / 4; // 25ms 周期 + hwp.set_buffer_size_near(buffer_size as alsa::pcm::Frames)?; + hwp.set_period_size_near(period_size as alsa::pcm::Frames, alsa::ValueOr::Nearest)?; pcm.hw_params(&hwp).context("Failed to set HwParams")?; let swp = pcm.sw_params_current()?; - // 设置 start_threshold 为 buffer_size 的一半,确保缓冲区有足够数据再开始播放 - // swp.set_start_threshold(buffer_size as alsa::pcm::Frames / 2)?; + // 设置 start_threshold,当缓冲区有 1 个 period 数据时就开始播放 + // 这样可以快速启动,同时保持足够的缓冲余量 + swp.set_start_threshold(period_size as alsa::pcm::Frames)?; pcm.sw_params(&swp)?; pcm.prepare()?; Ok(())