Skip to main content

mp4/mp4box/
ftyp.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4
5use crate::mp4box::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
8pub struct FtypBox {
9    pub major_brand: FourCC,
10    pub minor_version: u32,
11    pub compatible_brands: Vec<FourCC>,
12}
13
14impl FtypBox {
15    pub fn get_type(&self) -> BoxType {
16        BoxType::FtypBox
17    }
18
19    pub fn get_size(&self) -> u64 {
20        HEADER_SIZE + 8 + (4 * self.compatible_brands.len() as u64)
21    }
22}
23
24impl Mp4Box for FtypBox {
25    fn box_type(&self) -> BoxType {
26        self.get_type()
27    }
28
29    fn box_size(&self) -> u64 {
30        self.get_size()
31    }
32
33    fn to_json(&self) -> Result<String> {
34        Ok(serde_json::to_string(&self).unwrap())
35    }
36
37    fn summary(&self) -> Result<String> {
38        let mut compatible_brands = Vec::new();
39        for brand in self.compatible_brands.iter() {
40            compatible_brands.push(brand.to_string());
41        }
42        let s = format!(
43            "major_brand={} minor_version={} compatible_brands={}",
44            self.major_brand,
45            self.minor_version,
46            compatible_brands.join("-")
47        );
48        Ok(s)
49    }
50}
51
52impl<R: Read + Seek> ReadBox<&mut R> for FtypBox {
53    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
54        let start = box_start(reader)?;
55
56        if size < 16 || size % 4 != 0 {
57            return Err(Error::InvalidData("ftyp size too small or not aligned"));
58        }
59        let brand_count = (size - 16) / 4; // header + major + minor
60        let major = reader.read_u32::<BigEndian>()?;
61        let minor = reader.read_u32::<BigEndian>()?;
62
63        let mut brands = Vec::new();
64        for _ in 0..brand_count {
65            let b = reader.read_u32::<BigEndian>()?;
66            brands.push(From::from(b));
67        }
68
69        skip_bytes_to(reader, start + size)?;
70
71        Ok(FtypBox {
72            major_brand: From::from(major),
73            minor_version: minor,
74            compatible_brands: brands,
75        })
76    }
77}
78
79impl<W: Write> WriteBox<&mut W> for FtypBox {
80    fn write_box(&self, writer: &mut W) -> Result<u64> {
81        let size = self.box_size();
82        BoxHeader::new(self.box_type(), size).write(writer)?;
83
84        writer.write_u32::<BigEndian>((&self.major_brand).into())?;
85        writer.write_u32::<BigEndian>(self.minor_version)?;
86        for b in self.compatible_brands.iter() {
87            writer.write_u32::<BigEndian>(b.into())?;
88        }
89        Ok(size)
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::mp4box::BoxHeader;
97    use std::io::Cursor;
98
99    #[test]
100    fn test_ftyp() {
101        let src_box = FtypBox {
102            major_brand: str::parse("isom").unwrap(),
103            minor_version: 0,
104            compatible_brands: vec![
105                str::parse("isom").unwrap(),
106                str::parse("iso2").unwrap(),
107                str::parse("avc1").unwrap(),
108                str::parse("mp41").unwrap(),
109            ],
110        };
111        let mut buf = Vec::new();
112        src_box.write_box(&mut buf).unwrap();
113        assert_eq!(buf.len(), src_box.box_size() as usize);
114
115        let mut reader = Cursor::new(&buf);
116        let header = BoxHeader::read(&mut reader).unwrap();
117        assert_eq!(header.name, BoxType::FtypBox);
118        assert_eq!(src_box.box_size(), header.size);
119
120        let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap();
121        assert_eq!(src_box, dst_box);
122    }
123}