stream_processor_test/
elementary_stream.rs1use fidl_fuchsia_media::FormatDetails;
6
7pub trait ElementaryStream {
8 fn format_details(&self, version_ordinal: u64) -> FormatDetails;
9
10 fn is_access_units(&self) -> bool;
14
15 fn stream<'a>(&'a self) -> Box<dyn Iterator<Item = ElementaryStreamChunk> + 'a>;
16
17 fn capped_chunks<'a>(
21 &'a self,
22 max_size: usize,
23 ) -> Box<dyn Iterator<Item = ElementaryStreamChunk> + 'a> {
24 Box::new(self.stream().flat_map(move |chunk| CappedSizeChunks {
25 src: chunk,
26 offset: 0,
27 max_size,
28 }))
29 }
30
31 fn video_frame_count(&self) -> usize {
32 self.stream()
33 .filter(|chunk| match chunk.significance {
34 Significance::Video(VideoSignificance::Picture) => true,
35 _ => false,
36 })
37 .count()
38 }
39}
40
41#[derive(Clone, Debug)]
42pub struct ElementaryStreamChunk {
43 pub start_access_unit: bool,
44 pub known_end_access_unit: bool,
45 pub data: Vec<u8>,
46 pub significance: Significance,
47 pub timestamp: Option<u64>,
48}
49
50#[derive(Copy, Clone, Debug)]
51pub enum Significance {
52 Video(VideoSignificance),
53 Audio(AudioSignificance),
54}
55
56#[derive(Copy, Clone, Debug)]
57pub enum VideoSignificance {
58 Picture,
59 NotPicture,
60}
61
62#[derive(Copy, Clone, Debug)]
63pub enum AudioSignificance {
64 PcmFrames,
65 Encoded,
66}
67
68struct CappedSizeChunks {
69 src: ElementaryStreamChunk,
70 offset: usize,
71 max_size: usize,
72}
73
74impl Iterator for CappedSizeChunks {
75 type Item = ElementaryStreamChunk;
76 fn next(&mut self) -> Option<Self::Item> {
77 if self.offset >= self.src.data.len() {
78 return None;
79 }
80
81 let len = std::cmp::min(self.src.data.len() - self.offset, self.max_size);
82 let next_offset = self.offset + len;
83 let is_first_subchunk = self.offset == 0;
84 let is_last_subchunk = next_offset == self.src.data.len();
85 let chunk = ElementaryStreamChunk {
86 start_access_unit: self.src.start_access_unit && is_first_subchunk,
87 known_end_access_unit: self.src.known_end_access_unit && is_last_subchunk,
88 data: self.src.data[self.offset..next_offset].to_vec(),
89 timestamp: if is_first_subchunk { self.src.timestamp } else { None },
90 significance: self.src.significance,
91 };
92 self.offset = next_offset;
93
94 Some(chunk)
95 }
96}
97
98pub struct TimestampedStream<S, I> {
100 pub source: S,
101 pub timestamps: I,
102}
103
104impl<S, I> ElementaryStream for TimestampedStream<S, I>
105where
106 S: ElementaryStream,
107 I: Iterator<Item = u64> + Clone,
108{
109 fn format_details(&self, version_ordinal: u64) -> FormatDetails {
110 self.source.format_details(version_ordinal)
111 }
112
113 fn is_access_units(&self) -> bool {
114 self.source.is_access_units()
115 }
116
117 fn stream<'a>(&'a self) -> Box<dyn Iterator<Item = ElementaryStreamChunk> + 'a> {
118 let mut timestamps = self.timestamps.clone();
119 Box::new(self.source.stream().map(move |mut chunk| {
120 match chunk.significance {
121 Significance::Video(VideoSignificance::Picture) => {
122 chunk.timestamp = timestamps.next();
123 }
124 _ => {}
125 };
126 chunk
127 }))
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use super::*;
134
135 struct FakeStream {
136 data: Vec<u8>,
137 }
138
139 impl ElementaryStream for FakeStream {
140 fn format_details(&self, _version_ordinal: u64) -> FormatDetails {
141 Default::default()
142 }
143
144 fn is_access_units(&self) -> bool {
145 true
146 }
147
148 fn stream<'a>(&'a self) -> Box<dyn Iterator<Item = ElementaryStreamChunk> + 'a> {
149 Box::new(self.data.chunks(20).map(|data| ElementaryStreamChunk {
150 start_access_unit: true,
151 known_end_access_unit: true,
152 data: data.to_vec(),
153 significance: Significance::Video(VideoSignificance::Picture),
154 timestamp: None,
155 }))
156 }
157 }
158
159 #[fuchsia::test]
160 fn chunks_are_capped() {
161 let stream = TimestampedStream {
162 source: FakeStream { data: (0..).take(100).collect() },
163 timestamps: 0..,
164 };
165 let chunk_size = 10;
166 for (i, capped_chunk) in stream.capped_chunks(chunk_size).enumerate() {
167 if i % 2 == 0 {
168 assert!(capped_chunk.start_access_unit);
170 assert_eq!(capped_chunk.timestamp, Some(i as u64 / 2));
171 } else {
172 assert!(capped_chunk.known_end_access_unit);
174
175 assert!(capped_chunk.timestamp.is_none());
178 }
179
180 for j in 0..chunk_size {
181 assert_eq!(capped_chunk.data[j], (i * chunk_size + j) as u8);
182 }
183 }
184 }
185}