Files
craft-libs/crates/timeline
Dylan M. Taylor 6cc226e454 craft-timeline: OpenTimelineIO on the neutral model (#1002)
* craft-timeline: a neutral timeline model and the import builder

model: a Project of bins and items (media, sequences, subclips, and items no
interchange format carries), sequences of tracks with clips, transitions,
markers and effects with keyframes. Cut down from FilmCraft's project model to
what Final Cut Pro 7 XML, FCPXML and OpenTimelineIO describe; EffectCraft
already uses that shape for its interchange. Three things stay the
application's: effects (looked up through a Catalog the caller supplies),
generators beyond the common ones (Generator::Other with an opaque payload),
and per-object extension JSON (ext) that a format can carry unread.

fx: the effect and parameter ids the formats name themselves, and Standard,
a catalog of just those.

build: FilmCraft's import Builder and shared helpers (filmcraft-interchange
common.rs at 4bec22bd) on the model, with the effect registry replaced by the
catalog, and the bounds every import ends with (Imported::bound, from lib.rs).

* craft-timeline: Final Cut Pro 7 XML (xmeml) on the neutral model

fcp7: FilmCraft's xmeml reader and writer (filmcraft-interchange fcp7.rs at
4bec22bd) moved onto craft_timeline::model, behind feature xml.

- XML is read with craft-xml, strictly: at most 256 levels of nesting (the
  limit EffectCraft checked with its own depth scan before handing documents
  over), 2 000 000 nodes, 256 MiB; a document type declaration with an internal
  subset and entities XML does not define are refused. It is written with
  craft-xml's Writer.
- Effects other than motion, opacity, time remap, audio levels and pan are
  looked up through the caller's Catalog, by id and by name, and written with
  the catalog's stable name and category.
- An application generator round-trips as <namespace>:<payload> in effectid
  (FilmCraft wrote filmcraft:<json>); transparent video, counting leader and
  bars and tone are written by name, which the reader already took.
- Finding the sequence a clip item is in is one walk of the document instead of
  an ancestors() lookup per clip item.

tests/it: FilmCraft's FCP7 integration tests (the hand-written Premiere-style
document, the rich round trip, the 48-case property round trip, split mono and
stereo channels, sub-frame spans, absurd timebases and start timecodes, the
link-group bound) on the model, with a test catalog for the four application
effects they use.

* craft-timeline: FCPXML on the neutral model

fcpxml: FilmCraft's FCPXML 1.9-1.11 reader and writer (filmcraft-interchange
fcpxml.rs at 4bec22bd) moved onto craft_timeline::model, behind feature xml.
XML is read with craft-xml as fcp7 is; filters other than the transform,
blend and volume adjustments go through the caller's Catalog by name, and an
effect resource's uid is <namespace>.<effect id> (FilmCraft wrote
filmcraft.<id>). ExportOptions gains fcpxml_version.

tests/it/fcpxml.rs: FilmCraft's FCPXML integration tests on the model (the
hand-written connected-clips document, hostile lane numbers, standalone video
stills, self-referencing compound media, round trips at 1.9, 1.10 and 1.11,
the property round trip with its regression seed, percent-decoded relative
URLs). The FCP7 tests export to FCPXML again where FilmCraft's did.

* craft-timeline: OpenTimelineIO on the neutral model

otio: FilmCraft's OTIO reader and writer (filmcraft-interchange otio.rs at
4bec22bd) moved onto craft_timeline::model, behind feature otio (serde_json).

FilmCraft wrote its own serde types into metadata.filmcraft. Here the model's
fields are written as JSON by hand under metadata.<namespace>, with the same
keys and value shapes FilmCraft's serde produced (labels, settings, media
info, generators, effects with externally tagged parameter values, alignment,
marker kinds, channel layouts), so files FilmCraft wrote read back. Every key
the reader does not know comes back in the object's ext, and ext's keys are
written next to the model's: an application's palette, track volume, masks and
the like round-trip without the crate knowing them. ParamValue::Opaque and
Generator::Other carry values only the application reads.

The model's Sequence loses its ext: a sequence's is its item's, so there is
one place for it. ImportOptions gains name (for an unnamed timeline), as
FilmCraft's had.

