scuffle_mp4/boxes/types/
elst.rs1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use crate::boxes::header::{BoxHeader, FullBoxHeader};
7use crate::boxes::traits::BoxType;
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct Elst {
13 pub header: FullBoxHeader,
14 pub entries: Vec<ElstEntry>,
15}
16
17impl Elst {
18 pub fn new(entries: Vec<ElstEntry>) -> Self {
19 Self {
20 header: FullBoxHeader::new(Self::NAME, 0, 0),
21 entries,
22 }
23 }
24}
25
26#[derive(Debug, Clone, PartialEq)]
27pub struct ElstEntry {
29 pub segment_duration: u64,
30 pub media_time: i64,
31 pub media_rate_integer: i16,
32 pub media_rate_fraction: i16,
33}
34
35impl BoxType for Elst {
36 const NAME: [u8; 4] = *b"elst";
37
38 fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
39 let mut reader = io::Cursor::new(data);
40
41 let header = FullBoxHeader::demux(header, &mut reader)?;
42
43 let entry_count = reader.read_u32::<BigEndian>()?;
44
45 let mut entries = Vec::with_capacity(entry_count as usize);
46
47 for _ in 0..entry_count {
48 let (segment_duration, media_time, media_rate_integer, media_rate_fraction) = if header.version == 1 {
49 (
50 reader.read_u64::<BigEndian>()?, reader.read_i64::<BigEndian>()?, reader.read_i16::<BigEndian>()?, reader.read_i16::<BigEndian>()?, )
55 } else {
56 (
57 reader.read_u32::<BigEndian>()? as u64, reader.read_i32::<BigEndian>()? as i64, reader.read_i16::<BigEndian>()?, reader.read_i16::<BigEndian>()?, )
62 };
63
64 entries.push(ElstEntry {
65 segment_duration,
66 media_time,
67 media_rate_integer,
68 media_rate_fraction,
69 });
70 }
71
72 Ok(Self { header, entries })
73 }
74
75 fn primitive_size(&self) -> u64 {
76 self.header.size()
77 + 4 + (self.entries.len() as u64) * if self.header.version == 1 {
79 8 + 8 + 2 + 2 } else {
81 4 + 4 + 2 + 2 }
83 }
84
85 fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
86 self.header.mux(writer)?;
87
88 writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
89
90 for entry in &self.entries {
91 if self.header.version == 1 {
92 writer.write_u64::<BigEndian>(entry.segment_duration)?;
93 writer.write_i64::<BigEndian>(entry.media_time)?;
94 } else {
95 writer.write_u32::<BigEndian>(entry.segment_duration as u32)?;
96 writer.write_i32::<BigEndian>(entry.media_time as i32)?;
97 }
98
99 writer.write_i16::<BigEndian>(entry.media_rate_integer)?;
100 writer.write_i16::<BigEndian>(entry.media_rate_fraction)?;
101 }
102
103 Ok(())
104 }
105
106 fn validate(&self) -> io::Result<()> {
107 if self.header.flags != 0 {
108 return Err(io::Error::new(io::ErrorKind::InvalidData, "elst: version 1 is not supported"));
109 }
110
111 if self.header.version > 1 {
112 return Err(io::Error::new(io::ErrorKind::InvalidData, "elst: version must be 0 or 1"));
113 }
114
115 if self.header.version == 1 {
116 for entry in &self.entries {
117 if entry.segment_duration > u32::MAX as u64 {
118 return Err(io::Error::new(
119 io::ErrorKind::InvalidData,
120 "elst: segment_duration must be u32",
121 ));
122 }
123
124 if entry.media_time > i32::MAX as i64 {
125 return Err(io::Error::new(io::ErrorKind::InvalidData, "elst: media_time must be i32"));
126 }
127 }
128 }
129
130 Ok(())
131 }
132}