use crate::*;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Keyd {
pub key_namespace: FourCC,
pub key_value: Vec<u8>,
}
impl Atom for Keyd {
const KIND: FourCC = FourCC::new(b"keyd");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let key_namespace = FourCC::decode(buf)?;
let key_value = Vec::decode(buf)?;
Ok(Self {
key_namespace,
key_value,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.key_namespace.encode(buf)?;
self.key_value.encode(buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DataType {
WellKnown(u32),
Custom(String),
Unknown(u32, Vec<u8>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Dtyp {
pub data_type: DataType,
}
impl Atom for Dtyp {
const KIND: FourCC = FourCC::new(b"dtyp");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let namespace = u32::decode(buf)?;
let data_type = match namespace {
0 => DataType::WellKnown(u32::decode(buf)?),
1 => {
let remaining = buf.remaining();
let bytes = buf.slice(remaining).to_vec();
buf.advance(remaining);
DataType::Custom(
String::from_utf8(bytes)
.map_err(|err| Error::InvalidString(err.to_string()))?,
)
}
_ => {
let remaining = buf.remaining();
let raw = buf.slice(remaining).to_vec();
buf.advance(remaining);
DataType::Unknown(namespace, raw)
}
};
Ok(Self { data_type })
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
match &self.data_type {
DataType::WellKnown(code) => {
0u32.encode(buf)?;
code.encode(buf)
}
DataType::Custom(name) => {
1u32.encode(buf)?;
buf.append_slice(name.as_bytes());
Ok(())
}
DataType::Unknown(namespace, raw) => {
namespace.encode(buf)?;
buf.append_slice(raw);
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Loca {
pub locale: String,
}
impl Atom for Loca {
const KIND: FourCC = FourCC::new(b"loca");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
Ok(Self {
locale: String::decode(buf)?,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.locale.as_str().encode(buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Setu {
pub data: Vec<u8>,
}
impl Atom for Setu {
const KIND: FourCC = FourCC::new(b"setu");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
Ok(Self {
data: Vec::decode(buf)?,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.data.encode(buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MebxKey {
pub local_key_id: FourCC,
pub keyd: Keyd,
pub dtyp: Option<Dtyp>,
pub loca: Option<Loca>,
pub setu: Option<Setu>,
}
impl MebxKey {
fn decode_maybe<B: Buf>(buf: &mut B) -> Result<Option<Self>> {
let Some(header) = Header::decode_maybe(buf)? else {
return Ok(None);
};
if u32::from(header.kind) == 0xFFFF_FFFF {
return Err(Error::Reserved);
}
let size = header.size.unwrap_or(buf.remaining());
if size > buf.remaining() {
return Ok(None);
}
let mut body = buf.slice(size);
let mut keyd = None;
let mut dtyp = None;
let mut loca = None;
let mut setu = None;
while let Some(atom) = Any::decode_maybe(&mut body)? {
match atom {
Any::Keyd(atom) => keyd = atom.into(),
Any::Dtyp(atom) => dtyp = atom.into(),
Any::Loca(atom) => loca = atom.into(),
Any::Setu(atom) => setu = atom.into(),
unknown => crate::decode_unknown(&unknown, header.kind)?,
}
}
buf.advance(size);
Ok(Some(Self {
local_key_id: header.kind,
keyd: keyd.ok_or(Error::MissingBox(Keyd::KIND))?,
dtyp,
loca,
setu,
}))
}
fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
buf.encode_atom(self.local_key_id, |buf| {
self.keyd.encode(buf)?;
self.dtyp.encode(buf)?;
self.loca.encode(buf)?;
self.setu.encode(buf)
})
}
}
const KEYS_KIND: FourCC = FourCC::new(b"keys");
fn decode_keys<B: Buf>(header: &Header, buf: &mut B) -> Result<Vec<MebxKey>> {
let size = header.size.unwrap_or(buf.remaining());
let mut body = buf.slice(size);
let mut keys = Vec::new();
while let Some(key) = MebxKey::decode_maybe(&mut body)? {
keys.push(key);
}
buf.advance(size);
Ok(keys)
}
fn encode_keys<B: BufMut>(keys: &[MebxKey], buf: &mut B) -> Result<()> {
buf.encode_atom(KEYS_KIND, |buf| {
for key in keys {
key.encode(buf)?;
}
Ok(())
})
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Mebx {
pub metadata: MetaData,
pub keys: Vec<MebxKey>,
pub btrt: Option<Btrt>,
}
impl Atom for Mebx {
const KIND: FourCC = FourCC::new(b"mebx");
fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
let metadata = MetaData::decode(buf)?;
let mut keys = None;
let mut btrt = None;
while let Some(header) = Header::decode_maybe(buf)? {
let size = header.size.unwrap_or(buf.remaining());
if size > buf.remaining() {
break;
}
if header.kind == KEYS_KIND {
keys = Some(decode_keys(&header, buf)?);
continue;
}
match Any::decode_atom(&header, buf)? {
Any::Btrt(atom) => btrt = Some(atom),
unknown => Self::decode_unknown(&unknown)?,
}
}
skip_trailing_padding(buf);
Ok(Self {
metadata,
keys: keys.ok_or(Error::MissingBox(KEYS_KIND))?,
btrt,
})
}
fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
self.metadata.encode(buf)?;
encode_keys(&self.keys, buf)?;
self.btrt.encode(buf)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_keys() -> Vec<MebxKey> {
vec![
MebxKey {
local_key_id: FourCC::new(b"\0\0\0\x01"),
keyd: Keyd {
key_namespace: FourCC::new(b"mdta"),
key_value: b"com.apple.quicktime.location.ISO6709".to_vec(),
},
dtyp: Some(Dtyp {
data_type: DataType::WellKnown(1), }),
loca: None,
setu: None,
},
MebxKey {
local_key_id: FourCC::new(b"\0\0\0\x02"),
keyd: Keyd {
key_namespace: FourCC::new(b"mdta"),
key_value: b"com.example.custom-value".to_vec(),
},
dtyp: Some(Dtyp {
data_type: DataType::Custom("com.example.custom-type".into()),
}),
loca: Some(Loca {
locale: "en-US".into(),
}),
setu: Some(Setu {
data: vec![1, 2, 3, 4],
}),
},
]
}
#[test]
fn test_keys_roundtrip() {
let keys = sample_keys();
let mut buf = Vec::new();
encode_keys(&keys, &mut buf).unwrap();
let mut slice = buf.as_slice();
let header = Header::decode(&mut slice).unwrap();
let decoded = decode_keys(&header, &mut slice).expect("failed to decode keys");
assert_eq!(decoded, keys);
}
#[test]
fn test_mebx_roundtrip() {
let mebx = Mebx {
metadata: MetaData {
data_reference_index: 1,
},
keys: sample_keys(),
btrt: Some(Btrt {
buffer_size_db: 1,
max_bitrate: 2000,
avg_bitrate: 400,
}),
};
let mut buf = Vec::new();
mebx.encode(&mut buf).unwrap();
let decoded = Mebx::decode(&mut buf.as_slice()).expect("failed to decode mebx");
assert_eq!(decoded, mebx);
}
#[test]
fn test_mebx_roundtrip_no_btrt() {
let mebx = Mebx {
metadata: MetaData {
data_reference_index: 1,
},
keys: vec![],
btrt: None,
};
let mut buf = Vec::new();
mebx.encode(&mut buf).unwrap();
let decoded = Mebx::decode(&mut buf.as_slice()).expect("failed to decode mebx");
assert_eq!(decoded, mebx);
}
#[test]
fn test_mebx_missing_keys() {
let mut buf = Vec::new();
0u32.encode(&mut buf).unwrap(); 0u16.encode(&mut buf).unwrap(); 1u16.encode(&mut buf).unwrap();
let err = Mebx::decode_body(&mut buf.as_slice()).unwrap_err();
assert!(matches!(err, Error::MissingBox(kind) if kind == KEYS_KIND));
}
#[test]
fn test_mebx_key_rejects_reserved_local_key_id() {
let mut buf = Vec::new();
8u32.encode(&mut buf).unwrap(); 0xFFFF_FFFFu32.encode(&mut buf).unwrap();
let err = MebxKey::decode_maybe(&mut buf.as_slice()).unwrap_err();
assert!(matches!(err, Error::Reserved));
}
#[test]
fn test_keyd_non_utf8_key_value_roundtrip() {
let keyd = Keyd {
key_namespace: FourCC::new(b"uiso"),
key_value: b"cprt".to_vec(),
};
let mut buf = Vec::new();
keyd.encode(&mut buf).unwrap();
let decoded = Keyd::decode(&mut buf.as_slice()).expect("failed to decode keyd");
assert_eq!(decoded, keyd);
}
#[test]
fn test_loca_roundtrip() {
let loca = Loca {
locale: "fr-FR".into(),
};
let mut buf = Vec::new();
loca.encode(&mut buf).unwrap();
let decoded = Loca::decode(&mut buf.as_slice()).expect("failed to decode loca");
assert_eq!(decoded, loca);
}
#[test]
fn test_dtyp_well_known_roundtrip() {
let dtyp = Dtyp {
data_type: DataType::WellKnown(23), };
let mut buf = Vec::new();
dtyp.encode(&mut buf).unwrap();
let decoded = Dtyp::decode(&mut buf.as_slice()).expect("failed to decode dtyp");
assert_eq!(decoded, dtyp);
}
#[test]
fn test_dtyp_custom_roundtrip() {
let dtyp = Dtyp {
data_type: DataType::Custom("com.example.custom-type".into()),
};
let mut buf = Vec::new();
dtyp.encode(&mut buf).unwrap();
let decoded = Dtyp::decode(&mut buf.as_slice()).expect("failed to decode dtyp");
assert_eq!(decoded, dtyp);
}
#[test]
fn test_dtyp_unknown_namespace_preserves_bytes() {
let dtyp = Dtyp {
data_type: DataType::Unknown(99, vec![1, 2, 3, 4]),
};
let mut buf = Vec::new();
dtyp.encode(&mut buf).unwrap();
let decoded = Dtyp::decode(&mut buf.as_slice()).expect("failed to decode dtyp");
assert_eq!(decoded, dtyp);
}
}