Skip to content

分镜功能业务逻辑分析文档

1. 功能概述

分镜功能是AIGC视频创作系统的核心模块,提供从提示词生成分镜、编辑分镜内容、匹配素材、添加配音到最终合成视频的完整工作流。该功能支持AI辅助生成和手动编辑两种模式,帮助用户快速创建高质量的视频内容。

2. 核心概念

概念描述
分镜(StoryBoard)视频创作的基本单位,包含标题、描述、台词、特效、图片/视频素材、音频等元素
分镜数据(StoryBoardData)存储分镜所有信息的数据结构,包含标题、描述、台词、特效、媒体URL等字段
配置(Formula)用于大模型生成分镜的参数设置,包含提示词、系统提示词等
素材匹配将分镜内容与系统中的视频素材进行关联的过程
视频合成将分镜中的视频素材和音频素材合成为最终视频的过程

3. 模块结构

分镜功能采用组件化设计,主要包含以下核心组件:

views/aigc/component/
├── StoryBoard.vue          # 分镜管理主组件
├── StoryBoardItem.vue      # 单个分镜项组件
└── components/
    ├── VideoSynthesis.vue  # 视频合成组件
    ├── GenerateStoryBoard.vue  # 分镜生成组件
    ├── VideoArea.vue       # 视频区域组件
    ├── AudioArea.vue       # 音频区域组件
    ├── MaterialDialog.vue  # 素材选择弹窗
    ├── VideoPlayerDialog.vue # 视频播放弹窗
    └── VoiceDialog.vue     # 配音弹窗

各组件职责划分:

  • StoryBoard: 管理多个分镜项,提供分镜添加、删除、下载和视频合成等功能
  • StoryBoardItem: 负责单个分镜的内容编辑,包括标题、描述、台词、媒体素材管理等
  • VideoSynthesis: 处理视频合成的完整流程,包括素材上传、工程创建、任务执行等
  • GenerateStoryBoard: 提供AI生成分镜的功能,支持多种生成方式和平台选择

4. 业务流程图

mermaid
flowchart TD
    A[开始] --> B[创建/加载配置]
    B --> C{是否使用AI生成?}
    C -->|是| D[输入提示词]
    D --> E[调用LLM生成分镜]
    E --> F[生成多个分镜数据]
    C -->|否| G[手动添加分镜]
    F --> H[编辑分镜内容]
    G --> H
    H --> I[选择主题]
    I --> J{是否匹配素材?}
    J -->|是| K[打开素材对话框]
    K --> L[确认选择素材]
    J -->|否| M{是否添加配音?}
    L --> M
    M -->|是| N[打开配音对话框]
    N --> O[生成并添加配音]
    O --> P{是否合成视频?}
    M -->|否| P
    P -->|是| Q[选择合成方式]
    Q --> R[上传素材到ICE]
    R --> S[组建Timeline并创建工程]
    S --> T[执行项目剪辑]
    T --> U[查询生成任务状态]
    U --> V[获取临时下载地址]
    V --> W[保存单帧分镜]
    W --> X[下载视频]
    X --> Y[结束]

5. 核心业务逻辑详解

5.1 分镜管理流程

功能说明:管理分镜列表,包括添加、删除、查看和编辑分镜。

核心方法

  • addEmptyStoryBoard(): 添加一个空分镜到分镜列表
  • deleteStoryBoard(index): 根据索引删除指定分镜
  • handleItemUpdate(index, newValue): 更新指定分镜的数据

数据流向

  1. StoryBoard组件维护storyBoardData数组,包含所有分镜数据
  2. 每个分镜通过StoryBoardItem组件独立渲染和编辑
  3. StoryBoardItem组件通过事件机制将更新后的数据传递回父组件

5.2 配置管理流程

功能说明:管理用于生成分镜的配置,包括创建、加载、保存和删除配置。

核心方法

  • fetchFormulaList(): 获取配置列表
  • fetchFormulaSave(): 保存当前配置
  • loadFormula(e): 加载指定配置
  • fetchFormulaDelete(index, rowId): 删除指定配置

数据流向

  1. 配置数据从服务器获取并存储在formulaList中
  2. 用户可以选择加载现有配置或创建新配置
  3. 配置变更后通过API保存到服务器

5.3 LLM生成分镜流程

