attempt to trim every part from the recording; also out of ram
This commit is contained in:
parent
85297910ae
commit
164bb8faa3
2 changed files with 103 additions and 124 deletions
|
@ -47,14 +47,12 @@ pub(crate) enum Filter {
|
||||||
output: Cow<'static, str>
|
output: Cow<'static, str>
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Fast forward. Will output before, ff, and after parts separately.
|
/// Fast forward.
|
||||||
FastForward {
|
FastForward {
|
||||||
input: Cow<'static, str>,
|
input: Cow<'static, str>,
|
||||||
ffinput: Cow<'static, str>,
|
ffinput: Cow<'static, str>,
|
||||||
start: Time,
|
|
||||||
duration: Time,
|
|
||||||
multiplier: usize,
|
multiplier: usize,
|
||||||
output: [Cow<'static, str>; 3]
|
output: Cow<'static, str>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,90 +197,27 @@ impl Filter {
|
||||||
Self::FastForward {
|
Self::FastForward {
|
||||||
input,
|
input,
|
||||||
ffinput,
|
ffinput,
|
||||||
start,
|
|
||||||
duration,
|
|
||||||
multiplier,
|
multiplier,
|
||||||
output
|
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 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!(
|
writeln!(
|
||||||
complex,
|
complex,
|
||||||
"{vff}{}overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2{voverlay};",
|
"{}setpts=PTS/{multiplier}{vff};",
|
||||||
channel('v', ffinput)
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ const TRANSITION_LEN: Time = Time {
|
||||||
micros: 200_000
|
micros: 200_000
|
||||||
};
|
};
|
||||||
const FF_MULTIPLIER: usize = 8;
|
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 {
|
fn cmd() -> Command {
|
||||||
let mut cmd = Command::new("busybox");
|
let mut cmd = Command::new("busybox");
|
||||||
|
@ -227,7 +230,7 @@ impl<'a> Renderer<'a> {
|
||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/logo.svg"))
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/logo.svg"))
|
||||||
)?;
|
)?;
|
||||||
let logo_png = self.target.join("logo.png");
|
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
|
// copy fastforward then render to png
|
||||||
let fastforward_svg = self.target.join("fastforward.svg");
|
let fastforward_svg = self.target.join("fastforward.svg");
|
||||||
|
@ -242,7 +245,7 @@ impl<'a> Renderer<'a> {
|
||||||
svg2png(
|
svg2png(
|
||||||
&fastforward_svg,
|
&fastforward_svg,
|
||||||
&fastforward_png,
|
&fastforward_png,
|
||||||
128 * source_res.width() / 1920
|
FF_LOGO_SIZE * source_res.width() / 1920
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -270,20 +273,85 @@ impl<'a> Renderer<'a> {
|
||||||
let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
|
let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
|
||||||
|
|
||||||
let mut part1: Cow<'static, str> = intro.into();
|
let mut part1: Cow<'static, str> = intro.into();
|
||||||
let mut part2: Cow<'static, str> = rec.into();
|
|
||||||
let mut part3: Cow<'static, str> = outro.into();
|
let mut part3: Cow<'static, str> = outro.into();
|
||||||
|
|
||||||
// trim the recording
|
// the recording is fun because of all the fast forwarding
|
||||||
let rectrim = "rectrim";
|
let mut part2 = VecDeque::new();
|
||||||
let start = project.source.start.unwrap();
|
let mut part2_start_of_the_end = None;
|
||||||
let duration = project.source.end.unwrap() - start;
|
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 {
|
ffmpeg.add_filter(Filter::Trim {
|
||||||
input: part2,
|
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 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),
|
start: Some(start),
|
||||||
duration: Some(duration),
|
duration: Some(part2_last_part_duration),
|
||||||
output: rectrim.into()
|
output: rectrim.into()
|
||||||
});
|
});
|
||||||
part2 = 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
|
// fade out the intro
|
||||||
let introfade = "introfade";
|
let introfade = "introfade";
|
||||||
|
@ -299,7 +367,7 @@ impl<'a> Renderer<'a> {
|
||||||
// fade in the recording
|
// fade in the recording
|
||||||
let recfadein = "recfadein";
|
let recfadein = "recfadein";
|
||||||
ffmpeg.add_filter(Filter::Fade {
|
ffmpeg.add_filter(Filter::Fade {
|
||||||
input: part2,
|
input: part2.pop_front().unwrap(),
|
||||||
direction: "in",
|
direction: "in",
|
||||||
start: Time {
|
start: Time {
|
||||||
seconds: 0,
|
seconds: 0,
|
||||||
|
@ -308,18 +376,18 @@ impl<'a> Renderer<'a> {
|
||||||
duration: TRANSITION_LEN,
|
duration: TRANSITION_LEN,
|
||||||
output: recfadein.into()
|
output: recfadein.into()
|
||||||
});
|
});
|
||||||
part2 = recfadein.into();
|
part2.push_front(recfadein.into());
|
||||||
|
|
||||||
// fade out the recording
|
// fade out the recording
|
||||||
let recfadeout = "recfadeout";
|
let recfadeout = "recfadeout";
|
||||||
ffmpeg.add_filter(Filter::Fade {
|
ffmpeg.add_filter(Filter::Fade {
|
||||||
input: part2,
|
input: part2.pop_back().unwrap(),
|
||||||
direction: "out",
|
direction: "out",
|
||||||
start: duration - TRANSITION_LEN,
|
start: part2_last_part_duration - TRANSITION_LEN,
|
||||||
duration: TRANSITION_LEN,
|
duration: TRANSITION_LEN,
|
||||||
output: recfadeout.into()
|
output: recfadeout.into()
|
||||||
});
|
});
|
||||||
part2 = recfadeout.into();
|
part2.push_back(recfadeout.into());
|
||||||
|
|
||||||
// fade in the outro
|
// fade in the outro
|
||||||
let outrofade = "outrofade";
|
let outrofade = "outrofade";
|
||||||
|
@ -335,32 +403,8 @@ impl<'a> Renderer<'a> {
|
||||||
});
|
});
|
||||||
part3 = outrofade.into();
|
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
|
// concatenate everything
|
||||||
|
let mut parts = part2;
|
||||||
parts.push_front(part1);
|
parts.push_front(part1);
|
||||||
parts.push_back(part3);
|
parts.push_back(part3);
|
||||||
let concat = "concat";
|
let concat = "concat";
|
||||||
|
|
Loading…
Reference in a new issue