视频转 GIF 与 GIF 转视频
本教程共 32 篇 · 第 15 篇 · 更新于 2026-08-09 · 约 7 分钟阅读
本节目标:把视频片段转成画质好、体积小的 GIF;反过来把 GIF 压成 MP4 节省空间。
GIF 又老又低效,但在社交网络、文档、聊天里依然无处不在。直接用 FFmpeg 转 GIF 很容易,但默认输出色彩很差、体积也大。这一章讲怎么做出「又清晰又小」的 GIF。
视频转 GIF:最简写法
先截取一段,再输出 GIF:
ffmpeg -ss 00:00:05 -t 3 -i input.mp4 output.gif
从第 5 秒开始取 3 秒,直接生成 GIF。能跑,但效果通常很差——颜色发灰、有颗粒感。原因是 GIF 只支持 256 色调色板,而 FFmpeg 默认用的通用调色板对每一段画面都不是最优的。
高质量 GIF:两步调色板法
正确做法分两步:先为这段视频生成专属调色板,再用这个调色板输出 GIF。
第一步:生成调色板
ffmpeg -ss 00:00:05 -t 3 -i input.mp4 \
-vf "fps=15,scale=480:-1:flags=lanczos,palettegen" \
palette.png
fps=15:GIF 不需要太高帧率,15fps 足够顺滑,再高体积暴涨。scale=480:-1:flags=lanczos:缩到宽 480,用 lanczos 算法保锐利。palettegen:滤镜,统计这段画面所有像素,生成一张最优的 256 色调色板。
输出一张 palette.png(通常很小,几百字节到 1KB)。
第二步:用调色板输出 GIF
ffmpeg -ss 00:00:05 -t 3 -i input.mp4 -i palette.png \
-filter_complex "[0:v]fps=15,scale=480:-1:flags=lanczos[v];[v][1:v]paletteuse" \
output.gif
- 有两个输入:原视频和调色板图片。
[0:v]fps=15,scale=...[v]:先对视频做帧率和缩放处理。[v][1:v]paletteuse:把处理后的视频和第二个输入的调色板一起喂给 paletteuse 滤镜,按这个调色板量化颜色。
效果比直接转好非常多,体积往往还更小。
一条命令搞定(用滤镜链)
不想生成中间文件,可以把 palettegen 和 paletteuse 串在一条命令里:
ffmpeg -ss 00:00:05 -t 3 -i input.mp4 \
-filter_complex "[0:v]fps=15,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" \
output.gif
split[a][b] 把处理后的视频流复制成两份,一份给 palettegen 生成调色板,另一份和调色板一起给 paletteuse。效果和两步法完全一致。
Tip上面这条命令里
split之后的两条分支会自动排队执行:palettegen 必须先看完整个视频才能产出调色板,FFmpeg 会处理好这个顺序。
进一步优化
控制抖动(dithering)
GIF 颜色少,渐变区域容易出现色带。paletteuse 默认会用抖动算法来模拟更多颜色,但抖动也会让颗粒感变重、体积变大。
# 默认的 Bayer 抖动,颗粒较规则
ffmpeg ... -filter_complex "...paletteuse=dither=bayer:bayer_scale=3" output.gif
# 不用抖动,色块最干净,但渐变可能有断层
ffmpeg ... -filter_complex "...paletteuse=dither=none" output.gif
# Floyd–Steinberg 误差扩散,画质好但体积稍大
ffmpeg ... -filter_complex "...paletteuse=dither=floyd_steinberg" output.gif
每帧独立调色板
GIF 默认整个动画共用一张调色板。如果画面场景变化剧烈(比如从室内切到户外),共用调色板两边都照顾不好。可以让每帧用自己的调色板:
ffmpeg ... -filter_complex "...palettegen=stats_mode=single[pal];[v][pal]paletteuse=new=1" output.gif
代价是文件会大不少。大多数情况下,默认的全局调色板就够了。
降帧率和尺寸
影响 GIF 体积最明显的两个因素:
- 帧率:15fps 就很流畅了,10fps 也能用。每降一半帧率,体积大约少 30-40%。
- 宽度:480 宽适合聊天和文档,320 宽适合表情。
# 10fps、宽 320、无抖动,体积最小
ffmpeg -i input.mp4 \
-filter_complex "fps=10,scale=320:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse=dither=none" \
output.gif
从透明背景视频生成
如果源是带 alpha 通道的 WebM 或 MOV,GIF 也支持透明:
ffmpeg -i input.mov \
-filter_complex "fps=15,scale=480:-1:flags=lanczos,split[a][b];[a]palettegen=reserve_transparent=on[p];[b][p]paletteuse=alpha_threshold=128" \
output.gif
GIF 转视频(MP4)
GIF 体积大、不支持音频、浏览器解码效率低。把它转成 MP4 通常能小 50%-90%。
基本命令
ffmpeg -i input.gif \
-movflags faststart \
-pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
output.mp4
每个参数的作用:
-movflags faststart:把元信息挪到文件头,让浏览器可以边下边播。-pix_fmt yuv420p:保证所有浏览器和播放器都能播。scale=trunc(iw/2)*2:trunc(ih/2)*2:GIF 的尺寸可能是奇数,H.264 要求偶数,这里把宽高都取最近的偶数。
控制体积和质量
# 高质量 CRF 18
ffmpeg -i input.gif -movflags faststart -pix_fmt yuv420p -crf 18 output.mp4
# 限制码率
ffmpeg -i input.gif -movflags faststart -pix_fmt yuv420p -b:v 1M output.mp4
限制最大宽度
很多 GIF 很小,没必要放大:
ffmpeg -i input.gif \
-movflags faststart -pix_fmt yuv420p \
-vf "scale='min(720,iw)':-2" \
output.mp4
min(720,iw) 表示宽度最多 720,小的就保持原尺寸。
让 MP4 循环播放
GIF 默认无限循环,MP4 本身不循环,要靠 HTML 的 loop 属性或播放器设置。如果输出文件需要自带循环信息,可以加 -loop 0(但这是给 GIF 用的输出选项,对 MP4 无效)。网页里这样写:
<video src="output.mp4" autoplay loop muted playsinline></video>
常见问题
GIF 颜色很糟糕。 没生成调色板,直接输出了。用两步调色板法重转。
GIF 太大。
降帧率到 10-15、缩窄宽度到 320-480、试 dither=none。
MP4 播不了。
检查是不是加了 -pix_fmt yuv420p,宽高是不是偶数。某些老设备不支持 HEVC,用 H.264 最稳妥。
GIF 转 MP4 后速度不对。
GIF 的帧延迟不统一,FFmpeg 会处理。如果仍然异常,用 -fflags +genpts 重新生成时间戳:
ffmpeg -fflags +genpts -i input.gif -movflags faststart -pix_fmt yuv420p output.mp4
小结
- 视频转 GIF 别直接输出,用
palettegen+paletteuse两步法质量明显更好。 - 帧率 10-15、宽度 320-480 是体积和质量的甜点区。
- GIF 转 MP4 加
-pix_fmt yuv420p、-movflags faststart,并用trunc(iw/2)*2保证偶数尺寸。 - MP4 体积通常只有 GIF 的 1/2 到 1/10,网页和 App 里应该优先用 MP4。
下一章讲图片序列和视频的互相转换。