Допустим, у нас есть список имен файлов в правильном порядке, как это:
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
. Чтобы не загромождать текущий рабочий каталог (или, возможно, у нас нет прав на запись), мы используем подпрограмму процесса <(...)
.Но это создает файл в /dev/fd/
и если используются относительные имена файлов, то это приводит к сообщениям об ошибках, как это:
[concat @ 0xc181c0] Impossible to open '/dev/fd/5K1OCNKToUu.png'
Вот почему абсолютный путь дается. Но абсолютные пути не разрешены по умолчанию, в результате чего:
[concat @ 0x17da1c0] Unsafe file name '/home/5K1OCNKToUu.png'
Для solve что -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
В качестве опции ввода, игнорировать любые временные метки, хранящиеся в файле и вместо генерировать временные метки, предполагая постоянную частоту кадров кадров в секунду. Этот не совпадает с опцией -framerate, используемой для некоторых форматов ввода , таких как image2 или v4l2 (в старых версиях FFmpeg она была прежней). Если есть сомнения, используйте -framerate вместо входного параметра -r.
Мой взгляд на это, что вышеуказанные предупреждения от CONCAT, которые уведомляют о том, что он не мог найти длину игры для изображений, не приводят к плохому поведению, потому что фреймрейт вынужден после предупреждения Concat с -r
, который работает, но может и не быть предназначенным способом. Я бы использовал это только при ручном написании команд ffmpeg, но не в скриптах.
Собирая a небольшой скрипт для работы со списком имен файлов, указанных в начале:
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 [email protected]; 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
в frames
, то это использование concat
не будет работать. Изображения в других форматах, чем первые указания будут сброшены и бросать ошибки как:
[png @ 0x1ec8260] Invalid PNG signature 0xFFD8FFE000104A46.
Error while decoding stream #0:0: Invalid data found when processing input
В этом случае вам придется использовать concat filter вместо простого демультиплексора.Команда с тремя файлами из выше будет тогда быть изменен следующим образом:
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
Объяснение:
Каждые опции, используемые объясняется очень хорошо here. Обратите внимание, что опция -framerate
специфична для image importer и по крайней мере технически отличается от -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 (вход) Этот параметр устанавливает максимальное число пакетов, поставленных в очередь при чтении из файла или устройства. При низких потоках с задержкой/высокой скоростью передачи, пакеты могут быть отброшены, если они не будут прочитаны своевременно; повышение этого значения может избежать этого.
Собираем все это в сценарий еще раз:
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 [email protected]; 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[@]}
По какой-то очень странной причине вышеприведенная команда не работает, как это !!!. В моем случае она упала 33 из 35 кадров, указанных без предупреждения или сообщения об ошибке:
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 кадров. Но это расточительно и не очень выгодно.
Но как вы передаете массив с номерами в ffmpeg? – Jimmy
Эй @ Джимми, я на самом деле не помню, что я тогда делал. Я помню, как модифицировал исходный код ffmpeg для поддержки передачи числа чисел. Однако эта идея была ужасной и неэффективной. Я закончил писать код с нуля, который использует ffmpeg в качестве кодера. Насколько я знаю, нет простого способа. – Jona
Спасибо за ответ, интересно, символические ссылки могут быть простым способом сделать это. Вы пробовали это? – Jimmy