TL;DR: compare sources can't be given individual frame offsets that survive a selection change — even though the engine for it already exists in the codebase. We implemented the missing API + persistence layer, validated it in production, and have a PR up: #315.
Use case
Any compare where the sources don't share a time origin — for us, render vs source plate vs a shorter client reference (slates, differing heads, centered refs), but the need is generic. Each compare source needs its own frame offset on the shared playhead. #93 ("Compare Settings Bar") describes the UI for exactly this — a Compare Offset control and Sync Point selector — but has had no engineering activity since Feb 2024.
What we found in v1.3.0 (reading the source + live testing)
The per-source offset engine is already implemented:
- Each compare source runs on its own
SubPlayhead, which applies a per-source frame_offset_ in update_retiming() (with held first/last frames outside the range) — src/playhead/src/sub_playhead.cpp.
PlayheadActor::align_clip_frame_numbers() (driven by the "Auto Align" attribute) already computes and pushes offsets to every sub-playhead via media::source_offset_frames_atom, derived from first-frame timecode.
PlayheadBase exposes an integer attribute "Source Offset Frames" (src/playhead/src/playhead.cpp) which attribute_changed forwards to the hero sub-playhead. It is reachable from Python today and works — we verified live on 1.3.0 that setting it re-times the key source immediately.
Two gaps keep this from being usable as a per-source compare offset:
- Hero-only: the attribute only addresses the key sub-playhead; there is no public way to set the offset of an arbitrary compare source.
- No persistence: any selection / compare-mode change re-runs
align_clip_frame_numbers(), which recomputes or zeroes all offsets, so manually-set values are clobbered by the next click.
(Related observation while testing: with "Auto Align" active, the non-key side of a bare two-item compare is placed by index from the pair's start rather than by the same alignment as the key side unless all selected items span identical frame ranges — a per-source offset with persistence supersedes the workarounds this forces.)
We built it — PR attached
We implemented the small engine-reusing version of this and have been running it in production review sessions:
- A
PlayheadActor handler for (media::source_offset_frames_atom, <media uuid | selection index>, offset) targeting any sub-playhead.
- A new
"Manual" value for AutoAlignMode: offsets are remembered per media and re-applied by align_clip_frame_numbers() on every selection rebuild, so they survive selection and compare-mode changes.
"Source Alignment Frames" writable in Manual mode and live-updated in all modes; Manual offsets restore from serialised sessions.
- Thin Python API (
Playhead.set_source_offset_frames, auto_align_mode, source_alignment_frames).
Validated on production media (multi-source render/plate/reference lineups, image sequences, Windows builds of both v1.3.0 and current develop): offsets apply frame-accurately and persist through every bare-pair reselection. PR: #315.
The #93 settings-bar UI could then be layered on later; this issue is only about the API/persistence layer. Feedback on the preferred shape (atom signature vs. writable attribute) very welcome.
Environment
xSTUDIO v1.3.0 and develop (Windows builds), Python API via Connection().connect_remote_auto.
Cross-references: #93 (Compare Settings Bar), #166 (contact sheet align — same underlying need). The compare placement behaviors described above were verified with both image sequences (file-frame numbering) and QuickTimes (start timecode).
TL;DR: compare sources can't be given individual frame offsets that survive a selection change — even though the engine for it already exists in the codebase. We implemented the missing API + persistence layer, validated it in production, and have a PR up: #315.
Use case
Any compare where the sources don't share a time origin — for us, render vs source plate vs a shorter client reference (slates, differing heads, centered refs), but the need is generic. Each compare source needs its own frame offset on the shared playhead. #93 ("Compare Settings Bar") describes the UI for exactly this — a Compare Offset control and Sync Point selector — but has had no engineering activity since Feb 2024.
What we found in v1.3.0 (reading the source + live testing)
The per-source offset engine is already implemented:
SubPlayhead, which applies a per-sourceframe_offset_inupdate_retiming()(with held first/last frames outside the range) —src/playhead/src/sub_playhead.cpp.PlayheadActor::align_clip_frame_numbers()(driven by the "Auto Align" attribute) already computes and pushes offsets to every sub-playhead viamedia::source_offset_frames_atom, derived from first-frame timecode.PlayheadBaseexposes an integer attribute"Source Offset Frames"(src/playhead/src/playhead.cpp) whichattribute_changedforwards to the hero sub-playhead. It is reachable from Python today and works — we verified live on 1.3.0 that setting it re-times the key source immediately.Two gaps keep this from being usable as a per-source compare offset:
align_clip_frame_numbers(), which recomputes or zeroes all offsets, so manually-set values are clobbered by the next click.(Related observation while testing: with "Auto Align" active, the non-key side of a bare two-item compare is placed by index from the pair's start rather than by the same alignment as the key side unless all selected items span identical frame ranges — a per-source offset with persistence supersedes the workarounds this forces.)
We built it — PR attached
We implemented the small engine-reusing version of this and have been running it in production review sessions:
PlayheadActorhandler for(media::source_offset_frames_atom, <media uuid | selection index>, offset)targeting any sub-playhead."Manual"value forAutoAlignMode: offsets are remembered per media and re-applied byalign_clip_frame_numbers()on every selection rebuild, so they survive selection and compare-mode changes."Source Alignment Frames"writable in Manual mode and live-updated in all modes; Manual offsets restore from serialised sessions.Playhead.set_source_offset_frames,auto_align_mode,source_alignment_frames).Validated on production media (multi-source render/plate/reference lineups, image sequences, Windows builds of both v1.3.0 and current
develop): offsets apply frame-accurately and persist through every bare-pair reselection. PR: #315.The #93 settings-bar UI could then be layered on later; this issue is only about the API/persistence layer. Feedback on the preferred shape (atom signature vs. writable attribute) very welcome.
Environment
xSTUDIO v1.3.0 and
develop(Windows builds), Python API viaConnection().connect_remote_auto.Cross-references: #93 (Compare Settings Bar), #166 (contact sheet align — same underlying need). The compare placement behaviors described above were verified with both image sequences (file-frame numbering) and QuickTimes (start timecode).