ffmpeg 剪静止画面完全指南:freezedetect / mpdecimate / select 三方案对决

把几个 GB 的监控浓缩成'只有画面在动的片段',是最省钱省心的视频瘦身法。我在这台机器上把 ffmpeg 的每一条剪静止路线都跑了一遍:freezedetect 精确切段、mpdecimate 流式丢帧、select 保帧重排,外加 AV1/H.264 硬编。这篇是整个过程的路线图 + 血泪账。

环境:Core Ultra 5 235H / Arc 140T 核显(QSV 硬编)/ ffmpeg 9.0.1(gyan essentials)/ 测试源 1080p 10fps 监控段、180s 3 段目录。2026-09-11~12 实测。

三条路的定位

方案原理音频速度
freezedetect 检测 + atrim 精确切段先检测冻结区间,再按同边界切完整保留、严格同步~7.6s/目录(三方案最慢)
mpdecimate 单命令丢帧流式判重丢帧 + setpts 重排必须 -an 丢弃2.6~3.4s(最快)
select 保帧重排按活动帧 select + setpts 重排视频稀疏 vs 音频连续→不同步3.8~4s

方案一:freezedetect + 精确切段(音画最稳)

逻辑最直白:freezedetect 找出'画面冻结 ≥ 阈值'的时间段,反向得到活动区间(间隙 <5s 合一块),然后视频音频用相同的 atrim 边界切段再拼接。天然同步,因为音视频共享同一个时间轴。

:: 每段:视频音频同边界 -ss/-to,天然同步
ffmpeg -f concat -i list.txt -ss S -to E -c:v libx264 ultrafast -c:a aac seg.mp4

:: 合并后 AV1 硬编(Arc 140T av1_qsv preset7 gq27)
ffmpeg -i joined -filter:a atempo=5 -vf setpts=0.2*PTS -c:v av1_qsv -preset 7 -global_quality 27 out.mp4

实测(A 真实目录):7.6s,压缩 3.4x,V=30.93/A=30.91s 严格同步,音频完整保留。这是'声音有价值必须留'场景下的唯一正解——监控里的家人说话声不能丢。

n 阈值扫描:噪点 vs 静止的分界线

核心参数是 freezedetect=n=-50dB:d=...。n 是判定'变化'的灵敏度,对晚上全是噪点的监控,这个值决定你以为的'静止'是不是真的静止:

n(dB)活动秒占比段数
-60dB140.5s78%4
-55dB75.1s42%15
-50dB68.0s38%5
-48dB46.0s26%11
-45dB82.1s46%12
-42dB79.6s44%13

-48dB 是这一条曲线的'峡谷'(26%),-60dB 和 -45dB 反而更高——在噪点敏感区间 n 不是单调的,跳来跳去。实用的取法是分场景:晚上无人用 -48dB、白天有人用 -50dB(白天画面差异大,不会误剪有人活动,详见核显硬编实测)。

方案二:mpdecimate 单命令丢帧(最快,代价是没音频)

mpdecimate 是流式去重滤镜,边解码边丢'几乎一样的帧',不需要先检测。一条命令同时完成丢帧 + 重排 + 倍速 + 硬编:

ffmpeg -f concat -safe 0 -i list.txt ^
  -vf "mpdecimate=lo=768:hi=1536:frac=0.33,setpts=N/FRAME_RATE/TB,setpts=0.2*PTS" ^
  -an -c:v av1_qsv -preset 7 -global_quality 27 out.mp4

实测 A(无人夜)2.6s、B(白天有人)3.4s,压缩约 3x——三方案里最快的。但对监控来说音频必须丢(-an):视频丢帧后时间轴缩短,音频却全程保留,两者不可能对账。这条只适合'音频本来就是噪声'的场景。

方案三:select 保帧重排(音画死结)

select 方案按'活动帧'保留再 setpts 重排,但视频是按帧丢的(稀疏)、音频是按时间段留的(连续),粒度根本对不上。实测 A 目录 V=13.65s / A=30.85s 差了一倍多。结论:select 只丢帧不删时间,时长不缩短,音画无法同步,不可用

高频坑:trim/atrim 混用与 VFR 漂移

坑 A:视频用 trim、音频用 atrim,混用必报 Error linking filters。atrim 是音视频通用滤镜,和 setpts/fps 这类视频滤镜串联时类型协商失败。分家:视频专属 trim,音频专属 atrim,一条 filter_complex 才跑得通。

坑 B:必须先 fps 重采样成 CFR 再 select,否则 VFR 帧数漂移导致音画错位。同样,'逐文件 VFR 直读检测'会误判噪点(A 报 73% vs concat 12%),concat 统一 CFR 才是正确检测

最终交付形态

目录成片压缩率同步差
A(无人夜)13.5MB/180s6.4MB2.1x0.13s
B(白天有人)20.1MB/180s5.7MB3.5x0s
C(晚9点)~20MB/180s9.5MB2.1x0.12s

定码率 700k(≈源码率)是关键:监控源本身才 ~600k,同码率重编码质量不损失,剪静止自然带来压缩。给人看的话,最终我交付的是'剪静止 + 5 倍速 + 单条流 + H264'组合——A 1.4MB、B 1.9MB、C 2.3MB,音画同步 0.06s。但记住:剪静止的前提是'没人没动作',语义级的'有没有人'要用 YOLO 检测(见NPU 人形检测实战)。

提示:音频必须保留时用方案一(freezedetect + 同边界 atrim,严格同步);音频是噪声用方案二(mpdecimate,2.6s 极速);两者的编码器都建议 av1_qsv p7 gq27(画质/体积/速度平衡,详见 Arc 硬编实测)或 H264 -b:v 700k 全兼容。数据来自本机实测,仅供参考。

Condensing gigabytes of surveillance into 'only the moments where the picture moves' is the cheapest satisfying video shrink. I ran every ffmpeg static-cutting route on this box: freezedetect precise-trim, mpdecimate stream drop, select keep+resample, plus AV1/H.264 hardware encode. This is the roadmap and the battle scars.

Env: Core Ultra 5 235H / Arc 140T (QSV encode) / ffmpeg 9.0.1 (gyan essentials) / 1080p 10fps surveillance, 3×60s per dir. Tested 2026-09-11~12.

Where each route sits

ApproachMechanismAudioSpeed
freezedetect + atrim precise-trimdetect freeze windows, then trim same boundskept, in sync~7.6s/dir (slowest)
mpdecimate single-passdedupe frames + setpts resampledrops it (-an)2.6–3.4s (fastest)
select keep-frameselect active frames + resamplesparse V vs continuous A → desync3.8–4s

Route 1: freezedetect + precise-trim (best AV sync)

Most straightforward: freezedetect finds windows frozen ≥ threshold, invert to activity intervals (merge gaps <5s), then cut video and audio with the same atrim bounds and stitch. Naturally in sync, because both streams share one timeline.

:: 每段:视频音频同边界 -ss/-to,天然同步
ffmpeg -f concat -i list.txt -ss S -to E -c:v libx264 ultrafast -c:a aac seg.mp4

:: 合并后 AV1 硬编(Arc 140T av1_qsv preset7 gq27)
ffmpeg -i joined -filter:a atempo=5 -vf setpts=0.2*PTS -c:v av1_qsv -preset 7 -global_quality 27 out.mp4

Measured (real A dir): 7.6s, 3.4× compression, V=30.93/A=30.91s locked sync, full audio. The only correct answer when the audio matters — those family voices in the footage can't vanish.

The n-threshold sweep: noise vs stillness

The core flag is freezedetect=n=-50dB:d=.... n is the change-sensitivity that decides whether noisy night footage counts as 'still':

n(dB)activesharesegments
-60dB140.5s78%4
-55dB75.1s42%15
-50dB68.0s38%5
-48dB46.0s26%11
-45dB82.1s46%12
-42dB79.6s44%13

-48dB sits at the valley of this curve (26%); both -60dB and -45dB read higher — around noisy-sensitivity n isn't monotonic, it jumps. Practical split: -48dB for empty nights, -50dB for busy days (daytime differences are large enough not to falsely cut people — see hardware-encode tests).

Route 2: mpdecimate single-pass (fastest, minus audio)

mpdecimate is a streaming dedupe filter — it drops near-identical frames while decoding, no pre-detection pass. One command does drop + resample + speed + hardware encode:

ffmpeg -f concat -safe 0 -i list.txt ^
  -vf "mpdecimate=lo=768:hi=1536:frac=0.33,setpts=N/FRAME_RATE/TB,setpts=0.2*PTS" ^
  -an -c:v av1_qsv -preset 7 -global_quality 27 out.mp4

Measured: A (empty night) 2.6s, B (busy day) 3.4s, ~3× compression — the fastest of all three. But audio must be dropped (-an): dropped frames shorten the video timeline while audio keeps running; they can never reconcile. Only for footage whose audio is pure noise.

Route 3: select keep-frame (the AV deadlock)

select keeps 'active frames' then resamples, but video drops per-frame (sparse) while audio stays per-interval (continuous) — the granularities never match. Measured: A dir V=13.65s vs A=30.85s, off by more than half. Verdict: select drops frames but not time; duration won't shrink; AV can't sync; unusable.

Frequent traps: trim/atrim mixing & VFR drift

Trap A: video needs trim, audio needs atrim — mixing throws Error linking filters. atrim is a general AV filter; chaining it with video-only filters (setpts/fps) fails negotiation. Split: trim for video, atrim for audio, in one filter_complex.

Trap B: sample to CFR with fps before select, or VFR frame-count drift desyncs the AV. Similarly 'per-file VFR direct-read detection' misjudges noise (A floor 73% vs concat 12%) — concat unifies to CFR, which is the correct detection.

The final delivered form

DirSourceOutputRatioSync
A (empty night)13.5MB/180s6.4MB2.1x0.13s
B (busy day)20.1MB/180s5.7MB3.5x0s
C (9pm)~20MB/180s9.5MB2.1x0.12s

Constant 700k (≈ source bitrate) is the key: the source sits at ~600k, so same-bitrate re-encode loses no quality, and cutting stills does the compressing. For human viewing I shipped 'cut-still + 5× + single stream + H264' — A 1.4MB, B 1.9MB, C 2.3MB, sync 0.06s. But remember: cut-still assumes 'nobody/nothing moves'; the semantic 'is there a person' needs a detector (see YOLO person-cut).

Tip:Keep audio → Route 1 (freezedetect + same-bound atrim, locked sync); audio is noise → Route 2 (mpdecimate, blazing 2.6s). For encoder prefer av1_qsv p7 gq27 (see Arc encode tests) or H264 -b:v 700k for compatibility. Numbers are local measurements for reference.
返回文章列表