attempt to trim every part from the recording; also out of ram

This commit is contained in:
Dominic 2023-10-30 23:13:08 +01:00
parent 85297910ae
commit 164bb8faa3
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
2 changed files with 103 additions and 124 deletions

View file

@ -47,14 +47,12 @@ pub(crate) enum Filter {
output: Cow<'static, str>
},
/// Fast forward. Will output before, ff, and after parts separately.
/// Fast forward.
FastForward {
input: Cow<'static, str>,
ffinput: Cow<'static, str>,
start: Time,
duration: Time,
multiplier: usize,
output: [Cow<'static, str>; 3]
output: Cow<'static, str>
}
}
@ -199,90 +197,27 @@ impl Filter {
Self::FastForward {
input,
ffinput,
start,
duration,
multiplier,
output
} => {
let end = *start + *duration;
// ok so let's start by duplicating the audio and video 3 times
let vin = next_tmp_3(filter_idx);
let ain = next_tmp_3(filter_idx);
writeln!(
complex,
"{}split=3{}{}{};",
channel('v', input),
vin[0],
vin[1],
vin[2]
);
writeln!(
complex,
"{}asplit=3{}{}{};",
channel('a', input),
ain[0],
ain[1],
ain[2]
);
// next we cut those audio/videos into before, ff, after
let vcut = next_tmp_3(filter_idx);
let acut = next_tmp_3(filter_idx);
writeln!(complex, "{}trim=duration={start}{};", vin[0], vcut[0]);
writeln!(complex, "{}atrim=duration={start}{};", ain[0], acut[0]);
writeln!(
complex,
"{}trim=start={start}:duration={duration},setpts=PTS-STARTPTS{};",
vin[1], vcut[1]
);
writeln!(
complex,
"{}atrim=start={start}:duration={duration},asetpts=PTS-STARTPTS{};",
ain[1], acut[1]
);
writeln!(
complex,
"{}trim=start={end},setpts=PTS-STARTPTS{};",
vin[2], vcut[2]
);
writeln!(
complex,
"{}atrim=start={end},asetpts=PTS-STARTPTS{};",
ain[2], acut[2]
);
// now we speed up the ff part
let vff = next_tmp(filter_idx);
let aff = next_tmp(filter_idx);
writeln!(complex, "{}setpts=PTS/{multiplier}{vff};", vcut[1]);
writeln!(complex, "{}atempo={multiplier}{aff};", acut[1]);
// and we overlay the vff part
let voverlay = next_tmp(filter_idx);
writeln!(
complex,
"{vff}{}overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2{voverlay};",
channel('v', ffinput)
"{}setpts=PTS/{multiplier}{vff};",
channel('v', input)
);
writeln!(
complex,
"{}atempo={multiplier}{};",
channel('a', input),
channel('a', output)
);
writeln!(
complex,
"{vff}{}overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2{};",
channel('v', ffinput),
channel('v', output)
);
// and finally we place everything into the output
for (i, o) in [
(&vcut[0], &output[0]),
(&voverlay, &output[1]),
(&vcut[2], &output[2])
] {
write!(complex, "{i}null{};", channel('v', o));
}
writeln!(complex);
for (i, o) in [
(&acut[0], &output[0]),
(&aff, &output[1]),
(&acut[2], &output[2])
] {
write!(complex, "{i}anull{};", channel('a', o));
}
writeln!(complex);
}
}

View file

