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; 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}