22

I'm trying to use ffmpeg on a video to extract a list of specific frames, denoted by their frame numbers. So lets say I want to extract just one frame from 'test_video.mp4', frame number 150 to be exact. I can use the following command

ffmpeg -i test_video.mp4 -vf "select=gte(n\, 150)" -vframes 1 ~/test_image.jpg

But what if I want to exact a list of frames, such as [100, 110, 127, 270, 300]? I have been looking at this page (https://ffmpeg.org/ffmpeg-filters.html#select_002c-aselect) and can see that there are ways to extract multiple frames based on parity of frame number, timestamps, etc, but I can't find the syntax for doing what I need to do.

Ideally I would extract the list of given frames under the naming convention out_image%03d.jpg where the %03d is replaced by the given frame number, although a timestamp would also work.

Is there a way to do this?

John Allard
  • 3,564
  • 5
  • 23
  • 42
  • 1
    I tried a lot of combinations using syntax like the following: `ffmpeg -i Hamlet.mp4 -vf 'select=eq(1000\,n)+eq(150\,n)' -f image2 out_image%03d.jpg` -- but this did not do what I expected. What it did was start with frame 150 and gave me the next frame 151 (and following)... seems that select in this case is getting stuck and is just firing off the stream.... I will play with it a bit more. – Michael Back Jul 08 '16 at 04:06

2 Answers2

43

Use

ffmpeg -i in.mp4 -vf select='eq(n\,100)+eq(n\,184)+eq(n\,213)' -vsync 0 frames%d.jpg

FFmpeg is primarily a processor of timed video i.e. media with a cycle rate such as framerate or sample rate. It assumes that the output should be at the same rate as the source. If inadequate frames are supplied, it duplicates unless told not to. -vsync 0 is added which, in this case, tells it to suppress duplication.

Gyan
  • 85,394
  • 9
  • 169
  • 201
2

A very naive answer...

#! /bin/sh -e
# framegrab v0.1

frames="$1"
filename="$2"
printf '%s' "$frames" |
sed 's:\[\|\]::g; s:[, ]\+:\n:g' |
xargs printf '%03d\n' |
xargs -IFRAME ffmpeg -i "$filename" -vf "select=eq(n\,FRAME)" -vframes 1 out_imageFRAME.jpg

Call it with your argument list:

 framegrab '[100, 110, 127, 270, 300]' test_video.mp4
Michael Back
  • 1,821
  • 1
  • 16
  • 17
  • thanks for your help! I'm having trouble figuring out on my own though, does this run one ffmpeg process per frame number or is it getting all of the desired frames through a single ffmpeg call? – John Allard Jul 07 '16 at 21:04
  • I couldn't get this to work, it only outputs a single image file even when given a list of 5 frame numbers. – John Allard Jul 07 '16 at 21:09
  • So I fixed it by changing `sed 's:\[::g; s:\]::g; s/, */\n/g' |` to `sed 's:\[::g; s:\]::g; s/, */ /g' |`, but I can see now that it is just running ffmpeg many times over over. This technically works but is not ideal for the embedded system I'm running on, I was hoping there was a way for ffmpeg to handle it in one fell swoop – John Allard Jul 07 '16 at 21:18
  • Absolutely is not ideal.... It is just something to get you going for now. I have just looked at the man page for ffmpeg -- look at the following example: ffmpeg -i foo.avi -r 1 -s WxH -f image2 foo-%03d.jpeg – Michael Back Jul 07 '16 at 21:22
  • As far as `' '` verses `\n` -- it may be our versions of xargs don't work the same? I tested this on Linux and `' '` delimiting would not work for me... but `\n` did. – Michael Back Jul 08 '16 at 04:17