fix(android): playout Usage::Media + relay CallSetup advertises real IP
Three real bugs, one smoke-test session's worth of progress. 1. RELAY: wrong advertised addr in CallSetup The direct-call CallSetup computed `relay_addr = addr.ip()` where `addr = connection.remote_address()` — i.e. the CLIENT'S IP, not the relay's. So the relay was telling both parties "the call room is at the answerer's IP:4433", which meant each client dialed either the other client (no server listening) or themselves. Both endpoint.connect calls hung forever and the call never happened. Fix: compute the relay's own advertised IP once at startup. If the listen addr is 0.0.0.0, probe the primary outbound interface via the classic UDP-bind-and-connect(8.8.8.8:80) trick to discover the LAN IP the OS would use to reach external hosts. Thread the resulting advertised_addr_str into the CallSetup sender for both parties. 2. RELAY: accept loop serialized QUIC handshakes Previously the main accept loop called `wzp_transport::accept` which did both `endpoint.accept().await` AND `incoming.await` (the server- side QUIC handshake). A single slow handshake therefore blocked every subsequent client from being accepted. Unroll the helper here and move `incoming.await` into the per-connection spawned task, so every handshake runs in parallel. Also log "accept queue: new Incoming", "QUIC handshake complete", and "QUIC handshake failed" so we can tell immediately whether a client's packets are reaching the relay at all. 3. ANDROID: playout was routed to the silent in-call stream The Oboe playout stream was configured with Usage::VoiceCommunication, which routes to the Android in-call earpiece stream. That stream is silent unless the Activity has called AudioManager.setMode( IN_COMMUNICATION) and, even then, only the earpiece/BT headset get audio (not the loud speaker). Result: android→mac calls worked because mac had a normal media output, but mac→android calls were silent even though packets flowed through the relay just fine. Switch to Usage::Media + ContentType::Speech so Oboe routes to the loud speaker and uses the media volume slider. A later polish step will wire setMode + setSpeakerphoneOn from MainActivity.kt so we can go back to VoiceCommunication for AEC and proximity-sensor routing. Plus: heartbeat tracing every 2s in the send/recv tasks — frames_sent, last_rms, last_pkt_bytes, short_reads on the send side; decoded_frames, last_decode_n, last_written, decode_errs on the recv side. Will make the next "no sound" regression trivial to localize.
This commit is contained in:
@@ -224,6 +224,11 @@ impl CallEngine {
|
||||
encoder.set_aec_enabled(false);
|
||||
let mut buf = vec![0i16; frame_samples];
|
||||
|
||||
let mut heartbeat = std::time::Instant::now();
|
||||
let mut last_rms: u32 = 0;
|
||||
let mut last_pkt_bytes: usize = 0;
|
||||
let mut short_reads: u64 = 0;
|
||||
|
||||
loop {
|
||||
if !send_r.load(Ordering::Relaxed) {
|
||||
break;
|
||||
@@ -234,6 +239,7 @@ impl CallEngine {
|
||||
// so in steady state this spins once per frame.
|
||||
let read = crate::wzp_native::audio_read_capture(&mut buf);
|
||||
if read < frame_samples {
|
||||
short_reads += 1;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
|
||||
continue;
|
||||
}
|
||||
@@ -242,6 +248,7 @@ impl CallEngine {
|
||||
let sum_sq: f64 = buf.iter().map(|&s| (s as f64) * (s as f64)).sum();
|
||||
let rms = (sum_sq / buf.len() as f64).sqrt() as u32;
|
||||
send_level.store(rms, Ordering::Relaxed);
|
||||
last_rms = rms;
|
||||
|
||||
if send_mic.load(Ordering::Relaxed) {
|
||||
buf.fill(0);
|
||||
@@ -249,6 +256,7 @@ impl CallEngine {
|
||||
match encoder.encode_frame(&buf) {
|
||||
Ok(pkts) => {
|
||||
for pkt in &pkts {
|
||||
last_pkt_bytes = pkt.payload.len();
|
||||
if let Err(e) = send_t.send_media(pkt).await {
|
||||
send_drops.fetch_add(1, Ordering::Relaxed);
|
||||
if send_drops.load(Ordering::Relaxed) <= 3 {
|
||||
@@ -260,6 +268,21 @@ impl CallEngine {
|
||||
}
|
||||
Err(e) => error!("encode: {e}"),
|
||||
}
|
||||
|
||||
// Heartbeat every 2s with capture+encode+send state
|
||||
if heartbeat.elapsed() >= std::time::Duration::from_secs(2) {
|
||||
let fs = send_fs.load(Ordering::Relaxed);
|
||||
let drops = send_drops.load(Ordering::Relaxed);
|
||||
info!(
|
||||
frames_sent = fs,
|
||||
last_rms,
|
||||
last_pkt_bytes,
|
||||
short_reads,
|
||||
send_drops = drops,
|
||||
"send heartbeat (android)"
|
||||
);
|
||||
heartbeat = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -275,6 +298,15 @@ impl CallEngine {
|
||||
let mut current_codec = initial_profile.codec;
|
||||
let mut agc = wzp_codec::AutoGainControl::new();
|
||||
let mut pcm = vec![0i16; FRAME_SAMPLES_40MS];
|
||||
info!(codec = ?current_codec, "recv task starting (android/oboe)");
|
||||
|
||||
let mut heartbeat = std::time::Instant::now();
|
||||
let mut decoded_frames: u64 = 0;
|
||||
let mut written_samples: u64 = 0;
|
||||
let mut last_decode_n: usize = 0;
|
||||
let mut last_written: usize = 0;
|
||||
let mut decode_errs: u64 = 0;
|
||||
let mut first_packet_logged = false;
|
||||
|
||||
loop {
|
||||
if !recv_r.load(Ordering::Relaxed) {
|
||||
@@ -287,6 +319,10 @@ impl CallEngine {
|
||||
.await
|
||||
{
|
||||
Ok(Ok(Some(pkt))) => {
|
||||
if !first_packet_logged {
|
||||
info!(codec_id = ?pkt.header.codec_id, payload_bytes = pkt.payload.len(), is_repair = pkt.header.is_repair, "recv: first media packet received");
|
||||
first_packet_logged = true;
|
||||
}
|
||||
if !pkt.header.is_repair && pkt.header.codec_id != CodecId::ComfortNoise {
|
||||
{
|
||||
let mut rx = recv_rx_codec.lock().await;
|
||||
@@ -311,10 +347,22 @@ impl CallEngine {
|
||||
let _ = decoder.set_profile(new_profile);
|
||||
current_codec = pkt.header.codec_id;
|
||||
}
|
||||
if let Ok(n) = decoder.decode(&pkt.payload, &mut pcm) {
|
||||
agc.process_frame(&mut pcm[..n]);
|
||||
if !recv_spk.load(Ordering::Relaxed) {
|
||||
crate::wzp_native::audio_write_playout(&pcm[..n]);
|
||||
match decoder.decode(&pkt.payload, &mut pcm) {
|
||||
Ok(n) => {
|
||||
last_decode_n = n;
|
||||
decoded_frames += 1;
|
||||
agc.process_frame(&mut pcm[..n]);
|
||||
if !recv_spk.load(Ordering::Relaxed) {
|
||||
let w = crate::wzp_native::audio_write_playout(&pcm[..n]);
|
||||
last_written = w;
|
||||
written_samples = written_samples.saturating_add(w as u64);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
decode_errs += 1;
|
||||
if decode_errs <= 3 {
|
||||
tracing::warn!("decode error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user