@speclip/pi-talking-head

Pause-aware talking-head editing and B-roll planning for Pi, exported as generic pi-media EDLs

Packages

Package details

extensionskillprompt

Install @speclip/pi-talking-head from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@speclip/pi-talking-head
Package
@speclip/pi-talking-head
Version
0.3.1
Published
Sep 10, 2026
Downloads
2,293/mo · 413/wk
Author
tapcli
License
MIT
Types
extension, skill, prompt
Size
214.9 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions/talking-head/index.ts"
  ],
  "skills": [
    "./skills"
  ],
  "prompts": [
    "./prompts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@speclip/pi-talking-head

Pi 用的口播剪辑决策包。它读取 pi-speech 风格的词级时间戳,结合整句上下文识别词间停顿、语气词和相邻重复,生成保守的 A-roll 剪辑方案,并在查找素材前把 B-roll 候选压缩成“最小充分画面”。

它不直接调用 FFmpeg。最终输出是 pi-media 的通用 timeline EDL,由 pi-media 负责素材校验、不可变 revision、渲染和验收。

为什么这样拆

  • pi-talking-head:决定哪里该剪、气口留多少、B-roll 为什么出现。
  • pi-media:执行任意来源的通用 EDL,保证路径安全、素材哈希、渲染和凭证。
  • pi-speech:提供词级 beginMs / endMs 转录。

表达整理由 Agent 判断,插件校验取舍记录和时间线的一致性;候选匹配和技术验收都不能替代语义判断或真实试听。

环境要求

  • Node.js 22.19+
  • Pi 0.84.1–0.85.x
  • @speclip/pi-media 0.4.0+(本版配套渲染器:保持主素材帧率,输出实际帧/样本时间映射与渲染凭证;需单独升级)
  • 一份带词级时间戳的 JSON 转录;格式兼容 @speclip/pi-speech

安装和验证

0.3.1:减少工具交接中的返工

  • BGM mix 按内容比较,不再因对象字段顺序不同误报回执不匹配;原有哈希算法保持不变。
  • A-roll 未变时,talking_head_apply 可传 inheritBroll: true,省略 broll/continuityPlanReceipt,添加音乐无需重新匹配和选择 B-roll。原样提交旧完整 placements 也能复用;素材、清单、视觉证据、回执和当前连续性策略仍重新校验。
  • talking_head_broll_match/select 支持 needId + continuityPlanReceipt,不必重抄需求或补写 mask-cut 派生字段。完整 need 仍支持,两者不能同时传。
  • 选窗失败返回错误代码、候选起点、已有证据帧、尾部阈值和下一步,减少查源码与猜参数。

新调用示例与恢复方式见 工具交接说明。本版保留正式 0.3.0 的渲染器要求、默认相对响度模式及项目/回执格式;不迁移另行维护的 Speclip 私有 quality 版本。

npm install
npm run check
pi install ./

工作流

1. 创建口播项目

talking_head_create {
  projectId: "launch-video",
  sourcePath: "raw/launch.mp4",
  transcriptPath: "transcripts/launch.json",
  sourceDurationMs: 93224, // 来自 media_probe
  cutThresholdMs: 500,
  headPaddingMs: 50,
  tailPaddingMs: 80
}

初剪建议只收紧至少 500ms、且没有语气词或表达边界保护信号的词间停顿。每个保留片段前留 50ms、后留 80ms,避免切掉辅音、尾音和自然气口。停顿还会分为:

  • safe:至少 400ms,通常可以切。
  • review:150–399ms,必须结合语义和画面判断。
  • unsafe:少于 150ms,默认不切。

若停顿紧邻候选语气词,或位于问号、感叹号等表达边界之后,即使超过阈值也会降级为 review,不会进入默认自动剪辑。ASR 分段边界也先进入复核。工具返回 revision 1、内容与停顿摘要、editorialStatus: draftpreviewMediaOperation;初始方案不是完成版。50/80ms 是初始参考,最终每个接点按原音和表达节奏决定。

2. 分页检查整句和编辑候选

talking_head_get {
  projectId: "launch-video",
  sentenceOffset: 0,
  sentenceLimit: 20,
  pauseOffset: 0,
  pauseLimit: 50,
  fillerOffset: 0,
  fillerLimit: 50,
  repetitionOffset: 0,
  repetitionLimit: 50,
  contentOffset: 0,
  contentLimit: 50,
  includeWords: true,
  includeTranscriptText: true
}

工具会分页返回整句上下文、停顿、语气词和相邻重复;需要判断整段结构时可显式取得完整转录文本。 等只会成为 review 候选,Agent 必须判断它是口癖、语义成分还是刻意表达,不能自动删除。文本标点只能提供低置信度且可以并存的表达线索,不等同于声学情绪识别。

只有调用这个工具时才会把这些证据放进当前会话上下文;安装 package 不会把整份转录常驻注入上下文。

3. 先整理完整表达并提交编辑计划

“气口剪辑”默认包括全片检查、重录取舍、无效起句和重复讲述清理、保留独有信息,再处理停顿。只有用户明确要求仅处理间隔时,才使用 scope: pauses-only 并在 scopeReason 中记录原要求。

新增的 contentCandidates 提示跨句重复短语、近邻相似段落和重新起句,全部为低置信度 review。它们不是完整问题清单。diagnostics 会报告首尾相接的时间戳比例和候选截断:大量零间隔不能证明原音没有句内停顿。semanticBoundary: unknown 表示 ASR 分段没有可靠语义边界证据。

Agent 阅读完整转录、检查原素材之后,先确定输出 A-roll 顺序,再调用:

talking_head_editorial_plan {
  projectId: "launch-video",
  expectedRevision: 1,
  aroll: plannedAroll,
  plan: {
    scope: "full",
    reviewedSentenceIndexes: allReviewedSentenceIndexes,
    sourceReview: { method: "text-and-visual", notes: "已比较完整表达;尚未试听成片" },
    decisions: reviewedDecisions,
    uniqueInformation: retainedUniqueInformation,
    segmentReasons: plannedSegmentReasons,
    unresolvedIssues: []
  }
}
  • decisions 每项包含 idkindretake/false-start/filler/pause/other)、actionedit/keep/review)、candidateIdsrangesreasonranges 每项为零起始且首尾包含的 startWordIndex/endWordIndex,以及 disposition: keep/remove/review。自行发现的问题可不引用候选;纯停顿候选可以没有词区间。
  • 全部候选必须有决定;每个删掉的词必须有对应 disposition;声明保留或删除的词必须与 A-roll 一致。程序检查记录和时间线的一致性,不能验证文字理由是否真实反映听感。
  • uniqueInformation 每项包含 id/startWordIndex/endWordIndex/reason,所指词必须在输出中。支持把前一遍独有内容插入后一遍完整表达。空列表表示比较后确认没有独有信息。
  • segmentReasons 每项为 segmentId/reason,必须覆盖全部输出片段。reviewedSentenceIndexes 必须覆盖全部 ASR 分段。
  • 保存返回的 editorialPlanReceipt,应用时原样提交。它绑定项目、revision、源文件、转录、A-roll 顺序和编辑计划。A-roll 改动必须重新规划;仅 B-roll/BGM 改动可以沿用当前 revision 已保存的内容审阅。
