Android-NDK-010-ffmpeg-编译

320 阅读6分钟

1 FFmpeg 简介

FFmpeg 是一个开源的多媒体框架,能够处理音频、视频以及其他多媒体文件和流。它包括了各种工具和库来进行音视频处理、编码解码、转码、视频剪辑、音视频合成等工作。FFmpeg 的核心库包括:

  • libavcodec:编解码库,处理音视频编码和解码。
  • libavformat:格式库,负责读取、写入多种格式的容器文件。
  • libavfilter:过滤器库,支持各种音视频过滤。
  • libswscale:缩放库,支持图片和视频的像素格式转换和缩放。
  • libswresample:音频重采样库,用于音频格式的转换。
  • libpostproc:后处理库,进行视频后处理。

FFmpeg 主要的命令行工具包括:

  • ffmpeg:用于转换音视频格式。
  • ffplay:用于播放音视频。
  • ffprobe:用于查看音视频的元数据。

在 Android 开发中,FFmpeg 常用于处理音视频文件,或者进行实时音视频流处理。

2 如何在 Android 项目中集成 FFmpeg

在 Android 中集成 FFmpeg 通常有两种方式:

  1. 使用现成的 FFmpeg Android 库,例如 FFmpeg Android JavaMobileFFmpeg
  2. 使用 NDK 通过源码集成 FFmpeg,编译并在 Android 项目中调用。

下面介绍这两种方式的使用方法。


方法 1:使用 FFmpeg Android 库(例如 MobileFFmpeg)

MobileFFmpeg 是一个为 Android 提供 FFmpeg 功能的库,它封装了 FFmpeg 的操作,并提供了易于使用的 Java/Kotlin 接口。

1.1 添加 MobileFFmpeg 依赖

  1. 打开 build.gradle 文件,添加 MobileFFmpeg 的依赖项。
kotlin
复制代码
dependencies {
    implementation 'com.arthenica:ffmpeg-kit-full:5.1.1'
}

2. 同步项目以下载依赖。

1.2 使用 MobileFFmpeg 执行视频转码

例如,下面的代码使用 MobileFFmpeg 将一个视频文件转换为另一个格式。

kotlin
复制代码
import com.arthenica.mobileffmpeg.FFmpeg
import com.arthenica.mobileffmpeg.Config

fun convertVideo(inputPath: String, outputPath: String) {
    val command = "-i $inputPath -c:v libx264 -preset fast -c:a aac -b:a 192k -y $outputPath"
    
    // 执行 FFmpeg 命令
    val result = FFmpeg.execute(command)
    
    if (result == Config.RETURN_CODE_SUCCESS) {
        println("Video conversion successful!")
    } else {
        println("Video conversion failed!")
    }
}

1.3 使用 MobileFFmpeg 播放视频

除了转换,MobileFFmpeg 还可以用来播放视频或提取视频的某些信息。例如,使用 ffprobe 获取视频的信息:

kotlin
复制代码
val command = "-v quiet -print_format json -show_format -show_streams $videoFilePath"
val result = FFmpeg.execute(command)

if (result == Config.RETURN_CODE_SUCCESS) {
    println("Video info extracted successfully!")
} else {
    println("Failed to extract video info")
}

1.4 注意事项

  • 性能:FFmpeg 是一个高效的多媒体框架,但是它的性能取决于硬件设备,尤其在进行高质量视频转码时会消耗较多 CPU 和内存。
  • 权限:在操作文件时,确保你申请了合适的权限,尤其是对于访问存储的权限。
  • 兼容性:MobileFFmpeg 已经针对 Android 进行了优化,可以在多种 Android 设备上工作,但在一些低性能设备上可能会出现问题。

方法 2:通过 NDK 集成 FFmpeg 源码

如果你需要更大的灵活性,并且想直接通过 NDK 使用 FFmpeg,可以通过以下步骤集成 FFmpeg 源码到 Android 项目中。

2.1 下载 FFmpeg 源码

首先,你需要从 FFmpeg 的官网或 GitHub 仓库下载源码,并为 Android 编译它。你可以从 FFmpeg 官方网站 获取源码,或者使用 Git 克隆 FFmpeg 仓库:

bash
复制代码
git clone https://git.ffmpeg.org/ffmpeg.git

2.2 编译 FFmpeg 为 Android

使用 Android NDK 工具链编译 FFmpeg 源码。以下是简化的步骤:

2.3 JNI 调用 FFmpeg 函数

编译完成后,你可以通过 JNI 调用 FFmpeg 函数。例如,调用 FFmpeg 的解码函数来解码音视频数据:

cpp
复制代码
extern "C" JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_decodeVideo(JNIEnv* env, jobject /* this */) {
    // 使用 FFmpeg 函数解码视频或音频
    av_register_all();
    avformat_network_init();
    
    // 解码操作代码(示例)
}

2.4 调用本地方法

MainActivity.kt 中声明本地方法,并使用 JNI 调用:

kotlin
复制代码
class MainActivity : AppCompatActivity() {

    // 声明本地方法
    external fun decodeVideo()

    companion object {
        init {
            System.loadLibrary("ffmpeg")  // 加载本地库
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 调用本地方法
        decodeVideo()
    }
}

3 编译 FFmpeg 为 Android 详细步骤

  1. 下载 FFmpeg 源代码
  2. 安装 Android NDK 和 CMake
  3. 设置编译环境
  4. 编译 FFmpeg 为 Android
  5. 在 Android 项目中使用 FFmpeg

1. 下载 FFmpeg 源代码

你可以从 FFmpeg 官方 GitHub 仓库或官网下载源代码。

使用 Git 克隆 FFmpeg 仓库

bash
复制代码
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg

你可以选择下载稳定版本的 FFmpeg,或者直接使用最新的开发版本。

或者从 FFmpeg 官方网站下载

你也可以直接访问 FFmpeg 下载页面 获取源码。


2. 安装 Android NDK 和 CMake

你需要 Android NDK 和 CMake 来编译 FFmpeg 为 Android 的 .so 库。

  1. 打开 Android Studio,进入 SDK Manager(点击 Tools > SDK Manager)。
  2. SDK Tools 标签页中,确保勾选了 NDK (Side by side)CMake
  3. 安装或更新到适合的版本。

确认你已安装了 NDK 和 CMake 后,可以使用这些工具来编译 FFmpeg。


3. 设置编译环境

在准备编译 FFmpeg 前,我们需要设置编译环境,包括配置 Android NDK 工具链,指定目标架构等。

3.1 设置 Android NDK 环境变量

环境变量的设置通常包括以下两部分:

  • ANDROID_NDK_HOME:指向 NDK 根目录,告诉开发工具链在哪里找到 NDK。
  • ANDROID_SDK_HOME(可选):指向 Android SDK 根目录。
3.1.1 在 Linux/macOS 上设置 NDK 环境变量

如果你在 Linux 或 macOS 上开发,可以在你的 Shell 配置文件(例如 .bashrc.zshrc)中设置 NDK 环境变量:

  1. 打开终端

  2. 编辑配置文件,使用适合的文本编辑器(例如 nanovivim)打开配置文件:

    nano ~/.bashrc  # 如果你使用 bash
    

    或者,如果你使用 zsh

    nano ~/.zshrc
    
  3. 在文件中添加以下内容(根据你的 NDK 安装路径修改):

export NDK_PATH=/Users/fengxiemojie/Library/Android/sdk/ndk/22.1.7171670
export TOOLCHAIN_PATH=$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64
export CC=$TOOLCHAIN_PATH/bin/aarch64-linux-android30-clang
export CXX=$TOOLCHAIN_PATH/bin/aarch64-linux-android30-clang++
export AR=$TOOLCHAIN_PATH/bin/aarch64-linux-android-ar
export AS=$TOOLCHAIN_PATH/bin/aarch64-linux-android-as
export LD=$TOOLCHAIN_PATH/bin/aarch64-linux-android-ld
export RANLIB=$TOOLCHAIN_PATH/bin/aarch64-linux-android-ranlib
export STRIP=$TOOLCHAIN_PATH/bin/aarch64-linux-android-strip
其中 `/path/to/your/ndk` 是你本地 Android NDK 的安装路径。通常,如果你使用 Android Studio 安装 NDK,路径会类似于:

```
/Users/your-user-name/Library/Android/sdk/ndk/21.3.6528147
```
  1. 保存文件并关闭编辑器(如果使用 nano,按 Ctrl + X,然后按 Y 保存更改)。

  2. 重新加载配置文件,使更改生效:

    source ~/.bashrc   # 如果你使用 bash
    

    或者:

    source ~/.zshrc    # 如果你使用 zsh
    
3.1..2 在 Windows 上设置 NDK 环境变量

在 Windows 上,你可以通过 环境变量 来设置 Android NDK:

  1. 右键点击桌面上的“计算机”图标,选择 属性

  2. 在左侧选择 高级系统设置,然后点击 环境变量

  3. 系统变量 部分,点击 新建

  4. 在变量名框中输入 ANDROID_NDK_HOME,在变量值框中输入你的 NDK 安装路径。例如:

    C:\Users\your-user-name\AppData\Local\Android\Sdk\ndk\21.3.6528147
    
  5. 点击 确定 完成设置。

  6. 重新启动命令提示符或 IDE(如 Android Studio)以使环境变量生效。

3.1.3 验证 NDK 环境变量设置

你可以通过命令行验证 NDK 环境变量是否设置成功。

在终端中输入以下命令:

echo $ANDROID_NDK_HOME   # 在 Linux/macOS 上

或在 Windows 命令提示符中输入:

echo %ANDROID_NDK_HOME%  # 在 Windows 上

如果一切正常,你应该看到 NDK 的安装路径。

3.2. 其他常用的 NDK 环境变量

  • NDK_TOOLCHAIN_VERSION: 指定使用的 NDK 工具链版本。例如:4.9clang 等。
  • NDK_PROJECT_PATH: 指定 NDK 项目路径,通常用于 ndk-build 构建系统中。
  • ANDROID_NDK_HOME: 已经设置了,指向 NDK 安装路径。
  • ANDROID_SDK_HOME: 用于指定 Android SDK 的路径,通常不需要手动设置,除非你有多个 SDK 安装目录。

3.3 配置 FFmpeg 编译选项

使用 NDK 构建 FFmpeg 时,我们需要指定 Android 工具链和目标平台。可以通过创建一个 Android 配置文件(例如 android-arm64.sh)来进行配置。

ffmpeg 目录下创建一个 android 文件夹,然后在该文件夹中创建如下的脚本文件(例如 android-arm64.sh)来配置编译选项:

注意:这里面的 API 级别为所支持的最小设备版本,即仅可运行在该API 及以上平台


#!/bin/bash

# =============================
# 配置路径和环境变量
# =============================
# 设置 Android NDK 路径
NDK=/Users/fengxiemojie/Library/Android/sdk/ndk/22.1.7171670
if [ ! -d "$NDK" ]; then
    echo "NDK 路径无效,请检查: $NDK"
    exit 1
fi

# 设置工具链路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

# 架构、API 级别和目标平台
ARCH=arm64
API=30
CPU=armv8-a
PREFIX=$(pwd)/android/dynamics/$ARCH

# 检查工具链是否存在
if [ ! -d "$TOOLCHAIN" ]; then
    echo "工具链路径无效: $TOOLCHAIN"
    exit 1
fi

# =============================
# 清理之前的构建
# =============================
make distclean

# =============================
# 配置 FFmpeg
# =============================
./configure \
    --prefix=$PREFIX \
    --target-os=android \
    --arch=$ARCH \
    --cpu=$CPU \
    --enable-shared \
    --disable-static \
    --enable-neon \
    --enable-jni \
    --enable-mediacodec \
    --disable-debug \
    --disable-doc \
    --disable-programs \
    --enable-hwaccel=h264 \
    --cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
    --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang \
    --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ \
    --sysroot=$TOOLCHAIN/sysroot \
    --extra-cflags="-fPIC -I$TOOLCHAIN/sysroot/usr/include" \
    --extra-ldflags="-L$TOOLCHAIN/sysroot/usr/lib"

# =============================
# 构建和安装
# =============================
if [ $? -ne 0 ]; then
    echo "配置失败,请检查日志文件 config.log"
    exit 1
fi

make -j$(nproc)
if [ $? -ne 0 ]; then
    echo "编译失败,请检查输出日志"
    exit 1
fi

make install
if [ $? -ne 0 ]; then
    echo "安装失败,请检查输出日志"
    exit 1
fi

echo "FFmpeg 编译完成!输出路径: $PREFIX"

关键配置选项解释

  • --enable-shared:启用生成 .so 文件(共享库)。
  • --disable-static:禁用静态库生成,确保只生成动态库(.so)。
  • --prefix=$PREFIX:指定安装路径,编译后的 .so 文件将安装在此路径。
  • --cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android-:设置交叉编译前缀,确保工具链用于交叉编译。
  • --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang:设置 C 编译器。
  • --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++:设置 C++ 编译器。
  • --sysroot=$TOOLCHAIN/sysroot:指定工具链的 sysroot。
  • --extra-cflags="-fPIC -I$TOOLCHAIN/sysroot/usr/include":指定额外的编译选项,确保生成位置无关代码(PIC),并包含工具链头文件。
  • --extra-ldflags="-L$TOOLCHAIN/sysroot/usr/lib":指定链接时的库路径。

3.4 配置 FFmpeg 以支持 RTMP

在编译 FFmpeg 时,您需要启用 --enable-librtmp,并指定 librtmp 的路径。以下是更新后的 FFmpeg 配置步骤:


# =============================
# 配置路径和环境变量
# =============================
export NDK=/Users/fengxiemojie/Library/Android/sdk/ndk/22.1.7171670
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

# 设置目标架构和 API 版本
export TARGET=aarch64-linux-android
export API=21
export PREFIX=$(pwd)/$TARGET$API

# 设置 librtmp 的路径
export RTMP_LIB_PATH=$(pwd)/rtmpdump/$TARGET$API

# =============================
# 清理旧的构建
# =============================
make clean

# =============================
# 配置 FFmpeg 构建选项
# =============================
./configure \
    --prefix=$PREFIX \
    --target-os=android \
    --arch=$TARGET \
    --cpu=armv8-a \
    --enable-shared \  # 启用动态库
    --disable-static \ # 禁用静态库
    --enable-neon \    # 启用 NEON 优化
    --enable-jni \     # 启用 JNI
    --enable-mediacodec \ # 启用 MediaCodec
    --disable-debug \  # 禁用调试信息
    --disable-doc \    # 禁用文档生成
    --disable-programs \ # 禁用生成可执行文件
    --enable-librtmp \ # 启用 RTMP 支持
    --cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
    --cc=$TOOLCHAIN/bin/aarch64-linux-android$API-clang \
    --cxx=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++ \
    --sysroot=$TOOLCHAIN/sysroot \
    --extra-cflags="-fPIC -I$TOOLCHAIN/sysroot/usr/include -I$RTMP_LIB_PATH/include" \
    --extra-ldflags="-L$TOOLCHAIN/sysroot/usr/lib -L$RTMP_LIB_PATH/lib" \
    --extra-libs="-lrtmp"

if [ $? -ne 0 ]; then
    echo "配置失败,请检查日志文件 config.log"
    exit 1
fi

# =============================
# 构建 FFmpeg 生成动态库(.so)
# =============================
make -j$(nproc)
if [ $? -ne 0 ]; then
    echo "编译失败,请检查输出日志"
    exit 1
fi

# =============================
# 安装生成的动态库到目标路径
# =============================
make install
if [ $? -ne 0 ]; then
    echo "安装失败,请检查输出日志"
    exit 1
fi

echo "FFmpeg 编译完成!输出路径: $PREFIX"

关键配置选项解释:

