流媒体相关技术以及FFmpeg 使用经验

一.动图转换

前提知识补充:

WebP 动图、PNG 动图(通常指 APNG)与 GIF 均为支持动态图像的图片格式,三者核心功能都是通过多帧连续播放实现动画效果,但在技术特性、兼容性和应用场景上有显著差异。以下是具体解析:

一、三种格式的技术特性与关系

  1. GIF(Graphics Interchange Format)
    • 诞生背景:1987 年由 CompuServe 推出,是最早普及的动态图像格式,基于 LZW 压缩算法。
    • 技术特点
      • 最多支持 256 种颜色(8 位色深),色彩表现力有限;
      • 支持简单透明(只有 “完全透明” 和 “完全不透明” 两种状态);
      • 帧动画通过逐帧切换实现,支持循环播放、帧延迟控制。
    • 与其他格式的关系:是动态图像的 “元老”,WebP 动图和 APNG 均为解决 GIF 的缺陷(低色彩、大体积)而设计,可视为 GIF 的 “升级版”。
  2. WebP 动图(Animated WebP)
    • 诞生背景:2010 年由 Google 推出,是 WebP 静态格式的扩展,专为网页优化设计。
    • 技术特点
      • 支持 24 位真彩色(1600 万色)+ 8 位透明通道(半透明效果),色彩表现力远超 GIF;
      • 采用更高效的 VP8 视频编码压缩,同等画质下体积比 GIF 小 50% 以上;
      • 支持帧间压缩(类似视频编码,重复内容不重复存储),进一步减小体积。
    • 与 GIF 的关系:目标是替代 GIF 成为网页动态图像的主流格式,兼容 GIF 的动画逻辑但技术更先进。
  3. PNG 动图(APNG,Animated PNG)
    • 诞生背景:2004 年由 Mozilla 主导开发,是 PNG 静态格式的动画扩展(PNG 本身不支持动画,APNG 是其增强版)。
    • 技术特点
      • 支持 24 位真彩色 + 8 位透明通道,色彩和透明度表现与 PNG 一致(优于 GIF);
      • 压缩算法基于 PNG 的 DEFLATE,帧动画兼容 PNG 的无损特性,但体积通常比 WebP 动图大;
      • 向下兼容静态 PNG(不支持 APNG 的软件会显示第一帧)。
    • 与 GIF 的关系:是 PNG 生态下的动态扩展,解决了 GIF 的色彩和透明度缺陷,但压缩效率不及 WebP 动图。

二、兼容性对比:谁的应用范围最广?

GIF 兼容性碾压,是目前应用最广的动态图像格式,具体差异如下:

  • GIF
    • 支持所有浏览器(包括 IE6 等老旧浏览器)、操作系统(Windows/macOS/Linux)、图像软件(Photoshop、浏览器原生预览)、社交平台(微信、微博、Twitter 等);
    • 几乎没有兼容性门槛,是 “通用动态图像标准”。
  • WebP 动图
    • 支持现代浏览器(Chrome、Firefox、Edge、Safari 14+),但老旧浏览器(如 IE 全系、Safari 13 及以下)不支持;
    • 图像软件支持有限(Photoshop 需插件,GIMP 原生支持);
    • 社交平台兼容性参差不齐(微信、微博已支持,但部分小众平台仍不兼容)。
  • APNG
    • 浏览器支持:Firefox、Safari 原生支持,Chrome 需开启实验性选项(默认不支持),IE 完全不支持;
    • 软件支持:比 WebP 稍好(如 Photoshop 插件支持,macOS 预览原生支持),但仍不如 GIF 普及;
    • 社交平台支持较少(仅部分平台兼容,如 Reddit、Discord)。

三、总结:如何选择?

  • 追求兼容性:选 GIF,尤其需要适配老旧设备、全平台场景(如邮件签名、跨平台社交分享)。
  • 追求画质与体积平衡:选 WebP 动图,适合现代网页、App 内使用(需做好降级处理:对不支持的浏览器返回 GIF 版本)。
  • 需要无损画质 + 透明动画:选 APNG,适合对色彩精度要求高的场景(如图标动画),但需接受其兼容性限制和较大体积。

目前来看,GIF 仍是应用兼容范围最广的动态图像格式,而 WebP 动图凭借高效压缩,正逐步成为现代互联网的主流选择(需配合兼容性方案)。

使用背景:

笔者从网站保存了一些表情包动图,格式为 webp。经验证,发现 QQ 的 NT for PC 因为底层用的 webview 而能适配 Webp 动图动画效果,但是 手机版查看 webp 图片为静态,QQ PC老版本甚至不能显示。所以,我便试着使用 FFmpeg 作为格式转换成 gif,以求让生动的动态表情包能被各端看到。

操作:

因为 FFmpeg 对「动画 WebP」的读取支持很弱,先用 Google 提供的官方工具 libwebp 工具包 来提取帧,再交给 ffmpeg 合成 GIF。否则则会报错:

1
2
3
4
5
6
[webp @ 0x130f06b90] skipping unsupported chunk: ANIM
[webp @ 0x130f06b90] skipping unsupported chunk: ANMF
...
Could not find codec parameters for stream 0 (Video: webp, none): unspecified size
Invalid data found when processing input

这说明:
1.FFmpeg 的内置 webp 解码器 不支持动画 WebP 的容器格式(即使你装了 libwebp,也可能只是用于编码)
2.它跳过了 ANIM / ANMF 块 → 没有图像数据可读 → 解码失败

正确解决方案—— 用 webpmux 提取动图帧

webpmux(也是 libwebp 套件里的),它是专门处理 WebP 容器的,支持 ANIM/ANMF 动画结构

🔧 第一步:确认 webpmux 是否可用

1
webpmux -version

如果输出类似:

