Extract I-Frames in FFmpeg Using the Select Filter

This guide explains how to use FFmpeg's select video filter to isolate specific video frames based on criteria such as picture type. By targeting frame properties like I-frames (intra-coded keyframes), you can efficiently extract scene headers, thumbnails, or analyze video structure directly from the command line without re-encoding unnecessary content.

The Basic Syntax for Picture Type Selection

The select filter evaluates an expression for each frame. If the expression evaluates to a non-zero value, the frame is kept; if zero, it is discarded.

To filter frames by their slice type, use the pict_type variable. For example, to target only I-frames, compare pict_type to PICT_TYPE_I (or simply I):

ffmpeg -i input.mp4 -vf "select='eq(pict_type\,I)'" -vsync vfr output_%04d.jpg

Note: In newer versions of FFmpeg (5.1+), -vsync vfr is deprecated in favor of -fps_mode vfr.

Why Frame Rate Control Matters

When using the select filter, you must set variable frame rate mode using -fps_mode vfr (or -vsync vfr on older builds).

If you omit this flag, FFmpeg defaults to a constant frame rate (CFR) and duplicates remaining frames to fill the gaps left by dropped frames, resulting in duplicate images or an unaltered frame count.

Exporting Selected Frames to a Video File

If your goal is to output a playable video containing only the selected frames instead of still images, you must reset the presentation timestamps (PTS) using the setpts filter. Without resetting timestamps, players will pause during the gaps between frames.

Chain select with setpts:

ffmpeg -i input.mp4 -vf "select='eq(pict_type\,I)',setpts=N/FRAME_RATE/TB" -fps_mode vfr output.mp4
  • setpts=N/FRAME_RATE/TB: Reassigns timestamps sequentially based on the new frame count (N), creating smooth playback at the container's standard frame rate.

Common Criteria Options for select

The pict_type variable supports all standard frame types:

  • I-Frames: select='eq(pict_type\,I)'
  • P-Frames (Predicted): select='eq(pict_type\,P)'
  • B-Frames (Bi-directional): select='eq(pict_type\,B)'

You can also target other conditions alongside or instead of pict_type:

  • Keyframes (Independent of type definition):
    select='eq(key\,1)'
  • Combining conditions (e.g., I-frames with high scene change score):
    select='eq(pict_type\,I)*gt(scene\,0.3)'
  • Every Nth frame:
    select='not(mod(n\,30))'