fix(cli): port live mode to ring API (read_frame/write_frame removed)
AudioCapture and AudioPlayback no longer expose the old read_frame() and write_frame() methods — they were replaced with ring() returning &Arc<AudioRing> when the lock-free SPSC ring was introduced. The CLI live-mode loop still referenced the removed methods, which broke every workspace build that touched wzp-client bin (including the remote Linux x86_64 docker build). - Send loop: allocate a 960-sample scratch buffer, fill it in a loop via capture.ring().read() until a full 20 ms frame is available, sleep 2 ms between empty reads to avoid hot-spinning. - Recv loop: write decoded PCM into playback.ring() instead of calling write_frame(). Short writes on full ring drop the tail, which is the correct real-time behavior for CLI live mode. No behavioral change on the wire or in the call pipeline — this is purely a compile fix for cli.rs bitrot that accumulated since the ring API landed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -626,11 +626,21 @@ async fn run_live(transport: Arc<wzp_transport::QuinnTransport>) -> anyhow::Resu
|
|||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let config = CallConfig::default();
|
let config = CallConfig::default();
|
||||||
let mut encoder = CallEncoder::new(&config);
|
let mut encoder = CallEncoder::new(&config);
|
||||||
|
let mut frame = vec![0i16; FRAME_SAMPLES];
|
||||||
loop {
|
loop {
|
||||||
let frame = match capture.read_frame() {
|
// Pull a full 20 ms frame from the capture ring. The ring
|
||||||
Some(f) => f,
|
// may return a partial read when the CPAL callback hasn't
|
||||||
None => break,
|
// produced enough samples yet — keep reading until we
|
||||||
};
|
// accumulate a whole frame, sleeping briefly on empty
|
||||||
|
// returns so we don't hot-spin the CPU.
|
||||||
|
let mut filled = 0usize;
|
||||||
|
while filled < FRAME_SAMPLES {
|
||||||
|
let n = capture.ring().read(&mut frame[filled..]);
|
||||||
|
filled += n;
|
||||||
|
if n == 0 {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
let packets = match encoder.encode_frame(&frame) {
|
let packets = match encoder.encode_frame(&frame) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -661,7 +671,13 @@ async fn run_live(transport: Arc<wzp_transport::QuinnTransport>) -> anyhow::Resu
|
|||||||
// Repair packets feed the FEC decoder but don't produce audio.
|
// Repair packets feed the FEC decoder but don't produce audio.
|
||||||
if !is_repair {
|
if !is_repair {
|
||||||
if let Some(_n) = decoder.decode_next(&mut pcm_buf) {
|
if let Some(_n) = decoder.decode_next(&mut pcm_buf) {
|
||||||
playback.write_frame(&pcm_buf);
|
// Push the decoded frame into the playback
|
||||||
|
// ring. The CPAL output callback drains from
|
||||||
|
// here on its own clock; if the ring is full
|
||||||
|
// (rare in CLI live mode) the write returns
|
||||||
|
// a short count and the tail is dropped,
|
||||||
|
// which is the correct real-time behavior.
|
||||||
|
playback.ring().write(&pcm_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user