可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've browsed the internet for this very intensively, but I didn't find what I needed, only variations of it which are not quite the thing I want to use.
I've got several videos in different lengths and I want to extract 20 images out of every video from start to the end, to show the broadest impression of the video.
So one video is 16m 47s long => 1007s in total => I have to make one snapshot of the video every 50 seconds.
So I figured using the -r switch of ffmpeg with the value of 0.019860973 (eq 20/1007) but ffmpeg tells me that the framerate is too small for it...
The only way I figured out to do it would be to write a script which calls ffmpeg with a manipulated -ss switch and using -vframes 1 but this is quite slow and a little bit off for me since ffmpegs numerates the images itself...
Any suggestions or directions?
Thanks,
Vapire
回答1:
I was trying to find the answer to this question too. I made use of radri's answer but found that it has a mistake.
ffmpeg -i video.avi -r 0.5 -f image2 output_%05d.jpg
produces a frame every 2 seconds because -r means frame rate. In this case, 0.5 frames a second, or 1 frame every 2 seconds.
With the same logic, if your video is 1007 seconds long and you need only 20 frames, you need a frame every 50.53 seconds. Translated to frame rate, would be 0.01979 frames a second.
So your code should be
ffmpeg -i video.avi -r 0.01979 -f image2 output_%05d.jpg
I hope that helps someone, like it helped me.
回答2:
I know I'm a bit late to the party, but I figured this might help:
For everyone having the issue where ffmpeg is generating an image for every single frame, here's how I solved it (using blahdiblah's answer):
First, I grabbed the total number of frames in the video:
ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2
Then I tried using select to grab the frames:
ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>
But, no matter what the mod(n,100) was set to, ffmpeg was spitting out way too many frames. I had to add -vsync 0 to correct it, so my final command looked like this:
ffmpeg -i <input_file> -vsync 0 -vf "select='not(mod(n,100))'" <output_file>
(where 100 is the frame-frequency you'd like to use, for example, every 100th frame)
Hope that saves someone a little trouble!
回答3:
You could try convert video to N number of images ?
ffmpeg -i video.avi image%d.jpg
Update:
Or extract frame every 2 seconds:
ffmepg -i video.avi -r 0.5 -f image2 output_%05d.jpg
Update:
To get the video duration:
ffmpeg -i video.avi 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//
Then, depends on your programming language, you convert it into seconds. For example, in PHP, you could do it like this:
$duration = explode(":",$time);
$duration_in_seconds = $duration[0]*3600 + $duration[1]*60+ round($duration[2]);
where $time is the video duration. The you can execute $duration_in_seconds / 20
ffmepg -i video.avi -r $duration_in_seconds/20 -f image2 output_%05d.jpg
回答4:
In general, ffmpeg processes frames as they come, so anything based on the total duration/total number of frames requires some preprocessing.
I'd recommend writing a short shell script to get the total number of frames using something like
ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2
and then use the select
video filter to pick out the frames you need. So the ffmpeg command would look something like
ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>
except that instead of every hundredth frame, 100
would be replaced by the number calculated in the shell script to give you 20 frames total.
回答5:
I was having the same problem and came up with this script which seems to do the trick:
#/bin/sh
total_frames=`ffmpeg -i ./resources/iceageH264.avi -vcodec copy -acodec copy -f null dev/null 2>&1 | grep 'frame=' | cut -f 3 -d ' '`
numframes=$3
rate=`echo "scale=0; $total_frames/$numframes" | bc`
ffmpeg -i $1 -f image2 -vf "select='not(mod(n,$rate))'" -vframes $numframes -vsync vfr $2/%05d.png
To use it, save it as a .sh file and run it using the following parameters to export 20 frames:
./getFrames.sh ~/video.avi /outputpath 20
This will give you the specified number of frames distributed equally throughout the video.
回答6:
i had a similar question, namely how to extract ONE frame halfway a movie of unknown length. Thanks to the answers here, I came up with this solution which works very well indeed, I thought posting the php code would be useful:
<?php
header( 'Access-Control-Allow-Origin: *' );
header( 'Content-type: image/png' );
// this script returns a png image (with dimensions given by the 'size' parameter as wxh)
// extracted from the movie file specified by the 'source' parameter
// at the time defined by the 'time' parameter which is normalized against the
// duration of the movie i.e. 'time=0.5' gives the image halfway the movie.
// note that this can also be done using php-ffmpeg..
$time = floatval( $_GET[ 'time' ] );
$srcFile = $_GET[ 'source' ];
$size = $_GET[ 'size' ];
$tmpFile = tempnam( '/tmp', 'WTS.txt' );
$destFile = tempnam( '/tmp', 'WTS.png' );
$timecode = '00:00:00.000';
// we have to calculate the new timecode only if the user
// requests a timepoint after the first frame
if( $time > 0.0 ){
// extract the duration via a system call to ffmpeg
$command = "/usr/bin/ffmpeg -i ".$srcFile." 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,// >> ".$tmpFile;
exec( $command );
// read it back in from tmpfile (8 chars needed for 00:00:00), skip framecount
$fh = fopen( $tmpFile, 'r' );
$timecode = fread( $fh, 8 );
fclose( $fh );
// retieve the duration in seconds
$duration = explode( ":", $timecode );
$seconds = $duration[ 0 ] * 3600 + $duration[ 1 ] * 60 + round( $duration[ 2 ] );
$timepoint = floor( $seconds * $time );
$seconds = $timepoint % 60;
$minutes = floor( $timepoint / 60 ) % 60;
$hours = floor( $timepoint / 3600 ) % 60;
if( $seconds < 10 ){ $seconds = '0'.$seconds; };
if( $minutes < 10 ){ $minutes = '0'.$minutes; };
if( $hours < 10 ){ $hours = '0'.$hours; };
$timecode = $hours.':'.$minutes.':'.$seconds.'.000';
}
// extract an image from the movie..
exec( '/usr/bin/ffmpeg -i '.$srcFile.' -s '.$size.' -ss '.$timecode.' -f image2 -vframes 1 '.$destFile );
// finally return the content of the file containing the extracted frame
readfile( $destFile );
?>