Commit Graph
29 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 6baff9f5fe Integration fixes for the community batch
- Footage sources: the cancellable wrapper (#784) forwards frame_with (#752 float curves)
  and problem (#887); problem's default lives on the trait.
- Media: a called-off decode (#784) is not a footage problem (#887).
- Effect Controls rename (#953): Escape cancels (egui drops focus on Escape the same
  frame), the Rename item is translated; regression test.
- i18n (#873): drop the /out-i18n/ ignore rule and the note about an uncommitted script.
- Tests: text range selector caps (#926) re-borrow the layer; the RAM preview test (#793)
  sets the comp's own Resolution (#462).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:27:59 -04:00
Brandon Thomas 02c134fb41 Merge pull request #904 from vangel76/fix-903-multichannel-downmix
#903: downmix 5.1 and other multichannel audio to stereo

# Conflicts:
#	crates/media/src/lib.rs
2026-10-12 04:20:49 -04:00
Brandon ThomasandClaude Opus 5.5 42ef79a2fd Media: movies opened in place keep their orientation (#796 × #827)
Opening movies in place (#796) skipped the whole-file bytes #827 read the
track matrix from, so the pool and the probe lost the turn for movies on
disk. stream::movie_header reads just the ftyp and moov boxes (bounded,
positional reads) and both in-place paths derive the orientation from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:37:34 -04:00
Brandon Thomas 4e8e6e10b1 Merge pull request #796 from vangel76/fix-stream-video-open
#794: open movies in place instead of reading them whole

# Conflicts:
#	crates/media/src/lib.rs
2026-10-12 03:22:57 -04:00
Brandon Thomas 565fd4118d Merge pull request #784 from vangel76/fix-329-seek-latency
#329: scrubbing a long movie: superseded viewer frames call off their decodes

# Conflicts:
#	crates/media/tests/footage.rs
2026-10-12 03:22:50 -04:00
Brandon Thomas 1210ba91f4 Merge pull request #768 from vangel76/fix-762-animated-webp
#762: import animated WebP as footage with its frames

# Conflicts:
#	docs/file-format-parity.md
2026-10-12 03:22:30 -04:00
Brandon Thomas 78b32b61d4 Merge pull request #769 from vangel76/gap-mxf-import
#767: import MXF (OP1a / OP-Atom) footage

# Conflicts:
#	crates/ui-egui/src/menus.rs
#	docs/gaps.md
2026-10-12 03:20:05 -04:00
Brandon Thomas 3d02e65f2b Merge pull request #765 from vangel76/gap-dpx-import
#756: import DPX and Cineon stills and sequences

# Conflicts:
#	crates/media/tests/footage.rs
#	crates/ui-egui/src/menus.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-12 03:20:04 -04:00
Brandon Thomas 05f0d9ec62 Merge pull request #755 from vangel76/gap-tga-hdr-import
#745: import Targa (.tga) and Radiance HDR (.hdr) stills and sequences

# Conflicts:
#	crates/ui-egui/src/menus.rs
2026-10-12 03:20:03 -04:00
Brandon Thomas d57459a7d0 Merge pull request #742 from vangel76/gap-fbx-import
#736: import FBX 3D models (binary and ASCII)

# Conflicts:
#	crates/engine/src/commands/app_more.rs
#	crates/engine/src/commands/model3d.rs
#	crates/engine/src/media_browser.rs
#	crates/media/src/lib.rs
#	crates/media/src/probe.rs
#	crates/model/Cargo.toml
#	crates/model/src/lib.rs
#	crates/ui-egui/src/menus.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-12 03:20:00 -04:00
vangel76andClaude Sonnet 5.5 64fabbb2fc #903: downmix 5.1 and other multichannel audio to stereo
Problem: footage audio kept only its first two channels, so the centre, LFE and surrounds of a 5.1 file were dropped. AAC 5.1 was worse: FilmCraft's AAC-LC decoder delivers AAC's own order (C L R Ls Rs LFE), so the left output was the centre and front-right was lost.

Fix: crates/media/src/downmix.rs holds one ITU-R BS.775 stereo downmix (centre and surrounds at -3 dB, LFE left out, fixed normalisation by the largest row sum so full scale cannot clip), with channel orders for 3 to 8 channels in WAVE/SMPTE order and AAC's native order, and only the front pair for more than 8. MediaPool::read_stereo applies it, which preview, waveform and export mixdown all read through. Mono and stereo are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 20:36:41 +02:00
vangel76andClaude Opus 5.5 d648026ffd #794: open movies in place instead of reading them whole
Problem: probe() and MediaPool's movie sources called std::fs::read and then
filmcraft_codecs::open_bytes, so a clip cost its full size in memory, twice
while probing (a 245 MiB MP4 peaked at 494 MiB just to import).

Fix: container footage on disk is opened through a positional-read
ByteReader (stream::FileReader) and FilmCraft's reader openers
(filmcraft_media::reader::open_reader; MP4/MOV, Matroska/WebM, MXF, Ogg,
MPEG-PS/TS), which read the index and then single samples. Probing takes this
path only when nothing probed before containers (still/model extensions,
PSD/SVG/PDF, image magic) claims the file; everything else, whole-file audio
formats, in-memory files (add_bytes) and wasm32 builds keep the byte path.
Regression test: probing and decoding a frame and audio of a movie with a
256 MiB tail grows peak RSS by <64 MiB (512 MiB before), same results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:42:24 +02:00
vangel76andClaude Opus 5.5 a0decc80dd #329: superseded viewer frames call off their footage decodes
Problem: scrubbing a long movie showed the frame under the pointer
seconds after the pointer stopped. Every viewer frame a free frame-pool
thread had started kept running: each seeked the movie's one decoder and
decoded up to a GOP, and the newest frame waited behind all of them on
the decoder's lock (45 min 1080p H.264/HEVC, GOP 250: 1.8-3.6 s for the
last frame of a drag; a single jump is 0.1-0.4 s). FilmCraft's decoder
already polls a cancellation flag, but nothing set one.

Fix: FootageSource::cancellable / called_off and CancellableFootage (a
per-render view whose frame reads run under the render's own flag, so
reads on other threads answer to the right render). MediaPool runs them
under filmcraft_media::cancel::with_cancel. The frame workers render
through it, and a newer viewer frame calls off the comp's running viewer
frames of other times. A called-off render is not cached (frames, disk,
layer cache). Drag latency is now 0.1-0.3 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:01:54 +02:00
vangel76andClaude Opus 5.5 f0bd916543 #767: import MXF (OP1a / OP-Atom) footage
Problem: the Media Browser lists .mxf files, but File > Import's dialog
filtered them out and media::is_importable refused them, although the
pinned FilmCraft codecs behind crates/media already demux MXF (AVC,
MPEG-2 / XDCAM / D-10, VC-3 / DNxHD, ProRes, PCM audio) by content.

Fix: add "mxf" to VIDEO_EXTENSIONS and to the File > Import and Set
Proxy dialog filters. Tests check MPEG-2 4:2:2 long-GOP + 24-bit PCM and
DNxHD 36 MXF against ffmpeg frames (>35 dB, measured 64-67 dB), audio
level, and that the Import dialog offers MXF. AVI stays open: FilmCraft
has no AVI demuxer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:30:51 +02:00
vangel76andClaude Opus 5.5 5ba0c60a15 #762: import animated WebP as footage with its frames
Problem: an animated WebP (e.g. ffmpeg -c:v libwebp_anim) imported as a
still and showed only its first frame; probe_bytes handed every WebP to
the still path. Found while measuring #719.

Fix: a webp_anim module, following the animated GIF path. Probing reads
the RIFF chunk headers (VP8X animation and alpha flags, one ANMF per
frame) and describes a WebP with two or more frames as Video footage
(codec "WebP", alpha Straight or Ignore from VP8X). Frame delays give the
rate: delays within 1 ms of each other read as the standard rate they
round to (24, 25, 29.97...), else 1000 / gcd of the delays so uneven
timing is exact; delays of 10 ms or less count as 100 ms as in browsers.
Frames are decoded once with the image crate's WebP AnimationDecoder and
cached in the pool's existing GIF slot; gif_anim::Anim now keeps frame
starts as Ticks and serves both formats. Canvas x frames beyond the
existing memory cap is refused before decoding; a damaged tail keeps the
frames before it; a file without frames is a clear decode error. Still
WebP stays a still.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:29:26 +02:00
vangel76andClaude Opus 5.5 557c0bdbba #756: import DPX and Cineon stills and sequences
Problem: the Media Browser listed .dpx files and After Effects imports
DPX and Cineon scans (stills and numbered sequences), but import
refused them: nothing could decode either format, and .cin was not even
an image to the browser, so its runs never grouped into sequences.

Fix: a small pure-Rust reader written from the public SMPTE 268M and
Kodak Cineon specifications (crates/media/src/dpx.rs): luma, RGB, RGBA
and ABGR at 8/10/12/16 bits, 10-bit filled method A/B, 12-bit in 16-bit
words, both byte orders, lines padded to 32-bit words, unrotated
orientations and the pixel aspect ratio; the codec names the transfer
(log, linear, ...) and code values are shown as stored. RLE, signed,
float, Y'CbCr, packed 10/12-bit and transposed files are refused with a
clear error; every header field is bounds-checked and the data length
verified before allocating. Probe and frame decode use it first;
is_importable, the Media Browser kinds and the import dialog list
.dpx/.cin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:24:16 +02:00
vangel76andClaude Opus 5.5 1fdf817cc9 #745: import Targa (.tga) and Radiance HDR (.hdr) stills and sequences
Problem: the Media Browser lists .tga and .hdr files (and groups them into
image sequences), but media::is_importable had neither extension and the
still decoder never tried them, so importing them failed.

Fix: enable the image crate's built-in tga and hdr decoders (no new
dependency), add both to STILL_EXTENSIONS and the File > Import dialog, and
pick a still's format by signature with an extension fallback, since Targa
has no signature. Radiance HDR is linear float light and is sRGB-encoded like
OpenEXR (super-white kept); Targa alpha goes through the existing
unlabeled-alpha interpretation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:57:13 +02:00
vangel76andClaude Opus 5.5 977532f135 #736: import FBX 3D models (binary and ASCII)
Problem: After Effects 26 imports FBX models into Advanced 3D comps; File > Import rejected
.fbx, so FBX product shots and scenes could not be used.

Fix: a clean-room FBX 7.x reader in effectcraft-model (binary node records with 32/64-bit
offsets and zlib arrays, ASCII syntax) and a scene builder: meshes with normals, UVs and
per-polygon materials, the Model hierarchy with the SDK transform (pivots, offsets, pre/post
rotation, rotation order, geometric transform), Lambert/Phong materials onto
metallic-roughness with embedded or relative textures, and GlobalSettings axes turned to
Y-up. Wired into import, probing, the media browser and the file dialog. Hostile input
(truncation, corruption, huge arrays, deep nesting) fails with an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:46:43 +02:00
vangel76andClaude Opus 5.5 5661d5111c #729: import STL 3D models (binary and ASCII)
Problem: After Effects 26 imports STL models besides glTF and OBJ; we
refused .stl files, and the Media Browser and import dialog did not list
them as models.

Fix: a pure-Rust STL reader in effectcraft-model (binary and ASCII,
detected from the contents; several ASCII solids and polygon loops;
VisCAM/SolidView and Materialise Magics facet colours as plain
materials; Z-up turned to the model's Y-up; faceted normals from the
winding). Truncated, empty or non-finite input is an error, never a
crash. .stl is now a model extension for import, probing, the Media
Browser, the import dialog and the system report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:32:11 +02:00
Limestudio90andClaude Sonnet 5.5 bfbdadd34a Import animated GIFs as movies
GIFs went through the still-image path, so an animated GIF came in as a
single frame with zero duration. A GIF with two or more frames is now
probed as a movie: its frame rate is chosen so every per-frame delay is
a whole number of frames (uneven timing is kept exactly), delays of 0
or 1 hundredths count as 10 like in browsers, and the pool decodes the
frame shown at the requested time. A single-frame GIF stays a still.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 11:38:00 +02:00
bflatasticandClaude Opus 5.5 50c9549529 Import: image sequences with a Sequence option, ranges, folders and missing frames (#297)
Image sequences were found only by the desktop media prober: one picked
numbered still brought its whole run, with no way to import just that
frame, picking every frame made one sequence item per frame, the browser
couldn't make sequences at all, and a gap in the numbering closed up
silently.

The engine now groups numbered stills itself (engine/src/sequence.rs):
one picked file brings its run from its folder, several picked files of
one run import as that range, a picked or dropped folder imports its
files with each run as a sequence, and Force Alphabetical Order takes
every image of that type in the folder by name. `file.import` takes
`sequence` (default on), `alphabetical` and `frameRate`; Replace Footage,
Reload, proxies and render outputs probe the same way. The media layer
probes one file; the browser lists its file table for the run and
registers every frame with its media pool.

File > Import > File... asks, like After Effects' "PNG Sequence"
checkbox, whether picked numbered stills import as a sequence, with
Force Alphabetical Order and the frame rate. Sequences are named
`shot_[0001-0250].png`.

Interpret Footage gains Missing Frames (Show Placeholder, colour bars as
in After Effects, the new default; Hold Previous Frame; Skip) and Start
Frame (the frame number at the footage's first frame: trims or pads the
head). Saved projects keep Skip, so their sequences play as before.

Form notes keep a readable width and the form fits its rows, so OK stays
in view. docs/footage.md explains importing footage and sequences.

Test: tests_sequence (engine: run, picked range, every frame, Sequence
off, folder, alphabetical, Interpret Footage Missing Frames / Start
Frame, Replace Footage and Reload), project sequence unit tests,
sequence_gaps_show_placeholders_hold_or_close_up (media),
import_dialog_offers_the_image_sequence_option and
dropped_frames_import_as_one_sequence_with_sequence_interpretation (UI).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:00:07 +07:00
Brandon ThomasandClaude Opus 5.5 37c9b449c6 Never crash: media crate without expect()
The smart-object branch of the layered-file decoder bound the footage layer twice and
re-read it with expect("embedded implies a layer"); bind it once with a let-chain. Denies the
panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:18:36 -04:00
Brandon ThomasandClaude Opus 5.5 da0ec526f3 M13.2: PDF / Illustrator (.ai) / EPS vector footage (effectcraft-pdf): continuous rasterisation, layered composition import, Create Shapes from Vector Layer; SVG render tree clip groups
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:12:21 -04:00
Brandon ThomasandClaude Opus 5.5 36683d95ee Merge main into IO-2/SHP-5/OUT-2/PRV-2 work
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:39:47 -04:00
Brandon ThomasandClaude Opus 5.5 94e1c86d89 IO-2/SHP-5/PRV-2: PSD import, SVG import + vector/text conversions, persistent disk cache
- effectcraft-psd (L1): PSD/PSB reader from Adobe's published file format spec (layers, groups,
  masks, blend modes, text engine data, lfx2 effects, fill/adjustment layers, vector masks;
  1/8/16/32-bit, RGB/CMYK/Gray/Lab/Indexed, raw/RLE/ZIP) and a minimal writer for fixtures.
- File > Import: importAs footage|composition|compositionLayerSizes, layer (Choose Layer);
  groups -> precomps (pass-through collapses), text -> editable text, styles -> Layer Styles,
  adjustments -> adjustment layers with effects; UI asks the Import Kind for Photoshop files.
- effectcraft-svg (L2): SVG parser (shapes, paths, transforms, use, CSS, gradients) and
  rasteriser; .svg imports as footage, continuously rasterised when the switch is on.
- layer.create: shapesFromVector, shapesFromText, masksFromText, editableText (stub removed).
- Disk cache (render::disk_cache): LZ4 content-keyed frames and layer buffers, LRU size limit
  from prefs, atomic writes, restart persistence; edit.purge clears it, cache.diskStats query;
  viewer frames read/write it and the timeline shows disk-only frames in blue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:10:48 -04:00
Brandon Thomas a51f439d34 Merge branch 'main' into worktree-agent-a758de7c5b6efcc3d
# Conflicts:
#	crates/effects/src/lib.rs
#	crates/render/src/lib.rs
#	docs/parity.md
2026-10-02 21:48:49 -04:00
Brandon ThomasandClaude Opus 5.5 9a11c577f3 M9.11: 3D Channel effects on auxiliary channels
Aux-channel model (effectcraft_raster::channels3d: depth, object/material
ID, normals, UV, coverage, Cryptomatte ranks + manifests, MurmurHash3);
EffectHost::aux; multi-layer OpenEXR channels via the exr crate
(FootageSource::aux); the compositor's depth / layer-ID / Cryptomatte pass
(Renderer::comp_aux) so the effects work on precomps of 3D comps.
3D Channel Extract, Cryptomatte, Depth Matte, Depth of Field, EXtractoR,
Fog 3D, ID Matte, IDentifier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:28:37 -04:00
Brandon ThomasandClaude Opus 5.5 83c7e14b94 M7.4: model import
New crate effectcraft-model (L2): glTF 2.0 (.gltf/.glb, data URIs, external buffers and
images, PBR metallic-roughness with base/MR/normal/occlusion/emissive textures, node
hierarchy, skins, step/linear/cubic animations) and OBJ/MTL import, parametric primitives,
extruded/bevelled outline meshes and polygon triangulation with holes.

Project: FootageKind::Model, LayerSource::Model/Primitive, Layer::environment, Geometry and
Material Options builders, Environment light Source. Media probes and loads models
(FootageSource::model). Engine: layer.newModel, layer.new3dPrimitive/newPrimitive,
comp.renderer, layer.environment, material.set/reset/duplicateAssign/revealSource (stubs
replaced); File > Import and layer.addItem pick up models. Hand-written fixtures with
attribution sidecars.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:04:47 -04:00
Brandon ThomasandClaude Opus 5.5 d867178137 M6.8: media import via FilmCraft codecs (probe, frame cache, stills, sequences)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:28:14 -04:00