转发自白狼栈:查看原文
滤镜
什么是滤镜?百度百科介绍说“滤镜次要是用来实现图像的各种特殊效果 ……”。
咱们最早在 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 的分辨率是 544×960,所以等比例缩放一倍,下面的命令间接指定了 272×480,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=300×960/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 (300×529)即高度 529 不能被 2 整除。这是因为一些编解码器要求很多视频的宽高必须是 n 的倍数(这里 n 是 2),所以咱们写脚本解决视频或者图片宽高的时候,切记不要应用 -1,正确的用法是应用 -2。
ffmpeg -i r1ori.mp4 -vf scale=300:-2 -y filter.mp4 输入后果视频的分辨率是 300 × 530
问题二:老板为了刁难你,提出了一个新的要求:“我想要所有输入视频的分辨率是 300×500 且不能变形”,怎么办?
咱们晓得 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,就是下面大家看到的 284×500。
pad=300:ih:(ow-iw)/2:0:green,300:ih 即 300:500 就是红色框的宽高(ow-iw)/2,指的是红色框和蓝色框差值的一半,即两边各须要填充的范畴;最初一个参数示意须要填充的色彩,默认是彩色 black,为了调试不便咱们把色彩设为 green。
当初咱们保障了以后视频肯定会依照 300×500 的比例输入且不会变形,然而请留神老板说的“所有输入视频”,也就是说输出视频的分辨率可能是 200×300、544×960、500×400、200×800 等等各种比例都要保障依照 300×500 输入,很显然,下面的写法不齐全通用,怎么办?
当初咱们已知原输出视频的宽高和想要的宽高,针对这种状况,咱们制订一套解决规定即可解决:
- 宽高都偏小,不拉伸,不缩放
- 宽高都偏大,等比例放大,以高度为准
- 宽超出范围,等比例放大,以宽为准
- 高超出范围,等比例放大,以高为准
在理论的开发过程中,咱们要跟代码打交道,平时在命令行中的实现都是练习,所以基于该规定,咱们有了上面一段代码
<?php
declare(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 的两头局部裁剪进去,但仍放弃原视频的分辨率 544×960,如何做呢?
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 用于指定画布的大小,默认是 320×240,这里示意咱们创立一个 544×960 的画布,并命名为 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 的后面,在线播放的时候会略微快一些。
作业:咱们在音视频合成案例一文中介绍了两个案例,快去试试你能不能一条命令解决?
对于滤镜的根本介绍咱们就介绍到这里,有任何问题能够下方留言。