1use anyhow::Error;
6use bt_avdtp::{
7 self as avdtp, ErrorCode, ServiceCapability, ServiceCategory, StreamEndpoint, StreamEndpointId,
8};
9use fidl_fuchsia_bluetooth_bredr::AudioOffloadExtProxy;
10use fuchsia_bluetooth::types::PeerId;
11use fuchsia_inspect::{self as inspect, Property};
12use fuchsia_inspect_derive::{AttachError, Inspect};
13use futures::future::BoxFuture;
14use futures::{FutureExt, TryFutureExt};
15use log::{info, warn};
16use std::collections::HashMap;
17use std::fmt;
18use std::sync::Arc;
19use std::time::Duration;
20
21use crate::codec::{CodecNegotiation, MediaCodecConfig};
22use crate::media_task::{
23 MediaTask, MediaTaskBuilder, MediaTaskError, MediaTaskRunner, MediaTaskStatus,
24};
25
26pub struct Stream {
30 endpoint: StreamEndpoint,
31 media_task_builder: Arc<Box<dyn MediaTaskBuilder>>,
33 media_task_runner: Option<Box<dyn MediaTaskRunner>>,
35 media_task: Option<Box<dyn MediaTask>>,
37 peer_id: Option<PeerId>,
40 inspect: fuchsia_inspect::Node,
42}
43
44impl fmt::Debug for Stream {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.debug_struct("Stream")
47 .field("endpoint", &self.endpoint)
48 .field("peer_id", &self.peer_id)
49 .field("has media_task", &self.media_task.is_some())
50 .finish()
51 }
52}
53
54impl Inspect for &mut Stream {
55 fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
58 self.inspect = parent.create_child(name.as_ref());
59
60 let endpoint_state_prop = self.inspect.create_string("endpoint_state", "");
61 let callback =
62 move |stream: &StreamEndpoint| endpoint_state_prop.set(&format!("{:?}", stream));
63 callback(self.endpoint());
64 self.endpoint_mut().set_update_callback(Some(Box::new(callback)));
65 Ok(())
66 }
67}
68
69impl Stream {
70 pub fn build(endpoint: StreamEndpoint, media_task_builder: Box<dyn MediaTaskBuilder>) -> Self {
71 Self {
72 endpoint,
73 media_task_builder: Arc::new(media_task_builder),
74 media_task_runner: None,
75 media_task: None,
76 peer_id: None,
77 inspect: Default::default(),
78 }
79 }
80
81 fn as_new(&self) -> Self {
82 Self {
83 endpoint: self.endpoint.as_new(),
84 media_task_builder: self.media_task_builder.clone(),
85 media_task_runner: None,
86 media_task: None,
87 peer_id: None,
88 inspect: Default::default(),
89 }
90 }
91
92 pub fn endpoint(&self) -> &StreamEndpoint {
93 &self.endpoint
94 }
95
96 pub fn endpoint_mut(&mut self) -> &mut StreamEndpoint {
97 &mut self.endpoint
98 }
99
100 fn media_codec_config(&self) -> Option<MediaCodecConfig> {
101 find_codec_capability(self.endpoint.capabilities())
102 .and_then(|x| MediaCodecConfig::try_from(x).ok())
103 }
104
105 fn config_supported(&self, config: &MediaCodecConfig) -> bool {
108 let Some(supported) = self.media_codec_config() else {
109 return false;
110 };
111 supported.supports(&config)
112 }
113
114 fn config_compatible(&self, config: &MediaCodecConfig) -> bool {
117 let Some(supported) = self.media_codec_config() else {
118 return false;
119 };
120 MediaCodecConfig::negotiate(&supported, config).is_some()
121 }
122
123 fn build_media_task_runner(
124 &self,
125 peer_id: &PeerId,
126 config: &MediaCodecConfig,
127 ) -> Option<Box<dyn MediaTaskRunner>> {
128 match self.media_task_builder.configure(peer_id, &config) {
129 Err(e) => {
130 warn!("Failed to build media task: {e:?}");
131 None
132 }
133 Ok(mut media_task_runner) => {
134 if let Err(e) = media_task_runner.iattach(&self.inspect, "media_task") {
135 info!("Media Task inspect: {e}");
136 }
137 Some(media_task_runner)
138 }
139 }
140 }
141
142 fn supported_config_from_capability(
143 &self,
144 requested_cap: &ServiceCapability,
145 ) -> Option<MediaCodecConfig> {
146 MediaCodecConfig::try_from(requested_cap).ok().filter(|c| self.config_supported(c))
147 }
148
149 pub fn configure(
150 &mut self,
151 peer_id: &PeerId,
152 remote_id: &StreamEndpointId,
153 capabilities: Vec<ServiceCapability>,
154 ) -> Result<(), (ServiceCategory, ErrorCode)> {
155 if self.media_task_runner.is_some() {
156 return Err((ServiceCategory::None, ErrorCode::SepInUse));
157 }
158 let unsupported = ErrorCode::UnsupportedConfiguration;
159 let codec_cap =
160 find_codec_capability(&capabilities).ok_or((ServiceCategory::None, unsupported))?;
161 let media_unsupported = (ServiceCategory::MediaCodec, unsupported);
162 let config = self.supported_config_from_capability(codec_cap).ok_or(media_unsupported)?;
163 self.media_task_runner =
164 Some(self.build_media_task_runner(peer_id, &config).ok_or(media_unsupported)?);
165 self.peer_id = Some(peer_id.clone());
166 self.endpoint.configure(remote_id, capabilities).or_else(|e| {
167 self.media_task_runner = None;
168 Err(e)
169 })
170 }
171
172 pub fn set_delay(&mut self, delay: Duration) -> Result<(), ErrorCode> {
173 let Some(runner) = self.media_task_runner.as_mut() else {
174 return Err(ErrorCode::BadState);
175 };
176 match runner.set_delay(delay) {
177 Err(MediaTaskError::NotSupported) => Err(ErrorCode::NotSupportedCommand),
178 Err(_) => Err(ErrorCode::BadState),
179 Ok(()) => Ok(()),
180 }
181 }
182
183 pub fn reconfigure(
184 &mut self,
185 capabilities: Vec<ServiceCapability>,
186 ) -> Result<(), (ServiceCategory, ErrorCode)> {
187 let bad_state = (ServiceCategory::None, ErrorCode::BadState);
188 let _peer_id = self.peer_id.as_ref().ok_or(bad_state)?;
189 if let Some(requested_codec_cap) = find_codec_capability(&capabilities) {
190 let unsupported = (ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration);
191 let requested =
192 self.supported_config_from_capability(requested_codec_cap).ok_or(unsupported)?;
193 self.media_task_runner
194 .as_mut()
195 .ok_or(bad_state)?
196 .reconfigure(&requested)
197 .or(Err(unsupported))?;
198 }
199 self.endpoint.reconfigure(capabilities)
200 }
201
202 fn media_runner_ref(&mut self) -> Result<&mut Box<dyn MediaTaskRunner>, ErrorCode> {
203 self.media_task_runner.as_mut().ok_or(ErrorCode::BadState)
204 }
205
206 pub fn start(
210 &mut self,
211 ) -> Result<BoxFuture<'static, Result<MediaTaskStatus, Error>>, ErrorCode> {
212 let peer_id = self.peer_id.ok_or(ErrorCode::BadState)?;
213 if self.media_task_runner.is_none() {
214 return Err(ErrorCode::BadState);
215 }
216 let _ = self.endpoint.start()?;
217 let transport = self.endpoint.take_transport().ok_or(ErrorCode::BadState)?;
218 let offload = self.endpoint.audio_offload();
219 let mut task = match self.media_runner_ref()?.start(transport, offload) {
220 Ok(media_task) => media_task,
221 Err(e) => {
222 warn!("Failed to start media task: {e:?} {peer_id}");
223 let _ = self.endpoint.suspend()?;
224 return Err(ErrorCode::BadState);
225 }
226 };
227 let finished = task.finished();
228 self.media_task = Some(task);
229 Ok(finished.map_err(Into::into).boxed())
230 }
231
232 pub fn suspend(&mut self) -> Result<(), ErrorCode> {
234 self.endpoint.suspend()?;
235 let _ = self.media_task.take().ok_or(ErrorCode::BadState)?.stop();
236 Ok(())
237 }
238
239 pub fn watch_active(&mut self) -> BoxFuture<'static, bool> {
242 let Some(runner) = self.media_task_runner.as_mut() else {
243 return futures::future::ready(true).boxed();
244 };
245 runner.watch_active()
246 }
247
248 fn stop_media_task(&mut self) {
249 if let Some(mut task) = self.media_task.take() {
250 let _ = task.stop();
252 }
253 self.media_task_runner = None;
254 self.peer_id = None;
255 }
256
257 pub fn release(
259 &mut self,
260 responder: avdtp::SimpleResponder,
261 peer: &avdtp::Peer,
262 ) -> avdtp::Result<()> {
263 self.stop_media_task();
264 self.endpoint.release(responder, peer)
265 }
266
267 pub fn abort(&mut self) {
268 self.stop_media_task();
269 self.endpoint.abort()
270 }
271
272 pub async fn initiate_abort(&mut self, peer: &avdtp::Peer) {
273 self.stop_media_task();
274 self.endpoint.initiate_abort(peer).await
275 }
276}
277
278fn find_codec_capability(capabilities: &[ServiceCapability]) -> Option<&ServiceCapability> {
279 capabilities.iter().find(|cap| cap.category() == ServiceCategory::MediaCodec)
280}
281
282#[derive(Clone, Debug)]
284struct SeidRangeFrom {
285 from: u8,
286}
287
288impl Default for SeidRangeFrom {
289 fn default() -> Self {
290 Self { from: 1 }
291 }
292}
293
294impl Iterator for SeidRangeFrom {
295 type Item = u8;
296
297 fn next(&mut self) -> Option<Self::Item> {
298 let res = self.from;
299 if self.from == 0x3E {
300 self.from = 0x01;
301 } else {
302 self.from += 1;
303 }
304 Some(res)
305 }
306}
307
308pub struct StreamsBuilder {
311 builders: Vec<Box<dyn MediaTaskBuilder>>,
312 seid_range: SeidRangeFrom,
313 node: inspect::Node,
314}
315
316impl Default for StreamsBuilder {
317 fn default() -> Self {
318 Self {
319 builders: Default::default(),
320 seid_range: SeidRangeFrom { from: Self::START_SEID },
321 node: Default::default(),
322 }
323 }
324}
325
326impl Clone for StreamsBuilder {
327 fn clone(&self) -> Self {
328 Self {
329 builders: self.builders.clone(),
330 node: Default::default(),
331 seid_range: self.seid_range.clone(),
332 }
333 }
334}
335
336impl StreamsBuilder {
337 const START_SEID: u8 = 8;
340
341 pub fn add_builder(&mut self, builder: impl MediaTaskBuilder + 'static) {
343 self.builders.push(Box::new(builder));
344 self.node.record_uint("builders", self.builders.len() as u64);
345 }
346
347 pub async fn peer_streams(
348 &self,
349 peer_id: &PeerId,
350 offload: Option<AudioOffloadExtProxy>,
351 ) -> Result<Streams, MediaTaskError> {
352 let mut streams = Streams::default();
353 let mut seid_range = self.seid_range.clone();
354 for builder in &self.builders {
355 let endpoint_type = builder.direction();
356 let supported_res = builder.supported_configs(peer_id, offload.clone()).await;
357 let Ok(supported) = supported_res else {
358 info!(e:? = supported_res.err().unwrap(); "Failed to get supported configs from builder, skipping");
359 continue;
360 };
361 let codec_caps = supported.iter().map(ServiceCapability::from);
362 for codec_cap in codec_caps {
363 let capabilities = match endpoint_type {
364 avdtp::EndpointType::Source => vec![
365 ServiceCapability::MediaTransport,
366 ServiceCapability::DelayReporting,
367 codec_cap,
368 ],
369 avdtp::EndpointType::Sink => {
370 vec![ServiceCapability::MediaTransport, codec_cap]
371 }
372 };
373 let endpoint = avdtp::StreamEndpoint::new(
374 seid_range.next().unwrap(),
375 avdtp::MediaType::Audio,
376 endpoint_type,
377 capabilities,
378 )?;
379 streams.insert(Stream::build(endpoint, builder.clone()));
380 }
381 }
382 Ok(streams)
383 }
384
385 pub async fn negotiation(
386 &self,
387 peer_id: &PeerId,
388 offload: Option<AudioOffloadExtProxy>,
389 preferred_direction: avdtp::EndpointType,
390 ) -> Result<CodecNegotiation, Error> {
391 let mut caps_available = Vec::new();
392 for builder in &self.builders {
393 caps_available.extend(
394 builder
395 .supported_configs(peer_id, offload.clone())
396 .await?
397 .iter()
398 .map(ServiceCapability::from),
399 );
400 }
401 Ok(CodecNegotiation::build(caps_available, preferred_direction)?)
402 }
403}
404
405impl Inspect for &mut StreamsBuilder {
406 fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
407 self.node = parent.create_child(name.as_ref());
408 self.node.record_uint("builders", self.builders.len() as u64);
409 Ok(())
410 }
411}
412
413#[derive(Default)]
415pub struct Streams {
416 streams: HashMap<StreamEndpointId, Stream>,
417 inspect_node: fuchsia_inspect::Node,
418}
419
420impl fmt::Debug for Streams {
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 f.debug_struct("Streams").field("streams", &self.streams).finish()
423 }
424}
425
426impl Streams {
427 pub fn as_new(&self) -> Self {
430 let streams =
431 self.streams.iter().map(|(id, stream)| (id.clone(), stream.as_new())).collect();
432 Self { streams, ..Default::default() }
433 }
434
435 pub fn is_empty(&self) -> bool {
437 self.streams.is_empty()
438 }
439
440 pub fn insert(&mut self, stream: Stream) {
443 let local_id = stream.endpoint().local_id().clone();
444 if self.streams.insert(local_id.clone(), stream).is_some() {
445 warn!("Replacing stream with local id {local_id}");
446 }
447 }
448
449 pub fn get(&self, id: &StreamEndpointId) -> Option<&Stream> {
451 self.streams.get(id)
452 }
453
454 pub fn get_mut(&mut self, id: &StreamEndpointId) -> Option<&mut Stream> {
456 self.streams.get_mut(id)
457 }
458
459 pub fn information(&self) -> Vec<avdtp::StreamInformation> {
461 self.streams.values().map(|x| x.endpoint().information()).collect()
462 }
463
464 pub fn open(&self) -> impl Iterator<Item = &Stream> {
466 self.streams.values().filter(|s| s.endpoint().state() == avdtp::StreamState::Open)
467 }
468
469 pub fn streaming(&self) -> impl Iterator<Item = &Stream> {
471 self.streams.values().filter(|s| s.endpoint().state() == avdtp::StreamState::Streaming)
472 }
473
474 pub fn compatible(&self, codec_config: MediaCodecConfig) -> impl Iterator<Item = &Stream> {
476 self.streams.values().filter(move |s| s.config_compatible(&codec_config))
477 }
478}
479
480impl Inspect for &mut Streams {
481 fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
483 self.inspect_node = parent.create_child(name.as_ref());
484 for stream in self.streams.values_mut() {
485 stream.iattach(&self.inspect_node, inspect::unique_name("stream_"))?;
486 }
487 Ok(())
488 }
489}
490
491#[cfg(test)]
492pub(crate) mod tests {
493 use super::*;
494 use bt_channel_test_support::{Transport, create_test_channels};
495 use fuchsia_async as fasync;
496 use std::pin::pin;
497 use std::task::Poll;
498 use test_case::test_case;
499
500 use crate::media_task::tests::TestMediaTaskBuilder;
501 use crate::media_types::*;
502
503 pub(crate) fn sbc_mediacodec_capability() -> avdtp::ServiceCapability {
504 let sbc_codec_info = SbcCodecInfo::new(
505 SbcSamplingFrequency::FREQ48000HZ,
506 SbcChannelMode::MONO | SbcChannelMode::JOINT_STEREO,
507 SbcBlockCount::MANDATORY_SRC,
508 SbcSubBands::MANDATORY_SRC,
509 SbcAllocation::MANDATORY_SRC,
510 SbcCodecInfo::BITPOOL_MIN,
511 SbcCodecInfo::BITPOOL_MAX,
512 )
513 .expect("SBC codec info");
514
515 ServiceCapability::MediaCodec {
516 media_type: avdtp::MediaType::Audio,
517 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
518 codec_extra: sbc_codec_info.to_bytes().to_vec(),
519 }
520 }
521
522 pub(crate) fn aac_mediacodec_capability(bitrate: u32) -> avdtp::ServiceCapability {
523 let codec_info = AacCodecInfo::new(
524 AacObjectType::MANDATORY_SRC,
525 AacSamplingFrequency::FREQ48000HZ,
526 AacChannels::TWO,
527 true,
528 bitrate,
529 )
530 .expect("should work");
531 ServiceCapability::MediaCodec {
532 media_type: avdtp::MediaType::Audio,
533 codec_type: avdtp::MediaCodecType::AUDIO_AAC,
534 codec_extra: codec_info.to_bytes().to_vec(),
535 }
536 }
537
538 pub(crate) fn make_sbc_endpoint(seid: u8, direction: avdtp::EndpointType) -> StreamEndpoint {
539 StreamEndpoint::new(
540 seid,
541 avdtp::MediaType::Audio,
542 direction,
543 vec![avdtp::ServiceCapability::MediaTransport, sbc_mediacodec_capability()],
544 )
545 .expect("endpoint creation should succeed")
546 }
547
548 const LOW_BITRATE: u32 = 320_000;
549 const HIGH_BITRATE: u32 = 393_216;
550
551 pub(crate) fn make_aac_endpoint(seid: u8, direction: avdtp::EndpointType) -> StreamEndpoint {
552 StreamEndpoint::new(
553 seid,
554 avdtp::MediaType::Audio,
555 direction,
556 vec![avdtp::ServiceCapability::MediaTransport, aac_mediacodec_capability(LOW_BITRATE)],
557 )
558 .expect("endpoint creation should succeed")
559 }
560
561 fn make_stream(seid: u8, codec_type: avdtp::MediaCodecType) -> Stream {
562 let endpoint = match codec_type {
563 avdtp::MediaCodecType::AUDIO_SBC => {
564 make_sbc_endpoint(seid, avdtp::EndpointType::Source)
565 }
566 avdtp::MediaCodecType::AUDIO_AAC => {
567 make_aac_endpoint(seid, avdtp::EndpointType::Source)
568 }
569 _ => panic!("Unsupported codec_type"),
570 };
571 Stream::build(endpoint, TestMediaTaskBuilder::new().builder())
572 }
573
574 #[fuchsia::test]
575 fn streams_basic_functionality() {
576 let mut streams = Streams::default();
577
578 streams.insert(make_stream(1, avdtp::MediaCodecType::AUDIO_SBC));
579 streams.insert(make_stream(6, avdtp::MediaCodecType::AUDIO_AAC));
580
581 let first_id = 1_u8.try_into().expect("good id");
582 let missing_id = 5_u8.try_into().expect("good id");
583
584 assert!(streams.get(&first_id).is_some());
585 assert!(streams.get(&missing_id).is_none());
586
587 assert!(streams.get_mut(&first_id).is_some());
588 assert!(streams.get_mut(&missing_id).is_none());
589
590 let expected_info = vec![
591 make_sbc_endpoint(1, avdtp::EndpointType::Source).information(),
592 make_aac_endpoint(6, avdtp::EndpointType::Source).information(),
593 ];
594
595 let infos = streams.information();
596
597 assert_eq!(expected_info.len(), infos.len());
598
599 if infos[0].id() == &first_id {
600 assert_eq!(expected_info[0], infos[0]);
601 assert_eq!(expected_info[1], infos[1]);
602 } else {
603 assert_eq!(expected_info[0], infos[1]);
604 assert_eq!(expected_info[1], infos[0]);
605 }
606 }
607
608 #[fuchsia::test]
609 fn streams_filters_compatible_codecs() {
610 let mut streams = Streams::default();
611 streams.insert(make_stream(1, avdtp::MediaCodecType::AUDIO_SBC));
612 streams.insert(make_stream(6, avdtp::MediaCodecType::AUDIO_AAC));
613
614 let config_high_bitrate_aac =
616 MediaCodecConfig::try_from(&aac_mediacodec_capability(HIGH_BITRATE)).unwrap();
617
618 let compatible: Vec<_> = streams.compatible(config_high_bitrate_aac).collect();
619 assert_eq!(compatible.len(), 1);
620 let codec_capability = compatible[0]
621 .endpoint()
622 .capabilities()
623 .iter()
624 .find(|x| x.category() == avdtp::ServiceCategory::MediaCodec)
625 .expect("should have a codec");
626 assert_eq!(
627 MediaCodecConfig::try_from(codec_capability).unwrap().codec_type(),
628 &avdtp::MediaCodecType::AUDIO_AAC
629 );
630 }
631
632 #[test_case(Transport::Socket ; "socket")]
633 #[test_case(Transport::Fidl ; "fidl")]
634 #[fuchsia::test]
635 fn rejects_unsupported_configurations(transport: Transport) {
636 let _exec = fasync::TestExecutor::new();
638 let mut builder = TestMediaTaskBuilder::new_reconfigurable();
639 let mut stream =
640 Stream::build(make_sbc_endpoint(1, avdtp::EndpointType::Source), builder.builder());
641
642 let unsupported_sbc_codec_info = SbcCodecInfo::new(
644 SbcSamplingFrequency::FREQ44100HZ,
645 SbcChannelMode::JOINT_STEREO,
646 SbcBlockCount::SIXTEEN,
647 SbcSubBands::EIGHT,
648 SbcAllocation::LOUDNESS,
649 53,
650 53,
651 )
652 .expect("SBC codec info");
653
654 let unsupported_caps = vec![ServiceCapability::MediaCodec {
655 media_type: avdtp::MediaType::Audio,
656 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
657 codec_extra: unsupported_sbc_codec_info.to_bytes().to_vec(),
658 }];
659
660 let peer_id = PeerId(1);
661 let stream_id = 1.try_into().expect("StreamEndpointId");
662 let res = stream.configure(&peer_id, &stream_id, unsupported_caps.clone());
663 assert!(res.is_err());
664 assert_eq!(
665 res.err(),
666 Some((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
667 );
668
669 assert_eq!(
670 stream.reconfigure(unsupported_caps.clone()),
671 Err((ServiceCategory::None, ErrorCode::BadState))
672 );
673
674 let supported_sbc_codec_info = SbcCodecInfo::new(
675 SbcSamplingFrequency::FREQ48000HZ,
676 SbcChannelMode::JOINT_STEREO,
677 SbcBlockCount::SIXTEEN,
678 SbcSubBands::EIGHT,
679 SbcAllocation::LOUDNESS,
680 53,
681 53,
682 )
683 .expect("SBC codec info");
684
685 let sbc_codec_cap = ServiceCapability::MediaCodec {
686 media_type: avdtp::MediaType::Audio,
687 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
688 codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
689 };
690
691 let supported_caps = vec![ServiceCapability::MediaTransport, sbc_codec_cap.clone()];
692
693 let res = stream.configure(&peer_id, &stream_id, supported_caps.clone());
694 assert!(res.is_ok());
695
696 assert!(stream.endpoint_mut().establish().is_ok());
698 let (transport, _remote) = create_test_channels(transport);
699 match stream.endpoint_mut().receive_channel(transport) {
700 Ok(false) => {}
701 Ok(true) => panic!("Only should be expecting one channel"),
702 Err(e) => panic!("Expected channel to be accepted, got {:?}", e),
703 };
704
705 assert_eq!(
706 stream.reconfigure(unsupported_caps.clone()),
707 Err((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
708 );
709
710 let new_codec_caps = vec![ServiceCapability::MediaCodec {
711 media_type: avdtp::MediaType::Audio,
712 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
713 codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
714 }];
715
716 assert!(stream.reconfigure(new_codec_caps.clone()).is_ok());
717
718 let _ = stream.start().expect("stream should start ok");
720 let task = builder.expect_task();
721 assert_eq!(task.codec_config, MediaCodecConfig::try_from(&new_codec_caps[0]).unwrap());
722 }
723
724 #[test_case(Transport::Socket ; "socket")]
725 #[test_case(Transport::Fidl ; "fidl")]
726 #[fuchsia::test]
727 fn reconfigure_runner_fails(transport: Transport) {
728 let _exec = fasync::TestExecutor::new();
730 let mut builder = TestMediaTaskBuilder::new();
731 let mut stream =
732 Stream::build(make_sbc_endpoint(1, avdtp::EndpointType::Source), builder.builder());
733
734 let supported_sbc_codec_info = SbcCodecInfo::new(
735 SbcSamplingFrequency::FREQ48000HZ,
736 SbcChannelMode::JOINT_STEREO,
737 SbcBlockCount::SIXTEEN,
738 SbcSubBands::EIGHT,
739 SbcAllocation::LOUDNESS,
740 53,
741 53,
742 )
743 .expect("SBC codec info");
744
745 let orig_codec_cap = ServiceCapability::MediaCodec {
746 media_type: avdtp::MediaType::Audio,
747 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
748 codec_extra: supported_sbc_codec_info.to_bytes().to_vec(),
749 };
750
751 let supported_caps = vec![ServiceCapability::MediaTransport, orig_codec_cap.clone()];
752
753 let res = stream.configure(&PeerId(1), &(1.try_into().unwrap()), supported_caps.clone());
754 assert!(res.is_ok());
755
756 assert!(stream.endpoint_mut().establish().is_ok());
758 let (transport, _remote) = create_test_channels(transport);
759 match stream.endpoint_mut().receive_channel(transport) {
760 Ok(false) => {}
761 Ok(true) => panic!("Only should be expecting one channel"),
762 Err(e) => panic!("Expected channel to be accepted, got {:?}", e),
763 };
764
765 let _ = stream.start().expect("stream should start ok");
767 let task = builder.expect_task();
768 assert_eq!(task.codec_config, MediaCodecConfig::try_from(&orig_codec_cap).unwrap());
769 stream.suspend().expect("stream should suspend ok");
770
771 let mono_sbc_codec_info = SbcCodecInfo::new(
773 SbcSamplingFrequency::FREQ48000HZ,
774 SbcChannelMode::MONO,
775 SbcBlockCount::SIXTEEN,
776 SbcSubBands::EIGHT,
777 SbcAllocation::LOUDNESS,
778 53,
779 53,
780 )
781 .expect("SBC codec info");
782
783 let new_codec_caps = vec![ServiceCapability::MediaCodec {
784 media_type: avdtp::MediaType::Audio,
785 codec_type: avdtp::MediaCodecType::AUDIO_SBC,
786 codec_extra: mono_sbc_codec_info.to_bytes().to_vec(),
787 }];
788
789 assert_eq!(
791 stream.reconfigure(new_codec_caps.clone()),
792 Err((ServiceCategory::MediaCodec, ErrorCode::UnsupportedConfiguration))
793 );
794
795 let _ = stream.start().expect("stream should start ok");
797 let task = builder.expect_task();
798 assert_eq!(task.codec_config, MediaCodecConfig::try_from(&orig_codec_cap).unwrap());
799 stream.suspend().expect("stream should suspend ok")
800 }
801
802 #[test_case(Transport::Socket ; "socket")]
803 #[test_case(Transport::Fidl ; "fidl")]
804 #[fuchsia::test]
805 fn suspend_stops_media_task(transport: Transport) {
806 let mut exec = fasync::TestExecutor::new();
807
808 let mut task_builder = TestMediaTaskBuilder::new();
809 let mut stream = Stream::build(
810 make_sbc_endpoint(1, avdtp::EndpointType::Source),
811 task_builder.builder(),
812 );
813 let next_task_fut = task_builder.next_task();
814 let remote_id = 1_u8.try_into().expect("good id");
815
816 let sbc_codec_cap = sbc_mediacodec_capability();
817 let expected_codec_config =
818 MediaCodecConfig::try_from(&sbc_codec_cap).expect("codec config");
819
820 assert!(stream.configure(&PeerId(1), &remote_id, vec![]).is_err());
821 assert!(stream.configure(&PeerId(1), &remote_id, vec![sbc_codec_cap]).is_ok());
822
823 stream.endpoint_mut().establish().expect("establishment should start okay");
824 let (transport, _remote) = create_test_channels(transport);
825 let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
826
827 assert!(stream.start().is_ok());
828
829 let task = {
831 let mut next_task_fut = pin!(next_task_fut);
832 match exec.run_until_stalled(&mut next_task_fut) {
833 Poll::Ready(Some(task)) => task,
834 x => panic!("Expected next task to be sent after start, got {:?}", x),
835 }
836 };
837
838 assert_eq!(task.peer_id, PeerId(1));
839 assert_eq!(task.codec_config, expected_codec_config);
840
841 assert!(task.is_started());
842 assert!(stream.suspend().is_ok());
843 assert!(!task.is_started());
844 assert!(stream.start().is_ok());
845
846 let next_task_fut = task_builder.next_task();
847 let task = {
849 let mut next_task_fut = pin!(next_task_fut);
850 match exec.run_until_stalled(&mut next_task_fut) {
851 Poll::Ready(Some(task)) => task,
852 x => panic!("Expected next task to be sent after start, got {:?}", x),
853 }
854 };
855
856 assert!(task.is_started());
857 }
858
859 #[test_case(Transport::Socket ; "socket")]
860 #[test_case(Transport::Fidl ; "fidl")]
861 #[fuchsia::test]
862 fn media_task_ending_ends_future(transport: Transport) {
863 let mut exec = fasync::TestExecutor::new();
864
865 let mut task_builder = TestMediaTaskBuilder::new();
866 let mut stream = Stream::build(
867 make_sbc_endpoint(1, avdtp::EndpointType::Source),
868 task_builder.builder(),
869 );
870 let next_task_fut = task_builder.next_task();
871 let peer_id = PeerId(1);
872 let remote_id = 1_u8.try_into().expect("good id");
873
874 let sbc_codec_cap = sbc_mediacodec_capability();
875 let expected_codec_config =
876 MediaCodecConfig::try_from(&sbc_codec_cap).expect("codec config");
877
878 assert!(stream.configure(&peer_id, &remote_id, vec![]).is_err());
879 assert!(stream.configure(&peer_id, &remote_id, vec![sbc_codec_cap]).is_ok());
880
881 stream.endpoint_mut().establish().expect("establishment should start okay");
882 let (transport, _remote) = create_test_channels(transport);
883 let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
884
885 let stream_finish_fut = stream.start().expect("start to succeed with a future");
886 let mut stream_finish_fut = pin!(stream_finish_fut);
887
888 let task = {
889 let mut next_task_fut = pin!(next_task_fut);
890 match exec.run_until_stalled(&mut next_task_fut) {
891 Poll::Ready(Some(task)) => task,
892 x => panic!("Expected next task to be sent after start, got {:?}", x),
893 }
894 };
895
896 assert_eq!(task.peer_id, PeerId(1));
897 assert_eq!(task.codec_config, expected_codec_config);
898
899 assert!(task.is_started());
901
902 assert!(exec.run_until_stalled(&mut stream_finish_fut).is_pending());
903
904 task.end_prematurely(Some(Ok(MediaTaskStatus::Stopped)));
905 assert!(!task.is_started());
906
907 match exec.run_until_stalled(&mut stream_finish_fut) {
909 Poll::Ready(Ok(MediaTaskStatus::Stopped)) => {}
910 x => panic!("Expected to get ready Ok from finish future, but got {x:?}"),
911 };
912
913 assert!(stream.suspend().is_ok());
915
916 let result_fut = stream.start().expect("start to succeed with a future");
918
919 let next_task_fut = task_builder.next_task();
920 let mut next_task_fut = pin!(next_task_fut);
921 let task = match exec.run_until_stalled(&mut next_task_fut) {
922 Poll::Ready(Some(task)) => task,
923 x => panic!("Expected next task to be sent after restart, got {x:?}"),
924 };
925
926 assert!(task.is_started());
927
928 drop(result_fut);
930
931 assert!(task.is_started());
932 }
933
934 #[test_case(Transport::Socket ; "socket")]
935 #[test_case(Transport::Fidl ; "fidl")]
936 #[fuchsia::test]
937 fn set_delay_correct_results_transmits_to_task(transport: Transport) {
938 let mut _exec = fasync::TestExecutor::new();
939
940 let mut task_builder = TestMediaTaskBuilder::new_delayable();
941 let mut stream = Stream::build(
942 make_sbc_endpoint(1, avdtp::EndpointType::Source),
943 task_builder.builder(),
944 );
945 let peer_id = PeerId(1);
946 let remote_id = 1_u8.try_into().expect("good id");
947
948 let sbc_codec_cap = sbc_mediacodec_capability();
949
950 let code = stream
951 .set_delay(std::time::Duration::ZERO)
952 .expect_err("before configure, can't set a delay");
953 assert_eq!(ErrorCode::BadState, code);
954
955 assert!(stream.configure(&peer_id, &remote_id, vec![]).is_err());
956 assert!(stream.configure(&peer_id, &remote_id, vec![sbc_codec_cap]).is_ok());
957
958 let delay_set = std::time::Duration::from_nanos(0xfeed);
959
960 stream.set_delay(delay_set.clone()).expect("after configure, delay is fine");
961
962 stream.endpoint_mut().establish().expect("establishment should start okay");
963 let (transport, _remote) = create_test_channels(transport);
964 let _ = stream.endpoint_mut().receive_channel(transport).expect("ready for a channel");
965 let _stream_finish_fut = stream.start().expect("start to succeed with a future");
966
967 let media_task = task_builder.expect_task();
968 assert_eq!(delay_set, media_task.delay);
969 }
970}