ffmpeg extract pictures from several selected time points

3

1

I have a videofile and like to extract a single image from (about 1500) specific timepoints.(expl. -ss 00:50, ss 00:67, ss: 01:70 ...)

Is there a way to do this? I tried out to use this command:

ffmpeg.exe -ss 00:50 -i all.avi -t 00:00:01 -vframes 1 images-filename_%03d.png

This works great for a single picture, but is there a way to do this for such a lot of specific timepoints?

Does anyone have an advice?

Jakob Winter

Posted 2018-03-05T14:49:04.330

Reputation: 31

Answers

7

This answer improves upon @slhck's answer, which may fail to work in some situations.

One subtlety in selecting frames by (approximate) timestamp is that the actual timestamp of the frame may not be exactly equal to the desired timestamp. For example, in a video with framerate 23.98 (24000/1001) there is no frame with timestamp 1.0 - the closest frame has a timestamp value of 1.001.

The expression eq(t, specific_timepoint) evaluates to true only if t is exactly equal to specific_timepoint. Therefore it will fail to select a frame in the situation described above. As a workaround, we can select the first frame immediately following the desired timestamp, i.e. the frame whose timestamp value is greater than or equal to the specified timepoint, while the timestamp of its preceding frame was less than the specified timepoint. The selection expression for a single timepoint will be

lt(prev_pts * TB,timepoint) * gte(pts * TB,timepoint)

Note that I deliberately did not use a shorter variant

between(timepoint, prev_pts*TB, pts*TB)

which is equivalent to

lte(prev_pts * TB,timepoint) * gte(pts * TB,timepoint)

as it would select two consecutive frames when the timestamp value of (the first) one of them exactly matches the specified timepoint value.

Example selecting the frames corresponding to or immediately following the timepoints 1.5, 10 and 20:

ffmpeg -i input.mp4 -filter:v \
    "select='lt(prev_pts*TB\,1.5)*gte(pts*TB\,1.5) \
            +lt(prev_pts*TB\,10)*gte(pts*TB\,10)   \
            +lt(prev_pts*TB\,20)*gte(pts*TB\,20)'" \
    -vsync drop images-filename_%03d.png

Leon

Posted 2018-03-05T14:49:04.330

Reputation: 286

2

Add multiple timestamps as options for the select filter.

ffmpeg -i input.mp4 -filter:v \
"select='eq(t\,1.5)+eq(t\,10)+eq(t\,20)'" \
-vsync drop images-filename_%03d.png

If the filter evaluates to true, it'll output a frame, so the addition of checks comparing the frame timestamp t against a particular timestamp in seconds (e.g., 1.5) will give you all the frames at those timestamps.

slhck

Posted 2018-03-05T14:49:04.330

Reputation: 182 472

I added an improved version of this answer.

– Leon – 2018-06-10T07:26:34.307

Interesting approach! Haven't worked with fractional frame rates much in the past years, but they do cause lots of issues. – slhck – 2018-06-10T21:19:34.090