diff --git a/docs/sop/tts-narration.md b/docs/sop/tts-narration.md index 8e4c05d..a661a54 100644 --- a/docs/sop/tts-narration.md +++ b/docs/sop/tts-narration.md @@ -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` ## 流程 @@ -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` diff --git a/docs/sop/video-production-pipeline.md b/docs/sop/video-production-pipeline.md index 1d88091..4cef1a6 100644 --- a/docs/sop/video-production-pipeline.md +++ b/docs/sop/video-production-pipeline.md @@ -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 }) => ( +
+
{left}
+
{right}
+
+); +``` -## 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 会选空音轨导致静音) -## 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 |