Integrate Opus DRED

- Update to webrtc 7680e
This commit is contained in:
Jim Gustafson 2026-04-16 18:37:55 -07:00
parent db369de324
commit c39bdb22a7
7 changed files with 179 additions and 57 deletions

View File

@ -993,6 +993,99 @@ async fn run_plc_tests(test: &mut Test) -> Result<()> {
Ok(())
}
async fn run_dred_tests(test: &mut Test) -> Result<()> {
let configs = [
(60, true, 0, Some(0)), // Before DRED with FEC and Opus PLC
(60, false, 6, Some(5)), // DRED 60ms
(60, false, 12, Some(5)), // DRED 120ms
(60, false, 25, Some(5)), // DRED 250ms
(60, false, 50, Some(5)), // DRED 500ms
(60, false, 100, Some(5)), // DRED 1s
];
let losses = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50];
let test_cases: Vec<_> = configs
.iter()
.flat_map(|&(initial_packet_size_ms, enable_fec, dred_duration, decoder_complexity)| {
losses.iter().map(move |&loss| TestCaseConfig {
test_case_name: format!(
"ptime_{initial_packet_size_ms}-fec_{enable_fec}-dred_duration_{dred_duration}-plc_{}-loss_{loss}",
decoder_complexity.map_or("None".to_string(), |c| c.to_string())
),
length_seconds: 30,
client_a_config: CallConfig {
audio: AudioConfig {
input_name: "normal_phrasing".to_string(),
generate_spectrogram: false,
visqol_speech_analysis: false,
visqol_audio_analysis: false,
pesq_speech_analysis: false,
plc_speech_analysis: false,
complexity: 9,
initial_packet_size_ms,
enable_fec,
dred_duration,
decoder_complexity,
// Force DRED to be encoded with the expected loss rate.
min_packet_loss_percent: if dred_duration > 0 { loss } else { 0 },
// Only used for Deep PLC (decoder complexity 5) or DRED tests.
dnn_weights_path: "/data/deep_plc-dred-weights.bin".to_string(),
..Default::default()
},
profile: DeterministicLoss(loss),
..Default::default()
},
client_b_config: CallConfig {
audio: AudioConfig {
input_name: "normal_phrasing".to_string(),
visqol_speech_analysis: true,
visqol_audio_analysis: true,
pesq_speech_analysis: true,
plc_speech_analysis: true,
complexity: 9,
initial_packet_size_ms,
enable_fec,
dred_duration,
decoder_complexity,
// Force DRED to be encoded with the expected loss rate.
min_packet_loss_percent: if dred_duration > 0 { loss } else { 0 },
// Only used for Deep PLC (decoder complexity 5) or DRED tests.
dnn_weights_path: "/data/deep_plc-dred-weights.bin".to_string(),
..Default::default()
},
profile: DeterministicLoss(loss),
..Default::default()
},
iterations: 3,
..Default::default()
})
})
.collect();
test.run(
GroupConfig {
group_name: "dred_tests".to_string(),
summary_report_columns: SummaryReportColumns {
show_visqol_mos_speech: true,
show_visqol_mos_audio: true,
show_pesq_mos: true,
show_plc_mos: true,
show_video: false,
show_send_stats: false,
show_dred_stats: true,
..Default::default()
},
..Default::default()
},
test_cases,
vec![NetworkProfile::None],
)
.await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
@ -1078,6 +1171,7 @@ async fn main() -> Result<()> {
"changing_bandwidth_audio_test" => run_changing_bandwidth_audio_test(test).await?,
"profiling_suite" => run_perf_test(test).await?,
"plc_tests" => run_plc_tests(test).await?,
"dred_tests" => run_dred_tests(test).await?,
_ => panic!("unknown test set \"{test_set_name}\""),
}
test.report().await?;

View File

