如何用 FFmpeg 给视频加字幕
FFmpeg 字幕实操:硬编码 SRT/ASS、软编码字幕轨、样式自定义、多语言支持、字幕提取与格式转换。

字幕能提升无障碍体验、让视频内容可搜索、嘈杂环境下也能看懂。FFmpeg 能把字幕烧进画面、嵌成可切换的轨道、把已有字幕扒出来、还能在不同字幕格式之间转。这篇用实际命令把这些都讲一遍。
硬编码还是软编码?
加字幕之前先决定方式:
| 方式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 硬编码(burn-in) | 字幕直接渲染进画面像素 | 哪都能看到,兼容性最好 | 关不掉,得重编码 |
| 软编码(embedded) | 字幕作为独立轨道 | 可切换、支持多语言 | 看播放器是否支持 |
经验法则:社交媒体、要最大兼容性——硬编码;长视频、要多语言——软编码。如果你在做处理用户上传视频的 UGC 平台,软编码通常是更好的选择。
硬编码 SRT 字幕
SRT 是最常见的字幕格式,烧进画面就这一行:
# 硬编码 SRT 字幕
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" output.mp4
就这么简单。subtitles 滤镜(基于 libass)把 SRT 直接渲染到每一帧上。
改 SRT 的样式
默认渲染比较朴素,用 force_style 自定义:
# 自定义字体、字号、颜色
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontName=Arial,FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1'" output.mp4
常用参数:
| 参数 | 示例 | 说明 |
|---|---|---|
| FontName | Arial | 字体 |
| FontSize | 24 | 字号(点) |
| PrimaryColour | &H00FFFFFF | 文字颜色(ABGR 十六进制) |
| OutlineColour | &H00000000 | 描边颜色 |
| BackColour | &H80000000 | 阴影/背景色 |
| Outline | 2 | 描边粗细 |
| Shadow | 1 | 阴影深度 |
| Bold | 1 | 加粗(0 或 1) |
| Alignment | 2 | 位置(2=底部居中) |
| MarginV | 30 | 距边缘的垂直边距 |
注意:颜色用 ABGR(Alpha、蓝、绿、红),不是常规 RGB。
# 黄字、黑描边、大字号、位置偏上
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=28,PrimaryColour=&H0000FFFF,OutlineColour=&H00000000,Outline=3,MarginV=40'" output.mp4
路径里有特殊字符怎么办
字幕文件路径有空格或特殊字符时要转义:
# 转义路径中的空格
ffmpeg -i input.mp4 -vf "subtitles='my\ subtitles.srt'" output.mp4
# 或者用 filter 通过输入索引取字幕
ffmpeg -i input.mp4 -i subs.srt -filter_complex "[0:v][1:s]overlay" output.mp4
硬编码 ASS/SSA 带样式字幕
ASS(Advanced SubStation Alpha)支持丰富样式——字体、颜色、动画、逐行定位。硬编码 ASS 时所有样式都会保留:
# 硬编码 ASS(保留全部样式)
ffmpeg -i input.mp4 -vf "ass=subs.ass" output.mp4
ASS 文件长什么样
最小化的 ASS 结构:
[Script Info]
Title: My Subtitles
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,48,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,2,1,2,10,10,40,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,这是一条字幕。
Dialogue: 0,0:00:05.00,0:00:08.00,Default,,0,0,0,,这行有{\b1}加粗{\b0}文字。
把 SRT 转成 ASS 改样式
# SRT 转 ASS(之后可以编辑 ASS 的样式段)
ffmpeg -i subs.srt subs.ass
转完去改 ASS 文件的 [V4+ Styles] 段。视情况你可能还需要先换视频容器再加字幕。
软编码:嵌入字幕轨
软编码字幕作为独立流存在容器里,观众可以自己开关切换。
加 SRT 字幕轨
# 加 SRT 进 MP4
ffmpeg -i input.mp4 -i subs.srt -c copy -c:s mov_text output.mp4
# 加 SRT 进 MKV(MKV 原生支持)
ffmpeg -i input.mp4 -i subs.srt -c copy -c:s srt output.mkv
注意:MP4 容器装字幕只能用 mov_text 编码。MKV 直接支持 SRT、ASS 等。
加多语言字幕
# MKV 里加英文和西班牙文两条字幕
ffmpeg -i input.mp4 -i english.srt -i spanish.srt \
-map 0:v -map 0:a -map 1 -map 2 \
-c copy -c:s srt \
-metadata:s:s:0 language=eng -metadata:s:s:0 title="English" \
-metadata:s:s:1 language=spa -metadata:s:s:1 title="Spanish" \
output.mkv
MP4 同理:
# MP4 里加多语言字幕
ffmpeg -i input.mp4 -i english.srt -i chinese.srt \
-map 0:v -map 0:a -map 1 -map 2 \
-c copy -c:s mov_text \
-metadata:s:s:0 language=eng -metadata:s:s:0 title="English" \
-metadata:s:s:1 language=chi -metadata:s:s:1 title="中文" \
output.mp4
设置默认字幕轨
# 把第一条字幕轨设为默认
ffmpeg -i input.mp4 -i subs.srt \
-map 0 -map 1 \
-c copy -c:s mov_text \
-disposition:s:0 default \
output.mp4
ASS 作为软轨
# 把 ASS 嵌进 MKV(兼容播放器会保留全部样式)
ffmpeg -i input.mp4 -i subs.ass -map 0 -map 1 -c copy -c:s ass output.mkv
从视频里扒字幕
提取成 SRT
# 第一条字幕轨
ffmpeg -i input.mkv -map 0:s:0 output.srt
# 第二条字幕轨
ffmpeg -i input.mkv -map 0:s:1 output.srt
提取成 ASS
# 提取成 ASS(保留样式)
ffmpeg -i input.mkv -map 0:s:0 output.ass
看视频里有哪些字幕轨
# 列出所有流(含字幕)
ffprobe -v error -show_entries stream=index,codec_name,codec_type -of csv input.mkv
# 字幕轨详细信息
ffprobe -v error -select_streams s -show_entries stream=index,codec_name:stream_tags=language,title -of json input.mkv
一次提取所有字幕轨
#!/bin/bash
# 提取视频里所有字幕轨
INPUT="input.mkv"
# 数有几条字幕流
COUNT=$(ffprobe -v error -select_streams s -show_entries stream=index -of csv=p=0 "$INPUT" | wc -l)
for i in $(seq 0 $((COUNT - 1))); do
# 取语言标签
LANG=$(ffprobe -v error -select_streams s:$i -show_entries stream_tags=language -of csv=p=0 "$INPUT")
LANG=${LANG:-"track$i"}
ffmpeg -i "$INPUT" -map 0:s:$i "${INPUT%.*}_${LANG}.srt"
done
字幕格式互转
FFmpeg 能在文本字幕格式间转换:
# SRT 转 ASS
ffmpeg -i subs.srt subs.ass
# ASS 转 SRT(丢样式)
ffmpeg -i subs.ass subs.srt
# SRT 转 WebVTT
ffmpeg -i subs.srt subs.vtt
# SUB/IDX(DVD)转 SRT 需要 OCR,FFmpeg 干不了
# 用 SubtitleEdit 或 Tesseract 做位图转文字
| 转换 | 是否保留样式 |
|---|---|
| SRT → ASS | 加默认样式 |
| ASS → SRT | 全丢 |
| SRT → VTT | 保留基础格式 |
| VTT → SRT | 丢 VTT 特有功能 |
字幕时间不对
字幕整体提前/延后
# 字幕延迟 2.5 秒
ffmpeg -i input.mp4 -itsoffset 2.5 -i subs.srt -map 0 -map 1 -c copy -c:s mov_text output.mp4
硬编码时可以用 setpts 滤镜,或者直接改 SRT 文件(如果还要剪辑或合并视频,时间对齐就更重要):
# 硬编码时整体偏移
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" -ss 00:00:02.500 output.mp4
不同分辨率怎么处理字号
硬编码时字幕渲染受视频分辨率影响。要么保持原分辨率,要么明确缩放:
# 硬编码并保持原分辨率
ffmpeg -i input.mp4 -vf "subtitles=subs.srt" -c:v libx264 -crf 23 -c:a copy output.mp4
# 硬编码并缩到 1080p
ffmpeg -i input.mp4 -vf "subtitles=subs.srt,scale=-2:1080" -c:v libx264 -crf 23 -c:a copy output.mp4
按分辨率调字号
# 720p:小字号
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=20'" -c:v libx264 -crf 23 output.mp4
# 4K:大字号、加粗描边
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=48,Outline=3'" -c:v libx264 -crf 23 output.mp4
批量字幕操作
批量硬编码
# 假设每个视频都有同名 .srt
for f in *.mp4; do
srt="${f%.mp4}.srt"
if [ -f "$srt" ]; then
ffmpeg -i "$f" -vf "subtitles=$srt" -c:v libx264 -crf 23 -c:a copy "subtitled_${f}"
fi
done
批量软编码
# 把同名 SRT 作为字幕轨嵌进去
for f in *.mp4; do
srt="${f%.mp4}.srt"
if [ -f "$srt" ]; then
ffmpeg -i "$f" -i "$srt" -c copy -c:s mov_text "soft_${f}"
fi
done
常见坑
"No such filter: subtitles":你的 FFmpeg 编译时没带 libass。装个完整版:
# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt install ffmpeg libass-dev
字幕死活不显示:检查字幕文件编码,FFmpeg 要的是 UTF-8:
# 转成 UTF-8
iconv -f GBK -t UTF-8 subs_gbk.srt > subs_utf8.srt
字幕位置或大小不对:视频分辨率和字幕的预设分辨率对不上。用 force_style 强行覆盖:
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontSize=24,MarginV=30'" output.mp4
特殊字符显示成方框:字体不支持。指定能显示该语言的字体:
# 中文字幕推荐 Noto Sans CJK
ffmpeg -i input.mp4 -vf "subtitles=subs.srt:force_style='FontName=Noto Sans CJK SC'" output.mp4
MP4 输出报字幕 codec 错:MP4 只认 mov_text。其他字幕格式换成 MKV,或者直接硬编码。
想跑在云上:FFHub
硬编码字幕要重新编码视频,CPU 吃得很狠——一个 1 小时视频在普通机器上跑 20 多分钟很正常。FFHub 通过 API 在云端跑 FFmpeg,把烧字幕这种重活扔出去,本地不卡。
特别适合一份源视频要生成多个字幕版本(多语言、多样式)的场景。
小结
- 硬编码(
-vf "subtitles=subs.srt")保证哪都能看到 - 软编码(MP4 用
-c:s mov_text,MKV 用-c:s srt)支持开关切换 - 用
force_style改 SRT 外观 - 要丰富样式和动画用 ASS 格式
- 用
-map加多语言字幕 - 提取前用
ffprobe看清楚字幕轨结构 - 字幕文件务必 UTF-8 编码
延伸阅读
- 如何用 FFmpeg 剪辑与合并视频 - 切片拼接的同时保持字幕时间对齐
- 如何用 FFmpeg 转换视频格式 - MP4、MKV、WebM 对字幕的支持各有不同
- 如何用 FFmpeg 从视频提取音频 - 抽出音轨用于转录和字幕生成