功能说明:利用大语言模型(LLM)根据用户提供的提示词自动生成分镜内容。

核心方法

  • llmGenerate(): 调用LLM生成分镜数据

参数

  • model: 使用的模型名称,默认为'deepseek-r1-250120'
  • messages: 包含用户提示词和系统提示词的消息数组

执行流程

  1. 收集用户输入的提示词和额外提示词
  2. 调用sendMessagesArk API向LLM发送请求
  3. 解析LLM返回的JSON格式结果
  4. 将结果转换为分镜数据并更新storyBoardData

5.4 分镜内容编辑流程

功能说明:编辑单个分镜的详细内容,包括标题、描述、台词、特效、媒体素材等。

核心方法

  • topicChange(e): 处理主题选择变更
  • confirmSelectMaterial(topicId): 确认选择素材并获取视频素材
  • handleVoiceSuccess(audioUrl): 处理配音生成成功后的逻辑
  • handleGenerateSuccess(result): 处理生成分镜成功后的逻辑

数据存储

  • 分镜数据实时保存在localStorage中,键名为storyboard_item_${itemId}
  • 数据变更通过事件机制同步到父组件

5.5 视频合成流程

功能说明:将分镜中的视频和音频素材合成为最终视频。

核心方法

  • synthesizeVideos(synthesisType = 0): 执行视频合成的完整流程

参数

  • synthesisType: 合成类型,0表示每个分镜单独合成,1表示所有分镜合成为一个视频

执行流程

  1. 上传素材到ICE: 调用fetchUploadMediaByUrl将视频和音频素材上传到阿里云ICE
  2. 组建Timeline并创建工程: 根据合成类型选择相应的Timeline构建方法,然后创建编辑工程
  3. 执行项目剪辑: 调用fetchSubmitMediaProducing提交剪辑任务
  4. 查询生成任务状态: 轮询调用fetchGetMediaProducingJob查询任务状态,直到任务完成或失败
  5. 获取临时下载地址: 调用fetchGetPresignUrl获取生成视频的临时下载地址
  6. 保存单帧分镜: 调用saveIce保存分镜数据
  7. 下载视频: 调用downloadVideos下载生成的视频

6. 核心API和方法索引

6.1 StoryBoard.vue 核心方法

方法名描述参数返回值
llmGenerate调用LLM生成分镜Promise<void>
fetchFormulaList获取配置列表Promise<void>
fetchFormulaSave保存配置Promise<void>
loadFormula加载配置e: 配置IDvoid
addEmptyStoryBoard添加空白分镜void
deleteStoryBoard删除分镜index: 分镜索引void
confirmSynthesis确认合成方式void
startSynthesis开始合成视频Promise<void>
downloadStoryBoard下载分镜Promise<void>

6.x 分镜列表历史策略(更新)

  • 增加字段:generationId(字符串),表示一次分镜生成会话的唯一标识。
  • 保存逻辑:
    • currentGenerationId 为空时,首次保存会生成一个新的 generationId 并新增一条历史记录。
    • currentGenerationId 存在时,保存将只更新对应的历史记录,而不新增。
  • 加载逻辑:
    • 从历史加载后,会将 entry.generationId 绑定到 currentGenerationId
    • 若旧历史项缺少 generationId,会回写一个基于时间戳的ID到该条目并持久化,确保后续保存为更新操作。
  • 其他字段:
    • timestamp 首次创建时间;updatedAt 最近更新时间;expireAt 过期时间(默认7天)。

6.2 StoryBoardItem.vue 核心方法

方法名描述参数返回值
confirmSelectMaterial确认选择素材topicId: 主题IDPromise<void>
handleVoiceSuccess处理配音成功audioUrl: 音频URLvoid
onDelete删除当前分镜void
openGenerateDialog打开生成分镜对话框void
handleGenerateSuccess处理生成分镜成功result: 生成结果void
loadImageTopics加载图片主题列表Promise<void>
topicChange处理主题变更e: 选中的主题void

6.3 VideoSynthesis.vue 核心方法

方法名描述参数返回值
synthesizeVideos执行视频合成流程synthesisType: 合成类型(0/1)Promise<void>
saveIce保存单帧分镜data: 分镜数据Promise<void>
downloadVideos下载视频synthesisType: 合成类型(0/1)Promise<void>

