1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use std::{
ffi::OsStr,
fmt, io,
process::{Command, CommandArgs, Stdio},
};
use crate::child::FfmpegChild;
/// A wrapper around [`std::process::Command`] with some convenient preset argument
/// sets and customization for ffmpeg specifically.
pub struct FfmpegCommand {
inner: Command,
}
impl FfmpegCommand {
//// Argument presets
/// Generate a procedural test video.
/// Equivalent to `ffmpeg -i lavfi -f testsrc`
///
/// [FFmpeg `testsrc` filter documentation](https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-colorchart_002c-colorspectrum_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc)
pub fn testsrc(&mut self) -> &mut Self {
self.args(&["-f", "lavfi", "-i", "testsrc"]);
self
}
/// Preset for emitting raw decoded video frames on stdout.
/// Equivalent to `-f rawvideo -pix_fmt rgb24 -`.
pub fn rawvideo(&mut self) -> &mut Self {
self.args(&["-f", "rawvideo", "-pix_fmt", "rgb24", "-"]);
self
}
/// Configure the ffmpeg command to produce output on stdout.
///
/// Synchronizes two changes:
/// 1. Pass `pipe:1` to the ffmpeg command ("output on stdout")
/// 2. Set the `stdout` field of the inner `Command` to `Stdio::piped()`
pub fn pipe_stdout(&mut self) -> &mut Self {
self.args(&["-"]);
self.inner.stdout(Stdio::piped());
self
}
/// Automatically applied in the constructor of `FfmpegCommand`.
/// Configures logging with a level and format expected by the log parser.
///
/// Equivalent to `ffmpeg -loglevel level+info`.
///
/// The `level` flag adds a prefix to all log messages with the log level in square brackets,
/// allowing the parser to distinguish between ambiguous messages like
/// warnings vs errors.
///
/// The `+info` flag enables the `info` log level, which is the default level.
///
/// If this settings is manually overridden, the log parser should still work,
/// but lose some semantic distinction between log levels.
fn set_expected_loglevel(&mut self) -> &mut Self {
self.args(&["-loglevel", "level+info"]);
self
}
//// `std::process::Command` passthrough methods
///
/// Adds an argument to pass to the program.
///
/// Identical to `arg` in [`std::process::Command`].
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.inner.arg(arg.as_ref());
self
}
/// Adds multiple arguments to pass to the program.
///
/// Identical to `args` in [`std::process::Command`].
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
for arg in args {
self.arg(arg.as_ref());
}
self
}
/// Returns an iterator of the arguments that will be passed to the program.
///
/// Identical to `get_args` in [`std::process::Command`].
pub fn get_args(&self) -> CommandArgs<'_> {
self.inner.get_args()
}
/// Spawn the ffmpeg command as a child process, wrapping it in a
/// `FfmpegChild` interface.
///
/// Identical to `spawn` in [`std::process::Command`].
pub fn spawn(&mut self) -> io::Result<FfmpegChild> {
self.inner.spawn().map(FfmpegChild::from_inner)
}
/// Print a command that can be copy-pasted to run in the terminal.
/// Requires `&mut self` so that it chains seamlessly with other methods in the interface.
pub fn print_command(&mut self) -> &mut Self {
println!("Command: {:?}", self.inner);
self
}
//// Constructors
pub fn new() -> Self {
Self::new_with_exe("ffmpeg")
}
pub fn new_with_exe<S: AsRef<OsStr>>(exe: S) -> Self {
// Configure `Command`
let mut inner = Command::new(&exe);
inner.stdin(Stdio::piped());
inner.stderr(Stdio::piped());
inner.stdout(Stdio::piped());
// Configure `FfmpegCommand`
let mut ffmpeg_command = Self { inner };
ffmpeg_command.set_expected_loglevel();
ffmpeg_command
}
//// Escape hatches
/// Escape hatch to access the inner `Command`.
pub fn as_inner(&mut self) -> &Command {
&self.inner
}
/// Escape hatch to mutably access the inner `Command`.
pub fn as_inner_mut(&mut self) -> &mut Command {
&mut self.inner
}
}
impl Default for FfmpegCommand {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for FfmpegCommand {
/// Format the program and arguments of a Command for display. Any
/// non-utf8 data is lossily converted using the utf8 replacement
/// character.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl From<Command> for FfmpegCommand {
/// Convert a `Command` into a `FfmpegCommand`, making no guarantees about the
/// validity of its configured arguments and stdio. For example,
/// `set_expected_loglevel()` is not automatically applied, which can have
/// unexpected effects on log parsing.
fn from(inner: Command) -> Self {
Self { inner }
}
}
impl Into<Command> for FfmpegCommand {
fn into(self) -> Command {
self.inner
}
}