
簡介FFmpegAutoGenDemo.rar 是一份基于 C# 與 FFmpeg.AutoGen 封裝的 WPF 音視頻處理實(shí)戰(zhàn)資源面向希望將 FFmpeg 原生能力集成到 .NET 桌面應(yīng)用的開發(fā)者。項(xiàng)目通過自動化綁定將 FFmpeg 的 C 接口轉(zhuǎn)換為 C# 可調(diào)用類庫覆蓋音視頻解碼、編碼、轉(zhuǎn)碼、播放、濾鏡應(yīng)用等核心操作并給出 WPF 自定義控件的集成思路。資源共 71 個文件含 19 個 DLLFFmpeg 庫及托管封裝、12 個 C# 源碼、8 個可執(zhí)行文件及 XAML、配置文件等壓縮包約 37.9MB結(jié)構(gòu)完整便于直接參考與二次開發(fā)。目前已有 1615 人學(xué)習(xí)下載。從 FFmpegHelper、FFmpegBinariesHelper 等源碼中可系統(tǒng)掌握 FFmpeg 接口映射、內(nèi)存管理、異步線程處理及錯誤日志記錄的典型寫法適合具備基礎(chǔ) C# 知識、希望在 WPF 中快速上手 FFmpeg 的開發(fā)者。1. FFmpegAutoGenDemo.rar一個幫你把 FFmpeg 命令變成流水線的演示這個壓縮包名字直白FFmpeg AutoGen Demo說的是“用自動生成的方式跑 FFmpeg”。實(shí)際場景里很多人還在手敲命令處理單個視頻換臺機(jī)器、換批素材就亂了。而這個 demo 想解決的問題是把視頻處理從命令行手工操作變成由腳本根據(jù)素材清單自動拼出完整 FFmpeg 命令再統(tǒng)一執(zhí)行。適合給視頻批量轉(zhuǎn)碼、生成預(yù)覽、做短視頻拼接的從業(yè)者以及準(zhǔn)備用 FFmpeg 做自動化管線的開發(fā)者。你不需要記住一堆參數(shù)順序只需維護(hù)素材表格和生成規(guī)則剩下交給腳本去生成、去跑、去報(bào)錯。2. 先搞懂要自動生成什么FFmpeg 命令的五段式結(jié)構(gòu)與模板化思路2.1 FFmpeg 命令不是字符串是五段式參數(shù)拼接很多人一聽到“自動生成命令”第一反應(yīng)是拿字符串拼。但 FFmpeg 命令看起來是一長串其實(shí)內(nèi)部有嚴(yán)格的段落順序。亂拼能跑通一次第二次換個素材就崩這屬于把力氣用錯了地方。我一般把 FFmpeg 命令拆成五個可變段全局參數(shù)、輸入?yún)?shù)、輸入文件、濾鏡鏈、輸出參數(shù)。標(biāo)準(zhǔn)結(jié)構(gòu)大致是這樣的ffmpeg [全局參數(shù)] [輸入?yún)?shù)] -i 輸入文件 [濾鏡參數(shù)] [輸出參數(shù)] 輸出文件全局參數(shù)寫在最前面比如-hide_banner、-y控制的是整個進(jìn)程的行為輸入?yún)?shù)緊接著全局參數(shù)但放在-i前面表示這個輸入的讀取方式比如-re、-thread_queue_size輸入文件用-i指定如果有多個輸入每個輸入前面都可以帶自己的參數(shù)濾鏡參數(shù)在輸入之后因?yàn)闉V鏡要處理的是已經(jīng)讀取進(jìn)來的流輸出參數(shù)最后控制編碼器、碼率、格式等。為什么自動生成的時(shí)候必須按這個順序因?yàn)?FFmpeg 解析參數(shù)是從左到右逐段處理的把輸出參數(shù)寫到-i前面大部分時(shí)候會報(bào)錯因?yàn)榻馕銎鬟€沒看到輸入文件不知道這個輸出參數(shù)屬于誰。自動生成腳本如果只是簡單拼字符串很容易把變量填錯位置反而讓命令變得不可讀。所以我在做自動生成時(shí)第一步不是寫生成函數(shù)而是把這個五段式結(jié)構(gòu)定義成數(shù)據(jù)結(jié)構(gòu)。用 Python 的話一般是一個字典每個鍵對應(yīng)一個段落值是一個列表——我推薦用列表而不是字符串因?yàn)楹罄m(xù)追加參數(shù)時(shí)不容易漏掉空格也方便做條件判斷。# 定義一個 FFmpeg 命令的階段容器 cmd_parts { global: [], # 全局參數(shù) input: [], # 輸入?yún)?shù) input_file: [], # 輸入文件路徑 filter: [], # 濾鏡鏈 output: [], # 輸出參數(shù) }后續(xù)所有生成邏輯都只操作這個字典最后再按順序展平。這樣做的核心好處是每個段落互相獨(dú)立修改濾鏡不會影響輸出參數(shù)加輸入文件不會污染全局參數(shù)。demo 里展示的往往就是這么個思想只不過被壓縮包里的腳本封裝成了更具體的函數(shù)。參數(shù)列表而不是參數(shù)字符串還帶來一個隱形紅利可以應(yīng)對“同一個參數(shù)需要出現(xiàn)多次”的情況。比如一個輸入有一個-ss定位另一個輸入有自己的-ss如果拼成字符串你很難弄清哪個-ss屬于哪個-i但用列表加段落結(jié)構(gòu)就能保證每個-ss緊跟在它所屬的輸入?yún)?shù)列表里生成順序天然正確。2.2 模板化生成的最小實(shí)現(xiàn)用數(shù)據(jù)填充命令結(jié)構(gòu)定好了接下來就是模板化生成。所謂模板不是搞一套復(fù)雜的模板引擎而是“固定部分寫在代碼里可變部分由參數(shù)決定”。最常見的做法是寫一個生成函數(shù)輸入是一個描述素材的字典輸出是拼接好的命令列表。比如我要做一個“批量轉(zhuǎn)碼”的最小 demo輸入目錄里有一堆.mov要轉(zhuǎn)成.mp4統(tǒng)一用 H.264 編碼碼率 4Mbps分辨率保持原樣。這個任務(wù)用命令行手寫很簡單但自動生成的價(jià)值在于文件多、命名還可能不規(guī)則腳本可以自動發(fā)現(xiàn)并填充。import subprocess from pathlib import Path def build_transcode_cmd(src_path, dst_path, bitrate4M): 生成單條轉(zhuǎn)碼命令 return [ ffmpeg, -hide_banner, -y, # 全局參數(shù)不刷屏、覆蓋輸出 -i, str(src_path), # 輸入 -c:v, libx264, # 視頻編碼 -b:v, bitrate, # 視頻碼率 -preset, medium, # 編碼速度/質(zhì)量檔位 -c:a, aac, -b:a, 128k, # 音頻編碼 str(dst_path), ]這段代碼生成的不是字符串而是一個命令行列表。列表直接交給subprocess.run執(zhí)行可以避免字符串拆分時(shí)的引號問題。注意-b:v的值是4M不帶引號——如果帶引號在列表里會把4M當(dāng)成一個完整參數(shù)反而沒問題但如果用字符串拼接引號經(jīng)常會造成困擾。參數(shù)說明一下-hide_banner去掉開頭的版本信息日志更干凈-y是覆蓋輸出文件批處理時(shí)不會卡在詢問-preset medium是 libx264 的預(yù)設(shè)之一medium 是均衡檔生成 demo 時(shí)常用實(shí)際批量素材多且對速度敏感就用veryfast追求畫質(zhì)用slow。碼率單位M表示兆比特每秒FFmpeg 也接受k。生成函數(shù)只負(fù)責(zé)組裝不負(fù)責(zé)編碼跑不跑得通要交給執(zhí)行層。有了函數(shù)之后批量轉(zhuǎn)碼的主循環(huán)就非常簡單了src_dir Path(素材原始) dst_dir Path(轉(zhuǎn)碼輸出) dst_dir.mkdir(exist_okTrue) for src_path in src_dir.glob(*.mov): dst_path dst_dir / (src_path.stem .mp4) cmd build_transcode_cmd(src_path, dst_path) print(執(zhí)行:, .join(cmd)) subprocess.run(cmd, checkTrue)這里checkTrue會讓腳本在命令失敗時(shí)直接拋異常而不是吞掉錯誤繼續(xù)處理下一個方便在批量任務(wù)里定位到具體哪個文件出了問題。很多剛接觸的人習(xí)慣用os.system拼字符串一旦路徑里有空格就翻車用列表傳參是跨平臺的穩(wěn)妥寫法也是這個 demo 最值得借鑒的地方。3. 把 FFmpegAutoGenDemo 的核心跑通自動批量轉(zhuǎn)碼與拼接3.1 拆解 demo 里最常見的任務(wù)素材清單驅(qū)動命令生成看名字里的“AutoGen”自動生成最常見的目標(biāo)不是轉(zhuǎn)碼而是拼接。短視頻批量制作、課程切片合并、監(jiān)控片段匯總都是這個路子。手寫拼接命令很難受輸入文件多每個還要配不同的偏移或?yàn)V鏡寫一長串又臭又容易錯。所以 demo 里一般會有一張素材清單腳本讀清單、自動生成拼接參數(shù)。素材清單我用過幾種格式CSV、JSON、甚至 Excel。最穩(wěn)的是 CSV結(jié)構(gòu)簡單Python 標(biāo)準(zhǔn)庫直接讀編碼問題要注意用utf-8-sig去掉 BOM 頭。清單的字段通常有文件路徑、開始時(shí)間、結(jié)束時(shí)間、音量增益、是否添加字幕等。生成時(shí)按行遍歷把每一行轉(zhuǎn)成一個輸入塊和一個濾鏡塊。一個典型的清單長這樣path,start,end,audio_volume,rotate 素材/clip01.mov,00:00:00,00:00:05,1.0,0 素材/clip02.mov,00:00:10,00:00:15,1.2,90 素材/clip03.mov,00:00:20,00:00:30,0.8,0為什么要用 CSV因?yàn)橛帽砀窆芾硭夭淖钪庇^而且非程序員也能維護(hù)。生成腳本讀這個 CSV但更關(guān)鍵的是清單里的“開始時(shí)間”和“結(jié)束時(shí)間”決定了每個輸入需要先被切割再拼接這涉及 FFmpeg 濾鏡鏈的構(gòu)建。我把拼接分成兩類純拼接和帶處理的拼接。純拼接是把多個文件直接挨個接起來最簡單的做法是寫一個 concat 列表文件讓 FFmpeg 的 concat demuxer 處理不需要濾鏡速度快。但如果你要對每個片段做裁剪、旋轉(zhuǎn)、調(diào)音量concat demuxer 就不夠用了必須用filter_complex。AutoGen 的價(jià)值這里才真正體現(xiàn)filter_complex的文本非常容易寫錯括號和逗號自動生成能減少人為犯錯。3.2 生成拼接命令的完整代碼與參數(shù)說明下面這段代碼是我在做一個模擬項(xiàng)目 X 時(shí)寫的功能是讀 CSV對每個片段用trim裁剪加音量調(diào)整支持旋轉(zhuǎn)最后用concat拼接再統(tǒng)一導(dǎo)出 H.264。它直接體現(xiàn)了“自動生成”的過程——不是寫死某一條命令而是根據(jù)清單動態(tài)拼出filter_complex。import csv import subprocess from pathlib import Path def build_concat_cmd(csv_path, output_path): inputs [] # 所有輸入文件參數(shù) filter_parts [] # 每個片段的濾波處理 input_labels [] # 給每個輸入命名如 [0:v] [0:a] with open(csv_path, encodingutf-8-sig) as f: reader csv.DictReader(f) for idx, row in enumerate(reader): src Path(row[path]).resolve() start row[start] end row[end] volume float(row.get(audio_volume, 1.0)) rotate_deg int(row.get(rotate, 0)) # 每個輸入ss 是開始時(shí)間t 是截取時(shí)長 inputs.append([-ss, start, -t, end, -i, str(src)]) # 給這個輸入起一個濾鏡標(biāo)簽名 [i:v] [i:a] vlabel f[{idx}:v] alabel f[{idx}:a] # 視頻處理先裁剪再旋轉(zhuǎn) if rotate_deg ! 0: # FFmpeg 旋轉(zhuǎn)參數(shù)0不轉(zhuǎn), 190°, 2180°, 3270° vfilter f{vlabel}trim{start}:{end},setptsPTS-STARTPTS,transpose{rotate_deg//90}[v{idx}] else: vfilter f{vlabel}trim{start}:{end},setptsPTS-STARTPTS[v{idx}] filter_parts.append(vfilter) # 音頻處理裁剪 音量如果原文件沒有音頻再加 asetnsamples afilter f{alabel}atrim{start}:{end},asetptsPTS-STARTPTS,volume{volume}[a{idx}] filter_parts.append(afilter) input_labels.append(f[v{idx}][a{idx}]) # 把所有濾鏡用分號連起來最后的 concat 段收集所有標(biāo)簽 filter_complex ;.join(filter_parts) concat_part f{.join(input_labels)}concatn{len(input_labels)}:v1:a1[outv][outa] filter_complex ; concat_part # 組裝完整命令 cmd [ffmpeg, -hide_banner, -y] for inp in inputs: cmd.extend(inp) cmd [-filter_complex, filter_complex, -map, [outv], -map, [outa], -c:v, libx264, -preset, medium, -crf, 20, -c:a, aac, -b:a, 128k, output_path] return cmd # 執(zhí)行 cmd build_concat_cmd(素材清單.csv, 拼接結(jié)果.mp4) print(生成的 filter_complex 如下) print(cmd[cmd.index(-filter_complex) 1]) subprocess.run(cmd, checkTrue)邏輯說明這段代碼把每個輸入都按“開始時(shí)間”和“時(shí)長”先切開視頻用trim加setptsPTS-STARTPTS重置時(shí)間戳音頻用atrim加asetpts重置時(shí)間戳。變量rotate_deg接受 0、90、180、270轉(zhuǎn)換成 FFmpeg transpose 的 0-3 取值。注意我這里用rotate_deg // 90因?yàn)?transpose 參數(shù)正好是順時(shí)針旋轉(zhuǎn) 90° 的倍數(shù)。參數(shù)說明setptsPTS-STARTPTS是裁剪后最關(guān)鍵的一步——不重置時(shí)間戳的話拼接時(shí)會因?yàn)闀r(shí)間戳不連續(xù)而黑屏或卡頓volume1.0是原音量小于 1 是降低大于 1 是放大。concatn3:v1:a1里的n必須和輸入個數(shù)一致這個數(shù)字是自動算的手寫拼接時(shí)最容易漏改的就是它。-crf 20是恒定質(zhì)量數(shù)值越小畫質(zhì)越高20 在大多數(shù)場景是體積和畫質(zhì)的平衡點(diǎn)。3.3 從生成命令到真正執(zhí)行subprocess 調(diào)用與日志命令生成完最后還有一步執(zhí)行。很多 demo 到生成命令就停了拿來當(dāng)玩具可以實(shí)際用起來還缺執(zhí)行層。執(zhí)行層的關(guān)鍵不是簡單調(diào)subprocess.run而是輸出日志要能對應(yīng)到清單里的某一行。我習(xí)慣給每一條任務(wù)加一個唯一的標(biāo)識比如 CSV 行號然后記錄開始、結(jié)束、退出碼。日志格式類似這樣[task-03] 開始處理: 素材/clip02.mov這樣批量跑出問題時(shí)能立刻定位。import subprocess import sys def run_cmd(cmd, task_name): print(f[{task_name}] 開始執(zhí)行) # stdout/stderr 合并方便統(tǒng)一看日志 result subprocess.run(cmd, capture_outputTrue, textTrue) if result.returncode ! 0: print(f[{task_name}] 失敗退出碼 {result.returncode}) print(result.stderr[-2000:]) # 只打印最后 2000 字符避免刷屏 raise RuntimeError(f任務(wù) {task_name} 執(zhí)行失敗) else: print(f[{task_name}] 成功) # 用法 cmd build_concat_cmd(素材清單.csv, 拼接結(jié)果.mp4) run_cmd(cmd, task-01)注意capture_outputTrue會同時(shí)捕獲 stdout 和 stderr文本模式textTrue讓返回結(jié)果可以直接打印。FFmpeg 的進(jìn)度信息默認(rèn)輸出到 stderr錯誤信息也走 stderr所以失敗時(shí)看result.stderr比看終端輸出更可靠。有的版本 FFmpeg 進(jìn)度條在非終端環(huán)境下會一直輸出\r如果日志文件因此變得雜亂可以在命令里加-nostats或者-loglevel error但這會丟掉進(jìn)度信息要看你的取舍。4. 避坑與排查自動生成 FFmpeg 命令最常見的 5 個翻車點(diǎn)4.1 路徑里有空格和括號命令直接斷掉現(xiàn)象自動生成的命令在終端里手動執(zhí)行沒問題一旦放進(jìn)腳本跑就報(bào)錯提示找不到文件。尤其是 Windows 路徑C:\My Videos\final cut\clip (1).mov有空格、有括號字符串拼接直接炸。原因用字符串形式拼接命令時(shí)空格會把路徑拆成兩個參數(shù)括號在某些 shell 里有特殊含義。demo 里如果你用列表傳參空格不是問題但如果你用subprocess.run(cmd_str, shellTrue)或者os.system就會踩中。解決堅(jiān)持用列表傳參路徑用Path對象或絕對路徑。如果必須用字符串給整個路徑加雙引號并且對%、!等特殊字符做轉(zhuǎn)義??缙脚_更穩(wěn)的方式是用shlex.split或直接構(gòu)造列表。我自己在腳本里會寫一個斷言如果路徑里有空格就強(qiáng)制轉(zhuǎn)成Path.resolve()提前顯式化避免后續(xù)玄學(xué)問題。4.2 filter_complex 引號轉(zhuǎn)義失控現(xiàn)象filter_complex里的文字濾鏡比如drawtext需要寫文本內(nèi)容自動生成時(shí)引號嵌套執(zhí)行后報(bào)Failed to parse graph description。原因FFmpeg 的 filter 語法里參數(shù)值需要用單引號包裹而你的生成腳本本身可能用了 Python 字符串處理最后一層層的引號疊加傳到命令行時(shí) shell 再把引號吃掉filter 解析器看到的字符串就是不完整的。比如texthello world寫在 Python 字符串里如果你的字符串用了單引號就會提前結(jié)束。解決filter_complex 整個值在列表中是一個獨(dú)立元素不需要再套 shell 引號。你只需要保證 filter 內(nèi)部對值的引號正確。一般規(guī)則filter 內(nèi)部分隔符是:、,、;如果參數(shù)值本身包含這些字符必須用單引號包住并且值里的特殊字符如\、要做轉(zhuǎn)義。最穩(wěn)妥的辦法是不要用 f-string 硬湊而是寫一個專門的函數(shù)處理文本轉(zhuǎn)義。比如text參數(shù)可以先做value.replace(, \\\\)再包一層引號這是一個血淚經(jīng)驗(yàn)。4.3 輸入文件沒有音頻流map 就崩現(xiàn)象拼接命令生成了-map [outv] -map [outa]執(zhí)行到一半報(bào)Stream map [outa] matches no streams或者直接Output file #0 does not contain any stream。原因某些素材是無聲的比如屏幕錄制、監(jiān)控片段。concat濾鏡要求所有輸入要么都有音頻、要么都沒有音頻。如果你硬拼了音頻濾鏡FFmpeg 會因?yàn)檎也坏揭纛l流而失敗。解決生成前用 ffprobe 探測每個輸入是否有音頻流。探測方法放到最后一章寫。生成邏輯里加一個條件分支全部無音頻就只生成視頻濾鏡和v1:a0部分有音頻要么統(tǒng)一轉(zhuǎn)成相同的聲道數(shù)要么對無聲片段加一個安靜音軌anullsrc。這是自動生成腳本必須處理的邊界條件demo 里如果不處理只能算玩具。4.4 幀率、分辨率不一致導(dǎo)致拼接黑屏現(xiàn)象拼接結(jié)果總時(shí)長正確但中間有黑屏卡頓或者畫面突然變形。用 concat demuxer 拼接時(shí)更容易出現(xiàn)。原因不同素材的分辨率或幀率不一致FFmpeg 在 concat 時(shí)沒有統(tǒng)一規(guī)格導(dǎo)致播放器解碼吃力。另外trim之后沒有做幀率歸一化也會造成時(shí)間戳跳變。解決在濾鏡鏈里把每個輸入的分辨率和幀率統(tǒng)一。常見做法是每個視頻過濾后加scale1920:1080和fps25或者至少在 concatenated 濾鏡后加一次。自動生成時(shí)可以先用 ffprobe 讀取所有素材取最大分辨率作為統(tǒng)一目標(biāo)如果素材來自同一批錄制一般并不需要強(qiáng)制統(tǒng)一但拼接不同來源時(shí)必須有這一步。排查黑屏?xí)r先看日志里有沒有Non-monotonous DTS警告有就是時(shí)間戳問題。4.5 循環(huán)批處理時(shí)臨時(shí)文件沒清理磁盤寫滿現(xiàn)象批量跑了 200 個視頻后磁盤空間滿了任務(wù)停在某一步。刪除臨時(shí)文件后又能繼續(xù)跑。原因很多自動生成腳本會先把中間產(chǎn)物寫到臨時(shí)目錄比如生成 concat 列表文件、抽幀圖片、臨時(shí)音頻。循環(huán)里只創(chuàng)建不清理或者用了固定路徑導(dǎo)致后續(xù)任務(wù)覆蓋失敗。更隱蔽的是 FFmpeg 的-f concat列表文件如果被后續(xù)任務(wù)覆蓋前一個正在執(zhí)行的 FFmpeg 進(jìn)程會讀到不完整內(nèi)容。解決給每個任務(wù)建一個獨(dú)立的臨時(shí)目錄用tempfile.TemporaryDirectory或在主循環(huán)結(jié)束統(tǒng)一清理。生成 concat 列表時(shí)把列表文件寫進(jìn)任務(wù)自己的臨時(shí)目錄。最后再加一個兜底執(zhí)行完刪除該任務(wù)臨時(shí)目錄。我習(xí)慣于在腳本開頭用tempfile.mkdtemp()任務(wù)結(jié)束shutil.rmtree確保不會累積。日志里如果發(fā)現(xiàn)磁盤占用異常優(yōu)先查臨時(shí)目錄。5. 進(jìn)階讓自動生成更聰明——自動探測視頻參數(shù)與預(yù)覽校驗(yàn)5.1 用 ffprobe 自動獲取參數(shù)動態(tài)填充模板前面的拼接命令要求 CSV 里手動寫開始時(shí)間和結(jié)束時(shí)間這還是太笨。FFmpeg 自帶的 ffprobe 可以讀取視頻的寬度、高度、幀率、時(shí)長、音視頻流是否存在讓自動生成變成真正“自動”。比如我想寫一個函數(shù)輸入一個視頻路徑返回它的元信息字典。import json import subprocess def probe_video(path): cmd [ ffprobe, -v, quiet, -print_format, json, -show_streams, -show_format, str(path), ] info json.loads(subprocess.run(cmd, capture_outputTrue, checkTrue).stdout) streams info[streams] video_info next(s for s in streams if s[codec_type] video) audio_info next((s for s in streams if s[codec_type] audio), None) return { width: video_info[width], height: video_info[height], fps: eval(video_info[avg_frame_rate]), # 25/1 - 25.0 has_audio: audio_info is not None, duration: float(info[format][duration]), } # 示例 info probe_video(素材/clip01.mov) print(info)avg_frame_rate常見形式是25/1表示 25 幀每秒用eval直接計(jì)算分?jǐn)?shù)值但在安全敏感的場景不要用 eval可以改成Fraction或手動分割。拿到這些參數(shù)后拼接命令就可以自動決定是否需要scale、是否需要fps、是否需要音軌。前面避坑章的 4.3 和 4.4 就能靠這個函數(shù)在生成前解決。5.2 生成預(yù)覽圖與快速校驗(yàn)命令命令生成后別急著處理整段視頻可以先抽一幀看內(nèi)容對不對。抽幀校驗(yàn)也是自動生成里的一個習(xí)慣特別是帶旋轉(zhuǎn)和裁切的時(shí)候。生成一張拼出來的畫面預(yù)覽如果構(gòu)圖不對說明濾鏡參數(shù)有問題。import subprocess def make_preview(input_path, output_image, time00:00:01): cmd [ ffmpeg, -y, -ss, time, -i, str(input_path), -frames:v, 1, output_image, ] subprocess.run(cmd, checkTrue, capture_outputTrue)-ss放在-i之前是快速定位定位準(zhǔn)確但可能不是關(guān)鍵幀放在-i之后是逐幀解碼慢但精確。做預(yù)覽時(shí)放在前面就好夠用。實(shí)際流水線建議對每個拼接任務(wù)先跑預(yù)覽幀人工或腳本檢查圖像不是全黑再跑完整任務(wù)能省不少重跑時(shí)間。我自己的習(xí)慣是把這個校驗(yàn)也寫進(jìn)自動生成腳本作為步驟之間的一把鎖。FFmpegAutoGenDemo 這個方向說到底不是讓你抄一段現(xiàn)成代碼而是培養(yǎng)一種思維把視頻處理的“變數(shù)”集中到配置和清單里把命令生成邏輯做成可復(fù)用函數(shù)。等到素材從幾十個變成幾千個你會感謝當(dāng)初多寫的這幾層封裝。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取