影片組合 — 組裝、音訊、編碼、驗證
FFmpeg 是交付層,而非設計工具。它應接收已設計好的場景,並產出清晰的母版以及各平台的編碼檔案。
請先閱讀 references/FFMPEG-COOKBOOK.md 取得立即可用的指令,以及 scripts/validate.sh 進行交付前的檢查。
第零條規則:先探查,再處理。 針對未確認的串流屬性建立濾鏡圖表,是導致輸出檔案毀損的最常見原因。
ffprobe -v error -show_streams -show_format -of json input.mp4
1. 合併前先正規化
不相容的輸入會導致畫面抖動、音訊靜音、不同步及光暈。在進行多來源組裝前,請先讓每個輸入在以下屬性上達成一致:
| 屬性 | 目標值 |
|---|---|
| 解析度 | 專案母版 (例如 1920×1080) |
| 像素長寬比 | 1:1 |
| 影格率 | 專案 FPS,固定值 |
| 時間基數 | 一致 |
| 像素格式 | 交付用 yuv420p,含 Alpha 用 yuva444p10le/PNG |
| 色彩空間/轉換 | 單一標準;加上標籤 |
| 音訊 | 48 kHz、立體聲、一致的編碼器 |
ffmpeg -i in.mp4 -vf "scale=1920:1080:flags=lanczos,fps=30,setsar=1" \
-c:v libx264 -crf 14 -preset fast -pix_fmt yuv420p \
-c:a aac -ar 48000 -ac 2 -b:a 256k normalized.mp4
保持中間檔案的高品質(CRF 12–16 或 ProRes)。最終交付版本只編碼一次。
2. 串接
相同編碼、尺寸、影格率、像素格式及音訊佈局 → 使用串流複製,快速且無損:
printf "file 'shot_01.mp4'\nfile 'shot_02.mp4'\n" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
其他情況 → 使用濾鏡圖表:
ffmpeg -i a.mp4 -i b.mp4 -filter_complex \
"[0:v]scale=1920:1080,fps=30,setsar=1[v0]; \
[1:v]scale=1920:1080,fps=30,setsar=1[v1]; \
[v0][0:a][v1][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -crf 16 -pix_fmt yuv420p -c:a aac final.mp4
切勿對不相容的輸入直接使用串流複製串接。這個動作可能會「成功」,但產出的檔案播放時會出現問題。
3. 疊加、Alpha 與子母畫面
# 在背景上疊加透明元素
ffmpeg -i bg.mp4 -i logo.mov -filter_complex \
"[0:v][1:v]overlay=W-w-48:48:format=auto" -c:v libx264 -crf 16 out.mp4
# 子母畫面,來源已縮放並圓角處理
ffmpeg -i main.mp4 -i insert.mp4 -filter_complex \
"[1:v]scale=640:-2[pip];[0:v][pip]overlay=W-w-40:H-h-40" out.mp4
Alpha 規則:
- H.264/MP4 無法承載 Alpha 通道。請使用 PNG 序列、ProRes 4444 或支援 Alpha 的 WebM/VP9。
- 在每個階段後,務必在對比色背景上檢查 Alpha 效果。
- 注意預乘 Alpha 與直接 Alpha 在深色邊緣可能產生的光暈,請確保與來源渲染器的慣例一致。
4. 音訊
# 背景音樂,附帶淡入淡出及旁白自動降低音量
ffmpeg -i video.mp4 -i music.mp3 -i voice.wav -filter_complex \
"[1:a]volume=0.35,afade=t=in:st=0:d=1,afade=t=out:st=28:d=2[bed]; \
[2:a]volume=1.0[vo]; \
[bed][vo]sidechaincompress=threshold=0.08:ratio=6:attack=8:release=250[duck]; \
[duck][vo]amix=inputs=2:duration=first:dropout_transition=0[a]" \
-map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out.mp4
# 測量音量,再進行正規化
ffmpeg -i in.mp4 -af loudnorm=I=-14:TP=-1:LRA=11:print_format=json -f null -
ffmpeg -i in.mp4 -af loudnorm=I=-14:TP=-1:LRA=11 -c:v copy out.mp4
目標值因平台而異(多數網路平台為 -14 LUFS,廣播為 -23/-24)。先測量,再選擇。請避免對已達標的檔案再次正規化。
5. 字幕
# 硬燒字幕
ffmpeg -i in.mp4 -vf "subtitles=captions.srt:force_style='FontName=Inter,FontSize=22,PrimaryColour=&H00FFFFFF,OutlineColour=&H80000000,BorderStyle=3,Outline=1,Shadow=0'" \
-c:v libx264 -crf 18 -c:a copy out.mp4
# 軟字幕(可移動音軌)
ffmpeg -i in.mp4 -i captions.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng out.mp4
檢查從右到左書寫的語言:在 force_style 中使用具備字型塑形能力的字型、正確的 Alignment 設定,並檢查渲染後影格。確保字幕位於安全區域內且不與平台 UI 重疊。
6. 平台編碼
# 橫向 YouTube,1080p30
ffmpeg -i master.mov -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p \
-profile:v high -level 4.1 -movflags +faststart \
-c:a aac -b:a 192k -ar 48000 out_1080p.mp4
# 直向社群媒體,1080x1920
ffmpeg -i master.mov -vf "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
-c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p -movflags +faststart \
-c:a aac -b:a 192k out_vertical.mp4
# 供後續合成使用的高品質中間檔案
ffmpeg -i master.mov -c:v prores_ks -profile:v 3 -pix_fmt yuv422p10le -c:a pcm_s24le master.mov
確認編碼器是否存在:ffmpeg -encoders | grep -E "libx264|prores|nvenc"。硬體編碼器速度快,但單位位元率下的品質通常較低;出貨前請比較差異。
7. 影格、聯絡表、GIF
# 在特定時間點擷取靜態影格
ffmpeg -ss 00:00:03.5 -i in.mp4 -frames:v 1 frame.png
# 產生聯絡表
ffmpeg -i in.mp4 -vf "fps=1,scale=320:-1,tile=4x4" -frames:v 1 sheet.jpg
# 高品質 GIF(使用 palettegen/paletteuse)
ffmpeg -i in.mp4 -vf "fps=15,scale=640:-1:flags=lanczos,palettegen" palette.png
ffmpeg -i in.mp4 -i palette.png -lavfi "fps=15,scale=640:-1:flags=lanczos[x];[x][1:v]paletteuse" out.gif
8. 驗證(不可省略)
ffprobe -v error \
-show_entries format=duration,size,bit_rate,format_name \
-show_entries stream=index,codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,pix_fmt,channels,sample_rate,nb_frames \
-of json final.mp4
自動化檢查:
# 檢查黑畫面區段(意外出現的全黑影格)
ffmpeg -i final.mp4 -vf "blackdetect=d=0.1:pix_th=0.05" -an -f null - 2>&1 | grep blackdetect || true
# 檢查凍結畫面區段(靜止畫面時間過長)
ffmpeg -i final.mp4 -vf "freezedetect=n=0.003:d=2" -an -f null - 2>&1 | grep freeze || true
# 檢查音訊是否存在及峰值音量
ffmpeg -i final.mp4 -af volumedetect -f null - 2>&1 | grep -E "mean_volume|max_volume"
接著檢查第一影格、最後影格、每個轉場及文字較多的影格。數值證明檔案格式正確,但並不代表畫面看起來正確。
9. 影格精確度陷阱
- 可變影格率來源(螢幕錄影、手機拍攝)會與固定影格率時間軸產生偏移。請使用
fps=30進行正規化並檢查總長度。 - 少一影格:30 fps 的 N 個影格應恰好是 N/30 秒;一個 300 影格的渲染輸出應為 10.0 秒。若長度為 9.97 秒,則表示有影格遺失。
- 音訊較影片短:結尾會出現靜音;請明確地補足或修剪。
- 旋轉的手機影片:攜帶顯示矩陣;除非你確定需要,否則不要使用
-noautorotate。 - 編碼後色彩偏移:檢查
color_primaries/transfer標籤;瀏覽器預覽並非可靠的色彩參考。 - 重複編碼會降低品質;請保留高品質母版,並從母版進行編碼。
10. 交付套件
output/
final_1080p.mp4 # 主要檔案
final_vertical.mp4 # 平台變體(如有要求)
final_master.mov # ProRes/H.264 CRF 14,保留供未來編碼使用
thumbnail.png # 1280x720 或 1080x1920
contact-sheet.jpg
captions.srt
project-report.md # 工具版本、設定、已執行檢查項目
使用 ffprobe 報告精確的屬性;切勿憑猜測填寫。