- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>