feat(recording): make iOS export quality configurable#816
Conversation
Wire the existing recording-export-quality enum through the record command down to the Swift export preset. Adds a `--export-quality <medium|high>` option for iOS recordings that controls the AVAssetExportSession preset used when a recording is re-encoded. `medium` stays the default and selects AVAssetExportPresetMediumQuality, which preserves the fast simulator-friendly export. `high` opts into AVAssetExportPresetHighestQuality for evidence-grade output. This is separate from the existing integer `--quality <5-10>` capture flag that scales render resolution. Closes callstack#568
|
Review finding: The new flag is stored on the recording and forwarded to This means |
The --export-quality flag was only wired into the resize export path. The touch-overlay re-encode (finalizeRecordingOverlay -> overlayRecordingTouches -> recording-overlay.swift) ignored it and always picked AVAssetExportPresetMediumQuality, so record stop with --export-quality high had no effect when the stop path re-encodes only to burn in touch overlays. Thread the recording's exportQuality through finalizeRecordingOverlay and overlayRecordingTouches, pass it as --export-quality to recording-overlay.swift, and resolve the preset there via the same exportPresetName() helper used by recording-resize.swift. Medium stays the default when the arg is absent, so behavior is unchanged for callers that do not set it.
|
Good catch, fixed in 8f6eb35. I threaded the recording's exportQuality through the overlay path the same way it already flows through resize:
Added overlay unit tests mirroring the resize ones (default medium + forwarded high), and tightened the docs note so it reads "whenever a recording is re-encoded, whether that is --quality downscaling it or burning in touch overlays on record stop". swiftc -typecheck on the helper is clean, and typecheck/lint/unit tests pass. |
|
Added an end to end check for this to the PR description. Since the overlay burn-in and export-preset re-encode run on the macOS host (exportProcessedVideo compiles recording-overlay.swift and runs it with the args, the simulator only supplies the raw clip), I compiled the helper and ran the real binary against a test clip plus a two-tap gesture envelope, once per quality:
So high now genuinely picks the highest-quality preset on the overlay path and the default is unchanged. Screenshot and full table are in the updated description. |
What
Adds a configurable iOS recording export quality. A new
record start ... --export-quality <medium|high>option selects theAVAssetExportSessionpreset used whenever an iOS recording is re-encoded during the stop/export path, whether that re-encode is a--qualityresize/downscale or a touch-overlay burn-in.medium(default) selectsAVAssetExportPresetMediumQuality, keeping the fast, simulator-friendly export.highselectsAVAssetExportPresetHighestQualityfor evidence, release notes, or debugging visual artifacts.This wires up the
src/core/recording-export-quality.tsenum that was present but not yet consumed.Why
Closes #568. The recording resize export was hardcoded to
AVAssetExportPresetHighestQuality, so teams could not trade export speed against quality. The default now stays optimized for speed, and a higher-quality mode is available on demand through the same recording stop/export path.How it is wired
src/core/recording-export-quality.ts: keeps the existingRECORDING_EXPORT_QUALITIES/DEFAULT_RECORDING_EXPORT_QUALITYand adds a purerecordingExportQualityToPreset()mapping plus anisRecordingExportQuality()guard.src/utils/cli-flags.ts: registers the--export-qualityenum flag.src/commands/recording/index.ts: adds the field to the record command surface, CLI reader, allowed flags, and usage/help.src/daemon/handlers/record-trace-recording.ts: validates the flag (rejects unknown values withINVALID_ARGS), stores it on the recording session, and forwards it to the resize step.src/daemon/handlers/record-trace-finalize.ts: forwards the recording'sexportQualityinto the touch-overlay finalize step.src/recording/overlay.ts: bothresizeRecordingandoverlayRecordingTouchespass--export-qualityto their helpers (overlay defaults toDEFAULT_RECORDING_EXPORT_QUALITYwhen not set).ios-runner/.../recording-resize.swiftandios-runner/.../recording-overlay.swift: both parse--export-quality, default tomedium, and select the matching preset (medium falls back to highest when the medium preset is not compatible with the composition).Enum to preset mapping
--export-qualityAVAssetExportPresetmedium(default)AVAssetExportPresetMediumQualityhighAVAssetExportPresetHighestQualityCapture quality vs export quality
These stay distinct. The existing integer
--quality <5-10>capture flag scales render resolution and is unchanged. The new--export-quality <medium|high>only chooses how hard the exporter works to preserve the video when it re-encodes. The docs and help spell out the difference.Backward compatibility
The default is
medium, which matches the speed-optimized default already used by the export paths and the shippedDEFAULT_RECORDING_EXPORT_QUALITY = 'medium'. Callers that pass no flag get byte-identical output to before (verified in the e2e below: the no-flag run matches--export-quality mediumexactly). Anyone who wants the highest-quality export can pass--export-quality high.How tested
pnpm test:unit: full unit suite green (2581 passed in the original change; the overlay follow-up adds two more insrc/recording/__tests__/overlay.test.ts).src/core/__tests__/recording-export-quality.test.ts), CLI option parsing (src/commands/recording/index.test.ts), resize + overlay argument forwarding incl. default-preserved behavior (src/recording/__tests__/overlay.test.ts), and daemon validation + plumbing (src/daemon/handlers/__tests__/record-trace.test.ts). None require a live simulator.pnpm typecheck,pnpm lint,pnpm format:check: green.swiftc -typecheckon both modified Swift scripts: OK.End-to-end on the host (real re-encode)
The touch-overlay burn-in and export-preset re-encode are a macOS host post-process, not a simulator-internal step:
exportProcessedVideo(src/recording/overlay.ts) compilesrecording-overlay.swiftand runs it on the host with--input/--output/--events/--export-quality. The simulator only supplies the raw screen recording as input. So the export-quality logic this PR changes can be exercised end to end on the host exactly as production runs it.I compiled
recording-overlay.swiftand ran the real binary against a 720x1280 test clip plus a two-tap gesture envelope, once per quality:--export-quality mediumAVAssetExportPresetMediumQuality--export-quality highAVAssetExportPresetHighestQualityThe flag now demonstrably changes the overlay re-encode preset, and the default path is unchanged. Frames from each output (touch dot burned in):
A booted-simulator run would only change where the input clip comes from; it would not exercise any additional export-quality code, since that all runs on the host. If CI has a simulator lane, a full device capture is still welcome as a belt-and-suspenders check before merge.