wiki:Getting-Started/iPhone

Version 89 (modified by ming, 8 years ago) (diff)

--

Getting Started: Building for Apple iPhone, iPad and iPod Touch

  1. Features
  2. Requirements
  3. Build Preparation
  4. Building PJSIP
    1. Supporting multiple architectures (armv6, armv7, armv7s, arm64, and so on)
    2. Simulator
  5. Video Support
    1. Features
    2. Requirements
    3. Configuring
    4. Video capture orientation support
  6. OpenSSL Support
  7. Common problems
    1. Guide to accept calls in the background after kCFStreamNetworkServiceTypeVoIP is deprecated (iOS 9)
    2. Audio session management issue (iOS7)
    3. Crash after calling PJLIB APIs using Grand Central Dispatch (GCD)
    4. Audio lost or other issues with interruption (by a phone call or an alarm), headset plug/unplug, or Bluetooth input
    5. SIP transport keepalive while in background
  8. Other Problems (problem specific to a particular iOS version/device/simulator)
    1. Unable to accept incoming call in background mode
    2. Audio issue (no audio) (iOS7)
    3. Audio issues (inability to mute, volume control center synchronization, playback reduced volume) (iOS7)
    4. Unable to support Bluetooth input
    5. Sound not working in the simulator
    6. List of Issues
  9. Other iPhone Projects

Apple iOS target is supported by PJSIP version 1.7 and later.

Features

Some of the features of the iPhone port:

  • it has a native CoreAudio based audio device, which supports the following features:
    • the built-in/device's echo canceller
    • output volume setting
    • input latency setting
    • output latency setting
  • supports for the built-in iLBC codec
  • video on iOS is supported since PJSIP version 2.3

Requirements

  • iOS SDK, part of Xcode. Apple iPad is supported starting with iPhone SDK 3.2 onwards.

Build Preparation

  1. Get the source code, if you haven't already.
  2. Set your config_site.h to the following:
    #define PJ_CONFIG_IPHONE 1
    #include <pj/config_site_sample.h>
    
    This will activate iPhone specific settings in the config_site_sample.h.

Building PJSIP

Just run:

$ cd /path/to/your/pjsip/dir
$ ./configure-iphone
$ make dep && make clean && make

For iPhone 5, use armv7s architecture (only when libyuv is not used, because libyuv does not support armv7s):

$ cd /path/to/your/pjsip/dir
$ ARCH='-arch armv7s' ./configure-iphone
$ make dep && make clean && make

Open ipjsua.xcodeproj using Xcode in pjproject/pjsip-apps/src/pjsua/ios and build the project. You will see telnet instructions on the device's screen. Telnet to this address to operate the application. See PJSUA CLI Manual for commands available. (For release 2.1 and below, ipjsua is located in pjproject/pjsip-apps/src/ipjsua and does not have CLI telnet feature).

To use PJSIP in your application, you need to define PJ_AUTOCONF=1 in the Xcode's project config.

Notes:

  • the ./configure-iphone is a wrapper that calls the standard ./configure script with settings suitable for iPhone target.
  • the latest iPhone SDK version will be selected by default. You may change this by setting IPHONESDK environment variable to the desired SDK path. For ipjsua, select Project-Edit Project Settings-Base SDK and Targets-ipjsua-Get Info-Base SDK to change the SDK version.
  • you may pass standard ./configure options to this script too.
  • for more info, run ./configure-iphone --help
  • other customizations are similar to what is explained in Building with GNU page.

Supporting multiple architectures (armv6, armv7, armv7s, arm64, and so on)

You need to compile separately for each architecture. If your iPhone SDK has llvm-gcc compiler (which is the supported compiler starting iOS SDK 5) or clang, then you need to set ARCH environment variable to the desired architecture before running configure-iphone, for example:

export ARCH="-arch arm64"

Then you need to combine the resulting libraries using the lipo command. For example:

lipo -arch armv6 lib/armv6/libpjlib.a -arch armv7 lib/armv7/libpjlib.a -create -output lib/libpjlib.a

Simulator

To configure the build system for the iPhone simulator:

export DEVPATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer
ARCH="-arch i386" CFLAGS="-O2 -m32 -mios-simulator-version-min=5.0" LDFLAGS="-O2 -m32 -mios-simulator-version-min=5.0" ./configure-iphone
make dep && make clean && make

