一、编译的环境
1. MacOS 系统
系统:MacOS 15.1
2. FFmpeg 版本
FFmpeg 版本:7.1.1 注意是需要下载压缩包 FFmpeg 的下载链接
3. x264的版本
x264的版本:0.165.x 注意是需要下载压缩包 x264的版本下载的链接
4. Android Studio版本
Android Studio版本:Android Studio Ladybug | 2024.2.1 Patch 3
5. NDK的版本
NDK的版本:android-ndk-r27 ,注意这里是在mac环境进行编译,需要的NDK版本也是mac的版本,其他平台的自行下载对应的版本,NDK下载的链接
注意:这里是是编译的架构是 arm64-v8a(重点) 还有 armeabi-v7a , x86和x86_64已经用的不多,加上这里没有x86和x86_64的测试环境,所以这里就忽略了,需要的可以自行去进行编译。
二、NDK的压缩包解压
下载下来的NDK压缩包使用下面的命令来进行解压缩:
tar -xvf android-ndk-r27-darwin.zip
二、交叉编译x264的压缩包
下载下来的x264压缩包使用下面的命令来进行解压缩:
tar -xvf x264-master.tar.bz2
解压缩后的内容结果是:
创建一个sh文件,内容如下,根据自行的需求进行编译按照不同的架构:
#!/bin/bash
set -ex
# 替换为你的 NDK 路径,重点重点重点重点重点重点重点重点重点重点重点重点
NDK=/Users/renjianfa/Desktop/tool/audiovideo/android-ndk-r27
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
API=29
# arm64是arm64-v8a架构的,如果需要编译armeabi-v7a,需要修改为arm
ARCH=arm64
# ARCH=arm
# aarch64-linux-android是arm64-v8a架构的,如果需要编译armeabi-v7a,需要修改为arm-linux-androideabi
TARGET=aarch64-linux-android
# TARGET=arm-linux-androideabi
# x264 编译输出路径,自行修改成为的
X264_PATH=/Users/renjianfa/Desktop/tool/audiovideo/x264/x264-master/android/arm64
# X264_PATH=/Users/renjianfa/Desktop/tool/audiovideo/x264/x264-master/android/armv7
# 设置 sysroot 为 llvm 自带的 sysroot,兼容新 NDK
SYSROOT=$TOOLCHAIN/sysroot
make distclean || true
echo "输出路径: $X264_PATH"
# 显式指定编译器
# aarch64-linux-android29-clang是arm64-v8a架构的,如果需要编译armeabi-v7a,需要修改为armv7a-linux-androideabi29-clang
export CC=$TOOLCHAIN/bin/aarch64-linux-android29-clang
# export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi29-clang
export CFLAGS="--sysroot=$SYSROOT"
export LDFLAGS="--sysroot=$SYSROOT"
./configure \
--prefix=$X264_PATH \
--host=$TARGET \
--enable-shared \ #这里输出是so包的格式
--disable-static \ #这里输出的是.a 文件的格式
--enable-pic \
--disable-cli \
--disable-opencl \
--sysroot="$SYSROOT"
make -j$(nproc) install
echo "成功输出到:$X264_PATH"
find $X264_PATH
上面的sh文件自行命名,例如这边命名是: build_x264_android_arm64.sh (arm64-v8a架构) build_x264_android_armv7.sh (armeabi-v7a架构)
然后把 sh 文件拉进去解压后x264文件夹里面,然后执行下面的命令:
1. 编译arm64-v8a架构
chmod +x build_x264_android_arm64.sh
./build_x264_android_arm64.sh
最后在x264目录下生成 android / arm64 的文件夹,arm64文件夹里面有两个文件夹,分别是include文件夹和lib文件夹
2. 编译armeabi-v7a架构
chmod +x build_x264_android_armv7.sh
./build_x264_android_armv7.sh
最后在x264目录下生成 android / armv7 的文件夹,armv7文件夹里面同样有两个文件夹,分别是include文件夹和lib文件夹
三、使用FFmpeg结合x264一起编译
从FFmpeg的官网下载后的FFmpeg压缩包,使用下面的命令来进行解压缩:
tar -xvf ffmpeg-7.1.1.tar.xz
1. 编译arm64-v8a架构
创建一个sh文件,命名为: build_android_arm64_with_x264.sh (arm64-v8a架构) 内容如下:
#!/bin/bash
set -ex
# 目标Android版本
API=29
ARCH=arm64
CPU=armv8-a
TOOL_CPU_NAME=aarch64
#so库输出目录,根据自己的要求自行修改设置
OUTPUT=/Users/renjianfa/Desktop/tool/audiovideo/ffmpeg/ffmpeg-7.1.1/ffmpeg-7.1.1-uncompiled/ffmpeg-7.1.1/android/$CPU
# NDK的路径,根据自己的NDK位置进行设置
NDK=/Users/renjianfa/Desktop/tool/audiovideo/android-ndk-r27
# 编译工具链路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
# 编译环境
SYSROOT=$TOOLCHAIN/sysroot
TOOL_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android"
CC="$TOOL_PREFIX$API-clang"
CXX="$TOOL_PREFIX$API-clang++"
OPTIMIZE_CFLAGS="-march=$CPU"
# x264 编译输出路径,从上面的输出目录获取,根据自己的要求自行修改设置
X264_PATH=/Users/renjianfa/Desktop/tool/audiovideo/x264/x264-master/android/arm64
function build
{
# 重点,需要导入x264 的输出目录下的pkgconfig文件夹
export PKG_CONFIG_PATH=$X264_PATH/lib/pkgconfig
./configure \
--prefix=$OUTPUT \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--enable-neon \
--enable-jni \
--enable-gpl \
--enable-mediacodec \
--enable-cross-compile \
--disable-static \
--enable-shared \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffmpeg \
--enable-avfilter \
--enable-avdevice \
--disable-symver \
--enable-filters \
--enable-swscale \
--enable-swresample \
--enable-libx264 \
--enable-encoder=libx264 \
--extra-cflags="-I$X264_PATH/include" \
--extra-ldflags="-L$X264_PATH/lib" \
--extra-libs="-lx264" \
--cc=$CC \
--cxx=$CXX \
--strip=$TOOLCHAIN/bin/llvm-strip \
--nm=$TOOLCHAIN/bin/llvm-nm \
--ar=$TOOLCHAIN/bin/llvm-ar \
--ranlib=$TOOLCHAIN/bin/llvm-ranlib \
--sysroot=$SYSROOT \
--enable-network \
--enable-pthreads \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="-mno-stackrealign -m64" \
--enable-neon \
--enable-decoder=aac \
--enable-encoder=aac
make -j$(nproc)
make install
echo "============================ build android arm64-v8a with libx264 success =========================="
}
build
保存成功后运行下面的 sh 脚本文件:
chmod +x build_android_arm64_with_x264.sh
./build_android_arm64_with_x264.sh
编译成功后会生成四个文件夹,bin文件夹,include文件夹,lib文件夹,share文件夹:
这里我们重点关心是include文件夹和lib文件夹即可:
lib的文件夹:
include的文件夹:
2. 编译armeabi-v7a架构
创建一个sh文件,命名为: build_android_armv7_with_x264.sh (armeabi-v7a架构) 内容如下:
#!/bin/bash
set -ex
# 目标Android版本
API=29
ARCH=arm
CPU=armv7-a
TOOL_CPU_NAME=armv7a
#so库输出目录,根据自己的要求自行修改设置
OUTPUT=/Users/renjianfa/Desktop/tool/audiovideo/ffmpeg/ffmpeg-7.1.1/ffmpeg-7.1.1-uncompiled/ffmpeg-7.1.1/android/$CPU
# NDK的路径,根据自己的NDK位置进行设置
NDK=/Users/renjianfa/Desktop/tool/audiovideo/android-ndk-r27
# 编译工具链路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
# 编译环境
SYSROOT=$TOOLCHAIN/sysroot
TOOL_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-androideabi"
CC="$TOOL_PREFIX$API-clang"
CXX="$TOOL_PREFIX$API-clang++"
OPTIMIZE_CFLAGS="-march=$CPU"
# x264 编译输出路径,从上面的输出目录获取,根据自己的要求自行修改设置
X264_PATH=/Users/renjianfa/Desktop/tool/audiovideo/x264/x264-master/android/armv7
function build
{
# 重点,需要导入x264 的输出目录下的pkgconfig文件夹
export PKG_CONFIG_PATH=$X264_PATH/lib/pkgconfig
./configure \
--prefix=$OUTPUT \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--enable-neon \
--enable-jni \
--enable-gpl \
--enable-mediacodec \
--enable-cross-compile \
--disable-static \
--enable-shared \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffmpeg \
--enable-avfilter \
--enable-avdevice \
--disable-symver \
--enable-filters \
--enable-swscale \
--enable-swresample \
--enable-libx264 \
--enable-encoder=libx264 \
--extra-cflags="-I$X264_PATH/include" \
--extra-ldflags="-L$X264_PATH/lib" \
--extra-libs="-lx264" \
--cc=$CC \
--cxx=$CXX \
--strip=$TOOLCHAIN/bin/llvm-strip \
--nm=$TOOLCHAIN/bin/llvm-nm \
--ar=$TOOLCHAIN/bin/llvm-ar \
--ranlib=$TOOLCHAIN/bin/llvm-ranlib \
--sysroot=$SYSROOT \
--enable-network \
--enable-pthreads \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-cflags="-mno-stackrealign -m32" \
--enable-neon \
--enable-decoder=aac \
--enable-encoder=aac
make -j$(nproc)
make install
echo "============================ build android armv7-a with libx264 success =========================="
}
build
保存成功后运行下面的 sh 脚本文件:
chmod +x build_android_armv7_with_x264.sh
./build_android_armv7_with_x264.sh
编译成功后会生成四个文件夹,bin文件夹,include文件夹,lib文件夹,share文件夹:
这里我们重点关心是include文件夹和lib文件夹即可:
lib的文件夹:
include的文件夹:
3. 编译configure的参数解析
注意:这里是常用的参数,根据自己的需求来进行增添和修改
| configure的参数 | 作用解析 |
|---|---|
| prefix=$OUTPUT | 指定安装输出路径 |
| target-os=android | 目标系统是 Android |
| arch=$ARCH | 指定目标架构(arm, arm64, x86, x86_64) |
| cpu=$CPU | 指定目标架构CPU(armv7-a,armv8-a,i686, x86_64) |
| enable-neon | 开启 NEON SIMD 优化(只对 ARM 架构有用)) |
| enable-jni | 启用 JNI 支持,可以在 Android Java 层调用 FFmpeg API |
| enable-gpl | 打开 GPL 协议功能,这里主要是为了支持 x264(否则无法启用)) |
| enable-mediacode | 启用 Android MediaCodec 硬件编解码支持 |
| enable-cross-compile | 启用交叉编译模式,这里需要加上,结合x264进行编译 |
| disable-static 和 enable-shared | 生成 .so 动态库,禁用 .a 静态库。根据自己的需要来进行 |
| disable-doc, disable-ffplay, disable-ffprobe | 生成文档、播放器和探测工具, 这里一般不用,因为移植到android平台,节省体积 |
| disable-ffmpeg | 启用 ffmpeg 命令行工具,因为移植到android平台,一般用不到,选择disable就行,有内部的ffmpeg的api |
| enable-avfilter, enable-avdevice ,enable-filters , enable-swscale ,enable-swresample | 开启滤镜、设备接口、缩放、重采样等功能 |
| enable-libx264 | 开启 libx264 支持,这里混合交叉编译,必须加上 |
| enable-encoder=libx264 | 启用 x264 编码器,这里混合交叉编译,必须加上 |
| extra-cflags="-I$X264_PATH/include" | 指定 x264 的头文件、库路径和链接库,这里混合交叉编译,必须加上 |
| extra-ldflags="-L$X264_PATH/lib" | |
| extra-libs="-lx264" | |
| cc=$CC | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| cxx=$CXX | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| strip=$TOOLCHAIN/bin/llvm-strip | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| nm=$TOOLCHAIN/bin/llvm-nm | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| ar=$TOOLCHAIN/bin/llvm-ar | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| ranlib=$TOOLCHAIN/bin/llvm-ranlib | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| sysroot=$SYSROOT | 指定编译器和工具,确保走 Android NDK 提供的 clang/llvm 工具链 |
| enable-network | 启用网络(RTMP/HTTP 等)协议 |
| enable-pthreads | 启用 pthreads 多线程 |
| enable-decoder=aac | 显式调用AAC解码器 |
| enable-encoder=aac | 显式调用AAC编码器 |
| disable-symver | 禁用符号版本控制(Android 上通常需要禁用,否则链接问题) |
四、移植到Android平台
1. 新建jniLIbs文件夹,位于main目录下
在上面的x264编译后我们看到生成了include文件夹的内容和lib文件夹的内容, 也看到FFmpeg结合x264交叉编译后生成了include文件夹的内容和lib文件夹的内容。
在 Android Studio的 main文件夹目录下,创建一个jniLibs 的目录,用来添加FFmpeg和 x264的lib文件夹里面的内容和include文件夹里面的内容,如下:
同时新建一个arm64-v8a和armeabi-v7a 的文件夹,用来存放不同架构的内容,然后我们把FFmpeg和x264编译后的结果各自放到里面,include的文件夹和lib的文件夹, ,如下:
2. 配置CMakeLists.txt 文件,位于cpp文件夹里面
cpp的目录参考:
CMakeLists.txt 文件代码参考:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
# 指定 cmake 的最小版本
# 这行命令是可选的,我们可以不写这句话,但在有些情况下,如果 CMakeLists.txt 文件中使用了一些高版本 cmake 特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行 cmake。
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
# 设置项目名称
# 这个命令不是强制性的,但最好都加上。它会引入两个变量 demo_BINARY_DIR 和 demo_SOURCE_DIR,同时,cmake 自动定义了两个等价的变量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
project("native-lib")
# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/lib)
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR})
# 2. 添加头文件目录
include_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/include)
# 配置目标so库编译信息
# add_library() 的作用:
# add_library() 是 CMake 用于**创建库(静态库 .a 或 动态库 .so)**的命令。它可以用来:
# • 从源码编译一个库(静态 STATIC / 共享 SHARED)。
# • 引用一个已有的预编译库(IMPORTED)。
# native-lib → 目标库的名称。
# SHARED → 生成 动态库(.so),用于 JNI 调用。
# native-lib.cpp → 参与编译的源文件。
add_library( # Sets the name of the library.
h264-info
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
h264_info.cpp
)
# 3. 添加ffmpeg相关的so库
# add_library 用于创建一个库目标(静态库、动态库或模块库)。
# add_library(<target> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL|IMPORTED] <source1> <source2> ...)
# 参数说明:
# • <target>:指定库的名称。
# • STATIC:创建静态库(.a 或 .lib)。
# • SHARED:创建动态库(.so 或 .dll)。
# • MODULE:创建无导出符号的动态库,通常用于插件。
# • EXCLUDE_FROM_ALL:表示该库不会默认参与 make all 构建。
# • IMPORTED 用于引用外部库,而不是由当前 CMake 构建的库。
# • <source1> <source2> ...:库的源文件。
# set_target_properties
# set_target_properties 用于设置库或可执行目标的属性,例如库名称、输出目录等。
# set_target_properties(<target> PROPERTIES <property1> <value1> <property2> <value2> ...)
# • OUTPUT_NAME:指定生成的库或可执行文件的名称。
# • ARCHIVE_OUTPUT_DIRECTORY:指定静态库的输出目录。
# • LIBRARY_OUTPUT_DIRECTORY:指定动态库的输出目录。
# • RUNTIME_OUTPUT_DIRECTORY:指定可执行文件的输出目录。
# • IMPORTED_LOCATION 是 IMPORTED 目标的一个属性,它用于指定外部库(已编译的 .so 或 .a 文件)的实际存放路径。
add_library(avutil
SHARED
IMPORTED)
set_target_properties(avutil
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavutil.so)
add_library(swresample
SHARED
IMPORTED)
set_target_properties(swresample
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libswresample.so)
add_library(avcodec
SHARED
IMPORTED)
set_target_properties(avcodec
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavcodec.so)
add_library(avdevice
SHARED
IMPORTED)
set_target_properties(avdevice
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavdevice.so)
add_library(avfilter
SHARED
IMPORTED)
set_target_properties(avfilter
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavfilter.so)
add_library(swscale
SHARED
IMPORTED)
set_target_properties(swscale
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libswscale.so)
add_library(avformat
SHARED
IMPORTED)
set_target_properties(avformat
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libavformat.so)
add_library(postproc
SHARED
IMPORTED)
set_target_properties(postproc
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libpostproc.so)
add_library(x264
SHARED
IMPORTED)
set_target_properties(x264
PROPERTIES IMPORTED_LOCATION
${ffmpeg_lib_dir}/libx264.so)
# 查找代码中使用到的系统库
# find_library() 的作用
# find_library() 是 CMake 提供的一个命令,用于在 系统路径或指定路径中查找已编译的库文件(如 .so、.a、.lib 等),并返回其绝对路径。
# 在 Android NDK、FFmpeg、OpenCV、Oboe 等项目中,find_library() 主要用于查找 预编译库 的路径,并存储到变量中,以便后续 target_link_libraries() 进行链接。
# find_library 常用的库
# 1. log:这是最常用的库之一,提供 Android 原生日志功能,允许你在 C/C++ 代码中使用 __android_log_print 来输出调试信息。
# 2. android:这个库包含了 Native Activity 支持,用于与 Android 操作系统交互,比如处理输入事件、窗口管理等,常在完全使用 native 层开发应用时使用。
# 3. EGL:这是用于 OpenGL ES 的上下文管理的库,在需要使用 OpenGL 渲染时用于创建上下文、配置显示等。
# 4. GLESv1_CM:提供 OpenGL ES 1.x 的接口,已经较少使用,主要用于老旧设备或兼容老项目。
# 5. GLESv2:提供 OpenGL ES 2.0 接口,是很多游戏和图形渲染程序使用的图形库。
# 6. GLESv3:提供 OpenGL ES 3.0 及更高版本接口,适用于需要更高性能图形渲染的应用。
# 7. jnigraphics:这个库可以让 native 层直接访问 Java 层的 Bitmap 图像数据,比如对图片进行像素级别处理。
# 8. OpenSLES:这是 Android 提供的音频播放和录制库,适合对音频处理有较高实时性需求的项目,比如音频播放器或实时通信。
# 9. mediandk:这是 Android NDK 中的媒体处理库,支持音视频的编解码和媒体流的处理,适用于更底层的音视频应用。
# 10. z:也叫 zlib,是一个压缩和解压缩库,很多第三方库(比如 FFmpeg、libpng)都依赖它。
# 11. m:这是标准的数学库,包含了常用的数学函数,比如 sin, cos, sqrt 等,很多图形或科学计算相关的项目中都会用到。
# 12. dl:这是动态链接库的支持库,提供 dlopen, dlsym 等函数,用于在运行时动态加载 .so 文件。
find_library(log-lib log)
#出现 error: undefined symbol: ANativeWindow_fromSurface 说明你的 NDK 环境没有正确链接 android_native_app_glue 或者 android 库。你需要确保:
# 1. 正确包含 android/native_window_jni.h(这通常不是问题)。
# 2. 正确链接 android 库(通常是关键问题)。
find_library(android-lib android)
find_library(z-lib z)
find_library(m-lib m)
find_library(dl-lib dl)
# 指定编译目标库时,cmake要链接的库
# target_link_libraries() 的作用
# target_link_libraries() 是 CMake 中的一个命令,用于将一个或多个库链接到目标(可执行文件或库)。
# 它指定了该目标依赖的外部库文件(例如 .so、.a、.lib 等),从而使得目标在编译和链接时能够正确找到这些库。
target_link_libraries(
# 指定目标库, h264-info 是在上面 add_library 中配置的目标库
h264-info
# 4. 连接 FFmpeg 相关的库
avutil swresample avcodec avdevice avfilter swscale avformat postproc x264
${log-lib} ${android-lib} ${z-lib} ${m-lib} ${dl-lib}
)
3. 配置build.gradle文件
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a','armeabi-v7a'
}
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.compose.ui:ui-desktop:1.7.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
//列表适配器
implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.1.4"
}
4. 检查集成的结果
4-1. h264_info.cpp 的目录参考:
4-2. 新建一个h264_info.cpp 的文件,命名根据自己的需要进行修改,但是记得在cMakeList.txt 文件进行配置好,不然会报错,在cMakeList.txt 文件里面add_library 和 target_link_libraries
add_library( # Sets the name of the library.
h264-info
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
h264_info.cpp
)
target_link_libraries(
# 指定目标库, h264-info 是在上面 add_library 中配置的目标库
h264-info
# 4. 连接 FFmpeg 相关的库
avutil swresample avcodec avdevice avfilter swscale avformat postproc x264
${log-lib} ${android-lib} ${z-lib} ${m-lib} ${dl-lib}
)
4-3. 在h264-info.cpp 文件里面,加上这段JNI的代码,用来检查FFmpeg和x264的集成,也可以根据自己的要求来进行,这里只是作为参考
#include <cstdio>
#include <cstring>
#include <android/log.h>
#include "jni.h"
#include <string>
#include <unistd.h>
#include "iomanip"
#include "sstream"
#include <android/native_window_jni.h>
#include <android/native_window.h>
//由于 FFmpeg 库是 C 语言实现的,告诉编译器按照 C 的规则进行编译
extern "C" {
#include <libavcodec/version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <libavfilter/version.h>
#include <libswresample/version.h>
#include <libswscale/version.h>
#include <libpostproc//version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/version.h>
#include <libavutil/imgutils.h>
#include <x264_config.h>
#include <x264.h>
}
/*
* Class: com_byteflow_learnffmpeg_media_FFMediaPlayer
* Method: native_GetFFmpegVersion
* Signature: ()Ljava/lang/String;
*/
// 查看FFmpeg的核心库版本
extern "C"
JNIEXPORT jstring JNICALL
Java_com_android_ffmpeg_demo_view_ffmpeg_H264EncoderInfoActivity_getFFmpegCoreVersion(JNIEnv *env,
jobject thiz) {
std::string strBuilder = "";
//AV_STRINGIFY 的主要作用是 把宏参数转为字符串常量。
strBuilder.append("libavcodec:").append(AV_STRINGIFY(LIBAVCODEC_VERSION)).append("\n");
strBuilder.append("libavfilter:").append(AV_STRINGIFY(LIBAVFILTER_VERSION)).append("\n");
strBuilder.append("libavdevice:").append(AV_STRINGIFY(LIBAVDEVICE_VERSION)).append("\n");
strBuilder.append("libavformat:").append(AV_STRINGIFY(LIBAVFORMAT_VERSION)).append("\n");
strBuilder.append("libavutil:").append(AV_STRINGIFY(LIBAVUTIL_VERSION)).append("\n");
strBuilder.append("libpostproc:").append(AV_STRINGIFY(LIBPOSTPROC_VERSION)).append("\n");
strBuilder.append("libswresample:").append(AV_STRINGIFY(LIBSWRESAMPLE_VERSION)).append("\n");
strBuilder.append("libswscale:").append(AV_STRINGIFY(LIBSWSCALE_VERSION)).append("\n");
return env->NewStringUTF(strBuilder.c_str());
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_android_ffmpeg_demo_view_ffmpeg_H264EncoderInfoActivity_getH264Version(JNIEnv *env,
jobject thiz) {
const char* ver = X264_POINTVER; // 获取 x264 版本字符串
return env->NewStringUTF(ver);
}
extern "C"
JNIEXPORT jstring JNICALL Java_com_android_ffmpeg_demo_view_ffmpeg_H264EncoderInfoActivity_getH264EncoderInfo
(JNIEnv *env, jobject thiz) {
std::string strBuilder = "";
strBuilder.append("H264 Encoder Info:").append("\n\n");
// 查找 H264 编码器
const AVCodec *codec = avcodec_find_encoder_by_name("libx264");
if (!codec) {
strBuilder.append("H264 encoder not found in this FFmpeg build.").append("\n");
} else {
strBuilder.append("H264 encoder found in this FFmpeg build.").append("\n");
if (codec->name && strlen(codec->name) > 0) {
strBuilder.append("H264 encoder found, name:").append(codec->name).append("\n");
} else {
strBuilder.append("H264 encoder found, but name is empty.").append("\n");
}
if (codec->long_name && strlen(codec->long_name) > 0) {
strBuilder.append("H264 encoder found,long name:").append(codec->long_name).append(
"\n");
} else {
strBuilder.append("H264 encoder found, but long_name is empty.").append("\n");
}
}
// 判断是否是 libx264
if (codec->long_name && strstr(codec->long_name, "x264") && strstr(codec->name, "x264")) {
strBuilder.append("libx264 encoder is available!").append("\n");
} else {
strBuilder.append("libx264 encoder NOT found.").append("\n");
}
return env->NewStringUTF(strBuilder.c_str());
}
4-4. 在Activity里面调用JNI的方法,然后记得在AndroidManifest.xml文件里面配置Activity
//查看H264视频编码器的信息
class H264EncoderInfoActivity : AppCompatActivity() {
companion object {
init {
System.loadLibrary("x264")
//根据自己的需求进行修改命名,但是cMakeList.txt也需要修改
System.loadLibrary("h264-info")
}
}
private lateinit var binding: ActivityH264EncoderInfoBinding
//layout文件里面只是三个TextView,作为显示而已,所以不贴layout的代码了
// 获取x264的版本的信息
private external fun getH264EncoderInfo(): String
// 查看FFmpeg的核心库版本
private external fun getFFmpegCoreVersion(): String
// 获取x264的版本
private external fun getH264Version(): String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityH264EncoderInfoBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tvH264EncoderInfo.text = getH264Version()
binding.tvH264Version.text = getH264EncoderInfo()
binding.tvFfmpegVersion.text = getFFmpegCoreVersion()
}
}
4-5. Android Studio 编译 Apk 输出结果
如果输出下面的信息,说明FFmpeg 集成到Android 平台是成功了,同时H264 已经成功集成到 FFmpeg里面。 为后面的开发做准备。
五、更多FFmpeg编译参数查询链接地址
FFmpeg 移植到 Android 后,如果想要使用FFmpeg 命令的话,可以直接用开源库,例如: MobileFFmpeg,已经帮你封装好 JNI,直接 FFmpeg.execute(cmd) 就能跑。 FFmpegKit(MobileFFmpeg 的后继版本)。如果使用命令的方式,需要的话可以参考前几篇命令的学习。
后面的学习这边是打算学习使用FFmpeg 的API 的方式。
上面的编译是so包的方式,如果是需要.a 静态库的方式,需要自行修改configure得参数,并且正确配置好CmakeList.txt 文件。
上面部分命令的参数可以按照自己的需求去进行添加或者修改。(FFmpeg的configure参数) 上面的环境是Mac系统环境,需要使用Window或者Linux的,请自行查询对应的文档修改参数。 上面知识列举了部分的命令,如果没有合适的话,可以根据自己的需求到该网址进行查询并使用。 FFmpeg文档地址
本人在学习的过程中也遇到不少困难,毕竟是属于自学,可能有很多不足的地方,谢谢大家的支持。