Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support H.264 on native via user installed ffmpeg executable #7962

Merged
merged 35 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2e1d748
Initial ffmpeg H.264 support in frames example
emilk Oct 8, 2024
a06107e
Actual H.264 video playback inside of Rerun viewer
emilk Oct 8, 2024
770f24c
Fix timestamps and seeking
emilk Oct 9, 2024
ab3d52a
Code cleanup and better error handling
emilk Oct 9, 2024
b25a539
Better error handling
emilk Oct 9, 2024
32c5846
Improve log output and thread names
emilk Oct 9, 2024
32b6739
reduce ffmpeg decode delay
Wumpf Oct 25, 2024
19fe5d0
fix re_video example build
Wumpf Oct 25, 2024
e4d3740
add decode timestamp, nal header parsing, various comments, todo notes
Wumpf Oct 25, 2024
6aa8693
better timestamp syncing strategy?
Wumpf Oct 25, 2024
84d0a55
make ffmpeg an async decoder
Wumpf Oct 28, 2024
4897f19
set fps mode to passthrough
Wumpf Oct 28, 2024
f8c70fc
comments & debug output
Wumpf Oct 28, 2024
edceed6
crude reset implementation
Wumpf Oct 28, 2024
3f21b3d
fix frame reordering
Wumpf Oct 28, 2024
556910b
Merge remote-tracking branch 'origin/main' into andreas/ffmpeg-decoder-2
Wumpf Oct 30, 2024
67843f7
expose DTS through frame info
Wumpf Oct 30, 2024
3e59ee1
handle ffmpeg process lifecycle, handle backward seeks
Wumpf Oct 30, 2024
88b62a5
turn around scheduling & threading strategy again to make things snap…
Wumpf Oct 31, 2024
471e4c5
add access unit delimiter units
Wumpf Oct 31, 2024
4d2ebaf
fix long wait time for ffmpeg shutdown/reset
Wumpf Oct 31, 2024
8f71b5c
fixup webcodecs
Wumpf Oct 31, 2024
415bd09
document the meaning of a video sample better
Wumpf Oct 31, 2024
10abb11
fix lints, handle all ffmpeg events
Wumpf Oct 31, 2024
7e1fc90
The ultimate source on nal headers is unfortunately gone, no point in…
Wumpf Oct 31, 2024
b995197
remove old todo
Wumpf Oct 31, 2024
92c1c75
Merge remote-tracking branch 'origin/main' into andreas/ffmpeg-decoder-2
Wumpf Nov 4, 2024
2fdb06c
various improvement to ffmpeg error handling
Wumpf Nov 4, 2024
2858c00
more comment improvements
Wumpf Nov 4, 2024
5025f3b
utility method for writing to stream with mapped error
Wumpf Nov 4, 2024
8a43e24
hide banner (reduce log spam), easier to read frame info reordering
Wumpf Nov 4, 2024
53fff85
fix logspam on seeking
Wumpf Nov 4, 2024
6743ced
fix decode timestamp warning being the wrong way round
Wumpf Nov 4, 2024
696ea49
hover tooltips
Wumpf Nov 4, 2024
e2dc61e
make ffmpeg warnigns warn_once, fix swscalar message showing up a lot…
Wumpf Nov 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2370,6 +2370,15 @@ dependencies = [
"simd-adler32",
]

[[package]]
name = "ffmpeg-sidecar"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd1e249e0ceeb0f5c9f84a3c6941c3bde3ebc2815f4b94531a7e806af61c4c0"
dependencies = [
"anyhow",
]

