use crate::bitstream::BitReader;
use crate::error::FlacError;
use crate::frame::decode_frame;
use crate::metadata::{read_header, StreamInfo};
use crate::sample_bytes::md5_of_samples;
use crate::FlacAudio;
const MAX_TOTAL_SAMPLES: u64 = 1 << 30;
pub(crate) fn validate_stream_info(info: &StreamInfo) -> Result<(), FlacError> {
if info.channels < 1 || info.channels > 8 {
return Err(FlacError::Unsupported(format!(
"channel count {}",
info.channels
)));
}
if info.bits_per_sample < 4 || info.bits_per_sample > 32 {
return Err(FlacError::Unsupported(format!(
"{}-bit samples",
info.bits_per_sample
)));
}
Ok(())
}
pub fn decode(bytes: &[u8]) -> Result<FlacAudio, FlacError> {
if crate::ogg::is_ogg(bytes) {
let native = crate::ogg::to_native_flac(bytes, false)?;
return decode_native(&native);
}
decode_native(bytes)
}
fn decode_native(bytes: &[u8]) -> Result<FlacAudio, FlacError> {
let header = read_header(bytes)?;
let info = header.stream_info;
validate_stream_info(&info)?;
let channels = info.channels as u64;
if info.total_samples.saturating_mul(channels) > MAX_TOTAL_SAMPLES {
return Err(FlacError::LimitExceeded(format!(
"stream declares {} samples across {} channels, above the {MAX_TOTAL_SAMPLES}-sample decode cap",
info.total_samples, info.channels
)));
}
let mut reader = BitReader::new(&bytes[header.frame_start..]);
let mut out: Vec<Vec<i32>> = vec![Vec::new(); info.channels as usize];
loop {
if info.total_samples > 0 && out[0].len() as u64 >= info.total_samples {
break;
}
if reader.bits_left() < 16 {
break;
}
decode_frame(&mut reader, &info, &mut out)?;
if (out[0].len() as u64).saturating_mul(channels) > MAX_TOTAL_SAMPLES {
return Err(FlacError::LimitExceeded(format!(
"decoded sample count exceeds the {MAX_TOTAL_SAMPLES}-sample decode cap"
)));
}
}
if info.total_samples > 0 {
let total = info.total_samples as usize;
for channel in &mut out {
if channel.len() > total {
channel.truncate(total);
}
}
}
if info.md5 != [0u8; 16] {
let computed = md5_of_samples(&out, info.bits_per_sample);
if computed != info.md5 {
return Err(FlacError::CrcMismatch);
}
}
Ok(FlacAudio {
sample_rate: info.sample_rate,
channels: info.channels,
bits_per_sample: info.bits_per_sample,
samples: out,
})
}