Skip to content

Fix fabricated source duration in OTIO exports - #580

Open
nikhiljangid120 wants to merge 2 commits into
Breakthrough:mainfrom
nikhiljangid120:fix/otio-unknown-media-duration
Open

nikhiljangid120 wants to merge 2 commits into
Breakthrough:mainfrom
nikhiljangid120:fix/otio-unknown-media-duration

Conversation

@nikhiljangid120

@nikhiljangid120 nikhiljangid120 commented Oct 16, 2026 •

Copy link
Copy Markdown

Summary

Stop declaring every source media file as exactly 1,980 frames long in OTIO exports.

The existing ExternalReference.1.available_range always starts at zero and lasts 1,980 frames regardless of the source. This can declare exported clips (for example, frames 2,400–4,800) outside the media's available range.

Following the maintainer's review, write_scene_list_otio now accepts optional source_duration. When supplied, it exports the full source duration, starting at zero and rounded to six decimal places at the output frame rate. When unknown, the available range remains null, which OpenTimelineIO supports. A selected scene subset's final frame is never substituted for the source duration.

The CLI passes video_stream.duration. Each clip's source_range, frame rate, media target, and track layout remain unchanged. The optional argument is appended to preserve existing positional calls.

Regression coverage

  • 16 unknown-duration cases cover CFR/NTSC frame rates, audio enabled/disabled, empty scenes, short scenes, scenes beyond frame 1,980, and disjoint scene subsets.
  • 16 supplied-duration cases cover CFR/NTSC output and source timebases, audio enabled/disabled, duration beyond the selected scenes, and zero duration.
  • Check available ranges and unchanged source start/duration for every clip on each track.
  • Both CLI golden tests use the fixture's actual 1,980-frame source duration, including when exporting a scene subset.
  • Update the changelog with a reference to Fix fabricated source duration in OTIO exports #580.

Validation

Python 3.12.15 on Linux:

  • All 16 newly added supplied-duration cases failed against the previous PR head and pass after the revision.
  • Output-writer/CLI OTIO test selection: 76 passed.
  • Native OpenTimelineIO 0.18.1 deserialization and range checks: 24 combinations passed, preserving clip timing across known/unknown duration, CFR/NTSC timebases, zero duration, and audio/video tracks.
  • Repository-wide ruff check ., ruff format --check ., and git diff --check pass.
  • Broader suite: 493 passed, 3 skipped, 85 deselected. The 85 deselections comprise 83 default release-test exclusions plus test_fan_out_n1_matches_single_consumer and test_fan_out_frame_equality_across_consumers. A previous complete-suite attempt exceeded this sandbox's 4 GiB memory limit in the former test, which retains both full decoded videos. No claim of a fully passing complete suite.
  • Actual editor GUI import and other operating systems were not tested.

Prepared and tested with AI assistance. No claim of independent human line-by-line review.

@Breakthrough Breakthrough added this to the 0.7.3 milestone Oct 17, 2026

@Breakthrough Breakthrough left a comment •

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

First off, thanks for reporting this and submitting a PR!

It looks like the current iteration just removes the duration field entirely. Can we instead fill it in with the real source duration (when we have it available)?

write_scene_list_fcp7 already does this via the source_duration parameter, and the
CLI passes video_stream.duration to it in scenedetect/_cli/commands.py. Could you update the PR to:

  1. Add source_duration: FrameTimecode | None = None parameter to write_scene_list_otio, documented like the one in write_scene_list_fcp7.
  2. Write available_range with start_time 0 and duration = source_duration.seconds * fps, rounded the same way as the clip's source_range values.
  3. Pass source_duration=video_stream.duration from save_otio in scenedetect/_cli/commands.py. The golden tests in test_cli.py should then contain the real duration of goldeneye.mp4 instead of null.
  4. Add a test case that passes source_duration and checks the resulting range at both CFR and NTSC rates.

If the source duration happens to be None, keep available_range: null as you have now. I agree the last scene's end time isn't a good stand-in for a subset of scenes.

Thanks!

Edit: Also filed #580, feel free to reference that and add a changelog entry too.

@nikhiljangid120

Copy link
Copy Markdown
Author

Thanks @Breakthrough — updated in ba4f5a8 to follow your requested approach:

  • Added the optional, documented source_duration argument to write_scene_list_otio, after the existing arguments to preserve positional-call compatibility.
  • Known source duration now produces an available range starting at zero, with round(source_duration.seconds * fps, 6) as its duration. Unknown duration still emits null; selected scene endpoints are never used as a substitute.
  • save-otio passes video_stream.duration; both CLI golden tests now expect the fixture's actual 1,980-frame duration.
  • Added 16 parameterized cases covering CFR/NTSC output and source timebases, audio/video tracks, full duration beyond the selected scenes, and zero duration. Retained the 16 unknown-duration cases and updated the changelog with the PR reference.

Validation on Python 3.12.15:

  • All 16 new cases failed before the implementation and pass afterward.
  • Output-writer/CLI OTIO selection: 76 passed.
  • Broader suite: 493 passed, 3 skipped, 85 deselected. The deselections include 83 default release-test exclusions and the same two high-memory fan-out tests excluded previously; this is not a claim of a complete-suite pass.
  • Native OpenTimelineIO 0.18.1 deserialization/range checks: 24 combinations passed.
  • Repository-wide Ruff checks/formatting and git diff --check passed.

Actual editor GUI imports and other operating systems were not tested. Prepared and validated with AI assistance; no claim of independent human line-by-line review. Ready for another look whenever convenient.

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants