上周同事录一场 4 小时的产品评审会,录到第 3 小时 40 分,笔记本电源被保洁阿姨踢掉。录屏软件直接消失,桌上留下一个 3.7 GB 的 MP4。
双击,转两圈,"无法播放此文件",错误码 0xC00D36C4。她第一反应是重录,可那是已经开完的会。
文件最后被我救回来了,前后大概半小时。中间走了两个弯路:先装了三个号称"一键修复"的软件,差点把文件写得更烂;后来看到命令行报错,以为没戏,其实换条命令还能接着捞。
这类文件大多数能救,前提是别再对它反复折腾。 能不能救,跟文件大小有关,跟录制时怎么设参有关,也跟"损坏"这两个字的严重程度有关。
一、MP4 到底坏在哪一块
三种常见的翻车方式
第一类是软件自己崩。录到第三、第四个小时,软件无响应或者闪退。连续高负载跑几个小时,内存和显存都在临界点上。
第二类是断电。笔记本电池耗尽、插头被拔、台式机跳闸。这是最彻底的一种,进程没有任何机会收尾。
第三类是写入中断。磁盘写满,或者外接硬盘掉线,数据流硬生生断在半路。
三种情况的共同结果是一样的:一个体积正常、却打不开的文件。
MP4 的内部结构,决定了这件事有多严重
MP4 由一个个"盒子"组成,最关键的两个是 moov 和 mdat。
moov 是索引,装着视频总时长、每条轨道的编码参数、每一帧在文件里的字节偏移、哪些帧是关键帧。mdat 是数据本体,画面和声音都在里面。
可以这么理解:moov 是目录,mdat 是正文。目录没了,书还在,但你不知道从哪一页开始读。
关键在于写入顺序。录制软件为了效率,会把 mdat 一路往后追加,moov 留到最后统一计算,因为时长、帧偏移这些数值只有全部录完才确定。正常停止录制,moov 写进文件尾部,播放器正常打开;中途中断,moov 缺失或者只写了一半,文件就打不开了。
这也是为什么同样被切断,MP4 比 MKV 脆弱得多。MKV 的结构信息可以边录边追加,中断时通常只丢最后几秒,容器本身仍然能打开。
花一分钟判断你的文件属于哪一类
别急着装工具,先做三步。
- 看文件大小。如果是 0 KB,说明录制还没真正开始写入,底层没有有效数据,可以放弃了。
- 用 VLC**试播。它能跳过损坏的帧继续读。如果能看到部分画面或者听到声音,说明 mdat 基本完好,后面的方案把握都比较大。
- 命令行执行 ffprobe broken.mp4。报 moov atom not found,就是索引丢了,重点看方案一和方案四。
这三步加起来不到一分钟。有个细节提醒一下:别用系统自带播放器反复双击测试。反复失败有时会触发播放器的错误缓存,反而干扰后面工具对文件结构的识别。
二、参数设置:崩溃后能救回多少,录之前就定了
很多人把参数当画质开关调,其实它们同时决定了另一件事:文件出问题时,你手里还剩多少可恢复的空间。
分辨率和帧率不要顶到硬件上限
1080p 加 30fps,够覆盖会议、网课、代码演示这些绝大多数场景。游戏高光可以上 60fps。一上来就 4K60,还用软编码,等于让编码器和磁盘连续满负荷跑几个小时,崩溃概率会明显上升。
码率控制:录制场景选 CQP
CBR 固定码率,每个时间片分配相同的数据量。它是给直播用的,平台需要稳定的码率输入。录制用 CBR,静态画面浪费掉大量码率,运动画面又不够用。
VBR 按画面复杂程度分配数据量,是录制里比较常规的选择。
CQP 或者 CRF 不设目标码率,让编码器按画面复杂度自己决定要用多少数据,录制场景里这个最合适。日常用 CQP 20 到 22。如果后期还要二次剪辑,调到 18,多留一点细节。
具体量级给个参考:1080p60 的内容,码率落在 12000 到 20000 Kbps 是比较稳的区间。要传到 B 站,站方建议平均码率 20000 Kbps,峰值不超过 60000 Kbps。
关键帧间隔,它决定崩溃后能救回多细
关键帧是完整画面,其他帧只记录与前一帧的差异。间隔调大,文件会小很多,代价是拖动进度条时的恢复变慢。
| 关键帧间隔 | 10 分钟文件体积 | 拖动进度条响应 |
|---|---|---|
| 30 帧 | 约 1.8 GB | 即时 |
| 100 帧 | 约 1.2 GB | 约半秒延迟 |
| 250 帧 | 约 800 MB | 约两秒延迟 |
对长时间录制来说,关键帧间隔还有一层意义:它相当于可恢复的颗粒度。数据断在关键帧之间,那一段就只能整段丢掉。录制场景直接设 0,交给软件自动算;要手动设,别超过帧率的三倍。为了省空间把它调得很大,等于拿素材安全换硬盘容量。
编码器:长录优先硬件编码
NVENC、QSV、AMF 走的是显卡上的专用编码单元,CPU 占用低,发热也小。libx264 软编码画质可控,但连续跑几个小时,CPU 满载加上高温,本身就是崩溃的诱因。有独显就先试硬件编码,占用反而更低。
容器格式:录制用 MKV,交付再转 MP4
这是整篇最值得记的一条。
MP4 的 moov 必须等录制结束才能写入。MKV、FLV 这类容器可以边录边追加结构信息,突然中断通常只丢最后几秒,文件本身还能打开。
所以稳妥的做法是:录制阶段用 MKV,交付时无损重封装成 MP4。重封装只换外壳不重新编码,几秒完成,画质不变。要提醒一句,手动把 .mkv 改名成 .mp4 不算重封装,播放器依然不认。
用 OBS Studio**的话它自带这个功能。设置里勾上"自动重新封装录像",停止录制后直接产出 MP4,原始 MKV 也保留着。
几个容易被忽略的细节
- 保存路径放固态盘,剩余空间留足 50 GB。写满即崩。
- 电源计划改成高性能,把"在此时间后关闭硬盘"设成"从不"。默认策略在无操作时会降频、休眠硬盘,长录的时候就是隐患。
- 录屏程序用管理员身份启动,加进杀毒软件白名单,免得被当成屏幕抓取行为拦截。
- 长时间录制把预览窗口关掉,能省下持续的渲染开销。
- 开启自动分段,每 30 到 60 分钟切一个文件。
三、六种救回方案
前两个是命令行手段,第三个是图形化路线,不想碰命令行的看这条。后三个解决的是"让下一次不再崩"。
方案一:FFmpeg 无损重封装,重建索引
适用:moov 还在,只是位置或顺序异常,或者结构有轻微损坏。
csharp
bash
ffmpeg -i broken.mp4 -c copy -movflags +faststart fixed.mp4
FFmpeg**的 -c copy 表示只复制音视频流、不重新编码,所以是秒级完成、画质无损。-movflags +faststart 把 moov 挪到文件开头,播放器一打开就能读到元数据。
如果上面这条报错,按顺序再试这两条:
csharp
bash
# 时间戳错乱、出现负时间戳导致无法播放
ffmpeg -i broken.mp4 -c copy -fflags +genpts -avoid_negative_ts make_zero fixed.mp4
# 中间有坏帧,忽略错误帧继续处理
ffmpeg -err_detect ignore_err -i broken.mp4 -c copy fixed.mp4
我实测过一个被截断的 MP4,第一条命令直接报 moov atom not found 退出。这说明 moov 是真的没了,重封装无能为力,得跳到方案四。这个报错本身,就是最好的分诊工具。
方案二:用 ffprobe 批量体检,别在没救的文件上耗时间
一次长录产生十几个分段,或者素材盘里堆了一堆文件的时候,先判断哪些值得动手。
python
python
import json
import pathlib
import subprocess
def probe(path: pathlib.Path) -> dict:
"""用 ffprobe 读取文件结构,能正常返回就说明容器和索引可读。"""
cmd = [
"ffprobe", "-v", "error",
"-print_format", "json",
"-show_format", "-show_streams",
str(path),
]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
tail = [ln for ln in r.stderr.strip().splitlines() if ln.strip()]
return {"ok": False, "err": tail[-1] if tail else "unknown"}
info = json.loads(r.stdout)
fmt = info.get("format", {})
return {
"ok": True,
"duration": float(fmt.get("duration", 0)),
"size_mb": int(fmt.get("size", 0)) / 1024 / 1024,
"types": [s.get("codec_type") for s in info.get("streams", [])],
}
if __name__ == "__main__":
here = pathlib.Path(__file__).parent
files = sorted(here.glob("*.mp4"))
if not files:
print("当前目录下没有 mp4 文件")
for f in files:
res = probe(f)
if res["ok"]:
print(f"[可读] {f.name} | {res['duration']:.1f}s | "
f"{res['size_mb']:.1f}MB | 轨道={res['types']}")
else:
print(f"[待修] {f.name} | {f.stat().st_size / 1024 / 1024:.1f}MB | {res['err']}")
把脚本和视频放同一个目录跑,输出长这样:
kotlin
text
[待修] broken.mp4 | 0.4MB | broken.mp4: Invalid data found when processing input
[可读] test.mp4 | 10.0s | 0.5MB | 轨道=['video', 'audio']
它会顺手把视频轨、音频轨是否齐全打出来。轨道信息对后面的判断有用:只有视频轨没有音频轨,说明音频那块写坏了;两条轨都在却打不开,基本就是索引问题。
方案三:嗨格式录屏大师,把"崩了之后"交给软件自己兜
不是每个人都愿意敲命令行。而且真正省事的思路,本来就不该等到崩溃之后再补救。
我用过一段时间这类图形化录屏软件,一开始就是图它对参数面板做得比较直白:分辨率、帧率、码率档位、编码器都能直接选,硬件加速走显卡的编码单元,CPU 占用比纯软编码低一截。它的长时间录制可以开自动分段,比如每 30 分钟拆一个文件,某一段出问题也只丢那一段。
真正减少损失的是异常中断后的处理。软件崩溃或者断电,重新启动后它会去识别本次录制的缓存文件,提示恢复未完成的录制,把已经落盘的部分导出成正常文件。这比事后从 mdat 里一点点刨数据省事,因为缓存维护发生在录制过程之中,而不是等文件坏了再补。
边界也得说清楚。这套机制依赖软件自己的缓存刷盘策略。如果断电真的发生在缓存还没写入磁盘的那一瞬间,最后几十秒到几分钟照样会丢。它的定位是降低损失,不是数据保险箱。所以就算用它,前面说的分段录制和 MKV 容器这两个习惯,我还是建议一并留着。
方案四:untrunc,moov 完全丢失时用同源文件重建
如果方案一已经判定 moov 缺失,就靠这个工具。
原理是拿一段同设备、同参数录出来的正常视频当模板,把它的 moov 结构套到损坏文件上,再按 mdat 里的实际数据重算每帧偏移。全程不重新编码,画质不变。
bash
bash
# Ubuntu / Debian 上编译安装
sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev
git clone https://github.com/ponchio/untrunc.git
cd untrunc && make
# 修复:第一个参数是正常文件,第二个是损坏文件
./untrunc -v good.mp4 broken.mp4
跑完会在同目录生成 broken_fixed.mp4。Windows 用户可以直接下预编译包,里面带一个图形界面,填两个路径点一下就行,不用编译。
参考文件挑得好不好,直接决定成败,四个条件都得满足:
- 同一台设备、同一个软件版本录出来的
- 分辨率、帧率、编码格式一致
- 录制时间接近,保证编码器版本相同
- 文件扩展名一致
同设备场景下,公开统计的修复成功率在七成到九成之间;跨设备基本没戏。另外要有心理准备:修好的文件末尾可能出现黑屏或者时间轴轻微不准,因为原始文件的尾部本来就没写完,那是真实缺失,不是修复失败。
方案五:用 OBS 搭一套"崩得起"的录制流程
与其修,不如让文件不容易死。OBS 的路径是:设置 → 输出 → 输出模式切到"高级" → 录像标签页。
- 录像格式选 MKV 或 FLV,别直接选 MP4。
- 勾上"自动重新封装到 MP4",停止录制后自动产出 MP4,并保留原始 MKV。
- 开启自动分段,按时间或者按大小切。课程、会议这类长录按时间更直观,30 到 60 分钟一段。
- 编码器按显卡选 NVENC、QSV 或 AMF。
- 关键帧间隔设 0,交给软件自动决定。
- 输出分辨率跟显示器原生分辨率对齐,别再吃一次缩放运算。
再配合管理员权限启动、保存路径放固态盘、关掉录制预览,一整套下来长录稳定性会明显不一样。这些设置花五分钟,省下的可能是四个小时。
方案六:写个脚本,把重封装和巡检自动化
天天处理素材的话,可以把"录制停止"当成一个事件来处理:文件不再变大,判定录制结束,自动重封装并归档。
python
python
import pathlib
import subprocess
SRC = pathlib.Path("rec") # 录制目录,放 MKV
DST = pathlib.Path("exported") # 导出目录,输出 MP4
def remux(mkv: pathlib.Path) -> tuple:
"""只换封装不重新编码,-c copy 保证画质无损。"""
out = DST / f"{mkv.stem}.mp4"
cmd = [
"ffmpeg", "-y", "-v", "error", "-i", str(mkv),
"-c", "copy", "-movflags", "+faststart", str(out),
]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
return False, r.stderr.strip()[-200:]
return True, f"{out.stat().st_size / 1024 / 1024:.1f}MB"
if __name__ == "__main__":
DST.mkdir(exist_ok=True)
for f in sorted(SRC.glob("*.mkv")):
ok, msg = remux(f)
print(f"{f.name} -> {'完成 ' + msg if ok else '失败: ' + msg}")
想再往前一步,可以在录制期间加个看门狗:每隔几秒检查目标文件的修改时间,超过阈值(比如 60 秒)没更新就判定录制已经停止,立刻触发重封装,并发一条通知。无人值守的挂机录制里这个挺有用,人不在现场也能第一时间知道断了。
四、输出效果参考
| 手段 | 适用情况 | 恢复程度 | 耗时 | 是否损画质 |
|---|---|---|---|---|
| FFmpeg 重封装 | moov 存在但位置或顺序异常 | 通常完整恢复 | 几十秒到几分钟 | 否 |
| ffprobe 体检脚本 | 批量判断文件可修性 | 不修文件,只做分类 | 数百个文件几分钟 | 不涉及 |
| 图形化工具缓存恢复 | 软件崩溃、断电后重启 | 通常能拿回绝大部分 | 取决于缓存落盘情况 | 否 |
| untrunc 参考修复 | moov 完全丢失 | 同设备约 70% 到 90% | 10 分钟素材几分钟内 | 否,末尾可能异常 |
| OBS 分段加自动重封装 | 预防,降低单次损失 | 单段损坏只丢一段 | 录制时自动完成 | 否 |
| 自动化脚本 | 无人值守、批量素材 | 取决于上游修复手段 | 后台运行 | 否 |
所有方案都建议先复制一份原文件再动手。修复工具写盘过程中出错,会把本来还能救的文件覆盖掉,这种损失不可逆。
五、局限和注意事项
- 0 KB 文件没有修复价值。数据从没落盘,任何工具都变不出来。
- mdat 大面积损坏救不回来。花屏、绿屏、大段丢帧,说明数据本体坏了,重封装和 untrunc 都帮不上忙。
- 参考文件跨设备成功率很低。untrunc 的 moov 模板必须匹配编码参数,随手拿另一台电脑的视频顶替,基本白跑。
- 修复后的文件不是原件。结尾黑屏、时长略有偏差都属于正常现象。
- 磁盘坏道和文件系统错误要单独排查。同一个位置反复出问题,先跑一遍磁盘检测,否则修好的文件还是会在同样的位置卡住。
- 在线修复服务有隐私成本。素材要传到别人的服务器,涉及内部会议、未公开内容时不要用。
- 依赖软件自身缓存的方案,保护范围就到缓存刷盘那一刻为止。断电时间点不巧,最后一段照样丢。
- 别在原始损坏文件上反复运行不同的修复工具。每个工具都可能改写原文件,一次试坏,后面连备份都没有。
六、FAQ
Q1:文件大小正常,双击就是打不开,一定是 moov 丢了吗?
大概率是。MP4 打不开最常见的两种结构问题,一个是 moov 缺失,一个是 moov 在文件末尾没前置。前者靠 untrunc,后者靠 FFmpeg 重封装加 +faststart。跑一条 ffprobe 就能确认,不用猜。
Q2:录屏软件为什么非要用这种"会崩"的格式?
为了方便。MP4 兼容性最好,剪辑软件、上传平台、手机播放全都认。问题不在格式本身,而在于很多软件默认把 moov 放到最后写。把容器换成 MKV 再重封装,就绕开了这个设计。
Q3:只有一段损坏的视频,没有同设备的正常文件,还能修吗?
能试的还有两条路。一是 FFmpeg 重封装那几条命令,有时结构只是轻微异常就能救回来。二是 recover_mp4**这类直接从 mdat 里抠可播放片段的工具,它不需要参考文件,代价是结果不保证完整,可能只能拿到部分画面。
Q4:修好的文件时长不对、结尾黑屏,是不是等于修复失败?
不算。原始文件尾部数据本来就没写进去,修复工具只能基于现有数据重建。判断修复成功的标准是三条:能正常打开、进度条能拖完、中间画面和声音对得上。满足就算救回来了。
Q5:用 MKV 录,剪辑软件不认怎么办?
重封装成 MP4 就行。这一步只换外壳,不是转码,画质不变,几秒钟完成。注意别手动改扩展名,那不会重组内部结构,软件照样读不了。
Q6:为什么录到一半会崩?
常见的原因有五个:参数顶到硬件上限(4K60 加软编码);磁盘写满或者写入掉速;后台程序抢占 CPU、显存和音频设备;系统的硬盘休眠和降频策略;显卡驱动版本太旧。
Q7:分段录制会伤画质吗?
不会。分段只是把文件切开,每段的编码参数完全一致。唯一的影响是段与段之间的帧不连续,剪辑时接缝处可能要单独处理一下。
Q8:断电和软件崩溃,处理方式一样吗?
判断步骤一样,都是先看大小再跑 ffprobe。区别在概率。断电更容易造成 moov 完全缺失,这种情况直接上 untrunc 更省时间;软件崩溃时 moov 有时候已经写进去了,先试 FFmpeg 重封装,说不定一条命令就解决。
七、总结:按顺序做,别乱试
- 崩了先别动原文件,复制一份出来操作。
- 看文件大小,0 KB 的直接放弃。
- ffprobe 判断 moov 是否还在。
- 在,就用 FFmpeg 重封装;不在,就用 untrunc 配同源参考文件。
- 修完检查三件事:能打开、时长合理、音画同步。
- 下一次录制前把参数改掉:MKV 容器、自动分段、硬件编码、关闭硬盘休眠、路径放固态盘。