ffmpeg-skill
腳本位於此檔案旁的 scripts/ 資料夾中;使用 python3 <skill-dir>/scripts/<name>.py 執行,傳遞模板在 templates/ 中。此檔案已足夠完成工作:下方表格可引導請求,而即將執行腳本的 --help 是最簡潔的完整旗標列表。參考檔案與此檔案成本相同;僅在遇到問題時才開啟一個:references/scripts.md(所有 42 個腳本的每個旗標)、references/devices.md(iPhone HDR、GoPro、DJI、螢幕錄製、Zoom)、references/gotchas.md(結尾一行規則的詳細版本)。MCP:tools/list 預設僅顯示核心 12 個;其他 30 個(根據此表格)可透過 tools/call 依名稱呼叫,由 contract --json 描述;設定 FFMPEG_SKILL_MCP_FULL=1 以顯示全部 42 個。
共用旗標(每個腳本):--dry-run;--json(輸出路徑、其探測資訊、執行的指令);--json-brief(狀態/輸出/已驗證 + 一個 summary;優先使用於寫入步驟);--fast(預覽品質);--progress(百分比/預計時間);--timeout SECONDS(kind: timeout,預設 1800);--overwrite(步驟 7);--plan FILE(稍後執行 render.py FILE 的乾執行;若輸入已變更則拒絕)。重新編碼工具也接受 --hw(Apple Silicon GPU,大型檔案;FFMPEG_SKILL_HW=1 = 預設,非匯出)、--codec h264|hevc|av1|prores 與 --quality N:未設定時,SDR 為 x264(cut.py --keep-hevc:HEVC 來源保留 HEVC),HDR 為 x265 Main10;prores 需要 -o NAME.mov,h264 拒絕 HDR(color.py --to-sdr 先做)。
寫入工具在 --dry-run 下不執行任何操作;測量工具(probe、check、sync、multicam、scenes、cropdetect、report、silence、loudness、stabilize;overlay 用於 FFmpeg 8.0-8.1.0 上的多行 --text)仍可能執行 ffmpeg/ffprobe,跳過成品/側邊檔案(--edl、--sheet、產生的 .ass);verify 忽略此旗標。每個工具:contract --json dry_run。
工作流程(依此順序)
- 環境,僅在失敗時。 永遠不要以
doctor開始工作:故障機器會因kind: missing_tool或命名篩選器/編碼器的 ffmpeg 錯誤而失敗。在發生此類失敗後,或當被問及機器能做什麼時,執行python3 <skill-dir>/scripts/_contract.py doctor(或npx ffmpeg-skill doctor):讀取ok/usable,報告缺少的功能。contract --json的工具架構是供規劃代理使用,而非此工作流程。 - 探測你必須規劃的內容。 對每個你計劃使用的輸入執行
probe.py——持續時間、fps、解析度、編解碼器、聲道數、variable_frame_rate_suspected——以及當使用者詢問檔案時。不要在每次編輯前都單獨探測:每個寫入工具的--json已經包含其輸入和輸出的探測資訊。從真實數據規劃,絕不靠假設。 - 偏好無損。 如果請求可以在不重新編碼的情況下滿足(關鍵幀上的純剪輯、重新混流、僅音訊變更),則不要重新編碼。
cut.py和loudness.py預設對視訊進行串流複製;僅在需要幀精確剪輯時使用cut.py的--accurate。 - 使用
--dry-run --json規劃,然後執行。 信任--json,而非乾執行的摘要行,以獲取計劃中的任何數字(那裡的尺寸可能是佔位符)。在長時間編碼前使用,以報告事實。切勿將-o指向你在此工作中未建立的檔案,除非使用者要求替換它;僅在這種情況下傳遞--overwrite。 - 以合理順序鏈結。 沒有其他編輯的傳遞請求是一次模板執行(
render.py --template NAME INPUT),而不是手動建立的鏈結。否則:色彩(HDR→SDR / LUT)→ 剪輯 → 合併 → 靜音 → 適應 → 字幕/疊加 → 同步 → 音訊 → 響度 → 匯出。在字幕之前進行幀變更,以便文字大小為最終幀設計。盡可能少次重新編碼:中間產物使用 CRF 18,export.py最後。三步或以上:使用帶有 project.json 的render.py。 - 檢查可交付結果。 在報告之前,對指定的目的地執行
check.py OUTPUT --platform X(模板執行已自動完成)。格式行(編解碼器、像素格式、大小、真實峰值、色彩標籤、VFR)可以安全修復。判斷行會改變內容:持續時間(剪輯損失素材)、長寬比(裁切損失邊緣)、fps(丟失動態)、響度(環境聲音不應被增強)——僅在請求暗示答案時修復,否則陳述選擇及其代價。提及 WARN;不要追逐它們。 - 驗證輸出。 從寫入工具的
--json/--json-brief探測或probe.py確認持續時間、解析度、fps 和音訊符合請求,並報告這些數字(「final.mp4: 59.98 s, 1080x1920, 30 fps, AAC stereo」)。僅當腳本以退出碼 0 結束且輸出探測如預期時,步驟才算完成:非零退出碼、遺失或空檔案、或與請求矛盾的探測,皆應視為失敗,並附上腳本的錯誤訊息報告。 - 保留使用者的原始檔案。 切勿覆寫來源;在輸入旁或指定位置寫入新檔案。現有輸出路徑會被拒絕(
kind: input);--overwrite是唯一說「是,取代它」的方式。 - 查看圖片。 每當圖片發生變化(字幕、疊加、圖形、裁切/填充、調整大小、色彩、轉場、將片段縮放至第一個片段幀的
join.py、color.py --to-sdr)時,執行look.py OUTPUT --tiles 3x2(或--at T取得一幀)並檢視 PNG(整個片段佈局使用 4x3)。在Look:命名該 PNG 之前,不算完成——探測無法看到臉上是否有字幕。僅音訊:Look: not needed。需要注意的內容如check.py在步驟 5 中的行所述:- 機械性(此技能自身需驗證並報告的工作): 指定的文字/標誌位於指定位置,字幕在指定時間戳出現,尺寸為偶數。來自
fit.py --fit pad的郵箱效應是該模式的正確結果,絕非需要標記的缺陷。 - 判斷(報告它,不要默默通過或失敗): 主體或臉部是否被裁切,文字是否覆蓋在臉上,顏色看起來是否褪色,轉場是否合適。這些需要決定什麼是主體,由呼叫的代理決定——用一行話說出你所看到的;由他們判斷。
若無視覺能力,寫入
Look: PATH (pixels not inspected; agent has no image view)——切勿聲稱已檢查圖片但實際上未檢查,也不要為了不存在的功能而停滯不前。
- 機械性(此技能自身需驗證並報告的工作): 指定的文字/標誌位於指定位置,字幕在指定時間戳出現,尺寸為偶數。來自
在執行任何操作之前:該問什麼、該假設什麼
僅當答案會對輸出產生重大影響且請求未暗示答案時,才問一個簡短問題。當多個事項同時不明確時(例如模糊的「讓它適合社交媒體」留下目的地、長寬比、長度和字幕都不明),不要每回合只問一個:提出一個包含你預設值的組合方案,並讓使用者更改任何部分(「Reels:9:16 填充、60 秒、-14 LUFS、無字幕——可以嗎?」)。一個問題,一個答案,然後執行。切勿詢問 probe.py 可以告訴你的內容。
- 目的地 決定長寬比、長度限制、響度和編解碼器;「用於 Reels」回答了所有四個問題並命名了一個模板。沒有目的地且僅是簡單剪輯/字幕:保留來源格式並說明。如果使用者說「匯出」、「發布」或「傳遞」,請問去哪裡。
- 持續時間(「讓它 60 秒」)而沒有方法:對於 ≤1.5× 的變化使用加速,否則使用修剪,並說明是哪種。當內容是談話(修剪會失去話語)且變化很大時提問。
- 字幕 而沒有文字來源:如果存在本機引擎則使用
--transcribe,否則要求提供文字或時間檔案;切勿發明對話。 - 字型和品牌:如果使用者提到品牌、顏色或「我們的字型」,要求建立
brand.json一次,之後重複使用。 - CJK / 非拉丁文字:讓工具按文字系統選擇字型(
--font關閉此功能);僅對漢字文字使用--lang ja|ko。doctor --json.fonts.scripts會說明此處可以渲染什麼。豆腐字是失敗的工作。 - 裁切位置 用於
--fit crop:預設居中,但當請求或來源命名了一個偏離中心的主體(「將產品保持在右側」、在聯絡表中明顯偏離中心的人)時,使用--crop-x/--crop-y(0=左/上,1=右/下)而不是猜測居中,或請問保留哪個邊緣。 - 其他任何事項(轉場類型、字幕風格):選擇常規預設,說出你選擇的內容,用一行話提供替代方案。
此技能能做與不能做的決定
此技能剪輯、合併、測量、同步、匯出和檢查檔案——它執行編輯,而不是決定編輯。屬於人類、呼叫代理或其他技能的事項:
- 哪個剪輯是正確的,或可交付結果是否可批准——此技能測量並報告(
check.py的 PASS/WARN/FAIL、cut.py的持續時間誤差);由使用者或製作代理決定是否發布。 - 什麼使精彩片段有趣——
scenes.py --highlights透過測量的代理(音訊能量、持續時間)進行排名:是候選項,而非裁決。 - 縮圖或封面構圖——設計決策,非測量。
- 理解視訊是關於什麼的——此處沒有視覺能力,只有
look.py的聯絡表,供呼叫代理觀看,而非供此技能解釋。 - 判斷什麼看起來好看——「套用此 LUT」、「將曝光校正 +0.3 檔」(
color.py)是機械性的;「將此場景調色為電影感」屬於調色技能(color-grading-skill),該技能決定參數,然後呼叫color.py。 - 字幕中的文字——提示文字會按原樣燒錄。對幀來說太長意味著更小的尺寸、
--max-lines,或使用者自己的編輯;永遠不要重寫、縮短或改寫,即使被要求「讓它適合」——請說明,提供--max-lines 1並使用較小尺寸。 - 選擇你未被告知的主體或區域——一旦知道框的位置,「裁切到 x=200,y=0」是機械性的;「裁切以將講者保持畫面內」需要決定什麼是講者,這是由呼叫代理做出的判斷(來自
look.py的聯絡表)。
界線:相同輸入 + 相同明確參數始終產生相同可驗證輸出屬於此處;任何取決於品味、理解或看起來/聽起來好壞的事物屬於做出該判斷的人。
如果請求需要 42 個腳本均未公開的 FFmpeg 功能,請說明並命名最接近的內建選項——切勿猜測 scripts/*.py 之外的原始 ffmpeg/ffprobe 呼叫。這繞過了此技能所做的所有保證,因此它絕不是備用方案。
請求 → 腳本
此表格和 doctor --json 的 tools 列表是存在哪些內容的真實來源:僅命名你在其中之一看過的腳本(沒有 doctor.py、沒有 trim.py、沒有 subtitle.py)。
時間戳旗標(--start、--end、--at、--from、--duration、--offset,以及提示和章節檔案中的時間)接受秒、mm:ss(.fff)、hh:mm:ss(.fff) 或帶有 @fps 的 SMPTE hh:mm:ss:ff(00:01:02:[email protected])——直接貼上 NLE 提示表;長度旗標(--min-silence、--margin、--fade)為純秒。
| 使用者說 | 執行 |
|---|---|
| 「從 1:20 剪到 2:05」、「修剪前 10 秒」 | cut.py input.mp4 --start 1:20 --end 2:05 |
| 「只保留這些部分」、「移除中間」 | cut.py input.mp4 --segments 0-1:00,1:30-2:00 |
| 「讓它正好 60 秒」 | fit.py input.mp4 --duration 60(加速)或 --method trim |
| 「剪這段並轉成 HEVC / AV1 / ProRes」(指定輸出編解碼器) | cut.py input.mp4 --start 0:10 --end 0:40 --codec hevc(ProRes 需要 -o NAME.mov) |
| 「剪掉停頓」、「跳躍剪輯」 | silence.py input.mp4 [--threshold -40 --min-silence 0.8] |
| 「剪掉 um 和 uh」、「移除填充詞」 | silence.py input.mp4 --filler --words words.json(測量的單字時間;--transcribe 產生它們) |
| 「不要在句子內剪輯,只剪真正的停頓」 | silence.py input.mp4 --speech-aware——句子內的 --min-silence 以下的呼吸會保留,僅剪輯句子邊界的停頓;與 --filler 結合成一個列表 |
| 「從這一小時中剪出一個 60 秒的精彩片段」 | scenes.py long.mp4 --highlights 6 --target 60 --edl picks.txt → cut.py --segments |
| 「在節拍上剪輯」、「根據音樂編輯」 | scenes.py track.mp4 --beats --json > beats.json,然後 cut.py input.mp4 --segments ... --snap beats --snap-source beats.json(--snap-source 攜帶測量的網格) |
| 「這裡加速、那裡慢動作」(已知片段) | speedramp.py action.mp4 --segment 0-3:1.0 --segment 3-4:0.25 --segment 4-8:2.0 |
| 「定格此幀」、「凍結最後一幀」 | freeze.py clip.mp4 --hold 2 |
| 「流暢慢動作」、「半速但流暢」 | fit.py input.mp4 --duration 2x --smooth interpolate(慢)或 --smooth blend |
| 「反轉此片段」 | reverse.py input.mp4 |
| 「循環此片段以填滿 30 秒」 | loop.py bg_loop.mp4 --duration 30 |
| 「讓它垂直 / 9:16 / 方形」 | fit.py input.mp4 --aspect 9:16 --fit pad(或 --fit crop;使用 --pad-fill blur 製作模糊條) |
| 「調整大小到指定高度/寬度」 | fit.py input.mp4 --height 1080(或 --width,或兩者都用於精確幀) |
| 「裁切到此精確方框」(已知 x/y/w/h) | crop.py input.mp4 --x 100 --y 0 --width 1080 --height 1920 |
| 「用模糊背景代替黑條」 | fit.py input.mp4 --aspect 9:16 --fit blur(保留整個畫面,邊框為模糊、變暗的副本) |
| 「旋轉 90 度」、「鏡像」 | fit.py input.mp4 --rotate 90 / fit.py input.mp4 --flip h |
| 「地平線歪了」(已知度數) | straighten.py tilted.mp4 --degrees -2.5 |
| 「從這個 360 影片獲得平面視圖」(已知 yaw/pitch/fov) | sphere.py insta360.mp4 --yaw 90 --pitch 0 --h-fov 100 --v-fov 70 |
| 「這個舊影片有交錯掃瞄」 | deinterlace.py input.mp4 |
| 「有顆粒/雜訊,清理一下」 | denoise.py input.mp4 --strength medium |
| 「模糊/像素化這個臉/車牌」(已知方框) | redact.py input.mp4 --x 820 --y 140 --width 240 --height 240 --mode pixelate |
| 「移除這個固定標誌」(已知方框) | redact.py input.mp4 --x 40 --y 20 --width 180 --height 60 --mode delogo |
| 「穩定這個晃動的影片」 | stabilize.py input.mp4 |
| 「這個上面有黑條嗎?」 | cropdetect.py input.mp4 |
| 「iPhone Dolby Vision 片段看起來不對」 | color.py input.mp4 --to-sdr |
| 「將此 LUT 套用到整個片段」 | color.py input.mp4 --lut FILTER(例如 lut=color.lut 或 lut3d=color.cube) |
| 「調整曝光/對比度/飽和度/色調」 | color.py input.mp4 --exposure +1.0 --contrast 1.2 --saturation 0.8 |
| 「讓膚色在膚色色調範圍內」 | color.py input.mp4 --skin-protection 0.7(保留膚色的同時應用其他變更) |
| 「分級此片段以匹配給定的參考」 | color.py input.mp4 --match reference.mov(傳播顏色特徵) |
| 「檢查此視訊的規格」 | check.py input.mp4(或 --platform youtube 等) |
| 「這是什麼解析度/格式/時間長度/影格率」 | probe.py input.mp4 |
| 「關於這個檔案告訴我一切」 | probe.py input.mp4 --json(或 check.py) |
| 「這個檔案的有效組合是什麼?」 | probe.py input.mp4 --valid(根據 --platform 或 --pixel-format/--codecs/--max-size 等規則) |
| 「這個到期了嗎?」 / 「這個可以播放嗎?」 | check.py input.mp4 --integrity |
| 「正確縮放這個音訊的音量」 | loudness.py input.mp4 --loudness -14(短暫響度目標;流暢地歸一化音軌) |
| 「讓音量與這個參考相符」 | loudness.py input.mp4 --match reference.wav |
| 「讓聲音更大聲」 | loudness.py input.mp4 --gain 6(如果您沒有目標,簡單增益) |
| 「壓縮動態範圍,讓安靜部分更響亮」 | loudness.py input.mp4 --compress |
| 「在原始音訊旁加入背景音樂」 | mix.py video.mp4 bg_music.mp3 --mix bg --volume music=0.3 |
| 「在說話時將音樂變小聲」 | mix.py video.mp4 bg_music.mp3 --mix bg --duck speech(調整音量以容納旁白或對話) |
| 「開始時淡入音樂,結束時淡出」 | mix.py video.mp4 bg_music.mp3 --mix bg --fade-in 3 --fade-out 5 |
| 「將此音軌替換為另一個」 | mix.py video.mp4 new_audio.wav --mix replace |
| 「同步這兩個音軌」 | sync.py speaker.wav external_mic.wav |
| 「同步外部麥克風音訊與攝影機片段」 | `sync.py camera.mp4 external_mic.wav --json |
| 「對齊多個攝影機角度」 | multicam.py cam1.mp4 cam2.mp4 cam3.mp4 --reference cam2(產生帶有角度剪輯點和偏移量的 EDL) |
| 「將這些片段組合成一個」 | join.py clip1.mp4 clip2.mp4 clip3.mp4 |
| 「在段落之間交叉淡出」 | join.py clip1.mp4 clip2.mp4 --transition crossfade(此處有 --transition 旗標;否則合併後使用 transition.py?並非所有都支援,檢查 --help) |
| 「在片段之間添加交叉淡出」 | transition.py input.mp4 --effect crossfade --at 10(在 10 秒處)或 --transition crossfade --duration 1 在 join.py 中 |
| 「在此頂部添加文字疊加」 | `overlay.py input.mp4 --text |