Note that the exact paths may vary according to your SDK version. If you use iOS SDK before version 7.0, you can configure it this way instead:

ARCH="-arch i386" CFLAGS="-O2 -m32 -miphoneos-version-min=4.0" LDFLAGS="-O2 -m32 -miphoneos-version-min=4.0" ./configure-iphone


Video Support

Features

Video on iOS is supported since PJSIP version 2.3. It has the following features:

  • native capture
  • native preview
  • native OpenGL ES renderer
  • H.264 codec (via OpenH264 library, see below)

Requirements

libyuv

  1. Follow the instructions in ticket #1776

OpenH264

  1. Follow the instructions in ticket #1758

Configuring

Sample invocation of ./configure-iphone:

$ ./configure-iphone --with-libyuv=/Users/me/src/libyuv/trunk --with-openh264=/Users/me/opt

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

...
Using OpenH264 prefix... /Users/me/opt
checking OpenH264 availability... ok
Using libyuv prefix... /Users/me/src/libyuv/trunk
checking for I420Scale in -lyuv... yes
...

Set these in your config_site.h:

#define PJ_CONFIG_IPHONE 			1
#define PJMEDIA_HAS_VIDEO			1

#include <pj/config_site_sample.h>

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 device to get orientation change notification (by calling the API UIDevice.beginGeneratingDeviceOrientationNotifications and add a callback to receive UIDeviceOrientationDidChangeNotification).
  2. Inside the callback, call PJSUA API
    pjsua_vid_dev_set_setting(dev_id, PJMEDIA_VID_DEV_CAP_ORIENTATION, &new_orientation, PJ_TRUE)
    
    to set the video device to the correct orientation.

For sample usage, please refer to ipjsua sample app. Ticket #1861 explains this feature in detail.


OpenSSL Support

Follow the instructions below to enable TLS transport by using OpenSSL:

  1. Build and install OpenSSL-1.0.0 for iPhone by following these instructions or OpenSSL-0.9.8o with these instructions.
  2. Specify OpenSSL location when running configure-iphone, for example (with Bash):
    ./configure-iphone --with-ssl=[your_openssl_dir]
    
    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
    
  4. In XCode project setting of your application (for example, ipjsua), add libssl.a and libcrypto.a from OpenSSL ARM directory to the project's Libraries:
    1. In Group & Files pane, expand ipjsua, then right click Libraries, and select Add -> Existing Files....
    2. Find libssl.a and libcrypto.a from OpenSSL ARM directory (for example, ${HOME}/openssl/openssl_arm) and add them to the project.
  5. Build the app

Common problems

Guide to accept calls in the background after kCFStreamNetworkServiceTypeVoIP is deprecated (iOS 9)

Starting in iOS 9, kCFStreamNetworkServiceTypeVoIP is deprecated. Apple recommends that applications use VoIP Push Notifications (using PushKit framework) to avoid persistent connections as described in the Apple's official doc. This will require application to implement the setup and handling of push notifications in the application layer (for additional guides, you can refer to here and here). For now, PJSIP will still use kCFStreamNetworkServiceTypeVoIP, if you want to disable it right away, you can set PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT to 0.

Audio session management issue (iOS7)

Since the deprecation of C-interface Audio Session API in iOS 7 SDK, PJSIP now uses AVAudioSession to set audio session category (to PlayAndRecord) and activate/deactivate audio session. Application will need to do its own audio session management to handle input/output route and notifications (interruption, media server reset, etc). Please refer to Apple's official doc on Audio Session Programming Guide and our ticket #1697.

Here could be used as a quick start reference:

  • AVAudioSession::setCategory to enable/disable audio route via bluetooth, application can override audio session category by adding/removing option AVAudioSessionCategoryOptionAllowBluetooth:
    /* Enable */
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
    ...
    /* Disable */
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    
  • AVAudioSession::setPreferredInput for switching to other audio input route.
  • AVAudioSession::overrideOutputAudioPort for toggling on/off built-in loudspeaker.

Crash after calling PJLIB APIs using Grand Central Dispatch (GCD)