tests: FilmCraft's OTIO integration tests on the model (the golden document,
the lossless round trip now through ext, collections and bare tracks, items at
an audio sample rate, generator labels and identity, the property round trip,
marker URLs, percent-decoded relative URLs); its palette tests stay with
FilmCraft, whose palette merge they test, and are replaced by one ext round
trip. The OTIO unit tests (exact rational times, the nested-copies limit) and
a JSON nesting test.
2026-11-11 20:24:37 -05:00
..

craft-timeline

Editorial timeline interchange: the parts every reader and writer of an edit shares, and the text formats that are documents of their own.

Module What
time Exact media time. A Tick is 1/254 016 000 000 s, which divides evenly into every broadcast frame duration and every common audio sample duration; FrameRate is an exact rational; SMPTE timecode (drop and non-drop), feet+frames and typed-timecode parsing are displays of ticks. Every conversion is total: a zero or negative rate gives zero, not a division by zero, and results saturate at the storage limits.
rate Frame-rate conventions of documents: timebase + ntsc, floating rates snapped to exact ones, the highest rate an importer accepts (MAX_RATE).
report Report: what an import or export could not represent exactly, identical messages merged with a count.
path Media references: file:// URLs (localhost, drive letters, UNC hosts, percent-encoding) to and from paths, and relative references joined to a document's folder. Lexical only.
model A neutral timeline: a Project of bins and items (media with what a document says about it, sequences, subclips), sequences of video and audio Tracks holding TrackItems (speed, reverse, freeze frame, links, effects with keyframes, markers) and Transitions. Catalog is the application's effects, supplied by the caller. Every object has an ext slot for application JSON a format can carry unread.
fx The effect and parameter ids the formats name themselves (motion, opacity, time remap, volume, pan, the common dissolves) and Standard, a catalog of just those.
build Building a Project while reading a document (Builder, media deduplicated by key, track placement bounded at 256 tracks) and the bounds every import ends with (Imported::bound: implausible rates and start timecodes reset and reported).
ale Avid Log Exchange: AleDoc (heading, columns, rows), parse, write, the standard heading and columns.
edl CMX 3600 edit decision lists: parse to the list as written (events, their lines and timecode fields, M2, AUD, clip-name, source-file, effect-name and locator comments), write for events the caller built, channel codes, reel names, the frame-rate guess.
fcp7 (feature xml) Final Cut Pro 7 XML (xmeml 4 and 5, the dialect Premiere Pro reads and writes) to and from model. Read with craft-xml, strictly and bounded.
fcpxml (feature xml) FCPXML 1.9–1.11 to and from model: the primary storyline as V1, connected clips by lane, ref-clip compounds as nested sequences, rational times exact, transforms, opacity, volume and their keyframes, retiming through timeMap.
otio (feature otio) OpenTimelineIO JSON to and from model: timelines, collections, stacks as nested sequences, gaps, transitions, markers, linear time warps and freeze frames. What OTIO has no schema for goes in metadata.<namespace>, and the model's ext is merged into it, so an application's own data round-trips.
crate root Format (EDL, xmeml, FCPXML, OTIO, AAF, OMF), detect_text, decode_text.

No file I/O. EDL and ALE give the document as the format writes it. The formats that describe a whole edit read into and write from model; mapping model onto an application's own project stays in the application. AAF and OMF are in craft-aaf.

Feature xml: the XML formats, on craft-xml. Feature otio: OpenTimelineIO, on serde_json. Feature serde: Serialize / Deserialize for the time, report and Format types.

Specifications

Written from public descriptions only: SMPTE ST 12-1 (timecode); the CMX 3600 EDL format as documented by its users (event lines, FCM, M2, and the common * FROM CLIP NAME:, * SOURCE FILE:, * LOC: comment extensions); Avid's published description of ALE; RFC 8089 (file URIs).

Origin

Moved from FilmCraft's filmcraft-time (whole) and filmcraft-interchange (ale, the parser and line layout of edl, the path and rate helpers of common, Report, Format).

Hostile input

Parsers are line-oriented over text the caller already holds, so memory is bounded by the input. Timecode fields beyond i64 are refused; audio channels outside 1..=99 are refused and reported; an FPS heading that is not a positive finite rate up to 100 000 is no rate. Tests cover each.