Edit: As Mulvya noted, ffmpeg will always seek accurately with -ss. This issue is exclusive to avconv.
If you specify the -ss
option before the -i
option then avconv will inaccurately calculate the frame position.
You need to specify the -i
option first so that avconv knows it needs to "seek" through the stream until it accurately finds the correct timestamp.
Also, your example time 1.786
is not aligned to the frames per second -r 25
that you specify in your example.
Since 1/25=0.04
any value for -ss
should be divisible by 0.04
in order to specify an individual frame precisely.
The following should get the 46th frame of your video:
avconv -i input_video.h264 -r 25 -ss 1.8 -frames:v 1 output_image.jpg
If you want to get a specific frame by it's index then you will need to employ bc
:
avconv -i input_video.h264 -r 25 -ss 0$(echo "scale=2;1000/25" | bc -l) -frames:v 1 output_image.jpg
Where 1000
is the 1001st frame of the video (since 0/25
is the 1st frame).
Note that unlike @hamboy75's example we pass -l
(lower case L) to bc
so that it performs a floating point calculation (and doesn't round to the nearest integer). scale=2
is used to produce a number accurate to 2dp.
Also note that bc
has the "feature" of outputting numbers less than 1 without the leading zero (i.e. .04
) which avconv does not understand. Therefore we also need to insert a leading zero prefixed to the calculation 0$()
When using this command you will get output that looks like the following
frame= 0 fps= 0 q=0.0 size= 0kB time=10000000000.00 bitrate= 0.0kbit
frame= 0 fps= 0 q=0.0 size= 0kB time=10000000000.00 bitrate= 0.0kbit
frame= 0 fps= 0 q=0.0 size= 0kB time=10000000000.00 bitrate= 0.0kbit
frame= 0 fps= 0 q=0.0 size= 0kB time=10000000000.00 bitrate= 0.0kbit
frame= 0 fps= 0 q=0.0 size= 0kB time=10000000000.00 bitrate= 0.0kbit
This is because avconv is seeking through the file to find the specific frame you requested accurately. So frames with a larger index will take longer to extract because avconv will always "seek" through the stream from the stream's beginning.
It may therefore be more desirable to extract a range of frames:
avconv -i input_video.h264 -r 25 -ss 0$(echo "scale=2;7500/25" | bc -l) -t 0$(echo "scale=2;250/25" | bc -l) output_image_%04d.jpg
This example extracts 10 seconds worth of frames from 5 minutes within the video. Of course you could also just use:
avconv -i input_video.h264 -r 25 -ss 300.0 -t 10.0 | bc -l) output_image_%04d.jpg
However, remember that for any duration less than a second the value should be divisible by the frame rate for the video (i.e. 0.04
for 25 fps).
The images for each frame will be named output_image_0001.jpg
, output_image_0002.jpg
, output_image_0003.jpg
, etc. If you are looking to perform image comparison on the extracted frames you may want to consider using png
over jpg
for greater fidelity.
Note that if you specify a frame index larger than the number of frames present in the video, avconv will simply extract the last frame it finds. You may want to calculate the number of frames in a video by looking at the Duration:
part of the output of avconv -i input_video.h264
.