use crate::celt_redundancy::{RedundancyDecision, RedundancyPosition};
use crate::framing::OpusFrameRouting;
use crate::toc::{Bandwidth, ChannelMapping};
pub const REDUNDANT_FRAME_TENTHS_MS: u16 = 50;
pub const REDUNDANT_CROSS_LAP_TENTHS_MS: u16 = REDUNDANT_FRAME_TENTHS_MS / 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossLapPlacement {
FirstHalfAsIs,
SecondHalfAsIs,
}
impl CrossLapPlacement {
pub fn from_position(position: RedundancyPosition) -> Self {
match position {
RedundancyPosition::Beginning => CrossLapPlacement::FirstHalfAsIs,
RedundancyPosition::End => CrossLapPlacement::SecondHalfAsIs,
}
}
pub fn uses_first_half(self) -> bool {
matches!(self, CrossLapPlacement::FirstHalfAsIs)
}
pub fn second_half_is_used_as_is(self) -> bool {
let _ = self;
false
}
}
pub fn apply_mb_to_wb_override(carrier_bandwidth: Bandwidth, is_silk_only: bool) -> Bandwidth {
if is_silk_only && matches!(carrier_bandwidth, Bandwidth::Mb) {
Bandwidth::Wb
} else {
carrier_bandwidth
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RedundantFrameParams {
pub duration_tenths_ms: u16,
pub channels: ChannelMapping,
pub bandwidth: Bandwidth,
pub position: RedundancyPosition,
pub size_bytes: usize,
pub cross_lap: CrossLapPlacement,
}
impl RedundantFrameParams {
pub fn cross_lap_tenths_ms(&self) -> u16 {
REDUNDANT_CROSS_LAP_TENTHS_MS
}
pub fn first_half_is_used_as_is(&self) -> bool {
self.cross_lap.uses_first_half()
}
}
pub fn redundant_frame_params(
routing: &OpusFrameRouting,
decision: RedundancyDecision,
) -> Option<RedundantFrameParams> {
let (position, size_bytes) = match decision {
RedundancyDecision::Present {
position,
size_bytes,
} => (position, size_bytes),
RedundancyDecision::NotPresent | RedundancyDecision::Invalid => return None,
};
let channels = routing.channels;
let is_silk_only = matches!(
routing.operating_mode,
crate::framing::OperatingMode::SilkOnly
);
let bandwidth = apply_mb_to_wb_override(routing.toc_bandwidth, is_silk_only);
let cross_lap = CrossLapPlacement::from_position(position);
Some(RedundantFrameParams {
duration_tenths_ms: REDUNDANT_FRAME_TENTHS_MS,
channels,
bandwidth,
position,
size_bytes,
cross_lap,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::framing::OperatingMode;
use crate::toc::{Bandwidth, ChannelMapping, FrameCountCode, Mode};
fn fake_routing(
operating_mode: OperatingMode,
toc_bandwidth: Bandwidth,
channels: ChannelMapping,
) -> OpusFrameRouting {
let silk_layer = matches!(
operating_mode,
OperatingMode::SilkOnly | OperatingMode::Hybrid
);
let celt_layer = matches!(
operating_mode,
OperatingMode::Hybrid | OperatingMode::CeltOnly
);
let silk_bandwidth = if silk_layer {
Some(match operating_mode {
OperatingMode::Hybrid => crate::framing::SilkBandwidth::Wb,
OperatingMode::SilkOnly => match toc_bandwidth {
Bandwidth::Nb => crate::framing::SilkBandwidth::Nb,
Bandwidth::Mb => crate::framing::SilkBandwidth::Mb,
Bandwidth::Wb | Bandwidth::Swb | Bandwidth::Fb => {
crate::framing::SilkBandwidth::Wb
}
},
OperatingMode::CeltOnly => unreachable!(),
})
} else {
None
};
OpusFrameRouting {
operating_mode,
toc_bandwidth,
frame_size_tenths_ms: 200, channels,
silk_layer,
celt_layer,
silk_bandwidth,
silk_frames_per_channel: if silk_layer { Some(1) } else { None },
}
}
#[test]
fn redundant_frame_tenths_ms_is_50() {
assert_eq!(REDUNDANT_FRAME_TENTHS_MS, 50);
}
#[test]
fn cross_lap_is_half_of_redundant_frame() {
assert_eq!(REDUNDANT_CROSS_LAP_TENTHS_MS, 25);
assert_eq!(REDUNDANT_CROSS_LAP_TENTHS_MS * 2, REDUNDANT_FRAME_TENTHS_MS);
}
#[test]
fn cross_lap_from_position_total() {
assert_eq!(
CrossLapPlacement::from_position(RedundancyPosition::Beginning),
CrossLapPlacement::FirstHalfAsIs
);
assert_eq!(
CrossLapPlacement::from_position(RedundancyPosition::End),
CrossLapPlacement::SecondHalfAsIs
);
}
#[test]
fn uses_first_half_accessor() {
assert!(CrossLapPlacement::FirstHalfAsIs.uses_first_half());
assert!(!CrossLapPlacement::SecondHalfAsIs.uses_first_half());
}
#[test]
fn second_half_is_never_used_as_is() {
assert!(!CrossLapPlacement::FirstHalfAsIs.second_half_is_used_as_is());
assert!(!CrossLapPlacement::SecondHalfAsIs.second_half_is_used_as_is());
}
#[test]
fn mb_to_wb_override_silk_only_mb() {
assert_eq!(apply_mb_to_wb_override(Bandwidth::Mb, true), Bandwidth::Wb);
}
#[test]
fn mb_to_wb_override_skipped_for_hybrid() {
assert_eq!(apply_mb_to_wb_override(Bandwidth::Mb, false), Bandwidth::Mb);
}
#[test]
fn mb_to_wb_override_passthrough_for_nb_wb_swb_fb() {
for is_silk_only in [false, true] {
for bw in [Bandwidth::Nb, Bandwidth::Wb, Bandwidth::Swb, Bandwidth::Fb] {
assert_eq!(apply_mb_to_wb_override(bw, is_silk_only), bw);
}
}
}
#[test]
fn params_none_for_not_present() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
assert!(redundant_frame_params(&routing, RedundancyDecision::NotPresent).is_none());
}
#[test]
fn params_none_for_invalid() {
let routing = fake_routing(
OperatingMode::Hybrid,
Bandwidth::Swb,
ChannelMapping::Stereo,
);
assert!(redundant_frame_params(&routing, RedundancyDecision::Invalid).is_none());
}
#[test]
fn silk_only_nb_beginning_passes_nb_through() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Nb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 8,
};
let params = redundant_frame_params(&routing, decision).expect("should be present");
assert_eq!(params.duration_tenths_ms, REDUNDANT_FRAME_TENTHS_MS);
assert_eq!(params.channels, ChannelMapping::Mono);
assert_eq!(params.bandwidth, Bandwidth::Nb);
assert_eq!(params.position, RedundancyPosition::Beginning);
assert_eq!(params.size_bytes, 8);
assert_eq!(params.cross_lap, CrossLapPlacement::FirstHalfAsIs);
assert!(params.first_half_is_used_as_is());
assert_eq!(params.cross_lap_tenths_ms(), 25);
}
#[test]
fn silk_only_mb_bumps_to_wb() {
let routing = fake_routing(
OperatingMode::SilkOnly,
Bandwidth::Mb,
ChannelMapping::Stereo,
);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: 12,
};
let params = redundant_frame_params(&routing, decision).expect("should be present");
assert_eq!(params.bandwidth, Bandwidth::Wb);
assert_eq!(params.channels, ChannelMapping::Stereo);
assert_eq!(params.position, RedundancyPosition::End);
assert_eq!(params.size_bytes, 12);
assert_eq!(params.cross_lap, CrossLapPlacement::SecondHalfAsIs);
assert!(!params.first_half_is_used_as_is());
}
#[test]
fn silk_only_wb_passes_through() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 16,
};
let params = redundant_frame_params(&routing, decision).expect("should be present");
assert_eq!(params.bandwidth, Bandwidth::Wb);
}
#[test]
fn hybrid_swb_passes_through() {
let routing = fake_routing(
OperatingMode::Hybrid,
Bandwidth::Swb,
ChannelMapping::Stereo,
);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 4,
};
let params = redundant_frame_params(&routing, decision).expect("should be present");
assert_eq!(params.bandwidth, Bandwidth::Swb);
assert_eq!(params.channels, ChannelMapping::Stereo);
}
#[test]
fn hybrid_fb_passes_through() {
let routing = fake_routing(OperatingMode::Hybrid, Bandwidth::Fb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: 7,
};
let params = redundant_frame_params(&routing, decision).expect("should be present");
assert_eq!(params.bandwidth, Bandwidth::Fb);
}
#[test]
fn channel_count_inherited_under_all_carriers() {
let cases: &[(OperatingMode, Bandwidth)] = &[
(OperatingMode::SilkOnly, Bandwidth::Nb),
(OperatingMode::SilkOnly, Bandwidth::Mb),
(OperatingMode::SilkOnly, Bandwidth::Wb),
(OperatingMode::Hybrid, Bandwidth::Swb),
(OperatingMode::Hybrid, Bandwidth::Fb),
];
for &(mode, bw) in cases {
for &chan in &[ChannelMapping::Mono, ChannelMapping::Stereo] {
let routing = fake_routing(mode, bw, chan);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 3,
};
let params =
redundant_frame_params(&routing, decision).expect("present should populate");
assert_eq!(params.channels, chan, "{mode:?} {bw:?} {chan:?}");
}
}
}
#[test]
fn duration_is_fixed_at_50_tenths() {
let mut routing =
fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
for carrier_dur in [100u16, 200, 400, 600] {
routing.frame_size_tenths_ms = carrier_dur;
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: 5,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(
params.duration_tenths_ms, REDUNDANT_FRAME_TENTHS_MS,
"carrier {carrier_dur} should still produce 5 ms redundant"
);
}
}
#[test]
fn figure18_celt_to_silk_with_redundancy() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 10,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.cross_lap, CrossLapPlacement::FirstHalfAsIs);
assert!(params.first_half_is_used_as_is());
assert_eq!(params.bandwidth, Bandwidth::Wb);
}
#[test]
fn figure18_celt_to_hybrid_with_redundancy() {
let routing = fake_routing(OperatingMode::Hybrid, Bandwidth::Fb, ChannelMapping::Stereo);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 14,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.cross_lap, CrossLapPlacement::FirstHalfAsIs);
assert_eq!(params.bandwidth, Bandwidth::Fb);
assert_eq!(params.channels, ChannelMapping::Stereo);
}
#[test]
fn figure18_silk_to_celt_with_redundancy() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: 6,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.cross_lap, CrossLapPlacement::SecondHalfAsIs);
assert!(!params.first_half_is_used_as_is());
}
#[test]
fn figure18_hybrid_to_celt_with_redundancy() {
let routing = fake_routing(OperatingMode::Hybrid, Bandwidth::Swb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: 9,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.cross_lap, CrossLapPlacement::SecondHalfAsIs);
assert_eq!(params.bandwidth, Bandwidth::Swb);
}
#[test]
fn figure18_silk_to_silk_mb_carrier_bumps_to_wb() {
let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Mb, ChannelMapping::Mono);
for pos in [RedundancyPosition::Beginning, RedundancyPosition::End] {
let decision = RedundancyDecision::Present {
position: pos,
size_bytes: 5,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.bandwidth, Bandwidth::Wb, "MB→WB override on {pos:?}");
}
}
#[test]
fn carrier_frame_count_code_irrelevant() {
let _ = FrameCountCode::One; let _ = Mode::SilkOnly; let routing = fake_routing(OperatingMode::SilkOnly, Bandwidth::Wb, ChannelMapping::Mono);
let decision = RedundancyDecision::Present {
position: RedundancyPosition::Beginning,
size_bytes: 11,
};
let params_a = redundant_frame_params(&routing, decision).expect("present");
let mut routing2 = routing;
routing2.silk_frames_per_channel = Some(3);
let params_b = redundant_frame_params(&routing2, decision).expect("present");
assert_eq!(params_a, params_b);
}
#[test]
fn size_bytes_forwarded() {
let routing = fake_routing(OperatingMode::Hybrid, Bandwidth::Swb, ChannelMapping::Mono);
for &sz in &[2usize, 3, 7, 16, 64, 257, 1000] {
let decision = RedundancyDecision::Present {
position: RedundancyPosition::End,
size_bytes: sz,
};
let params = redundant_frame_params(&routing, decision).expect("present");
assert_eq!(params.size_bytes, sz);
}
}
#[test]
fn total_function_sweep_no_mb_in_output() {
let modes = [
OperatingMode::SilkOnly,
OperatingMode::Hybrid,
];
let bws_silk = [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb];
let bws_hyb = [Bandwidth::Swb, Bandwidth::Fb];
let chans = [ChannelMapping::Mono, ChannelMapping::Stereo];
let positions = [RedundancyPosition::Beginning, RedundancyPosition::End];
for mode in modes {
let bw_list: &[Bandwidth] = if matches!(mode, OperatingMode::SilkOnly) {
&bws_silk
} else {
&bws_hyb
};
for &bw in bw_list {
for &chan in &chans {
for &pos in &positions {
let routing = fake_routing(mode, bw, chan);
let decision = RedundancyDecision::Present {
position: pos,
size_bytes: 4,
};
let params = redundant_frame_params(&routing, decision)
.expect("Present should always yield params");
assert_ne!(
params.bandwidth,
Bandwidth::Mb,
"{mode:?}/{bw:?}/{chan:?}/{pos:?} must not emit MB"
);
assert_eq!(
params.duration_tenths_ms, REDUNDANT_FRAME_TENTHS_MS,
"{mode:?}/{bw:?}/{chan:?}/{pos:?}"
);
}
}
}
}
}
}