状态 输出与含义
draft 没有编辑计划,仅返回 previewMediaOperation
needs-review 有未决问题或 review 决定,仅返回预览
pauses-only-reviewed 用户明确限制的间隔处理,仅返回预览,不代表完整表达整理
content-reviewed 完整表达计划通过一致性校验,返回 mediaOperationlisteningStatus 仍为 pending

允许随时渲染明确标注的草稿小样,但不能把草稿或技术检查成功称为完整剪辑验收。旧快照仍可读取;没有编辑计划的旧版本按草稿对待,旧分析会从未变更的转录重新计算且不改写旧快照。依赖 create/get/apply 固定返回 mediaOperation 的调用方需要迁移到上述状态分支。

4. 先规划 B-roll 视觉连续性

在查找素材前,把最终 A-roll 和所有 B-roll 需求交给连续性规划工具:

talking_head_broll_plan {
  projectId: "launch-video",
  aroll: [
    { id: "hook", sourceStartMs: 50, sourceEndMs: 4120 },
    { id: "answer", sourceStartMs: 4860, sourceEndMs: 13200 }
  ],
  needs: [
    {
      id: "product-a",
      outputStartMs: 1000,
      outputEndMs: 3000,
      speechText: "接下来第一步,我们先打开设置页面",
      visualCueText: "打开设置页面",
      necessity: "essential",
      purpose: "demonstrate",
      searchTerms: ["设置", "打开"],
      reason: "展示第一步"
    },
    {
      id: "product-b",
      outputStartMs: 3300,
      outputEndMs: 5200,
      speechText: "然后第二步,在列表里选择设备",
      visualCueText: "选择设备",
      necessity: "supporting",
      purpose: "demonstrate",
      searchTerms: ["设备", "选择"],
      reason: "展示第二步"
    }
  ],
  cutCoverBeforeMs: 250,
  cutCoverAfterMs: 500
}

第一次分析时,开场、结论、个人观点、情绪和转折默认留在 A-roll。speechText 保存完整句子上下文,visualCueText 只写真正需要被看见的关键词;输出区间也只覆盖这个最短有效画面。只有“不看画面就难以理解或验证”的需求才能标为 essential,其余使用 supporting

规划结果同时返回 plan.selectionplan.coveragecontinuityPlanReceipt。规划器会先删除没有信息增量的 supporting 转场,并确保观看连续组不超过 8000ms、结尾至少保留 3000ms A-roll。两个 B-roll 之间不足 3000ms 的 A-roll 会按同一个观看连续组计算,但只有不超过 500ms 的闪屏才会被直接衔接覆盖。essential 违反连续性规则时会要求 Agent 缩短、移动或拆分画面,而不是直接查素材。plan.selection.omittedNeeds 只用于解释淘汰原因,后续严禁匹配;素材查找只能使用 plan.needs。v4 凭证会绑定最终入选区间和 A-roll 可见性策略,talking_head_apply 会重新核对。旧版快照仍可读取;v1-v3 凭证若包含 B-roll,应用前必须重新规划为 v4,不能绕过新规则。

规划结果处理两类问题:

  • 首轮稀疏化:先按 transition → establish → explain → demonstrate → evidence 的顺序淘汰 supporting 候选;mask-cut 和明确标注的 essential 不会被静默删除。完整语义只是最大上下文边界,不代表要用 B-roll 覆盖整句话。
  • B-roll 闪屏:只有入选的两个 B-roll 之间只露出不超过 500ms 的 A-roll 时,plan.needs 才会把前一个需求延长到后一个需求的起点。被淘汰的候选不会参与衔接。
  • 气口剪辑跳转:A-roll 相邻片段的源时间不连续时会生成 jumpCuts。未覆盖项标为 review,只表示 Agent 需要查看实际画面。只有确认跳点视觉上突兀时才能添加 purpose: "mask-cut",保存工作区内的审阅图片或视频,并提交 visualReview: { decision: "mask-with-broll", reviewedJumpCutOutputMs, artifactPath };规划器会计算文件哈希并绑定到 v4 凭证,应用时再次校验。否则继续保留 A-roll。
  • 时长与覆盖控制:不能把 B-roll 固定为三秒,也不能把完整句子直接变成覆盖区间。总覆盖量由 Agent 根据主题和画面信息增量判断,不设固定比例目标。硬约束是观看连续组不超过 8000ms、结尾至少保留 3000ms A-roll。这里的 3000ms 只用于判断 A-roll 是否形成有效人物窗口,不规定 B-roll 本身的固定时长。
  • 避免重复:同一素材 SHA-256 在一个时间线中只能使用一次,即使选择的是不同源时间段。每次选段还必须按实际画面填写 visualIdentity 的主体、动作、景别和角度;不同文件只要画面身份相同也会被拒绝。选择凭证会把该身份与素材、manifest 和时间戳一起哈希绑定。

是否加入 B-roll 只看当前画面能否增加理解或证据;不要为了达到某个覆盖比例而添加素材。

5. 文件名优先筛选 B-roll

只使用 plan.needs 中入选的最短有效区间扫描素材目录,绝不为 plan.selection.omittedNeeds 查找素材:

talking_head_broll_match {
  projectId: "launch-video",
  assetDirectory: "assets/broll",
  recursive: true,
  maxFiles: 1000,
  maxEntries: 20000,
  maxDepth: 12,
  maxCandidates: 5,
  candidateOffset: 0,
  needId: plan.needs[0].id,
  continuityPlanReceipt
}

每一个 plan.needs 都必须单独执行匹配。保存返回的 matchReceipt,后续只能从该次返回的候选页中选择素材。

该工具只读取目录项和文件名,不解码、不探测、不哈希视频。它最多返回限定数量的候选,并给出三种结果:

  • filename-direct:一个文件名唯一且明确命中,只检查这一条素材。
  • filename-shortlist:多个名字可能匹配,只检查返回的 shortlist。
  • visual-fallback:文件名没有有效语义,再对限定 shortlist 使用低成本联络表。

扫描同时受 maxFilesmaxEntriesmaxDepth 约束并支持取消。完整扫描时,若当前 shortlist 都不合格,可使用非空的 nextCandidateOffset 读取下一批。结果一旦截断,工具不会声称唯一命中,也不会返回可复用的分页游标;此时应缩小素材目录后重扫,避免文件系统枚举顺序造成候选漂移。

确定素材后才调用 media_probemedia_contact_sheet。长素材先低密度定位大致范围,再只对候选范围高密度抽帧;必须使用 contact_sheet_manifest.json 的真实时间戳,不能从 PNG 猜时间。找到连续可用且覆盖完整成片窗口的片段后,验证并生成 placement:

talking_head_broll_select {
  projectId: "launch-video",
  needId: plan.needs[0].id,
  continuityPlanReceipt,
  matchReceipt,
  assetPath: "assets/broll/mouse-demo.mp4",
  manifestPath: "analysis/mouse-demo/contact_sheet_manifest.json",
  selectedStartMs: 12400,
  evidenceTimestampsMs: [12400, 13900, 15400],
  fit: "cover",
  visualIdentity: {
    subject: "鼠标",
    action: "手部移动鼠标",
    shotScale: "close-up",
    angle: "top-down"
  }
}

matchselect 都会先验证 v4 continuityPlanReceipt,被首轮淘汰或被手动改回完整句子范围的 need 会在读取素材前直接拒绝。select 不接受未出现在对应 matchReceipt 候选中的素材;apply 会再按计划中的同一 need 完整复核项目、revision、计划、need 和候选素材。随后工具验证素材 SHA-256、manifest 分析范围、起始帧和覆盖片段尾部的证据时间戳,并把匹配来源与 visualIdentity 一起写入选择哈希。placement 内含选择凭证;不要手写、修改或删除其中字段。0.1.8 生成的旧 placement 不含 matchReceipt,应用前需要重新匹配并选择。

6. 用波形图和标准响度证据添加 BGM

先让 pi-media.media_audio_analyze 分析两份音频证据:原视频传入与最终 A-roll 完全一致的有序 ranges,BGM 则分析完整文件。工具会生成带 HH:MM:SS.mmm 绝对时间戳、短时 LUFS 曲线的波形 PNG,以及 EBU R128 / ITU-R BS.1770 manifest。

talking_head_bgm_plan {
  projectId: "launch-video",
  aroll,
  voiceAnalysisPath: "analysis/voice/audio_analysis_manifest.json",
  musicAnalysisPath: "analysis/bgm/audio_analysis_manifest.json",
  targetMusicBelowDialogueLu: 12
}

规划器根据真实时长返回 trimloop,并绑定目标 LU 差值,不接受音量百分比。默认使用 12 LU:这会在 sidechain 再次压低人声下方的 BGM 之前保留足够可听度。只有用户明确要求更轻的氛围底,或音乐本身特别密集、明亮时,才提高到 14–18 LU;不要把 18 LU 当成通用的“更安全”默认值。Agent 打开完整 BGM 波形 PNG 得到起止时间后,必须再调用一次 media_audio_analyze,只分析这个最终选中窗口;不能拿整首曲子的平均 LUFS 代替片段响度。随后提交视觉判断和选中窗口的 manifest:

