|  | // 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 {} |