use super::*;
impl<'a> ExtensionBodyDef<'a> for SupplementaryAudio<'a> {
const TAG_EXTENSION: u8 = 0x06;
const NAME: &'static str = "SUPPLEMENTARY_AUDIO";
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct SupplementaryAudio<'a> {
pub mix_type: bool,
pub editorial_classification: u8,
pub language_code_present: bool,
pub iso_639_language_code: Option<LangCode>,
pub private_data: &'a [u8],
}
impl<'a> Parse<'a> for SupplementaryAudio<'a> {
type Error = crate::error::Error;
fn parse(sel: &'a [u8]) -> Result<Self> {
if sel.is_empty() {
return Err(Error::BufferTooShort {
need: 1,
have: sel.len(),
what: "supplementary_audio body",
});
}
let flags = sel[0];
let mix_type = (flags & 0x80) != 0;
let editorial_classification = (flags >> 2) & 0x1F;
let language_code_present = (flags & 0x01) != 0;
let mut pos = 1;
let iso_639_language_code = if language_code_present {
if sel.len() < pos + ISO_639_LEN {
return Err(Error::BufferTooShort {
need: pos + ISO_639_LEN,
have: sel.len(),
what: "supplementary_audio body",
});
}
let lc = &sel[pos..pos + ISO_639_LEN];
pos += ISO_639_LEN;
Some(LangCode([lc[0], lc[1], lc[2]]))
} else {
None
};
Ok(SupplementaryAudio {
mix_type,
editorial_classification,
language_code_present,
iso_639_language_code,
private_data: &sel[pos..],
})
}
}
impl Serialize for SupplementaryAudio<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
1 + self.iso_639_language_code.map_or(0, |_| ISO_639_LEN) + self.private_data.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
buf[0] = (u8::from(self.mix_type) << 7)
| ((self.editorial_classification & 0x1F) << 2)
| 0x02
| u8::from(self.language_code_present);
let mut p = 1;
if let Some(lc) = self.iso_639_language_code {
buf[p..p + ISO_639_LEN].copy_from_slice(&lc.0);
p += ISO_639_LEN;
}
buf[p..p + self.private_data.len()].copy_from_slice(self.private_data);
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptors::extension::test_support::*;
use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
use crate::text::LangCode;
#[test]
fn parse_supplementary_audio_with_language() {
let flags = 0x80 | (0x17 << 2) | 0x02 | 0x01;
let sel = [flags, b'f', b'r', b'e', 0xAA];
let bytes = wrap(0x06, &sel);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
match &d.body {
ExtensionBody::SupplementaryAudio(b) => {
assert!(b.mix_type);
assert_eq!(b.editorial_classification, 0x17);
assert!(b.language_code_present);
assert_eq!(b.iso_639_language_code, Some(LangCode(*b"fre")));
assert_eq!(b.private_data, &[0xAA]);
}
other => panic!("expected SupplementaryAudio, got {other:?}"),
}
round_trip(&d);
}
#[test]
fn parse_supplementary_audio_no_language() {
let flags = ((0x01 << 2) & 0x7C) | 0x02; let sel = [flags];
let bytes = wrap(0x06, &sel);
let d = ExtensionDescriptor::parse(&bytes).unwrap();
match &d.body {
ExtensionBody::SupplementaryAudio(b) => {
assert!(!b.language_code_present);
assert_eq!(b.iso_639_language_code, None);
assert!(b.private_data.is_empty());
}
other => panic!("expected SupplementaryAudio, got {other:?}"),
}
round_trip(&d);
}
}