环境
Ubuntu 20.04
先将 ndk、JRTPLIB、JThread下载到本地。
JThread
文件结构
└── JThread
├── CMakeLists.txt
├── ChangeLog
├── LICENSE.MIT
├── README.md
├── TODO
├── builddist.sh
├── cmake
│ └── JThreadConfig.cmake.in
├── doc
│ └── manual.tex
├── pkgconfig
│ ├── CMakeLists.txt
│ └── jthread.pc.in
├── sphinxdoc
│ ├── Makefile
│ ├── README.md
│ └── source
│ ├── _static
│ ├── _templates
│ └── conf.py
└── src
├── CMakeLists.txt
├── jmutex.h
├── jmutexautolock.h
├── jthread.h
├── jthreadconfig.h.in
├── pthread
│ ├── jmutex.cpp
│ └── jthread.cpp
└── win32
├── jmutex.cpp
└── jthread.cpp
编译
在 JThread 目录下创建 build.sh 文件,内容如下:
#!/bin/bash
#ndk的路径,替换为自己的路径
export NDK_PATH=/home/kato/Android/android-ndk-r21e
#将要构建的架构
TARGETS=(arm64-v8a armeabi-v7a x86 x86_64)
#清除build文件夹下的内容
function clean_build() {
if ([ -d build ]); then
echo "prepare to clean cache"
(rm -rf ./build/*)
echo "complete"
else
echo "build is not a directory"
exit 0
fi
}
function prepare_build() {
# 检测是否有Build文件夹,有的话删除文件夹,没有的话创建文件夹
if ([ -e build ]); then
echo "you already have build dir"
clean_build
else
echo "prepare to create dir build"
mkdir build
fi
(
cd build
for dir in ${TARGETS[@]}; do
mkdir $dir
done
)
}
function prepare_target() {
#检测是否有所有的target文件夹,有则删除,没有则创建
if ([ -e target ] && [ -d target ]); then
echo "prepare to clean target"
rm -rf ./target/*
echo "clean target complete"
else
echo "you not have target_dir,we will create it"
mkdir target
fi
}
function create_child_dir() {
if ([ -e target ]); then
(
cd target
mkdir $1
)
else
echo "target is not a dir"
fi
}
function move_to_target() {
pwd
if ([ -e ./build/$1/src/libjthread.a ]); then
echo "prepare move target to ./target/$1"
cp ./build/$1/src/libjthread.a ./target/$1
cp ./build/$1/src/libjthread.so ./target/$1
echo "move to ./target/$1 finished"
else
echo "move error $1"
fi
}
function build_lib() {
cd build/$1
cmake ../.. \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=21 \
-DCMAKE_ANDROID_ARCH_ABI=$1 \
-DCMAKE_ANDROID_NDK=$NDK_PATH \
-DCMAKE_ANDROID_STL_TYPE=c++_static \
-DCMAKE_INSTALL_PREFIX=$(pwd)
}
function create_all_child_dir() {
for dir in ${TARGETS[@]}; do
create_child_dir $dir
echo "$dir created"
done
}
function create_all_target() {
prepare_build
prepare_target
create_all_child_dir
for target in ${TARGETS[@]}; do
(
build_lib $target
make
make install
)
move_to_target $target
done
}
function sbuild() {
echo "-------$1"
case $1 in
"all")
create_all_target
;;
"*") ;;
esac
}
sbuild_list=("all")
function _sbuild() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "${sbuild_list[*]}" -- ${cur}))
return 0
}
complete -o filenames -F _sbuild sbuild
将上述 NDK_PATH 替换成本机的 NDK 路径,修改 TARGETS 变量编译所需 ABI。
使用 bash shell 执行以下指令:
$ source build.sh
$ sbuild all
若编译顺利的话,会在 JThread 目录下生成 build 和 target 文件夹。
JRTPLIB
文件结构
略
大小端问题
Android JNI 中使用的是小端编程,则需要进行小端配置。
在 JRTPLIB 目录下找到 CMakeLists.txt 文件,并找到如下文本:
if (CMAKE_CROSSCOMPILING)
option (JRTPLIB_USE_BIGENDIAN "Target platfo.rm is big endian" OFF)
if (JRTPLIB_USE_BIGENDIAN)
set(RTP_ENDIAN "#define RTP_BIG_ENDIAN")
else (JRTPLIB_USE_BIGENDIAN)
set(RTP_ENDIAN "// Little endian system")
endif (JRTPLIB_USE_BIGENDIAN)
else (CMAKE_CROSSCOMPILING)
test_big_endian(JRTPLIB_BIGENDIAN)
if (JRTPLIB_BIGENDIAN)
set(RTP_ENDIAN "#define RTP_BIG_ENDIAN")
else (JRTPLIB_BIGENDIAN)
set(RTP_ENDIAN "// Little endian system")
endif (JRTPLIB_BIGENDIAN)
endif (CMAKE_CROSSCOMPILING)
option (JRTPLIB_USE_BIGENDIAN "Target platfo.rm is big endian" OFF):OFF 表示使用小端,ON表示使用大端。
编译
步骤同编译 JThread 大体一致,在 JRTPLIB 目录下创建 build.sh 。
#!/bin/bash
#ndk的路径,替换为自己的路径
export NDK_PATH=/home/kato/Android/android-ndk-r21e
#将要构建的架构
TARGETS=(arm64-v8a armeabi-v7a x86 x86_64)
#清除build文件夹下的内容
function clean_build() {
if ([ -d build ]); then
echo "prepare to clean cache"
(rm -rf ./build/*)
echo "complete"
else
echo "build is not a directory"
exit 0
fi
}
function prepare_build() {
# 检测是否有Build文件夹,有的话删除文件夹,没有的话创建文件夹
if ([ -e build ]); then
echo "you already have build dir"
clean_build
else
echo "prepare to create dir build"
mkdir build
fi
(
cd build
for dir in ${TARGETS[@]}; do
mkdir $dir
done
)
}
function prepare_target() {
#检测是否有所有的target文件夹,有则删除,没有则创建
if ([ -e target ] && [ -d target ]); then
echo "prepare to clean target"
rm -rf ./target/*
echo "clean target complete"
else
echo "you not have target_dir,we will create it"
mkdir target
fi
}
function create_child_dir() {
if ([ -e target ]); then
(
cd target
mkdir $1
)
else
echo "target is not a dir"
fi
}
function move_to_target() {
pwd
if ([ -e ./build/$1/src/libjrtp.a ]); then
echo "prepare move target to ./target/$1"
cp ./build/$1/src/libjrtp.a ./target/$1
cp ./build/$1/src/libjrtp.so ./target/$1
echo "move to ./target/$1 finished"
else
echo "move error $1"
fi
}
function build_lib() {
cd build/$1
cmake ../.. \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=21 \
-DCMAKE_ANDROID_ARCH_ABI=$1 \
-DCMAKE_ANDROID_NDK=$NDK_PATH \
-DCMAKE_ANDROID_STL_TYPE=c++_static \
-DCMAKE_INSTALL_PREFIX=$(pwd) \
-DCMAKE_FIND_ROOT_PATH=/home/kato/WorkSpace/CompileJTRP/JThread-master/build/$1
}
function create_all_child_dir() {
for dir in ${TARGETS[@]}; do
create_child_dir $dir
echo "$dir created"
done
}
function create_all_target() {
prepare_build
prepare_target
create_all_child_dir
for target in ${TARGETS[@]}; do
(
build_lib $target
make
make install
)
move_to_target $target
done
}
function sbuild() {
echo "-------$1"
case $1 in
"all")
create_all_target
;;
"*") ;;
esac
}
sbuild_list=("all")
function _sbuild() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -W "${sbuild_list[*]}" -- ${cur}))
return 0
}
complete -o filenames -F _sbuild sbuild
执行的步骤同上。
若编译顺利的话,也会在 JRTPLIB 目录下生成 build 和 target 文件夹。
在 Android 项目中使用
将相应的产物拷贝进相应的文件夹中。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10.2)
include_directories(
.
include
include/jrtplib3
include/jthread
)
aux_source_directory(. SRC)
aux_source_directory(rtsp SRC_RTSP)
list(APPEND SRC
${SRC_RTSP}
)
add_library(
skrtspplayer
SHARED
${SRC}
)
add_library(jrtp STATIC IMPORTED)
set_target_properties(jrtp
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI}/jrtplib3/libjrtp.a)
add_library(jthread STATIC IMPORTED)
set_target_properties(jthread
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI}/jthread/libjthread.a)
target_link_libraries(
skrtspplayer
jrtp
jthread
)
在对应模块的 build.gradle 文件中添加():
android {
defaultConfig {
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.10.2'
}
}
ndkVersion '17.2.4988734'
}
不出意外的话应该就没意外的可以了。
遇到的问题
在 build.sh 中的 function build_lib(): -DCMAKE_ANDROID_STL_TYPE=变量的值有两种:
c++_staticgnustl_static
若使用 gnistl_static ,并且 ndk 版本为 NDK.r.17 或更老的版本则不会出现编译错误。
JThread Android: STL 'gnustl_static' not supported by this NDK.
注意: 笔者第一次使用 android-ndk-r10e 版本进行编译时,成功之后将产物进行导入安卓项目时,使用的 ndk.r.21e 的较新版本时会出现编译错误。尽量使用较新的 ndk 版本进行编译,在导入项目时需注意交叉编译时使用的ndk版本和项目中使用的ndk版本之间的差异。
若使用 c++_static ,则使用的 ndk 版本应该没有限制(未验证)。