@ -34,6 +34,9 @@ const TRANSITION_LEN: Time = Time {
micros: 200_000
};
const FF_MULTIPLIER: usize = 8;
// logo sizes at full hd, will be scaled to source resolution
const FF_LOGO_SIZE: usize = 128;
const LOGO_SIZE: usize = 96;
fn cmd() -> Command {
let mut cmd = Command::new("busybox");
@ -227,7 +230,7 @@ impl<'a> Renderer<'a> {
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/logo.svg"))
)?;
let logo_png = self.target.join("logo.png");
svg2png(&logo_svg, &logo_png, 150 * source_res.width() / 1920)?;
svg2png(&logo_svg, &logo_png, LOGO_SIZE * source_res.width() / 1920)?;
// copy fastforward then render to png
let fastforward_svg = self.target.join("fastforward.svg");
@ -242,7 +245,7 @@ impl<'a> Renderer<'a> {
svg2png(
&fastforward_svg,
&fastforward_png,
128 * source_res.width() / 1920
FF_LOGO_SIZE * source_res.width() / 1920
)?;
Ok(())
@ -270,20 +273,85 @@ impl<'a> Renderer<'a> {
let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
let mut part1: Cow<'static, str> = intro.into();
let mut part2: Cow<'static, str> = rec.into();
let mut part3: Cow<'static, str> = outro.into();
// trim the recording
let rectrim = "rectrim";
// the recording is fun because of all the fast forwarding
let mut part2 = VecDeque::new();
let mut part2_start_of_the_end = None;
let mut part2_end_of_the_start = None;
// ok so ff is fun. we will add the ff'ed section as well as the part between
// the previous ff'ed section and our new section, unless we are the first
project.source.fast.sort();
for (i, (ff_st, ff_end)) in project.source.fast.iter().rev().enumerate() {
if let Some(prev_end) = part2_end_of_the_start {
let recffbetween = format!("recff{i}between");
ffmpeg.add_filter(Filter::Trim {
input: rec.clone().into(),
start: Some(*ff_end),
duration: Some(prev_end),
output: recffbetween.clone().into()
});
part2.push_front(recffbetween.into());
} else {
part2_start_of_the_end = Some(*ff_end);
}
part2_end_of_the_start = Some(*ff_st);
let recffpart = format!("recff{i}part");
ffmpeg.add_filter(Filter::Trim {
input: rec.clone().into(),
start: Some(*ff_st),
duration: Some(*ff_end - *ff_st),
output: recffpart.clone().into()
});
let recff = format!("recff{i}");
ffmpeg.add_filter(Filter::FastForward {
input: recffpart.into(),
ffinput: ff.clone().into(),
multiplier: FF_MULTIPLIER,
output: recff.clone().into()
});
part2.push_front(recff.into());
}
// if the recording was not ff'ed, perform a normal trim
let start = project.source.start.unwrap();
let duration = project.source.end.unwrap() - start;
ffmpeg.add_filter(Filter::Trim {
input: part2,
start: Some(start),
duration: Some(duration),
output: rectrim.into()
});
part2 = rectrim.into();
let end = project.source.end.unwrap();
let part2_last_part_duration;
if part2.is_empty() {
let rectrim = "rectrim";
part2_last_part_duration = end - start;
ffmpeg.add_filter(Filter::Trim {
input: rec.into(),
start: Some(start),
duration: Some(part2_last_part_duration),
output: rectrim.into()
});
part2.push_back(rectrim.into());
}
// otherwise add the first and last parts separately
else {
let rectrimst = "rectrimst";
ffmpeg.add_filter(Filter::Trim {
input: rec.clone().into(),
start: Some(start),
duration: Some(part2_end_of_the_start.unwrap() - start),
output: rectrimst.into()
});
part2.push_front(rectrimst.into());
let rectrimend = "rectrimend";
part2_last_part_duration = end - part2_start_of_the_end.unwrap();
ffmpeg.add_filter(Filter::Trim {
input: rec.into(),
start: Some(part2_start_of_the_end.unwrap()),
duration: Some(part2_last_part_duration),
output: rectrimend.into()
});
part2.push_back(rectrimend.into());
}
// fade out the intro
let introfade = "introfade";
@ -299,7 +367,7 @@ impl<'a> Renderer<'a> {
// fade in the recording
let recfadein = "recfadein";
ffmpeg.add_filter(Filter::Fade {
input: part2,
input: part2.pop_front().unwrap(),
direction: "in",
start: Time {
seconds: 0,
@ -308,18 +376,18 @@ impl<'a> Renderer<'a> {
duration: TRANSITION_LEN,
output: recfadein.into()
});
part2 = recfadein.into();
part2.push_front(recfadein.into());
// fade out the recording
let recfadeout = "recfadeout";
ffmpeg.add_filter(Filter::Fade {
input: part2,
input: part2.pop_back().unwrap(),
direction: "out",
start: duration - TRANSITION_LEN,
start: part2_last_part_duration - TRANSITION_LEN,
duration: TRANSITION_LEN,
output: recfadeout.into()
});
part2 = recfadeout.into();
part2.push_back(recfadeout.into());
// fade in the outro
let outrofade = "outrofade";
@ -335,32 +403,8 @@ impl<'a> Renderer<'a> {
});
part3 = outrofade.into();
// prepare list for concat
let mut parts = VecDeque::new();
parts.push_back(part2);
// fast-forward the requested parts in reverse order
project.source.fast.sort();
for (i, (ff_st, ff_end)) in project.source.fast.iter().rev().enumerate() {
let recff = [
format!("recff{i}b").into(),
format!("recff{i}ff").into(),
format!("recff{i}a").into()
];
ffmpeg.add_filter(Filter::FastForward {
input: parts.pop_front().unwrap(),
ffinput: ff.clone().into(),
start: *ff_st - start,
duration: *ff_end - *ff_st,
multiplier: FF_MULTIPLIER,
output: recff.clone()
});
parts.push_front(recff[2].clone());
parts.push_front(recff[1].clone());
parts.push_front(recff[0].clone());
}
// concatenate everything
let mut parts = part2;
parts.push_front(part1);
parts.push_back(part3);
let concat = "concat";