Version 9 (modified by perry, 17 years ago) (diff) |
---|
PJSIP FAQ
Here you can find answers to some of the most frequently asked questions about PJSIP. If you have a question not answered on this page, you can ask it on the PJSIP mailing list.
- General Questions
- Licensing Questions
- Build and Installation Issues
- Integration Issues
-
Audio Questions
- I'm having problems with no/poor audio (quality), please help!
- I'm having problems with echo, please help
- How can I reduce audio latency?
- How to run PJSUA without sound device?
- How can I adjust the audio volume level?
- Why does PJSIP refuse to send DTMF to remote?
- How can I send inband DTMF tones?
- I didn't hear ringing tones when I make a call, why?
- Outgoing RTP transmissions are not timed equally/properly. Why?
- Does PJSIP support G.723 or G.729 codecs?
- How can I add new codec to PJMEDIA?
- How can I manipulate audio samples directly?
- I always get "Bad RTP pt" error. Why?
- Does PJSIP support SRTP or ZRTP? If no, how can I implement them?
- Video Questions
-
SIP Questions
- Does PJSIP support RFC XYZ?
- I'm having problem with calling XYZ, please help!
- I cannot login/REGISTER to my server. It complains about authentication error.
- How can I attach user specific data to a call?
- With ICE enabled, INVITE packet can exceed MTU/1500 bytes. How can I reduce the message size?
- How can I instantiate multiple PJSIP stacks in my application?
- How can I support INFO method or other SIP methods not mentioned in PJSIP?
-
NAT Questions
- What NAT features does PJSIP support?
- Which STUN-bis draft does PJNATH support?
- Which ICE draft does PJNATH support?
- Which TURN draft does PJNATH support?
- What STUN-bis features does PJNATH support?
- Is there any simple STUN/TURN client which I can use to test my STUN/TURN implementation?
- Is there any simple STUN/TURN server in the samples?
- Performance
- Footprint
- Windows Specific Questions
- Windows Mobile Specific Questions
- Linux/uC-Linux Specific Questions
- MacOS X Specific Questions
- Symbian Specific Questions
- Miscellaneous
General Questions
Where can I find the latest release of PJSIP?
The latest PJSIP is always the SVN trunk version. Please find the information on how to retrieve PJSIP from SVN in the PJSIP Download and Getting Started page.
If PJSIP is said to be small footprint, then why the source is so big?
It is common in all projects to have source distribution that is larger than the resulting build. This is because source distribution contains other things such as documentations and scripts. The source tarball of PJSIP v0.7 is significantly larger than v0.5 because in v0.7 we include third party libraries in their original form of distribution, so that they get proper attribution and for easier update, unlike in v.0.5 where we just picked the source files that we like and included them in PJSIP.
I have downloaded PJSIP source codes, now what next?
First you'll need to build the sources, by following the instructions in Getting Started page.
Then you can get yourself familiar with PJSIP features by running pjsua application. pjsua is the reference implementation of PJSIP and PJSUA-LIB. Although it doesn't have all the PJSIP features, it contains most of them, so it's quite useful. Please find pjsua documentation in pjsua Manual page. It is also good to get familiar with pjsua since if you'll ever get any problems with PJSIP, most likely we will ask to reproduce it with pjsua, to determine whether the problem is PJSIP problem or the problem with the implementation. So please try this out. You may even be able to impress your mates by doing SIP calls, conference, IM, and presence from a command line application!
Next, I assume that you want to develop some sort of SIP applications, yes?
At this point, you'll need to decide which API abstractions to use. If building client applications, PJSUA-LIB API may be the better option to use, since this library has easy to use but yet powerful API. On the other hand, since it's a high level API, it may not provide all the flexibilities that are required by the application. Nor the performance, perhaps. Or it may just contain too many features! In this case, perhaps using PJSIP and PJMEDIA directly would be a better option. They will be "slightly" more difficult to use, but they will provide the utmost flexibility, performance, and size.
Then prepare the application project, by following the instructions in Getting Started | Using PJSIP for Applications documentation. The details would depend on what platform you're working on.
Then fill up the application features, by following the Documentation or by looking at PJSIP samples or PJMEDIA samples. In case you're using PJSIP and PJMEDIA directly, you can have a look at PJSUA-LIB source code for reference.
If you'll ever get into any troubles, we're ready to help in PJSIP mailing list. So good luck then!
What SIP products are compatible with PJSIP?
Basically as PJSIP is based on IETF standards (SIP, RTP/RTCP, STUN, ICE, etc.), so it should be compatible with other standard based products. We and the community are constantly using PJSIP with other open source products such as:
We have also heard people successfully done interoperability tests with commercial SIP servers or products, but we don't specificly maintain list of these products. Basically the principle stays the same, as long as they follow the standards, we are confident that PJSIP should be able to communicate with them.
Licensing Questions
Why is PJSIP licensed as GPL, and not (LGPL|MPL|BSD|choose your OSS license here)?
Basically we agree with FSF on this issue. Quoting the GPL FAQ:
Why should I use the GNU GPL rather than other free software licenses?
Using the GNU GPL will require that all the released improved versions be free software. This means you can avoid the risk of having to compete with a proprietary modified version of your own work.
We don't want people to take PJSIP, mess it up (erm, improve it), and keep the improvements as proprietary code. On the contrary, we want everybody to enjoy PJSIP and all its improvements, and the only way to make sure of this is by releasing PJSIP as GPL.
What about the "viral" nature of the GPL?
People often think that using GPL-ed software means that other software linked with the GPL software have to be GPL too, hence GPL is considered as viral. That is not exactly true, especially with PJSIP. When linking with PJSIP, only the application (that is based on PJSIP) has to be GPL-ed, since it is considered as derived work. Other libraries linked with PJSIP don't have to be GPL, because we specifically allow linking PJSIP with third party libraries, as long as they are listed in Third Party Software that are allowed to be linked with PJSIP.
Can I develop closed source products with PJSIP?
It depends. We use the standard GPL v2 or later for PJSIP, and GPL does allow using GPL-ed code for closed source development, as long as the resulting product is not redistributed (for example, it is only used for internal purpose). Please see GPL FAQ for more information about what can/can't be done with GPL software.
Alternatively, we can discuss alternative licensing for PJSIP, please contact support@… for details.
What are the licensing requirements of the third party libraries used by PJSIP?
Apart from the licensing requirements of PJSIP, please be aware that some third party libraries used by PJSIP put additional requirements to the end product (that is, the application using PJSIP). In particular, libraries such as these require some attribution to be put in the end product and/or the accompanying documentation:
- VAD by OpenH323 project (MPL license)
- PortAudio (BSD-ish license)
- GSM codec (BSD-ish license)
- Speex codec (BSD-ish license)
(the list above are not exhaustive, please see PJSIP Licensing page for the complete list).
In addition, the use of iLBC is governed by iLBC license. Please contact GIPS for details!
Build and Installation Issues
I'm having problem building PJSIP, please help!
Please check the Getting Started page.
I'm getting "Unable to open DSound.h error", please help!
You should really check the Getting Started page first.
Integration Issues
Can I use the SIP stack with my own media stack?
Sure. The SIP stack (pjsip) and media stack (pjmedia) is independent from one another, and they are only integrated at the higher level user agent library (pjsua-lib). Please see the library architecture diagram on PJSIP Documentation page for reference.
So yes, the SIP stack (pjsip) can be used without pjmedia, as long as you don't use pjsua-lib.
Can I use the media stack with my own SIP stack?
Yes. Please see above.
What platforms are PJSIP known to run on?
PJSIP runs on many platforms, embedded or non-embedded. Please see the platforms list that it currently supports in Platform features page. Apart from the platforms that we maintain ourselves, the community have also been constantly finding new platforms that PJSIP can run on, for example on Nintendo DS (a cool project by Samuel Vinson), and also on various platforms based on uC-Linux and ARM processor.
How can I port PJSIP to platform XYZ?
PJSIP has been designed for ultra-portability, and we have ported PJSIP to exotic platform such as Symbian (long time ago, PJSIP has also been ported to Linux kernel). People even have ported PJSIP to even more exotic platforms such as Nintendo DS (ported by Samuel Vinson) and Texas Instrument (TI) DSP. So there is a good chance that PJSIP will be port-able to your platform.
Please start from the Porting page. For additional information, there is also information on porting PJSIP to Symbian, to emphasize that even on difficult platform like this, PJSIP is still port-able.
Audio Questions
I'm having problems with no/poor audio (quality), please help!
We have created a Wiki page dedicated for troubleshooting all sorts of audio problems, please follow the instructions in Troubleshooting Sound Problems wiki. If this does not solve your problem, please consult the PJSIP mailing list.
I'm having problems with echo, please help
Please see above.
How can I reduce audio latency?
The end to end audio latency consists of the following components:
- The latency of the sound capture.
- Codec latency on both sender and receiver.
- Other algorithmic latency (such as AEC or sample rate conversion).
- Network latency.
- Jitter buffering on the receiver end.
- The latency of the sound playback.
On typical PC, normally it's the sound device and jitter buffer that contribute the most latency, so this article will try to present how to optimize these settings. Codec latency is determined by the codec algorithm and its ptime, but normally it shouldn't add too much latency; maybe around 20 to 30 ms. The default resampling algorithm in PJMEDIA adds about 5 ms latency. I have no idea about AEC latency. Network latency, well, we can't do anything about it.
Optimizing Jitter Buffer Latency
The jitter buffer parameters are specified in pjsua_media_config, with the relevant fields are:
- jb_min_pre - Jitter buffer minimum prefetch delay in msec. With the default settings, it will use the hard-coded value in stream.c, that is 60 ms.
- jb_max_pre - Jitter buffer maximum prefetch delay in msec. With the default settings, it will use the hard-coded value in stream.c, that is 240 ms.
- jb_max - Set maximum delay that can be accomodated by the jitter buffer msec. The default value is also hard-coded in stream.c, that is 360 ms.
You can tweak these jitter buffer settings according to your latency requirement. But please remember that the jitter buffer is not only used to accommodate network jitter, but also the jitter in the transmission by the sender. PJMEDIA for example, uses the sound device clock to drive its RTP transmission, and depending on the sound device clock accuracy, it may not transmit RTP packets in timely manner, thus this needs to be accommodated by the receiver's jitter buffer.
Optimizing Sound Device Latency
The default sound device back-end in PJMEDIA is PortAudio. PortAudio itself supports several types of host APIs, depending on the platform:
- On Windows, it supports both DirectSound and WMME.
- On Linux, it supports both ALSA and OSS.
- On MacOS X, it supports CoreAudio
- etc.
On Windows, DirectSound will have much better latency than WMME, so try to use DirectSound device if possible. Unfortunately, with PJSIP version 0.7.0, WMME will be used by default. But this subsequently has been fixed with ticket #384, so version later than 0.7.0 will use DirectSound if it is available (on Windows, that is).
PortAudio DirectSound has the default buffering latency set to 120 ms. This can be overridden by application, by setting PA_MIN_LATENCY_MSEC environment variable.
We can measure the sound device end-to-end latency using pjsua, by doing this experimentation:
- For this experiment, it's important to use speaker rather than headset, since we want the speaker signal to be captured by the microphone.
- Run pjsua with this command line arguments:
pjsua_vc6 --ec-tail 0 --rec-file latency.wav
- On pjsua menu, loop-back the microphone to the speaker, and record the microphone signal:
>>> cc 0 0 >>> cc 0 1
- Then tap on the microphone. You should hear it being played to the speaker too.
- Quit the application:
>>> q
Using WAV file waveform display, open latency.wav and measure the delay between the tap being captured by microphone and its echoed signal. This is the overall (microphone and speaker) delay of the sound device.
On my system, an IBM X23 laptop running Windows 2000 Professional, PortAudio DirectSound with default settings has about 320 ms latency. Setting PA_MIN_LATENCY_MSEC environment variable to 20 ms will result in reduction of the latency to about 220 ms, which is consistent with the setting.
With WMME backend, the overall latency is about 464 ms, using default settings. It seems that PortAudio sets the buffering latency to 200 ms for both input and output, in paDevInfo->defaultLowInputLatency. Unfortunately I don't know how to override this setting in application (setting PA_MIN_LATENCY_MSEC environment variable doesn't seem to change the latency). But if you want to change this setting, you can change PJMEDIA's pasound.c and hard-code inputParam.suggestedLatency and outputParam.suggestedLatency to your requirement (for example, 0.020). I have experimented with setting these to 0.020, and it reduced the latency to about 254 ms.
Your mileage may vary, of course.
How to run PJSUA without sound device?
Some PJSUA-LIB based applications do not need to interact with local sound device, thus do not need to have sound device in the deployed machine. PJSUA-LIB indeed supports running the media without sound device. With pjsua application, you can run pjsua without sound device with --null-audio option.
How can I adjust the audio volume level?
The easiest is to adjust the audio level in the conference bridge, using:
- pjmedia_conf_adjust_rx_level() and pjmedia_conf_adjust_tx_level(), if you use PJMEDIA directly, or
- pjsua_conf_adjust_rx_level() and pjsua_conf_adjust_tx_level() if you use PJSUA-API.
Note that the two different APIs have different level adjustment value; PJMEDIA expects the value in integer, while PJSUA expects the value in floating point. Please see the API documentation for the details.
Another way to manage the audio level is to use the operating system specific API to adjust the audio device level directly. Currently PJMEDIA does not provide portable APIs to interact with audio device level, so please see the operating system SDK documentation for information on how to achieve this.
Why does PJSIP refuse to send DTMF to remote?
PJSIP will only send RFC 2833 DTMF to remote if remote has indicated its capability to accept RFC 2833 events in its SDP. This is done by putting this line in the SDP:
a=rtpmap:101 telephone-event/8000
Without receiving this capability indication, PJSIP will refuse to send RFC 2833 event, and a call to pjsua_call_dial_dtmf() or pjmedia_session_dial_dtmf() or pjmedia_stream_dial_dtmf() will return error code PJMEDIA_RTP_EREMNORFC2833. In this case, you can send the DTMF tone inband instead (see below).
How can I send inband DTMF tones?
It's quite easy with PJSUA-LIB API:
- Once the call is established, create an instance of Tone Generator.
- Register this tone generator to pjsua's conference bridge, by calling pjsua_conf_add_port().
- Connect the tone generator to the call, with pjsua_conf_connect().
- Now instruct the tone generator to play some DTMF digits (pjmedia_tonegen_play_digits()). The digits then will be streamed to the call, and remote endpoint should receive the DTMF tone inband.
Here is a snippet to do it:
struct my_call_data { pj_pool_t *pool; pjmedia_port *tonegen; pjsua_conf_port_id toneslot; }; struct my_call_data *call_init_tonegen(pjsua_call_id call_id) { pj_pool_t *pool; struct my_call_data *cd; pjsua_call_info ci; pool = pjsua_pool_create("mycall", 512, 512); cd = PJ_POOL_ZALLOC_T(pool, struct my_call_data); cd->pool = pool; pjmedia_tonegen_create(cd->pool, 8000, 1, 160, 16, 0, &cd->tonegen); pjsua_conf_add_port(cd->pool, cd->tonegen, &cd->toneslot); pjsua_call_get_info(call_id, &ci); pjsua_conf_connect(cd->toneslot, ci.conf_slot); pjsua_call_set_user_data(call_id, (void*) cd); return cd; } void call_play_digit(pjsua_call_id call_id, const char *digits) { pjmedia_tone_digit d[16]; unsigned i, count = strlen(digits); struct my_call_data *cd; cd = (struct my_call_data*) pjsua_call_get_user_data(call_id); if (!cd) cd = call_init_tonegen(call_id); if (count > PJ_ARRAY_SIZE(d)) count = PJ_ARRAY_SIZE(d); pj_bzero(d, sizeof(d)); for (i=0; i<count; ++i) { d[i].digit = digits[i]; d[i].on_msec = 100; d[i].off_msec = 200; d[i].volume = 0; } pjmedia_tonegen_play_digits(cd->tonegen, count, d, 0); } void call_deinit_tonegen(pjsua_call_id call_id) { struct my_call_data *cd; cd = (struct my_call_data*) pjsua_call_get_user_data(call_id); if (!cd) return; pjsua_conf_remove_port(cd->toneslot); pjmedia_port_destroy(cd->tonegen); pj_pool_release(cd->pool); pjsua_call_set_user_data(call_id, NULL); }
Since we're allocating resources for the call, these resources must be released once the call is disconnected. We should do this in on_call_state() callback:
static void on_call_state(pjsua_call_id call_id, pjsip_event *e) { pjsua_call_info call_info; pjsua_call_get_info(call_id, &call_info); if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { call_deinit_tonegen(call_id); } }
With the above snippet, we just need to call call_play_digit() every time we need to send inband DTMF digit.
I didn't hear ringing tones when I make a call, why?
pjsua does not play any ring tones when the call gets 180/Ringing, since it's just a simple application for demonstration purpose. When you're building your own application, you can add this feature yourself. You can play a ringing tone by using the tone generator as described above (but connecting the tone generator to the sound device instead of to the call), or you can play your own WAV file instead.
Outgoing RTP transmissions are not timed equally/properly. Why?
In PJMEDIA default setup, media flow is normally triggered by the clock from the audio device. We made this kind of design to ensure that the audio device always gets fed whenever it needs to be fed, and also since this design would work best for DSP devices, where audio flow would be triggered by some kind of (soft) IRQs thus it should provide the best timing source for audio flow (and without needing to have multi-threading capability).
Unfortunately, not all audio devices provide good timing. Especially in PC world, and also with some uC-Linux based development boards that only support OSS, it is very common to have sound cards that can't provide reliable timing. On these platforms, audio frames will come in burst rather than one by one and spaced equally. So with 20ms ptime for example, rather than having one frame every 20ms, these devices would give PJMEDIA three or four frames every 60ms or 80ms. Since RTP packets are transmitted as soon as audio frame is available from the sound card, this would cause PJMEDIA to transmit RTP packets at (what looks like) irregular interval.
In my opinion, this should be fine, as the remote endpoint should be able to accommodate this with its jitter buffer.
If you don't like this, and rather want PJMEDIA to transmit RTP packets at good interval, you can install a master clock port between sound device and conference bridge, so that the master port will drive the media clock instead. A master clock port uses an internal thread to drive the media flow, so it should provide better timing on most platforms.
The steps to install master port between sound device and conference bridge are as follows:
- Create a splitter/combiner (splitcomb) port.
- Create a reverse phase port on the splitcomb (pjmedia_splitcomb_create_rev_channel()).
- Create the sound device port as usual.
- Connect the sound device port to the splitcomb (use pjmedia_snd_port_connect()).
- Create a master clock port, and specify the splitcomb's reverse channel as the upstream port, and the conference bridge as the downstream port.
- Start the master port.
For example, normally we connect the sound device to the conference bridge (the default setup in pjsua-lib) with the code below:
void connect_conf_bridge_to_snd_dev(pj_pool_t *pool, pjmedia_port *conf) { pjmedia_snd_port *snd; pjmedia_snd_port_create(..., &snd); pjmedia_snd_port_connect(snd, conf); }
The change required to install master clock between sound device and conference bridge would be something like this:
void connect_conf_bridge_to_snd_dev2(pj_pool_t *pool, pjmedia_port *conf) { pjmedia_port *splitcomb, *rev; pjmedia_snd_port *snd; pjmedia_master_port *m; pjmedia_splitcomb_create(pool, CLOCK_RATE, 1, SAMPLES_PER_FRAME, BITS, 0, &splitcomb); pjmedia_splitcomb_create_rev_channel(pool, splitcomb, 0, 0, &rev); pjmedia_snd_port_create(..., &snd); pjmedia_snd_port_connect(snd, splitcomb); pjmedia_master_port_create(pool, rev, conf, 0, &m); pjmedia_master_port_start(m); }
(Note that with PJSUA-LIB, we can get the instance of the conference bridge by calling pjsua_set_no_snd_dev()).
With the above snippet, the irregularity of the sound device clock will be normalized by the master port, so the conference bridge will be run by the good clock. Of course this means some buffering is needed to compensate the clock difference between the sound device and the master port, and this is what the reverse channel of the splitcomb is for. The reverse channel can accommodate up to PJMEDIA_SOUND_BUFFER_COUNT frames, so if the clock difference is larger than this, you will need to enlarge PJMEDIA_SOUND_BUFFER_COUNT to an acceptable value (32, for example).
Does PJSIP support G.723 or G.729 codecs?
Yes and no.
No, because there is no ready to use G.723/G.729 codec implementation in PJMEDIA. We specifically don't include G.723/G.729 support in our code because G.723/G.729 are both patented and royalty based codecs, so we are quite nervous with the possibility that some lawyers may contact us should we include them in PJMEDIA. So our decision is to include only free and open source codecs in PJMEDIA (such as G.711, GSM, and Speex).
But yes, because you can always add more codecs in PJMEDIA. Please see Adding a new codec question below for more info.
How can I add new codec to PJMEDIA?
First of all, read the Codec Framework documentation. Then, the easiest is to take other codec source file in pjmedia-codec directory, such as gsm.c, and replace GSM specific function calls with the functions that are provided by your codec library.
There are two "classes" to implement:
- The codec factory (pjmedia_codec_factory) - Codec factory will be queried by PJMEDIA to see if it can instantiate a codec instance based on a codec descriptor (pjmedia_codec_info). Codec factory also provides information about what particular codecs it supports, so that PJMEDIA can list these codecs in the local SDP capability descriptor.
- The codec itself (pjmedia_codec) - The codec instance provides functions to parse, encode and decode audio frames. Optionally it may provide a function to recover lost frames (known as Packet Lost Concealment feature, or PLC).
Once it's finished, you should end up with just two public APIs exported by the implementation: an initialization function, and a deinitialization function. The initialization function's primary task is to register the codec factory to PJMEDIA's codec manager, so that PJMEDIA knows about this new codec. While the deinitialization function is to unregister the codec factory, and to release resources, if any.
Then call the codec initialization function in the application. After this, the codec should be picked up automagically by the rest of PJMEDIA framework (that is, PJSIP should advertise the codec in outgoing SDP and negotiate it with remote's SDP, and encode/decode audio frames with the codec, if the codec is selected for the session).
How can I manipulate audio samples directly?
In PJMEDIA, audio frames are sent back and forth between what is called media port (pjmedia_port). So to be able to peek or manipulate audio frames, we need to implement our own media port.
Implementing media port should be easy. Basically we just need to implement these:
- Create a media port structure, deriving from pjmedia_port structure:
struct my_media_port { pjmedia_port base; // Your data goes here: ... };
- Fill in the media port information to describe the media port (like, the name, clock rate, bits per sample, etc.). Use pjmedia_port_info_init() to initialize the port into.
- Implement get_frame() callback (of the pjmedia_port) if the media port is a source (that is, the media port feed audio frames to other media ports).
- Implement put_frame() callback (of the pjmedia_port) if the media port is a sink (that is, other media ports may feed audio frames to our media port).
- Implement on_destroy(), if you need to reclaim resources when the media port is destroyed.
There are many media port sample implementations in PJMEDIA.
For source only media ports, samples include:
- playsine.c from the pjsip-apps/samples directory.
- mem_player.c from pjmedia (media port to playback audio from a buffer).
- wav_player.c from pjmedia (media port to playback audio from WAVE file).
- tonegen.c from pjmedia (media port to generate sine waves/DTMF/multi-frequency tones).
For sink only media ports, samples include:
- mem_capture.c from pjmedia (media port to save audio to a buffer).
- wav_writer.c from pjmedia (media port to save audio to a WAVE file).
For media ports that manipulates audio and provide both sink and source callbacks:
- resample_port.c from pjmedia (to convert sampling rate)
- echo_port.c from pjmedia (the AEC)
I always get "Bad RTP pt" error. Why?
From our experience, this can be caused by one of these:
- Remote endpoint has agreed to use one codec in the SDP negotiation, but it sends RTP with different codec. This had happened with some old version of a popular but not open source softphone (don't want to name it to protect the innocent). When this happens, the "Bad RTP pt" error message will be printed continuously in the log or screen (basically for every RTP packet!). The remedy in this case was to upgrade that softphone to a new version.
- Remote endpoint is sending a comfort noise packet. When this happens, the error message is not printed as often as the other case (maybe once in every few seconds).
When you encounter this problem, and if upgrading the softphone doesn't solve the problem, you can report this to PJSIP mailing list. When reporting, please include the error log and the complete INVITE message and the 200/OK response (containing the SDP), so that we can analyze which endpoint is behaving badly.
Does PJSIP support SRTP or ZRTP? If no, how can I implement them?
Currently PJMEDIA doesn't support SRTP nor ZRTP, although this has been on our TODO list for sometime now. Fortunately, implementing them is not too difficult, since media transport in PJMEDIA is separated from the stream.
Have a look at:
- transport_udp.c, the normal UDP media transport, and
- transport_ice.c, ICE media transport.
There, it's quite easy to see where RTP/RTCP packets are received (that is in on_rx_rtp()/on_rx_rtcp() respectively) or sent (in transport_send_rtp()/transport_send_rtcp()). So these are the locations where you should call the decryption and encryption function of SRTP/ZRTP.
Alternatively, there is a cleaner way to plug in SRTP/ZRTP functionality without changing existing code. As mentioned earlier, media transport is separated from stream. They are only attached to each other in application code (or in pjsua-lib). So then one can build an adapter, which is plugged between the media transport and the stream to do the SRTP/ZRTP stuffs. To the stream, this adapter will look like a media transport, and to existing media transport, this adapter will look like a stream. The benefit of this approach is we can use the same adapter for both kind of media transports, that is the UDP and ICE media transport.
Video Questions
Does PJSIP support video?
Unfortunately no at the moment. Supporting video has been in our TODO list for long time (more than a year!), but so far we haven't had enough time and commitment to do it.
When can I expect PJSIP to support video?
We're not sure.
How can I add video support in PJSIP?
Since video stream is independent from audio stream, the simplest "integration" would be to not integrate video with pjmedia at all. In other words, the application would have to manage everything related to video (capture, render, codec, and RTP) by itself, separate from audio stream which is managed by pjmedia. Maybe you can use the media transports supplied by pjmedia, and the RTP module to pack/unpack RTP packets, to help you up a little bit, but apart from these, basically you'll have to do everything on your own.
Also some processing needs to be done on the SDP to support video. So basically, for outgoing SDP, application has to add video information in the SDP after the SDP is created, and it has to parse video related information in the incoming SDP by itself.
And perhaps if synchronization between audio and video is needed, the application then will need to peek the RTP packet coming to the audio stream. This can be done perhaps by modifying the media transport, or by installing media transport adapter as described in SRTP/ZRTP question above.
SIP Questions
Does PJSIP support RFC XYZ?
The best way is to look at the PJSIP Features page and see if that particular feature is supported by PJSIP.
I'm having problem with calling XYZ, please help!
Please report the problem to PJSIP mailing list. Please provide the following information:
- Please describe in detail what are you trying to accomplish and how do you configure your equipments (for example, do you use proxy? What kind of proxy?).
- It would be best to try to reproduce the problem with pjsua, so that we can also try to reproduce your problem.
- In any case, please provide the complete log file. If you use pjsua, you can configure it to write log file with --log-file option. If you use your own application that is based on pjsua-lib, you can write the log by setting pjsua_logging_config.log_filename field.
I cannot login/REGISTER to my server. It complains about authentication error.
Most likely this is caused by wrong credential in the configuration. The remedy depends on what error was reported by PJSIP.
For PJSIP_ENOCREDENTIAL error:
- This error is caused by the realm specified in the credential doesn't match the realm challenged by the server in the 401/407 response. If you use PJSIP version 0.7-trunk or PJSIP version 0.7.1 or later, you can put wildcard ("*") as the realm to make PJSIP respond to any realms challenged by the server. If you use older PJSIP, you have to match the realm in the credential with the realm in the challenge. The realm normally would be equal to the domain name, but it doesn't have to. Asterisk, for example, always set the realm to "asterisk".
For PJSIP_EFAILEDCREDENTIAL error:
- If you encounter this error, most likely it was caused by a wrong credential. Check if the username and password combination is correct.
How can I attach user specific data to a call?
It depends on what user specific data means.
If user data means an application data, normally a pointer to your application data structure, to be associated with a call, which you want to retrieve later, you can attach this application data with one of these:
- For outgoing calls, specify the pointer in user_data argument when calling pjsua_call_make_call().
- For incoming calls, call pjsua_call_set_user_data() in on_incoming_call() callback to attach your user data.
Once application data is attached to a call, you can retrieve it at any time by calling pjsua_call_get_user_data().
Now for a different view on the question.
If by user data you mean to send custom SIP headers in outgoing SIP messages, you can put the custom headers in the pjsua_msg_data structure, which you can specify when sending SIP requests (for example, when sending INVITE, IM message, etc.). Here is a snippet on how to put a custom SIP header as pjsua_msg_data:
.. pjsua_msg_data msg_data; pjsip_generic_string_hdr my_hdr; pj_str_t hname = pj_str("My-Header"); pj_str_t hvalue = pj_str("This is the content of My-Header"); pjsua_msg_data_init(&msg_data); pjsip_generic_string_hdr_init2(&my_hdr, &hname, &hvalue); pj_list_push_back(&msg_data.hdr_list, &my_hdr); // Specify the msg_data in pjsua_im_send(), for example pjsua_im_send(.., &msg_data, NULL);
The above snippet will add "My-Header: This is the content of My-Header" to outgoing IM request. More than one headers can be added to pjsua_msg_data, of course.
With ICE enabled, INVITE packet can exceed MTU/1500 bytes. How can I reduce the message size?
Please see ticket #342 for info. Basically there are few settings to configure to make PJSIP sends smaller packet:
- Configure PJSIP to send compact form of SIP headers, by setting PJSIP_ENCODE_SHORT_HNAME macro to 1 in config_site.h. This will reduce SIP message size by approximately 50 bytes.
- Suppress the inclusion of Allow header in outgoing requests, by setting PJSIP_INCLUDE_ALLOW_HDR_IN_DLG macro to 0 in config_site.h. This will reduce SIP message size by approximately 86 bytes.
- Suppress the inclusion of SDP rtpmap attribute for static payload types, by setting PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT macro to 0 in config_site.h. Note that this setting should not cause bad effects for communication, since SDP rtpmap attributes for static payload types are optional. This will reduce SIP message size by approximately 65 bytes.
- Disable RTCP (advertisement) in SDP, by setting PJMEDIA_ADVERTISE_RTCP macro to 0 in config_site.h. When RTCP is disabled, no RTCP packets will be sent or received, and this will cause some RTCP TX statistics (including RTT report) to be unavailable. Other RTCP statistics such as RX statistics, as well as number of TX packets, will still be available since these values are generated locally. Disabling RTCP will reduce SIP message size by approximately 235 bytes for ICE with three candidates.
- Last resort, disable some unused network interfaces in the system to reduce the number of ICE candidates advertised in SDP. On Windows for example, it's quite common to have some Loopback network interface, and disabling it doesn't seem to cause (too much) harm.
How can I instantiate multiple PJSIP stacks in my application?
You don't need to. This may be necessary with other SIP stacks, but PJSIP inherently supports multiple identities (or accounts) throughout the libraries, so one instance of the stack can be used to perform multiple, separate registration and invite sessions.
How can I support INFO method or other SIP methods not mentioned in PJSIP?
Creating SIP method in pjsip is very simple:
const pjsip_method info_method = { PJSIP_OTHER_METHOD, { "INFO", 4 } };
You can then create an independent request or request within dialog of this method and send it statelessly, statefully, or inside dialog. The Dev Guide PDF will explain this in more detail.
NAT Questions
What NAT features does PJSIP support?
Quite a few:
- On the SIP side, PJSIP supports Symmetric Response Routing (rport) and STUN.
- On the media side, application can choose the normal UDP media transport (with STUN support) or the ICE media transport.
- For the media, the media stream will periodically transmit RTP packets during silence period, to keep NAT binding open. Also it will disable VAD for the first few hundred milliseconds after it is started, to hole-punch any NATs in between the endpoints. Please see PJMEDIA_CODEC_MAX_SILENCE_PERIOD and PJMEDIA_STREAM_VAD_SUSPEND_MSEC settings on how to fine tune this behavior.
- With ICE media transport, the transport periodically sends STUN keep-alive requests to keep NAT binding open, throughout the life of the application. The transport will also update its mapped address should the binding has changed. Note that this feature is not available for UDP media transport.
Which STUN-bis draft does PJNATH support?
At the time of this writing, draft-ietf-behave-rfc3489bis-10. But since STUN-bis (rfc3489bis) is updated often, please look at PJNATH documentation for the most up to date information.
Which ICE draft does PJNATH support?
At the time of this writing, draft-ietf-mmusic-ice-18. But since ICE is updated often, please look at PJNATH documentation for the most up to date information.
Which TURN draft does PJNATH support?
At the time of this writing, draft-ietf-behave-turn-03. But since TURN is updated often, please look at PJNATH documentation for the most up to date information.
What STUN-bis features does PJNATH support?
Currently everything in RFC3489bis, including MESSAGE-INTEGRITY and FINGERPRINT. However PJNATH currently does not support STUN over TCP.
Is there any simple STUN/TURN client which I can use to test my STUN/TURN implementation?
Yes. There is a simple STUN/TURN client implementation in pjnath/src/pjstun-client directory, which will be built by default by the make command. If you use Visual Studio, you can build this application from within pjnath/build/pjnath.dsw workspace.
This is a simple command line program to perform as STUN or TURN client. It supports STUN, TURN, short-term or long term credential, as well as FINGERPRINT mechanism.
Is there any simple STUN/TURN server in the samples?
Yes. There is a simple STUN/TURN server implementation in pjnath/src/pjstun-srv-test directory, which will be built by default by the make command. If you use Visual Studio, you can build this application from within pjnath/build/pjnath.dsw workspace.
This is a simple command line program to perform as STUN or TURN server. It supports STUN, TURN, short-term or long term credential, as well as FINGERPRINT mechanism.
Performance
How can I reduce the CPU usage/maximize the performance of my application?
There are few configuration settings to tweak to reduce the CPU usage of the application or to produce the best performance out of pjsip:
- Echo canceller. The software AEC probably is the most CPU intensive module in PJSIP. To reduce the CPU usage, shorten the EC tail length to lower value (the pjsua_media_config.ec_tail_len setting), or even disable it altogether by setting pjsua_media_config.ec_tail_len to zero.
- Codec. Use low complexity codec such as pcmu or pcma. When using pcmu or pcma, make sure pjmedia chooses the table based implementation, by setting PJMEDIA_HAS_ALAW_ULAW_TABLE macro to 1 (the default is normally enabled, but it may worth double-checking).
- Avoid resampling. Resampling is a CPU intensive process, thus it should be avoided if you can, by choosing uniform clock rate for all media components (sound device, conference bridge, codecs, WAV files, etc.).
- Choose effective sampling rate. Make sure that PJSUA-LIB selects the most effective sampling rate/clock rate for your application. For example, if the application only supports narrowband codecs (G.711, GSM, iLBC, G.723, or G.729), then the best sampling rate to choose would be 8KHz. Choosing higher sampling rate will only just waste CPU power since with higher sampling rate, the more processing is needed. With pjsua, sampling rate can be forced with --clock-rate option. In the application, this can be achieved by setting pjsua_media_config.clock_rate field.
- Logging. The default logging level is 5, which provides verbose information just in case some debugging is needed. When absolute performance is needed, you can decrease the logging verbosity level to 3 so that only vital information is displayed. This can be done by, either at compile time by setting PJ_LOG_MAX_LEVEL macro (in config_site.h as usual), or at run-time by calling pj_log_set_level().
- Threads. Use the optimum number of SIP worker threads in the application. The optimum number would be equal to the number of processors (or processor cores) in the system.
- Run-time checks. All the libraries are equipped with run-time checks to prevent bad parameters from crashing the software. This feature can be disabled by setting PJ_ENABLE_EXTRA_CHECK to zero.
- Stack checks. PJLIB is equipped with stack overflow detection. This feature can be disabled by setting PJ_OS_HAS_CHECK_STACK to zero.
- Safe module. PJSIP is equipped with mutex protection to protect PJSIP modules from being unregistered while they are still being accessed by PJSIP. If the application doesn't add/remove modules dynamically during run-time, you can disable this protection by setting PJSIP_SAFE_MODULE to zero.
- Unescape in place. By default, PJSIP will make a copy of escaped message sequence before unescaping it. You can configure PJSIP to unescape in place by setting PJSIP_UNESCAPE_IN_PLACE to one. Note that unescaping in place will modify the original message, so don't do this if the application needs to access the original message after it has been parsed (pjsip does not need this access).
- Hash tolower optimization. By setting PJ_HASH_USE_OWN_TOLOWER to one, the hash function will convert the key to lower case and calculate the hash value in one loop.
- Release mode. Don't forget to set the appropriate compiler optimization flag, and disable assertion with -DNDEBUG.
How can I configure pjsip to serve thousands of calls?
There are few settings to tweak:
- First apply the CPU reduction techniques above to maximize the performance.
- Do not use PJSUA-LIB, for now. Please see the issues with using PJSUA-LIB to serve large number of calls below.
- By default, PJSIP is configured to handle only 16384 simultaneous SIP transactions and dialogs. This should be enlargeed according to the requirement, by setting both PJSIP_MAX_TSX_COUNT and PJSIP_MAX_DIALOG_COUNT to the appropriate values (for example, 640*1024-1).
- If large number of TCP/TLS connections are needed, increase PJ_IOQUEUE_MAX_HANDLES to some large number (the default is only 64).
- We've found that the simple GUID generator (used by GNU build system for *nix and MacOS X) will produce duplicate Id after approximately 214 generations. This would cause things like transactions to have duplicated branch as previous transactions! On Linux, the ./configure script will detect the presence of libuuid (part of e2fsprogs) and use it if available, to avoid this problem. If you encounter this problem, please check if libuuid is available for ./configure on your system.
I try to use PJSUA-LIB to develop a server like application, but performance is poor
That is quite expected, unfortunately. PJSUA-LIB was initially aimed to build user agent client applications, so we didn't design it to handle large number of calls.
The main reason why scalability is bad with PJSUA-LIB is because it uses single conference bridge to terminate all media (calls, WAV files, tone generators, etc.). A conference bridge runs strictly on one thread, which can be the sound device thread (the so called sound device clock) or just a thread if no sound device is initialized. Because of this single thread restriction, it's quite easy to understand why PJSUA-LIB does not scale very well.
We are currently looking for other design possibilities to make PJSUA-LIB scale better (suggestions are welcome!). In the meantime, perhaps you can use this approach to make the application scale better.
Basically the idea is to manage the in the application, rather than in PJSUA-LIB. Since we manage the media ourselves, we can design the media interconnection to whatever suit the requirements. One possible design is to create one conference bridge for each call, so that each call's media (including anything related to the call, such as WAV files, tone generators, outgoing call leg, etc.) runs on its own thread.
Some steps to accomplish this are as follow:
- First thing we should do is to stop the PJSUA-LIB's conference bridge. This can be accomplished by calling pjsua_set_no_snd_dev(). With this call, PJSUA-LIB would not connect the conference bridge to any sound devices (including NULL sound device), so basically it would not run.
- Because the main conference bridge is no longer working, we cannot use the usual PJSUA-LIB's API to add media ports (thing such as pjsua_player_create()), since these API adds the media ports to the main bridge, which no longer works of course.
- When a call's media is established (like when the call is CONFIRMED), PJSUA-LIB will still register the call's stream to the main bridge, but we can just ignore this. The main bridge has been stopped, so although the stream is registered to it, it won't try to retrieve or send any audio to the stream.
- Since we're not using the main bridge anymore, the call's slot number in pjsua_call_info.conf_slot should not be used anymore.
- Now that the main bridge has been stopped, we can manage the media ourselves. We can create one conference bridge for each call, and attach it to a master port to make it run:
struct call_data { pj_pool_t *pool; pjmedia_conf *conf; pjmedia_port *cport; pjmedia_port *null; pjmedia_master_port *m; int call_slot; }; static void call_media_init(pjsua_call_id call_id) { pj_pool_t *pool; struct call_data *cd; pj_status_t status; pool = pjsua_pool_create("mycall", 1000, 1000); cd = PJ_POOL_ZALLOC_T(pool, struct call_data); cd->pool = pool; pjsua_call_set_user_data(call_id, (void*)cd); status = pjmedia_conf_create(pool, ..., PJMEDIA_CONF_NO_DEVICE, &cd->conf); cd->cport = pjmedia_conf_get_master_port(cd->conf); status = pjmedia_null_port_create(pool, ..., &cd->null); status = pjmedia_master_port_create(pool, cd->null, cd->cport, 0, &cd->m); status = pjmedia_master_port_start(cd->m); }
- Add call's media to the conference bridge. Unfortunately call's media is not normally exposed to PJSUA application, so we would need to include <pjsua-lib/pjsua_internal.h> to access it:
#include <pjsua-lib/pjsua_internal.h> .. pjsua_call *call = &pjsua_var.calls[call_id]; pjmedia_port *stream; pjmedia_session_get_port(call->session, 0, &stream); pjmedia_conf_add_port(cd->conf, cd->pool, stream, NULL, &cd->call_slot);
- Add other media ports to the conference bridge as necessary.
- Since we're allocating resources for the call, don't forget to release these resources once the call is disconnected:
static void call_media_deinit(pjsua_call_id call_id) { struct call_data *cd; cd = (struct call_data*) pjsua_call_get_user_data(call_id); if (!cd) return; pjmedia_master_port_stop(cd->m); pjmedia_master_port_destroy(cd->m, PJ_FALSE); pjmedia_conf_destroy(cd->conf); pjmedia_port_destroy(cd->null); ... pjsua_call_set_user_data(call_id, NULL); } static void on_call_state(pjsua_call_id call_id, pjsip_event *e) { pjsua_call_info call_info; pjsua_call_get_info(call_id, &call_info); if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { call_media_deinit(call_id); } }
As mentioned earlier, we are currently looking for alternative design to arrange the media for better scalability, so if you have more ideas, they're most welcome!
Footprint
How can I reduce the footprint and heap usage of my application?
With the default settings, PJSIP is not really optimized for size (nor speed), since the main objective is to have sufficient information for troubleshooting. Taking Windows as reference platform, the executable size of Release build with Visual C++ 6 is as follows:
pjsua_vc6.exe: file size=749,672 bytes 241664 .data 102400 .rdata 24576 .reloc 536576 .text (all numbers in decimal, bytes)
Note: with default settings, pjsua will include PJNATH/ICE implementation as well as many media goodies (e.g. all codecs), statically linked with the application, which you may not need in your embedded application. Hence the executable size is a bit large for pjsua.
The heap usage with 2 connected calls using Speex wideband codec and AEC enabled (press dd in pjsua menu, and scroll up to the portion where it dumps the memory pool statistic):
>>> dd .. Total 908508 of 1046304 (86 %) used! ..
To reduce the executable size, you can apply the following settings:
- Reduce logging verbosity. The default logging level is 5, which provides verbose information just in case some debugging is needed. You can decrease the logging verbosity level to 3 so that only vital information is displayed, by setting PJ_LOG_MAX_LEVEL macro (in config_site.h as usual). This will reduce executable size by approximately 28 KB.
- Turn off logging. Alternatively you can disable logging altogether, by setting PJ_LOG_MAX_LEVEL to 0. This will reduce executable size by another 28 KB.
- Disable Speex AEC, by setting PJMEDIA_HAS_SPEEX_AEC=0, to reduce executable size by 32 KB.
- Disable resampling, by setting PJMEDIA_HAS_LIBRESAMPLE=0, to reduce executable size by 45 KB.
- Disable unused codecs. To leave with only G.711 codecs (pcma and pcmu), disable speex with PJMEDIA_HAS_SPEEX_CODEC=0, disable iLBC with PJMEDIA_HAS_ILBC_CODEC=0, disable GSM codec with PJMEDIA_HAS_GSM_CODEC=0, and disable L16 codecs with PJMEDIA_HAS_L16_CODEC=0. This will reduce executable size by approximately 114 KB.
- Disable alaw/ulaw table. By default, a table based alaw/ulaw implementation is used. You can disable this by setting PJMEDIA_HAS_ALAW_ULAW_TABLE=0, which makes PJMEDIA to calculate the alaw/ulaw value rather than using the table. This will reduce executable size by approximately 28 KB.
- Disable error string. All libraries keep the description of the error codes in some static variables. You can omit this error description by setting PJ_HAS_ERROR_STRING=0. When the error description is omitted, pj_strerror() will just print the error code rather than the error description. You can then look for the error description in libraries source codes, or by searching on PJSIP website. Omitting the error description will reduce executable size by approximately 20 KB.
- Disable run-time checks. All the libraries are equipped with run-time checks to prevent bad parameters from crashing the software. You can disable this feature by setting PJ_ENABLE_EXTRA_CHECK to zero. This will reduce executable size by approximately 20 KB.
- Disable stack checks. PJLIB is equipped with stack overflow detection. You can disable this feature by setting PJ_OS_HAS_CHECK_STACK to zero, to reduce executable size by approximately 4 KB.
- Disable CRC32 table, by setting PJ_CRC32_HAS_TABLES=0, to reduce executable size by about 1 KB, only if you use ICE.
- Use your own sound device abstraction, rather than PortAudio. If you are porting PJSIP to an embedded platform, you will need to create your own sound device abstraction. So supposing we don't use PortAudio and use the NULL sound device implementation (PJMEDIA_SOUND_IMPLEMENTATION=PJMEDIA_SOUND_NULL_SOUND), we will reduce executable size by approximately 49 KB.
With all above optimizations set, we now have pjsua size (still with ICE/PJNATH and many media goodies like conference, WAV, etc. statically linked in the executable):
pjsua_vc6.exe: file size= 381,032 bytes 184320 .data 8192 .rdata 16384 .reloc 319488 .text
Using the same settings, if we take the executable size of simpleua.exe (this is a sample program to do simple call with audio, without conference bridge nor ICE/STUN):
simpleua.exe: file size= 155,648 bytes 28672 .data 4096 .rdata 139264 .text
At this point, the heap memory usage of pjsua with 2 calls has been reduced by about 100 KB:
>>> dd .. Total 793624 of 909024 (87 %) used! ..
We still want more heap usage reductions, of course!
Now, assuming that the product will only need to support, say, 8 calls, we can apply these settings to reduce heap memory usage:
- Transaction/dialog/call count. Set the maximum number of concurrent transactions/dialogs/calls with
# define PJSIP_MAX_TSX_COUNT 31 # define PJSIP_MAX_DIALOG_COUNT 31 # define PJSUA_MAX_CALLS 31
- Optimize pool sizes. These settings not only will reduce heap memory usage, but will also prevent the libraries from allocating too many large memory blocks. With the default settings, most memory pools are configured to allocate memory in 4KB blocks, and some system like Symbian will have difficulties in providing these blocks to PJSIP. Use the following setting to reduce the memory block size used by memory pools, at the expense of more calls to system's memory allocators (new or malloc) to allocate memory:
# define PJSIP_POOL_LEN_ENDPT 1000 # define PJSIP_POOL_INC_ENDPT 1000 # define PJSIP_POOL_RDATA_LEN 2000 # define PJSIP_POOL_RDATA_INC 2000 # define PJSIP_POOL_LEN_TDATA 2000 # define PJSIP_POOL_INC_TDATA 512 # define PJSIP_POOL_LEN_UA 2000 # define PJSIP_POOL_INC_UA 1000 # define PJSIP_POOL_TSX_LAYER_LEN 256 # define PJSIP_POOL_TSX_LAYER_INC 256 # define PJSIP_POOL_TSX_LEN 512 # define PJSIP_POOL_TSX_INC 128 # define PJMEDIA_SESSION_SIZE 1000 # define PJMEDIA_SESSION_INC 1000
With these settings applied, heap memory usage will be reduced very significantly. Looking at heap memory usage of pjsua with two G.711 calls:
pjsua_vc6 --clock-rate 8000 --ec-tail 0 --max-calls 2 --no-tcp >>> dd .. Total 120532 of 150344 (80 %) used! ..
So only about 150 KB for two calls, I think that should be affordable.
Windows Specific Questions
How can I use PJSIP in .NET applications?
For this, you will need to have ActiveX wrapper for PJSIP. Unfortunately, at this point we don't provide ActiveX projects for PJSIP, but you can use one from this project:
- Sipek Phone project, created by Sasa Coh, is a project to build a SIP softphone using .NET, and it has an PJSIP ActiveX wrapper as part of the project.
Windows Mobile Specific Questions
How can I build PJSIP for WinCE/Windows Mobile?
Have a look at this Getting Started | Windows Mobile page.
Linux/uC-Linux Specific Questions
How can I build PJSIP for Linux/uC-Linux?
Have a look at this Getting Started | GNU page.
MacOS X Specific Questions
How can I build PJSIP for MacOS X?
Have a look at this Getting Started | GNU page.
Symbian Specific Questions
How can I build PJSIP for Symbian?
We have provided a thorough instructions at the PJSIP for Symbian page, please check it out.
Miscellaneous
How can I use PJSIP in Python?
Start with this small PJSIP Python Tutorial page. It's not a comprehensive tutorial by any means, but hope that can give you a start.
How can I use PJSIP in TCL?
Antonio F. Cano Damas and Mats Bengtsson have contributed TCL binding for PJSIP, please have a look in the PJSIP Contribution page.