Siavash Sameni d5c298d0b5 feat(codec): Phase 2 — remove RaptorQ from Opus tiers, Codec2 unchanged
Phase 2 of the DRED integration (docs/PRD-dred-integration.md). With
Phase 1 having enabled DRED on every Opus profile, the app-level RaptorQ
layer is now redundant overhead on those tiers: +20% bitrate, +40–100 ms
receive-side latency (block wait), +CPU for stats we never used. This
phase removes RaptorQ from the Opus encode and decode paths on both the
desktop (wzp-client/call.rs) and Android (wzp-android/engine.rs) sides.
Codec2 tiers keep RaptorQ with their current ratios unchanged — DRED is
libopus-only and Codec2 has no neural equivalent.

Encoder changes (the real bandwidth / CPU win):
- CallEncoder::encode_frame and engine.rs encode loop now gate the
  RaptorQ path on !codec.is_opus():
    - Opus source packets emit fec_block=0, fec_symbol=0,
      fec_ratio_encoded=0 in the MediaHeader
    - fec_enc.add_source_symbol is skipped on Opus
    - generate_repair + repair packet emission is skipped on Opus
    - block_id and frame_in_block counters stay frozen at 0 for Opus
- Codec2 path is byte-for-byte identical to pre-Phase-2 behavior.

Decoder changes (mostly cleanup, since both live decoder paths were
already reading audio directly from source packets and only using the
RaptorQ decoder output for stats):
- CallDecoder::ingest skips fec_dec.add_symbol on Opus packets. Source
  packets still flow to the jitter buffer; Opus repair packets from old
  senders are dropped cleanly (repair packets never hit the jitter
  buffer either).
- engine.rs recv loop skips fec_dec.add_symbol, fec_dec.try_decode, and
  fec_dec.expire_before on Opus packets. The `fec_recovered` stat
  counter becomes Codec2-only (a separate DRED reconstruction counter
  lands in Phase 4).

Wire-format backward compat verified at pre-flight:
- Old receiver + new sender: engine.rs pipeline.rs path gates on
  non-zero fec_block/fec_symbol which now never fire for Opus, so the
  RaptorQ decoder simply isn't fed. Audio flows normally. Desktop
  CallDecoder's old path accumulated packets into the stale-eviction
  HashMap, which cleans up after 2s — harmless.
- New receiver + old sender: new receiver skips RaptorQ on Opus so
  old-sender repair packets are ignored entirely (no crash, no double-
  decode). Loses the (previously vestigial) RaptorQ recovery benefit,
  which was never actually active in the audio path. Source packets
  still decode normally.
- No wire format version bump required. MediaHeader is unchanged; we
  just zero the FEC fields on Opus packets.

Test changes:
- Removed `encoder_generates_repair_on_full_block` — asserted the old
  (pre-Phase-2) RaptorQ-on-Opus behavior and is now incorrect. Replaced
  with two symmetric tests:
    - `opus_source_packets_have_zero_fec_header_fields` — verifies
      Phase 2 invariants on Opus packets
    - `opus_encoder_never_emits_repair_packets` — runs 20 frames of
      non-silent sine wave through a GOOD-profile encoder, asserts
      exactly 20 output packets, zero repair
    - `codec2_encoder_generates_repair_on_full_block` — same shape as
      the old test but on CATASTROPHIC profile (Codec2 1200, 8
      frames/block, ratio 1.0) to verify Codec2 path still emits
      repairs as before

Verification:
- cargo check --workspace: zero errors
- cargo test -p wzp-codec --lib: 61 passing (Phase 1 baseline held)
- cargo test -p wzp-client --lib: 32 passing (+3 new Phase 2 tests,
  -1 old test removed)
- cargo check -p wzp-android --lib: zero errors (host link of
  wzp-android tests fails on -llog per pre-existing Android-only
  build.rs, unrelated to this work; integration build via
  build-and-notify.sh will validate Android end-to-end)
