水族馆

ffmpeg 转码心得

格式 / 编码 / 码率 / 硬解 / 分布式

阅读时间 2 分钟


前言

请注意,本文创建于 2019 年,其中涉及的 h264 和 h265 的兼容性的问题可能已经不再符合如今的实际情况。

本菜鸡用 ffmpeg 也有一段时间了,平时都是用来压制冻鳗、演唱会或者视频录像,试来试去也踩了不少坑…于是决定水一篇

这篇文章的目的,是想介绍一些常用、好用、能背下来的参数,以及一些基本的编码知识。

常用选项

-c:a copy

这是我常用参数之一,-c:a copy 表示不会对音频进行重新编码

为啥?

首先音频轨道的体积相对于整个文件来说是比较小的,一般来说,对音频重新压缩体积小不了多少,但是音质会有明显的降低,是得不偿失的

其次,就算音频轨道是 FLAC 96000kHz 24bit,也不会占用过多空间。并且本人认为无损音频是珍贵值得保留的。

-c:a aac -ab 64k -ac 1

使用 aac 编码器(mp4 格式最常见的编码器)。音频码率限制在 32k。音频通道数量为 1(也就是所谓的单声道)

与影视作品不同,一般的录音录像更有可能是电话级别的音质。主要内容是人声,并且带有严重失真和底噪。这种音频多给 1kb 都是浪费。

如果你确定录音设备是不是单声道,那么可以忽略 -ac 1 参数。但 64k 的码率对于人声来说仍然是足够的。

码率不是按声道平均分配。64k 双声道不代表每个声道只有 32k 的码率。编码器的算法可能会提取双声道中相同的部分(中置声道)并将调整码率分配的方式。

-c:v h264

H264 大概是我最常用的视频编码方式了

平常所说的 .avi .mp4 .mkv 都是视频格式,是一种容器,容器里包含了一个或多个视频/音频轨道,每一个视频轨道的编码方式称之为视频编码,常见的视频编码格式有 h264 h265 vp9 mpeg4 等等…

为什么用 h264?

就目前情况来看,h264 是压缩率比较高,支持设备比较多的一种格式

支持设备多意味着能够被硬解码,硬解意味着能够调用 GPU 而不是 CPU 更加流畅、省电地播放视频

h265 可以说是 h264 的进化版本,相同视频质量下,体积可以减少 40%左右,一般用于高清视频的压制

市面上大多数新出的设备都能硬解 h265,但还有更多的老设备在播放 h265 仍然只能软解,cpu 占用 100%,风扇呼呼响,却只能看个 ppt

ffmpeg 的 h264 编码默认采用 vbr 码率控制方式,VBR 会根据视频内容动态调整码率,视频变化多的时候分配高码率,视频画面静止的时候分配低码率,优点是能保证视频质量,缺点是无法预测视频大小,如果用于直播,可能会出现卡顿。

除此之外,还可以用 crf 值来指定 VBR 的码率,crf 的取值范围介于 0-51,一般来说,18-28 是一个合理的范围,默认值为 23,18 被认为是视觉无损,因此如果觉得 vbr 码率偏高或偏低,可以适当调整 crf。

若 Crf 值加 6,输出码率大概减少一半;若 Crf 值减 6,输出码率翻倍(在 8bit 的前提下)。

-preset medium

使用预设,预设是一系列参数的集合,不同的预设对应不同的编码速度和压缩率,编码速度越慢,压缩率越高。

假如你很有耐心,通常的建议是使用较慢的预设,目前所有的预设按照编码速度降序排列

  1. ultrafast
  2. superfast
  3. veryfast
  4. faster
  5. fast
  6. medium
  7. slow
  8. slower
  9. veryslow
  10. placebo

默认值是 medium,请不要使用 placebo,因为它以极高的编码时间为代价,仅仅提升了 1% 左右的压缩率,这是一种收益递减准则,veryslow 与 slower 相比提升了 3%;slower 与 slow 相比提升了 5%;slow 与 medium 相比提升了 5%~10%。你可以使用 --preset 来查看预设列表,也可以通过 x264 --fullhelp 来查看预设所采用的参数配置

在测试中,用不同的预设重现编码动画测试片段

ffmpeg -i test.mp4 -c:a copy -c:v libx264 -preset [preset] test_[preset].mp4

文件大小结果如下(test.mp4 是源文件)

文件大小结果

如果说 medium 的编码速度为 4x,大致来说每个等级之间相差 1x,到 veryslow 时甚至慢到 0.5x

而 superfast 速度飙到 15x,utralfast 更是快到了 20x

-tune animation

-tune 的参数主要配合视频类型和视觉优化的参数,或特别的情况。如果视频的内容符合其中一个可用的调整值又或者有其中需要,则可以使用此选项,否则建议不使用(如 tune grain 是为高比特率的编码而设计的)。

tune 的值有:

再来测一波-tune(test.mp4 是源文件,normal 是不加 -tune 参数的结果)(再说一遍,用的测试文件动画片段)

ffmpeg -i test.mp4 -c:a copy -c:v libx264 -tune [tune] test_[tune].mp4

测试结果

不出所料,animation 对动画的编码效果是最好的

关于显卡加速

压制发布视频一般不推荐使用,几乎所有情况下显卡编码质量都没有 CPU 编码好。编码质量是指在相同码率的情况下的视频质量。这是因为硬件编码芯片将算法写成电路并行运算加速,而 CPU 能使用最新的算法进行更复杂的编码。

但是,它快啊,快到离谱

市面上大部分新的 intel CPU 都有 QSV 芯片模块,用于加速视频的编码解码

查看 ffmpeg 支持的 qsv 编码器,在 windows 下是 findstr,在 linux 下是 grep

ffmpeg -decoders | findstr qsv

然后瞎用就是了

ffmpeg -i test.mp4 -c:v h264_qsv -c:a copy output.mp4

详情参考 ffmpeg 使用 QSV 硬件加速视频转码 或自行问 Google

又快又好 - 分布式转码

原理很简单,主机用 ffmpeg 将原视频无损切片(切片速度很快,跟复制差不多),将切好的视频片段分发到多个 Worker 中,多个 Worker 同时开始转码视频片段,转码完成后发送回主机,主机再将所有视频片段拼接

有人用 hadoop 实现了,我觉着就是切香蕉用杀猪刀了

python 写百来行我觉着就够用(不考虑 Worker 宕机,假设网络稳定)(挖坑预定)

切片

ffmpeg -i input.mp4 -acodec copy -vcodec copy \
-f segment -segment_time 20 -reset_timestamps 1 -map 0:0 -map 0:1 %d.mp4

转码

ffmpeg -i input.mp4 -vcodec libx264 -s 1280x720 output.mp4

合并

ffmpeg -f concat -i filelist.txt -c copy output.mp4

参考 分布式转码(三)实现