use std::io::{Seek as _, SeekFrom};
use anyhow::{Context, Result};
use bytes::Bytes;
use symphonia::core::formats::probe::Hint;
use symphonia::core::formats::{FormatOptions, TrackType};
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use super::super::MAX_SAMPLE_RATE;
use super::super::wave;
use super::BytesMediaSource;
pub fn probe_duration_bytes(data: Bytes) -> Result<Option<f64>> {
if ryf::sniff_wav(data.as_ref()) {
return wave::probe_duration(data.as_ref());
}
let source = BytesMediaSource::new(data);
let mss = MediaSourceStream::new(Box::new(source), Default::default());
probe_duration_inner(mss, Hint::new())
}
pub fn probe_duration_file(path: &str) -> Result<Option<f64>> {
let mut file =
std::fs::File::open(path).with_context(|| format!("Failed to open audio file: {path}"))?;
let mut prefix = [0u8; 40];
let n = std::io::Read::read(&mut file, &mut prefix)
.with_context(|| format!("Failed to read audio file: {path}"))?;
if ryf::sniff_wav(&prefix[..n]) {
file.seek(SeekFrom::Start(0))
.with_context(|| format!("Failed to read audio file: {path}"))?;
return wave::probe_duration_file(file);
}
file.seek(SeekFrom::Start(0))
.with_context(|| format!("Failed to read audio file: {path}"))?;
let mss = MediaSourceStream::new(Box::new(file), Default::default());
let mut hint = Hint::new();
if let Some(ext) = std::path::Path::new(path)
.extension()
.and_then(|e| e.to_str())
{
hint.with_extension(ext);
}
probe_duration_inner(mss, hint)
}
fn probe_duration_inner(mss: MediaSourceStream<'_>, hint: Hint) -> Result<Option<f64>> {
let format = symphonia::default::get_probe()
.probe(
&hint,
mss,
FormatOptions::default(),
MetadataOptions::default(),
)
.context("Unsupported audio format")?;
let Some(track) = format.default_track(TrackType::Audio) else {
return Ok(None);
};
let Some(audio_params) = track.codec_params.as_ref().and_then(|p| p.audio()) else {
return Ok(None);
};
let Some(sample_rate) = audio_params.sample_rate else {
return Ok(None);
};
if sample_rate == 0 || sample_rate > MAX_SAMPLE_RATE {
return Ok(None);
}
match track.num_frames {
Some(n) if n > 0 => Ok(Some(n as f64 / sample_rate as f64)),
_ => Ok(None),
}
}