feat(inspector): File > Cut Replay -- cut a sub-range into a new .vtx - #46
Merged
Conversation
The timeline window derived frame time as frame_index / (total_frames / duration_seconds). On captures with recording stalls the average fps is far below the live rate, so displayed times drift by the total stalled time (observed: a frame stamped 13:54 wall clock displayed as ~6:47 on a capture with 643s of stalls at ~29fps live rate). Map frame -> elapsed seconds through the footer ReplayTimeData table instead: created_utc preferred, game_time fallback, walking back to the nearest stamped frame for frames recorded without a value. Files without a time table keep the old linear mapping. Applies to the current-time readout and the strip hover tooltip.
BuildDroppedFrameMap flags every frame whose wall-clock span from the previous stamped frame (footer created_utc, game_time fallback) exceeds 1.5x the expected interval, and estimates the missing frame count per gap. The timeline strip paints flagged frames red, the info panel shows a gap/missing-frame summary plus an editable "Drop FPS" expected rate (default 30), and the hover tooltip reports the gap duration and estimated missing frames. Files without a time table flag nothing.
…tional Each gap from the dropped-frame map is overlaid on the slider track as a red band positioned and sized by wall clock (start_seconds / duration), so band width reads as gap duration - unlike the frame strip, where a two-minute stall spans only a few bars.
The slider axis is now elapsed seconds over the full capture duration: the grab sits at the current frame's footer-table time, and dragging resolves the frame via binary search for the last stamp at or before the grabbed time (zero entries skipped; linear fallback without a table). Equal slider distance now means equal wall-clock time, and the red gap bands align with the grab. Inside a gap the grab resolves to the frame at the gap's start, snapping to it on release.
Captures never sustain their nominal rate, so gap detection now runs at entered_fps * 0.75 (30 -> 22.5 effective). The input label shows the factor and the resulting effective rate.
ReplayCutService plans a cut snapped to whole chunks (the chunks containing the requested first/last frame) and writes a new file: header block and kept chunks copied verbatim, footer rebuilt with frame numbering rebased to 0, the time table sliced (tick values stay absolute), gaps/segments filtered+rebased, and duration recomputed from the sliced UTC stamps. Timeline events are not carried over; the footer is written uncompressed (the reader sniffs zstd by magic). The Cut Replay window accepts the range as elapsed time, frames, or absolute UTC (ISO-8601 or unix seconds/ms/100ns ticks), previews the snapped chunks/frames/time/size live, and runs the copy on a worker thread with a Save As dialog (defaults to <name>_cut.vtx). Verified headless on a 16556-frame capture: mid cut (frames 5000-9999) and tail cut (16000-16555) both reopen cleanly with matching frame contents, sliced time tables, and absolute UTC stamps.
Time/Frame/UTC cut ranges are now exact: when a bound falls inside a chunk, that edge chunk is re-serialized with only the kept frames (frames copied via a private SDK reader, SaveChunk framing mirrored: length prefix, zstd-if-beneficial, xxHash64 of the on-disk payload) and its seek-table entry regenerated. Whole interior chunks still copy verbatim. A new "Chunk (whole chunks)" range mode keeps the previous snap-to-chunk behavior via start/end chunk indices, and the preview reports how many frames each edge rewrite drops. Verified headless on a real capture: mid-chunk cut 5100..9150 (both edges rewritten), single-chunk double-trim 5150..5160, chunk-aligned 5000..9999 (pure verbatim path), and a 301-frame cut in the single-frame-chunk tail zone -- all reopen cleanly with per-frame entity content-hash matches, contiguous rebased seek tables, and absolute UTC stamps intact.
onur-zenos
force-pushed
the
fix/inspector-timeline-utc
branch
from
July 27, 2026 10:19
c36c349 to
a7e2cc2
Compare
onur-zenos
force-pushed
the
feat/inspector-cut-replay
branch
from
July 27, 2026 10:19
8483cab to
fe101e8
Compare
virtexalejandro
pushed a commit
that referenced
this pull request
Jul 27, 2026
…#46) * feat(inspector): File > Cut Replay -- write a sub-range as a new .vtx * feat(inspector): exact-frame cuts with edge-chunk rewriting
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
File > Cut Replay... -- writes a new .vtx containing a sub-range of the loaded replay.
Range modes:
The live preview shows the exact frames/time/chunks/size and how many frames each edge rewrite drops. The copy runs on a worker thread with a Save As dialog (defaults to
<name>_cut.vtx).How
ReplayCutService(new):SerializeHeaderwould stampnow()as the recorded time, so verbatim is the faithful option.ChunkedFileSink::SaveChunkframing exactly: frames read through a private SDK reader,SerializeChunk, zstd only-if-beneficial (>=512 B, must shrink, level 10), u32 length prefix,XXH3_64bitschecksum of the on-disk payload, and a regenerated seek-table entry.FlatBuffersVtxPolicy::SerializeFooter: frame numbering rebased to 0,created_utc/game_timesliced with absolute tick values preserved, gaps/segments filtered+rebased, duration recomputed from the sliced UTC stamps. Timeline events are not carried over. Footer written uncompressed (the reader sniffs zstd by magic).Testing
Headless harness on a real 16,556-frame capture, comparing per-frame entity content hashes against the source and validating seek-table contiguity:
Also GUI-validated end to end: a real cut reopens cleanly in the inspector and vtx_cli with contiguous seek table, exact time-table sizes, preserved header uuid/timestamp, and absolute UTC stamps.
Stacked on #45
Based on
fix/inspector-timeline-utc(uses itsTimelineViewService::FrameToElapsedSeconds/FrameAtElapsedSeconds). Merge #45 first, then this diff is just the cut feature.