Repository navigation
feat(preview): an unplayable video shows its own frame while its copy is made - #6589
miguel-heygen wants to merge 7 commits into
Conversation
…ask moves it to the front
…handling into helpers
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 0 important · 2 nits · head 87493be9
The still route sits behind the same eligibility gate as the copy, and the poster is only set for non-light copies and cleared on load or when the copy stops being live. Two nits are inline.
Scope and checks
- Read all 12 files. Also checked other
posterreaders in the runtime (sceneUrlsand the asset-failure diagnostic ininit.ts): both are unaffected. The retry path now also picks up failed unplayable copies at Full, throughswapToProxy. - Ran locally on the head: core
mediaProxy/previewCopiestests (66 pass); full core suite (5465 pass; the only failures are 3studio-apifiles that fail on import because studio-server isn't built here, unrelated); full studio-server suite (1555 pass);resolveStillwith real ffmpeg (ran, not skipped). oxlint, oxfmt and tsc are clean for core and studio-server. - Because the PR is stacked on #6586, the main
CIworkflow hasn't run. The Comments, regression, preview-regression and Player perf runs passed on this head. Required CI (including the core size budget in Build) runs after the retarget to main. - I couldn't check how the desktop host shows a
preview-copiesreport at Full, because hyperframes-internal is outside my access.
| const source = canonicalizeProxySource(projectDir, absoluteSourcePath); | ||
| const at = Math.max(0, Math.round(atSeconds * 10) / 10); | ||
| const key = fastCopyKey(source.sourcePath, PROXY_PARAMS_VERSION, `still@${at}`, box); | ||
| const cachePath = join(source.projectDir, CACHE_DIR_NAME, `${key}.jpg`); |
There was a problem hiding this comment.
nit: proxyCache.ts only budgets and idles out names whose extension is in PROXY_EXTENSIONS (the copy variants), so finished <key>.jpg stills are never evicted. Every cold copy of an unplayable clip leaves one behind in .transcode-cache until hyperframes clean runs. Each one is small, but a still for a source that later changes is never reclaimed. To fix, add .jpg to the swept set, or delete the still once its copy lands.
| box: PreviewProxyBox | null, | ||
| ): void { | ||
| const at = sourceTimeAtPlayhead(el); | ||
| if (at === undefined || stills.has(el) || el.hasAttribute("poster")) return; |
There was a problem hiding this comment.
nit: stills.has(el) (and the poster you just set) pins the frame from the first ask. If someone scrubs inside the same clip while the copy is still being made, the poster keeps showing the old time instead of the frame at the playhead. That's still better than black, but it doesn't match the body's "on each ask". If you want it to follow the playhead, re-set the poster when the rounded at changes and stills.has(el) is true.
The base branch was changed.
Edit accuracy: accurate 271 (base branch 271), smooth 138 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
Stacked on #6586.
What
While the copy of a video the browser cannot play is being made (HEVC without a decoder, ProRes), the video shows its own frame at the playhead instead of black, and the existing copy-progress report now runs at Full quality for those videos, as it already did for lighter copies below Full.
?hf-proxy=…&hf-proxy-still=<seconds>), to a tenth of a second, sized like the copy's box, with oneffmpeg -frames:v 1. It sits behind the same eligibility gate as the copy and is cached next to it, so a second ask costs nothing.posteron each ask while the video is at the playhead and has no poster of its own, and removes it when the copy has loaded. A failed copy leaves the frame up.previewCopiescounts the copy a file cannot play without separately from the lighter ones: it is reported at any level, andcopiesReady(which the host reads for lighter copies) is unchanged.Why
On a machine with no HEVC decoder a project of many HEVC clips showed only its captions over black until the copies were made, with nothing to say why. The copy-progress report only ran below Full quality, so at Full there was no sign of progress either.
Measured
24 separate 1080x1920 HEVC clips of 8 s, back to back, captions over them, in Studio served by the built CLI on a Linux box whose Chrome has no HEVC decoder (the same as a Windows machine without the HEVC extension). Seconds from the first clip appearing to its first picture, each run with a cold copy cache:
The wait for the copy is the same in both, and the box was shared, so the spread follows its load: the still is a separate ffmpeg that competes for the same CPU as the copies. In every run the still came before the copy.
Before
Two seconds after the first clip appears (night, then paper): the clip is black, only its caption shows.
After
The same moment: the clip's own frame stands in until the copy lands.
Tests
mediaProxy.test.ts: the poster is set for a video at the playhead and not for an idle one, carries the source time, and is removed when the copy loads.previewCopies.test.ts: the unplayable file's copy is counted at Full, a lighter one is not, andcopiesReadyignores the former.preview.test.ts: the route serves the JPEG behind the same gate and refuses a bad time or a request without the proxy param.proxyTranscoder.test.ts: a real ffmpeg cuts a JPEG, and the same tenth of a second finds the cached one.