Communitygithub.com

Istok-Media-Tool/basic

将一段原始的单次拍摄视频或音频(如一次连续拍摄的讲话片段,包含大量重拍、错误和结巴)根据预期的脚本剪辑成干净、最终的成品,使用 Whisper 转录结合静音检测来确定帧精确的剪辑点,然后通过 ffmpeg 进行组装。

basic 是什么?

该技能专为处理包含多次重拍、错误和结巴的连续拍摄素材而设计,旨在根据脚本自动生成精确的剪辑方案。它通过 Whisper 进行初步转录以了解大致内容,但特别强调依赖静音检测(detect_bursts)来获取更准确的语音边界,从而避免 Whisper 时间戳不准确的问题。随后,它对每个语音片段进行独立转录,以揭示隐藏在完整文件转录中的虚假开始。整个流程包括生成剪辑计划、经过用户确认后使用 ffmpeg 进行组装,并提供可选的紧密节奏调整。此技能适用于任何基于命令行的 AI 代理(如 Claude Code、Codex、Cursor),并提供了详细的操作步骤、脚本说明和平台注意事项。它支持 macOS 和 Linux 环境,并内置了检查机制以确保输出没有时间戳空洞或冻结帧。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Istok-Media-Tool/video-edit-skills/tree/HEAD/video-edit/skills/basic

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

基础视频剪辑(将原始素材剪辑以匹配脚本)

为何需要此技能

仅凭转录文本,从包含多次重拍的连续拍摄中准确剪辑非常困难。首次手动应用此流程时,如果仅信任 Whisper 从单次完整文件处理中得出的片段时间戳,得到的剪辑方案中某个句子看起来需要 22 秒才能说完,但实际上只需 5.75 秒;Whisper 将 16 秒的静音和两次虚假开始合并到了该片段中。如果信任该转录进行剪辑,最终视频将充满无效的静音片段。

请记住这个教训:此工作流程的目标是精确的剪辑点,而仅靠 Whisper 无法可靠地提供包含停顿、重拍或重复句子的素材的精确剪辑点。 以下所有步骤都是为了弥补这一点。

工作流程

1. 分析源文件

在进行任何其他操作之前,先探测视频的基本信息:

ffprobe -v error -show_entries format=duration,size -show_entries stream=width,height,codec_name,codec_type,avg_frame_rate -of default=noprint_wrappers=0 "<video>"

提取单声道 16kHz 音频用于转录(Whisper 需要此格式,且处理起来比完整视频更小/更快):

ffmpeg -y -i "<video>" -vn -ac 1 -ar 16000 audio.wav -loglevel error

如果用户提供了脚本/场景文件(txt、rtf、docx 等),现在就读取它,以便与预期的内容进行比较。.rtf 文件通常需要 textutil -convert txt -stdout file.rtf(macOS)来获取干净的文本,而不是原始的 RTF 标记。

2. 通过首次转录获得大致概览

whisper audio.wav --language <lang> --model medium --output_format txt --output_format srt --output_dir .

仅使用此结果来了解整体内容以及录音中不同台词的大致位置。不要 将其片段开始/结束时间用作真正的剪辑点——请参阅上面的“为何需要此技能”。如果录音很长,此步骤可以与步骤 3 并行运行(或跳过,以便进行步骤 3)。

3. 通过静音检测找到真实的语音边界

这一步才是真正重要的。运行:

python3 scripts/detect_bursts.py audio.wav > bursts.txt

这将提供采样精确的语音“片段”(检测到的静音的互补部分),并过滤掉几乎总是呼吸声而非单词的低于 0.5 秒的微小片段。信任这些时间戳,而不是 Whisper 的。

合理性检查:正常语速大约每秒 2-3 个单词。如果某个片段的时间长度与预期包含的文本量严重不符,则说明有问题。

4. 单独转录每个语音片段

python3 scripts/transcribe_bursts.py audio.wav bursts.txt --language <lang> --model medium

这将加载一次 Whisper 模型,然后将每个片段作为独立的短片段进行转录,使用 condition_on_previous_text=False 和 temperature=0。这个组合正是用来发现隐藏的重拍的:一个在完整文件处理中被标记为一个干净句子的片段,一旦被隔离出来,可能包含一个立即跟着正确内容的虚假开始——这在第一轮转录中完全不可见,它悄悄地合并或抑制了重复内容。

预期一个有明显重拍的录音会产生更多的语音片段而不是脚本中的行数——通常每个句子会有多个虚假开始的片段,尤其当说话者在某个句子处结巴最多时。这是正常的;这正是此步骤的目的。

5. 构建剪辑方案——在开始剪辑之前务必获得确认

