Skip to main content

bt_a2dp/
media_task.rs

1// Copyright 2020 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 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
62/// MediaTaskRunners are configured with information about the media codec when either peer in a
63/// conversation configures a stream endpoint.  When successfully configured, they can start
64/// MediaTasks by accepting a MediaStream, which will provide or consume media on that stream until
65/// dropped or stopped.
66///
67/// A builder that will make media task runners from requested configurations.
68pub trait MediaTaskBuilder: Send + Sync + DynClone {
69    /// Configure a new stream based on the given `codec_config` parameters.
70    /// Returns a MediaTaskRunner if the configuration is supported, an
71    /// MediaTaskError::NotSupported otherwise.
72    fn configure(
73        &self,
74        peer_id: &PeerId,
75        codec_config: &MediaCodecConfig,
76    ) -> Result<Box<dyn MediaTaskRunner>, MediaTaskError>;
77
78    /// Return the direction of tasks created by this builder.
79    /// Source tasks provide local encoded audio to a peer.
80    /// Sink tasks consume encoded audio from a peer.
81    fn direction(&self) -> EndpointType;
82
83    /// Provide a set of encoded media configurations that this task can support.
84    /// This can vary based on current system capabilities, and should be checked before
85    /// communicating capabilities to each peer.
86    /// `offload` is a proxy to the offload capabilities of the controller for this peer.
87    /// Returns a future that resolves to the set of MediaCodecConfigs that this builder supports,
88    /// typically one config per MediaCodecType, or an error if building the configs failed.
89    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
98/// MediaTaskRunners represent an ability of the media system to start streaming media.
99/// They are configured for a specific codec by `MediaTaskBuilder::configure`
100/// Typically a MediaTaskRunner can start multiple streams without needing to be reconfigured,
101/// although possibly not simultaneously.
102pub trait MediaTaskRunner: Send {
103    /// Start a MediaTask using the MediaStream given.
104    /// If the task started, returns a MediaTask which will finish if the stream ends or an
105    /// error occurs, and can be stopped using `MediaTask::stop` or by dropping the MediaTask.
106    /// This can fail with MediaTaskError::ResourcesInUse if a MediaTask cannot be started because
107    /// one is already running.
108    fn start(
109        &mut self,
110        stream: MediaStream,
111        offload: Option<AudioOffloadExtProxy>,
112    ) -> Result<Box<dyn MediaTask>, MediaTaskError>;
113
114    /// Try to reconfigure the MediaTask to accept a new configuration.  This differs from
115    /// `MediaTaskBuilder::configure` as it attempts to preserve the same configured session.
116    /// The runner remains configured with the initial configuration on an error.
117    fn reconfigure(&mut self, _config: &MediaCodecConfig) -> Result<(), MediaTaskError> {
118        Err(MediaTaskError::NotSupported)
119    }
120
121    /// Set the delay reported from the peer for this media task.
122    /// This should configure the media source or sink to attempt to compensate.
123    /// Typically this is zero for Sink tasks, but Source tasks can receive this info from the peer.
124    /// May only be supported before start.
125    /// If an Error is returned, the delay has not been set.
126    fn set_delay(&mut self, _delay: Duration) -> Result<(), MediaTaskError> {
127        Err(MediaTaskError::NotSupported)
128    }
129
130    /// Watch for active channel state changes on the media source.
131    /// Resolves to true when active, false when inactive.
132    /// Only transitions are reported: the returned future does not resolve if the state has not
133    /// changed since it was last reported, and may never resolve.  The end of the media task is
134    /// not reported here, `MediaTask::finished` reports that instead.
135    /// Default implementation is Ready(true) for tasks that are always active.
136    fn watch_active(&mut self) -> BoxFuture<'static, bool> {
137        futures::future::ready(true).boxed()
138    }
139
140    /// Add information from the running media task to the inspect tree
141    /// (i.e. data transferred, jitter, etc)
142    fn iattach(&mut self, _parent: &Node, _name: &str) -> Result<(), AttachError> {
143        Err("attach not implemented".into())
144    }
145}
146
147/// MediaTasks represent a media stream being actively processed (sent or received from a peer).
148/// They are are created by `MediaTaskRunner::start`.
149/// Typically a MediaTask will run a background task that is active until dropped or
150/// `MediaTask::stop` is called.
151pub trait MediaTask: Send {
152    /// Returns a Future that finishes when the running media task finishes for any reason.
153    /// Should return a future that immediately resolves if this task is finished.
154    fn finished(&mut self) -> BoxFuture<'static, Result<MediaTaskStatus, MediaTaskError>>;
155
156    /// Returns the result if this task has finished, and None otherwise
157    fn result(&mut self) -> Option<Result<MediaTaskStatus, MediaTaskError>> {
158        self.finished().now_or_never()
159    }
160
161    /// Stops the task normally, signalling to all waiters Ok(MediaTaskStatus::Stopped).
162    /// Returns the result sent to MediaTask::finished futures, which may be different from Ok(MediaTaskStatus::Stopped).
163    /// When this function returns, it is good practice to ensure the MediaStream that started
164    /// this task is also dropped.
165    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        /// The PeerId that was used to make this Task
182        pub peer_id: PeerId,
183        /// The configuration used to make this task
184        pub codec_config: MediaCodecConfig,
185        /// If still started, this holds the MediaStream.
186        pub stream: Arc<Mutex<Option<MediaStream>>>,
187        /// Sender for the shared result future. None if already sent.
188        sender: Arc<Mutex<Option<oneshot::Sender<Result<MediaTaskStatus, MediaTaskError>>>>>,
189        /// Shared result future.
190        result: Shared<BoxFuture<'static, Result<MediaTaskStatus, MediaTaskError>>>,
191        /// Delay the task was started with.
192        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        /// Return true if the background media task is running.
228        pub fn is_started(&self) -> bool {
229            // The stream being held represents the task running.
230            self.stream.lock().is_some()
231        }
232
233        /// End the streaming task without an external stop().
234        /// Sends an optional result from the task.
235        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            // Result should be available.
263            self.finished().now_or_never().unwrap()
264        }
265    }
266
267    pub struct TestMediaTaskRunner {
268        /// The peer_id this was started with.
269        pub peer_id: PeerId,
270        /// The config that this runner will start tasks for
271        pub codec_config: MediaCodecConfig,
272        /// If this is reconfigurable
273        pub reconfigurable: bool,
274        /// If this supports delay reporting
275        pub supports_set_delay: bool,
276        /// What the delay is right now
277        pub set_delay: Option<std::time::Duration>,
278        /// The Sender that will send a clone of the started tasks to the builder.
279        pub sender: mpsc::Sender<TestMediaTask>,
280        /// Receiver for active state changes.
281        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            // Don't particularly care if the receiver got dropped.
297            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                // No one can change the active state anymore, so it will never change again.
326                // Real implementations never signal termination here, they just stop changing.
327                Poll::Ready(None) => Poll::Pending,
328                Poll::Pending => Poll::Pending,
329            })
330            .boxed()
331        }
332    }
333
334    /// A TestMediaTask expects to be configured once, and then started and stopped as appropriate.
335    /// It will Error if started again while started or stopped while stopped, or if it was
336    /// configured multiple times.
337    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        /// Returns a type that implements MediaTaskBuilder.  When a MediaTask is built using
403        /// configure(), it will be available from `next_task`.
404        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        /// Gets a future that will return a handle to the next TestMediaTask that gets started
416        /// from a Runner that was retrieved from this builder.
417        /// The TestMediaTask, can tell you when it's started and give you a handle to the MediaStream.
418        pub fn next_task(&mut self) -> impl Future<Output = Option<TestMediaTask>> + '_ {
419            self.receiver.next()
420        }
421
422        /// Expects that a task had been built, and retrieves that task, or panics.
423        #[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}