ffmpeg常用命令

ffmpeg常用命令

ffmpeg简介及视频的本质

ffmpeg简介

ffmpeg是 Fast Forward MPEG (Motion Picture Experts Group) 的缩写,见这里,英语读法,会把“ff”和“mpeg”分开读,因为mpeg可以直接按一个单词发音[ˈempeɡ],而两个ff就直接按字母发音读。

其中Fast Forward字面意思是“快速向前”,也就是“快进”的意思,而“Motion Picture”字面意思是“会动的图片”,一般翻译为“电影”(比较正式的说法),事实上电影确实就是会动的图片。

视频的本质

视频的本质,是以一定速度按顺序显示的图片(比如在1秒钟时间内快速显示24张图,由于视觉暂留效果,我们看起来就是画面会“动”)。

小时候看的连环画,只要快速翻动,就会看到画面动起来
连环画.gif

快速翻动多张小黄鸭图片,就能看到小黄鸭在动

当多张图片之间的差别很小,图片够多,翻动的速度够快(即每秒钟翻动的页数够多)时,我们就能看到动起来的画面,这就是视频的原理。

而最初的电影,也是使用胶片记录,播放的时候用强光照射胶片,经放大后投影到电影幕布上,同时胶片不断的卷动,当卷动的速度够快时(一般每秒24张),这样我们在幕布上看到的画面就是连续流畅的会动的画面。
电影胶卷

现在我们说“剪片”或“剪辑”,通常都是用剪辑软件来对视频进行剪辑,而以前的胶片电影,那可是真的是要把胶卷剪开的,所以为什么现在叫“剪辑”,也是这个意思,拼接的话是用胶水粘起来的

数字时代的电影,已经不需要胶片,只需要以二进制的方式存成一个文件即可,比如我们常的mp4格式。但视频的本质没有变,它还是由一张一张的静止图片合成的,我们每一张图片,叫“一帧(frame)”,但是,如果真的把这些图片一张一张合成一个视频,那么视频的体积将会非常巨大,所以需要压缩,事实上我们看的视频都是压缩过的,因为很多时候,相邻图片大部分都是相同的,存储这么多非常类似的图片,是对硬盘空间的巨大浪费。

我们一般把前面说的“压缩”称为“编码”(比如最常见的h.264编码),而播放过程就是把编码后的视频解码还原出来。

视频的分辨率其实就是组成这个视频的图片的分辨率,比如视频分辨率是1920⨉1080,意思是组成这个视频的每张图片,横向有1920个像素,纵向有1080个像素,对于图片来说,每张图片的分辨率越大,它就越清晰,由此可见,组成一个视频的图片分辨率越高,视频就越清晰,也就是说,一个视频的分辨率越高就越清晰(但这不是绝对的,下面会说到分辨率越高,清晰度反而越低的情况)。

前面说过,胶片电影的播放是靠每秒钟卷动一定数量的胶片来让画面达到“会动”的效果,一般来说,要一秒16张就可以达到让我们看起来画面是运动的,当然再多一点,画面就会更细腻,对于胶片电影,一般一秒钟24张。我们称一个视频每秒钟播放的图片数量为该视频的“帧率(frame-rate)”。常见的帧率有24000/1001=23.976, 30000/1001=29.970, 60000/1001=59.940, 25.000, 50.000等等。这个数字是一秒钟内闪过的图像的数量。比如23.976,就是1001秒内,有24000张图像。视频的帧率是可以是恒定的(cfr:Const Frame-Rate),也可以是变化的(vfr:Variable Frame-Rate)。

帧率决定一个视频的流畅度,试想一下,我们把一本100页的连环画,每隔一页撕掉一页,那么每两页之间的画面变动就比较大,视觉上就是画面跳动大,不连贯,不流畅。

码率,即比特率(bit-rate),每秒钟播放多少比特(bit),对于生成一个视频(摄影机或者剪辑软件),表示每秒种会产生多少比特的数据。

现在我们来解释,为什么分辨率越高,视频反而越不清晰的情况。这种情况会在码率固定、帧率固定,而单独提高分辨率时发生。帧率固定,代表每秒钟要播放的图片数量是固定的(比如是24),码率固定表示生成一个视频时,每秒钟生成的文件体积是固定的,但是,如果我们提高每张图片的分辨率,很显然每秒24张分辨率更高的图片生成的文件体积更大,但现在我们却不能增加每秒钟生成的文件体积(不能增加码率),怎么办?

办法就是采样!由于每张图片分辨率变大,它本身的体积已经变大,完整保存每张图片肯定会导致码率变大,我们只能抽取每张图片里的一部分像素(这部分像素也能让你看清这个图片),这就是采样,毫无疑问,视频清晰度取决于从每张图片中抽取的像素数量(我们叫采样率),当一张图片的分辨率越高,代表它的像素越多,而我们能抽取的像素量却有限,所以,图片分辨率越高会导致采样率越低,这样的后果就是这张图片越不清晰,而我们知道,播放视频就是连续播放图片,既然你每张图片都不清晰,那表现出来就是这个视频不清晰,这就是为什么“分辨率越高,视频反而越不清晰”,因为这是被码率限制了,如果我们不限制码率,那么肯定是分辨率越高,视频就越清晰了。


ffmpeg的使用

ffmpeg命令生成器

ffmpeg命令很多,对初学者来说比较复杂,想要用ffmpeg对视频进行某种操作,如果不会就只能网上查,需要怎么写命令。

就算有些人使用过一段时间后熟悉了命令,但如果放下一段时间,很容易又不知道命令怎么用了,为了方便使用,很多人都写了一些工具,这些工具分为两类:

  • 1、一类是GUI工具,就是写一个壳,让你有一个界面可以选择文件,可以调整参数,最后点击转换实际上还是用ffmpeg转换的,只不过它是直接调用ffmpeg的api了,这类软件用起来更像是格式转换器;
  • 2、另一类是命令生成器工具,就是他也写了一个界面(通常是web界面,因为简单,无需下载安装),让你可以选择参数,但它并不调用ffmpeg api直接帮你转换文件,而是根据你提供的参数自动生成一条命令,你把这条命令粘贴到终端里面执行,就可以对你的视频进行处理。

我找到一个比较不错的ffmpeg命令生成器(界面如下)
image.jpg
这是生成器github,这是生成器界面

另外还有两个ffmpeg命令生成器,不过我感觉没有前面那个功能多:生成器2生成器3

一些参数解释

  • -codeccoder-decoder的缩写,coder:编码器,decoder:解码器,合起来就是:编码解码器,读作['kodɛk],可简写为-c,经常后面用冒号跟着v或a,比如-c:v表示指定视频编码(v:video),-c:a表示指定音频编码(a:audio);
  • v一般表示video(即视频),a一般表示audio(即音频);
  • -codec:vv代表“video”,用于指定视频编码格式,可简写为-c:v,另一种写法是-vcodec
  • -codec:aa代表“audio”,用于指定音频编码格式,可简写为-c:a,另一种写法是-acodec
  • -b:vb代表“bitrate”(即比特率),v代表“video”,用于指定视频比特率(即码率),也可写成-vb(video bitrate的意思);
  • -b:ab代表“bitrate”(即比特率),a代表“audio”,用于指定音频比特率(即码率),也可以写成-ab(表示audio bitrate的意思),但这种写法不是标准写法,我看有些类似的写法在ffmpeg新版本中都弃用了(虽然还能用);
  • -vbsf:已经弃用,现在一般写成-bsf:v,用于指定视频比特流过滤器(bit stream filter),比特流过滤器用于“在不做码流解码的前提下,对已经编码后的比特流做特定的修改、调整”,用ffmpeg -bsfs可查看所有支持的比特流过滤器;
  • -absf:已经弃用,现在一般写成-bsf:a,用于指定音频比特流过滤器,用ffmpeg -bsfs可查看所有支持的比特流过滤器;
  • -aq audio quality,音频质量,它是-q:a的别名;
  • -crf Constant Rate Factor,恒定速率因素,是x264和x265编码器的质量参数,取值范围为0-51,数字越大,视频质量越差,数字越小,视频质量越好,当然视频文件体积也越大;0为无损,51为最差,默认值为23,一般认为能用的数值是17-28,大于28就会有肉眼可见的模糊了,详见H.264 Video Encoding Guide
  • -vf video filter,用于指定视频过滤器,是-filter:v的简写,比如-vf crop=1080:743:0:454表示从输入的视频中的(0,454)坐标为左上角顶点,截取一个宽x高=1080×743的视频;
  • -af audio filter,用户指定音频过滤器,是-filter:a的缩写,比如-af "volume=1.5"表示把音量调整为原来的1.5倍。

比如,以下参数就是指定视频编码(-c:v)为libx264,音频编码(-c:a)为aac,音频比特率(-b:a)为64k

-c:v libx264 -c:a aac -b:a 64k

另外还可以不指定编码,直接用原视频的编码,copy表示直接复制原视频的内容(不改变编码)

-c:v copy -c:a copy

查看视频参数

使用以下命令可查看一个视频或gif图的参数(如分辨率、帧率、码率),注意这里包括gif图,因为gif图的本质也跟视频类似(即由一系列图片组成,并且是按顺序播放出来),当然普通图片也可以查看,只不过没有帧率和码率

# 事实上ffmpeg -i默认是需要指定输出文件的,但是假如只有输入文件,它会输出它的信息
# 而不对视频进行处理,只不过最后一行会有个提示:At least one output file must be specified
ffmpeg -i /path/to/xxx.mp4

# 如果我们把ffmpeg换成ffprobe就不会有最后那句提示,但整个输出结果都是一样的
ffprobe -i /path/to/xxx.mp4

输出截图如下,我们主要是要找到其中的Stream #0
image.jpg

像这张图我主要看这几个信息Video: h264, 360x640, 646 kb/s, 30 fps,分别是:视频编码为h264,分辨率为360×640,码率为646ksps,帧率为30fps。


但是ffmpeg(包括ffprobe)不管做什么操作,都会在头部输出它的编译信息,但这些信息在绝大多数时候都是没用的(如下图)
image.jpg

可以看到,使用了-hide_banner后,头部的一大段信息就没有了,这样就整洁多了
image.jpg

但是每次都加上-hide_banner太麻烦了,我们可以创建一个别名,在~/.bashrc中添加一句

alias ffmpegnb="ffmpeg -hide_banner"

这样,我们平时就使用ffmpegnb,nb代表no banner,当然你也可以认为是“牛逼”哈哈,反正这样用的话,就方便多了,不加-hide_banner也没有一大片密密麻麻的头部了
image.jpg

查看视频时长

查看视频时长(容器时长),只能使用ffprobe不能使用ffmpeg

# 输出结果会用[FORMAT][/FORMAT]标签包着
ffprobe -v error -show_entries format=duration -i  /path/to/input.mp4

# 输出结果直接是一个时间,没有[FORMAT][/FORMAT]标签包着
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i /path/to/input.mp4

只查看音频的时长(有些视频的画面和声音时长不同)

 ffprobe -v error -select_streams a -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i

主要是添加了-select_streams a,a表示audio,音频轨,同理还可用v表示视频轨,当然,也可以用v:0表示音频,v:1表示视频,这个0和1我不知道是不是轨道编号,参见这里

查看播放时长

ffmpeg -i /path/to/input.mp4 -f -

容器时长跟播放时长一般相等,但可能有时候有点区别,不过这样查看需要解码,如果视频大,会比较消耗时间和cpu资源,一般查看前面的容器时长就行。

转mp3

把当前目录下的所有m4a文件转成mp3

for foo in *.m4a; do ffmpeg -i "$foo" -acodec libmp3lame -aq 2 "${foo%.m4a}.mp3"; done

从当前目录下的所有mkv视频中提取mp3(mkv也可以换成其它格式,比如mp4/m4v/mov)

for i in *.mkv; do ffmpeg -i "$i" -b:a 320k -vn -f mp3 "${i%.mkv}.mp3"; done

-i inupt,用于指定输入文件路径;
-b:a b是bitrate,a是audio,即指定音频比特率(决定音质的参数,对于mp3来说,最高音质为320kbps,再高就需要flac格式了),该选项也可简写成-ab(即audio bitrate的意思)。
-vn video none,也就是不要把视频信息保存到输出文件中,其实就是去掉视频;
-f format,用于指定输出格式(可省略,因为ffmpeg会根据输出文的后缀自动获取应该输出什么格式)。


flac转320kbps音质的mp3(最高音质的mp3)

ffmpeg -i input.flac -ab 320k -map_metadata 0 -id3v2_version 3 output.mp3

转mp4

ffmpeg -i input.mov -vcodec libx264 output.mp4

检测音量

检测音量官方文档:Audio Volume Manipulation

要调整一个视频的音量,我们通常需要检测一下它原来的音量是多少,这样才方便调整,否则就要不停的试验。

以下命令用于检测一个视频中音频的音量

ffmpeg -i 爱的代价_clip_inc_20dB.mp4 -filter:a volumedetect -f null /dev/null

正常情况下,ffmpeg有输入必须有输出,但是我们检测音量不需要输出一个文件,所以我们把它输出到/dev/null里(熟悉Linux的童鞋应该知道,这是一个空设备,输出到这儿的数据就代表丢弃)。

可是-f null又是什么意思呢?因为正常的输出是xxx.mp4xxx.mov等等,是有后缀的,ffmpeg可以自动检测后缀并指定输出格式,但是这次我们输出到/dev/null,没有后缀,ffmpeg检测不出来要输出什么格式,它就会报错,所以我们用-f null手动指定它的输出格式为null-f表示format,格式的意思(搜遍了ffmpeg的帮助文档也没有-f这个参数,就算在网上搜,也很少相关资料,不知道为什么)。

上图命令执行结果如下图红框所示,mean_volume是平均音量,max_volume是最大音量
image.jpg
:mean有平均值的意思。

调整视频音量

调整音量官方文档:Audio Volume Manipulation

将音量增加20dB

 ffmpeg -i 爱的代价.mp4 -af "volumn=20dB" 爱的代价20dB.mp4

其中-af表示audio filter,音频过滤器,是-filter:a的简写,volumn=20dB意思是将原来的音量增加20dB(分贝)。


同理,如果增加负数,那就是减少相应的分贝,比如以下命令表示在原音量基础上减少5分贝

 ffmpeg -i 爱的代价.mp4 -af "volumn=-5dB" 爱的代价-5dB.mp4

当然,还可以用倍数表示,比如以下命令表示将音量调整为原音量的1.5倍

 ffmpeg -i 爱的代价.mp4 -af "volumn=1.5" 爱的代价1.5.mp4

也可以静音,把音量设置为0,就是静音

 ffmpeg -i 爱的代价.mp4 -af "volumn=0" 爱的代价_mute.mp4

压缩视频

目前我知道的压缩视频的参数有四个:

  1. 等比例缩小宽高(其实就是缩小组成视频的图片的宽高),比如1920×1080(1080p)的,可以等比例缩小到1280×720(720p);
  2. 降低比特率(也叫码率),比特率是视频每秒钟播放的比特数(即二进制位数,1Byte=8bit,1KB=1024Byte=1024x8bit=8192bit),码率有动态码率和固定码率;
  3. 降低帧率,帧率即每秒播放的图片数,一般最低24fps(frame per second, 帧每秒),现在大多数都是30fps;
  4. 增大恒定速度因素(-crf),取值范围0-51,一般认为是17-28范围比较正常,大于28,视频会比较模糊,小于17,视频文件会很大,并且没啥必要(比如肉眼可能已经分辨不出17跟16、15的区别)

等比例修改分辨率

ffmpeg -i input.mp4  -vcodec libx264 -preset fast -crf 28 -vf "scale=640:-1" output.mp4

scale=640:-1 表示等比例缩放,宽缩放为640,高为-1表示自动根据比例缩放。


修改视频分辨率,以下两种方法效果一样

# 使用-s,s是size(即尺寸)的意思,后面跟着的是宽x高,注意乘号就是小写字母“x”
ffmpeg -i input.mp4 -s 320x240 output.mp4

# 使用-vf,video filter(视频过滤器)的意思,后面的scale是“比例”的意思
ffmpeg -i input.mp4 -vf scale=320:240 output.mp4

# 当然还可以直接修改为原值乘以或除以一个数,其中iw和ih分别表示原来的宽和高
ffmpeg -i input.mp4 -vf scale=iw/2:ih/2 output.mp4
ffmpeg -i input.mp4 -vf scale=iw*0.9:ih*0.9 output.mp4

修改视频比特率(即码率,每秒钟播放的比特数)

# -b表示bitrate,v表示video,如果是音频的比特率那就是:-b:a,因为a表示audio,后面跟着的是你要设置的比特率数值,可以用k、M等单位,分别表示kbps/Mbps
ffmpeg -i input.mp4 -b:v 700k output.mp4

压缩当前目录下所有mp4文件

# 自动比特率(比特率是决定视频清晰度的其中一个参数)
for foo in *.mp4; do ffmpeg -i "$foo" "${foo%}_optimized.mp4"; done

# 指定视频比特率为5000kbps(比特率是决定视频清晰度的其中一个参数,越大越清晰,同时文件体积也越大)
for foo in *.mp4; do ffmpeg -i "$foo" -b:v 5000k "${foo%}_optimized.mp4"; done

修改帧率(每秒播放的图片数)

# 1、用 -r 参数设置帧率
ffmpeg -i /path/to/input.mp4 -r 25 /path/to/output.mp4

# 2、用fps的filter设置帧率"fps=fps=25"并没有写重复,就是这么写的,我也不知道为什么...
ffmpeg -i /path/to/input.mp4 -vf fps=fps=25 /path/to/input.mp4

# 设置帧率为29.97fps,下面三种方式具有相同的结果:
ffmpeg -i input.avi -r 29.97 output.mp4
ffmpeg -i input.avi -r 30000/1001 output.mp4
ffmpeg -i input.avi -r netsc output.mp4

按指定输出文件大小来压缩

# 控制输出文件大小 -fs (file size首字母缩写)
# 计算输出文件大小:(视频码率+音频码率) * 时长 /8 = 文件大小
ffmpeg -i input.avi -fs 1024K output.mp4

动态码率与缓冲区

bitrate,中文叫比特率,也叫码率,意思是视频每秒钟播放多少比特(bit),我们知道,1bit就是一个二进制位,所以每秒钟播放多少比特,这能影响整个视频的文件大小,比如一个视频文件总时长为10秒,如果每秒播放的比特数为5000kbps,那就表示5000/8=625KB,即一秒钟会播放625字节的数据,10秒就是6250KB,6250/1024=6.1M,说明这个视频文件的大小为6.1M。

然而比特率不可能一直保持在一直恒定的数值,即使你用-b:v 5000k这样把码率设置为5000k,它也不可能一直5000k,它会围绕这个值做一个上下浮动,因为播放视频其实就是播放一张张的图片,每秒钟图片包括的信息可能不一样,比较复杂的画面,它包含的信息可能比较多,码率就大点,简单的画面信息少,可能码率就少点。

如果我们设置固定码率,其实那些用比较少比特就能描述的画面,也会被硬塞一些无效数据用来“保持”固定码率,这个其实是没有必要的,所以就有了动态码率的说法。

动态码率
动态码率,就是可以用minratemaxrate来控制最大和最小码率,只要保证码率在这个范围就可以,这样,那些本来就不需要太多比特来描述的画面就不需要太高的码率,它会自动降低码率,这样就达到了压缩视频而又不会太影响清晰度的目的。

然而,单纯靠minratemaxrate是不行的,因为动态码率涉及到码率变化,那怎么变?多久变一次?minratemaxrate是无法指定的,所以我们要用另一个参数-bufsize来协助,bufsize,英文好点的一看就知道它应该是buffer size的缩写,也就是缓冲区。

缓冲区-bufsize
缓冲区,就是ffmpeg处理视频的时候,暂时存储视频数据的地方,每当缓冲区满了,ffmpeg就开始根据缓冲区的数据计算该缓冲区所有数据的码率应该是多少,当然它会在-minrate-maxrate指定的范围内,一般是平均值上下。

如果缓冲区没有缓冲区,ffmpeg可能很长时间才计算一次码率,但是如果这段时间内的视频内容变化比较大,码率一平均的话,就比较不适应该时间段内的所有时间,比如10秒才计算一次,那可能第一二秒需要的码率很低,而七八秒需要的码率很高,但由于时间拉太长了,导致低码率那边也被设置成偏高的码率,这就是没有必须的,而且这会造成视频不稳定。

如果缓冲区太小,那说明它很快满,每次满了,ffmpeg就得计算一次当前缓冲区数据的码率,这会导致视频质量降低,因为ffmpeg要一直遵守这个缓冲区的限制,让它没有空间去处理一些优化,因为缓冲区太小,它可能没有包含足够多的帧来让优化起效果。

所以无论是使用-b:v指定码率,还是用-minrate-maxrate指定,都建议设置一个-bufsize,事实上-minrate-maxrate-bufsize一起使用,否则报错,而-b:v倒是可以不用-bufsize

一般来说,-bufsize可以设置为跟-b:v一样大,然后慢慢测试增大它,直到码率跳动太大(也就是比你用-b:v设置的码率高太多或者低太多),这时的-bufsize值基本上就是太大了,你可以适当缩小一点试试,直到码率跳动范围不那么大。

实例

ffmpeg -i /path/to/input.mp4 -c:v libx264 -b:v 2M -maxrate 2M -bufsize 1M /path/to/output.mp4

参见:
Limiting the output bitrate
How to consider bitrate, -maxrate and -bufsize of a video for web

裁剪视频

ffmpeg -i input.mp4 -vf crop=1080:743:0:454 -b:v 5000k output.mp4
  • -vf video filter,表示使用某个视频过滤器
  • crop-vf指定的视频过滤器,即剪裁视频过滤器,它的参数为crop=w:h:x:y,w为width,指定裁剪宽度,h为height,指定裁剪高度,x和y裁剪起始点(左上角坐标点)
  • -b:v b即bitrate,比特率(也叫码率),v表示视频比特率,另外有-b:a表示音频比特率
  • 本例的-vf crop=1080:743:0:454表示从input.mp4的(0,454)坐标为左上角起始点,裁剪一个宽x高=1080×743的视频,-b:v 5000k表示指定视频比特率为5000kbps.

获取裁剪参数(简单情况)

根据前面的裁剪视频可知,要裁剪一个视频,我们要两个参数:

  • 1、从哪里开始裁剪(即裁剪起始点坐标,裁剪起始点都是视频左上角,所以这个坐标应该是要裁剪出来的视频的左上角在原视频里的坐标点);
  • 2、要裁多大尺寸(即要裁剪的宽x高是多少);

先用播放器(比如IINA)打开这个视频,暂停播放,然后对这个播放器窗口截屏。

注意:如果使用自带的截图,cmd+shift+4启动截屏后,按一下空格可以切换到对窗口截屏,此时光标变成一个相机,这个相机放到某个窗口上,它会用淡蓝色显示截取的区域,此时点击鼠标即可截取这个窗口,但它默认是有阴影的,有阴影就会影响尺寸,如果不想要阴影,可以按住option键再来点击鼠标,这样截出来的图就是没有阴影的。

如果用其它截图软件截,也是鼠标点击窗口就可以直接截取窗口的(千万不要自己去框选,这样是选不准的),但是有些截图软件也是默认有阴影的,但一般可以设置不要阴影,反正我们这个截图是不能要阴影的,否则会影响判断尺寸。

截取图片之后,用自带的“预览”打开,其实macOS双击图片默认就是用“预览”打开的,除非你修改过。


打开图片之后,用选择工具框选一下(打开后直接用鼠标划选就可以,因为默认就是选择工具),就可以知道它的尺寸,注意这是截图的实际尺寸
Xnip2021-09-24_17-16-16.jpg

如果用预览打开后,又用普通的截图工具去看这个宽度,看到的并不是截图的实际尺寸,而是等比例缩小过的,另外手工划选是不够准确的
Xnip2021-09-24_17-32-49.jpg

前面用预览打开的图中可以看到实际尺寸是720×1280,这个尺寸就是标准的9:16=0.5625,而我用普通的截图软件截的尺寸是363×642≈0.5654。

右击视频→显示简介,可以查看视频本身的尺寸,可以看到也是720×1280的,跟我们截的图完全一样,这说明我们在图片上看到的尺寸跟视频尺寸是一样的
image.jpg

我们再来看,如果我们想把这个抖音号截取掉,在预览里用选择工具框选的尺寸是720×132,这样我们就知道了y坐标是132,x坐标肯定是0,因为是从最左侧截起,所以我们现在就能确定ffmpeg中需要指定(0,132)为顶点
image.jpg

知道了顶点,还需要知道截取的宽高,我们再来用选择工具框选一下,发现我们要的宽高,宽就是原视频宽度,也就是720,高是1000(因为底部也会出现抖音号)
image.jpg

最后我们用ffmpeg -i来查看这个视频的比特率与帧率,发现都不高,毕竟是抖音上下载的,所以我们不用修改比特率和帧率了
image.jpg

根据我们得到的顶点坐标以及要截取的宽高,得到裁剪命令如下

ffmpeg -i input.mp4 -vf crop=720:1000:0:132 output.mp4

最终效果,裁剪出来的视频刚好是我们指定的720×1000,而且剪裁的起始位置(视频的左上角位置为起始位置)也确实是我们指定的(0,132)坐标,这样就完成了
image.jpg

:我知道抖音视频可以直接下载没有水印的,但是我这里只是一个演示而已,并不是说我搞这个就是为了裁剪抖音视频。


获取裁剪参数(复杂情况)

上边只是比较简单的情况,因为截图长宽跟视频长宽刚好一样,但是事实上,很多时候是不一样的,很多时候,我们截的图是一个等比例缩小的图。

比如我用我安卓机拍的视频,宽高为1080×1920,也是9:16=0.5625,但是我用播放器打开这个视频,然后对播放器窗口截图,发现截的图并不是1080×1920,原因是我电脑屏幕没有这么大,播放器的窗口肯定是等比例缩小了的,事实上比例还稍微有点不对(跟0.5625有点点出入,但是基本上没啥问题,因为前两位小数是一样的)。

又比如我用我安卓机录屏,录的视频尺寸是1080×2400,1080:2400=9/20=0.45
image.jpg

显然用播放器打开,截图也不可能截出这个尺寸,截到的都是等比例缩小的,我这边亲测我截到的图是742:1648=0.4502427184≈0.45
image.jpg

假设我们要把头部有名字的地方去掉,那么根据下图我们可以得到截取的顶点坐标为(0,164)
image.jpg

要截取的尺寸为:宽x高=742×1400
image.jpg

错误参数:按前面得到的数据,我们可以得到这样的命令,然而这个命令的参数是错的

ffmpeg -i input.mp4 -vf crop=742:1400:0:164 output.mp4

错误的原因是我们从截图里得到的尺寸跟视频实际尺寸不一样,所以我们要根据视频尺寸等比例放大才行。

视频原尺寸为:宽x高=1080×2400,我们截图得到的尺寸为:宽x高=742:1648

我们把原宽1080除以截图宽742看看:1080/742≈1.456,意思就是说,我们截图的一个像素,就相当于原视频中的1.456个像素;
再按这个方法看一下高:2400/1648≈1.456,意思就是说,截图的一个像素,相当于原视频中的1.456个像素,因为宽高是等比例缩小,所以无论是宽还是高,它的像素与原视频像素比都是相同的(当然会略有误差)。

现在我们来算一下:

  • 实际截取宽度:因为在截图中我们要截取的宽度为742(全宽),而这742个像素,每个像素都相当于原视频的1.456个,所以我们应该在原视频中截取742×1.456≈1080个像素;
  • 实际截取高度:因为在截图中我们要截取的高度为1400,而这1400个像素,每个像素都相当于原视频的1.456个,所以我们应该在原视频中截取1400×1.456≈2038个像素。
  • 实际顶点:因为实际顶点只有y轴有数值,所以只要计算y就好了,我们截图的顶点y值是164,由于截图的高一个像素相当于原视频1.456个像素,所以我们计算164个像素在原视频中应该是164×1.456≈238.78个像素,具体是239,还是238,可以尝试切一下看看,如果视频比较长,可以先从里面切出一小段(几秒钟时长就够),再来测试裁剪效果,在我这个例子中,由于我的y轴没有靠近一些不同颜色的边界,所以无论我取238还是289都不会影响效果,我这里就取238吧;

也就是说,我们实际裁剪出来的视频:宽x高=1080×2038,顶点为(0,238),得到如下裁剪命令

ffmpeg -i input.mp4 -vf crop=1080:2038:0:238 output.mp4

裁剪效果还不错,顶部的名字和底部左下角的小图标以及右下角的叉都被截掉了
Xnip2021-09-24_20-22-20.jpg


截取视频

比如我想截取出某个视频中第10秒到第21秒那一段,就叫截取!所以截取视频就像在视频剪辑软件的时间线里,用刀片工具把视频切割出需要的部分,删掉头尾,留下切割出来的部分并导出。

主要参数-ss指定起始时间、-t指定截取长度、-to指定截取结束时间点。-ss要放在-i之前,如果放在-i之后需要解码,会比较慢,但比较精确,建议在-i前后都放上-ss这样既精确又快,见:这里


截取视频(-i是input,-ss为截取起始时间,-t为截取的长度)

ffmpeg -ss 00:00:20.300 -t 00:00:18.500 -i 爱的代价.mp4 -c:v copy -c:a copy 爱的代价_clip.mp4

-ss-t都要放在-i前面(放在后面也可以但是会很慢,因为需要先做解码操作)。


截取视频(-i是input,-ss为截取起始时间,-to为截取结束时间)

ffmpeg -ss 00:00:05 -t 00:00:18 -i 爱的代价.mp4 -c:v copy -c:a copy 爱的代价_clip.mp4

该命令表示截取5-18秒时间段内的视频。


只指定起始时间,不指定长度,表示截取到末尾

ffmpeg -ss 00:33:45 -i /path/to/input.mp4 -c:v copy -c:a copy /path/to/output.mp4

只指定长度,不指定起始时间,表示从视频开头开始截

ffmpeg -t 00:00:20 -i /path/to/input.mp4  -c:v copy -c:a copy /path/to/output.mp4

从第5秒开始,截取20秒长的片段(-ss-t如果只写一个数字,则单位是秒)

ffmpeg -ss 5 -t 20 -i /path/to/input.mp4 -c:v copy -c:a copy /path/to/output.mp4

无论-ss-t,都用这个格式:时时:分分:秒秒.毫毫毫,比如-ss 00:00:20.300表示从0时0分20秒再过300毫秒处开始截取,而-t 00:00:18.500则表示截取长度为18秒再加500毫秒的长度。

当然,还可以是另一个格式,就是-ss 1 -t 5,只写一个数字,这个数字的单位是秒,所以这里表示从第1秒截取到第5秒。

:1s=1000ms,所以毫秒位置最大可写999,因为到了1000就进1了。

音画同步

我们经常能遇到视频中音频和画面不同步问题(也叫音画不同步,即声音和画面不同步),那要怎么调整呢?

我们假设有一个唱歌视频“爱的代价.mp4”,它的声音比视频快了1秒,也就是你听到声音之后才看到它说话的口型,因此我们要把它的音频延迟1秒,命令如下

ffmpeg -i 爱的代价.mp4 -itsoffset 00:00:01.000 -i 爱的代价.mp4 -map 0:v -map 1:a -vcodec copy -acodec copy 爱的代价_sync.mp4

解释:ffmpeg是可以有多个输入的,像上述命令就用了两个-i表示有两个输入,而且这两个输入都是同一个视频,因为我们要从其中一个视频中抽取视频轨,而从另一个视频中抽取音频轨,然后再把这两个轨道合并在一起,因为只有把音频和视频分开了,我们才能调整它的同步问题。

  • -i 爱的代价.mp4 第一个视频输入;
  • (同步的关键)-itsoffset 00:00:01.000 -i 爱的代价.mp4 第二个视频输入,它前面的-itsoffset 00:00:01.000表示这个视频延迟1秒钟输入;
  • -map 0:v-map 1:a -map 0:v表示抽取第1个输入文件中的视频轨道数据(0:v中0表示第一个输入文件,v表示video,即视频轨),-map 1:a表示抽取第2个输入文件中的音频轨道数据(1:a中1表示第二个输入文件,a表示audio,即音频);
  • vcodec copy 指定视频编码(v表示video),copy意思是直接复制原来的编码,不进行转码处理,还可写成-c:v copy
  • -acodec copy 指定音频编码(a是audio的意思),copy意思是直接复制原来的编码,不进行转码处理,还可写成-c:a copy
  • 爱的代价_sync.mp4 指定输出文件名(可以带路径),其中的.mp4表示输入格式,ffmpeg会自动获取这个格式,当然也可以在它前面用-f mp4手动指定(f表示format)。
  • -map 0:v-map 1:a 的顺序决定输出文件中视频轨和音频轨的顺序,如果把它们的顺序调换过来也是没问题的,只不过音频轨变成了第一个轨道,但调换顺序不会改变它们获取视频轨或音频轨的来源,因为0:v1:a中的0和1,已经决定了它们从第一还是第二个输入文件中获取(之所以用0和1而不用1和2,是因为编程届下标都是从0开始的)。
  • -vcodec copy -acodec copy只用来指定输出文件的音频和视频编码,跟它们的顺序无法,比如-map 0:v -map 1:a表示视频轨在前,音频轨在后,但指定编码可以反过来写-acodec copy -vcodec copy,因为这个跟顺序是无关的,vcode就是指定视频编码,跟它写在前还是写在后没有关系。

由于第二个视频整体输入被延迟了1秒,所以从它里面提取出来的音频也比原来延迟了一秒,这样第一个视频轨和第二个音频轨合成之后的效果,就变成了音频轨延迟一秒的效果。

当然,如果你事先不知道延迟了多少,只能靠肉眼看和听,猜一下延迟了多少,然后再来测试转换后的效果,虽然音画同步在不转换编码的情况下很快就能完成,但如果文件太大,比如上G,如果直接用原文件测试,每测试一次就相当于复制出一个上G 的文件,对电脑硬盘不好,建议先从里面切出一小段来测试音画同步,测试成功后,再对整个文件进行操作。

关于-map
map在这里是“映射”的意思,在ffmpeg里,-map选项用于手动控制每个输出文件的流选择。

如下图所示,ffmpeg会给每个输入文件一个编码,根据编程届的惯例,下标当然都是从0开始的,于是,第一个输入的文件被编号为#0,第二个被编号为#1,……,以此类推,
image.jpg
同理,每个视频可能有很多个轨道,当然一个正常的视频,至少也有一个视频轨和一个音频轨,但是有些可能有多个音频轨(比如一部电影可能有不同语言),以及有多个字幕轨(比如一部电影可能有不同语言字幕)。

每个轨道也会从0开始编号,而且前面还会标记它们属于哪个输入,比如:Stream #0:0表示第一个输入文件中的第一个轨道(通常第一个轨道都是视频轨道),当然由于编程届都是以0为下标的,所以第一个都是0;又比如:Stream #1:1表示第二个输入文件中的第二个轨道,一般第二个轨道都是音频轨道,当然由于编程届都是以0为下标的,所以第二的下标是1,以此类推。

所以,-map 0:v其实可以写成-map 0:0,因为v代表video(视频),0:v意思是抽取输入文件中编号为0的文件(就是第一个-i指定的那个文件)中的视频轨,而视频轨我们知道就是0号轨道,所以这两个写法在这里是相同的,当然有些有画中画效果的视频,它可能有两个甚至多个视频轨,这个就需要你按需求选择其中的某个轨道。

同理,-map 1:a中的a表示audio(音频),表示抽取音频轨,其实也是可以写成-map 1:1的,因为其实我们是要从输入编号为1的输入文件中(其实就是第二个-i指定的那个文件)中的音频轨,而在编号为1的输入文件中,音频轨是1,所以在这里-map 1:a相当于-map 1:1,当然,有些视频中可能会有多个音频轨,比如多语言的电影,比如分轨录制的音乐(钢琴一个轨、吉他一个轨、人声一个轨等等),这些就要根据实际情况选择你需要的轨道了。

-map 0:v-map 1:a的顺序决定了输出轨道的内容,如果调换过来,则会变成音频在第一个轨道,视频在第二个轨道,这对播放没什么影响,不过一般还是视频在第一个轨道好点。

添加水印和画中画

添加水印本质上就是图片叠加到视频的某个位置上,如果把图片换成视频,也就是视频叠加到视频上,就变成“画中画”了。


把水印图片添加到视频左上角

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=0:0" /path/to/output.mp4

-filter_complex 指定复杂过滤器
overlay=0:0 指定水印位置(水印图片左上角坐标),格式overlay=x:y,x、y是坐标,原点为视频的左上角,所以0:0表示水印的左上角刚好与视频的左上角重合。

注意:确定水印图片位置的本质,就是确定水印图片左上角那个点在视频中的坐标。


把水印添加到视频右下角

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=main_w-overlay_w:main_h-overlay_h" /path/to/output.mp4

main_w ffmpeg变量,表示视频的宽度;
main_h ffmpeg变量,表示视频的高度;
overlay_w ffmpeg变量,表示要覆盖到视频上的图片的宽度;
overlay_h ffmpeg变量,表示要覆盖到视频上的图片的高度;

其实main_woverlay_w可分别用大写W和小写w来表示,同理main_hoverlay_h可以用大写H和小写h来表示,即上边命令可简化成

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=W-w:H-h" /path/to/output.mp4

由于overlay指定的是水印图片左上角坐标,所以如果要放到右下角,实际上水印图片左上角的起始坐标是(视频宽-水印宽,视频高-水印高)。

如果你不想刚好靠到右上角边边,那就x,y分别减掉一些像素,比如10,这样就会变成(视频宽-水印宽-10,视频高-水印高-10)。


把水印添加到视频正中心

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2" /path/to/output.mp4

其实本质上都是计算水印图片左上角起始坐标应该在哪里,如果把一张图片放到一个视频的中心,毫无疑问,它的左上角坐标,x坐标为:视频宽的一半-水印图片宽的一半,写成数学表达式就是:视频宽/2-水印图片宽/2,合并一下,就是:(视频宽-水印图片宽)/2,而y坐标同理。


把水印添加到视频右上角

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=main_w-overlay_w:0" /path/to/output.mp4

前面说过放到右下角,其实右上角,就是把右下角那句命令的y坐标变成0。


把水印添加到视频左下角

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg  -filter_complex "overlay=0:main_h-overlay_h" /path/to/output.mp4

前面说过放到右下角,其实左下角,就是把右下角那句命令的x变成0。


同时添加两个水印(第一个添加到左上角、第二个添加到右下角)

ffmpeg -i /path/to/input.mp4 -i /path/to/bdlogo.jpg -i /path/to/googlelogo.jpg  -filter_complex "[0:v][1:v]overlay=0:0[flag]; [flag][2:v]overlay=W-w:H-h" -c:a copy /path/to/output.mp4

注意:图片属于无声音的静止视频,你用ffmpeg查看一个jpg图片的信息,你会发现它有一个视频轨道,视频格式为mjpeg

Input #0, image2, from '/Users/bruce/Downloads/bdlogo.jpg':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 4997 kb/s
  Stream #0:0: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 440x148 [SAR 144:144 DAR 110:37], 25 fps, 25 tbr, 25 tbn, 25 tb
  • 同时添加两个水印,本质上是先添加一个,再把添加后的结果再次添加第二个;
  • 同一滤镜链之间的不同滤镜用逗号隔开(在这里没有其它滤镜,所以没有体现出来)
  • 不同滤镜链之间用分号隔开,在这里体现出来了;
  • 滤镜链输入输出通过方括号标记命名;
  • [0:v][1:v]分别标记第一个输入文件和第二个视频文件中的视频轨(-i输入的文件从0开始算),v表示video,表示取它的视频轨;
  • [0:v][1:v]overlay=0:0[bkg]表示把第二个输入文件的视频轨覆盖到第一个输入文件视频轨上,覆盖起始坐标为(0,0),后面的[flag]用于标记这个操作的输出,你自己写成[abc]也可以,因为这个标记是让后面引用的,你标记的什么,后面就用什么就行;
  • [flag][2:v]overlay=100:75 [flag]表示第一个滤镜链的操作结果,[2:v]表示第三个输入文件的视频轨(由于从0开始数,所以第三个为2,v是video,表示取视频轨);
  • -c:a copy表示复制音频编码。注意不能复制视频编码,因为要覆盖一层图片上去,视频一定得重新编码,所以是无法复制的,如果用-c:v copy将会报错;
  • 假设视频和图片都只有一个轨道,则[0:v][1:v][2:v]可分别写成[0:0][1:0][2:0],冒号前面的数字表示-i输入文件(从0开始数),冒号后面的数字表示轨道编号,可以用ffmpeg -i /path/to/video.mp4ffmpeg -i /path/to/image.jpg来查看它们的轨道编号;
  • 再啰嗦一遍:图片是一种只有一个视频轨的静止视频,用ffmpeg -i /path/to/image.jpg来查看一张图片的信息,可以发现它们有一个视频轨;
  • 按这个原理,你同时添加n个水印都可以,本质上是一个添加完了再添加下一个,用方括号标记一下输出,下一个用的时候引用一下上一个输出就行;

同时添加两个水印(简单写法)

ffmpegnb -i /path/to/input.mp4 -i /path/to/watermark1.jpg -i /path/to/watermark2.jpg  -filter_complex "overlay=0:0, overlay=W-w:H-h" -c:a copy /path/to/output.mp4

前面的写法属于通用方法,现在这种写法属于简写,同样你也可以添加n个水印,只要-i输入图片的顺序对应好overlay指定位置的顺序就好了。


同时添加两个水印,并对水印进行缩放

ffmpegnb -i /Path/to/input.mp4 -i /Path/to/bdlogo.jpg -i /Path/to/googlelogo.jpg  -filter_complex "[1:v]scale=90:-1[img1];[2:v]scale=iw/2:-1[img2];[0:v][img1]overlay=25:(H-h)/2[flag]; [flag][img2]overlay=W-h:H-h" -c:a copy /Path/to/output.mp4
  • 原理与前面说过的第一种同时添加两个水印的方法一样,其实就是多个操作的组合,只不过用标记来标记一下前面操作的输出;
  • [1:v]scale=90:-1[img1] [1:v]表示第二个文件的视频轨,scale=90:-1表示把该视频的宽设置为90像素,高按比例缩放,[img1]表示输出,只不过用方括号表示标记,可以在后面引用这个值;
  • [2:v]scale=iw/2:-1[img2]跟前面一样,只不过这次处理的是第二个视频,只不过这次是把宽的原尺寸除以2,高还是按比例缩放;
  • 处理好图片的缩放之后,后面overlay的部分可以参考上边说的,都是指定两个视频轨,第二个会覆盖在第一个上,overlay用于指定覆盖者的左上角坐标;

水印宽高设置为视频宽高的百分比

ffmpeg -i /path/to/input.mp4 -i /path/to/image.jpg -filter_complex "[1:v][0:v]scale2ref=oh*mdar:ih[logo-out][video-out];[video-out][logo-out]overlay=0:0" -c:a copy /path/to/output.mp4

本例是把水印的高设置为视频的高,宽自动按原比例缩放。

我们主要是要理解这一句

[1:v][0:v]scale2ref=oh*mdar:ih[logo-out][video-out]

这句是scale2ref滤镜的使用,它的官方文档在:FFmpeg-scale2ref

这个scale就是缩放,ref我个人猜是reference,就是参考、参照。你有没有想过,同一个水印,覆盖到不同视频上,它可能会有不一样的结果,最典型的就是分辨率越大的视频,你会感觉水印在这个视频里越小,可以参考这里

要想避免这种情况的发生,我们需要参考原视频的宽高来去调整水印的宽高,比如,让水印的高永远都是视频高度的十分之一,不管分辨率大的视频还是小的视频。所以reference的意思就是参考原视频大小来对图片进行缩放。

scale2ref滤镜的输入,是先图片,后视频,而输出刚好反过来,这是固定顺序,它用于参考视频的宽高来设置图片的宽高。

oh*mdar:ih 这个,如果熟悉点的应该看的出来,冒号前后分别表示宽和高,i表示input,所以ih就是input height,即输入源的高度,而oh自然就是output height,输出高度,现在主要的问题是,哪个是输入,哪个是输出?

首先,我们知道,输入在前,输出在后,这是ffmpeg命令的惯例,别说ffmpeg,其实所有的命令,只要涉及输入输出的,肯定是输入在前,输出在后的。

所以毫无疑问,在scale2ref=oh*mdar:ih前边的,就是输入,在它后边的就是输出。我们可以看到,它前边有[1:v][0:v]两个源,第一个是图片(因为数字1表示第二个-i,从零开始数,第二个就是1),而第二个则是视频(0表示第一个-i,因为从0开始数)。

同样我们可以看到,输出也有两个:[logo-out][video-out],根据它们的名字就可以看出来,第一个输出[logo-out]是图片,第二个输出[video-out]是视频,这个是scale2ref过滤器的规定。

那问题来了,输入和输出都有两个,那ih到底指的是哪个输入的高呢?同理,oh指的是哪个输出的高呢?答案是:都是指最最靠近scale2ref=oh*mdar:ih这句命令的。对于输入,最靠近它的是[0:v],所以ih就表示视频的高度,而对于输出,[logo-out]最靠近它,oh是指输出图片的高度。

scale2ref本来就是设置图片宽高的,又因为oh*mdar:ih已经把图片的高设置为ih了,所以它的输出高度肯定也是ih,也就是说,oh其实是等于ih的,所以oh*mdar:ih其实也可以写成ih*mdar:ih(实测图片覆盖效果相同)。

mdar 是视频的显示宽高比(dar是display aspect ratio),也就是我们播放这个视频时它的宽高比(比如4:3、16:9等等),也可写成main_dar,是由(main_w / main_h) * main_sar计算而来;

sar 是sample aspect ratio的缩写(注意不是storage aspect ratio),中文应该理解为“样本宽高比”,“样本”是构成视频的帧(图片)的最小单位,其实就是一个像素,所以在视频中,SAR就是PAR,只不过是两种不同的说法,参见:Confused about PAR(Pixel Aspect Ratio) in video

关于SAR和DAR有以下等式(参见这里)

 SAR_x    width      DAR_x              width
------ x -------- = -------  ==> SAR x -------- = DAR
 SAR_y    height     DAR_y              height

SAR_x就是像素的宽度,SAR_y就是像素的高度,width就是视频横向的像素个数,height就是视频纵向的像素个数,比如1920×1080,就表示横向有1920个像素,纵向有1080个像素。

所以,其实我们可以把上面的等式分子分母分开来,那就有

像素宽 * 横向像素个数 = 播放时的宽
SAR_x * width = DAR_x

像素高 * 纵向像素个数 = 播放时的高
SAR_y * height = DAR_y

之所以会有这些概念,是因为NTSC和PAL电视制式编码以前是用电子管扫描的,现在要转成数字化,它们的等效像素并不是正方形的,而是长方形的。

比如一个704×576的视频,它播放出来是4:3,但按照我们普通的计算,768×576才是4:3,唯一的解释是:显示器的像素是长方形的,它的宽>高,说明横向704个像素加起来的宽度,相当于纵向768个像素加起来的宽度。

特别注意:网上有很多介绍PAR、SAR、DAR之间的关系的文章,大多数都说:PAR x SAR = DAR,然而这里的SAR是Storage Aspect Ratio,不是Sample Aspect Ratio。

这个等式要跟我前面那个等式对比的话,应该是这样

① SAR * (width/height) = DAR ➡ SAR指:Sample Aspect Ratio
   ⬇          ⬇          ⬇
② PAR *      SAR       = DAR ➡ SAR指:Storage Aspect Ratio

其实②中的PAR就是①中的SAR(S是sample,取样的意思,其实就是指单个像素),而②中的SAR(S是Storage)相当于①中的(width/height),也就是分辨率(因为存储的肯定是像素),至于DAR,跟前面等式中的DAR是一个意思,都是指视频播放出来的宽度。我当时研究这个都被这整疯了,查来查去,都对不上,后来在stackoverflow提问才明白:Confused about PAR(Pixel Aspect Ratio) in video


水印终极大招:把logo的高设置为视频高的十分之一,宽按比例缩放,这样无论什么分辨率的视频,logo相对视频的比例都是一样的

ffmpeg -i /path/to/input.mp4 -i /path/to/watermark.jpg -filter_complex "[1:v][0:v]scale2ref=oh*mdar:ih/10[logo-out][video-out];[video-out][logo-out]overlay=W-w-10:10" -c:a copy /Path/to/output.mp4
  • 主要是要理解scale2ref是用于把图片按视频的宽高做处理的,相关原理已经在前面讲过,这里就不讲了;
  • W-w-10:10,冒号前后分别是x,y坐标,单位是像素,y=10很容易看出来,可是x=W-w-10是什么鬼?大W是视频宽度,小w是图片宽度,由于定义图片就是定位它的左上角,而如果我想把logo放到视频右上角的话,logo的左上角x坐标就是:视频宽度-logo宽度,再减去10是要退回来一点,不让它刚好碰到边。

在某个时间段显示水印
典型的就是从抖音下载的视频的水印,一会儿在左上角,一会儿在右下角,实现原理是给overlay加个enable开关,只有在指定时间才执行overlay。

把watermark.jpg在1-5秒时加到左上角,6-10秒时加到右下角(已知input.mp4时长10s)

ffmpegnb -i /path/to/input.mp4 -i /path/to/bdlogo.jpg -filter_complex "[1:v][0:v]scale2ref=oh*mdar:ih/10[logo-out][video-out];[logo-out]split [logo1][logo2];[video-out][logo1]overlay=10:10:enable='between(t,1,5)'[flag]; [flag][logo2]overlay=W-w-10:H-h-10:enable='between(t,6,10)'" -c:a copy /path/to/output.mp4

主要原理:前面的命令中,overlay只给了两个参数,也就是x,y坐标:overlay=x:y,其实它还有第三个参数,就是enable参数,我们就是通过enable参数来开关overlay(加水印)功能:overlay=x:y:enable=1

between(t,1,5) t是视频当前播放时间,意思是,如果播放的时间在第1秒和第5秒之间,那么就返回1(true),否则返回0(false),overlay的enable就可以根据这个值来决定是否加水印,between也可以两个参数between(t,5),这样就是不指定起始时间,表示从视频的0秒开始,显示5秒钟水印。除此之外,还有lte(less than equal,小于等于),gt(greater than)等可以用;

split 把一个流分成多个,比如:[logo-out]split [logo1][logo2]就是把scale2ref处理后的logo分成了两个流,因为有两个位置要打水印,如果不分成两个流,那么第一个overlay用完这个[logo-out]之后,它就没了,可是第二个overlay还要用这个经过scale2ref处理的logo呢,当然你也可以用scale2ref把源图片再处理一次,但这就是重复动作了,属于浪费资源,另外,这句split还可以写成[logo-out]split=2[logo1][logo2](split可以随意分割成多个流,不一定是两个)。split是分视频流,如果要分音频流,要用asplit(a是audio),因为图片属于静止视频,图片里是视频轨,所以图片也用split,这是split官方文档:16.31 split, asplit

scale2ref纯粹就是前面的知识,用于根据视频的宽高来确定水印图片的宽高。另外添加两个水印的方法前面也有,这个其实就是前面的综合应用。


透明水印:方法一样,只要你的图片是png图片有透明部分,那么用前面的方法添加上去,图片透明的部分自然就是透明的。


gif图水印:方法一样,只要你的图片gif动图,那么用前面的方法添加上去gif图也是会动的,只不过gif图会在视频播放停止后停止,因为gif图是无限循环的,如果让它一直播放,会导致视频无限大,所以只能在视频播放停止的时候让它也停止。


画中画:画中画其实就是把前面所有的图片换成视频就行,同样的操作。只不过,由于视频是有时间的,如果覆盖上去的视频比主视频时间长,那么主视频会停留在最后一帧,画中画视频会继续播放。

删除水印

删除水印实例代码

ffmpegnb -i /path/to/input.mp4 -vf delogo=988:18:269:108:1 -c:a copy /path/to/output.mp4
  • -vf 一路看下来的应该不用解释了吧,vf就是video filter的意思,表示指定一个视频过滤器,而现在指定的视频过滤器是delogo
  • delogo 是一个用于删除水印的视频过滤器,delogo其实就是delete logo的缩写,delete一般可写成del,所以就变成了del logo,只不过logo也是l开头,所以两个l合成一个,就变成了delogo;
  • delogo参数为delogo=x:y:w:h:show,(x,y)是要删除的logo的左上角那个点的坐标,w和h是要删除的logo的宽和高,show可以是1和0,1的话,被删除的logo位置会加上一个绿色的框,0的话就什么都不加,默认是0;有一种说法是show前面还有个t,用于表示添加的绿色框的边框宽度,但我实测报错,也许那是以前的用法,后来去掉了吧;
  • 注意,被截取的logo左上角坐标x和y任何一个都不能为0,否则会报错“Logo area is outside of the frame.”,原因是删除logo的原理是根据周围像素计算logo里面应该填充什么像素,如果你有一个坐标为0,那就说明它的左边或上边没有像素,没有像素就导致它无法计算填充像素;同理,就算你的坐标都不是0,但x坐标加上logo宽度刚好碰到右边的边,或者y坐标加上logo高度刚好碰到下边的边,它也会报错,根本原因就是因为删除logo需要根据四周的像素来计算填充,有一条边没有像素,它都无法计算。
  • 如果你的logo确实是在最边边上,那你只能先把视频用pad填充一下,删除logo后,再截取回原尺寸,不过由于填充的只是纯色,这会导致计算填充像素不准确,比如你填充黑色像素或白色像素,就会导致你的logo删除后,会拉丝,拉黑色的或白色的比,但这也是没办法的。

删除logo后的视频

颜色填充

delogo删除水印时,如果遇到水印刚好在边角上,也就是水印左上角坐标有任意一个为0的情况,会报错,这时我们就要先给它填充一点像素,让水印不是在边上,然后就可以删除logo了,删除logo后再裁剪掉填充的像素(裁剪回原尺寸),不需要先导出视频再裁剪,可以直接链式操作。

pad使用实例

ffmpegnb -i /path/to/input.mp4 -vf pad=1940:1100:10:10:#FDDF88 -c:a copy /path/to/output.mp4
  • 我的视频尺寸是:1400×1076;
  • pad的语法是pad=w:h:x:y,w、h为填充后的宽高,x、y为原视频左上角在填充后的视频里的坐标;
  • 1940:1100:10:10:#FDDF88,表示填充后的视频宽高为1940×1100,原视频左上角坐标位于填充后的视频的(10,10)处,填充的颜色为#FDDF88,也可以写英文,比如:red、yellow、blue等等,不写颜色默认为黑色。
  • 其实1940×1100就是1920×1080各加了20像素,并且让原视频左上角坐标处于填充后视频的(10,10)处,能想像出来是什么样子吗?因为宽高各加了20,而现在原视频左上角坐标是(10,10),说明下边和右边各自还有10像素,也就相当于把视频加了一个10像素的边。

pad文档:11.168 pad


我的一个视频有两个顶到左边边上的logo,现在我要把这两个logo删掉,按道理应该这样写

ffmpeg -i /path/to/input.mp4 -filter_complex "delogo=0:40:70:100, delogo=0:180:300:200" -b:v 5000k -c:a copy /path/to/output.mp4

但实际上,上边命令会报错

[delogo @ 0x7fa7e39049c0] Logo area is outside of the frame.
[Parsed_delogo_0 @ 0x7fa7e39048c0] Failed to configure input pad on Parsed_delogo_0
Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument
Error while processing the decoded data for stream #0:0
Conversion failed!

报错的原因,就是因为两个delogo指定的x坐标都是0,我前面说了,删除logo本质上是需要把删除掉的区域填充回来的,而填充需要根据它四周的像素来计算应该填充什么,只要有一边缺少像素,它就无法计算,无法计算就会报错。

我们需要做的就是利用pad的功能,将它的左边填充一点像素,让x不为0,由于我的视频是1390×1076的,所以我在x方向填充10个像素,让它变成1400×1076,而原视频相对填充后的视频,其实就是x轴为10,y还是不变

# 原命令:会报错
ffmpeg -i /path/to/input.mp4 -filter_complex "delogo=0:40:70:100, delogo=0:180:300:200" -b:v 5000k -c:a copy /path/to/output.mp4

# 添加pad的命令:填充白色
ffmpeg -i /path/to/input.mp4 -filter_complex "pad=1400:1076:10:0:#FFFFFF ,delogo=10:40:70:100, delogo=10:180:300:200, crop=1390:1076:10:0" -b:v 5000k -c:a copy /path/to/output.mp4

:由于pad、delogo、crop都是视频过滤器,所以-filter_complex也可以换成-vf-filter:v

可以看到,logo确实能去掉但由于左侧填充的是纯白色,所以计算的填充像素,也是白色从左侧往右边融合,这样不太好看

把填充颜色改成取自logo周围环境的颜色

# 添加pad的命令:填充颜色取自logo周围
ffmpeg -i /path/to/input.mp4 -filter_complex "pad=1400:1076:10:0:#597886 ,delogo=10:40:70:100, delogo=10:180:300:200, crop=1390:1076:10:0" -b:v 5000k -c:a copy /path/to/output.mp4

可以看到,去除效果好多了
16342996274962.jpg

裁剪填充去水印联合应用

ffmpeg -i /path/to/input.mp4 -vf "crop=1400:1076:600:4, pad=1410:1076:10:0:#597886 ,delogo=10:40:70:100, delogo=10:180:300:200, crop=1400:1076:10:0" -b:v 5000k -c:a copy /path/to/output.mp4
  • -vf是video filter的缩写,用于指定视频过滤器;
  • 引号内的全部是视频过滤器,用逗号分开,我们可以看到用了四次过滤器,分别是:crop、pad、delogo、delogo、crop;
  • 整体意思是:先用crop裁剪视频,把视频的黑边去掉,然后由于剪裁后logo挨着最左边,所以用pad给它的左边填充10个像素(注意填充的颜色是要删除的logo周围的颜色,,这也是为什么要先剪裁掉黑边再填充的原因),然后删除两个logo,最后再用crop把pad填充的像素裁剪掉,变回原视频尺寸。
  • 注意crop和delogo的参数方向是反过来的,crop是w:h:x:y,而delogo是x:y:w:h,注意不要搞反了;
  • -b:v 5000k指定视频码率为5000k;
  • -c:a copy表示不改变音频编码,直接复制原来的音频编码,因为我们处理的是视频部分,跟音频无关;

修剪+裁剪+音量+比特率+音画同步

假设现在有一个视频,需要掐头去尾,并且需要裁剪视频画面中的一部分,还需要加大音量,调整比特率,最后还要做音画同步。

这里除了音画同步无法同时做,其它的都可以同时做,也就是说这可以分成两个命令来操作,一个命令做修剪+裁剪+增大音量+调整比特率,另一个命令做音画同步。

修剪+裁剪+增大音量+调整视频比特率

ffmpeg -hide_banner -ss 00:00:15.000 -t 00:03:29.000 -i "/path/to/测试视频.mp4" -vf crop=1912:1076:244:4 -af volume=22dB -b:v 5000k "/path/to/测试视频_crop.mp4"
  • -hide_banner 不输出一大堆头部信息;
  • -ss 00:00:15.000 -t 00:03:29.000 掐头去尾,表示从15秒开始裁剪到第03分44秒,只不过由于裁剪只能指定长度,所以需要把03:44减掉开头的15秒,这样我们就知道要截取的长度是03分29秒了;
  • -i "/path/to/测试视频.mp4" 指定输入文件;
  • -vf crop=1912:1076:244:4 裁剪视频,裁剪出的视频宽高为1912×1076,剪裁起始点(即视频左上角)坐标为(244,4);
  • -af volume=22dB 表示在原音量的基础上加大22分贝,一般来说,-28分贝的音频,它的音量我感觉挺合适;
  • -b:v 5000k 指定视频比特率为5000kbps;
  • "/path/to/测试视频_crop.mp4" 指定输出文件路径和文件夹名;
  • 视频文件的输入和输出路径,可以加双引号,也可以不加,但是如果遇到有空格的路径,不加双引号就会报错找不到路径,除非你把每个空格都用反斜杠\转义,但这样还不如用双引号方便;

音画同步

ffmpeg -hide_banner -i "/path/to/测试视频_crop.mp4" -itsoffset 00:00:00.700 -i "/path/to/测试视频_crop.mp4" -map 0:v -map 1:a -vcodec copy -acodec copy "/path/to/测试视频_crop_avsync.mp4"
  • -itsoffset 00:00:00.700 放在-i前面,表示这个-i输入的文件延迟700毫秒;
  • -map 0:v -map 1:a抽取第1个输入文件的视频轨和第二个输入文件的音频轨;
  • -vcodec copy -acodec copy 分别指定视频和音频编码,copy是指不转换编码,直接复制原来的编码;

我写了一个自动生成命令的脚本

<?php
/**
 * 把给定纯文本内容复制到系统剪贴板,兼容Mac/Win/Linux(只能普通文本内容,不支持富文本及图片甚至文件)
 * @param $content
 *
 * @return string|null
 */
function copyPlainTextToClipboard($content){
    //Mac和Win都自带,Linux(桌面系统)一般需要需要自己安装xclip(如Ubuntu: apt install xclip)
    $clipboard = PHP_OS=='Darwin' ? 'pbcopy' : (PHP_OS=='WINNT' ? 'clip' : 'xclip -selection clipboard');
    //echo不是php命令,而是shell命令,win/mac/linux都有echo这个命令的
    //在Mint(基于Ubuntu)系统中,发现在终端执行命令的方式调用上传,如果有使用shell_exec()则会导致终端不退出
    //但在Mac和Win就没这个问题
    $command = "echo '{$content}' | {$clipboard}";
    return shell_exec($command);
}

if(!isset($argv[1]) || !$argv[1]){
    exit("\n请输入视频文件路径!\n\n");
}
$videoPath = $argv[1];

//获致视频比特率
$cmd = '/usr/local/bin/ffprobe -hide_banner -i "'.$videoPath.'" 2>&1';
// echo "\n".$cmd."\n\n";exit;
$returnStr = shell_exec($cmd);
$arr = explode("\n",$returnStr);

$arr2 = [];
foreach($arr as $val){
    if(strpos(ltrim($val),'Video') !== false){
        $arr2 = explode(',', $val);
        break;
    }
}

$arr3 = [];
foreach($arr2 as $val){
    if(strpos(ltrim($val),'kb/s') !== false){
        $bitrateWithUnit = ltrim($val);
        break;
    }
}

$arr3 = explode(' ',$bitrateWithUnit);
$bitRate = (int)$arr3[0];
$bitRateOrigin = $bitRate;
if($bitRate/3<5000){
    if($bitRate<3000){
        $bitRate = $bitRate.'k';
    }else{
        if($bitRate/3<3000){
            $bitRate = '3000k';
        }else{
            $bitRate = ceil($bitRate/3).'k';
        }
    }
}else{
    $bitRate = '5000k';
}

//
$cmd = '/usr/local/bin/ffmpeg -hide_banner -i "'.$videoPath.'" -af volumedetect -f null /dev/null 2>&1';
// echo $cmd."\n";exit;
$returnStr = shell_exec($cmd);
$matches = [];
preg_match('/mean_volume\: (-\d{2}\.\d)/', $returnStr, $matches);
$meanVolume = round($matches[1]);
$volumeUpAmount = 0;
if($meanVolume < -28){
    $volumeUpAmount = -28 - $meanVolume;
}
// var_dump($meanVolume, $volumeUpAmount);

$videoPathLen = strlen($videoPath);
$videoPathWithoutExt = substr($videoPath, 0, $videoPathLen-4);
// $ext = substr($videoPath, $videoPathLen-4);

$content = '';
$volumeUp = $volumeUpAmount ? '-af volume='.$volumeUpAmount.'dB' : '';

//截取+裁剪+增大音量+修改比特率
$cmd5 = '/usr/local/bin/ffmpeg -hide_banner -ss 00:00:15.000 -t 00:03:29.000 -i "'.$videoPath.'" -vf crop=1912:1076:244:4 '.$volumeUp.' -b:v '.$bitRate.' "'.$videoPathWithoutExt.'_crop-volume-up.mp4"';$content .= "\n截取+裁剪+增大音量+修改比特率(原始视频比特率:{$bitRateOrigin} kb/s,原始平均音量:{$meanVolume}dB):\n{$cmd5}\n\n";

//先听到声音再看到口型(声音快了),把音频延迟700毫秒
$cmd7 = '/usr/local/bin/ffmpeg -hide_banner -i "'.$videoPathWithoutExt.'_crop-volume-up.mp4" -itsoffset 00:00:00.700 -i "'.$videoPathWithoutExt.'_crop-volume-up.mp4" -map 0:v -map 1:a -vcodec copy -acodec copy "'.$videoPathWithoutExt.'_crop-volume-up_avsync.mp4"';$content .= "\n把声音延迟700毫秒:\n".$cmd7."\n\n";

//先看到口型再听到声音(画面快了),把画面延迟700毫秒
$cmd8 = '/usr/local/bin/ffmpeg -hide_banner -itsoffset 00:00:00.700 -i "'.$videoPathWithoutExt.'_crop-volume-up.mp4" -i "'.$videoPathWithoutExt.'_crop-volume-up.mp4" -map 0:v -map 1:a -vcodec copy -acodec copy "'.$videoPathWithoutExt.'_crop-volume-up_avsync.mp4"';$content .= "\n把画面延迟700毫秒:\n".$cmd8."\n\n";

copyPlainTextToClipboard($content);
echo "\n命令已复制到剪贴板,请到编辑器中粘贴查看\n";

脚本使用方法

  • 1、新建一个php文件,比如ffmpegphp.php,然后把以上代码复制到文件内,保存;
  • 2、在终端中执行:php /path/to/ffmpegphp.php /path/to/测试视频.mp4,提示命令已复制到剪贴板后,然后你直接去编辑器里cmd+v就可以粘贴出脚本生成的命令;
  • 3、适当调整脚本生成的命令中的参数,即可复制命令去终端里执行,先执行第一条裁剪相关的命令,然后再复制音画步命令去执行就可以;
  • 3、更方便的方法:在~/.bashrc中添加一行(但这样就不能随意移动ffmpegphp.php文件的位置)
alias ffmpegphp="php /path/to/ffmpegphp.php"

重启终端,以后就可以直接用:ffmpegphp /path/to/测试视频.mp4的方式来生成命令了。

相关解释

  • 脚本以“-28”分贝为目标音量,如果目标音量低于-28分贝,则会计算需要增大多少分贝,并自动放置在命令中,如果大于等于-28分贝,则命令中不会有调整音量的选项;
  • 脚本会自动获取原视频比特率,并根据大小,把它的比特率除以3,或者除以2,如果你感觉不合适,可以根据原始比特率自己修改成合适的,因为我也输出了原始比特率;
  • 脚本会根据裁剪操作的输出文件名,自动作为音画同步命令的输入文件名,这样裁剪完成后,你直接复制音画同步命令到终端执行即可(当然你可能需要修改一下音频或视频延迟时间,毕竟每个视频需要的延迟时间不同);
  • 无论是裁剪还是音画同步,最终输出的文件路径,都是在原始视频所在的文件夹里,这样方便你查找;
  • 为什么不直接输出命令而是要粘贴到剪贴板?因为直接输出在终端,一个是不方便查看,另一个是你在终端执行一下第一条命令,第二条就找不到了,还不如先粘贴出来,还有就是你可能需要先修改一下参数再执行,而直接在终端中不方便修改参数;
  • 如果你经常有相同类型的视频需要处理,比如你要截取的大小和截取起始位置都相同,并且音频延迟时间也相同,则可以修改脚本中的参数,这样它每次输出的命令就直接是你想要的参数,你直接粘贴执行即可,无需再修改;
  • 执行两个命令后,最后得到的音画同步的文件名是以_crop-volume-up_avsync.mp4结尾的,然后你就可以删除原视频以及裁剪时生成的文件名以_crop-volume-up.mp4结尾的视频;
ffmpegphp /Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430.mp4

以下是我执行上述的ffmpegphp命令后,在编辑器粘贴得到的命令

截取+裁剪+增大音量+修改比特率(原始视频比特率:16076 kb/s,原始平均音量:-50dB):
/usr/local/bin/ffmpeg -hide_banner -ss 00:00:15.000 -t 00:03:29.000 -i "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430.mp4" -vf crop=1912:1076:244:4 -af volume=22dB -b:v 5000k "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up.mp4"


把声音延迟700毫秒:
/usr/local/bin/ffmpeg -hide_banner -i "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up.mp4" -itsoffset 00:00:00.700 -i "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up.mp4" -map 0:v -map 1:a -vcodec copy -acodec copy "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up_avsync.mp4"


把画面延迟700毫秒:
/usr/local/bin/ffmpeg -hide_banner -itsoffset 00:00:00.700 -i "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up.mp4" -i "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up.mp4" -map 0:v -map 1:a -vcodec copy -acodec copy "/Users/bruce/Downloads/Screenrecorder-2021-09-30-09-57-46-430_crop-volume-up_avsync.mp4"

合并视频

最近小侄子老要看挖土机,电视上也不好找这种片,所以就下载了一些,另外由于电视上的动画片很多都要花钱,而且有些老的片,一些英文片也不好找,所以我还下载了很多其它动画片,但由于家里的电视盒子不会自动播放下一个视频,所以我就把所有集合成一个视频,反正每集也不长。

合并视频官方文档:Concatenating media files

方式一:创建合并列表文件方式

ffmpeg -f concat -safe 0 -i mylist.txt -c copy /path/to/output.mp4

-safe 0我也不知道什么意思,在帮助文档里没查到。

mylist.txt里的内容如下

#这是注释,每一行是一个文件,可带路径
file '/path/to/1.mp4'
file '/path/to/2.mp4'

出现问题:我试过这样合并出一个文件,能合并但有一部分丢了声音,不知道为什么,不过官方文档也有写这种方法:Concatenating media files,但是用下面的方式二就没问题。


方式二:先转换成.ts文件再合并。

把多个mp4视频合并成一个

#先进入要合并的视频所在目录,然后把所有视频按顺序,转成1.ts、2.ts、…、n.ts
ffmpeg -i 挖掘机麦克斯第1集.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 1.ts
ffmpeg -i 挖掘机麦克斯第2集.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 2.ts
ffmpeg -i 挖掘机麦克斯第3集.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 3.ts

#再合成ts文件
ffmpeg -i "concat:1.ts|2.ts|3.ts" -acodec copy -vcodec copy -absf aac_adtstoasc 挖掘机麦克斯1-3集合并.mp4

其中-vbsf video bit stream filter,已弃用,改为-bsf:v(v表示video),-absf是audio bit stream filter的缩写,已弃用,改为-bsf:a(a表示audio)。


批量生成转换合并命令

<?php
    /**
     * Created by PhpStorm.
     * User: Bruce Xie
     * Date: 2021-02-09
     * Time: 14:38
     */

    $mergedName = '小猪佩奇第一季52集合并';

    $concatArr = [];
    $cmdArr = [];
    for($i = 1; $i <= 52; $i++){
        $num = $i<10 ? '0'.$i : $i;
        $cmdArr[] = 'ffmpeg -i '.$num.'.小猪佩奇S01E'.$num.'.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb '.$num.'.ts';
        $concatArr[] = $num . '.ts';
    }
    $cmdStr = join(" &&n",$cmdArr);
    $concatStr = join('|',$concatArr);
    $mergeCmd = 'ffmpeg -i "concat:'.$concatStr.'" -acodec copy -vcodec copy -absf aac_adtstoasc '.$mergedName.'.mp4';

    $contentToCopy = $cmdStr . " &&n" . $mergeCmd;

    $clipboard = PHP_OS=='Darwin' ? 'pbcopy' : (PHP_OS=='WINNT' ? 'clip' : 'xclip -selection clipboard');
    $command = "echo '{$contentToCopy}' | {$clipboard}";
    shell_exec($command);
    echo 'Done! 请直接到终端中粘贴并回车运行!';

输出结果如下

ffmpeg -i 01.小猪佩奇S01E01.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 01.ts &&
ffmpeg -i 02.小猪佩奇S01E02.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 02.ts &&
ffmpeg -i 03.小猪佩奇S01E03.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 03.ts &&
ffmpeg -i 04.小猪佩奇S01E04.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 04.ts &&
ffmpeg -i 05.小猪佩奇S01E05.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 05.ts &&
ffmpeg -i 06.小猪佩奇S01E06.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 06.ts &&
ffmpeg -i 07.小猪佩奇S01E07.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 07.ts &&
ffmpeg -i 08.小猪佩奇S01E08.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 08.ts &&
ffmpeg -i 09.小猪佩奇S01E09.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 09.ts &&
ffmpeg -i 10.小猪佩奇S01E10.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 10.ts &&
ffmpeg -i 11.小猪佩奇S01E11.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 11.ts &&
ffmpeg -i 12.小猪佩奇S01E12.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 12.ts &&
ffmpeg -i 13.小猪佩奇S01E13.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 13.ts &&
ffmpeg -i 14.小猪佩奇S01E14.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 14.ts &&
ffmpeg -i 15.小猪佩奇S01E15.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 15.ts &&
ffmpeg -i 16.小猪佩奇S01E16.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 16.ts &&
ffmpeg -i 17.小猪佩奇S01E17.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 17.ts &&
ffmpeg -i 18.小猪佩奇S01E18.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 18.ts &&
ffmpeg -i 19.小猪佩奇S01E19.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 19.ts &&
ffmpeg -i 20.小猪佩奇S01E20.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 20.ts &&
ffmpeg -i 21.小猪佩奇S01E21.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 21.ts &&
ffmpeg -i 22.小猪佩奇S01E22.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 22.ts &&
ffmpeg -i 23.小猪佩奇S01E23.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 23.ts &&
ffmpeg -i 24.小猪佩奇S01E24.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 24.ts &&
ffmpeg -i 25.小猪佩奇S01E25.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 25.ts &&
ffmpeg -i 26.小猪佩奇S01E26.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 26.ts &&
ffmpeg -i 27.小猪佩奇S01E27.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 27.ts &&
ffmpeg -i 28.小猪佩奇S01E28.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 28.ts &&
ffmpeg -i 29.小猪佩奇S01E29.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 29.ts &&
ffmpeg -i 30.小猪佩奇S01E30.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 30.ts &&
ffmpeg -i 31.小猪佩奇S01E31.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 31.ts &&
ffmpeg -i 32.小猪佩奇S01E32.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 32.ts &&
ffmpeg -i 33.小猪佩奇S01E33.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 33.ts &&
ffmpeg -i 34.小猪佩奇S01E34.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 34.ts &&
ffmpeg -i 35.小猪佩奇S01E35.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 35.ts &&
ffmpeg -i 36.小猪佩奇S01E36.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 36.ts &&
ffmpeg -i 37.小猪佩奇S01E37.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 37.ts &&
ffmpeg -i 38.小猪佩奇S01E38.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 38.ts &&
ffmpeg -i 39.小猪佩奇S01E39.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 39.ts &&
ffmpeg -i 40.小猪佩奇S01E40.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 40.ts &&
ffmpeg -i 41.小猪佩奇S01E41.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 41.ts &&
ffmpeg -i 42.小猪佩奇S01E42.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 42.ts &&
ffmpeg -i 43.小猪佩奇S01E43.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 43.ts &&
ffmpeg -i 44.小猪佩奇S01E44.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 44.ts &&
ffmpeg -i 45.小猪佩奇S01E45.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 45.ts &&
ffmpeg -i 46.小猪佩奇S01E46.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 46.ts &&
ffmpeg -i 47.小猪佩奇S01E47.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 47.ts &&
ffmpeg -i 48.小猪佩奇S01E48.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 48.ts &&
ffmpeg -i 49.小猪佩奇S01E49.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 49.ts &&
ffmpeg -i 50.小猪佩奇S01E50.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 50.ts &&
ffmpeg -i 51.小猪佩奇S01E51.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 51.ts &&
ffmpeg -i 52.小猪佩奇S01E52.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb 52.ts &&
ffmpeg -i "concat:01.ts|02.ts|03.ts|04.ts|05.ts|06.ts|07.ts|08.ts|09.ts|10.ts|11.ts|12.ts|13.ts|14.ts|15.ts|16.ts|17.ts|18.ts|19.ts|20.ts|21.ts|22.ts|23.ts|24.ts|25.ts|26.ts|27.ts|28.ts|29.ts|30.ts|31.ts|32.ts|33.ts|34.ts|35.ts|36.ts|37.ts|38.ts|39.ts|40.ts|41.ts|42.ts|43.ts|44.ts|45.ts|46.ts|47.ts|48.ts|49.ts|50.ts|51.ts|52.ts" -acodec copy -vcodec copy -absf aac_adtstoasc 小猪佩奇第一季52集合并.mp4

分离人声和伴奏

请看:使用spleeter分离人声和伴奏


参考:
ffmpeg中文文档
ffmpeg 音量调整
ffmpeg 获取帮助的命令
ffmpeg命令行map参数的使用
用ffmpeg解决音画不同步问题
ffmpeg stream offset命令(-itsoffset)无法正常工作
滤镜命名规则及使用libavfilter对视频尺寸进行裁切
FFmpeg-overlay
Limiting the output bitrate
Aspect Ratio – Understanding the Information and Using the Filter
利用FFmpeg切割视频(建议看)

打赏
订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请发表评论。x
()
x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

ffmpeg常用命令