forked from rerun-io/rerun
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Native video support for AV1 (rerun-io#7557)
### What * Part of rerun-io#7298 ### What Supports native decoding of AV1 videos. Downsides: it is extremely slow in debug builds In release builds it is ok, but there is still A LOT of performance on the table. ### TODO before merging * rerun-io#7563 * [x] Re-add `nasm` to `pixi.toml`, because it is needed to compile `rav1d` * [x] Make the asm-features of `rav1d` opt-in so users don't need `nasm` to compile `rerun` * [x] Put it behind a feature flag, in case compiling `rav1d` is difficult on some platforms * [x] Fix error handling so we don't crash on bad videos * ~Fix performance of debug builds, if possible~ * `dav1d` is always fast, but `rav1d` is super-slow in debug builds * [x] Or show error message in debug builds * [x] Review moved mp4 demux code to see nothing was lost ### Proof https://github.com/user-attachments/assets/1b1a0a03-9fac-4fa2-bcae-7beb9591067c ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7557?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7557?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! * [x] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide - [PR Build Summary](https://build.rerun.io/pr/7557) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. --------- Co-authored-by: jprochazk <[email protected]> Co-authored-by: Andreas Reich <[email protected]>
- Loading branch information
1 parent
3b09ca6
commit 2ce1c82
Showing
28 changed files
with
2,240 additions
and
1,356 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
//! Decodes an mp4 with AV1 in it to a folder of images. | ||
#![allow(clippy::unwrap_used)] | ||
|
||
use std::{ | ||
fs::{File, OpenOptions}, | ||
io::Write as _, | ||
path::{Path, PathBuf}, | ||
sync::Arc, | ||
time::{Duration, Instant}, | ||
}; | ||
|
||
use indicatif::ProgressBar; | ||
use parking_lot::Mutex; | ||
|
||
use re_video::demux::mp4::load_mp4; | ||
|
||
fn main() { | ||
// frames <video.mp4> | ||
let args: Vec<_> = std::env::args().collect(); | ||
let Some(video_path) = args.get(1) else { | ||
println!("Usage: frames <video.mp4>"); | ||
return; | ||
}; | ||
let output_dir = PathBuf::new().join(Path::new(video_path).with_extension("")); | ||
|
||
println!("Decoding {video_path}"); | ||
|
||
let video = std::fs::read(video_path).expect("failed to read video"); | ||
let video = load_mp4(&video).expect("failed to load video"); | ||
|
||
println!( | ||
"{} {}x{}", | ||
video.segments.len(), | ||
video.config.coded_width, | ||
video.config.coded_height | ||
); | ||
|
||
let progress = ProgressBar::new(video.samples.len() as u64).with_message("Decoding video"); | ||
progress.enable_steady_tick(Duration::from_millis(100)); | ||
|
||
let frames = Arc::new(Mutex::new(Vec::new())); | ||
let mut decoder = re_video::decode::av1::Decoder::new("debug_name".to_owned(), { | ||
let frames = frames.clone(); | ||
let progress = progress.clone(); | ||
move |frame| { | ||
progress.inc(1); | ||
frames.lock().push(frame); | ||
} | ||
}); | ||
|
||
let start = Instant::now(); | ||
for sample in &video.samples { | ||
decoder.decode(video.get(sample).unwrap()); | ||
} | ||
|
||
decoder.flush(); | ||
drop(decoder); | ||
let end = Instant::now(); | ||
progress.finish(); | ||
|
||
let frames = frames.lock(); | ||
|
||
println!( | ||
"Decoded {} frames in {:.2}ms", | ||
frames.len(), | ||
end.duration_since(start).as_secs_f64() * 1000.0 | ||
); | ||
|
||
println!("Writing frames to {}", output_dir.display()); | ||
std::fs::create_dir_all(&output_dir).expect("failed to create output directory"); | ||
|
||
let width = num_digits(frames.len()); | ||
for (i, frame) in frames.iter().enumerate() { | ||
if let Ok(frame) = frame { | ||
let mut file = OpenOptions::new() | ||
.write(true) | ||
.create(true) | ||
.truncate(true) | ||
.open(output_dir.join(format!("{i:0width$}.ppm"))) | ||
.expect("failed to open file"); | ||
write_binary_ppm(&mut file, frame.width, frame.height, &frame.data); | ||
} | ||
} | ||
} | ||
|
||
fn num_digits(n: usize) -> usize { | ||
(n as f64).log10().floor() as usize + 1 | ||
} | ||
|
||
fn write_binary_ppm(file: &mut File, width: u32, height: u32, rgba: &[u8]) { | ||
let header = format!("P6\n{width} {height}\n255\n"); | ||
|
||
let mut data = Vec::with_capacity(header.len() + width as usize * height as usize * 3); | ||
data.extend_from_slice(header.as_bytes()); | ||
|
||
for rgba in rgba.chunks(4) { | ||
data.extend_from_slice(&[rgba[0], rgba[1], rgba[2]]); | ||
} | ||
|
||
file.write_all(&data).expect("failed to write frame data"); | ||
} |
Oops, something went wrong.