格式转换这事儿,听起来就是“换个后缀”,但实际上涉及容器、编码、参数、兼容性一大堆破事。我接过一个需求:把一批监控录像从 H.264 转成 H.265,文件体积要缩小一半,还要在老旧播放器上能放。折腾了两天,换了三套参数才搞定。这篇文章把我做格式转换的经验记下来,省的以后自己再翻车。
一、格式转换到底转的是什么
很多人以为 mv a.mp4 b.mkv 就能转格式,实际上容器(封装格式)和编码(压缩算法)是两个东西:
- 容器(Container):MP4、MOV、MKV、AVI、TS…… 相当于一个盒子,里面装着视频流、音频流、字幕。
- 编码(Codec):H.264、H.265、VP9、AV1…… 负责压缩视频数据。
转换需求通常分三类:
1. 换容器不转码:速度最快,质量无损。比如 MP4 打包成 MOV。
2. 换编码:比如 H.264 → H.265,文件会变小,但要重新编码(慢,有画质损失)。
3. 改参数:比如降低码率、改分辨率、调帧率,也算格式转换的一部分。
下面分开讲。
二、安装(复用之前的就行)
FFmpeg 的安装第一篇已经详细写过。特别注意:如果要转 H.265,需要确保 FFmpeg 有 libx265。检查方法:
ffmpeg -encoders | grep x265
如果没有,请参考第一篇博客重新安装完整版。
三、快速测试:最简单的转码命令
把 H.264 的 MP4 转成 H.265 的 MP4:
ffmpeg -i input_h264.mp4 -c:v libx265 -c:a copy output_h265.mp4
音频直接复制(-c:a copy),只转视频。如果跑通了,恭喜你基础转换没问题。
四、常用格式转换参数
4.1 基本参数组合
-i input.mp4 # 输入文件
-c:v libx265 # 视频编码器
-c:a aac # 音频编码器
-b:v 1M # 视频码率(用于目标大小控制)
-b:a 128k # 音频码率
-vf scale=1920:1080 # 改变分辨率
-r 30 # 改变帧率
-f mp4 # 强制输出容器格式
4.2 常见容器转换
源格式 目标格式 命令(不转码)
MP4 → MOV ffmpeg -i a.mp4 -c copy a.mov 直接复制流,一秒完成
MOV → MP4 ffmpeg -i a.mov -c copy a.mp4 部分 MOV 转 MP4 可能需要 -movflags +faststart
MKV → MP4 ffmpeg -i a.mkv -c copy a.mp4 如果 MKV 内有 ass 字幕,MP4 不支持,需要转成 mov_text
AVI → MP4 ffmpeg -i a.avi -c:v libx264 -c:a aac a.mp4 AVI 里的编码可能是 MPEG-2 或 MJPEG,必须重编码
4.3 编码器对比(我个人选择)
编码器 参数 优势 劣势 适用场景
libx264 -c:v libx264 -preset medium -crf 23 兼容性最好,速度快 压缩率不如 H.265 通用分发、网页播放
libx265 -c:v libx265 -preset medium -crf 28 同画质下文件小 40-50% 慢,老设备不支持 存储归档、4K 视频
h264_amf -c:v h264_amf AMD GPU 加速 画质略差 快速转码
h264_nvenc -c:v h264_nvenc NVIDIA GPU 加速 同码率画质软 实时推流
踩坑:GPU 加速转出来的文件通常比 CPU 软编大 20-30%,而且控制码率不精确。能软编尽量软编。
五、实战:几种常见的转换需求
- MP4 转 MOV(不转码,保持质量)
ffmpeg -i input.mp4 -c copy input.mov
如果 MOV 要在 Final Cut Pro 里用,可能需要加 -movflags write_colr 写入颜色信息。
2. H.264 转 H.265 减小体积(保持画质)
ffmpeg -i input.mp4 -c:v libx265 -crf 24 -preset slower -c:a copy output_h265.mp4
-crf 24 在 H.265 里相当于 H.264 的 CRF 21 左右。一般 24-28 之间可以接受。-preset slower 能让文件更小,但编码时间翻倍。
实测:一段 1GB 的 H.264 视频,转成 H.265 后变成 450MB,肉眼几乎看不出区别。
- 降低码率到指定大小(比如把 500MB 压到 100MB)
先估算平均码率:目标大小(MB) * 8192 / 时长(秒) = 视频码率(kbps)
假设时长 600 秒,目标 100MB:
100 * 8192 / 600 ≈ 1365 kbps
ffmpeg -i input.mp4 -c:v libx264 -b:v 1365k -maxrate 1500k -bufsize 2000k -c:a aac -b:a 96k output.mp4
用两次编码更精确:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1365k -pass 1 -f mp4 /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1365k -pass 2 -c:a aac -b:a 96k output.mp4
- 将任意视频转成兼容 iPhone 的 H.264(基准线)
ffmpeg -i input.mkv -c:v libx264 -profile:v baseline -level 3.0 -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart output_iphone.mp4
-profile:v baseline 保证所有 iOS 设备都能硬解。-level 3.0 限制分辨率和码率。
5. 批量转换一个文件夹内的所有 MP4 到 HEVC
for f in *.mp4; do
ffmpeg -i "$f" -c:v libx265 -crf 28 -preset fast -c:a copy "${f%.mp4}_hevc.mp4"
done
小心文件名带空格——上面的 "$f" 已处理。如果源文件是 H.264,这样转没问题;如果已经是 H.265,再转一次会加倍损失。
6. 把 FLV 转成 MP4(常用于直播录制)
ffmpeg -i input.flv -c copy -movflags +faststart output.mp4
FLV 里的音视频通常已经是 H.264/AAC,直接复制流就行。加 -movflags +faststart 让 MOOV 原子移到头,方便网络播放。
- 调整视频分辨率并保持清晰度(缩放+锐化)
ffmpeg -i input_4k.mp4 -vf "scale=1920:1080,unsharp=5:5:1.0:5:5:0.0" -c:v libx264 -crf 20 output_1080p.mp4
unsharp 滤镜在缩小时增加锐度,看起来更清晰。
六、问题汇总(都是我真实遇到的)
1. Unknown encoder 'libx265'
原因:FFmpeg 编译时没开 x265。
解决:换用支持 x265 的版本,或者用 libx264 代替。检查方法:ffmpeg -encoders | grep 265。
- 转出来的 H.265 视频播放不了(VLC 可放,手机不行)
原因:手机硬件不支持 H.265,或者编码参数太高(比如 10bit)。
解决:强制使用 8bit 编码并降级到 Main profile:
-c:v libx265 -pix_fmt yuv420p -profile:v main
- MP4 转 MOV 后,颜色变灰
原因:MOV 容器默认没有写入颜色元数据(color range/primaries)。
解决:复制颜色信息:
ffmpeg -i input.mp4 -c copy -bsf:v h264_metadata=video_full_range_flag=1 output.mov
或者更简单地用 -movflags write_colr。
-
转码时提示 buffer overflow
场景:源视频码率很高(比如 50Mbps),转成低码率时缓冲区不够。
解决:加 -maxrate 和 -bufsize 参数,或者增加 -threads。 -
音频和视频不同步(转码后)
原因:源视频是 VFR(可变帧率),转 CFR(恒定帧率)时时间戳错乱。
解决:先提取时间戳或强制 CFR:
ffmpeg -i input.mp4 -vsync cfr -r 30 output.mp4
或者用 -copyts 保留原始时间戳。
6. 转换大文件时中途失败 No space left on device
原因:FFmpeg 会在输出目录创建临时文件(尤其是多 pass 编码时)。
解决:换到一个大分区,或者用 -f mp4 -movflags empty_moov 减少临时占用,但可能导致文件不标准。
- 转换后的视频在 Windows 资源管理器里不显示缩略图
原因:Windows 不识别某些编码或配置。
解决:加 -tag:v hvc1(对于 H.265)或 -tag:v avc1(H.264)来设置 FourCC 码:
ffmpeg -i input.mp4 -c:v libx265 -tag:v hvc1 output.mp4
七、一点小建议
1、能复制流就不转码:-c copy 速度极快,质量无损。只有必须换编码时才重编码。
2、先测试一小段:用 -t 30 测试参数效果,别一次转整个小时的视频。
3、CRF vs 固定码率:存档用途用 CRF(比如 23 for H.264, 28 for H.265),发布到流媒体平台用固定码率(更可控)。
4、保留原始元数据:转码可能会丢失拍摄时间、GPS 信息,加 -map_metadata 0 保留。
5、两遍编码(2-pass):当目标文件大小严格限定时非常有用,普通场景不需要。
写这篇的时候我犯了一个经典错误:想省时间用 GPU 硬件编码转一堆 H.265,结果出来的文件比原始还大。后来老老实实切回 CPU 软编,多花了一倍时间但文件小了一半。FFmpeg 的魔法在于掌握参数,而不是一味的快。
最后给出一个我的“万能转换脚本”开头:
#!/bin/bash
# ffmpeg-transcode.sh - 安全的格式转换
# 用法:./ffmpeg-transcode.sh input.mp4 output.mp4 [h264|h265]
IN="$1"
OUT="$2"
CODEC="${3:-h264}"
if [ "$CODEC" = "h265" ]; then
ENC="libx265"
CRF="28"
else
ENC="libx264"
CRF="23"
fi
ffmpeg -i "$IN" -c:v "$ENC" -crf "$CRF" -preset medium -c:a aac -b:a 128k -movflags +faststart "$OUT"
随时可以扩展。格式转换不是玄学,参数记不全没关系,记住 -c:v、-crf、-preset 三个就够了,其他的随用随查。