Commit Graph

1627 Commits

Author SHA1 Message Date
Brandon Thomas ba72c8944b Merge pull request #1534 from HEOJUNFO/fix/settings-text-overflow
fix(settings): keep long translations on the Preferences pages (#1517)
2026-10-13 00:03:37 -04:00
Brandon Thomas 76f423db77 Merge pull request #1532 from JeremyYao/fix/windows-duplicate-vulkan-driver
graphics: draw with the newer driver when Windows lists one GPU through two versions of it (#609 on the #827 start-up)
2026-10-13 00:03:37 -04:00
Brandon Thomas 1c7aa21957 Merge pull request #1526 from marcoarnulfo/fix/timeline-fourth-video-track
Timeline: scrolling the video tracks up leaves room to drop a new track
2026-10-13 00:03:35 -04:00
Brandon Thomas e6ba11e286 Merge pull request #1518 from sanyamsingh001/codex/ttml-speaker-metadata-oct12
captions: preserve speaker names in TTML metadata
2026-10-13 00:03:35 -04:00
Brandon Thomas 4e1f673ce0 Merge pull request #1516 from jnohclee-rgb/dharma/preserve-subclip-sources
fix(engine): preserve offline sources referenced through subclips
2026-10-13 00:03:34 -04:00
Brandon Thomas 5f60d980f6 Merge pull request #1512 from CokeRoach/fx-folder-names
Effects panel: keep the whole name of a folder that contains a slash
2026-10-13 00:03:33 -04:00
Brandon Thomas e565d4a2fd Merge pull request #1353 from marcoarnulfo/fix/mask-handles-selection
fix(masks): hide the mask handles while the clip is not selected
2026-10-13 00:03:32 -04:00
Brandon Thomas aa8d808a48 Merge pull request #1367 from marcoarnulfo/fix/pen-mask-undo-point
fix(masks): Delete / Backspace and Cmd+Z take back the last pen point while drawing
2026-10-13 00:03:32 -04:00
Brandon Thomas fae6adc577 Merge pull request #1386 from marcoarnulfo/fix/nested-audio-preview-hash
fix(previews): a nest's audio preview follows the sound of its sequence (#1370)
2026-10-13 00:03:31 -04:00
Brandon Thomas bd5261c8a3 Merge pull request #1383 from vangel76/fix-1371-list-presets
#1371: export preset query also matches the format id
2026-10-13 00:03:30 -04:00
Brandon Thomas 20487653ce Merge pull request #1378 from vangel76/fix-1368-nudge-track
#1368: nudge clip selection up/down works for linked video+audio
2026-10-13 00:03:29 -04:00
Brandon Thomas 1ee3f85c4c Merge pull request #1245 from JordyUSA/feat/audio-32bit-export
Export: 32-bit integer and 32-bit float audio
2026-10-13 00:03:28 -04:00
Brandon Thomas 37c17d7073 Merge pull request #1232 from haranishi/fix/1187-drag-clips-new-track
fix: 新規トラック移動のリンクと編集履歴を保持 (#1187)
2026-10-13 00:03:27 -04:00
Brandon Thomas 2ee1734de4 Merge pull request #1224 from liangforstudy/fix-help-link-tooltips
Help menu: say where each item goes; keep the address when no browser opens
2026-10-13 00:03:26 -04:00
Brandon Thomas 4046ff85f6 Merge pull request #1223 from webdevtodayjason/fix/ttml-fractional-ticks
Captions: preserve fractional TTML tick offsets
2026-10-13 00:03:26 -04:00
Brandon Thomas bb2f5958b6 Merge pull request #1218 from haranishi/fix/1184-clip-menu-marker
fix(timeline): クリップの右クリックにマーカー追加を用意する (#1184)
2026-10-13 00:03:25 -04:00
Brandon Thomas 0f174ab0d6 Merge pull request #1257 from AkashBhat14/fix-1249-apv-oracle-skip-old-ffmpeg
fix(apv): skip oracle tests when ffmpeg predates APV support
2026-10-13 00:03:24 -04:00
Brandon Thomas 0199bf3f37 Merge pull request #1259 from MarekSpodBiedry/issue-1251
test(i18n): skip the Japanese language step when no Japanese font is available (#1251)
2026-10-13 00:03:23 -04:00
Brandon Thomas 18dba32df7 Merge pull request #1309 from vbasky/fix/1295-column-padding
Pad sorted project-panel column headers
2026-10-13 00:03:22 -04:00
Brandon Thomas e61900cd3a Merge pull request #1302 from LloydNicholson/fix-1291-adts-aac-import
codecs: import raw ADTS .aac files (#1291)
2026-10-13 00:03:21 -04:00
Brandon Thomas dac0219ba8 Merge pull request #1298 from gyroninja/fix/macos-ctrl-click-context-menu
fix(ui): open the context menu on macOS Ctrl+Click
2026-10-13 00:03:20 -04:00
Brandon Thomas c2363399b9 Merge pull request #1275 from n-delic/fix-1081-align-video-clips
#1081: Align to Video Frame works on selected video and image clips
2026-10-13 00:03:19 -04:00
Brandon Thomas 1df794b882 Merge pull request #1274 from n-delic/fix-1023-eyedropper-transform-handles
#1023: the eyedropper picks from the Program monitor again
2026-10-13 00:03:18 -04:00
Brandon Thomas f25156c720 Merge pull request #1510 from Narmjep/fix/time-ruler
fix(timeline): stop Ruler timestamps disappearing while zooming (#1397)
2026-10-13 00:03:17 -04:00
Brandon Thomas 133f503851 Merge pull request #1507 from marcoarnulfo/fix/menubar-hover-switch
fix(menus): hovering another title switches the open menu
2026-10-13 00:03:17 -04:00
Brandon Thomas 5ecb74c73e Merge pull request #1496 from marcoarnulfo/fix/timeline-middle-button-pans
timeline: a middle-button drag pans, whatever the tool
2026-10-13 00:03:16 -04:00
Brandon Thomas 6c06ab14ca Merge pull request #1492 from marcoarnulfo/fix/hevc-pps-trailing-zero
fix(codecs): in-band parameter sets compare without trailing zero bytes
2026-10-13 00:03:15 -04:00
Brandon Thomas 5b0eaa9c30 Merge pull request #1489 from marcoarnulfo/fix/space-after-panel-drag
fix(ui): Space and other shortcuts keep working after dragging a pane divider
2026-10-13 00:03:14 -04:00
Brandon Thomas 2259e2ba15 Merge pull request #1481 from sanyamsingh001/codex/ingest-collisions-oct12
ingest: preserve same-named camera takes during ingest
2026-10-13 00:03:13 -04:00
Brandon Thomas 70031d5050 Merge pull request #1479 from marcoarnulfo/fix/audio-transitions
fix(audio): crossfade audio transitions through the clip handles
2026-10-13 00:03:13 -04:00
Brandon Thomas f5b8f11271 Merge pull request #1416 from vangel76/fix-1322-properties-scroll
#1322: the Properties panel scrolls
2026-10-13 00:03:12 -04:00
Brandon Thomas e8dd5ffc45 Merge pull request #1410 from vangel76/gap-radiance-hdr
#1407: import Radiance HDR stills and image sequences
2026-10-13 00:03:11 -04:00
Brandon Thomas 4ac34587b0 Merge pull request #1409 from vangel76/feat-gap-slot1
#1408: Shift constrains mask vertex and whole-mask drags to 45°
2026-10-13 00:03:11 -04:00
Brandon Thomas 2d2e701078 Merge pull request #1403 from vangel76/gap-mkv-mp1
#1402: import MP1 (A_MPEG/L1) audio from Matroska
2026-10-13 00:03:10 -04:00
Brandon Thomas 22ba7dd5c2 Merge pull request #1401 from webdevtodayjason/fix/vtt-tab-voices
Captions: preserve tab-separated WebVTT voices
2026-10-13 00:03:09 -04:00
Brandon Thomas 12c9f13d73 Merge pull request #1366 from jahvari/fix/windows-baseline-win03
WIN-03: Make AVI oracle file reads portable
2026-10-13 00:03:09 -04:00
Brandon Thomas 6bbd389f41 Merge pull request #1361 from marcoarnulfo/fix/sharpen-playback
render: Sharpen stays visible at reduced playback resolution
2026-10-13 00:03:08 -04:00
Brandon Thomas 3b177c9033 Merge pull request #1340 from marcoarnulfo/fix/clear-default-shortcut
fix(shortcuts): Clear and reassign also drop a command's panel copy of its key (part of #1333)
2026-10-13 00:03:07 -04:00
Brandon Thomas c7cc3088bd Merge pull request #1328 from marcoarnulfo/fix/mp4-nearest-frame
codecs: MP4 frames stamped just after the frame grid show on their own frame
2026-10-13 00:03:07 -04:00
Marco Arnulfo 3a78436665 fix(mcp): a slow bridge command no longer blocks every later call (part of #1538) 2026-10-13 05:40:35 +02:00
sanyamsingh001 13c89bf9df export: clip caption sidecars to the exported range
Co-authored-by: Codex <codex@openai.com>
2026-10-13 13:32:11 +10:00
heojunfo c11f7b5e09 fix(settings): keep long translations on the Preferences pages (#1517)
Field labels wrap in their column instead of running left over the
category list, dropdowns cut their current choice to their width,
checkbox labels wrap at the page edge, folder fields leave room for
Browse…, and category names too long for the list end in an ellipsis
with the full name in a tooltip. The page rect is registered as
settings.page; long_translations_stay_on_the_page checks every page
in eight languages against it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 11:45:59 +09:00
Jeremy Yao 1d1cec80b1 graphics: draw with the newer driver when Windows lists one GPU through two versions of it (#609)
A PC whose GPUs were installed from different driver packages of one vendor
(an ASUS G513QY: the integrated Radeon on the 2021 package, the RX 6800M on a
2026 one) gives the Vulkan loader two versions of that vendor's driver, and
the RX 6800M is listed once through each. wgpu's request_adapter takes the
first discrete entry, the 2021 driver's, and the window drawn through it dies
inside amdvlk64.dll (0xc0000005) before it paints: the main thread is in the
2021 driver, the fault on a thread of the 2026 one, no Rust frame anywhere.
Left to the #820 ladder, two crashed launches end in DirectX 12 only.

filmcraft_gpu::adapter::choose is wgpu's own rule (the adapters that can draw
to the surface, by device type for WGPU_POWER_PREF, then as enumerated) with
one addition: when the adapter that rule picks is listed again by the same
driver (same backend, device type, PCI vendor and device and driver name; the
same bus id when both report one), the entry exposing the most wgpu features
is used, i.e. the newer version (68 against 60 here, a strict superset).
Without such a duplicate the choice is wgpu's own, and entries of one GPU from
different drivers (RADV and AMDVLK, a vendor's driver and Dozen) are left
alone.

graphics::apply installs it as egui-wgpu's native_adapter_selector in the
default mode on Windows, as #609 was asked to be rebased onto the #827
start-up. DirectX 12 only, software mode and the other platforms keep their
adapter choice, and the ladder is untouched. The adapters and the choice go
to the log.

Tests: the laptop's listing (wgpu's rule picks the entry that crashed, choose
the other one), listings without two versions of one driver over every power
preference (choose equals wgpu's rule), surface, bus id and driver cases, and
one against the adapters of the machine running the tests (the rule is what
request_adapter returns there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:31:02 -04:00
Marco Arnulfo 4ad860cb69 Adjustment layers: a Transform replaces the picture below instead of drawing over it (fixes #1524) 2026-10-13 04:23:52 +02:00
Marco Arnulfo fe7efdaffc Timeline: scrolling the video tracks up leaves room to drop a new track (fixes #1522) 2026-10-13 04:22:50 +02:00
jnohclee-rgb e4a1a7fee3 fix(engine): retain surviving subclip chains and discard unused sources
Signed-off-by: jnohclee-rgb <336857373+jnohclee-rgb@users.noreply.github.com>
2026-10-13 09:30:49 +08:00
sanyamsingh001 2343fc8fa3 captions: preserve speaker names in TTML metadata
Co-authored-by: Codex <codex@openai.com>
2026-10-13 11:07:57 +10:00
jnohclee-rgb bcb7902f42 fix(engine): preserve offline sources referenced through subclips
Signed-off-by: jnohclee-rgb <336857373+jnohclee-rgb@users.noreply.github.com>
2026-10-13 08:56:52 +08:00
CokeRoach 9203732393
Effects panel: keep the whole name of a folder that contains a slash
The Effects panel drew a folder's name from the last `/`-separated part of
its key (`<top>/<folder>`), so Audio Effects ▸ Noise Reduction/Restoration
showed as "Restoration" — truncated in English and untranslated in every
other language, since the catalogs translate the full name. Split once,
after the top folder, instead.

Test: effects_panel_lists_the_premiere_transition_folders checks the label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XWUReif4gqcjU5GsjYioc
2026-10-13 09:40:52 +09:00
Narmjep 3b3599d11c fix(timeline): stop Ruler timestamps disappearing while zooming
The Ruler walked one grid (the tick stride) and labelled a frame only when it
was also a multiple of the label stride. Chosen independently, the two need not
divide: at 24 fps and 100 pt a second, 5-frame ticks against 48-frame labels
meet only every lcm(5, 48) = 240 frames — one timestamp per 10 s, the rest
returning at the next zoom step, where the ladder happens to divide.

Take the tick stride as the finest division of the label stride that still
leaves ticks 12 pt apart, so both are one grid. Every label then falls on a
tick and can no longer be skipped, and the ticks stay evenly spaced around it
instead of bunching either side. Labels are >= 110 pt apart, so a divisor of at
most 9 always meets the minimum. The labelled stride, and so every timecode and
the frame it is drawn at, is unchanged.

Strides are clamped to >= 1 (the loop advances by one and tests the other for
divisibility), and the ladder's products saturate, as FrameRate is plain serde
data from the project file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-13 02:27:47 +02:00
Marco Arnulfo f87796f0c8 fix(menus): hovering another title switches the open menu (fixes #1500) 2026-10-13 02:07:14 +02:00
CokeRoach b97bb74330
i18n: Korean (ko) interface catalog (#508)
Adds Korean as an interface language: Edit ▸ Language ▸ 한국어, Settings ▸
General ▸ Interface Language, and System Language for ko / ko-KR locales.

- crates/ui-egui/src/i18n/ko.tsv: 3,573 entries, the Spanish catalog's sources
  plus the macOS Settings item, written clean-room from the plain meaning of
  the English text (sidecar ko.tsv.attribution, ATTRIBUTION.md row).
- Hangul is drawn with the installed system face cjk.rs already adds (Malgun
  Gothic, Apple SD Gothic Neo, Noto Sans CJK KR…); switching is refused with a
  message when there is none, like Chinese.
- Test: korean_covers_the_menus_and_has_no_dead_entries.
- Docs: localization-parity, ROADMAP, ui-egui README.

Native-speaker review welcome.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XWUReif4gqcjU5GsjYioc
2026-10-13 08:44:01 +09:00
Marco Arnulfo 95297477fd timeline: a middle-button drag pans, whatever the tool (fixes #1490)
The monitors already pan with the middle button (#824). In the Timeline a middle-button drag went
down the left button's path and selected, moved, marqueed, scrubbed or cut, depending on the tool.
It now starts the Hand tool's pan from where the button went down.
2026-10-13 01:31:43 +02:00
Marco Arnulfo c1b1e86046 fix(codecs): in-band parameter sets compare without trailing zero bytes (fixes #1483)
ffmpeg keeps a trailing zero byte after the hvcC PPS that the in-band PPS lacks, so the byte-for-byte comparison read it as a parameter-set change and sent the stream to the software decoder from its first sample. A NAL unit never ends with 0x00 (H.264 7.4.1, H.265 7.4.2): those bytes are trailing_zero_8bits, so both sides are compared without them. The platform fallback test used a trailing zero byte as its changed SPS; it now changes nal_ref_idc.
2026-10-13 01:16:52 +02:00
Marco Arnulfo bef9a6e127 fix(ui): Space and other shortcuts keep working after dragging a pane divider (fixes #1482) 2026-10-13 01:12:57 +02:00
sanyamsingh001 fa7c6ff8ab ingest: preserve same-named camera takes during ingest
Co-authored-by: Codex <codex@openai.com>
2026-10-13 08:42:29 +10:00
Marco Arnulfo e22a49db1c fix(audio): crossfade audio transitions through the clip handles (fixes #1473) 2026-10-13 00:25:27 +02:00
Fabio Lussi ddaf78c221 Keep transform handles in reach when the picture is past the monitor edge
The Program monitor's transform box drew and hit-tested its handles at
their true positions only, and the painter and the ui clip stop at the
picture area. A still moved to or past the frame edge left its resize
handles off the monitor. Handles beyond the monitor now stop at its
edge (the picture itself is not moved), and the box is drawn over the
whole monitor rather than only the picture.

Fixes #1393

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:21:48 +02:00
sanyamsingh001 d91a7b3122 ui: keep Metadata and Info on the selected source item
Co-authored-by: Codex <codex@openai.com>
2026-10-13 08:11:35 +10:00
Marco Arnulfo 59506d9b4a fix(home): list recent projects on Home and in File ▸ Open Recent (fixes #1461) 2026-10-12 23:59:49 +02:00
Marco Arnulfo e5b1ed90a9 fix(dock): dock a panel along the edge of the whole workspace (fixes #1463) 2026-10-12 23:55:59 +02:00
bfenty 1ef2960514 h264: validate PPS fields before arithmetic; conceal weighted_bipred_idc 3 (#1441) 2026-10-12 21:15:13 +00:00
vangel76 d034c58edd #1450: cargo fmt for the new mpegts tests and window field
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:05:40 +02:00
vangel76 5198059a7c #1450: borrow program stream packets from the window instead of copying them
scan and read copied the 32-byte head of every packet and every pack, PSM
and PES packet (up to 64 KB) with to_vec before looking at it. They now
borrow the window; the borrows end before the next window access. Output is
unchanged: the new large-stream test (two streams, over 2 MiB, MPEG-1 and
MPEG-2 packs, cuts inside the last packets) passes on the old and the new
code, as do the existing round-trip and truncation tests. The allocation
saving follows from the removed to_vec calls; it is not measured (the
workspace forbids unsafe, so there is no counting allocator).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:05:29 +02:00
vangel76 afc54a5f38 #1450: do not zero-fill the program stream window past the end of the file
A window refill resized the buffer to at least 1 MiB although read_some can
only fill what the file still holds. Near the end of the file, where the
previous window was shorter, that zeroed about a megabyte for every packet
whose length ran past the end. The buffer is now capped at the bytes left.
Window contents are unchanged: proved against the old refill for many
lengths, positions and sizes, and the zeroed byte count is asserted (at most
1000 against about 500 MiB for the old refill in the tail scenario).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:04:44 +02:00
vangel76 da13964a82 #1450: table-driven CRC-32 for PSI sections
One table lookup per byte instead of eight shift steps. Proved equal to the
old bitwise loop (kept in the test as the reference) for every single byte,
random inputs of 0..4096 bytes, the CRC-32/MPEG-2 check value and the
section-plus-own-CRC-is-zero property.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:04:44 +02:00
vangel76 1990bde6d3 #1450: fix two stale doc comments in mpegts
Unit.pos for transport streams is a byte offset (scan stores off + k *
packet_size), not a packet index; frame_bytes carried the doc line of
frame_info above its own.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:02:55 +02:00
vangel76 5b76035c3d #1450: drop the unread PesHeader.stream_id and packet_length
Nothing outside one test read them. No behaviour change; the existing
header tests cover parse.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 23:02:55 +02:00
Marco Arnulfo 4d58c67745 fix(bitstream): map se(v) through the 64-bit codeNum so i32::MIN round-trips (fixes #1428) 2026-10-12 22:48:18 +02:00
MarekSpodBiedry 542300e911 fix(media-browser): drag every selected file to the Timeline, not just one (#1360) 2026-10-12 22:30:00 +02:00
vangel76 393284d78f #1430: mpegts: resync inside the loaded block, bad adaptation field is not lost sync
Problem: in ts::scan every packet-parse failure re-read a whole 4096-packet
block from an offset that advanced about one packet. packet() returned None
both for a bad sync byte and for an adaptation_field_length that overruns the
packet, so aligned packets with a bad adaptation field cost one ~770 KB read
each (a 564 KB file made ByteSource::read_at fetch 846 MB). The fallback
offset (`pos + got`) also overshot by k packets, skipping unread data, and the
five-packet sync window only looked at the first packet_size start positions.

Fix: packet() distinguishes Bad::Sync from Bad::Adaptation. A bad adaptation
field means "no payload, grid intact": count a warning and go on with the next
packet. A real sync loss is repaired inside the loaded block (resync(): first
start whose five sync bytes line up, any distance); the block is refilled
from the file at the exact new offset only when it is used up. read() skips a
bad-adaptation packet instead of ending the unit.

Tests: read-volume tests with a counting ByteSource (bad adaptation fields,
sync byte every 189 bytes), and a resync test with a garbage prefix and
mid-stream garbage around the block boundary for 188- and 192-byte packets.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 22:09:27 +02:00
vangel76 c6b5943e7e #1432: mpegts: bounds-check frame_info, cap chunks, units and allocations, drop non-PES data
Problem: frame_info indexed byte 6 of an AC-3 header that is only guaranteed
6 bytes long (panic), and gave E-AC-3 fscod2 3 a sample rate of 0. The PES
modes of Splitter never pruned their chunk list. read_unit reserved up to
4 GiB up front, the unit table was uncapped and 4-byte E-AC-3 "frames"
amplified input 16x. PesAsm kept passing continuation packets of a non-PES
unit start, and an adaptation-field-only unit start destroyed the PES in
progress.

Fix: b.get(6)? and None for the reserved rate; no chunk bookkeeping where
positions come from feed(); EAC3_MIN_FRAME = 8; MAX_UNITS (2^23) per stream
with a warning; read_unit preallocates at most 1 MiB and refuses units over
128 MiB; PesAsm gets a dropping state and ignores payload-less unit starts.
Regression tests for each.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 22:08:40 +02:00
vangel76 cf9ad66732 #1429: MPEG-1 program stream cut inside a pack header no longer panics
Problem: packet_len accepted an MPEG-1 pack header (b[4]>>4==2) with
only 6 bytes available, but scan reads the SCR from head[4..9]. A file
ending inside the last pack header (00 00 01 BA 21 00 00) panicked with
"range end index 9 out of range".

Fix: packet_len requires the full 12-byte MPEG-1 pack header; a shorter
tail is not a packet and takes the normal resync path. The MPEG-2 branch
already needs 14 bytes (b.get(13)) before scan reads 10, so it is fine.
Tests: truncate a valid MPEG-1 and MPEG-2 program stream at every byte
length, plus the minimal 7-byte input; both panicked before the fix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 22:07:25 +02:00
bfenty db6f5194fe Add edit.rippleDeleteHead (Ripple Delete Head Frame)
Remove the selected clips' heads - from each in point through the
playhead - and ripple the sequence closed. Linked clips act as one
group through edit::ripple_trim_group; a playhead past every member's
out point deletes the group whole via edit::ripple_delete_items.
Refused at or before a member's in point, and on locked members.

Tests: crates/engine/src/ripple_head_tests.rs (4 tests, red on main,
green with the patch, negative control by stash round-trip).

Fixes #1357
2026-10-12 19:52:29 +00:00
vangel76 8457e7bf31 #1322: the Properties panel scrolls
Problem: the Properties panel laid its sections out in a plain child ui, so
when the panel was shorter than its content (Transform, Crop, Audio, the
speed button) the lower part was cut off and the wheel did nothing.

Fix: put everything below the clip header in a vertical ScrollArea, keeping the
rows 14 px clear of the scrollbar so the keyframe arrows at the row end stay
clickable. The speed button gets the automation id `properties.speed`.
Regression test properties_scroll_ui scrolls a short panel and checks the
speed button moves; it fails without the fix.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:19:59 +02:00
vangel76 595f94f303 #1407: import Radiance HDR stills and image sequences
Problem: Premiere reads Radiance HDR (.hdr) pictures; FilmCraft listed
the extension nowhere and could not open them.

Fix: clean-room RGBE decoder in filmcraft-media from the public format
description: header with FORMAT, all eight axis orders, flat, old
run-length and new per-channel run-length scan lines. Frames are linear
float (Rec.709), like EXR. Allocations are bounded by the data present;
hostile headers and truncated data return errors. "hdr" joins the
importable still extensions; stills and image sequences share the decoder.
Output equals ffmpeg's decode exactly for flat and RLE files; EXPOSURE and
PRIMARIES lines are ignored and XYZE is refused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:12:44 +02:00
vangel76 9deba4a830 #1408: Shift constrains mask vertex and whole-mask drags to 45 degrees
Problem: Shift snapped pen segments (#853) but dragging an existing mask
vertex or the whole mask still moved freely.

Fix: while Shift is held the movement since the press is constrained to
horizontal/vertical/diagonal steps (accumulated raw vs applied totals per
gesture, so each frame's delta stays a single undo step). Unit test for
the pure step function; gaps.md G18 updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:12:19 +02:00
vangel76 af5d29310e #828: import PXR24-compressed OpenEXR
Problem: OpenEXR files with PXR24 compression (a common lossy 24-bit
float format from renderers and compositors) were refused with "not
supported yet".

Fix: decode PXR24 chunks (16 lines, deflated byte planes of per-line
sample differences; floats keep their top 24 bits) into the existing
scan-line pipeline. Bounds-checked, no panics on damaged data. Tests:
hand-built chunks with half/float/uint channels and a partial last
chunk, damaged-file cases, and an OpenImageIO-written oracle comparing
our decode with OpenEXR's own (skips without oiiotool). Docs updated;
stale AVI / FLAC-in-MP4 entries removed from G10.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:56:26 +02:00
vangel76 3516207de7 #1402: import MP1 (A_MPEG/L1) audio from Matroska
Problem: the Matroska demuxer mapped A_MPEG/L2 and A_MPEG/L3 only, so a
Layer I track was reported as an unsupported codec even though the MP1/MP2
decoder (#809) reads Layer I.

Fix: add Codec::Mp1 for A_MPEG/L1 and route it through the same packet
duration and decoder path as Mp2. Test remuxes synthetic Layer I frames
into Matroska with ffmpeg and compares our decode (max error 3e-5).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:55:35 +02:00
webdevtodayjason 20d196401a Captions: preserve tab-separated WebVTT voices 2026-10-12 13:51:26 -05:00
Marco Arnulfo 6192214def fix(previews): a nest's audio preview follows the sound of its sequence (#1370)
The content hash of a nested sequence covered only its video tracks, for
audio segments too. Changing the sound inside a nest (a clip's gain, a
muted track, a new clip) left the outer sequence's rendered audio preview
valid, so playback kept the old mix, or silence. Audio segments now hash
the nested sequence's mix (audio tracks, their clips recursively, master
and submixes) instead of its picture, so editing the picture inside a nest
no longer invalidates its sound either.
2026-10-12 19:45:00 +02:00
vangel76 5553e26e12 #1371: export preset query also matches the format id
Problem: `export --list-presets h264` returned nothing because the query
only matched name, description and category, while `h264` is the format
id the CLI takes everywhere else.

Fix: also match the preset's format id and label, compared with
punctuation stripped, so `h264`, `h.264` and `H.264` are equivalent.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 19:39:44 +02:00
vangel76 1d62db0172 #1368: nudge clip selection up/down no longer fails for linked video+audio
Problem: timeline.nudgeUp/nudgeDown returned an error for the whole selection when any selected clip was already on the outermost track in that direction. A video clip with its linked audio (A1 has no track above) therefore could never be nudged.

Fix: clips with no destination track stay where they are; the command only errors when nothing could move. Regression test covers a linked selection.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 19:35:54 +02:00
Nai-ITA d2ae945c30
Add files via upload 2026-10-12 18:56:50 +02:00
Marco Arnulfo c580406234 fix(masks): Delete / Backspace and Cmd+Z take back the last pen point while drawing (fixes #1345) 2026-10-12 18:45:56 +02:00
jahvari 44baf6c0f4 WIN-03: Make AVI oracle file reads portable
Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-10-12 11:43:18 -05:00
Marco Arnulfo 67f1ea1ae3 render: Sharpen stays visible at reduced playback resolution (fixes #1351) 2026-10-12 18:28:53 +02:00
Marco Arnulfo dadbfe0bf8 fix(masks): hide the mask handles while the clip is not selected (fixes #1341) 2026-10-12 18:17:42 +02:00
Marco Arnulfo 528e525d67 fix(shortcuts): Clear and reassign also drop a command's panel copy of its key (part of #1333) 2026-10-12 17:44:34 +02:00
Marco Arnulfo 3f61c036ff codecs: MP4 frames stamped just after the frame grid show on their own frame (fixes #1282)
Phone footage stamps its frames up to a millisecond after the frame grid
(a first frame 34.1 ms long at 30 fps). The frame shown at a time is the
last one starting at or before it, so a sequence frame asked at its exact
start got the picture before: the clip ran one frame behind and showed
frame 0 twice. The request is already rounded to the track timescale
(#73, as WebM in #83), but at 90 kHz a millisecond is 90 units.

When opening an MP4, the video samples of a file that is a constant rate
gone slightly off its grid are put back on it: the grid starts at the
first picture and steps by the nominal frame duration, and the file is
snapped only when every picture is less than a quarter of a frame from
its own grid point and no two pictures share a grid point. Anything
further off is variable frame rate and keeps all its times; a picture one
unit off is left where it is, since that can't be told from timescale
rounding. Only the video sample times move: the audio, the first picture
and the clip duration are unchanged.
2026-10-12 16:50:49 +02:00
Bart Boonstra 6bc526b5e1 Adjustment layers: a transition fades their effects in and out instead of cutting them in after it
During a transition on its track an adjustment layer rendered as nothing, because it has no picture
of its own: Cmd+D at its start left the effects off for the whole dissolve and then cut them in.
In a transition the layer is now the frame below with its effects (opacity, masks and Motion region
included), so a dissolve fades the effects. A fade from or to nothing keeps the layer's blend mode.
Frames where a transition touches an adjustment layer render on the CPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:28:00 +02:00
Vikram Bhaskaran 7fdccec8d7 Keep the project-panel sort arrow from covering a narrow column name. 2026-10-13 00:39:09 +11:00
gyro f21434cf9f fix(ui): open the context menu on macOS Ctrl+Click
On macOS, AppKit delivers Ctrl+Click to the winit view as a primary mouse press carrying the Control modifier — rightMouseDown only fires for a real right button or a two-finger trackpad click. winit 0.30 (button 0 → Left) and egui-winit 0.36 (Left → PointerButton::Primary) pass it through untouched, so egui's secondary_clicked() never fires and no context menu opens.

Context menus subscribe to egui's secondary click, so the translation is done once at the input boundary, as the platform convention requires: winit deliberately leaves Ctrl+Click handling to the application (rust-windowing/winit#4469, wontfix), and macOS apps translate it themselves (Firefox does the same in nsCocoaWindow.mm).

raw_input_hook runs before egui begins its pass, when pointer click state is computed — the only seam where a rewritten event has the right effect. Primary presses carrying the Control modifier become Secondary presses; the press and its release are paired so releasing Control mid-click still closes as a context click and never leaks a stray primary click, and a plain press or PointerGone clears any stale pairing. Real right clicks and other buttons pass through unchanged; Windows and Linux keep Ctrl+Click as a selection modifier, and control-channel/automation input is exempt.

Unit tests cover the transformation through egui's real pointer state: a rewritten Ctrl+Click makes pointer.secondary_clicked() true, plain clicks are untouched, and a stale pairing cannot corrupt the next click.
2026-10-12 05:21:42 -07:00
lloyd b6866db1b2 Import YUV4MPEG2 (.y4m) video; stop offering .ale for import (#1292)
Both were offered for import but could never be imported.

`.y4m` is in VIDEO_EXTENSIONS (and file-format-parity.md lists Y4M as
read), but nothing read it. `codecs::y4m` is a YUV4MPEG2 source written
from the format's public documentation (yuv4mpeg(5)): the W/H/F/A/I/C/X
stream header, FRAME lines with their own parameters, 8-bit 4:2:0 / 4:2:2
/ 4:4:4 / 4:4:4 + alpha / mono and 9–16-bit little-endian
(`420p10`, `422p12`, `mono16`, …) as Yuv8 / Yuv16 frames, XCOLORRANGE=FULL
and the pixel aspect. Frames are indexed once from their header lines and
read on demand (a reader opener too), so large files aren't loaded whole.
Sizes, rates and offsets are checked; 4:1:1 and unknown colour spaces are
an "unsupported" error.

The Media Browser listed `.ale` among project files, but FilmCraft only
writes ALE (File ▸ Export; Premiere doesn't import it either, per
file-format-parity.md), so importing one always failed. It is no longer
listed.

Tests: hand-built Y4M files (4:2:0 frame by frame, 10-bit 4:2:2 with frame
parameters, aspect and full range, mono, the reader path), truncated /
bit-flipped / hostile headers under catch_unwind, and an ffmpeg oracle
(8-bit 4:2:0 and 10-bit 4:2:2 files equal ffmpeg's rawvideo output byte
for byte); the browser no longer classifies `.ale`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:12:21 +02:00
lloyd d6df5c6abd media: decode uncompressed and PackBits YCbCr TIFF stills (#1293)
TIFFs stored as YCbCr (PhotometricInterpretation 6) without JPEG
compression failed with "does not support the color type Unknown(24)":
the image crate reads YCbCr only inside JPEG compression. ffmpeg writes
these whenever the source is YUV, so an ordinary frame grab
(`ffmpeg -i clip.mp4 -frames:v 1 grab.tiff`) couldn't be imported.

`still::ycbcr_tiff` decodes them from TIFF 6.0 (§21 YCbCr images, §20
ReferenceBlackWhite, §9 PackBits), reading the directory with
filmcraft-dng's TIFF parser: 8-bit chunky data units with subsampling 1, 2
or 4 each way (the image padded to whole data units), converted with the
file's YCbCrCoefficients and ReferenceBlackWhite (or the §21 defaults).
Every size, offset and count is checked, the picture is capped at 2^28
pixels, and separate planes, tiles and other bit depths are an
"unsupported" error. JPEG-compressed and RGB TIFFs still go to the image
crate.

Tests: hand-built files for data-unit placement (4:2:0), video-range
reference levels with PackBits (4:4:4), sizes that aren't whole data
units, and truncated/bit-flipped/hostile files under catch_unwind; an
ffmpeg oracle compares ffmpeg-written 4:2:0, 4:2:2 and 4:4:4 TIFFs (95×63)
with ffmpeg's own RGB decode (every channel within 6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:06:04 +02:00
lloyd 52b36f9764 codecs: import raw ADTS .aac files (#1291)
File ▸ Import and the Media Browser offer `.aac` (it is in AUDIO_EXTENSIONS),
and Export ▸ AAC Audio writes ADTS `.aac` (#814), but no opener read them:
`audio::opener` only accepted mp3/mp2/flac/ogg/aif by extension, so every
`.aac` failed with "unrecognised format".

`AudioFileSource` now decodes a standalone ADTS stream with our AAC decoder
(ISO/IEC 13818-7 §6.2): frame after frame from the first header (after an
ID3v2 tag), resyncing past damage, skipping frames with another
configuration, keeping a frame that doesn't decode as silence, and
upsampling HE-AAC to its output rate. Decoding stops with an error past
4 GiB of samples instead of allocating without bound.

Tests encode a stereo tone with our encoder's ADTS output and import it
(rate, channels, duration, level), and run truncated, bit-flipped and
bad-length streams under catch_unwind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:01:33 +02:00
NetterSilver eb3afad326 Transform shutter: use the sequence frame rate on adjustment layers (#1088)
Adjustment layers render their effects without an FxEnv, so Transform's
shutter fell back to 24 fps. In a 60 fps sequence a 360° shutter then
spanned 2.5 frames, blurring one frame before the move starts and one
after it ends. FxCtx now carries the sequence frame rate, set wherever a
sequence is known, and Transform uses it.
2026-10-12 12:11:03 +02:00
NDelic ec1570a4d8 #1081: Align to Video Frame works on selected video and image clips
Graphics ▸ Align to Video Frame (Left, Center Horizontally, Right, Top,
Center Vertically, Bottom) only reached layers inside a graphic clip. With
video or image clips selected instead, it now aligns each clip's scaled and
rotated bounds to the frame by moving Motion ▸ Position only, at the
playhead (a keyframe only when Position is already animated, as typing the
value would). Scale, rotation and other keyframes stay untouched; one undo
step. A selected or named graphic clip still aligns its layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:34:58 +02:00
NDelic d466995942 #1023: the eyedropper picks from the Program monitor again
With a colour parameter's eyedropper armed (Color Key, Ultra Key, …), a
click on the Program picture did nothing: the selected clip's on-monitor
transform handles (#639) are drawn over the picture and took the click.
The clip is always selected while its effect shows in Effect Controls, so
the eyedropper never fired. While an eyedropper is armed the picture
overlays (transform handles, masks, graphics) are not drawn, so the click
reaches it; they come back once the colour is picked or Esc disarms it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:30:31 +02:00
NDelic 37a845bc68 #1241: Select All in Effect Controls selects its keyframes
With Effect Controls focused, Edit ▸ Select All (⌘A / Ctrl+A) selected
every clip in the Timeline. It now selects every keyframe the panel shows
(effect parameters and mask parameters of the shown clips) and leaves the
Timeline selection alone. With no keyframes shown, or another panel
focused, Select All behaves as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:17:58 +02:00