03:00:00省 50%领取

技术配方

按时间基准抽帧:CFR、VFR 与精确帧索引验收

下载 Python 与 FFmpeg 抽帧配方,运行原创 CFR/VFR 夹具,查看有理数 PTS 表、三种选择规则、PNG 像素哈希与 16 项实际测试。

明确采用 at、after 或零起帧索引,从真实解码帧中取图,再验收时间戳与 PNG 像素。任意输入秒数不一定对应已有的一帧。

下载完整说明与检查表
原创 VFR 取帧时间对应示意
原创时间示意图与小型 RGB 合成夹具,附实际 PNG、framehash 日志和 16 场景回执。本配方是本机 FFmpeg 测试,不是模型生成结果。

01 · 环境与输入

  • 下载完整包并解压到自己控制的本地目录。配套视频是原创合成时间夹具,不是 H3 生成结果。

  • 准备 Python 3 与已有 FFmpeg。本次实跑环境为 macOS arm64、Python 3.12.3、FFmpeg 6.0,不需要 ffprobe 或额外 Python 包。

  • FFmpeg 不在 PATH 时设置 FFMPEG_BIN。选择新的输出目录,夹具脚本与抽帧脚本都拒绝覆盖。

  • 处理自己的片段前,明确时间相对第一帧解码 PTS,还是别的时间原点;同时决定采用显示朝向或原始朝向。本脚本使用第一帧 PTS 相对秒数。

02 · 执行与检查

  1. 01 · 先建立解码帧时间表

    先跑夹具,再处理正式片段。CFR 有 10 帧,时间为 0.00、0.10……0.90 秒;VFR 有 8 帧,为 0.00、0.04、0.12、0.28、0.32、0.60、0.64、0.92 秒。实测两者 time_base 都是 1/1000。CSV 与 inventory JSON 将每个零起解码索引对应到整数 PTS;秒数为 PTS 乘时间基准,不用平均 FPS 推算 VFR 位置。

    export FFMPEG_BIN=/YOUR/INSTALLED/ffmpeg
    python3 run-fixtures.py --out-dir ./my-run
  2. 02 · 明确 at、after 或精确解码索引

    at 取目标之前或恰在目标上的最后一个帧起点,after 取之后或恰在目标上的第一个起点。VFR 目标 0.20 秒时,at 取索引 2 的 0.12 秒,after 取索引 3 的 0.28 秒。--rule frame --index 3 同样取 0.28 秒;精确索引不代表任意时间都能精确匹配。脚本从起点解码,时间超过首末 PTS 范围会拒绝,不猜测尾帧显示时长。

    python3 extract-frame.py --input executed/vfr.mkv --rule at --time 0.20 --out ./at.png
    python3 extract-frame.py --input executed/vfr.mkv --rule after --time 0.20 --out ./after.png
    python3 extract-frame.py --input executed/vfr.mkv --rule frame --index 3 --out ./index3.png
  3. 03 · 分开验收原始 PTS 与保存像素

    查看 PNG 的 selection 回执与 inventory 附件,不只看文件名。源帧 framehash 保留时间戳,使用 passthrough 帧调度并明确时间基准;#tb 标头规定整数 PTS 的单位。脚本还会重新解码 PNG,确认 RGB 哈希与同朝向策略下选中的源帧一致。framehash 的数据包 duration 列不用于推测 VFR 显示停留;文件 SHA-256 核对字节,解码 RGB SHA-256 核对这次格式转换和朝向下的像素。

    python3 -m json.tool at.selection.json
    python3 -m json.tool at.inventory.json
  4. 04 · 选择旋转策略,保留失败检查

    display 使用默认自动旋转,raw 在输入前应用 -noautorotate。实际 90 度元数据夹具得到 display 180×320、raw 320×180;还验证了起点为 2 秒的片段按相对时间取帧。这里没有覆盖所有显示矩阵、镜像或损坏时间戳。把朝向策略保留在回执里,遇到歧义停止,不通过强制 CFR 或插帧掩盖。

    python3 extract-frame.py --input executed/rotation-90.mp4 --rule frame --index 0 --orientation display --out ./display.png
    python3 extract-frame.py --input executed/rotation-90.mp4 --rule frame --index 0 --orientation raw --out ./raw.png

03 · 验证输出

  • 下载脚本实跑通过回执中的 16 个命名场景,并记录当次 Python、FFmpeg 版本与两份实际执行脚本的准确 SHA-256。
  • CFR/VFR 表同时记录解码索引、整数 PTS 与有理数时间基准,包含每处 VFR 时间间隔。
  • 0.20 秒的 VFR at/after 输出分别对应表中的不同索引与 PTS,没有用合成的 0.20 秒帧代替。
  • 保存 PNG 的解码 RGB 哈希与同一朝向策略下的选中源帧一致。
  • 负时间、超过末帧 PTS 的时间和越界索引会报错停止。
  • 旋转尺寸符合明确的 display/raw 策略;非零起点采用第一帧 PTS 相对时间。
  • 回执没有把本机夹具通过扩展成模型画质、全部编码、全部显示变换或所有真实时间点都已验证。

查看实际执行记录

04 · 边界与排错

取出的画面比输入秒数更早或更晚。
先看选择规则与真实 PTS 表。目标位于两个 VFR 时间戳之间时,at 与 after 本来就可能取不同帧,不把报告时间改成输入时间。
PNG 横竖方向不对,或宽高交换。
先明确希望的显示朝向,对比 display 与 raw。PNG 像素使用选定策略,不假定视频旋转 side data 在 PNG 中还会作为观看指令生效。
抽帧脚本拒绝视频末尾的一段时间。
本策略止于最后已知 PTS,不推测尾帧显示多久。可直接选择最后帧索引,或另外建立其显示时长后再扩展规则。
按 FPS 算出的索引与帧表不一致。
以解码呈现顺序与有理数时间基准为准。平均 FPS、数据包和关键帧属于不同编号体系,VFR 更不能混用。
另一套 FFmpeg 上哈希不同。
先在相同像素格式与朝向下比较解码 RGB,再比较压缩 PNG 的文件字节。记录新工具版本,配套回执只证明其中记录的运行。

把 PNG、选择回执与源帧时间表一起保存。明确规则、真实时间戳与像素检查让取帧可以复核,但不会制造源视频从未包含的时间点帧。

文件与依据

查看成片检查与交付资料