TechEarl

Extract a Thumbnail or Frame From a Video with ffmpeg

Grab a thumbnail or a single frame from a video with ffmpeg: pull a frame at a timestamp, let the thumbnail filter pick a representative one, sample one every N seconds, and control JPEG quality with -q:v.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
Extract a thumbnail or single frame from a video with ffmpeg: grab a frame at a timestamp with a fast -ss seek, use the thumbnail filter for a representative frame, sample one every N seconds, and set JPEG quality with -q:v 2.

To pull a single frame out of a video as a thumbnail, seek to a timestamp and tell ffmpeg to write one frame:

bash
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 thumb.jpg

That grabs the frame at the five-second mark and writes it to thumb.jpg. The whole trick is the placement of -ss: putting it before -i makes ffmpeg seek the input by keyframe before it starts decoding, which is near-instant even on a two-hour file. -frames:v 1 then stops after the first decoded frame. The rest of this page is the variations: letting ffmpeg pick a representative frame for you, sampling one frame every N seconds, controlling JPEG quality, and scaling the output.

Grab a frame at a specific timestamp

The command above is the one you want most of the time. A few notes on the parts:

bash
# Five seconds in, written as a JPEG
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 thumb.jpg

# Same idea, written as PNG (lossless, larger file)
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 thumb.png

# Seconds work too: 90 = 1:30
ffmpeg -ss 90 -i in.mp4 -frames:v 1 thumb.jpg

-ss before -i is a fast input seek: ffmpeg jumps to the nearest keyframe at or before the timestamp, so it is quick but lands on a keyframe rather than the exact requested moment. For a thumbnail that almost never matters. When you need the frame at that exact time (frame-accurate), use two -ss flags: an input seek before -i to jump quickly to a keyframe a little before the target, then an output seek after -i to decode forward the remaining distance to the exact timestamp:

bash
# Input-seek to ~4s (fast, keyframe), then decode forward 1s to land on exactly 5s
ffmpeg -ss 00:00:04 -i in.mp4 -ss 00:00:01 -frames:v 1 thumb.jpg

The two add up to the target time (4s + 1s = 5s), so you get the speed of input seeking plus the frame accuracy of an output seek. Accurate seeking is on by default; pass -noaccurate_seek to turn it off. For a single still this is overkill, but it is the pattern to reach for when the precise frame matters.

Let ffmpeg pick a representative frame

If you do not have a timestamp in mind and just want a frame that looks like the video (not a black fade-in or a transition), use the thumbnail filter. It analyses a batch of frames and picks the one most representative of the set:

bash
ffmpeg -i in.mp4 -vf "thumbnail" -frames:v 1 thumb.jpg

By default it considers 100 frames at a time and scores them against the batch average, so it tends to skip the near-black opening frames that a naive first-frame grab lands on. It is the right default for auto-generating a poster image when you cannot hand-pick the moment. You can widen the analysis window by passing a frame count, for example thumbnail=300, at the cost of a little more decoding.

Sample one frame every N seconds

For a series of stills (a storyboard, a set of preview images, the input for a contact sheet) use the fps filter with a fractional rate. fps=1/10 means one frame every ten seconds:

bash
# One frame every 10 seconds, numbered shot-001.jpg, shot-002.jpg, ...
ffmpeg -i in.mp4 -vf "fps=1/10" shot-%03d.jpg

The %03d in the output name is a printf-style counter that ffmpeg fills in, zero-padded to three digits. Change the fraction to change the cadence: fps=1 is one frame per second, fps=1/60 is one per minute. Note that here -ss does not help much, because you are walking the whole file by design, so leave it off and let the filter sample across the entire duration.

If you want a fixed number of evenly spaced frames regardless of length, compute the rate from the duration, or reach for the thumbnail filter per segment. For a single tiled preview image, the tile filter pairs with fps to lay the samples out in a grid in one pass.

Control JPEG quality with -q:v

JPEG thumbnails default to a middling quality that can look soft. The -q:v flag (also spelled -qscale:v) sets the quality on a scale where lower is better: 1 is the best, 31 is the worst. For a crisp thumbnail, 2 is the standard choice:

bash
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 -q:v 2 thumb.jpg

-q:v 2 is the sweet spot for stills: visually near-lossless, still a reasonable file size. Drop to -q:v 1 if you want the absolute best and do not care about bytes. This flag only applies to lossy formats like JPEG; for PNG output it does nothing (PNG is already lossless), so save it for .jpg.

Scale the thumbnail

Resize while you extract by adding the scale filter. Use -1 for one dimension to preserve the aspect ratio:

bash
# 320px wide, height auto to keep the aspect ratio
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 -vf "scale=320:-1" -q:v 2 thumb.jpg

Combine it with the thumbnail filter by chaining them in one -vf chain (comma-separated, applied left to right):

bash
ffmpeg -i in.mp4 -vf "thumbnail,scale=320:-1" -frames:v 1 -q:v 2 thumb.jpg

If a dimension comes out odd and your encoder complains, use -2 instead of -1 to round to the nearest even number, which some codecs require.

-frames:v versus -vframes

You will see both spellings in older guides. They do the same job for video, but -frames:v is the current form and -vframes is an obsolete alias:

bash
# Current, preferred
ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 thumb.jpg

# Old alias, still works but deprecated
ffmpeg -ss 00:00:05 -i in.mp4 -vframes 1 thumb.jpg

Per the official documentation, -vframes is a deprecated alias and the stream-specifier form -frames:v is the one to use in new commands. Both stop writing after the given number of frames; reach for -frames:v so your commands age well.

FAQ

See also

Sources

Authoritative references this article was fact-checked against.

Tagsffmpegvideo thumbnailextract framescreenshot videoCLILinuxvideo

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Extract Audio From a Video with ffmpeg

Pull the audio out of a video with ffmpeg: copy the stream untouched when you just want to demux (fast, lossless), or re-encode to MP3, AAC, or WAV when you need a different format. Plus how to check the source codec first and grab a single segment.