1
webpmux version: 1.4.0

那就好,继续

如果没有这个命令,请重新安装最新版 libwebp:

1
brew reinstall --build-from-source libwebp

(有时候直接 install 会装上残缺版,必须从源码编译才能完整功能)


🐾 第二步:用 webpmux 提取动画帧为 PNG 序列

假设你的动图叫 1.webp,我们来一步步拆开它!

1️⃣ 先查看动画信息(确认是动画)

1
webpmux -info 1.webp

输出应该像这样:

1
2
3
4
5
File: 1.webp
RIFF data, WebP animation
Canvas: width x height, loop=%d
Frame 1: ...
...

✅ 出现 “WebP animation” 就说明是动画喵!


2️⃣ 创建帧存放目录

1
mkdir -p frames_1

3️⃣ 用 webpmux 提取每一帧

1
2
# 提取所有帧到 frames_1/%06d.webp
webpmux -get frame 1.webp -o frames_1/%06d.webp

这会生成 frames_1/000001.webp, 000002.webp … 每一帧都是一个独立的 WebP 文件喵~


4️⃣ 再用 dwebp 解每一帧成 PNG(这次是静态图,没问题了!)

1
2
3
4
5
# 遍历每帧 .webp 并转为 .png
for f in frames_1/*.webp; do
base=$(basename "$f" .webp)
dwebp "$f" -o "frames_1/${base}.png" -quiet
done

示例bash代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash

# 检查输入文件是否存在
if [ $# -ne 1 ]; then
echo "用法:$0 <输入动图.webp>"
exit 1
fi

input_webp="$1"

# 检查文件是否存在
if [ ! -f "$input_webp" ]; then
echo "错误:文件 $input_webp 不存在!"
exit 1
fi

# 获取动图总帧数(兼容 macOS grep)
# 先提取包含"Number of frames"的行,再用 awk 提取数字
frame_line=$(webpmux -info "$input_webp" | grep "Number of frames:")
if [ -z "$frame_line" ]; then
echo "错误:无法获取帧数,可能不是动图 WebP 文件!"
exit 1
fi
frame_count=$(echo "$frame_line" | awk '{print $4}') # 提取第4个字段(数字)

# 创建输出目录(以输入文件名命名)
output_dir="${input_webp%.webp}_frames"
mkdir -p "$output_dir"
echo "帧将保存到:$output_dir"

# 循环提取所有帧并转换为 PNG
for ((i=0; i<frame_count; i++)); do
# 提取第 i 帧(临时保存为 WebP 格式)
webpmux -get frame "$i" "$input_webp" -o "$output_dir/frame_$i.webp" >/dev/null 2>&1

# 将提取的帧转为 PNG
dwebp "$output_dir/frame_$i.webp" -o "$output_dir/frame_$i.png" >/dev/null 2>&1

# 删除临时 WebP 帧文件
rm -f "$output_dir/frame_$i.webp"

# 显示进度
echo -ne "处理中:$((i+1))/$frame_count 帧\r"
done

echo -e "\n完成!共提取 $frame_count 帧 PNG 文件"

第三步 用 ffmpeg 合成 GIF

示例代码:

1
ffmpeg -framerate 10 -start_number 0 -i ./webp/1_frames/frame_%d.png -loop 0 output1.gif

第一个参数(franmerate)建议:帧率(fps)= 总帧数 ÷ 总时长(秒)

常见场景下,28 帧动画的帧率设置有两种情况:

  1. 若总时长为 1 秒:帧率为 28 fps(但这种情况较少见,因为主流动画帧率多为 12、15、24、30 fps)。
  2. 若按主流帧率适配
    • 若按 14 fps 播放:总时长为 2 秒(28 ÷ 14 = 2);
    • 若按 24 fps 播放(接近电影标准):总时长约 1.17 秒(28 ÷ 24 ≈ 1.17);
    • 若按 30 fps 播放(接近视频标准):总时长约 0.93 秒(28 ÷ 30 ≈ 0.93)。

实际制作中,28 帧动画通常会按 14 fps24 fps 播放,前者更流畅且符合多数动画的节奏(12-15 fps 是常见的低成本动画帧率),后者更接近自然流畅的视觉体验。你可以根据动画的节奏需求,用上述公式反推合适的帧率(比如希望动画播放 2 秒,就用 14 fps)。

二.剪裁图片

最近 Gemini 的AI 生图模型 nano banana 想必大家都玩过了吧,那么AI 返回了这么一张表情包 9 宫格等等,该怎么对其裁剪成我们需要的一张张表情包呢

image-20251128022256696

1.先检查图片分辨率(是否可以特定的尺寸被整除)

1
ffmpeg -i emoji.png 2>&1 | grep "Stream #0:0" | grep -oE '[0-9]+x[0-9]+'
  • -oE-o只输出匹配部分,-E启用扩展正则表达式,兼容 macOS 的grep

需确保:

  • 宽度能被 5 整除(XXX % 5 == 0);

  • 高度能被 3 整除(YYY % 3 == 0

    如果不满足,可先缩放调整(例如缩放到 1200 * 720)

发现分辨率为 1024x572,不满足,那么缩放调整:

1
ffmpeg -i emoji.png -vf scale=1200:720 emoji_scaled.png

2.裁剪为 3×5 网格并保存到指定文件夹

执行以下命令(直接拆分并输出 15 张裁剪图):

1
2
3
4
5
6
7
8
9
# 创建目标文件夹(若不存在)
mkdir -p ./emoji_crops

# 再执行裁剪命令
ffmpeg -i emoji_scaled.png \
-vf "untile=layout=5x3" \
-fps_mode vfr \
-start_number 0 \
./emoji_crops/emoji_%02d.png

效果:
image-20251128023616468



新ICP备2025018290号-1
本站总访问量