PJLIB API should be called from a registered thread, otherwise it will raise assertion such as "Calling pjlib from unknown/external thread...". With GCD, we cannot really be sure of which thread executing the PJLIB function. Registering that thread to PJLIB seems to be a simple and easy solution, however it potentially introduces a random crash which is harder to debug. Here are few possible crash scenarios:

  • PJLIB's pj_thread_desc should remain valid until the registered thread stopped, otherwise crash of invalid pointer access may occur, e.g: in pj_thread_check_stack().
  • Some compatibility problems between GCD and PJLIB, see #1837 for more info.

If you want to avoid any possibility of blocking operation by PJLIB (or any higher API layer such as PJMEDIA, PJNATH, PJSUA that usually calls PJLIB), instead of dispatching the task using GCD, the safest way is to create and manage your own thread pool and register that thread pool to PJLIB. Or alternatively, simply use PJSUA timer mechanism (with zero delay), see pjsua_schedule_timer()/pjsua_schedule_timer2() docs for more info.

Audio lost or other issues with interruption (by a phone call or an alarm), headset plug/unplug, or Bluetooth input

For devices running iOS 4.0 or later, you need to enable your application to respond to remote-control events. This allows PJSIP to properly receive and process the above events. You can do this by adding the following code in your application:

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

For more details, please refer to Apple's doc on Remote Control of Multimedia.

It has also been reported that any time an audio interruption happens, audio is lost until the application is killed/restarted, also reported that this occurs only on iOS7.

Here is the reported working solution:

  • application should be configured to receive interruption events, see also problem with interruption.
  • forcefully shutdown the sound device when interruption begins, e.g: using pjsua_set_no_snd_dev()) for pjsua, or AudDevManager.setNoDev() for pjsua2
  • restart the sound device after interruption ends, e.g: using pjsua_set_snd_dev() for pjsua, or AudDevManager.setPlaybackDev()+setCaptureDev() for pjsua2.

Also note this is the recommended outline of the normal flow for audio interruption:

  • on interruption begin
    1. hold the calls
    2. stop any other media if any (i.e. disconnect all connections in the bridge)
    3. by default, sound device will be stopped after some idle period after there is no connection in the bridge, or alternatively just forcefully shutdown the sound device.
  • on interruption end
    1. unhold the calls
    2. resume any other media if any
    3. if sound device was not shutdown forcefully, first connection to the bridge will cause sound device to be started, otherwise manual restarting the sound device, by setting playback & capture device, is required.

SIP transport keepalive while in background

As the process is normally suspended when application is in the background, the worker thread that handles TCP keepalive timer is also suspended. So basically application needs to schedule periodic wakeup to allow the library send TCP keep-alive. Sample code:

- (void)keepAlive {
    /* Register this thread if not yet */
    if (!pj_thread_is_registered()) {
        static pj_thread_desc   thread_desc;
        static pj_thread_t     *thread;
	pj_thread_register("mainthread", thread_desc, &thread);
    }

    /* Simply sleep for 5s, give the time for library to send transport
     * keepalive packet, and wait for server response if any. Don't sleep
     * too short, to avoid too many wakeups, because when there is any
     * response from server, app will be woken up again (see also #1482).
     */
    pj_thread_sleep(5000);
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    /* Send keep alive manually at the beginning of background */
    pjsip_endpt_send_raw*(...);

    /* iOS requires that the minimum keep alive interval is 600s */
    [application setKeepAliveTimeout:600 handler: ^{
	[self performSelectorOnMainThread:@selector(keepAlive)
              withObject:nil waitUntilDone:YES];
    }];
}

Make sure that keepalive feature of SIP transport is not disabled, see PJSIP_TCP/TLS_KEEP_ALIVE_INTERVAL docs, and the keepalive interval is set to less than 600s.

Alternatively, configuring server to send keepalive ping packet, if possible, and client responds back by sending keepalive pong to the server, so we have two-way traffic. As there is no way to detect incoming ping from server, currently application can just always send pong packet whenever it becomes active (application will be woken up when receiving TCP packet), e.g: send pong packet in UIApplication::applicationDidBecomeActive().

Other Problems (problem specific to a particular iOS version/device/simulator)

Unable to accept incoming call in background mode

