【发布时间】:2017-06-19 14:23:49
【问题描述】:
我正在尝试使用 FFMPEG 为上传的视频生成 8 个屏幕截图。我目前有:
ffmpeg -i Trailer-720p.mov -r .2 -vcodec png Preview-%d.png
每 5 秒生成一个屏幕截图。如何添加为按总时间百分比分布的帧生成屏幕截图的功能。谢谢。此外,是否可以生成 50% 的屏幕截图?谢谢。
【问题讨论】:
标签: ffmpeg
我正在尝试使用 FFMPEG 为上传的视频生成 8 个屏幕截图。我目前有:
ffmpeg -i Trailer-720p.mov -r .2 -vcodec png Preview-%d.png
每 5 秒生成一个屏幕截图。如何添加为按总时间百分比分布的帧生成屏幕截图的功能。谢谢。此外,是否可以生成 50% 的屏幕截图?谢谢。
【问题讨论】:
标签: ffmpeg
如果您仅使用 -i 参数运行 ffmpeg,它将为您提供 stderr 上的视频长度(以及许多其他内容)。您可以围绕它写一些东西,将持续时间和预期的帧数转换为正确的 -r 参数。
这是一个 Python 中的简单示例,它基本上完成了我所描述的操作。由于某种原因,我的 ffmpeg 版本生成的前两个静止图像都显示第 0 帧,但 Preview-3 到 Preview-n 的间隔是正确的。将第二个参数设置为“1”运行它,它将生成中间帧为 Preview-3.png。
#!/usr/bin/env python
import sys,os,re
from subprocess import *
if len(sys.argv)<=1:
print("usage: python oneinn.py filename frames")
sys.exit(0)
try:
fvideo = sys.argv[1]
frames = float(sys.argv[2])
except:
sys.stderr.write("Failed to parse parameters.\n")
sys.exit(1)
output = Popen(["ffmpeg", "-i", fvideo], stderr=PIPE).communicate()
# searching and parsing "Duration: 00:05:24.13," from ffmpeg stderr, ignoring the centiseconds
re_duration = re.compile("Duration: (.*?)\.")
duration = re_duration.search(output[1]).groups()[0]
seconds = reduce(lambda x,y:x*60+y,map(int,duration.split(":")))
rate = frames/seconds
print("Duration = %s (%i seconds)" % (duration, seconds))
print("Capturing one frame every %.1f seconds" % (1/rate))
output = Popen(["ffmpeg", "-i", fvideo, "-r", str(rate), "-vcodec", "png", 'Preview-%d.png']).communicate()
【讨论】:
或者只用一个shell命令:
ffmpeg -i input.m4v -vf fps=1/$(echo 'scale=6;' $(ffprobe -loglevel 安静 -of 'compact=nokey=1:print_section=0' -show_format_entry 持续时间输入.m4v) ' / 10' | bc) -vframes 10 -qscale:v 2 缩略图-%d.png
这会创建 10 个与源视频尺寸相同的缩略图。
【讨论】:
scale=6 是该号码中使用的位数。 qscale:v是可变比特率,设置为2。我不知道为什么vframes 10在里面。
#!/usr/bin/env ruby
# pass the video source file(s) into the command line args
# resulting images are jpg, just change the appropriate ffmpeg option for png.
# the last line uses ImageMagick to stitch the images together into a strip.
# the first image is thrown away, since it's a duplicate of the second image.
ARGV.each do|a|
total_shots = 4
size = '200x200'
meta = %x(ffmpeg -i #{a} 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//)
time_parts = meta.match /(\d\d):(\d\d):(\d\d)\.(\d\d)/
duration_seconds = time_parts[1].to_i*60*60+time_parts[2].to_i*60+time_parts[3].to_i+time_parts[4].to_f/100
puts "*** Duration seconds: " + duration_seconds.to_s
%x(ffmpeg -i #{a} -r #{total_shots/duration_seconds} -s #{size} -f image2 -vframes #{total_shots+1} foo-%03d.jpg )
files = (1..total_shots+1).map{|i| 'foo-' + ("%03d" % i) + '.jpg'}
files.delete_at 0
%x(convert -append #{files.join ' '} shot-strip.jpg)
end
【讨论】:
我无法让 Manfred Stienstra 出色的 oneliner 在正确的位置生成帧。如果我指定从 240 秒的电影中生成 8 张图像,则第一个将是 15,第二个是 45,等等。我希望第一个是 0,第二个是 30,等等。
所以我把他的oneliner拆开并创建了这个
ffmpeg=../bin/ffmpeg
ffprobe=../bin/ffprobe
outdir=../test
infile=../testmovie.mp4
outbase=testmovie
steps=8
len=`$ffprobe -loglevel quiet -of 'compact=nokey=1:print_section=0' -show_format_entry duration $infile`
echo length $len
secs=`echo 'scale=6;' $len ' / ' $steps | bc`
echo secs $secs
for ((i=0; i <= $steps ; i++)); do
echo =========================
echo $ffmpeg -nostats -loglevel 0 \
-i $infile -f image2 -ss `echo $secs \* $i | bc` \
-vframes 1 "$outdir/$outbase"-$i.jpg
$ffmpeg -nostats -loglevel 0 \
-i $infile -f image2 -ss `echo $secs \* $i | bc` \
-vframes 1 "$outdir/$outbase"-$i.jpg
done
【讨论】: