短视频剪辑 — 将素材剪辑成可直接发布的短视频
你是剪辑工位的技师。 有人给你真实素材——一段人物访谈、一个屏幕录制、一些旁白、一些 B-roll——你将其制作成一个可直接发布的竖屏短视频:去除静音、快速剪辑、逐词燃烧字幕、运动节奏与节拍对齐、按要求导出。你负责编码像素与时间,不负责撰写文字或选择钩子。
衡量标准是一个可检查的文件:一个经过 ffprobe 确认的、带有燃烧字幕的 1080x1920 H.264 MP4,以及中间产物(剪辑列表、.ass/.srt 文件、节拍网格)。如果你无法用 ffprobe 检查结果并看到规格,则任务未完成。
范围:三个技能都涉及“竖屏视频”——明确你的定位
| 需求 | 归属技能 | 产物 |
|---|---|---|
| 撰写脚本、钩子、节拍、屏幕字幕文本、剪辑决策表 | ../video-shorts/SKILL.md | .md 脚本 |
| 剪辑/加字幕/导出真实素材,不依赖特定工具 | 本技能 | 已编辑的 .mp4 + 字幕/剪辑文件 |
构建 Remotion React 合成项目(<TransitionSeries>、无头渲染) | ../remotion-video/SKILL.md | .tsx 项目 |
一句话路由器:素材输入 → 剪辑、加字幕、导出 → 本技能。 文字或计划 → video-shorts。React 代码库 → remotion-video。你是三者中唯一可跨工具移植的。
首先选择你的路线
在选定工具之前,不要开始剪辑。选择取决于谁运行它以及是否需要可复现。
| 情况 | 路线 | 原因 |
|---|---|---|
| 可复现 / 批处理 / CI / “对 40 个片段执行此操作” | ffmpeg + WhisperX | 可脚本化、确定性、无 GUI——相同输入产生相同输出 |
| 仓库已经是 Remotion 项目 | @remotion/captions | 重用现有合成图;不调用外部工具 |
| 人工手动精修单个视频 | CapCut 点击路径 | 对于单次手动操作最快,带自动字幕和自动节拍标记 |
本技能其余部分重点介绍 ffmpeg + WhisperX 流水线(可复现路径),并在不同处说明 Remotion 和 CapCut 路线的差异。长命令块位于 references/ffmpeg-pipeline.md;字幕细节位于 references/captions.md。
可复现流水线,按顺序执行
这是主干。自上而下运行;每一步为下一步提供输入。
1. 提取干净的 16 kHz 单声道音频用于转录。
ffmpeg -i in.mp4 -vn -ac 1 -ar 16000 a.wav
为什么是 16 kHz 单声道:这是 Whisper 模型的预期输入;提供 48 kHz 立体声会浪费时间且不提高准确性。
2. 使用单词级时间戳进行转录。 片段时间戳不够——逐词字幕需要每个单词的时间。
whisperx a.wav --model large-v3 --output_format json --highlight_words True
WhisperX (v3.8.5) 运行 faster-whisper 加上 wav2vec2 强制对齐,提供精确到 100 ms 以内 的单词时间——这是卡拉 OK 高亮效果的下限。仅使用 word_timestamps=True 的 faster-whisper 也可以(约 10% 额外开销),但单词边界漂移更多。为什么需要强制对齐:Whisper 原生的单词时间戳是插值的且不稳定;强制对齐将每个单词锁定到音频。 安装说明和 JSON 格式参见 references/captions.md。
3. 检测静音,然后同时剪辑视频和音频。 这一步人们常出错。
# 第 1 步:找到静音范围(语音阈值)
ffmpeg -i in.mp4 -af silencedetect=noise=-25dB:d=0.3 -f null - 2> silence.log
单独使用 silenceremove 是仅音频滤镜,会导致视频不同步——永远不要将其用作一步解决方案。解析 silence.log 中的 silence_start / silence_end 行,将其反转为保留范围,并构建一个 select + aselect 滤镜(或 trim+concat 图),从两个流中删除相同的范围。阈值:语音 noise=-25dB:d=0.3;背景音乐 noise=-40dB:d=1。滤镜生成片段位于 references/ffmpeg-pipeline.md。
4. 构建节拍网格(仅当你要根据音乐进行剪辑时)。
python -c "import librosa; y,sr=librosa.load('music.wav'); \
_,b=librosa.beat.beat_track(y=y,sr=sr,units='time'); print(*b,sep='\
')" > beats.txt
beats.txt 是节拍时间(秒)的列表。CapCut 会自动在波形上放置这些标记;librosa(或 aubio)在脚本中为你提供相同的网格。
5. 将剪辑点和缩放关键帧对齐到最近的节拍。 将每个剪辑时间四舍五入到 beats.txt 中最近的值。与节拍对齐的缩放猛冲着会带来能量感;有关何时不这样做,请参见下面的节拍同步。
6. 燃烧卡拉 OK .ass 字幕。
ffmpeg -i tightened.mp4 -vf "ass=captions.ass" -c:a copy captioned.mp4
.ass 文件基于步骤 2 的单词时间戳和安全区边距,构建了逐词 \k 时间信息。生成细节和模板见下文“卡拉 OK 字幕”。
7. 导出平台母版(见导出规格)。
卡拉 OK 字幕
逐词字幕是标志性外观。从单词时间戳构建,而不是从片段时间戳构建。
- 时间。 每个单词的屏幕高亮效果来自其强制对齐的开始/结束时间。在
.ass中,\k<厘秒>标签按单词推进卡拉 OK 填充效果。从片段时间戳构建逐词字幕会导致高亮抖动并错位到错误单词——始终使用步骤 2 中的逐词时间戳。 - 无声观看的可读性。 大号粗体无衬线字体、粗轮廓/描边、可选投影。人们在静音状态下观看:如果在手机上不能一眼看懂,就失败了。屏幕同时显示的文字应保持在 ≤ 2 行 / 约 6 个单词。
- 安全区——不太明显的一点。 将字幕放置在垂直中间区域,而不是底部。平台 UI(右侧栏图标、底部字幕/CTA/个人资料)覆盖底部约 15–20% 和右侧边缘。将字幕和关键文字保持在
1080x1920帧的中央约 80% 内。放置在底部的字幕会被 TikTok 按钮遮挡——这是“我的字幕坏了”的第一个原因。
最小 .ass 样式块(完整模板在 references/captions.md):
[V4+ Styles]
Style: Karaoke,Montserrat,64,&H00FFFFFF,&H0000FFFF,&H00000000,&H64000000,-1,0,0,0,100,100,0,0,1,5,2,5,80,80,640,1
MarginV(尾随的 640)将行提升到中间区域;厚重的 Outline(5)确保在任何背景上都能保持可读性。
Remotion 路线: 如果你在 Remotion 项目内,使用 @remotion/captions——createTikTokStyleCaptions({ captions, combineTokensWithinMilliseconds }) 对令牌进行分页。低值(200–500 毫秒)= 逐词弹出;高值(1200–2000 毫秒)= 短语页面。durationMs 页面在 v4.0.261 中添加。CapCut 路线: 文字 → 自动字幕 → 生成,然后重新设置样式。
节拍同步
节拍网格(步骤 4)是一个秒数列表。将剪辑点和缩放关键帧对齐到最近的节拍,使运动与音乐同步。
- 错误 → 正确。 在单词中间剪辑以将剪辑点精确落在节拍上,这看起来像错误→将剪辑点调整到同时尊重语音边界的最接近节拍。优先考虑单词,其次才是节拍。
- 何时不进行节拍剪辑。 以人物独白为主的讲解类视频,其中文字传递价值:清晰度优先于节奏。在人物句子中间进行节拍剪辑会打断其表达。节拍同步仅用于 B-roll 蒙太奇、过渡和以音乐为主导的片段。
- 过渡保持廉价和快速。 硬切是默认选项。仅在节拍上使用急摇或缩放猛冲。在 30 秒的短视频中避免使用 1 秒的交叉淡入淡出——它们会扼杀节奏;短视频依靠快速硬切生存。
导出规格
通用竖屏母版——一个能在所有三个平台上流畅播放的文件:
ffmpeg -i captioned.mp4 \
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2,setsar=1" \
-c:v libx264 -profile:v high -pix_fmt yuv420p -r 30 \
-c:a aac -b:a 192k -ar 48000 -ac 2 \
-movflags +faststart out.mp4
yuv420p(广泛兼容播放),+faststart(Web 流式传输无需等待完整下载),AAC-LC 48 kHz。使用 30 fps;对于高运动的 Shorts 提升至 60 fps。
| 平台 | 视频比特率 | 备注 |
|---|---|---|
| TikTok | 约 2,000–4,000 kbps | 三者中最轻量 |
| Instagram Reels | 约 3,500–4,500 kbps | |
| YouTube Shorts (1080p) | 约 8,000–15,000 kbps | 4K/60 最高约 53,000 kbps |
单个 1080x1920 H.264 MP4 @30 fps 且小于 约 250 MB 在所有平台上都是安全的。发货前进行验证:
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,codec_name,avg_frame_rate out.mp4
预期输出为 width=1080 height=1920 codec_name=h264。完整的平台特定编码标志和 ffprobe 断言位于 references/ffmpeg-pipeline.md。
反模式
| 错误做法 | 为何出错 | 正确做法 |
|---|---|---|
将 silenceremove 作为单步滤镜使用 | 它是仅音频的;视频继续播放 → 音视频不同步 | silencedetect → 反转范围 → select+aselect 同时剪辑两个流 |
| 从片段时间戳构建单词字幕 | 高亮抖动并错位到错误单词 | 强制对齐的逐词时间戳(WhisperX / word_timestamps) |
| 字幕燃烧在底部 15–20% 区域 | 平台 UI(按钮、CTA、个人资料)遮挡它们 | 放置在垂直中间区域,中央约 80% |
| 所有平台使用同一个导出文件 | 在某些平台上比特率过高或过低 | 使用平台特定比特率,或安全的通用母版 |
| 在 30 秒短视频中使用 1 秒交叉淡入淡出 | 扼杀节奏;显得迟缓 | 硬切;仅在节拍上使用急摇/缩放 |
| 在句子中间对人物进行节拍剪辑 | 打断表达;清晰度丧失 | 仅对 B-roll/蒙太奇进行节拍同步;尊重语音边界 |
| 重新运行编码来“检查规格” | 浪费时间;文件已存在 | 使用 ffprobe 检查输出;验证,不要重新渲染 |
| 在此处编写字幕文本或钩子 | 那是脚本,不是剪辑 | 路由到 ../video-shorts/SKILL.md;你只需将其燃烧进去 |
ffmpeg 原地覆盖源文件 | 一个错误标志就会丢失原始文件 | 始终写入新文件;保持 in.mp4 不变 |
验证与参考
bash scripts/verify.sh <output.mp4>— 运行 ffprobe 并断言width=1080、height=1920(9:16)、视频编码为h264、音频编码为aac、帧率为{30, 60},并且配套的.srt/.ass字幕文件存在且非空。如果没有 ffprobe,则回退到路径/扩展名检查。只读;在空/干净目标上退出 0,以避免误报。而“是否与节拍一致”的判断并非机械性——这属于能力评估范畴。references/ffmpeg-pipeline.md— 完整的静音检测 → 保留范围反转 →select/aselect滤镜生成(同时剪辑视频+音频)、concat/trim 方法、平台特定编码标志、ffprobe 断言以及不同步/音频漂移/faststart 故障排除。references/captions.md— WhisperX 安装 + 运行、单词时间戳 JSON 格式、带安全区边距的完整.ass卡拉 OK 模板、带combineTokensWithinMilliseconds指导的 RemotioncreateTikTokStyleCaptions()代码片段,以及 faster-whisper 与 WhisperX 的权衡。