= Getting Started: Building for Android = [[TracNav(Getting-Started/TOC)]] [[PageOutline(2-3,,inline)]] == Requirements == * Besides the [http://developer.android.com/sdk/index.html Android SDK], you will also need the [http://developer.android.com/tools/sdk/ndk/index.html Android NDK]. * Optional if you want to build and and run the sample applications (i.e: pjsua2 and pjsua): * [http://www.swig.org/download.html SWIG] * ~~Eclipse with [http://developer.android.com/tools/sdk/eclipse-adt.html ADT Plugin]~~ (deprecated since 2.6) * [https://developer.android.com/studio/index.html Android Studio] * Telnet application to interact with PJSUA command line. If you are not familiar with telnet, please find a tutorial suitable for your development platform. == Build Preparation == 1. [wiki:Getting-Started/Download-Source#GettingfromSubversiontrunk Get the source code from repository], if you haven't already. 2. Set your [wiki:Getting-Started/Build-Preparation config_site.h] to the following: {{{ /* Activate Android specific settings in the 'config_site_sample.h' */ #define PJ_CONFIG_ANDROID 1 #include }}} == Building PJSIP == Just run: {{{ $ cd /path/to/your/pjsip/dir $ export ANDROID_NDK_ROOT=/path_to_android_ndk_dir $ ./configure-android $ make dep && make clean && make }}} > Tips: On MinGW32/MSys, use absolute path format {{{D:/path/to/android/ndk}}} instead of {{{/D/path/to/android/ndk}}} for setting {{{ANDROID_NDK_ROOT}}}. This will build {{{armeabi}}} target, to build for other targets such as {{{arm64-v8a, armeabi-v7a, x86}}} see next section. === Building for other architectures === * Make sure to cleanup all existing binary and intermediate files, e.g: {{{ $ cd /path/to/your/pjsip/dir $ make clean # cleanup pjsua sample app $ cd pjsip-apps/src/pjsua/android/jni $ make clean # also cleanup pjsua2 sample app (SWIG) $ cd /path/to/your/pjsip/dir $ cd pjsip-apps/src/swig $ make clean }}} * Specify the target arch in {{{TARGET_ABI}}} and run it with {{{--use-ndk-cflags}}}, for example: {{{ TARGET_ABI=arm64-v8a ./configure-android --use-ndk-cflags }}} Also you should adjust [https://developer.android.com/ndk/guides/abis.html#gc Application.mk] and [https://developer.android.com/ndk/guides/abis.html#am library packaging path] (see also #1803). * If your app is intended to run on older Android devices, you may need to set {{{APP_PLATFORM}}} to {{{android-20}}} or lower, please check [#failloadlib this issue]. === Notes === * The {{{./configure-android}}} is a wrapper that calls the standard {{{./configure}}} script with settings suitable for Android target. Standard {{{./configure}}} options should be applicable to this script too. Please check {{{./configure-android --help}}} for more info. * Other customizations are similar to what is explained in [wiki:Getting-Started/Autoconf Building with GNU] page. == Video Support == === Features === Video on Android will be supported since PJSIP version 2.4. It has the following features: - native capture - native OpenGL ES 2.0 renderer (requires Android 2.2 (API level 8) or higher). - H.264 codec (via OpenH264 library, see below) === Requirements === ==== OpenH264 (this is recommended if you need H264 codec) ==== Provides video codec H.264, alternatively you can use ffmpeg (together with libx264). 1. Follow the instructions in ticket #1947 (or ticket #1758 if you use PJSIP before version 2.6). 1. Copy all library .so files into your Android application project directory, for example: {{{ cp /Users/me/openh264/android/*.so /Users/me/pjproject-2.0/pjsip-apps/src/swig/java/android/libs/armeabi }}} ==== libvpx (if you need VP8 or VP9 codec) ==== [https://www.webmproject.org/code/ libvpx]. ==== libyuv (recommended) ==== Provides format conversion and video manipulation, alternatively you can use ffmpeg. If you are using 2.5.5 or newer, libyuv should be built and enabled automatically, see ticket #1937 for more info. If you are using 2.5.1 or older: 1. Follow the instructions in ticket #1776 1. Copy all library .so files into your Android application project directory, for example: {{{ cp /Users/me/libyuv-android/*.so /Users/me/pjproject-2.0/pjsip-apps/src/swig/java/android/libs/armeabi }}} ==== ffmpeg (optional) ==== Provides format conversion and video manipulation as well as video codecs: H.264 (together with libx264) and H263P/H263-1998. 1. Follow the instructions from the web on how to build ffmpeg for android. We followed the instructions provided [http://www.roman10.net/how-to-build-ffmpeg-with-ndk-r9/ here] and successfully built with Android NDK !r10. 1. Copy all library .so files into your Android application project directory, for example: {{{ cp /Users/me/src/ffmpeg-2.5/android/arm/lib/*.so /Users/me/pjproject-2.0/pjsip-apps/src/swig/java/android/libs/armeabi }}} ==== AMediaCodec, native Android codecs (experimental) ==== This is available since 2.11, it provides H264, VP8, and VP9 video codecs (also AMR-NB & AMR-WB audio codecs). Please check [https://github.com/pjsip/pjproject/pull/2552 here] for how to enable it. === Configuring === To enable video, append this into {{{config_site.h}}}: {{{ #define PJMEDIA_HAS_VIDEO 1 }}} Specify third-party video libraries when invoking {{{./configure-android}}}, e.g: {{{ $ ./configure-android --with-openh264=/Users/me/openh264/android }}} Make sure openh264 is detected by {{{./configure-android}}}: {{{ ... Using OpenH264 prefix... /Users/me/openh264/android checking OpenH264 availability... ok ... }}} '''Note''': if you use PJSIP before version 2.6, you need to specify external libyuv via the configure script param {{{--with-libyuv}}}, check ticket #1776 for more info. === Adding Video Capture Device to Your Application === Copy the java part of PJSIP Android capture device to the application's source directory: {{{ cp pjmedia/src/pjmedia-videodev/android/PjCamera*.java [your_app]/src/org/pjsip/ }}} === Using Video API === Please check [wiki:Video_Users_Guide Video User's Guide]. === Video capture orientation support === To send video in the proper orientation (i.e. head always up regardless of the device orientation), application needs to do the following: 1. Setup the application to get orientation change notification (by adding {{{android:configChanges="orientation|keyboardHidden|screenSize"}}} in the application manifest file and override the callback {{{onConfigurationChanged()}}}). 2. Inside the callback, call PJSUA2 API {{{VidDevManager.setCaptureOrient()}}} to set the video device to the correct orientation. For sample usage, please refer to pjsua2 sample app. Ticket #1861 explains this feature in detail. == OpenSSL Support == 1. Build OpenSSL (tested with OpenSSL 1.0.2s) for Android. The instruction provided here is specifically for arm64. For other architectures, modify accordingly. Please visit [http://stackoverflow.com/questions/11929773/compiling-the-latest-openssl-for-android this page] for reference and some examples. Note: you need to change the NDK path and the API platform level below. {{{ cd openssl-1.0.2s export NDK=[your_android_ndk_path]/android-ndk-r17b $NDK/build/tools/make-standalone-toolchain.sh --platform=android-28 --toolchain=aarch64-linux-android-4.9 --install-dir=`pwd`/android-toolchain-arm64 export TOOLCHAIN_PATH=`pwd`/android-toolchain-arm64/bin export TOOL=aarch64-linux-android export NDK_TOOLCHAIN_BASENAME=${TOOLCHAIN_PATH}/${TOOL} export CC=$NDK_TOOLCHAIN_BASENAME-gcc export CXX=$NDK_TOOLCHAIN_BASENAME-g++ export LINK=${CXX} export LD=$NDK_TOOLCHAIN_BASENAME-ld export AR=$NDK_TOOLCHAIN_BASENAME-ar export RANLIB=$NDK_TOOLCHAIN_BASENAME-ranlib export STRIP=$NDK_TOOLCHAIN_BASENAME-strip export ARCH_FLAGS= export ARCH_LINK= export CPPFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 " export CXXFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 -frtti -fexceptions " export CFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 " export LDFLAGS=" ${ARCH_LINK} " ./Configure android make }}} Then copy the libraries into lib folder: {{{ mkdir lib cp lib*.a lib/ }}} 1. Specify OpenSSL location when running {{{configure-android}}}, for example (with Bash): (change the openssl path folder) {{{ TARGET_ABI=arm64-v8a ./configure-android --use-ndk-cflags --with-ssl=[your_openssl_path]/openssl-1.0.2a }}} And check that OpenSSL is detected by the configure script: {{{ ... checking for OpenSSL installations.. checking openssl/ssl.h usability... yes checking openssl/ssl.h presence... no aconfigure: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor! aconfigure: WARNING: openssl/ssl.h: proceeding with the compiler's result checking for openssl/ssl.h... yes checking for ERR_load_BIO_strings in -lcrypto... yes checking for SSL_library_init in -lssl... yes OpenSSL library found, SSL support enabled ... }}} 1. Build the libraries: {{{ make dep && make }}} If you encounter linking errors, you need to add this in user.mak: {{{ export LIBS += "-ldl -lz" }}} {{{#!comment ###Is this still relevant? === Issue with using OpenSSL on Android M === As reported [https://plus.google.com/+AndroidDevelopers/posts/bLqbRJ5o5id here], Google has moved from OpenSSL to BoringSSL, and hence app that links against platform libraries (such as libcrypto.so) will likely break. On Android M, you might get error like this: {{{ java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "OPENSSL_add_all_algorithms_noconf" referenced by "/data/app/someapp/lib/arm/libMyAppApp-arm-android-eabi.so" }}} The solution would be to include the OpenSSL library in your project/APK. }}} == Trying our sample application and creating your own === Setting up the target device === To run or debug application (such as the sample applications below), first we need to setup the target device: - using virtual device: http://developer.android.com/tools/devices/index.html - using real device: http://developer.android.com/tools/device.html === Building and running pjsua2 sample application === #pjsua2 A sample application using [http://www.pjsip.org/docs/book-latest/html/index.html pjsua2 API] with SWIG Java binding, is located under {{{pjsip-apps/src/swig/java/android}}}. It is not built by default, and you need [http://www.swig.org/download.html SWIG] to build it. Follow these steps to build pjsua2 sample application: 1. Make sure SWIG is in the build environment PATH. 1. Run {{{make}}} from directory {{{$PJDIR/pjsip-apps/src/swig}}} (note that the Android NDK root should be in the PATH), e.g: {{{ $ cd /path/to/your/pjsip/dir $ cd pjsip-apps/src/swig $ make }}} This step should produce: - native library {{{libpjsua2.so}}} in {{{pjsip-apps/src/swig/java/android/app/src/main/jniLibs/armeabi}}} - note: if you are building for other target ABI, you'll need to manually move {{{libpjsua2.so}}} to the appropriate target ABI directory, e.g: {{{jniLibs/armeabi-v7a}}}, please check [https://developer.android.com/ndk/guides/abis.html#am here] for target ABI directory names. - pjsua2 Java interface (a lot of {{{.java}}} files) in {{{pjsip-apps/src/swig/java/android/app/src/main/java/org/pjsip/pjsua2}}} 1. Make sure any library dependencies are copied to {{{pjsip-apps/src/swig/java/android/app/src/main/jniLibs/armeabi}}} (or the appropriate target ABI directory), e.g: {{{libopenh264.so}}} for video support. 1. Open pjsua2 app project in Android Studio, it is located in {{{pjsip-apps/src/swig/java/android}}}. 1. Run it. ==== Log output ==== The pjsua2 sample application will write log messages to '''!LogCat''' window. === Creating your own application === #create-own-app For developing Android application, you should use [http://www.pjsip.org/docs/book-latest/html/index.html pjsua2 API] whose Java interface available via SWIG Java binding. 1. First, build {{{pjproject}}} libraries as described above. 1. Also build {{{pjsua2 sample application}}} as described above, this step is required to generate the pjsua2 Java interface and the native library. 1. Create Android application outside the PJSIP sources for your project. 1. Get pjsua2 Java interface and native library from pjsua2 sample application: 1. Copy pjsua2 Java interface files from {{{pjsip-apps/src/swig/java/android/app/src/main/java}}} to your project's {{{app/src/main/java}}} folder, e.g: {{{ $ cd $YOUR_PROJECT_DIR/app/src/main/java $ cp -r $PJSIP_DIR/pjsip-apps/src/swig/java/android/app/src/main/java . # Cleanup excess pjsua2 application sources. $ rm -r org/pjsip/pjsua2/app }}} 1. Copy native library {{{libpjsua2.so}}} from {{{pjsip-apps/src/swig/java/android/app/src/main/jniLibs}}} to your project's {{{app/src/main/jniLibs}}} folder: {{{ $ cd $YOUR_PROJECT_DIR/app/src/main/jniLibs $ cp -r $PJSIP_DIR/{pjsip-apps/src/swig/java/android/app/src/main/jniLibs . }}} 1. Start writing your application, please check [http://www.pjsip.org/docs/book-latest/html/index.html pjsua2 docs] for reference. === Pjsua sample application with telnet interface === #pjsua There is also the usual [http://www.pjsip.org/pjsua.htm pjsua] with telnet command line user interface, which is located under {{{pjsip-apps/src/pjsua/android}}}. It is not built by default and you need [http://www.swig.org/download.html SWIG] to build it. Application flow and user interface are handled mainly in the native level, so it doesn't use pjsua2 API with Java interface. Follow these steps to build pjsua: 1. Make sure SWIG is in the build environment PATH. - Alternatively, update SWIG path in {{{$PJDIR/pjsip-apps/src/pjsua/android/jni/Makefile}}} file. 1. Run {{{make}}} from directory {{{$PJDIR/pjsip-apps/src/pjsua/android/jni}}} (note that the Android NDK root should be in the PATH), e.g: {{{ $ cd /path/to/your/pjsip/dir $ cd pjsip-apps/src/pjsua/android/jni $ make }}} 1. Open pjsua2 app project in Android Studio, it is located in {{{pjsip-apps/src/pjsua/android}}}. 1. Run it. 1. You will see telnet instructions on the device's screen. Telnet to this address to operate the application. See [wiki:PJSUA-CLI PJSUA CLI Manual] for command reference. {{{#!comment == Debugging native code with Eclipse == #debug-native Here are the steps for debugging PJSIP native code using Eclipse: 1. Build PJSIP with debugging enabled, e.g: insert {{{CFLAGS += -g}}} into {{{user.mak}}} in PJSIP root directory. 1. Make sure that the JNI part of the application is built using {{{ndk-build}}}. For reference, check pjsua's {{{Android.mk}}} build config in {{{pjsip-apps/src/pjsua/android/jni}}}, it contains sample of how to import PJSIP build settings (build search paths, build flags, etc) and SWIG invocation. 1. Enable NDK plugin for Eclipse, check [http://tools.android.com/recent/usingthendkplugin this] and follow the instructions. - CDT can also be fetched from [http://www.eclipse.org/cdt/downloads.php CDT download page], normally Eclipse for C/C++ will have CDT installed, just make sure that the CDT version is 7.0.2 or newer. 1. It is recommended to introduce delay (about 5 seconds) in the application code between loading the native code library and calling any native functions (to be debugged), e.g: {{{ try { System.loadLibrary("some_native_lib.so"); } catch (UnsatisfiedLinkError e) { return -1; } // Wait for GDB init if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { try { Thread.sleep(5000); } catch (InterruptedException e) { } } // Start calling native functions here // ... }}} 1. Load the PJSIP project to Eclipse (if not yet), and try put breakpoint anywhere in the PJSIP code before launching the Android application debug configuration in Eclipse. }}} == Important Issue(s) when Developing Android Apps == #issues === Unable to Make or Receive Call (Problem with sending and receiving large (INVITE) requests over TCP) === #invite The issue is documented in #1488. The solution is to try using port other than 5060 in *both* client and server, and/or reducing the SIP message size by following our [https://trac.pjsip.org/repos/wiki/FAQ#sip-msg-size FAQ here]. === Garbage Collector May Crash Your App (Pjsua2 API) === #gc Please check this pjsua2 book page about [http://www.pjsip.org/docs/book-latest/html/intro_pjsua2.html#problems-with-garbage-collection problems with GC]. === OpenSLES audio device deadlock upon shutdown === #opensl As reported in [https://groups.google.com/forum/#!topic/android-ndk/G7dLKAGGL28 Android NDK forum], when shutting down OpenSLES sound device backend, it may block forever: {{{ W/libOpenSLES(6434): frameworks/wilhelm/src/itf/IBufferQueue.c:57: pthread 0x5fce71c0 (tid 6670) sees object 0x5fcd0080 was locked by pthread 0x5f3a2cb0 (tid 6497) at frameworks/wilhelm/src/itf/IObject.c:411 }}} Currently, the only workaround is to use PJSIP's Android JNI sound device instead (one way to do this is by defining PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI to 1 and PJMEDIA_AUDIO_DEV_HAS_OPENSL to 0). === Bad audio recording quality on some devices === #badrecord Reported that audio quality recorded on the microphone is bad and the speed is twice what it should be, it only happens on some devices. It could be fixed by setting audio mode via {{{AudioManager}}} to {{{MODE_IN_COMMUNICATION}}} in the application, e.g: {{{ AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int original_mode = am.getMode(); /* Set audio mode before using audio device, for example before making/answering a SIP call */ am.setMode(AudioManager.MODE_IN_COMMUNICATION); ... /* Restore back to the original mode after finished with audio device */ am.setMode(original_mode); }}} === Build failure or configure-android error with "--use-ndk-cflags" using android-ndk-r13 or later === #err_ndkr13 Release notes to !r13 stated that NDK_TOOLCHAIN_VERSION now defaults to Clang, resulting error on configure-android which expects gcc: {{{ configure-android error: compiler not found, please check environment settings (TARGET_ABI, etc) }}} It can also cause PJSIP to fail to build on android-ndk-r15. As a workaround, you can specify NDK_TOOLCHAIN_VERSION (to 4.9) to use gcc. {{{ NDK_TOOLCHAIN_VERSION=4.9 TARGET_ABI=armeabi-v7a ./configure-android --use-ndk-cflags }}} === !UnsatisfiedLinkError exception with "cannot locate 'rand'" message === #failloadlib This may occur when running pjsua2 sample app: {{{ Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lorg/pjsip/pjsua2/app/MyApp; ... java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1285]: 37 cannot locate 'rand'... }}} As described [http://stackoverflow.com/questions/27338318/cannot-load-library-reloc-library1285-cannot-locate-rand here], this happens if you've built your native components with the android-21 target, but are trying to run it on a device with an older Android version, so re-run configure with APP_PLATFORM set to lower platform version, e.g: {{{ APP_PLATFORM=android-19 ./configure-android }}} === !UnsatisfiedLinkError exception with "Native method not found: org.pjsip.pjsua2.pjsua2JNI.swig_module_init" message === #no_swig_module_init The reason might be: - The Java interface files and/or the native library wasn't copied to the appropriate folder. Please have a look at [#create-own-app here] - You built the lib using newer APP_PLATFORM on an older device. Please have a look at [#failloadlib here] {{{#!comment === Error configure-android with "--use-ndk-cflags" using android-ndk-r11 or later === #err_ndkr11 Starting from !r11, Android NDK has removed the sample folder and changed the toolchain version, hence the configure-android with "--use-ndk-cflags" will fail with this error: {{{ samples/hello-jni: No such file or directory. Stop. configure-android error: failed to run ndk-build, check ANDROID_NDK_ROOT env var }}} Please apply the fix in ticket #1950. }}} === A review of Android audio output latency === #latency For a review of Android audio output latency, please have a look at ticket #1841. == Other Android projects == Also have a look at the following PJSIP Android project: * [http://code.google.com/p/csipsimple/ csipsimple] project, an Android port of pjsip.