| // Copyright 2024 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; |
| use crate::decodable_enum; |
| |
| use super::AudioLocation; |
| |
| #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] |
| pub enum CodecConfigurationType { |
| SamplingFrequency, |
| FrameDuration, |
| AudioChannelAllocation, |
| OctetsPerCodecFrame, |
| CodecFramesPerSdu, |
| } |
| |
| impl From<CodecConfigurationType> for u8 { |
| fn from(value: CodecConfigurationType) -> Self { |
| match value { |
| CodecConfigurationType::SamplingFrequency => 1, |
| CodecConfigurationType::FrameDuration => 2, |
| CodecConfigurationType::AudioChannelAllocation => 3, |
| CodecConfigurationType::OctetsPerCodecFrame => 4, |
| CodecConfigurationType::CodecFramesPerSdu => 5, |
| } |
| } |
| } |
| |
| decodable_enum! { |
| pub enum FrameDuration<u8, crate::packet_encoding::Error, OutOfRange> { |
| SevenFiveMs = 0x00, |
| TenMs = 0x01, |
| } |
| } |
| |
| decodable_enum! { |
| pub enum SamplingFrequency<u8, crate::packet_encoding::Error, OutOfRange> { |
| F8000Hz = 0x01, |
| F11025Hz = 0x02, |
| F16000Hz = 0x03, |
| F22050Hz = 0x04, |
| F24000Hz = 0x05, |
| F32000Hz = 0x06, |
| F44100Hz = 0x07, |
| F48000Hz = 0x08, |
| F88200Hz = 0x09, |
| F96000Hz = 0x0A, |
| F176400Hz = 0x0B, |
| F192000Hz = 0x0C, |
| F384000Hz = 0x0D, |
| } |
| } |
| |
| /// Codec Configuration LTV Structures |
| /// |
| /// Defined in Assigned Numbers Section 6.12.5. |
| #[derive(Clone, PartialEq, Eq, Debug)] |
| pub enum CodecConfiguration { |
| SamplingFrequency(SamplingFrequency), |
| FrameDuration(FrameDuration), |
| AudioChannelAllocation(std::collections::HashSet<AudioLocation>), |
| OctetsPerCodecFrame(u16), |
| CodecFramesPerSdu(u8), |
| } |
| |
| impl LtValue for CodecConfiguration { |
| type Type = CodecConfigurationType; |
| |
| const NAME: &'static str = "Codec Compatability"; |
| |
| fn type_from_octet(x: u8) -> Option<Self::Type> { |
| match x { |
| 1 => Some(CodecConfigurationType::SamplingFrequency), |
| 2 => Some(CodecConfigurationType::FrameDuration), |
| 3 => Some(CodecConfigurationType::AudioChannelAllocation), |
| 4 => Some(CodecConfigurationType::OctetsPerCodecFrame), |
| 5 => Some(CodecConfigurationType::CodecFramesPerSdu), |
| _ => None, |
| } |
| } |
| |
| fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> { |
| match ty { |
| CodecConfigurationType::SamplingFrequency => 2..=2, |
| CodecConfigurationType::FrameDuration => 2..=2, |
| CodecConfigurationType::AudioChannelAllocation => 5..=5, |
| CodecConfigurationType::OctetsPerCodecFrame => 3..=3, |
| CodecConfigurationType::CodecFramesPerSdu => 2..=2, |
| } |
| } |
| |
| fn into_type(&self) -> Self::Type { |
| match self { |
| CodecConfiguration::SamplingFrequency(_) => CodecConfigurationType::SamplingFrequency, |
| CodecConfiguration::FrameDuration(_) => CodecConfigurationType::FrameDuration, |
| CodecConfiguration::AudioChannelAllocation(_) => { |
| CodecConfigurationType::AudioChannelAllocation |
| } |
| CodecConfiguration::OctetsPerCodecFrame(_) => { |
| CodecConfigurationType::OctetsPerCodecFrame |
| } |
| CodecConfiguration::CodecFramesPerSdu(_) => CodecConfigurationType::CodecFramesPerSdu, |
| } |
| } |
| |
| 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 { |
| CodecConfiguration::SamplingFrequency(freq) => buf[0] = u8::from(*freq), |
| CodecConfiguration::FrameDuration(fd) => buf[0] = u8::from(*fd), |
| CodecConfiguration::AudioChannelAllocation(audio_locations) => { |
| let locations: Vec<&AudioLocation> = audio_locations.into_iter().collect(); |
| buf[0..4] |
| .copy_from_slice(&AudioLocation::to_bits(locations.into_iter()).to_le_bytes()); |
| } |
| CodecConfiguration::OctetsPerCodecFrame(octets) => { |
| buf[0..2].copy_from_slice(&octets.to_le_bytes()) |
| } |
| CodecConfiguration::CodecFramesPerSdu(frames) => buf[0] = *frames, |
| }; |
| Ok(()) |
| } |
| |
| fn decode_value( |
| ty: &CodecConfigurationType, |
| buf: &[u8], |
| ) -> Result<Self, crate::packet_encoding::Error> { |
| match ty { |
| CodecConfigurationType::SamplingFrequency => { |
| let freq = SamplingFrequency::try_from(buf[0])?; |
| Ok(Self::SamplingFrequency(freq)) |
| } |
| CodecConfigurationType::FrameDuration => { |
| let fd = FrameDuration::try_from(buf[0])?; |
| Ok(Self::FrameDuration(fd)) |
| } |
| CodecConfigurationType::AudioChannelAllocation => { |
| let raw_value = u32::from_le_bytes(buf[0..4].try_into().unwrap()); |
| let locations = AudioLocation::from_bits(raw_value).collect::<HashSet<_>>(); |
| Ok(Self::AudioChannelAllocation(locations)) |
| } |
| CodecConfigurationType::OctetsPerCodecFrame => { |
| let value = u16::from_le_bytes(buf[0..2].try_into().unwrap()); |
| Ok(Self::OctetsPerCodecFrame(value)) |
| } |
| CodecConfigurationType::CodecFramesPerSdu => Ok(Self::CodecFramesPerSdu(buf[0])), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests {} |