make-gif

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Video GIF

视频转GIF

Convert a video clip to a high-quality GIF using the mandatory 2-pass palette workflow.
Single-pass GIF always produces banding and color artifacts. The palettegen → paletteuse pipeline analyzes the actual clip to build an optimal 256-color palette, then renders with it. Never skip this.
使用必选的两步调色板工作流将视频剪辑转换为高质量GIF。
单步生成的GIF总会出现色带和色彩失真问题。palettegen → paletteuse流程会分析实际剪辑内容来构建最优的256色调色板,再基于该调色板渲染GIF。切勿跳过这一步。

Process

操作流程

1. Gather parameters

1. 收集参数

Ask for any not already provided in the request:
  • Start time — default
    0
  • Duration or end time — required; warn if >30s (file size grows rapidly)
  • Width — default
    480
    px; height auto-calculated to preserve aspect ratio
  • FPS — default
    15
    ; higher = smoother + larger file
If the user asks about aspect ratio or the source has unusual dimensions, probe first:
bash
ffprobe -v quiet -print_format json -show_streams "$INPUT" | \
  python3 -c "import json,sys; s=[s for s in json.load(sys.stdin)['streams'] if s['codec_type']=='video'][0]; print(s['width'], 'x', s['height'])"
询问请求中未提供的以下参数:
  • 开始时间 —— 默认值为
    0
  • 时长或结束时间 —— 必填项;若超过30秒需发出警告(文件大小会快速增长)
  • 宽度 —— 默认值为
    480
    px;高度会自动计算以保持宽高比
  • 帧率(FPS) —— 默认值为
    15
    ;帧率越高,画面越流畅,但文件体积越大
若用户询问宽高比,或源视频尺寸特殊,先执行以下命令探测:
bash
ffprobe -v quiet -print_format json -show_streams "$INPUT" | \
  python3 -c "import json,sys; s=[s for s in json.load(sys.stdin)['streams'] if s['codec_type']=='video'][0]; print(s['width'], 'x', s['height'])"

2. Build the 2-pass command

2. 构建两步执行命令

Pass 1 — generate optimized palette:
bash
ffmpeg -ss $START -t $DURATION -i "$INPUT" \
  -vf "fps=$FPS,scale=$WIDTH:-1:flags=lanczos,palettegen=stats_mode=full" \
  /tmp/palette_$$.png -y
Pass 2 — render GIF using palette:
bash
ffmpeg -ss $START -t $DURATION -i "$INPUT" -i /tmp/palette_$$.png \
  -lavfi "fps=$FPS,scale=$WIDTH:-1:flags=lanczos [x]; [x][1:v] paletteuse=dither=bayer:bayer_scale=5" \
  "$OUTPUT" -y
Use
$$
(shell PID) in the palette temp path to avoid collisions with concurrent runs.
第一步——生成优化调色板:
bash
ffmpeg -ss $START -t $DURATION -i "$INPUT" \
  -vf "fps=$FPS,scale=$WIDTH:-1:flags=lanczos,palettegen=stats_mode=full" \
  /tmp/palette_$$.png -y
第二步——使用调色板渲染GIF:
bash
ffmpeg -ss $START -t $DURATION -i "$INPUT" -i /tmp/palette_$$.png \
  -lavfi "fps=$FPS,scale=$WIDTH:-1:flags=lanczos [x]; [x][1:v] paletteuse=dither=bayer:bayer_scale=5" \
  "$OUTPUT" -y
在调色板临时路径中使用
$$
(Shell进程ID),避免并发运行时发生冲突。

3. Confirm before running

3. 运行前确认

Show the full 2-pass command and estimated output path. Add a size warning if
duration × fps
is large (rough heuristic: >20s at 15fps at 480px → likely >10MB).
展示完整的两步执行命令和预估输出路径。若
时长 × 帧率
数值较大(大致判断标准:480px分辨率下15帧率超过20秒 → 文件体积可能超过10MB),需添加大小警告。

4. Run both passes, then clean up

4. 执行两步命令,然后清理临时文件

bash
rm -f /tmp/palette_$$.png
Report output path and file size.
bash
rm -f /tmp/palette_$$.png
报告输出路径和文件大小。

Key Decisions

关键决策说明

  • stats_mode=full
    on palettegen analyzes the entire clip — not just the first frame — for better palette coverage across motion.
  • dither=bayer:bayer_scale=5
    is the sweet spot for photographic content. Use
    dither=none
    for flat-color content (illustrations, slides, screen recordings with solid backgrounds).
  • -ss
    placed before
    -i
    uses container-level fast seek. Apply to both passes for consistent start points and dramatically faster seeks on long source files.
  • flags=lanczos
    on scale gives sharper downsampling than the default
    bilinear
    .
  • To control loop count, add
    -loop $N
    to pass 2:
    0
    = infinite,
    1
    = play once,
    2
    = play twice.
  • palettegen的
    stats_mode=full
    会分析整个剪辑(而非仅第一帧),以便在动态画面中获得更好的调色板覆盖效果。
  • dither=bayer:bayer_scale=5
    是摄影类内容的最佳选择。对于纯色内容(插画、幻灯片、纯色背景的屏幕录制),使用
    dither=none
  • -ss
    放在
    -i
    之前会使用容器级快速定位。两步命令都应用该设置,可确保起始点一致,并大幅提升长源文件的定位速度。
  • scale的
    flags=lanczos
    比默认的
    bilinear
    能提供更清晰的下采样效果。
  • 如需控制循环次数,在第二步命令中添加
    -loop $N
    0
    =无限循环,
    1
    =播放一次,
    2
    =播放两次。