1#![allow(clippy::large_futures)]
6
7use crate::pcm_audio::*;
8use crate::timestamp_validator::*;
9use fidl_fuchsia_media::*;
10use fidl_fuchsia_sysmem2::*;
11
12use rand::prelude::*;
13use std::rc::Rc;
14use stream_processor_encoder_factory::*;
15use stream_processor_test::*;
16
17pub const TEST_PCM_FRAME_COUNT: usize = 3000;
18
19pub struct AudioEncoderTestCase {
20 pub settings: EncoderSettings,
22 pub input_framelength: usize,
24 pub input_frames_per_second: u32,
26 pub channel_count: usize,
27 pub output_tests: Vec<AudioEncoderOutputTest>,
28}
29
30pub struct AudioEncoderOutputTest {
34 pub output_file: Option<&'static str>,
37 pub input_audio: PcmAudio,
38 pub expected_output_size: OutputSize,
39 pub expected_digests: Option<Vec<ExpectedDigest>>,
40}
41
42impl AudioEncoderOutputTest {
43 pub fn saw_wave_test(
44 frames_per_second: u32,
45 expected_output_size: OutputSize,
46 expected_digests: Vec<ExpectedDigest>,
47 ) -> Self {
48 Self {
49 output_file: None,
50 input_audio: PcmAudio::create_saw_wave(
51 PcmFormat {
52 pcm_mode: AudioPcmMode::Linear,
53 bits_per_sample: 16,
54 frames_per_second,
55 channel_map: vec![AudioChannelId::Cf],
56 },
57 TEST_PCM_FRAME_COUNT,
58 ),
59 expected_output_size,
60 expected_digests: Some(expected_digests),
61 }
62 }
63}
64
65impl AudioEncoderTestCase {
66 pub async fn run(self) -> Result<()> {
67 self.test_termination().await?;
68 self.test_early_termination().await?;
69 self.test_timestamps().await?;
70 self.test_outputs().await
71 }
72
73 async fn test_outputs(self) -> Result<()> {
74 let mut cases = vec![];
75 let easy_framelength = self.input_framelength;
76 for (output_test, stream_lifetime_ordinal) in
77 self.output_tests.into_iter().zip(OrdinalPattern::Odd.into_iter())
78 {
79 let settings = self.settings.clone();
80 let pcm_audio = output_test.input_audio;
81 let stream = Rc::new(PcmAudioStream {
82 pcm_audio,
83 encoder_settings: settings.clone(),
84 frames_per_packet: (0..).map(move |_| easy_framelength),
85 timebase: None,
86 });
87 let mut validators: Vec<Rc<dyn OutputValidator>> =
88 vec![Rc::new(TerminatesWithValidator {
89 expected_terminal_output: Output::Eos { stream_lifetime_ordinal },
90 })];
91 match output_test.expected_output_size {
92 OutputSize::PacketCount(v) => {
93 validators.push(Rc::new(OutputPacketCountValidator {
94 expected_output_packet_count: v,
95 }));
96 }
97 OutputSize::RawBytesCount(v) => {
98 validators
99 .push(Rc::new(OutputDataSizeValidator { expected_output_data_size: v }));
100 }
101 }
102
103 if let Some(expected_digests) = output_test.expected_digests {
104 validators.push(Rc::new(BytesValidator {
105 output_file: output_test.output_file,
106 expected_digests,
107 }));
108 }
109 cases.push(TestCase {
110 name: "Audio encoder output test",
111 stream,
112 validators,
113 stream_options: Some(StreamOptions {
114 queue_format_details: false,
115 ..StreamOptions::default()
116 }),
117 });
118 }
119
120 let spec = TestSpec {
121 cases,
122 relation: CaseRelation::Serial,
123 stream_processor_factory: Rc::new(EncoderFactory),
124 };
125
126 spec.run().await.map(|_| ())
127 }
128
129 async fn test_termination(&self) -> Result<()> {
130 let easy_framelength = self.input_framelength;
131 let stream = self.create_test_stream((0..).map(move |_| easy_framelength));
132 let eos_validator = Rc::new(TerminatesWithValidator {
133 expected_terminal_output: Output::Eos { stream_lifetime_ordinal: 1 },
134 });
135
136 let case = TestCase {
137 name: "Terminates with EOS test",
138 stream,
139 validators: vec![eos_validator],
140 stream_options: None,
141 };
142
143 let spec = TestSpec {
144 cases: vec![case],
145 relation: CaseRelation::Concurrent,
146 stream_processor_factory: Rc::new(EncoderFactory),
147 };
148
149 spec.run().await.map(|_| ())
150 }
151
152 async fn test_early_termination(&self) -> Result<()> {
153 let easy_framelength = self.input_framelength;
154 let stream = self.create_test_stream((0..).map(move |_| easy_framelength));
155 let count_validator =
156 Rc::new(OutputPacketCountValidator { expected_output_packet_count: 1 });
157
158 const ODD_OUTPUT_PACKET_SIZE: u64 = 4096 - 1;
162
163 let stream_options = Some(StreamOptions {
164 output_buffer_collection_constraints: Some(BufferCollectionConstraints {
165 buffer_memory_constraints: Some(BufferMemoryConstraints {
166 min_size_bytes: Some(ODD_OUTPUT_PACKET_SIZE),
167 ..buffer_memory_constraints_default()
168 }),
169 ..buffer_collection_constraints_default()
170 }),
171 stop_after_first_output: true,
172 ..StreamOptions::default()
173 });
174 let case = TestCase {
175 name: "Early termination test",
176 stream,
177 validators: vec![count_validator],
178 stream_options,
179 };
180
181 let spec = TestSpec {
182 cases: vec![case],
183 relation: CaseRelation::Concurrent,
184 stream_processor_factory: Rc::new(EncoderFactory),
185 };
186
187 spec.run().await.map(|_| ())
188 }
189
190 async fn test_timestamps(&self) -> Result<()> {
191 let max_framelength = self.input_framelength * 5;
192
193 let fixed_framelength = self.input_framelength + 1;
194 let fixed_framelength_stream =
195 self.create_test_stream((0..).map(move |_| fixed_framelength));
196 let pcm_frame_size = fixed_framelength_stream.pcm_audio.frame_size();
197
198 let stream_options = Some(StreamOptions {
199 input_buffer_collection_constraints: Some(BufferCollectionConstraints {
200 buffer_memory_constraints: Some(BufferMemoryConstraints {
201 min_size_bytes: Some((max_framelength * pcm_frame_size) as u64),
202 ..buffer_memory_constraints_default()
203 }),
204 ..buffer_collection_constraints_default()
205 }),
206 ..StreamOptions::default()
207 });
208
209 let fixed_framelength_case = TestCase {
210 name: "Timestamp extrapolation test - fixed framelength",
211 validators: vec![Rc::new(TimestampValidator::new(
212 self.input_framelength,
213 pcm_frame_size,
214 fixed_framelength_stream.timestamp_generator(),
215 fixed_framelength_stream.as_ref(),
216 ))],
217 stream: fixed_framelength_stream,
218 stream_options: stream_options.clone(),
219 };
220
221 let variable_framelength_stream = self.create_test_stream((0..).map(move |i| {
222 let mut rng = StdRng::seed_from_u64(i as u64);
223 rng.gen::<usize>() % max_framelength + 1
224 }));
225 let variable_framelength_case = TestCase {
226 name: "Timestamp extrapolation test - variable framelength",
227 validators: vec![Rc::new(TimestampValidator::new(
228 self.input_framelength,
229 pcm_frame_size,
230 variable_framelength_stream.timestamp_generator(),
231 variable_framelength_stream.as_ref(),
232 ))],
233 stream: variable_framelength_stream,
234 stream_options,
235 };
236
237 let spec = TestSpec {
238 cases: vec![fixed_framelength_case, variable_framelength_case],
239 relation: CaseRelation::Concurrent,
240 stream_processor_factory: Rc::new(EncoderFactory),
241 };
242
243 spec.run().await.map(|_| ())
244 }
245
246 fn create_test_stream(
247 &self,
248 frames_per_packet: impl Iterator<Item = usize> + Clone,
249 ) -> Rc<PcmAudioStream<impl Iterator<Item = usize> + Clone>> {
250 let pcm_format = PcmFormat {
251 pcm_mode: AudioPcmMode::Linear,
252 bits_per_sample: 16,
253 frames_per_second: self.input_frames_per_second,
254 channel_map: match self.channel_count {
255 1 => vec![AudioChannelId::Cf],
256 2 => vec![AudioChannelId::Lf, AudioChannelId::Rf],
257 c => panic!("{} is not a valid channel count", c),
258 },
259 };
260 let pcm_audio = PcmAudio::create_saw_wave(pcm_format.clone(), TEST_PCM_FRAME_COUNT);
261 let settings = self.settings.clone();
262 Rc::new(PcmAudioStream {
263 pcm_audio,
264 encoder_settings: settings.clone(),
265 frames_per_packet: frames_per_packet,
266 timebase: Some(zx::MonotonicDuration::from_seconds(1).into_nanos() as u64),
267 })
268 }
269}