0. 前言
FFmpeg号称是音视频应用的瑞士军刀,广泛应用于视频网站和大型软件如Youtube和Chrome。它诞生于2004年,在LGPL、GPL协议下发布,任何人都可以在遵守协议的情况下自由使用。
FFmpeg由C语言实现, 具有良好的跨平台特性,适用于PC和各种嵌入式设备。FFmpeg原生提供了C语言API,也有各路大神为其他编程语言提供的适配器,常见编程语言基本上都能找到对应适配器,比如:
Java版:JavaCV
Python版:ffmpeg-python
甚至浏览器上也能跑,Javascript版:ffmpeg.wasm
FFmpeg主要由两大部分组成:
第一部分:
3个独立的可执行应用程序,分别是:
- ffmpeg.exe:音视频转码器;
- ffplay.exe:音视频播放器;
- ffprobe.exe:多媒体码流分析器。
第二部分:
7个应用开发库,可以根据各自不同需求灵活使用这些库,开发自己的应用。
- libavcodec:音视频编码器和解码器;
- libavformat:各种多媒体容器格式的封装、解封工具;
- libavutil:多媒体应用常用的工具集,主要是为了简化开发。包含诸如字符串处理函数、随机数生成器、常用数据结构、数学函数、加密和多媒体相关函数等;
- libavdevice:通用的音视频采集和渲染框架,支持多种输入输出设备;
- libavfilter:通用的音视频过滤框架,包含多种过滤器;
- libswscale:高性能图像伸缩、色彩空间、像素格式转换;
- libswresample:高性能音频重采样,采样格式转换。
FFmpeg的下载和安装,可以查看官网文档。
1. 基本概念
要把FFmpeg用好,需要对视音频处理有全局视野和核心知识储备,不至于走太多弯路。有必要先熟悉一些基本概念。
1.1 原始数字视频和音频
与模拟信号区分开,即用数字信号来表示视频和音频。 视频本质上是一帧帧的图像在连续播放,由于帧率(即一秒钟匀速呈现图像的张数,如25帧/秒)超过了人眼识别的速度,给人一种连续的感觉。音频也是同样道理,音频由一个个采样点组成(PCM),通常采样率(8000KHz)超过人耳能辨别的速度。
1.2 编码格式
原始音视频数据量巨大,存储和传输都成很大问题,于是人们发明了各种压缩算法来降低数据量,这些算法就是编码器和解码器。 常见的视频编码格式有:
H.264
H.265
VP8
VP9
常见的音频编码格式有:
AAC
MP3
以上这些编码格式都是有损的,意思是经过压缩后,原始信息的一部分丢失并无法复原了,但是好处是体积极大的降低了。 用FFmpeg查询所有支持的编码格式
ffmpeg -codecs
1.3 编码器/解码器
编码器、解码器分别是用于针对一种编码格式进行编码(压缩)和解码(解压缩)的,需要注意的是对同一种编码格式如H.264,可能存在多种编码器和解码器,如libx264包含了H.264编码器和解码器,而nvenc_h264只包含H.264编码器。
FFmpeg查询所有支持的编码器
ffmpeg -encoders
FFmpeg查询所有支持的解码器
ffmpeg -decoders
1.4 封装格式
封装格式又叫容器格式,常见的有avi、flv、mp4等。容器可以实现把多种媒体数据封装在一个文件里,比如把一路H.264的视频和一路MP3的音频,封装到flv容器里。
FFmpeg查询所有支持的容器格式
ffmpeg -formats
每一种容器,能装载的媒体格式是有限制的,可以进一步的,使用下面命令查看ffmpeg支持的容器格式相关参数,包括默认的音频编码和视频编码
ffmpeg -h muxer=容器格式
如 ffmpeg -h muxer=flv 用于查看flv容器格式详细信息。
1.5 封装器/解封装器
封装器、解封装器分别用于针对一种封装格式进行封装(合并)和解封装(拆分)。
FFmpeg查询所有支持的封装器
ffmpeg -muxers
FFmpeg查询所有支持的解封装器
ffmpeg -demuxers
1.6 基本转码流程
有了上面基础概念,我们可以理解FFmpeg基本的转码流程,如下图所示
graph LR
输入文件 --解封装器--> 编码后数据包1 --解码器--> 解码后数据帧 --编码器--> 编码后数据包2 --封装器--> 输出文件
2. FFmpeg的使用
FFmpeg命令行很强大也复杂,但最常用的并不复杂。通常只需要掌握简单版本,就可胜任80%的功能了
2.1 简单版FFmpeg使用
ffmpeg [全局参数] [输入文件参数] -i 输入文件 [输出文件参数] 输出文件
注:命令中用[ ]括起来的是可选项
例1:flv转mp4
ffmpeg -i demo.flv out.mp4
例2:将pcm转为mp3
ffmpeg -y -f s16le -ac 1 -ar 8000 -acodec pcm_s16le -i out.mp3
注:由于pcm文件不包含参数信息,需要通过
[输入文件参数]来指定
例3: 查看文件信息
ffmpeg -i demo.flv
例4:转编码格式
ffmpeg -i demo.flv -c:v h264 -c:a aac out.mp4
例5:转文件容器格式
ffmpeg -i demo.flv -c copy out.avi
例6:剪切音视频
ffmpeg -ss 00:00:00 -t 00:00:30 -i demo.flv -vcodec copy -acodec copy out.mp4
2.2 高级版FFmpeg使用
ffmpeg [全局参数] {[输入文件参数] -i 输入文件} {[输出文件参数] 输出文件}
注:命令中{}括起来部分,代表可以重复出现多次,因此FFmpeg可以有多个输入文件、多个输出文件,并且单独为每个输入或输出设置参数。
例如 ffmpeg -i A.avi -i B.mp4 out1.mkv out2.wav 就是一个合法的FFmpeg命令。
那就引出了一个问题,FFmpeg是如何知道,这些音频和视频,分别输出到哪个文件里呢?
原来,[输出文件参数] 里有个map参数,可用于选择输入的来源。注意,map参数是输出文件的参数,是输出选择输入,而不是输入选择输出。一定要明确FFmpeg的这种模式。
如果没有为输出指定map参数,FFmpeg将按照默认规则,自动选择输入源
- 对于视频,选择分辨率最高的那个
- 对于音频,选择通道数最多的那个
map参数组成:
[输入文件序号]:[流序号] ,或者 [输入文件序号]:[a/v]:[流序号]
其中a/v用于指定音频或视频
例如:
-map 0选择第1个文件,选择所有流-map 1:a选择第2个文件,选择所有音频流-map 3:a:2选择第4个文件,选择第3个音频流
下面这个例子,从第1个文件获取视频流,从第2个文件获取音频流,生成一个mp4文件
ffmpeg -i video.mp4 -i audio.m4a -map 0:v -map 1:a output.mp4
2.3 参数说明
| 参数 | 参数说明 |
|---|---|
| -y | 允许覆盖(全局参数) |
| -f s16le | 指定容器格式为s16le |
| -ac 1 | 指定音频通道数为1 |
| -ar 8000 | 指定音频采样率为8k |
| -acodec pcm_s16le | 指定音频编码格式为pcm_s16le |
| -acodec pcm_s16le | 指定音频编码格式为pcm_s16le |
| -c:v h264 | 指定视频编码格式为h264 |
| -c:a aac | 指定音频编码格式为aac |
| -c copy | 编码格式保持不变 |
| -ss | 指定从什么时间开始 |
| -t | 指定需要截取多长时间 |
| -map 0 | 选择第1个文件,选择所有流 |
| -map 1:a | 选择第2个文件,选择所有音频流 |
| -map 3:a:2 | 选择第4个文件,选择第3个音频流 |