将转录的语音片段根据它们属于脚本的哪一行/思想进行分组。对于每一行,通常你会看到以下一种情况:

  • 一个干净、完整的片段——很简单,那就是可用的素材。
  • 一个虚假开始后跟一个完整片段——使用完整的那个,丢弃片段。
  • 多个措辞相同的完整、完整素材——这些是真正的表演重复;仅凭转录无法判断表现/情感,因此使用合理的默认值(例如,后面的尝试,因为人们通常会进入状态)进行剪辑,并在 Dublovi 模式(如下)中让用户选择。
  • 多个措辞确实不同的完整素材(说话者每次即兴发挥不同)——这是一个真正的内容选择,而不仅仅是表演选择。明确向用户提供备选方案,而不是默默选择——他们可能更喜欢自己即兴的短语而不是脚本中的,反之亦然。

对于这两种重复素材,用户与你一起通过观看它们来决定:这些素材将进入计划编辑器的 Dublovi 模式(两个素材并排,左/右,“Ovaj je bolji”),在每个视频上都是如此。不要在聊天中描述素材,也不要在那里问“dubl 1 ili 2”。使用你的默认选择进行剪辑,将每个重复列在 takes_src.json(计划编辑器技能,“Dublovi”)中并打开编辑器;如果用户选择另一个,则在剪辑中替换该素材。

然后向用户写出完整的方案:为每行保留的确切文本、其精确的时间戳范围,以及正在剪辑的内容和原因(静音、虚假开始、丢弃的备选素材)。在运行任何剪辑命令之前等待明确确认。这不是形式——编辑视频感觉具有破坏性,且不观看输出很难抽查,因此用户需要查看方案以在渲染时间花费之前发现问题(选择了错误的素材、接续在一起的两个句子听起来不自然),而不是之后。

如果用户询问总时长,只需将所有保留片段的时长相加——但需说明在添加了剪辑之间的填充后(第 6 步),实际的最终时长会稍长一些。

6. 剪辑和组装

将确认的选择(每行一个 start<TAB>end 对,按输出顺序)写入文件,然后:

scripts/cut_and_concat.sh "<source_video>" selections.txt final_output.mp4

在手动运行或改编 cut_and_concat.sh 之前,先阅读其顶部的注释块——它记录了常见问题(ffmpeg 从 bash 循环中窃取 stdin,淡出会吞掉单词的最后一个音节,为什么每个片段在拼接之前需要单独重新编码),如果从头重写而不是直接使用脚本,这些问题很容易被悄无声息地重新引入。

默认设置:每个切入前填充 0.15 秒,每个切出后填充 0.35 秒,仅 5 毫秒淡入(无淡出),每个片段重新编码为 h264 (crf 18) + aac 192k,每个片段剪裁到精确的帧数,其音频也修剪到相同的长度,使用 ffmpeg 的 concat 滤镜(而不是 concat 解复用器——请参阅下面的“检查每个渲染”)进行连接。如果需要为特定剪辑进行不同处理(例如,带有大量尾随呼吸声的句子可能需要更多的 POST_PAD),可通过 PRE_PAD、POST_PAD、CRF、FADE_IN 环境变量覆盖这些值。

如果经过第一轮处理后某个句子的最后一个字母听起来仍然被剪断,这几乎总是意味着该特定剪辑的 POST_PAD 太紧了(说话者的拖尾辅音比静音检测器的阈值捕捉到的更长)——为该片段增加 POST_PAD,而不是使用淡出,那只会让紧的剪辑听起来更糟。

紧促的节奏——用户的标准

默认填充(0.15 秒 / 0.35 秒)在每个剪辑处留下大约 0.5 秒的停顿。用户发现这打破了节奏("mora biti sve zbijenije i tečnije"),因此对于他们的视频,在组装之前要收紧每个剪辑:

python3 scripts/tighten_cuts.py audio.wav selections.txt tight.txt
PRE_PAD=0.04 POST_PAD=0.08 scripts/cut_and_concat.sh "<source_video>" tight.txt final_output.mp4 <abs workdir>

它将每个边缘移动到语音真正开始/结束的位置(-42 dB),并分割任何超过 0.30 秒的内部停顿。在真实的剪辑中,这将把 1:48 的时长缩短到 1:41,且每个单词都完好无损。之后,通过转录结果进行检查:保留文本的每个单词必须仍然存在。任何放置在旧剪辑上的内容(特写、字幕、标题)的时间必须通过源时间重新映射,而不是通过固定的偏移量进行移动。

当两个单独剪辑的部分被连接在一起时(例如,主部分 + 片尾),连接点应取自第一个部分的实际帧数(nb_read_frames / fps),而不是其容器时长:在真实的剪辑中,0.04 秒的差异导致一张广角镜头的帧出现在一个特写镜头和片尾之间,并且片尾的第一个字幕提前了一帧出现。在连接点结束的叠加层必须精确运行到那一帧。

