| // Copyright 2023 The Fuchsia Authors. All rights reserved. | |
| // 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 {} |