bt_a2dp/
codec.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 anyhow::format_err;
6use bt_avdtp::{self as avdtp, MediaCodecType, ServiceCapability, StreamEndpointId};
7use fidl_fuchsia_media as media;
8use log::{trace, warn};
9
10use crate::media_types::{
11    AacChannels, AacCodecInfo, AacObjectType, AacSamplingFrequency, SbcAllocation, SbcBlockCount,
12    SbcChannelMode, SbcCodecInfo, SbcSamplingFrequency, SbcSubBands,
13};
14use crate::rtp::{AacRtpPacketBuilder, RtpPacketBuilder, SbcRtpPacketBuilder};
15
16/// Stores the media codec configuration for an A2DP stream, and provides utility for integration
17/// with Fuchsia media.
18#[derive(Clone, Debug, PartialEq)]
19pub struct MediaCodecConfig {
20    codec_type: avdtp::MediaCodecType,
21    codec_extra: Vec<u8>,
22}
23
24/// The number of frames in each SBC packet when encoding.
25/// 5 is chosen by default as it represents a low amount of latency and fits within the default
26/// L2CAP MTU.
27/// RTP Header (12 bytes) + 1 byte (SBC header) + 5 * SBC Frame (119 bytes) = 608 bytes < 672
28/// TODO(40986, 41449): Update this based on the input format and the codec settings.
29const ENCODED_FRAMES_PER_SBC_PACKET: u8 = 5;
30const PCM_FRAMES_PER_SBC_FRAME: u32 = 640;
31
32/// The number of frames in each AAC packet when encoding.
33/// Only one encoded AudioMuxElement is sent per RTP frame. 1024 is the most our current
34/// AAC encoder will put in a single AudioMuxElement.
35const ENCODED_FRAMES_PER_AAC_PACKET: u8 = 1;
36const PCM_FRAMES_PER_AAC_FRAME: u32 = 1024;
37
38impl MediaCodecConfig {
39    /// Try to build a codec config given a codec type and the codec specific information elements
40    /// for the codec specified in `extra`.  Fails with OutOfRange if the codec is not supported.
41    pub fn build(codec_type: MediaCodecType, extra: &[u8]) -> avdtp::Result<Self> {
42        match codec_type {
43            MediaCodecType::AUDIO_SBC => {
44                let _ = SbcCodecInfo::try_from(extra)?;
45            }
46            MediaCodecType::AUDIO_AAC => {
47                let _ = AacCodecInfo::try_from(extra)?;
48            }
49            _ => return Err(avdtp::Error::OutOfRange),
50        };
51        Ok(Self { codec_type, codec_extra: extra.to_vec() })
52    }
53
54    /// Build an SBC configuration with minimum defaults for configuration
55    /// (48000 Hz Mono, 16 Blocks, 8 SubBands, Loudness Allocation, 2-29 bitpool)
56    /// This is the minimunm configuration requried by both Sink and Source as defined in the
57    /// A2DP Specificaiton 1.2 Section 4.3.2.
58    pub fn min_sbc() -> Self {
59        let codec_info = SbcCodecInfo::new(
60            SbcSamplingFrequency::FREQ48000HZ,
61            SbcChannelMode::MONO,
62            SbcBlockCount::SIXTEEN,
63            SbcSubBands::EIGHT,
64            SbcAllocation::LOUDNESS,
65            2,
66            29,
67        )
68        .expect("Minimum Codec Info should build");
69        Self::build(MediaCodecType::AUDIO_SBC, &codec_info.to_bytes()).unwrap()
70    }
71
72    /// Mandatory sink configuration for AAC
73    /// As defined by the A2DP Specification Sectrion 4.5
74    pub fn min_aac_sink() -> Self {
75        let codec_info = AacCodecInfo::new(
76            AacObjectType::MANDATORY_SNK,
77            AacSamplingFrequency::MANDATORY_SNK,
78            AacChannels::MANDATORY_SNK,
79            true,
80            0,
81        )
82        .expect("Min Codec Info should build");
83        Self::build(MediaCodecType::AUDIO_AAC, &codec_info.to_bytes()).unwrap()
84    }
85
86    pub fn codec_type(&self) -> &MediaCodecType {
87        &self.codec_type
88    }
89
90    pub fn codec_extra(&self) -> &[u8] {
91        self.codec_extra.as_slice()
92    }
93
94    pub fn make_packet_builder(
95        &self,
96        max_packet_size: usize,
97    ) -> avdtp::Result<Box<dyn RtpPacketBuilder>> {
98        match self.codec_type {
99            MediaCodecType::AUDIO_AAC => {
100                let builder = AacRtpPacketBuilder::new(max_packet_size);
101                return Ok(Box::new(builder));
102            }
103            MediaCodecType::AUDIO_SBC => {
104                let builder = SbcRtpPacketBuilder::new(max_packet_size);
105                return Ok(Box::new(builder));
106            }
107            _ => unreachable!(),
108        }
109    }
110
111    /// Returns true if the given MediaCodecConfig is a compatible subset of this configuration.
112    pub fn supports(&self, other: &MediaCodecConfig) -> bool {
113        if &self.codec_type != other.codec_type() {
114            return false;
115        }
116        match self.codec_type {
117            MediaCodecType::AUDIO_SBC => {
118                let codec_info = SbcCodecInfo::try_from(self.codec_extra()).expect("should parse");
119                let other_info = SbcCodecInfo::try_from(other.codec_extra()).expect("should parse");
120                codec_info.supports(&other_info)
121            }
122            MediaCodecType::AUDIO_AAC => {
123                let codec_info = AacCodecInfo::try_from(self.codec_extra()).expect("should parse");
124                let other_info = AacCodecInfo::try_from(other.codec_extra()).expect("should parse");
125                codec_info.supports(&other_info)
126            }
127            _ => false,
128        }
129    }
130
131    /// Negotiate the best supported configuration, given another configuration.
132    /// This can be seen as a kind of intersection of the capabilities of the two configs.
133    /// IF this returns Some(result), then a.supports(result) and b.supports(result) will both be
134    /// true.
135    pub fn negotiate(a: &MediaCodecConfig, b: &MediaCodecConfig) -> Option<MediaCodecConfig> {
136        if a.codec_type != b.codec_type {
137            return None;
138        }
139        match a.codec_type {
140            MediaCodecType::AUDIO_AAC => {
141                let a = AacCodecInfo::try_from(a.codec_extra()).expect("should parse");
142                let b = AacCodecInfo::try_from(b.codec_extra()).expect("should parse");
143                AacCodecInfo::negotiate(&a, &b).map(|matched| MediaCodecConfig {
144                    codec_type: MediaCodecType::AUDIO_AAC,
145                    codec_extra: matched.to_bytes().to_vec(),
146                })
147            }
148            MediaCodecType::AUDIO_SBC => {
149                let a = SbcCodecInfo::try_from(a.codec_extra()).expect("should parse");
150                let b = SbcCodecInfo::try_from(b.codec_extra()).expect("should parse");
151                SbcCodecInfo::negotiate(&a, &b).map(|matched| MediaCodecConfig {
152                    codec_type: MediaCodecType::AUDIO_SBC,
153                    codec_extra: matched.to_bytes().to_vec(),
154                })
155            }
156            _ => unreachable!(),
157        }
158    }
159
160    /// Retrieves a set of EncoderSettings that is suitable to configure a StreamProcessor to encode
161    /// to the target configuration for this MediaCodecConfig.
162    /// Returns Err(OutOfRange) if this does not specify a single configuration.
163    pub fn encoder_settings(&self) -> avdtp::Result<fidl_fuchsia_media::EncoderSettings> {
164        let encoder_settings = match self.codec_type {
165            MediaCodecType::AUDIO_SBC => {
166                let codec_info = SbcCodecInfo::try_from(self.codec_extra())?;
167
168                let sub_bands = match codec_info.sub_bands() {
169                    SbcSubBands::FOUR => media::SbcSubBands::SubBands4,
170                    SbcSubBands::EIGHT => media::SbcSubBands::SubBands8,
171                    _ => return Err(avdtp::Error::OutOfRange),
172                };
173
174                let allocation = match codec_info.allocation_method() {
175                    SbcAllocation::SNR => media::SbcAllocation::AllocSnr,
176                    SbcAllocation::LOUDNESS => media::SbcAllocation::AllocLoudness,
177                    _ => return Err(avdtp::Error::OutOfRange),
178                };
179
180                let block_count = match codec_info.block_count() {
181                    SbcBlockCount::FOUR => media::SbcBlockCount::BlockCount4,
182                    SbcBlockCount::EIGHT => media::SbcBlockCount::BlockCount8,
183                    SbcBlockCount::TWELVE => media::SbcBlockCount::BlockCount12,
184                    SbcBlockCount::SIXTEEN => media::SbcBlockCount::BlockCount16,
185                    _ => return Err(avdtp::Error::OutOfRange),
186                };
187
188                let channel_mode = match codec_info.channel_mode() {
189                    SbcChannelMode::MONO => media::SbcChannelMode::Mono,
190                    SbcChannelMode::DUAL_CHANNEL => media::SbcChannelMode::Dual,
191                    SbcChannelMode::STEREO => media::SbcChannelMode::Stereo,
192                    SbcChannelMode::JOINT_STEREO => media::SbcChannelMode::JointStereo,
193                    _ => return Err(avdtp::Error::OutOfRange),
194                };
195
196                media::EncoderSettings::Sbc(media::SbcEncoderSettings {
197                    sub_bands,
198                    allocation,
199                    block_count,
200                    channel_mode,
201                    bit_pool: codec_info.max_bitpool() as u64,
202                })
203            }
204            MediaCodecType::AUDIO_AAC => {
205                let codec_info = AacCodecInfo::try_from(self.codec_extra())?;
206                let bit_rate = if codec_info.variable_bit_rate() {
207                    media::AacBitRate::Variable(media::AacVariableBitRate::V3)
208                } else {
209                    media::AacBitRate::Constant(media::AacConstantBitRate {
210                        bit_rate: codec_info.bitrate(),
211                    })
212                };
213
214                let channel_mode = match codec_info.channels() {
215                    AacChannels::ONE => media::AacChannelMode::Mono,
216                    AacChannels::TWO => media::AacChannelMode::Stereo,
217                    x => return Err(format_err!("unsuported number of channels: {:?}", x).into()),
218                };
219
220                media::EncoderSettings::Aac(media::AacEncoderSettings {
221                    transport: media::AacTransport::Latm(media::AacTransportLatm {
222                        mux_config_present: true,
223                    }),
224                    channel_mode,
225                    bit_rate,
226                    aot: media::AacAudioObjectType::Mpeg2AacLc,
227                })
228            }
229            _ => return Err(format_err!("Unsupported codec {:?}", self.codec_type).into()),
230        };
231        Ok(encoder_settings)
232    }
233
234    /// Construct a ServiceCapability that represents this codec config.
235    pub fn capability(&self) -> ServiceCapability {
236        match self.codec_type {
237            MediaCodecType::AUDIO_SBC => SbcCodecInfo::try_from(self.codec_extra()).unwrap().into(),
238            MediaCodecType::AUDIO_AAC => AacCodecInfo::try_from(self.codec_extra()).unwrap().into(),
239            _ => unreachable!(),
240        }
241    }
242
243    /// The number of channels that is selected in the configuration.  Returns OutOfRange if
244    /// the configuration supports a range of channel counts.
245    pub fn channel_count(&self) -> avdtp::Result<usize> {
246        match self.codec_type {
247            MediaCodecType::AUDIO_SBC => {
248                SbcCodecInfo::try_from(self.codec_extra())?.channel_count()
249            }
250            MediaCodecType::AUDIO_AAC => {
251                AacCodecInfo::try_from(self.codec_extra())?.channel_count()
252            }
253            _ => unreachable!(),
254        }
255    }
256
257    /// The number of frames that should be included when building a packet to send to a peer.
258    pub fn frames_per_packet(&self) -> usize {
259        match self.codec_type {
260            MediaCodecType::AUDIO_SBC => ENCODED_FRAMES_PER_SBC_PACKET as usize,
261            MediaCodecType::AUDIO_AAC => ENCODED_FRAMES_PER_AAC_PACKET as usize,
262            _ => unreachable!(),
263        }
264    }
265
266    pub fn pcm_frames_per_encoded_frame(&self) -> usize {
267        match self.codec_type {
268            MediaCodecType::AUDIO_SBC => PCM_FRAMES_PER_SBC_FRAME as usize,
269            MediaCodecType::AUDIO_AAC => PCM_FRAMES_PER_AAC_FRAME as usize,
270            _ => unreachable!(),
271        }
272    }
273
274    pub fn rtp_frame_header(&self) -> &[u8] {
275        match self.codec_type {
276            MediaCodecType::AUDIO_SBC => &[ENCODED_FRAMES_PER_SBC_PACKET as u8],
277            MediaCodecType::AUDIO_AAC => &[],
278            _ => unreachable!(),
279        }
280    }
281
282    /// Return the sampling freqeuncy selected by this configuration, or return OutOfRange if
283    /// more than one is selected.
284    pub fn sampling_frequency(&self) -> avdtp::Result<u32> {
285        let freq = match self.codec_type {
286            MediaCodecType::AUDIO_SBC => {
287                SbcCodecInfo::try_from(self.codec_extra())?.sampling_frequency()?
288            }
289            MediaCodecType::AUDIO_AAC => {
290                AacCodecInfo::try_from(self.codec_extra())?.sampling_frequency()?
291            }
292            _ => unreachable!(),
293        };
294        Ok(freq)
295    }
296
297    pub fn stream_encoding(&self) -> &'static str {
298        match self.codec_type {
299            MediaCodecType::AUDIO_SBC => media::AUDIO_ENCODING_SBC,
300            MediaCodecType::AUDIO_AAC => media::AUDIO_ENCODING_AAC,
301            _ => unreachable!(),
302        }
303    }
304
305    pub fn mime_type(&self) -> &'static str {
306        match self.codec_type {
307            MediaCodecType::AUDIO_SBC => "audio/sbc",
308            MediaCodecType::AUDIO_AAC => "audio/aac",
309            _ => unreachable!(),
310        }
311    }
312}
313
314impl From<&MediaCodecConfig> for ServiceCapability {
315    fn from(config: &MediaCodecConfig) -> Self {
316        config.capability()
317    }
318}
319
320impl TryFrom<&ServiceCapability> for MediaCodecConfig {
321    type Error = avdtp::Error;
322
323    fn try_from(value: &ServiceCapability) -> Result<Self, Self::Error> {
324        match value {
325            ServiceCapability::MediaCodec {
326                media_type: avdtp::MediaType::Audio,
327                codec_type,
328                codec_extra,
329            } => {
330                match codec_type {
331                    &MediaCodecType::AUDIO_SBC => {
332                        let _ = SbcCodecInfo::try_from(codec_extra.as_slice())?;
333                    }
334                    &MediaCodecType::AUDIO_AAC => {
335                        let _ = AacCodecInfo::try_from(codec_extra.as_slice())?;
336                    }
337                    _ => return Err(avdtp::Error::OutOfRange),
338                };
339                Ok(MediaCodecConfig {
340                    codec_type: codec_type.clone(),
341                    codec_extra: codec_extra.clone(),
342                })
343            }
344            _ => Err(avdtp::Error::OutOfRange),
345        }
346    }
347}
348
349/// Selects a codec and a set of capabilities for that codec, based on a preferential list of
350/// partially-defined codec capabilities, using each codec's support to find best compatable
351/// matching capability.
352/// Currently supports SBC and AAC codec capabilities.
353#[derive(Debug, Clone)]
354pub struct CodecNegotiation {
355    preferred_codecs: Vec<MediaCodecConfig>,
356    preferred_direction: avdtp::EndpointType,
357}
358
359impl CodecNegotiation {
360    /// Make a new codec negotiation set using `codecs` as an ordered list "ideal" capabilities.
361    /// Capabilities earlier in the list are preferred if compatible when selecting.
362    /// When selecting endpoints, ones of `direction` are chosen over ones other directions.
363    /// Returns an error if any of the capabilities provided can't be negotiated, or aren't codecs.
364    pub fn build(
365        codecs: Vec<ServiceCapability>,
366        direction: avdtp::EndpointType,
367    ) -> avdtp::Result<Self> {
368        let expected = codecs.len();
369        let preferred_codecs: Vec<_> =
370            codecs.iter().filter_map(|c| MediaCodecConfig::try_from(c).ok()).collect();
371        if preferred_codecs.len() != expected {
372            return Err(format_err!("Unsupported capability used in CodecNegotiation").into());
373        }
374        Ok(Self { preferred_codecs, preferred_direction: direction })
375    }
376
377    /// Given a set of endpoints, return the endpoint id, and the ServiceCapabilities we should
378    /// request when configuring that endpoint for streaming, based on our preferences for codecs
379    /// and delay reporting.
380    /// Returns None if none of the endpoints can be supported by the supported codecs.
381    pub fn select(
382        &self,
383        endpoints: &[avdtp::StreamEndpoint],
384    ) -> Option<(Vec<ServiceCapability>, StreamEndpointId)> {
385        let (codec_cap, id) = self.select_codec(endpoints)?;
386        let caps = endpoints.iter().find(|x| x.local_id() == &id).map(|x| {
387            if x.capabilities().contains(&ServiceCapability::DelayReporting) {
388                vec![
389                    ServiceCapability::MediaTransport,
390                    ServiceCapability::DelayReporting,
391                    codec_cap,
392                ]
393            } else {
394                vec![ServiceCapability::MediaTransport, codec_cap]
395            }
396        });
397        if caps.is_none() {
398            warn!(id:%; "Couldn't find endpoint after codec negotiation!");
399            return None;
400        }
401        Some((caps.unwrap(), id))
402    }
403
404    /// Given a set of endpoints, return the endpoint id, and a ServiceCapability representing the
405    /// selected compatible codec parameters for that endpoint, based on our preferences.
406    /// Returns None if none of the endpoints can be supported by the preferred codecs.
407    pub fn select_codec(
408        &self,
409        endpoints: &[avdtp::StreamEndpoint],
410    ) -> Option<(ServiceCapability, StreamEndpointId)> {
411        let (preferred_dir, others): (Vec<_>, Vec<_>) =
412            endpoints.iter().partition(|e| e.endpoint_type() == &self.preferred_direction);
413        let codecs_with_ids: Vec<_> = preferred_dir
414            .iter()
415            .chain(others.iter())
416            .filter_map(|e| Self::get_codec_cap(e).map(|cap| (cap, e.local_id())))
417            .collect();
418        for preferred in &self.preferred_codecs {
419            for (codec, id) in &codecs_with_ids {
420                if let Ok(config) = MediaCodecConfig::try_from(*codec) {
421                    if let Some(negotiated) = MediaCodecConfig::negotiate(&config, &preferred) {
422                        trace!("Codec negotiation selected: {:?}", negotiated);
423                        return Some((negotiated.capability(), (*id).clone()));
424                    }
425                }
426            }
427        }
428        None
429    }
430
431    /// Change the preferred direction.  Calls to `select` after this call will prefer to match
432    /// endpoints that are of `direction`
433    pub fn set_direction(&mut self, direction: avdtp::EndpointType) {
434        self.preferred_direction = direction;
435    }
436
437    /// Returns the currently preferred direction
438    pub fn direction(&self) -> avdtp::EndpointType {
439        self.preferred_direction
440    }
441
442    fn get_codec_cap<'a>(stream: &'a avdtp::StreamEndpoint) -> Option<&'a ServiceCapability> {
443        stream
444            .capabilities()
445            .iter()
446            .find(|cap| cap.category() == avdtp::ServiceCategory::MediaCodec)
447    }
448}
449
450#[cfg(test)]
451mod tests {
452    use super::*;
453
454    use bt_avdtp::{MediaType, StreamEndpoint};
455
456    use crate::media_types::*;
457
458    const TEST_SAMPLE_FREQ: u32 = 44100;
459
460    fn test_codec_cap(codec_type: MediaCodecType) -> ServiceCapability {
461        let codec_extra = match codec_type {
462            MediaCodecType::AUDIO_SBC => vec![41, 245, 2, 53],
463            MediaCodecType::AUDIO_AAC => vec![128, 1, 4, 4, 226, 0],
464            _ => vec![],
465        };
466        ServiceCapability::MediaCodec { media_type: MediaType::Audio, codec_type, codec_extra }
467    }
468
469    fn test_endp_caps(
470        codec_type: MediaCodecType,
471        additional: Vec<ServiceCapability>,
472    ) -> Vec<ServiceCapability> {
473        [ServiceCapability::MediaTransport, test_codec_cap(codec_type)]
474            .into_iter()
475            .chain(additional.into_iter())
476            .collect()
477    }
478
479    fn change_extra(mut cap: &mut ServiceCapability, extra: Vec<u8>) {
480        if let ServiceCapability::MediaCodec { codec_extra, .. } = &mut cap {
481            *codec_extra = extra;
482            return;
483        }
484        panic!("Can't change extra for a non-MediaCodec cap: {:?}", cap);
485    }
486
487    fn build_test_config(codec_type: MediaCodecType) -> MediaCodecConfig {
488        MediaCodecConfig::try_from(&test_codec_cap(codec_type)).expect("builds okay")
489    }
490
491    #[test]
492    fn test_basic() {
493        let res =
494            MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_SBC)).expect("builds");
495        assert_eq!(Some(44100), res.sampling_frequency().ok());
496        assert_eq!(&[5], res.rtp_frame_header());
497        assert_eq!(media::AUDIO_ENCODING_SBC, res.stream_encoding());
498
499        let res =
500            MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_AAC)).expect("builds");
501        assert_eq!(Some(44100), res.sampling_frequency().ok());
502        assert_eq!(0, res.rtp_frame_header().len());
503        assert_eq!(media::AUDIO_ENCODING_AAC, res.stream_encoding());
504    }
505
506    #[test]
507    fn test_from_capability() {
508        // Wrong length extra.
509        let mut cap = test_codec_cap(MediaCodecType::AUDIO_SBC);
510        assert!(MediaCodecConfig::try_from(&cap).is_ok());
511        change_extra(&mut cap, vec![]);
512        assert!(MediaCodecConfig::try_from(&cap).is_err());
513        change_extra(&mut cap, vec![0; 6]);
514        assert!(MediaCodecConfig::try_from(&cap).is_err());
515
516        let mut cap = test_codec_cap(MediaCodecType::AUDIO_AAC);
517        assert!(MediaCodecConfig::try_from(&cap).is_ok());
518        change_extra(&mut cap, vec![]);
519        assert!(MediaCodecConfig::try_from(&cap).is_err());
520        change_extra(&mut cap, vec![0; 4]);
521        assert!(MediaCodecConfig::try_from(&cap).is_err());
522
523        // Unknown codec is error.
524        let cap = avdtp::ServiceCapability::MediaCodec {
525            media_type: MediaType::Audio,
526            codec_type: MediaCodecType::AUDIO_NON_A2DP,
527            codec_extra: vec![],
528        };
529        assert!(MediaCodecConfig::try_from(&cap).is_err());
530    }
531
532    #[test]
533    fn test_sampling_frequency() {
534        let freq = build_test_config(MediaCodecType::AUDIO_SBC)
535            .sampling_frequency()
536            .expect("SBC frequency should be known and singular");
537        assert_eq!(TEST_SAMPLE_FREQ, freq);
538        let freq = build_test_config(MediaCodecType::AUDIO_AAC)
539            .sampling_frequency()
540            .expect("SBC frequency should be known and singular");
541        assert_eq!(TEST_SAMPLE_FREQ, freq);
542
543        let multi_freq_info = SbcCodecInfo::new(
544            SbcSamplingFrequency::MANDATORY_SNK, // SNK requires two frequencies, which is not singular.
545            SbcChannelMode::MANDATORY_SNK,
546            SbcBlockCount::MANDATORY_SNK,
547            SbcSubBands::MANDATORY_SNK,
548            SbcAllocation::MANDATORY_SNK,
549            2,
550            250,
551        )
552        .expect("codecinfo");
553        let multi_freq_config =
554            MediaCodecConfig::build(MediaCodecType::AUDIO_SBC, &multi_freq_info.to_bytes())
555                .expect("MediaCodecConfig should build");
556
557        assert!(multi_freq_config.sampling_frequency().is_err());
558    }
559
560    #[test]
561    fn test_supports() {
562        // Codecs must match.
563        let sbc = build_test_config(MediaCodecType::AUDIO_SBC);
564        let aac = build_test_config(MediaCodecType::AUDIO_AAC);
565
566        assert!(!sbc.supports(&aac));
567        assert!(!aac.supports(&sbc));
568        assert!(sbc.supports(&sbc));
569    }
570
571    /// Build an endpoint with the specified type, and local id.
572    fn test_codec_endpoint(
573        id: u8,
574        capabilities: Vec<ServiceCapability>,
575        direction: avdtp::EndpointType,
576    ) -> StreamEndpoint {
577        avdtp::StreamEndpoint::new(id, avdtp::MediaType::Audio, direction, capabilities)
578            .expect("media endpoint")
579    }
580
581    #[test]
582    fn test_codec_negotiation() {
583        // It should choose nothing if there aren't any local priorities (there aren't any streams)
584        // or if there is no endpoint to choose from.
585        let empty_negotiation =
586            CodecNegotiation::build(vec![], avdtp::EndpointType::Sink).expect("builds okay");
587
588        let sbc_seid = 1u8;
589        let aac_seid = 2u8;
590
591        let remote_endpoints = vec![
592            test_codec_endpoint(
593                aac_seid,
594                test_endp_caps(MediaCodecType::AUDIO_AAC, vec![]),
595                avdtp::EndpointType::Sink,
596            ),
597            test_codec_endpoint(
598                sbc_seid,
599                test_endp_caps(MediaCodecType::AUDIO_SBC, vec![]),
600                avdtp::EndpointType::Sink,
601            ),
602        ];
603
604        assert!(empty_negotiation.select(&remote_endpoints).is_none());
605
606        let priority_order = vec![
607            test_codec_cap(MediaCodecType::AUDIO_AAC),
608            test_codec_cap(MediaCodecType::AUDIO_SBC),
609        ];
610        let negotiation =
611            CodecNegotiation::build(priority_order, avdtp::EndpointType::Sink).expect("builds");
612
613        assert!(negotiation.select(&Vec::new()).is_none());
614
615        // Should choose the highest-priority capability that matches, regardless of order.
616
617        let aac_config = MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_AAC))
618            .expect("codec_config");
619        let aac_negotiated =
620            MediaCodecConfig::negotiate(&aac_config, &aac_config).expect("negotiated config");
621
622        let sbc_config = MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_SBC))
623            .expect("codec_config");
624        let sbc_negotiated =
625            MediaCodecConfig::negotiate(&sbc_config, &sbc_config).expect("negotiated config");
626
627        assert_eq!(
628            negotiation.select(&remote_endpoints),
629            Some((
630                vec![ServiceCapability::MediaTransport, aac_negotiated.capability()],
631                aac_seid.try_into().unwrap()
632            ))
633        );
634
635        let mut reversed_endpoints: Vec<_> = remote_endpoints.iter().map(|e| e.as_new()).collect();
636        reversed_endpoints.reverse();
637
638        assert_eq!(
639            negotiation.select(&reversed_endpoints),
640            Some((
641                vec![ServiceCapability::MediaTransport, aac_negotiated.capability()],
642                aac_seid.try_into().unwrap()
643            ))
644        );
645
646        // Should skip an endpoint if it can't match up to one it supports,
647        // even if it's higher priority.
648
649        // An AAC endpoint incompatable with the test codec caps.
650        let incompatible_aac_endpoint = test_codec_endpoint(
651            aac_seid,
652            vec![
653                AacCodecInfo::new(
654                    AacObjectType::MPEG4_AAC_SCALABLE,
655                    AacSamplingFrequency::FREQ96000HZ,
656                    AacChannels::ONE,
657                    true,
658                    0,
659                )
660                .expect("aac codec builds")
661                .into(),
662                ServiceCapability::MediaTransport,
663            ],
664            avdtp::EndpointType::Sink,
665        );
666        let incompatible_aac_endpoints =
667            vec![incompatible_aac_endpoint, remote_endpoints[1].as_new()];
668
669        assert_eq!(
670            negotiation.select(&incompatible_aac_endpoints),
671            Some((
672                vec![ServiceCapability::MediaTransport, sbc_negotiated.capability()],
673                sbc_seid.try_into().unwrap()
674            ))
675        );
676    }
677
678    #[test]
679    fn test_codec_negotiation_none_match() {
680        let priority_order = vec![test_codec_cap(MediaCodecType::AUDIO_SBC)];
681        let negotiation =
682            CodecNegotiation::build(priority_order, avdtp::EndpointType::Sink).expect("builds");
683
684        let aac_seid = 2u8;
685        // When none of the remote endpoints match, it should refuse to choose.
686        let remote_endpoints = vec![test_codec_endpoint(
687            aac_seid,
688            test_endp_caps(MediaCodecType::AUDIO_AAC, vec![]),
689            avdtp::EndpointType::Sink,
690        )];
691
692        assert_eq!(negotiation.select(&remote_endpoints), None);
693    }
694
695    #[test]
696    fn test_codec_negotiation_prefers_direction() {
697        let priority_order = vec![
698            test_codec_cap(MediaCodecType::AUDIO_AAC),
699            test_codec_cap(MediaCodecType::AUDIO_SBC),
700        ];
701        let mut negotiation =
702            CodecNegotiation::build(priority_order, avdtp::EndpointType::Sink).expect("builds");
703
704        assert!(negotiation.select(&Vec::new()).is_none());
705
706        let sbc_sink_seid = 1u8;
707        let aac_sink_seid = 2u8;
708        let sbc_source_seid = 3u8;
709        let aac_source_seid = 4u8;
710        // Should pick Sink endpoints over Source Endpoints, even if they are later and do match.
711        let remote_endpoints = vec![
712            test_codec_endpoint(
713                aac_source_seid,
714                test_endp_caps(MediaCodecType::AUDIO_AAC, vec![]),
715                avdtp::EndpointType::Source,
716            ),
717            test_codec_endpoint(
718                sbc_source_seid,
719                test_endp_caps(MediaCodecType::AUDIO_SBC, vec![]),
720                avdtp::EndpointType::Source,
721            ),
722            test_codec_endpoint(
723                aac_sink_seid,
724                test_endp_caps(MediaCodecType::AUDIO_AAC, vec![]),
725                avdtp::EndpointType::Sink,
726            ),
727            test_codec_endpoint(
728                sbc_sink_seid,
729                test_endp_caps(MediaCodecType::AUDIO_SBC, vec![]),
730                avdtp::EndpointType::Sink,
731            ),
732        ];
733
734        let aac_config = MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_AAC))
735            .expect("codec_config");
736        let aac_negotiated =
737            MediaCodecConfig::negotiate(&aac_config, &aac_config).expect("negotiated config");
738        let expected_capabilities =
739            vec![ServiceCapability::MediaTransport, aac_negotiated.capability()];
740
741        assert_eq!(
742            negotiation.select(&remote_endpoints),
743            Some((expected_capabilities.clone(), aac_sink_seid.try_into().unwrap()))
744        );
745
746        // Order of the endpoints shouldn't matter.
747        let mut reversed_endpoints: Vec<_> = remote_endpoints.iter().map(|e| e.as_new()).collect();
748        reversed_endpoints.reverse();
749
750        assert_eq!(
751            negotiation.select(&remote_endpoints),
752            Some((expected_capabilities.clone(), aac_sink_seid.try_into().unwrap()))
753        );
754
755        // Direction is a preference, so if there aren't any that match the preferred direction,
756        // it will pick one that is supported and the other direction.
757        let oops_all_sources = vec![
758            test_codec_endpoint(
759                aac_source_seid,
760                test_endp_caps(MediaCodecType::AUDIO_AAC, vec![]),
761                avdtp::EndpointType::Source,
762            ),
763            test_codec_endpoint(
764                sbc_source_seid,
765                test_endp_caps(MediaCodecType::AUDIO_SBC, vec![]),
766                avdtp::EndpointType::Source,
767            ),
768        ];
769        assert_eq!(
770            negotiation.select(&oops_all_sources),
771            Some((expected_capabilities.clone(), aac_source_seid.try_into().unwrap()))
772        );
773
774        // Changing the preferred direction means the new direction is preferred.
775        negotiation.set_direction(avdtp::EndpointType::Source);
776
777        assert_eq!(
778            negotiation.select(&reversed_endpoints),
779            Some((expected_capabilities, aac_source_seid.try_into().unwrap()))
780        );
781    }
782
783    #[test]
784    fn test_codec_negotiation_adds_delayreporing_when_supported() {
785        let priority_order = vec![
786            test_codec_cap(MediaCodecType::AUDIO_AAC),
787            test_codec_cap(MediaCodecType::AUDIO_SBC),
788        ];
789        let negotiation =
790            CodecNegotiation::build(priority_order, avdtp::EndpointType::Sink).expect("builds");
791
792        assert!(negotiation.select(&Vec::new()).is_none());
793
794        let sbc_sink_seid = 1u8;
795        let remote_endpoints = vec![test_codec_endpoint(
796            sbc_sink_seid,
797            test_endp_caps(MediaCodecType::AUDIO_SBC, vec![ServiceCapability::DelayReporting]),
798            avdtp::EndpointType::Sink,
799        )];
800
801        let sbc_config = MediaCodecConfig::try_from(&test_codec_cap(MediaCodecType::AUDIO_SBC))
802            .expect("codec_config");
803        let sbc_negotiated =
804            MediaCodecConfig::negotiate(&sbc_config, &sbc_config).expect("negotiated config");
805        let expected_capabilities = vec![
806            ServiceCapability::MediaTransport,
807            ServiceCapability::DelayReporting,
808            sbc_negotiated.capability(),
809        ];
810
811        assert_eq!(
812            negotiation.select(&remote_endpoints),
813            Some((expected_capabilities.clone(), sbc_sink_seid.try_into().unwrap()))
814        );
815
816        let remote_endpoints_no_delay = vec![test_codec_endpoint(
817            sbc_sink_seid,
818            test_endp_caps(MediaCodecType::AUDIO_SBC, vec![]),
819            avdtp::EndpointType::Sink,
820        )];
821
822        let expected_capabilities_no_delay =
823            vec![ServiceCapability::MediaTransport, sbc_negotiated.capability()];
824
825        assert_eq!(
826            negotiation.select(&remote_endpoints_no_delay),
827            Some((expected_capabilities_no_delay.clone(), sbc_sink_seid.try_into().unwrap()))
828        );
829    }
830
831    #[test]
832    fn test_negotiate() {
833        let sbc_mandatory_snk = SbcCodecInfo::new(
834            SbcSamplingFrequency::MANDATORY_SNK,
835            SbcChannelMode::MANDATORY_SNK,
836            SbcBlockCount::MANDATORY_SNK,
837            SbcSubBands::MANDATORY_SNK,
838            SbcAllocation::MANDATORY_SNK,
839            23,
840            SbcCodecInfo::BITPOOL_MAX,
841        )
842        .unwrap();
843        let sbc_snk_config = MediaCodecConfig::try_from(&sbc_mandatory_snk.into()).unwrap();
844
845        // When remote end has a different set of things, we choose the single config that is within
846        // both.
847        let sbc_codec_48 = SbcCodecInfo::new(
848            SbcSamplingFrequency::FREQ48000HZ,
849            SbcChannelMode::JOINT_STEREO,
850            SbcBlockCount::MANDATORY_SRC,
851            SbcSubBands::MANDATORY_SRC,
852            SbcAllocation::MANDATORY_SRC,
853            SbcCodecInfo::BITPOOL_MIN,
854            45,
855        )
856        .unwrap();
857        let sbc_48_config = MediaCodecConfig::try_from(&sbc_codec_48.into()).unwrap();
858
859        let negotiated = MediaCodecConfig::negotiate(&sbc_snk_config, &sbc_48_config)
860            .expect("negotiation to succeed");
861
862        assert_eq!(
863            negotiated.capability(),
864            SbcCodecInfo::new(
865                SbcSamplingFrequency::FREQ48000HZ,
866                SbcChannelMode::JOINT_STEREO,
867                SbcBlockCount::SIXTEEN,
868                SbcSubBands::EIGHT,
869                SbcAllocation::LOUDNESS,
870                23,
871                45,
872            )
873            .unwrap()
874            .into()
875        );
876
877        assert!(sbc_snk_config.supports(&negotiated));
878        assert!(sbc_48_config.supports(&negotiated));
879
880        // If the configs don't overlap, returns None.
881        let sbc_codec_44 = SbcCodecInfo::new(
882            SbcSamplingFrequency::FREQ44100HZ,
883            SbcChannelMode::JOINT_STEREO,
884            SbcBlockCount::MANDATORY_SRC,
885            SbcSubBands::MANDATORY_SRC,
886            SbcAllocation::MANDATORY_SRC,
887            SbcCodecInfo::BITPOOL_MIN,
888            45,
889        )
890        .unwrap();
891        let sbc_44_config = MediaCodecConfig::try_from(&sbc_codec_44.into()).unwrap();
892
893        assert!(MediaCodecConfig::negotiate(&sbc_48_config, &sbc_44_config).is_none());
894    }
895}