From 33e53b380ff1f404a705e5eb7ceb83381c8fb702 Mon Sep 17 00:00:00 2001 From: soupkitchenfilms <13426391+soupkitchenfilms@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:35:19 +1000 Subject: [PATCH] OTIO import: map ImageSequenceReference clip trims into media frame space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xSTUDIO clip active ranges are media-native file frames, but OTIO source_range is expressed in the media reference's available_range space. Conforms anchored to absolute timecode (EDL/CSV-derived OTIOs where available_range.start_time is the timecode-as-frames of the first file frame) imported with trims ~1.2M frames outside the media — every clip displayed the wrong frames and cross-track record sync was lost. Re-anchor ImageSequenceReference trims through start_frame: trim = start_frame + (source_start - available_start) This collapses to the previous verbatim behaviour whenever available_start == start_frame (xSTUDIO/NLE exports), and rescues timecode-anchored conforms. Verified frame-exact against a 25-clip 4-track production conform (record positions and cut-in frames match the generating lineup). Signed-off-by: soupkitchenfilms <13426391+soupkitchenfilms@users.noreply.github.com> --- src/timeline/src/timeline_actor.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/timeline/src/timeline_actor.cpp b/src/timeline/src/timeline_actor.cpp index a1c005e3d..933e5079f 100644 --- a/src/timeline/src/timeline_actor.cpp +++ b/src/timeline/src/timeline_actor.cpp @@ -616,11 +616,32 @@ void process_item( auto source_range = ii->source_range(); if (source_range) { + // SKF patch 0007: xstudio clip active ranges are MEDIA-NATIVE + // file frames (a trim of 1123 must re-import as 1123). OTIO + // source_range however is expressed in the media reference's + // available_range space, which TC-anchored conforms base at + // absolute timecode frames (e.g. 1205912), a million frames + // past the file numbering. Re-anchor ImageSequenceReference + // trims to the file numbering: + // trim = start_frame + (source_start - available_start) + // Collapses to verbatim when available_start == start_frame + // (xstudio / NukeStudio exports). + auto start_val = source_range->start_time().value(); + if (auto *isr = dynamic_cast( + ii->media_reference())) { + if (auto avail = isr->available_range()) { + start_val = static_cast(isr->start_frame()) + + (start_val - + avail->start_time() + .rescaled_to(source_range->start_time().rate()) + .value()); + } + } self->mail( active_range_atom_v, FrameRange( FrameRateDuration( - static_cast(source_range->start_time().value()), + static_cast(start_val), FrameRate(fps_to_flicks(source_range->start_time().rate()))), FrameRateDuration( static_cast(source_range->duration().value()),