我们最早在ffmpeg是如何转码的一文中理解过滤镜,来回顾下当时的转码流程图。
从图中可以看到滤镜前后画的是虚线,表示可有可无,在术语中,滤镜指的是在编码之前针对解码器解码出来的原始数据(即音视频帧)进行处理的动作,我们还可以称它为过滤器。
ffmpeg内置了大概近400种滤镜,我们可以用 ffmpeg -filters 命令查看所有的滤镜,也可以用命令 ffmpeg -h filter=xxx 或者查看官方文档理解每一种滤镜。

实际在大部分音视频的处理过程中都离不开滤镜,以是你该当能明白其主要性。
多个滤镜可以结合在一起利用形成滤镜链或者滤镜图,在每一个滤镜中,不仅可以对输入源进行处理,A滤镜处理好的结果还可以作为B滤镜的输入参数,通过B滤镜连续处理。
针对滤镜的处理,ffmpeg供应了两种处理办法,大略滤镜和繁芜滤镜。
大略滤镜大略滤镜指的是只有一个输入和输出,而且担保输入和输出的流类型相同。
比如我们在上篇文章流的操作(二)如何选择流?末端提到的把原视频 r3.mp4 等比例缩放一倍
ffmpeg -i r3.mp4 -vf scale=272:480 -y filter.mp4
-vf 是 -filter:v 的简写,类似的我们还可以利用 -filter:a 或者 -af 针对音频流做处理。
-filter的语法规则:-filter[:stream_specifier] filtergraph (output,per-stream)stream_specifier流的类型我们一样平常用a表示音频,v表示视频,filtergraph表示详细的滤镜,这里用的是scale滤镜。
scale滤镜用于调度视频的大小,比如等比例缩放、等比例放大,不做等比例操作输出就变形了,变形结果我们一样平常不考虑。
由于我们知道原视频 r1ori.mp4 的分辨率是 544x960,以是等比例缩放一倍,上面的命令直接指定了 272x480,scale滤镜自带很多参数,我们先容几个常用的。
in_w in_h 或者 iw ih 表示输入视频的宽高out_w out_h 或者 ow oh 表示输出视频的宽高
当然不一定是视频,输入输出也可以是图片。
以是原视频缩放一倍我们还可以这样写:
ffmpeg -i r3.mp4 -vf scale=iw/2:ih/2 -y filter.mp4
问题一:如果我们要把原视频的宽度调度为300且保持原分辨率,怎么办?
列一个方程 544/960 = 300/x ,x=300x960/540,很麻烦,结果还不一定能整除,为此我们可以直接指定高度即是-1,它会自动做等比例处理。
ffmpeg -i r1ori.mp4 -vf scale=300:-1 -y filter.mp4
结果创造转码失落败了,提示
[libx264 @ 0x7ff509053a00] height not divisible by 2 (300x529) Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height [aac @ 0x7ff50904e200] Qavg: 28010.410 [aac @ 0x7ff50904e200] 2 frames left in the queue on closing
提示我们 height not divisible by 2 (300x529)即高度529不能被2整除。这是由于一些编解码器哀求很多视频的宽高必须是n的倍数(这里n是2),以是我们写脚本处理视频或者图片宽高的时候,牢记不要利用-1,精确的用法是利用-2。
ffmpeg -i r1ori.mp4 -vf scale=300:-2 -y filter.mp4 输出结果视频的分辨率是 300 × 530
干系学习资料推举,点击下方链接免费报名,先码住不迷路~】
音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高等开拓
【免费分享】音视频学习资料包、大厂口试题、技能视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有须要的可以点击788280672加群免费领取~
问题二:老板为了刁难你,提出了一个新的哀求:“我想要所有输出视频的分辨率是 300x500且不能变形”,怎么办?
我们知道3:5的宽高比是很少见的,现在常见的分辨率是16:9、4:3,也便是说原视频我们必须要经由一番处理才可以知足老板的变态需求。
针对原视频 r1ori.mp4,如果担保宽度是300,等比例缩放后高度是530,逼迫设置高度为500就会变形,也便是说我们只能让高度即是500,只管即便缩小宽度试试。
ffmpeg -i r1ori.mp4 -vf scale=-2:500 -y filter.mp4 输出的结果视频的分辨率是284x500
如上图,蓝色框表示视频的真实宽高,赤色框表示目标宽高,有些像html中的css一样,可以给空出来的部分添补颜色即内边距不就可以了?
查阅了文档我们创造pad滤镜可以办理我们的问题。
pad滤镜的语法规则:-pad=width[:height[:x[:y[:color]]]]
1、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:500:(300-iw)/2:0" -y filter2.mp4 2、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0 -y filter.mp4 3、ffmpeg -i r1ori.mp4 -vf scale=-2:500,pad=300:500:-1:0:black -y filter.mp4 4、ffmpeg -i r1ori.mp4 -vf "scale=-2:500,pad=300:ih:(ow-iw)/2:0:green" -y filter.mp4
上面供应4中写法,我们以方法4做个大略先容。
scale=-2:500,指原视频按照等比例缩放,高度即是500,便是上面大家看到的284x500。
pad=300:ih:(ow-iw)/2:0:green,300:ih即300:500便是赤色框的宽高(ow-iw)/2,指的是赤色框和蓝色框差值的一半,即两边各须要添补的范围;末了一个参数表示须要添补的颜色,默认是玄色 black,为了调试方便我们把颜色设为green。
现在我们担保了当前视频一定会按照300x500的比例输出且不会变形,但是请把稳老板说的“所有输出视频”,也便是说输入视频的分辨率可能是200x300、544x960、500x400、200x800等等各种比例都要担保按照300x500输出,很显然,上面的写法不完备通用,怎么办?
现在我们已知原输入视频的宽高和想要的宽高,针对这种情形,我们制订一套处理规则即可办理:
宽高都偏小,不拉伸,不缩放宽高都偏大,等比例缩小,以高度为准宽超出范围,等比例缩小,以宽为准高超出范围,等比例缩小,以高为准在实际的开拓过程中,我们要跟代码打交道,平时在命令行中的实现都是练习,以是基于该规则,我们有了下面一段代码
<?phpdeclare(strict_types=1);class CalculatorService{ / 用户视频分辨率转换 规则: 宽高都偏小,不拉伸,不缩放 宽高都偏大,等比例缩小,以高度为准 宽超出范围,等比例缩小,以宽为准 高超出范围,等比例缩小,以高为准 @param int $inputWidth 输入视频的宽度 @param int $inputHeight 输入视频的高度 @param int $outWidth 输出视频的宽高 @param int $outHeight 输出视频的高度 @return string scale / public function getSize(int $inputWidth, int $inputHeight, int $outWidth, int $outHeight): string { $scale = ""; if ($inputWidth <= $outWidth && $inputHeight <= $outHeight) { $scale = "scale={$inputWidth}:{$inputHeight},pad={$outWidth}:{$outHeight}:-1:-1:green"; } elseif (($inputWidth > $outWidth && $inputHeight > $outHeight) || ($inputHeight > $outHeight) ) { $scale = "scale=-2:{$outHeight},pad={$outWidth}:{$outHeight}:-1:0:green"; } elseif ($inputWidth > $outWidth) { $scale = "scale={$outWidth}:-2,pad={$outWidth}:{$outHeight}:0:-1:green"; } return $scale; }}$calculatorService = new CalculatorService();var_dump($calculatorService->getSize(200, 300, 300, 500));var_dump($calculatorService->getSize(544, 960, 300, 500));var_dump($calculatorService->getSize(500, 400, 300, 500));var_dump($calculatorService->getSize(200, 600, 300, 500));// 结果string(37) "scale=200:300,pad=300:500:-1:-1:green"string(35) "scale=-2:500,pad=300:500:-1:0:green"string(35) "scale=300:-2,pad=300:500:0:-1:green"string(35) "scale=-2:500,pad=300:500:-1:0:green"
为了方便理解,大家可以参考下面的图逐一对应。
繁芜滤镜
相对付大略滤镜,繁芜滤镜是可以处理任意数量输入和输出效果的滤镜图,它险些无所不能。
繁芜滤镜用命令 -filter_complex 表示,它还有一个别名 -lavfi。
上篇文章先容到流和滤镜结合是一种最主要、最常用的方法。依然是将输入视频 r3.mp4 等比例缩放一倍,我们以手动选择流的办法为例。
ffmpeg -i r3.mp4 -filter_complex "[0]scale=272:480[out]" -map 0:a -map "[out]" -y filter.mp4
大略剖析如下:
命令 "[0]scale=272:480[out]" 中的[0]表示第一个输入的视频,由于要对视频做处理,以是也可以用[0:v]表示,如果要对音频单独处理,就须要用 [0:a] 了;[0] 结合scale滤镜,表示的便是把第一个输入的视频作为scale滤镜的参数输入;[out] 中括号是必须要的,out是自定义的一个别名,结合scale滤镜,表示的是把scale滤镜输出的结果命名为[out],但并非是终极输出的结果,只能作为中间过程输出的一个结果;-map "[out]" 便是直接选择[out] 流作为输出我们说过,一个滤镜的输出作为另一个滤镜的输入,这样就极大的避免了写多条命令反复编解码操作,我们的原则只有一个,能用一条命令处理的绝不用两条命令。
有损编解码器反复编解码操作会降落原视频质量。
比如现在要把原视频 r1ori.mp4 的中间部分裁剪出来,但仍保持原视频的分辨率544x960,如何做呢?
ffmpeg -i r1ori.mp4 -filter_complex "nullsrc=s=544x960[background]; \crop=iw:(ih/2 - 110):0:250[middle]; \[background][middle]overlay=shortest=1:x=(main_w-overlay_w)/2:y=(main_h-overlay_h)/2[out]" \-map "[out]" -map 0:a -movflags +faststart -y fc.mp4
这个命令就显得轻微长了一些,在这条命令中利用了nullsrc、crop、overlay三种常见滤镜。
nullsrc滤镜用于创建一个空的视频,大略的说便是一个空的画布或者说是绿布,由于默认创建的颜色是绿色的。s用于指定画布的大小,默认是320x240,这里表示我们创建一个544x960的画布,并命名为background;
关于nullsrc还有很多种不同的用户,比如利用nullsrc和CIQRCodeGenerator创建一个“白狼栈”首页的二维码
ffmpeg -f lavfi -i nullsrc=s=200x200,coreimage=filter=CIQRCodeGenerator@inputMessage=\ http\\\\\://manks.top/@inputCorrectionLevel=H -frames:v 1 manks.png
crop滤镜用于裁剪视频,也便是说视频的任意区域任意大小,我们都可以裁剪出来。crop=iw:(ih/2 - 110):0:250[middle]; 这里我们裁剪原视频的中间部分并命名为middle;
overlay滤镜表示两个视频相互叠加,shortest官网是这么先容的:“If set to 1, force the output to terminate when the shortest input terminates. Default value is 0.”,由于我们利用nullsrc创建了一个没有韶光轴的画布,以是这里须要以middle的视频韶光为终极韶光,故设置为1。main_w和main_h表示主视频的宽高,overlay_w和overlay_h表示叠加视频的宽高。如果要把A视频叠加到B视频上,则main_w和main_h表示B视频的宽高,overlay_w和overlay_h表示A视频的宽高。合起来便是把middle叠加到background之上且置于background的中间(相称于有个叠加层的观点);
末了一个参数是-movflags,它跟mp4的元数据有关,设为faststart表示会将moov移动到mdat的前面,在线播放的时候会轻微快一些。
原文 https://zhuanlan.zhihu.com/p/392020462