Skip to content

Instantly share code, notes, and snippets.

@CL-Jeremy
Created August 2, 2020 14:18
Show Gist options
  • Save CL-Jeremy/1f897f685a0acd28b34d6570633c064a to your computer and use it in GitHub Desktop.
Save CL-Jeremy/1f897f685a0acd28b34d6570633c064a to your computer and use it in GitHub Desktop.
Test example for applying FPS filter and encoding GIF with rust-ffmpeg
extern crate ffmpeg;
use ffmpeg::*;
const INPUT: &str = "input.gif";
const OUTPUT: &str = "output.gif";
const FPS: f64 = 25.0;
fn main() {
init().map_err(|e| format!("Unable to initialize ffmpeg: {}", e)).unwrap();
let mut input_context = format::input(&INPUT)
.map_err(|e| format!("Unable to open input file: {}", e)).unwrap();
let mut output_context = format::output(&OUTPUT)
.map_err(|e| format!("Unable to open output file: {}", e)).unwrap();
let stream = input_context.streams().best(media::Type::Video)
.ok_or("The file has no video tracks").unwrap();
let stream_index = stream.index();
let mut decoder = stream.codec().decoder().video()
.map_err(|e| format!("Unable to decode the codec used in the video: {}", e)).unwrap();
let buffer_args = format!("width={}:height={}:pix_fmt={}:time_base={}:sar={}",
decoder.width(),
decoder.height(),
decoder.format().descriptor().unwrap().name(),
stream.time_base(),
(|sar: util::rational::Rational| match sar.numerator() {
0 => "1".to_string(),
_ => format!("{}/{}", sar.numerator(), sar.denominator()),
})(decoder.aspect_ratio()),
);
let mut filter = filter::Graph::new();
filter.add(&filter::find("buffer").unwrap(), "in", &buffer_args).unwrap();
filter.add(&filter::find("buffersink").unwrap(), "out", "").unwrap();
filter.output("in", 0).unwrap().input("out", 0).unwrap().parse(&format!("fps=fps={},format=bgr8", FPS)[..]).unwrap();
filter.validate().unwrap();
let codec = ffmpeg::encoder::find(output_context.format().codec(&OUTPUT, media::Type::Video))
.expect("Unable to find encoder").video().unwrap();
let mut output_stream = output_context.add_stream(codec).unwrap();
let mut encoder = output_stream.codec().encoder().video().unwrap();
encoder.set_format(format::Pixel::BGR8);
encoder.set_width(decoder.width());
encoder.set_height(decoder.height());
encoder.set_time_base((1, 100));
output_stream.set_time_base((1, 100));
let mut encoder = encoder.open_as(codec).unwrap();
output_stream.set_parameters(&encoder);
output_context.write_header().unwrap();
let mut i = 0;
let mut count = -1;
let mut min_pts = 0;
for (s, packet) in input_context.packets() {
if s.index() != stream_index {
continue;
}
let mut vid_frame = util::frame::video::Video::empty();
let decoded = decoder.decode(&packet, &mut vid_frame).unwrap();
if !decoded || 0 == vid_frame.width() {
continue;
}
count += 1;
let mut rgba_frame = util::frame::video::Video::empty();
let mut rgba_encoded = Packet::empty();
filter.get("in").unwrap().source().add(&vid_frame).unwrap();
while match filter.get("out").unwrap().sink().frame(&mut rgba_frame) {
Ok(_) => { eprintln!("Frame {} passed", count);
true },
Err(_) => { eprintln!("Frame {} failed", count);
false }
} {
if let Ok(true) = encoder.encode(&rgba_frame, &mut rgba_encoded) {
rgba_encoded.set_stream(0);
rgba_encoded.set_pts(Option::from(min_pts));
rgba_encoded.write_interleaved(&mut output_context).unwrap();
min_pts += (1.0 / FPS * 100.0) as i64;
i += 1;
}
}
}
count += 1;
let mut rgba_frame = util::frame::video::Video::empty();
let mut rgba_encoded = Packet::empty();
filter.get("in").unwrap().source().flush().unwrap();
while match filter.get("out").unwrap().sink().frame(&mut rgba_frame) {
Ok(_) => { eprintln!("Frame {} passed", count);
true },
Err(_) => { eprintln!("Frame {} failed", count);
false }
} {
if let Ok(true) = encoder.encode(&rgba_frame, &mut rgba_encoded) {
rgba_encoded.set_stream(0);
rgba_encoded.set_pts(Option::from(min_pts));
rgba_encoded.write_interleaved(&mut output_context).unwrap();
min_pts += (1.0 / FPS * 100.0) as i64;
i += 1;
}
}
println!("Total input frames: {}", count);
println!("Total output frames: {}", i);
println!("Total duration: {:.2}s", min_pts as f64 / 100.0 - 1.0 / FPS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment