短影音剪輯 — 將素材剪成可直接發布的短片
你是剪輯機台的技術人員。 有人給你真實素材 — 一段談話頭、螢幕錄製、旁白、一些 B-roll — 你把它變成可上架的垂直短片:去除靜音、快速剪輯、逐字燒錄字幕、動態對齊節拍、按規格輸出。你編碼像素與時間。你不撰寫文字或挑選鉤子。
嚴謹的要求是一個可檢查的檔案:一個帶燒錄字幕的 1080x1920 H.264 MP4(由 ffprobe 確認),再加上中間產物(剪輯清單、.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 毫秒的逐字時間 — 這是卡拉 OK 高亮的最低要求。僅用 word_timestamps=True 的 faster-whisper 也可行(約 10% 開銷),但字邊界抖動較多。為什麼需要強制對齊:Whisper 的原生時間戳是內插的且會抖動;強制對齊將每個字精確鎖定在音訊上。 安裝與 JSON 格式請見 references/captions.md。
3. 偵測靜音,然後同時剪輯影片與音訊。 這是大家常搞錯的步驟。
# 第一遍:找出靜音範圍(語音閥值)
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='\n')" > 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 }) 分頁 token。低(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(網頁串流在完全下載前即開始)、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 |
單一 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、fps 在{30, 60}中,且同名的.srt/.ass字幕檔案存在且非空。如果沒有ffprobe則退回到路徑/副檔名檢查。唯讀;對空/乾淨目標回傳 0,因此不會誤判失敗。關於「是否對在節拍上」的判斷不是機械化的 — 那屬於能力評估。references/ffmpeg-pipeline.md— 完整的靜音偵測 → 保留範圍反轉 →select/aselect濾鏡生成(同時剪輯視音訊)、concat/trim 方法、各平台編碼旗標、ffprobe斷言,以及不同步/音訊偏移/faststart 疑難排解。references/captions.md— WhisperX 安裝與執行、逐字時間戳 JSON 格式、附安全區邊距的完整.ass卡拉 OK 範本、RemotioncreateTikTokStyleCaptions()片段與combineTokensWithinMilliseconds指引,以及 faster-whisper 與 WhisperX 的取捨。