mod import;
pub use import::*;
use bytes::{Buf, BufMut, Bytes};
const MARKER: [u8; 4] = *b"fLaC";
const STREAMINFO_LEN: usize = 34;
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("buffer too short for FLAC STREAMINFO")]
Short,
#[error("invalid FLAC stream marker")]
InvalidMarker,
#[error("first metadata block is not STREAMINFO")]
MissingStreamInfo,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config {
pub min_block_size: u16,
pub max_block_size: u16,
pub min_frame_size: u32,
pub max_frame_size: u32,
pub sample_rate: u32,
pub channel_count: u32,
pub bits_per_sample: u32,
pub total_samples: u64,
pub md5: [u8; 16],
}
impl Config {
pub fn parse_stream_info<T: Buf>(buf: &mut T) -> Result<Self> {
if buf.remaining() < STREAMINFO_LEN {
return Err(Error::Short);
}
let min_block_size = buf.get_u16();
let max_block_size = buf.get_u16();
let min_frame_size = buf.get_uint(3) as u32;
let max_frame_size = buf.get_uint(3) as u32;
let packed = buf.get_u64();
let sample_rate = (packed >> 44) as u32;
let channel_count = (((packed >> 41) & 0x7) as u32) + 1;
let bits_per_sample = (((packed >> 36) & 0x1f) as u32) + 1;
let total_samples = packed & 0xF_FFFF_FFFF;
let mut md5 = [0u8; 16];
buf.copy_to_slice(&mut md5);
Ok(Self {
min_block_size,
max_block_size,
min_frame_size,
max_frame_size,
sample_rate,
channel_count,
bits_per_sample,
total_samples,
md5,
})
}
pub fn parse<T: Buf>(buf: &mut T) -> Result<Self> {
if buf.remaining() < 4 {
return Err(Error::Short);
}
let mut marker = [0u8; 4];
buf.copy_to_slice(&mut marker);
if marker != MARKER {
return Err(Error::InvalidMarker);
}
if buf.remaining() < 4 {
return Err(Error::Short);
}
let header = buf.get_u32();
let block_type = ((header >> 24) & 0x7f) as u8;
if block_type != 0 {
return Err(Error::MissingStreamInfo);
}
Self::parse_stream_info(buf)
}
pub fn encode_stream_info(&self) -> Bytes {
let mut buf = Vec::with_capacity(STREAMINFO_LEN);
buf.put_u16(self.min_block_size);
buf.put_u16(self.max_block_size);
buf.put_uint(self.min_frame_size as u64, 3);
buf.put_uint(self.max_frame_size as u64, 3);
let packed = ((self.sample_rate as u64 & 0xF_FFFF) << 44)
| ((self.channel_count.saturating_sub(1) as u64 & 0x7) << 41)
| ((self.bits_per_sample.saturating_sub(1) as u64 & 0x1f) << 36)
| (self.total_samples & 0xF_FFFF_FFFF);
buf.put_u64(packed);
buf.put_slice(&self.md5);
buf.into()
}
pub fn description(&self) -> Bytes {
let stream_info = self.encode_stream_info();
let mut buf = Vec::with_capacity(MARKER.len() + 4 + stream_info.len());
buf.put_slice(&MARKER);
buf.put_u8(0x80);
buf.put_uint(stream_info.len() as u64, 3);
buf.put_slice(&stream_info);
buf.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample() -> Config {
Config {
min_block_size: 4608,
max_block_size: 4608,
min_frame_size: 16,
max_frame_size: 9102,
sample_rate: 44_100,
channel_count: 2,
bits_per_sample: 24,
total_samples: 120_832,
md5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
}
}
#[test]
fn stream_info_roundtrip() {
let cfg = sample();
let encoded = cfg.encode_stream_info();
assert_eq!(encoded.len(), STREAMINFO_LEN);
let parsed = Config::parse_stream_info(&mut encoded.as_ref()).unwrap();
assert_eq!(parsed, cfg);
}
#[test]
fn description_roundtrip() {
let cfg = sample();
let desc = cfg.description();
assert_eq!(desc.len(), 4 + 4 + STREAMINFO_LEN);
assert_eq!(&desc[..4], b"fLaC");
assert_eq!(desc[4], 0x80);
let parsed = Config::parse(&mut desc.as_ref()).unwrap();
assert_eq!(parsed, cfg);
}
#[test]
fn parse_rejects_bad_marker() {
let mut desc = sample().description().to_vec();
desc[0] = b'X';
assert!(matches!(Config::parse(&mut desc.as_slice()), Err(Error::InvalidMarker)));
}
#[test]
fn parse_rejects_non_streaminfo_first_block() {
let mut desc = sample().description().to_vec();
desc[4] = 0x80 | 0x04; assert!(matches!(
Config::parse(&mut desc.as_slice()),
Err(Error::MissingStreamInfo)
));
}
}