ffmpeg入门篇-滤镜的基本使用

滤镜

什么是滤镜?百度百科介绍说“滤镜主要是用来实现图像的各种特殊效果......”。

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

 

问题二:老板为了刁难你,提出了一个新的要求:“我想要所有输出视频的分辨率是 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输出,很显然,上面的写法不完全通用,怎么办?

现在我们已知原输入视频的宽高和想要的宽高,针对这种情况,我们制定一套处理规则即可解决:

  1. 宽高都偏小,不拉伸,不缩放

  2. 宽高都偏大,等比例缩小,以高度为准

  3. 宽超出范围,等比例缩小,以宽为准

  4. 高超出范围,等比例缩小,以高为准

在实际的开发过程中,我们要跟代码打交道,平时在命令行中的实现都是练习,所以基于该规则,我们有了下面一段代码

<?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

 

简单分析如下:

  1. 命令 "[0]scale=272:480[out]" 中的[0]表示第一个输入的视频,因为要对视频做处理,所以也可以用[0:v]表示,如果要对音频单独处理,就需要用 [0:a] 了;

  2. [0] 结合scale滤镜,表示的就是把第一个输入的视频作为scale滤镜的参数输入;

  3. [out] 中括号是必须要的,out是自定义的一个别名,结合scale滤镜,表示的是把scale滤镜输出的结果命名为[out],但并非是最终输出的结果,只能作为中间过程输出的一个结果;

  4. -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

 

这个命令就显得稍微长了一些,在这条命令中使用了nullsrccropoverlay三种常见滤镜。

nullsrc滤镜用于创建一个空的视频,简单的说就是一个空的画布或者说是绿布,因为默认创建的颜色是绿色的。s用于指定画布的大小,默认是320x240,这里表示我们创建一个544x960的画布,并命名为background;

关于nullsrc还有很多种不同的操作,比如使用nullsrc和CIQRCodeGenerator创建一个“007”首页的二维码

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的前面,在线播放的时候会稍微快一些。

关于滤镜的基本介绍我们就介绍到这里

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340