How Frame Resolution Scaling Enforces the 1998px Max Height Constraint for Claude Compatibility

The claude-video project caps frame height at 1998 pixels using an FFmpeg scale filter in skills/watch/scripts/frames.py that applies MAX_READ_DIMENSION = 1998 while preserving aspect ratio and ensuring even dimensions.

Frame resolution scaling in the bradautomates/claude-video repository ensures every extracted video frame meets Claude's display requirements. The implementation centers on a single FFmpeg filter configuration that hard-limits height regardless of source video dimensions or user-requested width.

The Core Scaling Mechanism in frames.py

The video frame extraction pipeline resides in skills/watch/scripts/frames.py. At the heart of the system is the private helper _scale_filter, which constructs the FFmpeg scale filter string:

def _scale_filter(resolution: int) -> str:
    return (
        f"scale=w='min({resolution},iw)':h='min({MAX_READ_DIMENSION},ih)':"
        "force_original_aspect_ratio=decrease:force_divisible_by=2"
    )

MAX_READ_DIMENSION = 1998 is defined near line 30 of the file. This constant represents the absolute ceiling for frame height.

The filter operates with three key behaviors:

  1. Width control — The output width becomes the smaller of the user-requested resolution (default 512) and the source width (iw)
  2. Height ceiling — The output height is capped at MAX_READ_DIMENSION (1998 px) or the source height (ih), whichever is lower
  3. Aspect ratio preservationforce_original_aspect_ratio=decrease prevents stretching by scaling down until both constraints are satisfied

Why 1998 Pixels?

Claude's UI cannot display video preview frames taller than 1998 pixels. By enforcing this limit at extraction time, the skill guarantees every generated JPEG renders without additional post-processing. No frames exceed the threshold regardless of original video resolution.

The filter also includes force_divisible_by=2 to prevent codec errors, as many video encoders require even-numbered dimensions.

How the Filter Integrates Into Extraction

During frame extraction, _scale_filter feeds directly into the FFmpeg command. In the extract function (lines 94-95), the constructed filter string becomes part of the video filter chain:

ffmpeg -vf fps=...,scale=w='min(512,iw)':h='min(1998,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2

This ensures the height constraint applies uniformly across all processing paths.

Practical Usage Examples

Extract frames with default settings (512px width, 1998px height cap)

python -m skills.watch.scripts.frames \
    /path/to/video.mp4 ./out_frames

The generated command uses the default resolution of 512, with height automatically capped at 1998 pixels.

Request larger width while maintaining height ceiling

python -m skills.watch.scripts.frames \
    /path/to/video.mp4 ./out_frames \
    --resolution 1024

The resulting filter becomes:


scale=w='min(1024,iw)':h='min(1998,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2

For a 3840 × 2160 source video, output frames scale to 1024 × 176 (width limited to 1024, height calculated from aspect ratio, both well under 1998px). For a vertical video like 2160 × 3840, the height hits the 1998px ceiling and width adjusts proportionally.

Debug the exact filter string

from skills.watch.scripts.frames import _scale_filter, MAX_READ_DIMENSION

resolution = 1280
print(_scale_filter(resolution))

# → scale=w='min(1280,iw)':h='min(1998,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2

Source File References

File Purpose
skills/watch/scripts/frames.py Contains MAX_READ_DIMENSION, _scale_filter, and extract function
skills/watch/scripts/watch.py Orchestrates the full pipeline and passes resolution to frames.py
AGENTS.md Documents Claude compatibility constraints

According to the bradautomates/claude-video source code, these components work together to guarantee frame resolution scaling never produces output incompatible with Claude's display limits.

Summary

  • MAX_READ_DIMENSION = 1998 in skills/watch/scripts/frames.py hard-codes the height ceiling
  • The _scale_filter helper builds an FFmpeg scale filter that applies this limit while preserving aspect ratio
  • Both dimensions use min() constraints — width respects user input or source, height respects 1998px or source
  • Even dimensions are enforced via force_divisible_by=2 to prevent codec errors
  • The filter integrates into FFmpeg commands in the extract function (lines 94-95)

Frequently Asked Questions

What happens if I request a resolution taller than 1998 pixels?

The _scale_filter function does not accept height as a parameter. You control width via the --resolution argument, and height derives from aspect ratio with the 1998px ceiling applied automatically. Even with extremely wide requests, height cannot exceed MAX_READ_DIMENSION.

Does the 1998px limit affect video download quality?

No. The limit applies only to frame extraction for preview images in Claude. The watch.py skill downloads full-resolution video separately. Frame scaling occurs afterward to generate Claude-compatible thumbnails.

Why is the constant named MAX_READ_DIMENSION rather than MAX_HEIGHT?

The naming suggests potential future flexibility, though the current implementation in frames.py applies this value exclusively to height in the FFmpeg filter. The constant's position and usage make its purpose unambiguous despite the generic name.

Can I override the 1998px limit by modifying the source code?

Yes. Changing MAX_READ_DIMENSION in skills/watch/scripts/frames.py (line ~30) would affect all subsequent extractions. However, frames exceeding 1998px height will not display properly in Claude's interface, making this modification incompatible with the tool's design intent.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →