Skip to content

Commit

Permalink
Show a preview of videos when selected and hovered (#7490)
Browse files Browse the repository at this point in the history
### What
This is useful for quickly figuring out "what is this blob?" when
hovering.

I decided to just simply play the video on a loop with no time controls.


![image](https://github.com/user-attachments/assets/3cccef5a-5f28-4347-8de2-d6dea11e0104)


### 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/7490?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/7490?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/7490)
- [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`.
  • Loading branch information
emilk authored Sep 24, 2024
1 parent 921c217 commit 199f48a
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5230,7 +5230,7 @@ dependencies = [
[[package]]
name = "re_mp4"
version = "0.1.0"
source = "git+https://github.com/rerun-io/re_mp4?rev=b2c39c374ed33c6e377dc368d6eba3fe051c61e3#b2c39c374ed33c6e377dc368d6eba3fe051c61e3"
source = "git+https://github.com/rerun-io/re_mp4?rev=3236c76f9228cf6ab0b2bfb1b8f9ffcde975ea05#3236c76f9228cf6ab0b2bfb1b8f9ffcde975ea05"
dependencies = [
"byteorder",
"bytes",
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,8 @@ egui_tiles = { git = "https://github.com/rerun-io/egui_tiles", rev = "b2f5e23252
egui_commonmark = { git = "https://github.com/rerun-io/egui_commonmark", rev = "7a9dc755bfa351a3796274cb8ca87129b051c084" } # https://github.com/lampsitter/egui_commonmark/pull/65

# commit on `rerun-io/mp4` `master` branch: https://github.com/rerun-io/re_mp4/tree/master
# https://github.com/rerun-io/mp4/commit/b2c39c374ed33c6e377dc368d6eba3fe051c61e3
re_mp4 = { git = "https://github.com/rerun-io/re_mp4", rev = "b2c39c374ed33c6e377dc368d6eba3fe051c61e3" }
# https://github.com/rerun-io/mp4/commit/3236c76f9228cf6ab0b2bfb1b8f9ffcde975ea05
re_mp4 = { git = "https://github.com/rerun-io/re_mp4", rev = "3236c76f9228cf6ab0b2bfb1b8f9ffcde975ea05" }

# commit on `rerun-io/re_arrow2` `main` branch
# https://github.com/rerun-io/re_arrow2/commit/e4717d6debc6d4474ec10db8f629f823f57bad07
Expand Down
10 changes: 7 additions & 3 deletions crates/store/re_video/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use std::{collections::BTreeMap, ops::Range};

use itertools::Itertools;

pub use re_mp4::TrackKind;

pub type TrackId = u64;
pub use re_mp4::{TrackId, TrackKind};

/// Decoded video data.
#[derive(Clone)]
Expand Down Expand Up @@ -70,6 +68,12 @@ impl VideoData {
}
}

/// Duration of the video, in seconds.
#[inline]
pub fn duration_sec(&self) -> f64 {
self.duration.into_secs(self.timescale)
}

/// Duration of the video, in milliseconds.
#[inline]
pub fn duration_ms(&self) -> f64 {
Expand Down
49 changes: 39 additions & 10 deletions crates/viewer/re_data_ui/src/blob.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use re_renderer::external::re_video::VideoLoadError;
use re_renderer::{external::re_video::VideoLoadError, video::FrameDecodingResult};
use re_types::components::{Blob, MediaType};
use re_ui::{list_item::PropertyContent, UiExt};
use re_viewer_context::UiLayout;
Expand Down Expand Up @@ -105,15 +105,11 @@ pub fn blob_preview_and_save_ui(
image_preview_ui(ctx, ui, ui_layout, query, entity_path, image);
}
// Try to treat it as a video if treating it as image didn't work:
else {
let video_result = blob_row_id.map(|row_id| {
ctx.cache.entry(|c: &mut re_viewer_context::VideoCache| {
c.entry(row_id, blob, media_type.as_ref().map(|mt| mt.as_str()))
})
else if let Some(blob_row_id) = blob_row_id {
let video_result = ctx.cache.entry(|c: &mut re_viewer_context::VideoCache| {
c.entry(blob_row_id, blob, media_type.as_ref().map(|mt| mt.as_str()))
});
if let Some(video_result) = &video_result {
show_video_blob_info(ui, ui_layout, video_result);
}
show_video_blob_info(ctx.render_ctx, ui, ui_layout, &video_result);
}

if !ui_layout.is_single_line() && ui_layout != UiLayout::Tooltip {
Expand Down Expand Up @@ -155,6 +151,7 @@ pub fn blob_preview_and_save_ui(
}

fn show_video_blob_info(
render_ctx: Option<&re_renderer::RenderContext>,
ui: &mut egui::Ui,
ui_layout: UiLayout,
video_result: &Result<re_renderer::video::Video, VideoLoadError>,
Expand Down Expand Up @@ -211,7 +208,39 @@ fn show_video_blob_info(
}
});

// TODO(andreas): A mini video player at this point would be awesome!
if let Some(render_ctx) = render_ctx {
// Show a mini-player for the video:

// TODO(emilk): Some time controls would be nice,
// but the point here is not to have a nice viewer,
// but to show the user what they have selected
let timestamp_in_seconds = ui.input(|i| i.time) % video.data().duration_sec();
ui.ctx().request_repaint(); // TODO(emilk): schedule a repaint just in time for the next frame of video

let decode_stream_id = re_renderer::video::VideoDecodingStreamId(
egui::Id::new("video_miniplayer").value(),
);

if let Some(texture) =
match video.frame_at(render_ctx, decode_stream_id, timestamp_in_seconds) {
FrameDecodingResult::Ready(texture)
| FrameDecodingResult::Pending(texture) => Some(texture),

FrameDecodingResult::Error(err) => {
ui.error_label(&err.to_string());
None
}
}
{
crate::image::texture_preview_ui(
render_ctx,
ui,
ui_layout,
"video_preview",
re_renderer::renderer::ColormappedTexture::from_unorm_rgba(texture),
);
}
}
});
}
Err(err) => {
Expand Down
16 changes: 7 additions & 9 deletions crates/viewer/re_data_ui/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,33 +43,32 @@ pub fn image_preview_ui(
let annotations = crate::annotations(ctx, query, entity_path);
let debug_name = entity_path.to_string();
let texture = image_to_gpu(render_ctx, &debug_name, image, &image_stats, &annotations).ok()?;
texture_preview_ui(render_ctx, ui, ui_layout, entity_path, texture);
texture_preview_ui(render_ctx, ui, ui_layout, &debug_name, texture);
Some(())
}

/// Show the given texture with an appropriate size.
fn texture_preview_ui(
pub fn texture_preview_ui(
render_ctx: &re_renderer::RenderContext,
ui: &mut egui::Ui,
ui_layout: UiLayout,
entity_path: &re_log_types::EntityPath,
debug_name: &str,
texture: ColormappedTexture,
) {
if ui_layout.is_single_line() {
let preview_size = Vec2::splat(ui.available_height());
let debug_name = entity_path.to_string();
ui.allocate_ui_with_layout(
preview_size,
egui::Layout::centered_and_justified(egui::Direction::TopDown),
|ui| {
ui.set_min_size(preview_size);

match show_image_preview(render_ctx, ui, texture.clone(), &debug_name, preview_size)
match show_image_preview(render_ctx, ui, texture.clone(), debug_name, preview_size)
{
Ok(response) => response.on_hover_ui(|ui| {
// Show larger image on hover.
let hover_size = Vec2::splat(400.0);
show_image_preview(render_ctx, ui, texture, &debug_name, hover_size).ok();
show_image_preview(render_ctx, ui, texture, debug_name, hover_size).ok();
}),
Err((response, err)) => response.on_hover_text(err.to_string()),
}
Expand All @@ -86,10 +85,9 @@ fn texture_preview_ui(
.clamp(ui.available_width())
.at_most(16.0 * texture.texture.width().max(texture.texture.height()) as f32),
);
let debug_name = entity_path.to_string();
show_image_preview(render_ctx, ui, texture, &debug_name, preview_size).unwrap_or_else(
show_image_preview(render_ctx, ui, texture, debug_name, preview_size).unwrap_or_else(
|(response, err)| {
re_log::warn_once!("Failed to show texture {entity_path}: {err}");
re_log::warn_once!("Failed to show texture {debug_name}: {err}");
response
},
);
Expand Down

0 comments on commit 199f48a

Please sign in to comment.