← All posts

如何用 FFmpeg 给视频加字幕

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

FFHub·2026-04-28
如何用 FFmpeg 给视频加字幕

字幕能提升无障碍体验、让视频内容可搜索、嘈杂环境下也能看懂。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

常用参数:

参数示例说明
FontNameArial字体
FontSize24字号(点)
PrimaryColour&H00FFFFFF文字颜色(ABGR 十六进制)
OutlineColour&H00000000描边颜色
BackColour&H80000000阴影/背景色
Outline2描边粗细
Shadow1阴影深度
Bold1加粗(0 或 1)
Alignment2位置(2=底部居中)
MarginV30距边缘的垂直边距

注意:颜色用 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 给视频加字幕 | FFHub