会议录屏 PPT 页面提取 MVP 编码方案 V001
创建人员:dev.developer.project
文件职责:冻结单视频 PPT 稳定页面发现、原分辨率 PNG/PDF 输出、内存边界与失败提交的最小实现合同。
管理规范/模板:../../../编码规范.md;../../../开发审计规范.md;../../../../common/dev-doc/编码规范.md。
引用文件:../../../ai-media-processor/draft/会议录屏PPT页面提取方案_v0.1.md;../../../dev/project-dev/extract_ppt_slides.py;../../../dev/project-dev/test/test_extract_ppt_slides.py。
记录方式:重型单工具 V001;独立方案审核 PASS 前不开始实现,审核只检查本方案既定最小范围。
1. 事项与边界
- 事项:
DEV-PROJECT-INFO-MEETING-RECORDING-SLIDE-EXTRACTION-MVP-20260801-001。
- 输入:一个本地视频路径;可选
--output 指定一个尚不存在的正式输出目录。
- 输出:
pages/slide_001.png... 与 <视频名>.slides.pdf;不输出 JSON、manifest、PPTX、OCR 或额外业务产物。
- 明确排除:人像识别/裁剪/遮挡、PPT 区域裁剪、会议界面移除、转写联动、批处理、GUI、API、数据库、Skill、GPU 依赖和大量阈值参数。
- 实现文件限于一个脚本、一个测试文件和一页用法;不修改现有转写工具或其他开发目标。
该事项虽为单脚本 MVP,但包含最长约 4 小时的 FFmpeg 子进程、流式资源约束和正式目录原子提交,按重型最小方案先审后做;这不授权增加模块或产品能力。
2. 公开调用与依赖
python dev/project-dev/extract_ppt_slides.py "D:\videos\会议录屏.mp4"
python dev/project-dev/extract_ppt_slides.py "D:\videos\会议录屏.mp4" --output "D:\results\会议录屏.slides"
- 运行依赖:Python 3.12、FFmpeg/FFprobe、Pillow、NumPy;均通过 PATH 或当前 Python 环境解析,不硬编码个人路径。
- 默认输出为输入同目录的
<stem>.slides/。输入必须是普通文件;输出存在或为符号链接时在任何 FFmpeg 动作前失败。
- 所有子进程使用参数数组,不经 shell;设置 UTF-8 文本解码并支持中文路径。
3. 两阶段最小算法
3.1 稀疏流式扫描
- FFprobe 读取第一条视频流的宽、高、时长;缺视频流、无效尺寸或时长时明确失败。
- 单个 FFmpeg 进程以
1 fps 解码,按原宽高比缩到不超过 320 px 宽并输出 rgb24 rawvideo 到管道。
- Python 每次只读取一个固定大小帧,保留当前帧、前一帧、当前稳定段摘要和已确认页面的小尺寸特征;不建立采样帧列表、不落盘扫描缩略图。
- 两个相邻样本的归一化平均绝对差和显著变化像素比例同时低于内置阈值时视为稳定;至少两个相邻样本稳定后才形成候选。单个过渡样本不会形成页面。
- 一个稳定段内记录清晰度最高的时间点;清晰度使用灰度梯度方差,只保存时间、紧凑灰度特征、感知哈希和内容分数。
3.2 动画合并与全局去重
- 近似重复:候选与任一已确认页面同时满足低感知哈希距离和低像素差时跳过;返回旧页不改变首次出现顺序。
- 同页动画:候选只与最近一个逻辑页面比较;若主体结构近似、变化区域有限且内容分数不显著下降,则用后一个稳定候选替换该页的提取时间和特征,但保留原列表位置。
- 新页面:不满足上述两类时按首次出现追加。
- 阈值全部是脚本内有名称的常量,不公开为大量 CLI 参数;真实业务视频出现具体误判后才做单一阈值修复。
- 页面内视频、动态仪表盘、模板高度相似但正文变化很小的页面属于启发式已知限制,不以 OCR 或人像处理绕过。
3.3 原分辨率提取与 PDF
- 扫描完成且至少有一个候选后,逐候选调用 FFmpeg 从原视频准确定位并输出一张无损 PNG;每张以 Pillow 核验尺寸严格等于 FFprobe 原始宽高。
- PDF 生成器逐张读取 PNG、转换 RGB、压缩后写入标准 PDF 图像页;同一时刻只保留一张原分辨率图像,不把全部页面图像装入内存。
- PDF 页尺寸与对应图像宽高一致,页对象和 PNG 顺序均为候选首次出现顺序;测试用
pdfinfo 核对可打开和页数。
4. 输出提交与失败安全
- 在正式输出的同级目录创建唯一暂存目录,完整写入
pages/ 和 PDF;只有 PNG 数量大于零、连续编号、尺寸一致且 PDF 完成后,才用一次目录重命名提交正式结果。
- 正式输出及暂存写入期间绝不打开源视频为写模式,不移动、重命名或删除源视频。
- 主流程用
try/finally 清理本次暂存目录;FFmpeg 失败、无稳定页、PNG/PDF 失败、用户中断或提交失败均不留下正式目录。若目录重命名已经成功,则正式目录已完整,不做破坏性回滚。
- 不覆盖既有输出;并发创建冲突以明确错误结束。
- 进度输出仅包含扫描帧计数、确认页面数、原图提取进度和 PDF 阶段,不生成 JSON 日志。
5. 测试与验收门禁
5.1 离线单元测试
- 稳定段需要连续样本,单个过渡帧被过滤。
A -> B -> A 全局重复只保留 A,B 且顺序不变。
A初始 -> A完整 小区域增量合并为一页并采用后一个稳定时间;内容明显减少时不覆盖较完整状态。
- PDF 逐页写入可被
pdfinfo 打开,页数等于 PNG 数,PNG 编号连续且保持原尺寸。
- 既有输出、缺失视频、无稳定页和注入 FFmpeg/PDF 失败均明确失败;暂存与正式目录不存在。
- 中文输入/输出路径通过。
5.2 合成视频 CLI 冒烟
用 Pillow 生成 640x360 的受控序列并由 FFmpeg 编码成短视频:稳定 A初始、单帧过渡、稳定 A完整、单帧过渡、稳定 B、返回 A完整。完整 CLI 必须:
- exit=
0;输出 2 张 640x360 PNG,顺序为 A完整,B;返回页不重复;过渡帧不输出;
- PDF 可打开且页数=
2;
- 原视频大小、SHA-256、创建/修改时间前后不变;
- 不产生 JSON、缩略图或正式目录外业务文件。
5.3 资源有界证据
- 代码检查确认扫描函数逐帧消费 FFmpeg stdout,不返回或累计全部采样帧;已确认页面只保留小尺寸特征。
- 测试以长迭代器喂入大量重复扫描帧,确认候选数量保持常数且不创建帧列表;CLI 冒烟记录 Python 峰值 RSS 作为基线,不设复杂性能矩阵。
- 首次真实 PPT 录屏的页面可读性、动画合并和阈值效果由请求方后续验收;本轮不等待真实样本,也不自行使用未授权业务视频。
6. 实施与审核顺序
- V001 独立方案审核 PASS 后实施脚本、测试和用法。
- 先做 py_compile/单元测试,再做合成视频完整 CLI、安全与 PDF 检查。
- 完成需求-代码逐项自检后,向
dev.reviewer.project 提交一次实现审核;PASS 后再交 case_analysis.media_processor 做普通功能验收。
- 本轮只确认离线 MVP;真实录屏验收发现具体问题后才允许最小修复,不扩展功能。
7. 当前状态
PENDING_INDEPENDENT_DESIGN_REVIEW