Skip to content

Per-source compare offsets: engine exists, API access + persistence missing (builds on #93) #314

Description

@adam-ealovega

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:

  1. Hero-only: the attribute only addresses the key sub-playhead; there is no public way to set the offset of an arbitrary compare source.
  2. 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:

  1. A PlayheadActor handler for (media::source_offset_frames_atom, <media uuid | selection index>, offset) targeting any sub-playhead.
  2. 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.
  3. "Source Alignment Frames" writable in Manual mode and live-updated in all modes; Manual offsets restore from serialised sessions.
  4. 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).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions