一、音视频开发环境搭建: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代码必备
小提示: 我在项目中遇到过NDK版本不匹配导致编译失败的情况。后来发现,用Android Studio自带的SDK Manager下载NDK最稳,别自己去官网瞎折腾。

配置路径很简单: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,其实就三步:

  1. build.gradle里声明CMake配置
  2. 写一个CMakeLists.txt文件
  3. 在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-v7aarm64-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
注意: 编译前一定要确认NDK路径和工具链路径正确。我见过有人把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包里的代码,其他地方完全不用动。

我的习惯: 每个模块的Java/Kotlin代码和Native代码一一对应。比如AudioEncoder.kt对应audio_encoder.cpp,这样找代码时特别方便。

知识体系总览

下面这张图把整个环境搭建的脉络梳理清楚了,你可以对照着检查自己有没有遗漏:

音视频开发环境搭建知识体系 环境搭建总目标 Android Studio配置 NDK与CMake集成 FFmpeg编译与导入 项目结构规划 SDK/NDK/CMake安装 API版本选择 LLDB调试工具 CMakeLists.txt编写 build.gradle配置 abiFilters设置 编译脚本编写 .so库导入 头文件配置 模块化分包 Java/C++对应 jniLibs目录 四步走:装工具 → 配CMake → 编FFmpeg → 搭结构

这张图把四个核心环节串起来了。你按照这个顺序走,基本不会出大问题。我个人建议每完成一步就做个标记,别一口气全搞完再验证——那样出了问题都不知道是哪一步的锅。

好了,环境搭建这部分就聊到这儿。记住一句话:环境搭得好,开发没烦恼。把基础打扎实了,后面写代码才能行云流水。


公众号:蓝海资料掘金营,微信deep3321