use std::error::Error;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use atome::export::{to_flac, to_flac_into, FlacSummary};
use pfac::{BundleOptions, BundleWriter};
use vtome::transcode::{transcode, transcode_into, Settings, Summary};
fn main() -> Result<(), Box<dyn Error>> {
let mut arguments = std::env::args().skip(1);
let Some(input) = arguments.next().map(PathBuf::from) else {
eprintln!("usage: split_audio <video> [output directory]");
eprintln!(" make split-audio FILE=camera.mov");
return Ok(());
};
let out = arguments
.next()
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/example/split"));
fs::create_dir_all(&out)?;
println!("{}\n", input.display());
by_path(&input, &out)?;
println!();
by_writer(&input, &out)?;
Ok(())
}
fn by_path(input: &Path, out: &Path) -> Result<(), Box<dyn Error>> {
println!("by path -> {}", out.display());
let video = out.join("video.mp4");
let summary = transcode(
input,
&video,
&Settings::default(),
progress(" video"),
&AtomicBool::new(false),
)?;
println!("\r{}", describe_video(&summary, &video));
let audio = out.join("audio.flac");
match to_flac(input, &audio) {
Ok(summary) => println!(" audio {}", describe_audio(&summary, &audio)),
Err(error) => {
let _ = fs::remove_file(&audio);
println!(" audio none ({error})");
}
}
Ok(())
}
fn by_writer(input: &Path, out: &Path) -> Result<(), Box<dyn Error>> {
let bundle_path = out.join("project.pfac");
println!("by writer -> {}", bundle_path.display());
let mut bundle = BundleWriter::create(&bundle_path, &BundleOptions::stored())?;
let mut entry = bundle.entry("video/clip.mp4")?;
let summary = transcode_into(
input,
&mut entry,
&Settings::default(),
progress(" video"),
&AtomicBool::new(false),
)?;
let bytes = entry.finish()?;
println!(
"\r video video/clip.mp4 {} ({bytes} bytes in the bundle)",
describe(&summary)
);
let mut entry = bundle.entry("audio/clip.flac")?;
match to_flac_into(input, &mut entry) {
Ok(summary) => {
let bytes = entry.finish()?;
println!(
" audio audio/clip.flac {} ({bytes} bytes in the bundle)",
describe_flac(&summary)
);
}
Err(error) => println!(" audio none ({error})"),
}
bundle.finish()?;
let bundle = pfac::Bundle::open(&bundle_path)?;
println!(" bundle holds {:?}", bundle.names().collect::<Vec<_>>());
Ok(())
}
fn progress(label: &'static str) -> impl FnMut(vtome::transcode::Progress) {
move |progress| {
print!("\r{label} {:>3.0}%", progress.fraction * 100.0);
let _ = std::io::Write::flush(&mut std::io::stdout());
}
}
fn describe(summary: &Summary) -> String {
format!(
"{} in {}, {}x{} at {:.2} fps, {}-bit, {} frames, {:.1} s{}",
summary.encoding,
summary.container,
summary.width,
summary.height,
summary.frame_rate.as_f64(),
summary.bit_depth,
summary.frames,
summary.duration.as_secs_f64(),
if summary.encoder_hardware { ", hardware encoder" } else { "" },
)
}
fn describe_video(summary: &Summary, path: &Path) -> String {
format!(" video {} {}", path.display(), describe(summary))
}
fn describe_flac(summary: &FlacSummary) -> String {
format!(
"{} Hz, {} ch, {}-bit, {} frames per channel, {:.1} s",
summary.sample_rate,
summary.channels,
summary.bits_per_sample,
summary.frames,
summary.duration(),
)
}
fn describe_audio(summary: &FlacSummary, path: &Path) -> String {
format!("{} {}", path.display(), describe_flac(summary))
}