6.4 GenerateStoryBoard.vue 核心方法

方法名描述参数返回值
comfyuiGenerate使用comfyui生成图片Promise<Array>
generateVideoWithFramepack使用framepack生成视频Promise<void>
selectImage选择图片image: 图片URLPromise<void>

7. 数据结构定义

7.1 分镜数据结构 (StoryBoardItemData)

javascript
{
  description: string,  // 分镜描述
  line: string,         // 角色台词
  title: string,        // 分镜标题
  name: string,         // 角色名
  effect: string,       // 特效描述
  prompt: string,       // 提示词
  videoUrl: string,     // 视频URL
  audioUrl: string,     // 音频URL
  thumbUrl: string,     // 缩略图URL
  videoKey: string,     // 视频键名
  projectId: string,    // 工程ID
  jobId: string,        // 任务ID
  mediaURL: string,     // 媒体URL
  iceVideoUrl: string,  // ICE视频URL
  bucket: string,       // OSS桶名
  key: string           // OSS键名
}

7.2 配置数据结构 (Formula)

javascript
{
  name: string,         // 配置名称
  description: string,  // 配置描述
  data: {
    prompt: string,     // 用户提示词
    extraPrompt: string,// 额外提示词
    systemPrompt: string,// 系统提示词
    number: number      // 分镜数量
  }
}

8. 使用场景与典型操作

8.1 AI辅助生成分镜

场景说明:用户希望通过AI快速生成多个分镜。

操作流程

  1. 在"LLM生成分镜"区域输入提示词
  2. 可选择性地添加额外提示词
  3. 点击"生成"按钮
  4. 系统调用LLM生成多个分镜
  5. 查看生成的分镜列表,可进行进一步编辑

8.2 手动编辑分镜

场景说明:用户希望手动创建和编辑分镜内容。

操作流程

  1. 点击"添加分镜"按钮创建空分镜
  2. 编辑分镜标题、描述、台词等信息
  3. 选择主题以匹配相关配置
  4. 点击"匹配素材"按钮选择视频素材
  5. 点击"配音"按钮为分镜添加语音
  6. 可选择性地通过"生成分镜"按钮使用AI生成内容

8.3 合成视频

场景说明:用户完成分镜编辑后,希望将其合成为视频。

操作流程

  1. 点击"合成视频"按钮
  2. 选择合成方式:每个分镜单独合成或所有分镜合成为一个视频
  3. 系统自动执行视频合成的完整流程
  4. 合成完成后自动下载生成的视频

9. 常见问题与解决方案

  1. 问题:生成分镜时没有结果 解决方案:检查提示词是否清晰明确,尝试调整提示词内容

  2. 问题:无法选择素材主题 解决方案:确保已正确加载主题列表,刷新页面后重试

  3. 问题:视频合成失败 解决方案:检查分镜中是否包含有效的视频和音频素材,确保网络连接稳定

  4. 问题:下载的视频无法播放 解决方案:确认视频格式兼容性,尝试使用其他播放器

10. 输入输出示例

10.1 LLM生成分镜示例

输入

javascript
// 提示词输入
formula.data.prompt = "创作一个关于未来科技城市的短视频分镜";
formula.data.extraPrompt = "要求展现城市的空中交通、智能建筑和未来生活场景";

// 调用生成分镜方法
llmGenerate();

输出

javascript
// 生成的分镜数据示例
storyBoardData = [
  {
    title: "未来城市空中交通",
    description: "清晨的未来科技城市,空中交通繁忙而有序,自动驾驶飞行器在高楼间穿梭",
    line: "欢迎来到2150年的新都市,这里的交通已经彻底改变了我们的生活方式",
    name: "解说员",
    effect: "从空中俯瞰城市全景,镜头缓慢推进",
    prompt: "A futuristic cityscape with flying cars and tall skyscrapers in the morning, digital art style",
    videoUrl: "",
    audioUrl: "",
    thumbUrl: ""
  },
  // 更多分镜...
]

11. 详细流程图(方法、数据示例、错误示例)

11.1 页面结构与核心组件

views/aigc/index.vue
├── el-tabs
│   ├── StoryBoard(分镜主组件)
│   └── StoryBoardConfig(配置管理)
└── 关键交互:
    ├── confirmSynthesis → 打开 VideoSynthesis 弹窗
    └── startSynthesis → 调用 VideoSynthesis.synthesizeVideos(synthesisType)

