1use bt_avdtp::{EndpointType, MediaStream};
6use dyn_clone::DynClone;
7use fidl_fuchsia_bluetooth_bredr::AudioOffloadExtProxy;
8use fuchsia_bluetooth::types::PeerId;
9use fuchsia_inspect::Node;
10use fuchsia_inspect_derive::AttachError;
11use futures::FutureExt;
12use futures::future::{BoxFuture, Shared};
13use std::sync::Arc;
14use std::time::Duration;
15use thiserror::Error;
16
17use crate::codec::MediaCodecConfig;
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum MediaTaskStatus {
21 AudioDisabled,
22 Stopped,
23}
24
25#[derive(Debug, Error, Clone)]
26#[non_exhaustive]
27pub enum MediaTaskError {
28 #[error("Operation or configuration not supported")]
29 NotSupported,
30 #[error("Peer closed the media stream")]
31 PeerClosed,
32 #[error("Resources needed are already being used")]
33 ResourcesInUse,
34 #[error("Media stream error: {0}")]
35 MediaStream(Arc<std::io::Error>),
36 #[error("Codec configuration error: {0}")]
37 CodecConfig(Arc<bt_avdtp::Error>),
38 #[error("FIDL error: {0}")]
39 Fidl(#[from] fidl::Error),
40 #[error("Other Media Task Error: {0}")]
41 Other(String),
42}
43
44impl From<std::io::Error> for MediaTaskError {
45 fn from(error: std::io::Error) -> Self {
46 Self::MediaStream(Arc::new(error))
47 }
48}
49
50impl From<bt_avdtp::Error> for MediaTaskError {
51 fn from(error: bt_avdtp::Error) -> Self {
52 Self::CodecConfig(Arc::new(error))
53 }
54}
55
56impl From<anyhow::Error> for MediaTaskError {
57 fn from(error: anyhow::Error) -> Self {
58 Self::Other(error.to_string())
59 }
60}
61
62pub trait MediaTaskBuilder: Send + Sync + DynClone {
69 fn configure(
73 &self,
74 peer_id: &PeerId,
75 codec_config: &MediaCodecConfig,
76 ) -> Result<Box<dyn MediaTaskRunner>, MediaTaskError>;
77
78 fn direction(&self) -> EndpointType;
82
83 fn supported_configs(
90 &self,
91 peer_id: &PeerId,
92 offload: Option<AudioOffloadExtProxy>,
93 ) -> BoxFuture<'static, Result<Vec<MediaCodecConfig>, MediaTaskError>>;
94}
95
96dyn_clone::clone_trait_object!(MediaTaskBuilder);
97
98pub trait MediaTaskRunner: Send {
103 fn start(
109 &mut self,
110 stream: MediaStream,
111 offload: Option<AudioOffloadExtProxy>,
112 ) -> Result<Box<dyn MediaTask>, MediaTaskError>;
113
114 fn reconfigure(&mut self, _config: &MediaCodecConfig) -> Result<(), MediaTaskError> {
118 Err(MediaTaskError::NotSupported)
119 }
120
121 fn set_delay(&mut self, _delay: Duration) -> Result<(), MediaTaskError> {
127 Err(MediaTaskError::NotSupported)
128 }
129
130 fn watch_active(&mut self) -> BoxFuture<'static, bool> {
137 futures::future::ready(true).boxed()
138 }
139
140 fn iattach(&mut self, _parent: &Node, _name: &str) -> Result<(), AttachError> {
143 Err("attach not implemented".into())
144 }
145}
146
147pub trait MediaTask: Send {
152 fn finished(&mut self) -> BoxFuture<'static, Result<MediaTaskStatus, MediaTaskError>>;
155
156 fn result(&mut self) -> Option<Result<MediaTaskStatus, MediaTaskError>> {
158 self.finished().now_or_never()
159 }
160
161 fn stop(&mut self) -> Result<MediaTaskStatus, MediaTaskError>;
166}
167
168pub mod tests {
169 use super::*;
170
171 use fuchsia_sync::Mutex;
172 use futures::channel::{mpsc, oneshot};
173 use futures::stream::StreamExt;
174 use futures::{Future, TryFutureExt};
175 use std::fmt;
176 use std::sync::Arc;
177 use std::task::Poll;
178
179 #[derive(Clone)]
180 pub struct TestMediaTask {
181 pub peer_id: PeerId,
183 pub codec_config: MediaCodecConfig,
185 pub stream: Arc<Mutex<Option<MediaStream>>>,
187 sender: Arc<Mutex<Option<oneshot::Sender<Result<MediaTaskStatus, MediaTaskError>>>>>,
189 result: Shared<BoxFuture<'static, Result<MediaTaskStatus, MediaTaskError>>>,
191 pub delay: Duration,
193 }
194
195 impl fmt::Debug for TestMediaTask {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 f.debug_struct("TestMediaTask")
198 .field("peer_id", &self.peer_id)
199 .field("codec_config", &self.codec_config)
200 .field("result", &self.result.clone().now_or_never())
201 .finish()
202 }
203 }
204
205 impl TestMediaTask {
206 pub fn new(
207 peer_id: PeerId,
208 codec_config: MediaCodecConfig,
209 stream: MediaStream,
210 delay: Duration,
211 ) -> Self {
212 let (sender, receiver) = oneshot::channel();
213 let result = receiver
214 .map_ok_or_else(|_err| Ok(MediaTaskStatus::Stopped), |result| result)
215 .boxed()
216 .shared();
217 Self {
218 peer_id,
219 codec_config,
220 stream: Arc::new(Mutex::new(Some(stream))),
221 sender: Arc::new(Mutex::new(Some(sender))),
222 result,
223 delay,
224 }
225 }
226
227 pub fn is_started(&self) -> bool {
229 self.stream.lock().is_some()
231 }
232
233 pub fn end_prematurely(
236 &self,
237 task_result: Option<Result<MediaTaskStatus, MediaTaskError>>,
238 ) {
239 let _removed_stream = self.stream.lock().take();
240 let mut lock = self.sender.lock();
241 let sender = lock.take();
242 if let (Some(result), Some(sender)) = (task_result, sender) {
243 sender.send(result).expect("send ok");
244 }
245 }
246 }
247
248 impl MediaTask for TestMediaTask {
249 fn finished(&mut self) -> BoxFuture<'static, Result<MediaTaskStatus, MediaTaskError>> {
250 self.result.clone().boxed()
251 }
252
253 fn stop(&mut self) -> Result<MediaTaskStatus, MediaTaskError> {
254 let _ = self.stream.lock().take();
255 {
256 let mut lock = self.sender.lock();
257 if let Some(sender) = lock.take() {
258 let _ = sender.send(Ok(MediaTaskStatus::Stopped));
259 return Ok(MediaTaskStatus::Stopped);
260 }
261 }
262 self.finished().now_or_never().unwrap()
264 }
265 }
266
267 pub struct TestMediaTaskRunner {
268 pub peer_id: PeerId,
270 pub codec_config: MediaCodecConfig,
272 pub reconfigurable: bool,
274 pub supports_set_delay: bool,
276 pub set_delay: Option<std::time::Duration>,
278 pub sender: mpsc::Sender<TestMediaTask>,
280 pub active_receiver: Option<Arc<Mutex<mpsc::UnboundedReceiver<bool>>>>,
282 }
283
284 impl MediaTaskRunner for TestMediaTaskRunner {
285 fn start(
286 &mut self,
287 stream: MediaStream,
288 _offload: Option<AudioOffloadExtProxy>,
289 ) -> Result<Box<dyn MediaTask>, MediaTaskError> {
290 let task = TestMediaTask::new(
291 self.peer_id.clone(),
292 self.codec_config.clone(),
293 stream,
294 self.set_delay.unwrap_or(Duration::ZERO),
295 );
296 let _ = self.sender.try_send(task.clone());
298 Ok(Box::new(task))
299 }
300
301 fn set_delay(&mut self, delay: std::time::Duration) -> Result<(), MediaTaskError> {
302 if self.supports_set_delay {
303 self.set_delay = Some(delay);
304 Ok(())
305 } else {
306 Err(MediaTaskError::NotSupported)
307 }
308 }
309
310 fn reconfigure(&mut self, config: &MediaCodecConfig) -> Result<(), MediaTaskError> {
311 if self.reconfigurable {
312 self.codec_config = config.clone();
313 Ok(())
314 } else {
315 Err(MediaTaskError::NotSupported)
316 }
317 }
318
319 fn watch_active(&mut self) -> BoxFuture<'static, bool> {
320 let Some(receiver) = self.active_receiver.clone() else {
321 return futures::future::ready(true).boxed();
322 };
323 futures::future::poll_fn(move |cx| match receiver.lock().poll_next_unpin(cx) {
324 Poll::Ready(Some(val)) => Poll::Ready(val),
325 Poll::Ready(None) => Poll::Pending,
328 Poll::Pending => Poll::Pending,
329 })
330 .boxed()
331 }
332 }
333
334 pub struct TestMediaTaskBuilder {
338 sender: Mutex<mpsc::Sender<TestMediaTask>>,
339 receiver: mpsc::Receiver<TestMediaTask>,
340 active_sender: mpsc::UnboundedSender<bool>,
341 active_receiver: Arc<Mutex<mpsc::UnboundedReceiver<bool>>>,
342 reconfigurable: bool,
343 supports_set_delay: bool,
344 configs: Vec<MediaCodecConfig>,
345 direction: EndpointType,
346 }
347
348 impl TestMediaTaskBuilder {
349 pub fn new() -> Self {
350 let (sender, receiver) = mpsc::channel(5);
351 let (active_sender, active_receiver) = mpsc::unbounded();
352 let _ = active_sender.unbounded_send(true);
353 Self {
354 sender: Mutex::new(sender),
355 receiver,
356 active_sender,
357 active_receiver: Arc::new(Mutex::new(active_receiver)),
358 reconfigurable: false,
359 supports_set_delay: false,
360 configs: vec![crate::codec::MediaCodecConfig::min_sbc()],
361 direction: EndpointType::Sink,
362 }
363 }
364
365 pub fn new_inactive() -> Self {
366 let (sender, receiver) = mpsc::channel(5);
367 let (active_sender, active_receiver) = mpsc::unbounded();
368 Self {
369 sender: Mutex::new(sender),
370 receiver,
371 active_sender,
372 active_receiver: Arc::new(Mutex::new(active_receiver)),
373 reconfigurable: false,
374 supports_set_delay: false,
375 configs: vec![crate::codec::MediaCodecConfig::min_sbc()],
376 direction: EndpointType::Sink,
377 }
378 }
379
380 pub fn set_active(&self, active: bool) {
381 let _ = self.active_sender.unbounded_send(active);
382 }
383
384 pub fn with_configs(&mut self, configs: Vec<MediaCodecConfig>) -> &mut Self {
385 self.configs = configs;
386 self
387 }
388
389 pub fn with_direction(&mut self, direction: EndpointType) -> &mut Self {
390 self.direction = direction;
391 self
392 }
393
394 pub fn new_reconfigurable() -> Self {
395 Self { reconfigurable: true, ..Self::new() }
396 }
397
398 pub fn new_delayable() -> Self {
399 Self { supports_set_delay: true, ..Self::new() }
400 }
401
402 pub fn builder(&self) -> Box<dyn MediaTaskBuilder> {
405 Box::new(TestMediaTaskBuilderBuilder {
406 sender: self.sender.lock().clone(),
407 active_receiver: self.active_receiver.clone(),
408 reconfigurable: self.reconfigurable,
409 supports_set_delay: self.supports_set_delay,
410 configs: self.configs.clone(),
411 direction: self.direction,
412 })
413 }
414
415 pub fn next_task(&mut self) -> impl Future<Output = Option<TestMediaTask>> + '_ {
419 self.receiver.next()
420 }
421
422 #[track_caller]
424 pub fn expect_task(&mut self) -> TestMediaTask {
425 self.receiver
426 .try_next()
427 .expect("should have made a task")
428 .expect("shouldn't have dropped all senders")
429 }
430 }
431
432 #[derive(Clone)]
433 struct TestMediaTaskBuilderBuilder {
434 sender: mpsc::Sender<TestMediaTask>,
435 active_receiver: Arc<Mutex<mpsc::UnboundedReceiver<bool>>>,
436 reconfigurable: bool,
437 supports_set_delay: bool,
438 configs: Vec<MediaCodecConfig>,
439 direction: EndpointType,
440 }
441
442 impl MediaTaskBuilder for TestMediaTaskBuilderBuilder {
443 fn configure(
444 &self,
445 peer_id: &PeerId,
446 codec_config: &MediaCodecConfig,
447 ) -> Result<Box<dyn MediaTaskRunner>, MediaTaskError> {
448 let runner = TestMediaTaskRunner {
449 peer_id: peer_id.clone(),
450 codec_config: codec_config.clone(),
451 sender: self.sender.clone(),
452 active_receiver: Some(self.active_receiver.clone()),
453 reconfigurable: self.reconfigurable,
454 supports_set_delay: self.supports_set_delay,
455 set_delay: None,
456 };
457 Ok::<Box<dyn MediaTaskRunner>, _>(Box::new(runner))
458 }
459
460 fn direction(&self) -> EndpointType {
461 self.direction
462 }
463
464 fn supported_configs(
465 &self,
466 _peer_id: &PeerId,
467 _offload: Option<AudioOffloadExtProxy>,
468 ) -> BoxFuture<'static, Result<Vec<MediaCodecConfig>, MediaTaskError>> {
469 futures::future::ready(Ok(self.configs.clone())).boxed()
470 }
471 }
472}