feat(video+desktop): camera capture, video UI, E2E AEAD wiring, test fixes
Blockers 4 & 5: browser getUserMedia → JPEG IPC → Rust I420 pipeline; remote video strip renders decoded frames via canvas; EncryptingTransport wraps QuinnTransport so WZP AEAD is applied to all media (C2 fix). Test fixes: HandshakeResult.session destructuring across relay/client/crypto integration tests; video_codecs field added to all CallOffer/CallAnswer structs; wzp-video pipeline_roundtrip integration tests added. PRD docs: five Kimi-ready specs for E2E encryption, Android NDK 0.9 migration, quality upgrade flow, wire-format hardening, and clippy debt. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -114,11 +114,7 @@ impl EchoCanceller {
|
||||
/// Number of delayed samples available to release.
|
||||
fn delay_available(&self) -> usize {
|
||||
let buffered = self.delay_write - self.delay_read;
|
||||
if buffered > self.delay_samples {
|
||||
buffered - self.delay_samples
|
||||
} else {
|
||||
0
|
||||
}
|
||||
buffered.saturating_sub(self.delay_samples)
|
||||
}
|
||||
|
||||
/// Process a near-end (microphone) frame, removing the estimated echo.
|
||||
@@ -161,8 +157,8 @@ impl EchoCanceller {
|
||||
let mut sum_near_sq: f64 = 0.0;
|
||||
let mut sum_err_sq: f64 = 0.0;
|
||||
|
||||
for i in 0..n {
|
||||
let near_f = nearend[i] as f32;
|
||||
for (i, sample) in nearend.iter_mut().enumerate() {
|
||||
let near_f = *sample as f32;
|
||||
|
||||
// Position of far-end "now" for this near-end sample.
|
||||
let base = (self.far_pos + fl * ((n / fl) + 2) + i - n) % fl;
|
||||
@@ -190,7 +186,7 @@ impl EchoCanceller {
|
||||
}
|
||||
|
||||
let out = error.clamp(-32768.0, 32767.0);
|
||||
nearend[i] = out as i16;
|
||||
*sample = out as i16;
|
||||
|
||||
sum_near_sq += (near_f as f64).powi(2);
|
||||
sum_err_sq += (out as f64).powi(2);
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Codec2Decoder {
|
||||
|
||||
/// Number of compressed bytes per frame.
|
||||
fn bytes_per_frame(&self) -> usize {
|
||||
(self.inner.bits_per_frame() + 7) / 8
|
||||
self.inner.bits_per_frame().div_ceil(8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Codec2Encoder {
|
||||
|
||||
/// Number of compressed bytes per frame.
|
||||
fn bytes_per_frame(&self) -> usize {
|
||||
(self.inner.bits_per_frame() + 7) / 8
|
||||
self.inner.bits_per_frame().div_ceil(8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ impl NoiseSupressor {
|
||||
|
||||
// f32 → i16 with clamping
|
||||
for (i, &val) in output.iter().enumerate() {
|
||||
let clamped = val.max(-32768.0).min(32767.0);
|
||||
let clamped = val.clamp(-32768.0, 32767.0);
|
||||
pcm[offset + i] = clamped as i16;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ pub fn dred_duration_for(codec: CodecId) -> u8 {
|
||||
/// mode; unset or empty leaves DRED enabled.
|
||||
fn read_legacy_fec_env() -> bool {
|
||||
match std::env::var(LEGACY_FEC_ENV) {
|
||||
Ok(v) => !v.is_empty() && v != "0" && v.to_ascii_lowercase() != "false",
|
||||
Ok(v) => !v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("false"),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
@@ -252,7 +252,7 @@ impl OpusEncoder {
|
||||
let clamped = if self.legacy_fec_mode {
|
||||
loss_pct.min(100)
|
||||
} else {
|
||||
loss_pct.max(DRED_LOSS_FLOOR_PCT).min(100)
|
||||
loss_pct.clamp(DRED_LOSS_FLOOR_PCT, 100)
|
||||
};
|
||||
let _ = self.inner.set_packet_loss(clamped);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ fn build_fir_kernel() -> [f64; FIR_TAPS] {
|
||||
let fc = CUTOFF_HZ / SAMPLE_RATE; // normalised cutoff (0..0.5)
|
||||
let beta_denom = bessel_i0(KAISER_BETA);
|
||||
|
||||
for i in 0..FIR_TAPS {
|
||||
for (i, slot) in kernel.iter_mut().enumerate() {
|
||||
// Sinc
|
||||
let n = i as f64 - m / 2.0;
|
||||
let sinc = if n.abs() < 1e-12 {
|
||||
@@ -61,7 +61,7 @@ fn build_fir_kernel() -> [f64; FIR_TAPS] {
|
||||
let t = 2.0 * i as f64 / m - 1.0; // range [-1, 1]
|
||||
let kaiser = bessel_i0(KAISER_BETA * (1.0 - t * t).max(0.0).sqrt()) / beta_denom;
|
||||
|
||||
kernel[i] = sinc * kaiser;
|
||||
*slot = sinc * kaiser;
|
||||
}
|
||||
|
||||
// Normalise to unity DC gain.
|
||||
@@ -180,9 +180,7 @@ impl Upsampler8to48 {
|
||||
work.extend_from_slice(&self.history);
|
||||
for &s in input {
|
||||
work.push(s as f64);
|
||||
for _ in 1..RATIO {
|
||||
work.push(0.0);
|
||||
}
|
||||
work.resize(work.len() + (RATIO - 1), 0.0f64);
|
||||
}
|
||||
|
||||
let out_len = stuffed_len;
|
||||
|
||||
Reference in New Issue
Block a user