Blob Blame History Raw
From: Peter Lemenkov <lemenkov@gmail.com>
Date: Sat, 23 May 2015 14:25:35 +0300
Subject: [PATCH] Backport rtpengine up to 64bc0c8 from 2.1.x branch

We have to remove thefollowing bits:

* module dependency specification interface
* processing context API

diff --git a/modules/rtpengine/Makefile b/modules/rtpengine/Makefile
new file mode 100644
index 000000000..33d6853a7
--- /dev/null
+++ b/modules/rtpengine/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile 5901 2009-07-21 07:45:05Z bogdan_iancu $
+#
+# print example module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=rtpengine.so
+LIBS=
+
+include ../../Makefile.modules
+
diff --git a/modules/rtpengine/README b/modules/rtpengine/README
new file mode 100644
index 000000000..0da096c49
--- /dev/null
+++ b/modules/rtpengine/README
@@ -0,0 +1,661 @@
+rtpengine Module
+
+Maxim Sobolev
+
+   Sippy Software, Inc.
+
+Juha Heinanen
+
+   TuTPro, Inc.
+
+Edited by
+
+Maxim Sobolev
+
+Edited by
+
+Bogdan-Andrei Iancu
+
+Edited by
+
+Juha Heinanen
+
+Edited by
+
+Sas Ovidiu
+
+Edited by
+
+Carsten Bock
+
+   ng-voice GmbH
+
+Edited by
+
+Richard Fuchs
+
+   Sipwise GmbH
+
+   Copyright © 2003-2008 Sippy Software, Inc.
+
+   Copyright © 2005 Voice Sistem SRL
+
+   Copyright © 2009-2014 TuTPro Inc.
+
+   Copyright © 2010 VoIPEmbedded Inc.
+
+   Copyright © 2013-2014 Sipwise GmbH
+     __________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Multiple RTP proxy usage
+        1.3. Dependencies
+
+              1.3.1. OpenSIPS Modules
+              1.3.2. External Libraries or Applications
+
+        1.4. Parameters
+
+              1.4.1. rtpengine_sock (string)
+              1.4.2. rtpengine_disable_tout (integer)
+              1.4.3. rtpengine_tout (integer)
+              1.4.4. rtpengine_retr (integer)
+              1.4.5. extra_id_pv (string)
+              1.4.6. setid_avp (string)
+
+        1.5. Functions
+
+              1.5.1. rtpengine_use_set(setid)
+              1.5.2. rtpengine_offer([flags])
+              1.5.3. rtpengine_answer([flags])
+              1.5.4. rtpengine_delete([flags])
+              1.5.5. rtpengine_manage([flags])
+              1.5.6. rtpengine_start_recording()
+
+        1.6. Exported Pseudo Variables
+
+              1.6.1. $rtpstat
+
+        1.7. MI Commands
+
+              1.7.1. rtpengine_enable
+              1.7.2. rtpengine_show
+
+   2. Frequently Asked Questions
+
+   List of Examples
+
+   1.1. Set rtpengine_sock parameter
+   1.2. Set rtpengine_disable_tout parameter
+   1.3. Set rtpengine_tout parameter
+   1.4. Set rtpengine_retr parameter
+   1.5. Set extra_id_pv parameter
+   1.6. Set setid_avp parameter
+   1.7. rtpengine_use_set usage
+   1.8. rtpengine_offer usage
+   1.9. rtpengine_answer usage
+   1.10. rtpengine_delete usage
+   1.11. rtpengine_manage usage
+   1.12. rtpengine_start_recording usage
+   1.13. $rtpstat Usage
+   1.14. rtpengine_enable usage
+   1.15. rtpengine_show usage
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   This is a module that enables media streams to be proxied via
+   an RTP proxy. The only RTP proxy currently known to work with
+   this module is the Sipwise rtpengine
+   https://github.com/sipwise/rtpengine. The rtpengine module is a
+   modified version of the original rtpproxy module using a new
+   control protocol. The module is designed to be a drop-in
+   replacement for the old module from a configuration file point
+   of view, however due to the incompatible control protocol, it
+   only works with RTP proxies which specifically support it.
+
+1.2. Multiple RTP proxy usage
+
+   The rtpengine module can support multiple RTP proxies for
+   balancing/distribution and control/selection purposes.
+
+   The module allows definition of several sets of rtpengines.
+   Load-balancing will be performed over a set and the admin has
+   the ability to choose what set should be used. The set is
+   selected via its id - the id being defined with the set. Refer
+   to the “rtpengine_sock” module parameter definition for syntax
+   description.
+
+   The balancing inside a set is done automatically by the module
+   based on the weight of each RTP proxy from the set.
+
+   The selection of the set is done from script prior using
+   rtpengine_delete(), rtpengine_offer() or rtpengine_answer()
+   functions - see the rtpengine_use_set() function.
+
+   Another way to select the set is to define setid_avp module
+   parameter and assign setid to the defined avp before calling
+   rtpengine_offer() or rtpengine_manage() function. If forwarding
+   of the requests fails and there is another branch to try,
+   remember to unset the avp after calling rtpengine_delete()
+   function.
+
+   For backward compatibility reasons, a set with no id take by
+   default the id 0. Also if no set is explicitly set before
+   rtpengine_delete(), rtpengine_offer() or rtpengine_answer() the
+   0 id set will be used.
+
+   IMPORTANT: if you use multiple sets, take care and use the same
+   set for both rtpengine_offer()/rtpengine_answer() and
+   rtpengine_delete()!! If the set was selected using setid_avp,
+   the avp needs to be set only once before rtpengine_offer() or
+   rtpengine_manage() call.
+
+1.3. Dependencies
+
+1.3.1. OpenSIPS Modules
+
+   The following modules must be loaded before this module:
+     * tm module - (optional) if you want to have
+       rtpengine_manage() fully functional
+
+1.3.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running OpenSIPS with this module loaded:
+     * None.
+
+1.4. Parameters
+
+1.4.1. rtpengine_sock (string)
+
+   Definition of socket(s) used to connect to (a set) RTP proxy.
+   It may specify a UNIX socket or an IPv4/IPv6 UDP socket.
+
+   Default value is “NONE” (disabled).
+
+   Example 1.1. Set rtpengine_sock parameter
+...
+# single rtproxy
+modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221")
+# multiple rtproxies for LB
+modparam("rtpengine", "rtpengine_sock",
+        "udp:localhost:12221 udp:localhost:12222")
+# multiple sets of multiple rtproxies
+modparam("rtpengine", "rtpengine_sock",
+        "1 == udp:localhost:12221 udp:localhost:12222")
+modparam("rtpengine", "rtpengine_sock",
+        "2 == udp:localhost:12225")
+...
+
+1.4.2. rtpengine_disable_tout (integer)
+
+   Once an RTP proxy was found unreachable and marked as disabled,
+   the rtpengine module will not attempt to establish
+   communication to that RTP proxy for rtpengine_disable_tout
+   seconds.
+
+   Default value is “60”.
+
+   Example 1.2. Set rtpengine_disable_tout parameter
+...
+modparam("rtpengine", "rtpengine_disable_tout", 20)
+...
+
+1.4.3. rtpengine_tout (integer)
+
+   Timeout value in waiting for reply from RTP proxy.
+
+   Default value is “1”.
+
+   Example 1.3. Set rtpengine_tout parameter
+...
+modparam("rtpengine", "rtpengine_tout", 2)
+...
+
+1.4.4. rtpengine_retr (integer)
+
+   How many times the module should retry to send and receive
+   after timeout was generated.
+
+   Default value is “5”.
+
+   Example 1.4. Set rtpengine_retr parameter
+...
+modparam("rtpengine", "rtpengine_retr", 2)
+...
+
+1.4.5. extra_id_pv (string)
+
+   The parameter sets the PV defination to use when the “b”
+   parameter is used on rtpengine_delete(), rtpengine_offer(),
+   rtpengine_answer() or rtpengine_manage() command.
+
+   Default is empty, the “b” parameter may not be used then.
+
+   Example 1.5. Set extra_id_pv parameter
+...
+modparam("rtpengine", "extra_id_pv", "$avp(extra_id)")
+...
+
+1.4.6. setid_avp (string)
+
+   The parameter defines an AVP that, if set, determines which RTP
+   proxy set rtpengine_offer(), rtpengine_answer(),
+   rtpengine_delete(), and rtpengine_manage() functions use.
+
+   There is no default value.
+
+   Example 1.6. Set setid_avp parameter
+...
+modparam("rtpengine", "setid_avp", "$avp(setid)")
+...
+
+1.5. Functions
+
+1.5.1.  rtpengine_use_set(setid)
+
+   Sets the ID of the RTP proxy set to be used for the next
+   rtpengine_delete(), rtpengine_offer(), rtpengine_answer() or
+   rtpengine_manage() command. The parameter can be an integer or
+   a config variable holding an integer.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   BRANCH_ROUTE.
+
+   Example 1.7. rtpengine_use_set usage
+...
+rtpengine_use_set("2");
+rtpengine_offer();
+...
+
+1.5.2.  rtpengine_offer([flags])
+
+   Rewrites SDP body to ensure that media is passed through an RTP
+   proxy. To be invoked on INVITE for the cases the SDPs are in
+   INVITE and 200 OK and on 200 OK when SDPs are in 200 OK and
+   ACK.
+
+   Meaning of the parameters is as follows:
+     * flags - flags to turn on some features.
+       The “flags” string is a list of space-separated items. Each
+       item is either an individual token, or a token in
+       “key=value” format. The possible tokens are described
+       below.
+          + via-branch=... - Include the “branch” value of one of
+            the “Via” headers in the request to the RTP proxy.
+            Possible values are: “1” - use the first “Via” header;
+            “2” - use the second “Via” header; “auto” - use the
+            first “Via” header if this is a request, or the second
+            one if this is a reply; “extra” - don't take the value
+            from a header, but instead use the value of the
+            “extra_id_pv” variable. This can be used to create one
+            media session per branch on the RTP proxy. When
+            sending a subsequent “delete” command to the RTP
+            proxy, you can then stop just the session for a
+            specific branch when passing the flag '1' or '2' in
+            the “rtpengine_delete”, or stop all sessions for a
+            call when not passing one of those two flags there.
+            This is especially useful if you have serially forked
+            call scenarios where the RTP proxy gets an “offer”
+            command for a new branch, and then a “delete” command
+            for the previous branch, which would otherwise delete
+            the full call, breaking the subsequent “answer” for
+            the new branch. This flag is only supported by the
+            Sipwise rtpengine RTP proxy at the moment!
+          + asymmetric - flags that UA from which message is
+            received doesn't support symmetric RTP. (automatically
+            sets the 'r' flag)
+          + force-answer - force “answer”, that is, only rewrite
+            SDP when corresponding session already exists in the
+            RTP proxy. By default is on when the session is to be
+            completed.
+          + internal, external - these flags specify the direction
+            of the SIP message. These flags only make sense when
+            the RTP proxy is running in bridge mode. “internal”
+            corresponds to the proxy's first interface, “external”
+            corresponds to the RTP proxy's second interface. You
+            always have to specify two flags to define the
+            incoming network and the outgoing network. For
+            example, “internal external” should be used for SIP
+            message received from the local interface and sent out
+            on the external interface, and “external internal”
+            vice versa. Other options are “internal internal” and
+            “external external”. So, for example if a SIP requests
+            is processed with “internal external” flags, the
+            corresponding response must be processed with
+            “internal external” flags.
+          + auto-bridge - this flag an alternative to the
+            “internal” and “external” flags in order to do
+            automatic bridging between IPv4 on the "internal
+            network" and IPv6 on the "external network". Instead
+            of explicitly instructing the RTP proxy to select a
+            particular address family, the distinction is done by
+            the given IP in the SDP body by the RTP proxy itself.
+            Not supported by Sipwise rtpengine.
+          + address-family=... - instructs the RTP proxy that the
+            recipient of this SDP body expects to see addresses of
+            a particular family. Possible values are “IP4” and
+            “IP6”. For example, if the SDP body contains IPv4
+            addresses but the recipient only speaks IPv6, you
+            would use “address-family=IP6” to bridge between the
+            two address families.
+            Sipwise rtpengine remembers the address family
+            preference of each party after it has seen an SDP body
+            from them. This means that normally it is only
+            necessary to explicitly specify the address family in
+            the “offer”, but not in the “answer”.
+            Note: Please note, that this will only work properly
+            with non-dual-stack user-agents or with dual-stack
+            clients according to RFC6157 (which suggest ICE for
+            Dual-Stack implementations). This short-cut will not
+            work properly with RFC4091 (ANAT) compatible clients,
+            which suggests having different m-lines with different
+            IP-protocols grouped together.
+          + force - instructs the RTP proxy to ignore marks
+            inserted by another RTP proxy in transit to indicate
+            that the session is already goes through another
+            proxy. Allows creating a chain of proxies. Not
+            supported and ignored by Sipwise rtpengine.
+          + trust-address - flags that IP address in SDP should be
+            trusted. Without this flag, the RTP proxy ignores
+            address in the SDP and uses source address of the SIP
+            message as media address which is passed to the RTP
+            proxy.
+          + replace-origin - flags that IP from the origin
+            description (o=) should be also changed.
+          + replace-session-connection - flags to change the
+            session-level SDP connection (c=) IP if media
+            description also includes connection information.
+          + symmetric - flags that for the UA from which message
+            is received, support symmetric RTP must be forced.
+          + repacketize=NN - requests the RTP proxy to perform
+            re-packetization of RTP traffic coming from the UA
+            which has sent the current message to increase or
+            decrease payload size per each RTP packet forwarded if
+            possible. The NN is the target payload size in ms, for
+            the most codecs its value should be in 10ms
+            increments, however for some codecs the increment
+            could differ (e.g. 30ms for GSM or 20ms for G.723).
+            The RTP proxy would select the closest value supported
+            by the codec. This feature could be used for
+            significantly reducing bandwith overhead for low
+            bitrate codecs, for example with G.729 going from 10ms
+            to 100ms saves two thirds of the network bandwith. Not
+            supported by Sipwise rtpengine.
+          + ICE=... - controls the RTP proxy's behaviour regarding
+            ICE attributes within the SDP body. Possible values
+            are: “force” - discard any ICE attributes already
+            present in the SDP body and then generate and insert
+            new ICE data, leaving itself as the only ICE
+            candidates; “remove” instructs the RTP proxy to
+            discard any ICE attributes and not insert any new ones
+            into the SDP. The default (if no “ICE=...” is given at
+            all), new ICE data will only be generated if no ICE
+            was present in the SDP originally; otherwise the RTP
+            proxy will only insert itself as an additional ICE
+            candidate. Other SDP substitutions (c=, m=, etc) are
+            unaffected by this flag.
+          + RTP, SRTP, AVP, AVPF - These flags control the RTP
+            transport protocol that should be used towards the
+            recipient of the SDP. If none of them are specified,
+            the protocol given in the SDP is left untouched.
+            Otherwise, the “SRTP” flag indicates that SRTP should
+            be used, while “RTP” indicates that SRTP should not be
+            used. “AVPF” indicates that the advanced RTCP profile
+            with feedback messages should be used, and “AVP”
+            indicates that the regular RTCP profile should be
+            used. See also the next set of flags below.
+          + RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF - these serve
+            as an alternative, more explicit way to select between
+            the different RTP protocols and profiles supported by
+            the RTP proxy. For example, giving the flag
+            “RTP/SAVPF” has the same effect as giving the two
+            flags “SRTP AVPF”.
+          + to-tag - force inclusion of the “To” tag. Normally,
+            the “To” tag is always included when present, except
+            for “delete” messages. Including the “To” tag in a
+            “delete” messages allows you to be more selective
+            about which dialogues within a call are being torn
+            down.
+          + rtcp-mux-demux - if rtcp-mux (RFC 5761) was offered,
+            make the RTP proxy accept the offer, but not offer it
+            to the recipient of this message.
+          + rtcp-mux-reject - if rtcp-mux was offered, make the
+            RTP proxy reject the offer, but still offer it to the
+            recipient. Can be combined with “rtcp-mux-offer” to
+            always offer it.
+          + rtcp-mux-offer - make the RTP proxy offer rtcp-mux to
+            the recipient of this message, regardless of whether
+            it was offered originally or not.
+          + rtcp-mux-accept - if rtcp-mux was offered, make the
+            RTP proxy accept the offer and also offer it to the
+            recipient of this message. Can be combined with
+            “rtcp-mux-offer” to always offer it.
+          + media-address=... - force a particular media address
+            to be used in the SDP body. Address family is detected
+            automatically.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.8. rtpengine_offer usage
+route {
+...
+    if (is_method("INVITE")) {
+        if (has_body("application/sdp")) {
+            if (rtpengine_offer())
+                t_on_reply("1");
+        } else {
+            t_on_reply("2");
+        }
+    }
+    if (is_method("ACK") && has_body("application/sdp"))
+        rtpengine_answer();
+...
+}
+
+onreply_route[1]
+{
+...
+    if (has_body("application/sdp"))
+        rtpengine_answer();
+...
+}
+
+onreply_route[2]
+{
+...
+    if (has_body("application/sdp"))
+        rtpengine_offer();
+...
+}
+
+1.5.3.  rtpengine_answer([flags])
+
+   Rewrites SDP body to ensure that media is passed through an RTP
+   proxy. To be invoked on 200 OK for the cases the SDPs are in
+   INVITE and 200 OK and on ACK when SDPs are in 200 OK and ACK.
+
+   See rtpengine_offer() function description above for the
+   meaning of the parameters.
+
+   This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+   FAILURE_ROUTE, BRANCH_ROUTE.
+
+   Example 1.9. rtpengine_answer usage
+
+   See rtpengine_offer() function example above for example.
+
+1.5.4.  rtpengine_delete([flags])
+
+   Tears down the RTPProxy session for the current call.
+
+   See rtpengine_offer() function description above for the
+   meaning of the parameters. Note that not all flags make sense
+   for a “delete”.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.10. rtpengine_delete usage
+...
+rtpengine_delete();
+...
+
+1.5.5.  rtpengine_manage([flags])
+
+   Manage the RTPProxy session - it combines the functionality of
+   rtpengine_offer(), rtpengine_answer() and rtpengine_delete(),
+   detecting internally based on message type and method which one
+   to execute.
+
+   It can take the same parameters as rtpengine_offer(). The flags
+   parameter to rtpengine_manage() can be a configuration variable
+   containing the flags as a string.
+
+   Functionality:
+     * If INVITE with SDP, then do rtpengine_offer()
+     * If INVITE with SDP, when the tm module is loaded, mark
+       transaction with internal flag FL_SDP_BODY to know that the
+       1xx and 2xx are for rtpengine_answer()
+     * If ACK with SDP, then do rtpengine_answer()
+     * If BYE or CANCEL, or called within a FAILURE_ROUTE[], then
+       do rtpengine_delete()
+     * If reply to INVITE with code >= 300 do rtpengine_delete()
+     * If reply with SDP to INVITE having code 1xx and 2xx, then
+       do rtpengine_answer() if the request had SDP or tm is not
+       loaded, otherwise do rtpengine_offer()
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.11. rtpengine_manage usage
+...
+rtpengine_manage();
+...
+
+1.5.6.  rtpengine_start_recording()
+
+   This function will send a signal to the RTP proxy to record the
+   RTP stream on the RTP proxy. This function is not supported by
+   Sipwise rtpengine at the moment!
+
+   This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE.
+
+   Example 1.12. rtpengine_start_recording usage
+...
+rtpengine_start_recording();
+...
+
+1.6. Exported Pseudo Variables
+
+1.6.1. $rtpstat
+
+   Returns the RTP statistics from the RTP proxy. The RTP
+   statistics from the RTP proxy are provided as a string and it
+   does contain several packet counters. The statistics must be
+   retrieved before the session is deleted (before
+   rtpengine_delete()).
+
+   Example 1.13. $rtpstat Usage
+...
+    append_hf("X-RTP-Statistics: $rtpstat\r\n");
+...
+
+1.7. MI Commands
+
+1.7.1. rtpengine_enable
+
+   Enables a RTP proxy if parameter value is greater than 0.
+   Disables it if a zero value is given.
+
+   The first parameter is the RTP proxy url (exactly as defined in
+   the config file).
+
+   The second parameter value must be a number in decimal.
+
+   NOTE: if a RTP proxy is defined multiple times (in the same or
+   diferente sete), all of its instances will be enables/disabled.
+
+   Example 1.14.  rtpengine_enable usage
+...
+$ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0
+...
+
+1.7.2. rtpengine_show
+
+   Displays all the RTP proxies and their information: set and
+   status (disabled or not, weight and recheck_ticks).
+
+   No parameter.
+
+   Example 1.15.  rtpengine_show usage
+...
+$ opensipsctl fifo rtpengine_show
+...
+
+Chapter 2. Frequently Asked Questions
+
+   2.1.
+
+   How do I migrate from “rtpproxy” or “rtpproxy-ng” to
+   “rtpengine”?
+
+   For the most part, only the names of the functions have
+   changed, with “rtpproxy” in each name replaced with
+   “rtpengine”. For example, “rtpproxy_manage()” has become
+   “rtpengine_manage()”. A few name duplications have also been
+   resolved, for example there is now a single
+   “rtpengine_delete()” instead of “unforce_rtp_proxy()” and the
+   identical “rtpproxy_destroy()”.
+
+   The largest difference to the old module is how flags are
+   passed to “rtpengine_offer()”, “rtpengine_answer()”,
+   “rtpengine_manage()” and “rtpengine_delete()”. Instead of
+   having a string of single-letter flags, they now take a string
+   of space-separated items, with each item being either a single
+   token (word) or a “key=value” pair.
+
+   For example, if you had a call “rtpproxy_offer("FRWOC+PS");”,
+   this would then become:
+rtpengine_offer("force trust-address symmetric replace-origin replace-se
+ssion-connection ICE=force RTP/SAVPF");
+
+   Finally, if you were using the second paramater (explicit media
+   address) to any of these functions, this has been replaced by
+   the “media-address=...” option within the first string of
+   flags.
+
+   2.2.
+
+   Where can I find more about OpenSIPS?
+
+   Take a look at http://www.opensips.org/.
+
+   2.3.
+
+   Where can I post a question about this module?
+
+   First at all check if your question was already answered on one
+   of our mailing lists:
+     * User Mailing List -
+       http://lists.opensips.org/cgi-bin/mailman/listinfo/users
+     * Developer Mailing List -
+       http://lists.opensips.org/cgi-bin/mailman/listinfo/devel
+
+   E-mails regarding any stable OpenSIPS release should be sent to
+   <users@lists.opensips.org> and e-mails regarding development
+   versions should be sent to <devel@lists.opensips.org>.
+
+   If you want to keep the mail private, send it to
+   <users@lists.opensips.org>.
+
+   2.4.
+
+   How can I report a bug?
+
+   Please follow the guidelines provided at:
+   https://github.com/OpenSIPS/opensips/issues.
diff --git a/modules/rtpengine/bencode.c b/modules/rtpengine/bencode.c
new file mode 100644
index 000000000..9de3f5e98
--- /dev/null
+++ b/modules/rtpengine/bencode.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright ??
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2014-06-17 Initial upload
+ */
+
+
+
+#include "bencode.h"
+#include <stdio.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+
+/* set to 0 for alloc debugging, e.g. through valgrind */
+#define BENCODE_MIN_BUFFER_PIECE_LEN	512
+
+#define BENCODE_HASH_BUCKETS		31 /* prime numbers work best */
+
+struct __bencode_buffer_piece {
+	char *tail;
+	unsigned int left;
+	struct __bencode_buffer_piece *next;
+	char buf[0];
+};
+struct __bencode_free_list {
+	void *ptr;
+	free_func_t func;
+	struct __bencode_free_list *next;
+};
+struct __bencode_hash {
+	struct bencode_item *buckets[BENCODE_HASH_BUCKETS];
+};
+
+
+
+
+
+static bencode_item_t __bencode_end_marker = {
+	.type = BENCODE_END_MARKER,
+	.iov[0].iov_len = 1,
+	.iov[0].iov_base = "e",
+	.iov_cnt = 1,
+	.str_len = 1,
+};
+
+
+
+
+static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end);
+
+
+
+static void __bencode_item_init(bencode_item_t *item) {
+	item->last_child = item->parent = item->child = item->sibling = NULL;
+}
+
+static void __bencode_container_init(bencode_item_t *cont) {
+	cont->iov[0].iov_len = 1;
+	cont->iov[1].iov_base = "e";
+	cont->iov[1].iov_len = 1;
+	cont->iov_cnt = 2;
+	cont->str_len = 2;
+}
+
+static void __bencode_dictionary_init(bencode_item_t *dict) {
+	dict->type = BENCODE_DICTIONARY;
+	dict->iov[0].iov_base = "d";
+	dict->value = 0;
+	__bencode_container_init(dict);
+}
+
+static void __bencode_list_init(bencode_item_t *list) {
+	list->type = BENCODE_LIST;
+	list->iov[0].iov_base = "l";
+	__bencode_container_init(list);
+}
+
+static struct __bencode_buffer_piece *__bencode_piece_new(unsigned int size) {
+	struct __bencode_buffer_piece *ret;
+
+	if (size < BENCODE_MIN_BUFFER_PIECE_LEN)
+		size = BENCODE_MIN_BUFFER_PIECE_LEN;
+	ret = BENCODE_MALLOC(sizeof(*ret) + size);
+	if (!ret)
+		return NULL;
+
+	ret->tail = ret->buf;
+	ret->left = size;
+	ret->next = NULL;
+
+	return ret;
+}
+
+int bencode_buffer_init(bencode_buffer_t *buf) {
+	buf->pieces = __bencode_piece_new(0);
+	if (!buf->pieces)
+		return -1;
+	buf->free_list = NULL;
+	buf->error = 0;
+	return 0;
+}
+
+static void *__bencode_alloc(bencode_buffer_t *buf, unsigned int size) {
+	struct __bencode_buffer_piece *piece;
+	void *ret;
+
+	if (!buf)
+		return NULL;
+	if (buf->error)
+		return NULL;
+
+	piece = buf->pieces;
+
+	if (size <= piece->left)
+		goto alloc;
+
+	piece = __bencode_piece_new(size);
+	if (!piece) {
+		buf->error = 1;
+		return NULL;
+	}
+	piece->next = buf->pieces;
+	buf->pieces = piece;
+
+	assert(size <= piece->left);
+
+alloc:
+	piece->left -= size;
+	ret = piece->tail;
+	piece->tail += size;
+	return ret;
+}
+
+void bencode_buffer_free(bencode_buffer_t *buf) {
+	struct __bencode_free_list *fl;
+	struct __bencode_buffer_piece *piece, *next;
+
+	for (fl = buf->free_list; fl; fl = fl->next)
+		fl->func(fl->ptr);
+
+	for (piece = buf->pieces; piece; piece = next) {
+		next = piece->next;
+		BENCODE_FREE(piece);
+	}
+}
+
+static bencode_item_t *__bencode_item_alloc(bencode_buffer_t *buf, unsigned int payload) {
+	bencode_item_t *ret;
+
+	ret = __bencode_alloc(buf, sizeof(struct bencode_item) + payload);
+	if (!ret)
+		return NULL;
+	ret->buffer = buf;
+	__bencode_item_init(ret);
+	return ret;
+}
+
+bencode_item_t *bencode_dictionary(bencode_buffer_t *buf) {
+	bencode_item_t *ret;
+
+	ret = __bencode_item_alloc(buf, 0);
+	if (!ret)
+		return NULL;
+	__bencode_dictionary_init(ret);
+	return ret;
+}
+
+bencode_item_t *bencode_list(bencode_buffer_t *buf) {
+	bencode_item_t *ret;
+
+	ret = __bencode_item_alloc(buf, 0);
+	if (!ret)
+		return NULL;
+	__bencode_list_init(ret);
+	return ret;
+}
+
+static void __bencode_container_add(bencode_item_t *parent, bencode_item_t *child) {
+	if (!parent)
+		return;
+	if (!child)
+		return;
+
+	assert(child->parent == NULL);
+	assert(child->sibling == NULL);
+
+	child->parent = parent;
+	if (parent->last_child)
+		parent->last_child->sibling = child;
+	parent->last_child = child;
+	if (!parent->child)
+		parent->child = child;
+
+	while (parent) {
+		parent->iov_cnt += child->iov_cnt;
+		parent->str_len += child->str_len;
+		parent = parent->parent;
+	}
+}
+
+static bencode_item_t *__bencode_string_alloc(bencode_buffer_t *buf, const void *base,
+		int str_len, int iov_len, int iov_cnt, bencode_type_t type)
+{
+	bencode_item_t *ret;
+	int len_len;
+
+	assert((str_len <= 99999) && (str_len >= 0));
+	ret = __bencode_item_alloc(buf, 7);
+	if (!ret)
+		return NULL;
+	len_len = sprintf(ret->__buf, "%d:", str_len);
+
+	ret->type = type;
+	ret->iov[0].iov_base = ret->__buf;
+	ret->iov[0].iov_len = len_len;
+	ret->iov[1].iov_base = (void *) base;
+	ret->iov[1].iov_len = iov_len;
+	ret->iov_cnt = iov_cnt + 1;
+	ret->str_len = len_len + str_len;
+
+	return ret;
+}
+
+bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len) {
+	char *sd = __bencode_alloc(buf, len);
+	if (!sd)
+		return NULL;
+	memcpy(sd, s, len);
+	return bencode_string_len(buf, sd, len);
+}
+
+bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len) {
+	return __bencode_string_alloc(buf, s, len, len, 1, BENCODE_STRING);
+}
+
+bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len) {
+	int i;
+
+	if (iov_cnt < 0)
+		return NULL;
+	if (str_len < 0) {
+		str_len = 0;
+		for (i = 0; i < iov_cnt; i++)
+			str_len += iov[i].iov_len;
+	}
+
+	return __bencode_string_alloc(buf, iov, str_len, iov_cnt, iov_cnt, BENCODE_IOVEC);
+}
+
+bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i) {
+	bencode_item_t *ret;
+	int alen, rlen;
+
+	alen = 8;
+	while (1) {
+		ret = __bencode_item_alloc(buf, alen + 3);
+		if (!ret)
+			return NULL;
+		rlen = snprintf(ret->__buf, alen, "i%llde", i);
+		if (rlen < alen)
+			break;
+		alen <<= 1;
+	}
+
+	ret->type = BENCODE_INTEGER;
+	ret->iov[0].iov_base = ret->__buf;
+	ret->iov[0].iov_len = rlen;
+	ret->iov[1].iov_base = NULL;
+	ret->iov[1].iov_len = 0;
+	ret->iov_cnt = 1;
+	ret->str_len = rlen;
+
+	return ret;
+}
+
+bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val) {
+	bencode_item_t *str;
+
+	if (!dict || !val)
+		return NULL;
+	assert(dict->type == BENCODE_DICTIONARY);
+
+	str = bencode_string_len(dict->buffer, key, keylen);
+	if (!str)
+		return NULL;
+	__bencode_container_add(dict, str);
+	__bencode_container_add(dict, val);
+	return val;
+}
+
+bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item) {
+	if (!list || !item)
+		return NULL;
+	assert(list->type == BENCODE_LIST);
+	__bencode_container_add(list, item);
+	return item;
+}
+
+static int __bencode_iovec_cpy(struct iovec *out, const struct iovec *in, int num) {
+	memcpy(out, in, num * sizeof(*out));
+	return num;
+}
+
+static int __bencode_str_cpy(char *out, const struct iovec *in, int num) {
+	char *orig = out;
+
+	while (--num >= 0) {
+		memcpy(out, in->iov_base, in->iov_len);
+		out += in->iov_len;
+		in++;
+	}
+	return out - orig;
+}
+
+static int __bencode_iovec_dump(struct iovec *out, bencode_item_t *item) {
+	bencode_item_t *child;
+	struct iovec *orig = out;
+
+	assert(item->iov[0].iov_base != NULL);
+	out += __bencode_iovec_cpy(out, &item->iov[0], 1);
+
+	child = item->child;
+	while (child) {
+		out += __bencode_iovec_dump(out, child);
+		child = child->sibling;
+	}
+
+	if (item->type == BENCODE_IOVEC)
+		out += __bencode_iovec_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len);
+	else if (item->iov[1].iov_base)
+		out += __bencode_iovec_cpy(out, &item->iov[1], 1);
+
+	assert((out - orig) == item->iov_cnt);
+	return item->iov_cnt;
+}
+
+static int __bencode_str_dump(char *out, bencode_item_t *item) {
+	char *orig = out;
+	bencode_item_t *child;
+
+	assert(item->iov[0].iov_base != NULL);
+	out += __bencode_str_cpy(out, &item->iov[0], 1);
+
+	child = item->child;
+	while (child) {
+		out += __bencode_str_dump(out, child);
+		child = child->sibling;
+	}
+
+	if (item->type == BENCODE_IOVEC)
+		out += __bencode_str_cpy(out, item->iov[1].iov_base, item->iov[1].iov_len);
+	else if (item->iov[1].iov_base)
+		out += __bencode_str_cpy(out, &item->iov[1], 1);
+
+	assert((out - orig) == item->str_len);
+	*out = '\0';
+	return item->str_len;
+}
+
+struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail) {
+	struct iovec *ret;
+
+	if (!root)
+		return NULL;
+	assert(cnt != NULL);
+	assert(root->iov_cnt > 0);
+
+	ret = __bencode_alloc(root->buffer, sizeof(*ret) * (root->iov_cnt + head + tail));
+	if (!ret)
+		return NULL;
+	*cnt = __bencode_iovec_dump(ret + head, root);
+	return ret;
+}
+
+char *bencode_collapse(bencode_item_t *root, int *len) {
+	char *ret;
+	int l;
+
+	if (!root)
+		return NULL;
+	assert(root->str_len > 0);
+
+	ret = __bencode_alloc(root->buffer, root->str_len + 1);
+	if (!ret)
+		return NULL;
+	l = __bencode_str_dump(ret, root);
+	if (len)
+		*len = l;
+	return ret;
+}
+
+char *bencode_collapse_dup(bencode_item_t *root, int *len) {
+	char *ret;
+	int l;
+
+	if (!root)
+		return NULL;
+	assert(root->str_len > 0);
+
+	ret = BENCODE_MALLOC(root->str_len + 1);
+	if (!ret)
+		return NULL;
+
+	l = __bencode_str_dump(ret, root);
+	if (len)
+		*len = l;
+	return ret;
+}
+
+static unsigned int __bencode_hash_str_len(const unsigned char *s, int len) {
+	unsigned long *ul;
+	unsigned int *ui;
+	unsigned short *us;
+
+	if (len >= sizeof(*ul)) {
+		ul = (void *) s;
+		return *ul % BENCODE_HASH_BUCKETS;
+	}
+	if (len >= sizeof(*ui)) {
+		ui = (void *) s;
+		return *ui % BENCODE_HASH_BUCKETS;
+	}
+	if (len >= sizeof(*us)) {
+		us = (void *) s;
+		return *us % BENCODE_HASH_BUCKETS;
+	}
+	if (len >= sizeof(*s))
+		return *s % BENCODE_HASH_BUCKETS;
+
+	return 0;
+}
+
+static unsigned int __bencode_hash_str(bencode_item_t *str) {
+	assert(str->type == BENCODE_STRING);
+	return __bencode_hash_str_len(str->iov[1].iov_base, str->iov[1].iov_len);
+}
+
+static void __bencode_hash_insert(bencode_item_t *key, struct __bencode_hash *hash) {
+	unsigned int bucket, i;
+
+	i = bucket = __bencode_hash_str(key);
+
+	while (1) {
+		if (!hash->buckets[i]) {
+			hash->buckets[i] = key;
+			break;
+		}
+		i++;
+		if (i >= BENCODE_HASH_BUCKETS)
+			i = 0;
+		if (i == bucket)
+			break;
+	}
+}
+
+static bencode_item_t *__bencode_decode_dictionary(bencode_buffer_t *buf, const char *s, const char *end) {
+	bencode_item_t *ret, *key, *value;
+	struct __bencode_hash *hash;
+
+	if (*s != 'd')
+		return NULL;
+	s++;
+
+	ret = __bencode_item_alloc(buf, sizeof(*hash));
+	if (!ret)
+		return NULL;
+	__bencode_dictionary_init(ret);
+	ret->value = 1;
+	hash = (void *) ret->__buf;
+	memset(hash, 0, sizeof(*hash));
+
+	while (s < end) {
+		key = __bencode_decode(buf, s, end);
+		if (!key)
+			return NULL;
+		s += key->str_len;
+		if (key->type == BENCODE_END_MARKER)
+			break;
+		if (key->type != BENCODE_STRING)
+			return NULL;
+		__bencode_container_add(ret, key);
+
+		if (s >= end)
+			return NULL;
+		value = __bencode_decode(buf, s, end);
+		if (!value)
+			return NULL;
+		s += value->str_len;
+		if (value->type == BENCODE_END_MARKER)
+			return NULL;
+		__bencode_container_add(ret, value);
+
+		__bencode_hash_insert(key, hash);
+	}
+
+	return ret;
+}
+
+static bencode_item_t *__bencode_decode_list(bencode_buffer_t *buf, const char *s, const char *end) {
+	bencode_item_t *ret, *item;
+
+	if (*s != 'l')
+		return NULL;
+	s++;
+
+	ret = __bencode_item_alloc(buf, 0);
+	if (!ret)
+		return NULL;
+	__bencode_list_init(ret);
+
+	while (s < end) {
+		item = __bencode_decode(buf, s, end);
+		if (!item)
+			return NULL;
+		s += item->str_len;
+		if (item->type == BENCODE_END_MARKER)
+			break;
+		__bencode_container_add(ret, item);
+	}
+
+	return ret;
+}
+
+static bencode_item_t *__bencode_decode_integer(bencode_buffer_t *buf, const char *s, const char *end) {
+	long long int i;
+	const char *orig = s;
+	char *convend;
+	bencode_item_t *ret;
+
+	if (*s != 'i')
+		return NULL;
+	s++;
+
+	if (s >= end)
+		return NULL;
+
+	if (*s == '0') {
+		i = 0;
+		s++;
+		goto done;
+	}
+
+	i = strtoll(s, &convend, 10);
+	if (convend == s)
+		return NULL;
+	s += (convend - s);
+
+done:
+	if (s >= end)
+		return NULL;
+	if (*s != 'e')
+		return NULL;
+	s++;
+
+	ret = __bencode_item_alloc(buf, 0);
+	if (!ret)
+		return NULL;
+	ret->type = BENCODE_INTEGER;
+	ret->iov[0].iov_base = (void *) orig;
+	ret->iov[0].iov_len = s - orig;
+	ret->iov[1].iov_base = NULL;
+	ret->iov[1].iov_len = 0;
+	ret->iov_cnt = 1;
+	ret->str_len = s - orig;
+	ret->value = i;
+
+	return ret;
+}
+
+static bencode_item_t *__bencode_decode_string(bencode_buffer_t *buf, const char *s, const char *end) {
+	unsigned long int sl;
+	char *convend;
+	const char *orig = s;
+	bencode_item_t *ret;
+
+	if (*s == '0') {
+		sl = 0;
+		s++;
+		goto colon;
+	}
+
+	sl = strtoul(s, &convend, 10);
+	if (convend == s)
+		return NULL;
+	s += (convend - s);
+
+colon:
+	if (s >= end)
+		return NULL;
+	if (*s != ':')
+		return NULL;
+	s++;
+
+	if (s + sl > end)
+		return NULL;
+
+	ret = __bencode_item_alloc(buf, 0);
+	if (!ret)
+		return NULL;
+	ret->type = BENCODE_STRING;
+	ret->iov[0].iov_base = (void *) orig;
+	ret->iov[0].iov_len = s - orig;
+	ret->iov[1].iov_base = (void *) s;
+	ret->iov[1].iov_len = sl;
+	ret->iov_cnt = 2;
+	ret->str_len = s - orig + sl;
+
+	return ret;
+}
+
+static bencode_item_t *__bencode_decode(bencode_buffer_t *buf, const char *s, const char *end) {
+	if (s >= end)
+		return NULL;
+
+	switch (*s) {
+		case 'd':
+			return __bencode_decode_dictionary(buf, s, end);
+		case 'l':
+			return __bencode_decode_list(buf, s, end);
+		case 'i':
+			return __bencode_decode_integer(buf, s, end);
+		case 'e':
+			return &__bencode_end_marker;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			return __bencode_decode_string(buf, s, end);
+		default:
+			return NULL;
+	}
+}
+
+bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len) {
+	assert(s != NULL);
+	return __bencode_decode(buf, s, s + len);
+}
+
+
+static int __bencode_dictionary_key_match(bencode_item_t *key, const char *keystr, int keylen) {
+	assert(key->type == BENCODE_STRING);
+
+	if (keylen != key->iov[1].iov_len)
+		return 0;
+	if (memcmp(keystr, key->iov[1].iov_base, keylen))
+		return 0;
+
+	return 1;
+}
+
+bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *keystr, int keylen) {
+	bencode_item_t *key;
+	unsigned int bucket, i;
+	struct __bencode_hash *hash;
+
+	if (!dict)
+		return NULL;
+	if (dict->type != BENCODE_DICTIONARY)
+		return NULL;
+
+	/* try hash lookup first if possible */
+	if (dict->value == 1) {
+		hash = (void *) dict->__buf;
+		i = bucket = __bencode_hash_str_len((const unsigned char *) keystr, keylen);
+		while (1) {
+			key = hash->buckets[i];
+			if (!key)
+				return NULL; /* would be there, but isn't */
+			assert(key->sibling != NULL);
+			if (__bencode_dictionary_key_match(key, keystr, keylen))
+				return key->sibling;
+			i++;
+			if (i >= BENCODE_HASH_BUCKETS)
+				i = 0;
+			if (i == bucket)
+				break; /* fall back to regular lookup */
+		}
+	}
+
+	for (key = dict->child; key; key = key->sibling->sibling) {
+		assert(key->sibling != NULL);
+		if (__bencode_dictionary_key_match(key, keystr, keylen))
+			return key->sibling;
+	}
+
+	return NULL;
+}
+
+void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t func, void *p) {
+	struct __bencode_free_list *li;
+
+	if (!p)
+		return;
+	li = __bencode_alloc(buf, sizeof(*li));
+	if (!li)
+		return;
+	li->ptr = p;
+	li->func = func;
+	li->next = buf->free_list;
+	buf->free_list = li;
+}
diff --git a/modules/rtpengine/bencode.h b/modules/rtpengine/bencode.h
new file mode 100644
index 000000000..ed1eb8c8d
--- /dev/null
+++ b/modules/rtpengine/bencode.h
@@ -0,0 +1,555 @@
+/*
+ * Copyright ??
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2014-06-17 Initial upload
+ */
+
+
+#ifndef _BENCODE_H_
+#define _BENCODE_H_
+
+#include <sys/uio.h>
+#include <string.h>
+
+#if defined(PKG_MALLOC) || defined(pkg_malloc)
+/* opensips */
+# include "../../mem/mem.h"
+# include "../../str.h"
+# ifndef BENCODE_MALLOC
+# define BENCODE_MALLOC pkg_malloc
+# define BENCODE_FREE pkg_free
+# endif
+#else
+/* mediaproxy-ng */
+# include "str.h"
+# ifndef BENCODE_MALLOC
+# define BENCODE_MALLOC malloc
+# define BENCODE_FREE free
+# endif
+#endif
+
+struct bencode_buffer;
+enum bencode_type;
+struct bencode_item;
+struct __bencode_buffer_piece;
+struct __bencode_free_list;
+
+typedef enum bencode_type bencode_type_t;
+typedef struct bencode_buffer bencode_buffer_t;
+typedef struct bencode_item bencode_item_t;
+typedef void (*free_func_t)(void *);
+
+enum bencode_type {
+	BENCODE_INVALID = 0,
+	BENCODE_STRING,		/* byte string */
+	BENCODE_INTEGER,	/* long long int */
+	BENCODE_LIST,		/* flat list of other objects */
+	BENCODE_DICTIONARY,	/* dictionary of key/values pairs. keys are always strings */
+	BENCODE_IOVEC,		/* special case of a string, built through bencode_string_iovec() */
+	BENCODE_END_MARKER,	/* used internally only */
+};
+
+struct bencode_item {
+	bencode_type_t type;
+	struct iovec iov[2];	/* when decoding, iov[1] contains the contents of a string object */
+	unsigned int iov_cnt;
+	unsigned int str_len;	/* length of the whole ENCODED object. NOT the length of a byte string */
+	long long int value;	/* when decoding an integer, contains the value; otherwise used internally */
+	bencode_item_t *parent, *child, *last_child, *sibling;
+	bencode_buffer_t *buffer;
+	char __buf[0];
+};
+
+struct bencode_buffer {
+	struct __bencode_buffer_piece *pieces;
+	struct __bencode_free_list *free_list;
+	int error:1;		/* set to !0 if allocation failed at any point */
+};
+
+
+
+
+
+/*** INIT & DESTROY ***/
+
+/* Initializes a bencode_buffer_t object. This object is used to group together all memory allocations
+ * made when encoding or decoding. Its memory usage is always growing, until it is freed, at which point
+ * all objects created through it become invalid. The actual object must be allocated separately, for
+ * example by being put on the stack.
+ * Returns 0 on success or -1 on failure (if no memory could be allocated). */
+int bencode_buffer_init(bencode_buffer_t *buf);
+
+/* Destroys a previously initialized bencode_buffer_t object. All memory used by the object is freed
+ * and all objects created through it become invalid. */
+void bencode_buffer_free(bencode_buffer_t *buf);
+
+/* Creates a new empty dictionary object. Memory will be allocated from the bencode_buffer_t object.
+ * Returns NULL if no memory could be allocated. */
+bencode_item_t *bencode_dictionary(bencode_buffer_t *buf);
+
+/* Creates a new empty list object. Memory will be allocated from the bencode_buffer_t object.
+ * Returns NULL if no memory could be allocated. */
+bencode_item_t *bencode_list(bencode_buffer_t *buf);
+
+/* Adds a pointer to the bencode_buffer_t object's internal free list. When the bencode_buffer_t
+ * object is destroyed, the specified function will be called on this pointer. */
+void bencode_buffer_destroy_add(bencode_buffer_t *buf, free_func_t, void *);
+
+/* Returns the buffer associated with an item, or NULL if pointer given is NULL */
+static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *);
+
+
+
+
+
+/*** DICTIONARY BUILDING ***/
+
+/* Adds a new key/value pair to a dictionary. Memory will be allocated from the same bencode_buffer_t
+ * object as the dictionary was allocated from. Returns NULL if no memory could be allocated, otherwise
+ * returns "val".
+ * The function does not check whether the key being added is already present in the dictionary.
+ * Also, the function does not reorder keys into lexicographical order; keys will be encoded in
+ * the same order as they've been added. The key must a null-terminated string.
+ * The value to be added must not have been previously linked into any other dictionary or list. */
+static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val);
+
+/* Identical to bencode_dictionary_add() but doesn't require the key string to be null-terminated */
+bencode_item_t *bencode_dictionary_add_len(bencode_item_t *dict, const char *key, int keylen, bencode_item_t *val);
+
+/* Convenience function to add a string value to a dictionary, possibly duplicated into the
+ * bencode_buffer_t object. */
+static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val);
+static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val);
+
+/* Ditto, but for a "str" object */
+static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val);
+static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val);
+
+/* Ditto, but adds a string created through an iovec array to the dictionary. See
+ * bencode_string_iovec(). */
+static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key,
+	const struct iovec *iov, int iov_cnt, int str_len);
+
+/* Convenience functions to add the respective (newly created) objects to a dictionary */
+static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val);
+static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key);
+static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key);
+
+
+
+
+
+/*** LIST BUILDING ***/
+
+/* Adds a new item to a list. Returns "item".
+ * The item to be added must not have been previously linked into any other dictionary or list. */
+bencode_item_t *bencode_list_add(bencode_item_t *list, bencode_item_t *item);
+
+/* Convenience function to add the respective (newly created) objects to a list */
+static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s);
+static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list);
+static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list);
+
+
+
+
+
+/*** STRING BUILDING & HANDLING ***/
+
+/* Creates a new byte-string object. The given string does not have to be null-terminated, instead
+ * the length of the string is specified by the "len" parameter. Returns NULL if no memory could
+ * be allocated.
+ * Strings are not copied or duplicated, so the string pointed to by "s" must remain valid until
+ * the complete document is finally encoded or sent out. */
+bencode_item_t *bencode_string_len(bencode_buffer_t *buf, const char *s, int len);
+
+/* Creates a new byte-string object. The given string must be null-terminated. Otherwise identical
+ * to bencode_string_len(). */
+static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s);
+
+/* Creates a new byte-string object from a "str" object. The string does not have to be null-
+ * terminated. */
+static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s);
+
+/* Identical to the above three functions, but copies the string into the bencode_buffer_t object.
+ * Thus, the given string doesn't have to remain valid and accessible afterwards. */
+bencode_item_t *bencode_string_len_dup(bencode_buffer_t *buf, const char *s, int len);
+static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s);
+static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s);
+
+/* Creates a new byte-string object from an iovec array. The created object has different internal
+ * semantics (not a BENCODE_STRING, but a BENCODE_IOVEC) and must not be treated like other string
+ * objects. The array pointer and contents must still be valid and accessible when the complete
+ * document is encoded. The full length of the string composed of the iovec array is given in the
+ * "str_len" parameter, which can be negative, in which case the array is iterated to calculate the
+ * length. */
+bencode_item_t *bencode_string_iovec(bencode_buffer_t *buf, const struct iovec *iov, int iov_cnt, int str_len);
+
+/* Convenience function to compare a string object to a regular C string. Returns 2 if object
+ * isn't a string object, otherwise returns according to strcmp(). */
+static inline int bencode_strcmp(bencode_item_t *a, const char *b);
+
+/* Converts the string object "in" into a str object "out". Returns "out" on success, or NULL on
+ * error ("in" was NULL or not a string object). */
+static inline str *bencode_get_str(bencode_item_t *in, str *out);
+
+
+
+
+
+/*** INTEGER BUILDING ***/
+
+/* Creates a new integer object. Returns NULL if no memory could be allocated. */
+bencode_item_t *bencode_integer(bencode_buffer_t *buf, long long int i);
+
+
+
+
+
+/*** COLLAPSING & ENCODING ***/
+
+/* Collapses and encodes the complete document structure under the "root" element (which normally
+ * is either a dictionary or a list) into an array of "iovec" structures. This array can then be
+ * passed to functions ala writev() or sendmsg() to output the encoded document as a whole. Memory
+ * is allocated from the same bencode_buffer_t object as the "root" object was allocated from.
+ * The "head" and "tail" parameters specify additional "iovec" structures that should be included
+ * in the allocated array before or after (respectively) the iovec structures used by the encoded
+ * document. Both parameters can be zero if no additional elements in the array are required.
+ * Returns a pointer to the allocated array or NULL if no memory could be allocated. The number of
+ * array elements is returned in "cnt" which must be a valid pointer to an int. This number does
+ * not include any additional elements allocated through the "head" or "tail" parameters.
+ *
+ * Therefore, the contents of the returned array are:
+ * [0 .. (head - 1)]                         = unused and uninitialized iovec structures
+ * [(head) .. (head + cnt - 1)]              = the encoded document
+ * [(head + cnt) .. (head + cnt + tail - 1)] = unused and uninitialized iovec structures
+ *
+ * The returned array will be freed when the corresponding bencode_buffer_t object is destroyed. */
+struct iovec *bencode_iovec(bencode_item_t *root, int *cnt, unsigned int head, unsigned int tail);
+
+/* Similar to bencode_iovec(), but instead returns the encoded document as a null-terminated string.
+ * Memory for the string is allocated from the same bencode_buffer_t object as the "root" object
+ * was allocated from. If "len" is a non-NULL pointer, the length of the genrated string is returned
+ * in *len. This is important if the encoded document contains binary data, in which case null
+ * termination cannot be trusted. The returned string is freed when the corresponding
+ * bencode_buffer_t object is destroyed. */
+char *bencode_collapse(bencode_item_t *root, int *len);
+
+/* Identical to bencode_collapse() but fills in a "str" object. Returns "out". */
+static str *bencode_collapse_str(bencode_item_t *root, str *out);
+
+/* Identical to bencode_collapse(), but the memory for the returned string is not allocated from
+ * a bencode_buffer_t object, but instead using the function defined as BENCODE_MALLOC (normally
+ * malloc() or pkg_malloc()), similar to strdup(). Using this function, the bencode_buffer_t
+ * object can be destroyed, but the returned string remains valid and usable. */
+char *bencode_collapse_dup(bencode_item_t *root, int *len);
+
+
+
+
+
+/*** DECODING ***/
+
+/* Decodes an encoded document from a string into a tree of bencode_item_t objects. The string does
+ * not need to be null-terminated, instead the length of the string is given through the "len"
+ * parameter. Memory is allocated from the bencode_buffer_t object. Returns NULL if no memory could
+ * be allocated or if the document could not be successfully decoded.
+ *
+ * The returned element is the "root" of the document tree and normally is either a list object or
+ * a dictionary object, but can also be a single string or integer object with no other objects
+ * underneath or besides it (no childred and no siblings). The type of the object can be determined
+ * by its ->type property.
+ *
+ * The number of bytes that could successfully be decoded into an object tree can be accessed through
+ * the root element's ->str_len property. Normally, this number should be equal to the "len" parameter
+ * passed, in which case the full string could be decoded. If ->str_len is less than "len", then there
+ * was additional stray byte data after the end of the encoded document.
+ *
+ * The document tree can be traversed through the ->child and ->sibling pointers in each object. The
+ * ->child pointer will be NULL for string and integer objects, as they don't contain other objects.
+ * For lists and dictionaries, ->child will be a pointer to the first contained object. This first
+ * contained object's ->sibling pointer will point to the next (second) contained object of the list
+ * or the dictionary, and so on. The last contained element of a list of dictionary will have a
+ * NULL ->sibling pointer.
+ *
+ * Dictionaries are like lists with ordered key/value pairs. When traversing dictionaries like
+ * lists, the following applies: The first element in the list (where ->child points to) will be the
+ * key of the first key/value pair (guaranteed to be a string and guaranteed to be present). The
+ * next element (following one ->sibling) will be the value of the first key/value pair. Following
+ * another ->sibling will point to the key of the next (second) key/value pair, and so on.
+ *
+ * However, to access children objects of dictionaries, the special functions following the naming
+ * scheme bencode_dictionary_get_* below should be used. They perform key lookup through a simple
+ * hash built into the dictionary object and so perform the lookup much faster. Only dictionaries
+ * created through a decoding process (i.e. not ones created from bencode_dictionary()) have this
+ * property. The hash is efficient only up to a certain number of elements (BENCODE_HASH_BUCKETS
+ * in bencode.c) contained in the dictionary. If the number of children object exceeds this number,
+ * key lookup will be slower than simply linearily traversing the list.
+ *
+ * The decoding function for dictionary object does not check whether keys are unique within the
+ * dictionary. It also does not care about lexicographical order of the keys.
+ *
+ * Decoded string objects will contain the raw decoded byte string in ->iov[1] (including the correct
+ * length). Strings are NOT null-terminated. Decoded integer objects will contain the decoded value
+ * in ->value.
+ *
+ * All memory is freed when the bencode_buffer_t object is destroyed.
+ */
+bencode_item_t *bencode_decode(bencode_buffer_t *buf, const char *s, int len);
+
+/* Identical to bencode_decode(), but returns successfully only if the type of the decoded object match
+ * "expect". */
+static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect);
+
+/* Identical to bencode_decode_expect() but takes a "str" argument. */
+static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect);
+
+
+
+
+
+/*** DICTIONARY LOOKUP & EXTRACTION ***/
+
+/* Searches the given dictionary object for the given key and returns the respective value. Returns
+ * NULL if the given object isn't a dictionary or if the key doesn't exist. The key must be a
+ * null-terminated string. */
+static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key);
+
+/* Identical to bencode_dictionary_get() but doesn't require the key to be null-terminated. */
+bencode_item_t *bencode_dictionary_get_len(bencode_item_t *dict, const char *key, int key_len);
+
+/* Identical to bencode_dictionary_get() but returns the value only if its type is a string, and
+ * returns it as a pointer to the string itself. Returns NULL if the value is of some other type. The
+ * returned string is NOT null-terminated. Length of the string is returned in *len, which must be a
+ * valid pointer. The returned string will be valid until dict's bencode_buffer_t object is destroyed. */
+static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len);
+
+/* Identical to bencode_dictionary_get_string() but fills in a "str" struct. Returns str->s, which
+ * may be NULL. */
+static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str);
+
+/* Looks up the given key in the dictionary and compares the corresponding value to the given
+ * null-terminated string. Returns 2 if the key isn't found or if the value isn't a string, otherwise
+ * returns according to strcmp(). */
+static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str);
+
+/* Identical to bencode_dictionary_get() but returns the string in a newly allocated buffer (using the
+ * BENCODE_MALLOC function), which remains valid even after bencode_buffer_t is destroyed. */
+static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len);
+
+/* Combines bencode_dictionary_get_str() and bencode_dictionary_get_string_dup(). Fills in a "str"
+ * struct, but copies the string into a newly allocated buffer. Returns str->s. */
+static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str);
+
+/* Identical to bencode_dictionary_get_string() but expects an integer object. The parameter "defval"
+ * specified which value should be returned if the key is not found or if the value is not an integer. */
+static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval);
+
+/* Identical to bencode_dictionary_get(), but returns the object only if its type matches "expect". */
+static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect);
+
+
+
+
+
+/**************************/
+
+static inline bencode_buffer_t *bencode_item_buffer(bencode_item_t *i) {
+	if (!i)
+		return NULL;
+	return i->buffer;
+}
+
+static inline bencode_item_t *bencode_string(bencode_buffer_t *buf, const char *s) {
+	return bencode_string_len(buf, s, strlen(s));
+}
+
+static inline bencode_item_t *bencode_string_dup(bencode_buffer_t *buf, const char *s) {
+	return bencode_string_len_dup(buf, s, strlen(s));
+}
+
+static inline bencode_item_t *bencode_str(bencode_buffer_t *buf, const str *s) {
+	return bencode_string_len(buf, s->s, s->len);
+}
+
+static inline bencode_item_t *bencode_str_dup(bencode_buffer_t *buf, const str *s) {
+	return bencode_string_len_dup(buf, s->s, s->len);
+}
+
+static inline bencode_item_t *bencode_dictionary_add(bencode_item_t *dict, const char *key, bencode_item_t *val) {
+	if (!key)
+		return NULL;
+	return bencode_dictionary_add_len(dict, key, strlen(key), val);
+}
+
+static inline bencode_item_t *bencode_dictionary_add_string(bencode_item_t *dict, const char *key, const char *val) {
+	if (!val)
+		return NULL;
+	return bencode_dictionary_add(dict, key, bencode_string(bencode_item_buffer(dict), val));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_string_dup(bencode_item_t *dict, const char *key, const char *val) {
+	if (!val)
+		return NULL;
+	return bencode_dictionary_add(dict, key, bencode_string_dup(bencode_item_buffer(dict), val));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_str(bencode_item_t *dict, const char *key, const str *val) {
+	if (!val)
+		return NULL;
+	return bencode_dictionary_add(dict, key, bencode_str(bencode_item_buffer(dict), val));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_str_dup(bencode_item_t *dict, const char *key, const str *val) {
+	if (!val)
+		return NULL;
+	return bencode_dictionary_add(dict, key, bencode_str_dup(bencode_item_buffer(dict), val));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_integer(bencode_item_t *dict, const char *key, long long int val) {
+	return bencode_dictionary_add(dict, key, bencode_integer(bencode_item_buffer(dict), val));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_dictionary(bencode_item_t *dict, const char *key) {
+	return bencode_dictionary_add(dict, key, bencode_dictionary(bencode_item_buffer(dict)));
+}
+
+static inline bencode_item_t *bencode_dictionary_add_list(bencode_item_t *dict, const char *key) {
+	return bencode_dictionary_add(dict, key, bencode_list(bencode_item_buffer(dict)));
+}
+
+static inline bencode_item_t *bencode_list_add_string(bencode_item_t *list, const char *s) {
+	return bencode_list_add(list, bencode_string(bencode_item_buffer(list), s));
+}
+
+static inline bencode_item_t *bencode_list_add_list(bencode_item_t *list) {
+	return bencode_list_add(list, bencode_list(bencode_item_buffer(list)));
+}
+
+static inline bencode_item_t *bencode_list_add_dictionary(bencode_item_t *list) {
+	return bencode_list_add(list, bencode_dictionary(bencode_item_buffer(list)));
+}
+
+static inline bencode_item_t *bencode_dictionary_get(bencode_item_t *dict, const char *key) {
+	if (!key)
+		return NULL;
+	return bencode_dictionary_get_len(dict, key, strlen(key));
+}
+
+static inline char *bencode_dictionary_get_string(bencode_item_t *dict, const char *key, int *len) {
+	bencode_item_t *val;
+	val = bencode_dictionary_get(dict, key);
+	if (!val || val->type != BENCODE_STRING)
+		return NULL;
+	*len = val->iov[1].iov_len;
+	return val->iov[1].iov_base;
+}
+
+static inline char *bencode_dictionary_get_str(bencode_item_t *dict, const char *key, str *str) {
+	str->s = bencode_dictionary_get_string(dict, key, &str->len);
+	if (!str->s)
+		str->len = 0;
+	return str->s;
+}
+
+static inline char *bencode_dictionary_get_string_dup(bencode_item_t *dict, const char *key, int *len) {
+	const char *s;
+	char *ret;
+	s = bencode_dictionary_get_string(dict, key, len);
+	if (!s)
+		return NULL;
+	ret = BENCODE_MALLOC(*len);
+	if (!ret)
+		return NULL;
+	memcpy(ret, s, *len);
+	return ret;
+}
+
+static inline char *bencode_dictionary_get_str_dup(bencode_item_t *dict, const char *key, str *str) {
+	str->s = bencode_dictionary_get_string_dup(dict, key, &str->len);
+	return str->s;
+}
+
+static inline long long int bencode_dictionary_get_integer(bencode_item_t *dict, const char *key, long long int defval) {
+	bencode_item_t *val;
+	val = bencode_dictionary_get(dict, key);
+	if (!val || val->type != BENCODE_INTEGER)
+		return defval;
+	return val->value;
+}
+
+static inline bencode_item_t *bencode_decode_expect(bencode_buffer_t *buf, const char *s, int len, bencode_type_t expect) {
+	bencode_item_t *ret;
+	ret = bencode_decode(buf, s, len);
+	if (!ret || ret->type != expect)
+		return NULL;
+	return ret;
+}
+
+static inline bencode_item_t *bencode_decode_expect_str(bencode_buffer_t *buf, const str *s, bencode_type_t expect) {
+	return bencode_decode_expect(buf, s->s, s->len, expect);
+}
+
+static inline bencode_item_t *bencode_dictionary_get_expect(bencode_item_t *dict, const char *key, bencode_type_t expect) {
+	bencode_item_t *ret;
+	ret = bencode_dictionary_get(dict, key);
+	if (!ret || ret->type != expect)
+		return NULL;
+	return ret;
+}
+static inline str *bencode_collapse_str(bencode_item_t *root, str *out) {
+	out->s = bencode_collapse(root, &out->len);
+	return out;
+}
+static inline int bencode_strcmp(bencode_item_t *a, const char *b) {
+	int len;
+	if (a->type != BENCODE_STRING)
+		return 2;
+	len = strlen(b);
+	if (a->iov[1].iov_len < len)
+		return -1;
+	if (a->iov[1].iov_len > len)
+		return 1;
+	return memcmp(a->iov[1].iov_base, b, len);
+}
+static inline int bencode_dictionary_get_strcmp(bencode_item_t *dict, const char *key, const char *str) {
+	bencode_item_t *i;
+	i = bencode_dictionary_get(dict, key);
+	if (!i)
+		return 2;
+	return bencode_strcmp(i, str);
+}
+
+static inline str *bencode_get_str(bencode_item_t *in, str *out) {
+	if (!in || in->type != BENCODE_STRING)
+		return NULL;
+	out->s = in->iov[1].iov_base;
+	out->len = in->iov[1].iov_len;
+	return out;
+}
+
+static inline bencode_item_t *bencode_dictionary_add_iovec(bencode_item_t *dict, const char *key,
+		const struct iovec *iov, int iov_cnt, int str_len)
+{
+	return bencode_dictionary_add(dict, key, bencode_string_iovec(bencode_item_buffer(dict), iov, iov_cnt, str_len));
+}
+
+#endif
diff --git a/modules/rtpengine/doc/rtpengine.xml b/modules/rtpengine/doc/rtpengine.xml
new file mode 100644
index 000000000..30ef34a31
--- /dev/null
+++ b/modules/rtpengine/doc/rtpengine.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+
+<!ENTITY admin SYSTEM "rtpengine_admin.xml">
+<!ENTITY faq SYSTEM "rtpengine_faq.xml">
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../doc/entities.xml">
+%docentities;
+
+]>
+
+<book>
+	<bookinfo>
+	<title>rtpengine Module</title>
+	<productname class="trade">&osipsname;</productname>
+	<authorgroup>
+		<author>
+		<firstname>Maxim</firstname>
+		<surname>Sobolev</surname>
+		<affiliation><orgname>Sippy Software, Inc.</orgname></affiliation>
+		<address>
+			<email>sobomax@sippysoft.com</email>
+		</address>
+		</author>
+		<author>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+		<affiliation><orgname>TuTPro, Inc.</orgname></affiliation>
+		<address>
+			<email>jh@tutpro.com</email>
+		</address>
+		</author>
+		<editor>
+		<firstname>Maxim</firstname>
+		<surname>Sobolev</surname>
+		<address>
+			<email>sobomax@sippysoft.com</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Bogdan-Andrei</firstname>
+		<surname>Iancu</surname>
+		<address>
+			<email>bogdan@voice-system.ro</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Juha</firstname>
+		<surname>Heinanen</surname>
+		<address>
+			<email>jh@tutpro.com</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Sas</firstname>
+		<surname>Ovidiu</surname>
+		<address>
+			<email>osas@voipembedded.com</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<affiliation><orgname>ng-voice GmbH</orgname></affiliation>
+		<address>
+			<email>carsten@ng-voice.com</email>
+		</address>
+		</editor>
+		<editor>
+		<firstname>Richard</firstname>
+		<surname>Fuchs</surname>
+		<affiliation><orgname>Sipwise GmbH</orgname></affiliation>
+		<address>
+			<email>rfuchs@sipwise.com</email>
+		</address>
+		</editor>
+	</authorgroup>
+	<copyright>
+		<year>2003-2008</year>
+		<holder>Sippy Software, Inc.</holder>
+	</copyright>
+	<copyright>
+		<year>2005</year>
+		<holder>Voice Sistem SRL</holder>
+	</copyright>
+	<copyright>
+		<year>2009-2014</year>
+		<holder>TuTPro Inc.</holder>
+	</copyright>
+	<copyright>
+		<year>2010</year>
+		<holder><ulink url='http://www.voipembedded.com'>VoIPEmbedded Inc.</ulink></holder>
+	</copyright>
+	<copyright>
+		<year>2013-2014</year>
+		<holder>Sipwise GmbH</holder>
+	</copyright>
+	</bookinfo>
+	<toc></toc>
+
+	&admin;
+	&faq;
+	
+
+</book>
diff --git a/modules/rtpengine/doc/rtpengine_admin.xml b/modules/rtpengine/doc/rtpengine_admin.xml
new file mode 100644
index 000000000..ee9e3a8df
--- /dev/null
+++ b/modules/rtpengine/doc/rtpengine_admin.xml
@@ -0,0 +1,715 @@
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This is a module that enables media streams to be proxied
+		via an &rtp; proxy. The only &rtp; proxy currently known to work
+		with this module is the Sipwise rtpengine
+		<ulink url="https://github.com/sipwise/rtpengine"></ulink>.
+		The rtpengine module is a modified version of the original
+		rtpproxy module using a new control protocol. The module is
+		designed to be a drop-in replacement for the old module from
+		a configuration file point of view, however due to the
+		incompatible control protocol, it only works with &rtp; proxies
+		which specifically support it.
+	</para>
+	</section>
+
+	<section>
+	<title>Multiple &rtp; proxy usage</title>
+	<para>
+		The rtpengine module can support multiple &rtp; proxies for
+		balancing/distribution and control/selection purposes.
+	</para>
+	<para>
+		The module allows definition of several sets of rtpengines.
+		Load-balancing will be performed over a set and the admin has the
+		ability to choose what set should be used. The set is selected via
+		its id - the id being defined with the set. Refer to the
+		<quote>rtpengine_sock</quote> module parameter definition for syntax
+		description.
+	</para>
+	<para>
+		The balancing inside a set is done automatically by the module based on
+		the weight of each &rtp; proxy from the set.
+	</para>
+	<para>
+		The selection of the set is done from script prior using
+		rtpengine_delete(), rtpengine_offer() or rtpengine_answer()
+		functions - see the rtpengine_use_set() function.
+	</para>
+	<para>
+	        Another way to select the set is to define setid_avp
+	        module parameter and assign setid to the defined avp
+	        before calling rtpengine_offer() or rtpengine_manage()
+	        function.  If forwarding of the requests fails and
+	        there is another branch to try, remember to unset the
+	        avp after calling rtpengine_delete() function.
+	</para>
+	<para>
+		For backward compatibility reasons, a set with no id take by default
+		the id 0. Also if no set is explicitly set before
+		rtpengine_delete(), rtpengine_offer() or rtpengine_answer()
+		the 0 id set will be used.
+	</para>
+	<para>
+		IMPORTANT: if you use multiple sets, take care and use the same set for
+		both rtpengine_offer()/rtpengine_answer() and rtpengine_delete()!!
+		If the set was selected using setid_avp, the avp needs to be
+		set only once before rtpengine_offer() or rtpengine_manage() call.
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&osips; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>tm module</emphasis> - (optional) if you want to
+				have rtpengine_manage() fully functional
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before
+		running &osips; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Parameters</title>
+	<section id="rtpengine.p.rtpengine_sock">
+		<title><varname>rtpengine_sock</varname> (string)</title>
+		<para>
+		Definition of socket(s) used to connect to (a set) &rtp; proxy. It may
+		specify a UNIX socket or an IPv4/IPv6 UDP socket.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NONE</quote> (disabled).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_sock</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# single rtproxy
+modparam("rtpengine", "rtpengine_sock", "udp:localhost:12221")
+# multiple rtproxies for LB
+modparam("rtpengine", "rtpengine_sock",
+	"udp:localhost:12221 udp:localhost:12222")
+# multiple sets of multiple rtproxies
+modparam("rtpengine", "rtpengine_sock",
+	"1 == udp:localhost:12221 udp:localhost:12222")
+modparam("rtpengine", "rtpengine_sock",
+	"2 == udp:localhost:12225")
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.rtpengine_disable_tout">
+		<title><varname>rtpengine_disable_tout</varname> (integer)</title>
+		<para>
+		Once an &rtp; proxy was found unreachable and marked as disabled, the rtpengine
+		module will not attempt to establish communication to that &rtp; proxy for
+		rtpengine_disable_tout seconds.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>60</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_disable_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "rtpengine_disable_tout", 20)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.rtpengine_tout">
+		<title><varname>rtpengine_tout</varname> (integer)</title>
+		<para>
+		Timeout value in waiting for reply from &rtp; proxy.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>1</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_tout</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "rtpengine_tout", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.rtpengine_retr">
+		<title><varname>rtpengine_retr</varname> (integer)</title>
+		<para>
+		How many times the module should retry to send and receive after
+		timeout was generated.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>5</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_retr</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "rtpengine_retr", 2)
+...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.extra_id_pv">
+		<title><varname>extra_id_pv</varname> (string)</title>
+		<para>
+			The parameter sets the PV defination to use when the <quote>b</quote>
+			parameter is used on rtpengine_delete(), rtpengine_offer(),
+			rtpengine_answer() or rtpengine_manage() command.
+		</para><para>
+			Default is empty, the <quote>b</quote> parameter may not be used then.
+		</para>
+		<example>
+		<title>Set <varname>extra_id_pv</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "extra_id_pv", "$avp(extra_id)")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="rtpengine.p.setid_pv">
+		<title><varname>setid_avp</varname> (string)</title>
+		<para>
+			The parameter defines an AVP that, if set,
+			determines which &rtp; proxy set
+			rtpengine_offer(), rtpengine_answer(),
+			rtpengine_delete(), and rtpengine_manage()
+			functions use.
+		</para>
+		<para>
+			There is no default value.
+		</para>
+		<example>
+		<title>Set <varname>setid_avp</varname> parameter</title>
+<programlisting format="linespecific">
+...
+modparam("rtpengine", "setid_avp", "$avp(setid)")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="rtpengine.f.rtpengine_use_set">
+		<title>
+		<function moreinfo="none">rtpengine_use_set(setid)</function>
+		</title>
+		<para>
+		Sets the ID of the &rtp; proxy set to be used for the next
+		rtpengine_delete(), rtpengine_offer(), rtpengine_answer()
+		or rtpengine_manage() command. The parameter can be an integer or
+		a config variable holding an integer.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		BRANCH_ROUTE.
+		</para>
+		<example>
+		<title><function>rtpengine_use_set</function> usage</title>
+		<programlisting format="linespecific">
+...
+rtpengine_use_set("2");
+rtpengine_offer();
+...
+</programlisting>
+		</example>
+	</section>
+        <section id="rtpengine.f.rtpengine_offer">
+                <title>
+                <function moreinfo="none">rtpengine_offer([flags])</function>
+                </title>
+                <para>
+                Rewrites &sdp; body to ensure that media is passed through
+                an &rtp; proxy. To be invoked
+		on INVITE for the cases the SDPs are in INVITE and 200 OK and on 200 OK
+		when SDPs are in 200 OK and ACK.
+                </para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+			<emphasis>flags</emphasis> - flags to turn on some features.
+			</para>
+			<para>The <quote>flags</quote> string is a list of space-separated items. Each item
+			is either an individual token, or a token in <quote>key=value</quote> format. The
+			possible tokens are described below.</para>
+			<itemizedlist>
+				<listitem><para>
+				<emphasis>via-branch=...</emphasis> - Include the <quote>branch</quote>
+				value of one of the <quote>Via</quote> headers in the request to the
+				&rtp; proxy. Possible values are:
+				<quote>1</quote> - use the first <quote>Via</quote> header;
+				<quote>2</quote> - use the second <quote>Via</quote> header;
+				<quote>auto</quote> - use the first <quote>Via</quote> header if this is
+				a request, or the second one if this is a reply;
+				<quote>extra</quote> - don't take the value from a header, but instead use
+				the value of the <quote>extra_id_pv</quote> variable.
+				This can be used to create one media session per branch
+				on the &rtp; proxy. When sending a subsequent <quote>delete</quote> command to
+				the &rtp; proxy, you can then stop just the session for a specific branch when
+				passing the flag '1' or '2' in the <quote>rtpengine_delete</quote>, or stop
+				all sessions for a call when not passing one of those two flags there. This is
+				especially useful if you have serially forked call scenarios where the &rtp; proxy
+				gets an <quote>offer</quote> command for a new branch, and then a
+				<quote>delete</quote> command for the previous branch, which would otherwise
+				delete the full call, breaking the subsequent <quote>answer</quote> for the
+				new branch. <emphasis>This flag is only supported by the Sipwise rtpengine
+				&rtp; proxy at the moment!</emphasis>
+				</para></listitem>
+				<listitem><para>
+				<emphasis>asymmetric</emphasis> - flags that UA from which message is
+				received doesn't support symmetric RTP. (automatically sets the 'r' flag)
+				</para></listitem>
+				<listitem><para>
+				<emphasis>force-answer</emphasis> - force <quote>answer</quote>, that is,
+				only rewrite &sdp; when corresponding session already exists
+				in the &rtp; proxy. By default is on when the session is to be
+				completed.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>internal, external</emphasis> - these flags specify the direction of
+				the SIP message. These flags only make sense when the &rtp; proxy is running
+				in bridge mode. <quote>internal</quote> corresponds to the proxy's first
+				interface, <quote>external</quote> corresponds to the &rtp; proxy's
+				second interface. You always have to specify two flags to define
+				the incoming network and the outgoing network. For example, <quote>internal
+				external</quote> should be
+				used for SIP message received from the local interface and sent out on the
+				external interface, and <quote>external internal</quote> vice versa. Other
+				options are <quote>internal internal</quote> and <quote>external
+				external</quote>.
+				So, for example if a SIP requests is processed with <quote>internal
+				external</quote> flags, the corresponding
+				response must be processed with <quote>internal external</quote> flags.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>auto-bridge</emphasis> - this flag an alternative to the
+				<quote>internal</quote> and <quote>external</quote> flags
+				in order to do automatic bridging between IPv4 on the
+				"internal network" and IPv6 on the "external network". Instead of
+				explicitly instructing the &rtp; proxy to select a particular address
+				family, the distinction is done by the given IP in the SDP body by
+				the RTP proxy itself. Not supported by Sipwise rtpengine.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>address-family=...</emphasis> - instructs the &rtp; proxy that the
+				recipient of this &sdp; body expects to see addresses of a particular family.
+				Possible values are <quote>IP4</quote> and <quote>IP6</quote>. For example,
+				if the &sdp; body contains IPv4 addresses but the recipient only speaks IPv6,
+				you would use <quote>address-family=IP6</quote> to bridge between the two
+				address families.
+				</para><para>
+				Sipwise rtpengine remembers the address family preference of each party after
+				it has seen an &sdp; body from them. This means that normally it is only
+				necessary to explicitly specify the address family in the <quote>offer</quote>,
+				but not in the <quote>answer</quote>.
+				</para><para>
+				Note: Please note, that this will only work properly with non-dual-stack user-agents or with
+				dual-stack clients according to RFC6157 (which suggest ICE for Dual-Stack implementations).
+				This short-cut will not work properly with RFC4091 (ANAT) compatible clients, which suggests
+				having different m-lines with different IP-protocols grouped together.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>force</emphasis> - instructs the &rtp; proxy to ignore marks
+				inserted by another &rtp; proxy in transit to indicate that the
+				session is already goes through another proxy. Allows creating
+				a chain of proxies. Not supported and ignored by Sipwise rtpengine.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>trust-address</emphasis> - flags that IP address in SDP should
+				be trusted. Without this flag, the &rtp; proxy ignores address in
+				the SDP and uses source address of the SIP message as media
+				address which is passed to the RTP proxy.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>replace-origin</emphasis> - flags that IP from the origin
+				description (o=) should be also changed.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>replace-session-connection</emphasis> - flags to change the session-level
+				SDP connection (c=) IP if media description also includes
+				connection information.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>symmetric</emphasis> - flags that for the UA from which
+				message is received, support symmetric RTP must be forced.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>repacketize=NN</emphasis> - requests the &rtp; proxy to perform
+				re-packetization of RTP traffic coming from the UA which
+				has sent the current message to increase or decrease payload
+				size per each RTP packet forwarded if possible.  The NN is the
+				target payload size in ms, for the most codecs its value should
+				be in 10ms increments, however for some codecs the increment
+				could differ (e.g. 30ms for GSM or 20ms for G.723).  The
+				&rtp; proxy would select the closest value supported by the codec.
+				This feature could be used for significantly reducing bandwith
+				overhead for low bitrate codecs, for example with G.729 going
+				from 10ms to 100ms saves two thirds of the network bandwith.
+				Not supported by Sipwise rtpengine.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>ICE=...</emphasis> - controls the &rtp; proxy's behaviour
+				regarding ICE attributes within the &sdp; body. Possible values
+				are: <quote>force</quote> - 
+				discard any ICE attributes already present in the &sdp; body
+				and then generate and insert new ICE data, leaving itself
+				as the <emphasis>only</emphasis> ICE candidates;
+				<quote>remove</quote> instructs the &rtp; proxy to discard
+				any ICE attributes and not insert any new ones into the &sdp;.
+				The default (if no <quote>ICE=...</quote> is given at all),
+				new ICE data will only be generated
+				if no ICE was present in the &sdp; originally; otherwise
+				the &rtp; proxy will only insert itself as an
+				<emphasis>additional</emphasis> ICE candidate. Other
+				&sdp; substitutions (c=, m=, etc) are unaffected by this flag.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>RTP, SRTP, AVP, AVPF</emphasis> - These flags control the &rtp;
+				transport protocol that should be used towards the recipient of
+				the &sdp;. If none of them are specified, the protocol given in
+				the &sdp; is left untouched. Otherwise, the <quote>SRTP</quote> flag indicates that
+				SRTP should be used, while <quote>RTP</quote> indicates that SRTP should not be used.
+				<quote>AVPF</quote> indicates that the advanced RTCP profile with feedback messages
+				should be used, and <quote>AVP</quote> indicates that the regular RTCP profile
+				should be used. See also the next set of flags below.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF</emphasis> - these serve as
+				an alternative, more explicit way to select between the different &rtp; protocols
+				and profiles supported by the &rtp; proxy. For example, giving the flag
+				<quote>RTP/SAVPF</quote> has the same effect as giving the two flags
+				<quote>SRTP AVPF</quote>.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>to-tag</emphasis> - force inclusion of the <quote>To</quote> tag.
+				Normally, the <quote>To</quote> tag is always included when present, except
+				for <quote>delete</quote> messages. Including the <quote>To</quote> tag in
+				a <quote>delete</quote> messages allows you to be more selective about which
+				dialogues within a call are being torn down.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>rtcp-mux-demux</emphasis> - if rtcp-mux (RFC 5761) was
+				offered, make the &rtp; proxy accept the offer, but not offer it to the
+				recipient of this message.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>rtcp-mux-reject</emphasis> - if rtcp-mux was offered, make the
+				&rtp; proxy reject the offer, but still offer it to the recipient. Can be
+				combined with <quote>rtcp-mux-offer</quote> to always offer it.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>rtcp-mux-offer</emphasis> - make the &rtp; proxy offer rtcp-mux
+				to the recipient of this message, regardless of whether it was offered
+				originally or not.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>rtcp-mux-accept</emphasis> - if rtcp-mux was offered, make the
+				&rtp; proxy accept the offer and also offer it to the recipient of this
+				message. Can be combined with <quote>rtcp-mux-offer</quote> to always offer it.
+				</para></listitem>
+				<listitem><para>
+				<emphasis>media-address=...</emphasis> - force a particular media address to
+				be used in the &sdp; body. Address family is detected automatically.
+				</para></listitem>
+			</itemizedlist>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from ANY_ROUTE.
+                </para>
+		<example>
+		<title><function>rtpengine_offer</function> usage</title>
+		<programlisting format="linespecific">
+route {
+...
+    if (is_method("INVITE")) {
+        if (has_body("application/sdp")) {
+            if (rtpengine_offer())
+                t_on_reply("1");
+        } else {
+            t_on_reply("2");
+        }
+    }
+    if (is_method("ACK") &amp;&amp; has_body("application/sdp"))
+        rtpengine_answer();
+...
+}
+
+onreply_route[1]
+{
+...
+    if (has_body("application/sdp"))
+        rtpengine_answer();
+...
+}
+
+onreply_route[2]
+{
+...
+    if (has_body("application/sdp"))
+        rtpengine_offer();
+...
+}
+</programlisting>
+                </example>
+	</section>
+        <section id="rtpengine.f.rtpengine_answer">
+                <title>
+                <function moreinfo="none">rtpengine_answer([flags])</function>
+                </title>
+		<para>
+		Rewrites &sdp; body to ensure that media is passed through
+		an &rtp; proxy. To be invoked
+		on 200 OK for the cases the SDPs are in INVITE and 200 OK and on ACK
+		when SDPs are in 200 OK and ACK.
+		</para>
+		<para>
+		See rtpengine_offer() function description above for the meaning of the
+		parameters.
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+		FAILURE_ROUTE, BRANCH_ROUTE.
+		</para>
+		<example>
+		 <title><function>rtpengine_answer</function> usage</title>
+		<para>
+		See rtpengine_offer() function example above for example.
+		</para>
+		</example>
+        </section>
+	<section id="rtpengine.f.rtpengine_delete">
+		<title>
+		<function moreinfo="none">rtpengine_delete([flags])</function>
+		</title>
+		<para>
+		Tears down the RTPProxy session for the current call.
+		</para>
+		<para>
+		See rtpengine_offer() function description above for the meaning of the
+		parameters. Note that not all flags make sense for a <quote>delete</quote>.
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>rtpengine_delete</function> usage</title>
+		<programlisting format="linespecific">
+...
+rtpengine_delete();
+...
+</programlisting>
+		</example>
+	</section>
+
+    <section id="rtpengine.f.rtpengine_manage">
+        <title>
+        <function moreinfo="none">rtpengine_manage([flags])</function>
+        </title>
+		<para>
+		Manage the RTPProxy session - it combines the functionality of
+		rtpengine_offer(), rtpengine_answer() and rtpengine_delete(), detecting
+		internally based on message type and method which one to execute.
+		</para>
+		<para>
+		It can take the same parameters as <function>rtpengine_offer().</function>
+		The flags parameter to rtpengine_manage() can be a configuration variable
+		containing the flags as a string.
+		</para>
+		<para>
+		Functionality:
+		</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+			If INVITE with SDP, then do <function>rtpengine_offer()</function>
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			If INVITE with SDP, when the tm module is loaded, mark transaction with
+			internal flag FL_SDP_BODY to know that the 1xx and 2xx are for
+			<function>rtpengine_answer()</function>
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			If ACK with SDP, then do <function>rtpengine_answer()</function>
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			If BYE or CANCEL, or called within a FAILURE_ROUTE[], then do <function>rtpengine_delete()</function>
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			If reply to INVITE with code >= 300 do <function>rtpengine_delete()</function>
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+			If reply with SDP to INVITE having code 1xx and 2xx, then
+			do <function>rtpengine_answer()</function> if the request had SDP or tm is not loaded,
+			otherwise do <function>rtpengine_offer()</function>
+			</para>
+		</listitem>
+	</itemizedlist>
+
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		 <title><function>rtpengine_manage</function> usage</title>
+		<programlisting format="linespecific">
+...
+rtpengine_manage();
+...
+</programlisting>
+		</example>
+        </section>
+
+	<section id="rtpengine.f.start_recording">
+		<title>
+		<function moreinfo="none">rtpengine_start_recording()</function>
+		</title>
+		<para>
+		This function will send a signal to the &rtp; proxy to record
+		the RTP stream on the &rtp; proxy.
+		<emphasis>This function is not supported by Sipwise rtpengine at the moment!</emphasis>
+		</para>
+		<para>
+		This function can be used from REQUEST_ROUTE and ONREPLY_ROUTE.
+		</para>
+		<example>
+		<title><function>rtpengine_start_recording</function> usage</title>
+		<programlisting format="linespecific">
+...
+rtpengine_start_recording();
+...
+		</programlisting>
+		</example>
+	</section>
+
+
+	</section>
+
+	<section>
+		<title>Exported Pseudo Variables</title>
+		<section>
+			<title><function moreinfo="none">$rtpstat</function></title>
+			<para>
+			Returns the &rtp; statistics from the &rtp; proxy. The &rtp; statistics from the &rtp; proxy
+			are provided as a string and it does contain several packet counters. The statistics
+			must be retrieved before the session is deleted	(before <function>rtpengine_delete()</function>).
+			</para>
+
+		<example>
+		<title>$rtpstat Usage</title>
+		<programlisting format="linespecific">
+...
+    append_hf("X-RTP-Statistics: $rtpstat\r\n");
+...
+		</programlisting>
+		</example>
+	        </section>
+
+	</section>
+
+	<section>
+		<title><acronym>MI</acronym> Commands</title>
+		<section id="rtpengine.m.rtpengine_enable">
+			<title><function moreinfo="none">rtpengine_enable</function></title>
+			<para>
+			Enables a &rtp; proxy if parameter value is greater than 0.
+			Disables it if a zero value is given.
+			</para>
+			<para>
+			The first parameter is the &rtp; proxy url (exactly as defined in
+			the config file).
+			</para>
+			<para>
+			The second parameter value must be a number in decimal.
+			</para>
+			<para>
+			NOTE: if a &rtp; proxy is defined multiple times (in the same or
+			diferente sete), all of its instances will be enables/disabled.
+			</para>
+			<example>
+			<title>
+			<function moreinfo="none">rtpengine_enable</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ opensipsctl fifo rtpengine_enable udp:192.168.2.133:8081 0
+...
+			</programlisting>
+			</example>
+		</section>
+
+			<section id="rtpengine.m.rtpengine_show">
+			<title><function moreinfo="none">rtpengine_show</function></title>
+			<para>
+			Displays all the &rtp; proxies and their information: set and
+			status (disabled or not, weight and recheck_ticks).
+			</para>
+			<para>
+			No parameter.
+			</para>
+			<example>
+			<title>
+				<function moreinfo="none">rtpengine_show</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ opensipsctl fifo rtpengine_show
+...
+			</programlisting>
+			</example>
+		</section>
+	</section>
+
+</chapter>
+
diff --git a/modules/rtpengine/doc/rtpengine_faq.xml b/modules/rtpengine/doc/rtpengine_faq.xml
new file mode 100644
index 000000000..8c821ca77
--- /dev/null
+++ b/modules/rtpengine/doc/rtpengine_faq.xml
@@ -0,0 +1,94 @@
+<!-- Module FAQ -->
+
+<chapter>
+
+    <title>&faqguide;</title>
+    <qandaset defaultlabel="number">
+	<qandaentry>
+	    <question>
+		    <para>How do I migrate from <quote>rtpproxy</quote> or <quote>rtpproxy-ng</quote> to
+		    <quote>rtpengine</quote>?</para>
+	    </question>
+	    <answer>
+		<para>
+			For the most part, only the names of the functions have changed, with
+			<quote>rtpproxy</quote> in each name replaced with <quote>rtpengine</quote>.
+			For example, <quote>rtpproxy_manage()</quote> has become
+			<quote>rtpengine_manage()</quote>. A few name duplications have also been resolved,
+			for example there is now a single <quote>rtpengine_delete()</quote> instead of
+			<quote>unforce_rtp_proxy()</quote> and the identical <quote>rtpproxy_destroy()</quote>.
+		</para>
+		<para>
+			The largest difference to the old module is how flags are passed to
+			<quote>rtpengine_offer()</quote>, <quote>rtpengine_answer()</quote>,
+			<quote>rtpengine_manage()</quote> and <quote>rtpengine_delete()</quote>. Instead of
+			having a string of single-letter flags, they now take a string of space-separated
+			items, with each item being either a single token (word) or a <quote>key=value</quote>
+			pair.
+		</para>
+		<para>
+			For example, if you had a call <quote>rtpproxy_offer("FRWOC+PS");</quote>, this would
+			then become:
+		</para>
+		<programlisting>
+rtpengine_offer("force trust-address symmetric replace-origin replace-session-connection ICE=force RTP/SAVPF");
+		</programlisting>
+		<para>
+			Finally, if you were using the second paramater (explicit media address) to any of
+			these functions, this has been replaced by the <quote>media-address=...</quote>
+			option within the first string of flags.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>Where can I find more about OpenSIPS?</para>
+	    </question>
+	    <answer>
+		<para>
+			Take a look at &osipshomelink;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>Where can I post a question about this module?</para>
+	    </question>
+	    <answer>
+		<para>
+			First at all check if your question was already answered on one of
+			our mailing lists:
+		</para>
+		<itemizedlist>
+		    <listitem>
+			<para>User Mailing List - &osipsuserslink;</para>
+		    </listitem>
+		    <listitem>
+			<para>Developer Mailing List - &osipsdevlink;</para>
+		    </listitem>
+		</itemizedlist>
+		<para>
+			E-mails regarding any stable &osips; release should be sent to
+			&osipsusersmail; and e-mails regarding development versions
+			should be sent to &osipsdevmail;.
+		</para>
+		<para>
+			If you want to keep the mail private, send it to
+			&osipshelpmail;.
+		</para>
+	    </answer>
+	</qandaentry>
+	<qandaentry>
+	    <question>
+		<para>How can I report a bug?</para>
+	    </question>
+	    <answer>
+		<para>
+			Please follow the guidelines provided at:
+			&osipsbugslink;.
+		</para>
+	    </answer>
+	</qandaentry>
+    </qandaset>
+</chapter>
+
diff --git a/modules/rtpengine/rtpengine.c b/modules/rtpengine/rtpengine.c
new file mode 100644
index 000000000..cc86867c8
--- /dev/null
+++ b/modules/rtpengine/rtpengine.c
@@ -0,0 +1,1908 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2014-06-17 Imported from rtpproxy module
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifndef __USE_BSD
+#define  __USE_BSD
+#endif
+#include <netinet/ip.h>
+#ifndef __FAVOR_BSD
+#define __FAVOR_BSD
+#endif
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../../str.h"
+#include "../../flags.h"
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../data_lump.h"
+#include "../../data_lump_rpl.h"
+#include "../../error.h"
+#include "../../forward.h"
+#include "../../mem/mem.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_to.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parser_f.h"
+#include "../../parser/sdp/sdp.h"
+#include "../../resolve.h"
+#include "../../timer.h"
+#include "../../trim.h"
+#include "../../ut.h"
+#include "../../pt.h"
+#include "../../pvar.h"
+#include "../../msg_translator.h"
+#include "../../usr_avp.h"
+#include "../../socket_info.h"
+#include "../../mod_fix.h"
+#include "../../dset.h"
+#include "../../route.h"
+#include "../../modules/tm/tm_load.h"
+#include "rtpengine.h"
+#include "rtpengine_funcs.h"
+#include "bencode.h"
+
+#if !defined(AF_LOCAL)
+#define	AF_LOCAL AF_UNIX
+#endif
+#if !defined(PF_LOCAL)
+#define	PF_LOCAL PF_UNIX
+#endif
+
+#define DEFAULT_RTPE_SET_ID		0
+
+#define MI_ENABLE_RTP_ENGINE			"rtpengine_enable"
+#define MI_MIN_RECHECK_TICKS		0
+#define MI_MAX_RECHECK_TICKS		(unsigned int)-1
+
+#define MI_SHOW_RTP_ENGINES			"rtpengine_show"
+
+#define MI_RTP_ENGINE_NOT_FOUND		"RTP engine not found"
+#define MI_RTP_ENGINE_NOT_FOUND_LEN	(sizeof(MI_RTP_ENGINE_NOT_FOUND)-1)
+#define MI_SET						"Set"
+#define MI_SET_LEN					(sizeof(MI_SET)-1)
+#define MI_NODE						"node"
+#define MI_NODE_LEN					(sizeof(MI_NODE)-1)
+#define MI_INDEX					"index"
+#define MI_INDEX_LEN				(sizeof(MI_INDEX)-1)
+#define MI_DISABLED					"disabled"
+#define MI_DISABLED_LEN				(sizeof(MI_DISABLED)-1)
+#define MI_WEIGHT					"weight"
+#define MI_WEIGHT_LEN				(sizeof(MI_WEIGHT)-1)
+#define MI_RECHECK_TICKS			"recheck_ticks"
+#define MI_RECHECK_T_LEN			(sizeof(MI_RECHECK_TICKS)-1)
+
+
+
+#define	CPORT		"22222"
+
+enum rtpe_operation {
+	OP_OFFER = 1,
+	OP_ANSWER,
+	OP_DELETE,
+	OP_START_RECORDING,
+	OP_QUERY,
+};
+
+struct ng_flags_parse {
+	int via, to, packetize, transport;
+	bencode_item_t *dict, *flags, *direction, *replace, *rtcp_mux;
+};
+
+static const char *command_strings[] = {
+	[OP_OFFER]		= "offer",
+	[OP_ANSWER]		= "answer",
+	[OP_DELETE]		= "delete",
+	[OP_START_RECORDING]	= "start recording",
+	[OP_QUERY]		= "query",
+};
+
+static char *gencookie();
+static int rtpe_test(struct rtpe_node*, int, int);
+static int start_recording_f(struct sip_msg *, char *, char *);
+static int rtpengine_answer1_f(struct sip_msg *, char *, char *);
+static int rtpengine_offer1_f(struct sip_msg *, char *, char *);
+static int rtpengine_delete1_f(struct sip_msg *, char *, char *);
+static int rtpengine_manage1_f(struct sip_msg *, char *, char *);
+
+static int parse_flags(struct ng_flags_parse *, struct sip_msg *, enum rtpe_operation *, const char *);
+
+static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op);
+static int add_rtpengine_socks(struct rtpe_set * rtpe_list, char * rtpengine);
+static int fixup_set_id(void ** param, int param_no);
+static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2);
+static struct rtpe_set * select_rtpe_set(int id_set);
+static struct rtpe_node *select_rtpe_node(str, int);
+static char *send_rtpe_command(struct rtpe_node *, bencode_item_t *, int *);
+static int get_extra_id(struct sip_msg* msg, str *id_str);
+
+static int rtpengine_set_store(modparam_t type, void * val);
+static int rtpengine_add_rtpengine_set( char * rtp_proxies);
+
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+
+/* Pseudo-Variables */
+static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+
+/*mi commands*/
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
+		void* param );
+static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree,
+		void* param);
+
+
+static int rtpengine_disable_tout = 60;
+static int rtpengine_retr = 5;
+static int rtpengine_tout = 1;
+static pid_t mypid;
+static unsigned int myseqn = 0;
+static str extra_id_pv_param = {NULL, 0};
+static char *setid_avp_param = NULL;
+
+static char ** rtpe_strings=0;
+static int rtpe_sets=0; /*used in rtpengine_set_store()*/
+static int rtpe_set_count = 0;
+static unsigned int current_msg_id = (unsigned int)-1;
+/* RTP proxy balancing list */
+struct rtpe_set_head * rtpe_set_list =0;
+struct rtpe_set * selected_rtpe_set =0;
+struct rtpe_set * default_rtpe_set=0;
+
+/* array with the sockets used by rtpengine (per process)*/
+static unsigned int rtpe_no = 0;
+static int *rtpe_socks = 0;
+
+static int     setid_avp_type;
+static int_str setid_avp;
+
+typedef struct rtpe_set_link {
+	struct rtpe_set *rset;
+	pv_spec_t rpv;
+} rtpe_set_link_t;
+
+/* tm */
+static struct tm_binds tmb;
+
+/*0-> disabled, 1 ->enabled*/
+unsigned int *natping_state=0;
+
+static pv_elem_t *extra_id_pv = NULL;
+
+#define ANY_ROUTE     (REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE)
+static cmd_export_t cmds[] = {
+	{"rtpengine_use_set",  (cmd_function)set_rtpengine_set_f,    1,
+		fixup_set_id, 0,
+		ANY_ROUTE},
+	{"rtpengine_start_recording", (cmd_function)start_recording_f,      0,
+		0, 0,
+		ANY_ROUTE },
+	{"rtpengine_offer",	(cmd_function)rtpengine_offer1_f,     0,
+		0, 0,
+		ANY_ROUTE},
+	{"rtpengine_offer",	(cmd_function)rtpengine_offer1_f,     1,
+		fixup_spve_null, 0,
+		ANY_ROUTE},
+	{"rtpengine_answer",	(cmd_function)rtpengine_answer1_f,    0,
+		0, 0,
+		ANY_ROUTE},
+	{"rtpengine_answer",	(cmd_function)rtpengine_answer1_f,    1,
+		fixup_spve_null, 0,
+		ANY_ROUTE},
+	{"rtpengine_manage",	(cmd_function)rtpengine_manage1_f,     0,
+		0, 0,
+		ANY_ROUTE},
+	{"rtpengine_manage",	(cmd_function)rtpengine_manage1_f,     1,
+		fixup_spve_null, 0,
+		ANY_ROUTE},
+	{"rtpengine_delete",  (cmd_function)rtpengine_delete1_f,    0,
+		0, 0,
+		ANY_ROUTE},
+	{"rtpengine_delete",  (cmd_function)rtpengine_delete1_f,    1,
+		fixup_spve_null, 0,
+		ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static pv_export_t mod_pvs[] = {
+    {{"rtpstat", (sizeof("rtpstat")-1)}, /* RTP-Statistics */
+     1000, pv_get_rtpstat_f, 0, 0, 0, 0, 0},
+    {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+	{"rtpengine_sock",         STR_PARAM|USE_FUNC_PARAM,
+	                         (void*)rtpengine_set_store          },
+	{"rtpengine_disable_tout", INT_PARAM, &rtpengine_disable_tout },
+	{"rtpengine_retr",         INT_PARAM, &rtpengine_retr         },
+	{"rtpengine_tout",         INT_PARAM, &rtpengine_tout         },
+	{"extra_id_pv",           STR_PARAM, &extra_id_pv_param.s },
+	{"setid_avp",             STR_PARAM, &setid_avp_param },
+	{0, 0, 0}
+};
+
+static mi_export_t mi_cmds[] = {
+	{MI_ENABLE_RTP_ENGINE,     0, mi_enable_rtp_proxy,  0,                0, 0},
+	{MI_SHOW_RTP_ENGINES,      0, mi_show_rtpengines,   MI_NO_INPUT_FLAG, 0, 0},
+	{ 0, 0, 0, 0, 0, 0}
+};
+
+struct module_exports exports = {
+	"rtpengine",
+	MODULE_VERSION,
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,           /* exported statistics */
+	mi_cmds,     /* exported MI functions */
+	mod_pvs,     /* exported pseudo-variables */
+	0,           /* extra processes */
+	mod_init,
+	0,           /* reply processing */
+	mod_destroy, /* destroy function */
+	child_init
+};
+
+int msg_has_sdp(struct sip_msg *msg)
+{
+	str body;
+	struct part *p;
+	struct multi_body *m;
+
+	if(parse_headers(msg, HDR_CONTENTLENGTH_F,0) < 0) {
+		LM_ERR("cannot parse cseq header");
+		return 0;
+	}
+
+	body.len = get_content_length(msg);
+	if (!body.len)
+		return 0;
+
+	m = get_all_bodies(msg);
+	if (!m) {
+		LM_DBG("cannot parse body\n");
+		return 0;
+	}
+
+	for (p = m->first; p; p = p->next) {
+		if (p->content_type == ((TYPE_APPLICATION << 16) + SUBTYPE_SDP))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+
+static inline int str_eq(const str *p, const char *q) {
+	int l = strlen(q);
+	if (p->len != l)
+		return 0;
+	if (memcmp(p->s, q, l))
+		return 0;
+	return 1;
+}
+
+
+static int rtpengine_set_store(modparam_t type, void * val){
+
+	char * p;
+	int len;
+
+	p = (char* )val;
+
+	if(p==0 || *p=='\0'){
+		return 0;
+	}
+
+	if(rtpe_sets==0){
+		rtpe_strings = (char**)pkg_malloc(sizeof(char*));
+		if(!rtpe_strings){
+			LM_ERR("no pkg memory left\n");
+			return -1;
+		}
+	} else {/*realloc to make room for the current set*/
+		rtpe_strings = (char**)pkg_realloc(rtpe_strings,
+										  (rtpe_sets+1)* sizeof(char*));
+		if(!rtpe_strings){
+			LM_ERR("no pkg memory left\n");
+			return -1;
+		}
+	}
+
+	/*allocate for the current set of urls*/
+	len = strlen(p);
+	rtpe_strings[rtpe_sets] = (char*)pkg_malloc((len+1)*sizeof(char));
+
+	if(!rtpe_strings[rtpe_sets]){
+		LM_ERR("no pkg memory left\n");
+		return -1;
+	}
+
+	memcpy(rtpe_strings[rtpe_sets], p, len);
+	rtpe_strings[rtpe_sets][len] = '\0';
+	rtpe_sets++;
+
+	return 0;
+}
+
+
+static int add_rtpengine_socks(struct rtpe_set * rtpe_list,
+										char * rtpengine){
+	/* Make rtp proxies list. */
+	char *p, *p1, *p2, *plim;
+	struct rtpe_node *pnode;
+	int weight;
+
+	p = rtpengine;
+	plim = p + strlen(p);
+
+	for(;;) {
+			weight = 1;
+		while (*p && isspace((int)*p))
+			++p;
+		if (p >= plim)
+			break;
+		p1 = p;
+		while (*p && !isspace((int)*p))
+			++p;
+		if (p <= p1)
+			break; /* may happen??? */
+		/* Have weight specified? If yes, scan it */
+		p2 = memchr(p1, '=', p - p1);
+		if (p2 != NULL) {
+			weight = strtoul(p2 + 1, NULL, 10);
+		} else {
+			p2 = p;
+		}
+		pnode = shm_malloc(sizeof(struct rtpe_node));
+		if (pnode == NULL) {
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memset(pnode, 0, sizeof(*pnode));
+		pnode->idx = rtpe_no++;
+		pnode->rn_recheck_ticks = 0;
+		pnode->rn_weight = weight;
+		pnode->rn_umode = 0;
+		pnode->rn_disabled = 0;
+		pnode->rn_url.s = shm_malloc(p2 - p1 + 1);
+		if (pnode->rn_url.s == NULL) {
+			shm_free(pnode);
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memmove(pnode->rn_url.s, p1, p2 - p1);
+		pnode->rn_url.s[p2 - p1] 	= 0;
+		pnode->rn_url.len 			= p2-p1;
+
+		LM_DBG("url is %s, len is %i\n", pnode->rn_url.s, pnode->rn_url.len);
+		/* Leave only address in rn_address */
+		pnode->rn_address = pnode->rn_url.s;
+		if (strncasecmp(pnode->rn_address, "udp:", 4) == 0) {
+			pnode->rn_umode = 1;
+			pnode->rn_address += 4;
+		} else if (strncasecmp(pnode->rn_address, "udp6:", 5) == 0) {
+			pnode->rn_umode = 6;
+			pnode->rn_address += 5;
+		} else if (strncasecmp(pnode->rn_address, "unix:", 5) == 0) {
+			pnode->rn_umode = 0;
+			pnode->rn_address += 5;
+		}
+
+		if (rtpe_list->rn_first == NULL) {
+			rtpe_list->rn_first = pnode;
+		} else {
+			rtpe_list->rn_last->rn_next = pnode;
+		}
+
+		rtpe_list->rn_last = pnode;
+		rtpe_list->rtpe_node_count++;
+	}
+	return 0;
+}
+
+
+/*	0-succes
+ *  -1 - erorr
+ * */
+static int rtpengine_add_rtpengine_set( char * rtp_proxies)
+{
+	char *p,*p2;
+	struct rtpe_set * rtpe_list;
+	unsigned int my_current_id;
+	str id_set;
+	int new_list;
+
+	/* empty definition? */
+	p= rtp_proxies;
+	if(!p || *p=='\0'){
+		return 0;
+	}
+
+	for(;*p && isspace(*p);p++);
+	if(*p=='\0'){
+		return 0;
+	}
+
+	rtp_proxies = strstr(p, "==");
+	if(rtp_proxies){
+		if(*(rtp_proxies +2)=='\0'){
+			LM_ERR("script error -invalid rtp proxy list!\n");
+			return -1;
+		}
+
+		*rtp_proxies = '\0';
+		p2 = rtp_proxies-1;
+		for(;isspace(*p2); *p2 = '\0',p2--);
+		id_set.s = p;	id_set.len = p2 - p+1;
+
+		if(id_set.len <= 0 ||str2int(&id_set, &my_current_id)<0 ){
+		LM_ERR("script error -invalid set_id value!\n");
+			return -1;
+		}
+
+		rtp_proxies+=2;
+	}else{
+		rtp_proxies = p;
+		my_current_id = DEFAULT_RTPE_SET_ID;
+	}
+
+	for(;*rtp_proxies && isspace(*rtp_proxies);rtp_proxies++);
+
+	if(!(*rtp_proxies)){
+		LM_ERR("script error -empty rtp_proxy list\n");
+		return -1;;
+	}
+
+	/*search for the current_id*/
+	rtpe_list = rtpe_set_list ? rtpe_set_list->rset_first : 0;
+	while( rtpe_list != 0 && rtpe_list->id_set!=my_current_id)
+		rtpe_list = rtpe_list->rset_next;
+
+	if(rtpe_list==NULL){	/*if a new id_set : add a new set of rtpe*/
+		rtpe_list = shm_malloc(sizeof(struct rtpe_set));
+		if(!rtpe_list){
+			LM_ERR("no shm memory left\n");
+			return -1;
+		}
+		memset(rtpe_list, 0, sizeof(struct rtpe_set));
+		rtpe_list->id_set = my_current_id;
+		new_list = 1;
+	} else {
+		new_list = 0;
+	}
+
+	if(add_rtpengine_socks(rtpe_list, rtp_proxies)!= 0){
+		/*if this list will not be inserted, clean it up*/
+		goto error;
+	}
+
+	if (new_list) {
+		if(!rtpe_set_list){/*initialize the list of set*/
+			rtpe_set_list = shm_malloc(sizeof(struct rtpe_set_head));
+			if(!rtpe_set_list){
+				LM_ERR("no shm memory left\n");
+				return -1;
+			}
+			memset(rtpe_set_list, 0, sizeof(struct rtpe_set_head));
+		}
+
+		/*update the list of set info*/
+		if(!rtpe_set_list->rset_first){
+			rtpe_set_list->rset_first = rtpe_list;
+		}else{
+			rtpe_set_list->rset_last->rset_next = rtpe_list;
+		}
+
+		rtpe_set_list->rset_last = rtpe_list;
+		rtpe_set_count++;
+
+		if(my_current_id == DEFAULT_RTPE_SET_ID){
+			default_rtpe_set = rtpe_list;
+		}
+	}
+
+	return 0;
+error:
+	return -1;
+}
+
+
+static int fixup_set_id(void ** param, int param_no)
+{
+	int int_val, err;
+	struct rtpe_set* rtpe_list;
+	rtpe_set_link_t *rtpl = NULL;
+	str s;
+
+	rtpl = (rtpe_set_link_t*)pkg_malloc(sizeof(rtpe_set_link_t));
+	if(rtpl==NULL) {
+		LM_ERR("no more pkg memory\n");
+		return -1;
+	}
+	memset(rtpl, 0, sizeof(rtpe_set_link_t));
+	s.s = (char*)*param;
+	s.len = strlen(s.s);
+
+	if(s.s[0] == PV_MARKER) {
+		if ( pv_parse_spec(&s, &rtpl->rpv) == NULL ) {
+			LM_ERR("invalid parameter %s\n", s.s);
+			return -1;
+		}
+	} else {
+		int_val = str2s(*param, strlen(*param), &err);
+		if (err == 0) {
+			pkg_free(*param);
+			if((rtpe_list = select_rtpe_set(int_val)) ==0){
+				LM_ERR("rtpe_proxy set %i not configured\n", int_val);
+				return E_CFG;
+			}
+			rtpl->rset = rtpe_list;
+		} else {
+			LM_ERR("bad number <%s>\n",	(char *)(*param));
+			return E_CFG;
+		}
+	}
+	*param = (void*)rtpl;
+	return 0;
+}
+
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
+												void* param )
+{	struct mi_node* node;
+	str rtpe_url;
+	unsigned int enable;
+	struct rtpe_set * rtpe_list;
+	struct rtpe_node * crt_rtpe;
+	int found;
+
+	found = 0;
+
+	if(rtpe_set_list ==NULL)
+		goto end;
+
+	node = cmd_tree->node.kids;
+	if(node == NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if(node->value.s == NULL || node->value.len ==0)
+		return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+
+	rtpe_url = node->value;
+
+	node = node->next;
+	if(node == NULL)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	enable = 0;
+	if( strno2int( &node->value, &enable) <0)
+		goto error;
+
+	for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL;
+					rtpe_list = rtpe_list->rset_next){
+
+		for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL;
+						crt_rtpe = crt_rtpe->rn_next){
+			/*found a matching rtpe*/
+
+			if(crt_rtpe->rn_url.len == rtpe_url.len){
+
+				if(strncmp(crt_rtpe->rn_url.s, rtpe_url.s, rtpe_url.len) == 0){
+					/*set the enabled/disabled status*/
+					found = 1;
+					crt_rtpe->rn_recheck_ticks =
+						enable? MI_MIN_RECHECK_TICKS : MI_MAX_RECHECK_TICKS;
+					crt_rtpe->rn_disabled = enable?0:1;
+				}
+			}
+		}
+	}
+
+end:
+	if(found)
+		return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	return init_mi_tree(404,MI_RTP_ENGINE_NOT_FOUND,MI_RTP_ENGINE_NOT_FOUND_LEN);
+error:
+	return init_mi_tree( 400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+}
+
+
+
+#define add_rtpe_node_int_info(_parent, _name, _name_len, _value, _attr,\
+								_len, _string, _error)\
+	do {\
+		(_string) = int2str((_value), &(_len));\
+		if((_string) == 0){\
+			LM_ERR("cannot convert int value\n");\
+				goto _error;\
+		}\
+		if(((_attr) = add_mi_attr((_parent), MI_DUP_VALUE, (_name), \
+				(_name_len), (_string), (_len))   ) == 0)\
+			goto _error;\
+	}while(0);
+
+static struct mi_root* mi_show_rtpengines(struct mi_root* cmd_tree,
+												void* param)
+{
+	struct mi_node* node, *crt_node, *set_node;
+	struct mi_root* root;
+	struct mi_attr * attr;
+	struct rtpe_set * rtpe_list;
+	struct rtpe_node * crt_rtpe;
+	char * string, *id;
+	int id_len, len;
+
+	string = id = 0;
+
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		return 0;
+	}
+
+	if(rtpe_set_list ==NULL)
+		return root;
+
+	node = &root->node;
+	node->flags |= MI_IS_ARRAY;
+
+	for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != NULL;
+					rtpe_list = rtpe_list->rset_next){
+
+		id =  int2str(rtpe_list->id_set, &id_len);
+		if(!id){
+			LM_ERR("cannot convert set id\n");
+			goto error;
+		}
+
+		if(!(set_node = add_mi_node_child(node, MI_IS_ARRAY|MI_DUP_VALUE, MI_SET, MI_SET_LEN,
+									id, id_len))) {
+			LM_ERR("cannot add the set node to the tree\n");
+			goto error;
+		}
+
+		for(crt_rtpe = rtpe_list->rn_first; crt_rtpe != NULL;
+						crt_rtpe = crt_rtpe->rn_next){
+
+			if(!(crt_node = add_mi_node_child(node, MI_DUP_VALUE,
+					MI_NODE, MI_NODE_LEN,
+					crt_rtpe->rn_url.s, crt_rtpe->rn_url.len)) ) {
+				LM_ERR("cannot add the child node to the tree\n");
+				goto error;
+			}
+
+			LM_DBG("adding node name %s \n",crt_rtpe->rn_url.s );
+
+			add_rtpe_node_int_info(crt_node, MI_INDEX, MI_INDEX_LEN,
+				crt_rtpe->idx, attr, len,string,error);
+			add_rtpe_node_int_info(crt_node, MI_DISABLED, MI_DISABLED_LEN,
+				crt_rtpe->rn_disabled, attr, len,string,error);
+			add_rtpe_node_int_info(crt_node, MI_WEIGHT, MI_WEIGHT_LEN,
+				crt_rtpe->rn_weight, attr, len, string,error);
+			add_rtpe_node_int_info(crt_node, MI_RECHECK_TICKS,MI_RECHECK_T_LEN,
+				crt_rtpe->rn_recheck_ticks, attr, len, string, error);
+		}
+	}
+
+	return root;
+error:
+	if (root)
+		free_mi_tree(root);
+	return 0;
+}
+
+
+static int
+mod_init(void)
+{
+	int i;
+	pv_spec_t avp_spec;
+	unsigned short avp_flags;
+	str s;
+
+	if(register_mi_mod(exports.name, mi_cmds)!=0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
+	/* any rtpengine configured? */
+	if(rtpe_set_list)
+		default_rtpe_set = select_rtpe_set(DEFAULT_RTPE_SET_ID);
+
+	/* storing the list of rtp proxy sets in shared memory*/
+	for(i=0;i<rtpe_sets;i++){
+		if(rtpengine_add_rtpengine_set(rtpe_strings[i]) !=0){
+			for(;i<rtpe_sets;i++)
+				if(rtpe_strings[i])
+					pkg_free(rtpe_strings[i]);
+			pkg_free(rtpe_strings);
+			return -1;
+		}
+		if(rtpe_strings[i])
+			pkg_free(rtpe_strings[i]);
+	}
+
+	if (extra_id_pv_param.s && *extra_id_pv_param.s) {
+		extra_id_pv_param.len = strlen(extra_id_pv_param.s);
+		if(pv_parse_format(&extra_id_pv_param, &extra_id_pv) < 0) {
+			LM_ERR("malformed PV string: %s\n", extra_id_pv_param.s);
+			return -1;
+		}
+	} else {
+		extra_id_pv = NULL;
+	}
+
+	if (setid_avp_param) {
+		s.s = setid_avp_param; s.len = strlen(s.s);
+		pv_parse_spec(&s, &avp_spec);
+		if (avp_spec.type != PVT_AVP) {
+			LM_ERR("malformed or non AVP definition <%s>\n",
+					setid_avp_param);
+			return -1;
+		}
+		if (pv_get_avp_name(0, &(avp_spec.pvp), &(setid_avp.n),
+					&avp_flags) != 0) {
+			LM_ERR("invalid AVP definition <%s>\n", setid_avp_param);
+			return -1;
+		}
+		setid_avp_type = avp_flags;
+	}
+
+	if (rtpe_strings)
+		pkg_free(rtpe_strings);
+
+	if (load_tm_api( &tmb ) < 0)
+	{
+		LM_DBG("could not load the TM-functions - answer-offer model"
+				" auto-detection is disabled\n");
+		memset(&tmb, 0, sizeof(struct tm_binds));
+	}
+
+	return 0;
+}
+
+
+static int
+child_init(int rank)
+{
+	int n;
+	char *cp;
+	struct addrinfo hints, *res;
+	struct rtpe_set  *rtpe_list;
+	struct rtpe_node *pnode;
+
+	if(rtpe_set_list==NULL )
+		return 0;
+
+	/* Iterate known RTP proxies - create sockets */
+	mypid = getpid();
+
+	rtpe_socks = (int*)pkg_malloc( sizeof(int)*rtpe_no );
+	if (rtpe_socks==NULL) {
+		LM_ERR("no more pkg memory\n");
+		return -1;
+	}
+
+	for(rtpe_list = rtpe_set_list->rset_first; rtpe_list != 0;
+		rtpe_list = rtpe_list->rset_next){
+
+		for (pnode=rtpe_list->rn_first; pnode!=0; pnode = pnode->rn_next){
+			char *hostname;
+
+			if (pnode->rn_umode == 0) {
+				rtpe_socks[pnode->idx] = -1;
+				goto rptest;
+			}
+
+			/*
+			 * This is UDP or UDP6. Detect host and port; lookup host;
+			 * do connect() in order to specify peer address
+			 */
+			hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->rn_address) + 1));
+			if (hostname==NULL) {
+				LM_ERR("no more pkg memory\n");
+				return -1;
+			}
+			strcpy(hostname, pnode->rn_address);
+
+			cp = strrchr(hostname, ':');
+			if (cp != NULL) {
+				*cp = '\0';
+				cp++;
+			}
+			if (cp == NULL || *cp == '\0')
+				cp = CPORT;
+
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_flags = 0;
+			hints.ai_family = (pnode->rn_umode == 6) ? AF_INET6 : AF_INET;
+			hints.ai_socktype = SOCK_DGRAM;
+			if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) {
+				LM_ERR("%s\n", gai_strerror(n));
+				pkg_free(hostname);
+				return -1;
+			}
+			pkg_free(hostname);
+
+			rtpe_socks[pnode->idx] = socket((pnode->rn_umode == 6)
+			    ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
+			if ( rtpe_socks[pnode->idx] == -1) {
+				LM_ERR("can't create socket\n");
+				freeaddrinfo(res);
+				return -1;
+			}
+
+			if (connect( rtpe_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) {
+				LM_ERR("can't connect to a RTP proxy\n");
+				close( rtpe_socks[pnode->idx] );
+				rtpe_socks[pnode->idx] = -1;
+				freeaddrinfo(res);
+				return -1;
+			}
+			freeaddrinfo(res);
+rptest:
+			pnode->rn_disabled = rtpe_test(pnode, 0, 1);
+		}
+	}
+
+	return 0;
+}
+
+
+static void mod_destroy(void)
+{
+	struct rtpe_set * crt_list, * last_list;
+	struct rtpe_node * crt_rtpe, *last_rtpe;
+
+	/*free the shared memory*/
+	if (natping_state)
+		shm_free(natping_state);
+
+	if(rtpe_set_list == NULL)
+		return;
+
+	for(crt_list = rtpe_set_list->rset_first; crt_list != NULL; ){
+
+		for(crt_rtpe = crt_list->rn_first; crt_rtpe != NULL;  ){
+
+			if(crt_rtpe->rn_url.s)
+				shm_free(crt_rtpe->rn_url.s);
+
+			last_rtpe = crt_rtpe;
+			crt_rtpe = last_rtpe->rn_next;
+			shm_free(last_rtpe);
+		}
+
+		last_list = crt_list;
+		crt_list = last_list->rset_next;
+		shm_free(last_list);
+	}
+
+	shm_free(rtpe_set_list);
+}
+
+
+
+static char * gencookie(void)
+{
+	static char cook[34];
+
+	sprintf(cook, "%d_%u ", (int)mypid, myseqn);
+	myseqn++;
+	return cook;
+}
+
+
+
+static const char *transports[] = {
+	[0x00]	= "RTP/AVP",
+	[0x01]	= "RTP/SAVP",
+	[0x02]	= "RTP/AVPF",
+	[0x03]	= "RTP/SAVPF",
+};
+
+static int parse_flags(struct ng_flags_parse *ng_flags, struct sip_msg *msg, enum rtpe_operation *op,
+		const char *flags_str)
+{
+	char *e;
+	const char *err;
+	str key, val;
+
+	if (!flags_str)
+		return 0;
+
+	while (1) {
+		while (*flags_str == ' ')
+			flags_str++;
+
+		key.s = (void *) flags_str;
+		val.len = key.len = -1;
+		val.s = NULL;
+
+		e = strpbrk(key.s, " =");
+		if (!e)
+			e = key.s + strlen(key.s);
+		else if (*e == '=') {
+			key.len = e - key.s;
+			val.s = e + 1;
+			e = strchr(val.s, ' ');
+			if (!e)
+				e = val.s + strlen(val.s);
+			val.len = e - val.s;
+		}
+
+		if (key.len == -1)
+			key.len = e - key.s;
+		if (!key.len)
+			break;
+
+		/* XXX make this prettier */
+		err = "unknown flag";
+		switch (key.len) {
+			case 3:
+				if (str_eq(&key, "ICE")) {
+					err = "missing value";
+					if (!val.s)
+						goto error;
+					err = "invalid value";
+					if (str_eq(&val, "force") || str_eq(&val, "remove"))
+						bencode_dictionary_add_str(ng_flags->dict, "ICE", &val);
+					else
+						goto error;
+				}
+				else if (str_eq(&key, "RTP")) {
+					ng_flags->transport |= 0x100;
+					ng_flags->transport &= ~0x001;
+				}
+				else if (str_eq(&key, "AVP")) {
+					ng_flags->transport |= 0x100;
+					ng_flags->transport &= ~0x002;
+				}
+				else
+					goto error;
+				break;
+
+			case 4:
+				if (str_eq(&key, "SRTP"))
+					ng_flags->transport |= 0x101;
+				else if (str_eq(&key, "AVPF"))
+					ng_flags->transport |= 0x102;
+				else
+					goto error;
+				break;
+
+			case 5:
+				if (str_eq(&key, "force"))
+					bencode_list_add_string(ng_flags->flags, "force");
+				else
+					goto error;
+				break;
+
+			case 6:
+				if (str_eq(&key, "to-tag"))
+					ng_flags->to = 1;
+				else
+					goto error;
+				break;
+
+			case 7:
+				if (str_eq(&key, "RTP/AVP"))
+					ng_flags->transport = 0x100;
+				else
+					goto error;
+				break;
+
+			case 8:
+				if (str_eq(&key, "internal"))
+					bencode_list_add_string(ng_flags->direction, "internal");
+				else if (str_eq(&key, "external"))
+					bencode_list_add_string(ng_flags->direction, "external");
+				else if (str_eq(&key, "RTP/AVPF"))
+					ng_flags->transport = 0x102;
+				else if (str_eq(&key, "RTP/SAVP"))
+					ng_flags->transport = 0x101;
+				else
+					goto error;
+				break;
+
+			case 9:
+				if (str_eq(&key, "symmetric"))
+					bencode_list_add_string(ng_flags->flags, "symmetric");
+				else if (str_eq(&key, "RTP/SAVPF"))
+					ng_flags->transport = 0x103;
+				else
+					goto error;
+				break;
+
+			case 10:
+				if (str_eq(&key, "via-branch")) {
+					err = "missing value";
+					if (!val.s)
+						goto error;
+					err = "invalid value";
+					if (*val.s == '1' || *val.s == '2')
+						ng_flags->via = *val.s - '0';
+					else if (str_eq(&val, "auto"))
+						ng_flags->via = 3;
+					else if (str_eq(&val, "extra"))
+						ng_flags->via = -1;
+					else
+						goto error;
+				}
+				else if (str_eq(&key, "asymmetric"))
+					bencode_list_add_string(ng_flags->flags, "asymmetric");
+				else
+					goto error;
+				break;
+
+			case 11:
+				if (str_eq(&key, "auto-bridge"))
+					bencode_list_add_string(ng_flags->flags, "auto-bridge");
+				else if (str_eq(&key, "repacketize")) {
+					err = "missing value";
+					if (!val.s)
+						goto error;
+					ng_flags->packetize = 0;
+					while (isdigit(*val.s)) {
+						ng_flags->packetize *= 10;
+						ng_flags->packetize += *val.s - '0';
+						val.s++;
+					}
+					err = "invalid value";
+					if (!ng_flags->packetize)
+						goto error;
+					bencode_dictionary_add_integer(ng_flags->dict, "repacketize", ng_flags->packetize);
+				}
+				else
+					goto error;
+				break;
+
+			case 12:
+				if (str_eq(&key, "force-answer")) {
+					err = "cannot force answer in non-offer command";
+					if (*op != OP_OFFER)
+						goto error;
+					*op = OP_ANSWER;
+				}
+				else
+					goto error;
+				break;
+			case 13:
+				if (str_eq(&key, "trust-address"))
+					bencode_list_add_string(ng_flags->flags, "trust-address");
+				else if (str_eq(&key, "media-address")) {
+					err = "missing value";
+					if (!val.s)
+						goto error;
+				}
+				else
+					goto error;
+				break;
+
+			case 14:
+				if (str_eq(&key, "replace-origin"))
+					bencode_list_add_string(ng_flags->replace, "origin");
+				else if (str_eq(&key, "address-family")) {
+					err = "missing value";
+					if (!val.s)
+						goto error;
+					err = "invalid value";
+					if (str_eq(&val, "IP4") || str_eq(&val, "IP6"))
+						bencode_dictionary_add_str(ng_flags->dict, "address family", &val);
+					else
+						goto error;
+				}
+				else if (str_eq(&key, "rtcp-mux-demux"))
+					bencode_list_add_string(ng_flags->rtcp_mux, "demux");
+				else if (str_eq(&key, "rtcp-mux-offer"))
+					bencode_list_add_string(ng_flags->rtcp_mux, "offer");
+				else
+					goto error;
+				break;
+
+			case 15:
+				if (str_eq(&key, "rtcp-mux-reject"))
+					bencode_list_add_string(ng_flags->rtcp_mux, "reject");
+				else if (str_eq(&key, "rtcp-mux-accept"))
+					bencode_list_add_string(ng_flags->rtcp_mux, "accept");
+				else
+					goto error;
+				break;
+
+			case 26:
+				if (str_eq(&key, "replace-session-connection"))
+					bencode_list_add_string(ng_flags->replace, "session-connection");
+				else
+					goto error;
+				break;
+
+			default:
+				goto error;
+		}
+
+		flags_str = e;
+	}
+
+	return 0;
+
+error:
+	if (val.s)
+		LM_ERR("error processing flag `%.*s' (value '%.*s'): %s\n", key.len, key.s,
+				val.len, val.s, err);
+	else
+		LM_ERR("error processing flag `%.*s': %s\n", key.len, key.s, err);
+	return -1;
+}
+
+static bencode_item_t *rtpe_function_call(bencode_buffer_t *bencbuf, struct sip_msg *msg,
+	enum rtpe_operation op, const char *flags_str, str *body_out)
+{
+	struct ng_flags_parse ng_flags;
+	bencode_item_t *item, *resp;
+	str callid, from_tag, to_tag, body, viabranch, error;
+	int ret;
+	struct rtpe_node *node;
+	char *cp;
+
+	/*** get & init basic stuff needed ***/
+
+	memset(&ng_flags, 0, sizeof(ng_flags));
+
+	if (get_callid(msg, &callid) == -1 || callid.len == 0) {
+		LM_ERR("can't get Call-Id field\n");
+		return NULL;
+	}
+	if (get_to_tag(msg, &to_tag) == -1) {
+		LM_ERR("can't get To tag\n");
+		return NULL;
+	}
+	if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) {
+		LM_ERR("can't get From tag\n");
+		return NULL;
+	}
+	if (bencode_buffer_init(bencbuf)) {
+		LM_ERR("could not initialize bencode_buffer_t\n");
+		return NULL;
+	}
+	ng_flags.dict = bencode_dictionary(bencbuf);
+
+	if (op == OP_OFFER || op == OP_ANSWER) {
+		ng_flags.flags = bencode_list(bencbuf);
+		ng_flags.direction = bencode_list(bencbuf);
+		ng_flags.replace = bencode_list(bencbuf);
+		ng_flags.rtcp_mux = bencode_list(bencbuf);
+
+		if (extract_body(msg, &body) == -1) {
+			LM_ERR("can't extract body from the message\n");
+			goto error;
+		}
+		bencode_dictionary_add_str(ng_flags.dict, "sdp", &body);
+	}
+
+	/*** parse flags & build dictionary ***/
+
+	ng_flags.to = (op == OP_DELETE) ? 0 : 1;
+
+	if (parse_flags(&ng_flags, msg, &op, flags_str))
+		goto error;
+
+	/* only add those if any flags were given at all */
+	if (ng_flags.direction && ng_flags.direction->child)
+		bencode_dictionary_add(ng_flags.dict, "direction", ng_flags.direction);
+	if (ng_flags.flags && ng_flags.flags->child)
+		bencode_dictionary_add(ng_flags.dict, "flags", ng_flags.flags);
+	if (ng_flags.replace && ng_flags.replace->child)
+		bencode_dictionary_add(ng_flags.dict, "replace", ng_flags.replace);
+	if ((ng_flags.transport & 0x100))
+		bencode_dictionary_add_string(ng_flags.dict, "transport-protocol",
+				transports[ng_flags.transport & 0x003]);
+	if (ng_flags.rtcp_mux && ng_flags.rtcp_mux->child)
+		bencode_dictionary_add(ng_flags.dict, "rtcp-mux", ng_flags.rtcp_mux);
+
+	bencode_dictionary_add_str(ng_flags.dict, "call-id", &callid);
+
+	if (ng_flags.via) {
+		if (ng_flags.via == 1 || ng_flags.via == 2)
+			ret = get_via_branch(msg, ng_flags.via, &viabranch);
+		else if (ng_flags.via == -1 && extra_id_pv)
+			ret = get_extra_id(msg, &viabranch);
+		else
+			ret = -1;
+		if (ret == -1 || viabranch.len == 0) {
+			LM_ERR("can't get Via branch/extra ID\n");
+			goto error;
+		}
+		bencode_dictionary_add_str(ng_flags.dict, "via-branch", &viabranch);
+	}
+
+	item = bencode_list(bencbuf);
+	bencode_dictionary_add(ng_flags.dict, "received-from", item);
+	bencode_list_add_string(item, (msg->rcv.src_ip.af == AF_INET) ? "IP4" : (
+		(msg->rcv.src_ip.af == AF_INET6) ? "IP6" :
+		"?"
+	) );
+	bencode_list_add_string(item, ip_addr2a(&msg->rcv.src_ip));
+
+	if ((msg->first_line.type == SIP_REQUEST && op != OP_ANSWER)
+		|| (msg->first_line.type == SIP_REPLY && op == OP_ANSWER))
+	{
+		bencode_dictionary_add_str(ng_flags.dict, "from-tag", &from_tag);
+		if (ng_flags.to && to_tag.s && to_tag.len)
+			bencode_dictionary_add_str(ng_flags.dict, "to-tag", &to_tag);
+	}
+	else {
+		if (!to_tag.s || !to_tag.len) {
+			LM_ERR("No to-tag present\n");
+			goto error;
+		}
+		bencode_dictionary_add_str(ng_flags.dict, "from-tag", &to_tag);
+		bencode_dictionary_add_str(ng_flags.dict, "to-tag", &from_tag);
+	}
+
+	bencode_dictionary_add_string(ng_flags.dict, "command", command_strings[op]);
+
+	/*** send it out ***/
+
+	if (bencbuf->error) {
+		LM_ERR("out of memory - bencode failed\n");
+		goto error;
+	}
+
+	if(msg->id != current_msg_id)
+		selected_rtpe_set = default_rtpe_set;
+
+	do {
+		node = select_rtpe_node(callid, 1);
+		if (!node) {
+			LM_ERR("no available proxies\n");
+			goto error;
+		}
+
+		cp = send_rtpe_command(node, ng_flags.dict, &ret);
+	} while (cp == NULL);
+	LM_DBG("proxy reply: %.*s\n", ret, cp);
+
+	/*** process reply ***/
+
+	resp = bencode_decode_expect(bencbuf, cp, ret, BENCODE_DICTIONARY);
+	if (!resp) {
+		LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp);
+		goto error;
+	}
+	if (!bencode_dictionary_get_strcmp(resp, "result", "error")) {
+		if (!bencode_dictionary_get_str(resp, "error-reason", &error))
+			LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp);
+		else
+			LM_ERR("proxy replied with error: %.*s\n", error.len, error.s);
+		goto error;
+	}
+
+	if (body_out)
+		*body_out = body;
+
+	return resp;
+
+error:
+	bencode_buffer_free(bencbuf);
+	return NULL;
+}
+
+static int rtpe_function_call_simple(struct sip_msg *msg, enum rtpe_operation op, const char *flags_str)
+{
+	bencode_buffer_t bencbuf;
+
+	if (!rtpe_function_call(&bencbuf, msg, op, flags_str, NULL))
+		return -1;
+
+	bencode_buffer_free(&bencbuf);
+	return 1;
+}
+
+static bencode_item_t *rtpe_function_call_ok(bencode_buffer_t *bencbuf, struct sip_msg *msg,
+		enum rtpe_operation op, const char *flags_str, str *body)
+{
+	bencode_item_t *ret;
+
+	ret = rtpe_function_call(bencbuf, msg, op, flags_str, body);
+	if (!ret)
+		return NULL;
+
+	if (bencode_dictionary_get_strcmp(ret, "result", "ok")) {
+		LM_ERR("proxy didn't return \"ok\" result\n");
+		bencode_buffer_free(bencbuf);
+		return NULL;
+	}
+
+	return ret;
+}
+
+
+
+static int
+rtpe_test(struct rtpe_node *node, int isdisabled, int force)
+{
+	bencode_buffer_t bencbuf;
+	bencode_item_t *dict;
+	char *cp;
+	int ret;
+
+	if(node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS){
+	    LM_DBG("rtpe %s disabled for ever\n", node->rn_url.s);
+		return 1;
+	}
+	if (force == 0) {
+		if (isdisabled == 0)
+			return 0;
+		if (node->rn_recheck_ticks > get_ticks())
+			return 1;
+	}
+
+	if (bencode_buffer_init(&bencbuf)) {
+		LM_ERR("could not initialized bencode_buffer_t\n");
+		return 1;
+	}
+	dict = bencode_dictionary(&bencbuf);
+	bencode_dictionary_add_string(dict, "command", "ping");
+	if (bencbuf.error)
+		goto benc_error;
+
+	cp = send_rtpe_command(node, dict, &ret);
+	if (!cp) {
+		LM_ERR("proxy did not respond to ping\n");
+		goto error;
+	}
+
+	dict = bencode_decode_expect(&bencbuf, cp, ret, BENCODE_DICTIONARY);
+	if (!dict || bencode_dictionary_get_strcmp(dict, "result", "pong")) {
+		LM_ERR("proxy responded with invalid response\n");
+		goto error;
+	}
+
+	LM_INFO("rtp proxy <%s> found, support for it %senabled\n",
+	    node->rn_url.s, force == 0 ? "re-" : "");
+
+	bencode_buffer_free(&bencbuf);
+	return 0;
+
+benc_error:
+        LM_ERR("out of memory - bencode failed\n");
+error:
+	bencode_buffer_free(&bencbuf);
+	return 1;
+}
+
+static char *
+send_rtpe_command(struct rtpe_node *node, bencode_item_t *dict, int *outlen)
+{
+	struct sockaddr_un addr;
+	int fd, len, i, vcnt;
+	char *cp;
+	static char buf[0x10000];
+	struct pollfd fds[1];
+	struct iovec *v;
+
+	v = bencode_iovec(dict, &vcnt, 1, 0);
+	if (!v) {
+		LM_ERR("error converting bencode to iovec\n");
+		return NULL;
+	}
+
+	len = 0;
+	cp = buf;
+	if (node->rn_umode == 0) {
+		memset(&addr, 0, sizeof(addr));
+		addr.sun_family = AF_LOCAL;
+		strncpy(addr.sun_path, node->rn_address,
+		    sizeof(addr.sun_path) - 1);
+#ifdef HAVE_SOCKADDR_SA_LEN
+		addr.sun_len = strlen(addr.sun_path);
+#endif
+
+		fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+		if (fd < 0) {
+			LM_ERR("can't create socket\n");
+			goto badproxy;
+		}
+		if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+			close(fd);
+			LM_ERR("can't connect to RTP proxy\n");
+			goto badproxy;
+		}
+
+		do {
+			len = writev(fd, v + 1, vcnt);
+		} while (len == -1 && errno == EINTR);
+		if (len <= 0) {
+			close(fd);
+			LM_ERR("can't send command to a RTP proxy\n");
+			goto badproxy;
+		}
+		do {
+			len = read(fd, buf, sizeof(buf) - 1);
+		} while (len == -1 && errno == EINTR);
+		close(fd);
+		if (len <= 0) {
+			LM_ERR("can't read reply from a RTP proxy\n");
+			goto badproxy;
+		}
+	} else {
+		fds[0].fd = rtpe_socks[node->idx];
+		fds[0].events = POLLIN;
+		fds[0].revents = 0;
+		/* Drain input buffer */
+		while ((poll(fds, 1, 0) == 1) &&
+		    ((fds[0].revents & POLLIN) != 0)) {
+			recv(rtpe_socks[node->idx], buf, sizeof(buf) - 1, 0);
+			fds[0].revents = 0;
+		}
+		v[0].iov_base = gencookie();
+		v[0].iov_len = strlen(v[0].iov_base);
+		for (i = 0; i < rtpengine_retr; i++) {
+			do {
+				len = writev(rtpe_socks[node->idx], v, vcnt + 1);
+			} while (len == -1 && (errno == EINTR || errno == ENOBUFS));
+			if (len <= 0) {
+				LM_ERR("can't send command to a RTP proxy\n");
+				goto badproxy;
+			}
+			while ((poll(fds, 1, rtpengine_tout * 1000) == 1) &&
+			    (fds[0].revents & POLLIN) != 0) {
+				do {
+					len = recv(rtpe_socks[node->idx], buf, sizeof(buf)-1, 0);
+				} while (len == -1 && errno == EINTR);
+				if (len <= 0) {
+					LM_ERR("can't read reply from a RTP proxy\n");
+					goto badproxy;
+				}
+				if (len >= (v[0].iov_len - 1) &&
+				    memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) {
+					len -= (v[0].iov_len - 1);
+					cp += (v[0].iov_len - 1);
+					if (len != 0) {
+						len--;
+						cp++;
+					}
+					goto out;
+				}
+				fds[0].revents = 0;
+			}
+		}
+		if (i == rtpengine_retr) {
+			LM_ERR("timeout waiting reply from a RTP proxy\n");
+			goto badproxy;
+		}
+	}
+
+out:
+	cp[len] = '\0';
+	*outlen = len;
+	return cp;
+badproxy:
+	LM_ERR("proxy <%s> does not respond, disable it\n", node->rn_url.s);
+	node->rn_disabled = 1;
+	node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout;
+
+	return NULL;
+}
+
+/*
+ * select the set with the id_set id
+ */
+
+static struct rtpe_set * select_rtpe_set(int id_set ){
+
+	struct rtpe_set * rtpe_list;
+	/*is it a valid set_id?*/
+
+	if(!rtpe_set_list || !rtpe_set_list->rset_first){
+		LM_ERR("no rtp_proxy configured\n");
+		return 0;
+	}
+
+	for(rtpe_list=rtpe_set_list->rset_first; rtpe_list!=0 &&
+		rtpe_list->id_set!=id_set; rtpe_list=rtpe_list->rset_next);
+	if(!rtpe_list){
+		LM_ERR(" script error-invalid id_set to be selected\n");
+	}
+
+	return rtpe_list;
+}
+/*
+ * Main balancing routine. This does not try to keep the same proxy for
+ * the call if some proxies were disabled or enabled; proxy death considered
+ * too rare. Otherwise we should implement "mature" HA clustering, which is
+ * too expensive here.
+ */
+static struct rtpe_node *
+select_rtpe_node(str callid, int do_test)
+{
+	unsigned sum, sumcut, weight_sum;
+	struct rtpe_node* node;
+	int was_forced;
+
+	if(!selected_rtpe_set){
+		LM_ERR("script error -no valid set selected\n");
+		return NULL;
+	}
+	/* Most popular case: 1 proxy, nothing to calculate */
+	if (selected_rtpe_set->rtpe_node_count == 1) {
+		node = selected_rtpe_set->rn_first;
+		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
+			node->rn_disabled = rtpe_test(node, 1, 0);
+		return node->rn_disabled ? NULL : node;
+	}
+
+	/* XXX Use quick-and-dirty hashing algo */
+	for(sum = 0; callid.len > 0; callid.len--)
+		sum += callid.s[callid.len - 1];
+	sum &= 0xff;
+
+	was_forced = 0;
+retry:
+	weight_sum = 0;
+	for (node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) {
+
+		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
+			/* Try to enable if it's time to try. */
+			node->rn_disabled = rtpe_test(node, 1, 0);
+		}
+		if (!node->rn_disabled)
+			weight_sum += node->rn_weight;
+	}
+	if (weight_sum == 0) {
+		/* No proxies? Force all to be redetected, if not yet */
+		if (was_forced)
+			return NULL;
+		was_forced = 1;
+		for(node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) {
+			node->rn_disabled = rtpe_test(node, 1, 1);
+		}
+		goto retry;
+	}
+	sumcut = sum % weight_sum;
+	/*
+	 * sumcut here lays from 0 to weight_sum-1.
+	 * Scan proxy list and decrease until appropriate proxy is found.
+	 */
+	for (node=selected_rtpe_set->rn_first; node!=NULL; node=node->rn_next) {
+		if (node->rn_disabled)
+			continue;
+		if (sumcut < node->rn_weight)
+			goto found;
+		sumcut -= node->rn_weight;
+	}
+	/* No node list */
+	return NULL;
+found:
+	if (do_test) {
+		node->rn_disabled = rtpe_test(node, node->rn_disabled, 0);
+		if (node->rn_disabled)
+			goto retry;
+	}
+	return node;
+}
+
+static int
+get_extra_id(struct sip_msg* msg, str *id_str) {
+	if(msg==NULL || extra_id_pv==NULL || id_str==NULL) {
+		LM_ERR("bad parameters\n");
+		return -1;
+	}
+	if (pv_printf_s(msg, extra_id_pv, id_str)<0) {
+		LM_ERR("cannot print the additional id\n");
+		return -1;
+	}
+
+	return 1;
+
+}
+
+static int
+set_rtpengine_set_from_avp(struct sip_msg *msg)
+{
+	struct usr_avp *avp;
+	int_str setid_val;
+
+	if ((setid_avp_param == NULL) ||
+			(avp = search_first_avp(setid_avp_type, setid_avp.n, &setid_val, 0))
+			== NULL)
+		return 1;
+
+	if (avp->flags&AVP_VAL_STR) {
+		LM_ERR("setid_avp must hold an integer value\n");
+		return -1;
+	}
+
+	selected_rtpe_set = select_rtpe_set(setid_val.n);
+	if(selected_rtpe_set == NULL) {
+		LM_ERR("could not locate rtpengine set %d\n", setid_val.n);
+		return -1;
+	}
+
+	LM_DBG("using rtpengine set %d\n", setid_val.n);
+
+	current_msg_id = msg->id;
+
+	return 1;
+}
+
+static int rtpengine_delete(struct sip_msg *msg, const char *flags) {
+	return rtpe_function_call_simple(msg, OP_DELETE, flags);
+}
+
+static int
+rtpengine_delete1_f(struct sip_msg* msg, char* str1, char* str2)
+{
+	str flags;
+
+	if (set_rtpengine_set_from_avp(msg) == -1)
+		return -1;
+
+	flags.s = NULL;
+	if (str1)
+		fixup_get_svalue(msg, (gparam_p)str1, &flags);
+
+	return rtpengine_delete(msg, flags.s);
+}
+
+/* This function assumes p points to a line of requested type. */
+
+static int
+set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2)
+{
+	rtpe_set_link_t *rtpl;
+	pv_value_t val;
+
+	rtpl = (rtpe_set_link_t*)str1;
+
+	current_msg_id = 0;
+	selected_rtpe_set = 0;
+
+	if(rtpl->rset != NULL) {
+		current_msg_id = msg->id;
+		selected_rtpe_set = rtpl->rset;
+	} else {
+		if(pv_get_spec_value(msg, &rtpl->rpv, &val)<0) {
+			LM_ERR("cannot evaluate pv param\n");
+			return -1;
+		}
+		if(!(val.flags & PV_VAL_INT)) {
+			LM_ERR("pv param must hold an integer value\n");
+			return -1;
+		}
+		selected_rtpe_set = select_rtpe_set(val.ri);
+		if(selected_rtpe_set==NULL) {
+			LM_ERR("could not locate rtpengine set %d\n", val.ri);
+			return -1;
+		}
+		current_msg_id = msg->id;
+	}
+	return 1;
+}
+
+static int
+rtpengine_manage(struct sip_msg *msg, const char *flags)
+{
+	int method;
+	int nosdp;
+
+	if(msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1)
+				|| (msg->cseq==NULL)))
+	{
+		LM_ERR("no CSEQ header\n");
+		return -1;
+	}
+
+	method = get_cseq(msg)->method_id;
+
+	if(!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL
+				|| method==METHOD_BYE || method==METHOD_UPDATE))
+		return -1;
+
+	if(method==METHOD_CANCEL || method==METHOD_BYE)
+		return rtpengine_delete(msg, flags);
+
+	if(msg_has_sdp(msg))
+		nosdp = 0;
+	else
+		nosdp = parse_sdp(msg);
+
+	if(msg->first_line.type == SIP_REQUEST) {
+		if(method==METHOD_ACK && nosdp==0)
+			return rtpengine_offer_answer(msg, flags, OP_ANSWER);
+		if(method==METHOD_UPDATE && nosdp==0)
+			return rtpengine_offer_answer(msg, flags, OP_OFFER);
+		if(method==METHOD_INVITE && nosdp==0) {
+			if(route_type==FAILURE_ROUTE)
+				return rtpengine_delete(msg, flags);
+			return rtpengine_offer_answer(msg, flags, OP_OFFER);
+		}
+	} else if(msg->first_line.type == SIP_REPLY) {
+		if(msg->first_line.u.reply.statuscode>=300)
+			return rtpengine_delete(msg, flags);
+		if(nosdp==0) {
+			if(method==METHOD_UPDATE)
+				return rtpengine_offer_answer(msg, flags, OP_ANSWER);
+			if(tmb.t_gett==NULL || tmb.t_gett()==NULL
+					|| tmb.t_gett()==T_UNDEFINED)
+				return rtpengine_offer_answer(msg, flags, OP_ANSWER);
+			return rtpengine_offer_answer(msg, flags, OP_OFFER);
+		}
+	}
+	return -1;
+}
+
+static int
+rtpengine_manage1_f(struct sip_msg *msg, char *str1, char *str2)
+{
+	str flags;
+
+	if (set_rtpengine_set_from_avp(msg) == -1)
+	    return -1;
+
+	flags.s = NULL;
+	if (str1)
+		fixup_get_svalue(msg, (gparam_p)str1, &flags);
+
+	return rtpengine_manage(msg, flags.s);
+}
+
+static int
+rtpengine_offer1_f(struct sip_msg *msg, char *str1, char *str2)
+{
+	str flags;
+
+	if (set_rtpengine_set_from_avp(msg) == -1)
+	    return -1;
+
+	flags.s = NULL;
+	if (str1)
+		fixup_get_svalue(msg, (gparam_p)str1, &flags);
+	return rtpengine_offer_answer(msg, flags.s, OP_OFFER);
+}
+
+static int
+rtpengine_answer1_f(struct sip_msg *msg, char *str1, char *str2)
+{
+	str flags;
+
+	if (set_rtpengine_set_from_avp(msg) == -1)
+	    return -1;
+
+	if (msg->first_line.type == SIP_REQUEST)
+		if (msg->first_line.u.request.method_value != METHOD_ACK)
+			return -1;
+
+	flags.s = NULL;
+	if (str1)
+		fixup_get_svalue(msg, (gparam_p)str1, &flags);
+	return rtpengine_offer_answer(msg, flags.s, OP_ANSWER);
+}
+
+static int
+rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op)
+{
+	bencode_buffer_t bencbuf;
+	bencode_item_t *dict;
+	str body, newbody;
+	struct lump *anchor;
+
+	dict = rtpe_function_call_ok(&bencbuf, msg, op, flags, &body);
+	if (!dict)
+		return -1;
+
+	if (!bencode_dictionary_get_str_dup(dict, "sdp", &newbody)) {
+		LM_ERR("failed to extract sdp body from proxy reply\n");
+		goto error;
+	}
+
+	anchor = del_lump(msg, body.s - msg->buf, body.len, 0);
+	if (!anchor) {
+		LM_ERR("del_lump failed\n");
+		goto error_free;
+	}
+	if (!insert_new_lump_after(anchor, newbody.s, newbody.len, 0)) {
+		LM_ERR("insert_new_lump_after failed\n");
+		goto error_free;
+	}
+
+	bencode_buffer_free(&bencbuf);
+	return 1;
+
+error_free:
+	pkg_free(newbody.s);
+error:
+	bencode_buffer_free(&bencbuf);
+	return -1;
+}
+
+
+static int
+start_recording_f(struct sip_msg* msg, char *foo, char *bar)
+{
+	return rtpe_function_call_simple(msg, OP_START_RECORDING, NULL);
+}
+
+/*
+ * Returns the current RTP-Statistics from the RTP-Proxy
+ */
+static int
+pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param,
+		  pv_value_t *res)
+{
+	bencode_buffer_t bencbuf;
+	bencode_item_t *dict, *tot, *in, *out;
+	static char buf[256];
+	str ret;
+
+	dict = rtpe_function_call_ok(&bencbuf, msg, OP_QUERY, NULL, NULL);
+	if (!dict)
+		return -1;
+
+	tot = bencode_dictionary_get_expect(dict, "totals", BENCODE_DICTIONARY);
+	in = bencode_dictionary_get_expect(tot, "input", BENCODE_DICTIONARY);
+	in = bencode_dictionary_get_expect(in, "rtp", BENCODE_DICTIONARY);
+	out = bencode_dictionary_get_expect(tot, "output", BENCODE_DICTIONARY);
+	out = bencode_dictionary_get_expect(out, "rtp", BENCODE_DICTIONARY);
+
+	if (!in || !out)
+		goto error;
+
+	ret.s = buf;
+	ret.len = snprintf(buf, sizeof(buf),
+			"Input: %lli bytes, %lli packets, %lli errors; "
+			"Output: %lli bytes, %lli packets, %lli errors",
+			bencode_dictionary_get_integer(in, "bytes", -1),
+			bencode_dictionary_get_integer(in, "packets", -1),
+			bencode_dictionary_get_integer(in, "errors", -1),
+			bencode_dictionary_get_integer(out, "bytes", -1),
+			bencode_dictionary_get_integer(out, "packets", -1),
+			bencode_dictionary_get_integer(out, "errors", -1));
+
+	bencode_buffer_free(&bencbuf);
+	return pv_get_strval(msg, param, res, &ret);
+
+error:
+	bencode_buffer_free(&bencbuf);
+	return -1;
+}
+
diff --git a/modules/rtpengine/rtpengine.h b/modules/rtpengine/rtpengine.h
new file mode 100644
index 000000000..24d124b05
--- /dev/null
+++ b/modules/rtpengine/rtpengine.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003 Porta Software Ltd
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * ---------
+ * 2014-06-17 Imported from rtpproxy module
+*/
+
+
+#ifndef _RTPENGINE_H
+#define _RTPENGINE_H
+
+#include "bencode.h"
+#include "../../str.h"
+
+struct rtpe_node {
+	unsigned int		idx;			/* overall index */
+	str					rn_url;			/* unparsed, deletable */
+	int					rn_umode;
+	char				*rn_address;	/* substring of rn_url */
+	int					rn_disabled;	/* found unaccessible? */
+	unsigned			rn_weight;		/* for load balancing */
+	unsigned int		rn_recheck_ticks;
+	int                     rn_rep_supported;
+	int                     rn_ptl_supported;
+	struct rtpe_node	*rn_next;
+};
+
+
+struct rtpe_set{
+	unsigned int 		id_set;
+	unsigned			weight_sum;
+	unsigned int		rtpe_node_count;
+	int 				set_disabled;
+	unsigned int		set_recheck_ticks;
+	struct rtpe_node	*rn_first;
+	struct rtpe_node	*rn_last;
+	struct rtpe_set     *rset_next;
+};
+
+
+struct rtpe_set_head{
+	struct rtpe_set		*rset_first;
+	struct rtpe_set		*rset_last;
+};
+
+#endif
diff --git a/modules/rtpengine/rtpengine_funcs.c b/modules/rtpengine/rtpengine_funcs.c
new file mode 100644
index 000000000..838555244
--- /dev/null
+++ b/modules/rtpengine/rtpengine_funcs.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ * 2014-06-17 Imported from rtpproxy module
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "rtpengine_funcs.h"
+#include "../../dprint.h"
+#include "../../config.h"
+#include "../../ut.h"
+#include "../../forward.h"
+#include "../../resolve.h"
+#include "../../globals.h"
+#include "../../udp_server.h"
+#include "../../pt.h"
+#include "../../parser/msg_parser.h"
+#include "../../trim.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parser_f.h"
+#include "../../parser/sdp/sdp_helpr_funcs.h"
+
+#define READ(val) \
+	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+#define advance(_ptr,_n,_str,_error) \
+	do{\
+		if ((_ptr)+(_n)>(_str).s+(_str).len)\
+			goto _error;\
+		(_ptr) = (_ptr) + (_n);\
+	}while(0);
+#define one_of_16( _x , _t ) \
+	(_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
+	||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
+#define one_of_8( _x , _t ) \
+	(_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+	||_x==_t[5]||_x==_t[6])
+
+
+
+/**
+ * return:
+ * -1: error
+ *  1: text or sdp
+ *  2: multipart
+ */
+int check_content_type(struct sip_msg *msg)
+{
+	static unsigned int appl[16] = {
+		0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/,
+		0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/,
+		0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/,
+		0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/,
+		0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/,
+		0x4c505041/*APPL*/};
+	static unsigned int icat[16] = {
+		0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/,
+		0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/,
+		0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/,
+		0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/,
+		0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/,
+		0x54414349/*ICAT*/};
+	static unsigned int ion_[8] = {
+		0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/,
+		0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/,
+		0x004e4f69/*iON_*/,0x004e4f49/*ION_*/};
+	static unsigned int sdp_[8] = {
+		0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/,
+		0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/,
+		0x00504473/*sDP_*/,0x00504453/*SDP_*/};
+	str           str_type;
+	unsigned int  x;
+	char          *p;
+
+	if (!msg->content_type)
+	{
+		LM_WARN("the header Content-TYPE is absent!"
+			"let's assume the content is text/plain ;-)\n");
+		return 1;
+	}
+
+	trim_len(str_type.len,str_type.s,msg->content_type->body);
+	if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M')
+			&& strncasecmp(str_type.s, "multipart/mixed", 15) == 0) {
+		return 2;
+    }
+	p = str_type.s;
+	advance(p,4,str_type,error_1);
+	x = READ(p-4);
+	if (!one_of_16(x,appl))
+		goto other;
+	advance(p,4,str_type,error_1);
+	x = READ(p-4);
+	if (!one_of_16(x,icat))
+		goto other;
+	advance(p,3,str_type,error_1);
+	x = READ(p-3) & 0x00ffffff;
+	if (!one_of_8(x,ion_))
+		goto other;
+
+	/* skip spaces and tabs if any */
+	while (*p==' ' || *p=='\t')
+		advance(p,1,str_type,error_1);
+	if (*p!='/')
+	{
+		LM_ERR("no / found after primary type\n");
+		goto error;
+	}
+	advance(p,1,str_type,error_1);
+	while ((*p==' ' || *p=='\t') && p+1<str_type.s+str_type.len)
+		advance(p,1,str_type,error_1);
+
+	advance(p,3,str_type,error_1);
+	x = READ(p-3) & 0x00ffffff;
+	if (!one_of_8(x,sdp_))
+		goto other;
+
+	if (*p==';'||*p==' '||*p=='\t'||*p=='\n'||*p=='\r'||*p==0) {
+		LM_DBG("type <%.*s> found valid\n", (int)(p-str_type.s), str_type.s);
+		return 1;
+	} else {
+		LM_ERR("bad end for type!\n");
+		return -1;
+	}
+
+error_1:
+	LM_ERR("body ended :-(!\n");
+error:
+	return -1;
+other:
+	LM_ERR("invalid type for a message\n");
+	return -1;
+}
+
+
+/*
+ * Get message body and check Content-Type header field
+ */
+int extract_body(struct sip_msg *msg, str *body )
+{
+	char c;
+	int ret;
+	str mpdel;
+	char *rest, *p1, *p2;
+	struct hdr_field hf;
+	unsigned int mime;
+
+	if (get_body(msg,body)!=0 || body->len==0) {
+		LM_ERR("failed to get the message body\n");
+		goto error;
+	}
+
+	/*
+	 * Better use the content-len value - no need of any explicit
+	 * parcing as get_body() parsed all headers and Conten-Length
+	 * body header is automaticaly parsed when found.
+	 */
+	if (msg->content_length==0) {
+		LM_ERR("failed to get the content length in message\n");
+		goto error;
+	}
+
+	body->len = get_content_length(msg);
+	if (body->len==0) {
+		LM_ERR("message body has length zero\n");
+		goto error;
+	}
+
+	if (body->len + body->s > msg->buf + msg->len) {
+		LM_ERR("content-length exceeds packet-length by %d\n",
+				(int)((body->len + body->s) - (msg->buf + msg->len)));
+		goto error;
+	}
+
+	/* no need for parse_headers(msg, EOH), get_body will
+	 * parse everything */
+	/*is the content type correct?*/
+	if((ret = check_content_type(msg))==-1)
+	{
+		LM_ERR("content type mismatching\n");
+		goto error;
+	}
+
+	if(ret!=2)
+		goto done;
+
+	/* multipart body */
+	if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) {
+		goto error;
+	}
+	p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel);
+	if (p1 == NULL) {
+		LM_ERR("empty multipart content\n");
+		return -1;
+	}
+	p2=p1;
+	c = 0;
+	for(;;)
+	{
+		p1 = p2;
+		if (p1 == NULL || p1 >= body->s+body->len)
+			break; /* No parts left */
+		p2 = find_next_sdp_line_delimiter(p1, body->s+body->len,
+				mpdel, body->s+body->len);
+		/* p2 is text limit for application parsing */
+		rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2);
+		if ( rest > p2 ) {
+			LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1);
+			return -1;
+		}
+		while( rest<p2 ) {
+			memset(&hf,0, sizeof(struct hdr_field));
+			rest = get_sdp_hdr_field(rest, p2, &hf);
+			if(hf.type==HDR_EOH_T)
+				break;
+			if(hf.type==HDR_ERROR_T)
+				return -1;
+			if(hf.type==HDR_CONTENTTYPE_T) {
+				if(decode_mime_type(hf.body.s, hf.body.s + hf.body.len,
+						&mime, NULL)==NULL)
+					return -1;
+				if (((((unsigned int)mime)>>16) == TYPE_APPLICATION)
+						&& ((mime&0x00ff) == SUBTYPE_SDP)) {
+					c = 1;
+				}
+			}
+		} /* end of while */
+		if(c==1)
+		{
+			if (rest < p2 && *rest == '\r') rest++;
+			if (rest < p2 && *rest == '\n') rest++;
+			if (rest < p2 && p2[-1] == '\n') p2--;
+			if (rest < p2 && p2[-1] == '\r') p2--;
+			body->s = rest;
+			body->len = p2-rest;
+			goto done;
+		}
+	}
+
+error:
+	return -1;
+
+done:
+	/*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/
+	return 1;
+}
+
+/*
+ * ser_memmem() returns the location of the first occurrence of data
+ * pattern b2 of size len2 in memory block b1 of size len1 or
+ * NULL if none is found. Obtained from NetBSD.
+ */
+void *
+ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2)
+{
+        /* Initialize search pointer */
+        char *sp = (char *) b1;
+
+        /* Initialize pattern pointer */
+        char *pp = (char *) b2;
+
+        /* Initialize end of search address space pointer */
+        char *eos = sp + len1 - len2;
+
+        /* Sanity check */
+        if(!(b1 && b2 && len1 && len2))
+                return NULL;
+
+        while (sp <= eos) {
+                if (*sp == *pp)
+                        if (memcmp(sp, pp, len2) == 0)
+                                return sp;
+
+                        sp++;
+        }
+
+        return NULL;
+}
+
+/*
+ * Some helper functions taken verbatim from tm module.
+ */
+
+/*
+ * Extract Call-ID value
+ * assumes the callid header is already parsed
+ * (so make sure it is, before calling this function or
+ *  it might fail even if the message _has_ a callid)
+ */
+int
+get_callid(struct sip_msg* _m, str* _cid)
+{
+
+        if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) {
+                LM_ERR("failed to parse call-id header\n");
+                return -1;
+        }
+
+        if (_m->callid == NULL) {
+                LM_ERR("call-id not found\n");
+                return -1;
+        }
+
+        _cid->s = _m->callid->body.s;
+        _cid->len = _m->callid->body.len;
+        trim(_cid);
+        return 0;
+}
+
+/*
+ * Extract tag from To header field of a response
+ */
+int
+get_to_tag(struct sip_msg* _m, str* _tag)
+{
+
+        if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || (!_m->to))) {
+                LM_ERR("To header field missing\n");
+                return -1;
+        }
+
+        if (get_to(_m)->tag_value.len) {
+                _tag->s = get_to(_m)->tag_value.s;
+                _tag->len = get_to(_m)->tag_value.len;
+        } else {
+                _tag->s = NULL; /* fixes gcc 4.0 warnings */
+                _tag->len = 0;
+        }
+
+        return 0;
+}
+
+/*
+ * Extract tag from From header field of a request
+ */
+int
+get_from_tag(struct sip_msg* _m, str* _tag)
+{
+
+        if (parse_from_header(_m)<0) {
+                LM_ERR("failed to parse From header\n");
+                return -1;
+        }
+
+        if (get_from(_m)->tag_value.len) {
+                _tag->s = get_from(_m)->tag_value.s;
+                _tag->len = get_from(_m)->tag_value.len;
+        } else {
+                _tag->s = NULL; /* fixes gcc 4.0 warnings */
+                _tag->len = 0;
+        }
+
+        return 0;
+}
+
+/*
+ * Extract URI from the Contact header field
+ */
+int
+get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c)
+{
+
+        if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact)
+                return -1;
+        if (!_m->contact->parsed && parse_contact(_m->contact) < 0) {
+                LM_ERR("failed to parse Contact body\n");
+                return -1;
+        }
+        *_c = ((contact_body_t*)_m->contact->parsed)->contacts;
+        if (*_c == NULL)
+                /* no contacts found */
+                return -1;
+
+        if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
+                LM_ERR("failed to parse Contact URI [%.*s]\n",
+                        (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:"");
+                return -1;
+        }
+        return 0;
+}
+
+/*
+ * Extract branch from Via header
+ */
+int
+get_via_branch(struct sip_msg* msg, int vianum, str* _branch)
+{
+	struct via_body *via;
+	struct via_param *p;
+
+	if ((parse_headers(msg, (vianum == 1) ? HDR_VIA1_F : HDR_VIA2_F, 0) == -1) < 0)
+		return -1;
+
+	via = (vianum == 1) ? msg->via1 : msg->via2;
+	for (p = via->param_lst; p; p = p->next)
+	{
+		if (p->name.len == strlen("branch")
+				&& strncasecmp(p->name.s, "branch", strlen("branch")) == 0) {
+			_branch->s = p->value.s;
+			_branch->len = p->value.len;
+			return 0;
+		}
+	}
+	return -1;
+}
diff --git a/modules/rtpengine/rtpengine_funcs.h b/modules/rtpengine/rtpengine_funcs.h
new file mode 100644
index 000000000..1f07f2d56
--- /dev/null
+++ b/modules/rtpengine/rtpengine_funcs.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * opensips is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _RTPENGINE_FUNCS_H
+#define _RTPENGINE_FUNCS_H
+
+#include "../../str.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/contact/contact.h"
+
+int extract_body(struct sip_msg * , str *);
+int check_content_type(struct sip_msg * );
+void *ser_memmem(const void *, const void *, size_t, size_t);
+int get_callid(struct sip_msg *, str *);
+int get_to_tag(struct sip_msg *, str *);
+int get_from_tag(struct sip_msg *, str *);
+int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **);
+int get_via_branch(struct sip_msg *, int, str *);
+
+#endif