【Python】从 MP4 文件中提取中英双语字幕并生成双语字幕文件(如 .srt)
文章目录
- 🎯 目标
- ✅ 所需工具与库
- 安装依赖
- 🔍 第一步:查看 MP4 中的字幕轨道
- 📥 第二步:用 ffmpeg 提取字幕为 .srt 文件
- 🔗 第三步:合并中英文字幕为双语字幕
- ✅ 改进版:智能时间匹配合并双语字幕(推荐脚本)
- 🔧 使用方法
- ✅ 最终成果
- 💡 补充说明
- 🧩 完整脚本结构概览
- 🚀 进阶建议
在 Windows 系统下使用 Python 从 MP4 文件中提取中英双语字幕并生成双语字幕文件(如
.srt
),可以分为以下几个步骤:
🎯 目标
- 从 MP4 文件中提取内嵌的中文和英文字幕流。
- 将提取出的字幕转换为标准
.srt
格式。 - 合并中英文字幕,生成 双语字幕(例如:上行英文,下行中文)。
✅ 所需工具与库
ffmpeg
:用于提取字幕流(MP4 中可能包含subrip
、ass
、mov_text
等格式的内嵌字幕)pysubs2
:处理 SRT/ASS 字幕格式,支持转换和合并- Python 3.x
安装依赖
pip install pysubs2
注意:你需要先安装
ffmpeg
并将其加入系统 PATH。可从官网下载或使用 https://www.gyan.dev/ffmpeg/builds/ 提供的 Windows 构建版本。
🔍 第一步:查看 MP4 中的字幕轨道
使用 ffprobe
(ffmpeg 自带)来检查有哪些字幕流:
import subprocess
import jsondef list_subtitle_streams(video_path):cmd = ['ffprobe','-v', 'quiet','-print_format', 'json','-show_streams','-select_streams', 's', # 只看字幕流video_path]result = subprocess.run(cmd, capture_output=True, text=True)data = json.loads(result.stdout)subtitles = []for i, stream in enumerate(data['streams']):tags = stream.get('tags', {})language = tags.get('language', 'und')subtype = stream.get('codec_name', '')title = tags.get('title', '')subtitles.append({'index': stream['index'],'type': subtype,'lang': language,'title': title,'tags': tags})print(f"[{i}] Index: {stream['index']}, Lang: {language}, Title: {title}, Type: {subtype}")return subtitles# 示例调用
video_file = "example.mp4"
subs = list_subtitle_streams(video_file)
输出示例:
[0] Index: 3, Lang: eng, Type: mov_text
[1] Index: 4, Lang: chi, Type: mov_text
记下英文和中文对应的 index
(比如 3 和 4)。
📥 第二步:用 ffmpeg 提取字幕为 .srt 文件
def extract_subtitle_stream(video_path, stream_index, output_srt):cmd = ['ffmpeg','-i', video_path,'-map', f'0:{stream_index}','-c:s', 'srt',output_srt,'-y' # 覆盖输出]result = subprocess.run(cmd, capture_output=True, text=True)if result.returncode == 0:print(f"✅ 成功提取字幕到 {output_srt}")else:print("❌ 提取失败:", result.stderr)raise Exception(result.stderr)# 提取英文和中文字幕
extract_subtitle_stream(video_file, stream_index=3, output_srt="en.srt")
extract_subtitle_stream(video_file, stream_index=4, output_srt="zh.srt")
⚠️ 如果原始字幕是
mov_text
类型,ffmpeg 可以直接转成.srt
;如果是ass
或其他复杂格式,建议先保存为.ass
再用pysubs2
处理。
🔗 第三步:合并中英文字幕为双语字幕
使用 pysubs2
读取两个 .srt
文件,并按时间轴合并。
import pysubs2def create_bilingual_srt(en_srt_path, zh_srt_path, output_path):# 加载字幕en_subs = pysubs2.load(en_srt_path, encoding="utf-8")zh_subs = pysubs2.load(zh_srt_path, encoding="utf-8")# 创建新的字幕对象bilingual_subs = pysubs2.SSAFile()# 按事件时间合并from collections import defaultdictmerged_events = defaultdict(dict)# 添加英文字幕for ev in en_subs:key = (ev.start, ev.end)merged_events[key]['en'] = ev.text# 添加中文字幕for ev in zh_subs:key = (ev.start, ev.end)merged_events[key]['zh'] = ev.text# 构造双语字幕for (start, end), texts in merged_events.items():en_text = texts.get('en', '')zh_text = texts.get('zh', '')# 双语格式:上英下中combined_text = f"{en_text}\n{zh_text}" if en_text and zh_text else (en_text or zh_text)new_event = pysubs2.SSAEvent(start=start, end=end, text=combined_text)bilingual_subs.events.append(new_event)# 排序bilingual_subs.sort()# 保存bilingual_subs.save(output_path, encoding="utf-8-sig") # utf-8-sig 防止乱码print(f"🎉 双语字幕已保存至: {output_path}")# 调用函数
create_bilingual_srt("en.srt", "zh.srt", "bilingual.srt")
✅ 改进版:智能时间匹配合并双语字幕(推荐脚本)
import pysubs2
from typing import List, Tupledef check_subtitle_order(subs: pysubs2.ssafile.SSAFile) -> bool:"""检查字幕的顺序是否正确"""for i in range(len(subs) - 1):if subs[i].end > subs[i + 1].start:return Falsereturn Truedef merge_subtitles(zh_path: str, en_path: str, output_path: str):# 加载字幕zh_subs = pysubs2.load(zh_path, encoding="utf-8")en_subs = pysubs2.load(en_path, encoding="utf-8")# 按开始时间排序zh_subs.sort()en_subs.sort()# 检查字幕的顺序是否正确if not check_subtitle_order(zh_subs) or not check_subtitle_order(en_subs):raise ValueError("❌ 字幕顺序错误,请检查!")# 合并所有事件(中文 + 英文),然后按开始时间排序all_events = []for event in zh_subs:all_events.append(("zh", event.start, event.end, event.text))for event in en_subs:all_events.append(("en", event.start, event.end, event.text))# 按开始时间排序all_events.sort(key=lambda x: x[1])all_events_org = all_events.copy()subs_result = []i = 0while i < len(all_events) - 1:kind, start, end, text = all_events[i]kind2, start2, end2, text2 = all_events[i + 1]zh_line = text if kind == 'zh' else Noneen_line = text if kind == 'en' else None# 第一种情况:如果两个事件无重叠,则直接输出if start2 >= end:subs_result.append((start, end, zh_line, en_line))# 第二种情况:如果两个事件有重叠,且 end < end2elif end <= end2:subs_result.append((start, start2, zh_line, en_line))zh_line = text if kind == 'zh' else text2en_line = text2 if kind2 == 'en' else textsubs_result.append((start2, end, zh_line, en_line))all_events[i + 1] = (kind2, end, end2, text2)# 第三种情况:如果两个事件有重叠,且 end > end2elif end > end2:subs_result.append((start, start2, zh_line, en_line))zh_line = text if kind == 'zh' else text2en_line = text2 if kind2 == 'en' else textsubs_result.append((start2, end2, zh_line, en_line))all_events[i] = (kind, end2, end, text)i += 1i += 1if i == len(all_events) - 1:kind, start, end, text = all_events[i]zh_line = text if kind == 'zh' else Noneen_line = text if kind == 'en' else Nonesubs_result.append((start, end, zh_line, en_line))def get_subs_merge(subs_result: List[Tuple[int, int, str, str]]):subs_merge = []i = 0while i < len(subs_result) - 1:# 对于中文和英文有一个为空,且另一个重复的两行,进行合并zh_line = subs_result[i][2]en_line = subs_result[i][3]zh_line_next = subs_result[i + 1][2]en_line_next = subs_result[i + 1][3]start = subs_result[i][0]end = subs_result[i][1]start_next = subs_result[i + 1][0]end_next = subs_result[i + 1][1]if zh_line == zh_line_next and en_line == en_line_next:subs_merge.append((min(start, start_next), max(end, end_next), zh_line, en_line))i += 2continueelif (en_line is None or en_line_next is None) and zh_line == zh_line_next:if en_line is None:en_line = en_line_nextsubs_merge.append((min(start, start_next), max(end, end_next), zh_line, en_line))i += 2continueelif (zh_line is None or zh_line_next is None) and en_line == en_line_next:if zh_line is None:zh_line = zh_line_nextsubs_merge.append((min(start, start_next), max(end, end_next), zh_line, en_line))i += 2continuesubs_merge.append(subs_result[i])i += 1if i == len(subs_result) - 1:subs_merge.append(subs_result[i])return subs_mergesubs_merge = get_subs_merge(subs_result)while len(subs_merge) != len(subs_result):subs_result = subs_mergesubs_merge = get_subs_merge(subs_result)# 构建输出字幕对象merged_subs = pysubs2.SSAFile()for t0, t1, zh_line, en_line in subs_merge:# 添加到输出字幕event = pysubs2.SSAEvent()event.start = t0event.end = t1event.text = f'{zh_line}\n{en_line}'merged_subs.events.append(event)merged_subs.save(output_path, encoding="utf-8")print(f"✅ 双语字幕已保存至: {output_path}")
🔧 使用方法
merge_subtitles("en.srt", "zh.srt", "bilingual_aligned.srt")
✅ 最终成果
你将得到一个 bilingual.srt
文件,每条字幕显示为:
1
00:00:10,000 --> 00:00:13,000
你好,最近怎么样?
Hello, how are you?
💡 补充说明
问题 | 解决方案 |
---|---|
字幕编码乱码 | 使用 encoding="utf-8-sig" 或尝试 "gbk" |
时间轴不匹配 | 使用 pysubs2 的 .adjust() 方法对齐或做时间插值 |
MP4 无内嵌字幕 | 此方法无效,需 OCR 提取(可用 VSR 工具 + paddleOCR ) |
外挂字幕 | 直接加载 .srt 文件即可 |
🧩 完整脚本结构概览
# main.py
1. list_subtitle_streams(video.mp4)
2. extract_subtitle_stream(... index=3 -> en.srt)
3. extract_subtitle_stream(... index=4 -> zh.srt)
# 4. create_bilingual_srt("en.srt", "zh.srt", "bilingual.srt")
4. merge_subtitles("en.srt", "zh.srt", "bilingual_aligned.srt")
🚀 进阶建议
- 使用
pysubs2
支持样式定制(字体、颜色等)。 - 若想生成外挂双语字幕供播放器使用,可将
.srt
放在视频同目录并重命名为video.chi.eng.srt
。 - 批量处理多个视频?加个
os.listdir()
循环即可。