Commit Graph
32 Commits
Author SHA1 Message Date
aiongg 0a881266c8 CI: xtask ci in the dev profile, one test binary per crate, nextest (#2860)
* Tests: one integration-test binary per crate

Each crate with several tests/*.rs files keeps them as modules of tests/it/,
so cargo links one test executable per crate instead of one per file. Files
that change process-wide state or time a workload stay binaries of their own.

* Profiles: line tables for workspace crates, no debug info for dependencies

* Windows ARM64: nightly and on release pushes instead of every push to main

* xtask ci: clippy and tests in the dev profile

The release profile's thin LTO and single codegen unit made linking the test
binaries take most of the gate. The codec and audio crates stay at opt-level 3
in the dev profile, so their tests run at release speed.

* xtask ci: nextest, doctests, no examples, one wasm check

Tests run with cargo nextest when it is installed (cargo test otherwise) and
build with --lib --bins --tests, so examples aren't linked; doctests run
separately. The wasm check keeps going past the first failing crate. VP8
fixture generation locks across processes, since nextest runs each test in
its own.

* VP9 synthetic fixtures: write aside and rename

A test in another process could read a fixture ffmpeg was still writing.

* Fuzz tests: a third of each sweep by default, all of it with FILMCRAFT_FUZZ_ITERS

Every seed, mutation kind and thread count still runs. Header bytes each get
one overwrite value and two bit flips in rotation, so every value and bit
index still occurs across the region. FILMCRAFT_FUZZ_ITERS=N runs the whole
sweep plus N random rounds per seed, as before.

* Platform fallback tests: one failure point per hybrid path

The failure-anywhere test injected a failure at four points around every
restart point; the hybrid's paths differ only in the first GOP, around the
second and last restart points, at the end and never. The replay test needs
the first two GOPs.

* Export tests: shorter ranges with the same checks

The bitrate test measures 0.5–1.5 s, where each preset reaches its own
target (on 0–3 s the simple demo left two presets at the same rate). The
every-preset test exports three frames per preset.

* Audio tests: effects in parallel, a table-driven FFT for the Opus quality metric

The extreme-settings test keeps both settings and the 40 s tail for every
effect, one thread each. The Opus quality metric's FFT builds its twiddles
once and allocates nothing per transform (same algorithm and summation order).

* CI: dev-profile gate with nextest

xtask ci no longer builds with --release, so the release-profile overrides
go; nextest is installed for the test step.

* CI: apt packages through the cached install-apt-packages action

* Review fixes: fuzz tests with a panic hook in their own binaries, loud fixture lock

The fuzz tests of av1, h264, hevc, mpeg2v, vp8 and vp9 count panics through
a process-wide hook, so a panic in another test of the same process would
fail them; they are binaries of their own again. The VP8 fixture lock fails
the test instead of skipping it silently, and the full-sweep doc comments say
how many truncation points the full run samples.

* Fuzz tests: rotate the default subset per run (FILMCRAFT_FUZZ_SEED replays one)

* platform tests: decode-switch tests in their own binaries, one NVENC lock
2026-10-23 13:34:16 -04:00
Earl Co 199b70c1d1 security(interchange): bound imported track indices (#2910) 2026-10-23 10:51:39 -04:00
PaulandCodex 0b432e3834 fix(interchange): avoid UTF-8 slice panics in file URLs
Use a checked localhost prefix probe so Unicode file URLs retain their media paths without slicing inside a UTF-8 character. Add public converter, resolver and EDL import regressions.

Fixes #1591

Co-authored-by: Codex <noreply@openai.com>
2026-10-14 16:24:29 -06:00
Brandon Thomas b653b5d80f Merge pull request #1085 from carlitosteso3/fix/xml-audio-gain-371
#371: split stereo halves from FCP7 XML play on their own side
2026-10-11 20:34:42 -04:00
Brandon Thomas ebbe9240c7 Merge pull request #945 from vbasky/fix/909-fcpxml-point-markers
Import FCPXML one-frame markers as points
2026-10-11 20:29:57 -04:00
Brandon Thomas 156086dc87 Merge pull request #946 from vbasky/fix/900-fcp7-xml-markers
Export FCP7 XML markers on source media
2026-10-11 19:49:51 -04:00
Claude 8e1fb41258 #371: split stereo halves from FCP7 XML play on their own side
Premiere writes a stereo clip to FCP7 XML as two mono clip items on two
tracks (sourcetrack trackindex 1 and 2). The importer already picks
channel 0 and channel 1 for them, but left both centred, and a centred
one-channel clip plays on both sides. So both channels summed on L and
on R: +3 dB per channel for an uncorrelated stereo effect (and its L-R
doubled), +6 dB for dual-mono dialogue.

Import: a clip that plays one channel of a split two-channel file is
panned to that channel's side (channel 0 left, channel 1 right). An
Audio Pan filter on the clip item still overrides it.

Export: a one-channel clip always writes its Audio Pan, also when it is
centred, so a centred Breakout to Mono round-trips centred.

Measured with filmcraft-cli (import, export WAV): a split stereo file
and a split dual-mono file now come out at 0.00 dB and sample-identical
to the source (before: +3.01 dB and +6.02 dB per channel); a mono file
on a mono track was and stays at 0 dB.

Tests (crates/interchange/tests/fcp7.rs):
split_stereo_halves_are_panned_to_their_side and
split_stereo_half_keeps_its_audio_pan fail without the import change;
centred_mono_breakout_round_trips_centred fails without the export
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012HrAqZmrrDLRpxBJatxvaJ
2026-10-11 10:50:36 -05:00
Vikram Bhaskaran 3da5dec8d4 Export FCP7 XML master-clip media markers (xmeml 4/5).
Timeline clipitem markers already round-tripped; source-media annotations on
master clips were omitted with an empty export report. Write those markers on
<clip> so re-import keeps them.

Fixes storytold/filmcraft#900.
2026-10-11 15:03:07 +11:00
Vikram Bhaskaran 50f9cfecc0 Fix OTIO nested sequence vs clip instance markers (#901)
Export was overwriting Stack markers with the using clip's markers, so a nest with no instance markers lost its sequence markers, and instance markers were imported onto the nested sequence. Keep sequence markers on the Stack and store clip instance markers in metadata.filmcraft.clip.
2026-10-11 15:02:25 +11:00
Vikram Bhaskaran b13567439b Fix FCPXML one-frame markers importing as ranges
FCPXML writes point markers as one frame at the sequence rate. Import
compared duration to 1 tick, so a 24fps one-frame marker became a
range. Treat duration ≤ one sequence frame as a FilmCraft point.
2026-10-11 15:01:54 +11:00
Nigel Ihza Firdausy 06ebdfa08a interchange: preserve named audio dissolves on EDL import 2026-10-11 00:53:37 +07:00
Brandon ThomasandClaude Opus 5.5 24f8279327 FCP7: trackindex 1 means channel 0 when the file is split across mono tracks (#463)
A split-stereo left clip exported trackindex 1 and re-imported as full stereo,
losing the left/right split. Treat trackindex 1 as channel 0 when another
audio clip item of the same file in the sequence uses a higher index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:42:52 -04:00
MarekSpodBiedry c25cc5b18b FCP7 XML: keep the source channel of split mono audio tracks (#463) 2026-10-09 21:02:47 +02:00
Brandon ThomasandClaude Opus 5.5 701d7c7111 Merge origin/main into multi-stream audio
Keeps #359's contiguous_starts audio timing, now per stream in Mp4Audio /
MkvAudio (starts and preroll in source sample frames). MP4 audio streams and
their decoders are built from the same (track, info) pairs so they stay
index-aligned. Adapts new main code to audio_streams / TrackItem::audio_stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:08:09 -04:00
txiki739andClaude Opus 5.5 d39692af63 #340: FCP7 XML clip source span always matches its record span
write_clip rounded start, end, in and out to frames independently, so
a clip placed by seconds at 23.976 (start 0, in 20 s, 3 s long) was
written as start 0 / end 72 / in 480 / out 551: 71 source frames for
72 record frames.

At normal speed and on a freeze frame the source span is the record
span, so out is now in + (end - start). Speed-changed clips keep their
independently rounded out. The master duration is raised to out when
rounding pushes out past it; pproTicksIn/Out still carry exact ticks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:37:12 +02:00
Idiotic f146d7ba9f feat(media,engine): implement multi-stream container audio support and schema v13
- Update MediaInfo to store all discovered audio streams in audio_streams

- Retain independent stream metadata and decoder state for MP4/MOV and Matroska/WebM

- Update audio_placement_specs to allocate successive tracks via ensure_audio_tracks

- Isolate clip rendering to read assigned audio stream rather than replaying stream 0

- Bump project schema to v13 with backwards compatibility for legacy v12 projects

Resolves #312

Fixes #200

Fixes #315
2026-10-08 22:36:45 -04:00
Brandon Thomas 19d2f15148 Merge remote-tracking branch 'origin/main' into nest-116
# Conflicts:
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/panels/timeline.rs
2026-10-07 06:00:36 -04:00
Brandon Thomas 6a0fca1cd1 Merge pull request #64 from punkhop/fix/xml-master-clip-keeps-its-bin
interchange: an FCP7 master clip lands in its bin when a sequence used it first
2026-10-07 05:56:44 -04:00
justjohn12345andClaude Opus 5.5 9b31f4eb84 NEST15: nested sequences in AAF and OMF, the way Premiere Pro writes them
A nested sequence was left out of both formats as a gap, with a report entry
telling the user to "render and replace" it first (a command FilmCraft does
not have). An AAF with nested compositions was read with gaps in their place.

What Premiere Pro 26.5.2 does, read off files it exported for a sequence with
a nest:
- AAF: the nested sequence is a composition mob of its own, and its clips are
  source clips that point at it.
- OMF: the nested sequence's sound is rendered into the document as one clip
  named after the sequence.

Export
- AAF: a nested sequence is written as a composition mob (no usage code; the
  exported sequence stays the top-level one). A clip of it is a source clip
  at that mob's first slot of the clip's kind, its start in that slot's edit
  units. Nests in nests are followed; a nest used twice is written once. With
  embedded or consolidated audio, the media of the clips inside a nest is
  prepared with the rest (`NestNeeds::Inside`).
- OMF: `audio_needs` lists the range of the nested sequence each nest clip
  plays (`NestNeeds::Render`); the engine mixes it through the export audio
  pipeline with what is on the clip, and it is written as media. Without that
  essence the nest is a gap and the report names the sequence.

Import (AAF)
- A source clip that points at a composition becomes a clip of a nested
  sequence; the composition is read once.
- Premiere Pro's layout is read: all video tracks in one slot (a nested scope
  with a segment per track; before, only the last segment was read, so a
  Premiere AAF came in with one video track), and "top-level" tagged on nested
  compositions too (top-level are those no other composition uses).
- A composition without a frame size takes that of the first picture it
  shows, not of the first picture in the file.

A sequence that claims to be inside itself is a gap with a report entry in
both directions; the walks are bounded (16 levels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:53:34 -04:00
justjohn12345andClaude Opus 5.5 12585410b3 NEST15: a nested sequence is reel AX in an EDL, in every reel mode
The export report already said a nested sequence is written "as a single
source (reel AX)", but the reel came from the reel mode: with the default
(file name) a nest called "Nested Sequence 02" got reel NESTED_S. A nested
sequence has no tape and no file, so its reel is now AX whatever the mode.

Premiere Pro 26.5.2 writes the same for a nest (seen in an EDL it exported):
one event from reel AX, the nested sequence's name in "FROM CLIP NAME", and
the nested sequence's own time as source timecode. A test pins that event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:53:19 -04:00
Peet GuercioandClaude Opus 5.5 56cc93f4ea interchange: an FCP7 master clip lands in its bin when a sequence used it first
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 05:42:03 -07:00
punkhopandClaude Opus 5.5 4aa3f79953 interchange: FCP7 XML import reports a clip item whose file is not defined
Problem: a clip item whose `<file id="...">` reference has no definition
anywhere in the document was dropped without a word, so a sequence could
come back one clip short with an empty import report. FilmCraft's own
export writes titles and adjustment layers this way.

Solution: the importer adds a warning that names the clip item: `clip
"Title" refers to a file the document does not define; skipped`. Nothing
else about the import changes.

Test: `fcp7_import_reports_a_clip_item_whose_file_is_not_defined`
(FilmCraft's own export, a hand-written document, and a document whose
files are all defined).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:02:27 -07:00
punkhopandClaude Opus 5.5 6899f15e51 interchange: an export names every title it cannot carry
Problem: exporting a sequence with a graphic (title) clip to Final Cut
Pro 7 XML returned a report that listed other losses and said nothing
about the title. The XML holds a clip item for it that points at a file
the document never defines, and importing the XML gives the sequence
back without the title. OTIO (a clip with a missing media reference,
read back as offline media) and EDL (an ordinary event) were silent in
the same way; FCPXML said "clip without media skipped" and AAF "graphics,
titles and adjustment layers are not exported" without saying which clip.
Adjustment layers take the same paths.

Solution: every exporter names each graphic clip and adjustment layer
it cannot carry, one warning per clip, with what was written instead:
`graphic clip "Title" at frame 48 has no Final Cut Pro 7 XML equivalent:
it is written as a clip item without media and is not read back on
import`. One helper (`common::uncarried_clip`) builds the name for all
formats. What is written is not changed, byte for byte.

Why only a warning: FCP7 XML has a Text generator, but this crate's
importer does not read one (unknown generators become Black Video), and
a title here is a stack of text and shape layers with per-character
styles, pins and roll settings that the generator's few parameters
cannot hold. Writing one would need a new import path and would still
lose most of a title, so the honest result for now is to say what
happens. crates/interchange/README.md has the table per format.

Tests: crates/interchange/tests/uncarried_clips.rs (each format names
the title and the layer once; two titles are two lines; nothing is said
without such clips; a re-import gives back what the report says), and
the engine's `interchange_export_reports_the_title_it_cannot_carry`
through `file.exportInterchange`. The two naming tests fail without the
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 20:01:34 -07:00
Brandon Thomas 0948dcf682 Merge branch 'main' into M3.13/M8.10 2026-10-03 11:50:01 -04:00
Brandon ThomasandClaude Opus 5.5 2a199425d0 M8.10: HDR-aware Lumetri, mastering metadata tone-map peak, HSL Refine, Lumetri Presets grid, Comparison View match
- Lumetri grades PQ / HLG working spaces in their own signal normalised to
  HDR White (Basic Correction) / HDR Range (Curves) in cd/m²
  (filmcraft_color::GradeSpace): exposure in stops of light, highlights above
  HDR White kept, HDR Specular scales them; Rec. 709 unchanged.
- HDR static metadata (mdcv/clli, Matroska MasteringMetadata/MaxCLL/MaxFALL)
  on VideoStreamInfo::hdr; PQ media use MaxCLL / mastering peak as the
  BT.2390 source peak; media.colorInfo reports it.
- HSL Secondary Refine: Denoise (median of the key) and Blur (Gaussian).
- Lumetri Presets (18 original looks in 4 folders) with thumbnails rendered by
  our Lumetri on a procedural preview picture; Effects panel rows and a
  thumbnail grid when wide; lumetri.presets / applyPreset / presetThumbnails.
- Color Match: Comparison View toggle; Apply Match uses its reference frame.
- Goldens lumetri_hdr_pq and lumetri_presets_grid; D-Log M stays unsupported
  (no published formula).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:31:00 -04:00
Brandon ThomasandClaude Opus 5.5 0658967a5b M11.4: AAF (Edit Protocol) and OMF 2.0 import/export in filmcraft-interchange
Shared mob-style composition model; AAF objects in structured storage over
filmcraft-cfb; OMF objects in a Bento container; embedded / consolidated audio
supplied by the caller (essence::audio_needs, MediaOptions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:23:20 -04:00
Brandon ThomasandClaude Opus 5.5 b98c886133 M11.7: offline media and relinking
- Missing media no longer fails: opening a project scans for missing files
  (`media.findMissing`, `file.open` → missingMedia). Missing clips render our own
  offline slate (render::offline: red striped field, warning triangle, "MEDIA NOT
  FOUND", file name, hint) with silent audio, and the Link Media dialog opens.
- Media identity: imports record the size and a fast content fingerprint (hash of
  size + first/last MiB) in MediaClip::identity (optional field, older builds
  ignore it, no schema bump).
- media.relink checks file name / extension / fingerprint ("Clip ID") / duration /
  media start / frame size+rate+channels, refuses a mismatched fingerprint unless
  forced, supports Align Timecode (Project::shift_media_time), and relinks the
  others automatically by the derived folder remap (Windows/macOS separators).
- media.autoRelink {from,to} | {folder}, media.search, media.replaceFootage,
  media.makeOffline {deleteFiles?}, media.offlineAll, media.status, media.linkMedia.
- Media pool caches sources per reference (path, offline flag), so relink, Make
  Offline and undo take effect at once; proxy-aware resolution and a full-res
  provider that export uses.
- codecs::gop no longer holds its lock while decoding: rayon decoders inside a
  parallel export could steal another frame of the same source and deadlock.
- UI: Link Media dialog (rows, match options, folder search with preview, Locate…,
  Link, Offline, Offline All, Cancel), Make Offline dialog, Project panel offline
  markers and context menu, media thumbnails keyed by media reference.
- Tests: moved media opens offline (slate rendered) and relinks others by folder,
  prefix remap and folder search, fingerprint mismatch refused, Make Offline,
  headless UI test of the dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:00:35 -04:00
Brandon ThomasandClaude Opus 5.5 2fabddec3d M11.6: interchange API tests (detect, merge_into, rebase_source_timecode)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:39:14 -04:00
Brandon ThomasandClaude Opus 5.5 4e2fefdcda M11.6: OTIO — OpenTimelineIO JSON import/export
Timeline/Stack/Track/Clip.1+2/Gap/Transition/Marker/External/Missing/GeneratorReference,
LinearTimeWarp/FreezeFrame, SerializableCollection; exact RationalTime↔Tick by rate snapping;
nested sequences as Stacks; metadata.filmcraft round-trips effects, labels, links, groups,
gain, track state, sequence settings and media info. Golden plain-OTIO document with gaps
and transitions; lossless property round trips including sample-accurate audio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:38:33 -04:00
Brandon ThomasandClaude Opus 5.5 d6908b6c0d M11.5: FCPXML — 1.9–1.11 import/export
Exact rational time strings, formats/assets/media/effect resources, library/event/project,
spine with asset-clip/clip/ref-clip/gap/transition, connected clips and secondary storylines
by lane, srcEnable audio/video merge, timeMap constant speed/reverse/freeze, adjust-transform/
blend/volume with keyframes, markers and chapter markers. Golden document and round trips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:30:18 -04:00
Brandon ThomasandClaude Opus 5.5 b31c655338 M11.4: FCP7 XML — xmeml v4/v5 import/export
Project/bin/clip/sequence, clipitems with pproTicks-exact source, files by id, nested
sequences, generatoritems, transitionitems with alignment, Basic Motion/Opacity/Time Remap/
Audio Levels/Audio Pan filters with keyframes, generic effects by id/name, labels, markers,
links, enabled/locked. Golden Premiere-style document and property round trips.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:27:48 -04:00
Brandon ThomasandClaude Opus 5.5 c14a7d44a4 M11.3: EDL — filmcraft-interchange crate with CMX 3600 import/export
New L2 crate filmcraft-interchange (roxmltree for XML reading). API: import/import_with,
export, export_edl_per_track, detect, merge_into, rebase_source_timecode, Report.
EDL: drop-frame FCM, dissolves/wipes/keys, BL fades, V/A/AA/B/AA/V/A3/A4 channels,
M2 speed/reverse/freeze, reel modes (8/32), clip name/source file/effect/LOC comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:38:19 -04:00