Created
July 28, 2023 20:05
-
-
Save maurges/ea2ff3543770346464f61e69d5a9840f to your computer and use it in GitHub Desktop.
Rust ffmpeg benchmark
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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"] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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