macOS 下 FFmpeg 视频转码入门及进阶使用心得

2018-02-24 21:59:26 +08:00
 mrcotter2013

如今较为常见的视频封装格式有 mp4 和 mkv 等, 内部的视频编码格式从前几年盛行的 H.264/x264 逐渐开始向新一代的 HEVC/x265( High Efficiency Video Coding 高效视频编码)过渡,而常见的音频编码格式无非 AC3、DTS 或者 AAC 等。无论是借助带有 GUI 的编码软件,还是使用命令行,FFmpeg 是最为广泛使用的工具,理论上 FFmpeg 支持各个平台,包括 Windows、macOS、iOS 以及 Android 等,这里只介绍在 macOS 下的使用。通过简单的命令,你可以大致了解 FFmpeg 在视频转换上的强大之处,视频编码部分也集中在 x264、x265,以及如何压制 macOS High Sierra 和 iOS 11 可以正确识别并生成缩略图的 HEVC 10bit 视频。最后会用一个较为复杂的例子,应用 -filter_complex 进行视频帧率的插值运算、嵌入 pgs 图形字幕,以及最后输出 HEVC 编码进行说明。

安装

如果有看过我以前文章的朋友,可能会注意到使用 Homebrew 编译 mpv 的一个重要依赖就是 FFmpeg。不过,如果将其用作视频转码,默认编译的 FFmpeg 会缺少一部分组件,因此这里可能需要重新安装 FFmpeg。以我个人编译版本为例,使用 --HEAD 来配合最新的 mpv,在 Terminal 中输入如下命令:

brew install ffmpeg --HEAD --with-fdk-aac --with-sdl2 --with-freetype --with-libass --with-libbluray --with-libvorbis --with-libvpx --with-opus --with-webp --with-x265

等待安装结束即可。

基础篇

压制 x264 编码视频文件

ffmpeg -i input.mp4 -c:a libfdk_aac -c:v libx264 -crf 20 -preset slow output.mp4

使用 FFmpeg 编码的基本规则, -i 之后的文件为输入的视频文件,即 input.mp4 ,支持的格式众多,例如 mkv、flv、vob 等等,文件可以包含目录,使用 macOS 的文件拖拽功能很方便。output.mp4 即为输出文件,文件名可自定义,视频封装格式建议对应编码格式,不应将 mpeg-2 或者 vp8 编码的视频也封装为 mp4。-c:a 之后表示输出文件的音频编码器,一般 mp4 常用的音频编码为 AAC-LC,按照官方 Wiki 指南,建议使用编码器 libfdk_aac 而不是 aaclibfdk_aac 音质更好,这也是为什么在前文中编译 FFmpeg 增加 --with-fdk-aac 的原因。-c:v 之后代表输出文件的视频编码器,使用 libx264 即可压制 x264 编码的视频流。-crf 20 代表视频编码的码率系数,数字越大,压制的效果越差,建议选择范围在 16 - 28,压制高质量的视频建议取值 20 以下。-preset slow 代表一组控制压缩时间和文件大小的参数选择,一般常选 fastmediumslow

以上都是基于 one-pass 压制,如果需要严格控制码率则需要使用 two-pass,更详细的介绍,可以参考 Encode / H.264

压制 HEVC 10bit 编码视频文件

其实 FFmpeg 很早就开始支持 HEVC (x265) 的视频转码,只是一直改动较大,而最近的版本也终于支持编码 macOS High Sierra 下 Quicktime 可以播放,并且在系统中能够正确预览并生成缩略图的视频文件。编码命令的改动很小,添加一个 format tag 参数即可,如下:

ffmpeg -i input.mp4 \
       -c:v libx265 -preset medium -crf 18 -pix_fmt yuv420p10le \
       -c:a libfdk_aac -b:a 256k \
       -tag:v hvc1 \
       output_10bit.mp4

和压制 x264 视频非常类似,主要的不同点在于 -c:v 视频编码器需换为 libx265,并且压制 10bit 需要指定色彩空间,添加 -pix_fmt yuv420p10le。在音频编码参数中,如何增加的 -b:a,可以控制音频文件的码率,按需使用。最后,非常重要的一点,必须添加参数 tag:v hvc1,这样输出的 Video Stream 会被标记为 hvc1,可以被 macOS 以及 iOS 11 原生支持播放,否则默认会被标记为 hev1,不被原生支持,第三方播放器播放倒没什么问题。

进阶篇

修改视频分辨率

假如原视频的分辨率为 1920x1080,为了降低文件大小,最简单的办法是将其转压成一个分辨率较低的版本,例如 720p,即 1280x720,那么我们可以使用 scale 视频滤镜来缩放视频:

ffmpeg -i input.mp4 -vf scale=-2:720 -c:v libx264 -crf 20 -preset slow -c:a copy output.mp4

-vf scale=-2:720 会自动计算对应的横向分辨率(需为 2 的倍数,因此为 -2 ),源文件音频编码保持不变,因此设为 copy 即可。特殊情况下,遇到源文件视频比例错误,除了修改分辨率数值,还需要设置 dar 参数,例如:

ffmpeg -i input.avi -vf scale=722x406,setdar=16/9 -c:v libx264 -c:a libfdk_aac -preset slow -crf 20 output.mp4

另外,绝对不建议增大分辨率,因为毫无意义,受限于原视频的视频质量,增大分辨率除了体积增大,画质只会更差。

反交错( Deinterlace )

偶尔我会遇到一些早期使用 VCD/DVD 时代编码的视频,其中一个重要的特点就是隔行扫描,而直接转码的结果就是视频中快速运动的物体都能看到非常明显的扫描线。解决办法同样需要应用 vf 视频滤镜中的 yadif 来进行反交错,如下:

ffmpeg -i input.vob -vf yadif -c:v libx264 -preset slow -crf 20 -c:a libfdk_aac -b:a 256k output.mp4

如果压制出来的效果不佳(还是有扫描线),可以尝试将 vf 的部分改为 -vf yadif=1:-1:0,mcdeint=2:1:10

旋转视频

需要将原视频进行旋转,同样可以应用视频滤镜来达到目的,如下:

ffmpeg -i input.mov -vf "transpose=1" -c:a copy output.mov

其中,

0 = 90 Counter Clockwise and Vertical Flip  (default) 
1 = 90 Clockwise 
2 = 90 Counter Clockwise 
3 = 90 Clockwise and Vertical Flip

如果想要 180 度翻转视频,则需要改为 -vf "transpose=2,transpose=2"。值得注意的是,旋转视频意味着对视频进行重编码,输出质量会稍微受到影响,可以添加 crf 参数控制视频输出质量,音频部分可以使用 copy

一个复杂的“栗子”

最后的这个例子,是我最近遇到的一个视频,简要的编码信息如下:

Input #0, matroska,webm, from 'Input.mkv':
    Duration: 00:23:55.97, start: 0.000000, bitrate: 16372 kb/s
    Stream #0:0: Video: hevc (Main 10), yuv420p10le(tv, bt709), 1920x1080, SAR 1:1 DAR 16:9, 59.94 fps, 59.94 tbr, 1k tbn, 59.94 tbc (default)
    Stream #0:1(jpn): Audio: flac, 48000 Hz, stereo, s32 (24 bit) (default)
    Stream #0:2(jpn): Audio: flac, 48000 Hz, stereo, s32 (24 bit)
    Stream #0:3(chi): Subtitle: hdmv_pgs_subtitle (default)
    Stream #0:4(chi): Subtitle: hdmv_pgs_subtitle

可以看到,这是一个 HEVC 10bit 编码,分辨率 1080p,帧率 59.94 fps 的视频文件,带有两条 flac 编码的音轨,另有两条是 pgs 格式的图形字幕。我自己的 Macbook Pro 已经无法完全流畅地播放这个视频了,除了 HEVC 带来的巨大计算量,高帧率也是一个麻烦,可惜网上没有其它好的片源,因此,我只有自己尝试压缩。目标:维持分辨率但帧率减半,即降为 29. 97 fps,音轨只需要第一条,并且重编码为 AAC-LC,原片为日语,因此必须带有字幕,图形字幕直接嵌入视频,最后以 HEVC 10bit 重编码,少许降低码率。

改变帧率普遍会使用 -vf fps=fps=29.97 这类的参数,但自己尝试后发现一个问题,视频观看的感觉有跳跃性,不流畅,很像是丢帧的感觉。因为将帧率减半,意味着有一半的信息都丢弃了,而普通降低帧率的算法只有简单的插值运算甚至完全没有,造成了视频不连贯的效果。因此,改变帧率正确的做法是进行运动插值运算( Motion Interpolation ),此法既可用在提高帧率上,也可以用于降低帧率,最终的结果都是提高视频播放的流畅度。这里会使用 -filter_complex 代替 vf,联合应用 minterpolateoverlay 以及 map 来解决帧率、嵌入视频,和保留一条音轨的问题。压制命令如下:

ffmpeg -i input.mkv \
-filter_complex "[0:v]minterpolate='fps=29.97:mi_mode=mci:me_mode=bidir:mc_mode=aobmc:vsbmc=1'[bg],[bg][0:s:0]overlay[v]" -map "[v]" -map 0:a:0 \
-c:v libx265 -preset medium -crf 18 -pix_fmt yuv420p10le \
-c:a libfdk_aac -b:a 256k \
-tag:v hvc1 \
output_10bit.mp4

