use crate::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Opus {
pub audio: Audio,
pub dops: Dops,
pub btrt: Option<Btrt>,
}
impl Atom for Opus {
const KIND: FourCC = FourCC::new(b"Opus");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let audio = Audio::decode(buf)?;
let mut dops = None;
let mut btrt = None;
while let Some(atom) = Any::decode_maybe(buf)? {
match atom {
Any::Dops(atom) => dops = atom.into(),
Any::Btrt(atom) => btrt = atom.into(),
unknown => Self::decode_unknown(&unknown)?,
}
}
skip_trailing_padding(buf);
Ok(Self {
audio,
dops: dops.ok_or(Error::MissingBox(Dops::KIND))?,
btrt,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.audio.encode(buf)?;
self.dops.encode(buf)?;
self.btrt.encode(buf)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Dops {
pub output_channel_count: u8,
pub pre_skip: u16,
pub input_sample_rate: u32,
pub output_gain: i16,
pub channel_mapping: Option<OpusChannelMapping>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OpusChannelMapping {
pub family: u8,
pub stream_count: u8,
pub coupled_count: u8,
pub channel_mapping: Vec<u8>,
}
impl OpusChannelMapping {
fn validate(&self, output_channel_count: u8) -> Result<()> {
if self.family == 0 {
return Err(Error::InvalidCombination(
"Opus channel mapping family 0 has no table",
));
}
if output_channel_count == 0 {
return Err(Error::InvalidCombination(
"Opus output channel count must not be zero",
));
}
if self.channel_mapping.len() != output_channel_count as usize {
return Err(Error::InvalidCombination(
"Opus channel mapping needs one entry per output channel",
));
}
if self.stream_count == 0 {
return Err(Error::InvalidCombination(
"Opus stream count must not be zero",
));
}
if self.coupled_count > self.stream_count {
return Err(Error::InvalidCombination(
"Opus coupled count exceeds the stream count",
));
}
let decoded = self.stream_count as u16 + self.coupled_count as u16;
if decoded > 255 {
return Err(Error::InvalidCombination(
"Opus streams decode to more than 255 channels",
));
}
if self
.channel_mapping
.iter()
.any(|&index| index != 255 && index as u16 >= decoded)
{
return Err(Error::InvalidCombination(
"Opus channel mapping names a channel past the decoded ones",
));
}
Ok(())
}
}
impl Atom for Dops {
const KIND: FourCC = FourCC::new(b"dOps");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let version = u8::decode(buf)?;
if version != 0 {
return Err(Error::UnknownVersion(version));
}
let output_channel_count = u8::decode(buf)?;
let pre_skip = u16::decode(buf)?;
let input_sample_rate = u32::decode(buf)?;
let output_gain = i16::decode(buf)?;
let family = u8::decode(buf)?;
let channel_mapping = match family {
0 => None,
family => {
let mapping = OpusChannelMapping {
family,
stream_count: u8::decode(buf)?,
coupled_count: u8::decode(buf)?,
channel_mapping: Vec::decode_exact(buf, output_channel_count as usize)?,
};
mapping.validate(output_channel_count)?;
Some(mapping)
}
};
Ok(Self {
output_channel_count,
pre_skip,
input_sample_rate,
output_gain,
channel_mapping,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
if let Some(mapping) = &self.channel_mapping {
mapping.validate(self.output_channel_count)?;
}
(0u8).encode(buf)?;
self.output_channel_count.encode(buf)?;
self.pre_skip.encode(buf)?;
self.input_sample_rate.encode(buf)?;
self.output_gain.encode(buf)?;
match &self.channel_mapping {
None => (0u8).encode(buf)?,
Some(mapping) => {
mapping.family.encode(buf)?;
mapping.stream_count.encode(buf)?;
mapping.coupled_count.encode(buf)?;
mapping.channel_mapping.encode(buf)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
const ENCODED_DOPS_STEREO: &[u8] = &[
0x00, 0x00, 0x00, 0x13, b'd', b'O', b'p', b's', 0x00, 0x02, 0x01, 0x38, 0x00, 0x00, 0xbb,
0x80, 0xff, 0xfe, 0x00,
];
const ENCODED_DOPS_SURROUND: &[u8] = &[
0x00, 0x00, 0x00, 0x1b, b'd', b'O', b'p', b's', 0x00, 0x06, 0x01, 0x38, 0x00, 0x00, 0xbb,
0x80, 0x00, 0x00, 0x01, 0x04, 0x02, 0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
];
fn surround() -> Dops {
Dops {
output_channel_count: 6,
pre_skip: 312,
input_sample_rate: 48000,
output_gain: 0,
channel_mapping: Some(OpusChannelMapping {
family: 1,
stream_count: 4,
coupled_count: 2,
channel_mapping: vec![0, 4, 1, 2, 3, 5],
}),
}
}
fn decode(bytes: &[u8]) -> Result<Dops> {
Dops::decode(&mut std::io::Cursor::new(bytes))
}
fn encode(dops: &Dops) -> Result<Vec<u8>> {
let mut buf = Vec::new();
dops.encode(&mut buf)?;
Ok(buf)
}
#[test]
fn test_dops_family_0() {
let dops = Dops {
output_channel_count: 2,
pre_skip: 312,
input_sample_rate: 48000,
output_gain: -2,
channel_mapping: None,
};
assert_eq!(decode(ENCODED_DOPS_STEREO).unwrap(), dops);
assert_eq!(encode(&dops).unwrap(), ENCODED_DOPS_STEREO);
}
#[test]
fn test_dops_family_1() {
let dops = surround();
assert_eq!(decode(ENCODED_DOPS_SURROUND).unwrap(), dops);
assert_eq!(encode(&dops).unwrap(), ENCODED_DOPS_SURROUND);
}
#[test]
fn test_dops_any_family() {
for family in [2, 3, 255] {
let mut dops = surround();
dops.channel_mapping.as_mut().unwrap().family = family;
let encoded = encode(&dops).unwrap();
assert_eq!(decode(&encoded).unwrap(), dops);
}
}
#[test]
fn test_dops_silent_channel() {
let mut dops = surround();
dops.channel_mapping.as_mut().unwrap().channel_mapping[5] = 255;
let encoded = encode(&dops).unwrap();
assert_eq!(decode(&encoded).unwrap(), dops);
}
#[test]
fn test_dops_truncated_table() {
let mut bytes = ENCODED_DOPS_SURROUND[..ENCODED_DOPS_SURROUND.len() - 1].to_vec();
bytes[3] -= 1;
assert!(decode(&bytes).is_err());
}
#[test]
fn test_dops_rejects_invalid_table() {
let invalid: [fn(&mut OpusChannelMapping); 4] = [
|mapping| mapping.stream_count = 0,
|mapping| mapping.coupled_count = mapping.stream_count + 1,
|mapping| mapping.channel_mapping[0] = 6,
|mapping| {
mapping.stream_count = 200;
mapping.coupled_count = 100;
},
];
for corrupt in invalid {
let mut dops = surround();
corrupt(dops.channel_mapping.as_mut().unwrap());
assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
let mapping = dops.channel_mapping.as_ref().unwrap();
let mut bytes = ENCODED_DOPS_SURROUND.to_vec();
bytes[19] = mapping.stream_count;
bytes[20] = mapping.coupled_count;
bytes[21..].copy_from_slice(&mapping.channel_mapping);
assert!(matches!(decode(&bytes), Err(Error::InvalidCombination(_))));
}
}
#[test]
fn test_dops_rejects_family_0_table() {
let mut dops = surround();
dops.channel_mapping.as_mut().unwrap().family = 0;
assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
}
#[test]
fn test_dops_rejects_table_length_mismatch() {
let mut dops = surround();
dops.output_channel_count = 5;
assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
}
}