Skip to main content

audio_encoder_test_lib/
test_suite.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::pcm_audio::*;
6use crate::timestamp_validator::*;
7use fidl_fuchsia_media::*;
8use fidl_fuchsia_sysmem2::*;
9
10use rand::prelude::*;
11use std::rc::Rc;
12use stream_processor_encoder_factory::*;
13use stream_processor_test::*;
14
15pub const TEST_PCM_FRAME_COUNT: usize = 3000;
16
17pub struct AudioEncoderTestCase {
18    // Encoder settings. This is a function because FIDL unions are not Copy or Clone.
19    pub settings: EncoderSettings,
20    /// The number of PCM input frames per encoded frame.
21    pub input_framelength: usize,
22    /// Sampling frequency to use for generating input for timestamp-related tests.
23    pub input_frames_per_second: u32,
24    pub channel_count: usize,
25    pub output_tests: Vec<AudioEncoderOutputTest>,
26}
27
28/// An output test runs audio through the encoder and checks that the output is expected.
29/// It checks the output size and if the hash was passed in, it checks that all that data
30/// emitted when hashed sequentially results in the expected digest. Oob bytes are hashed first.
31pub struct AudioEncoderOutputTest {
32    /// If provided, the output will also be written to this file. Use this to verify new files
33    /// with a decoder before using their digest in tests.
34    pub output_file: Option<&'static str>,
35    pub input_audio: PcmAudio,
36    pub expected_output_size: OutputSize,
37    pub expected_digests: Option<Vec<ExpectedDigest>>,
38}
39
40impl AudioEncoderOutputTest {
41    pub fn saw_wave_test(
42        frames_per_second: u32,
43        expected_output_size: OutputSize,
44        expected_digests: Vec<ExpectedDigest>,
45    ) -> Self {
46        Self {
47            output_file: None,
48            input_audio: PcmAudio::create_saw_wave(
49                PcmFormat {
50                    pcm_mode: AudioPcmMode::Linear,
51                    bits_per_sample: 16,
52                    frames_per_second,
53                    channel_map: vec![AudioChannelId::Cf],
54                },
55                TEST_PCM_FRAME_COUNT,
56            ),
57            expected_output_size,
58            expected_digests: Some(expected_digests),
59        }
60    }
61}
62
63impl AudioEncoderTestCase {
64    pub async fn run(self) -> Result<()> {
65        self.test_termination().await?;
66        self.test_early_termination().await?;
67        self.test_timestamps().await?;
68        self.test_outputs().await
69    }
70
71    async fn test_outputs(self) -> Result<()> {
72        let mut cases = vec![];
73        let easy_framelength = self.input_framelength;
74        for (output_test, stream_lifetime_ordinal) in
75            self.output_tests.into_iter().zip(OrdinalPattern::Odd.into_iter())
76        {
77            let settings = self.settings.clone();
78            let pcm_audio = output_test.input_audio;
79            let stream = Rc::new(PcmAudioStream {
80                pcm_audio,
81                encoder_settings: settings.clone(),
82                frames_per_packet: (0..).map(move |_| easy_framelength),
83                timebase: None,
84            });
85            let mut validators: Vec<Rc<dyn OutputValidator>> =
86                vec![Rc::new(TerminatesWithValidator {
87                    expected_terminal_output: Output::Eos { stream_lifetime_ordinal },
88                })];
89            match output_test.expected_output_size {
90                OutputSize::PacketCount(v) => {
91                    validators.push(Rc::new(OutputPacketCountValidator {
92                        expected_output_packet_count: v,
93                    }));
94                }
95                OutputSize::RawBytesCount(v) => {
96                    validators
97                        .push(Rc::new(OutputDataSizeValidator { expected_output_data_size: v }));
98                }
99            }
100
101            if let Some(expected_digests) = output_test.expected_digests {
102                validators.push(Rc::new(BytesValidator {
103                    output_file: output_test.output_file,
104                    expected_digests,
105                }));
106            }
107            cases.push(TestCase {
108                name: "Audio encoder output test",
109                stream,
110                validators,
111                stream_options: Some(StreamOptions {
112                    queue_format_details: false,
113                    ..StreamOptions::default()
114                }),
115            });
116        }
117
118        let spec = TestSpec {
119            cases,
120            relation: CaseRelation::Serial,
121            stream_processor_factory: Rc::new(EncoderFactory),
122        };
123
124        spec.run().await.map(|_| ())
125    }
126
127    async fn test_termination(&self) -> Result<()> {
128        let easy_framelength = self.input_framelength;
129        let stream = self.create_test_stream((0..).map(move |_| easy_framelength));
130        let eos_validator = Rc::new(TerminatesWithValidator {
131            expected_terminal_output: Output::Eos { stream_lifetime_ordinal: 1 },
132        });
133
134        let case = TestCase {
135            name: "Terminates with EOS test",
136            stream,
137            validators: vec![eos_validator],
138            stream_options: None,
139        };
140
141        let spec = TestSpec {
142            cases: vec![case],
143            relation: CaseRelation::Concurrent,
144            stream_processor_factory: Rc::new(EncoderFactory),
145        };
146
147        spec.run().await.map(|_| ())
148    }
149
150    async fn test_early_termination(&self) -> Result<()> {
151        let easy_framelength = self.input_framelength;
152        let stream = self.create_test_stream((0..).map(move |_| easy_framelength));
153        let count_validator =
154            Rc::new(OutputPacketCountValidator { expected_output_packet_count: 1 });
155
156        // Pick an output packet size likely not divisible by any output codec frame size, to test
157        // that half filled output packets are cleaned up in the codec without error when the
158        // client disconnects early.
159        const ODD_OUTPUT_PACKET_SIZE: u64 = 4096 - 1;
160
161        let stream_options = Some(StreamOptions {
162            output_buffer_collection_constraints: Some(BufferCollectionConstraints {
163                buffer_memory_constraints: Some(BufferMemoryConstraints {
164                    min_size_bytes: Some(ODD_OUTPUT_PACKET_SIZE),
165                    ..buffer_memory_constraints_default()
166                }),
167                ..buffer_collection_constraints_default()
168            }),
169            stop_after_first_output: true,
170            ..StreamOptions::default()
171        });
172        let case = TestCase {
173            name: "Early termination test",
174            stream,
175            validators: vec![count_validator],
176            stream_options,
177        };
178
179        let spec = TestSpec {
180            cases: vec![case],
181            relation: CaseRelation::Concurrent,
182            stream_processor_factory: Rc::new(EncoderFactory),
183        };
184
185        spec.run().await.map(|_| ())
186    }
187
188    async fn test_timestamps(&self) -> Result<()> {
189        let max_framelength = self.input_framelength * 5;
190
191        let fixed_framelength = self.input_framelength + 1;
192        let fixed_framelength_stream =
193            self.create_test_stream((0..).map(move |_| fixed_framelength));
194        let pcm_frame_size = fixed_framelength_stream.pcm_audio.frame_size();
195
196        let stream_options = Some(StreamOptions {
197            input_buffer_collection_constraints: Some(BufferCollectionConstraints {
198                buffer_memory_constraints: Some(BufferMemoryConstraints {
199                    min_size_bytes: Some((max_framelength * pcm_frame_size) as u64),
200                    ..buffer_memory_constraints_default()
201                }),
202                ..buffer_collection_constraints_default()
203            }),
204            ..StreamOptions::default()
205        });
206
207        let fixed_framelength_case = TestCase {
208            name: "Timestamp extrapolation test - fixed framelength",
209            validators: vec![Rc::new(TimestampValidator::new(
210                self.input_framelength,
211                pcm_frame_size,
212                fixed_framelength_stream.timestamp_generator(),
213                fixed_framelength_stream.as_ref(),
214            ))],
215            stream: fixed_framelength_stream,
216            stream_options: stream_options.clone(),
217        };
218
219        let variable_framelength_stream = self.create_test_stream((0..).map(move |i| {
220            let mut rng = StdRng::seed_from_u64(i as u64);
221            rng.random_range(1..=max_framelength)
222        }));
223        let variable_framelength_case = TestCase {
224            name: "Timestamp extrapolation test - variable framelength",
225            validators: vec![Rc::new(TimestampValidator::new(
226                self.input_framelength,
227                pcm_frame_size,
228                variable_framelength_stream.timestamp_generator(),
229                variable_framelength_stream.as_ref(),
230            ))],
231            stream: variable_framelength_stream,
232            stream_options,
233        };
234
235        let spec = TestSpec {
236            cases: vec![fixed_framelength_case, variable_framelength_case],
237            relation: CaseRelation::Concurrent,
238            stream_processor_factory: Rc::new(EncoderFactory),
239        };
240
241        spec.run().await.map(|_| ())
242    }
243
244    fn create_test_stream<I>(&self, frames_per_packet: I) -> Rc<PcmAudioStream<I>> {
245        let pcm_format = PcmFormat {
246            pcm_mode: AudioPcmMode::Linear,
247            bits_per_sample: 16,
248            frames_per_second: self.input_frames_per_second,
249            channel_map: match self.channel_count {
250                1 => vec![AudioChannelId::Cf],
251                2 => vec![AudioChannelId::Lf, AudioChannelId::Rf],
252                c => panic!("{} is not a valid channel count", c),
253            },
254        };
255        let pcm_audio = PcmAudio::create_saw_wave(pcm_format.clone(), TEST_PCM_FRAME_COUNT);
256        let settings = self.settings.clone();
257        Rc::new(PcmAudioStream {
258            pcm_audio,
259            encoder_settings: settings.clone(),
260            frames_per_packet: frames_per_packet,
261            timebase: Some(zx::MonotonicDuration::from_seconds(1).into_nanos() as u64),
262        })
263    }
264}