mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 10:10:05 -06:00
security(audio): bound sequence sample rates and AAF/OMF export channel buffers (#3082)
* security(audio): bound sequence sample rates and AAF/OMF export channel buffers * fix(engine): bound export audio channels and samples independently
This commit is contained in:
@@ -20,6 +20,9 @@ pub struct StreamResampler {
|
||||
next_out: Option<i64>,
|
||||
}
|
||||
|
||||
/// Maximum source frames fetched in a single resampler pull.
|
||||
pub const MAX_RESAMPLE_SOURCE_SPAN: usize = 384_000 * 10;
|
||||
|
||||
impl StreamResampler {
|
||||
/// Rates of zero are treated as 1 Hz rather than dividing by zero.
|
||||
pub fn new(from: u32, to: u32) -> Self {
|
||||
@@ -53,7 +56,7 @@ impl StreamResampler {
|
||||
}
|
||||
let held_end = self.held_start + self.held.first().map_or(0, Vec::len) as i64;
|
||||
if last + 1 > held_end {
|
||||
let want = (last + 1 - held_end) as usize;
|
||||
let want = ((last + 1 - held_end) as usize).min(MAX_RESAMPLE_SOURCE_SPAN);
|
||||
let got = pull(held_end, want);
|
||||
if self.held.is_empty() {
|
||||
self.held = vec![Vec::new(); got.len().max(1)];
|
||||
|
||||
@@ -130,6 +130,9 @@ fn dequantize(pcm: &[u8], channels: usize, bits: u16) -> Vec<Vec<f32>> {
|
||||
out
|
||||
}
|
||||
|
||||
const MAX_EXPORT_AUDIO_CHANNELS: usize = 64;
|
||||
const MAX_EXPORT_AUDIO_SAMPLES: usize = 64 * 1024 * 1024;
|
||||
|
||||
/// Decode a media range at `sample_rate` (planar).
|
||||
fn decode(s: &Session, need: &AudioNeed, sample_rate: u32) -> Result<Vec<Vec<f32>>> {
|
||||
let src = s
|
||||
@@ -138,7 +141,12 @@ fn decode(s: &Session, need: &AudioNeed, sample_rate: u32) -> Result<Vec<Vec<f32
|
||||
.ok_or_else(|| EngineError::Other(format!("media {} is offline", need.path.as_deref().unwrap_or("?"))))?;
|
||||
let start = need.start.to_units_floor(sample_rate as i64);
|
||||
let frames = (need.end.to_units_floor(sample_rate as i64) - start).max(0) as usize;
|
||||
let mut out: Vec<Vec<f32>> = vec![Vec::with_capacity(frames); need.channels.max(1) as usize];
|
||||
let ch = (need.channels.max(1) as usize).min(MAX_EXPORT_AUDIO_CHANNELS);
|
||||
let total_samples = ch.checked_mul(frames).ok_or_else(|| EngineError::Other("audio export buffer size overflow".into()))?;
|
||||
if total_samples > MAX_EXPORT_AUDIO_SAMPLES {
|
||||
return Err(EngineError::Other(format!("audio export buffer size {total_samples} samples exceeds maximum allowed ({MAX_EXPORT_AUDIO_SAMPLES})")));
|
||||
}
|
||||
let mut out: Vec<Vec<f32>> = vec![Vec::with_capacity(frames); ch];
|
||||
let chunk = sample_rate as usize * 10;
|
||||
let mut done = 0usize;
|
||||
while done < frames {
|
||||
|
||||
@@ -105,10 +105,11 @@ fn audio_rate_str(sr: u32) -> String {
|
||||
|
||||
fn parse_audio_rate(s: &str) -> Option<u32> {
|
||||
let s = s.trim();
|
||||
match s.strip_suffix('k') {
|
||||
let r = match s.strip_suffix('k') {
|
||||
Some(k) => k.parse::<f64>().ok().map(|v| (v * 1000.0).round() as u32),
|
||||
None => s.parse().ok(),
|
||||
}
|
||||
};
|
||||
r.filter(|&sr| sr > 0 && sr <= 384_000)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user