Anamorphic media: pixel aspect ratio in placement, monitors and New Sequence From Clip

A 1440x1080 clip with 4:3 pixels showed squeezed: the demuxers read its
pixel aspect ratio, but nothing after import used it. New Sequence From
Clip made a 1440x1080 sequence with square pixels, the Motion transform
sized clips from storage pixels (pillarboxed at 1440 wide in a 1920x1080
sequence), and the monitors drew the frame at its storage aspect.

- project: checked_par / sane_par / par_ratio / conformed_size treat
  file and project ratios as hostile (a zero term, or more than 8:1
  either way, is square). MediaClip::pixel_aspect (Interpret Footage
  wins over the file), Project::source_par (subclips follow the parent,
  nested sequences have their own, graphics and adjustment layers have
  none) and Project::conformed_source_size.
- render: motion_matrix takes the source's ratio and works in square
  display units (anchor in source pixels, position in sequence pixels,
  scale and rotation at the display aspect); Scale to Frame Size fits
  the display size. CPU, GPU plan, adjustment regions and the mask /
  graphics overlays share it.
- engine: New Sequence From Clip, New Sequence from an item and
  multicam sequences copy the clip's ratio; default media scaling and
  Fit / Fill frame use the display size; generators made for a
  non-square sequence get its pixels; transcodes render pixel for
  pixel. clip.interpretFootage takes pixelAspect ([num, den] or "file").
- ui: the Source and Program monitors draw the picture at its display
  aspect; overlays map both axes; Sequence Settings shows the display
  aspect (16:9 for 1440x1080 at 4:3).

Tests: render par_tests (fill, GPU plan parity, Scale to Frame Size,
non-square sequences, rotation, Interpret Footage, hostile ratios),
engine par_tests (New Sequence From Clip, overwrite into 1920x1080,
default scaling, Fit / Fill, Interpret Footage with undo and refused
values, hostile file ratios, generators, and ffmpeg-made MP4 and MKV
files), project unit tests, and the headless par_ui monitor test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
lloyd
2026-10-09 18:28:56 +02:00
parent ef6391df90
commit ff8728af08
21 changed files with 933 additions and 51 deletions
+10
View File
@@ -220,6 +220,16 @@ file ──► codecs (MP4/MOV, MKV, audio) demux + decode, GOP-aware see
`audio(start, frames, rate)` in media time. Sources are `Send + Sync` and shared by monitors,
thumbnails, playback and export. The engine's `MediaPool` creates one per project item, lazily,
through registered openers (`codecs::openers()`: MP4/MOV, MKV/WebM, audio files).
- **Pixel aspect ratio.** Demuxers report it in `VideoStreamInfo::par` (MP4/MOV `pasp`, Matroska
display size, MXF aspect ratio, MPEG sequence header / VUI); Interpret Footage's
`Interpretation::par` overrides it (`clip.interpretFootage {pixelAspect}`), and
`SequenceSettings::par` is the sequence's. Both go through `project::checked_par` (a zero term
or more than 8:1 either way = square). `render::motion_matrix` works in square display units,
so a 1440 x 1080 clip with 4:3 pixels fills a 1920 x 1080 square-pixel sequence at 100 %
(Scale to Frame Size, Set to Frame Size and Fit / Fill use `Project::conformed_source_size`);
graphics and adjustment layers are in sequence pixels. New Sequence From Clip copies the clip's
ratio, and the Source and Program monitors draw the picture at its display aspect. The
renderer does not read decoded frames' own `VideoFrame::par`.
- **Offline media and proxies.** The pool caches each item's source per reference (path, offline
flag), so relinking and undo take effect at once. Media that can't be opened renders the offline
slate (`render::offline`) instead of failing. With proxies enabled, an item with a proxy reads