Skip to content

Letterbox video to its stream aspect and anchor the example's status bar - #189

Merged
ctate merged 8 commits into
mainfrom
fix/video-aspect-status-chrome
Jul 24, 2026
Merged

ctate merged 8 commits into
mainfrom
fix/video-aspect-status-chrome

Conversation

@ctate

@ctate ctate commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

What

Two visual fixes, one framework and one example:

  1. Video surfaces letterbox instead of stretching. The compositor previously scaled the decoded frame to fill the layout rect, distorting any stream whose aspect didn't match the box. The video draw now computes a centered aspect-fit from the stream's reported dimensions — pillar bars on wide boxes, letterbox bars on tall ones, none at exact aspect — with the bars drawn as a black field at the surface's effective corner radius (exact silhouette, seam-free tangent-masked picture quad). Contain is the default and only fit for now. Unknown dimensions (pre-LOADED) keep the placeholder across the whole box; a replaced source snaps to the placeholder and re-fits to the new stream's geometry — never a stale mis-fitted frame (the retained texture is purged on both channel acquire and release). Dimensions ride the journaled LOADED report, so replay renders byte-identical.

  2. The video-player example's status bar anchors full-bleed — flush to the window's left/right/bottom edges with its own padding and a hairline token separator, on both screens, instead of floating inside the page padding.

Tests

Paint-level pins for pillarbox/letterbox/exact-aspect geometry with reference pixels, thin-bar corner radii, radius clamping, unknown-dimension placeholders, stream-size diff invalidation, LOADED→re-fit end to end, stop/replace purge semantics, and the example's status-bar window-edge geometry. Verified live on hardware at wide and tall window shapes with a real stream.

ctate added 8 commits July 23, 2026 21:44
- The builder stamps contain fit and the LOADED report's stream dimensions on every media surface the video channel feeds; the emit computes the centered aspect-fitted quad over a black backdrop, so every host composites the same geometry with no fit math of its own
- Unknown dimensions pre-LOADED keep the full-frame placeholder draw (no guessed geometry, no divide-by-zero); a source replacement re-fits from the new report
- Paint-level pins: reference-rendered pillarbox/letterbox/exact-fit pixels and quads, the Ui stamp, and a null-platform end-to-end letterbox + re-fit test; the video doc states contain-by-default
- The page padding wraps only the content column; the status bar is the root column's last child - flush left/right/bottom with its own padding and the token hairline separator, on both screens
- A layout test pins the full-bleed frame at the window edges
- One fix fragment: contain-by-default letterboxing on the video surface, placeholder pre-LOADED, re-fit on replacement
…er the bars

- Stream dimensions ride a dedicated Widget.stream_size stamped only on the surface the active playback feeds - a source-less <video> beside a custom-surface load keeps its placeholder, and image_src keeps its source-crop meaning on every widget
- The letterbox paints black over exactly the remainder bars, leaving the deterministic id-derived placeholder under the picture quad for goldens and replay screenshots
- A bar-inset quad drops the radius mask (its corners sit in the bars) while each bar rounds only its outer corners, keeping a rounded surface's silhouette
…s, purge on video claims

- stream_size joins the widget diff's content comparison so a LOADED report alone invalidates retained paint, and the known-geometry draw stretches into the engine's quad - a decoder whose true dimensions round off the report can never open a host-side contain seam
- Bars thinner than the corner radius keep the frame's mask on the quad (a hairline bar cannot cover a rounded corner), pinned alongside the drop-the-mask case
- A video-channel claim purges the previous playback's retained texture (entry, host copy, repaint) so a replacement's new fit never composites the old stream's stale frame; generic producer re-claims keep the adoption-boundary dedup contract
- The fitted shape is now three commands: black across the whole frame at the frame's radius (the silhouette, exact by construction - no per-bar corner approximation), the placeholder confined to the quad, and the picture quad
- The quad's mask is the tight inset radius max(0, corner - bar): internally tangent to the frame's corner circle, so hairline bars neither leak picture past the silhouette nor notch more corner than the silhouette demands
- A declared image_src crop drives the fitted quad's aspect - the crop is what draws, never flattened into the full stream's proportions
…n video release

- The tangent-mask math starts from the renderer-effective corner radius (clamped to half the frame's short side), so an unbounded style radius can no longer round a fitted quad into a circle
- A declared crop clips against the stream bounds before driving the quad's aspect, and the draw samples exactly the clipped region - an out-of-range crop never stretches its visible remainder
- The video-channel release purges the adopted frame: stop/replace/failure drop the surface's contain stamp on the next rebuild, and a retained freeze-frame would composite distorted under "no playback" chrome; pause and natural completion keep their claim and picture
…tract

- The fitted draw always samples the whole texture at the stream's aspect: DrawImage.src is adopted-texture pixel coordinates and the video texture's size is unknowable at emit (macOS budget-fits large decodes), so a stream-coordinate crop cannot be translated - crops stay a generic-producer facility in texture coordinates
- MediaSurfaceBinding is documented loop-thread-only on both halves: release purges loop-thread runtime texture state, matching its only callers (the video channel's update-dispatch paths); producer threads hold the sink, never the binding
@vercel

vercel Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
native-sdk Ready Ready Preview, Comment Jul 24, 2026 4:56am

@ctate
ctate merged commit 7563a4e into main Jul 24, 2026
22 checks passed

This branch was successfully deployed

1 active deployment
Preview — 3b15fb15 Deployed Jul 24, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant