在之前的文章入门视频看这一篇足够 中我们提到了 FFmpeg,也使用了 FFmpeg 最基本的命令:ffmpeg -i somemoive.mp4。今天我们就通过源码的方式来剖析 FFmpeg 的运行机制。

概览

ffmpeg总共有 7 大库,分别是

  • avdevice
  • avcodec
  • avformat
  • swresample
  • swscale
  • postproc
  • avfilter

加上他们的共同依赖avutil,就算是八大库吧。

这几大库按需配置,为了减少体积,建议将不用的关闭掉,关闭的时候还要根据他们之间的依赖关系,这个依赖关系可以在源代码ffbuild/config.mak 中看出。

下载/编译/运行(VSCode)

git clone

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

配置

./configure --enable-debug=3 --disable-optimizations

比如我只想编译 h264 的解码器和支持硬解,那么只需要如下即可,没有配置的都会关闭,只打开你所配置的。

./configure --disable-everything --enable-decoder=h264 --enable-hwaccel=h264_dxva2

其他参数

  1. –enable-shared 默认情况下编译的库是静态的,如果我们想编译动态的,那么就可以加上这个选项。

make

make -j2

执行这一步后会发现多了很多文件。其中,以 _g 结尾的就是可以调试的程序

  • ffmpeg_g
  • ffplay_g
  • ffprobe_g

编辑 launch.json 文件


{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/ffmpeg_g",
            "args": [
                "-i",
                "/Users/kyson.cn/Downloads/kyson.cn.mp4"
            ],
            "cwd": "${workspaceFolder}"
        }
    ]
}

运行

运行后打印出了如下信息:

ffmpeg version N-115575-g4e120fbbbd Copyright (c) 2000-2024 the FFmpeg developers
  built with Apple clang version 15.0.0 (clang-1500.3.9.4)
  configuration: --enable-debug=3 --disable-optimizations
  libavutil      59. 21.100 / 59. 21.100
  libavcodec     61.  6.100 / 61.  6.100
  libavformat    61.  3.104 / 61.  3.104
  libavdevice    61.  2.100 / 61.  2.100
  libavfilter    10.  2.102 / 10.  2.102
  libswscale      8.  2.100 /  8.  2.100
  libswresample   5.  2.100 /  5.  2.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/kyson/Downloads/7365320336742599978.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    comment         : vid:v12044gd0000cordva7og65qe9e89sqg
    aigc_info       : {"aigc_label_type": 0}
    encoder         : Lavf58.76.100
  Duration: 00:01:12.91, start: 0.000000, bitrate: 948 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 576x768 [SAR 1:1 DAR 3:4], 909 kb/s, 30 fps, 30 tbr, 15360 tbn (default)
      Metadata:
        handler_name    : VideoHandler
        vendor_id       : [0][0][0][0]
  Stream #0:1[0x2](und): Audio: aac (HE-AACv2) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 32 kb/s (default)
      Metadata:
        handler_name    : SoundHandler
        vendor_id       : [0][0][0][0]
At least one output file must be specified

这是因为我们在 launch.json中指定的参数是 -i,也就相当于执行我们之前提到的 ffmpeg -i yourmovie.mp4 命令。

下载/编译/运行(Xcode)

相信大部分人在 Mac 开发使用的还是 Xcode,因此我这边顺带把 Xcode 的配置也说了,懒得操作的,可以到 GitHub 里找一个别人现成编译好的,比如这个:FFmpeg-in-Xcode
大概思路就是:

  1. 新建一个 Mac 下的命令行工程,暂且命名为 ffmpegDemo;
  2. 拿到 make 产物,然后拖到 Xcode 项目中,等待其 index 完成(过程漫长)
  3. 新建一个 target,暂且命名为 ffmpeg_cmd ,设置其 directory 以及 scheme
  4. 搞定

最终版本给大家截个图:
xcode 配置 ffmpeg

小试身手

在 ffmpeg.c 中打个断点,可以看到成功了。

成功

调用栈:

调用栈

我们看一下堆栈,分别在如下文件中:

  • ffmpeg_opt.c:负责解析命令行输入的参数

更进一步

格式转换

ffmpeg 的另外一个功能是转换文件类型,比如将 mp4 文件转换为 avi,对应的命令为: ffmpeg -i yourvideo.mp4 target.avi

如果要逐步调试,查看调用堆栈的话,我们只需要在 launch.json 中稍加改动即可。

        "args": [
                "-i",
                "/Users/kyson.cn/Downloads/kyson.cn.mp4"
                "/Users/kyson.cn/Downloads/target.avi"
            ],

改完后,点击编译运行,就能在 /Users/kyson.cn/Downloads 文件夹下找到 target.avi 文件;另一方面,在 vscode 中也会打印出相应的数据,来展示 avi 文件的信息:

Output #0, avi, to '/Users/kyson.cn/Downloads/target.avi':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    ICMT            : vid:v12044gd0000cordva7og65qe9e89sqg
    aigc_info       : {"aigc_label_type": 0}
    ISFT            : Lavf61.3.104
  Stream #0:0(und): Video: mpeg4 (FMP4 / 0x34504D46), yuv420p(tv, bt709, progressive), 576x768 [SAR 1:1 DAR 3:4], q=2-31, 200 kb/s, 30 fps, 30 tbn (default)
      Metadata:
        handler_name    : VideoHandler
        vendor_id       : [0][0][0][0]
        encoder         : Lavc61.6.100 mpeg4
      Side data:
        cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: N/A
  Stream #0:1(und): Audio: ac3 ([0] [0][0] / 0x2000), 44100 Hz, stereo, fltp, 192 kb/s (default)
      Metadata:
        handler_name    : SoundHandler
        vendor_id       : [0][0][0][0]
        encoder         : Lavc61.6.100 ac3
[out#0/avi @ 0x7f9288707140] video:6605KiB audio:1710KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 1.337688%
frame= 2187 fps=1126 q=31.0 Lsize=    8427KiB time=00:01:12.90 bitrate= 946.9kbits/s speed=37.5x    

视频截取

命令为: ffmpeg -ss 00:00:00 -t 00:00:30 -i yourvideo.mp4 -vcodec copy -acodec copy target.mp4,含义是截取视频 00:00:00 到 00:00:30 的视频,不改变音视频编码方式,目标视频文件名为 target.mp4

提取音频

对应的命令为:ffmpeg -i yourvideo.mp4 -vn getAudio.mp3

提取图片

每隔 5s 提取视频中的一张图片:ffmpeg -i yourvideo.mp4 -r 5 -f image2 image_%2d.png

转 m3u8 文件

将视频转成 m3u8 格式,方便网络传输:

ffmpeg -i yourvideo.mp4 -vcodec copy -acodec copy -hls_list_size 0 -f hls/index.m3u8

需要注意的是,要提前建一个 hls 文件夹。

设置 IDR 帧间隔

可以通过 -g(GOP size)参数控制 IDR 帧的间隔。GOP 是指 "Group of Pictures",设置 GOP size 也意味着设置了插入 IDR 帧的频率。

ffmpeg -i input.mp4 -g 60 output.mp4

这意味着每隔 60 帧插入一个 IDR 帧(在 30fps 的视频中相当于每隔 2 秒插入一个 IDR 帧)。

其他

其他还有转码之类。

总结

参考

ffmpeg CompilationGuide

FFmpeg播放HLS流程分析

FFmpeg视频切片.m3u8文件的流程与播放

m3u8和mp4格式有什么区别

ijkplayer视频秒开优化指北

Mac下调试FFmpeg的两种方式(支持10.15.4及以上)

FFmpeg原理介绍