一段时间以来,我一直使用这几个命令将视频片段转换为动画 gif,并ffmpeg
计算出最佳调色板:
ffmpeg -ss $START -i $IN_FILE -t $LENGTH -vf "fps=$FPS,scale=$WIDTH:-1:flags=lanczos,palettegen" palette.png
ffmpeg -ss $START -i $IN_FILE -i palette.png -t $LENGTH -filter_complex "fps=$FPS,scale=$WIDTH:-1:flags=lanczos [x]; [x] [1:v] paletteuse" output.gif
这对于本地文件非常有效,但如果我开始使用远程 URL $IN_FILE
,它会下载所需的部分两次 - 一次用于调色板生成,一次用于实际转换。
提前下载完整文件通常是不可能的——通常我对较长视频中间的一个非常小的序列感兴趣。
我尝试使用-ss
和下载一小部分-t
并将其保存(不重新编码)到临时文件:
ffmpeg -ss $START -i $IN_URL -t $LENGTH -vc copy -ac none temp.mkv
在这种情况下,我确实避免了带宽浪费(仅下载文件的相关部分,并且仅下载一次),但搜索不再精确,因为-ss
在进行流复制时,输入只有关键帧的粒度可供查找。
理论上,在转换为 gif 时,可以进行额外的精确搜索并修复此问题,但似乎没有办法获取上面生成的临时文件开始的原始时间戳,因此无法计算转码为 gif-ss
时应该在哪里。我尝试过-copy_ts
,但没有得到任何好的结果。
简单的解决方案是在第一步中重新编码(可能在此过程中应用比例/重新采样,以避免以后执行两次),但我想避免一次额外无用编码的成本/质量损失。
那么:我怎样才能将一个可能很大的网络文件的一小部分执行最佳调色板视频到 gif 的转换,高效地获取它(=下载一次,只下载相关部分),精确查找,不需要额外的重新编码?
答案1
这个问题可以很容易地通过使用具有多个链的过滤器图,这使我们能够仅执行一次搜索/下载/过滤,并以多种方式处理它。搜索/过滤的流被馈送到调色板生成器和到调色板应用程序过滤器,它与生成的调色板一起使用。图形上:
.--> palettegen [pal]---.
input / |
[0:v] -> fps -> scale -> split=2 [a][b] V
with `-> [b] fifo [b] -> [b] [pal] paletteuse -> out.gif
precise
seek
翻译过来就是:
ffmpeg -ss $START -I $IN_URL -t $LENGTH -filter_complex "fps=$FPS,scale=$WIDTH:-1:flags=lanczos,split=2 [a][b]; [a] palettegen [pal]; [b] fifo [b]; [b] [pal] paletteuse" out.gif
请注意,要使用相同的流作为两个独立管道分支的输入,必须使用过滤split
器。
编辑:fifo
感谢@Gyan的评论;这是必要的,因为palettegen
需要等到流结束才能生成调色板,并且在获得调色板之前paletteuse
无法开始使用[b]
,因此如果视频足够大,默认缓冲区将[b]
不够用,并将ffmpeg
开始丢帧。解决方案是fifo
在中间添加一个来处理任意大小的缓冲(必须注意不要超过视频长度,因为在内存中缓冲整个流可能会对可用 RAM 造成负担)。
(无耻地插入:这是命令我现在正在使用在我的tube2gif_bot电报机器人)
最重要的是,这是一个关于理解你正在使用的命令;问题中引用的第二个命令已经使用了复杂的过滤图,但是由于我盲目地从网络上复制粘贴它,我并没有真正尝试理解不透明的过滤图语法,所以我没有想到只需稍微调整它就会是最好的解决方案。