wiki:Getting-Started/Android

Version 53 (modified by ming, 10 years ago) (diff)

--

Getting Started: Building for Android

  1. Requirements
  2. Build Preparation
  3. Building PJSIP
  4. Video Support
    1. Features
    2. Requirements
    3. Configuring
    4. Adding Video Capture Device to Your Application
    5. Using Video API
  5. Setting up the target device
  6. Building and running pjsua2 sample application
    1. Log output
  7. Creating your own application
  8. Pjsua application with telnet interface
  9. Debugging native code with Eclipse
  10. OpenSSL Support
  11. Important Issue(s) when Developing Android Apps
    1. Unable to Make or Receive Call (Problem with sending and receiving large (INVITE) requests over TCP)
    2. Garbage Collector May Crash Your App (Pjsua2 API)
    3. OpenSLES audio device deadlock upon shutdown
    4. Bad audio recording quality on some devices
    5. UnsatisfiedLinkError exception with "cannot locate 'rand'" message
  12. Other Android projects

Android target is available since 2.2.

Requirements

  • Besides the Android SDK, you will also need the Android NDK (minimum version r8b).
  • Optional if you want to build and and run the sample application PJSUA:
    • SWIG (minimum version 2.0.5)
    • Eclipse with ADT Plugin.
    • 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. Get the source code from repository, if you haven't already. This tutorial applies to PJSIP version 2.2 and above (or the 2.1 from SVN trunk dated 2013/04/24 or later).
  2. Set your config_site.h to the following:
    /* Activate Android specific settings in the 'config_site_sample.h' */
    #define PJ_CONFIG_ANDROID 1
    #include <pj/config_site_sample.h>
    
    To also enable video, append this into config_site.h:
    #define PJMEDIA_HAS_VIDEO 1
    

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

Notes:

  • to build for other targets, e.g: arm64 (arm64-v8a), x86, or armv7, instead of just './configure-android', specify the target arch in TARGET_ABI and run it, for example:
    TARGET_ABI=armeabi-v7a ./configure-android --use-ndk-cflags
    
  • for some targets, there may be error like
    error: undefined reference to '__stack_chk_fail_local'
    
    this can be fixed by adding -fno-stack-protector into CFLAGS, e.g: via user.mak file.
  • for 64bit targets (e.g: arm64-v8a), it might be necessary to do some changes, which is explained in detail in #1803
  • the ./configure-android is a wrapper that calls the standard ./configure script with settings suitable for Android target.
  • you may pass standard ./configure options to this script too.
  • for more info, run ./configure-android --help
  • other customizations are similar to what is explained in 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 (recommended)