views/aigc/component/StoryBoard.vue
├── StoryBoardItem 列表(单项编辑与素材匹配)
├── VideoSynthesis 弹窗(完整合成流程)
└── 常用方法:
    ├── confirmSynthesis() / startSynthesis()
    ├── downloadStoryBoard()
    ├── loadImageTopics()(主题缓存与加载)
    └── handleItemUpdate(index, newValue)

views/aigc/component/StoryBoardItem.vue
├── VideoArea(视频区域:打开素材、播放、删除)
├── AudioArea(音频区域:配音与设置)
├── MaterialDialog(素材选择)
├── VideoPlayerDialog(视频预览)
├── VoiceDialog(配音:GSV/Azure 等)
└── 关键方法:
    ├── confirmSelectMaterial(topicId) → 拉取/选择素材
    ├── handleVoiceSuccess(result) → 处理配音生成与直传
    └── onDelete / openGenerateDialog / handleGenerateSuccess

views/aigc/component/components/VideoSynthesis.vue
└── 完整合成 6 步:
    1) 上传/注册素材到 ICE
    2) 组建 Timeline 并创建工程
    3) 执行项目剪辑
    4) 轮询生成任务状态
    5) 获取临时下载地址(OSS 预签名)
    6) 保存单帧分镜并下载

11.2 分镜生成流程(GenerateStoryBoard.vue)

mermaid
flowchart TD
    A[开始:填写提示词/参数] --> B[拉取生成记录 fetchRemoteGenerationRecords]
    B --> C{选择平台/方式}
    C -->|即梦| D[发起生成任务]
    D --> E[轮询/回填视频结果]
    E --> F{是否保存为素材?}
    F -->|是| G[下载视频 Blob]
    G --> H[adminDirectUpload → OSS]
    H --> I[material 记录入库]
    F -->|否| J[结束]

    subgraph 输入
      P1(prompt)
      P2(description/effect → 组合提示词)
      P3(imageTopicId)
    end

    subgraph 输出
      O1(videoUrl)
      O2(proxyPreviewUrl)
      O3(material.key)
    end
  • 核心方法

    • buildPromptFromDescEffect(description, effect): 组合提示词
    • fetchRemoteGenerationRecords():合并远端/本地生成记录
    • downloadVideoBlob(url):下载生成的视频结果
    • adminDirectUpload(blob, { key, contentType }):直传 OSS
  • 输入示例

json
{
  "prompt": "未来城市空中交通,数字艺术风格",
  "generateType": "文生视频",
  "platform": "即梦",
  "frames": 121,
  "aspectRatio": "9:16",
  "imageTopicId": 1024
}
  • 输出示例
json
{
  "videoUrl": "https://example.com/generated.mp4",
  "proxyPreviewUrl": "https://example.com/proxy/preview.mp4",
  "operation": { "saved": true },
  "material": { "key": "ai/temp/video/task-12345.mp4" }
}
  • 错误示例
json
{ "message": "视频下载失败", "code": 10000 }

11.3 素材选择与视频区域(StoryBoardItem.vue + VideoArea.vue)

mermaid
flowchart TD
    A[打开素材弹窗] --> B[confirmSelectMaterial(topicId)]
    B --> C[匹配/选择视频素材]
    C --> D[回填 item.videoUrl / videoKey]
    D --> E[VideoArea.tryPlay() 自动播放]
    D --> F[VideoPlayerDialog 预览]
    D --> G[删除视频 → onRemoveVideo]

    subgraph 输入
      P1(topicId)
      P2(item.title/description/line)
    end

    subgraph 输出
      O1(item.videoUrl)
      O2(item.videoKey):::oss
      O3(item.thumbUrl)
    end

    classDef oss fill:#eef,stroke:#66f,stroke-width:1px;
  • 方法索引

    • confirmSelectMaterial(topicId):根据主题取素材
    • handleVoiceSuccess(result):处理配音生成、确定后缀、直传/预签名
    • VideoArea.onOpenMaterial/onOpenVideo/onRemoveVideo:交互能力
  • 错误示例

json
{ "message": "未找到符合条件的素材", "code": 10000 }

11.4 视频合成详细流程(VideoSynthesis.vue)

mermaid
flowchart TD
    S[开始 synthesizeVideos] --> U1[步骤1: 素材上传/注册]
    U1 --> U2[步骤2: 组建Timeline并创建工程]
    U2 --> U3[步骤3: 执行项目剪辑]
    U3 --> U4[步骤4: 查询生成任务状态]
    U4 --> U5[步骤5: 获取临时下载地址]
    U5 --> U6[步骤6: 保存单帧分镜]
    U6 --> D[下载视频]

    subgraph 步骤1 细节
      A1[videoKey → ossObjectExists]
      A1 -->|存在| A2[fetchRegisterMediaInfo(video, oss://bucket/key)]
      A1 -->|不存在| A3[使用 http(s) videoUrl]
      A4[audioPath → ossObjectExists]
      A4 -->|存在| A5[fetchRegisterMediaInfo(audio, oss://bucket/key)]
      A4 -->|不存在| A6[ensureAudioPresignedUrl(blob→PUT uploadUrl→GET presign_url)]
      A7[fetchUploadMediaByUrl({data}) → 返回 videoMediaId/audioMediaId]
    end

    subgraph 步骤2 细节
      B1[buildTimelineSingle]
      B2[fetchCreateEditingProject → ProjectId]
    end

    subgraph 步骤3 细节
      C1[fetchSubmitMediaProducing → JobId]
    end

    subgraph 步骤4 细节
      D1[循环 3-5s]
      D2[fetchGetMediaProducingJob → Status]
      D3{Status}
      D3 -->|Success| D4[继续]
      D3 -->|Failed| E1[报错并停止]
    end

    subgraph 步骤5 细节
      F1[fetchGetPresignUrl(bucket,key) → presign_url]
    end

    subgraph 步骤6 细节
      G1[fetchIceSave(data) → 成功]
    end
  • 输入示例(步骤1:上传/注册)
json
{
  "data": [
    {
      "title": "未来城市空中交通",
      "description": "清晨的未来科技城市...",
      "line": "欢迎来到2150年的新都市...",
      "videoUrl": "https://img.stooland.com/ai/data/image/2-153-14908.mp4?...",
      "audioUrl": "https://gsv.ai-lab.top/outputs/61dc5b...e.wav",
      "thumbUrl": "https://img.stooland.com/...thumb.webp"
    }
  ]
}
  • 输出示例(上传结果)
json
[
  {
    "videoMediaId": "606c6bf32a7171f0b86c0361c0d66502",
    "audioMediaId": "90f192f32a7171f0bfc20360d1d76502",
    "videoJobId": "9b5b2d640b6ac838",
    "audioJobId": "40cbd605e042e02c"
  }
]
  • 输入示例(步骤2:创建工程)
json
{
  "title": "ICE自动剪辑2025-05-11-12-00",
  "coverURL": "https://img.stooland.com/...thumb.webp",
  "timeline": { "VideoTracks": [...], "AudioTracks": [...], "SubtitleTracks": [...] }
}
  • 输出示例(创建工程)
json
{ "ProjectId": "75573520d5294d87b6310f76bb32cd4d", "Status": 1 }
  • 输入示例(步骤3:提交剪辑)
json
{
  "projectId": "75573520d5294d87b6310f76bb32cd4d",
  "outputMediaTarget": "oss-object",
  "outputMediaConfig": { "MediaURL": "https://aigc-sz-linkt.oss-cn-shenzhen.aliyuncs.com/ice-output/ICE自动剪辑2025-05-11-12-00.mp4" }
}
  • 输出示例(提交剪辑)
json
{ "JobId": "6de19229972c4e1795fc3aac4df1487d", "MediaId": "f40d73...", "ProjectId": "75573520..." }
  • 输出示例(步骤4:查询任务)
json
{ "MediaProducingJob": { "Status": "Success", "MediaURL": "https://aigc-sz-linkt.../ICE自动剪辑2025-05-11-12-00.mp4" } }
  • 输入/输出示例(步骤5:预签名)
json
// 输入
{ "bucket": "aigc-sz-linkt", "key": "ice-output/ICE自动剪辑2025-05-11-12-00.mp4" }
// 输出
{ "presign_url": "http://aigc-sz-linkt.oss-cn-shenzhen.aliyuncs.com/ice-output/ICE...mp4?x-oss-..." }
  • 输入/输出示例(步骤6:保存分镜)
json
// 输入
{ "title": "未来城市空中交通", "projectId": "...", "jobId": "...", "iceVideoUrl": "http://..." }
// 输出
{ "responseHeader": { "returnCode": 0, "message": "成功" } }
  • 下载示例
json
{ "fileName": "未来城市空中交通.mp4", "url": "http://...presign_url..." }

11.5 错误数据示例与兼容策略

  • 上传/注册异常:
json
{ "message": "上传ICE素材异常:返回为空", "returnCode": 10000 }
  • 缺少视频素材ID(步骤1校验)
json
{ "message": "第 1 条素材上传/注册失败:缺少视频素材ID", "returnCode": 10000 }
  • 生成任务失败(步骤4)
json
{ "MediaProducingJob": { "Status": "Failed", "Message": "剪辑失败" } }
  • 获取临时下载地址失败(步骤5)
json
{ "timestamp": 1760596678, "returnCode": 1001, "message": "参数错误,system_error_lack_params" }

说明:后端已修正 getPresignUrl 的参数解析(region 默认 cn-shenzhen),该错误在缺省参数场景下已被避免。

  • ICE 媒体已存在(后端兼容)
json
// 原始 ICE 异常
{ "code": "MediaAlreadyExist", "message": "registerMediaInfo: mediaId: 60b6c2d... 已存在" }

// 后端兼容后的返回(成功透传 MediaId)
{ "responseHeader": { "returnCode": 0, "message": "成功" }, "responseData": { "MediaId": "60b6c2d32e5071f080580360d1d76502", "existed": 1 } }

11.6 方法索引与组件映射(扩展)

  • StoryBoard.vue

    • confirmSynthesis():合成确认弹窗
    • startSynthesis():调用子组件 synthesizeVideos(synthesisType)
    • downloadStoryBoard():导出 ZIP 与 presign 视频
    • loadImageTopics():主题列表加载与缓存
  • StoryBoardItem.vue

    • confirmSelectMaterial(topicId):素材选择
    • handleVoiceSuccess(result):配音生成与直传处理
    • openGenerateDialog() / handleGenerateSuccess(result):AI 生成分镜
  • VideoSynthesis.vue

    • synthesizeVideos(synthesisType):完整 6 步合成流程
    • saveIce(data):保存单帧分镜
    • downloadVideos(synthesisType):下载合成结果
  • utils/ice-lib.js

    • buildTimelineSingle(videoMediaId, audioMediaId, line, main, subtitleSegments, includeSubtitles):单分镜时间线
    • convertStoryBoardDataToTimeline(storyBoardData, videoClipConfig, audioClipConfig, includeSubtitles):整片时间线

11.7 使用建议与注意事项

  • 优先使用 videoKey/audioPath(OSS)注册素材,减少重复上传与网络风险。
  • audioUrlblob: 时,自动走直传流程以生成 ICE 可访问的 presign_url
  • 轮询生成任务时合理控制间隔(3-5s),避免过度请求。
  • fetchGetPresignUrl 已对 region 设置默认值,前端无需传参即可使用。
  • 后端对 MediaAlreadyExist 进行兼容,前端只需使用返回的 MediaId

11.8 团队Wiki拆分与链接

为方便团队协作与按主题阅读,已将第11节拆分为4个子页:

  • 分镜生成(Generate)→ ./storyboard-wiki-generation.md
  • 素材处理(Materials)→ ./storyboard-wiki-materials.md
  • 工程剪辑与合成(Synthesis)→ ./storyboard-wiki-synthesis.md
  • 错误与排障(Errors & Troubleshooting)→ ./storyboard-wiki-errors.md

如需导出到外部Wiki系统,可直接使用以上MD文件。

10.2 合成视频示例

输入

javascript
// 准备好的分镜数据
const storyBoardData = [/* 包含视频URL和音频URL的分镜数据 */];

// 调用视频合成方法
startSynthesis();

输出

javascript
// 合成成功后的分镜数据
storyBoardData = [
  {
    // 原有分镜数据
    iceVideoUrl: "https://example.com/synthesized-video.mp4", // 新增的合成视频URL
    projectId: "prj-123456", // 新增的工程ID
    jobId: "job-789012" // 新增的任务ID
  },
  // 更多分镜...
]