136 lines
4.1 KiB
Markdown
136 lines
4.1 KiB
Markdown
# 混剪功能规格(简化版)
|
||
|
||
## 核心需求
|
||
|
||
- **输入**:用户选择素材 + 设定每个素材截取时长(3-15s)
|
||
- **输出**:1-3个不同内容的混剪视频
|
||
- **总时长**:15s-60s
|
||
- **差异化**:同顺序 + 同时长 + **随机截取起点**
|
||
|
||
## 多视频差异化算法
|
||
|
||
### 核心原理
|
||
|
||
**随机起点 + 容错机制**:
|
||
- 每个视频使用**随机截取起点**,确保内容完全不同
|
||
- 支持**不同长度的素材**,ICE自动容错处理
|
||
- 容错:如果起点超出素材长度,ICE自动从0开始截取
|
||
|
||
**随机种子**:使用 `素材ID×1000000 + 视频序号×10000 + URL哈希%1000` 确保可重现性
|
||
|
||
### 算法实现
|
||
|
||
**随机起点生成**:
|
||
```java
|
||
// 1. 先获取视频实际时长
|
||
int actualDuration = getVideoDuration(videoUrl);
|
||
|
||
// 2. 生成随机种子
|
||
long randomSeed = (material.getFileId() * 1000000L) +
|
||
(videoIndex * 10000L) +
|
||
(material.getFileUrl().hashCode() % 1000);
|
||
Random random = new Random(randomSeed);
|
||
|
||
// 3. 根据实际时长计算起始范围
|
||
int maxStartOffset = Math.max(0, actualDuration - duration);
|
||
int startOffset = random.nextInt(maxStartOffset + 1);
|
||
int endOffset = startOffset + duration;
|
||
```
|
||
|
||
**获取视频时长方案**:
|
||
1. **数据库字段**:上传时预存duration字段(推荐)
|
||
2. **FFprobe工具**:命令行获取视频元数据
|
||
3. **ICE元数据API**:调用ICE查询接口
|
||
4. **默认60秒**:保守值,兼容性最好
|
||
|
||
**容错机制**:
|
||
- 根据实际时长计算最大起始偏移,避免超出素材长度
|
||
- 如果获取时长失败,使用默认值60秒
|
||
- ICE自动处理边界情况
|
||
|
||
### ICE Timeline构建
|
||
|
||
每个素材片段包含参数:
|
||
- `MediaURL`:素材地址
|
||
- `In`:随机截取起始点(0到实际时长-duration之间)
|
||
- `Out`:截取结束点 = `In + duration`
|
||
- `TimelineIn/TimelineOut`:时间轴位置(顺序拼接)
|
||
|
||
ICE自动处理超出素材长度的情况,无需额外判断。
|
||
|
||
## API设计
|
||
|
||
### 请求格式
|
||
|
||
```http
|
||
POST /api/mix/create
|
||
{
|
||
"title": "美食视频",
|
||
"materials": [
|
||
{ "fileId": 123, "fileUrl": "https://xxx/v1.mp4", "duration": 5 },
|
||
{ "fileId": 456, "fileUrl": "https://xxx/v2.mp4", "duration": 8 },
|
||
{ "fileId": 789, "fileUrl": "https://xxx/v3.mp4", "duration": 5 }
|
||
],
|
||
"produceCount": 3
|
||
}
|
||
```
|
||
|
||
### 后端处理流程
|
||
|
||
1. 校验请求参数(总时长15-60s)
|
||
2. 循环生成produceCount个视频:
|
||
- videoIndex = 0, 1, 2...
|
||
- 获取每个素材的实际时长(数据库/FFprobe/ICE API)
|
||
- 生成随机起点(基于素材ID×1000000 + videoIndex×10000 + URL哈希)
|
||
- 根据实际时长计算起始范围,避免超出素材长度
|
||
- 构建Timeline,传递随机In/Out参数给ICE
|
||
- 提交ICE任务
|
||
3. 保存任务并返回任务ID
|
||
|
||
## 校验规则
|
||
|
||
| 规则 | 前端 | 后端 |
|
||
|------|------|------|
|
||
| 总时长 15-60s | ✅ | ✅ |
|
||
| 单素材 3-15s | ✅ | ✅ |
|
||
| 至少选1个素材 | ✅ | ✅ |
|
||
| 生成数量 1-3 | ✅ | ✅ |
|
||
|
||
## 实现清单
|
||
|
||
### 已完成
|
||
- [x] 前端时长选择和实时计算
|
||
- [x] 后端VO(MaterialItem)实现
|
||
- [x] 后端DO(materialsJson)字段
|
||
- [x] 数据库迁移脚本
|
||
- [x] 后端Controller(/api/mix/create)
|
||
- [x] 后端Service(多视频生成逻辑)
|
||
- [x] ICE Timeline构建(随机起点+实际时长+容错)
|
||
- [x] 批量任务提交和状态跟踪
|
||
|
||
### 测试验证
|
||
- [ ] 编译验证
|
||
- [ ] 端到端功能测试
|
||
- [ ] 多视频差异化验证
|
||
|
||
---
|
||
|
||
## 代码修改清单
|
||
|
||
### 核心修改
|
||
1. **BatchProduceAlignment.java**
|
||
- 新增方法:`produceSingleVideoWithOffset(materials, videoIndex, userId)`
|
||
- 新增方法:`getVideoDuration(videoUrl)` - 获取视频实际时长
|
||
- 核心逻辑:先获取实际时长,再生成随机起点
|
||
- 容错机制:根据实际时长计算范围,避免超出长度
|
||
|
||
2. **MixTaskServiceImpl.java**
|
||
- 循环生成produceCount个视频
|
||
- 每次传入不同的videoIndex,确保随机起点不同
|
||
|
||
3. **数据库结构(可选改进)**
|
||
- 新增字段:`duration INTEGER COMMENT '视频时长(秒)'`
|
||
- 上传时预处理:使用FFprobe获取时长并存储
|
||
|
||
*版本:v3.0 - 简化版(ICE自动容错)*
|