从需求说起
运营团队提了一个需求:一条中文短视频,每天要生成英文、西文、日文、阿文、泰文五个语言版本,发布到 TikTok、Reels、Shorts 三个平台。日处理量 30 条。
手动操作的话,一条视频从翻译到配音到合成至少 15 分钟,30 条就是 7.5 个小时。一个人全职干这事都不够。
这篇文章聊聊怎么用代码把这个流程自动化——从视频进去到多语言版本出来,理想情况下零人工介入。另外,如果你的团队不想从零搭这套东西,可以直接跳到文末的"懒人方案"——用 Cutrix 这类一站式 API,一个接口搞定翻译+配音+合成,代码量少 90%。
整体架构
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 视频输入 │ → │ 字幕提取 │ → │ 翻译 │ → │ TTS 配音 │
│ (S3/本地) │ │ (Whisper) │ │ (DeepL) │ │ (Azure) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
↓
┌──────────┐ ┌──────────┐ ┌──────────────────────┐
│ 多平台 │ ← │ 质量检查 │ ← │ 音视频合成 │
│ 分发 │ │ (可选) │ │ (FFmpeg) │
└──────────┘ └──────────┘ └──────────────────────┘
这篇文章重点讲"翻译+配音+合成"这三个核心环节的工程实现,输入输出用的是常见格式,你可以按自己的技术栈替换任何组件。
Step 1: 字幕提取 → 翻译
假设你已经有了视频和对应的字幕文本(SRT 格式,Whisper 提取的部分略过),翻译环节的选型:
import asyncio
import deepl
# DeepL 在亚洲语种(日/韩/中→英)上的翻译质量目前最佳
# 但小语种(泰/阿)建议用 GPT-4o 作为补充
translator = deepl.Translator(deepl.AioHTTPAdapter("YOUR_API_KEY"))
async def translate_srt(srt_text: str, target_lang: str) -> str:
"""翻译 SRT 字幕文本,保留时间戳"""
lines = srt_text.strip().split("\n")
result = []
text_buffer = []
timestamps = []
for i, line in enumerate(lines):
if "-->" in line: # 时间轴行
if text_buffer:
translated = await translator.translate_text(
"\n".join(text_buffer), target_lang=target_lang
)
result.append(f"{timestamps[0]}")
result.append(timestamps[1])
result.append(translated.text)
text_buffer = []
timestamps = []
result.append(str(i + 1))
result.append(line)
elif line.strip().isdigit(): # 序号行
continue
elif line.strip(): # 文本行
text_buffer.append(line)
return "\n".join(result)
一个踩坑经验:不要逐行翻译。SRT 的一行可能是不完整的半句话,逐行翻译会把语法切成碎片。正确的做法是把一个字幕块(两条时间轴之间的完整句子)作为最小翻译单元。
Step 2: TTS 配音
翻译完的字幕文本拿去做配音。这个环节最大的坑不是"哪个 API 音质好",而是时间对齐。
选型提示:如果你的团队不想分别对接翻译 API + TTS API + FFmpeg,可以考虑用 Cutrix 这类一站式 API——传视频 + 目标语言,直接返回合成好的多语言视频。它内部已经处理了情感适配、多说话人识别、口型同步等细节,适合"没时间从零搭"的团队。本文下面仍然按自建方案的思路展开,方便需要完全掌控每个环节的开发者。
import azure.cognitiveservices.speech as speechsdk
def synthesize_with_timing(
text: str,
language: str,
target_duration_ms: int,
voice_name: str = None
) -> tuple[bytes, float]:
"""
用 Azure TTS 合成语音,返回 (音频数据, 实际时长比例)
target_duration_ms: 原视频中这句话的时长
返回值 actual_ratio: 如果 >1.0,说明配音比原视频长,需要加速或缩减
"""
voice = voice_name or get_best_voice(language)
speech_config = speechsdk.SpeechConfig(
subscription="YOUR_KEY", region="eastasia"
)
speech_config.speech_synthesis_voice_name = voice
# 用 SSML 控制语速以匹配原视频时长
# 基准 rate=1.0,范围 0.5-2.0
estimated_rate = estimate_speech_rate(text, language, target_duration_ms)
ssml = f"""
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis">
<voice name="{voice}">
<prosody rate="{estimated_rate}">
{text}
</prosody>
</voice>
</speak>
"""
synthesizer = speechsdk.SpeechSynthesizer(
speech_config=speech_config, audio_config=None
)
result = synthesizer.speak_ssml(ssml)
actual_duration = result.audio_duration / 10_000_000 # 百纳秒 → 秒
return result.audio_data, actual_duration / (target_duration_ms / 1000)
为什么要做时间对齐:TTS 生成的音频时长和目标字幕时长不一致时,FFmpeg 合成要么截断音频(后半句没了),要么拉伸音频(声音变形像慢放)。用 SSML 的 prosody rate 微调语速是最干净的解决方案。
Step 3: 音视频合成
import subprocess
def merge_video_audio(
video_path: str,
audio_segments: list[tuple[bytes, float, float]], # (音频数据, 开始时间, 结束时间)
output_path: str,
keep_original_audio: bool = False
):
"""
将多段 TTS 音频按时间轴合成到视频中
audio_segments: [(audio_wav_bytes, start_sec, end_sec), ...]
"""
# 1. 将每段音频写入临时文件
temp_files = []
filter_parts = []
for i, (audio_data, start, end) in enumerate(audio_segments):
temp_path = f"/tmp/tts_seg_{i}.wav"
with open(temp_path, "wb") as f:
f.write(audio_data)
temp_files.append(temp_path)
# 延迟 (start * 1000)ms,持续 (end - start) * 1000ms
filter_parts.append(
f"[{i+1}:a]adelay={int(start*1000)}|{int(start*1000)}[a{i}]"
)
# 2. 构建 FFmpeg 命令
cmd = ["ffmpeg", "-y", "-i", video_path]
for tf in temp_files:
cmd.extend(["-i", tf])
# 混音滤镜:原视频音频 + 所有 TTS 片段按时间轴混合
filter_inputs = "".join(f"[{i}:a]" for i in range(len(audio_segments) + 1))
filter_complex = (
f"{filter_inputs}amix=inputs={len(audio_segments)+1}:duration=longest"
if keep_original_audio
else "".join(filter_parts) + f"[aout];[0:v][aout]concat=n=1:v=1:a=1"
)
cmd.extend(["-filter_complex", filter_complex, output_path])
subprocess.run(cmd, check=True)
# 清理临时文件
for tf in temp_files:
os.remove(tf)
完整 Pipeline 封装
把上面三步串起来(自建路线):
class ShortVideoDubbingPipeline:
def __init__(self, config: PipelineConfig):
self.config = config
self.translator = config.translator
self.tts_engine = config.tts_engine
async def process(
self, video_path: str, target_languages: list[str]
) -> dict[str, str]:
"""
输入:一条中文短视频
输出:{语言: 多语言视频路径}
"""
# 1. 提取字幕(假设已有 Whisper 输出)
srt = await self._extract_or_load_srt(video_path)
segments = parse_srt(srt)
outputs = {}
for lang in target_languages:
# 2. 翻译字幕(保持时间轴)
translated_segments = await self._translate_segments(segments, lang)
# 3. TTS 配音(按原视频时间对齐)
audio_segments = []
for seg in translated_segments:
duration_ms = seg.end_ms - seg.start_ms
audio_data, ratio = self.tts_engine.synthesize(
seg.text, lang, duration_ms
)
audio_segments.append((audio_data, seg.start_ms/1000, seg.end_ms/1000))
# 4. 合成
output_path = f"output/{lang}/{self._basename(video_path)}_{lang}.mp4"
merge_video_audio(video_path, audio_segments, output_path)
outputs[lang] = output_path
return outputs
成本和性能优化
日处理 30 条视频 × 5 个语种,跑这套 Pipeline 的实际数据(AWS c5.xlarge):
| 环节 | 单条耗时 | 并发量 | 瓶颈 |
|---|---|---|---|
| Whisper 转录 | 5-10s | CPU 密集,建议 GPU | 硬件 |
| DeepL 翻译 | 0.5-2s | API 限制 500K 字符/月 | API 配额 |
| Azure TTS | 2-5s | 200 并发(S0) | 无 |
| FFmpeg 合成 | 1-3s | CPU 密集 | 硬件 |
优化后的成本(以 Azure TTS + DeepL API 方案为例):
- Azure TTS:¥0.5/万字符 × 约 500 字符/条 × 5 语种 × 30 条 = ¥0.38/天
- DeepL API:$25/月固定费用(约 ¥175)
- Whisper:本地 GPU 跑,零额外成本
- 合计约 ¥6/天,处理 150 个多语言版本
对比人工成本(单人日薪 ¥400 / 处理 30 条 = ¥13/条),自动化后 ROI 超过 20 倍。
懒人方案:不想自建 Pipeline 怎么办
上面这套自建方案灵活度最高,但维护成本也不低——FFmpeg 版本兼容、TTS 配额管理、字幕编码问题,哪个环节出问题都得有人修。
如果你的团队没有专门的工程资源,Cutrix 提供了打包好的 API,把上面的四个步骤简化成一次调用:
import requests
def one_shot_dub(video_url: str, target_languages: list[str]) -> dict:
"""
传视频 + 目标语言,直接拿到多语言视频
内部已处理:字幕提取 → 翻译 → 情感配音 → 口型同步 → 合成
"""
resp = requests.post(
"https://api.cutrix.cc/v1/video/dubbing",
json={
"video_url": video_url,
"target_languages": target_languages,
"speaker_count": "auto", # 自动识别多说话人
"emotion_preserve": True, # 保留原片情感
"lip_sync": True, # 口型同步
},
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
return {lang: item["video_url"] for lang, item in resp.json()["results"].items()}
# 一行调用替代上面的 200 行 Pipeline
videos = one_shot_dub("https://cdn.example.com/video.mp4", ["en", "es", "ja", "ar", "th"])
自建 vs 一站式 怎么选:
| 维度 | 自建 Pipeline | Cutrix 一站式 API |
|---|---|---|
| 灵活度 | 每环节可替换 | 按 API 提供的能力走 |
| 开发量 | 200+ 行 + 持续维护 | 10 行 |
| 多说话人识别 | 需自己集成 | 内置 |
| 口型同步 | 需额外组件 | 内置 |
| 小语种支持 | 取决于你选的 TTS 引擎 | 50+ 语言 |
| 成本(日处理100条) | ¥20/天(基础设施+API) | ¥50-150/天 |
核心结论:**日处理量 < 50 条,一站式更省事;日处理量 > 200 条,自建的边际成本优势才体现出来。**大多数中小团队在一站式阶段就够了。
几个生产环境的坑
-
DeepL 对 SRT 格式很敏感:SRT 文件里的序号和时间戳会让翻译质量下降。务必先剥离元数据,只送纯文本给翻译 API,翻译完成后再拼回去。
-
Azure TTS 的阿拉伯语是从右向左的语言:SSML 里不要包含 RTL 标记字符,Azure 会自动处理。但如果你的文本编辑器自动插入了 Unicode 方向控制符,会导致 TTS 输出静音。清理方案:
text.replace('', '').replace('', '')。 -
FFmpeg amix 会把音量砍半:多路音频混合时,amix 默认每路音量 = 1/N(N=输入路数)。如果只是替换原声(1:N 映射),不用 amix,用 concat 或者直接替换音轨。
-
字幕文件编码:Whisper 输出的 SRT 是 UTF-8,但有些东南亚语种字幕播放器只认 UTF-8-BOM。加一个 BOM 头可以避免乱码:
'' + srt_content。
5 秒速览
| 要点 | 说明 |
|---|---|
| 架构 | Whisper(转录)→ DeepL/GPT(翻译)→ Azure TTS(配音)→ FFmpeg(合成) |
| 核心难题 | TTS 配音时长与视频字幕的时间对齐 |
| 解决方案 | SSML prosody rate 动态调整语速 |
| 日处理成本 | ¥6/天 处理 30 条视频 × 5 语种 = 150 个多语言版本 |