介绍
PJSIP是一个功能强大的开源多媒体通信库,广泛应用于VoIP、SIP协议通信和实时音视频处理等场景。在Android开发中,如果需要实现SIP通话、音视频通信等功能,PJSIP是一个非常优秀的选择。
本文将详细介绍如何在Linux环境下使用Android NDK编译PJSIP库,并将其集成到Android工程中。通过NDK编译可以充分发挥PJSIP的性能优势,同时保证在不同Android设备上的兼容性。
主要内容包括:
- 环境准备和依赖配置
- PJSIP源码获取和配置
- 使用NDK交叉编译OpenSSL
- 使用NDK交叉编译PJSIP
- Android Studio工程集成
- JNI接口封装和调用示例
- 常见问题解决方案
Windows下的NDK用msys2测试过编译,但msys和Windows的NDK的不太兼容,使用Configure时找不到NDK里面的编译器,而且有一些依赖问题。
环境准备
下载源代码和安装依赖
# 安装必要的依赖
sudo apt install swig
在Android Studio中安装NDK,打开Tools -> SDK Manager -> SDK Tools,选择NDK进行安装。(勾选Show Package Details可以选择稳定版)
下载PJSIP源码:https://github.com/pjsip/pjproject/releases
下载oboe:https://github.com/google/oboe/releases(下载产物zip和源代码)
下载OpenSSL源码:https://github.com/openssl/openssl/releases (下载1.1.0到3.4的版本,我下载的是3.4.2)
创建一个目录,分别放置上述源代码和包:
pjproject-build/
├── pjproject-2.15.1/
├── oboe/
│ ├── oboe-1.10.0.aar
│ └── oboe-1.10.0.pom
├── oboe-1.10.0/
└── openssl-3.4.2/
后面我将用PJ_DIR
表示pjproject-2.15.1
的路径,OBOE_DIR
表示oboe
的路径,OBOE_SRC_DIR
表示oboe-1.10.0
的路径,OPENSSL_DIR
表示openssl-3.4.2
的路径。
使用NDK交叉编译OpenSSL
设置ANDROID_NDK_ROOT
环境变量,指向NDK的安装路径,并在$OPENSSL_DIR
目录下执行终端命令:
export ANDROID_NDK_ROOT=~/Android/Sdk/ndk/28.2.13676358
PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
cd $OPENSSL_DIR
./Configure android-arm64 -D__ANDROID_API__=28
make -j$((`nproc` - 2))
注意: 上述配置只会编译出 arm64-v8a 架构的OpenSSL库。如果需要支持其他架构,需要分别编译,这个可以最后做,执行./Configure
前先执行make clean
清除构建,不然可能编译不过:
- armeabi-v7a:
./Configure android-arm -D__ANDROID_API__=28
- x86:
./Configure android-x86 -D__ANDROID_API__=28
- x86_64:
./Configure android-x86_64 -D__ANDROID_API__=28
- arm64-v8a:
./Configure android-arm64 -D__ANDROID_API__=28
执行完Configure会打印如下信息:
Configuring OpenSSL version 3.4.2 for target android-arm64
Using os-specific seed configuration
Created configdata.pm
Running configdata.pm
Created Makefile.in
Created Makefile
Created include/openssl/configuration.h
**********************************************************************
*** ***
*** OpenSSL has been successfully configured ***
*** ***
*** If you encounter a problem while building, please open an ***
*** issue on GitHub <https://github.com/openssl/openssl/issues> ***
*** and include the output from the following command: ***
*** ***
*** perl configdata.pm --dump ***
*** ***
*** (If you are new to OpenSSL, you might want to consult the ***
*** 'Troubleshooting' section in the INSTALL.md file first) ***
*** ***
**********************************************************************
最终会在$OPENSSL_DIR
目录下生成libcrypto.so
和libssl.so
两个库文件。
在$OPENSSL_DIR
目录下创建lib
目录,并将上述两个库文件复制进去:
mkdir lib
cp libcrypto.so libssl.so lib/
NOTE: 测试过Configure时使用-static
选项,编译会报错
使用NDK交叉编译OBOE
cd $OBOE_SRC_DIR
export ANDROID_NDK=$ANDROID_NDK_ROOT
./build_all_android.sh
可以在build里面看到不同架构的liboboe.so
文件,最后一步会用到这些库文件。
构建PJSIP
在$PJ_DIR
目录中创建pjlib/include/pj/config_site.h
文件,内容如下:
/* Activate Android specific settings in the 'config_site_sample.h' */
#define PJ_CONFIG_ANDROID 1
#include <pj/config_site_sample.h>
#define PJMEDIA_HAS_VIDEO 1
根据之前配置好的$OPENSSL_DIR
和$OBOE_DIR
,构建PJSIP:
cd $PJ_DIR
export ANDROID_NDK_ROOT=~/Android/Sdk/ndk/28.2.13676358
./configure-android -with-ssl=$OPENSSL_DIR --with-oboe=$OBOE_DIR
配置的时候会打印如下信息:
configure-android: APP_PLATFORM not specified, using latest
configure-android: TARGET_ABI not specified, using arm64-v8a
如果希望配置其他平台,可以指定APP_PLATFORM
和TARGET_ABI
,例如:
APP_PLATFORM=android-28 TARGET_ABI=armeabi-v7a ./configure-android -with-ssl=$OPENSSL_DIR --with-oboe=$OBOE_DIR --with-ssl=$OPENSSL_DIR
然后编译:
make dep -j$((`nproc` - 2))
make -j$((`nproc` - 2))
然后再编译swig生成的Java接口:
export JAVA_HOME=[安卓studio路径]/jbr/bin
cd pjsip-apps/src/swig
make #这里最好不要用-j参数
注意,这一步和上一步configure-android
的APP_PLATFORM
和TARGET_ABI
是绑定的,只会编译对应的架构的库。
NOTE: 备注,安卓SDK27不能编译,至少要28,编译时会报错:
../src/pjmedia-codec/and_vid_mediacodec.cpp:1065:5: error: 'AMediaCodec_setAsyncNotifyCallback' is unavailable: introduced in Android 28
1065 | AMediaCodec_setAsyncNotifyCallback(and_media_data->enc, async_cb,
| ^
/home/jiang/Android/Sdk/ndk/28.2.13676358/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/media/NdkMediaCodec.h:499:16: note: 'AMediaCodec_setAsyncNotifyCallback' has been explicitly marked unavailable here
499 | media_status_t AMediaCodec_setAsyncNotifyCallback(
| ^
../src/pjmedia-codec/and_vid_mediacodec.cpp:1067:5: error: 'AMediaCodec_setAsyncNotifyCallback' is unavailable: introduced in Android 28
1067 | AMediaCodec_setAsyncNotifyCallback(and_media_data->dec, async_cb,
| ^
/home/jiang/Android/Sdk/ndk/28.2.13676358/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/media/NdkMediaCodec.h:499:16: note: 'AMediaCodec_setAsyncNotifyCallback' has been explicitly marked unavailable here
499 | media_status_t AMediaCodec_setAsyncNotifyCallback(
| ^
../src/pjmedia-codec/and_aud_mediacodec.cpp:1096:5: error: 'AMediaCodec_setAsyncNotifyCallback' is unavailable: introduced in Android 28
1096 | AMediaCodec_setAsyncNotifyCallback(codec_data->enc, async_cb, codec_data);
| ^
/home/jiang/Android/Sdk/ndk/28.2.13676358/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/media/NdkMediaCodec.h:499:16: note: 'AMediaCodec_setAsyncNotifyCallback' has been explicitly marked unavailable here
499 | media_status_t AMediaCodec_setAsyncNotifyCallback(
| ^
../src/pjmedia-codec/and_aud_mediacodec.cpp:1097:5: error: 'AMediaCodec_setAsyncNotifyCallback' is unavailable: introduced in Android 28
1097 | AMediaCodec_setAsyncNotifyCallback(codec_data->dec, async_cb, codec_data);
复制其他依赖库
需要的库文件:
- OpenSSL的
libcrypto.so
和libssl.so
- OBOE的
liboboe.so
- PJSIP的
libpjsua2.so
和libc++_shared.so
如果需要多种架构,可以按照之前的教程编译不同的库
最终复制到$PJ_DIR/pjsip-apps/src/swig/java/android/pjsua2/src/main/jniLibs/
的不同架构目录下,例如:
jniLibs/
├── arm64-v8a/
├── armeabi-v7a/
├── x86/
└── x86_64/
然后这个pjsua2可以复制到自己的工程里面用include的方式以子项目引入
最终打包
已经上传GitHub,地址是:https://github.com/helywin/pjsua2