Provides video codec H.264, alternatively you can use ffmpeg (together with libx264).

  1. Follow the instructions in ticket #1758
  2. Copy all library .so files into your Android application project directory, for example:
    cp /Users/me/openh264-1.0.0/android/*.so /Users/me/pjproject-2.0/pjsip-apps/src/swig/java/android/libs/armeabi
    

libyuv (recommended)

Provides format conversion and video manipulation, alternatively you can use ffmpeg.

  1. Follow the instructions in ticket #1776
  2. 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 here and successfully built with Android NDK r10.
  2. 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
    

Configuring

Sample invocation of ./configure-android:

$ ./configure-android --with-openh264=/Users/me/openh264-1.0.0/android --with-libyuv=/Users/me/libyuv-android/jni

Make sure openh264 and libyuv are detected by ./configure-android:

...
Using OpenH264 prefix... /Users/me/openh264-1.0.0/android
checking OpenH264 availability... ok
Using libyuv prefix... /Users/me/libyuv-android/jni
checking for I420Scale in -lyuv... yes
...

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 Video User's Guide.

Setting up the target device

To run or debug application (such as the sample applications below), first we need to setup the target device:

Building and running pjsua2 sample application

A sample application using pjsua2 API with SWIG Java binding, is located under pjsip-apps/src/swig/java/android. It is not built by default, and you need SWIG to build it.

Follow these steps to build pjsua2 sample application:

  1. Make sure SWIG is in the build environment PATH.
  2. 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
    
  3. Make sure any library dependencies are copied to $PJDIR/pjsip-apps/src/swig/java/android/libs/armeabi, e.g: libyuv.so & libopenh264.so for video support.
  4. Create Android project from pjsua. In Eclipse:
    1. From menu: File --> New --> Project
    2. Select Android Project from Existing Code, press Next
    3. In Root Directory, put the location of pjsua2 source code (i.e. $PJDIR/pjsip-apps/src/swig/java/android) and press Finish
  5. You may need to select different Android SDK than what is configured in pjsua2. You can do this from the project's Properties.
  6. Run it.

Log output

The pjsua2 sample application will write log messages to LogCat window (to show LogCat window, from Eclipse menu: Window --> Show View --> Other --> type LogCat)

Creating your own application

For developing Android application, you should use pjsua2 API whose Java interface available via SWIG Java binding.

Note that this has only been tested using Eclipse with ADT, however it might also be applicable for Android Studio.

  1. First, build pjproject libraries as described above.
  2. Also build pjsua2 sample application as described above, this step is required to generate the pjsua2 Java interface and the native library.
  3. Create Android application outside the PJSIP sources for your project.
  4. Get pjsua2 Java interface and native library from pjsua2 sample application:
    1. Copy pjsua2 Java interface files from pjsip-apps/src/swig/java/android/src to your project's src folder:
      $ cd $YOUR_PROJECT_DIR
      $ cp -r $PJSIP_DIR/pjsip-apps/src/swig/java/android/src .
      
      # Cleanup excess pjsua2 application sources.
      $ rm -r src/org/pjsip/pjsua2/app
      
    2. Copy native library libpjsua2.so from pjsip-apps/src/swig/java/android/libs to your project's libs folder:
      $ cd $YOUR_PROJECT_DIR
      $ cp -r $PJSIP_DIR/pjsip-apps/src/swig/java/android/libs .
      
  5. Start writing your application, please check pjsua2 docs for reference.

Pjsua application with telnet interface

There is also the usual 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 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/Android.mk file.
  2. Run ndk-build from directory $PJDIR/pjsip-apps/src/pjsua/android (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
    $ ndk-build
    
  3. Create Android project from pjsua. In Eclipse:
    1. From menu: File --> New --> Project
    2. Select Android Project from Existing Code, press Next
    3. In Root Directory, put the location of pjsua source code (i.e. $PJDIR/pjsip-apps/src/pjsua/android) and press Finish
  4. You may need to select different Android SDK than what is configured in pjsua. You can do this from the project's Properties.
  5. Run it.
  6. You will see telnet instructions on the device's screen. Telnet to this address to operate the application. See PJSUA CLI Manual for command reference.

Debugging native code with Eclipse

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.
  2. 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.
  3. Enable NDK plugin for Eclipse, check this and follow the instructions.
    • CDT can also be fetched from 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.
  4. 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
    // ...
    
  5. 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.

OpenSSL Support

  1. Build OpenSSL (tested with OpenSSL 1.0.2a) for Android. The instruction provided here is specifically for arm64. For other architectures, modify accordingly. Please visit this page for reference and some examples. Note: change the NDK path below.
    cd openssl-1.0.2a
    
    export NDK=[your_android_ndk_path]/android-ndk-r10d
    
    $NDK/build/tools/make-standalone-toolchain.sh --platform=android-21 --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 libs folder:
    mkdir libs
    cp lib*.a libs/
    
  2. 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
    ...
    
  3. Build the libraries:
    make dep && make
    
    If you encounter linking errors, you need to add this in user.mak:
    export LIBS += "-ldl -lz"
    

Important Issue(s) when Developing Android Apps

Unable to Make or Receive Call (Problem with sending and receiving large (INVITE) requests over TCP)

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 FAQ here.

Garbage Collector May Crash Your App (Pjsua2 API)

There are two problems with Java garbage collector (gc):

  • it delays the destruction of Java objects (including pjsua2 objects), causing the code in object's destructor to be executed out of order
  • the gc operation may be run on different thread, not previously registered to PJLIB

Due to problems above, application MUST immediately destroy pjsua2 objects using object's delete() method, instead of relying on the gc to clean up the object.

For example, to delete an Account:

acc.delete();

OpenSLES audio device deadlock upon shutdown

As reported in 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

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);

UnsatisfiedLinkError exception with "cannot locate 'rand'" message

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 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

Other Android projects

Also have a look at the following PJSIP Android project: