Understanding the MAX_FPS Cap in claude-video: Why It's Set to 2.0
The MAX_FPS cap limits frame extraction to 2.0 frames per second to prevent unbounded token costs and ensure predictable LLM processing budgets when analyzing video content.
The bradautomates/claude-video repository implements a hard ceiling on frame extraction rates to balance visual fidelity against the computational and financial constraints of large language model (LLM) inference. This architectural safeguard lives in the frame extraction pipeline and directly impacts how video content is prepared for AI analysis.
What Is the MAX_FPS Cap?
In skills/watch/scripts/frames.py, line 19 defines a global constant that governs the entire extraction pipeline:
MAX_FPS = 2.0
This constant enforces an upper bound on sampling rates regardless of user input or video duration. The cap is applied through the _clamp_fps helper function (lines 49-52), which adjusts requested frame rates downward before calculating extraction targets:
def _clamp_fps(fps: float, duration_seconds: float, max_frames: int) -> tuple[float, int]:
fps = min(fps, MAX_FPS) # Hard ceiling enforced here
target = min(max_frames, max(1, int(round(fps * duration_seconds))))
return fps, target
By centralizing the limit in a single constant, the maintainers created a tunable bottleneck that protects downstream systems from accidental resource exhaustion.
Why the 2.0 FPS Limit Exists
The 2.0 frames-per-second threshold represents a calculated trade-off between three competing constraints: token economy, processing speed, and visual comprehension.
Preventing Token Budget Overruns
Every extracted frame generates tokens when sent to an LLM for captioning or question answering. Without the MAX_FPS ceiling, a 30-minute video sampled at 30 fps would produce 54,000 frames, creating prohibitive API costs and context window overflows. At 2 fps, the same video yields a maximum of 3,600 frames, which the max_frames parameter (typically set to 100–200 in skills/watch/scripts/config.py) further constrains to a manageable subset.
Maintaining Extraction Performance
High frame rates trigger exponentially more ffmpeg decode operations. By capping extraction at 2 fps, the pipeline minimizes I/O overhead and keeps the preprocessing step responsive for interactive use cases. This is particularly critical when skills/watch/scripts/watch.py orchestrates batch processing of multiple video segments.
Preserving Semantic Granularity
Visual changes in typical content—scene cuts, slide transitions, speaker gestures—occur at frequencies well below 2 fps. The 2 fps sampling captures sufficient semantic information for transcription and Q&A tasks while filtering redundant interstitial frames that add noise without meaning.
How MAX_FPS Is Enforced in the Code
The enforcement mechanism is transparent and deterministic. When watch.py initiates extraction, it passes requested parameters through _clamp_fps, which applies the min(fps, MAX_FPS) operation before calculating the target frame count.
Consider a 5-minute video (300 seconds) with a budget of 100 frames:
duration = 300.0
max_frames = 100
requested_fps = 10.0
fps, target = _clamp_fps(fps=requested_fps, duration_seconds=duration, max_frames=max_frames)
# Result: fps = 2.0, target = 100
Even though the caller requested 10 fps, the function returns 2.0 fps and calculates the target as min(100, round(2.0 * 300)) = 100, respecting both the FPS cap and the frame budget.
Working With the FPS Cap
Understanding the cap's behavior helps developers optimize their video analysis workflows without accidentally triggering resource constraints.
Automatic Clamping Behavior
By default, all extraction requests pass through _clamp_fps, ensuring the 2 fps ceiling applies universally. This prevents pipeline failures due to accidental misconfiguration in calling code or user input.
Focused Extraction Mode
The auto_fps_focus function (also in frames.py) handles user-specified time ranges by calculating a higher proposed fps for short segments. However, the cap still applies: _clamp_fps limits the effective rate to 2 fps even when the focus mode suggests denser sampling. This ensures temporary zooms on specific timestamps don't explode the token budget.
fps, target = auto_fps_focus(duration_seconds=8.0, max_frames=100)
# Proposed fps may exceed 2.0, but returned fps is clamped to MAX_FPS
CLI Overrides
Advanced users can bypass the cap using the command-line interface's --fps flag, which allows explicit override of the default behavior:
python3 -m skills.watch.scripts.frames video.mp4 out_dir --fps 5
This override is intentional: it permits high-density extraction when token costs are acceptable and the user explicitly accepts the trade-off. The default path, however, strictly respects the 2.0 limit defined in the source.
Summary
MAX_FPS = 2.0inskills/watch/scripts/frames.pycreates a hard ceiling on frame extraction rates to protect LLM token budgets.- The
_clamp_fpsfunction enforces this limit usingmin(fps, MAX_FPS)before calculating target frame counts. - The 2 fps threshold balances cost efficiency, processing speed, and semantic completeness for typical video content.
- Focused extraction and CLI overrides provide flexibility, but the default pipeline strictly adheres to the cap to prevent resource exhaustion.
Frequently Asked Questions
Can I increase MAX_FPS beyond 2.0 for higher quality?
Yes, but you must modify the source code in skills/watch/scripts/frames.py line 19 or use the --fps CLI flag to override it temporarily. Increasing the cap raises token costs linearly and may degrade performance for long videos, so adjust with caution and monitor your LLM API usage.
Does the FPS cap apply to focused extraction mode?
Partially. The auto_fps_focus function may calculate a higher proposed fps for short time ranges, but the _clamp_fps function still applies the MAX_FPS ceiling to the final value. The cap protects the budget even when analyzing brief segments, though the target frame calculation may suggest denser sampling initially.
Where is MAX_FPS defined in the codebase?
The constant is defined at line 19 of skills/watch/scripts/frames.py, alongside the _clamp_fps helper function that enforces it. This centralized definition makes it easy to adjust the system-wide limit as LLM pricing or hardware capabilities evolve.
How does MAX_FPS interact with max_frames?
These parameters work sequentially. First, _clamp_fps limits the fps to 2.0. Then it calculates the theoretical frame count as fps * duration_seconds. Finally, it applies min(max_frames, theoretical_count) to ensure the result never exceeds your absolute frame budget. This two-stage clamping guarantees both rate and volume constraints are respected.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →