use std::process::ExitCode;
use rff::Engine;
use crate::args::{self, Action};
pub fn run(args: Vec<String>) -> ExitCode {
let cli = match args::parse(&args) {
Ok(cli) => cli,
Err(err) => {
eprintln!("Error parsing options: {err}");
return ExitCode::FAILURE;
}
};
if !cli.hide_banner {
eprint!("{}", banner());
}
for warning in &cli.warnings {
eprintln!("[warning] {warning}");
}
let engine = Engine::new();
match cli.action {
Action::Version => {
print_version();
ExitCode::SUCCESS
}
Action::Help => {
print_help();
ExitCode::SUCCESS
}
Action::ListCodecs => {
print_codecs(&engine);
ExitCode::SUCCESS
}
Action::ListFormats => {
print_formats(&engine);
ExitCode::SUCCESS
}
Action::Transcode(spec) => {
if spec.inputs.is_empty() && spec.outputs.is_empty() {
print_usage();
return ExitCode::FAILURE;
}
match rff::transcode::run(&engine, &spec) {
Ok(report) => {
eprintln!(
"done — {} packet(s) written, {} frame(s) decoded",
report.packets_written, report.frames_decoded
);
ExitCode::SUCCESS
}
Err(err) => {
eprintln!("[error] {err}");
ExitCode::FAILURE
}
}
}
}
}
pub fn banner() -> String {
format!(
"remade_ffmpeg_rs {ver} (ffmpeg-compatible CLI) — Remade With Rust, by Mata Network\n\
Not affiliated with or endorsed by the FFmpeg project. \"FFmpeg\" is a trademark of Fabrice Bellard.\n",
ver = rff::VERSION,
)
}
pub fn print_version() {
println!("remade_ffmpeg_rs version {}", rff::VERSION);
println!("A clean-room, permissively-licensed media toolkit written in Rust.");
println!("Apache-2.0. https://github.com/Remade-With-Rust/remade_ffmpeg_rs");
}
fn print_help() {
println!("{}", banner());
println!("Usage: ffmpeg [options] [-i input]... [output options] output\n");
println!("Common options:");
println!(" -i FILE add an input file (repeatable)");
println!(" -f FMT force container format for the next input/output");
println!(" -c:v CODEC video codec for the output (e.g. h264, copy)");
println!(" -c:a CODEC audio codec for the output (e.g. opus, copy)");
println!(" -b:v / -b:a RATE target bitrate (e.g. 2M, 128k)");
println!(" -preset P encoder speed/quality (h264: fast|medium|slow)");
println!(" -profile P encoder profile (h264: baseline|main|high)");
println!(" -g N keyframe interval in frames");
println!(" -vf FILTERS video filter graph (e.g. scale=640:480, crop=W:H:X:Y)");
println!(" -map SPEC select streams: INPUT[:v|:a|:N] (repeatable)");
println!(" -y / -n overwrite / never overwrite the output");
println!(" -codecs list supported codecs");
println!(" -formats list supported container formats");
println!(" -hide_banner suppress the startup banner");
println!(" -version print version and exit");
}
fn print_usage() {
eprintln!("Usage: ffmpeg [options] [-i input]... output (try `ffmpeg -h`)");
}
fn print_codecs(engine: &Engine) {
println!("Codecs:");
println!(" D. = decoding supported");
println!(" .E = encoding supported");
println!(" -----");
let mut codecs: Vec<_> = engine.codecs.iter().collect();
codecs.sort_by_key(|c| c.name);
for codec in codecs {
let d = if codec.can_decode() { 'D' } else { '.' };
let e = if codec.can_encode() { 'E' } else { '.' };
println!(
" {d}{e} {:<9} {:<8} {}",
codec.media_type.to_string(),
codec.name,
codec.long_name
);
}
}
fn print_formats(engine: &Engine) {
println!("File formats:");
println!(" D. = demuxing supported");
println!(" .E = muxing supported");
println!(" -----");
let mut formats: Vec<_> = engine.formats.iter().collect();
formats.sort_by_key(|f| f.name);
for format in formats {
let d = if format.can_demux() { 'D' } else { '.' };
let e = if format.can_mux() { 'E' } else { '.' };
println!(" {d}{e} {:<8} {}", format.name, format.long_name);
}
}