FFMPEG:从可变长度的视频中提取20个图像

时间:2011-12-30 12:22:11

标签: ffmpeg video-processing thumbnails

我已经非常密切地浏览了互联网,但我找不到我需要的东西,只是它的变化并不是我想要使用的东西。

我有几个不同长度的视频,我想从开始到结束从每个视频中提取20张图片,以展示最广泛的视频印象。

所以一个视频是16米47s长=>总计1007s =>我必须每隔50秒制作一个视频快照。

所以我想使用ffmpeg的-r开关,其值为0.019860973(eq 20/1007),但是ffmpeg告诉我帧速率太小了......

我想出去做的唯一方法就是编写一个脚本,用一个被操纵的-ss开关调用ffmpeg并使用-vframes 1但这对我很慢而且有点偏离,因为ffmpegs会对图像本身进行计算...

有任何建议或指示吗?

谢谢, Vapire

8 个答案:

答案 0 :(得分:16)

我知道我参加派对有点晚了,但我认为这可能会有所帮助:

对于每个人都有问题,ffmpeg为每一帧生成一个图像,这里是我如何解决它(使用blahdiblah的答案):

首先,我抓住了视频中的总帧数:

ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2

然后我尝试使用select来抓取帧:

ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>

但是,无论mod(n,100)被设置为什么,ffmpeg都会吐出太多帧。我不得不添加-vsync 0来纠正它,所以我的最终命令看起来像这样:

ffmpeg -i <input_file> -vsync 0 -vf "select='not(mod(n,100))'" <output_file>

(其中100是你想要使用的帧频,例如,每100帧一次)

希望能帮助别人省点麻烦!

答案 1 :(得分:14)

我也试图找到这个问题的答案。我利用了拉德里的答案,但发现它有一个错误。

ffmpeg -i video.avi -r 0.5 -f image2 output_%05d.jpg

每2秒产生一帧,因为-r表示帧速率。在这种情况下,每秒0.5帧,或每2秒1帧。

使用相同的逻辑,如果您的视频长度为1007秒,并且您只需要20帧,则每50.53秒需要一帧。转换为帧速率,每秒0.01979帧。

所以你的代码应该是

ffmpeg -i video.avi -r 0.01979 -f image2 output_%05d.jpg

我希望帮助某人,就像它帮助了我一样。

答案 2 :(得分:5)

您可以尝试将视频转换为N个图像吗?

ffmpeg -i video.avi image%d.jpg

更新

或者每2秒抽取一次帧:

ffmepg -i video.avi -r 0.5 -f image2 output_%05d.jpg

更新

获取视频时长:

ffmpeg -i video.avi 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//

然后,取决于您的编程语言,您将其转换为秒。例如,在PHP中,您可以这样做:

$duration = explode(":",$time); 
$duration_in_seconds = $duration[0]*3600 + $duration[1]*60+ round($duration[2]);

其中$ time是视频时长。您可以执行$ duration_in_seconds / 20

ffmepg -i video.avi -r $duration_in_seconds/20 -f image2 output_%05d.jpg

答案 3 :(得分:5)

通常,ffmpeg会在帧到来时处理帧,因此基于总持续时间/总帧数的任何内容都需要进行一些预处理。

我建议编写一个简短的shell脚本来使用类似

的内容来获取帧总数
ffprobe -show_streams <input_file> | grep "^nb_frames" | cut -d '=' -f 2

然后使用select video filter挑选出您需要的帧。所以ffmpeg命令看起来像

ffmpeg -i <input_file> -vf "select='not(mod(n,100))'" <output_file>

除了代替每百帧,100将替换为shell脚本中计算的数字,总共为20帧。

答案 4 :(得分:3)

我遇到了同样的问题并提出了这个似乎可以解决问题的脚本:

#/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

要使用它,请将其另存为.sh文件并使用以下参数运行它以导出20帧:

./getFrames.sh ~/video.avi /outputpath 20

这将为您提供在整个视频中平均分配的指定帧数。

答案 5 :(得分:1)

我有一个类似的问题,即如何在一半未知长度的电影中提取一帧。感谢这里的答案,我想出了这个解决方案确实非常有效,我认为发布php代码会很有用:

<?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 );
?> 

答案 6 :(得分:0)

我遇到了同样的问题,使用选择过滤器对我不起作用,大型视频的使用率非常慢,所以我在python中附带了此脚本

它要做的是获取持续时间(以秒为单位),根据屏幕截图数量计算时间间隔,最后使用输入文件之前的查找功能在每个时间间隔拍摄屏幕截图

0.5+0.9+0.2=1.6

答案 7 :(得分:0)

我确实如此,并且100%成功。

$time = exec("ffmpeg -i input.mp4 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//");
$duration = explode(":",$time);
$duration_in_seconds = $duration[0]*3600 + $duration[1]*60+ round($duration[2]);
$duration_in_seconds = $duration_in_seconds/20;

exec("ffmpeg -i input.mp4 -r 1/$duration_in_seconds thumb_%d.jpg");