- Pre-existing broken integration test in
  crates/wzp-client/tests/handshake_integration.rs (SignalMessage
  schema drift) is NOT caused by this commit — baseline had the same
  3 compile errors before Phase 2. Flagged as a separate cleanup task.

Expected observable effects on a real call:
- Opus 24k outgoing bitrate drops from ~28.8 kbps (ratio 0.2 RaptorQ)
  to ~25 kbps (base 24 kbps + DRED ~1–10 kbps signal-dependent)
- Opus receive-side latency drops ~40 ms on clean network (no more
  block wait — jitter buffer emits as soon as a source packet arrives)
- Codec2 calls show no latency or bitrate change

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:42:33 +04:00

WarzonePhone

Custom lossy VoIP protocol built in Rust. E2E encrypted, FEC-protected, adaptive quality, designed for hostile network conditions.

Quick Start

# Build
cargo build --release

# Run relay
./target/release/wzp-relay --listen 0.0.0.0:4433

# Send a test tone
./target/release/wzp-client --send-tone 5 relay-addr:4433

# Web bridge (browser calls)
./target/release/wzp-web --port 8080 --relay 127.0.0.1:4433 --tls
# Open https://localhost:8080/room-name in two browser tabs

Architecture

See docs/ARCHITECTURE.md for the full system architecture with Mermaid diagrams covering:

  • System overview and data flow
  • Crate dependency graph (8 crates)
  • Wire formats (MediaHeader, MiniHeader, TrunkFrame, SignalMessage)
  • Cryptographic handshake (X25519 + Ed25519 + ChaCha20-Poly1305)
  • Identity model (BIP39 seed, featherChat compatible)
  • Quality profiles (GOOD/DEGRADED/CATASTROPHIC)
  • FEC protection (RaptorQ with interleaving)
  • Adaptive jitter buffer (NetEq-inspired)
  • Telemetry stack (Prometheus + Grafana)
  • Deployment topology

Features

  • 3 quality tiers: Opus 24k (28.8 kbps) / Opus 6k (9 kbps) / Codec2 1200 (2.4 kbps)
  • RaptorQ FEC: Recovers from 20-100% packet loss depending on tier
  • E2E encryption: ChaCha20-Poly1305 with X25519 key exchange
  • Adaptive jitter buffer: EMA-based playout delay tracking
  • Silence suppression: VAD + comfort noise (~50% bandwidth savings)
  • ML noise removal: RNNoise (nnnoiseless pure Rust port)
  • Mini-frames: 67% header compression for steady-state packets
  • Trunking: Multiplex sessions into batched datagrams
  • featherChat integration: Shared BIP39 identity, token auth, call signaling
  • Prometheus metrics: Relay, web bridge, inter-relay probes
  • Grafana dashboard: Pre-built JSON with 18 panels

Documentation

Document Description
ARCHITECTURE.md Full system architecture with diagrams
TELEMETRY.md Prometheus metrics specification
INTEGRATION_TASKS.md featherChat integration tracker
WZP-FC-SHARED-CRATES.md Shared crate strategy
grafana-dashboard.json Importable Grafana dashboard

Binaries

Binary Description
wzp-relay Relay daemon (SFU room mode, forward mode, probes)
wzp-client CLI client (send-tone, record, live mic, echo-test, drift-test, sweep)
wzp-web Browser bridge (HTTPS + WebSocket + AudioWorklet)
wzp-bench Component benchmarks

Linux Build

./scripts/build-linux.sh --prepare   # Create Hetzner VM + install deps
./scripts/build-linux.sh --build     # Build release binaries
./scripts/build-linux.sh --transfer  # Download to target/linux-x86_64/
./scripts/build-linux.sh --destroy   # Delete VM

Tests

cargo test --workspace   # 272 tests

License

MIT OR Apache-2.0

Description
No description provided
Readme 147 MiB
Languages
Rust 78%
Kotlin 7.9%
Shell 6.7%
TypeScript 3.2%
C++ 1.5%
Other 2.6%