[[package]]
name = "filetime"
version = "0.2.25"
Expand Down Expand Up @@ -6318,6 +6327,7 @@ dependencies = [
"criterion",
"crossbeam",
"econtext",
"ffmpeg-sidecar",
"indicatif",
"itertools 0.13.0",
"js-sys",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ econtext = "0.2" # Prints error contexts on crashes
ehttp = "0.5.0"
enumset = "1.0.12"
env_logger = { version = "0.10", default-features = false }
ffmpeg-sidecar = "1.1.2"
fixed = { version = "1.28", default-features = false }
flatbuffers = "23.0"
futures-channel = "0.3"
Expand Down
10 changes: 8 additions & 2 deletions crates/store/re_video/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ features = ["all"]


[features]
default = ["av1"]
default = ["av1", "ffmpeg"]

## Enable serialization for data structures that support it.
serde = ["dep:serde"]

## Native AV1 decoding.
av1 = ["dep:dav1d"]

## Decode H.264 using ffmpeg over CLI.
ffmpeg = ["dep:ffmpeg-sidecar"]

## Enable faster native video decoding with assembly.
## You need to install [nasm](https://nasm.us/) to compile with this feature.
nasm = [
Expand All @@ -49,9 +52,11 @@ econtext.workspace = true
itertools.workspace = true
parking_lot.workspace = true
re_mp4.workspace = true
serde = { workspace = true, optional = true }
thiserror.workspace = true

ffmpeg-sidecar = { workspace = true, optional = true }
serde = { workspace = true, optional = true }

# We enable re_rav1d on native, UNLESS we're on Linux Arm64
# See https://github.com/rerun-io/rerun/issues/7755
[target.'cfg(all(not(target_arch = "wasm32"), not(all(target_os = "linux", target_arch = "aarch64"))))'.dependencies]
Expand Down Expand Up @@ -83,6 +88,7 @@ web-sys = { workspace = true, features = [
] }

[dev-dependencies]
# For the `frames` example:
indicatif.workspace = true
criterion.workspace = true

Expand Down
1 change: 1 addition & 0 deletions crates/store/re_video/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ fn main() {
native: { not(target_arch = "wasm32") },
linux_arm64: { all(target_os = "linux", target_arch = "aarch64") },
with_dav1d: { all(feature = "av1", native, not(linux_arm64)) }, // https://github.com/rerun-io/rerun/issues/7755
with_ffmpeg: { all(feature= "ffmpeg", native) }
}
}
42 changes: 34 additions & 8 deletions crates/store/re_video/examples/frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use indicatif::ProgressBar;
use parking_lot::Mutex;

fn main() {
re_log::setup_logging();

// frames <video.mp4>
let args: Vec<_> = std::env::args().collect();
let Some(video_path) = args.get(1) else {
Expand Down Expand Up @@ -83,13 +85,20 @@ fn main() {
.create(true)
.truncate(true)
.open(output_dir.join(format!("{i:0width$}.ppm")))
.expect("failed to open file");
write_binary_ppm(
&mut file,
frame.content.width,
frame.content.height,
&frame.content.data,
);
.expect("failed to oformatpen file");

let frame = &frame.content;
match frame.format {
re_video::PixelFormat::Rgb8Unorm => {
write_ppm_rgb24(&mut file, frame.width, frame.height, &frame.data);
}
re_video::PixelFormat::Rgba8Unorm => {
write_ppm_rgba32(&mut file, frame.width, frame.height, &frame.data);
}
re_video::PixelFormat::Yuv { .. } => {
re_log::error_once!("YUV frame writing is not supported");
}
}
}
}
}
Expand All @@ -98,7 +107,24 @@ 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]) {
fn write_ppm_rgb24(file: &mut File, width: u32, height: u32, rgb: &[u8]) {
assert_eq!(width as usize * height as usize * 3, rgb.len());

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 rgb in rgb.chunks(3) {
data.extend_from_slice(&[rgb[0], rgb[1], rgb[2]]);
}

file.write_all(&data).expect("failed to write frame data");
}

fn write_ppm_rgba32(file: &mut File, width: u32, height: u32, rgba: &[u8]) {
assert_eq!(width as usize * height as usize * 4, rgba.len());

let header = format!("P6\n{width} {height}\n255\n");

let mut data = Vec::with_capacity(header.len() + width as usize * height as usize * 3);
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_video/src/decode/async_decoder_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl AsyncDecoderWrapper {
let comms = Comms::default();

let thread = std::thread::Builder::new()
.name("av1_decoder".into())
.name(format!("decoder of {debug_name}"))
.spawn({
let comms = comms.clone();
move || {
Expand Down
5 changes: 3 additions & 2 deletions crates/store/re_video/src/decode/av1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ impl SyncDav1dDecoder {
re_tracing::profile_function!();
econtext::econtext_function_data!(format!(
"chunk timestamp: {:?}",
chunk.composition_timestamp
chunk.presentation_timestamp
));

re_tracing::profile_scope!("send_data");
match self.decoder.send_data(
chunk.data,
None,
Some(chunk.composition_timestamp.0),
Some(chunk.presentation_timestamp.0),
Some(chunk.duration.0),
) {
Ok(()) => {}
Expand Down Expand Up @@ -255,6 +255,7 @@ fn create_frame(debug_name: &str, picture: &dav1d::Picture) -> Result<Frame> {
info: FrameInfo {
presentation_timestamp: Time(picture.timestamp().unwrap_or(0)),
duration: Time(picture.duration()),
..Default::default()
},
})
}
Expand Down
Loading
Loading