V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mrcotter2013
V2EX  ›  程序员

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

  •  9
     
  •   mrcotter2013 · 2018-02-24 21:59:26 +08:00 · 13013 次点击
    这是一个创建于 2463 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如今较为常见的视频封装格式有 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/

    第 1 条附言  ·  2018-02-25 18:28:17 +08:00

    补充一条用来自动切黑边的脚本吧,因为有时候碰到比较懒的小组发的片源,连黑边都不切,这样外挂字幕就跑到黑边去了,我很不喜欢....... 保存代码为 crop-border.sh 并修改权限,用法:./crop-border.sh input.mp4 output.mp4

    #!/bin/bash
    
    # autodetect crop size
    crop=`ffmpeg -i $1 -t 1 -vf cropdetect -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1`
    echo "detected crop fromat:	$crop"
    echo "input:			$1"
    echo "output:			$2"
    date ; read -t 5 -p "Hit ENTER or wait five seconds" ; date
    ffmpeg -i $1 -c:a copy -c:v libx265 -preset medium -crf 18 -pix_fmt yuv420p10le -tag:v hvc1 -vf $crop -y $2
    

    压制的参数 -c:a 和 -c:v 部分还需调整

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

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

    Ubuntu 等 Linux 系统有 cpulimit 可以用来限制进程对 CPU 的占用率
    FindHao
        15
    FindHao  
       2018-02-25 09:06:38 +08:00
    你的这个不算入门了,这个才算:
    https://help.findyoutube.net/ffmpeg
    LOL
    mrcotter2013
        16
    mrcotter2013  
    OP
       2018-02-25 09:07:26 +08:00
    @xxx027 #9 当你改变了视频速率,用到了 setpts,就需要对音频进行重采样,记得是用 atempo
    mrcotter2013
        17
    mrcotter2013  
    OP
       2018-02-25 09:07:59 +08:00
    @wwqgtxx #11 太细致了,有特殊需求才会用到......
    mrcotter2013
        18
    mrcotter2013  
    OP
       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
        19
    yuzenan888  
       2018-02-25 09:34:44 +08:00 via iPhone
    谢谢,已收藏。不过用 MacBook Pro 编码效率实在是低的可怜。
    tony1016
        20
    tony1016  
       2018-02-25 10:10:33 +08:00
    非常不错
    liwufan
        21
    liwufan  
       2018-02-25 10:26:21 +08:00 via iPhone
    写的挺全的,有点想看用 ffmpeg 切割合并封装之类的操作,笔记本上面视频转码太烫了
    Cavolo
        22
    Cavolo  
       2018-02-25 15:13:50 +08:00
    @mrcotter2013 thanks
    WindowPain
        23
    WindowPain  
       2018-02-25 16:34:26 +08:00 via iPhone
    一直想入门一下 ffmpeg,现在懂了点皮毛,感谢。
    likuku
        24
    likuku  
       2018-02-25 17:21:34 +08:00
    @yuzenan888 追求效率,那么 Mac 上就用 videotoolbox GPU 加速编码器,
    速度感人,12 寸 macbook 都可以飞速编码。
    yuzenan888
        25
    yuzenan888  
       2018-02-25 17:37:00 +08:00
    @likuku HEVC 10bit 可以 GPU 加速么?
    mrcotter2013
        26
    mrcotter2013  
    OP
       2018-02-25 18:20:21 +08:00
    @likuku #24 如果硬件支持,Video Toolbox 确实支持 HEVC 硬件编码和解码,不过 Mac 上有什么工具可以用?
    mrcotter2013
        27
    mrcotter2013  
    OP
       2018-02-25 18:31:10 +08:00
    @liwufan #21 参考官方指南 Concatenate: https://trac.ffmpeg.org/wiki/Concatenate

    另外,我补充一个切黑边的脚本。至于按时间分割,搜索参数 -ss
    likuku
        28
    likuku  
       2018-02-25 19:52:45 +08:00
    @yuzenan888
    @mrcotter2013

    目前 videotoolbox 的确只支持 h264 编码。N 卡在 Ubuntu 16.04 上 ffmpeg 可以用 nvenc 编 hevc/h265
    likuku
        29
    likuku  
       2018-02-25 19:56:01 +08:00
    补充:NVENC 在笔记本电脑上对显卡要求特别高,我自己试验时,只有 N 家的专业线显卡在笔记本上可以用。

    台式机,就没那么多限制了,普通 GTX 的 N 卡 就可以玩 NVENC

    追求画质的,请忽略这些,目前 GPU 加速编码的视频画质还逃不脱挑剔的视频编码大佬的鄙视。
    mrcotter2013
        30
    mrcotter2013  
    OP
       2018-02-25 20:19:32 +08:00
    @likuku #29 GPU 硬件编码得到的画质较差是共识,这么多年都这样..... 直播类型、需要实时传输的视频不追求高画质,HEVC 又能极大地缩小体积,更倾向于用硬件编码
    JerryCha
        31
    JerryCha  
       2018-02-25 21:08:18 +08:00
    gui 用户路过
    njwangchuan
        32
    njwangchuan  
       2018-04-24 10:52:23 +08:00
    @mrcotter2013 请教楼主一个问题,我有一个需求,从 mp4 文件中抽取音频,视频只有 15fps,直接抽取音频的话,时长跟原视频不一样,调研了一下是音频和容器的帧率不一样导致。

    ffmpeg -i iput.mp4 -vn -acodec copy output.aac

    原视频信息:

    Duration: 01:51:19.68, start: 0.000000, bitrate: 332 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x272 [SAR 1:1 DAR 40:17], 263 kb/s, 15 fps, 15 tbr, 16k tbn, 30 tbc (default)
    Metadata:
    handler_name : VideoHandler
    Stream #0:1(und): Audio: aac (HE-AAC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 63 kb/s (default)
    Metadata:
    handler_name : SoundHandler

    抽取后的音频:

    Duration: 01:27:37.50, bitrate: 82 kb/s
    Stream #0:0: Audio: aac (HE-AAC), 44100 Hz, stereo, fltp, 82 kb/s

    音频码率和时长都发生了变化,重新编码录音有比较耗时,请问有什么方法能快速解决这个问题,谢谢!
    mrcotter2013
        33
    mrcotter2013  
    OP
       2018-04-24 13:56:23 +08:00
    我倒没遇到过这种,所以源视频中两者的帧率就不一样?用 -map 试试呢?

    ffmpeg -i input.mp4 -map 0:1 -c:a copy output.aac

    如果仍然会变,那就必须重编码

    ffmpeg -i input.mp4 -map 0:1 -c:a libfdk_aac output.aac
    mgcnrx11
        34
    mgcnrx11  
       2019-02-08 10:17:32 +08:00
    brew 上关于 ffmpeg 的编译选项都被移除了,尝试找了一个别人维护的 formula

    https://github.com/justinmayer/homebrew-tap/blob/master/Formula/ffmpeg.rb

    tag 完后,执行编译安装的选项可能略有不同

    brew install justinmayer/tap/ffmpeg --with-fdk-aac --with-libass --with-libbluray --with-webp
    thelderfrog
        35
    thelderfrog  
       2019-03-05 12:05:06 +08:00
    dxppp
        36
    dxppp  
       2022-06-20 17:51:27 +08:00
    现在我为了压制 h265 会这么用
    ffmpeg -i input.mp4 -c:v hevc_videotoolbox -crf 20 -preset slow -c:a libfdk_aac -tag:v hvc1 output.mp4
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2642 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 10:20 · PVG 18:20 · LAX 02:20 · JFK 05:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.