scuffle_mp4/boxes/types/
subs.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 Subs {
13 pub header: FullBoxHeader,
14
15 pub entries: Vec<SubsEntry>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub struct SubsEntry {
21 pub sample_delta: u32,
22 pub subsamples: Vec<SubSampleEntry>,
23}
24
25#[derive(Debug, Clone, PartialEq)]
26pub struct SubSampleEntry {
28 pub subsample_size: u32,
29 pub subsample_priority: u8,
30 pub discardable: u8,
31 pub codec_specific_parameters: u32,
32}
33
34impl BoxType for Subs {
35 const NAME: [u8; 4] = *b"subs";
36
37 fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
38 let mut reader = io::Cursor::new(data);
39
40 let header = FullBoxHeader::demux(header, &mut reader)?;
41
42 let entry_count = reader.read_u32::<BigEndian>()?;
43 let mut entries = Vec::with_capacity(entry_count as usize);
44
45 for _ in 0..entry_count {
46 let sample_delta = reader.read_u32::<BigEndian>()?;
47 let subsample_count = reader.read_u16::<BigEndian>()?;
48 let mut subsamples = Vec::with_capacity(subsample_count as usize);
49
50 for _ in 0..subsample_count {
51 let subsample_size = if header.version == 1 {
52 reader.read_u32::<BigEndian>()?
53 } else {
54 reader.read_u16::<BigEndian>()? as u32
55 };
56 let subsample_priority = reader.read_u8()?;
57 let discardable = reader.read_u8()?;
58 let codec_specific_parameters = reader.read_u32::<BigEndian>()?;
59 subsamples.push(SubSampleEntry {
60 subsample_size,
61 subsample_priority,
62 discardable,
63 codec_specific_parameters,
64 });
65 }
66
67 entries.push(SubsEntry {
68 sample_delta,
69 subsamples,
70 });
71 }
72
73 Ok(Self { header, entries })
74 }
75
76 fn primitive_size(&self) -> u64 {
77 let size = self.header.size();
78 let size = size + 4; size + self
81 .entries
82 .iter()
83 .map(|e| {
84 let size = 4; let size = size + 2; size + e.subsamples.len() as u64
88 * if self.header.version == 1 {
89 4 + 1 + 1 + 4
90 } else {
91 2 + 1 + 1 + 4
92 }
93 })
94 .sum::<u64>()
95 }
96
97 fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
98 self.header.mux(writer)?;
99
100 writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
101 for entry in &self.entries {
102 writer.write_u32::<BigEndian>(entry.sample_delta)?;
103 writer.write_u16::<BigEndian>(entry.subsamples.len() as u16)?;
104 for subsample in &entry.subsamples {
105 if self.header.version == 1 {
106 writer.write_u32::<BigEndian>(subsample.subsample_size)?;
107 } else {
108 writer.write_u16::<BigEndian>(subsample.subsample_size as u16)?;
109 }
110 writer.write_u8(subsample.subsample_priority)?;
111 writer.write_u8(subsample.discardable)?;
112 writer.write_u32::<BigEndian>(subsample.codec_specific_parameters)?;
113 }
114 }
115
116 Ok(())
117 }
118
119 fn validate(&self) -> io::Result<()> {
120 if self.header.version > 1 {
121 return Err(io::Error::new(io::ErrorKind::InvalidData, "subs version must be 0 or 1"));
122 }
123
124 if self.header.version == 0 {
125 for entry in &self.entries {
126 for subsample in &entry.subsamples {
127 if subsample.subsample_size > u16::MAX as u32 {
128 return Err(io::Error::new(
129 io::ErrorKind::InvalidData,
130 "subs subsample_size must be less than 2^16",
131 ));
132 }
133 }
134 }
135 }
136
137 Ok(())
138 }
139}