snapcast_client/decoder/
flac.rs1use anyhow::{Result, bail};
7use snapcast_proto::SampleFormat;
8use snapcast_proto::message::codec_header::CodecHeader;
9use symphonia::core::audio::SampleBuffer;
10use symphonia::core::codecs::{CODEC_TYPE_FLAC, CodecParameters, DecoderOptions};
11use symphonia::core::formats::Packet;
12
13use crate::decoder::Decoder;
14
15fn parse_streaminfo(payload: &[u8]) -> Result<(SampleFormat, CodecParameters)> {
20 if payload.len() < 42 {
21 bail!(
22 "FLAC header too small ({} bytes, need >= 42)",
23 payload.len()
24 );
25 }
26 if &payload[0..4] != b"fLaC" {
27 bail!("not a FLAC header (missing fLaC magic)");
28 }
29
30 let si = &payload[8..];
32
33 let b10 = si[10] as u32;
36 let b11 = si[11] as u32;
37 let b12 = si[12] as u32;
38
39 let sample_rate = (b10 << 12) | (b11 << 4) | (b12 >> 4);
40 let channels = ((b12 >> 1) & 0x07) + 1;
41 let bits_per_sample = (((b12 & 0x01) << 4) | (si[13] as u32 >> 4)) + 1;
42
43 let sf = SampleFormat::new(sample_rate, bits_per_sample as u16, channels as u16);
44
45 let mut params = CodecParameters::new();
46 params
47 .for_codec(CODEC_TYPE_FLAC)
48 .with_sample_rate(sample_rate)
49 .with_bits_per_sample(bits_per_sample)
50 .with_channels(
51 symphonia::core::audio::Channels::from_bits(((1u64 << channels) - 1) as u32)
52 .unwrap_or(symphonia::core::audio::Channels::FRONT_LEFT),
53 )
54 .with_extra_data(payload[8..42].to_vec().into_boxed_slice());
55
56 Ok((sf, params))
57}
58
59pub struct FlacDecoder {
61 decoder: Box<dyn symphonia::core::codecs::Decoder>,
62 sample_format: SampleFormat,
63 packet_id: u64,
64}
65
66impl FlacDecoder {
67 fn new_from_params(sf: SampleFormat, params: &CodecParameters) -> Result<Self> {
68 let decoder = symphonia::default::get_codecs()
69 .make(params, &DecoderOptions::default())
70 .map_err(|e| anyhow::anyhow!("failed to create FLAC decoder: {e}"))?;
71 Ok(Self {
72 decoder,
73 sample_format: sf,
74 packet_id: 0,
75 })
76 }
77}
78
79impl Decoder for FlacDecoder {
80 fn set_header(&mut self, header: &CodecHeader) -> Result<SampleFormat> {
81 tracing::trace!(
82 codec = "flac",
83 payload_len = header.payload.len(),
84 "set_header"
85 );
86 let (sf, params) = parse_streaminfo(&header.payload)?;
87 *self = Self::new_from_params(sf, ¶ms)?;
88 Ok(self.sample_format)
89 }
90
91 fn decode(&mut self, data: &mut Vec<u8>) -> Result<bool> {
92 if data.is_empty() {
93 return Ok(false);
94 }
95
96 tracing::trace!(
97 codec = "flac",
98 input_bytes = data.len(),
99 packet_id = self.packet_id,
100 "decode"
101 );
102
103 let packet = Packet::new_from_slice(0, self.packet_id, 0, data);
104 self.packet_id += 1;
105
106 let decoded = match self.decoder.decode(&packet) {
107 Ok(buf) => buf,
108 Err(e) => {
109 tracing::warn!(codec = "flac", error = %e, "decode failed");
110 return Ok(false);
111 }
112 };
113
114 let spec = *decoded.spec();
115 let frames = decoded.frames() as u64;
116
117 let mut sample_buf = SampleBuffer::<i16>::new(frames, spec);
118 sample_buf.copy_interleaved_ref(decoded);
119 let samples = sample_buf.samples();
120
121 let mut out = Vec::with_capacity(samples.len() * 2);
122 for &s in samples {
123 out.extend_from_slice(&s.to_le_bytes());
124 }
125
126 *data = out;
127 Ok(true)
128 }
129}
130
131pub fn create(header: &CodecHeader) -> Result<FlacDecoder> {
133 let (sf, params) = parse_streaminfo(&header.payload)?;
134 FlacDecoder::new_from_params(sf, ¶ms)
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 fn flac_header_44100_16_2() -> Vec<u8> {
144 let mut h = Vec::new();
145 h.extend_from_slice(b"fLaC");
146 h.push(0x80);
148 h.extend_from_slice(&[0x00, 0x00, 0x22]); h.extend_from_slice(&[0x00, 0x10]); h.extend_from_slice(&[0x10, 0x00]); h.extend_from_slice(&[0x00, 0x00, 0x00]); h.extend_from_slice(&[0x00, 0x00, 0x00]); h.push(0x0A);
164 h.push(0xC4);
165 h.push(0x42);
166 h.push(0xF0);
167
168 h.extend_from_slice(&[0; 20]);
170
171 assert_eq!(h.len(), 42);
172 h
173 }
174
175 #[test]
176 fn parse_streaminfo_44100_16_2() {
177 let payload = flac_header_44100_16_2();
178 let (sf, _params) = parse_streaminfo(&payload).unwrap();
179 assert_eq!(sf.rate(), 44100);
180 assert_eq!(sf.bits(), 16);
181 assert_eq!(sf.channels(), 2);
182 }
183
184 #[test]
185 fn parse_streaminfo_48000_24_2() {
186 let mut h = Vec::new();
187 h.extend_from_slice(b"fLaC");
188 h.push(0x80);
189 h.extend_from_slice(&[0x00, 0x00, 0x22]);
190 h.extend_from_slice(&[0x10, 0x00]); h.extend_from_slice(&[0x10, 0x00]); h.extend_from_slice(&[0; 6]); h.push(0x0B);
200 h.push(0xB8);
201 h.push(0x03);
202 h.push(0x70);
203 h.extend_from_slice(&[0; 20]);
204
205 let (sf, _) = parse_streaminfo(&h).unwrap();
206 assert_eq!(sf.rate(), 48000);
207 assert_eq!(sf.bits(), 24);
208 assert_eq!(sf.channels(), 2);
209 }
210
211 #[test]
212 fn parse_streaminfo_too_small() {
213 assert!(parse_streaminfo(&[0; 10]).is_err());
214 }
215
216 #[test]
217 fn parse_streaminfo_bad_magic() {
218 let mut h = vec![0u8; 42];
219 h[0..4].copy_from_slice(b"NOPE");
220 assert!(parse_streaminfo(&h).is_err());
221 }
222
223 #[test]
224 fn create_decoder_from_header() {
225 let header = CodecHeader {
226 codec: "flac".into(),
227 payload: flac_header_44100_16_2(),
228 };
229 let dec = create(&header);
230 if let Err(e) = &dec {
231 eprintln!("Error: {e:?}");
232 }
233 let dec = dec.unwrap();
234 assert_eq!(dec.sample_format.rate(), 44100);
235 }
236}