我试图弄清楚如何指定要转换为视频的特定图像列表。我知道我们可以做类似的事情:
ffmpeg -i image_04%d.png
这将从文件夹中选择与序列匹配的所有图像。但是,在我的情况下,图像文件不一定按其名称所暗示的顺序排列。原因是订单保存在数据库中,文件名本质上是数据库行 ID。
如何指定正确的输入序列?我本质上是从代码而不是从命令行调用 ffmpeg。因此,也欢迎对代码进行任何更改。
谢谢!
我试图弄清楚如何指定要转换为视频的特定图像列表。我知道我们可以做类似的事情:
ffmpeg -i image_04%d.png
这将从文件夹中选择与序列匹配的所有图像。但是,在我的情况下,图像文件不一定按其名称所暗示的顺序排列。原因是订单保存在数据库中,文件名本质上是数据库行 ID。
如何指定正确的输入序列?我本质上是从代码而不是从命令行调用 ffmpeg。因此,也欢迎对代码进行任何更改。
谢谢!
这是凯文想法的一个脚本,对我有用。您可能想要替换文件名模式 (shot*.png) 和输出文件名 movie.mp4。完成后,脚本会删除所有 frame_ ... 文件。
# script to create movie from multiple png files
# taken in choronoligcal order
# start at 0
count=0
# take all files named shot - something ordered by date
for f in `ls -rt shot*.png`
do
# get the index in 0000 format
printf -v counts "%04d" $count
# link file to a frame_0000 named symbolic link
ln -s $f frame_$counts.png
# increment counter
count=`expr $count + 1`
done
# create movie
# slowed down by factor 5
# ffmpeg -f image2 -i frame_%04d.png -vcodec mpeg4 -vf "setpts=5*PTS" movie.mp4
ffmpeg -i frame_%04d.png movie.mp4
# remove the links
rm frame_*.png
假设我们有一个按正确顺序排列的文件名列表,如下所示:
frames=(
"5K1OCNKToUu.png" "kJuKFQS0Fgnp.png" "00v4U4JTyUn.png" "3sg9sDwPZoX.png"
"1KsuEk9mboa9.png" "qNrI8zlRBmW.png" "MOvSca3wlPsP.png" "5rXcxfGQXunY.png"
"hjruIcoN0aTn.jpg" "OhRttnWtKy.png" "e2Qj8jCixc.png" "Uze2H7vzrt4.png"
"n14qhmjiBW3.png" "ZDMvY4g1hzgS.png" "ibnb7MxELyGp.png" "9c8QGWmBEDNg.png"
"STQT0t7oqPEK.png" "jI7UvpbLDWPc.png" "6clazeaUAJHv.png" "ylJ40r9uMK9d.png"
"RICq5KV00P6.png" "zjCLrappFMPq.png" "TJQTDv313KBo.png" "Gu3pLpWylsuo.png"
"Ksym4SB6VYNv.png" "rIyj0LJIjBVX.png" "pSUm2J8xYU.png" "Rnsr0H0m7p9A.png"
"x4vVomlOolxt.png" "2W1QURLQUyE8.png" "m3JgtDzQ0VgE.png" "CrjN9TVJKMAU.png"
"IO6pnF83ivqo.png" "hY15nsYDvr4h.png" "1GagDdBM9L7.png"
)
并且我们想要创建一个具有特定帧速率的动画 GIF,例如FPS=30
。这可以像在其他答案中一样通过创建符号链接来完成,例如命名001.png
为035.png
然后使用:
ffmpeg -i 03%d.png
另一种方法是利用ffmpeg
's concat-feature。不幸的是,这说起来容易做起来难,因为该功能需要视频流,这意味着图像需要根据需要循环播放。
连接三个图像的命令之间有 500 毫秒是这样的:
ffmpeg -f concat -safe 0 -i <(cat <<EOF
file '$(pwd)/1KsuEk9mboa9.png'
duration 0.5
file '$(pwd)/hjruIcoN0aTn.png'
duration 0.5
file '$(pwd)/n14qhmjiBW3.png'
duration 0.5
EOF
) -framerate 2 out.gif
经测试ffmpeg version 3.0.1-3
。
解释:
concat demuxer需要一个文件,其中包含一个由关键字前缀的相对文件名列表file
。为了不弄乱当前工作目录(或者我们可能没有写权限),我们使用 process substition <( ... )
。但这会在其中创建一个文件/dev/fd/
,如果使用了相对文件名,则会导致如下错误消息:
[concat @ 0xc181c0] Impossible to open '/dev/fd/5K1OCNKToUu.png'
这就是给出绝对路径的原因。但是默认情况下不允许使用绝对路径,导致:
[concat @ 0x17da1c0] Unsafe file name '/home/5K1OCNKToUu.png'
为了解决使用-safe 0
。
尝试指定输入帧速率时-r
ffmpeg -f concat -safe 0 -r 2 -i <(cat <<EOF
file '$(pwd)/1KsuEk9mboa9.png'
file '$(pwd)/hjruIcoN0aTn.png'
file '$(pwd)/n14qhmjiBW3.png'
EOF
) -framerate 2 out.gif
出现这样的错误:
[concat @ 0x1458220] DTS -230575710986777 < 0 out of order
DTS -230575710986777, next:40000 st:0 invalid dropping
PTS -230575710986777, next:40000 invalid dropping st:0
无论如何,生成的 GIF 都像预期的那样工作。使用duration
选项concat
明确地解决这些警告/错误。
注意评论-r
作为输入选项,忽略文件中存储的任何时间戳,而是在假设帧速率 fps 不变的情况下生成时间戳。这与用于某些输入格式(如 image2 或 v4l2)的 -framerate 选项不同(它曾经在旧版本的 FFmpeg 中相同)。如果有疑问,请使用 -framerate 而不是输入选项 -r。
我对此的看法是,来自 concat 的上述警告,它通知您它找不到图像的播放长度,不会导致不良行为,因为在 concat 警告之后强制帧速率-r
,这有效,但可能不是预期的方式。我只会在手动编写 ffmpeg 命令时使用它,而不是在脚本中。
组合一个小脚本来处理开头指定的文件名列表:
function makeGif() {
local targetName=$1; shift
local FPS=$1; shift
# concat doesn't recognize .33 as returned by bc by default
local delay=$(bc <<< "scale=5; x=1/$FPS; if (x<1) print 0; x")
local list
for file in $@; do
list+=$(printf "\nfile '$(pwd)/$file'\nduration $delay")
done
ffmpeg -f concat -safe 0 -i <(cat <<< "$list") -r $FPS "$targetName".gif
#ffmpeg -f concat -safe 0 -i <(cat <<< "$list") -r $FPS -c:v libx264 -crf 5 -pix_fmt yuv420p "$targetName".mkv
}
makeGif out 30 ${frames[@]}
上面代码中未注释的行将给出一个 H264 编码的 mkv。
异构图像类型
如果您的图像列表包含不同类型的图像,例如通过替换hjruIcoN0aTn.png
为hjruIcoN0aTn.jpg
in frames
,那么这种用法 ofconcat
将不起作用。第一个指定格式以外的其他格式的图像将被丢弃并引发错误,例如:
[png @ 0x1ec8260] Invalid PNG signature 0xFFD8FFE000104A46.
Error while decoding stream #0:0: Invalid data found when processing input
在这种情况下,您将不得不使用concat 过滤器而不是简单的解复用器。然后必须将上面包含三个文件的命令更改为:
ffmpeg \
-f image2 -loop 1 -thread_queue_size 4096 -framerate 30 -t 0.5 -i "$(pwd)/1KsuEk9mboa9.png" \
-f image2 -loop 1 -thread_queue_size 4096 -framerate 30 -t 0.5 -i "$(pwd)/hjruIcoN0aTn.jpg" \
-f image2 -loop 1 -thread_queue_size 4096 -framerate 30 -t 0.5 -i "$(pwd)/n14qhmjiBW3.png" \
-filter_complex 'concat=n=3:v=1 [vmerged]' \
-map '[vmerged]' -r 30 out.gif
解释:
使用的每个选项在这里都得到了很好的解释。请注意,该-framerate
选项特定于图像导入器,并且至少在技术上与 不同-r
,实际上在这种情况下它们会产生相同的结果。
这-thread_queue_size
似乎是必要的,因为我们将一张图像延长了相当长的 500 毫秒,导致以下错误消息:
[image2 @ 0x18856e0] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
[image2 @ 0x188a100] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
[image2 @ 0x188b9e0] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
该选项的描述让我有理由认为过大的延迟是错误的:
-thread_queue_size size (input) 此选项设置从文件或设备读取时排队数据包的最大数量。低延迟/高速率的直播流,如果没有及时读取数据包,可能会被丢弃;提高这个值可以避免它。
再次将所有这些放入脚本中:
function makeGif() {
local targetName=$1; shift
local FPS=$1; shift
local delay=$(bc <<< "scale=5; x=1/$FPS; if (x<1) print 0; x")
local list=()
local nFiles=0
for file in $@; do
list+=( -f image2 -loop 1 -thread_queue_size 4096 -framerate $FPS -t $delay -i "$(pwd)/$file" )
nFiles=$((nFiles+1))
done
ffmpeg ${list[@]} -filter_complex "concat=n=$nFiles:v=1 [vmerged]" -map '[vmerged]' -r $FPS "$targetName".gif
#ffmpeg ${list[@]} -filter_complex "concat=n=$nFiles:v=1 [vmerged]" -map '[vmerged]' -r $FPS -c:v libx264 -crf 5 -pix_fmt yuv420p "$targetName".mkv
}
makeGif out 30 ${frames[@]}
由于一些非常奇怪的原因,上面的命令不能按原样工作!!!. 在我的情况下,它丢弃了指定的 35 帧中的 33 帧,而没有警告或错误消息:
frame= 2 fps=0.0 q=-0.0 Lsize= 2kB time=00:00:00.07 bitrate= 179.7kbits/s dup=0 drop=33 speed=0.155x
这很奇怪的原因是,它的工作原理与预期的延迟一样完美,即每个图像显示 2 帧,即更改1/$FPS
为2/$FPS
:
frame= 70 fps=0.0 q=-0.0 Lsize= 701kB time=00:00:02.33 bitrate=2465.4kbits/s speed= 2.9x
这一事实表明了一个简单的解决方法:以两倍的帧速率进行编码,并将每个图像显示 2 帧。但这很浪费,而且无论如何都不利。
您的应用程序可以创建从 0001 开始的符号链接,该链接尊重原始帧的顺序,然后将该序列传递给 ffmpeg。视频完成后,删除符号链接即可。
好吧,解决方案是修补 image2 类以支持包含可供选择的数字的列表。比我提供给 ffmpeg 的输入文件模式以及具有指定顺序的数字数组来读取输入。这非常有效。