talking_head_bgm_select {
  projectId: "launch-video",
  aroll,
  planReceipt,
  selectedMusicAnalysisPath: "analysis/bgm-window/audio_analysis_manifest.json",
  selectedStartMs: 12_000,
  selectedEndMs: 42_000,
  evidenceTimestampsMs: [12_000, 42_000],
  fadeInMs: 500,
  fadeOutMs: 1500
}

长 BGM 必须选择一个与成片等长的连续窗口;短 BGM 选择至少 500ms 的循环窗口。完整 bgm 返回值交给 talking_head_apply。渲染时 pi-media 会在循环接缝做短交叉淡化、在口播出现时 sidechain ducking,并在结尾淡出。波形只能展示振幅和结构,不能单独证明音乐情绪,仍需结合用户要求和素材信息判断。

7. 写入人工确认后的时间线

talking_head_apply {
  projectId: "launch-video",
  expectedRevision: 1,
  aroll: [
    { id: "hook", sourceStartMs: 50, sourceEndMs: 4120 },
    { id: "answer", sourceStartMs: 4860, sourceEndMs: 13200 }
  ],
  broll: [selectionA.placement, selectionB.placement],
  bgm: selectedBgm.bgm,
  continuityPlanReceipt,
  editorialPlanReceipt
}

B-roll 使用成片时间轴定位,assetStartMs 来自联络表 manifest,并永远保留主口播音轨。talking_head_apply 会重新校验连续性规划凭证、选段哈希、素材与 manifest 的 SHA-256;规划区间、画面身份或时间点被手改、素材被替换都会拒绝写入新修订。同一素材 SHA-256 或跨文件重复画面身份也会被拒绝。若两个 B-roll 之间仍存在不超过 500ms 的 A-roll、观看连续组超过 8000ms,或结尾 A-roll 少于 3000ms,也会要求重新规划。当前不支持 B-roll 相互重叠,因为未定义 z-order。BGM 的素材、两份响度 manifest、波形图、起止时间、循环策略和混音参数也会被重新校验。

8. 交给 pi-media 渲染

完整表达计划通过后,用同一个源文件创建 pi-media 项目,再把上一步的 mediaOperation 原样传入;草稿只能使用明确标注的 previewMediaOperation

project_create { projectId: "launch-video-render", sourcePath: "raw/launch.mp4" }

edit_apply {
  projectId: "launch-video-render",
  expectedRevision: 1,
  operations: [mediaOperation]
}

render {
  projectId: "launch-video-render",
  revision: 2,
  outputPath: "out/launch-final.mp4"
}

review { path: "out/launch-final.mp4" }

新渲染器默认采用第一段主素材帧率,保存累计视频帧/音频样本对齐后的 timelineTiming 与正式渲染回执;字幕重映射应使用实际输出映射。review.accepted 是技术检查结果,不代表表达、接点或听感已通过。无法直接试听时必须列出成片接点并标为待听感验收。

工具

工具 作用
talking_head_create 从视频和词级转录建立 revision 1,用探测时长保护源文件边界并生成默认 EDL
talking_head_get 读取指定 revision,分页返回整句与编辑候选,可选导出 pi-media EDL
talking_head_editorial_plan 校验全片内容取舍、独有信息和输出区间,生成绑定凭证
talking_head_broll_plan 按主题规划 B-roll,保护有效 A-roll 窗口并报告覆盖率与待审阅跳点
talking_head_broll_match 只用文件名和目录名匹配本地 B-roll,返回受限 shortlist 与匹配凭证
talking_head_broll_select 验证匹配凭证和 pi-media 联络表 manifest 后生成 placement
talking_head_bgm_plan 比较最终 A-roll 与完整 BGM 的 EBU R128 manifest,决定截取或循环
talking_head_bgm_select 验证波形图起止时间、淡入淡出和 BGM 选择凭证
talking_head_apply 写入新的不可变口播 revision,并返回 pi-media EDL

当前边界

当前版本不负责语音转录、声学情绪识别、联网素材搜索、字幕、画面理解、音频解码或渲染。它提供整句文本、编辑计划及其凭证、时间轴、B-roll 选择凭证和 BGM 编排凭证;波形、EBU R128 检测、循环、ducking、混音和最终渲染仍由 pi-media 完成。