Skip to content

Conversation

@IsseW
Copy link
Member

@IsseW IsseW commented Dec 18, 2025

Related

  • Closes RR-3212
  • Closes RR-3163
    TODO: Maybe more video related issues?

What

Support out of order samples for video streams.

Implementation details

  • We can pre-allocate chunk samples from the rrd manifest, which are then filled in as that chunk arrives.
  • We no longer keep track of GOPs the same way, and no longer rely on stable gop indices. Instead we keep track of keyframes, and instead of storing indices into this we keep it sorted and binary search keyframes.
  • Which chunk and buffer a frame should use is now stored within the frame itself.

@IsseW IsseW added 📺 re_viewer affects re_viewer itself include in changelog feat-video anything video decoding, player, querying, data modelling of videos etc. labels Dec 18, 2025
@IsseW IsseW marked this pull request as draft December 18, 2025 18:44
@github-actions
Copy link

github-actions bot commented Dec 18, 2025

Web viewer built successfully.

Result Commit Link Manifest
2ce66fe https://rerun.io/viewer/pr/12277 +nightly +main

View image diff on kitdiff.

Note: This comment is updated whenever you push a commit.

@IsseW IsseW force-pushed the isse/out-of-order-video-stream branch from 3924ef3 to 86b40da Compare January 7, 2026 11:45
@IsseW
Copy link
Member Author

IsseW commented Jan 7, 2026

Since our lints are failing because of re_video depending on re_core_types. I'm considering either making chunk id a generic parameter used to identify source.

Or we could consider moving re_video out of util.

Resolved by using tuid instead of chunk id

@IsseW IsseW marked this pull request as ready for review January 8, 2026 08:45
@IsseW IsseW requested a review from Wumpf January 8, 2026 08:45
Copy link
Member

@Wumpf Wumpf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed everything except video_stream_cache.rs changes and feel like I need a break atm. Maybe also better if you talk me through that one.

We should now also be able to remove the chunk store hack for not compacting video stream, right? Grep for RR-3212 to see what I mean.

let end_video_time = re_video::Time::from_secs(end_time, timescale);

// Find the GOPs that contain our time range
let start_gop = video
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

below got renamed to end_keyframe. This is inconsistent now. should rename this as well

Comment on lines 169 to 173
let first_frame_pts = video_descr
.samples
.front()
.and_then(|s| s.sample())
.map_or(Time::ZERO, |s| s.presentation_timestamp);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not having a first_frame_pts is fine most of the time but may cause issues. We may want to come up with a different strategy of obtaining that offset 🤔
See large comment block in submit_chunk below why we need it in the first place

0 => std::time::Duration::ZERO,
1 => {
let first = self.samples.front()?;
let first = self.samples.iter().find_map(|s| s.sample())?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method documentation should point out how we handle absent samples and gaps in sample ranges

.iter_index_range_clamped(&seg.sample_range)
.map(|(_idx, sample)| sample.presentation_timestamp)
.iter()
.filter_map(|sample| Some(sample.sample()?.presentation_timestamp))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doc string should be updated to mention that reserved-only samples are simply ignored

.get(*p)
.map(|s| s.sample())
.inspect(|_s| {
debug_assert!(_s.is_some(), "Keyframes should always be loaded");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
debug_assert!(_s.is_some(), "Keyframes should always be loaded");
debug_assert!(_s.is_some(), "Keyframes mentioned in the keyframe lookup list should always be loaded");

because otherwise future-me would be confused about what magic is supposed to load all keyframes in existance

Comment on lines +516 to +521
let sample_range = video_description.keyframe_indices[keyframe_idx]
..video_description
.keyframe_indices
.get(keyframe_idx + 1)
.copied()
.unwrap_or_else(|| video_description.samples.next_index());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this "give me the GOP range starting at keyframe_idx X" happens quite a bit, worth considering having a utility for that if only to name the thing we're doing here

Comment on lines +957 to +958
/// The raw buffer this sample is in.
pub buffer: arrow::buffer::Buffer,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know why we arrived at this, but I don't particularly like having arrow buffer here:

  • it increases the ref count for that arrow buffer a lot
  • it drags in libarrow dependency, until now re_video didn't need any arrow at all to do its work
  • arrow buffer under the hood is already a byte span into a ref counted buffer, that makes the byte_span below kinda pointless. I.e. if we're using arrow buffers here we might as well only use arrow buffers

It also means that SampleMetadata is no longer really metadata but really forms the actual sample, which is what distinguished it previously from Chunk which had a copy of the data for transfer to the decoder. I figure at that point we might as well rename it to Sample then.

... so either we check again whether we can not depend on arrow here, keeping stable buffer indices around, or we go all in and drop byte_span, rename the thing and at a later point maybe even remove Chunk

Comment on lines +605 to +607
let Some(sample) = sample.sample() else {
continue;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we distinguish unloaded here and immediately treat things as live stream if we encounter unloaded samples?

}
}

struct VideoData {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this struct and why is it needed? Name doesn't really tell me what it is used for

struct VideoStreamCacheEntry {
used_this_frame: AtomicBool,
video_stream: Arc<RwLock<PlayableVideoStream>>,
known_chunk_offsets: BTreeMap<ChunkId, ChunkSamples>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this more like chunk sample ranges?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat-video anything video decoding, player, querying, data modelling of videos etc. include in changelog 📺 re_viewer affects re_viewer itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants