Permalink
Browse files

More examples & documentation (#344)

* Improving documentation for time ranges.
* Adding examples for flattening & summarizing timing.
* Adding the 2017 SIGGRAPH slides PDF, which fell off in the doc refactor.
  • Loading branch information...
jminor committed Oct 23, 2018
1 parent 1c10fd9 commit a13284e8da4428347b822e11cb1c297225971200
Binary file not shown.
@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-
import os
import sphinx_rtd_theme
import opentimelineio
PACKAGE_TITLE = 'OpenTimelineIO'
PACKAGE_NAME = 'opentimelineio'
PACKAGE_DIR = 'opentimelineio'
AUTHOR_NAME = 'Pixar Animation Studios'
import opentimelineio
try:
RELEASE = opentimelineio.__version__
except AttributeError:
@@ -280,22 +278,22 @@ def run_apidoc(_):
argv = [
'--force',
'--no-toc',
# '--separate',
'--module-first',
'--output-dir',
'./api/modules',
'../opentimelineio'
# '--separate',
'--module-first',
'--output-dir',
'./api/modules',
'../opentimelineio'
] + ignore_paths
from sphinx.ext import apidoc
apidoc.main(argv)
def setup(app):
"""This method is a hook into the Sphinx builder system and injects the apidoc module into it so it runs autodoc
before running build.
"""This method is a hook into the Sphinx builder system and injects the
apidoc module into it so it runs autodoc before running build.
If you mess with this, you may not see any effect in a local build, this was added to get api documentation building
on the ReadTheDocs server.
If you mess with this, you may not see any effect in a local build, this
was added to get api documentation building on the ReadTheDocs server.
"""
app.connect('builder-inited', run_apidoc)
@@ -42,6 +42,7 @@ Tutorials
tutorials/feature-matrix
tutorials/otio-file-format-specification
tutorials/otio-timeline-structure
tutorials/time-ranges
tutorials/versioning-schemas
tutorials/wrapping-otio
tutorials/write-an-adapter
@@ -0,0 +1,149 @@
# 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.html).
## 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.
@@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# Copyright 2018 Pixar Animation Studios
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
import opentimelineio as otio
import sys
inputpath, outputpath = sys.argv[1:]
# Read the file
timeline = otio.adapters.read_from_file(inputpath)
video_tracks = timeline.video_tracks()
audio_tracks = timeline.audio_tracks()
print("Read {} video tracks and {} audio tracks.".format(
len(video_tracks),
len(audio_tracks)
))
# Take just the video tracks - and flatten them into one.
# This will trim away any overlapping segments, collapsing everything
# into a single track.
print("Flattening {} video tracks into one...".format(len(video_tracks)))
onetrack = otio.algorithms.flatten_stack(video_tracks)
# Now make a new empty Timeline and put that one Track into it
newtimeline = otio.schema.Timeline(name="{} Flattened".format(timeline.name))
newtimeline.tracks[:] = [onetrack]
# keep the audio track(s) as-is
newtimeline.tracks.extend(audio_tracks)
# ...and save it to disk.
print("Saving {} video tracks and {} audio tracks.".format(
len(newtimeline.video_tracks()),
len(newtimeline.audio_tracks())
))
otio.adapters.write_to_file(newtimeline, outputpath)
Oops, something went wrong.

0 comments on commit a13284e

Please sign in to comment.