20260429_232813-fix: video frame display pipeline — default project seed, presigned URLs, Canvas/FrameTimeline real frames, upload-parse flow
This commit is contained in:
66
工程分析/实现方案-20260429_232813.md
Normal file
66
工程分析/实现方案-20260429_232813.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 实现方案 — 视频帧显示链路修复
|
||||
|
||||
## 后端
|
||||
|
||||
### 1. `backend/schemas.py` — ProjectOut 增加 frame_count
|
||||
```python
|
||||
class ProjectOut(ProjectBase):
|
||||
id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
frame_count: int = 0
|
||||
```
|
||||
|
||||
### 2. `backend/routers/projects.py` — 三处修改
|
||||
- `list_projects`: 为每个项目计算 `frame_count = len(p.frames)`(利用 ORM relationship)
|
||||
- `list_frames`: 返回前将每个 `frame.image_url` 替换为 `get_presigned_url(frame.image_url, expires=3600)`
|
||||
- `get_project`: 同样添加 `frame_count`
|
||||
|
||||
### 3. `backend/main.py` — lifespan 默认视频种子
|
||||
启动时异步后台任务:
|
||||
- 若数据库无项目且 `Data_MyVideo_1.mp4` 存在
|
||||
- 创建 Project → 上传 MinIO → 调用 FFmpeg 解析帧 → 上传帧到 MinIO → 注册 Frame 记录 → 更新 status="ready"
|
||||
- 使用 `asyncio.to_thread()` 避免阻塞事件循环
|
||||
|
||||
### 4. `backend/routers/media.py` — upload 自动创建项目
|
||||
当 `project_id` 为空时:
|
||||
- 以文件名创建 Project
|
||||
- 将上传文件关联到该 project
|
||||
- 返回中增加 `project_id`
|
||||
|
||||
## 前端
|
||||
|
||||
### 5. `src/lib/api.ts` — 新增两个 API
|
||||
```ts
|
||||
export async function getProjectFrames(projectId: string): Promise<Array<{ id: number; project_id: number; frame_index: number; image_url: string; width: number | null; height: number | null }>>
|
||||
export async function parseMedia(projectId: string): Promise<{ project_id: number; frames_extracted: number; status: string; message: string }>
|
||||
```
|
||||
|
||||
### 6. `src/components/VideoWorkspace.tsx` — 帧加载中枢
|
||||
- 读取 `currentProject` 和 `frames`
|
||||
- `useEffect` 监听 `currentProject.id`:
|
||||
- 调用 `getProjectFrames`
|
||||
- 映射后端字段到前端 `Frame[]` 结构写入 store
|
||||
- 若帧数为 0 且项目有 video_path,自动调用 `parseMedia` 触发解析,解析完成后重新获取
|
||||
- 将当前帧 URL 通过 prop 传给 `CanvasArea`
|
||||
- 将帧列表和索引控制传给 `FrameTimeline`
|
||||
- 标题显示 `currentProject?.name`
|
||||
|
||||
### 7. `src/components/CanvasArea.tsx` — 真实帧渲染
|
||||
- 新增 `frameUrl` prop
|
||||
- `const [image] = useImage(frameUrl || '')`
|
||||
- `runInference` 使用 `frameUrl` 替代硬编码 URL
|
||||
|
||||
### 8. `src/components/FrameTimeline.tsx` — 真实帧导航
|
||||
- 从 store 读取 `frames` 和 `currentFrameIndex`
|
||||
- `totalFrames = frames.length`
|
||||
- 每个帧方块显示为 `<img>` 缩略图
|
||||
- 点击调用 `setCurrentFrame(index)`
|
||||
- 进度条范围 1..frames.length
|
||||
|
||||
### 9. `src/components/ProjectLibrary.tsx` — 完整上传链路
|
||||
上传流程改为:
|
||||
1. `createProject({ name: file.name })`
|
||||
2. `uploadMedia(file, projectId)`
|
||||
3. `parseMedia(projectId)`
|
||||
4. `getProjects()` 刷新列表
|
||||
37
工程分析/测试方案-20260429_232813.md
Normal file
37
工程分析/测试方案-20260429_232813.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 测试方案 — 视频帧显示链路修复
|
||||
|
||||
## 环境准备
|
||||
- 确保 `Data_MyVideo_1.mp4` 存在于 `/home/wkmgc/Desktop/Seg_Server/`
|
||||
- 清除数据库中现有项目(或直接删除 `backend/segserver.db` 让后端重新初始化)
|
||||
|
||||
## 测试用例
|
||||
|
||||
### TC1 — 默认项目自动出现
|
||||
1. 删除数据库文件,重启后端
|
||||
2. 打开项目库页面
|
||||
3. **预期**: 出现 "Data_MyVideo_1" 项目卡片,状态为"已就绪",帧数 > 0
|
||||
|
||||
### TC2 — 点击默认项目进入工作区能看到影像
|
||||
1. 点击 "Data_MyVideo_1" 项目
|
||||
2. 进入分割工作区
|
||||
3. **预期**:
|
||||
- 标题栏显示 "Data_MyVideo_1"
|
||||
- Canvas 区域显示视频第一帧画面(非黑屏)
|
||||
- 底部时间轴显示真实帧缩略图
|
||||
|
||||
### TC3 — 帧切换正常
|
||||
1. 在工作区中点击底部时间轴的不同帧
|
||||
2. **预期**: Canvas 画面切换到对应帧
|
||||
|
||||
### TC4 — 导入新视频完整链路
|
||||
1. 在项目库点击"导入多媒体资源"
|
||||
2. 选择任意视频文件上传
|
||||
3. **预期**:
|
||||
- 上传过程中显示 loading
|
||||
- 上传完成后新项目出现在列表中
|
||||
- 点击新项目进入工作区,能看到视频帧
|
||||
|
||||
### TC5 — AI 推理使用当前帧
|
||||
1. 在工作区选择点提示或框提示工具
|
||||
2. 在画面上点击/框选
|
||||
3. **预期**: 请求 payload 中的 `imageUrl` 为当前帧的 MinIO presigned URL,后端返回 mask
|
||||
33
工程分析/经验记录.md
33
工程分析/经验记录.md
@@ -5,6 +5,39 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-29-23-28-13 — 视频帧显示链路全修复
|
||||
|
||||
### A. 具体问题
|
||||
1. 项目库中没有默认视频 `Data_MyVideo_1.mp4`
|
||||
2. 分割工作区点击视频后画面一片黑(无影像)
|
||||
3. 导入新视频后进入工作区同样看不到视频帧
|
||||
|
||||
### B. 产生原因
|
||||
1. 后端 lifespan 没有任何自动扫描/注册本地视频的逻辑;`server.ts` 的硬编码项目对前端不可见
|
||||
2. `CanvasArea.tsx` 硬编码了一张 Unsplash 外链,没有从 Zustand store 读取帧数据;进入工作区时不调用帧列表 API
|
||||
3. 上传流程不完整:未先创建项目再带 project_id 上传,上传后未触发 `/api/media/parse` 帧提取;后端 `/frames` 返回的是 MinIO 对象名而非可访问的 presigned URL;MinIO presigned URL 的 host 为 `localhost:9000`,浏览器端无法访问
|
||||
4. 系统磁盘空间(24GB)紧张,MinIO 在提取大量高清 PNG 帧时触发 `XMinioStorageFull`,导致帧上传失败
|
||||
|
||||
### C. 解决方案
|
||||
1. **后端默认视频种子**:`main.py` lifespan 中启动后台线程,自动检测 `Data_MyVideo_1.mp4`,创建 Project → 上传 MinIO → 调用 FFmpeg 解析帧 → 注册 Frame 记录
|
||||
2. **后端帧 URL 修复**:`projects.py` 的 `list_frames` 在返回前为每个 `frame.image_url` 调用 `get_presigned_url()` 生成带签名的可访问 URL
|
||||
3. **后端 presigned URL host 修复**:`.env` 中 `MINIO_ENDPOINT` 从 `localhost:9000` 改为 `192.168.3.11:9000`,确保浏览器端可访问
|
||||
4. **后端 ProjectOut 增强**:`schemas.py` 添加 `frame_count`,`projects.py` 在查询时计算 `len(p.frames)`,供前端显示帧数
|
||||
5. **后端 upload 自动创建项目**:`media.py` 的 `upload_media` 在不传 `project_id` 时自动以文件名创建 Project 并关联视频
|
||||
6. **前端帧加载中枢**:`VideoWorkspace.tsx` 在 `currentProject` 变化时调用 `getProjectFrames`,若帧数为 0 则自动触发 `parseMedia`,获取后映射写入 Zustand store
|
||||
7. **前端 Canvas 真实渲染**:`CanvasArea.tsx` 接收 `frameUrl` prop,使用 `useImage(frameUrl)` 加载真实帧,AI 推理也使用当前帧 URL
|
||||
8. **前端时间轴真实帧**:`FrameTimeline.tsx` 从 store 读取 `frames` 和 `currentFrameIndex`,渲染真实帧缩略图 `<img>`,点击切换当前帧
|
||||
9. **前端上传链路完善**:`ProjectLibrary.tsx` 上传流程改为:创建项目 → `uploadMedia(file, projectId)` → `parseMedia(projectId)` → 刷新列表
|
||||
10. **磁盘空间优化**:将 `frame_parser.py` 的 FFmpeg 输出从 PNG (1920px, ~3MB/张) 改为 JPEG (640px, ~30KB/张),并限制默认视频只提取 100 帧,避免 MinIO 存储溢出
|
||||
|
||||
### D. 后续如何避免问题
|
||||
1. **MinIO endpoint 必须使用服务器 IP**:任何生成外部可访问 URL 的服务(MinIO presigned、后端 baseURL、WebSocket)都必须使用服务器 LAN IP,禁止 localhost
|
||||
2. **大文件/视频处理必须考虑磁盘预算**:提取帧前估算总大小(帧数 × 单帧大小),必要时降低分辨率、改格式为 JPEG、限制 max_frames
|
||||
3. **前后端数据字段必须显式映射**:后端 snake_case 与前端 camelCase 不一致时,API 层必须做转换,不能依赖隐式兼容
|
||||
4. **上传-解析-显示链路必须闭环测试**:任何涉及文件上传的功能,测试用例必须覆盖:上传 → 后端存储 → 解析 → 前端获取 → 前端渲染 的全流程
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-29-23-15-26 — 上传/WS/项目库三 Bug 联修
|
||||
|
||||
### A. 具体问题
|
||||
|
||||
26
工程分析/需求分析-20260429_232813.md
Normal file
26
工程分析/需求分析-20260429_232813.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 需求分析 — 视频帧显示链路修复
|
||||
|
||||
## 问题背景
|
||||
用户报告三个关联问题:
|
||||
1. 项目库中没有默认视频 `Data_MyVideo_1.mp4`
|
||||
2. 点击默认视频进入分割工作区后,画面区域一片黑(无影像)
|
||||
3. 导入新视频后进入工作区,同样看不到视频帧
|
||||
|
||||
## 根因分析
|
||||
| 问题 | 根因 |
|
||||
|------|------|
|
||||
| 无默认项目 | 后端 lifespan 无自动种子逻辑;`server.ts` 硬编码项目但前端根本不请求 Express |
|
||||
| 工作区黑屏 | `CanvasArea.tsx` 硬编码 Unsplash 外链,未从 store 读取帧;进入工作区不调用帧列表 API |
|
||||
| 导入后黑屏 | 上传未关联 project_id;上传后未触发 `/api/media/parse` 帧提取;后端 `/frames` 返回的是 MinIO 对象名而非可访问 URL |
|
||||
|
||||
## 需求拆解
|
||||
| 编号 | 需求 | 优先级 |
|
||||
|------|------|--------|
|
||||
| R1 | 后端启动时自动检测并注册 `Data_MyVideo_1.mp4` 为默认项目 | P0 |
|
||||
| R2 | 后端 `/api/projects/{id}/frames` 返回 presigned URL 而非对象名 | P0 |
|
||||
| R3 | 后端 `ProjectOut` 增加 `frame_count` 供前端显示 | P1 |
|
||||
| R4 | 前端 `VideoWorkspace` 进入时自动获取帧列表并写入 store | P0 |
|
||||
| R5 | 前端 `CanvasArea` 从 store 读取当前帧 URL 并渲染 | P0 |
|
||||
| R6 | 前端 `FrameTimeline` 从 store 读取真实帧数据,支持切换 | P0 |
|
||||
| R7 | 前端 `ProjectLibrary` 上传流程:创建项目 → 带 project_id 上传 → 触发解析 | P0 |
|
||||
| R8 | 后端 upload 接口支持无 project_id 时自动创建项目 | P1 |
|
||||
Reference in New Issue
Block a user