一、音视频开发环境搭建:Android Studio配置、NDK与CMake集成、FFmpeg编译与导入、项目结构规划
说实话,音视频开发的门槛有一半都在环境搭建上。我见过太多人卡在FFmpeg编译这一步,折腾两天还没跑起来。其实说白了,只要把几个关键环节理清楚,这事儿没那么玄乎。
今天我就带你把这套环境从头到尾捋一遍。嗯,咱们从最基础的Android Studio开始。
1.1 Android Studio配置:别小看这一步
你可能觉得Android Studio谁不会装?但我告诉你,音视频开发需要的SDK和普通App开发不太一样。我个人习惯是先把下面这几个东西装好:
- Android SDK:API 21以上,建议同时保留API 21和API 33两个版本
- NDK:我推荐r21e或r23c,太新的版本反而容易踩坑
- CMake:3.18.0以上,但别超过3.22.0
- LLDB:调试Native代码必备
配置路径很简单:File → Settings → Appearance & Behavior → System Settings → Android SDK → SDK Tools。勾选NDK、CMake、LLDB,点Apply就行。
1.2 NDK与CMake集成:让Java和C++握手
为什么要用NDK?说白了,音视频处理这种计算密集型的活儿,Java扛不住。我们需要C++来干脏活累活,Java只负责UI和业务逻辑。
CMake就是那个牵线搭桥的。你想想看,Java和C++是两个世界的人,CMake就是翻译官。在Android里集成CMake,其实就三步:
- 在
build.gradle里声明CMake配置 - 写一个
CMakeLists.txt文件 - 在Java/Kotlin代码里用
System.loadLibrary()加载
来看一个典型的CMakeLists.txt:
cmake_minimum_required(VERSION 3.18.0)
project("avrecorder")
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加本地库
add_library(
avrecorder
SHARED
src/main/cpp/native-lib.cpp
src/main/cpp/audio_encoder.cpp
src/main/cpp/video_encoder.cpp
)
# 查找系统库
find_library(
log-lib
log
)
# 链接库
target_link_libraries(
avrecorder
${log-lib}
)
add_library里的SHARED表示生成动态库(.so文件)。如果你有多个源文件,全部列在这里就行。
然后在build.gradle里这样配置:
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.18.0"
}
}
}
这里有个坑——abiFilters。我曾经只写了arm64-v8a,结果在旧手机上直接崩溃。建议至少保留armeabi-v7a和arm64-v8a两个架构。
1.3 FFmpeg编译与导入:最头疼的一关
FFmpeg编译,嗯,这可能是整个环境搭建里最磨人的环节。我第一次编译FFmpeg时,光配置参数就试了十几次。但别怕,我给你一套经过验证的方案。
为什么要自己编译?因为Google Play上那些预编译的FFmpeg库,要么功能不全,要么优化不到位。自己做音视频开发,还是得亲手编译才放心。
我推荐用FFmpeg 4.4.2版本,稳定且文档齐全。编译脚本如下:
#!/bin/bash
# 编译FFmpeg for Android的脚本
NDK=/path/to/your/ndk
API=21
ARCH=arm64
PREFIX=$(pwd)/android/$ARCH
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
./configure \
--prefix=$PREFIX \
--target-os=android \
--arch=$ARCH \
--enable-cross-compile \
--cc=$TOOLCHAIN/bin/aarch64-linux-android21-clang \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--disable-static \
--enable-shared \
--enable-gpl \
--enable-version3 \
--enable-nonfree \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffmpeg \
--enable-encoder=aac \
--enable-encoder=h264 \
--enable-decoder=aac \
--enable-decoder=h264 \
--enable-parser=aac \
--enable-parser=h264 \
--enable-muxer=mp4 \
--enable-demuxer=mp4 \
--enable-protocol=file
make -j4
make install
linux-x86_64写成windows-x86_64,结果编译出来的库根本用不了。
编译完成后,你会得到一堆.so文件和头文件。把它们复制到Android项目的src/main/jniLibs目录下,结构是这样的:
app/src/main/jniLibs/
├── arm64-v8a/
│ ├── libavcodec.so
│ ├── libavformat.so
│ ├── libavutil.so
│ ├── libswresample.so
│ └── libswscale.so
└── armeabi-v7a/
├── libavcodec.so
├── libavformat.so
├── libavutil.so
├── libswresample.so
└── libswscale.so
然后在CMakeLists.txt里链接这些库:
# 设置FFmpeg路径
set(FFMPEG_PATH ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
# 导入FFmpeg库
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_PATH}/libavcodec.so)
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${FFMPEG_PATH}/libavformat.so)
# 链接到你的本地库
target_link_libraries(
avrecorder
avcodec
avformat
avutil
swresample
swscale
${log-lib}
)
1.4 项目结构规划:好结构是成功的一半
项目结构这事儿,我踩过不少坑。早期我图省事,把所有代码都塞在一个包里,结果后期维护时想死的心都有。后来我总结了一套比较合理的结构:
app/
├── src/
│ ├── main/
│ │ ├── java/com/example/avrecorder/
│ │ │ ├── ui/ # UI层
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── CameraPreview.kt
│ │ │ │ └── RecordButton.kt
│ │ │ ├── recorder/ # 录制核心
│ │ │ │ ├── AudioRecorder.kt
│ │ │ │ ├── VideoRecorder.kt
│ │ │ │ └── MediaMuxer.kt
│ │ │ ├── encoder/ # 编码器
│ │ │ │ ├── AudioEncoder.kt
│ │ │ │ └── VideoEncoder.kt
│ │ │ ├── pusher/ # 推流模块
│ │ │ │ ├── RtmpPusher.kt
│ │ │ │ └── FlvMuxer.kt
│ │ │ └── utils/ # 工具类
│ │ │ ├── PermissionUtils.kt
│ │ │ └── FileUtils.kt
│ │ ├── cpp/ # Native代码
│ │ │ ├── native-lib.cpp
│ │ │ ├── audio_encoder.cpp
│ │ │ ├── video_encoder.cpp
│ │ │ └── CMakeLists.txt
│ │ └── jniLibs/ # FFmpeg库
│ │ ├── arm64-v8a/
│ │ └── armeabi-v7a/
│ └── test/ # 测试代码
└── build.gradle
这个结构的好处是:UI、录制、编码、推流各司其职,互不干扰。你想想看,如果以后要换推流协议,只需要改pusher包里的代码,其他地方完全不用动。
AudioEncoder.kt对应audio_encoder.cpp,这样找代码时特别方便。
知识体系总览
下面这张图把整个环境搭建的脉络梳理清楚了,你可以对照着检查自己有没有遗漏:
这张图把四个核心环节串起来了。你按照这个顺序走,基本不会出大问题。我个人建议每完成一步就做个标记,别一口气全搞完再验证——那样出了问题都不知道是哪一步的锅。
好了,环境搭建这部分就聊到这儿。记住一句话:环境搭得好,开发没烦恼。把基础打扎实了,后面写代码才能行云流水。