Skip to main content

fuchsia_audio_device/
stream_config.rs

1// Copyright 2019 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 anyhow::format_err;
6use async_helpers::maybe_stream::MaybeStream;
7use fidl::endpoints::ClientEnd;
8use fidl::prelude::*;
9use fidl_fuchsia_hardware_audio::*;
10use fuchsia_inspect_derive::{IValue, Inspect};
11use fuchsia_sync::Mutex;
12
13use fuchsia_async as fasync;
14use fuchsia_inspect as inspect;
15use futures::{StreamExt, select};
16use log::{info, warn};
17use std::sync::Arc;
18
19use crate::audio_frame_sink::AudioFrameSink;
20use crate::audio_frame_stream::AudioFrameStream;
21use crate::frame_vmo;
22use crate::types::{AudioSampleFormat, Error, Result};
23
24pub(crate) enum StreamConfigOrTask {
25    StreamConfig(Box<SoftStreamConfig>),
26    Task(fasync::Task<Result<()>>),
27    Complete,
28}
29
30impl StreamConfigOrTask {
31    /// Start the task if it's not running.
32    /// Does nothing if the task is running or completed.
33    pub(crate) fn start(&mut self) {
34        *self = match std::mem::replace(self, StreamConfigOrTask::Complete) {
35            StreamConfigOrTask::StreamConfig(st) => {
36                StreamConfigOrTask::Task(fasync::Task::spawn(st.process_requests()))
37            }
38            x => x,
39        }
40    }
41}
42
43/// Number of frames within the duration.  This includes frames that end at exactly the duration.
44pub(crate) fn frames_from_duration(
45    frames_per_second: usize,
46    duration: fasync::MonotonicDuration,
47) -> usize {
48    assert!(
49        duration >= zx::MonotonicDuration::from_nanos(0),
50        "frames_from_duration is not defined for negative durations"
51    );
52    let mut frames = duration.into_seconds() * frames_per_second as i64;
53    let frames_partial =
54        ((duration.into_nanos() % 1_000_000_000) as f64 / 1e9) * frames_per_second as f64;
55    frames += frames_partial as i64;
56    frames as usize
57}
58
59/// A software fuchsia audio output, which implements Audio Driver Streaming Interface
60/// as defined in //docs/concepts/drivers/driver_interfaces/audio_streaming.md
61#[derive(Inspect)]
62pub struct SoftStreamConfig {
63    /// The Stream channel handles format negotiation, plug detection, and gain
64    stream_config_stream: StreamConfigRequestStream,
65
66    /// The Unique ID that this stream will present to the system
67    unique_id: [u8; 16],
68    /// The manufacturer of the hardware for this stream
69    manufacturer: String,
70    /// A product description for the hardware for the stream
71    product: String,
72    /// The clock domain that this stream will present to the system
73    clock_domain: u32,
74
75    /// True when this represents an output
76    is_output: bool,
77
78    /// The supported format of this output.
79    /// Currently only support one format per output is supported.
80    supported_formats: PcmSupportedFormats,
81
82    /// The number of audio frames per packet from the frame stream.
83    /// Used to calculate audio buffer sizes.
84    /// If an input, this is the amount of space we reserve for audio frames.
85    packet_frames: usize,
86
87    /// The size of a frame.
88    /// Used to report the driver transfer size.
89    frame_bytes: usize,
90
91    /// The request stream for the ringbuffer.
92    ring_buffer_stream: MaybeStream<RingBufferRequestStream>,
93
94    /// A pointer to the ring buffer for this stream
95    frame_vmo: Arc<Mutex<frame_vmo::FrameVmo>>,
96
97    /// The current delay that has been communicated exists after the audio is retrieved.
98    external_delay: zx::MonotonicDuration,
99
100    /// Replied to plugged state watch.
101    plug_state_replied: bool,
102
103    /// Replied to gain state watch.
104    gain_state_replied: bool,
105
106    /// Replied to delay info watch.
107    delay_info_replied: bool,
108
109    /// Inspect node
110    #[inspect(forward)]
111    inspect: SoftStreamConfigInspect,
112}
113
114#[derive(Default, Inspect)]
115struct SoftStreamConfigInspect {
116    inspect_node: inspect::Node,
117    ring_buffer_format: IValue<Option<String>>,
118    frame_vmo_status: IValue<Option<String>>,
119}
120
121impl SoftStreamConfigInspect {
122    fn record_current_format(&mut self, current: &(u32, AudioSampleFormat, u16)) {
123        self.ring_buffer_format
124            .iset(Some(format!("{} rate: {} channels: {}", current.1, current.0, current.2)));
125    }
126
127    fn record_vmo_status(&mut self, new: &str) {
128        self.frame_vmo_status.iset(Some(new.to_owned()));
129    }
130}
131
132impl SoftStreamConfig {
133    /// Create a new software audio device, returning a client channel which can be supplied
134    /// to the AudioCore and will act correctly as an audio output driver channel which can
135    /// render audio in the `pcm_format` format, and an AudioFrameStream which produces the
136    /// audio frames delivered to the audio output.
137    /// Spawns a task to handle messages from the Audio Core and setup of internal VMO buffers
138    /// required for audio output.  See AudioFrameStream for more information on timing
139    /// requirements for audio output.
140    /// `packet_duration`: desired duration of an audio packet returned by the stream. Rounded down to
141    /// end on a audio frame boundary.
142    /// `initial_external_delay`: delay that is added after packets have been returned from the stream
143    pub fn create_output(
144        unique_id: &[u8; 16],
145        manufacturer: &str,
146        product: &str,
147        clock_domain: u32,
148        pcm_format: fidl_fuchsia_media::PcmFormat,
149        packet_duration: zx::MonotonicDuration,
150        initial_external_delay: zx::MonotonicDuration,
151    ) -> Result<(ClientEnd<StreamConfigMarker>, AudioFrameStream)> {
152        let (client, soft_stream_config) = SoftStreamConfig::build(
153            unique_id,
154            manufacturer,
155            product,
156            clock_domain,
157            true,
158            pcm_format,
159            packet_duration,
160            initial_external_delay,
161        )?;
162        Ok((client, AudioFrameStream::new(soft_stream_config)))
163    }
164
165    pub fn create_input(
166        unique_id: &[u8; 16],
167        manufacturer: &str,
168        product: &str,
169        clock_domain: u32,
170        pcm_format: fidl_fuchsia_media::PcmFormat,
171        buffer: zx::MonotonicDuration,
172    ) -> Result<(ClientEnd<StreamConfigMarker>, AudioFrameSink)> {
173        let (client, soft_stream_config) = SoftStreamConfig::build(
174            unique_id,
175            manufacturer,
176            product,
177            clock_domain,
178            false,
179            pcm_format,
180            buffer,
181            zx::MonotonicDuration::from_nanos(0),
182        )?;
183        Ok((client, AudioFrameSink::new(soft_stream_config)))
184    }
185
186    fn build(
187        unique_id: &[u8; 16],
188        manufacturer: &str,
189        product: &str,
190        clock_domain: u32,
191        is_output: bool,
192        pcm_format: fidl_fuchsia_media::PcmFormat,
193        packet_duration: zx::MonotonicDuration,
194        initial_external_delay: zx::MonotonicDuration,
195    ) -> Result<(ClientEnd<StreamConfigMarker>, SoftStreamConfig)> {
196        if pcm_format.bits_per_sample % 8 != 0 {
197            // Non-byte-aligned format not allowed.
198            return Err(Error::InvalidArgs);
199        }
200        let (client, request_stream) =
201            fidl::endpoints::create_request_stream::<StreamConfigMarker>();
202
203        let number_of_channels = pcm_format.channel_map.len();
204        let attributes = vec![ChannelAttributes::default(); number_of_channels];
205        let channel_set = ChannelSet { attributes: Some(attributes), ..Default::default() };
206        let supported_formats = PcmSupportedFormats {
207            channel_sets: Some(vec![channel_set]),
208            sample_formats: Some(vec![SampleFormat::PcmSigned]),
209            bytes_per_sample: Some(vec![(pcm_format.bits_per_sample / 8) as u8]),
210            valid_bits_per_sample: Some(vec![pcm_format.bits_per_sample as u8]),
211            frame_rates: Some(vec![pcm_format.frames_per_second]),
212            ..Default::default()
213        };
214
215        let packet_frames =
216            frames_from_duration(pcm_format.frames_per_second as usize, packet_duration);
217
218        let soft_stream_config = SoftStreamConfig {
219            stream_config_stream: request_stream,
220            unique_id: unique_id.clone(),
221            manufacturer: manufacturer.to_string(),
222            product: product.to_string(),
223            is_output,
224            clock_domain,
225            supported_formats,
226            packet_frames,
227            frame_bytes: (pcm_format.bits_per_sample / 8) as usize,
228            ring_buffer_stream: Default::default(),
229            frame_vmo: Arc::new(Mutex::new(frame_vmo::FrameVmo::new()?)),
230            external_delay: initial_external_delay,
231            plug_state_replied: false,
232            gain_state_replied: false,
233            delay_info_replied: false,
234            inspect: Default::default(),
235        };
236        Ok((client, soft_stream_config))
237    }
238
239    pub(crate) fn frame_vmo(&self) -> Arc<Mutex<frame_vmo::FrameVmo>> {
240        self.frame_vmo.clone()
241    }
242
243    pub(crate) fn packet_frames(&self) -> usize {
244        self.packet_frames
245    }
246
247    fn frames_per_second(&self) -> u32 {
248        *self.supported_formats.frame_rates.as_ref().unwrap().get(0).unwrap()
249    }
250
251    /// Delay that is reported to the audio subsystem.
252    /// Includes the buffered packets if this is an output, and the current external delay.
253    fn current_delay(&self) -> zx::MonotonicDuration {
254        let packet_delay_nanos = if self.is_output {
255            (i64::try_from(self.packet_frames).unwrap() * 1_000_000_000)
256                / self.frames_per_second() as i64
257        } else {
258            0
259        };
260        zx::MonotonicDuration::from_nanos(packet_delay_nanos) + self.external_delay
261    }
262
263    async fn process_requests(mut self) -> Result<()> {
264        loop {
265            select! {
266                stream_config_request = self.stream_config_stream.next() => {
267                    match stream_config_request {
268                        Some(Ok(r)) => {
269                            if let Err(e) = self.handle_stream_request(r) {
270                                warn!(e:?; "stream config request")
271                            }
272                        },
273                        Some(Err(e)) => {
274                            warn!(e:?; "stream config error, stopping");
275                            return Err(e.into());
276                        },
277                        None => {
278                            warn!("stream config disconnected, stopping");
279                            return Ok(());
280                        },
281                    }
282                }
283                ring_buffer_request = self.ring_buffer_stream.next() => {
284                    match ring_buffer_request {
285                        Some(Ok(r)) => {
286                            if let Err(e) = self.handle_ring_buffer_request(r) {
287                                warn!(e:?; "ring buffer request")
288                            }
289                        },
290                        Some(Err(e)) => {
291                            warn!(e:?; "ring buffer error, dropping stream");
292                            let _ = MaybeStream::take(&mut self.ring_buffer_stream);
293                        },
294                        None => {
295                            warn!("ring buffer finished, dropping");
296                            let _ = MaybeStream::take(&mut self.ring_buffer_stream);
297                        },
298                    }
299                }
300            }
301        }
302    }
303
304    fn handle_stream_request(
305        &mut self,
306        request: StreamConfigRequest,
307    ) -> std::result::Result<(), anyhow::Error> {
308        match request {
309            StreamConfigRequest::GetHealthState { responder } => {
310                responder.send(&HealthState::default())?;
311            }
312            StreamConfigRequest::SignalProcessingConnect { protocol, control_handle: _ } => {
313                let _ = protocol.close_with_epitaph(zx::Status::NOT_SUPPORTED);
314            }
315            StreamConfigRequest::GetProperties { responder } => {
316                #[rustfmt::skip]
317                let prop = StreamProperties {
318                    unique_id:                Some(self.unique_id),
319                    is_input:                 Some(!self.is_output),
320                    can_mute:                 Some(false),
321                    can_agc:                  Some(false),
322                    min_gain_db:              Some(0f32),
323                    max_gain_db:              Some(0f32),
324                    gain_step_db:             Some(0f32),
325                    plug_detect_capabilities: Some(PlugDetectCapabilities::Hardwired),
326                    clock_domain:             Some(self.clock_domain),
327                    manufacturer:             Some(self.manufacturer.to_string()),
328                    product:                  Some(self.product.to_string()),
329                    ..Default::default()
330                };
331                responder.send(&prop)?;
332            }
333            StreamConfigRequest::GetSupportedFormats { responder } => {
334                let pcm_formats = self.supported_formats.clone();
335                let formats_vector = &[SupportedFormats {
336                    pcm_supported_formats: Some(pcm_formats),
337                    ..Default::default()
338                }];
339                responder.send(formats_vector)?;
340            }
341            StreamConfigRequest::CreateRingBuffer { format, ring_buffer, control_handle: _ } => {
342                let pcm = format.pcm_format.ok_or_else(|| format_err!("No pcm_format included"))?;
343                // If the ring buffer was previously active, we must shut it down.
344                let _ = self.frame_vmo.lock().stop();
345                drop(MaybeStream::take(&mut self.ring_buffer_stream));
346                let current = (pcm.frame_rate, pcm.into(), pcm.number_of_channels.into());
347                self.inspect.record_current_format(&current);
348                if let Err(e) = self.frame_vmo.lock().set_format(current.0, current.1, current.2) {
349                    info!("Error creating ring buffer: {e:?}");
350                    let _ = ring_buffer.close_with_epitaph(zx::Status::INVALID_ARGS);
351                    return Ok(());
352                }
353                self.ring_buffer_stream.set(ring_buffer.into_stream());
354                self.delay_info_replied = false;
355            }
356            StreamConfigRequest::WatchGainState { responder } => {
357                if self.gain_state_replied {
358                    // We will never change gain state.
359                    responder.drop_without_shutdown();
360                    return Ok(());
361                }
362                let gain_state = GainState {
363                    muted: Some(false),
364                    agc_enabled: Some(false),
365                    gain_db: Some(0.0f32),
366                    ..Default::default()
367                };
368                responder.send(&gain_state)?;
369                self.gain_state_replied = true
370            }
371            StreamConfigRequest::WatchPlugState { responder } => {
372                if self.plug_state_replied {
373                    // We will never change plug state.
374                    responder.drop_without_shutdown();
375                    return Ok(());
376                }
377                let time = fasync::MonotonicInstant::now();
378                let plug_state = PlugState {
379                    plugged: Some(true),
380                    plug_state_time: Some(time.into_nanos() as i64),
381                    ..Default::default()
382                };
383                responder.send(&plug_state)?;
384                self.plug_state_replied = true;
385            }
386            StreamConfigRequest::SetGain { target_state, control_handle: _ } => {
387                if let Some(true) = target_state.muted {
388                    warn!("Mute is not supported");
389                }
390                if let Some(true) = target_state.agc_enabled {
391                    warn!("AGC is not supported");
392                }
393                if let Some(gain) = target_state.gain_db {
394                    if gain != 0.0 {
395                        warn!("Non-zero gain setting not supported");
396                    }
397                }
398            }
399        }
400        Ok(())
401    }
402
403    fn handle_ring_buffer_request(
404        &mut self,
405        request: RingBufferRequest,
406    ) -> std::result::Result<(), anyhow::Error> {
407        match request {
408            RingBufferRequest::GetProperties { responder } => {
409                let prop = RingBufferProperties {
410                    needs_cache_flush_or_invalidate: Some(false),
411                    // TODO(https://fxbug.dev/42074396): Make driver_transfer_bytes (output) more accurate.
412                    driver_transfer_bytes: Some((self.packet_frames * self.frame_bytes) as u32),
413                    ..Default::default()
414                };
415                responder.send(&prop)?;
416            }
417            RingBufferRequest::GetVmo {
418                min_frames,
419                clock_recovery_notifications_per_ring,
420                responder,
421            } => {
422                // Require a minimum amount of frames for three packets.
423                let min_frames_from_duration = 3 * self.packet_frames as u32;
424                let ring_buffer_frames =
425                    (min_frames + self.packet_frames as u32).max(min_frames_from_duration);
426                self.inspect.record_vmo_status("gotten");
427                match self
428                    .frame_vmo
429                    .lock()
430                    .get_vmo(ring_buffer_frames as usize, clock_recovery_notifications_per_ring)
431                {
432                    Err(e) => {
433                        warn!(e:?; "Error on vmo set format");
434                        responder.send(Err(GetVmoError::InternalError))?;
435                    }
436                    Ok(vmo_handle) => {
437                        responder.send(Ok((ring_buffer_frames, vmo_handle)))?;
438                    }
439                }
440            }
441            RingBufferRequest::Start { responder } => {
442                let time = fasync::MonotonicInstant::now();
443                self.inspect.record_vmo_status(&format!("started @ {time:?}"));
444                match self.frame_vmo.lock().start(time.into()) {
445                    Ok(()) => responder.send(time.into_nanos() as i64)?,
446                    Err(e) => {
447                        warn!(e:?; "Error on frame vmo start");
448                        responder.control_handle().shutdown_with_epitaph(zx::Status::BAD_STATE);
449                    }
450                }
451            }
452            RingBufferRequest::Stop { responder } => match self.frame_vmo.lock().stop() {
453                Ok(stopped) => {
454                    if !stopped {
455                        info!("Stopping an unstarted ring buffer");
456                    }
457                    self.inspect.record_vmo_status(&format!(
458                        "stopped @ {:?}",
459                        fasync::MonotonicInstant::now()
460                    ));
461                    responder.send()?;
462                }
463                Err(e) => {
464                    warn!(e:?; "Error on frame vmo stop");
465                    responder.control_handle().shutdown_with_epitaph(zx::Status::BAD_STATE);
466                }
467            },
468            RingBufferRequest::WatchClockRecoveryPositionInfo { responder } => {
469                self.frame_vmo.lock().set_position_responder(responder);
470            }
471            RingBufferRequest::SetActiveChannels { active_channels_bitmask, responder } => {
472                match self.frame_vmo.lock().set_active_channels(active_channels_bitmask) {
473                    Ok(time) => responder.send(Ok(time.into_nanos()))?,
474                    Err(e) => responder.send(Err(Into::<zx::Status>::into(e).into_raw()))?,
475                }
476            }
477            RingBufferRequest::WatchDelayInfo { responder } => {
478                if self.delay_info_replied {
479                    // We will never change delay state.
480                    // TODO(https://fxbug.dev/42128949): Reply again when the external_delay changes from
481                    // outside instead of just on startup.
482                    responder.drop_without_shutdown();
483                    return Ok(());
484                }
485                // internal_delay is at least our packet duration (we buffer at least that much)
486                // plus whatever delay has been communicated from the client.
487                let delay_info = DelayInfo {
488                    internal_delay: Some(self.current_delay().into_nanos()),
489                    ..Default::default()
490                };
491                responder.send(&delay_info)?;
492                self.delay_info_replied = true;
493            }
494            RingBufferRequest::_UnknownMethod { .. } => (),
495        }
496        Ok(())
497    }
498}
499
500#[cfg(test)]
501pub(crate) mod tests {
502    use super::*;
503
504    use fidl_fuchsia_media::{AudioChannelId, AudioPcmMode, PcmFormat};
505
506    use async_utils::PollExt;
507    use fixture::fixture;
508    use futures::future;
509    use futures::task::Poll;
510
511    const TEST_UNIQUE_ID: &[u8; 16] = &[5; 16];
512    const TEST_CLOCK_DOMAIN: u32 = 0x00010203;
513
514    pub(crate) fn with_audio_frame_stream<F>(_name: &str, test: F)
515    where
516        F: FnOnce(fasync::TestExecutor, StreamConfigProxy, AudioFrameStream) -> (),
517    {
518        let exec = fasync::TestExecutor::new_with_fake_time();
519        let format = PcmFormat {
520            pcm_mode: AudioPcmMode::Linear,
521            bits_per_sample: 16,
522            frames_per_second: 44100,
523            channel_map: vec![AudioChannelId::Lf, AudioChannelId::Rf],
524        };
525        let (client, frame_stream) = SoftStreamConfig::create_output(
526            TEST_UNIQUE_ID,
527            "Google",
528            "UnitTest",
529            TEST_CLOCK_DOMAIN,
530            format,
531            zx::MonotonicDuration::from_millis(100),
532            zx::MonotonicDuration::from_millis(50),
533        )
534        .expect("should always build");
535        test(exec, client.into_proxy(), frame_stream)
536    }
537
538    #[fuchsia::test]
539    fn test_frames_from_duration() {
540        const FPS: usize = 48000;
541        // At 48kHz, each frame is 20833 and 1/3 nanoseconds. We add one nanosecond
542        // because frames need to be completely within the duration.
543        const ONE_FRAME_NANOS: i64 = 20833 + 1;
544        const THREE_FRAME_NANOS: i64 = 20833 * 3 + 1;
545
546        assert_eq!(0, frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(0)));
547
548        assert_eq!(
549            0,
550            frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(ONE_FRAME_NANOS - 1))
551        );
552        assert_eq!(
553            1,
554            frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(ONE_FRAME_NANOS))
555        );
556
557        // Three frames is an exact number of nanoseconds, we should be able to get an exact number
558        // of frames from the duration.
559        assert_eq!(
560            2,
561            frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(THREE_FRAME_NANOS - 1))
562        );
563        assert_eq!(
564            3,
565            frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(THREE_FRAME_NANOS))
566        );
567        assert_eq!(
568            3,
569            frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(THREE_FRAME_NANOS + 1))
570        );
571
572        assert_eq!(FPS, frames_from_duration(FPS, zx::MonotonicDuration::from_seconds(1)));
573        assert_eq!(72000, frames_from_duration(FPS, zx::MonotonicDuration::from_millis(1500)));
574
575        assert_eq!(10660, frames_from_duration(FPS, zx::MonotonicDuration::from_nanos(222084000)));
576    }
577
578    #[fuchsia::test]
579    fn soft_stream_config_audio_should_end_when_stream_dropped() {
580        let format = PcmFormat {
581            pcm_mode: AudioPcmMode::Linear,
582            bits_per_sample: 16,
583            frames_per_second: 48000,
584            channel_map: vec![AudioChannelId::Lf, AudioChannelId::Rf],
585        };
586
587        let mut exec = fasync::TestExecutor::new_with_fake_time();
588        let (client, frame_stream) = SoftStreamConfig::build(
589            TEST_UNIQUE_ID,
590            &"Google".to_string(),
591            &"UnitTest".to_string(),
592            TEST_CLOCK_DOMAIN,
593            true,
594            format,
595            zx::MonotonicDuration::from_millis(100),
596            zx::MonotonicDuration::from_millis(50),
597        )
598        .expect("should always build");
599
600        drop(frame_stream);
601
602        assert_eq!(Poll::Pending, exec.run_until_stalled(&mut future::pending::<()>()));
603
604        // The audio client should be dropped (normally this causes audio to remove the device)
605        assert_eq!(Err(zx::Status::PEER_CLOSED), client.channel().write(&[0], &mut Vec::new()));
606    }
607
608    // Returns the number of frames that were ready in the stream, draining the stream.
609    fn frames_ready(exec: &mut fasync::TestExecutor, frame_stream: &mut AudioFrameStream) -> usize {
610        let mut frames = 0;
611        while exec.run_until_stalled(&mut frame_stream.next()).is_ready() {
612            frames += 1;
613        }
614        frames
615    }
616
617    #[fixture(with_audio_frame_stream)]
618    #[fuchsia::test]
619    fn send_positions(
620        mut exec: fasync::TestExecutor,
621        stream_config: StreamConfigProxy,
622        mut frame_stream: AudioFrameStream,
623    ) {
624        // Poll the frame stream, which should start the processing of proxy requests.
625        assert_eq!(0, frames_ready(&mut exec, &mut frame_stream));
626        let _stream_config_properties = exec.run_until_stalled(&mut stream_config.get_properties());
627        let _formats = exec.run_until_stalled(&mut stream_config.get_supported_formats());
628        let (ring_buffer, server) = fidl::endpoints::create_proxy::<RingBufferMarker>();
629
630        #[rustfmt::skip]
631        let format = Format {
632            pcm_format: Some(fidl_fuchsia_hardware_audio::PcmFormat {
633                number_of_channels:      2u8,
634                sample_format:           SampleFormat::PcmSigned,
635                bytes_per_sample:        2u8,
636                valid_bits_per_sample:   16u8,
637                frame_rate:              44100,
638            }),
639            ..Default::default()
640        };
641
642        let result = stream_config.create_ring_buffer(&format, server);
643        assert!(result.is_ok());
644
645        let _ring_buffer_properties = exec.run_until_stalled(&mut ring_buffer.get_properties());
646
647        let some_active_channels_mask = 0xc3u64;
648        let result =
649            exec.run_until_stalled(&mut ring_buffer.set_active_channels(some_active_channels_mask));
650        assert!(result.is_ready());
651        let _ = match result {
652            Poll::Ready(Ok(Err(e))) => assert_eq!(e, zx::Status::INVALID_ARGS.into_raw()),
653            x => panic!("Expected error reply to set_active_channels, got {:?}", x),
654        };
655
656        let clock_recovery_notifications_per_ring = 10u32;
657        let _ = exec.run_until_stalled(
658            &mut ring_buffer.get_vmo(88200, clock_recovery_notifications_per_ring),
659        ); // 2 seconds.
660
661        exec.set_fake_time(fasync::MonotonicInstant::from_nanos(42));
662        let _ = exec.wake_expired_timers();
663        let start_time = exec.run_until_stalled(&mut ring_buffer.start());
664        if let Poll::Ready(s) = start_time {
665            assert_eq!(s.expect("start time error"), 42);
666        } else {
667            panic!("start error");
668        }
669
670        // Watch number 1.
671        let mut position_info = ring_buffer.watch_clock_recovery_position_info();
672        let result = exec.run_until_stalled(&mut position_info);
673        assert!(!result.is_ready());
674
675        // Now advance in between notifications, with a 2 seconds total in the ring buffer
676        // and 10 notifications per ring we can get watch notifications every 200 msecs.
677        exec.set_fake_time(fasync::MonotonicInstant::after(zx::MonotonicDuration::from_millis(
678            201,
679        )));
680        let _ = exec.wake_expired_timers();
681        // Each frame is 100ms, there should be two of them ready now.
682        assert_eq!(2, frames_ready(&mut exec, &mut frame_stream));
683        let result = exec.run_until_stalled(&mut position_info);
684        assert!(result.is_ready());
685
686        // Watch number 2.
687        let mut position_info = ring_buffer.watch_clock_recovery_position_info();
688        let result = exec.run_until_stalled(&mut position_info);
689        assert!(!result.is_ready());
690        exec.set_fake_time(fasync::MonotonicInstant::after(zx::MonotonicDuration::from_millis(
691            201,
692        )));
693        let _ = exec.wake_expired_timers();
694        assert_eq!(2, frames_ready(&mut exec, &mut frame_stream));
695        let result = exec.run_until_stalled(&mut position_info);
696        assert!(result.is_ready());
697
698        // Watch number 3.
699        let mut position_info = ring_buffer.watch_clock_recovery_position_info();
700        let result = exec.run_until_stalled(&mut position_info);
701        assert!(!result.is_ready());
702        exec.set_fake_time(fasync::MonotonicInstant::after(zx::MonotonicDuration::from_millis(
703            201,
704        )));
705        let _ = exec.wake_expired_timers();
706        assert_eq!(2, frames_ready(&mut exec, &mut frame_stream));
707        let result = exec.run_until_stalled(&mut position_info);
708        assert!(result.is_ready());
709
710        let result = exec.run_until_stalled(&mut ring_buffer.stop());
711        assert!(result.is_ready());
712    }
713
714    #[fixture(with_audio_frame_stream)]
715    #[fuchsia::test]
716    fn watch_delay_info(
717        mut exec: fasync::TestExecutor,
718        stream_config: StreamConfigProxy,
719        mut frame_stream: AudioFrameStream,
720    ) {
721        let mut frame_fut = frame_stream.next();
722        // Poll the frame stream, which should start the processing of proxy requests.
723        exec.run_until_stalled(&mut frame_fut).expect_pending("no frames at the start");
724        let _stream_config_properties = exec.run_until_stalled(&mut stream_config.get_properties());
725        let _formats = exec.run_until_stalled(&mut stream_config.get_supported_formats());
726        let (ring_buffer, server) = fidl::endpoints::create_proxy::<RingBufferMarker>();
727
728        #[rustfmt::skip]
729        let format = Format {
730            pcm_format: Some(fidl_fuchsia_hardware_audio::PcmFormat {
731                number_of_channels:      2u8,
732                sample_format:           SampleFormat::PcmSigned,
733                bytes_per_sample:        2u8,
734                valid_bits_per_sample:   16u8,
735                frame_rate:              44100,
736            }),
737            ..Default::default()
738        };
739
740        let result = stream_config.create_ring_buffer(&format, server);
741        assert!(result.is_ok());
742
743        let result = exec.run_until_stalled(&mut ring_buffer.watch_delay_info());
744
745        // Should account for the external_delay here.
746        match result {
747            Poll::Ready(Ok(DelayInfo { internal_delay: Some(x), .. })) => {
748                assert_eq!(zx::MonotonicDuration::from_millis(150).into_nanos(), x)
749            }
750            other => panic!("Expected the correct delay info, got {other:?}"),
751        }
752    }
753}