FFMPEG COMMAND

Extract a thumbnail from a video with FFmpeg

A thumbnail is a one-frame encode. Seek with -ss, ask for one frame, choose the image quality. The variants scale it, let FFmpeg pick a representative frame, build a contact sheet and write WebP, each checked with ffprobe.

Verified with FFmpeg 9.0.1 on 26 August 2026. Search intent: ffmpeg extract frame.

the command
ffmpeg -ss 3.5 -i input.mp4 -frames:v 1 -q:v 2 thumb.jpg

Seeks to 3.5 s, decodes one frame and writes it as a high-quality JPEG at the source size. Output: 1280x720, 69,539 bytes. -ss before -i makes the seek instant on long files.

Flags, explained

-ss 3.5
Seek to 3.5 s. Before -i it jumps to the nearest keyframe and decodes forward to the exact time, fast; after -i it decodes from the start.
-frames:v 1
Stop after one video frame. Without it FFmpeg writes every frame as an image (or fails on a single-image filename).
-q:v 2
JPEG quality, 2 (best) to 31 (worst). At 31 the same frame was 16,398 bytes instead of 69,539.
thumbnail=90
Pick the most representative frame of each batch of 90 frames.
fps=1/2 ... tile=3x2
Sample one frame every 2 s and arrange the samples in a 3 columns by 2 rows grid.
thumb-%03d.jpg
Output pattern for an image sequence: %03d is a zero-padded counter starting at 1.
-c:v libwebp -quality 80
Encode the frame as WebP at quality 80.

Variants

Scaled thumbnail

shell
ffmpeg -ss 3.5 -i input.mp4 -frames:v 1 -vf "scale=640:-2" -q:v 2 thumb.jpg

Same frame at 640x360, 23,969 bytes. -2 keeps the height even and proportional.

Let FFmpeg pick a representative frame

shell
ffmpeg -i input.mp4 -vf "thumbnail=90" -frames:v 1 thumb.png

The thumbnail filter looks at batches of 90 frames and keeps the one closest to the average, which avoids black fades and cut frames. PNG output: 1280x720, 162,286 bytes. It decodes the batch, so it is slower than a seek.

Contact sheet (one frame every 2 s in a 3x2 grid)

shell
ffmpeg -i input.mp4 -vf "fps=1/2,scale=320:-2,tile=3x2" -frames:v 1 sheet.jpg

fps=1/2 takes one frame every two seconds, each is scaled to 320 px wide and tile lays six of them out. Output: 960x360, 33,691 bytes for the 12 s file.

One image every 4 seconds

shell
ffmpeg -i input.mp4 -vf "fps=1/4" thumb-%03d.jpg

Writes thumb-001.jpg, thumb-002.jpg and so on: 3 files for a 12 s video. Add -q:v 2 for quality and a scale filter before fps to shrink them.

WebP output

shell
ffmpeg -ss 3.5 -i input.mp4 -frames:v 1 -c:v libwebp -quality 80 thumb.webp

Needs a build with libwebp (the Homebrew macOS build answered "Encoder not found"; the command ran on a build with --enable-libwebp). Output: 1280x720, 30,262 bytes against 69,539 for the JPEG.

Pitfalls we hit

  • Forgetting -frames:v 1 with a plain filename either fails or, with a %d pattern, dumps every frame of the video to disk.
  • A timestamp beyond the end of the file produces no output and no error at -v error. Probe the duration first.
  • The frame at -ss is often a fade or a cut. Use the thumbnail filter, or pick a timestamp from detect_scenes, when the image is user-facing.
  • JPEG quality with -q:v is a mjpeg scale (2 to 31), not a percentage; -q:v 80 is not "80 %", it is clamped to the worst setting.

How we verified it

Run with FFmpeg 9.0.1 on 26 August 2026 against a 12 s, 1280x720, 30 fps H.264/AAC file. Primary command: mjpeg 1280x720, 69,539 bytes; the same seek with -ss after -i gave a byte-identical file. scale=640:-2: 640x360, 23,969 bytes. thumbnail=90: png 1280x720, 162,286 bytes. Contact sheet: 960x360, 33,691 bytes. fps=1/4 sequence: 3 files. -q:v 31: 16,398 bytes. WebP ran on the KinoPipe worker build (Debian, libwebp): 1280x720, 30,262 bytes; the Homebrew build reported "Encoder not found".

The same edit as a typed operation

generate_video_thumbnail takes a timestamp and an output format, validates the timestamp against the probed duration and returns a signed image URL. generate_video_filmstrip does the contact sheet (2 to 20 equidistant frames in one tile) without a filter graph to write.

MCP tool: generate_video_thumbnailPOST /api/v1/tools/video-thumbnail-generator
request body
{
  "inputs": [{ "id": "main", "url": "https://example.com/video.mp4" }],
  "options": { "timestamp": 3.5, "format": "webp" }
}

FAQ

How do I extract a single frame from a video with FFmpeg?

Seek with -ss before -i, ask for one frame with -frames:v 1 and give an image filename: ffmpeg -ss 3.5 -i input.mp4 -frames:v 1 -q:v 2 thumb.jpg. The output is at the source resolution unless you add a scale filter.

How do I get a thumbnail every N seconds?

Use the fps filter with a fraction and an output pattern: -vf fps=1/4 thumb-%03d.jpg writes one image every 4 seconds. Scale before fps to control the size.

How do I make a contact sheet or filmstrip?

Chain fps, scale and tile: -vf "fps=1/2,scale=320:-2,tile=3x2" -frames:v 1 sheet.jpg samples a frame every 2 s and lays six of them in a grid.

Why is my thumbnail black?

The frame at the timestamp is a fade or the first frame of the file. Move the timestamp, or let the thumbnail filter pick a representative frame from a batch.

Related commands

All FFmpeg commands