7

Suppose I have a list of timestamps, e.g. 1.4, 5.6, 10.5, etc.

For each timestamp, I want to extract the nearest frame and save it to a jpeg file.

I can do this one frame at a time:

ffmpeg -i video.mp4 -ss 1.4 -frames 1 frame_1_4.jpg
ffmpeg -i video.mp4 -ss 5.6 -frames 1 frame_5_6.jpg
ffmpeg -i video.mp4 -ss 10.5 -frames 1 frame_10_5.jpg
...

The problem with this approach is it seems to be resource intensive for a few reasons: running ffmpeg over and over incurs process overhead, and if the video has distant keyframes (or in the worst case none at all) then the decoder must decode several frames for each invocation. If you assume the worst case (no usable keyframes) then the most efficient way to extract the frames would be to decode the stream once and pick off each frame as it occurs. For only three frames it might not be too bad, but if I wanted to extract a list of, say, 100 or 200 or more frames, it quickly gets unwieldy even for files with good keyframes.

A very rudimentary test showed that for a test file, five minutes long, with keyframes every 5 seconds, each invocation of ffmpeg takes an average of about 0.22 seconds to run, with the fastest being 0.11 and the slowest being 0.34 seconds. The same file can be decoded to null in only 6 seconds. Therefore, if I wanted say 40 frames from this file, I'd be waiting at most 12 seconds, whereas the entire file's decode time is half that. This tells me a lot of the overhead is just in ffmpeg initializing, opening the file, etc.

A very naive approach might be to extract all frames to disk and then pick off the ones desired. Of course, this is even slower than just running ffmpeg multiple times since the JPEG encoder is running over and over and the disk space needed can quickly get insane.

Is there perhaps a vf select or related filter that can select out the specific list of timestamps I provide and extract only those frames?

(For clarification, the filenames with the timestamps do not have to be generated by ffmpeg; just sequential numbers would be fine since the calling application/script can just rename the files itself based on the same list of timestamps)

2

1 Answer 1

4

If you can convert timestamps into frame numbers - ie. 100, 184, 213 - then you can do:

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

(It seems -vsync 0 is important to get correct frames)

And it would need some script to get list and generate string for select=.

Script could even get timestamps and FPS to calculate frame numbers.


Based on question from Stack Overflow: shell - Extract list of specific frames using ffmpeg


Eventually I would use Python and module cv (OpenCV) because it can read frame by frame (probably using ffmpeg) from video file (or webcam, or stream) and save frame as image.


EDIT:

Documentation for select shows that you can use variable t as timestamp
so maybe it would work directly with 1.4, 5.6, 10.5

ffmpeg -i video.mp4 -vf select='eq(t\,1.4)+eq(t\,5.6)+eq(t\,10.5)' -vsync 0 frame_%d.jpg

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .