Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dump last frame of video file using ffmpeg/mencoder/transcode et. al

I'd like to grab the last frame in a video (.mpg, .avi, whatever) and dump it into an image file (.jpg, .png, whatever). Toolchain is a modern Linux command-line, so things like mencoder, transcode, ffmpeg &c.

Cheers, Bob.

like image 750
bobbogo Avatar asked Mar 09 '11 15:03

bobbogo


People also ask

How do I extract frames from a video using ffmpeg?

Use the FFmpeg executable with the seek option. You'll need to convert to a time first, e.g. if I want frame 150 and my video is 29.97 FPS the command will be ffmpeg -ss 00:00:05.01 -i myvideo. avi -frames:v 1 myimage. jpg .

What is frame in ffmpeg?

ffmpeg can be used to change the frame rate of an existing video, such that the output frame rate is lower or higher than the input frame rate. The output duration of the video will stay the same.

How do I set video duration in ffmpeg?

Use the -t option to specify a time limit: `-t duration' Restrict the transcoded/captured video sequence to the duration specified in seconds. hh:mm:ss[.


5 Answers

This isn't a complete solution, but it'll point you along the right path.

Use ffprobe -show_streams IN.AVI to get the number of frames in the video input. Then

ffmpeg -i IN.AVI -vf "select='eq(n,LAST_FRAME_INDEX)'" -vframes 1 LAST_FRAME.PNG

where LAST_FRAME_INDEX is the number of frames less one (frames are zero-indexed), will output the last frame.

like image 167
blahdiblah Avatar answered Oct 04 '22 19:10

blahdiblah


I have a mp4 / h264 matroska input video file. And none of the above solutions worked for me. (Although I sure they work for other file formats).

Combining samrad's answer above and also this great answer and came up with this working code:

input_fn='output.mp4'

image_fn='output.png'

rm -f $image_fn

frame_count=`ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 $input_fn`

ffmpeg -i $input_fn -vf "select='eq(n,$frame_count-1)'" -vframes 1 "$image_fn" 2> /dev/null
like image 23
sr9yar Avatar answered Oct 04 '22 18:10

sr9yar


I couldn't get Nelson's solution to work. This worked for me. https://gist.github.com/samelie/32ecbdd99e07b9d8806f

EDIT (just in case the link disappears, here is the shellscript—bobbogo):

#!/bin/bash

fn="$1"
of=`echo $1 | sed s/mp4/jpg/`

lf=`ffprobe -show_streams "$fn" 2> /dev/null | grep nb_frames | head -1 | cut -d \= -f 2`
rm -f "$of"
let "lf = $lf - 1"
ffmpeg -i $fn -vf select=\'eq\(n,$lf\) -vframes 1 $of
like image 42
samrad Avatar answered Oct 04 '22 19:10

samrad


One thing I have not seen mentioned is that the expected frame count can be off if the file contains dupes. If your method of counting frames is causing your image extraction command to come back empty, this might be what is mucking it up.

I have developed a short script to work around this problem. It is posted here.

like image 29
Jeff Avatar answered Oct 04 '22 19:10

Jeff


I found that I had to add -vsycn 2 to get it to work reliably. Here's the full command I use:

ffmpeg -y -sseof -3 -i $file -vsync 2 -update 1 -vf scale=640:480 -q:v 1 /tmp/test.jpg

If you write to a NFS folder then the results will be very inconsistent. So I write to /tmp then copy the file once done. If it is not done, I do a kill -9 on the process ID.

like image 29
Faraz H Avatar answered Oct 04 '22 19:10

Faraz H