How Claude-Video's Frame Budget Scales With Video Duration: A Technical Deep Dive

Claude-Video uses a budget-aware algorithm that dynamically adjusts frames-per-second based on video duration, ensuring the total frame count never exceeds a hard cap of approximately 300 frames regardless of video length.

This article examines how the bradautomates/claude-video repository implements scalable video frame extraction. The system intelligently balances visual coverage against processing constraints through a ceiling-based approach that adapts to any input duration.

The Frame Budget Architecture

Claude-Video's frame extraction system centers on three core constants defined in the configuration layer. These values govern how the tool responds to videos of varying lengths.

  • MAX_FRAMES: Hard upper limit (default ~300 frames)
  • DEFAULT_FPS: Preferred extraction rate (typically 30 fps)
  • duration_secs: Runtime detected via ffprobe

The scaling logic lives primarily in skills/watch/scripts/frames.py, with duration detection handled by skills/watch/scripts/download.py and configuration values stored in skills/watch/scripts/config.py.

Duration Detection With ffprobe

Before any frame extraction occurs, the system must determine video length. The download.py script invokes ffprobe to retrieve precise duration metadata.


# Simplified representation of duration detection

import subprocess
import json

def get_video_duration(path):
    out = subprocess.check_output([
        "ffprobe", "-v", "error",
        "-show_entries", "format=duration",
        "-of", "json", path
    ])
    return float(json.loads(out)["format"]["duration"])

This duration value feeds directly into the dynamic fps calculation that follows.

Dynamic FPS Calculation Formula

The heart of Claude-Video's scaling mechanism is a single mathematical operation that computes the target extraction rate. From frames.py, the formula executes as:

[ \text{target_fps} = \min(\text{DEFAULT_FPS},; \frac{\text{MAX_FRAMES}}{\text{duration_secs}}) ]

This cap-and-floor approach produces two distinct behavioral modes:

Short videos (e.g., 10 seconds): MAX_FRAMES / duration_secs yields 30 fps or higher, so extraction proceeds at DEFAULT_FPS—capturing maximum detail without exceeding the budget.

Long videos (e.g., 5 minutes / 300 seconds): The division drops to 1 fps or lower, forcing sparse sampling that preserves visual coverage across the full timeline while respecting the 300-frame ceiling.

FFmpeg Execution With Computed Rate

The calculated target_fps passes directly to ffmpeg's fps video filter. This filter transparently drops or duplicates frames to achieve the desired rate, guaranteeing the output never surpasses MAX_FRAMES.


# Representative extraction logic from frames.py

import subprocess
import pathlib

MAX_FRAMES = 300
DEFAULT_FPS = 30

def extract_frames(video_path, output_dir):
    # Duration detection

    duration_secs = get_video_duration(video_path)
    
    # Budget-aware fps calculation

    target_fps = min(DEFAULT_FPS, MAX_FRAMES / duration_secs)
    
    # Directory preparation

    pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    # Frame extraction with computed rate

    subprocess.run([
        "ffmpeg", "-i", video_path,
        "-vf", f"fps={target_fps}",
        "-q:v", "2",
        f"{output_dir}/%05d.jpg"
    ], check=True)

Performance Implications

The scaling strategy creates predictable performance characteristics:

  • Frame count remains bounded at ~300 frames maximum regardless of input duration
  • Processing time grows roughly linearly with video length due to ffmpeg's sequential decoding
  • Memory usage stays constant since the pipeline streams frames rather than buffering

This bounded behavior protects downstream components—such as captioning models or Whisper transcription—from resource exhaustion when processing arbitrarily long videos.

Practical Usage Example


# Running the watch skill on a 2-minute video

# The skill automatically computes ~2.5 fps to stay under budget

$ claudecode /watch https://example.com/long-video.mp4

# For a 2-minute (120s) video:

# target_fps = min(30, 300 / 120) = min(30, 2.5) = 2.5 fps

# Result: approximately 300 frames extracted across full duration

Summary

  • MAX_FRAMES (~300) acts as an inviolable ceiling on total frame output
  • ffprobe duration detection enables duration-aware parameter adjustment
  • min(DEFAULT_FPS, MAX_FRAMES/duration) formula guarantees budget compliance
  • ffmpeg fps filter executes the computed rate with frame dropping/duplication
  • Processing resources scale predictably without hard duration limits on input videos

Frequently Asked Questions

What is the maximum number of frames Claude-Video will extract from any video?

Claude-Video enforces a hard MAX_FRAMES limit of approximately 300 frames, defined in skills/watch/scripts/config.py. This ceiling applies universally regardless of video duration, ensuring consistent downstream processing loads.

Why does frame extraction slow down for longer videos?

The system intentionally reduces frames-per-second for lengthy inputs. A 10-second video extracts at 30 fps, while a 5-minute video drops to ~1 fps. This maintains representative visual coverage across the full timeline without exceeding the 300-frame budget.

Can I customize the MAX_FRAMES or DEFAULT_FPS values?

Both constants reside in skills/watch/scripts/config.py and can be modified before deployment. Adjusting MAX_FRAMES upward increases visual granularity at the cost of downstream processing time, while changing DEFAULT_FPS alters the baseline extraction rate for short videos.

How does Claude-Video handle videos with variable frame rates?

The ffprobe duration detection in skills/watch/scripts/download.py reports total runtime in seconds, which drives the fps calculation. The ffmpeg fps filter then normalizes output to the computed constant rate, smoothing any source variabilities into a uniform extraction pattern.

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 →