  • --enable-librtmp:启用 RTMP 协议支持。
  • --extra-cflags="-I$RTMP_LIB_PATH/include":指定 librtmp 的头文件路径。
  • --extra-ldflags="-L$RTMP_LIB_PATH/lib":指定 librtmp 的库文件路径。
  • --extra-libs="-lrtmp":在链接时链接 librtmp 库。

3.5 给予执行权限

如果你创建了配置脚本,记得给它执行权限:

chmod +x android-arm64.sh
chmod +x android-armeabi-v7a.sh

4. 编译 FFmpeg 为 Android

验证主机依赖: 在 macOS 上,确保安装了以下依赖:

# 安装 Homebrew(如果还没有安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装编译所需的工具和库
brew install yasm pkg-config libx264

#在 macOS 上,确保安装了以下依赖
brew install automake autoconf libtool pkg-config
  • yasm:一个汇编程序,FFmpeg 编译时需要使用。
  • pkg-config:一个帮助 C 编译器查找库文件的工具。
  • libx264:一个用于 H.264 视频编码的库。如果你不需要 H.264 编码,可以跳过安装 libx264

4.1 运行编译脚本

ffmpeg 源码目录下,运行以下命令来编译 FFmpeg:

//动态库
./android/dynamics/android-arm64.sh
./android/dynamics/android-armeabi-v7a.sh

//静态库
./android/static/android-arm64.sh
./android/static/android-armeabi-v7a.sh

这个命令会开始下载必要的依赖(如果没有下载过的话),并开始编译 FFmpeg。

image.png

4.2 多架构编译

如果你需要支持多个架构(如 armeabi-v7a, arm64-v8a, x86_64 等),你可以为每个架构创建不同的配置脚本并分别编译。例如,创建 android-arm.shandroid-x86_64.sh,然后分别执行:

./android/android-arm.sh
./android/android-x86_64.sh

4.3 检查编译结果

编译完成后,FFmpeg 的库文件(.so)将被安装到你指定的 PREFIX 目录下。检查该目录,应该会有类似以下文件结构:

makefile
复制代码
android/
    arm64/
        lib/
            libavcodec.so
            libavformat.so
            libavfilter.so
            libswscale.so
            ...
        include/
            libavcodec/
            libavformat/
            ...

这些 .so 文件就是你需要将其包含到 Android 项目中的本地库。


5. 在 Android 项目中使用 FFmpeg

完成 FFmpeg 编译后,你需要将这些 .so 库文件集成到 Android 项目中。

5.1 创建 Android 项目

在 Android 项目的 app/src/main 目录下创建一个 jniLibs 文件夹,并将编译好的 .so 文件放入适当的架构文件夹中。例如:

css
复制代码
app/
    src/
        main/
            jniLibs/
                arm64-v8a/
                    libavcodec.so
                    libavformat.so
                    libavfilter.so
                    libswscale.so
                    ...
                armeabi-v7a/
                    ...
                x86_64/
                    ...

5.2 build.gradle 中配置 NDK

build.gradle 文件中配置支持的 NDK 架构:

kotlin
复制代码
android {
    compileSdkVersion 34

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 34
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a", "x86_64"
        }
    }

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

5.3 调用 FFmpeg 本地方法

在 Kotlin 或 Java 中通过 JNI 调用 FFmpeg 的本地方法。首先,在 C++ 中编写 JNI 函数,然后在 Kotlin/Java 中声明这些本地方法。

cpp
复制代码
#include <jni.h>
#include <string>
#include <ffmpeg/avformat.h>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
    av_register_all();
    return env->NewStringUTF("FFmpeg Initialized!");
}

然后在 Kotlin 中声明并调用这个本地方法:

kotlin
复制代码
class MainActivity : AppCompatActivity() {
    external fun stringFromJNI(): String

    companion object {
        init {
            System.loadLibrary("ffmpeg")  // 加载本地库
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val textView: TextView = findViewById(R.id.sample_text)
        textView.text = stringFromJNI()
    }
}