A winning team
250+ partnerships for progress.
01/Apr/2025
This article provides tips, pointers, and code snippets to help you get started with WebRTC/SIP using OpenSIPS and RTPEngine. While implementation of OpenSIPs and RTPEngine like WSS to SIP or SIP to WSS audio not transmit even call is not connecting so implement this tutorial which help some what help you.
In the initialisation section of opensips.cfg
A listen statement is required to make opensips accept websocket connections. The usual port is 443, but you can use a different port if you want. Generally, you will also want to add an alias matched to the published hostname. I also recommend that you consider setting a value for maximum file handles as this directly impacts the number of simultaneous connections possible on one server. The default value may not be enough.
open_files_limit=4096
alias=wss:myhostname.com:7443
listen=wss:192.168.1.1:7443
In the module loading section
Module need to load for configuration file.
loadmodule "proto_udp.so"
loadmodule "proto_tls.so
loadmodule "proto_wss.so"
loadmodule "tls_mgm.so
loadmodule "rtpengine.so"
loadmodule "nathelper.so"
Below are some suggested module parameter settings that you may find useful. This is not an exhaustive list but rather a guideline to ensure you consider including them. The values in the following code snippet are taken from my test server and may not be suitable for your specific setup. Be sure to evaluate and assign values based on your requirements.
modparam("proto_wss", "wss_max_msg_chunks", 16)
modparam("tls_mgm", "tls_method", "SSLv23")
modparam("tls_mgm", "verify_cert", "0")
modparam("tls_mgm", "require_cert", "0")
modparam("rtpengine", "rtpengine_sock", "udp:123.4.5.68:2123")
Main Route Guidelines
This section provides essential guidelines and code snippets to assist with handling requests efficiently. Over time, additional details may be added. Feel free to leave feedback in the comments if you’d like more information on specific topics.
Source Type Identification
For all incoming requests, determine the transport protocol and set a corresponding flag to retain and reference this information throughout the route:
kamailio
Copy
Edit
if ($pr == "ws" || $pr == "wss") {
setflag(SRC_WS);
} else {
setflag(SRC_SIP);
}
NAT Handling
WebSocket connections should always be treated as originating from devices behind NAT. Functions like fix_nated_register() and fix_nated_contact() help correct SIP requests automatically.
For registrations from NATed devices, use branch flags to identify and store this information in the location table. If your server handles both traditional SIP and WebRTC devices, you may need two branch flags to distinguish:
WebRTC registrations
NATed SIP registrations
Key Considerations for Handling Initial INVITE Requests
When processing an initial INVITE request:
Create a new dialog (requires the DIALOG module).
Insert a Record-Route header (requires the RR module).
Set up branch routing (t_on_branch) and define a reply handler (t_on_reply).
Use the topology hiding module for security and to reduce SIP packet size, which is crucial for WebRTC.
If topology hiding is not used, ensure there’s logic to handle loose-routed requests properly.
Additionally, sequential requests (such as re-INVITEs) may need to interact with RTPEngine. A common sub-route can be used to relay both initial and loose-routed requests while managing RTPEngine processing.
Media Proxying with RTPEngine
Configuring RTPEngine correctly is often a challenge for VoIP engineers setting up WebRTC with OpenSIPS or Kamailio. While documentation exists, working examples are scarce. Below is a practical example to help.
Since RTPEngine activation needs to be checked at multiple points, the following sub-route centralizes the logic by setting a flag (USE_RTPE), which can be referenced elsewhere in the script:
kamailio
Copy
Edit
route[RTPE_CHECK] {
if (isflagset(SRC_WS) || isflagset(SRC_SIP)) {
setflag(USE_RTPE);
}
}
By implementing this structured approach, your WebRTC and SIP handling will be more efficient and maintainable.
route[IS_RTPE_REQUIRED] {
# Sets the flag to show if RTPEngine is required
if (isbflagset(WS_DEVICE)) {
# WebRTC destination
setflag(USE_RTPE);
} else {
# SIP destination
if (isflagset(SRC_SIP)) {
# Check SDP for rfc1918 addresses - some natted SIP user devices need RTPEngine
if (nat_uac_test("8") || isbflagset(NATTED_CLIENT))
setflag(USE_RTPE);
} else
setflag(USE_RTPE);
}
}
The scope of a transaction flag in OpenSIPS is limited to the handling of the current request so I found it useful to duplicate the function of this flag in a dialog flag that could be interrogated in sequential requests or responses. It may also be useful to add a distinctive parameter to the Record-Route headers because this allows you to use the function check_route_param() in subsequent sequential loose-routed requests to see if a call is using RTPEngine. For example, by calling add_rr_param(“;RTPE=Y”) in the initial INVITE request, would make it possible to check the BYE request with check_route_param(“RTPE=Y”). [Useful because dialog flags don’t always behave as you want at the end of the dialog when BYE is received].
It’s a good idea to activate RTPEngine in a branch route and not in the main route of your code. This is what my branch route looks like:
branch_route[1] {
if (isflagset(USE_RTPE) && is_method("INVITE|UPDATE") && has_body("application/sdp"))
route(rtpengine_offer);
}
You will need to call RTPEngine_answer from your onreply handler. Here is a snippet from my handler:
if (isflagset(USE_RTPE)) {
if (t_check_status("(183)|(200)") && has_body("application/sdp"))
route(rtpengine_answer);
}
Now you just need to know what the two sub-routes look like that deal with activation and control of RTPEngine. The parameters shown below may include options that are not strictly necessary. For example, you may not actually need to include “replace-session-connection”. So why did I include them? Because I tried so many combinations and variations, with mixed results, that it became necessary to work more methodically and not to take anything for granted. So I made some options explicit rather than implicit just for clarity (and for the sake of my own sanity). You may also not want to use the same transcode options as are shown in my examples for the SIP-to-web connection.
route[rtpengine_offer] {
if (isflagset(SRC_WS) && isbflagset(WS_DEVICE))
# - Web to web
$var(reflags) = "trust-address replace-origin replace-session-connection SDES-off ICE=force";
else if (isflagset(SRC_WS))
# - Web to SIP
$var(reflags) = "trust-address replace-origin replace-session-connection rtcp-mux-demux ICE=remove RTP/AVP";
else if (isbflagset(WS_DEVICE))
# - SIP to web
$var(reflags) = "trust-address replace-origin replace-session-connection rtcp-mux-offer ICE=force transcode-PCMU transcode-G722 SDES-off UDP/TLS/RTP/SAVP";
else
# - SIP to SIP
$var(reflags) = "trust-address replace-origin replace-session-connection rtcp-mux-demux ICE=remove RTP/AVP";
rtpengine_offer("$var(reflags)");
}
route[rtpengine_answer] {
if (isflagset(SRC_WS) && isbflagset(WS_DEVICE))
$var(reflags) = "trust-address replace-origin replace-session-connection SDES-off ICE=force";
else if (isflagset(SRC_WS))
$var(reflags) = "trust-address replace-origin replace-session-connection rtcp-mux-require ICE=force RTP/SAVPF";
else
$var(reflags) = "trust-address replace-origin replace-session-connection rtcp-mux-demux ICE=remove RTP/AVP";
rtpengine_answer("$var(reflags)");
}
How do you test it?
Testing your WebRTC solution can be difficult. I found that results would depend not only on the chosen client and browser, but I even got different results on different laptops, PC’s and mobile phones. As you would expect, the network environment makes a difference too. For example, you should test Web-to-Web calls where both devices are on the same LAN and also where one device is on your LAN and the other is on a remote site depending entirely on access via the Internet. Test calls in both directions too.
I found a small number of free-to-try WebRTC clients that were useful for testing, although you need to avoid any that are not being kept updated because this is an evolving technology. Here are two links you may want to check.