Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 61 additions & 17 deletions docs/sop/tts-narration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## 工具

- [MiniMax TTS](https://www.minimax.chat/) — AI 语音合成
- [MiniMax TTS](https://www.minimax.chat/) — speech-2.8-hd 模型
- API: `https://api.minimaxi.com/v1/t2a_v2`

## 流程

Expand All @@ -11,28 +12,71 @@
- 从脚本中提取旁白文字
- 添加标点控制停顿节奏
- 长句拆短,每句 < 30 字为宜
- **每个场景单独生成一段 TTS**(便于后续对齐)

### 2. 音色选择

- 根据视频类型选择音色:
- 宣传视频 → 沉稳大气的男声/女声
- 知识点视频 → 清晰自然的讲解音色
- 试听 2-3 种音色,选最匹配的
| voice_id | 风格 | 适用场景 |
|----------|------|---------|
| vincent_wenxing_v1 | 闻星克隆声(偏年轻男声) | 机智流日报/产品视频 |
| presenter_male | 播音员男声(中性正式) | 品牌中性版本 |

### 3. 合成参数
### 3. 合成代码

- 语速:1.0x(默认),知识点视频可 0.9x
- 输出格式:WAV(高质量)或 MP3(轻量)
- 采样率:≥ 24000 Hz
```python
import requests, os

### 4. 后处理
payload = {
'model': 'speech-2.8-hd',
'text': '旁白文本',
'voice_setting': {
'voice_id': 'vincent_wenxing_v1',
'speed': 1.0, # 0.95-1.05 范围
},
'audio_setting': {
'sample_rate': 32000,
'format': 'wav', # ⚠️ 必须用 wav,mp3 格式有解码问题
}
}

- 检查发音准确性(专有名词、英文缩写)
- 裁剪首尾静音
- 必要时手动分段,匹配画面时长
r = requests.post('https://api.minimaxi.com/v1/t2a_v2',
headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'},
json=payload, timeout=60)

### 5. 注意事项
resp = r.json()
if resp.get('data') and resp['data'].get('audio'):
# ⚠️ 关键:audio 是 hex 编码,不是 base64!
audio = bytes.fromhex(resp['data']['audio'])
with open('output.wav', 'wb') as f:
f.write(audio)
# 转 mp3
os.system('ffmpeg -y -i output.wav -c:a libmp3lame -ar 44100 -q:a 2 output.mp3')
```

- 中英混合文本可能发音不准,建议分开合成再拼接
- 数字、日期建议写成中文("二零二六年"而非"2026年")
- 保留原始 WAV,最终渲染时再压缩
### 4. 拼接(多段 TTS + gap)

```bash
# 生成静音间隔
ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=stereo -t 0.8 -q:a 2 gap.mp3

# 拼接
printf 'file s00.mp3\nfile gap.mp3\nfile s01.mp3\nfile gap.mp3\nfile s02.mp3\n' > concat.txt
ffmpeg -y -f concat -safe 0 -i concat.txt -c:a libmp3lame -ar 44100 -q:a 2 narration.mp3
```

### 5. Silencedetect(找段落边界)

```bash
ffmpeg -i narration.mp3 -af silencedetect=noise=-30dB:d=0.7 -f null -
# >1s 的静默 = 段落边界
# 用于精确设置 Remotion Sequence 的 from/durationInFrames
```

### 6. 注意事项

- ⚠️ audio 字段是 **hex 编码**(`bytes.fromhex()`),不是 base64
- ⚠️ 输出格式必须用 **wav**(mp3 有解码问题)
- MiniMax API key 分两种:sk-cp(Token Plan,推荐)和 sk-api(标准)
- 语速 speed: 产品视频用 0.98-1.0,日报用 1.0-1.05
- TTS 输出偏轻,合并时需要 `volume=2.0` 增益
- Fallback:`python3 -m edge_tts --voice zh-CN-YunyangNeural --text "..." --write-media output.mp3`
189 changes: 159 additions & 30 deletions docs/sop/video-production-pipeline.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,184 @@
# 视频制作全流程 SOP

> 基于 anet.sh 产品视频 20 轮迭代实战经验(5.85 → 8.18/10)

## 流程概览

```
选题 → 脚本 → AI素材生成 → 语音合成视频合成 → 渲染 → QA发布
选题 → 脚本 → TTS 配音 → 视觉素材Remotion 排版 → 渲染 → 合并音频+BGM质检 → 上传
```

## 1. 选题

- 在 GitHub Issue 中创建选题,标注视频类型(宣传/资讯 or 知识点)
- 在 GitHub Issue 中创建选题,标注视频类型(宣传/资讯/知识点)
- 明确目标受众、时长、发布平台
- **单篇专题比填充式多条新闻效果好**(Vincent 实测反馈)
- 时长建议:15s(社交传播)/ 45-65s(官网)/ 90-120s(B站/YouTube)

## 2. 脚本撰写

- 确定视频结构:开场 → 正文(分段) → 结尾 CTA
- 每段标注:画面描述 + 旁白文字 + 时长预估
- 脚本模板见 templates/
### 口语化原则
- ❌ 流水账新闻播报、模板式开场
- ✅ 直接问问题开头、用人话解释术语、给出判断
- ✅ 结尾必须有**反问**(引发转发表态)

### 产品视频结构(8 段模板)
```
Hook(10s) → Install(15s) → Architecture(20s) → Features(20s)
→ Demo(15s) → Dashboard(15s) → Security(15s) → CTA(10s)
```

### 资讯视频结构(术语视觉化)
```
s00: "你知道 XXX 是什么吗?简单说,就是…"
s01: "具体发生了什么。这说明什么?"
s02: "某人说了一句话:不只是加速旧工作"
s03: "你今天还觉得 AI 只是帮你打字快一点吗?"
```

## 3. TTS 配音(MiniMax)

```python
# MiniMax speech-2.8-hd
payload = {
'model': 'speech-2.8-hd',
'text': '旁白文本',
'voice_setting': { 'voice_id': 'vincent_wenxing_v1', 'speed': 1.0 },
'audio_setting': { 'sample_rate': 32000, 'format': 'wav' }
}
# ⚠️ audio 是 hex 编码:bytes.fromhex(resp['data']['audio'])
# ⚠️ 输出用 wav(mp3 有解码问题)
```

各段 TTS 生成后用 ffmpeg 拼接(0.8-1.0s gap):
```bash
printf 'file s00.mp3\nfile gap.mp3\nfile s01.mp3\n...' > concat.txt
ffmpeg -f concat -safe 0 -i concat.txt -c:a libmp3lame narration.mp3
```

用 silencedetect 找段落边界,确定 Remotion 场景时间轴:
```bash
ffmpeg -i narration.mp3 -af silencedetect=noise=-30dB:d=0.7 -f null -
```

## 4. 视觉素材

### 优先级(实测经验)
1. **真实截图**(Playwright HiDPI)— 最真实,无 AI 感 ⭐推荐
2. **即梦概念图**(Dreamina 5.0)— 适合抽象概念,但有 AI 感
3. **录屏**(Playwright recordVideo)— 最真实但依赖服务器在线

### Playwright HiDPI 截图
```javascript
const page = await browser.newPage({
viewport: { width: 1920, height: 1080 },
deviceScaleFactor: 2 // 关键!出图更清晰
});
await page.waitForTimeout(5000); // 等数据加载完
await page.screenshot({ path: 'output.png' });
```

### Dreamina 文生图
```bash
dreamina text2image \
--prompt="描述(中文最佳,300-400字)" \
--ratio=16:9 --model_version=5.0 --resolution_type=4k \
--poll=60
```
⚠️ 必须加「不要文字 no text」否则出乱码

## 5. BGM 生成(MiniMax music)

```bash
mmx music generate \
--prompt "Clean minimal electronic ambient, tech product demo" \
--instrumental --genre "ambient electronic" \
--mood "professional, clean" --bpm 100 \
--model music-2.6 --out bgm.mp3
```

BGM 加入对评分提升 +0.4(D4 节奏感 + D5 完成品感)。

## 3. AI 素材生成(Dreamina)
## 6. Remotion 排版

- 根据脚本中的画面描述生成图片/视频片段
- 使用 text-to-image 或 image-to-video
- 小批量生成,验证质量后再批量扩展
- 注意 credits 余量:生成前 `dreamina user_credit` 检查
### 横屏双列布局(推荐)
```tsx
const DualCol = ({ left, right }) => (
<div style={{ display: "flex", gap: 48, width: "100%", height: "100%", alignItems: "center" }}>
<div style={{ flex: "0 0 60%", height: "100%" }}>{left}</div>
<div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 20 }}>{right}</div>
</div>
);
```

## 4. 语音合成(MiniMax TTS)
### 关键注意事项
- 截图用 `objectFit: "contain"`(不要 cover,会裁切)
- Sequence durationInFrames **延伸到下一场景开始**(避免黑帧)
- 场景只做 fade-in(fo=0),**不要 fade-out**(会导致两场景同时透明)

- 将旁白文字输入 MiniMax TTS 生成音频
- 选择合适的音色和语速
- 输出格式:WAV 或 MP3
## 7. 渲染

## 5. 视频合成(Remotion)
```bash
# ⚠️ 必须指定 video-bitrate!默认 CRF 暗色内容只出 200kbps
npx remotion render CompositionID out.mp4 --codec h264 --video-bitrate "4M"
```

- 使用 Remotion 编排素材:图片/视频 + 音频 + 字幕
- 配置分辨率、帧率、时长
- CJK 字体需显式指定路径,避免 □□□ 乱码
## 8. 合并音频(ffmpeg)

## 6. 渲染(ffmpeg)
```bash
# Narration + BGM + limiter(防削顶)
ffmpeg -y \
-i video.mp4 -i narration.mp3 -i bgm.mp3 \
-filter_complex "[1:a]volume=2.0[voice];[2:a]volume=0.06[bgm];[voice][bgm]amix=inputs=2:duration=first:normalize=0,alimiter=limit=0.7[aout]" \
-map 0:v -map "[aout]" \
-c:v copy -c:a aac -b:a 192k -ac 2 \
final.mp4
```

- Remotion 输出帧序列 → ffmpeg 编码为最终视频
- 常用参数:`-c:v libx264 -preset medium -crf 23`
- 字幕渲染:`drawtext` 滤镜,注意 fontfile 路径
⚠️ **必须 `-map 0:v -map 1:a`**(Remotion 输出有空音轨,不加 map 会选空音轨导致静音)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Correct ffmpeg mapping note to use mixed output

The warning here contradicts the command right above it: the pipeline builds a mixed track in filter_complex and maps it with -map "[aout]", but the note says to map 1:a. If someone follows the note, they will bypass the mixed/limited output (and in some variants can reselect an unintended track), which defeats the documented narration + BGM + limiter step.

Useful? React with 👍 / 👎.


## 7. 质量检查(QA)
## 9. 质检

- `ffprobe` 验证输出文件:时长、分辨率、帧率、音轨
- 目视检查:画面完整性、字幕对齐、音画同步
- AI 自评不可靠,必须人工/工具验证
```bash
# 黑帧检测(暗色主题用 0.995)
ffmpeg -i final.mp4 -vf blackdetect=d=0.3:pic_th=0.995 -an -f null -

## 8. 发布
# 音量检测
ffmpeg -i final.mp4 -af volumedetect -f null -
# 目标:mean -25~-20dB, max ≥ -6dB
```

## 10. 字幕硬烧(社交平台必备)

```bash
ffmpeg -i final.mp4 \
-vf "subtitles=subs.srt:force_style='FontName=Noto Sans SC,FontSize=32,Outline=3,Shadow=2,MarginV=50,BorderStyle=4'" \
-c:v libx264 -b:v 4M -c:a copy \
final_subs.mp4
```

- 上传至目标平台(B站、YouTube 等)
- 填写标题、描述、标签
- 在 Issue 中标记完成,附发布链接
大字幕对 D8 传播潜力提升 +1.0(静音友好)。

## 11. 交付物矩阵

| 版本 | 比例 | 时长 | 字幕 | 用途 |
|------|------|------|------|------|
| 完整版 | 16:9 | 60-120s | 硬烧 | B站/YouTube |
| 精简版 | 16:9 | 30-45s | 硬烧 | 朋友圈/视频号 |
| 15s 横版 | 16:9 | 15s | 无 | 信息流广告 |
| 15s 竖版 | 9:16 | 15s | 无 | 小红书/抖音 |

## 12. 经验教训(20 轮迭代总结)

| # | 教训 | 评分影响 |
|---|------|---------|
| 1 | 码率必须 ≥3Mbps(`--video-bitrate "4M"`) | P0 |
| 2 | 暗色主题 blackdetect 误报(用 pic_th=0.995) | 避免误判 |
| 3 | Sequence gap 导致黑帧(延伸 duration) | P0 |
| 4 | fade-out 导致透明帧(fo=0 只 fade-in) | P0 |
| 5 | BGM 加分 +0.4(MiniMax music-2.6) | D4+D5 |
| 6 | 大字幕加分 +0.2(静音友好) | D8 |
| 7 | 截图 contain > cover(cover 裁切内容) | P1 |
| 8 | 录屏依赖服务器(Connection failed 废片) | 风险 |
| 9 | HiDPI 截图(deviceScaleFactor: 2)更清晰 | D2 |
| 10 | 等页面数据加载(骨架屏 = P1 bug) | D2 |