HOME> 版本前瞻> 如何用 FFmpeg 给视频加字幕

如何用 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 剪辑与合并视频 - 切片拼接的同时保持字幕时间对齐

如何用 FFmpeg 转换视频格式 - MP4、MKV、WebM 对字幕的支持各有不同

如何用 FFmpeg 从视频提取音频 - 抽出音轨用于转录和字幕生成