edit | blame | history | raw

本地视频语音转写 4 小时长视频优化编码方案 V002

创建人员:dev.developer.project
文件职责:在 V001 已冻结合同上,补齐正式输出提交的中断回滚,以及 4 小时验收的外部资源采样和正确性证据合同。
管理规范/模板:../../../dev-doc/编码规范.md../../../dev-doc/开发审计规范.md../../../../common/dev-doc/编码规范.md
引用文件:CODE-DESIGN-PROJECT-INFO-LOCAL-MEDIA-TRANSCRIBE-LONG-VIDEO-OPT-V001.md../../../dev-doc/开发审计报告.md../../../ai-media-processor/draft/本地视频语音转写_4小时长视频性能与资源优化需求_v0.1.md../../../dev/project-dev/transcribe_media.py../../../dev/project-dev/test/test_transcribe_media.py
记录方式:V001 的后继方案;只闭环审核 F1/F2,V001 已通过合同保持冻结,复审通过前不实现。

1. 事项、前序结论与变更边界

  • 事项:DEV-PROJECT-INFO-LOCAL-MEDIA-TRANSCRIBE-LONG-VIDEO-OPT-20260801-001
  • 前序审核:DEV-AUDIT-PROJECT-INFO-LOCAL-MEDIA-TRANSCRIBE-LONG-VIDEO-OPT-DESIGN-20260801-001=HOLD/2
  • 本版只增加两项:F1BaseException 提交回滚合同与注入测试;F2 的外部只读资源采样器和完整验收证据清单。
  • V001 已通过内容全部冻结:公开 CLI/四文件名不变;duration <= 1200 s 走原路径;长视频使用 1200 s 核心块、两侧 5 s 重叠、首尾裁边和中点唯一归属;模型一次加载、首块语言检测后复用;完整 FLAC 加最多一个临时块;不扩展 CPU/模型降级、GUI、API、数据库、批处理或断点续跑。

2. F1:正式输出提交的中断安全

2.1 实现合同

_commit_outputs 仍按 FLAC、TXT、SRT、JSON 的既有顺序逐项 Path.replace,外部签名和无覆盖检查不变,但提交区的捕获边界从 Exception 扩大为 BaseException

  1. 记录本次调用实际创建的正式目录,以及每个已经成功移动到正式位置的文件。
  2. 捕获任何 BaseException 后,对已移动文件逐项执行无覆盖目标的删除;只有本次调用创建且已为空的正式目录才删除。回滚本身采用尽力但不遮蔽原异常的清理分支,并保留回滚失败详情供普通异常错误消息使用。
  3. 若原异常是 KeyboardInterruptSystemExit 或其他非 ExceptionBaseException,回滚结束后以裸 raise 原样重抛;main 继续按现有合同把 KeyboardInterrupt 转为退出码 130,不会伪装成普通转写错误。
  4. 若原异常是普通 Exception,回滚后继续包装为现有 MediaTranscriptionError("提交输出文件失败,已回滚已写结果……");不改变 CLI 退出码或用户可见合同。
  5. 外层 transcribe_video(...): finally 继续删除相邻暂存目录;提交函数不删除原视频、不覆盖既有正式文件,也不删除调用前已经存在的输出目录。

如果某个正式文件无法回滚,错误信息必须明确列出残留路径;不得打印“未提交输出文件”或将该运行判为成功。该情况不改变本需求的正常验收路径,但防止回滚失败被静默掩盖。

2.2 中断注入测试

在系统临时目录使用四个合成暂存文件,参数化注入 KeyboardInterrupt:分别在第 123 个文件成功移动后、下一个提交动作开始前中断。每个用例必须断言:

  • 捕获到的仍是原 KeyboardInterrupt,不是 MediaTranscriptionError
  • 四个正式文件全部不存在;
  • 若正式目录由本次调用创建,则目录不存在;
  • 调用 transcribe_video 的集成清理用例中,相邻暂存目录不存在;
  • 原视频/输入暂存之外的文件未变化。

另保留一个普通 OSError 注入用例,证明仍被包装为 MediaTranscriptionError 且已移动正式文件回滚。现有 6 项测试必须全部回归。

3. F2:外部只读资源采样器

3.1 执行与归属

新增仅供真实验收使用的 dev/project-dev/test/monitor_transcribe_resources.ps1;它不进入生产 CLI,不修改被测脚本,也不读取或写入模型、源视频或正式输出内容。采样器负责启动隔离环境中的 Python 命令并把该 Python PID 冻结为 root_python_pid

  • 固定采样周期:500 ms,从根 Python 创建后开始,到根进程退出且最后一次样本落盘后结束;周期和实际采样时间均写证据。
  • 进程归属:每次通过 Windows ParentProcessId 递归构造根 Python 的后代集合;仅名称为 ffmpeg.exe 的后代计入 FFmpeg 工作集。每条样本保存根 PID、后代 PID/父 PID/进程名;不把系统中其他 Python 或 FFmpeg 进程计入。
  • RAM:记录根 Python WorkingSet64、全部归属 FFmpeg 子进程 WorkingSet64 之和,以及同一采样时点的两者合计;任务和逐块 RAM 峰值均取“同一时点合计”的最大值,不把两个不同时点的独立峰值相加。
  • GPU:每个周期读取 nvidia-smi --query-compute-apps=pid,used_gpu_memory --format=csv,noheader,nounits,只汇总根 Python 和其后代 PID;另记录 GPU 型号。系统全局显存可作诊断列,但不得用于替代本任务 PID 归属值。
  • 块号:被测程序用立即刷新的稳定行 正在转写分块 i/n 宣告当前块;采样器解析 stdout 并把最后一次已宣告的 block_index/block_total 绑定到样本。完整 FLAC 提取和模型加载阶段块号为空,仍计入任务全局峰值。
  • 临时媒体:每周期只枚举本次相邻暂存目录中的 *.flac,记录完整 FLAC、分块 FLAC的路径类别、数量和字节;临时块指标单独排除正式候选完整 FLAC,只允许同时最多 1 个分块文件且单块峰值 <500 MB

