Commit Graph

56 Commits

Author SHA1 Message Date
Brandon Thomas 6102a3794a GPU1: blend modes on the GPU compositor (#30)
All 27 blend modes now composite on the GPU instead of sending the
whole frame to the CPU.

- render::plan: PlanLayer carries its Blend; plan_frame no longer falls
  back to a CPU frame for non-Normal modes. Media, graphic clips,
  multicam angles and CPU-rendered layer images (standard effects) keep
  their clip's mode; inside a transition the mode is Normal, as in the
  CPU reference. execute_cpu composites with each layer's mode.
- gpu: Normal keeps the single fixed-function pass. Dissolve also uses
  fixed-function "over": the CPU's 64-bit pixel hash is reproduced in
  WGSL on u32 pairs (pattern bit-exact). The other 25 modes copy the
  accumulator region under the layer's quad into a backdrop texture and
  composite in fs_blend with the CPU math (sRGB-encoded straight colour,
  W3C formulas, back to linear, same premultiplied mix and clamping).
- Tests: per-mode GPU-vs-CPU parity (gpu crate: overlapping transformed
  layers, partial opacity/alpha, empty/partial backdrop; golden: every
  mode through plan_frame incl. an effect layer, blended clips in a
  cross dissolve), exact-value edge cases (black/white, Divide by 0,
  Dodge/Burn at 0 and 1, zero alpha) within 4.9e-4, Dissolve pattern
  exact, off-output layers, quad bounds.
- Bench: new stack3-blend playback scenario (V2 Screen, V3 Overlay);
  CPU ms/frame 151-214 -> 86-89, Full 19/173 and 138/54 -> 191/1, 192/0.
- Docs: architecture §5, gpu crate docs, testing, performance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 03:25:16 -04:00
Brandon Thomas 0948dcf682 Merge branch 'main' into M3.13/M8.10 2026-10-03 11:50:01 -04:00
Brandon Thomas 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 Thomas 81873deedb Merge branch 'main' into worktree-agent-a861ca53f77717930
# Conflicts:
#	Cargo.toml
#	docs/architecture.md
#	xtask/src/main.rs
2026-10-03 08:55:10 -04:00
Brandon Thomas 8e7eebce41 M6.6: fix the architecture crate table after merging the MXF writer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:12:07 -04:00
Brandon Thomas 9ec0c7213b Merge branch 'worktree-agent-a1a2bf10c26331f84' into worktree-agent-a861ca53f77717930
# Conflicts:
#	docs/architecture.md
2026-10-03 07:11:48 -04:00
Brandon Thomas 41942e17d8 Merge branch 'main' into worktree-agent-a3c3d94e29f8131bb 2026-10-03 07:11:09 -04:00
Brandon Thomas 167f971e05 Merge branch 'main' into worktree-agent-a861ca53f77717930
# Conflicts:
#	crates/ui-egui/src/panels/dialogs.rs
2026-10-03 05:04:28 -04:00
Brandon Thomas 6cddaf08aa M11.4: document AAF / OMF interchange and the cfb crate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:55:23 -04:00
Brandon Thomas 09947712d0 M6.6: MXF OP1a / OP-Atom writer and MXF export formats
filmcraft-mxf gains a streaming writer (ST 377-1 partitions, primer, header
metadata, VBR index segments with temporal offsets, RIP; ST 378 OP1a
frame wrapping, ST 390 OP-Atom clip wrapping) for VC-3, ProRes, AVC byte
stream and PCM with start timecode and caller-supplied package UMIDs, plus
write_opatom_pcm. filmcraft-export adds mxf-op1a / mxf-opatom formats
(DNxHR, ProRes or H.264 + PCM; OP-Atom writes one mono file per channel),
presets, the engine mxfVideoCodec parameter and CLI .mxf inference.
Tests read our files back and check them against ffprobe / ffmpeg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:45:27 -04:00
Brandon Thomas 72e0dda167 Merge main into M9.15 2026-10-03 04:16:41 -04:00
Brandon Thomas 263a39132f M9.15: READMEs for mpeg2v / mpegts / ac3 (specs, accuracy, benchmark); architecture and testing docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:14:23 -04:00
Brandon Thomas d5e3d7e2ef Merge branch 'main' into worktree-agent-a8349582751c0ec31 2026-10-03 03:51:43 -04:00
Brandon Thomas 02a7039ac5 M11.4: filmcraft-cfb, a Compound File Binary ([MS-CFB]) reader and writer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:51:18 -04:00
Brandon Thomas c1c846aaad M12.7: Project panel and Media Browser parity
List / Icon / Freeform views, Metadata Display, view presets, font size,
Preview Area, bin tabs and windows, hover-scrub In/Out; Media Browser tree,
Favorites, history, file types, thumbnails, Edit Columns, import and drag.
Engine: project_panel and media_browser modules, Services::list_entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:14:31 -04:00
Brandon Thomas 0bc1d4e8b8 M4.9: merge main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:04:16 -04:00
Brandon Thomas aa3ad1d950 M4.9: H.264 inverse transforms reconstruct across columns at once
The column pass and the add/clip of idct4/idct8 work on whole rows so they
vectorise (same integer steps in the spec's row-then-column order; checked
against the per-column formulation on random blocks). Docs: draft decoding in
architecture.md and perf.stats fields in control-protocol.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:46:52 -04:00
Brandon Thomas 1fa560c1c7 M7.8: audio parity — 5.1 multichannel, voice-over record, Remix, Clip Mixer automation
5.1 / multichannel:
- audio-dsp::channels: layouts (L R C LFE Ls Rs), ITU-R BS.775 up/downmix, 5.1 Mixdown Types,
  equal-power 5.1 panner (puck, Center %, LFE).
- Mixer graph buses are 2 or 6 channels wide; 5.1 tracks/submixes/Mix; strips feeding a 5.1 bus
  use the 5.1 panner (lanes pan51.x/y/center/lfe stored in MixerStrip::lanes, no schema change);
  5.1 strips fold into stereo buses with BS.775; per-channel meters; clip effects on 6 channels.
- file.newSequence {mix, trackType}, sequence.settings {mix}; playback honours the 5.1 Mixdown
  Type preference on stereo devices and plays 6 channels on 5.1 devices.
- Export Audio Channels 5.1: WAV WAVE_FORMAT_EXTENSIBLE (mask 0x3F), QuickTime PCM with a chan
  atom, AAC channel configuration 6 (reordered to C L R Ls Rs LFE).
- Track Mixer: 5.1 panner puck with Center/LFE, N-channel meters, Show/Hide Tracks, Meter
  Input(s) Only; transport record button runs voice-over.

Voice-over: engine AudioInput trait + SyntheticInput, cpal input in the app, audio.voiceover.*
commands (punch-in, pre/post-roll, countdown cues, WAV next to project / scratch disk, one undo
step), track header mic button and Voice-Over Record Settings dialog.

Remix: beat tracking + chroma/cepstra self-similarity planner in audio-dsp, hidden `remix` clip
effect holding the plan, render::remix playback with equal-power crossfades, clip.remix.*
commands, Remix tool and Remix Properties dialog.

Clip Mixer automation: per-track Latch/Touch/Write (clipMixer.setMode/touch/release) heard live
and written as thinned clip Volume/Panner keyframes at the end of the pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:40:35 -04:00
Brandon Thomas 84f7df6479 Merge branch 'main' into worktree-agent-a7998c724fdfa5d6b 2026-10-02 20:09:15 -04:00
Brandon Thomas 9dde8107ad Merge main into M6.5 export parity
# Conflicts:
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/control.rs
#	docs/control-protocol.md
2026-10-02 19:59:52 -04:00
Brandon Thomas c59d21181f M6.5: export parity — presets, Export mode settings, queue, Quick Export, image sequences
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:27:19 -04:00
Brandon Thomas c98f5b76d3 Merge branch 'main' into worktree-agent-a1e148ad04b0018ef
# Conflicts:
#	ROADMAP.md
2026-10-02 17:33:45 -04:00
Brandon Thomas efa8167962 M9.14: MXF (OP1a/OP-Atom) and Ogg demuxers, image sequences, BWF timecode
- crates/mxf (L0): KLV walk, partitions, primer, header metadata, index tables, material ->
  file package resolution, AVC/VC-3/ProRes/MPEG-2 identification, PCM/AES3 (ST 331) sound,
  timecode; OP1a and OP-Atom (clip wrapping).
- crates/ogg (L0): RFC 3533 pages/CRC/packets, RFC 7845 Opus timing (granules, pre-skip, end trim).
- codecs: MxfSource (GOP cache, POC ordering for AVC without temporal offsets, MPEG-2 reported
  unsupported), OggSource (Opus with pre-roll seeking, Vorbis), Annex B H.264 decoder.
- media: image sequences (numbered stills, missing frames held, frame cache); BWF bext/iXML
  TimeReference -> start timecode.
- engine/UI: file.import {imageSequence}, auto-detect setting, file.importImageSequence,
  Media Browser "Import as Image Sequence"; CLI probe details for MXF/Ogg/BWF/sequences.
- Oracle tests vs ffmpeg for MXF, Ogg, image sequences, BWF; truncation/mutation tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 17:33:29 -04:00
Brandon Thomas 45b8c0c2a0 M8.9 + M12.6: Lumetri Scopes complete; Metadata, Timecode, Events, Progress, Reference Monitor panels
M8.9 (scopes):
- New L3 crate filmcraft-scopes (wasm-clean): Waveform RGB/Luma/YC/YC no Chroma,
  Parade RGB/YUV/RGB-White, Histogram, Vectorscope YUV (75/100 % targets, skin-tone
  line) and HLS; Rec. 601/709/2100 matrices, Clamp Signal, 8-bit/float/HDR (PQ cd/m2)
  scales; numeric summaries; density painting. < 1 ms per scope at 1080p.
- Lumetri Scopes panel rewritten: multi-scope grid, wrench / right-click menu (presets,
  scopes, parade/waveform type, targets, colour space, brightness, scale), footer
  (colour space, Clamp Signal, 8 Bit/Float/HDR). Settings in UiState::panels.
- scopes.read engine query returns levels, histogram bins, waveform/parade columns and
  vectorscope peaks.

M12.6 (panels):
- Metadata panel with editable Name/Label/log fields (metadata.get / metadata.set,
  one undo step, stored in ProjectItem::metadata; no schema change, stays v11).
- Timecode panel (rows: current/media/duration/in-out/remaining, any time display).
- Events panel on a new engine event log (failed/disabled commands, toasts, auto-save
  errors, jobs started/finished/failed/cancelled; events.list / events.clear).
- Progress panel (jobs with progress and Cancel).
- Reference Monitor (parked or ganged to Program; picture or scopes).
- All open from Window > menu, have automation ids; ui.set {"panels": ...} merges state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 17:32:22 -04:00
Brandon Thomas f5aa00ff7d Merge branch 'main' into worktree-agent-aacf40607c4b686e0
# Conflicts:
#	xtask/src/main.rs
2026-10-02 13:01:34 -04:00
Brandon Thomas 1f6562c08a M4.8: HEVC transform/SAO speed-up, catch-up decoding, perf.stats
- HEVC inverse transform: sum basis rows of non-zero coefficients with a
  const-generic size (bit-exact, tested against the direct product);
  SAO edge offset gets a check-free interior loop. HEVC CPU/frame -60%.
- H.264/HEVC/VP9 planes are moved into VideoFrames, not copied.
- Catch-up decoding: frame jobs tell sources which frames are late
  (filmcraft_media::cancel::with_catch_up); the GOP cache skips
  non-reference samples of late frames (H.264 nal_ref_idc 0, HEVC
  sub-layer non-reference pictures). Scrubs keep the last 2 s decoded.
- perf.stats: engine query + UI/control method (decode counters,
  playback shown/dropped, frame-worker decode/render ms, cache use).
- Timeline waveform source peak cached; bench seek section and --only.
- docs/performance.md with before/after results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:01:16 -04:00
Brandon Thomas 3139e63870 M5.11: Effects panel Legacy bin and alphabetical folders, Text param editing, vfx goldens and docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:04:45 -04:00
Brandon Thomas f5ac5989f0 Merge branch 'worktree-agent-a7d751de0d72abab7' into worktree-agent-a6d2cbb6d6b3e5d7a
# Conflicts:
#	crates/edit/src/lib.rs
2026-10-01 23:31:39 -04:00
Brandon Thomas fc18a6962e M10.5: Text-based editing / transcripts
- crates/speech (L2): Transcriber trait, FixedTranscriber, Whisper model catalogue (pinned,
  SHA-256 verified) with optional downloader (feature `download`: ureq + rustls + RustCrypto),
  pure-Rust Whisper inference on candle with word timestamps (feature `whisper`), log-mel, VAD
  word tightening, MFCC speaker clustering. Both features off by default and for wasm.
- project: per-media-item transcripts (Project::transcripts); schema v9 with a no-op v8->v9
  migration and a v8 fixture.
- edit::transcript: sequence transcript derived from clip transcripts, search, paragraphs,
  word ranges, pause/filler detection, ripple delete of many ranges, caption layout.
- engine: transcript.* commands (generate, set, delete, inspect, search, select, extract, lift,
  renameSpeaker, removeFillers, removePauses, createCaptions, models, downloadModel);
  Session::transcriber for hosts/tests; features `whisper` / `speech-download` (also on the
  desktop and CLI apps). Without whisper, transcript.generate fails with a clear error.
- ui: Text panel Transcript tab (Transcribe button, speaker paragraphs of clickable words,
  Shift+click selection -> In/Out, extract/lift/remove fillers/create captions, search) with
  text.transcript.* automation ids; headless UI test.
- docs/transcripts.md, crates/speech/README.md, architecture and project-files updates.
  whisper-tiny measures 12.2% WER over 797 words on local samples.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:25:49 -04:00
Brandon Thomas 2d5267a7df M12.4: agent-friendly CLI: exec/describe/inspect/import/export, run from stdin with JSON lines and --keep-going, --save/--save-as, --bridge to the live app, exit codes; tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:26:44 -04:00
Brandon Thomas 5f613aff25 M13.2: FilmCraft web app (apps/filmcraft-web) with Blob media, OPFS recovery, WebAudio, WebCodecs
- eframe web runner on WebGPU (WebGL2 fallback; GPU compositor on WebGPU, CPU on WebGL2).
- fs: virtual file table of File/Blob handles, 1 MiB chunked async range reads with an
  LRU cache and read-ahead (pending + retry instead of blocking), downloads for written
  files, /opfs/ paths written to OPFS.
- import: File System Access picker / <input type=file>, drag-and-drop anywhere,
  container index prefetch (moov wherever it is), retried imports; project open/save.
- recovery: OPFS auto-save snapshot while dirty, media copies, reopened on next visit.
- audio: AudioWorklet output fed ahead from the UI thread; played-frame audio clock.
- webcodecs: H.264/HEVC/VP9/AV1 MP4/MOV decoded by the browser VideoDecoder via a
  reader opener (fallback to our decoders on any error), probed with isConfigSupported.
- api: window.filmcraft (execute/request/commands/screenshot/importFiles/importUrl/
  openProject/files/readFile/info).
- cargo xtask web [--dev] [--serve PORT] (wasm-bindgen CLI); xtask wasm also checks
  filmcraft-ui-egui and filmcraft-web; layer L6 for the web app.
- Original web icon (favicon.svg) with attribution sidecar; docs/web.md; headless-Chrome
  smoke test (tests/smoke.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:52:35 -04:00
Brandon Thomas dc9c5fd32c Merge branch 'main' into worktree-agent-a98dbfec8c9b949c7 2026-10-01 13:06:51 -04:00
Brandon Thomas f0c705493a M9.7: AV1 inter-GOP seek tests in MP4/WebM, decode perf bench, docs and roadmap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:03:34 -04:00
Brandon Thomas a9278c81cc Merge M12 (multicam) into M5.5–7 (masks/presets): schema v7 = masks, v8 = multicam (WIP: CI not yet run)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:55:13 -04:00
Brandon Thomas 35a2d2a861 M9.7: AV1 import from MP4 (av01/av1C) and Matroska/WebM (V_AV1)
Av1Decoder in filmcraft-codecs: feeds the av1C configuration OBUs before the
first sample (and after every reset), converts pictures to 8/16-bit planar
YUV with the stream's colour description, and reports random-access points
from the first frame header of each temporal unit (shown key frame) so GOP
seeking ignores bogus container sync flags. Matroska V_AV1 tracks map to an
av01 sample entry; MP4 labels show the AV1 profile.

All-intra 8-bit MP4 and 10-bit WebM files seek sample-exactly against
libdav1d. Inter frames still return "unsupported" until inter prediction
lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:18:49 -04:00
Brandon Thomas b351559ffe M5.5: mask tracking (Shi-Tomasi features, pyramidal Lucas-Kanade with forward-backward check, RANSAC similarity/rigid/translation fit) as a background job writing Mask Path keyframes; track buttons + method menu in Effect Controls
masks.track {direction, frames?, method?, wait?}: progress + cancel via jobs.*, keyframes written live as one undo step per run. Synthetic known-motion tests: <0.75 px drift over 10 frames (render), 0.29 px max vertex error over -8..+12 frames end to end (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:14:37 -04:00
Brandon Thomas 650a266352 M12.2: multicam and sync — Synchronize (In/Out/timecode ± hours/clip marker/audio), Merge Clips, Create Multi-Camera Source Sequence (camera names, audio camera 1/all/switch, Processed Clips bin), multi-camera clips (angle render, Switch Audio mix, GPU plan passthrough), switch angle / Ctrl+N cut / keys 1–9, live recording as one undo step, Enable/Flatten/Edit Cameras, audio follows video; nested sequence audio now plays; project schema v7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:08:45 -04:00
Brandon Thomas 1813bbaca2 M9.7: AV1 headers, symbol decoder, CDFs, intra decoding, transforms, intra prediction
New L0 crate filmcraft-av1 from the AV1 Bitstream & Decoding Process
Specification v1.0.0 with Errata 1 (tables extracted from the spec's Markdown
source by tools/extract_tables.py).

Stages: OBU parsing, sequence and uncompressed frame headers (all fields),
reference state (set_frame_refs, load_previous, reference update,
show_existing_frame), symbol decoder with CDF adaptation and
init/load/save/frame-end CDF handling, tiles, intra frame mode info
(partitions, segment ids, delta q/lf, CDEF index, modes, angle deltas, CfL,
palette, filter intra, tx size), coefficient decoding, dequantisation,
inverse transforms (DCT 4-64, ADST, identity, WHT), intra prediction (all
modes, edge filter/upsampling, recursive filter, CfL, palette), LR syntax.

All-intra libsvtav1 streams with in-loop filters disabled decode bit-exactly
against libdav1d (8/10-bit, 128x128..1280x720, odd sizes, presets 2-8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:00:03 -04:00
Brandon Thomas 226c139741 M9.6: DNxHD/DNxHR (SMPTE ST 2019-1 VC-3) decoder and DNxHR encoder
New L0 crate filmcraft-dnx, written from SMPTE ST 2019-1:2016 + Amd 1:2023
(tables extracted from the standard's text by tools/extract_tables.py).

Decoder: every compression ID (DNxHD 1235-1260 incl. field-coded and
macroblock-adaptive interlace, thin rasters, RGB 4:4:4; DNxHR 1270-1274
LB/SQ/HQ/HQX/444), 8/10/12-bit, 4:2:0/4:2:2/4:4:4, DCT and lossless alpha,
macroblock rows in parallel. Matches ffmpeg within IDCT precision (max 2 LSB,
<=50 per million beyond 1, 87-99% identical) on 22 ffmpeg fixtures; follows
ffmpeg where it departs from the standard's text (reconstruction offset,
CID 1271 weights, 4:4:4 signalling) - documented in the README.

Encoder: DNxHR LB/SQ/HQ/HQX(10/12-bit)/444, CBR at the eq. 7.1 frame sizes,
uniform-qsf binary search + per-macroblock refinement; ffmpeg decodes every
profile with -xerror (HQ luma PSNR 58.8-66.3 dB).

Wiring: MOV AVdn/AVdh import (codec label from the CID), export
Format::DnxHr (MOV AVdh, dnxProfile lb|sq|hq|hqx) in the export dialog and
file.exportMedia.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:53:02 -04:00
Brandon Thomas fbaf487237 M5.5: effect masks (ellipse / 4-point polygon / pen Bezier) with keyframable path, feather, opacity, expansion, inverted, modes; opacity masks; WGSL coverage parity; on-monitor editing
- project:😷 MaskPath (cubic Bezier, clip pixels) as ParamValue::Path (vertex-wise keyframe interpolation), Mask, MaskMode, TrackMethod; schema v7 (no-op migration)
- render:😷 analytic antialiased coverage via signed distance + feather falloff in linear light; masked effect = lerp(original, effected, coverage); opacity masks before Motion; adjustment layers
- gpu::GpuMask: same coverage + mix in WGSL compute (max |cpu-gpu| ~3e-6)
- engine masks.* commands (add/remove/set/moveVertex/translate/addVertex/removeVertex/toggleVertexSmooth/select/list); keyframe commands take "mask"
- UI: Effect Controls mask rows; Program monitor overlay (vertices, handles, feather/expansion handles, body drag, pen tool) with automation ids
- goldens: mask_blur, mask_color, mask_inverted, mask_opacity

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:48:47 -04:00
Brandon Thomas 9f4208bb08 Merge main into M11.7–9 (relink, proxies, project manager): GopCache keeps main's continuation algorithm with a same-thread reentrancy guard (nested rayon requests decode privately instead of deadlocking); ProRes export combines profile choice with HDR signalling; project schema v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:19:19 -04:00
Brandon Thomas a1cc0a6838 M11.9: proxies (create / attach / toggle / reconnect) and ingest settings
- media.createProxies: background job (jobs.list progress) transcoding with our own
  encoders — ProRes 422 Proxy ¼/½, ProRes LT ½, H.264 ¼/½ (audio included) — into
  <media dir>/Proxies or a chosen folder; attached when the job finishes
  (Session::poll_persistence applies finished media jobs).
- media.attachProxies (duration ±1 frame and frame rate must match; size/aspect may
  differ), media.detachProxies, media.reconnectFullRes, media.toggleProxies
  (Preferences ▸ Media ▸ enableProxies; bumps the view revision without dirtying the
  project), media.proxyPresets.
- Proxy frames are smaller; ProxySource reports the original size and the compositor
  derives its pixel scale from the frame, so Motion and effects render the same
  picture (44 dB PSNR vs full res with Motion + Gaussian Blur, 48 dB at ½).
  Export always renders full resolution.
- ExportSettings::prores_profile (proxy / lt / standard / hq).
- project.ingestSettings + file.import: copy (fingerprint-verified), transcode,
  create proxies, copy and create proxies.
- UI: Toggle Proxies button on both monitors (lit when on), Create Proxies dialog,
  Project panel Proxy submenu and P badges.
- Measured (one core, 4K H.264 testsrc2): CPU composite 201 → 35 ms/frame at ¼ with
  ProRes Proxy ¼, 277 → 131 ms at ½ with H.264 ¼; decode 9.2 → 0.7 ms with H.264 ¼.
- Tests: create+attach+toggle same composite modulo scale, attach checks, export
  ignores proxies, ingest copy + proxies, ignored 4K benchmark, headless UI test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:06:56 -04:00
Brandon Thomas a9af6fac71 Merge M8 (LUTs, colour management, Colour Match) into M4.6 playback; project schema v5 for colour management; architecture §5.3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:05:55 -04:00
Brandon Thomas c47c90f070 Merge branch 'main' into worktree-agent-a0342df5d4a386968 2026-10-01 09:03:47 -04:00
Brandon Thomas 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 Thomas 1653430c42 M8.8: colour management — sequence working space (Rec. 709 / Rec. 2100 PQ / HLG, wide gamut), Interpret Footage colour space, log/HDR decode with BT.2390 tone mapping, HDR export signalling, HDR scopes
- model: SequenceSettings::color (ColorPipeline: working space, wide gamut, Auto Tone Map Media;
  serde default Rec. 709) and Interpretation::color_space (override; None = metadata)
- render::colorman: source colour space from the override or VUI/colr/MKV metadata, decode tables
  (camera log with super-whites, PQ, HLG + OOTF), gamut matrix, tone mapping into SDR working
  spaces, gamut mapping; working → SDR monitor transform at the top level
  (RenderOptions::working_output keeps working values for HDR export and scopes). Plain Rec. 709
  media in a Rec. 709 sequence keeps the old fast path and stays on the GPU; managed layers and
  HDR/wide-gamut sequences composite on the CPU
- export: H.264 and ProRes of a PQ/HLG sequence encode BT.2020 PQ/HLG and signal it: VUI / ProRes
  header, colr (nclx/nclc), and for PQ mdcv + clli boxes and H.264 SEI (payloadTypes 137/144);
  `sdr: true` (and render previews) export tone-mapped SDR. isobmff reads/writes mdcv and clli
- commands: sequence.colorSettings (Sequence ▸ Color Management…), clip.interpretFootage
  (Clip ▸ Modify ▸ Interpret Footage…, Project panel context menu), color.spaces,
  media.colorInfo; dialogs with automation ids colorDialog.*
- scopes: HDR sequences show a cd/m² waveform on a PQ scale (203 marked) and a BT.2020
  vectorscope from the working-space render (scopes.hdrWaveform)
- display-referred wide-gamut input is desaturated into gamut keeping in-gamut colours exactly;
  the soft compression is reserved for camera gamuts
- tests: colorman unit tests, engine (interpret/undo, PQ working space values and display),
  goldens log_to_rec709 and hdr_tone_map (own renders + sidecars), export PQ/HLG × ProRes/H.264
  read back by our demuxer and ffprobe (color_transfer/primaries/space, mastering display and
  content light side data), headless UI (dialogs, HDR scopes)
- docs: architecture §5.2 (pipeline, HDR export, display colour management: no macOS EDR yet)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:56:47 -04:00
Brandon Thomas 995621ff67 M7.4: Essential Sound panel — Browse/Edit tabs, type buttons, collapsible sections with switches and sliders, presets, Clip Volume/Mute footer; docs
- crates/ui-egui/src/panels/essential_sound.rs: per-type sections (Loudness with Auto-Match,
  Repair, Clarity, Creative, Ducking with Generate Keyframes, Duration note, Pan), preset dropdown
  with save/delete, code-drawn type glyphs, automation ids for every control; every control runs an
  essentialSound.* command and a slider drag is one undo step
- headless kittest test (essential_sound_ui.rs) with optional wgpu snapshots
- docs: architecture §5.2, control-protocol ids, testing, ROADMAP

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:40:20 -04:00
Brandon Thomas 4b83e088eb Merge main into the playback branch (mixer recordStart moves to the end of preroll; GopCache keeps main's random-access key walk-back)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:30:04 -04:00
Brandon Thomas 2a6e27b35f M4.6: playback scheduling: preroll, scrub supersede, real-time frame skipping under CPU overload, hidden-window drops
- Play waits for the first PREROLL_FRAMES (6) frames, at most 0.5 s, before starting the clock
  and audio: the first frames after Play were due before any decoder could produce them.
- A new on-screen request for a view drops the queued and cancels the running request it
  replaces (scrubbing used to decode every visited position oldest-first).
- playback_plan: when per-frame CPU work (effects; decode excluded, it is sequential anyway)
  exceeds what the workers can do in real time, start only frames that can still be on time and
  space them evenly, instead of every frame arriving late. Cost estimate updated from finished
  and (as a lower bound) cancelled prefetch jobs. No change while the workers keep up.
- PlaybackMeter::resync: frames while the window is hidden (occluded/minimized) are not drops.
- Scopes render after the Program prefetch while playing (prio 40, was 2).
- ui.inspect playback.preroll; docs/architecture.md frame scheduling.

bench, same-session A/B, median of 3 (GPU path, Full, 8 s, load avg 22–64):
  h264-1080 190/2 → 192/0 shown/dropped (3/3 runs 0 dropped)
  stack3    189/3 → 192/0;  h264-2160 173/19 → 192/0;  demo 186/6 → 189/3
  seek-storm: 10 → 15 of 40 seeks shown within 150 ms, 1581 → 1300 ms CPU per seek
  after-preview live effects play (median of 2): 118/74 → 134/58, cancelled jobs 74 → 48;
    plays from previews 192/0 both

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:10:50 -04:00
Brandon Thomas 9baa34fa45 Merge M10.1–2 + M3.2/M3.11 into M7.2 (audio mixer): preferences gain audio + playback + trim groups; ROADMAP refreshed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:17:12 -04:00