In this tutorial series, I will walk you through creating an iOS VoIP app. After finishing this series, you’ll have a working VoIP app, that can make outbound VoIP call and receive incoming call.
To achieve this, we’re going to utilize an open source library – pjsip.
Prerequisite and Environment
To follow this tutorial, you should know the basics of developing an iOS app. Also, you should be comfortable with shell, since we’ll execute shell commands to do the compiling of pjsip
For the environment, we’re using:
- OS X Mavericks 10.9
- Xcode 5.1.1
- Pjsip 2.2.1
(This article was written on 2014 and posted on my old blog. Most of the content should still work on latest Mac and iOS, i.e, macOS Sierra and Xcode 8.3. Please let me know if you find anything not working, thanks.)
In its official website, we can see the definition of pjsip:
PJSIP is a free and open source multimedia communication library written in C language implementing standard based protocols such as SIP, SDP, RTP, STUN, TURN, and ICE. It combines signaling protocol (SIP) with rich multimedia framework and NAT traversal functionality into high level API that is portable and suitable for almost any type of systems ranging from desktops, embedded systems, to mobile handsets.
Wow, so many terms. Terrifying, isn’t it? And it does takes a lot of time and effort to understand all of them. Fourtunately, for now, we only need to pay attention to the single most important one here: SIP.
SIP(Session Initiation Protocol), as its name implies, is the protocol to initiate a session. We use SIP, to prepare and initiate, or receive VoIP calls.
For example, a client sends a sip message –
REGISTER – to register itself on the server, and sends an
INVITE message to initiate a call.
Pjsip implemented SIP and many other protocols needed to support VoIP calling. By using pjsip, we could easily create a VoIP app.
Compiling pjsip for iOS
First of all, you need to download the pjsip library. And this is the official link.
Pjsip supports multiple platforms: Windows, Mac, Linux, iOS, Android, etc. In order to use it in our iOS project, we need to compile pjsip specifically for iOS platform.
You could simply follow the following steps to compile it for iOS, or check out the official tutorial for further details.
Configure pjsip to target iOS platform
To specify the target platform is iOS, we need to create a file:
pjlib/include/pj/config_site.h, and set it’s content as:
#define PJ_CONFIG_IPHONE 1 #include
The 1st line tells pjsip to target the iOS platform, and the 2nd line includes some basic settings from the sample configuration file.
Actually compile the pjsip library
This is very standard and natural, just use the following command to actually compile it:
$ ./configure-iphone && make dep && make clean && make
Specifying armv7s or arm64 as architecture
By default, the target architecture is armv7. This architecture is available for iPhone since iPhone 3GS, and all iPad.
However, besides this, for iPhone 5, iPhone 5c, and iPad 4, we have armv7s. And for iPhone 5s and iPad air, we have arm64.
If you want to get the max out of the device, you could specify the specific architecture by setting the
ARCH variable. For example:
$ ARCH='-arch armv7s' ./configure-iphone && make dep && make clean && make
$ ARCH='-arch arm64' ./configure-iphone && make dep && make clean && make
Undefined symbols for architecture armv7s
If you compile for a different architecture after a previous one, you may get an error like this:
Undefined symbols for architecture armv7s: "_main", referenced from: implicit entry/start for main executable ld: symbol(s) not found for architecture armv7s clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [../bin/samples/arm-apple-darwin9/pjsua2_demo] Error 1 make: *** [pjsua2_demo] Error 2 make: *** [samples] Error 2 make: *** [all] Error 1
This is caused by a bug of the Makefile, the make clean statement failed to remove this file:
So, to fix this issue, just simply run
$ make clean $ rm pjsip-apps/build/output/pjsua2_sample-arm-apple-darwin9/pjsua2_demo.o
And then, go ahead and run the compile command.
The compilation result
After compiling, the pjsip binary libraries are located in:
pjlib/lib pjlib-util/lib pjmedia/lib pjsip/lib pjnath/lib
The compiling command also compiles some third party libs being used by pjsip. These third party libraries are located in:
In order to use the pjsip libraries, we need to include the corresponding header files. Naturally, the header files are located in:
pjlib/include pjlib-util/include pjmedia/include pjnath/include pjsip/include
Run the testing project
Pjsip already contains an iOS project that you could build and run. Simply open pjsip-apps/src/pjsua/ios/ipjsua.xcodeproj in your Xcode, and do the following steps, you’ll be able to get a running pjsip app immediately.
Configure the bundle identifier
We need to configure the Bundle Identifier in order to run it on our own devices.
- We could do this by configure the Bundle Identifier in the “General” settings panel.
- Alternatively, we could also edit the Bundle identifier field in
For iPhone 5 or later
If you are testing on iPhone 5 or later, and you didn’t specify the ARCH variable during compiling, you may get an error similar to the following:
No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=armv7s, VALID_ARCHS=armv7).
In order to solve it, just turn off the ONLY_ACTIVE_ARCH flag. Open the Build Settings, and change the “Build Active Architecutre Only” flag from YES to NO.
Please note that, if you’re using iPhone 5s, and set the ARCH to arm64, you might get an compilation error of:
Implicit declaration of function 'showNotification' is invalid in C99
To make the compiler happy, just add this line of code before the on_incoming_call function:
pj_bool_t showNotification(pjsua_call_id call_id);
Library not found for -lmilenage-arm-apple-darwin9
According to this ticket, milenage is only used in very rare scenarios. So by default, this library is not compiled.
To solve it, you could:
- simply remove this linking requirement. Go to the “Build Phases” tab, and remove the “libmilenage-arm-apple-darwin9.a” from the Link Binary With Libraries section.
- Or, manually compile it by executing make in folder third_party/build/milenage
Actually run it in your device.
Great, everything is done, you can now compile and run it on your own device!
When the app launched, you’ll see the pjsua logo and a message like:
Telnet to 192.168.1.103:2323
You could now connect to the app via Telnet, and send PJSUA-CLI commands to register account, make call, send SMS, etc.