| // Copyright 2023 Google LLC | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | use std::collections::HashSet; | 
 | use std::ops::RangeBounds; | 
 |  | 
 | use crate::core::ltv::LtValue; | 
 |  | 
 | #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] | 
 | pub enum CodecCapabilityType { | 
 |     SupportedSamplingFrequencies, | 
 |     SupportedFrameDurations, | 
 |     SupportedAudioChannelCounts, | 
 |     SupportedOctetsPerCodecFrame, | 
 |     SupportedMaxCodecFramesPerSdu, | 
 | } | 
 |  | 
 | impl From<CodecCapabilityType> for u8 { | 
 |     fn from(value: CodecCapabilityType) -> Self { | 
 |         match value { | 
 |             CodecCapabilityType::SupportedSamplingFrequencies => 1, | 
 |             CodecCapabilityType::SupportedFrameDurations => 2, | 
 |             CodecCapabilityType::SupportedAudioChannelCounts => 3, | 
 |             CodecCapabilityType::SupportedOctetsPerCodecFrame => 4, | 
 |             CodecCapabilityType::SupportedMaxCodecFramesPerSdu => 5, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 
 | pub enum FrameDurationSupport { | 
 |     SevenFiveMs, | 
 |     TenMs, | 
 |     BothNoPreference, | 
 |     PreferSevenFiveMs, | 
 |     PreferTenMs, | 
 | } | 
 |  | 
 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 
 | pub enum SamplingFrequency { | 
 |     F8000Hz, | 
 |     F11025Hz, | 
 |     F16000Hz, | 
 |     F22050Hz, | 
 |     F24000Hz, | 
 |     F32000Hz, | 
 |     F44100Hz, | 
 |     F48000Hz, | 
 |     F88200Hz, | 
 |     F96000Hz, | 
 |     F176400Hz, | 
 |     F192000Hz, | 
 |     F384000Hz, | 
 | } | 
 |  | 
 | impl SamplingFrequency { | 
 |     fn bitpos(&self) -> u8 { | 
 |         match self { | 
 |             SamplingFrequency::F8000Hz => 0, | 
 |             SamplingFrequency::F11025Hz => 1, | 
 |             SamplingFrequency::F16000Hz => 2, | 
 |             SamplingFrequency::F22050Hz => 3, | 
 |             SamplingFrequency::F24000Hz => 4, | 
 |             SamplingFrequency::F32000Hz => 5, | 
 |             SamplingFrequency::F44100Hz => 6, | 
 |             SamplingFrequency::F48000Hz => 7, | 
 |             SamplingFrequency::F88200Hz => 8, | 
 |             SamplingFrequency::F96000Hz => 9, | 
 |             SamplingFrequency::F176400Hz => 10, | 
 |             SamplingFrequency::F192000Hz => 11, | 
 |             SamplingFrequency::F384000Hz => 12, | 
 |         } | 
 |     } | 
 |  | 
 |     fn from_bitpos(value: u8) -> Option<Self> { | 
 |         match value { | 
 |             0 => Some(SamplingFrequency::F8000Hz), | 
 |             1 => Some(SamplingFrequency::F11025Hz), | 
 |             2 => Some(SamplingFrequency::F16000Hz), | 
 |             3 => Some(SamplingFrequency::F22050Hz), | 
 |             4 => Some(SamplingFrequency::F24000Hz), | 
 |             5 => Some(SamplingFrequency::F32000Hz), | 
 |             6 => Some(SamplingFrequency::F44100Hz), | 
 |             7 => Some(SamplingFrequency::F48000Hz), | 
 |             8 => Some(SamplingFrequency::F88200Hz), | 
 |             9 => Some(SamplingFrequency::F96000Hz), | 
 |             10 => Some(SamplingFrequency::F176400Hz), | 
 |             11 => Some(SamplingFrequency::F192000Hz), | 
 |             12 => Some(SamplingFrequency::F384000Hz), | 
 |             _ => None, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /// Codec Capability LTV Structures | 
 | /// | 
 | /// Defined in Assigned Numbers Section 6.12.4. | 
 | #[derive(Clone, PartialEq, Eq, Debug)] | 
 | pub enum CodecCapability { | 
 |     SupportedSamplingFrequencies(std::collections::HashSet<SamplingFrequency>), | 
 |     SupportedFrameDurations(FrameDurationSupport), | 
 |     SupportedAudioChannelCounts(std::collections::HashSet<u8>), | 
 |     SupportedMaxCodecFramesPerSdu(u8), | 
 |     SupportedOctetsPerCodecFrame { min: u16, max: u16 }, | 
 | } | 
 |  | 
 | impl LtValue for CodecCapability { | 
 |     type Type = CodecCapabilityType; | 
 |  | 
 |     const NAME: &'static str = "Codec Compatability"; | 
 |  | 
 |     fn type_from_octet(x: u8) -> Option<Self::Type> { | 
 |         match x { | 
 |             1 => Some(CodecCapabilityType::SupportedSamplingFrequencies), | 
 |             2 => Some(CodecCapabilityType::SupportedFrameDurations), | 
 |             3 => Some(CodecCapabilityType::SupportedAudioChannelCounts), | 
 |             4 => Some(CodecCapabilityType::SupportedOctetsPerCodecFrame), | 
 |             5 => Some(CodecCapabilityType::SupportedMaxCodecFramesPerSdu), | 
 |             _ => None, | 
 |         } | 
 |     } | 
 |  | 
 |     fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> { | 
 |         match ty { | 
 |             CodecCapabilityType::SupportedAudioChannelCounts => 2..=2, | 
 |             CodecCapabilityType::SupportedFrameDurations => 2..=2, | 
 |             CodecCapabilityType::SupportedMaxCodecFramesPerSdu => 2..=2, | 
 |             CodecCapabilityType::SupportedOctetsPerCodecFrame => 5..=5, | 
 |             CodecCapabilityType::SupportedSamplingFrequencies => 3..=3, | 
 |         } | 
 |     } | 
 |  | 
 |     fn into_type(&self) -> Self::Type { | 
 |         match self { | 
 |             CodecCapability::SupportedAudioChannelCounts(_) => { | 
 |                 CodecCapabilityType::SupportedAudioChannelCounts | 
 |             } | 
 |             CodecCapability::SupportedFrameDurations(_) => { | 
 |                 CodecCapabilityType::SupportedFrameDurations | 
 |             } | 
 |             CodecCapability::SupportedMaxCodecFramesPerSdu(_) => { | 
 |                 CodecCapabilityType::SupportedMaxCodecFramesPerSdu | 
 |             } | 
 |             CodecCapability::SupportedOctetsPerCodecFrame { .. } => { | 
 |                 CodecCapabilityType::SupportedOctetsPerCodecFrame | 
 |             } | 
 |             CodecCapability::SupportedSamplingFrequencies(_) => { | 
 |                 CodecCapabilityType::SupportedSamplingFrequencies | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     fn value_encoded_len(&self) -> u8 { | 
 |         // All the CodecCapabilities are constant length. Remove the type octet. | 
 |         let range = Self::length_range_from_type(self.into_type()); | 
 |         let std::ops::Bound::Included(len) = range.start_bound() else { | 
 |             unreachable!(); | 
 |         }; | 
 |         (len - 1).into() | 
 |     } | 
 |  | 
 |     fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> { | 
 |         match self { | 
 |             CodecCapability::SupportedAudioChannelCounts(counts) => { | 
 |                 buf[0] = counts | 
 |                     .iter() | 
 |                     .fold(0, |acc, count| if *count > 8 { acc } else { acc | (1 << (*count - 1)) }); | 
 |             } | 
 |             CodecCapability::SupportedFrameDurations(support) => { | 
 |                 buf[0] = match support { | 
 |                     FrameDurationSupport::SevenFiveMs => 0b000001, | 
 |                     FrameDurationSupport::TenMs => 0b000010, | 
 |                     FrameDurationSupport::BothNoPreference => 0b000011, | 
 |                     FrameDurationSupport::PreferSevenFiveMs => 0b010011, | 
 |                     FrameDurationSupport::PreferTenMs => 0b100011, | 
 |                 }; | 
 |             } | 
 |             CodecCapability::SupportedMaxCodecFramesPerSdu(max) => { | 
 |                 buf[0] = *max; | 
 |             } | 
 |             CodecCapability::SupportedOctetsPerCodecFrame { min, max } => { | 
 |                 let min_bytes = min.to_le_bytes(); | 
 |                 buf[0..=1].copy_from_slice(&min_bytes); | 
 |  | 
 |                 let max_bytes = max.to_le_bytes(); | 
 |                 buf[2..=3].copy_from_slice(&max_bytes); | 
 |             } | 
 |             CodecCapability::SupportedSamplingFrequencies(supported) => { | 
 |                 let sup_bytes = supported | 
 |                     .iter() | 
 |                     .fold(0u16, |acc, freq| acc | (1 << freq.bitpos())) | 
 |                     .to_le_bytes(); | 
 |                 buf[0..=1].copy_from_slice(&sup_bytes) | 
 |             } | 
 |         }; | 
 |         Ok(()) | 
 |     } | 
 |  | 
 |     fn decode_value( | 
 |         ty: &CodecCapabilityType, | 
 |         buf: &[u8], | 
 |     ) -> Result<Self, crate::packet_encoding::Error> { | 
 |         match ty { | 
 |             CodecCapabilityType::SupportedAudioChannelCounts => { | 
 |                 let mut supported = HashSet::new(); | 
 |                 for bitpos in 0..8 { | 
 |                     if (buf[0] & (1 << bitpos)) != 0 { | 
 |                         supported.insert(bitpos + 1); | 
 |                     } | 
 |                 } | 
 |                 Ok(Self::SupportedAudioChannelCounts(supported)) | 
 |             } | 
 |             CodecCapabilityType::SupportedFrameDurations => { | 
 |                 let support = match buf[0] & 0b110011 { | 
 |                     0b000001 => FrameDurationSupport::SevenFiveMs, | 
 |                     0b000010 => FrameDurationSupport::TenMs, | 
 |                     0b000011 => FrameDurationSupport::BothNoPreference, | 
 |                     0b010011 => FrameDurationSupport::PreferSevenFiveMs, | 
 |                     0b100011 => FrameDurationSupport::PreferTenMs, | 
 |                     _ => { | 
 |                         return Err(crate::packet_encoding::Error::InvalidParameter(format!( | 
 |                             "Unrecognized bit pattern: {:#b}", | 
 |                             buf[0] | 
 |                         ))); | 
 |                     } | 
 |                 }; | 
 |                 Ok(Self::SupportedFrameDurations(support)) | 
 |             } | 
 |             CodecCapabilityType::SupportedMaxCodecFramesPerSdu => { | 
 |                 Ok(Self::SupportedMaxCodecFramesPerSdu(buf[0])) | 
 |             } | 
 |             CodecCapabilityType::SupportedOctetsPerCodecFrame => { | 
 |                 let min = u16::from_le_bytes([buf[0], buf[1]]); | 
 |                 let max = u16::from_le_bytes([buf[2], buf[3]]); | 
 |                 Ok(Self::SupportedOctetsPerCodecFrame { min, max }) | 
 |             } | 
 |             CodecCapabilityType::SupportedSamplingFrequencies => { | 
 |                 let bitflags = u16::from_le_bytes([buf[0], buf[1]]); | 
 |                 let mut supported = HashSet::new(); | 
 |                 for bitpos in 0..13 { | 
 |                     if bitflags & (1 << bitpos) != 0 { | 
 |                         let Some(f) = SamplingFrequency::from_bitpos(bitpos) else { | 
 |                             continue; | 
 |                         }; | 
 |                         let _ = supported.insert(f); | 
 |                     } | 
 |                 } | 
 |                 Ok(Self::SupportedSamplingFrequencies(supported)) | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | #[cfg(test)] | 
 | mod tests {} |