Skip to content

Instantly share code, notes, and snippets.

@maurges
Created July 28, 2023 20:05
Show Gist options
  • Save maurges/ea2ff3543770346464f61e69d5a9840f to your computer and use it in GitHub Desktop.
Save maurges/ea2ff3543770346464f61e69d5a9840f to your computer and use it in GitHub Desktop.
Rust ffmpeg benchmark
[package]
name = "ffmpeg-benchmark"
version = "0.1.0"
edition = "2021"
[dependencies]
ffmpeg-next = { version = "6.0.0", default-features = false, features = ["codec", "format", "software-scaling", "software-resampling"] }
/*
On my machine this gives 2000 fps with optimizations and 120 fps without.
This tells that either slint example fucked up somewhere, or that overhead for presenting a frame there is prohibiteiely high.
*/
fn main() {
let path = std::env::args().nth(1).expect("No path to video file given");
let mut input_context = ffmpeg_next::format::input(&path).unwrap();
let video_stream = input_context.streams().best(ffmpeg_next::media::Type::Video).unwrap();
let video_stream_index = video_stream.index();
let decoder_context = ffmpeg_next::codec::Context::from_parameters(video_stream.parameters()).unwrap();
let mut packet_decoder = decoder_context.decoder().video().unwrap();
let mut to_rgb_rescaler = None::<ffmpeg_next::software::scaling::Context>;
println!("Starting decoding");
let mut frames = 0_usize;
let mut prev_time = std::time::Instant::now();
for (stream, packet) in input_context.packets() {
if stream.index() != video_stream_index { continue; }
packet_decoder.send_packet(&packet).unwrap();
let mut decoded_frame = ffmpeg_next::util::frame::Video::empty();
while packet_decoder.receive_frame(&mut decoded_frame).is_ok() {
// build rescaler if frame size doesn't match
let to_rgb_rescaler = match to_rgb_rescaler {
None => {
to_rgb_rescaler = Some(rgba_rescaler_for_frame(&decoded_frame));
to_rgb_rescaler.as_mut().unwrap()
}
Some(ref mut r) if r.input().format != decoded_frame.format() => {
*r = rgba_rescaler_for_frame(&decoded_frame);
r
}
Some(ref mut r) => r
};
let mut rgb_frame = ffmpeg_next::util::frame::Video::empty();
to_rgb_rescaler.run(&decoded_frame, &mut rgb_frame).unwrap();
let _copied_frame = video_frame_to_pixel_buffer(&rgb_frame);
frames += 1;
let duration = prev_time.elapsed().as_micros() as f64;
prev_time = std::time::Instant::now();
let freq = (1_000_000_f64 / duration) as usize;
print!("\rFrame {}, fps {} ", frames, freq);
}
}
}
fn rgba_rescaler_for_frame(frame: &ffmpeg_next::util::frame::Video) -> ffmpeg_next::software::scaling::Context {
ffmpeg_next::software::scaling::Context::get(
frame.format(),
frame.width(),
frame.height(),
ffmpeg_next::format::Pixel::RGB24,
frame.width(),
frame.height(),
ffmpeg_next::software::scaling::Flags::BILINEAR,
)
.unwrap()
}
fn video_frame_to_pixel_buffer(
frame: &ffmpeg_next::util::frame::Video,
) -> Vec<u8> {
let width: usize = frame.width().try_into().unwrap();
let height: usize = frame.height().try_into().unwrap();
let mut pixel_buffer = Vec::new();
pixel_buffer.resize(width * height * 3, 0);
let input_line_iter = frame.data(0).chunks_exact(frame.stride(0));
let output_line_iter = pixel_buffer.chunks_mut(width * 3);
for (in_line, out_line) in input_line_iter.zip(output_line_iter) {
out_line.copy_from_slice(&in_line[..out_line.len()])
}
pixel_buffer
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment