导读
对于想要学习音视频开发的android开发者来说,使用NDK对FFmpeg进行交叉编译是一道必须迈过去的坎,网上关于使用NDK对FFmpeg进行交叉编译的教程有很多,
但是不经修改能顺利编译通过的比较少。
其实参照网上的教程不能编译通过很多时候不是人家的教程写的有问题,很多时候更多的是因为环境的差异导致了编译出错,而对于一个入门者来说编译报错了自己却不知道怎么改。
如果对交叉编译不太了解的童鞋们可以参考我之前写的文章:音视频学习之NDK交叉编译基础
NDK编译FFmpeg
今天我们来使用NDK对FFmpeg进行交叉编译并集成到Android Studio中去。
笔者的开发环境是:
- 电脑系统 Mac OS
- NDK 21.1.6352462
- FFmpeg源码5.0.1
1、编写编译脚本
首先我们需要下载好对应版本的NDK和FFmpeg源码,然后我们新建一个编译脚本文件build_ffmpeg_android.sh
:
#清空上次的编译
make clean
#配置你的NDK路径
export NDK=/Users/flyer/Documents/Android/sdk/ndk/21.1.6352462
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
function build_android
{
./configure \
--prefix=$PREFIX \
--disable-postproc \
--disable-debug \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-doc \
--disable-avdevice \
--disable-static \
--enable-shared \
--enable-neon \
--enable-hwaccels \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-decoder=mpeg4_mediacodec \
--enable-hwaccel=h264_mediacodec \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"
make clean
make -j16
make install
echo "============================build android ffmpeg end=========================="
}
#arm64-v8a 参数配置
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
# 函数调用
build_android
注意脚本文件中的注释,要把NDK的路径改为自己的NDK路径,尽可能使用与笔者相同版本的NDK,比如使用NDKr23的版本就可能编译不通过,貌似是因为NDKr23的改掉导致了toolchains下一些列工具找不到了。
2、赋予脚本运行权限
将脚本文件拷贝到下载好的FFmpeg源码目录之下,然后使用命令行chmod +x build_ffmpeg_android.sh
赋予脚本可执行权限。
3、运行编译脚本
运行命令行./build_ffmpeg_android.sh
即可进行编译,如果编译成功可以在FFmpeg的源码目录下的android
文件夹内找到对于的动态库和FFmpeg相关的头文件。
因为上述脚本通过PREFIX=$(pwd)/android/$CPU
配置了编译的输出路径。
注意上述的脚本只编译了arm64-v8a架构的动态库,童鞋们可以尝试下如何编译其他架构的动态库。
集成到Android Studio中
1、 新建Android Studio Native工程
交叉编译成功后,我们通过Android Studio新建一个Native工程,不同的AS版本文件路径可能不一样,比如笔者的项目路径是这样的:
2、拷贝动态库和头文件到工程
然后我们在工程的libs目录下新建一个arm64-v8a
目录,然后将我们编译出来的动态库文件拷贝到新建的arm64-v8a
目录中去;
然后我们在cpp目录下新建一个ffmpeg_include
目录用于存放FFmpeg的头文件,创建好之后我们将交叉编译目录include
下头文件拷贝到新建的ffmpeg_include
中去。
3、修改CMakeLists.txt
下面修改以下CMakeLists.txt配置文件,主要是配置FFmpeg动态库的搜索路径和头文件的搜索路径:
cmake_minimum_required(VERSION 3.10.2)
# 引入FFmpeg的头文件
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg_include)
# 动态链接库或静态链接库的搜索路径
link_directories(${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI})
#找到包含所有的cpp文件
file(GLOB allCpp src/main/cpp/*.*)
add_library(
# 生成的库的名字
flyffmpeg-lib
# 动态库
SHARED
# 源文件
${allCpp} )
target_link_libraries( #目标库
flyffmpeg-lib
# 把ffmpeg的动态库依赖进来
avformat
avcodec
avutil
avfilter
swscale
swresample
# NDK中的log库
log )
注意上面的文件修改了最终生成的动态库的名字flyffmpeg-lib
,所以在使用的时候需要注意一下加载正确的动态库。
4、修改build.gradle
这一步的目的主要是为了将libs目录下so文件打包进APK,我们将app目录下build.gradle修改成:
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.fly.ffmpeg.practice"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags '-std=c++11'
}
// abiFilters
ndk {
abiFilters "arm64-v8a"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
// 指定CMakeLists.txt路径
path file('CMakeLists.txt')
version '3.10.2'
}
}
buildFeatures {
viewBinding true
}
sourceSets {
main {
//将依赖库打进apk,否则可能出现找不到库
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
注意注释部分即可。
5、修改native-lib.cpp
最后我们修改以下native-lib.cpp
,在这里返回FFmpeg的配置:
#include <jni.h>
#include <string>
extern "C"{
#include <libavcodec/avcodec.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_fly_ffmpeg_practice_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
// 返回ffmpeg配置信息
return env->NewStringUTF(avcodec_configuration());
}
运行如果可以能看到正确的配置信息则说明集成成功。
推荐阅读
FFmpeg连载1-开发环境搭建
FFmpeg连载2-分离视频和音频
FFmpeg连载3-视频解码
FFmpeg连载4-音频解码
FFmpeg连载5-音视频编码
FFmpeg连载6-音频重采样
FFmpeg连载8-视频合并以及替换视频背景音乐实战
ffplay调试环境搭建
ffplay整体框架
ffplay数据读取线程
ffplay音视频解码线程
ffplay音视频同步
关注我,一起进步,人生不止coding!!!