错误难免,逐渐完善。

本文介绍FFmpeg最基础的概念,了解FFmpeg的简单使用,帮助理解FFmpeg工程源码。内容主要节选和翻译自书籍《FFmpeg Basics》及官网文档“Documentation-ffmpeg

FFmpeg是一款用于多媒体处理的自由软件工程,基于GPL许可证发布。FFmpeg提供的最核心的命令行工具是ffmpegffmpeg命令行工具的主要特征是输出快速、高品质、文件尺寸小。“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

命令行基本格式为:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
格式分解如下:

  1. ffmpeg
  2. global_options
  3. input1_options -i input1
  4. input2_options -i input2
  5. ...
  6. output1_options output1
  7. output2_options output2
  8. ...

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”是输出选项。
  1. _______ ______________
  2. | | | |
  3. | input | demuxer | encoded data | decoder
  4. | file | ---------> | packets | -----+
  5. |_______| |______________| |
  6. v
  7. _________
  8. | |
  9. | decoded |
  10. | frames |
  11. |_________|
  12. ________ ______________ |
  13. | | | | |
  14. | output |
  15. ffmpeg调用libavformat库(包含解复用器demuxer),从输入文件中读取到包含编码数据的包(packet)。如果有多个输入文件,ffmpeg尝试追踪多个有效输入流的最小时间戳(timestamp),用这种方式实现多个输入文件的同步。

  16. 然后编码包被传递到解码器(decoder),解码器解码后生成原始帧(frame),原始帧可以被滤镜(filter)处理(图中未画滤镜),经滤镜处理后的帧送给编码器,编码器将之编码后输出编码包。最终,由复用器(muxex)将编码码写入特定封装格式的输出文件。

  17. 在多媒体处理中,术语滤镜(filter)指的是修改未编码的原始音视频数据帧的一种软件工具。滤镜分为音频滤镜和视频滤镜。FFmpeg提供了很多内置滤镜,可以用很多方式将这些滤镜组合使用。通过一些复杂指令,可以将解码后的帧从一个滤镜引向另一个滤镜。这简化了媒体处理,因为有损编解码器对媒体流进行多次解码和编码操作会降低总体质量,而引入滤镜后,不需要多次解码编码操作,相关处理可以使用多个滤镜完成,而滤镜处理的是原始数据,不会造成数据损伤。

  18. 滤镜通常以滤镜链(filterchain, 以逗号分隔的滤镜序列)和滤镜图(filtergraph, 以分号分隔的滤镜序列)的形式使用。

  19. 滤镜链由滤镜构成,滤镜图由滤镜链构成,这样可以提供复杂多样的组合方式以应对不同的应用场景。

  20. 在滤镜图中可以使用连接标号(link lable),连接标号表示特定滤镜/滤镜链的输入或输出,参4.1节。

  21. 例如,我们想要把一个经过降噪处理后的输出文件与输入原文件进行比较,如果不使用滤镜图,我们需要至少两条命令:
    ffmpeg -i input.mpg -vf hqdn3d,pad=2*iw output.mp4
    ffmpeg -i output.mp4 -i input.mpg -filter_complex overlay=w compare.mp4

  22. 如果使用带有连接标号的滤镜图,则一条命令就可以了:
    ffplay -i i.mpg -vf split[a][b];[a]pad=2*iw[A];[b]hqdn3d[B];[A][B]overlay=w

  23. FFmpeglibavfilter库提供了滤镜API,支持多路输入和多路输出。

  24. 命令行中,“-filter”选项指定滤镜,“-vf”(同“-filter:v”)选项表示使用视频滤镜,“-af”(同“-filter:a”)选项表示使用音频滤镜。

  25. filter的语法为:
    [input_link_lable1][input_link_lable2]... filter_name=parameters [output_link_lable1][output_link_lable12]...

  26. 上述语法中,输入输出都有连接标号(link lable),连接符号是可选项,输入连接标号表示滤镜的输入,输出连接标号表示滤镜的输出。连接标号通常用在滤镜图中,通常前一个滤镜的输出标号会作为后一个滤镜的输入标号,通过同名的标号将滤镜及滤镜链连接起来。连接标号的用法参考4.3.2节示例。

  27. 示例1
    ffplay -f lavfi -i testsrc -vf transpose=1

  28. transpose=1”是filter,此行命令表示使用transpose视频滤镜产生一个顺时针旋转90度的测试图案

  29. 示例2
    ffmpeg -i input.mp3 -af atempo=0.8 output.mp3

  30. atempo=0.8”是filter,此行命令表示使用atempo音频滤镜将输入音频速率降低到80%后写入输出文件

  31. 注意:有些滤镜只会修改帧属性而不会修改帧内容。例如,fps滤镜,setpts滤镜等。

  32. 滤镜链filterchain是以逗号分隔的filter序列,语法如下:
    filter1,fiter2,filter3,...,filterN-2,filterN-1,filterN

  33. 滤镜链中如果有空格,需要将滤镜链用双引号括起来,因为命令行中空格是分隔参数用的。

  34. 示例1
    ffmpeg -i input.mpg -vf hqdn3d,pad=2*iw output.mp4

  35. hqdn3d,pad=2iw”是filterchain,第一个filter是“hqdn3d”(降噪);第二个filter是“pad=2iw”(将图像宽度填充到输入宽度的2倍)。此行命令表示,将输入视频经降噪处理后,再填充视频宽度为输入宽度的2倍。

  36. 滤镜图filtergraph通常是以分号分隔的filterchain序列。filtergraph分为简单filtergraph和复杂filtergraph

  37. filtergraph的语法如下:
    filter1;fiter2;filter3;...;filterN-2;filterN-1;filterN

  38. 简单filtergraph只能处理单路输入流和单路输出流,而且要求输入和输出具有相同的流类型。

  39. 简单filtergraph由-filter选项指定。简单filtergraph示意图如下:

  40.  _______        _____________________        ________
  41. |       |      |                     |      |        |
  42. | input | ---> | simple filter graph | ---> | output |
  43. |_______|      |_____________________|      |________|
  44.  
  45. 复杂filtergraph用于简单filtergraph处理不了的场合。比如,多路输入流和(或)多路输出流,或者输出流与输入流类型不同。

  46. 有些特殊的filter本身就属于复杂filtergraph,用-filter_complex选项或-lavfi选项指定,如overlay滤镜和amix滤镜就是复杂filtergraphoverlay滤镜有两个视频输入和一个视频输出,将两个输入视频混合在一起。而amix滤镜则是将两个输入音频混合在一起。

  47. 复杂filtergraph示意图如下:

  48.  _________
  49. |         |
  50. | input 0 |\                    __________
  51. |_________| \                  |          |
  52.              \   _________    /| output 0 |
  53.               \ |         |  / |__________|
  54.  _________     \| complex | /
  55. |         |     |         |/
  56. | input 1 |---->| filter  |\
  57. |_________|     |         | \   __________
  58.                /| graph   |  \ |          |
  59.               / |         |   \| output 1 |
  60.  _________   /  |_________|    |__________|
  61. |         | /
  62. | input 2 |/
  63. |_________|
  64.  
  65. 示例1
    ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

  66. 上例中"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滤镜的输入。整行命令实现的功能是:将输入分隔为两路,其中一路经过裁剪和垂直翻转后,再与另一路混合,生成输出文件。示意图如下所示:

  67.                 [main]
  68. input --> split ---------------------> overlay --> output
  69.             |                             ^
  70.             |[tmp]                  [flip]|
  71.             +-----> crop --> vflip -------+
  72.  
  73. “-codec copy”可使能流拷贝模式。流拷贝直接将输入流拷贝到输出,仅涉及解复用和复用,不涉及解码和编码,因此也不支持滤镜操作。流拷贝对于修改容器格式或容器级别元数据非常有用。因为不涉及编解码操作,整个过程会非常快。示意图如下所示:

  74.  _______              ______________            ________
  75. |       |            |              |          |        |
  76. | input |  demuxer   | encoded data |  muxer   | output |
  77. | file  | ---------> | packets      | -------> | file   |
  78. |_______|            |______________|          |________|
  79.  
  80. 有些容器,如AVIMP4等,可以包含多种不同类型的流。FFmpeg可以识别5种流类型:音频(audio, a),视频(video, v),字幕(subtitle, s),附加数据(attachment, t)和普通数据(data, d)。

  81. 流选择是从输入文件中选定某些流进行处理。流选择有两种模式,1) 使用-map选项手动指定要选择的流;2) 无-map选项时由FFmpeg根据相应规则自动选择流。

  82. 自动选择模式下,每种类型的流只选择一路,规则如下:

  83. 音频流:选择具有最多通道的流,若多个音频流通道数相同且通道数最多,则选第一个

  84. 视频流:选择具有最高分辨率的流,若多个视频流分辨率相同且是最高分辨率,则选第一个

  85. 字幕流:选择第一个字幕流。注意:字幕流有文本字幕流和图形字幕流,输出格式默认的字幕编码器仅处理其支持的字幕类型

  86. 手动选择模式下,要选定的流由-map选项后的流指定符(stream specifer)指定。stream_specifier语法如下:
    [-]file_index:stream_type[:stream_index]

  87. -表示排除此流,不带-表示选中此流。文件序号file_index和流序号stream_index都是从0开始计数。

  88. 几个特殊的stream_specifier如下:

  89. --map 0 选择所有类型的所有流。

  90. --map i:v 选择文件i中所有的视频流,i:ai:s等同理。

  91. --map -vn 排除所有视频流,-an、-sn等同理。

  92. 示例:

  93. 假设ffmpeg命令行如下:
    ffmpeg -i file1 -i file2 select_streams output

  94. 其中有两个输入文件file1file2,选择的流位于select_streams

  95. file1的流组成与对应的stream_specifier如下:

  96. file streams            stream_specifier
  97. 1st video               0:v:0
  98. 2nd video               0:v:1
  99. 1st audio               0:a:0
  100. 2nd audio               0:a:1
  101. 1st subtitle            0:s:0
  102. 2nd subtitle            0:s:1
  103. 3rd subtitle            0:s:2
  104. file2的流组成与对应的stream_specifier如下:

  105. file streams            stream_specifier
  106. 1st video               1:v:0
  107. 1st audio               1:a:0
  108. 1st subtitle            1:s:0
  109. select_streams各种示例说明如下:
    -map 0 -map 1

  110. 选择两个文件的所有流

  111. -map 0:s:2 -map 1:v:0 -map 1:a:0

  112. 选择file13rd字幕流,file21st视频流和file21st音频流

  113. -map 0 -map 1:s:0 -an

  114. 选择file1除音频外的所有流和file21st字幕流

  115. -map 0 -map 1 -map -0:v:0 -map -0:a:1

  116. 选择除file11st视频流和2nd音频流外的所有流,选择file2中的所有流

  117. 除-map选项外,stream_specifier还可用在很多其他选项中,形式有如下几种:

  118. specifer形式                描述
  119. stream_index                选择索引为stream_index的流
  120. stream_type[:stream_index]  选择类型为stream_type索引为stream_index的流
  121. p:program_id[:stream_index] 选择节目program_id中索引为stream_index的流
  122. stream_id                   选择指定ID的流
  123. 例如,使用-b选项设置音频流和视频流的码率:
    ffmpeg -i input.mpg -b:a 128k -b:v 1500k output.mp4

  124. 其中“-b:a”和“-b:v”就是stream_specifier

  125. 如果某个复杂filtergraph中的输出流未携带标号,则这些流将被添加到第一个输出文件中。如果封装器格式不支持某种流类型,将会导致致命错误。

  126. 如果未使用-map选项,包含这些复杂filtergraph输出流将导致不会对这些流类型启用自动选择。

  127. 如果使用了-map选项,除-map选定的流之外,这些filtergraph输出流也会被包含进来。

  128. 复杂filtergraph的输出流若带标号,则标号必须被映射一次,且只能被映射一次。

  129. 假设有三个输入文件用于示例,其流组成成分如下:

  130. input file 'A.avi'
  131.       stream 0: video 640x360
  132.       stream 1: audio 2 channels
  133. input file 'B.mp4'
  134.       stream 0: video 1920x1080
  135.       stream 1: audio 2 channels
  136.       stream 2: subtitles (text)
  137.       stream 3: audio 5.1 channels
  138.       stream 4: subtitles (text)
  139. input file 'C.mkv'
  140.       stream 0: video 1280x720
  141.       stream 1: audio 2 channels
  142.       stream 2: subtitles (image)
  143. 示例1:无标号filtergraph的流选择
    ffmpeg -i A.avi -i C.mkv -i B.mp4 -filter_complex "overlay" out1.mp4 out2.srt

  144. -filter_complex选项指定了一个复杂filtergraph,此filtergraph由单个视频滤镜overlay构成。overlay滤镜需要两个视频输入,但此处并未为overlay滤镜指定输入,因此A.aviC.mkv中头两个有效视频流会被作为overlay滤镜的输入。overlay滤镜输出无标号,因此overlay滤镜的输出会被写入第一个输出文件out1.mp4中。

  145. 本来自动选择模式会选中B.mp4中的“stream 0”视频流(最高分辨率真)和B.mp4中的“stream 3”音频流(最多通道数)。但overlay滤镜输出流是视频流,因此,不会对视频流进行自动选择,即不会选择B.mp4中的“stream 0”。

  146. 不会选中任何字幕流,因为MP4封装格式未注册默认字幕编码器,用户也未指定字幕编码器,无编码器可用所以不会选择字幕流。

  147. 第二个输出文件out2.srt,仅接受文本类型的字幕流。所以,就算C.mkv中的“stream 2"是第一个被找到的字幕流,也会因类型不符合被忽略掉。B.mp4中的“stream 2”会被选中,因为它才是第一个文本字幕流。

  148. 示例2:带标号filtergraph的流选择

  149. ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
  150.        -map '[outv]' -an        out1.mp4 \
  151.                                 out2.mkv \
  152.        -map '[outv]' -map 1:a:0 out3.mkv
  153. 上述命令会执行失败,因为filtergraph的输出标号[outv]被映射了两次。此命令不会生成任何输出文件。

  154. ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
  155.        -an        out1.mp4 \
  156.                   out2.mkv \
  157.        -map 1:a:0 out3.mkv
  158. 上述命令也会执行失败,因为hue滤镜有一个输出标号[outv],但此标号未作任何映射。

  159. 正确的命令应该写成下面这样:

  160. ffmpeg -i A.avi -i B.mp4 -i C.mkv -filter_complex "[1:v]hue=s=0,split=2[outv1][outv2];overlay;aresample" \
  161.         -map '[outv1]' -an        out1.mp4 \
  162.                                   out2.mkv \
  163.         -map '[outv2]' -map 1:a:0 out3.mkv
  164. “[1:v]”表示B.mp4中的视频流,B.mp4中的视频流被发送到hub滤镜,hub滤镜的输出被split滤镜拷贝了一份,生成两份输出,两份输出用标号[outv1]和[outv2]表示。

  165. overlay滤镜需要两个视频输入,使用头两个未使用的视频流作输入,即A.avi和C.mkv中的视频流。overlay滤镜输出未带标号,所以overlay滤镜输出被发送到第一个输出文件out1.mp4,有没有-map选项对此无影响。

  166. aresample滤镜使用第一个未使用的音频流(A.avi中的“stream 1”)作为输入。aresample滤镜输出也未带标号,所以avresample滤镜输出也被映射到第一个输出文件out1.mp4。-an选项仅仅抑制了音频流的自动或手动流选择,而不会抑制filtergraph的输出。所以,out1.mp4有三个输入流:1)overlay滤镜输出、2)aresample滤镜输出和3)标号outv1,B.mp4中1)2)排序应在3)之前。

  167. 映射到out2.mkv的视频、音频和字幕流由自动选择模式选定。

  168. out3.mkv由hue滤镜输出和B.mp4中的“stream 1”构成。

  169. 流处理和流选择是互不影响的(字幕例外)。流处理通过-codec选项设置,-codec选项针对输出文件中的流。FFmpeg对-codec选项的处理是在流选择(stream selection)过程之后的,因此-codec选项(流处理)不会影响流选择。如果某类型的流未指定-codec选项,将会使用输出文件muxer注册的默认编码器。

  170. 上述规则不适用于字幕。如果一个输出文件指定了字幕编码器,那么找到的第一个字幕流(文本字幕或图形字幕)总会被包含进来。FFmpeg不会检查编码器是否能转换选定的流或已转换的流能否被输出格式接受。这通常也适用:当用户手动设置编码器时,流选择过程不能检查编码流是否可以复用到输出文件中。如果编码流不能复用到输出文件,FFmpeg会终止,所有的输出文件处理会失败。

  171. 待补充

  172. 待补充

  173. [1] FFmpeg Basics

  174. [2] ffmpeg.htmlhttp://ffmpeg.org/ffmpeg.html

  175. [3] What are the the data and attachment stream type?, https://ffmpeg.org/pipermail/ffmpeg-user/2015-June/027333.html

  176. 2018-12-15 V1.0 首次整理

版权声明:本文为leisure_chn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/leisure_chn/p/10297002.html