提示

FFmpeg 视频格式转换:从 H.264 到 H.265,从 MP4 到 MOV,我踩过的那些坑

格式转换这事儿,听起来就是“换个后缀”,但实际上涉及容器、编码、参数、兼容性一大堆破事。我接过一个需求:把一批监控录像从 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%,而且控制码率不精确。能软编尽量软编。

五、实战:几种常见的转换需求

  1. 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,肉眼几乎看不出区别。

  1. 降低码率到指定大小(比如把 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
  1. 将任意视频转成兼容 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 原子移到头,方便网络播放。

  1. 调整视频分辨率并保持清晰度(缩放+锐化)
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。

  1. 转出来的 H.265 视频播放不了(VLC 可放,手机不行)
    原因:手机硬件不支持 H.265,或者编码参数太高(比如 10bit)。
    解决:强制使用 8bit 编码并降级到 Main profile:
-c:v libx265 -pix_fmt yuv420p -profile:v main
  1. 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。

  1. 转码时提示 buffer overflow
    场景:源视频码率很高(比如 50Mbps),转成低码率时缓冲区不够。
    解决:加 -maxrate 和 -bufsize 参数,或者增加 -threads。

  2. 音频和视频不同步(转码后)
    原因:源视频是 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 减少临时占用,但可能导致文件不标准。

  1. 转换后的视频在 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 三个就够了,其他的随用随查。

顶部
×
🔖
收藏本站
将本站添加到浏览器书签,方便下次访问
Ctrl + D (Windows/Linux)
+ D (Mac)