From 2ac5308a432d9cd1b093e9ce57fb797c0efa2ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 5 Oct 2026 20:45:04 +0200 Subject: [PATCH 1/4] feat(recording): cap the touch overlay frame rate at the caller's --fps record start --fps now also caps the touch overlay's composited frame rate (at most 30), carried as a recording fact so a recovered stop honours it. An overlay export that runs out of its budget names that in overlayWarning. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../RecordingScripts/recording-overlay.swift | 30 ++++++++++++++----- .../src/recording/__tests__/overlay.test.ts | 17 ++++++++++- packages/capture-kit/src/recording/overlay.ts | 16 ++++++++-- .../src/recording/recording-facts.test.ts | 11 ++++++- .../src/recording/recording-facts.ts | 7 +++++ .../src/screen-recording-runtime-host.ts | 2 ++ .../contracts/src/screen-recording-runtime.ts | 2 ++ .../src/recording/finalize.ts | 1 + .../src/recording/live-snapshot.test.ts | 2 ++ .../src/recording/live-snapshot.ts | 1 + .../src/recording/manifest.ts | 5 ++++ .../src/recording/recovery.test.ts | 2 ++ .../src/recording/recovery.ts | 1 + .../platform-android/src/recording/runtime.ts | 1 + .../src/recording/completion.test.ts | 26 ++++++++++++++++ .../src/recording/completion.ts | 2 ++ .../platform-apple/src/recording/runtime.ts | 1 + src/commands/recording/index.ts | 2 +- ...me-screen-recording-finalizer-host.test.ts | 12 ++++++++ ...runtime-screen-recording-finalizer-host.ts | 1 + website/docs/docs/commands.md | 3 +- 21 files changed, 131 insertions(+), 14 deletions(-) diff --git a/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift b/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift index 0c4a1bcec7..5851e5a842 100644 --- a/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +++ b/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift @@ -88,7 +88,10 @@ func run() throws { let renderSize = resolvedRenderSize(for: sourceVideoTrack) let videoComposition = AVMutableVideoComposition() videoComposition.renderSize = renderSize - videoComposition.frameDuration = resolvedFrameDuration(for: sourceVideoTrack) + videoComposition.frameDuration = resolvedFrameDuration( + for: sourceVideoTrack, + maximumFrameRate: parsedArgs.maximumFrameRate + ) let instruction = AVMutableVideoCompositionInstruction() instruction.timeRange = fullRange @@ -158,11 +161,15 @@ func run() throws { func parseArguments( _ arguments: [String] -) throws -> (inputPath: String, outputPath: String, eventsPath: String, timeoutSeconds: Double) { +) throws -> ( + inputPath: String, outputPath: String, eventsPath: String, timeoutSeconds: Double, + maximumFrameRate: Int32 +) { var inputPath: String? var outputPath: String? var eventsPath: String? var timeoutSeconds = defaultRecordingExportTimeoutSeconds + var maximumFrameRate = maximumCompositedFrameRate var index = 0 while index < arguments.count { @@ -194,6 +201,13 @@ func parseArguments( } timeoutSeconds = milliseconds / 1000 index += 2 + case "--max-fps": + let rawValue = try recordingOptionValue(arguments, nextIndex, "--max-fps") + guard let framesPerSecond = Int32(rawValue), framesPerSecond > 0 else { + throw RecordingScriptError.invalidArgs("--max-fps must be a positive integer") + } + maximumFrameRate = min(framesPerSecond, maximumCompositedFrameRate) + index += 2 default: throw RecordingScriptError.invalidArgs("Unknown argument: \(argument)") } @@ -201,10 +215,10 @@ func parseArguments( guard let inputPath, let outputPath, let eventsPath else { throw RecordingScriptError.invalidArgs( - "Usage: recording-overlay.swift --input