Porting PJSIP and PJMEDIA Stack to SymbianOS
This article gives some information about porting and using PJSIP on Symbian OS platforms.
Table of Contents
1. Development Status
Platforms and SDKs
Development Tools
Porting Status
2. Getting and Building the Source
Getting the Source
Build Requirements and Installations
Building the Source
Running the Test and Sample Programs
3. Design Changes
4. Porting Details
5. Application Notes and Known Issues
1. Development Status
Platforms and SDKs
The porting effort is done on the following platforms/SDKs:
- S60 3rd Edition SDK
This is the main target of the porting effort, therefore it's the one that will be most tested. Versions of SDK that have been tested are:
- S60 3rd Edition
- S60 3rd Edition MR (Maintenance Release)
Other versions may work, but we have not tested them.
S60 3rd Edition SDK can be downloaded from Forum Nokia website, in S60 Platform SDKs for Symbian OS, for C++ page.
- UIQ 3.0 SDK
The initial porting effort was done on UIQ SDK 3.0 and it worked. More over PJ doesn't use any features provided outside Symbian OS standard features, so it should work with any Symbian OS SDK (with the exception of audio device abstraction, which currently uses Audio Streaming API/plug-in and this may not be available on certain Symbian SDK).
UIQ 3.0 SDK can be downloaded from UIQ Developer Community website.
|
Development Tools
The build system that we use is the .MMP based project definition, so we only use development tools that can import the project from this file. The following tools are known to work:
- Command Line (ABLD)
The Symbian command line build system is supported for both S60 and UIQ SDK.
- CodeWarrior for Symbian OS 3.1
CodeWarrior is supported, but be aware that Nokia has announced that developers will not be able to purchase new licenses after 2007. Developers working on S60 SDK should probably use Carbide C++ instead.
- Visual Studio 6
Both command line and VS6 are supported for development with UIQ 3.0 SDK.
- Carbide C++ version 1.2
Carbide C++ is now supported. But please note that version 1.0 will not work with PJSIP, and version 1.1 was not tested.
Carbide C++ Express Edition is free, but developers who want on-device debugging will need to purchase the Developer edition license for about 300 Euros (21 days evaluation is available).
Carbide C++ can be downloaded from Forum Nokia: Carbide Development Tools for Symbian OS C++ page.
|
Note that Carbide VS is not supported for building the libraries because it has a very annoying "bug" that rejects directory name with dash ('-') character (we tested this in early 2007, so the situation may have changed now).
Porting Status
As of December 2007, the Symbian porting effort is mostly complete. The port has been tested both on S60 3rd Edition emulator and device.
Screenshot of symbian_ua application on S60 emulator
2. Getting and Building the Source
Getting the Source
The Symbian port is available in the SVN trunk. Please see the download page for more info.
Build Requirements and Preparations
Requirements
Please see Platforms and SDK and Development Tools above for SDK and development tools requirements.
SDK Installation
Install the SDK in the same drive as PJ's source tree (Symbian build system does not support placing SDK and source files on separate drives), and install it on drive:\Symbian directory (for example "C:\Symbian"), because this is the path configured in PJ's batch files.
Prepare Your config_site.h
Some recommended settings for Symbian are provided in config_site_sample.h, you should include this file in your config_site.h file:
#include <pj/config_site_sample.h>
|
Building Simple Symbian SIP User Agent Application
The Symbian specific build files are located in build.symbian directory under PJ's source tree. The project files are organized as .mmp files, with each library and executable having its own .mmp file. As usual, these .mmp files are put in bld.inf file.
Library List
These are the list of libraries to be built:
- pjlib.mmp, the platform abstraction and framework.
- pjlib-util.mmp, for text scanner, encryption, DNS, XML, etc.
- pjnath.mmp, for STUN, TURN, ICE, etc.
- symbian_audio.mmp, Symbian audio device abstraction implementaton.
- pjmedia.mmp, the media framework (codec, conference bridge, RTP/RTCP, and the likes).
- pjsdp.mmp, part of pjmedia related to SDP.
- pjsip.mmp, SIP core stack.
- pjsip-ua.mmp, SIP user agent functionality.
- pjsip-simple.mmp, SIP SIMPLE for presence, IM, etc.
- pjsua-lib.mmp, the high level user agent library integrating SIP, media, and NAT.
|
Applications
The source distribution provides a simple command line based SIP application based on PJSUA-LIB, called symbian_ua, which sources are in pjsip-apps/src/symbian_ua directory.
- pjlib-test.mmp, for testing pjlib under Symbian.
- symbian_ua.mmp, the simple, command-line based SIP user agent application for Symbian.
- symsndtest.mmp, for testing the sound device abstraction.
|
Building with Command Line Tools
The Symbian command line tools can be used to build the libraries for both S60 and UIQ SDK. To build the libraries and application:
c:\> cd $(PJDIR)
c:\> cd build.symbian
c:\> set EPOCROOT=\Symbian\UIQ3SDK\
c:\> 00.bat
c:\> 01.bat gcce urel
|
- Note
-
- Set EPOCROOT to the appropriate SDK location.
- The 01.bat can take target argument, for example 01.bat winscw udeb. If called without arguments, it will build the libraries for all targets as specified in BLD.INF.
Building for UIQ 3 SDK with Visual Studio 6
Alternatively, you can open the VS6 workspace files created by 00.BAT batch file, located under \Symbian\UIQ3SDK\epoc32\BUILD\$(PJDIR) directory, and build all projects under VS6.
Building for S60 3rd Edition SDK with CodeWarrior
Steps for building the libraries/programs with CodeWarrior are pretty easy:
- Run CodeWarrior for Symbian OS
- Import each .MMP file into CodeWarrior project:
- Select File ==> Import Project from .mmp File..
- Select the appropriate S60 SDK from "Symbian Importer - SDK Selection" dialog, and press Next.
- Browse the .mmp file to import from $(PJDIR)/build.symbian directory, leave other settings unchanged, and press Next
- Leave "Default Symbian Project Import Template", and press Finish
- CodeWarrior will create a project from the .mmp file.
- Build the project by selecting Project ==> Make from the menu.
- Repeat above steps for other libraries (as listed in Library List above).
|
Building for S60 3rd Edition SDK with Carbide C++
A complete tutorial on using Carbide C++ for PJSIP is available, please see Building and Debugging PJSIP on Symbian S60 3rd Edition Device using Carbide C++.
Running/Debugging the Test or Sample Programs
Emulator Configuration
Currently most test or sample programs are built as Symbian console programs. To enable console mode in the emulator:
- Open $(SDK)\epoc32\data\epoc.ini file
- add TextShell line at the end of the file. With this settings, the emulator will now start in Console mode (and it will start faster too!).
- Enlarge the console screen size, by changing ScreenWidth to 640, and ScreenHeight to 480.
|
Running Applications with Emulator
To run the application, just run \Symbian\$(SDK)\epoc32\release\winscw\udeb\symbian_ua.exe file.
Note however that debugging is not possible with this approach.
Running Applications with Visual Studio
Open the VS6 project workspace generated by Symbian abld command (invoked by 00.BAT and 01.BAT) for the application, and build the project. All libraries that the application depends (such as PJLIB, PJLIB-UTIL, PJSIP, etc.) must have been built before the application can be built.
After the application has been built, you can run the application in the Emulator by pressing Debug button from Visual Studio. This will launch the Emulator and you should see the output of PJLIB's log in the Emulator.
With this IDE configuration, you can also set breakpoints, watches, and perform step-by-step debugging as usual.
Note that there are some known issues with some applications that you may want to know before running the applications.
Running Applications with CodeWarrior
Change the executable for the debug session by:
- selecting Edit ==> WINSCW UDEB Settings from the menu,
- select Target ==> Runtime Settings from the tree,
- browse symbian_ua.exe executable from the right pane.
|
Debug the project by selecting Project ==> Debug from the menu.
Note that there are some known issues with some applications that you may want to know before running the applications.
Running/Debugging Applications with Carbide C++
A complete tutorial on using Carbide C++ for PJSIP is available, please see Building and Debugging PJSIP on Symbian S60 3rd Edition Device using Carbide C++.
3. Design Changes
The Symbian port requires the following design changes to be made on all libraries and applications. Please see Porting Details section below on the detailed explanation of these.
- Threading is Not Supported
Multithreading is not supported. Applications MUST run on a single thread only.
- No Polling
Polling is not necessary for Symbian, since ioqueue and timer heap are implemented as Active Objects. Application just needs to call CActiveScheduler::WaitForAnyRequest() to poll the whole application (not only PJ) in a single place.
- Sources are Compiled as C++ Sources
Because PJLIB's PJ_TRY/PJ_CATCH framework is implemented with C++'s try/catch construct, all sources which include <pjlib.h> or <pjlib/except.h> MUST be compiled as C++ source. Practically, now all libraries are compiled as C++ sources.
- Constant global variables are evil!
For Symbian 9 (S60 3rd Edition included), the libraries may be built as .DSO (Dynamic Shared Object, Symbian term for Dynamic Link Library/DLL). This is controlled by the #ifdef macro in the .MMP files.
With .DSO, Symbian does not really want us to use (const) global variables. If we try to access these variables (that are exported from a .DSO) from application, we will get garbage values since the variables are not initialized properly.
Unfortunately, the libraries do have some (const) global variables. Things like PJ_VERSION, PJ_AF_INET, PJ_SOCK_STREAM, they're all const global variables. So Symbian application can no longer access these const global variables anymore. Instead, it must access these variable through their accessor functions (for example, pj_get_version(), pj_AF_INET(), pj_SOCK_STREAM(), etc.)
|
4. Porting Details
PJLIB
PJLIB has been implemented and tested successfully (it has passed pjlib-test testing). The following components have been re-implemented specificly for SymbianOS:
- PjSymbianOS Singleton Class
-
File: src/os_symbian.h, src/os_core_symbian.cpp
The PjSymbianOS class is a singleton class to keep global objects such as RSocketServ and RHostResolver instances, and is initialized in pj_init() function. This class also provides other utility functions such as Unicode conversion and socket address conversion between Symbian's TInetAddr and PJLIB's pj_sockaddr_in.
- Threading, Synchronization Objects, and Thread Local Storage (TLS)
-
File: src/os_core_symbian.cpp
For this initial porting effort, it was decided that threading will not be supported, which means that pj_thread_create() will always return failure. Synchronization object APIs (such as mutex and semaphores) are implemented as dummy functions which always return success. Thread local storage (or thread specific data) and atomic variables are supported with emulation, since these functionalities are needed by upper layer libraries.
The decision to disable thread support was made to enable building the libraries as static libraries. If PJLIB has to support Symbian threads, it has to use the Dll namespace, which can only be used by DLL targets.
In the future, it is possible that PJLIB will provide "virtual" threading (cooperative multitasking) by using Active Objects, however it is unlikely that this will be implemented in this first release.
- Address Resolution (gethostbyname())
-
File: src/addr_resolv_symbian.cpp
The address resolution API provides pj_gethostbyname() function to resolve host's IP addresses. The function uses Symbian's RHostResolver::GetByName() API to perform the resolution. A global RHostResolver instance is kept by PjSymbianOS singleton object.
- Exception Framework (PJ_TRY, PJ_CATCH_ANY)
-
File: include/except.h, src/exception_symbian.cpp
This is one of the major pain in the porting effort. Symbian OS does not support setjmp()/longjmp() very well, and there is no equivalent exception handling framework for the C language (many articles wrongly say that Symbian's Leave/Trap mechanism is equivalent to setjmp()/longjmp(), but clearly it's not!).
The only working solution now seems to map PJ_TRY/PJ_CATCH into C++'s try/catch mechanism. Not all PJLIB's exception constructs are supported, however. For this release, PJ_CATCH macro is not supported (only PJ_CATCH_ANY is supported).
As the implication of this, all sources have been patched to compile cleanly as C++ programs.
- Socket Address Byte Ordering
-
File: src/sock_symbian.cpp
IP address and port in pj_sockaddr_in are stored in network byte order, consistent with other PJLIB targets. These values will be converted to host byte order when PJLIB needs to call Symbian native socket API (and vice versa).
- Socket API
-
File: src/sock_symbian.cpp
PJLIB socket API provides BSD style abstraction for performing socket operations, such as pj_sock_socket(), pj_sock_bind(), pj_sock_send(), pj_sock_sendto(), pj_sock_recv(), pj_sock_recvfrom(), etc. The implementation for SymbianOS is provided in src/sock_symbian.cpp file.
The pj_sock_t socket descriptor is mapped to CPjSocket class (declared in src/os_symbian.h file), which is a class to wrap Symbian's RSocket. The CPjSocket class abstraction is needed because pj_sock_t needs to work with PJLIB's select() API, which Symbian doesn't provide.
Note that there is 1500 bytes compile time limitation on the receive buffer when the socket is used with pj_select(). This limitation only affects the socket when it's used with pj_select(); there is no such limitation when the socket is used directly (with pj_sock_recv() or pj_sock_recvfrom()) or when IOQueue is used.
- Socket select() API
-
File: src/sock_select_symbian.cpp
PJLIB implements select() abstraction with the usual pj_sock_select() API.
Note that it's not recommended to mix pj_select() call with IOqueue call, since when pj_ioqueue_recv()/pj_ioqueue_recvfrom() is called, the flag and socket internal buffer used by select() will be cleared, and this will cause packet already received to be dropped..
- IOQueue
-
File: src/ioqueue_symbian.cpp
The PJLIB's IOQueue is reimplemented for Symbian OS. Currently only socket receive and socket accept are implemented asynchronously (non-blocking); socket connect() and socket sending operations are done synchronously (blocking).
- Timer Heap
-
File: src/timer_symbian.cpp
The timer heap is reimplemented to use Symbian's Active Object.
- OS Error Reporting
-
File: src/os_error_symbian.cpp
This file contains function to retrieve the error string associated with Symbian error codes.
- Pool Allocation Backend
-
File: src/pool_policy_new.cpp
The default memory allocation/deallocation backend for SymbianOS is using new/delete operator instead of the traditional malloc()/free().
- Unicode
-
File: src/unicode_symbian.cpp
Unicode - ANSI conversion is implemented with Symbian CnvUtfConverter::ConvertToUnicodeFromUtf8 and CnvUtfConverter::ConvertFromUnicodeToUtf8 functions.
- vsnprintf (Lack Of)
-
File: src/compat/string_compat.c
Since there's no vsnprintf function in Symbian, an implementation is provided which just maps the call to vsprintf.
|
PJLIB-UTIL
There's no Symbian specific part in this library.
PJNATH
There's no Symbian specific part in this library.
PJSIP
There's no Symbian specific part in this library.
PJSIP-UA
There's no Symbian specific part in this library.
PJSIP-SIMPLE
There's no Symbian specific part in this library.
PJMEDIA
- No SDP
-
The SDP related functionalities have been put into PJSDP library, separated from PJMEDIA (to allow PJMEDIA to be compiled as C project rather than C++ project).
- Sound Device
-
Sound device abstraction also has been split into separate project (e.g. symbian_audio.mmp and null_audio.mmp) to allow target specific sound device implementation.
|
PJSDP
There's no Symbian specific part in this library.
PJSUA-LIB
- No Polling
There are couple of places in pjsua_core.c where pjsip_endpt_handle_events() call is replaced by pj_thread_sleep(), since there is no polling for Symbian.
|
5. Known Issues
Application Notes
Memory Leaks
No memory leak is reported as of now. However, in addition to performing the general shutdown procedure, application must also call CloseSTDLIB() to ensure that STDLIB resources are properly released since PJLIB does use STDLIB.
For sample code to properly shutdown the application, please see pjsua_destroy() implementation in pjsua_core.c file (part of PJSUA-LIB).
Symbian Open Source SIP and media stack porting is a work in progress.
Please send your feedback to pjsippjsip.org mailing-list or to the author:
(C)2003-2007 Benny Prijono <bennylppjsip.org>
|