在Ubuntu中交叉编译Android可用的JRTPLIB和JThread

268 阅读2分钟

环境

Ubuntu 20.04

android-ndk-r21e

JRTPLIB

JThread

先将 ndkJRTPLIBJThread下载到本地。

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 目录下生成 buildtarget 文件夹。

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 目录下生成 buildtarget 文件夹。

在 Android 项目中使用

image.png

将相应的产物拷贝进相应的文件夹中。

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++_static
  • gnustl_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 版本应该没有限制(未验证)。

参考

移植jrtplib到安卓平台

NDK need gnustl_static