错误难免, 逐渐完善.
本文介绍 FFMPEG 最基础的概念, 了解 FFMPEG 的简单使用, 帮助理解 FFMPEG 工程源码. 内容主要节选和翻译自书籍《FFMPEG Basics》及官网文档 "Documentation-ffmpeg http://ffmpeg.org/ffmpeg.html"
1. 概述
FFMPEG 是一款用于多媒体处理的自由软件工程, 基于 GPL 许可证发布. FFMPEG 提供的最核心的命令行工具是 FFMPEG,FFMPEG 命令行工具的主要特征是输出快速, 高品质, 文件尺寸小."FFmpeg" 中 "FF" 表示 "Fast Forward","mpeg" 表示 "Moving Pictures Experts Group".
FFMPEG 提供如下四个命令行工具:
-- FFMPEG 音视频编码器 / 解码器
-- ffplay 媒体播放器
- ffprobe 显示媒体文件信息
-- ffserver 多媒体流广播服务器, 使用 HTTP 和 RTSP 协议
FFMPEG 提供如下软件开发库:
- -- libavcodec software library for various multimedia codecs
- -- libavdevice software library for devices
- -- libavfilter software library containing filters
- -- libavformat software library for media formats
- -- libavutil software library containing various utilities
- -- libpostproc software library for post processing
- -- libswresample software library for audio resampling
- -- libswscale software library for media scaling
2. 命令行格式
命令行基本格式为:
FFMPEG [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
格式分解如下:
- FFMPEG
- global_options
- input1_options -i input1
- input2_options -i input2
- ...
- output1_options output1
- output2_options output2
- ...
FFMPEG 读取任意数量的输入 "文件"(可以是常规文件, 管道, 网络流, 录制设备等, 由 "-i" 选项指定), 写入任意数量的输出 "文件". 命令行中无法被解释为选项 (option) 的任何元素都会被当作输出文件.
每个输入或输出文件, 原则上都可以包含任意数量的流. FFMPEG 中流的类型有五种: 视频 (video), 音频(audio), 字幕(subtitle), 附加数据(attachment), 普通数据(data). 文件中流的数量和(或) 流类型种数的极限值由文件封装格式决定. 选择哪路输入的哪路流输出到哪一路输出, 这个选择过程既可以由 FFMPEG 自动完成, 也可以通过 "-map" 选项指定(后续 "Steam selection" 章节会深入描述).
注: 关于附加数据 (attachment) 和普通数据 (data) 的说明如下:
Attachments could be liner notes, related images, metadata files, fonts, etc.
Data tracks would be for things like timecode, navigation items, cmml, streaming tracks.
参考资料[3] "What are the the data and attachment stream type?"
命令行中的输入文件及输入文件中的流都可以通过对应的索引引用(文件, 流的索引都是从 0 开始). 例如, 2:3 表示第 3 个输入文件中的第 4 个流.(后续 "Stream specifiers" 章节会详细描述).
一个通用规则是: 输入 / 输出选项 (options) 作用于跟随此选项后的第一个文件. 因此, 顺序很重要, 并且可以在命令行中多次指定同一选项. 每个选项仅作用于离此选项最近的下一输入或输出文件. 全局选项不受此规则限制.
不要把输入文件和输出文件混在一起 --- 应该先将输入文件写完, 再写输出文件. 也不要把不同文件的选项混在一起, 各选项仅对其下一输入或输出文件有效, 一个选项不能跨越一个文件传递到后续文件.
举几个命令行例子:
设置输出文件码率为 64kbit/s:
FFMPEG -i input.avi -b:v 64k -bufsize 64k output.avi
其中 "-b:v 64k" 和 "-bufsize 64k" 是输出选项.
强制输入文件帧率 (仅对 raw 格式有效) 是 1fps, 输出文件帧率为 24fps:
FFMPEG -r 1 -i input.m2v -r 24 output.avi
其中 "-r 1" 是输入选项,"-r 24" 是输出选项.
FFMPEG -y -i video.avi -s vga video.mp4
其中 "-y" 是全局选项,"-s vga" 是输出选项.
3. 转码过程
- _______ ______________
- | | | |
- | input | demuxer | encoded data | decoder
- | file | ---------> | packets | -----+
- |_______| |______________| |
- v
- _________
- | |
- | decoded |
- | frames |
- |_________|
- ________ ______________ |
- | | | | |
- | output | <-------- | encoded data | <----+
- | file | muxer | packets | encoder
- |________| |______________|
FFMPEG 调用 libavformat 库(包含解复用器 demuxer), 从输入文件中读取到包含编码数据的包(packet). 如果有多个输入文件, FFMPEG 尝试追踪多个有效输入流的最小时间戳(timestamp), 用这种方式实现多个输入文件的同步.
然后编码包被传递到解码器 (decoder), 解码器解码后生成原始帧(frame), 原始帧可以被滤镜(filter) 处理 (图中未画滤镜), 经滤镜处理后的帧送给编码器, 编码器将之编码后输出编码包. 最终, 由复用器(muxex) 将编码码写入特定封装格式的输出文件.
4. 滤镜(filter)
在多媒体处理中, 术语滤镜 (filter) 指的是修改未编码的原始音视频数据帧的一种软件工具. 滤镜分为音频滤镜和视频滤镜. FFMPEG 提供了很多内置滤镜, 可以用很多方式将这些滤镜组合使用. 通过一些复杂指令, 可以将解码后的帧从一个滤镜引向另一个滤镜. 这简化了媒体处理, 因为有损编解码器对媒体流进行多次解码和编码操作会降低总体质量, 而引入滤镜后, 不需要多次解码编码操作, 相关处理可以使用多个滤镜完成, 而滤镜处理的是原始数据, 不会造成数据损伤.
滤镜通常以滤镜链 (filterchain, 以逗号分隔的滤镜序列) 和滤镜图 (filtergraph, 以分号分隔的滤镜序列) 的形式使用.
滤镜链由滤镜构成, 滤镜图由滤镜链构成, 这样可以提供复杂多样的组合方式以应对不同的应用场景.
在滤镜图中可以使用连接标号(link lable), 连接标号表示特定滤镜 / 滤镜链的输入或输出, 参 4.1 节.
例如, 我们想要把一个经过降噪处理后的输出文件与输入原文件进行比较, 如果不使用滤镜图, 我们需要至少两条命令:
- FFMPEG -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
- FFMPEG -i output.mp4 -i input.mpg -filter_complex overlay=w compare.mp4
如果使用带有连接标号的滤镜图, 则一条命令就可以了:
ffplay -i i.mpg -vf split[a][b];[a]pad=2*iw[A];[b]hqdn3d[B];[A][B]overlay=w
4.1 滤镜 (filter) 的使用
FFMPEG 的 libavfilter 库提供了滤镜 API, 支持多路输入和多路输出.
命令行中,"-filter" 选项指定滤镜,"-vf"(同 "-filter:v")选项表示使用视频滤镜,"-af"(同 "-filter:a")选项表示使用音频滤镜.
filter 的语法为:
[input_link_lable1][input_link_lable2]... filter_name=parameters [output_link_lable1][output_link_lable12]...
上述语法中, 输入输出都有连接标号(link lable), 连接符号是可选项, 输入连接标号表示滤镜的输入, 输出连接标号表示滤镜的输出. 连接标号通常用在滤镜图中, 通常前一个滤镜的输出标号会作为后一个滤镜的输入标号, 通过同名的标号将滤镜及滤镜链连接起来. 连接标号的用法参考 4.3.2 节示例.
示例 1:
ffplay -f lavfi -i testsrc -vf transpose=1
"transpose=1" 是 filter, 此行命令表示使用 transpose 视频滤镜产生一个顺时针旋转 90 度的测试图案
示例 2:
FFMPEG -i input.mp3 -af atempo=0.8 output.mp3
"atempo=0.8" 是 filter, 此行命令表示使用 atempo 音频滤镜将输入音频速率降低到 80% 后写入输出文件
注意: 有些滤镜只会修改帧属性而不会修改帧内容. 例如, fps 滤镜, setpts 滤镜等.
4.2 滤镜链 (filterchain) 的使用
滤镜链 filterchain 是以逗号分隔的 filter 序列, 语法如下:
filter1,fiter2,filter3,...,filterN-2,filterN-1,filterN
滤镜链中如果有空格, 需要将滤镜链用双引号括起来, 因为命令行中空格是分隔参数用的.
示例 1:
FFMPEG -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
"hqdn3d,pad=2iw" 是 filterchain, 第一个 filter 是 "hqdn3d"(降噪); 第二个 filter 是 "pad=2iw"(将图像宽度填充到输入宽度的 2 倍). 此行命令表示, 将输入视频经降噪处理后, 再填充视频宽度为输入宽度的 2 倍.
4.3 滤镜图 (filtergraph) 的使用
滤镜图 filtergraph 通常是以分号分隔的 filterchain 序列. filtergraph 分为简单 filtergraph 和复杂 filtergraph.
filtergraph 的语法如下:
filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN
4.3.1 简单滤镜图(simple filtergraph)
简单 filtergraph 只能处理单路输入流和单路输出流, 而且要求输入和输出具有相同的流类型.
简单 filtergraph 由 - filter 选项指定. 简单 filtergraph 示意图如下:
- _______ _____________________ ________
- | | | | | |
- | input | ---> | simple filter graph | ---> | output |
- |_______| |_____________________| |________|
4.3.2 复杂滤镜图(complex filtergraph)
复杂 filtergraph 用于简单 filtergraph 处理不了的场合. 比如, 多路输入流和 (或) 多路输出流, 或者输出流与输入流类型不同.
有些特殊的 filter 本身就属于复杂 filtergraph, 用 - filter_complex 选项或 - lavfi 选项指定, 如 overlay 滤镜和 amix 滤镜就是复杂 filtergraph.overlay 滤镜有两个视频输入和一个视频输出, 将两个输入视频混合在一起. 而 amix 滤镜则是将两个输入音频混合在一起.
复杂 filtergraph 示意图如下:
- _________
- | |
- | input 0 |\ __________
- |_________| \ | |
- \ _________ /| output 0 |
- \ | | / |__________|
- _________ \| complex | /
- | | | |/
- | input 1 |---->| filter |\
- |_________| | | \ __________
- /| graph | \ | |
- / | | \| output 1 |
- _________ / |_________| |__________|
- | | /
- | input 2 |/
- |_________|
示例 1:
FFMPEG -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
上例中 "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" 是复杂滤镜图, 由三个滤镜链构成 (分号分隔), 第二个滤镜链 "[tmp] crop=iw:ih/2:0:0, vflip [flip]" 由两个滤镜构成(逗号分隔). 第一个滤镜链中: 滤镜 split 产生两个输出[main] 和[tmp]; 第二个滤镜链中:[tmp]作为 crop 滤镜的输入,[flip]作为 vflip 滤镜的输出, crop 滤镜输出连接到 vflip 滤镜的输入; 第三个滤镜链中:[main]和 [flip] 作为 overlay 滤镜的输入. 整行命令实现的功能是: 将输入分隔为两路, 其中一路经过裁剪和垂直翻转后, 再与另一路混合, 生成输出文件. 示意图如下所示:
- [main]
- input --> split ---------------------> overlay --> output
- | ^
- |[tmp] [flip]|
- +-----> crop --> vflip -------+
5. 流拷贝(stream copy)
"-codec copy" 可使能流拷贝模式. 流拷贝直接将输入流拷贝到输出, 仅涉及解复用和复用, 不涉及解码和编码, 因此也不支持滤镜操作. 流拷贝对于修改容器格式或容器级别元数据非常有用. 因为不涉及编解码操作, 整个过程会非常快. 示意图如下所示:
- _______ ______________ ________
- | | | | | |
- | input | demuxer | encoded data | muxer | output |
- | file | ---------> | packets | -------> | file |
- |_______| |______________| |________|
6. 流选择(stream selection)
有些容器, 如 AVI,MP4 等, 可以包含多种不同类型的流. FFMPEG 可以识别 5 种流类型: 音频 (audio, a), 视频(video, v), 字幕(subtitle, s), 附加数据(attachment, t) 和普通数据(data, d).
流选择是从输入文件中选定某些流进行处理. 流选择有两种模式, 1) 使用 - map 选项手动指定要选择的流; 2) 无 - map 选项时由 FFMPEG 根据相应规则自动选择流.
6.1 流选择自动模式
自动选择模式下, 每种类型的流只选择一路, 规则如下:
音频流: 选择具有最多通道的流, 若多个音频流通道数相同且通道数最多, 则选第一个
视频流: 选择具有最高分辨率的流, 若多个视频流分辨率相同且是最高分辨率, 则选第一个
字幕流: 选择第一个字幕流. 注意: 字幕流有文本字幕流和图形字幕流, 输出格式默认的字幕编码器仅处理其支持的字幕类型
6.2 流选择手动模式
手动选择模式下, 要选定的流由 - map 选项后的流指定符 (stream specifer) 指定. stream_specifier 语法如下:
[-]file_index:stream_type[:stream_index]
带 - 表示排除此流, 不带 - 表示选中此流. 文件序号 file_index 和流序号 stream_index 都是从 0 开始计数.
几个特殊的 stream_specifier 如下:
--map 0 选择所有类型的所有流.
--map i:v 选择文件 i 中所有的视频流, i:a,i:s 等同理.
--map -vn 排除所有视频流,-an,-sn 等同理.
示例:
假设 FFMPEG 命令行如下:
FFMPEG -i file1 -i file2 select_streams output
其中有两个输入文件 file1 和 file2, 选择的流位于 select_streams
file1 的流组成与对应的 stream_specifier 如下:
- file streams stream_specifier
- 1st video 0:v:0
- 2nd video 0:v:1
- 1st audio 0:a:0
- 2nd audio 0:a:1
- 1st subtitle 0:s:0
- 2nd subtitle 0:s:1
- 3rd subtitle 0:s:2
file2 的流组成与对应的 stream_specifier 如下:
- file streams stream_specifier
- 1st video 1:v:0
- 1st audio 1:a:0
- 1st subtitle 1:s:0
select_streams 各种示例说明如下:
-map 0 -map 1
选择两个文件的所有流
-map 0:s:2 -map 1:v:0 -map 1:a:0
选择 file1 的 3rd 字幕流, file2 的 1st 视频流和 file2 的 1st 音频流
-map 0 -map 1:s:0 -an
选择 file1 除音频外的所有流和 file2 的 1st 字幕流
-map 0 -map 1 -map -0:v:0 -map -0:a:1
选择除 file1 的 1st 视频流和 2nd 音频流外的所有流, 选择 file2 中的所有流
6.3 扩展 1:stream_specifier 的各种形式
除 - map 选项外, stream_specifier 还可用在很多其他选项中, 形式有如下几种:
specifer 形式 描述
stream_index 选择索引为 stream_index 的流
stream_type[:stream_index] 选择类型为 stream_type 索引为 stream_index 的流
p:program_id[:stream_index] 选择节目 program_id 中索引为 stream_index 的流
stream_id 选择指定 ID 的流
例如, 使用 - b 选项设置音频流和视频流的码率:
FFMPEG -i input.mpg -b:a 128k -b:v 1500k output.mp4
其中 "-b:a" 和 "-b:v" 就是 stream_specifier.
6.4 扩展 2: 复杂 filtergraph 中的流选择
如果某个复杂 filtergraph 中的输出流未携带标号, 则这些流将被添加到第一个输出文件中. 如果封装器格式不支持某种流类型, 将会导致致命错误.
如果未使用 - map 选项, 包含这些复杂 filtergraph 输出流将导致不会对这些流类型启用自动选择.
如果使用了 - map 选项, 除 - map 选定的流之外, 这些 filtergraph 输出流也会被包含进来.
复杂 filtergraph 的输出流若带标号, 则标号必须被映射一次, 且只能被映射一次.
假设有三个输入文件用于示例, 其流组成成分如下:
- input file 'A.avi'
- stream 0: video 640x360
- stream 1: audio 2 channels
- input file 'B.mp4'
- stream 0: video 1920x1080
- stream 1: audio 2 channels
- stream 2: subtitles (text)
- stream 3: audio 5.1 channels
- stream 4: subtitles (text)
- input file 'C.mkv'
- stream 0: video 1280x720
- stream 1: audio 2 channels
- stream 2: subtitles (image)
示例 1: 无标号 filtergraph 的流选择
FFMPEG -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt
-filter_complex 选项指定了一个复杂 filtergraph, 此 filtergraph 由单个视频滤镜 overlay 构成. overlay 滤镜需要两个视频输入, 但此处并未为 overlay 滤镜指定输入, 因此 A.avi 和 C.mkv 中头两个有效视频流会被作为 overlay 滤镜的输入. overlay 滤镜输出无标号, 因此 overlay 滤镜的输出会被写入第一个输出文件 out1.mp4 中.
本来自动选择模式会选中 B.mp4 中的 "stream 0" 视频流 (最高分辨率真) 和 B.mp4 中的 "stream 3" 音频流(最多通道数). 但 overlay 滤镜输出流是视频流, 因此, 不会对视频流进行自动选择, 即不会选择 B.mp4 中的 "stream 0".
不会选中任何字幕流, 因为 MP4 封装格式未注册默认字幕编码器, 用户也未指定字幕编码器, 无编码器可用所以不会选择字幕流.
第二个输出文件 out2.srt, 仅接受文本类型的字幕流. 所以, 就算 C.mkv 中的 "stream 2" 是第一个被找到的字幕流, 也会因类型不符合被忽略掉. B.mp4 中的 "stream 2" 会被选中, 因为它才是第一个文本字幕流.
示例 2: 带标号 filtergraph 的流选择
- FFMPEG -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
- -map '[outv]' -an out1.mp4 \
- out2.mkv \
- -map '[outv]' -map 1:a:0 out3.mkv
上述命令会执行失败, 因为 filtergraph 的输出标号 [outv] 被映射了两次. 此命令不会生成任何输出文件.
- FFMPEG -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
- -an out1.mp4 \
- out2.mkv \
- -map 1:a:0 out3.mkv
上述命令也会执行失败, 因为 hue 滤镜有一个输出标号[outv], 但此标号未作任何映射.
正确的命令应该写成下面这样:
- FFMPEG -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0,split=2[outv1][outv2];overlay;aresample" \
- -map '[outv1]' -an out1.mp4 \
- out2.mkv \
- -map '[outv2]' -map 1:a:0 out3.mkv
"[1:v]" 表示 B.mp4 中的视频流, B.mp4 中的视频流被发送到 hub 滤镜, hub 滤镜的输出被 split 滤镜拷贝了一份, 生成两份输出, 两份输出用标号 [outv1] 和[outv2]表示.
overlay 滤镜需要两个视频输入, 使用头两个未使用的视频流作输入, 即 A.avi 和 C.mkv 中的视频流. overlay 滤镜输出未带标号, 所以 overlay 滤镜输出被发送到第一个输出文件 out1.mp4, 有没有 - map 选项对此无影响.
aresample 滤镜使用第一个未使用的音频流 (A.avi 中的 "stream 1") 作为输入. aresample 滤镜输出也未带标号, 所以 avresample 滤镜输出也被映射到第一个输出文件 out1.mp4.-an 选项仅仅抑制了音频流的自动或手动流选择, 而不会抑制 filtergraph 的输出. 所以, out1.mp4 有三个输入流: 1)overlay 滤镜输出, 2)aresample 滤镜输出和 3)标号 outv1,B.mp4 中 1)2)排序应在 3)之前.
映射到 out2.mkv 的视频, 音频和字幕流由自动选择模式选定.
out3.mkv 由 hue 滤镜输出和 B.mp4 中的 "stream 1" 构成.
6.5 扩展 3: 流处理(stream handling)
流处理和流选择是互不影响的 (字幕例外). 流处理通过 - codec 选项设置,-codec 选项针对输出文件中的流. FFMPEG 对 - codec 选项的处理是在流选择(stream selection) 过程之后的, 因此 - codec 选项 (流处理) 不会影响流选择. 如果某类型的流未指定 - codec 选项, 将会使用输出文件 muxer 注册的默认编码器.
上述规则不适用于字幕. 如果一个输出文件指定了字幕编码器, 那么找到的第一个字幕流 (文本字幕或图形字幕) 总会被包含进来. FFMPEG 不会检查编码器是否能转换选定的流或已转换的流能否被输出格式接受. 这通常也适用: 当用户手动设置编码器时, 流选择过程不能检查编码流是否可以复用到输出文件中. 如果编码流不能复用到输出文件, FFMPEG 会终止, 所有的输出文件处理会失败.
7. 选项
待补充
8. 示例
待补充
9. 参考资料
[1] FFMPEG Basics
[2] http://ffmpeg.org/ffmpeg.html , http://ffmpeg.org/ffmpeg.html
[3] What are the the data and attachment stream type?,
10. 修改记录
2018-12-15 V1.0 首次整理
来源: https://www.cnblogs.com/leisure_chn/p/10297002.html