这里看起来会很复杂,实际上 -filter_complex 的工作模式就像是 pipe,[0:v] 表示输入文件的视频流,对应 Stream #0:0。从 minterpolatevsbmc=1 都是插补滤镜的设置参数,具体的作用可以查看官方文档[bg]代表该滤镜输出后的视频流,并传递给下一个滤镜 overlay[0:s:0]表示输入文件的第一个字幕通,对应 Stream #0:3,所以如果是 [0:s:1] 则对应 Stream #0:4。 overlay 就会将该图形字幕嵌入到视频中,然后输出为 [v],进行 mapping。视频取处理后的 [v],音频取原输入文件的第一个音频通道,[0:a:0] 即代表 Stream #0:1。最后和此前压制视频的参数就一模一样了,压制为 HEVC 10bit 编码的视频文件。

需要注意的是,运动插值运算非常耗时,CPU 占用确不高,应该是 minterpolate 滤镜只能调用单核的缘故。在我的电脑上, 此 23 分钟左右的视频压制一次耗时约 20 小时,请谨慎使用。

我的原文链接: http://kris2d.info/posts/a7465832/

13112 次点击
所在节点    程序员
36 条回复
expy
2018-02-24 22:13:02 +08:00
预处理多可以考虑 vapoursynth.
Pudge1337
2018-02-24 23:28:06 +08:00
一串代码就可以对视频转码,感觉不错!
likuku
2018-02-24 23:55:31 +08:00
ffmpeg v3.4 目前不建议用,编码完成后的打包时,遇到多次无法完成打包而进入死循环 /假死。
likuku
2018-02-24 23:56:55 +08:00
3# 的状况,目前只在 macOS 上见到,多次复现。
mrcotter2013
2018-02-25 00:16:07 +08:00
@likuku #3 查看了一下 FFmpeg 目前通过 brew 安装的稳定版是 3.4.2 ( commit 6a97ba5 ),情况是否改善?

我在教程中使用的是 --HEAD 版本( commit 28924f4 ),自己平时也经常使用,不过没碰到过你说的死循环情况。
ashfinal
2018-02-25 00:30:26 +08:00
好文先马再看。
zhang1215
2018-02-25 00:33:37 +08:00
win 下一直用 ffmpeg,来学习学习
Cavolo
2018-02-25 01:13:06 +08:00
不建议笔记本,煎鸡蛋
xxx027
2018-02-25 02:08:37 +08:00
如果帧率减少了,那音频也要相应减速吧?不然会音画不同步。
xxx027
2018-02-25 02:13:45 +08:00
@xxx027 #9 又再想了一下,应该不需要。
wwqgtxx
2018-02-25 06:37:41 +08:00
很多情况下如果通过-x265-programs 设置设置 x265 的参数还能优化优化压制速度
ech0x
2018-02-25 07:58:39 +08:00
有没有类似对各种编码,封装详细解释的文章资料啊,求推荐。
mrcotter2013
2018-02-25 08:59:41 +08:00
@expy #1 嗯,很早就知道,不过一直没用过......猜测在字幕组压视频用 vapoursynth 比较多
mrcotter2013
2018-02-25 09:03:21 +08:00
@Cavolo #8 ffmpeg -threads 了解一下,另外 brew info cputhrottle 也可以了解一下

Ubuntu 等 Linux 系统有 cpulimit 可以用来限制进程对 CPU 的占用率
FindHao
2018-02-25 09:06:38 +08:00
你的这个不算入门了,这个才算:
https://help.findyoutube.net/ffmpeg
LOL
mrcotter2013
2018-02-25 09:07:26 +08:00
@xxx027 #9 当你改变了视频速率,用到了 setpts,就需要对音频进行重采样,记得是用 atempo
mrcotter2013
2018-02-25 09:07:59 +08:00
@wwqgtxx #11 太细致了,有特殊需求才会用到......
mrcotter2013
2018-02-25 09:15:47 +08:00
@ech0x #12 这个话题大概可以写一本书了.....不同的封装格式有一定的限制,支持的音视频格式也不同。

比如标准 mp4 常见视频格式 H.264/x264、x265,但也可以是 vp8、vp9,音频格式只支持 AAC ;如果要封装 AC-3 音轨,macOS 和 iOS 需要使用 m4v 封装; DTS 就需要换成 mkv 了。
yuzenan888
2018-02-25 09:34:44 +08:00
谢谢,已收藏。不过用 MacBook Pro 编码效率实在是低的可怜。
tony1016
2018-02-25 10:10:33 +08:00
非常不错

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/432359

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX