使用 ffmpeg 命令进行视频的编辑
摘要
最近项目用到 ffmpeg 的命令对视频或音频进行编辑,在这里由项目需求出发做一个归纳。
通过雷霄骅先生的一篇博文1 可以了解关于 ffmpeg 详尽的知识和用途,让人不由得对这位先驱产生敬意。
文末的参考资料中可以看到一些文档以及教程。通过这些文档和教程,就可以在满足了需求之后再回头学习各个命令的意义和作用,从而在遇到新需求时能够更加快速的找到解决方案。
涉及的 ffmpeg 命令
预设的配置值
//质量参数, 0~50, 越小质量越高. 18~23 肉眼不可分辨的变化
private $crf = 16;
//编码预设值 preset: 可选:ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
private $preset = 'ultrafast';
质量参数 crf
越小,质量越高,输出的文件就越大;编码预设值 preset
可以理解为压制程度与时间的关系值,时间越长,压制程度就要高,输出的文件就越小,反之同理。
项目需要对视频进行一系列的操作,最终输出处理后的视频。所以,在这个一系列操作的过程中,我把编码预设值设成了 ultrafast
,是希望尽量缩短这个过程所耗费的时间。当然,这是由操作所需的临时空间换来的。操作完成后,再由 2-pass
压制视频为所需的文件大小输出。
而关于质量参数的值,系统默认的好像是 18,但是经过多次操作后,视频的质量还是有了肉眼可分辨的下降。设为 16 是可满足我项目的需求的一个值。
为视频添加字幕
ffmpeg -i {$originFileTmpPath} -vf subtitles={$subtitlePath} -crf {$this->crf} -preset {$this->preset} {$subFilePath}
根据输入文件内容,拼接音频并输出到指定路径:
(此处用的是博文2的方法二「FFmpeg concat 分离器」)
ffmpeg -f concat -safe 0 -i {$fileListPath} -c copy {$finalFilePath}
这里多了 -safe 0
,原因是:
在 filelist.txt 里面加入/的话会报一个不安全文件路径的错误
Unsafe file path
只要加上-safe 0
就可以解决啦
保留原声合并音(视)频3
ffmpeg -i {$input} -i {$addFilePath} -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -crf {$this->crf} -preset {$this->preset} {$output}
注意:inputs=输入流数量,duration=决定流的结束(longest最长输入时间,shortest最短,first第一个输入持续的时间),dropout_transition= 输入流结束时,容量重整时间
截取时间段内的音频
ffmpeg -ss {$startTime} -i {$input} -to {$endTime} -c copy -copyts {$outputPath}
ffmpeg -ss {$startTime} -i {$input} -t {$duration} -c copy {$outputPath}
如果你仅仅在输入文件之前( -i 之前)指定了 ss 选项,那么时间戳会被重置为 0,此时选项 t 和选项 to 产生的效果一样。
如:ffmpeg -ss 00:01:00 -i video.mp4 -to 00:02:00 -c copy cut.mp4
ffmpeg -i video.mp4 -ss 00:01:00 -to 00:02:00 -c copy cut.mp4
上面例子中,第一个命令会得到从 00:01:00 到 00:03:00 的片断,而第二个命令会真正得到从 00:01:00 到 00:02:00 的片断。
关于 mpeg 格式的截取,这个 关于I帧的解释:什么是GOP 博文里的一个测试还是很具象地解释了 mpeg 格式为什么难以精确剪辑。
关于视频的精确剪辑,直接:
ffmpeg -i {$input} -ss {$startTime} -to {$endTime} {$outputPath}
ffmpeg 会默认重新解码编码,从而截取精确的视频时间段。当然,所耗的时间也会变长了。
调整音频速率4
ffmpeg -i {$input} -filter:a atempo={$multiple} {$output}
$multiple float|int 倍率调整范围为[0.5, 2.0]
超过调整范围时,比如 4 倍,可以通过重复参数实现:
ffmpeg -i {$input} -filter:a atempo=2,atempo=2 {$output}
去除音视频的原声
ffmpeg -i {$input} -c:v copy -an {$output}
-c:v copy
表示复制视频,-an
表示不需要音频。
为不带音频的视频文件添加音频
ffmpeg -i {$input} -i {$audio} {$output}
#0:0
表示视频流,#0:1
表示音频流。该命令只适用于给音频流为空的视频文件添加音频流。
生成指定长度的空白音频
ffmpeg -f lavfi -i aevalsrc=0 -t {$seconds} -q:a 9 -ac 1 -ar 16000 -acodec pcm_s16le {$output}
视频转音频,wav 格式
ffmpeg -i {$input} -vn -ar 16000 -ac {$channel} -f wav {$output}
-vn
表示不需要视频。
生成视频封面图
ffmpeg -ss 00:00:00 -t 00:00:00.001 -i {$input} -r 1 -f image2 {$output}
改变音频文件的音量
ffmpeg -i {$input} -filter: volume={$multiple} -y {$output}
获取音视频时长
ffmpeg -i {$input} 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//
通过命令行命令,搜索、截取出 00:00:00 格式的音视频时长。
获取视频帧图
ffmpeg -i {$input} -vf scale=160:-1 -r {$numPerSecond} -start_number 000000 {$dir}/%11d.png
-start_number
设置输出文件名开始的数字。
修改视频分辨率5
ffmpeg -i {$input} -vf scale=iw*{$multiple}:ih*{$multiple} -crf {$this->crf} -preset {$this->preset} {$output}
-vf
表示滤镜。scale
为滤镜里的缩放功能。iw/ih
表示原始尺寸的大小。scale=-1:ih*2
表示高度乘 2,宽度等比缩放。
2-pass 压制视频可自定义码率6 = 可自定义大小
ffmpeg -i {$input} -strict -2 -passlogfile {$dir} -vb {$bitRate}k -pass 1 -f mp4 -y /dev/null
ffmpeg -i {$input} -strict -2 -passlogfile {$dir} -vb {$bitRate}k -pass 2 -f mp4 {$output}
两条命令可以分开执行,也可以用 &&
连接一起执行。
-strict -2 -passlogfile {$dir}
用于指定生成中间临时文件的目录。
-vb
可理解为 video bitrate。
生成纯色视频
ffmpeg -f lavfi -i color=white:1280*720:d=10 -format rgb32 -f matroska -vcodec libx264 -b:v 2000k color.mp4
循环图片生成视频
ffmpeg -stream_loop {$frame} -i {$input} {$output}
参考资料
2015 年中文翻译的文档
官网英文文档
FFmpeg 分 P 教学——哔哩哔哩