Repository navigation
fix: prepare video validation without delaying capture #4188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8194aff
7e440f1
629a9fb
5b87b59
a5c82f5
49b95be
d853858
c2e2d27
79470c8
776aab8
6829cbf
d8bc80e
3e988a8
e6b8b20
9bf6505
b92d87d
e5cde02
0ad30c8
5a8515f
4394ea2
7ff691c
7f15773
837491f
8566ffd
693d5fd
f026759
e83a593
b1150a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,7 +10,7 @@ import { | |||||||||||
| compileSwiftSourceFile, | ||||||||||||
| resolveRecordingScriptPath, | ||||||||||||
| } from './swift-cache.ts'; | ||||||||||||
| import { waitForPlayableVideo } from './video.ts'; | ||||||||||||
| import { waitForPlayableVideo, type VideoValidator } from './video.ts'; | ||||||||||||
| import { | ||||||||||||
| DEFAULT_RECORDING_EXPORT_QUALITY, | ||||||||||||
| type RecordingExportQuality, | ||||||||||||
|
|
@@ -29,9 +29,9 @@ export function getRecordingOverlaySupportWarning( | |||||||||||
| * `record stop` is one daemon request, which the client abandons after `record`'s 90 s envelope: | ||||||||||||
| * the caller gets "Daemon request timed out" while the daemon finishes the stop. So the overlay must | ||||||||||||
| * end well inside it. The budget starts with the stop's finalization, so the playability checks | ||||||||||||
| * before the overlay (on a cold host they compile their validator first) come out of it, as do | ||||||||||||
| * compiling the helper, its export, its exit and verifying its output; past it the stop keeps the raw video and its | ||||||||||||
| * gesture telemetry, with a warning. | ||||||||||||
| * before the overlay come out of it, as do compiling the overlay helper, exporting, exiting and | ||||||||||||
| * verifying its output. Past the budget, the stop keeps the raw video and gesture telemetry, with | ||||||||||||
| * a warning. | ||||||||||||
| */ | ||||||||||||
| export const OVERLAY_BUDGET_MS = 70_000; | ||||||||||||
|
|
||||||||||||
|
|
@@ -79,6 +79,7 @@ async function exportProcessedVideo(params: { | |||||||||||
| scriptArgs: string[]; | ||||||||||||
| commandDescription: string; | ||||||||||||
| deadline: Deadline; | ||||||||||||
| validator: VideoValidator | undefined; | ||||||||||||
| }): Promise<void> { | ||||||||||||
| const { host, videoPath, scriptPath, scriptArgs, commandDescription, deadline } = params; | ||||||||||||
| const outputPath = temporarySiblingVideoPath(videoPath); | ||||||||||||
|
|
@@ -117,7 +118,7 @@ async function exportProcessedVideo(params: { | |||||||||||
| exportOverran = deadline.remainingMs() <= HELPER_EXIT_GRACE_MS; | ||||||||||||
| throw error; | ||||||||||||
| } | ||||||||||||
| if (!(await waitForPlayableVideo(outputPath, { host, deadline }))) { | ||||||||||||
| if (!(await waitForPlayableVideo(outputPath, { validator: params.validator, deadline }))) { | ||||||||||||
| exportOverran = deadline.isExpired(); | ||||||||||||
| throw new AppError('COMMAND_FAILED', 'The composited video was not verified as playable'); | ||||||||||||
| } | ||||||||||||
|
|
@@ -158,6 +159,7 @@ function temporarySiblingVideoPath(videoPath: string): string { | |||||||||||
|
|
||||||||||||
| export async function overlayRecordingTouches(params: { | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No longer raised on 🆕 🔵 Nit · Inconsistent The
Suggested change
Detailspersona: Reply Useful? Reply |
||||||||||||
| host: HostEnvironment; | ||||||||||||
| validator: VideoValidator | undefined; | ||||||||||||
| videoPath: string; | ||||||||||||
| telemetryPath: string; | ||||||||||||
| exportQuality?: RecordingExportQuality; | ||||||||||||
|
|
@@ -186,5 +188,6 @@ export async function overlayRecordingTouches(params: { | |||||||||||
| ], | ||||||||||||
| commandDescription: `Failed to add touch overlays to the ${targetLabel}`, | ||||||||||||
| deadline: params.deadline, | ||||||||||||
| validator: params.validator, | ||||||||||||
| }); | ||||||||||||
| } | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔵 Nit · Overlay discards a successfully exported composite when validator preparation is still pending
finalizeScreenRecordingreads the preparation once (const prepared = validator.read()) and hands that same promise towaitForPlayableVideoboth for the raw check and, throughoverlayTouches, toexportProcessedVideo. The predecessor verified the composited output with the host (waitForPlayableVideo(outputPath, { host, deadline })), which compiled the validator on demand, so the check always had an authority by the time the export finished.Suggested change
Do not reuse the pending preparation promise as the authority for verifying the composited output. Either re-read
validator.read()after the export (so an executable published during the export is used), or skip the composite verification with an explicit typed reason instead of deleting a composite the helper already produced.Details
Now, after a long overlay export the deadline has little left, and
isPlayableVideoraces the still-pending preparation promise againstdeadline.remainingMs(). If preparation has not settled within what the overlay left ofOVERLAY_BUDGET_MS, the race rejects withvideo_validator_not_prepared,exportProcessedVideotreats it asThe composited video was not verified as playable, deletes the exported file in itsfinally, and the stop returns the raw video with anoverlayWarning— even though the helper exported the composite successfully. The same happens whenread()answeredundefined(nothing prepared and nothing published): the raw-video check infinalizeScreenRecordingthrows out ofcomplete, soshowTouchesstops can only ever keep the raw video.The verified composite is thrown away for a reason unrelated to the export, and the user-visible fix (retry stop) re-runs the whole export.
persona:
javascriptReply
@apex-sentinel-staging note ...to add context, or@apex-sentinel-staging pivot ...if this is wrong — I will re-check the cited code.Useful? Reply
@apex-sentinel-staging like, or@apex-sentinel-staging dislike <why>if it is not.