# 视频生成模板(video-prompt) > 提炼自 `kb-raw/04-SOP工作流/…/flow/4-视频生成prompt.md`(含**降级策略**与**图片压缩**), > 参数约束以本仓实测为准(见 `references/jimeng-api.md`)。 > > **这是 VOC-SOP 阶段 4b 的提示词与参数规范**:把关键帧变成 AI 视频片段。 > 本技能对应 `fmode-mv video`。 --- ## 一、参数约束(★ 实测修正过 KB 的记载) | 参数 | 约束 | 说明 | |---|---|---| | `frames` | **`[121, 241]` 且 `≡ 1 (mod 24)`** | 合法值只有 **121/145/169/193/217/241** | | 单镜时长 | **只能是 5~10 秒的整数秒** | `frames = 24 × 秒数 + 1` | | `method` | `'2'` 首帧 / `'3'` 首尾帧 | `'3'` 需要 2 张同比例图 | | `config.image_urls` | method=2 → 1 张;method=3 → `[首帧, 尾帧]` 2 张 | | | 首帧图比例 | **长边 / 短边 ≤ 3** | 否则被拒 | | 尾帧图比例 | **必须与首帧相同**(method=3) | | | 分辨率 | 统一 **720p**(¥0.28/秒)省成本 | 1080p ¥0.63/秒;Pro ¥1/秒 | | prompt 长度 | 建议 ≤400 字,最长 800 字 | | | 输出画幅 | **跟随输入图**,不跟随 `sizeDate` | | > ⚠️ **KB 只说「范围 [121,241]」,实测还要求 `frames % 24 === 1`。** > 传 122 会被拒:`x2v pipeline: frames % 24 should be 1`。 > 所以**不存在 3 秒或 13 秒的镜头** —— 这一点会反过来约束分镜设计。 --- ## 二、图片预处理(生成视频前必做) 即梦只吃**公网 URL**,且对二进制体积有限制(约 4–5MB base64 字符串)。 大 PNG 直传报 `Image Decode Error: invalid binary data file size`。 ```js // KB 的 sharp 版 const sharp = require('sharp'); await sharp(inputPath).resize(1280, 720).jpeg({ quality: 80 }).toFile(outputPath); // 2560×1440 PNG (~5.6MB) → 1280×720 JPEG (~200KB);base64 从 ~750万字符降到 ~28万字符 ``` ```js // 本技能仓的零依赖版(lib/jimeng.mjs 的 prepareImageUrl()):ffmpeg 压缩 + 七牛上传 ffmpeg -y -v error -i in.png \ -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \ -q:v 4 out.jpg // 再 POST /parse/functions/qiniu_uptoken 拿 uptoken → multipart 上传 → 返回 https 直链 ``` > 法一需要 `npm i sharp`;本仓选法二(**保持零 npm 依赖**)。 --- ## 三、生成方式选择(第一镜 vs 其余) | 位置 | 方式 | 理由 | |---|---|---| | **第 1 段** | `method=3`(首尾帧) | 用第 2 镜的关键帧做尾帧,做「空间推进」转场 | | **其余段** | `method=2`(首帧) | 更稳、更省,失败率低 | | **末段**(可选) | `method=3` | 与首帧呼应闭环(KB 版本4 的做法:仅第 1、9 段用 method=3) | | **场景跨度大的段** | **必须 `method=2`** | 室内→室外、人物→空镜这类两帧会崩坏 | ### 3.1 场景跨度大时禁用 method=3(硬规则) `method=3` 只用于这三种情况: 1. 同场景的连续动作 2. 相似场景的过渡 3. 数据 / 状态渐变 其余一律 `method=2`。 ### 3.2 首尾帧设计要点 - **同一人物**:同服装、同发型、同配饰、同场景基本元素;提示词中明确「与首帧同一人物」。 - **呼应三层**:视觉(同机位/同构图/同环境,不同细节)、情感(消极→积极)、主题(核心道具变化)。 - **写法模板**:「与首帧[相同的房间/位置/人物],但[表情不同/服装不同/光线不同],展示变化和成长」 --- ## 四、method=3 降级策略(必须内置) KB 原话:`method=3` 比 `method=2` 更容易失败 —— 两张图数据量翻倍、首尾帧对画面一致性要求更高。 ```js async function generateVideo(data) { if (data.method === '3') { const result = await submitVideo({ ...data, method: '3' }); if (result.data?.workId) return pollAndDownload(result.data.workId); console.log('method=3 失败,降级为 method=2'); } // 降级:只传首帧 return submitVideo({ ...data, method: '2' }); } ``` **降级后必须做两件事**: 1. 把 prompt 改成描述「从首帧到预期效果的过渡」(因为失去了尾帧约束) 2. **在产物与日志里留痕**(本技能用 `degraded: true` + 日志写明「已从 method=3 降级」) > **绝不静默降级。** 用户必须能分清哪些镜头是首尾帧、哪些是退而求其次的首帧。 --- ## 五、运镜与音效(每镜必填) ### 5.1 运镜词库(一镜一种,禁止叠加) | 运镜 | 适用 | |---|---| | 固定机位静止 | 强调等待/结果/冲击;**AI 最稳,优先使用** | | 缓慢推近 | 靠近情绪、紧张升级 | | 缓慢拉远 | 释放空间、收尾 | | 横向跟随 | 跟随主体位移 | | 缓慢上摇 / 下摇 | 揭示垂直空间 | | 手持轻微晃动 | 真实感、临场感 | **禁止**:「推 + 环绕 + 上摇」叠加;**环绕镜头一律不用**。 ### 5.2 音效描述(prompt 必须包含) > 每个视频的 prompt 中**必须包含背景音效描述**。 示例:温暖氛围音乐|励志管弦乐|科技感电子音|雨声与远处车流|钢琴单音| 海浪与环境风声|咖啡馆背景人声|心跳声渐强|低沉的弦乐 drone ### 5.3 运动真实(防「AI 味」,来自版本5 的结论) 视频侧从「画面真实」转向**「运动真实」**,prompt 里强制加: - 手持微抖动(**而非丝滑漂浮**) - 自然眨眼、呼吸、重心转移 - motion blur - `consistent identity & lighting` - 环境动态(树叶、衣物、光影流动) - 节奏不要太「AI」(动作不过于均匀同步) --- ## 六、视频 prompt 骨架(可直接抄) ```text {{主体动作,一句话}}, {{镜头运动:slow push in / pull back / pan left / handheld micro-shake}}, {{运动真实约束:gentle natural motion, subtle handheld micro-shake, natural breathing, hair strands catching the light, motion blur, consistent identity and lighting}}, {{音效描述:warm ambient piano / tense low strings / rain and distant traffic}}, {{画质词:4k, cinematic, film grain, shallow depth of field}} ``` **中文示例(改写为英文后投喂)**: > 女子在窗边缓缓转头看向镜头,阳光掠过发丝; > 镜头缓慢推近;手持轻微晃动,自然呼吸节奏,发丝随风轻摆,运动模糊自然; > 温暖氛围钢琴;4k 高清,电影感,胶片颗粒 --- ## 七、完整配置对象 ```js { id: 1, name: '第1段视频 - 开场推镜', method: '3', // '3'=首尾帧(第一段), '2'=首帧 frames: 121, // 5 秒;必须 ∈ {121,145,169,193,217,241} resolution: '720p', // 省钱统一 720p firstImage: 'photo1.png', lastImage: 'photo2.png', // 仅 method=3 prompt: '完整的情景描述 + 运镜 + 音效 + 4k 画质词' } ``` 提交体: ```js // method=2 await postJson('/getVideoV3_720p', { prompt: PROMPT, frames: 121, method: '2', config: { image_urls: [firstFrameUrl] }, }); // method=3 —— 必须传数组,[首帧, 尾帧] await postJson('/getVideoV3_720p', { prompt: PROMPT, frames: 121, method: '3', config: { image_urls: [firstFrameUrl, lastFrameUrl] }, }); ``` --- ## 八、健壮性要求(缺一不可) | 要求 | 做法 | |---|---| | **幂等** | 生成前、下载前都检查文件是否已存在,存在即跳过(`fs.existsSync`) | | **workId 留痕** | 记到日志,格式:`[时间戳] 类型 \| workId: xxx \| 内容: xxx` | | **段间隔防限流** | 每段之间 `await sleep(15000)` | | **并发退避** | 遇 `50430` / `Concurrent Limit`:退避 `10000 × (retry + 1)`,上限 5 次 | | **HTML 检测** | 轮询返回以 ` 以上九条在本技能仓中**全部已实现**:见 `lib/jimeng.mjs`(`submit` / `poll` / `assertValidFrames`) > 与 `scripts/video.mjs`(幂等、workId 日志、降级留痕)。 --- ## 九、映射预检(生成前必做) ``` photo1 → 视频1(首帧), 视频1(尾帧) photo3 → 视频2(首帧) photo4 → 视频3(首帧) ... ``` - [ ] 每张照片都有对应的视频段? - [ ] 视频脚本里的首帧路径都指向存在的文件? - [ ] method=3 的两张图都存在? - [ ] 映射关系与分镜文档一致? > **踩坑记录**:某项目 photo1(楼外场景)生成了但没有对应视频,后期才发现需要补生成。 --- ## 十、成本 | 分辨率 | 单价 | 5 秒单镜 | 8 镜整片(每镜 5s) | |---|---|---|---| | 720p | ¥0.28/秒 | **¥1.41**(实测实扣) | ≈ ¥11 | | 1080p | ¥0.63/秒 | ¥3.15 | ≈ ¥25 | | Pro | ¥1.00/秒 | ¥5.00 | ≈ ¥40 | > 试跑省钱:`fmode-mv video --from work/storyboard.json --limit 1`(1 镜,¥1.41) > 零费用验证:`--mode still`(Ken Burns 兜底,不调 API)