在日常视频处理中,将独立字幕(如 B 站下载的 .srt 文件)合并到视频中并保持无损,是高频需求。本文基于 FFmpeg 与 Python 标准库,提供一套稳定、可复用、支持中文的解决方案,核心特点是不重新编码视频/音频(速度快、无损)、正确处理 UTF-8 中文字幕、设置字幕语言元数据。
![图片[1]_Python + FFmpeg 实现 MP4 与 SRT 字幕无损合并(完整方案)_知途无界](https://zhituwujie.com/wp-content/uploads/2026/01/d2b5ca33bd20260119092628-1024x526.png)
一、实现目标
- 合并 MP4 视频与 SRT 字幕为一个新视频文件;
- 不重新编码视频/音频(保持原画质/音质,合并速度快);
- 正确处理 UTF-8 编码的中文字幕(避免乱码);
- 设置字幕语言元数据(如
chi,方便播放器识别); - 封装为可复用的 Python 函数,支持灵活调用。
二、环境准备
1. 安装 FFmpeg
FFmpeg 是核心工具,需先安装并配置环境变量。
- Ubuntu/Debian:
sudo apt update && sudo apt install ffmpeg -y - macOS(Homebrew):
brew install ffmpeg - Windows:
从 FFmpeg 官网 下载编译好的二进制包(如ffmpeg-master-latest-win64-gpl.zip),解压后将bin目录(含ffmpeg.exe)添加到系统PATH(或通过完整路径调用)。 - 验证安装:
终端执行ffmpeg -version,输出版本信息则成功。
2. Python 环境
仅需 Python 3.7+,无需第三方库(使用标准库 subprocess、os 等)。
三、核心思路:FFmpeg 命令解析
合并字幕的本质是通过 FFmpeg 将 SRT 字幕封装到 MP4 容器中,关键命令如下:
ffmpeg -sub_charenc UTF-8 -i input.mp4 -i subtitle.srt -c:v copy -c:a copy -c:s mov_text -metadata:s:s:0 language=chi output.mp4
参数详解
| 参数 | 作用 |
|---|---|
-sub_charenc UTF-8 | 强制指定字幕编码为 UTF-8(解决中文乱码问题,必须加!) |
-i input.mp4 | 输入视频文件 |
-i subtitle.srt | 输入字幕文件(SRT 格式) |
-c:v copy | 视频流不重新编码(直接复制,保持原画质,速度快) |
-c:a copy | 音频流不重新编码(直接复制,保持原音质) |
-c:s mov_text | 字幕流编码为 mov_text(MP4 容器支持的字幕格式,兼容主流播放器) |
-metadata:s:s:0 language=chi | 设置第 0 条字幕流的语言为 chi(中文,播放器可识别) |
output.mp4 | 输出合并后的视频文件 |
四、完整 Python 实现代码
以下代码封装为函数,支持文件校验、错误处理、灵活参数配置:
import subprocess
import os
from typing import Optional
def merge_subtitle_to_video(
video_path: str,
subtitle_path: str,
output_path: str,
subtitle_language: str = "chi",
overwrite_output: bool = True
) -> None:
"""
将 MP4 视频与 SRT 字幕无损合并(不重新编码视频/音频)
:param video_path: 输入视频文件路径(MP4)
:param subtitle_path: 输入字幕文件路径(SRT)
:param output_path: 输出合并后的视频路径(MP4)
:param subtitle_language: 字幕语言元数据(如 "chi" 表示中文,"eng" 表示英文)
:param overwrite_output: 是否覆盖已存在的输出文件(默认 True)
:raises FileNotFoundError: 视频或字幕文件不存在
:raises subprocess.CalledProcessError: FFmpeg 执行失败
"""
# 1. 校验输入文件是否存在
if not os.path.isfile(video_path):
raise FileNotFoundError(f"视频文件不存在: {video_path}")
if not os.path.isfile(subtitle_path):
raise FileNotFoundError(f"字幕文件不存在: {subtitle_path}")
# 2. 确保输出目录存在
output_dir = os.path.dirname(output_path)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
# 3. 构建 FFmpeg 命令(参数列表形式,避免 shell 注入风险)
cmd = [
"ffmpeg",
"-sub_charenc", "UTF-8", # 强制字幕编码为 UTF-8(关键!)
"-i", video_path, # 输入视频
"-i", subtitle_path, # 输入字幕
"-c:v", "copy", # 视频流不重新编码
"-c:a", "copy", # 音频流不重新编码
"-c:s", "mov_text", # 字幕编码为 MP4 支持的 mov_text
"-metadata:s:s:0", f"language={subtitle_language}" # 设置字幕语言
]
# 4. 处理输出文件覆盖(若需覆盖,添加 -y 参数)
if overwrite_output:
cmd.insert(1, "-y") # 插入到 ffmpeg 后,作为输入前的参数
# 5. 添加输出文件路径
cmd.append(output_path)
# 6. 执行 FFmpeg 命令(捕获输出,失败时抛异常)
try:
result = subprocess.run(
cmd,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True # 输出为字符串(而非字节)
)
print(f"字幕合并成功!输出文件: {output_path}")
print(f"FFmpeg 输出: {result.stdout}")
except subprocess.CalledProcessError as e:
error_msg = f"FFmpeg 执行失败!返回码: {e.returncode}\n错误信息: {e.stderr}"
raise RuntimeError(error_msg) from e
# -------------------------- 示例调用 --------------------------
if __name__ == "__main__":
# 示例路径(替换为实际文件路径)
video_file = "input_video.mp4" # 输入视频(MP4)
subtitle_file = "subtitle.srt" # 输入字幕(SRT,需 UTF-8 编码)
output_file = "output_with_sub.mp4" # 输出视频
try:
merge_subtitle_to_video(
video_path=video_file,
subtitle_path=subtitle_file,
output_path=output_file,
subtitle_language="chi", # 中文
overwrite_output=True
)
except Exception as e:
print(f"合并失败: {e}")
五、代码结构解析
1. 文件校验
通过 os.path.isfile 提前检查视频和字幕文件是否存在,避免因路径错误导致的 FFmpeg 报错(更友好的错误提示)。
2. 使用 subprocess.run 的优势
- 参数列表形式:避免直接使用字符串命令(如
ffmpeg -i ...),防止 shell 注入攻击; - 捕获输出:通过
stdout=subprocess.PIPE和stderr=subprocess.PIPE捕获 FFmpeg 的输出日志,便于调试; - 自动抛异常:
check=True确保 FFmpeg 执行失败时(返回非 0 码)抛出CalledProcessError,统一错误处理逻辑。
3. 选择 mov_text 的原因
MP4 容器原生不支持直接嵌入 SRT 字幕,需转换为容器兼容的格式。mov_text 是 MP4 官方推荐的字幕编码格式,支持主流播放器(VLC、QuickTime、手机端播放器等),且体积小、兼容性强。
六、常见问题与解决方案
1. 中文字幕乱码
- 原因:SRT 字幕文件编码非 UTF-8(如 GBK、ANSI),或未通过
-sub_charenc UTF-8强制指定编码。 - 解决:
- 确保 SRT 文件本身为 UTF-8 编码(可用记事本/VS Code 另存为 UTF-8 格式);
- 代码中必须保留
-sub_charenc UTF-8参数(关键!)。
2. 输出文件覆盖失败
- 现象:目标路径已存在文件,FFmpeg 报错
File exists。 - 解决:代码中通过
overwrite_output=True自动添加-y参数(FFmpeg 的覆盖标志),或在调用时手动删除旧文件。
3. 字幕语言元数据未生效
- 现象:播放器未识别字幕语言(如显示为“未知”)。
- 解决:检查
-metadata:s:s:0 language=chi参数是否正确(s:s:0表示第一条字幕流,chi是中文的标准语言代码)。
4. 需要将字幕“烧录”到画面中(硬字幕)
若需字幕固定在画面中(不可关闭),需使用重新编码视频的方式(速度慢、有损):
# 烧录字幕的 FFmpeg 命令(替换原函数中的 cmd 构建部分)
cmd = [
"ffmpeg",
"-i", video_path,
"-vf", f"subtitles={subtitle_path}:force_style='FontName=SimHei,Fontsize=24,PrimaryColour=&HFFFFFF&'", # 指定字体(如黑体)
"-c:a", "copy", # 音频仍不重新编码
output_path
]
- 缺点:视频会被重新编码(耗时、画质可能下降),字幕无法关闭。
七、总结
本文方案通过 Python 调用 FFmpeg,实现了 MP4 与 SRT 字幕的无损合并,核心优势是速度快(不重新编码)、中文兼容(UTF-8 强制编码)、可复用(封装为函数)。通过合理的参数配置和错误处理,可稳定应用于日常视频处理场景(如 B 站视频字幕合并、教学视频压制等)。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容