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