This page has been visited 12410 times.
Author: Liviu Chircu <liviu@opensips.org>
Table of Content (hide)

This tutorial illustrates the required steps in order to perform audio transcoding with the D-series cards manufactured by Sangoma, using the sngtc_server daemon and the OpenSIPS sngtc module as its client. The latest version of the transcoding library, as of 1 August 2013 is: sng-tc-linux-1.3.4.1
As mentioned in the module documentation, transcoding may only be done if the UAC performs early SDP negotiation and the UAS supports late SDP negotiation.
The necessary firmware for the D-series transcoding cards can be set up using the sngtc_tool. The cards which require PCI connectivity (D100 and D500) also need additional kernel drivers. Please refer to the Sangoma wiki for installation tutorials.
Installing the sngtc_server is straightforward and also documented in the Sangoma wiki. After doing the configuration, it can be started using the init script:
$ /etc/init.d/sngtc_server_ctrl start
By default it logs to /var/log/sngtc_server.log
Due to the limitations of the transcoding library, transcoding sessions can only be created if both codec A and codec B are known. Since this cannot be accomplished with a standard SIP call flow, the module restricts the receiving UA to perform late SDP negotiation. The diagram below explains this better:

The sngtc module exports 3 functions. They are called upon receiving INVITE, 200 OK and ACK messages. A short overview on their purpose would be:
All functions properly handle retransmissions. More details can be found in the module documentation. A basic configuration file is shown below:
####### Global Parameters #########
debug=6
log_stderror=yes
log_facility=LOG_LOCAL0
fork=yes
children=6
memdump=1
auto_aliases=no
listen=udp:eth1:5060
disable_tcp=yes
disable_tls=no
exec_msg_threshold=200000
####### Modules ########
mpath = "modules/"
loadmodule "exec.so"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "sipmsgops.so"
loadmodule "mi_fifo.so"
loadmodule "db_mysql.so"
loadmodule "uri.so"
loadmodule "dialog.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "sngtc.so"
modparam("tm", "fr_timer", 5)
modparam("tm", "fr_inv_timer", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "disable_6xx_block", 1)
modparam("tm", "onreply_avp_mode", 1)
modparam("rr", "append_fromtag", 0)
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("uri", "use_uri_table", 0)
modparam("dialog", "db_mode", 0)
modparam("dialog", "default_timeout", 3600)
modparam("usrloc", "db_mode", 1)
modparam("usrloc", "db_url", "mysql://root:sangoma@localhost/opensips")
####### Routing Logic ########
route {
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
}
force_rport();
if (has_totag()) {
# sequential request withing a dialog should
# take the path determined by record-routing
if (loose_route()) {
if (is_method("BYE")) {
setsflag(FAIL_TRANS_FLAG);
} else if (is_method("INVITE")) {
sngtc_offer();
} else if (is_method("ACK")) {
sngtc_caller_answer();
}
# route it out to whatever destination was set by loose_route()
# in $du (destination URI).
route(1);
} else {
if (is_method("ACK")) {
if (t_check_trans()) {
# non loose-route, but stateful ACK; must be an ACK after
# a 487 or e.g. 404 from upstream server
t_relay();
exit;
} else {
# ACK without matching transaction ->
# ignore and discard
exit;
}
}
sl_send_reply("404","Not here");
}
exit;
}
# CANCEL processing
if (is_method("CANCEL"))
{
if (t_check_trans())
t_relay();
exit;
}
t_check_trans();
# preloaded route checking
if (loose_route()) {
xlog("L_ERR",
"Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
sl_send_reply("403","Preload Route denied");
exit;
}
# record routing
if (!is_method("REGISTER|MESSAGE"))
record_route();
# requests for my domain
if (is_method("PUBLISH|SUBSCRIBE"))
{
sl_send_reply("503", "Service Unavailable");
exit;
}
if (is_method("REGISTER"))
{
if (!save("location"))
sl_reply_error();
exit;
}
if ($rU == NULL) {
sl_send_reply("484","Address Incomplete");
exit;
}
# do lookup with method filtering
if (!lookup("location","m")) {
t_newtran();
t_reply("404", "Not Found");
exit;
}
if (is_method("INVITE"))
sngtc_offer();
route(1);
}
route[1] {
# for INVITEs enable some additional helper routes
if (is_method("INVITE")) {
t_on_reply("2");
t_on_failure("1");
}
if (!t_relay()) {
send_reply("500", "Internal Error");
}
exit;
}
onreply_route[2] {
if ($rs == 200)
sngtc_callee_answer("11.12.13.14", "11.12.13.14");
}
failure_route[1] {
if (t_was_cancelled()) {
exit;
}
if (next_branches()) {
t_on_reply("2");
t_on_failure("1");
t_relay();
}
}
The transcoding sessions on the Sangoma cards are closed in a transparent manner, based on dialog callbacks.