4、FFmpeg入门:FFmpeg核心组件、常用命令、交叉编译Android库
好,咱们今天聊聊FFmpeg。说实话,做Android音视频开发,FFmpeg是绕不过去的一道坎。我当年刚接触时也觉得这东西又大又复杂,编译一次能折腾一整天。但等你真正理解了它的核心组件和设计思路,你会发现——嗯,其实也没那么玄乎。
4.1 FFmpeg是什么?
FFmpeg,全称是Fast Forward MPEG。说白了,它是一个开源的、跨平台的音视频处理工具集。你能想到的跟音视频有关的操作——转码、剪辑、拼接、推流、拉流、滤镜、截图——它几乎都能干。
我个人习惯把它比作「音视频领域的瑞士军刀」。你想想看,一个工具能处理几乎所有格式,能跑在Windows、Linux、macOS、Android、iOS上,这本身就说明它的设计有多扎实。
核心要点:FFmpeg不是单个程序,而是一套完整的音视频处理框架。它由多个库和可执行文件组成,你可以按需取用。
4.2 FFmpeg核心组件
FFmpeg的架构,我建议你从这几个核心组件入手理解:
| 组件 | 全称 | 作用 |
|---|---|---|
| libavformat | Format Library | 封装/解封装,处理容器格式(MP4、MKV、FLV等) |
| libavcodec | Codec Library | 编解码核心,处理H.264、AAC、MP3等编码格式 |
| libavutil | Utility Library | 工具库,提供内存管理、数学运算、数据结构等基础能力 |
| libswscale | Software Scaling | 图像缩放、色彩空间转换(比如YUV转RGB) |
| libswresample | Software Resample | 音频重采样、格式转换、声道布局调整 |
| libavfilter | Filter Library | 滤镜处理,支持复杂的音视频滤镜链 |
| libavdevice | Device Library | 设备输入输出,比如摄像头、麦克风采集 |
我记得刚开始学的时候,总搞不清libavformat和libavcodec的区别。后来在项目中调试一个MP4文件播放卡顿的问题,才真正理解了:libavformat负责把文件拆成一个个数据包,libavcodec负责把这些包解码成原始图像或声音。一个是拆快递的,一个是拆包装的——这么想就清楚了。
我的建议:先重点掌握libavformat、libavcodec、libavutil这三个。其他组件可以在用到时再深入。一口吃不成胖子,做音视频开发尤其如此。
4.3 FFmpeg常用命令
命令行的FFmpeg,是调试和验证思路最快的方式。我在项目中经常先用命令行跑通流程,再把它翻译成代码。下面这几个命令,我建议你背下来:
4.3.1 查看文件信息
ffmpeg -i input.mp4
这个命令会输出文件的编码格式、分辨率、帧率、码率、时长等信息。排查问题时的第一步,我几乎都是先跑这个。
4.3.2 格式转换
ffmpeg -i input.avi -c:v libx264 -c:a aac output.mp4
把AVI转成MP4,视频用H.264编码,音频用AAC编码。这里-c:v和-c:a分别指定视频和音频的编码器。
4.3.3 截取片段
ffmpeg -i input.mp4 -ss 00:01:00 -t 10 -c copy output.mp4
从第1分钟开始,截取10秒的内容。-ss指定起始时间,-t指定时长。-c copy表示直接复制,不重新编码,速度极快。
注意:-ss放在-i前面还是后面,效果不一样。放在前面是「快速定位」,放在后面是「精确定位」。我曾经因为搞混这个,截出来的视频总是多出几帧黑屏。后来养成了习惯:需要快速截取时用前者,需要精确到帧时用后者。
4.3.4 缩放视频
ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4
-vf是视频滤镜参数,scale是缩放滤镜。这里把视频缩放到1280x720。
4.3.5 提取音频
ffmpeg -i input.mp4 -vn -c:a copy audio.aac
-vn表示不要视频流,只提取音频。如果音频编码是AAC,直接用-c:a copy复制,不需要重新编码。
4.4 交叉编译Android库
好,到了最让人头疼的部分——交叉编译。说白了,就是在你的电脑上编译出能在Android手机上运行的库。我当年第一次编译FFmpeg for Android,折腾了整整两天。现在回头看,其实只要搞明白几个关键点,就没那么难。
4.4.1 准备工作
你需要准备:
- NDK(Native Development Kit)——推荐使用r21或r23版本
- FFmpeg源码——从官网下载,我习惯用4.4.x稳定版
- Linux或macOS环境——Windows下建议用WSL或Docker
4.4.2 编写编译脚本
下面是我常用的编译脚本模板,针对Android ARM64架构:
#!/bin/bash
NDK=/path/to/android-ndk-r23b
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
API=21
export CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
export CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
export LD=$TOOLCHAIN/bin/ld
export AR=$TOOLCHAIN/bin/llvm-ar
export AS=$TOOLCHAIN/bin/llvm-as
export NM=$TOOLCHAIN/bin/llvm-nm
export STRIP=$TOOLCHAIN/bin/llvm-strip
./configure \
--target-os=android \
--arch=aarch64 \
--cc=$CC \
--cxx=$CXX \
--ld=$LD \
--ar=$AR \
--as=$AS \
--nm=$NM \
--strip=$STRIP \
--enable-cross-compile \
--enable-shared \
--disable-static \
--disable-doc \
--disable-programs \
--disable-avdevice \
--enable-avformat \
--enable-avcodec \
--enable-avutil \
--enable-swscale \
--enable-swresample \
--enable-avfilter \
--prefix=./android/arm64
make -j8
make install
关键点说明:
--enable-shared:编译成动态库(.so),Android上推荐用动态库--disable-programs:不编译ffmpeg、ffplay等可执行文件,我们只需要库--disable-avdevice:Android上一般用不到设备输入输出,关掉可以减小体积API=21:最低支持Android 5.0,覆盖了绝大多数设备
4.4.3 编译产物
编译完成后,在./android/arm64/lib目录下会生成一系列.so文件:
libavcodec.so
libavformat.so
libavutil.so
libswscale.so
libswresample.so
libavfilter.so
把这些.so文件放到Android项目的app/src/main/jniLibs/arm64-v8a/目录下,再把对应的头文件放到app/src/main/cpp/include/目录下,就可以在JNI代码中调用了。
避坑指南:我曾经在编译时忘了加--enable-cross-compile,结果编译出来的库在手机上跑起来就崩。排查了半天才发现是交叉编译标志没开。嗯,这种低级错误,犯过一次就记住了。
4.5 知识体系总览
下面这张图,是我梳理的FFmpeg入门知识体系。你可以把它当作学习路线图:
从这张图你可以看到,FFmpeg入门其实就三条线:理解组件、会用命令、能编译库。三条线缺一不可,但也不用同时精通。我个人建议的顺序是:先玩命令,再学组件,最后搞编译。命令玩熟了,你对FFmpeg的能力边界就有了直观感受,后面学组件和编译时心里就有底了。
最后说一句:FFmpeg的学习曲线确实有点陡,但只要你把核心组件搞明白,把编译流程跑通一遍,后面再遇到音视频相关的问题,你至少知道该从哪里下手。我在项目中遇到过的绝大多数音视频问题,最终都能用FFmpeg找到答案——要么是命令行验证,要么是库调用。所以,花时间把这一章吃透,绝对值。
公众号:蓝海资料掘金营,微信deep3321