1use anyhow::format_err;
6use thiserror::Error;
7use {fidl_fuchsia_bluetooth_bredr as bredr, fidl_fuchsia_media as media};
8
9use crate::audio;
10
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
13pub struct CodecId(u8);
14
15#[derive(Clone, Copy, Debug, Error, Eq, Hash, PartialEq)]
16#[error("Codec ID {id:?} was not an 8 bit value")]
17pub struct CodecOutOfRangeError {
18 id: i64,
19}
20
21impl CodecId {
22 pub const CVSD: CodecId = CodecId(0x01);
23 pub const MSBC: CodecId = CodecId(0x02);
24}
25
26impl From<u8> for CodecId {
27 fn from(x: u8) -> Self {
28 Self(x)
29 }
30}
31
32impl TryFrom<i64> for CodecId {
33 type Error = CodecOutOfRangeError;
34
35 fn try_from(x: i64) -> Result<Self, CodecOutOfRangeError> {
36 if x > 255 || x < 0 {
37 return Err(CodecOutOfRangeError { id: x });
38 } else {
39 Ok((x as u8).into())
40 }
41 }
42}
43
44impl Into<u8> for CodecId {
45 fn into(self) -> u8 {
46 self.0
47 }
48}
49
50impl Into<i64> for CodecId {
53 fn into(self) -> i64 {
54 self.0 as i64
55 }
56}
57
58fn unsupported_codec_id(codec: CodecId) -> audio::Error {
59 audio::Error::UnsupportedParameters { source: format_err!("Unknown CodecId: {codec:?}") }
60}
61
62impl TryFrom<CodecId> for media::EncoderSettings {
63 type Error = audio::Error;
64
65 fn try_from(value: CodecId) -> Result<Self, Self::Error> {
66 match value {
67 CodecId::MSBC => Ok(media::EncoderSettings::Msbc(Default::default())),
68 CodecId::CVSD => Ok(media::EncoderSettings::Cvsd(Default::default())),
69 _ => Err(unsupported_codec_id(value)),
70 }
71 }
72}
73
74impl TryFrom<CodecId> for media::PcmFormat {
75 type Error = audio::Error;
76
77 fn try_from(value: CodecId) -> Result<Self, Self::Error> {
78 let frames_per_second = match value {
79 CodecId::CVSD => 64000,
80 CodecId::MSBC => 16000,
81 _ => return Err(unsupported_codec_id(value)),
82 };
83 Ok(media::PcmFormat {
84 pcm_mode: media::AudioPcmMode::Linear,
85 bits_per_sample: 16,
86 frames_per_second,
87 channel_map: vec![media::AudioChannelId::Lf],
88 })
89 }
90}
91
92impl TryFrom<CodecId> for media::DomainFormat {
93 type Error = audio::Error;
94
95 fn try_from(value: CodecId) -> Result<Self, Self::Error> {
96 Ok(media::DomainFormat::Audio(media::AudioFormat::Uncompressed(
97 media::AudioUncompressedFormat::Pcm(media::PcmFormat::try_from(value)?),
98 )))
99 }
100}
101
102impl TryFrom<CodecId> for fidl_fuchsia_hardware_audio::DaiSupportedFormats {
103 type Error = audio::Error;
104
105 fn try_from(value: CodecId) -> Result<Self, Self::Error> {
106 let frames_per_second = match value {
107 CodecId::CVSD => 64000,
108 CodecId::MSBC => 16000,
109 _ => return Err(unsupported_codec_id(value)),
110 };
111 use fidl_fuchsia_hardware_audio::*;
112 Ok(DaiSupportedFormats {
113 number_of_channels: vec![1],
114 sample_formats: vec![fidl_fuchsia_hardware_audio::DaiSampleFormat::PcmSigned],
115 frame_formats: vec![DaiFrameFormat::FrameFormatStandard(DaiFrameFormatStandard::I2S)],
116 frame_rates: vec![frames_per_second],
117 bits_per_slot: vec![16],
118 bits_per_sample: vec![16],
119 })
120 }
121}
122
123#[cfg(test)]
124impl TryFrom<CodecId> for fidl_fuchsia_hardware_audio::Format {
125 type Error = audio::Error;
126 fn try_from(value: CodecId) -> Result<Self, Self::Error> {
127 let frame_rate = match value {
128 CodecId::CVSD => 64000,
129 CodecId::MSBC => 16000,
130 _ => {
131 return Err(audio::Error::UnsupportedParameters {
132 source: format_err!("Unsupported CodecID {value}"),
133 })
134 }
135 };
136 Ok(Self {
137 pcm_format: Some(fidl_fuchsia_hardware_audio::PcmFormat {
138 number_of_channels: 1u8,
139 sample_format: fidl_fuchsia_hardware_audio::SampleFormat::PcmSigned,
140 bytes_per_sample: 2u8,
141 valid_bits_per_sample: 16u8,
142 frame_rate,
143 }),
144 ..Default::default()
145 })
146 }
147}
148
149impl PartialEq<i64> for CodecId {
150 fn eq(&self, other: &i64) -> bool {
151 self.0 as i64 == *other
152 }
153}
154
155impl std::fmt::Display for CodecId {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 match self.0 {
158 0x01 => write!(f, "{}", "CVSD"),
159 0x02 => write!(f, "{}", "MSBC"),
160 unknown => write!(f, "Unknown({:#x})", unknown),
161 }
162 }
163}
164
165impl CodecId {
166 pub fn is_supported(&self) -> bool {
167 match self {
168 &CodecId::MSBC | &CodecId::CVSD => true,
169 _ => false,
170 }
171 }
172
173 pub fn oob_bytes(&self) -> Vec<u8> {
174 use bt_a2dp::media_types::{
175 SbcAllocation, SbcBlockCount, SbcChannelMode, SbcCodecInfo, SbcSamplingFrequency,
176 SbcSubBands,
177 };
178 match self {
179 &CodecId::MSBC => SbcCodecInfo::new(
180 SbcSamplingFrequency::FREQ16000HZ,
181 SbcChannelMode::MONO,
182 SbcBlockCount::SIXTEEN,
183 SbcSubBands::EIGHT,
184 SbcAllocation::LOUDNESS,
185 26,
186 26,
187 )
188 .unwrap()
189 .to_bytes()
190 .to_vec(),
191 _ => vec![],
193 }
194 }
195
196 pub fn mime_type(&self) -> Result<&str, audio::Error> {
197 match self {
198 &CodecId::MSBC => Ok("audio/msbc"),
199 &CodecId::CVSD => Ok("audio/cvsd"),
200 _ => Err(audio::Error::UnsupportedParameters { source: format_err!("codec {self}") }),
201 }
202 }
203
204 pub fn from_parameter_set(param_set: &bredr::HfpParameterSet) -> CodecId {
205 use bredr::HfpParameterSet::*;
206 match param_set {
207 T2 | T1 => CodecId::MSBC,
208 _ => CodecId::CVSD,
209 }
210 }
211}
212
213pub fn codecs_to_string(codecs: &Vec<CodecId>) -> String {
214 let codecs_string: Vec<String> = codecs.iter().map(ToString::to_string).collect();
215 let codecs_string: Vec<&str> = codecs_string.iter().map(AsRef::as_ref).collect();
216 let joined = codecs_string.join(", ");
217 joined
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223
224 #[fuchsia::test]
225 fn codecs_format() {
226 let cvsd = CodecId(0x1);
227 let mbsc = CodecId(0x2);
228 let unknown = CodecId(0xf);
229
230 let cvsd_string = format!("{:}", cvsd);
231 assert_eq!(String::from("CVSD"), cvsd_string);
232
233 let mbsc_string = format!("{:}", mbsc);
234 assert_eq!(String::from("MSBC"), mbsc_string);
235
236 let unknown_string = format!("{:}", unknown);
237 assert_eq!(String::from("Unknown(0xf)"), unknown_string);
238
239 let joined_string = codecs_to_string(&vec![cvsd, mbsc, unknown]);
240 assert_eq!(String::from("CVSD, MSBC, Unknown(0xf)"), joined_string);
241 }
242}