feat: per-step encode profiling in send task stats
Some checks failed
Build Release Binaries / build-amd64 (push) Failing after 3m48s
Some checks failed
Build Release Binaries / build-amd64 (push) Failing after 3m48s
Adds average microsecond timings for each encode step: - avg_agc_us: AGC processing - avg_opus_us: Opus encoding - avg_fec_us: FEC encode + repair generation - avg_send_us: QUIC send_media - avg_total_us: sum of above Logged every 5 seconds in send stats. Resets each interval. Use to identify which step is bottlenecking the encode loop on devices where fps drops below 50. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -336,6 +336,12 @@ async fn run_call(
|
||||
let mut last_stats_log = Instant::now();
|
||||
let mut frames_sent: u64 = 0;
|
||||
let mut frames_dropped: u64 = 0;
|
||||
// Per-step timing accumulators (reset every stats log)
|
||||
let mut t_agc_us: u64 = 0;
|
||||
let mut t_opus_us: u64 = 0;
|
||||
let mut t_fec_us: u64 = 0;
|
||||
let mut t_send_us: u64 = 0;
|
||||
let mut t_frames: u64 = 0;
|
||||
loop {
|
||||
if !state.running.load(Ordering::Relaxed) {
|
||||
break;
|
||||
@@ -359,9 +365,12 @@ async fn run_call(
|
||||
}
|
||||
|
||||
// AGC: normalize capture volume before encoding
|
||||
let t0 = Instant::now();
|
||||
capture_agc.process_frame(&mut capture_buf);
|
||||
t_agc_us += t0.elapsed().as_micros() as u64;
|
||||
|
||||
// Opus encode
|
||||
let t0 = Instant::now();
|
||||
let encoded_len = match encoder.encode(&capture_buf, &mut encode_buf) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
@@ -369,6 +378,7 @@ async fn run_call(
|
||||
continue;
|
||||
}
|
||||
};
|
||||
t_opus_us += t0.elapsed().as_micros() as u64;
|
||||
let encoded = &encode_buf[..encoded_len];
|
||||
|
||||
// Build source packet
|
||||
@@ -394,6 +404,7 @@ async fn run_call(
|
||||
};
|
||||
|
||||
// Send source packet — drop on error, never break
|
||||
let t0 = Instant::now();
|
||||
if let Err(e) = transport.send_media(&source_pkt).await {
|
||||
send_errors += 1;
|
||||
frames_dropped += 1;
|
||||
@@ -408,11 +419,14 @@ async fn run_call(
|
||||
last_send_error_log = Instant::now();
|
||||
}
|
||||
// Don't feed to FEC either — the source is lost
|
||||
t_send_us += t0.elapsed().as_micros() as u64;
|
||||
continue;
|
||||
}
|
||||
t_send_us += t0.elapsed().as_micros() as u64;
|
||||
frames_sent += 1;
|
||||
|
||||
// Feed encoded frame to FEC encoder
|
||||
let t0 = Instant::now();
|
||||
if let Err(e) = fec_enc.add_source_symbol(encoded) {
|
||||
warn!("fec add_source error: {e}");
|
||||
}
|
||||
@@ -469,9 +483,12 @@ async fn run_call(
|
||||
block_id = block_id.wrapping_add(1);
|
||||
frame_in_block = 0;
|
||||
}
|
||||
t_fec_us += t0.elapsed().as_micros() as u64;
|
||||
t_frames += 1;
|
||||
|
||||
// Periodic stats every 5 seconds
|
||||
if last_stats_log.elapsed().as_secs() >= 5 {
|
||||
let avg = |total: u64| if t_frames > 0 { total / t_frames } else { 0 };
|
||||
info!(
|
||||
seq = s,
|
||||
block_id,
|
||||
@@ -480,8 +497,14 @@ async fn run_call(
|
||||
send_errors,
|
||||
ring_avail = state.capture_ring.available(),
|
||||
capture_overflows = state.capture_ring.overflow_count(),
|
||||
avg_agc_us = avg(t_agc_us),
|
||||
avg_opus_us = avg(t_opus_us),
|
||||
avg_fec_us = avg(t_fec_us),
|
||||
avg_send_us = avg(t_send_us),
|
||||
avg_total_us = avg(t_agc_us + t_opus_us + t_fec_us + t_send_us),
|
||||
"send stats"
|
||||
);
|
||||
t_agc_us = 0; t_opus_us = 0; t_fec_us = 0; t_send_us = 0; t_frames = 0;
|
||||
last_stats_log = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user