@ -169,6 +169,7 @@ pub struct MediaFileIo {
}
impl Test {
#[allow(clippy::too_many_arguments)]
pub fn new(
root_path: &PathBuf,
output_dir: &str,

View File

@ -1,4 +1,4 @@
webrtc.version=7680c
webrtc.version=7680e
ringrtc.version.major=2
ringrtc.version.minor=68

View File

@ -1,10 +1,10 @@
{
"android": "8be76daee99a7ea273683dbcd396f92db4d7aa0a7a0aac61631cd53693322cf0",
"ios": "665f1f19f3de74444c7953f70d599a94c57113374ac62d558f9bce11dc21d1e8",
"linux-x64": "74b2e7f9e36b24e81eb06df067e9459d97a9f4d89e1d10dc81c766fb823d1f51",
"linux-arm64": "77ee9067a80db4556b02f1f8d97a363ddd2f53499c7e0b1c945bc753d840458d",
"mac-x64": "217bc4868e2751b51cd05e332fc87f219b314c8312e1cf02ec75d3be20eefdcc",
"mac-arm64": "60536d4de2926fe5a71dae57d671f8d43e1b6cbbb55e555e3cfd79b40c6e6f48",
"windows-x64": "c28a52dc851655b45bcc90251b2cc32aa0741e5b34c9ec6ec3ecefbe970f9ce6",
"windows-arm64": "0cafb1d30500fe4f3dca465202d6bffb3c54e2ae5dc6e05ce638d600d132b60a"
"android": "220b03c2a3af7b9262459aee837b432c6c05d120887c0fe5fb6283f3721e99cb",
"ios": "43642dea9ebf9db29d69f0cb1dd9082978a69501bf0e69a14eaf37d015ae1faa",
"linux-x64": "194055090f078b7e9f946b9f756144612b854f0d17fe077824d52753d7d7d916",
"linux-arm64": "00c54e0fd9afd38c28f011d9ea5d09ba0472e4ae2c93ac756d8e051b55f9f8bf",
"mac-x64": "07ac844dc85d808cbfead56e38584515b623efaddcd49dee54cb74da2f9338a0",
"mac-arm64": "f35c85ebfaa6bc6dc70d56df552c0805c40f62643e8ff12df23093b663102273",
"windows-x64": "8c748f17a958c96c931040329c95cb6235b782ffea2d19c5e1c0ed43b5f12d81",
"windows-arm64": "93457fb51b8cd9cbfdcb2dff2a591896fb971c4ddcd38ac1854530e3f2173b29"
}

View File

@ -37,7 +37,9 @@ use crate::{
},
error::RingRtcError,
webrtc::{
ice_gatherer::IceGatherer, media::MediaStream, peer_connection::AudioLevel,
ice_gatherer::IceGatherer,
media::{MediaStream, configure_dred_from_assets},
peer_connection::AudioLevel,
peer_connection_observer::NetworkRoute,
},
};
@ -583,47 +585,11 @@ where
let mut call_manager = self.call_manager()?;
// Change the call configuration based on available assets. Don't use this
// if building for the call_sim feature.
if cfg!(not(feature = "call_sim")) && call_config.audio_encoder_config.dred_duration > 0 {
// See if model assets are available for Opus DRED (including Deep PLC).
info!("assets: Checking for opus-dred assets in the registry...");
if let Some(versions) = self.asset_registry.get_options_for("opus-dred") {
info!(
"assets: There are {} opus-dred asset versions available",
versions.len()
);
if let Some(version) = versions.first() {
if let Some(asset) = self.asset_registry.get_asset("opus-dred", *version) {
info!("assets: opus-dred asset version {version} found");
// Set the weights on both the encoder and decoder.
// Lifetime: Each clone of the Arc<asset_content> will be held by
// a connection or group call object which lives as long as the
// PeerConnection in WebRTC.
call_config.audio_encoder_config.dnn_weights =
Some(Arc::clone(&asset.content));
call_config.audio_decoder_config.dnn_weights = Some(asset.content);
// Set up Opus for the optimal DRED configuration.
// Disable FEC and make sure that Opus Deep PLC is always used.
call_config.audio_encoder_config.enable_fec = false;
call_config.audio_decoder_config.complexity = Some(5);
} else {
warn!("assets: Failed to get opus-dred asset version {version}!");
}
} else {
warn!("assets: No versions found for opus-dred!");
}
} else {
warn!("assets: No opus-dred assets found!");
}
if call_config.audio_encoder_config.dnn_weights.is_none() {
warn!("assets: Disabling DRED since no dnn weights were found!");
call_config.audio_encoder_config.dred_duration = 0;
}
}
configure_dred_from_assets(
&self.asset_registry,
&mut call_config.audio_encoder_config,
&mut call_config.audio_decoder_config,
);
match self.direction {
// This happens after received_offer and an offer is put in self.pending_call.

View File

@ -63,7 +63,8 @@ use crate::{
webrtc::{
self,
media::{
AudioEncoderConfig, AudioTrack, VideoFrame, VideoFrameMetadata, VideoSink, VideoTrack,
AudioDecoderConfig, AudioEncoderConfig, AudioTrack, VideoFrame, VideoFrameMetadata,
VideoSink, VideoTrack, configure_dred_from_assets,
},
peer_connection::{AudioLevel, PeerConnection, Protocol, ReceivedAudioLevel, SendRates},
peer_connection_factory::{self as pcf, AudioJitterBufferConfig, PeerConnectionFactory},
@ -2927,12 +2928,24 @@ impl Client {
// Currently, this occurs via on_sfu_client_joined (Joining -> Joined) or
// or via peek_result_inner (Joining -> Pending -> Joined)
fn on_client_joined(state: &mut State) {
let mut encoder_config = AudioEncoderConfig {
dred_duration: state.dred_duration,
..AudioEncoderConfig::default()
};
let mut decoder_config = AudioDecoderConfig::default();
configure_dred_from_assets(
&state.asset_registry,
&mut encoder_config,
&mut decoder_config,
);
state
.peer_connection
.configure_audio_encoders(&AudioEncoderConfig {
dred_duration: state.dred_duration,
..AudioEncoderConfig::default()
});
.configure_audio_encoders(&encoder_config);
state
.peer_connection
.configure_audio_decoders(&decoder_config);
}
pub fn on_signaling_message_received(

View File

@ -12,7 +12,7 @@ use crate::webrtc::ffi::media;
pub use crate::webrtc::peer_connection_factory::RffiPeerConnectionFactoryOwner;
#[cfg(feature = "sim")]
use crate::webrtc::sim::media;
use crate::{lite::sfu::DemuxId, webrtc};
use crate::{core::assets::AssetRegistry, lite::sfu::DemuxId, webrtc};
/// Rust wrapper around WebRTC C++ MediaStream object.
#[derive(Clone, Debug)]
@ -541,3 +541,51 @@ impl AudioDecoderConfig {
}
}
}
/// If dred_duration > 0, looks up DNN weights from the asset registry
/// and configures both encoder and decoder for DRED. If no weights are
/// found, disables DRED by setting dred_duration to 0.
pub fn configure_dred_from_assets(
asset_registry: &AssetRegistry,
encoder_config: &mut AudioEncoderConfig,
decoder_config: &mut AudioDecoderConfig,
) {
// Don't load assets if we aren't using DRED or building for the Call Simulator.
if cfg!(feature = "call_sim") || encoder_config.dred_duration == 0 {
return;
}
info!("assets: Checking for opus-dred assets in the registry...");
if let Some(versions) = asset_registry.get_options_for("opus-dred") {
info!(
"assets: There are {} opus-dred asset versions available",
versions.len()
);
if let Some(version) = versions.first() {
if let Some(asset) = asset_registry.get_asset("opus-dred", *version) {
info!("assets: opus-dred asset version {version} found");
// Set the weights on both the encoder and decoder.
// Lifetime: Each clone of the Arc<asset_content> will be held by
// a connection or group call object which lives as long as the
// PeerConnection in WebRTC.
encoder_config.dnn_weights = Some(Arc::clone(&asset.content));
decoder_config.dnn_weights = Some(asset.content);
// Disable FEC when using DRED.
encoder_config.enable_fec = false;
return;
} else {
warn!("assets: Failed to get opus-dred asset version {version}!");
}
} else {
warn!("assets: No versions found for opus-dred!");
}
} else {
warn!("assets: No opus-dred assets found!");
}
warn!("assets: Disabling DRED since no dnn weights were found!");
encoder_config.dred_duration = 0;
}