use ez_ffmpeg::frame_export::{FrameExtractor, Sampling, VideoFrame};
use ez_ffmpeg::{FfmpegContext, Input, Output};
use std::fs::File;
use std::io::{BufWriter, Write};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = match std::env::args().nth(1) {
Some(path) => path,
None => {
println!(
"usage: keyframe_thumbnails [input-file] (generating keyframe_source.mp4 fallback)"
);
generate_source("keyframe_source.mp4")?;
"keyframe_source.mp4".to_string()
}
};
let mut exported = 0u64;
for frame in FrameExtractor::new(path.as_str())
.sampling(Sampling::KeyframesOnly)
.width(160) .frames()?
{
let frame = frame?;
let name = format!("keyframe_{:03}.ppm", frame.index());
write_ppm(&name, &frame)?;
println!(
"keyframe {:>2}: pts={:>9?}us -> {}",
frame.index(),
frame.pts_us(),
name
);
exported += 1;
}
println!("exported {exported} keyframe thumbnail(s)");
Ok(())
}
fn generate_source(path: &str) -> Result<(), Box<dyn std::error::Error>> {
FfmpegContext::builder()
.input(Input::from("testsrc2=duration=12:size=640x360:rate=10").set_format("lavfi"))
.output(
Output::from(path)
.set_video_codec("mpeg4")
.set_video_codec_opt("g", "10"),
)
.build()?
.start()?
.wait()?;
Ok(())
}
fn write_ppm(path: &str, frame: &VideoFrame) -> std::io::Result<()> {
let mut w = BufWriter::new(File::create(path)?);
write!(w, "P6\n{} {}\n255\n", frame.width(), frame.height())?;
w.write_all(frame.as_bytes())?;
w.flush()
}