如何使用ffmpeg

时间:2016-12-15 23:36:57

标签: ffmpeg h.264 keyframe libavcodec ffprobe

我知道如果你知道帧数(如下所示),如何使用ffmpeg从视频中提取一组帧作为jpg文件

Extracting Frames: [40, 59, 73, 110]
/usr/bin/ffmpeg -y -hide_banner -nostats -loglevel error -i /home/pi/movie.mp4 -vf select='eq(n\,40)+eq(n\,59)+eq(n\,73)+eq(n\,110)',scale=640:-1 -vsync 0 /tmp/%04d.jpg

这会将帧[40,59,73,110]提取为文件/tmp/0000.jpg,/tmp/0001.jpg等。

我也知道如何在给定的时间间隔内提取所有关键帧:

ffmpeg -ss <start_time> -i video.mp4 -t <duration> -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0 frame%03d.jpg

这将获得从start_timestart_time+duration的所有I帧。

但我想要做的是给出一个帧号列表,并让ffmpeg为每个帧号提取最接近的关键帧。有没有办法用ffmpeg做到这一点,还是我必须在libavcodec上编写自己的程序才能做到这一点?

2 个答案:

答案 0 :(得分:3)

首先我们需要一些测试数据。请注意,如果您可以跳到FFmpeg命令 自己这样做;这就是我对最终命令的看法:

ffprobe -select_streams v -show_entries frame=pict_type -of flat \
'Breaking Bad 5x4 Fifty-One.mp4' > pict_type.txt

现在让我们找到最大的窗口:

#!/usr/bin/awk -f
BEGIN {
  FS = "[.\42]"
}
$5 != "I" {
  pa++
}
$5 == "I" {
  qu = pa > qu ? pa : qu
  pa = 1
}
END {
  print qu
}

运行它会产生240,这意味着半径为120.如果我们的目标框架是 1000:

ffmpeg -i 'Breaking Bad 5x4 Fifty-One.mp4' \
-vf "select='eq(pict_type,I)*lt(abs(n-1000),120)'" -frames 1 outfile.jpg

如果我们的目标框架是1000和2000:

ffmpeg -i 'Breaking Bad 5x4 Fifty-One.mp4' \
-vf "select='eq(pict_type,I)*(lt(abs(n-1000),120)+lt(abs(n-2000),120))'" \
-frames 2 -vsync 0 %d.jpg

http://ffmpeg.org/ffmpeg-utils.html#Expression-Evaluation

答案 1 :(得分:3)

如果速度是主要问题,您可以选择在解复用器阶段跳过非关键帧。

ffmpeg -discard nokey -i video.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)" -vsync 0 frame%03d.jpg

这应该提供20-100倍的速度。

如果您需要使用discard选项提取特定半径内的所有关键帧,请使用

ffmpeg -discard nokey -i video.mp4 -q:v 2 -vf select="eq(pict_type\,PICT_TYPE_I)*(lt(abs(t-14),3)+lt(abs(t-107),3)+lt(abs(t-2113),3))" -vsync 0 frame%03d.jpg

此处,将选择半径为3秒t = 14s,107s和2113s的关键帧。

由于编号错误,您无法使用n引用discard - &gt; ffmpeg仅向过滤器发送关键帧,n表示过滤的帧的计数。因此,所有变量和值都以秒为单位。如果您的视频是固定帧率,则t只是n/ fps