admin管理员组

文章数量:1531505


生活中,你敢拍视频吗?

哦,我是指,你手机就128G那么点存储,拍一段10分钟的 1080P 视频都能占你 1.5GB 空间,拍1小时的 1080P 视频就能占用9个GB的空间!256G也禁不住你折腾!

除了挤占你本地的存储,还占用你的云存储空间,15个GB的云空间,相册一同步几个拍摄的视频就挤满了!

恐怕,你生活中不愿意拍视频,很大原因是怕视频占存储空间吧!

你可能想过“为什么,我下载的2个小时的超清电影也就一点几个GB,我拍20分钟的视频就要3GB大小?太奇怪了!”

其实,你下载的那些1GB大小的电影,原始数据都是好几十GB大小的,只是经过了压制组几十个小时甚至十几天的压制(用的是服务器级别16核处理器),才在保证画质的前提下,将体积压缩到了几十分之一。

越高的压缩比需要更长的时间,而且不是线性关系。

当然,你可能不需要那么夸张的压缩比,只希望拍摄的视频能减小到 1/3 到 1/10 的大小,例如你希望拍20分钟的1080P 60fps视频只占用0.3GB,而不是3GB;希望1080P录屏5分钟只占用30MB,而不是150MB。而这个愿望,完全可以得到满足!

FFmpeg 是一个开源的强有力的音频和视频转换器(你见过的各种音视频转化器、播放器中很多都是ffmpeg的套壳应用)。论质量,就不要提其它什么xx工厂、xx视频转换器,因为让ffmpeg转化出最佳大小,体积能减小到1/10,画质很少损失,而其它转换器转到这个大小,画面都已经没法看了!

举个例子,我用红米K20P拍了5秒的4K 60fps视频,占用26MB,经过-pass 2,二压,总共压制用时1分钟,体积缩小到了4.96MB,画质没有明显减弱。

一段5秒1080P 60fps视频,占用13.28MB,经过-pass 2,二压,总共压制用时21秒,体积缩小到了2.06MB,我自己肉眼所见,很难察觉到画质有损失。

如果你只求视频体积缩小到拍摄体积的1/3,压制速度还能更快很多!例如我的一段10秒1080P 60fps的视频,占用25.29MB,经过用 -crf 24 参数压制,用时32秒,压制到了6.26MB,画质依然杠杠的。

既然ffmpeg辣么强大,有啥缺点呢?只有一个:它是命令行工具,需要经过学习才能使用,对没有学习能力的用户是禁区。就像飞机仪表盘上有一大堆按键一样,一个强大的工具都需要学习过后才能使用,学过就不难了。

现在旗舰手机的性能都很强大了,例如上面的压制成绩,就是我手上K20p的骁龙855的成绩。用手机在空闲时间(例如用户晚上睡觉的时间)压制白天拍摄的视频,是可以做到很可观的存储节约的。假设一晚上压制5个小时,就可以将一个半小时的、占用13.5GB的1080P 60fps视频压制到1/4 体积大小。(这里我们就可以看到,即便手机电脑性能再强,遇到吃CPU的任务,例如,压制视频,性能也永远不够)

在手机上有使用ffmpeg有两种方法,一种是安装Termux,用“pkg install ffmpeg”安装后直接使用;第二种是使用ffmpeg的图形界面app,在这里我推荐 FFmpeg media encoder,作者在保持更新,软件免费好用,可以存储许多预设,不用每次动手输入,只是内置一个小横条广告,捐赠1.49刀后可以去除。

FFmpeg Media Encoder

Termux

Termux在各种软件市场都能下载到,ffmpeg media encoder在酷安中搜索就可以看到有大佬搬运(有能力的直接上play商店下载就行),我现在用的最新版是3.0.9。

我手上没有 ios 设备,但我相信 ios 上也一定有好用的命令行工具和FFmpeg GUI工具。

但是,使用上面两种工具,都还是需要和命令行接触,即便网上有很多命令抄过来就能用,但知其所以然更能帮助我们得到更好的音视频处理能力,不管是对以后生活还是工作都有帮助,再也不用去漫天寻找音频、视频的提取、合并、转码、压制、裁剪工具了。

之前要把一个音画不同步的视频同步下,是要花很大力气并且有损的,用上FFmpeg后,就可以画质无损、一条命令解决了。

我有幸前几天认真阅读了ffmpeg的manpage,现在已经能做简单的压制、提取、打包了,不过读一遍印象不够,而且因为manpage是英文,复习效率不高,所以结合网上的机器翻译中文文档(网上的翻译的中文文档质量真是难堪),做一个自己的精校版学习笔记,写这个的首要目的是为了方便自己查阅复习用。

正文由此开始

\1. 语法

 FFmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...

 FFmpeg [全局选项] {[输入文件选项] -i 输入文件路径} ... {[输出文件选项] 输出文件地址} ...

 其中,-i 表示 input,[] 括起来表示可选项,{} 括起来表示一个或多个必输入项,括号外的全部必输入。 

\2. 描述

 FFmpeg是一个非常快的视音频、视频转换器,他也可以从直播音频、视频源进行抓取。他可以飞快地在各种不同的采样率之间进行转换、调整视频大小、使用高质量多相滤镜。

 FFmpeg读取一个或多个输入文件(可以是普通文件、管道文件、网络视频流、抓取设备等等)(这些输入文件被 -i 选项指定),然后写到一个或多个输出文件(被一段文本路径指定)。在一行命令中任何不能被识别为选项的内容都会被当成输出路径对待。

 每个输入或输出路径可以,原则上,包含许多个不同种类的“流”(视频流、音频流、字幕流、附件流、数据流)。允许包含多少个流,受文件封装格式限制。可以自动或用 -map 选项(参阅“流选择”章节)手动选择哪个文件的哪个流写到哪个输出文件。

 在选项中涉及输入文件时,你必须使用它们的索引号(从0开始的序号)。例如,第一个输入文件是0,第二个文件是1,以此类推。同样地,涉及一个文件内的流时也是使用这样的索引。例如,2:3(注意这里是英文冒号)表示第三个输入文件中的第四个流。(参阅“流选择”章节)。

 作为总的原则,选项总是只作用在它之后的那个文件上。因此,顺序很重要,你可以在一行命令中使用同一个选项多次。每次使用这个选项,都只作用在后面那个文件上。有一种例外,就是全局选项(例如啰嗦提示级别)(应当被首先指示出)。

 不要混淆输入和输出文件,输入文件是先标出,输出文件是后标出。也不要混淆本该属于不同类型文件的选项。所有选项只对它后面那个文件起作用一次,然后就会被恢复默认值。

  *设置输出视频的码率为64Kbps:

   FFmpeg -i input.avi -b:v 64k -bufsize 64k output.avi

  *强制输出视频帧率为24fps:

   FFmpeg -i input.avi -r 24 output.avi

  *强制输入文件帧率为1fps,同时输出视频为24fps:

   FFmpeg -r 1 -i input.m2v -r 24 output.avi

 处理的输入文件为原始数据(raw input file)时,可能需要一些格式选项。

\3. 详细描述

 FFmpeg对输出文件的转码过程可用下图简述:

 (纯文本不好画,教程中所有的图,请到 http://FFmpeg/FFmpeg.html 查看)

 FFmpeg先调用包含分流器(demuxer)的libavformat库读取输入文件,得到数据包(内含被编码后的数据)。当存在多个输入文件时,FFmpeg 将通过跟踪最小的时间戳来尝试同步所有活跃的输入流。

 编码数据包然后被传递到解码器(除非复制流被选择用于流,详见后面进一步的说明),解码器产生的未压缩的帧(原始视频/PCM音频/…),后者可以进一步被滤镜处理(见下一节)。通过滤镜处理后,这些帧被传递到编码器,编码器将其编码,并输出编码后的数据包。最后,这些数据包被传递给混合器,以将编码数据写入到输出文件。

3.1 滤镜

 在使原始数据编码之前,FFmpeg 可以使用libavfilter库中的滤镜处理原始音频和视频帧。几个连接的滤镜可以形成一个滤镜组(filtergraphs)。 FFmpeg有两种filtergraphs:简单和复合。

3.11 简单filtergraphs

 简单filtergraphs是那些恰好只有一个输入和输出、并且输入输出都是相同类型的滤镜组。在下图中,它们可以被表示为,简单地在解码和编码之间插入一个附加步骤:

 简单filtergraphs由对每个流的滤镜选项(-vf 和 -af 分别表示针对视频和音频的滤镜)配置。一个针对视频的简单FilterGraph可以看下这个例子:

 需知晓的是一些滤镜只改变帧的属性而不触及帧的内容。例如在上例中,fps滤镜只改变帧的数量,不改变帧的内容。又如setpts滤镜,仅设置时间戳而保持帧不变。

3.1.2 复合filtergraphs

 复合filtergraphs 是那些不能被描述为简单的线性处理链的滤镜组。例如,当滤镜组具有多个输入和(或)输出,或当输入和输出流是不同的类型。它们可以被表示为以下图:

 复合filtergraphs可使用 -filter_complex 选项指配。注意,此选项是全局性的,因为复合FilterGraph,就其本质,不能只与单个流、单个文件相关联。

 -lavfi 选项相当于 -filter_complex 选项。

 一个复合 FilterGraph 的简单的例子是 overlay 滤镜,它具有两个视频输入和一个视频输出,内有一个视频叠加在另一个视频上面。其对应功能的音频滤镜是 amix 滤镜。

3.2 复制流

 复制流是通过添加 copy 选项到 -codec 选项完成的。它使 FFmpeg 对指定的流忽略解码和编码步骤,所以它只能混合和拆包(例如从一个mkv文件拆包提取出视频流,放到一个mp4输出文件内打包,就仅仅是把视频流复制了,达到了无损转格式)。它对于改变所述容器的格式或修改容器级的元数据是有用的。在这种情况下,可以简化为这样:

 由于不存在解码或编码,复制流非常快(几乎等于复制粘贴的速度),没有画质或音质损失。然而因为许多因素,某些情况下可能无法使用。应用滤镜显然也是不可能的,因为滤镜仅能作用在未压缩的数据上(而拆包后的数据流都还是被编码了的、压缩了的数据)。

\4. 流的选择

 FFmpeg 提供 -map 选项来手动控制每个输出文件中流的选择。用户可以跳过 -map 选项,让 FFmpeg 执行自动流选择(规则在下面提到)。不管是手动映射(map)还是自动选择,使用 -vn / -an / -sn / -dn 选项分别可以跳过输出文件对视频流、音频流、字幕流、数据流的选择(不过对那些复合filtergraphs 输出的流不起作用)。

4.1 描述

 接下来的小节叙述了涉及到流的选择的各种规则。后面会有例子帮你理解。

 虽然,为了能够精确的反映程序的行为,文档作者们付出了很多努力来完善这个文档,但是FFmpeg 是在被持续开发和完善的,自从写下这个文档,有些代码就可能已经改变了。

4.1.1 自动流选择

 对于某一个输出文件,在映射(map)选项缺失的情况下,FFmpeg 会检查输出文件格式,看哪些种类的流可以被打包进去,例如视频流、音频流、字幕流。对于每个可以打包进去的种类,FFmpeg 会从所有可用的输入文件中选择一个流。

 FFmpeg 会基于以下原则选择流:

  *对于视频,选择最高分辨率的流

  *对于音频,选择有最多声道的流

  *对于字幕,选择第一个被找到的流,但是警告,输出文件的默认字幕编码器可能是基于文本的、也可能是基于图像的,只有与输出文件字幕类型相同的字幕流才会被选择。

 在相同种类的几个流的参数同样高时,选择索引号最低的那个流。

 数据流或附件流不会被自动选择,他们只能被 -map 选项选择。

4.1.2 手动流选择

 当 -map 选项被使用时,除了下面会提到的 filtergraph 输出流,只有用户映射的(user-mapped)流会被包含在那个输出文件。

4.1.3 复合filtergraph

 当有一些复合filtergraph输出流带着未标记的 pads 时,这些输出流会被加到第一个输出文件。当这个流不被输出文件的格式支持时,就会导致严重错误。当没有 -map 选项时,这些复合filtergraph输出流的包含,会导致它们类型的流的自动选择被跳过。当有 -map 选项时,除了被手动映射的流,这些复合filtergraph输出流也会被额外囊括进去。

4.1.4 流处理

 流处理是独立于流选择之外的(除了下面会讲到的字幕是例外)。流处理是通过-codec 选项来设定的,作用于指定输出文件内的流。尤其是,FFmpeg 是在流选择过程之后才实施流处理过程的,所以流处理过程不会影响流选择。如果对某个流没有指定 -codec 选项,FFmpeg 会根据输出文件的混合器(muxer)选择默认注册的编码器(encoder)。

 对于字幕存在例外。如果一个输出文件的字幕编码器被指定,第一个被找到的字幕流(不管是文字、还是图像类型)都会被打包进去。FFmpeg 不检查指定的编码器是否能转换选择的流,也不检查转换后的流是否能被输出文件的格式兼容。这条规则是通用的:当用户手动设定一个编码器时,流选择过程无法检查被编码过的流可以被混合到输出文件中。如果不能,FFmpeg 会放弃所有输出文件,然后处理过程失败。

4.2 范例

 接下来的例子会阐释FFmpeg 流选择方法的行为、难点和限制。

 假设有下列三个输入文件:

  input file 'A.avi'

    stream 0: video 640x360

    stream 1: audio 2 channels

  input file 'B.mp4'

    stream 0: video 1920x1080

    stream 1: audio 2 channels

    stream 2: subtitles (text)

    stream 3: audio 5.1 channels

    stream 4: subtitles (text)

  input file 'C.mkv'

    stream 0: video 1280x720

    stream 1: audio 2 channels

    stream 2: subtitles (image)

 例子:自动流选择

  FFmpeg -i A.avi -i B.mp4 out1.mkv out2.wav -map 1:a -c:a copy out3.mov

 这里指定了三个输出文件,对于前两个,没有设定 -map 选项,所以FFmpeg 会为这两个文件自动选择流。

  out1.mkv 是一个Mastroska 容器的文件,可以存放视频、音频、字幕流,所以FFmpeg会每个种类选一个流。

   对视频流,会选择 B.mp4 中的 stream 0,也就是有最高分辨率的那个;

   对音频流,会选择 B.mp4 中的 stream 3,也就是有最大声道数的那个;

   对字幕流,会选择 B.mp4 中的 stream 2,也就是 A.avi 和 B.mp4 中第一个字幕流。

  out2.wav 只接受音频流,所以只有 B.mp4 中的 stream 3 会被选择。

  对于 out3.mov,因为有了 -map 选项,就不会发生流的自动选择了。-map 1:a 选项会选择第二个输入文件 B.mp4 中的所有音频流,不会有任何其它流被打包到这个输出文件。

 例子:自动字幕选择

  FFmpeg -i C.mkv out1.mkv -c:s dvdsub -an out2.mkv

 尽管out1.mkv是一个能够容纳字幕流的Matroska容器文件,但这个例子里只有视频和音频流会被选择。C.mkv 里的字幕是基于图像的,而Mastroska的默认字幕编码器是基于文字的,所以转码会失败,因而字幕流不会被选择。不过,out2.mkv 里,我们在命令里手动指定了一个字母编码器,所以,除了视频流,字幕流也会被选择加进out2.mkv。-an 选项禁止了 out2.mkv 的音频流选择。

 例子:未写标签的filtergraph 输出

  FFmpeg -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt

 这里我们通过-filter_complex 选项设置了一个filtergraph ,后者包含一个视频滤镜。overlay 滤镜要求正好两个视频输入,因为没有指定哪两个,所以会默认使用前两个可用的视频,也就是A.avi 和C.mkv 。由于这样,本来会选择 B.mp4 里面的流的自动视频流选择过程也就被跳过了。在B.mp4 的stream 3 里的音频流由最多的声道,会被自动选择上。然而不会有字幕流会被选择上,因为mp4 格式没有默认注册的字幕编码器,而且用户也没有指定一个字母编码器。

 对于第二个输出文件,out2.srt,只接受基于文字的字幕流。所以尽管第一个可用字幕流(那个属于C.mkv 的)存在,但由于它是基于图像的所以会被跳过。被选择的流,B.mp4 中的 stream 2 ,是第一个基于文本的字幕流。

 例子:写了标签的filtergraph 输出

 (下述代码中,末尾的 / 在终端里表示换一行显示,继续写命令,执行的时候是把那几行命令当一行执行的)

  FFmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \

    -map '[outv]' -an out1.mp4 \

          out2.mkv \

    -map '[outv]' -map 1

本文标签: 指南ffmpeg小白入坑