# Time Ranges

## Overview

A Timeline and all of the Tracks and Stacks it contains work together to place the Clips, Gaps and Transitions relative to each other in time. You can think of this as a 1-dimensional coordinate system. Simple cases of assembling Clips into a Track will lay the Clips end-to-end, but more complex cases involve nesting, cross-dissolves, trimming, and speed-up/slow-down effects which can lead to confusion. In an attempt to make this easy to work with OpenTimelineIO uses the following terminology and API for dealing with time ranges.

Note: You probably also want to refer to [Timeline Structure](otio-timeline-structure.md).

## Clips

There are several ranges you might want from a Clip. For each of these, it is important to note which time frame (the 1-dimensional coordinate system of time) the range is relative to. We call these the “Clip time frame” and the “parent time frame” (usually the Clip’s parent Track).

### Ranges within the Clip and its media:

#### Clip.available_range()

The available_range() method on Clip returns a TimeRange that tells you how much media is available via the Clip’s media_reference. If a Clip points to a movie file on disk, then this should tell you how long that movie is and what timecode it starts at. For example: “wedding.mov” starts at timecode 01:00:00:00 and is 30 minutes long.

Note that available_range() may return None if the range is not known.

#### Clip.source_range

Setting the source_range property on a Clip will trim the Clip to only that range of media.

The source_range is specified in the Clip time frame.

Note that source_range may be None indicating that the Clip should use the full available_range() whatever that may be. If both source_range and available_range() are None, then the Clip is invalid. You need at least one of those.

Usually this will be a shorter segment than the available_range() but this is not a hard constraint. Some use cases will intentionally ask for a Clip that is longer (or starts earlier) than the available media as a way to request that new media (a newly animated take, or newly recorded audio) be made to fill the requested source_range.

#### Clip.trimmed_range()

This will return the Clip’s source_range if it is set, otherwise it will return the available_range(). This tells you how long the Clip is meant to be in its parent Track or Stack.

The trimmed_range() is specified in the Clip time frame.

#### Clip.visible_range()

This will return the same thing as trimmed_range() but also takes any adjacent Transitions into account. For example, a Clip that is trimmed to end at frame 10, but is followed by a cross-dissolve with out_offset of 5 frames, will have a visible_range() that ends at frame 15.

The visible_range() is specified in the Clip time frame.

#### Clip.duration()

This is the way to ask for the Clip’s “natural” duration. In oitoview or most common non-linear editors, this is the duration of the Clip you will see in the timeline user interface.

Clip.duration() is a convenience for Clip.trimmed_range().duration(). If you want a different duration, then you can ask for Clip.available_range().duration() or Clip.visible_range().duration() explicitly. This makes it clear in your code when you are asking for a different duration.

### Ranges of the Clip in its parent Track or Stack:

#### Clip.range_in_parent()

The answer to this depends on what type of the Clip’s parent. In the typical case, the Clip is inside a Track, so the Clip.range_in_parent() will give you the range within that Track where this Clip is visible. Each clip within the Track will have a start time that is directly after the previous clip’s end. So, given a Track with clipA and clipB in it, this is always true:

The range_in_parent() is specified in the parent time frame.

clipA.range_in_parent().end_time_exclusive() == clipB.range_in_parent().start_time

If the parent is a Stack, then range_in_parent() is less interesting. The start time will always have .value == 0 and the duration is the Clip’s duration. This means that the start of each clip in a Stack is aligned. If you want to shift them around, then use a Stack of Tracks (like the top-level Timeline has by default) and then you can use Gaps to shift the contents of each Track around.

#### Clip.trimmed_range_in_parent()

This is the same as Clip.range_in_parent() but trimmed to the parent source_range. In most cases the parent has a source_range of None, so there is no trimming, but in cases where the parent is trimmed, you may want to ask where a Clip is relative to the trimmed parent. In cases where the Clip is completely trimmed by the parent, the Clip.trimmed_range_in_parent() will be None.

The trimmed_range_in_parent() is specified in the parent time frame.

## Tracks

TODO.

## Markers

Markers can be attached to any Item (Clips, Tracks, Stacks, Gaps, etc.)

Each Marker has a marked_range which specifies the position and duration of the Marker relative to the object it is attached to.

The marked_range of a Marker on a Clip is in the Clip’s time frame (same as the Clip’s source_range, trimmed_range(), etc.)

The marked_range of a Marker on a Track is in the Track’s time frame (same as the Track’s source_range, trimmed_range(), etc.)

The marked_range.duration.value may be 0 if the Marker is meant to be a instantaneous moment in time, or some other duration if it spans a length of time.

## Transitions

TODO.

## Gaps

TODO.

## Stacks

TODO.

## Timelines

TODO.