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::{
|
||||
render::filter::channel,
|
||||
time::{format_time, Time}
|
||||
};
|
||||
use anyhow::bail;
|
||||
use camino::Utf8PathBuf as PathBuf;
|
||||
use camino::{Utf8Path as Path, Utf8PathBuf as PathBuf};
|
||||
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) 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 {
|
||||
cmd.arg("-f").arg("concat").arg("-safe").arg("0");
|
||||
}
|
||||
|
@ -45,7 +50,7 @@ impl FfmpegInput {
|
|||
if let Some(duration) = self.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
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> anyhow::Result<()> {
|
||||
let mut cmd = cmd();
|
||||
pub fn run(mut self, filter_graph: Option<&Path>) -> anyhow::Result<()> {
|
||||
let mut cmd = new_cmd();
|
||||
cmd.arg("ffmpeg").arg("-hide_banner").arg("-y");
|
||||
|
||||
// determine whether the video need to be re-encoded
|
||||
|
@ -120,7 +125,7 @@ impl Ffmpeg {
|
|||
}
|
||||
|
||||
// append all the inputs
|
||||
for i in self.inputs {
|
||||
for i in &self.inputs {
|
||||
i.append_to_cmd(&mut cmd);
|
||||
}
|
||||
|
||||
|
@ -149,6 +154,37 @@ impl Ffmpeg {
|
|||
} else {
|
||||
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("-map").arg("[v]");
|
||||
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 LOGO_SIZE: usize = 96;
|
||||
|
||||
fn cmd() -> Command {
|
||||
fn new_cmd() -> Command {
|
||||
let mut cmd = Command::new("busybox");
|
||||
cmd.arg("ash")
|
||||
.arg("-exuo")
|
||||
|
@ -49,7 +49,7 @@ fn cmd() -> Command {
|
|||
}
|
||||
|
||||
fn ffprobe() -> Command {
|
||||
let mut cmd = cmd();
|
||||
let mut cmd = new_cmd();
|
||||
cmd.arg("ffprobe")
|
||||
.arg("-v")
|
||||
.arg("error")
|
||||
|
@ -119,11 +119,11 @@ fn svg2mp4(svg: PathBuf, mp4: PathBuf, duration: Time) -> anyhow::Result<()> {
|
|||
});
|
||||
ffmpeg.set_filter_output("out");
|
||||
ffmpeg.set_duration(duration);
|
||||
ffmpeg.run()
|
||||
ffmpeg.run(None)
|
||||
}
|
||||
|
||||
fn svg2png(svg: &Path, png: &Path, size: usize) -> anyhow::Result<()> {
|
||||
let mut cmd = cmd();
|
||||
let mut cmd = new_cmd();
|
||||
let size = size.to_string();
|
||||
cmd.arg("inkscape")
|
||||
.arg("-w")
|
||||
|
@ -181,7 +181,7 @@ impl<'a> Renderer<'a> {
|
|||
..FfmpegInput::new(recording_txt)
|
||||
});
|
||||
ffmpeg.enable_loudnorm();
|
||||
ffmpeg.run()?;
|
||||
ffmpeg.run(None)?;
|
||||
|
||||
let width = ffprobe_video("stream=width", &recording_mp4)?.parse()?;
|
||||
let height = ffprobe_video("stream=height", &recording_mp4)?.parse()?;
|
||||
|
@ -425,7 +425,7 @@ impl<'a> Renderer<'a> {
|
|||
|
||||
// we're done :)
|
||||
ffmpeg.set_filter_output(overlay);
|
||||
ffmpeg.run()?;
|
||||
ffmpeg.run(Some(&self.target.join("filter_graph.png")))?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue