1use anyhow::format_err;
6use fidl_fuchsia_bluetooth_bredr as bredr;
7use fidl_fuchsia_media as media;
8use fuchsia_async as fasync;
9use fuchsia_audio_codec::{StreamProcessor, StreamProcessorOutputStream};
10use fuchsia_audio_device::stream_config::SoftStreamConfig;
11use fuchsia_audio_device::{AudioFrameSink, AudioFrameStream, AudioStreamItem};
12use fuchsia_bluetooth::types::{PeerId, peer_audio_stream_id};
13use fuchsia_sync::Mutex;
14use futures::stream::BoxStream;
15use futures::task::Context;
16use futures::{AsyncWriteExt, FutureExt, StreamExt};
17use log::{error, info, warn};
18use media::AudioDeviceEnumeratorProxy;
19use std::pin::pin;
20
21use crate::audio::{Control, ControlEvent, Error, HF_INPUT_UUID, HF_OUTPUT_UUID};
22use crate::codec_id::CodecId;
23use crate::sco;
24
25pub struct InbandControl {
28 audio_core: media::AudioDeviceEnumeratorProxy,
29 session_task: Option<(PeerId, fasync::Task<()>)>,
30 event_sender: Mutex<futures::channel::mpsc::Sender<ControlEvent>>,
31 stream: Mutex<Option<futures::channel::mpsc::Receiver<ControlEvent>>>,
32}
33
34struct AudioSession {
39 audio_frame_sink: AudioFrameSink,
40 audio_frame_stream: AudioFrameStream,
41 sco: sco::Connection,
42 codec: CodecId,
43 decoder: StreamProcessor,
44 encoder: StreamProcessor,
45 event_sender: futures::channel::mpsc::Sender<ControlEvent>,
46}
47
48impl AudioSession {
49 fn setup(
50 connection: sco::Connection,
51 codec: CodecId,
52 audio_frame_sink: AudioFrameSink,
53 audio_frame_stream: AudioFrameStream,
54 event_sender: futures::channel::mpsc::Sender<ControlEvent>,
55 ) -> Result<Self, Error> {
56 if !codec.is_supported() {
57 return Err(Error::UnsupportedParameters {
58 source: format_err!("unsupported codec {codec}"),
59 });
60 }
61 let decoder = StreamProcessor::create_decoder(codec.mime_type()?, Some(codec.oob_bytes()))
62 .map_err(|e| Error::audio_core(format_err!("creating decoder: {e:?}")))?;
63 let encoder = StreamProcessor::create_encoder(codec.try_into()?, codec.try_into()?)
64 .map_err(|e| Error::audio_core(format_err!("creating encoder: {e:?}")))?;
65 Ok(Self {
66 sco: connection,
67 decoder,
68 encoder,
69 audio_frame_sink,
70 audio_frame_stream,
71 codec,
72 event_sender,
73 })
74 }
75
76 async fn encoder_to_sco(
77 mut encoded_stream: StreamProcessorOutputStream,
78 proxy: bredr::ScoConnectionProxy,
79 codec: CodecId,
80 ) -> Error {
81 let packet: Vec<u8> = vec![0; 60]; let mut request =
84 bredr::ScoConnectionWriteRequest { data: Some(packet), ..Default::default() };
85
86 const MSBC_ENCODED_LEN: usize = 57; if codec == CodecId::MSBC {
88 let packet: &mut [u8] = request.data.as_mut().unwrap().as_mut_slice();
89 packet[0] = 0x01; }
92 let mut h2_marker = [0x08u8, 0x38, 0xc8, 0xf8].iter().cycle();
94 loop {
95 match encoded_stream.next().await {
96 Some(Ok(encoded)) => {
97 if codec == CodecId::MSBC {
98 if encoded.len() % MSBC_ENCODED_LEN != 0 {
99 warn!("Got {} bytes, uneven number of packets", encoded.len());
100 }
101 for sbc_packet in encoded.as_slice().chunks_exact(MSBC_ENCODED_LEN) {
102 let packet: &mut [u8] = request.data.as_mut().unwrap().as_mut_slice();
103 packet[1] = *h2_marker.next().unwrap();
104 packet[2..59].copy_from_slice(sbc_packet);
105 if let Err(e) = proxy.write(&request).await {
106 return e.into();
107 }
108 }
109 } else {
110 for cvsd_packet in encoded.as_slice().chunks_exact(60) {
113 let packet: &mut [u8] = request.data.as_mut().unwrap().as_mut_slice();
114 packet.copy_from_slice(cvsd_packet);
115 if let Err(e) = proxy.write(&request).await {
116 return e.into();
117 }
118 }
119 }
120 }
121 Some(Err(e)) => {
122 warn!("Error in encoding: {e:?}");
123 return Error::audio_core(format_err!("Couldn't read encoded: {e:?}"));
124 }
125 None => {
126 warn!("Error in encoding: Stream is ended!");
127 return Error::audio_core(format_err!("Encoder stream ended early"));
128 }
129 }
130 }
131 }
132
133 async fn pcm_to_encoder(mut encoder: StreamProcessor, mut stream: AudioFrameStream) -> Error {
134 loop {
135 match stream.next().await {
136 Some(Ok(AudioStreamItem::Data(pcm))) => {
137 if let Err(e) = encoder.write_all(pcm.as_slice()).await {
138 return Error::audio_core(format_err!("write to encoder: {e:?}"));
139 }
140 if let Err(e) = encoder.flush().await {
142 return Error::audio_core(format_err!("flush encoder: {e:?}"));
143 }
144 }
145 Some(Ok(AudioStreamItem::AudioDisabled)) => {
146 continue;
147 }
148 Some(Err(e)) => {
149 warn!("Audio output error: {e:?}");
150 return Error::audio_core(format_err!("output error: {e:?}"));
151 }
152 None => {
153 warn!("Ran out of audio input!");
154 return Error::audio_core(format_err!("Audio input end"));
155 }
156 }
157 }
158 }
159
160 async fn decoder_to_pcm(
161 mut decoded_stream: StreamProcessorOutputStream,
162 mut sink: AudioFrameSink,
163 ) -> Error {
164 let mut decoded_packets = 0;
165 loop {
166 match decoded_stream.next().await {
167 Some(Ok(decoded)) => {
168 decoded_packets += 1;
169 if decoded_packets % 500 == 0 {
170 info!(
171 "Got {} decoded bytes from decoder: {decoded_packets} packets",
172 decoded.len()
173 );
174 }
175 if let Err(e) = sink.write_all(decoded.as_slice()).await {
176 warn!("Error sending to sink: {e:?}");
177 return Error::audio_core(format_err!("send to sink: {e:?}"));
178 }
179 }
180 Some(Err(e)) => {
181 warn!("Error in decoding: {e:?}");
182 return Error::audio_core(format_err!("Couldn't read decoder: {e:?}"));
183 }
184 None => {
185 warn!("Error in decoding: Stream is ended!");
186 return Error::audio_core(format_err!("Decoder stream ended early"));
187 }
188 }
189 }
190 }
191
192 async fn sco_to_decoder(
193 proxy: bredr::ScoConnectionProxy,
194 mut decoder: StreamProcessor,
195 codec: CodecId,
196 ) -> Error {
197 loop {
198 let data = match proxy.read().await {
199 Ok(bredr::ScoConnectionReadResponse { data: Some(data), .. }) => data,
200 Ok(_) => return Error::audio_core(format_err!("Invalid Read response")),
201 Err(e) => return e.into(),
202 };
203 let packet = match codec {
204 CodecId::CVSD => data.as_slice(),
205 CodecId::MSBC => {
206 let (_header, packet) = data.as_slice().split_at(2);
208 if packet[0] != 0xad {
209 info!(
210 "Packet didn't start with syncword: {:#02x} {}",
211 packet[0],
212 packet.len()
213 );
214 }
215 packet
216 }
217 _ => {
218 return Error::UnsupportedParameters {
219 source: format_err!("Unknown CodecId: {codec:?}"),
220 };
221 }
222 };
223 if let Err(e) = decoder.write_all(packet).await {
224 return Error::audio_core(format_err!("Failed to write to decoder: {e:?}"));
225 }
226 if let Err(e) = decoder.flush().await {
229 return Error::audio_core(format_err!("Failed to flush decoder: {e:?}"));
230 }
231 }
232 }
233
234 async fn run(mut self) {
235 let peer_id = self.sco.peer_id;
236 let Ok(encoded_stream) = self.encoder.take_output_stream() else {
237 error!("Couldn't take encoder output stream");
238 return;
239 };
240 let sco_write =
241 AudioSession::encoder_to_sco(encoded_stream, self.sco.proxy.clone(), self.codec);
242 let sco_write = pin!(sco_write);
243 let audio_to_encoder = AudioSession::pcm_to_encoder(self.encoder, self.audio_frame_stream);
244 let audio_to_encoder = pin!(audio_to_encoder);
245
246 let Ok(decoded_stream) = self.decoder.take_output_stream() else {
247 error!("Couldn't take decoder output stream");
248 return;
249 };
250 let decoder_to_sink =
251 pin!(AudioSession::decoder_to_pcm(decoded_stream, self.audio_frame_sink));
252 let sco_read =
253 AudioSession::sco_to_decoder(self.sco.proxy.clone(), self.decoder, self.codec);
254 let sco_read = pin!(sco_read);
255 let e = futures::select! {
256 e = audio_to_encoder.fuse() => { warn!(e:?; "PCM to encoder write"); e},
257 e = sco_write.fuse() => { warn!(e:?; "Write encoded to SCO"); e},
258 e = sco_read.fuse() => { warn!(e:?; "SCO read to decoder"); e},
259 e = decoder_to_sink.fuse() => { warn!(e:?; "SCO decoder to PCM"); e},
260 };
261 let _ = self.event_sender.try_send(ControlEvent::Stopped { id: peer_id, error: Some(e) });
262 }
263
264 fn start(self) -> fasync::Task<()> {
265 fasync::Task::spawn(self.run())
266 }
267}
268
269impl InbandControl {
270 pub fn create(proxy: AudioDeviceEnumeratorProxy) -> Result<Self, Error> {
271 let (sender, receiver) = futures::channel::mpsc::channel(1);
272 Ok(Self {
273 audio_core: proxy,
274 session_task: None,
275 event_sender: Mutex::new(sender),
276 stream: Mutex::new(Some(receiver)),
277 })
278 }
279
280 fn running_id(&mut self) -> Option<PeerId> {
281 self.session_task
282 .as_mut()
283 .and_then(|(running, task)| {
284 let mut cx = Context::from_waker(&std::task::Waker::noop());
285 task.poll_unpin(&mut cx).is_pending().then_some(running)
288 })
289 .copied()
290 }
291
292 const LOCAL_MONOTONIC_CLOCK_DOMAIN: u32 = 0;
293
294 const AUDIO_BUFFER_DURATION: zx::MonotonicDuration = zx::MonotonicDuration::from_millis(15);
297
298 fn start_input(&mut self, peer_id: PeerId, codec_id: CodecId) -> Result<AudioFrameSink, Error> {
299 let audio_dev_id = peer_audio_stream_id(peer_id, HF_INPUT_UUID);
300 let (client, sink) = SoftStreamConfig::create_input(
301 &audio_dev_id,
302 "Fuchsia",
303 super::DEVICE_NAME,
304 Self::LOCAL_MONOTONIC_CLOCK_DOMAIN,
305 codec_id.try_into()?,
306 Self::AUDIO_BUFFER_DURATION,
307 )
308 .map_err(|e| Error::audio_core(format_err!("Couldn't create input: {e:?}")))?;
309
310 self.audio_core.add_device_by_channel(super::DEVICE_NAME, true, client)?;
311 Ok(sink)
312 }
313
314 fn start_output(
315 &mut self,
316 peer_id: PeerId,
317 codec_id: CodecId,
318 ) -> Result<AudioFrameStream, Error> {
319 let audio_dev_id = peer_audio_stream_id(peer_id, HF_OUTPUT_UUID);
320 let (client, stream) = SoftStreamConfig::create_output(
321 &audio_dev_id,
322 "Fuchsia",
323 super::DEVICE_NAME,
324 Self::LOCAL_MONOTONIC_CLOCK_DOMAIN,
325 codec_id.try_into()?,
326 Self::AUDIO_BUFFER_DURATION,
327 zx::MonotonicDuration::from_millis(0),
328 )
329 .map_err(|e| Error::audio_core(format_err!("Couldn't create output: {e:?}")))?;
330 self.audio_core.add_device_by_channel(super::DEVICE_NAME, false, client)?;
331 Ok(stream)
332 }
333}
334
335impl Control for InbandControl {
336 fn start(
337 &mut self,
338 id: PeerId,
339 connection: sco::Connection,
340 codec: CodecId,
341 ) -> Result<(), Error> {
342 if let Some(running) = self.running_id() {
343 if running == id {
344 return Err(Error::AlreadyStarted);
345 }
346 return Err(Error::UnsupportedParameters {
347 source: format_err!("Only one peer can be started inband at once"),
348 });
349 }
350 let frame_sink = self.start_input(id, codec)?;
351 let frame_stream = self.start_output(id, codec)?;
352 let session = AudioSession::setup(
353 connection,
354 codec,
355 frame_sink,
356 frame_stream,
357 self.event_sender.lock().clone(),
358 )?;
359 self.session_task = Some((id, session.start()));
360 Ok(())
361 }
362
363 fn stop(&mut self, id: PeerId) -> Result<(), Error> {
364 if self.running_id() != Some(id) {
365 return Err(Error::NotStarted);
366 }
367 self.session_task = None;
368 let _ = self.event_sender.get_mut().try_send(ControlEvent::Stopped { id, error: None });
369 Ok(())
370 }
371
372 fn connect(&mut self, _id: PeerId, _supported_codecs: &[CodecId]) {
373 }
375
376 fn disconnect(&mut self, id: PeerId) {
377 let _ = self.stop(id);
378 }
379
380 fn take_events(&self) -> BoxStream<'static, ControlEvent> {
381 self.stream.lock().take().unwrap().boxed()
382 }
383
384 fn failed_request(&self, _request: ControlEvent, _error: Error) {
385 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 use fidl_fuchsia_bluetooth_bredr::ScoConnectionRequestStream;
394
395 use crate::sco::test_utils::connection_for_codec;
396
397 const ZERO_INPUT_SBC_PACKET: [u8; 60] = [
400 0x80, 0x10, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb,
401 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7,
402 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb,
403 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00,
404 ];
405
406 const ZERO_INPUT_CVSD_PACKET: [u8; 60] = [0x55; 60];
408
409 #[derive(PartialEq, Debug)]
410 enum ProcessedRequest {
411 ScoRead,
412 ScoWrite(Vec<u8>),
413 }
414
415 async fn process_sco_request(
417 sco_request_stream: &mut ScoConnectionRequestStream,
418 read_data: Vec<u8>,
419 ) -> Option<ProcessedRequest> {
420 match sco_request_stream.next().await {
421 Some(Ok(bredr::ScoConnectionRequest::Read { responder })) => {
422 let response = bredr::ScoConnectionReadResponse {
423 status_flag: Some(bredr::RxPacketStatus::CorrectlyReceivedData),
424 data: Some(read_data),
425 ..Default::default()
426 };
427 responder.send(&response).expect("sends okay");
428 Some(ProcessedRequest::ScoRead)
429 }
430 Some(Ok(bredr::ScoConnectionRequest::Write { payload, responder })) => {
431 responder.send().expect("response to write");
432 Some(ProcessedRequest::ScoWrite(payload.data.unwrap()))
433 }
434 None => None,
435 x => panic!("Expected read or write requests, got {x:?}"),
436 }
437 }
438
439 #[fuchsia::test]
440 async fn reads_audio_from_connection() {
441 let (proxy, _audio_enumerator_requests) =
442 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
443 let mut control = InbandControl::create(proxy).unwrap();
444
445 let (connection, mut sco_request_stream) =
446 connection_for_codec(PeerId(1), CodecId::MSBC, true);
447
448 control.start(PeerId(1), connection, CodecId::MSBC).expect("should be able to start");
449
450 let (connection2, _request_stream) = connection_for_codec(PeerId(1), CodecId::MSBC, true);
451 let _ = control
452 .start(PeerId(1), connection2, CodecId::MSBC)
453 .expect_err("Starting twice shouldn't be allowed");
454
455 for _ in 1..10 {
458 assert_eq!(
459 Some(ProcessedRequest::ScoRead),
460 process_sco_request(&mut sco_request_stream, ZERO_INPUT_SBC_PACKET.to_vec()).await
461 );
462 }
463
464 control.stop(PeerId(1)).expect("should be able to stop");
465 let _ = control.stop(PeerId(1)).expect_err("can't stop a stopped thing");
466
467 let mut extra_requests = 0;
469 while let Some(r) =
470 process_sco_request(&mut sco_request_stream, ZERO_INPUT_SBC_PACKET.to_vec()).await
471 {
472 assert_eq!(ProcessedRequest::ScoRead, r);
473 extra_requests += 1;
474 }
475
476 info!("Got {extra_requests} extra ScoConnectionProxy Requests after stop");
477 }
478
479 #[fuchsia::test]
480 async fn audio_setup_error_bad_codec() {
481 let (proxy, _) =
482 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
483 let mut control = InbandControl::create(proxy).unwrap();
484
485 let (connection, _sco_request_stream) =
486 connection_for_codec(PeerId(1), CodecId::MSBC, true);
487 let res = control.start(PeerId(1), connection, 0xD0u8.into());
488 assert!(res.is_err());
489 }
490
491 #[fuchsia::test]
492 async fn decode_sco_audio_path() {
493 use fidl_fuchsia_hardware_audio as audio;
494 let (proxy, mut audio_enumerator_requests) =
495 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
496 let mut control = InbandControl::create(proxy).unwrap();
497
498 let (connection, mut sco_request_stream) =
499 connection_for_codec(PeerId(1), CodecId::MSBC, true);
500
501 control.start(PeerId(1), connection, CodecId::MSBC).expect("should be able to start");
502
503 let audio_input_stream_config;
504 let mut _audio_output_stream_config;
505 loop {
506 match audio_enumerator_requests.next().await {
507 Some(Ok(media::AudioDeviceEnumeratorRequest::AddDeviceByChannel {
508 is_input,
509 channel,
510 ..
511 })) => {
512 if is_input {
513 audio_input_stream_config = channel.into_proxy();
514 break;
515 } else {
516 _audio_output_stream_config = channel.into_proxy();
517 }
518 }
519 x => panic!("Expected audio device by channel, got {x:?}"),
520 }
521 }
522
523 let (ring_buffer, server) = fidl::endpoints::create_proxy::<audio::RingBufferMarker>();
524 audio_input_stream_config
525 .create_ring_buffer(&CodecId::MSBC.try_into().unwrap(), server)
526 .expect("create ring buffer");
527
528 assert_eq!(
530 Some(ProcessedRequest::ScoRead),
531 process_sco_request(&mut sco_request_stream, ZERO_INPUT_SBC_PACKET.to_vec()).await
532 );
533
534 let notifications_per_ring = 20;
535 let (frames, _vmo) = ring_buffer
538 .get_vmo(16000, notifications_per_ring)
539 .await
540 .expect("fidl")
541 .expect("response");
542
543 let mut position_info = ring_buffer.watch_clock_recovery_position_info();
545 let mut position_notifications = 0;
546
547 let _ = ring_buffer.start().await;
548
549 let frames_per_notification = frames / notifications_per_ring;
551 let expected_notifications = 12000 / frames_per_notification;
554
555 if position_info.poll_unpin(&mut Context::from_waker(&std::task::Waker::noop())).is_ready()
558 {
559 position_notifications += 1;
560 position_info = ring_buffer.watch_clock_recovery_position_info();
561 }
562 for _ in 1..100 {
563 assert_eq!(
564 Some(ProcessedRequest::ScoRead),
565 process_sco_request(&mut sco_request_stream, ZERO_INPUT_SBC_PACKET.to_vec()).await
566 );
567 if position_info
569 .poll_unpin(&mut Context::from_waker(&std::task::Waker::noop()))
570 .is_ready()
571 {
572 position_notifications += 1;
573 position_info = ring_buffer.watch_clock_recovery_position_info();
574 }
575 }
576
577 assert!(position_notifications >= expected_notifications);
581 assert!(position_notifications <= expected_notifications + 1);
582 }
583
584 #[fuchsia::test]
585 async fn encode_sco_audio_path_msbc() {
586 use fidl_fuchsia_hardware_audio as audio;
587 let (proxy, mut audio_enumerator_requests) =
588 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
589 let mut control = InbandControl::create(proxy).unwrap();
590
591 let (connection, mut sco_request_stream) =
592 connection_for_codec(PeerId(1), CodecId::MSBC, true);
593
594 control.start(PeerId(1), connection, CodecId::MSBC).expect("should be able to start");
595
596 let audio_output_stream_config;
597 let mut _audio_input_stream_config;
598 loop {
599 match audio_enumerator_requests.next().await {
600 Some(Ok(media::AudioDeviceEnumeratorRequest::AddDeviceByChannel {
601 is_input,
602 channel,
603 ..
604 })) => {
605 if !is_input {
606 audio_output_stream_config = channel.into_proxy();
607 break;
608 } else {
609 _audio_input_stream_config = channel.into_proxy();
610 }
611 }
612 x => panic!("Expected audio device by channel, got {x:?}"),
613 }
614 }
615
616 let (ring_buffer, server) = fidl::endpoints::create_proxy::<audio::RingBufferMarker>();
617 audio_output_stream_config
618 .create_ring_buffer(&CodecId::MSBC.try_into().unwrap(), server)
619 .unwrap();
620
621 let notifications_per_ring = 20;
625 let (_frames, _vmo) = ring_buffer
627 .get_vmo(16000, notifications_per_ring)
628 .await
629 .expect("fidl")
630 .expect("response");
631
632 let _ = ring_buffer.start().await;
633
634 let next_header = &mut [0x01, 0x08];
636 for _sco_frame in 1..100 {
637 'sco: loop {
638 match process_sco_request(&mut sco_request_stream, ZERO_INPUT_SBC_PACKET.to_vec())
639 .await
640 {
641 Some(ProcessedRequest::ScoRead) => continue 'sco,
642 Some(ProcessedRequest::ScoWrite(data)) => {
643 assert_eq!(60, data.len());
644 assert_eq!(&ZERO_INPUT_SBC_PACKET[2..], &data[2..]);
646 assert_eq!(next_header, &data[0..2]);
647 match next_header[1] {
649 0x08 => next_header[1] = 0x38,
650 0x38 => next_header[1] = 0xc8,
651 0xc8 => next_header[1] = 0xf8,
652 0xf8 => next_header[1] = 0x08,
653 _ => unreachable!(),
654 };
655 break 'sco;
656 }
657 x => panic!("Expected read or write but got {x:?}"),
658 };
659 }
660 }
661 }
662
663 #[fuchsia::test]
664 async fn encode_sco_audio_path_cvsd() {
665 use fidl_fuchsia_hardware_audio as audio;
666 let (proxy, mut audio_enumerator_requests) =
667 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
668 let mut control = InbandControl::create(proxy).unwrap();
669
670 let (connection, mut sco_request_stream) =
671 connection_for_codec(PeerId(1), CodecId::CVSD, true);
672
673 control.start(PeerId(1), connection, CodecId::CVSD).expect("should be able to start");
674
675 let audio_output_stream_config;
676 let mut _audio_input_stream_config;
677 loop {
678 match audio_enumerator_requests.next().await {
679 Some(Ok(media::AudioDeviceEnumeratorRequest::AddDeviceByChannel {
680 is_input,
681 channel,
682 ..
683 })) => {
684 if !is_input {
685 audio_output_stream_config = channel.into_proxy();
686 break;
687 } else {
688 _audio_input_stream_config = channel.into_proxy();
689 }
690 }
691 x => panic!("Expected audio device by channel, got {x:?}"),
692 }
693 }
694
695 let (ring_buffer, server) = fidl::endpoints::create_proxy::<audio::RingBufferMarker>();
696 audio_output_stream_config
697 .create_ring_buffer(&CodecId::CVSD.try_into().unwrap(), server)
698 .unwrap();
699
700 let notifications_per_ring = 10;
704 let (_frames, _vmo) = ring_buffer
706 .get_vmo(64000, notifications_per_ring)
707 .await
708 .expect("fidl")
709 .expect("response");
710
711 let _ = ring_buffer.start().await;
712
713 for _sco_frame in 1..100 {
715 'sco: loop {
716 match process_sco_request(&mut sco_request_stream, ZERO_INPUT_CVSD_PACKET.to_vec())
717 .await
718 {
719 Some(ProcessedRequest::ScoRead) => continue 'sco,
720 Some(ProcessedRequest::ScoWrite(data)) => {
721 assert_eq!(60, data.len());
723 assert_eq!(&ZERO_INPUT_CVSD_PACKET, data.as_slice());
724 break 'sco;
725 }
726 x => panic!("Expected read or write but got {x:?}"),
727 };
728 }
729 }
730 }
731
732 #[fuchsia::test]
733 async fn read_from_audio_output() {
734 use fidl_fuchsia_hardware_audio as audio;
735 let (proxy, mut audio_enumerator_requests) =
736 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
737 let mut control = InbandControl::create(proxy).unwrap();
738
739 let (connection, mut sco_request_stream) =
740 connection_for_codec(PeerId(1), CodecId::MSBC, true);
741
742 control.start(PeerId(1), connection, CodecId::MSBC).expect("should be able to start");
743
744 let audio_output_stream_config;
745 let mut _audio_input_stream_config;
746 loop {
747 match audio_enumerator_requests.next().await {
748 Some(Ok(media::AudioDeviceEnumeratorRequest::AddDeviceByChannel {
749 is_input,
750 channel,
751 ..
752 })) => {
753 if !is_input {
754 audio_output_stream_config = channel.into_proxy();
755 break;
756 } else {
757 _audio_input_stream_config = channel.into_proxy();
758 }
759 }
760 x => panic!("Expected audio device by channel, got {x:?}"),
761 }
762 }
763
764 let (ring_buffer, server) = fidl::endpoints::create_proxy::<audio::RingBufferMarker>();
765 audio_output_stream_config
766 .create_ring_buffer(&CodecId::MSBC.try_into().unwrap(), server)
767 .expect("create ring buffer");
768
769 let notifications_per_ring = 20;
770 let (_frames, _vmo) = ring_buffer
772 .get_vmo(16000, notifications_per_ring)
773 .await
774 .expect("fidl")
775 .expect("response");
776
777 let _ = ring_buffer.start().await;
778
779 'position_notifications: for i in 1..20 {
782 let mut position_info = ring_buffer.watch_clock_recovery_position_info();
783 loop {
784 let sco_activity = Box::pin(process_sco_request(
785 &mut sco_request_stream,
786 ZERO_INPUT_SBC_PACKET.to_vec(),
787 ));
788 use futures::future::Either;
789 match futures::future::select(position_info, sco_activity).await {
790 Either::Left((result, _sco_fut)) => {
791 assert!(result.is_ok(), "Position Info failed at {i}");
792 continue 'position_notifications;
793 }
794 Either::Right((_sco_pkt, position_info_fut)) => {
795 position_info = position_info_fut;
796 }
797 }
798 }
799 }
800 }
801
802 #[fuchsia::test]
803 async fn audio_output_error_sends_to_events() {
804 let (proxy, mut audio_enumerator_requests) =
805 fidl::endpoints::create_proxy_and_stream::<media::AudioDeviceEnumeratorMarker>();
806 let mut control = InbandControl::create(proxy).unwrap();
807 let mut events = control.take_events();
808
809 let (connection, _sco_request_stream) =
810 connection_for_codec(PeerId(1), CodecId::MSBC, true);
811
812 control.start(PeerId(1), connection, CodecId::MSBC).expect("should be able to start");
813
814 let audio_output_stream_config;
815 let mut _audio_input_stream_config;
816 loop {
817 match audio_enumerator_requests.next().await {
818 Some(Ok(media::AudioDeviceEnumeratorRequest::AddDeviceByChannel {
819 is_input,
820 channel,
821 ..
822 })) => {
823 if !is_input {
824 audio_output_stream_config = channel.into_proxy();
825 break;
826 } else {
827 _audio_input_stream_config = channel.into_proxy();
828 }
829 }
830 x => panic!("Expected audio device by channel, got {x:?}"),
831 }
832 }
833
834 drop(audio_output_stream_config);
835
836 match events.next().await {
838 Some(ControlEvent::Stopped { id, error: Some(_) }) => {
839 assert_eq!(PeerId(1), id);
840 }
841 x => panic!("Expected the peer to have error stop, but got {x:?}"),
842 };
843 }
844}