5

我试图弄清楚如何指定要转换为视频的特定图像列表。我知道我们可以做类似的事情:

ffmpeg -i image_04%d.png

这将从文件夹中选择与序列匹配的所有图像。但是,在我的情况下,图像文件不一定按其名称所暗示的顺序排列。原因是订单保存在数据库中,文件名本质上是数据库行 ID。

如何指定正确的输入序列?我本质上是从代码而不是从命令行调用 ffmpeg。因此,也欢迎对代码进行任何更改。

谢谢!

4

4 回答 4

5

这是凯文想法的一个脚本,对我有用。您可能想要替换文件名模式 (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
于 2012-12-05T21:03:03.103 回答
2

假设我们有一个按正确顺序排列的文件名列表,如下所示:

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.png035.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.pnghjruIcoN0aTn.jpgin 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/$FPS2/$FPS

frame=   70 fps=0.0 q=-0.0 Lsize=     701kB time=00:00:02.33 bitrate=2465.4kbits/s speed= 2.9x 

这一事实表明了一个简单的解决方法:以两倍的帧速率进行编码,并将每个图像显示 2 帧。但这很浪费,而且无论如何都不利。

于 2016-05-29T18:40:01.787 回答
1

您的应用程序可以创建从 0001 开始的符号链接,该链接尊重原始帧的顺序,然后将该序列传递给 ffmpeg。视频完成后,删除符号链接即可。

于 2012-04-29T03:13:07.260 回答
0

好吧,解决方案是修补 image2 类以支持包含可供选择的数字的列表。比我提供给 ffmpeg 的输入文件模式以及具有指定顺序的数字数组来读取输入。这非常有效。

于 2012-05-17T16:31:06.180 回答