1use bytes::Bytes;
2
3use super::audio::PreparedAudio;
4
5#[derive(Clone)]
8pub struct Clip {
9 pub input: Bytes,
10 pub start: Option<f64>,
11 pub end: Option<f64>,
12}
13
14impl Clip {
15 pub fn new(input: impl Into<Bytes>) -> Self {
17 Self { input: input.into(), start: None, end: None }
18 }
19
20 pub fn trimmed(input: impl Into<Bytes>, start: Option<f64>, end: Option<f64>) -> Self {
22 Self { input: input.into(), start, end }
23 }
24}
25
26pub(super) fn trim_frame(sec: Option<f64>, fps: f64) -> Option<u64> {
29 sec.map(|s| (s.max(0.0) * fps).ceil() as u64)
30}
31
32pub(super) fn trim_audio(
38 audio: Option<&PreparedAudio>,
39 start: Option<f64>,
40 end: Option<f64>,
41) -> Option<PreparedAudio> {
42 let a = audio?;
43 if start.is_none() && end.is_none() {
44 return Some(a.clone());
45 }
46 let ticks_per_sec = a.info.timescale.max(1) as f64;
47 let start_tick = (start.unwrap_or(0.0).max(0.0) * ticks_per_sec) as u64;
48 let end_tick = end.map(|e| (e.max(0.0) * ticks_per_sec) as u64);
49 let mut acc: u64 = 0;
50 let mut kept = Vec::new();
51 for (payload, dur) in &a.samples {
52 let sample_start = acc;
53 acc += *dur as u64;
54 if sample_start < start_tick {
55 continue;
56 }
57 if end_tick.is_some_and(|et| sample_start >= et) {
58 break;
59 }
60 kept.push((payload.clone(), *dur));
61 }
62 Some(PreparedAudio { info: a.info.clone(), samples: kept, handling: a.handling.clone() })
63}