Starting in iOS 9, this method to accept incoming call in bg is deprecated, please have a look at #bg-call.

If while in the background, ipjsua (or your application) is unable to detect if there is an incoming call and display the local notification:

  1. Note that background feature only works with TCP.
  2. Make sure that voip is included in the required background modes (UIBackgroundModes) in the application’s Info.plist file.
  3. Make sure that the TCP socket is successfully wrapped with CFReadStreamRef (check if there is a message: "Failed to configure TCP transport for VoIP usage").
  4. Check whether you can accept the incoming call by bringing the app to the foreground. If yes, make sure that the incoming call request comes from the wrapped TCP socket (check the log for the INVITE request).

Note: these steps do not troubleshoot audio problems.

Audio issue (no audio) (iOS7)

There are a few reports that the audio is not working on iOS 7 (so far it's only reported to happen in iPhone 5) after getting the following error:

iPhone mediaserverd[45] <Error>: 10:53:59.986 ERROR:     [0x240b000] 740: _vp: initialize hw input: fs mismatched! REF=0.000000Hz, MIC=44100.000000Hz
iPhone mediaserverd[45] <Error>: Resampler2 bad sample rate(s) :        0.00    16000.00

One solution is to set the sound device's clock rate to 44100 Hz as to avoid resampling.

Audio issues (inability to mute, volume control center synchronization, playback reduced volume) (iOS7)

There are several issues with the use of Voice Processing IO Audio Unit in iOS 7, such as unable to mute the volume using the side volume button, unable to synchronize with the control center volume slide bar, significantly reduced volume for audio playback to speaker after a call. Currently there is no workaround for this. Please refer to ticket #1697 for more details.

Unable to support Bluetooth input

Since Bluetooth input support is only available for iOS 3.1 or later, you need to specifically specify that your deployment target is iOS 3.1 or above. You can do this either in user.mak:

export CFLAGS += -D__IPHONE_OS_VERSION_MIN_REQUIRED=30100

or in config_site.h:

#define __IPHONE_OS_VERSION_MIN_REQUIRED 30100

You need to recompile PJSIP after this.

Sound not working in the simulator

Go to System Preferences > Sound > Sound Effects and then uncheck and recheck "Play user interface sound effects". If it still doesn't work, you can try some other suggestions here.

List of Issues

#1113
iPhone: Echo cancellation may not work on iOS using SDK 4.x
#1115
iPhone: Long delay when calling AudioConverterNew() causing timeout in acquiring pjsua lock (thanks Bogdan Krakowski the report)
#1130
Incoming TCP connection on iPhone iOS4 BG mode would fail over and over with EAGAIN error (thanks Bogdan Krakowski for the report)
#1172
Crash in coreaudio_dev (iPhone OS) when there is no call during a GSM interruption
#1174
Fix crash in coreaudio_dev's interruptionListener (iPhone OS) and support for multiple interrupted streams (thanks to Seth Hinze for the patch)
#1194
iPhone: Fix problems with echo cancellation and Bluetooth input (SDK 4.x)
#1225
iPhone: UDP socket send error after waking up from background mode (thanks Joe Meade for the report)
#1226
iPhone (info only): No audio in calls if app is woken up from background mode without network connectivity (thanks Joe Meade for the report)
#1228
Long delay in iPhone initialization (thanks Guy Zelkha for the report)
#1231
Crash in iPhone iOS when trying to make a call after the application is left running for days (thanks Alejandro Orellana for the report)
#1245
Assertion may be raised if iPhone app woken up to perform registration and the server is down (thanks Alejandro Orellana for the report)
#1246
Use CFHost API for pj_getaddrinfo() on iPhone OS
#1324
Build correctness on non-video platforms (Symbian, iPhone, Windows Mobile)
#1335
Fixes assertion when interruptionListener is called from an unregistered thread on iPhone OS (thanks to Ilya Kalinin for the patch)
#1342
Disable local host resolution on iPhone OS
#1482
iPhone: Solution for application getting killed by iOS 5 due to too many wakeups
#1597
Handle socket failure in STUN resolution when coming back from iPhone/iOS background mode

Other iPhone Projects

Also have a look at these PJSIP iPhone ports by pjsip users:

Attachments (1)

Download all attachments as: .zip