Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
4a10d10d47 |
2 changed files with 50 additions and 14 deletions
|
@ -1,12 +1,17 @@
|
||||||
use super::{cmd, filter::Filter};
|
use super::{filter::Filter, new_cmd};
|
||||||
use crate::{
|
use crate::{
|
||||||
render::filter::channel,
|
render::filter::channel,
|
||||||
time::{format_time, Time}
|
time::{format_time, Time}
|
||||||
};
|
};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use camino::Utf8PathBuf as PathBuf;
|
use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf};
|
||||||
use rational::Rational;
|
use rational::Rational;
|
||||||
use std::{borrow::Cow, fmt::Write as _, process::Command};
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
fmt::Write as _,
|
||||||
|
io::Write as _,
|
||||||
|
process::{Command, Stdio}
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct FfmpegInput {
|
pub(crate) struct FfmpegInput {
|
||||||
pub(crate) concat: bool,
|
pub(crate) concat: bool,
|
||||||
|
@ -29,7 +34,7 @@ impl FfmpegInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_to_cmd(self, cmd: &mut Command) {
|
fn append_to_cmd(&self, cmd: &mut Command) {
|
||||||
if self.concat {
|
if self.concat {
|
||||||
cmd.arg("-f").arg("concat").arg("-safe").arg("0");
|
cmd.arg("-f").arg("concat").arg("-safe").arg("0");
|
||||||
}
|
}
|
||||||
|
@ -45,7 +50,7 @@ impl FfmpegInput {
|
||||||
if let Some(duration) = self.duration {
|
if let Some(duration) = self.duration {
|
||||||
cmd.arg("-t").arg(format_time(duration));
|
cmd.arg("-t").arg(format_time(duration));
|
||||||
}
|
}
|
||||||
cmd.arg("-i").arg(self.path);
|
cmd.arg("-i").arg(&self.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +109,8 @@ impl Ffmpeg {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(mut self) -> anyhow::Result<()> {
|
pub fn run(mut self, filter_graph: Option<&Path>) -> anyhow::Result<()> {
|
||||||
let mut cmd = cmd();
|
let mut cmd = new_cmd();
|
||||||
cmd.arg("ffmpeg").arg("-hide_banner").arg("-y");
|
cmd.arg("ffmpeg").arg("-hide_banner").arg("-y");
|
||||||
|
|
||||||
// determine whether the video need to be re-encoded
|
// determine whether the video need to be re-encoded
|
||||||
|
@ -120,7 +125,7 @@ impl Ffmpeg {
|
||||||
}
|
}
|
||||||
|
|
||||||
// append all the inputs
|
// append all the inputs
|
||||||
for i in self.inputs {
|
for i in &self.inputs {
|
||||||
i.append_to_cmd(&mut cmd);
|
i.append_to_cmd(&mut cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +154,37 @@ impl Ffmpeg {
|
||||||
} else {
|
} else {
|
||||||
write!(complex, "{}null[v]", channel('v', &self.filters_output));
|
write!(complex, "{}null[v]", channel('v', &self.filters_output));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(filter_graph) = filter_graph {
|
||||||
|
let mut graph2dot = new_cmd();
|
||||||
|
graph2dot.stdin(Stdio::piped());
|
||||||
|
graph2dot.stdout(Stdio::piped());
|
||||||
|
graph2dot.arg("graph2dot");
|
||||||
|
let mut graph2dot = graph2dot.spawn()?;
|
||||||
|
|
||||||
|
let mut dot = new_cmd();
|
||||||
|
dot.stdin(Stdio::from(graph2dot.stdout.take().unwrap()));
|
||||||
|
dot.arg("dot").arg("-Tpng").arg("-o").arg(filter_graph);
|
||||||
|
let mut dot = dot.spawn()?;
|
||||||
|
|
||||||
|
let mut stdin = graph2dot.stdin.take().unwrap();
|
||||||
|
for (i, _) in self.inputs.iter().enumerate() {
|
||||||
|
writeln!(stdin, "nullsrc[{i}];[{i}]nullsink;");
|
||||||
|
}
|
||||||
|
writeln!(stdin, "{complex};")?;
|
||||||
|
writeln!(stdin, "[v]nullsink");
|
||||||
|
drop(stdin);
|
||||||
|
|
||||||
|
let status = graph2dot.wait()?;
|
||||||
|
if !status.success() {
|
||||||
|
bail!("graph2dot failed with status code {:?}", status.code());
|
||||||
|
}
|
||||||
|
let status = dot.wait()?;
|
||||||
|
if !status.success() {
|
||||||
|
bail!("graphviz dot failed with status code {:?}", status.code());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd.arg("-filter_complex").arg(complex);
|
cmd.arg("-filter_complex").arg(complex);
|
||||||
cmd.arg("-map").arg("[v]");
|
cmd.arg("-map").arg("[v]");
|
||||||
cmd.arg("-map").arg(channel('a', &self.filters_output));
|
cmd.arg("-map").arg(channel('a', &self.filters_output));
|
||||||
|
|
|
@ -38,7 +38,7 @@ const FF_MULTIPLIER: usize = 8;
|
||||||
const FF_LOGO_SIZE: usize = 128;
|
const FF_LOGO_SIZE: usize = 128;
|
||||||
const LOGO_SIZE: usize = 96;
|
const LOGO_SIZE: usize = 96;
|
||||||
|
|
||||||
fn cmd() -> Command {
|
fn new_cmd() -> Command {
|
||||||
let mut cmd = Command::new("busybox");
|
let mut cmd = Command::new("busybox");
|
||||||
cmd.arg("ash")
|
cmd.arg("ash")
|
||||||
.arg("-exuo")
|
.arg("-exuo")
|
||||||
|
@ -49,7 +49,7 @@ fn cmd() -> Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ffprobe() -> Command {
|
fn ffprobe() -> Command {
|
||||||
let mut cmd = cmd();
|
let mut cmd = new_cmd();
|
||||||
cmd.arg("ffprobe")
|
cmd.arg("ffprobe")
|
||||||
.arg("-v")
|
.arg("-v")
|
||||||
.arg("error")
|
.arg("error")
|
||||||
|
@ -119,11 +119,11 @@ fn svg2mp4(svg: PathBuf, mp4: PathBuf, duration: Time) -> anyhow::Result<()> {
|
||||||
});
|
});
|
||||||
ffmpeg.set_filter_output("out");
|
ffmpeg.set_filter_output("out");
|
||||||
ffmpeg.set_duration(duration);
|
ffmpeg.set_duration(duration);
|
||||||
ffmpeg.run()
|
ffmpeg.run(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn svg2png(svg: &Path, png: &Path, size: usize) -> anyhow::Result<()> {
|
fn svg2png(svg: &Path, png: &Path, size: usize) -> anyhow::Result<()> {
|
||||||
let mut cmd = cmd();
|
let mut cmd = new_cmd();
|
||||||
let size = size.to_string();
|
let size = size.to_string();
|
||||||
cmd.arg("inkscape")
|
cmd.arg("inkscape")
|
||||||
.arg("-w")
|
.arg("-w")
|
||||||
|
@ -181,7 +181,7 @@ impl<'a> Renderer<'a> {
|
||||||
..FfmpegInput::new(recording_txt)
|
..FfmpegInput::new(recording_txt)
|
||||||
});
|
});
|
||||||
ffmpeg.enable_loudnorm();
|
ffmpeg.enable_loudnorm();
|
||||||
ffmpeg.run()?;
|
ffmpeg.run(None)?;
|
||||||
|
|
||||||
let width = ffprobe_video("stream=width", &recording_mp4)?.parse()?;
|
let width = ffprobe_video("stream=width", &recording_mp4)?.parse()?;
|
||||||
let height = ffprobe_video("stream=height", &recording_mp4)?.parse()?;
|
let height = ffprobe_video("stream=height", &recording_mp4)?.parse()?;
|
||||||
|
@ -425,7 +425,7 @@ impl<'a> Renderer<'a> {
|
||||||
|
|
||||||
// we're done :)
|
// we're done :)
|
||||||
ffmpeg.set_filter_output(overlay);
|
ffmpeg.set_filter_output(overlay);
|
||||||
ffmpeg.run()?;
|
ffmpeg.run(Some(&self.target.join("filter_graph.png")))?;
|
||||||
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue