How to Make an iOS VoIP App With Pjsip: Part 4

We have successfully compiled pjsip and the demo in part 1, managed to set up the VoIP server in part 2, and discussed the basic usage of pjsip APIs in part 3.

In this part 4, we will start building our own iOS VoIP app from stratch. By setting up a new project to make use of the pjsip libraries and header files, we’ll have a good foundation project in which we can call any pjsip methods.

You could download the source code of this project on Github here. Each commit of the project, is representing one step of this tutorial.

(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.)

Creating new project

Let’s start by creating a Single View Application

And name it as simpleVoIP

Using pjsip and the architecture problem

Normally, using pjsip is a very tedious and error prone procedure. You need to:

  1. Compile pjsip for different architectures: armv7, armv7s, arm64, and i386 (for the detail of how to do this, you can check out the 1st tutorial)
  2. Use the corresponding static libraries in your project based on your target platform: armv7 for iPhone 4, armv7s for iPhone 5, arm64 for iPhone 5s, and i386 for simulator.

In fact, what you’ll get after compiling pjsip, is not a single static library, but a set of static libraries. For example, in this tutorial, we’ll use the following libraries (these are the same libraries being used in the pjsip built-in demo):

Pjsip libraries:

  • pjlib/lib/libpj-arm-apple-darwin9.a
  • pjlib-util/lib/libpjlib-util-arm-apple-darwin9.a
  • pjmedia/lib/libpjmedia-arm-apple-darwin9.a
  • pjmedia/lib/libpjmedia-audiodev-arm-apple-darwin9.a
  • pjmedia/lib/libpjmedia-codec-arm-apple-darwin9.a
  • pjnath/lib/libpjnath-arm-apple-darwin9.a
  • pjsip/lib/libpjsip-ua-arm-apple-darwin9.a
  • pjsip/lib/libpjsip-arm-apple-darwin9.a
  • pjsip/lib/libpjsip-simple-arm-apple-darwin9.a
  • pjsip/lib/libpjsua-arm-apple-darwin9.a

Third party libraries:

  • third_party/lib/libg7221codec-arm-apple-darwin9.a
  • third_party/lib/libgsmcodec-arm-apple-darwin9.a
  • third_party/lib/libilbccodec-arm-apple-darwin9.a
  • third_party/lib/libresample-arm-apple-darwin9.a
  • third_party/lib/libspeex-arm-apple-darwin9.a
  • third_party/lib/libsrtp-arm-apple-darwin9.a

So, if we want to switch between simulator and real device during testing, we’ll have to change this whole set of libraries being used.

For example, when we switch to simulator, we’ll need to link something like i386/libpj-arm-apple-darwin9.a, i386/libpjlib-util-arm-apple-darwin9.a, etc. (assuming i386 is the folder containing i386 pjsip libraries); and when we switch to real device, we’ll need to change them to armv7/libpj-arm-apple-darwin9.a, armv7/libpjlib-util-arm-apple-darwin9.a, etc. instead.

This is very annoying, tedious, and error prone. So let’s introduce fat libraries to help us with this problem.

Fat library

A fat library is a library that integrate libraries of different architectures into one single libary. This way, we don’t need to switch between different libraries when we change the target device/platform.

To create a fat library, you can use the following command:

$ export LIB_NAME="libg7221codec-arm-apple-darwin9.a"
$ lipo -arch armv7 armv7/$LIB_NAME -arch armv7s armv7s/$LIB_NAME -arch arm64 arm64/$LIB_NAME -arch i386 i386/$LIB_NAME -create -output unified/$LIB_NAME

This command integrates the armv7, armv7s, arm64 and i386 libraries into a single fat library. You could use the lipo command to look into the result:

$ lipo -info unified/libg7221codec-arm-apple-darwin9.a

Architectures in the fat file: unified/libg7221codec-arm-apple-darwin9.a are: armv7 armv7s i386 arm64

I strongly recommend you to try building your own fat libraries, so that you will be more familiar with this whole process. But if you just want to start working on your iOS VoIP app right away, you could download the fat library I built here.

Adding the libraries into the project

After the previous step, adding the libraries is just a piece of cake now.

Simply drag those libxxx.a into the Frameworks group, and Xcode will automatically link them during compile. You can see that in the Build Phases tab.

The header files of pjsip

In order to use pjsip, you need to update your Header Search Path of your project, so that it can reach all the header files contained in the “include” folder of the following 5 pjsip folders:

  • pjlib/include
  • pjlib-util/include
  • pjmedia/include
  • pjnath/include
  • pjsip/include

Usually, we would create a folder that contains all of them, so that we only need to add that folder in the search path.

One way to do this, is to manually create a pjsip-include folder, and move all the files in these 5 folders into it.

Another approach, is to let pjsip do it for you: by specifying a output folder for the —prefix parameter of configure-iphone script during the compiling process. This way, all the needed header files will be included in a single “include” folder in the folder you specified.

For example, you could use the following command to compile pjsip

$ ./configure-iphone --prefix=/Users/xianwen.chen/Pjsip/pjproject-2.2.1/output/ && make dep && make && make install

After compiling, there will be two folders in the “output” folder: a “lib” folder containing all the compiled binary libraries, and a “include” folder containing all the needed header files. You can then simply use this “include” folder for all the pjsip header files.

Again, if you want to skip these steps, you can download the “pjsip-include” folder made by me directly here.

Adding the header files into the project

To use the “pjsip-include”, let’s copy this folder into the project directory. This way, our project can be successfully compiled regardless of the outer environment.

In this case, we’ll copy it to the simpleVoIP folder:

And then, we add the path of this folder into the “Header Search Paths”:

Link required frameworks

Pjsip makes use of the iOS built in frameworks, to perform tasks like using the network, audio, etc.

We need to link these required frameworks in order to run our app. In this tutorial, we’ll need three frameworks:

  • AVFoundation.framework
  • CFNetwork.framework
  • AudioToolbox.framework

Add these frameworks in the “Build Phases” tab:

Great, now you’re free to make use of any pjsip functions in this project!

To be continued…

9 thoughts on “How to Make an iOS VoIP App With Pjsip: Part 4

  1. Hello! I try to integrate pjsip to my app for entire week now. I tried s lot of different approaches, pods, etc. (including your tutorial ) but always have the same annoying issue :

    Apple Mach-O Linker (ld) Error

    Linker command failed with exit code 1 (use -v to see invocation)

    It appears only when I build for real device. For simulator it works perfectly. Probably you have some ideas of what it can be? Thank.

    Like

    • I think maybe you only compiled the architecture for simulator, i.e., i386 or x86_64. But didn’t compile it for real device architecture: arm64, armv7s, etc.

      You can run lipo -info your_pjsip_lib to check what architecture you’ve compiled in the lib.

      Like

      • Thank for response. The issue is not related to fat libs, they are ok.

        lipo -info showed all required architectures.
        You know what’s ridiculous? I used my fat libs for ipjsua ( project that is shipped with PJSIP ) and it works for all devices. But when I use them in my own app it simply does not work. I don’t know, probably it’s kinda bug of xCode…

        Like

          • I would be happy to provide some detail description of link error but.. I don’t have it! All I have is this:

            Linker command failed with exit code 1 (use -v to see invocation)

            That’s why I say it’s annoying error 😉 Very descriptive, now doubt…

            Like

  2. Ok, for anyone who may be interested, i have finally figured this problem out.
    The reason of linking error was the new technique used by apple since iOS 9.0 – App Thinning.
    In particular, there is option, called “enable bitcode” that is applied to any project by default ( here the link to apple docs: https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html ). This produces this error, because: “f you provide bitcode, all apps and frameworks in the app bundle (all targets in the project) need to include bitcode. ” – from apple docs.
    The problem is that PJSIP creates static libs which don’t support this bitcode feature.

    So, in order to deal with this problem, just disable “enable bitcode” in project settings!
    I am surprised that I wasn’t able to find this solution anywhere.

    Hope it will help someone!

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s