Repository navigation
Description
Confirmed symptom / product concern
On iOS, the player chrome controls themselves appear to change size and/or position between videos with different widths/aspect ratios. This is not merely a report that the aspect-fit picture becomes wider/shorter inside a fixed player.
The user is unsure this content-dependent chrome movement is desirable. Player controls should normally occupy a stable screen-relative layout for a fixed device, scene size, orientation, Dynamic Type setting, and chrome state, regardless of the video's presentation aspect.
The earlier framing of this issue treated active-picture resizing as the likely phenomenon. That remains a useful comparison to measure, but chrome movement is the primary report.
Current code trace
There is no intentional source-aspect input to mobile chrome geometry:
DetailViewpresentsCustomPlayerViewin a.fullScreenCoverand ignores safe areas.CustomPlayerViewputsPlayerLayerViewandCustomPlayerChromebeside each other as siblings in one full-surface blackZStack.- The UIKit host's backing layer is the
AVPlayerLayer. Fit/Fill changesvideoGravity(resizeAspect/resizeAspectFill) and therefore the active picture rectangle, but novideoRect, presentation size, natural size, encoded dimensions, or media aspect is passed toCustomPlayerChrome. CustomPlayerChromemeasures its own proposed viewport withonGeometryChange;MobilePlayerChromeLayoutPolicyselects phone-landscape/compact variants from horizontal/vertical size classes, idiom, and that viewport size only.- In phone landscape, top utility sides, center play/pause, bottom insets, platter padding, skip buttons, menu minimum widths, and row spacing are fixed by the selected chrome variant.
- The only
GeometryReaderuses in the chrome are menu/timeline geometry; neither reads the player layer or active video rectangle. - On regular iPad layouts,
ViewThatFitscan switch header/menu composition based on available horizontal space. That space should come from the scene/window proposal, not the video aspect.
Therefore the current source does not deliberately anchor or size chrome to the active video rectangle. If the controls genuinely move with source aspect, it is an unintended emergent layout/lifecycle effect and needs device evidence rather than dismissal as normal aspect-fit behavior.
Plausible non-aspect inputs to rule out
Several state changes can alter the chrome and may correlate with switching videos:
- iPhone landscape entry timing, scene geometry, safe-area/status-bar/home-indicator changes;
- the initial zero/unmeasured
chromeViewportSizebecoming measured, or crossing a compact-layout boundary on iPad; - chrome visibility (
statusBarHidden/ persistent system overlays) changing system safe-area behavior; - conditional PiP availability changing the top-trailing utility group;
- conditional Quality availability (
supportsQualityReload) changing the menu strip; - title length and iPad
ViewThatFitschoosing inline versus stacked headers; - transport status, skip marker, Up Next, subtitle, scrub-preview, or open-menu state inserting/removing overlays/rows;
- a controller/item replacement rebuilding the player/chrome while the orientation or full-screen-cover transition is still settling.
None of these is supposed to scale controls from video width. The investigation must compare like-for-like chrome state so a capability/title/status difference is not mistaken for aspect coupling.
Discovery plan
Use a physical iPhone first, holding device/orientation, Dynamic Type, display mode, backend/session capability, and chrome state constant. Compare representative portrait/phone-shot, 4:3, 16:9, approximately 2:1, and 2.39:1 items.
Capture after layout settles and, separately, during startup/item replacement:
- outer full-screen/player bounds and safe-area insets;
- measured
chromeViewportSizeand selectedMobilePlayerChromeLayoutPolicybranch; - frames of top utility controls, center play/pause, bottom control platter, timeline, and menu buttons;
AVPlayerLayer.boundsandvideoRectas an independent comparison;- horizontal/vertical size classes, interface orientation, system-overlay visibility, PiP possibility, available menus, and relevant inserted chrome states.
A diagnostic overlay/log should use normalized geometry and labels only; do not record media titles, server URLs, tokens, or other private data.
Product decision
After proving which frames move:
- If only
videoRectchanges: keep that as ordinary aspect-preserving Fit/Fill behavior unless a separate stable-picture-envelope design is desired. - If chrome frames change while viewport/state are equivalent: treat it as a layout bug and anchor chrome to the stable full-screen/safe-area viewport, not the active picture.
- If capability/title/status changes cause the apparent shift: decide whether those variations are valid or whether chrome groups need reserved positions/stable envelopes across items.
Do not solve chrome movement by stretching video, silently cropping content, or binding controls to a hard-coded 16:9 frame.
Acceptance criteria
- Reproduce/document the control movement on a physical iPhone with at least two different source aspects while matching orientation, Fit/Fill selection, chrome visibility, and playback state.
- Measure chrome frames separately from
AVPlayerLayer.videoRect; identify exactly which controls change size/position and by how many points. - Identify whether the trigger is source aspect, scene/safe-area transition, a mobile layout-policy branch, conditional control availability, title/content sizing, or player reconstruction timing.
- For a fixed viewport and equivalent chrome state, top controls, center transport, bottom platter/timeline, subtitles, and status overlays remain screen-relative and stable across source aspects.
- Control hit targets remain at least 44×44 pt and VoiceOver focus order/labels remain stable.
- Source display aspect remains correct; no stretch or accidental crop is introduced to stabilize chrome.
- Verify online and completed Offline playback because both converge on
CustomPlayerView; expand across backends only if session capabilities are part of the cause. - Audit iPad portrait/landscape plus compact Stage Manager/Split View, where
ViewThatFitsand compact-policy boundaries can legitimately reflow; do not assume iPhone findings apply unchanged. - Regression-check iPhone landscape entry/exit and chrome auto-hide/system-overlay transitions.
Platform boundary
Initial confirmed scope is LabstreamMobile on iPhone. iPadOS shares the player/chrome implementation but has different orientation, size-class, and windowing behavior and is an explicit audit target. visionOS, macOS, and tvOS are not confirmed affected and should not be redesigned without separate evidence.
Relationship to earlier work
- iPhone/iPad player: add Fit ⇄ Fill (aspect-fill) display mode with pinch gesture #213 added the current mobile Fit ⇄ Fill control; it governs picture gravity, not intended chrome geometry.
- Audit and fix iPhone player sizing, chrome, and orientation-specific UI #236 performed a broad iPhone player sizing/chrome/orientation pass but left exhaustive aspect/letterbox transitions unverified.
Both are closed and neither provides the focused geometry evidence required for this report.
Activity
[-]iOS player: decide whether the visible video frame should stay stable across aspect ratios[/-][+]iOS player chrome changes size or position between video aspect ratios[/+]jlipworth commentedon Jul 23, 2026
Correction after user clarification
The primary symptom is chrome-control movement: on iOS, the player controls themselves appear to change size and/or position depending on the video's width/aspect ratio. It is not just the visible video raster changing shape under normal aspect-fit presentation.
I re-traced current
mainwith that distinction:PlayerLayerViewandCustomPlayerChromeare siblings in the same full-screenZStack.AVPlayerLayer.videoRect, presentation size, natural size, encoded dimensions, or media aspect is passed into chrome layout.GeometryReaderusage is limited to menu/timeline layout and does not read the active video rectangle.So there is no intentional code path that anchors chrome to video width. If the reported controls move with source aspect, that is either an unintended layout/lifecycle effect or a correlated state change (orientation/safe-area settling, PiP/Quality availability, title/menu composition, status/Up Next/subtitle insertion, or player reconstruction). It should not be waved away as standard
.resizeAspectbehavior.The issue title/body have been corrected to make chrome movement primary. The earlier video-rectangle theory is retained only as a comparison measurement: device evidence should log/overlay outer viewport, chrome frames, and
videoRectseparately while holding chrome state constant.jlipworth commentedon Jul 23, 2026
Additional reproduction: narrower bottom chrome on one item
A more specific physical-iPhone observation strengthens this as a chrome-container issue: for most videos, the bottom chrome extends so wide that its outer corners are visibly clipped by the iPhone's rounded display corners. On one item, the bottom chrome is noticeably less wide and sits differently. The media title and private library details are intentionally omitted.
Revised code finding / leading hypothesis
The normal corner clipping is consistent with the current presentation chain applying
.ignoresSafeArea()to the entireCustomPlayerView, including its chrome, then giving phone-landscape bottom controls only an 8 pt outer horizontal inset. In other words, the platter is allowed to approach the full physical display bounds rather than an explicit rounded-corner-safe content guide.The platter width is also not explicitly anchored at the call site with a stable
frame(maxWidth: .infinity)or a named safe-area/container width. It relies on proposal propagation through the bottomVStackintocontrols, then throughphoneLandscapeControls; the latter contains a flexible full-width scrubber plus a content-sized/fixed menu row. A different chrome branch, available-menu set, or transient viewport proposal can therefore plausibly produce a narrower resolved envelope even though video aspect is not a direct input.The highest-value checks are now:
chromeViewportSize, selectedisPhoneLandscapeChromebranch, bottomcontrolsframe, and platter frame for a normal clipped item versus the narrower item.availableMenusand PiP availability so a content/capability-driven intrinsic-width change is visible.AVPlayerLayer.videoRectseparately, but do not use it as the leading explanation for the chrome-width difference.The leading hypothesis is now an under-constrained bottom-chrome width combined with whole-player safe-area ignoring and/or a transient layout-policy proposal, not active-video-rectangle sizing. The intended fix direction, once measured, should be a single stable screen/safe-area-relative chrome envelope for equivalent iPhone landscape states, with deliberate rounded-corner clearance rather than accidental clipping.
jlipworth commentedon Sep 29, 2026
Release-owner decision: defer this issue for the 1.7.2 (build 1) candidate. Keep it open; this is an explicit scope deferral, not a fix or passing acceptance result. Candidate preparation and remaining release gates are recorded in #332.
Correct the player safe-area comments and layout contract (#258)
Keep iOS player chrome in one stable safe-area envelope (#258) (#356)
Sweep dead code, redundant tests and superseded docs (#365)