From 00fa109f0790cf472d7e85ca1acf4ea1e5c44e8f Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Tue, 7 Apr 2026 10:34:14 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20codec2=20support=20=E2=80=94=20adaptive?= =?UTF-8?q?=20encoder/decoder,=20per-packet=20codec=20switch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Android engine: - Use wzp_codec::create_encoder/create_decoder (factory) instead of hardcoded OpusEncoder/OpusDecoder - Recv path: auto-switch decoder based on incoming packet's codec_id - Supports mixed-codec rooms (one client Opus, another Codec2) Desktop client already uses factory functions — no changes needed. Codec selection via QualityProfile: - GOOD: Opus 24kbps - DEGRADED: Opus 6kbps - CATASTROPHIC: Codec2 1200bps Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/wzp-android/src/engine.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/wzp-android/src/engine.rs b/crates/wzp-android/src/engine.rs index b9ada0d..95d7011 100644 --- a/crates/wzp-android/src/engine.rs +++ b/crates/wzp-android/src/engine.rs @@ -16,8 +16,6 @@ use std::time::Instant; use bytes::Bytes; use tracing::{error, info, warn}; use wzp_codec::agc::AutoGainControl; -use wzp_codec::opus_dec::OpusDecoder; -use wzp_codec::opus_enc::OpusEncoder; use wzp_crypto::{KeyExchange, WarzoneKeyExchange}; use wzp_fec::{RaptorQFecDecoder, RaptorQFecEncoder}; use wzp_proto::{ @@ -333,11 +331,9 @@ async fn run_call( stats.state = CallState::Active; } - // Initialize Opus codec - let mut encoder = - OpusEncoder::new(profile).map_err(|e| anyhow::anyhow!("opus encoder init: {e}"))?; - let mut decoder = - OpusDecoder::new(profile).map_err(|e| anyhow::anyhow!("opus decoder init: {e}"))?; + // Initialize codec (Opus or Codec2 based on profile) + let mut encoder = wzp_codec::create_encoder(profile); + let mut decoder = wzp_codec::create_decoder(profile); // Initialize FEC encoder/decoder let mut fec_enc = wzp_fec::create_encoder(&profile); @@ -598,6 +594,14 @@ async fn run_call( // Source packets: decode directly if !is_repair { + // Switch decoder to match incoming codec if different + if pkt.header.codec_id != decoder.codec_id() { + let switch_profile = QualityProfile { + codec: pkt.header.codec_id, + ..profile + }; + let _ = decoder.set_profile(switch_profile); + } match decoder.decode(&pkt.payload, &mut decode_buf) { Ok(samples) => { playout_agc.process_frame(&mut decode_buf[..samples]);