1use 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 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
43pub(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#[derive(Inspect)]
62pub struct SoftStreamConfig {
63 stream_config_stream: StreamConfigRequestStream,
65
66 unique_id: [u8; 16],
68 manufacturer: String,
70 product: String,
72 clock_domain: u32,
74
75 is_output: bool,
77
78 supported_formats: PcmSupportedFormats,
81
82 packet_frames: usize,
86
87 frame_bytes: usize,
90
91 ring_buffer_stream: MaybeStream<RingBufferRequestStream>,
93
94 frame_vmo: Arc<Mutex<frame_vmo::FrameVmo>>,
96
97 external_delay: zx::MonotonicDuration,
99
100 plug_state_replied: bool,
102
103 gain_state_replied: bool,
105
106 delay_info_replied: bool,
108
109 #[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 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 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 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 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(¤t);
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 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 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 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 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 responder.drop_without_shutdown();
483 return Ok(());
484 }
485 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 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 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 assert_eq!(Err(zx::Status::PEER_CLOSED), client.channel().write(&[0], &mut Vec::new()));
606 }
607
608 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 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 ); 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 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 exec.set_fake_time(fasync::MonotonicInstant::after(zx::MonotonicDuration::from_millis(
678 201,
679 )));
680 let _ = exec.wake_expired_timers();
681 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 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 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 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 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}