flowly_mp4/mp4box/
elst.rs

1use byteorder::{BigEndian, WriteBytesExt};
2use serde::Serialize;
3use std::io::Write;
4use std::mem::size_of;
5
6use crate::mp4box::*;
7
8#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
9pub struct ElstBox {
10    pub version: u8,
11    pub flags: u32,
12
13    #[serde(skip_serializing)]
14    pub entries: Vec<ElstEntry>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
18pub struct ElstEntry {
19    pub segment_duration: u64,
20    pub media_time: u64,
21    pub media_rate: u16,
22    pub media_rate_fraction: u16,
23}
24
25impl ElstBox {
26    pub fn get_type(&self) -> BoxType {
27        BoxType::ElstBox
28    }
29
30    pub fn get_size(&self) -> u64 {
31        let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4;
32        if self.version == 1 {
33            size += self.entries.len() as u64 * 20;
34        } else if self.version == 0 {
35            size += self.entries.len() as u64 * 12;
36        }
37        size
38    }
39}
40
41impl Mp4Box for ElstBox {
42    const TYPE: BoxType = BoxType::ElstBox;
43
44    fn box_size(&self) -> u64 {
45        self.get_size()
46    }
47
48    fn to_json(&self) -> Result<String, Error> {
49        Ok(serde_json::to_string(&self).unwrap())
50    }
51
52    fn summary(&self) -> Result<String, Error> {
53        let s = format!("elst_entries={}", self.entries.len());
54        Ok(s)
55    }
56}
57
58impl BlockReader for ElstBox {
59    fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self, Error> {
60        let (version, flags) = read_box_header_ext(reader);
61
62        let entry_count = reader.get_u32();
63        let entry_size = {
64            let mut entry_size = 0;
65            entry_size += if version == 1 {
66                size_of::<u64>() + size_of::<i64>() // segment_duration + media_time
67            } else {
68                size_of::<u32>() + size_of::<i32>() // segment_duration + media_time
69            };
70
71            entry_size += size_of::<i16>() + size_of::<i16>(); // media_rate_integer + media_rate_fraction
72            entry_size
73        };
74
75        if entry_count as usize > reader.remaining() / entry_size {
76            return Err(Error::InvalidData(
77                "elst entry_count indicates more entries than could fit in the box",
78            ));
79        }
80
81        let mut entries = Vec::with_capacity(entry_count as usize);
82        for _ in 0..entry_count {
83            let (segment_duration, media_time) = if version == 1 {
84                (reader.get_u64(), reader.get_u64())
85            } else {
86                (reader.get_u32() as u64, reader.get_u32() as u64)
87            };
88
89            entries.push(ElstEntry {
90                segment_duration,
91                media_time,
92                media_rate: reader.get_u16(),
93                media_rate_fraction: reader.get_u16(),
94            });
95        }
96
97        Ok(ElstBox {
98            version,
99            flags,
100            entries,
101        })
102    }
103
104    fn size_hint() -> usize {
105        8
106    }
107}
108
109impl<W: Write> WriteBox<&mut W> for ElstBox {
110    fn write_box(&self, writer: &mut W) -> Result<u64, Error> {
111        let size = self.box_size();
112        BoxHeader::new(Self::TYPE, size).write(writer)?;
113
114        write_box_header_ext(writer, self.version, self.flags)?;
115
116        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
117        for entry in self.entries.iter() {
118            if self.version == 1 {
119                writer.write_u64::<BigEndian>(entry.segment_duration)?;
120                writer.write_u64::<BigEndian>(entry.media_time)?;
121            } else {
122                writer.write_u32::<BigEndian>(entry.segment_duration as u32)?;
123                writer.write_u32::<BigEndian>(entry.media_time as u32)?;
124            }
125            writer.write_u16::<BigEndian>(entry.media_rate)?;
126            writer.write_u16::<BigEndian>(entry.media_rate_fraction)?;
127        }
128
129        Ok(size)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::mp4box::BoxHeader;
137
138    #[tokio::test]
139    async fn test_elst32() {
140        let src_box = ElstBox {
141            version: 0,
142            flags: 0,
143            entries: vec![ElstEntry {
144                segment_duration: 634634,
145                media_time: 0,
146                media_rate: 1,
147                media_rate_fraction: 0,
148            }],
149        };
150        let mut buf = Vec::new();
151        src_box.write_box(&mut buf).unwrap();
152        assert_eq!(buf.len(), src_box.box_size() as usize);
153
154        let mut reader = buf.as_slice();
155        let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
156        assert_eq!(header.kind, BoxType::ElstBox);
157        assert_eq!(src_box.box_size(), header.size);
158
159        let dst_box = ElstBox::read_block(&mut reader).unwrap();
160        assert_eq!(src_box, dst_box);
161    }
162
163    #[tokio::test]
164    async fn test_elst64() {
165        let src_box = ElstBox {
166            version: 1,
167            flags: 0,
168            entries: vec![ElstEntry {
169                segment_duration: 634634,
170                media_time: 0,
171                media_rate: 1,
172                media_rate_fraction: 0,
173            }],
174        };
175        let mut buf = Vec::new();
176        src_box.write_box(&mut buf).unwrap();
177        assert_eq!(buf.len(), src_box.box_size() as usize);
178
179        let mut reader = buf.as_slice();
180        let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
181        assert_eq!(header.kind, BoxType::ElstBox);
182        assert_eq!(src_box.box_size(), header.size);
183
184        let dst_box = ElstBox::read_block(&mut reader).unwrap();
185        assert_eq!(src_box, dst_box);
186    }
187}