问题
I want to use avconv to get a single image at a specified time out of a video file.
I've read just enough about libav to think I know what I'm doing, but not enough to actually know.
I've tried:
avconv -ss 00:00:01.786 -r 25 -i input_video.h264 -frames 1 output_image.jpg
as well as using t to avoid the 'frames' parameter:
avconv -ss 00:00:01.786 -r 25 -i input_video.h264 -t 0.01 output_image.jpg
and passing in seconds rather than using the hh:mm:ss.xxx format:
avconv -ss 1.786 -r 25 -i input_video.h264 -t 0.01 output_image.jpg
What I see is when ss is set to 0 (that's '0', '00:00:00.000', '0.0', etc.) the output_image is just the first frame of the video. As expected.
Any other value for ss - even 0.0001 - gives me the last frame of the video.
I'm using the latest avconv from the Raspbian wheezy repo. This behaviour feels like a bug to me, but I don't know the other intricacies of video streaming well enough to be sure.
Does anyone have any idea what I'm doing wrong?
Bonus question: I'd actually like to get a whole bunch of these images out of the same video. Stringing commands together seemed to work before, ie:
avconv -ss 1.786 -r 25 -i input_video.h264 -t 0.01 output_image1.jpg
-ss 3.454 -r 25 -i input_video.h264 -t 0.01 output_image2.jpg
-ss 5.823 -r 25 -i input_video.h264 -t 0.01 output_image3.jpg
-ss etc,etc.
but I switched back to single images to debug this problem. Assuming the first issue gets solved, is that the best way to structure this command or is there a better one?
回答1:
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
.
回答2:
in my case i wanted an image each 60 seconds the first 3 minutes...
for i in {0..3} ; do avconv -ss echo $i*60.0 | bc
-i input_video.h264 -frames:v 1 -y out$i.jpg ; done
If you need it a certain times you can change this a bit :9.
来源:https://stackoverflow.com/questions/25308594/using-avconv-to-get-a-single-frame-from-h264-video-at-set-time