annotools.video¶
Frame sampling for models with no native video input: decode at a target rate, thin evenly to a frame
budget, then send the frames through the preview pipeline. Requires annotools[media] (PyAV).
video ¶
Frame sampling from video files via PyAV (annotools[media]).
sample_frames ¶
sample_frames(
uri: str,
*,
fps: float = 1.0,
start: float | None = None,
end: float | None = None,
max_frames: int = 32,
) -> tuple[list[tuple[float, Image.Image]], dict[str, Any]]
Decode uri and return frames sampled at fps between start and end.
Frames are picked at the first decoded timestamp at or after each target time, then thinned
evenly to max_frames (first and last kept when max_frames > 1) so a long clip cannot blow the
token budget; feed the result through
preview (and a grid) before sending it to a model that has no native video input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
Local path or fsspec URL of a video PyAV can decode. |
required |
fps
|
float
|
Target sampling rate in frames per second (> 0). |
1.0
|
start
|
float | None
|
Start time in seconds (>= 0); |
None
|
end
|
float | None
|
End time in seconds (> |
None
|
max_frames
|
int
|
Upper bound on returned frames (>= 1). |
32
|
Returns:
| Type | Description |
|---|---|
list[tuple[float, Image]]
|
|
dict[str, Any]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an invalid range, rate, or |
FileNotFoundError
|
When the URI does not exist. |
OSError
|
For other read failures. |
ImportError
|
When PyAV is not installed ( |
Examples:
>>> from annotools import sample_frames
>>> frames, meta = sample_frames("clip.mp4", fps=1, end=5)
>>> [round(t) for t, _ in frames]
[0, 1, 2, 3, 4]
References
- Spec:
.agents/knowledge/spec/preview-video.md(annotools repository). - Claude and GPT accept no native video in the vision path (send frames); Gemini samples video
at 1 fps, 258 tokens per frame:
.agents/knowledge/references/mllm-models.md(verified 2026-08-27).