若根 PID、进程树、GPU 查询、块号关联或必填样本字段缺失,采样器必须把 sampling_valid=false 和具体原因写入汇总;该次运行不得宣告资源验收 PASS。采样器只在下述任务临时根写证据,不触碰原视频及其目录。

3.2 固定证据文件

真实 19:54 回归与 4 小时运行分别使用独立子目录,并至少保存:

  • command.json:原样参数数组、环境 Python 绝对路径、开始/结束时间、退出码、墙钟耗时、采样周期和根 PID;
  • stdout.logstderr.log:完整且按产生顺序写入;
  • resource_samples.csvtimestamp_utc,elapsed_seconds,root_python_pid,descendant_processes,block_index,block_total,python_working_set_bytes,ffmpeg_working_set_bytes,combined_working_set_bytes,task_gpu_memory_mib,temp_chunk_count,temp_chunk_bytes
  • resource_block_peaks.csv:每块的首末采样时间、样本数、Python/FFmpeg/合计 RAM 峰值、任务 GPU 峰值、临时块数量和字节峰值;
  • resource_summary.json:采样有效性、GPU 型号、全局峰值、逐块峰值、门禁值和逐项 PASS/FAIL;
  • acceptance_evidence.json:源文件前后指纹、媒体/四文件属性、时间轴与边界核验结果。

resource_summary.json 必须直接计算并保存:

ram_growth_bytes = max(last_3_block_ram_peaks) - max(first_3_block_ram_peaks)

其中 block_ram_peak 是该块内 combined_working_set_bytes 的最大样本;4 小时 12 块运行的门禁为 ram_growth_bytes <= 2 * 1024^3。同时要求任务全局 combined_working_set_bytes <= 12 * 1024^3、任务 GPU 峰值 < 10 * 1024 MiB、临时分块数量 <=1、临时分块峰值 <500 * 1024^2。不得用平均值、进程全局值或前三/后三块以外窗口替代。

4. 完整正确性与源文件不变证据

acceptance_evidence.json 的自动核验不得省略以下项目:

  1. 原始 G:\熊猫财经\20260731-155802.mp4 在任何生成/回归动作前、全部动作结束后分别记录 size_bytesCreationTimeUtcLastWriteTimeUtcSHA-256,四项逐一相等;原视频不移动、重命名、覆盖或删除。
  2. 19:54 回归必须证明 duration <= 1200 s、未出现分块进度、四文件完整、FLAC 为 16000 Hz/1 ch,并核对既有 705 段基线及 TXT/SRT/JSON 对应。
  3. 4 小时副本只位于专属任务临时根;记录生成命令、实际时长、大小和 SHA-256。其完整运行必须 exit 0、墙钟 <=100 min,四文件均存在且没有额外正式输出。
  4. 用 FFprobe 保存正式 FLAC 的 codec、采样率、声道、时长;要求 flac/16000/1,且其时长与测试媒体音轨在明确的 FFmpeg 封装容差内。
  5. JSON 必须可解析并为 large-v3/cuda/float16/vad_filter=true;每段满足 0 <= start <= end <= flac_duration。最终段按 (start,end,text) 非递减排序;SRT 序号严格为 1..N,时间合法且与对应 JSON 段一致;TXT 非空行、SRT 块、JSON 段的数量和文本逐项一致。
  6. 对每个核心边界 1200,2400,...,13200 s 自动导出 [boundary-15,boundary+15] 内的 JSON/SRT/TXT 对照行;逐边界记录无重复段、无逆序、无非法时间,并人工抽查可读性后记录 reviewer/time/result。不得只抽首、中、末三个位置。
  7. 逐块进度必须严格为 1..12 且只出现一次开始宣告;全局输出段保持连续 SRT 序号。开始前三块与最后三块必须各有样本和峰值,否则 RAM 增长结论无效。

5. 实施、测试和放行顺序

  1. 本方案复审 PASS 后才修改代码与测试。
  2. 先执行语法编译、现有回归以及 F1/分块/时间轴新增单元测试;失败不进入真实媒体运行。
  3. 再对 19:54 源视频执行完整短路径回归并生成独立证据。
  4. 最后从只读源在专属临时根生成约 4 小时副本,运行一次完整采样验收;采样或正确性证据任一必填项缺失即 FAIL,不用推断补齐。
  5. 测试媒体、四文件输出和全部证据保留到 case_analysis.media_processor 完成只读复核;不在本事项中扩展修复范围。

6. 审核问题逐项映射

审核项 V002 闭环位置 验收门禁
F1 用户中断可留下部分正式输出 第 2 节 捕获 BaseException、先回滚、非普通异常原样重抛;第 1/2/3 次成功移动后注入中断,四正式文件和暂存均不存在
F2 资源采样不可复核 第 3 节 外部采样器、500 ms、根 PID/FFmpeg 后代归属、逐样本/逐块/汇总证据、固定 RAM 增长公式
F2 正确性和源不变证据不完整 第 4 节 全部边界 ±15 s、合法范围、排序/连续序号、四文件一致、源大小/创建与修改时间/SHA-256 前后对照

7. 当前状态

PENDING_INDEPENDENT_DESIGN_REREVIEW