检查每个渲染——在发送任何内容之前

在真实的剪辑中,用户在 1:05 处发现了一张广角镜头的帧("to ne sme da nam se dešava")。真正的原因更严重:旧的 cut_and_concat.sh 使用 concat 解复用器连接片段,这根据其容器时长和 AAC 启动/填充来放置每个片段,而不是根据其实际帧数。这在 10 个剪辑处留下了一个 80 毫秒的空洞(2 帧)——重新编码后屏幕上出现冻结帧——并且音频缓慢地领先于画面,因此基于它的每个叠加层和字幕都偏离了一两帧。修复后的脚本(其头部的陷阱 #4)将每个片段剪裁到精确的帧数,将其音频填充/修剪到完全相同的长度,并使用 concat 滤镜进行连接。

以下由此衍生出的规则:

  • 在发送给用户之前,对每个渲染后的视频(剪辑、缩放、添加字幕、最终版)运行检查器:

    python3 scripts/check_video.py final.mp4 --ass captions.ass --edges edges.txt --cuts joins.txt
    

    joins.txt = 最终时间线上剪辑列表中每个连接点的位置(每个部分的 durs.txt 的累计和,以及部分边界)。始终传递它:相同构图内的跳剪得分远低于场景阈值,因此没有它,检查器在真实的剪辑中(1:15)遗漏了在连接点前 3 帧处的一个缩放变化——用户看到的一个 3 帧的闪烁。

    它报告时间戳空洞、单个冻结帧、音频与视频长度不一致、仅几帧长的镜头、持续时间少于 0.4 秒或开始/结束于画面剪辑 1-3 帧内的字幕,以及距离剪辑 2-5 帧的叠加层边缘(edges.txt:每行一个特写 / B-roll 的开始或结束,以秒为单位)。退出码为 1 表示发现了问题:修复并重新渲染;只有“OK - 未发现问题”才能发送。

  • 每个剪辑位置都取自帧数(nb_frames / fps,或脚本写入的 durs.txt),绝不能取自 format=duration。

  • 当剪辑发生变化时,通过源时间(旧时间线 -> 源 -> 新时间线)移动特写、字幕和标题,绝不能通过固定偏移量。

  • 在剪辑处结束的叠加层(片尾前的特写)精确运行到该剪辑的帧;字幕与其片段一起结束。

  • 将位于连接点 6 帧范围内的每个缩放变化、特写结束和字幕边缘对齐到该连接点上(仅当连接点正好在特写开始之前时才对齐)。相隔几帧的两个画面变化总是看起来像是故障;在一帧内则看起来像是一次剪辑。

  • 给最后一个片段一个小的尾部(约 0.5 秒),这样最后一个字幕和单词就不会被切断。

7. 交付

cut_and_concat.sh 的主输出是高质量版本(在 crf 18 下重新编码)。如果它需要通过有文件大小限制的工具,请为此目的制作一个二次压缩副本,而不是压缩主版本:

ffmpeg -nostdin -y -i final_output.mp4 -c:v libx264 -preset slow -crf 24 -pix_fmt yuv420p -c:a aac -b:a 128k final_output_share.mp4 -loglevel error

如果用户还想要动态的特写/缩放裁切以增加动态效果,请参阅 zoom 技能——它可以接受此技能的输出并重新剪辑,或者(更高效地)你可以通过将 zoom 技能的每个片段滤镜链直接写入 selections.txt 的第三列,然后一次性运行 cut_and_concat.sh 来将两者合并。如果需要的话,旋转到垂直方向是单独处理的(不属于任一技能)——询问用户,因为并非总是需要。

平台说明

  • macOS 默认没有 GNU timeout——不要将 ffmpeg 调用包装在其中一个中;它们对于短片段来说很快,并且调用工具自己的后台/超时处理涵盖了步骤 2 中的长时间完整文件处理。
  • 任何时候你自己编写一个 while read ...; done < file 循环(而不是使用捆绑的脚本)并在其中调用 ffmpeg,请在 ffmpeg 调用中添加 -nostdin 并将其 stdin 重定向到 /dev/null——否则 ffmpeg 会消耗循环 read 所需的行,从而破坏时间戳与应用到的迭代之间的对应关系,且没有明显的错误。

剪辑之后:缩放和字幕通过计划编辑器进行

当剪辑后的视频还需要进行缩放/特写(zoom 技能)和/或添加字幕(subtitle 技能)时,用户的标准是使用 plan-editor 技能:一个交互式的点击单词工具,用户在其中与你一起决定缩放,并在同一个句子中修正字幕文本("Izmeni tekst")。保留此技能方案中每个剪辑的保留文本——它将用作该工具的 sentences.txt(每个剪辑片段一行,与 selections.txt 顺序相同)。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相关技能