若干年前,写过一篇“技术宅学会几招FFmpeg”。至今仍觉得这篇小文挺实用,时不时会翻出来,对照着使用场景把FFmpeg命令行一抄,直接运行或者简单修改一下参数,就能完成任务,非常方便!有时候,又觉得时过境迁,这篇小文覆盖的使用场景太单薄,亟需补充一下。毕竟自己也换了新的工作环境,碰到了一些新需求。于是,便有了本文。
我们仍然按照使用场景来归类,分为视频/图像、音频、其他工具等。我会视情况,持续更新本文的内容——记得收藏本文的链接哦!
一.视频/图像
【Case 100】从最基础的场景开始,在视频文件的指定时间点抽取一帧图像,目标文件格式通过扩展名来控制,可以是.png、.jpg、.webp等:
ffmpeg -ss 5 -i D:\input.mp4 -frames:v 1 D:\snapshot.png
如果需要的是缩略图,或者出于节省存储空间考虑,那就要对图像进行缩放。可以通过scale过滤器来指定你想要的图像宽和高,也可以宽或高只指定一个,另一个用“-1”代替,以保持图像的原始宽高比(确保图像内容不被扭曲):
ffmpeg -ss 5 -i D:\input.mp4 -frames:v 1 -vf scale=120:-1 D:\snapshot.jpg
注:-vf表示简单过滤器。所谓“简单”,是指过滤器只能有一个输入和一个输出。
【Case 101】挑战一下,另一种“既要…又要…”的缩略图:既要限定宽和高(比如120x80),又需要保持图像的原始宽高比,图像内容之外的区域用黑色填充:
ffmpeg -ss 5 -i D:\input.mp4 -frames:v 1 -vf "scale='min(120/iw, 80/ih)'*iw:'min(120/iw, 80/ih)'*ih, pad=120:80:(ow-iw)/2:(oh-ih)/2:black" D:\snapshot.webp
解释一下:我们使用了两个过滤器。先使用scale过滤器,iw和ih分别表示源图像的宽和高,相对于目标图像尺寸120x80,分别计算宽和高的缩放比例,选择两者中较小的比例对原始图像进行缩放。然后使用pad过滤器来填充:前两个参数指定目标图像的宽和高(把它想象成一块黑色画布),紧接着的两个参数用来指定实际图像在“画布”上渲染的起始点(左上角)坐标x:y,最后一个参数是填充色。ow和oh分别指代输出图像的宽和高,本例中其实就是120和80。
【Case 102】怎样从视频文件里按照一定的间隔(比如10fps)抽取一系列的缩略图呢?
ffmpeg -i D:\input.mp4 -r 10 -vf "scale='min(120/iw, 80/ih)'*iw:'min(120/iw, 80/ih)'*ih, pad=120:80:(ow-iw)/2:(oh-ih)/2:black" -y D:\Thumbnails\Pic-%05d.jpeg
【Case 103】有些手机的前置摄像头拍的视频是左右颠倒的,需要做一次水平翻转:
ffmpeg -i D:\input.mp4 -vf hflip D:\flipped.mp4
“买一送一”,送一条垂直翻转:ffmpeg -i D:\input.mp4 -vf vflip D:\flipped.mp4
那么,如何同时做水平和垂直翻转呢?-vf后面可以跟多个过滤器,使用逗号分开即可,注意要加一对引号:
ffmpeg -i D:\input.mp4 -vf "hflip, vflip" D:\flipped2.mp4
还有以前学过的一招,一起汇总在这儿:旋转90度(弧度值为正数表示顺时针,负数表示逆时针),记得要将输出图像的宽和高对调一下:
ffmpeg -i D:\input.mp4 -vf "rotate=a='90*PI/180':ow=ih:oh=iw" D:\rotate90.mp4
当然,上述针对视频文件的处理,对图像文件同样也是适用的。
【Case 104】随着智能手机的普及以及UGC短视频内容的风行,现在随处可见竖版视频。如果在某些场景下你偏偏要用横版视频,怎么从竖版视频里抠出一块来呢?该crop过滤器上场啦!举个例子,居中截取与源视频等宽、宽高比是16:9的那一块区域:
ffmpeg -i D:\input.mp4 -vf "crop=in_w:(in_w*9/16):0:(in_h-in_w*9/16)/2" D:\cutout.mp4
【Case 105】画中画(或者说在一个视频上叠加另一个视频或图片)是一种常见应用,实现起来也很简单,就是使用overlay复合过滤器。偶然发现一篇不错的文章,里面提到了在视频上叠加计时器的实现,觉得挺巧妙的,所以在这里也记录一下。他的思路分两步:先从一个含有时间戳的视频中裁剪制作出一个计时器视频,然后再将这个计时器视频叠加在主视频上。下面来演练一遍。
第一步:利用一个叫testsrc的视频源,它输出的视频是这样的:
(执行ffplay -f lavfi -i testsrc播放一下就知道了…)
可以定位到时间戳的起始坐标为(224, 94),裁剪两位数字图像的宽度是61,高度是52,生成30秒的计时器视频timer.mp4,完整指令如下:
ffmpeg -f lavfi -i testsrc -vf crop=61:52:224:94 -t 30 D:\timer.mp4
第二步:如果只想在主视频的左上角简单叠加,就这样执行:
ffmpeg -i D:\input.mp4 -i D:\timer.mp4 -filter_complex overlay D:\output1.mp4
如果想把计时器视频缩放后再叠加,比如宽度放大到80(高度指定为-1可以保持原始图像的宽高比),则要利用一个叫movie的简单过滤器(顺便练习一下将叠加的位置改到左下角),如下:
ffmpeg -i D:\input.mp4 -vf "movie=timer.mp4, scale=80:-1[tm]; [in][tm]overlay=0:H-h" D:\output2.mp4
注意:movie后面的filename参数不能带文件路径,于是为了让ffmpeg能顺利找到资源,须将控制台的工作目录设置在包含timer.mp4的地方。
最后补充一下,更完美的方案应该是先执行ffprobe -i D:\input.mp4查看主视频的时长,然后对应地制作等时长的计时器视频,然后再做视频叠加。
【Case 106】发现60fps的视频在某些手机上倍速播放时直接卡住了,估计是帧率太高,CPU忙不过来!这时候可以通过-r把帧率降下来试试。
ffmpeg -i D:\input.mp4 -r 30 D:\out30fps.mp4
假设采用的是H.264编码器,还可以通过profile和level来调整画质。可选的profile有:baseline、extended、main、high,再通过level来控制比特率(这里有更详细的对照表)。根据应用领域的不同,baseline profile多应用于实时通信领域,main profile多应用于流媒体领域,high profile则多应用于广电和存储领域。还要注意,有些低端设备或早期的设备只能解码baseline。
ffmpeg -i D:\input.mp4 -profile:v baseline -level 3.0 D:\output1.mp4
ffmpeg -i D:\input.mp4 -profile:v main -level 4.2 D:\output2.mp4
ffmpeg -i D:\input.mp4 -profile:v high -level 5.1 D:\output3.mp4
二.音频
【Case 200】实验室的Matlab算法能接受的数据格式可能仅限于.wav,而客户提供的素材可能是任意一种压缩格式,需要做一次转换:
ffmpeg -i D:\input.m4a D:\output.wav
好简单吧,通过输出文件的扩展名.wav就能控制数据格式。当然,如果想要生成.mp3或.ogg压缩格式,如法炮制,把输出文件名改成output.mp3或output.ogg即可。如果拿到的素材是视频文件,只想提取其中的音频,用法也是类似的(-vn表示丢弃视频数据,这个参数是可选的):
ffmpeg -i D:\input.mp4 -vn D:\output.wav
【Case 201】常见的音频采样频率有11025Hz、22050Hz、24000Hz、44100Hz(CD音质)、48000Hz、96000Hz等。如何进行改变呢?
ffmpeg -i D:\input.m4a -ar 48000 D:\output.wav
【Case 202】假如客户给的素材是5.1声道的,而我们只能处理单声道或双声道,怎么办?
ffmpeg -i D:\input.m4a -ac 1 D:\output.wav
如果输出的是纯PCM数据文件,需要使用-f来限定格式:
ffmpeg -i D:\input.m4a -ac 1 -acodec pcm_s16le -f s16le D:\output.pcm
【Case 203】常见的采样精度/位深有8位、16位、24位、32位等,即单个声道每个采样点用多少Bits来表示。如果要改变位深,则需使用-acodec来指定编码器(如果想查询当前有哪些可用的编码器,参见Case 300),可选值为:pcm_u8、pcm_s16le、pcm_s24le、pcm_s32le等,其中“u”表示无符号数,“s”表示有符号数,“le”表示little-endian(即低字节在前的多字节数据格式):
ffmpeg -i D:\input.m4a –acodec pcm_s24le D:\output.wav
注:-acodec与-c:a是等价的。
【Case 204】如何改变音量呢?使用-af音频过滤器,volume=1表示正常音量,=0.5表示变为一半,=2表示变为2倍。volume的取值也可以带上dB单位,正数表示音量增加xxdB,负数表示音量减小xxdB:
ffmpeg -i D:\input.m4a -af volume=2 D:\output.wav
ffmpeg -i D:\input.m4a -af volume=-2dB D:\output.wav
【Case 205】如果目标文件是一种压缩格式,我们可能还想使用-ab来指定它的码率/bitrate(CBR-恒定码率),以控制音质(码率越高、音质越好):
ffmpeg -i D:\input.m4a -ab 128k D:\output.mp3
注:-ab与-b:a是等价的。
或者也可以用-aq(取决于特定的编码器,VBR-可变码率,取值越小,码率越高):
ffmpeg -i D:\input.m4a -aq 2 D:\output2.mp3
【Case 206】如何将多个音频文件首尾相连合成为一个?假设有四个源文件,分别是input1.m4a、input2.wav、input3.mp3、input4.mp4(格式不限),然后要使用-filter_complex复合过滤器(-lavfi是其等价形式),“concat”之前的[0:0]表示第一个输入源的0号输出(注意:索引都是从0开始的,对于有多路输出的源文件,0号输出是视频流,1号输出是音频流),[1:0]表示第二个输入源的0号输出,以此类推;“concat”之后的n=4表示总共有4路流参与合成,v=0表示丢弃视频数据,a=1表示为合成一路音频流:
ffmpeg -i D:\input1.m4a -i D:\input2.wav -i D:\input3.mp3 -i D:\input3.mp4 -filter_complex "[0:0][1:0][2:0][3:1]concat=n=4:v=0:a=1" –ab 128k –y D:\concat.mp3
脑补这样一张图:
【Case 207】继续讨论多文件混合的场景,如果我们要的不是首尾相连,而是混音呢?那就要用到amerge这个复合过滤器了:
ffmpeg -i D:\input1.m4a -i D:\input2.wav -filter_complex "[0:0][1:0]amerge=inputs=2" –ab 128k D:\merge.mp3
需要注意的是,合成的文件时长以较短的那个为准——这个有点让人无法接受!有个“笨”办法,就是使用-stream_loop让较短的那个输入文件循环输出,像这样:
ffmpeg -stream_loop 2 -i D:\input1.m4a -i D:\input2.wav -filter_complex "[0:0][1:0]amerge" –ab 128k -y D:\merge.mp3
很别扭呀!还是来看看更为强大的amix过滤器吧,它可以通过duration来指定目标文件的时长以哪个为准(可选值为longest、shortest、first),还可以通过weights指定各路流的混音权重(缺省均为1/N):
ffmpeg -i D:\input1.m4a -i D:\input2.wav -i D:\input3.mp3 -filter_complex "[0:0][1:0][2:0]amix=inputs=3:duration=longest:weights='0.1 0.1 0.8'" –ab 128k D:\mix.mp3
【Case 208】继续深入探讨:在混音时,可以为各个音效文件指定在时间线上的开始时间吗?答案是肯定的。我们这次把最长的主音频文件作为第一个输入源,然后通过adelay过滤器将第二个音效文件延迟5秒开始,将第三个音效文件延迟至第30秒开始(delays参数值,不带单位时默认为毫秒,小写s表示秒,大写S表示采样点数量;可以为各个声道分别指定延迟时间,或者通过all=1为所有声道指定相同的延时):
ffmpeg -i D:\input1.m4a -i D:\input2.wav -i D:\input3.mp3 -filter_complex "[1:a]adelay=delays=5s:all=1[a1]; [2:a]adelay=delays=30s:all=1[a2]; [0:a][a1][a2] amix=inputs=3:duration=first:weights='0.6 0.2 0.2'" –ab 128k -y D:\mix.mp3
值得注意的是,这次我们换了一种标注方法:[1:a]表示第二个输入源的音频输出,经过adelay处理之后把输出流标记为[a1];[2:a]表示第三个输入源的音频输出,经过adelay处理之后把输出流标记为[a2];然后第一个输入源的音频输出流[0:a]与[a1]和[a2]一起喂给amix过滤器进行混音。目标文件的时长以第一个输入源为准。各路流的混音比例分别是60%、20%、20%(加起来等于100%)。
【Case 209】一个立体声音频流,将其中一个声道的数据清零(静音),输出仍然是立体声。
将右声道静音:ffmpeg -i D:\input.wav -af "pan=stereo|c0=c0" D:\right_muted.wav
将左声道静音:ffmpeg -i D:\input.wav -af "pan=stereo|c1=c1" D:\left_muted.wav
【Case 210】如何从一个立体声音频流中抽取一个声道的数据,然后生成一个单声道音频文件?本可以使用-map_channel,但这个参数被废弃了。官网建议使用pan过滤器,用法如下:
只取左声道:ffmpeg -i D:\input.wav -af "pan=1c|c0=c0" D:\mono_left.wav
只取右声道:ffmpeg -i D:\input.wav -af "pan=1c|c0=c1" D:\mono_right.wav
雨露均沾(左90%+右10%):ffmpeg -i D:\input.wav -af "pan=1c|c0=0.9*c0+0.1*c1" D:\mono_right.wav
注:对比Case 202,-ac 1是会自动进行多声道混音的。
【Case 211】将一个音频文件的波形图画出来,可以利用showwavespic过滤器很容易做到。用s参数指定输出图像的宽和高(图像宽度影响波形的疏密);split_channels取值为1表示各个声道分开画,取值为0表示所有声道重叠画在一起:
ffmpeg -i D:\input.m4a -filter_complex showwavespic=s=1280x720:split_channels=1 D:\waveform.png
三.其他工具
【Case 300】查看FFmpeg自带的编解码器、文件容器格式:
ffmpeg –codecs
ffmpeg –decoders
ffmpeg –encoders
ffmpeg –formats
【Case 301】有时候需要录屏,但又懒得去安装一款第三方录屏软件。可以这样:
ffmpeg -f gdigrab -i desktop D:\screenshots.mp4
注:按下“Q”键或者 Ctrl+C 即可停止录屏。
【Case 302】基于上述录屏命令,在视频右下角加上文字水印:
ffmpeg -f gdigrab -i desktop -vf drawtext="fontsize=80:fontcolor=white:text='TEST':x=(w-text_w-10):y=(h-text_h-10)" -y D:\screenshots.mp4
【Case 303】基于上述录屏命令,改成在视频右上角加上图片水印:
ffmpeg -f gdigrab -i desktop -i D:\logo.png -filter_complex "[0:0][1:0]overlay=x=main_w-overlay_w-10:y=0" -y D:\screenshots.mp4
【Case 304】通过笔记本电脑的内置摄像头采集一段视频。摄像头的名字未必总是“Integrated Camera”,可能因不同机型而不同,可以在Windows系统的“设备管理器 | 照相机”下查看(或者执行ffmpeg -list_devices true -f dshow -i dummy):
ffmpeg -f dshow -i video="Integrated Camera" D:\capture.mp4
注:按下“Q”键或者 Ctrl+C 即可停止采集。
========
官网资料:
http://ffmpeg.org/ffmpeg.html#Audio-Options