]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add rtp endpoint contributed by sangoma
authorAnthony Minessale <anthm@freeswitch.org>
Wed, 8 Aug 2012 19:46:38 +0000 (14:46 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Wed, 8 Aug 2012 19:46:38 +0000 (14:46 -0500)
src/mod/endpoints/mod_sofia/Makefile.am
src/mod/endpoints/mod_sofia/mod_sofia.c
src/mod/endpoints/mod_sofia/mod_sofia.h
src/mod/endpoints/mod_sofia/rtp.c [new file with mode: 0644]

index fb0ab25e0bd7b1c67344fa665e2a1c522391a11d..efd6b2c38594a8ebf6928f7aa46a1853ee953cf6 100644 (file)
@@ -9,7 +9,7 @@ SOFIAUA_BUILDDIR=$(SOFIA_BUILDDIR)/libsofia-sip-ua
 SOFIALA=$(SOFIAUA_BUILDDIR)/libsofia-sip-ua.la
 
 mod_LTLIBRARIES = mod_sofia.la
-mod_sofia_la_SOURCES = mod_sofia.c sofia.c sofia_glue.c sofia_presence.c sofia_reg.c sip-dig.c mod_sofia.h
+mod_sofia_la_SOURCES = mod_sofia.c sofia.c sofia_glue.c sofia_presence.c sofia_reg.c sip-dig.c rtp.c mod_sofia.h
 mod_sofia_la_CFLAGS  = $(AM_CFLAGS) -I. $(SOFIA_CMD_LINE_CFLAGS)
 mod_sofia_la_CFLAGS += -I$(SOFIAUA_DIR)/bnf -I$(SOFIAUA_BUILDDIR)/bnf
 mod_sofia_la_CFLAGS += -I$(SOFIAUA_DIR)/http -I$(SOFIAUA_BUILDDIR)/http
index 52e7333607ce54332f3d8081e3cd122880231f20..650b89839954faf3972e53b4355b2d69a805f46d 100644 (file)
@@ -5705,6 +5705,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load)
        SWITCH_ADD_API(api_interface, "sofia_dig", "SIP DIG", sip_dig_function, "<url>");
        SWITCH_ADD_CHAT(chat_interface, SOFIA_CHAT_PROTO, sofia_presence_chat_send);
 
+       crtp_init(*module_interface);
+
        /* indicate that the module should continue to be loaded */
        return SWITCH_STATUS_SUCCESS;
 }
index 5ef35315e7d28ef70c0f188553f9edf40f5f1384..6d01de8a384a6ae0b9c2196cd4686284b4233b54 100644 (file)
@@ -1190,6 +1190,7 @@ void sofia_process_dispatch_event(sofia_dispatch_event_t **dep);
 char *sofia_glue_get_host(const char *str, switch_memory_pool_t *pool);
 void sofia_presence_check_subscriptions(sofia_profile_t *profile, time_t now);
 void sofia_msg_thread_start(int idx);
+void crtp_init(switch_loadable_module_interface_t *module_interface);
 
 
 /* For Emacs:
diff --git a/src/mod/endpoints/mod_sofia/rtp.c b/src/mod/endpoints/mod_sofia/rtp.c
new file mode 100644 (file)
index 0000000..bf3e36c
--- /dev/null
@@ -0,0 +1,649 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Mathieu Rene <mrene@avgs.ca>
+ *
+ * rtp.c -- RTP Controllable Channel Module
+ *
+ */
+
+#include <switch.h>
+#include "mod_sofia.h"
+
+#define kLOCALADDR "local_addr"
+#define kLOCALPORT "local_port"
+#define kREMOTEADDR "remote_addr"
+#define kREMOTEPORT "remote_port"
+#define kCODEC "codec"
+#define kPTIME "ptime"
+#define kPT "pt"
+#define kRFC2833PT "rfc2833_pt"
+#define kMODE "mode"
+#define kRATE "rate"
+
+static struct {
+    switch_memory_pool_t *pool;
+    switch_endpoint_interface_t *endpoint_interface;
+} crtp;
+
+typedef struct {
+    switch_core_session_t *session;
+    switch_channel_t *channel;
+    switch_codec_t read_codec, write_codec;
+    switch_frame_t read_frame;
+    
+
+    
+    switch_rtp_bug_flag_t rtp_bugs;
+    switch_rtp_t *rtp_session;
+    
+    uint32_t timestamp_send;
+    
+    const char *local_address;
+    const char *remote_address;
+    const char *codec;
+    int ptime;
+    
+    const switch_codec_implementation_t *negotiated_codecs[SWITCH_MAX_CODECS];
+       int num_negotiated_codecs;
+    
+    char *origin;
+    
+    switch_port_t local_port;
+    switch_port_t remote_port;
+    switch_payload_t agreed_pt; /*XXX*/
+    sofia_dtmf_t dtmf_type;
+    enum {
+        RTP_SENDONLY,
+        RTP_RECVONLY,
+        RTP_SENDRECV
+    } mode;
+} crtp_private_t;
+
+static switch_status_t channel_on_init(switch_core_session_t *session);
+static switch_status_t channel_on_destroy(switch_core_session_t *session);
+static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
+                                                                                                       switch_caller_profile_t *outbound_profile,
+                                                                                                       switch_core_session_t **new_session, 
+                                                                                                       switch_memory_pool_t **pool,
+                                                                                                       switch_originate_flag_t flags, switch_call_cause_t *cancel_cause);
+static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
+static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
+static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg);
+static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf);
+static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event);
+
+switch_state_handler_table_t crtp_state_handlers = {
+       .on_init = channel_on_init,
+       .on_destroy = channel_on_destroy
+};
+
+switch_io_routines_t crtp_io_routines = {
+       .outgoing_channel = channel_outgoing_channel,
+       .read_frame = channel_read_frame,
+       .write_frame = channel_write_frame,
+       .receive_message = channel_receive_message,
+    .receive_event = channel_receive_event,
+    .send_dtmf = channel_send_dtmf
+};
+
+
+void crtp_init(switch_loadable_module_interface_t *module_interface)
+{
+    switch_endpoint_interface_t *endpoint_interface;
+    //switch_api_interface_t *api_interface;
+
+    crtp.pool = module_interface->pool;
+    endpoint_interface = switch_loadable_module_create_interface(module_interface, SWITCH_ENDPOINT_INTERFACE);
+    endpoint_interface->interface_name = "rtp";
+    endpoint_interface->io_routines = &crtp_io_routines;
+    endpoint_interface->state_handler = &crtp_state_handlers;
+    crtp.endpoint_interface = endpoint_interface;
+    
+    //SWITCH_ADD_API(api_interface, "rtp_test", "test", test_function, "");
+}
+
+static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
+                                                                                                       switch_caller_profile_t *outbound_profile,
+                                                                                                       switch_core_session_t **new_session, 
+                                                                                                       switch_memory_pool_t **pool,
+                                                                                                       switch_originate_flag_t flags, switch_call_cause_t *cancel_cause)
+{
+    switch_channel_t *channel;
+    char name[128];
+    crtp_private_t *tech_pvt = NULL;    
+    switch_caller_profile_t *caller_profile;
+    
+    const char *err;
+
+    
+    const char  *local_addr = switch_event_get_header_nil(var_event, kLOCALADDR),
+                *szlocal_port = switch_event_get_header_nil(var_event, kLOCALPORT),
+                *remote_addr = switch_event_get_header_nil(var_event, kREMOTEADDR),
+                *szremote_port = switch_event_get_header_nil(var_event, kREMOTEPORT),
+                *codec  = switch_event_get_header_nil(var_event, kCODEC),
+                *szptime  = switch_event_get_header_nil(var_event, kPTIME),
+                //*mode  = switch_event_get_header_nil(var_event, kMODE),
+                //*szrfc2833_pt = switch_event_get_header_nil(var_event, kRFC2833PT),
+                *szrate = switch_event_get_header_nil(var_event, kRATE),
+                *szpt = switch_event_get_header_nil(var_event, kPT);
+    
+    
+    switch_port_t local_port = !zstr(szlocal_port) ? atoi(szlocal_port) : 0,
+                 remote_port = !zstr(szremote_port) ? atoi(szremote_port) : 0;
+    
+    int ptime  = !zstr(szptime) ? atoi(szptime) : 0,
+        //rfc2833_pt = !zstr(szrfc2833_pt) ? atoi(szrfc2833_pt) : 0,
+        rate = !zstr(szrate) ? atoi(szrate) : 8000,
+        pt = !zstr(szpt) ? atoi(szpt) : 0;
+    
+        if (
+            ((zstr(remote_addr) || remote_port == 0) && (zstr(local_addr) || local_port == 0)) ||
+            zstr(codec) ||
+            zstr(szpt)) {
+        
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required arguments\n");
+        goto fail;
+    }
+    
+    
+    if (!(*new_session = switch_core_session_request(crtp.endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 0, pool))) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't request session.\n");
+        goto fail;
+    }
+    
+    channel = switch_core_session_get_channel(*new_session);
+    
+    tech_pvt = switch_core_session_alloc(*new_session, sizeof *tech_pvt);
+    tech_pvt->session = *new_session;
+    tech_pvt->channel = channel;
+    tech_pvt->local_address = switch_core_session_strdup(*new_session, local_addr);
+    tech_pvt->local_port = local_port;
+    tech_pvt->remote_address = switch_core_session_strdup(*new_session, remote_addr);
+    tech_pvt->remote_port = remote_port;
+    tech_pvt->ptime = ptime;
+    tech_pvt->agreed_pt = pt;
+    tech_pvt->dtmf_type = DTMF_2833; /* XXX */
+    
+    if (zstr(local_addr) || local_port == 0) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "The local address and port must be set\n");
+        goto fail;
+    } else if (zstr(remote_addr) || remote_port == 0) {
+        tech_pvt->mode = RTP_RECVONLY;
+    } else {
+        tech_pvt->mode = RTP_SENDRECV;
+    }
+    
+    switch_core_session_set_private(*new_session, tech_pvt);
+    
+    caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
+    switch_channel_set_caller_profile(channel, caller_profile);
+    
+    
+    snprintf(name, sizeof(name), "rtp/%s", outbound_profile->destination_number);
+       switch_channel_set_name(channel, name);
+    
+    switch_channel_set_state(channel, CS_INIT);
+
+       if (switch_core_codec_init(&tech_pvt->read_codec,
+                                                          codec,
+                                                          NULL,
+                                                          rate,
+                                                          ptime,
+                                                          1,
+                                                          /*SWITCH_CODEC_FLAG_ENCODE |*/ SWITCH_CODEC_FLAG_DECODE,
+                                                          NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+        goto fail;
+       } else {
+               if (switch_core_codec_init(&tech_pvt->write_codec,
+                                                                  codec,
+                                                                  NULL,
+                                                                  rate,
+                                                                  ptime,
+                                                                  1,
+                                                                  SWITCH_CODEC_FLAG_ENCODE /*| SWITCH_CODEC_FLAG_DECODE*/, 
+                                                                  NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+            goto fail;
+               }
+       }
+    
+    if (switch_core_session_set_read_codec(*new_session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n");
+        goto fail;
+    }
+    
+    if (switch_core_session_set_write_codec(*new_session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n");
+        goto fail;
+    }
+    
+    if (!(tech_pvt->rtp_session = switch_rtp_new(local_addr, local_port, remote_addr, remote_port, tech_pvt->agreed_pt,
+                                           tech_pvt->read_codec.implementation->samples_per_packet, ptime * 1000,
+                                             flags, "soft", &err, switch_core_session_get_pool(*new_session)))) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't setup RTP session: [%s]\n", err);
+        goto fail;
+    }
+    
+    if (switch_core_session_thread_launch(*new_session) != SWITCH_STATUS_SUCCESS) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't start session thread.\n"); 
+        goto fail;
+    }
+    
+    switch_channel_mark_answered(channel);
+    
+    return SWITCH_CAUSE_SUCCESS;
+    
+fail:
+     if (tech_pvt) {
+        if (tech_pvt->read_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->read_codec);
+               }
+               
+               if (tech_pvt->write_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->write_codec);
+               }
+    }
+    
+    if (*new_session) {
+        switch_core_session_destroy(new_session);
+    }
+    return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
+}
+
+static switch_status_t channel_on_init(switch_core_session_t *session)
+{
+    
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    
+    switch_channel_set_state(channel, CS_CONSUME_MEDIA);
+    
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t channel_on_destroy(switch_core_session_t *session)
+{
+    crtp_private_t *tech_pvt = switch_core_session_get_private(session);
+    
+       if ((tech_pvt = switch_core_session_get_private(session))) {
+        
+               if (tech_pvt->read_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->read_codec);
+               }
+               
+               if (tech_pvt->write_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->write_codec);
+               }
+       }
+    
+    return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
+{
+    crtp_private_t *tech_pvt;
+    switch_channel_t *channel;
+    switch_status_t status;
+    
+    channel = switch_core_session_get_channel(session);
+       assert(channel != NULL);
+       
+       tech_pvt = switch_core_session_get_private(session);
+       assert(tech_pvt != NULL);
+    
+    if (!tech_pvt->rtp_session || tech_pvt->mode == RTP_SENDONLY) {
+       switch_yield(20000); /* replace by local timer XXX */
+        goto cng;
+    }
+        
+    if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) {
+        switch_dtmf_t dtmf = { 0 };
+        switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, &dtmf);
+        switch_channel_queue_dtmf(channel, &dtmf);
+    }
+    
+    tech_pvt->read_frame.flags = SFF_NONE;
+    tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+    status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags);
+
+    if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+        goto cng;
+    }
+    
+    *frame = &tech_pvt->read_frame;
+    return SWITCH_STATUS_SUCCESS;
+    
+cng:
+    *frame = &tech_pvt->read_frame;
+    tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+    tech_pvt->read_frame.flags |= SFF_CNG;
+    tech_pvt->read_frame.datalen = 0;
+    
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
+{
+    crtp_private_t *tech_pvt;
+    switch_channel_t *channel;
+    //int frames = 0, bytes = 0, samples = 0;
+    
+    channel = switch_core_session_get_channel(session);
+       assert(channel != NULL);
+       
+       tech_pvt = switch_core_session_get_private(session);
+       assert(tech_pvt != NULL);
+    
+    
+#if 0
+    if (!switch_test_flag(frame, SFF_CNG) && !switch_test_flag(frame, SFF_PROXY_PACKET)) {
+               if (tech_pvt->read_codec.implementation->encoded_bytes_per_packet) {
+                       bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet;
+                       frames = ((int) frame->datalen / bytes);
+               } else
+                       frames = 1;
+        
+               samples = frames * tech_pvt->read_codec.implementation->samples_per_packet;
+       }
+    
+    tech_pvt->timestamp_send += samples;
+#endif
+    if (tech_pvt->mode == RTP_RECVONLY) {
+       return SWITCH_STATUS_SUCCESS;
+    }
+
+    switch_rtp_write_frame(tech_pvt->rtp_session, frame);
+
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf)
+{
+       crtp_private_t *tech_pvt = NULL;
+    
+       tech_pvt = switch_core_session_get_private(session);
+       assert(tech_pvt != NULL);
+    
+    switch(tech_pvt->dtmf_type) {
+        case DTMF_2833:
+        {
+            switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Enqueuing RFC2833 DTMF %c of length %d\n", dtmf->digit, dtmf->duration);
+            return switch_rtp_queue_rfc2833(tech_pvt->rtp_session, dtmf);
+        }
+        case DTMF_NONE:
+        default:
+               {
+                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Discarding DTMF %c of length %d, DTMF type is NONE\n", dtmf->digit, dtmf->duration);
+               }
+    }
+    
+    return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_bool_t compare_var(switch_event_t *event, switch_channel_t *channel, const char *varname)
+{
+    const char *chan_val = switch_channel_get_variable_dup(channel, varname, SWITCH_FALSE, -1);
+    const char *event_val = switch_event_get_header(event, varname);
+
+    if (zstr(chan_val) || zstr(event_val)) {
+       return 1;
+    }    
+
+    return strcasecmp(chan_val, event_val);
+}
+
+static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event)
+{
+    const char *command = switch_event_get_header(event, "command");
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    crtp_private_t *tech_pvt = switch_core_session_get_private(session);
+    char *codec  = switch_event_get_header_nil(event, kCODEC);
+    char *szptime  = switch_event_get_header_nil(event, kPTIME);
+    char *szrate = switch_event_get_header_nil(event, kRATE);
+    char *szpt = switch_event_get_header_nil(event, kPT);
+
+    int ptime  = !zstr(szptime) ? atoi(szptime) : 0,
+               rate = !zstr(szrate) ? atoi(szrate) : 8000,
+               pt = !zstr(szpt) ? atoi(szpt) : 0;
+
+
+    if (!zstr(command) && !strcasecmp(command, "media_modify")) {
+        /* Compare parameters */
+        if (compare_var(event, channel, kREMOTEADDR) ||
+            compare_var(event, channel, kREMOTEPORT)) {
+               char *remote_addr = switch_event_get_header(event, kREMOTEADDR);
+               char *szremote_port = switch_event_get_header(event, kREMOTEPORT);
+               switch_port_t remote_port = !zstr(szremote_port) ? atoi(szremote_port) : 0;
+               const char *err;
+
+
+            switch_channel_set_variable(channel, kREMOTEADDR, remote_addr);
+            switch_channel_set_variable(channel, kREMOTEPORT, szremote_port);
+            
+            if (switch_rtp_set_remote_address(tech_pvt->rtp_session, remote_addr, remote_port, 0, SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error setting RTP remote address: %s\n", err);
+            } else {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set RTP remote: %s:%d\n", remote_addr, (int)remote_port);
+                tech_pvt->mode = RTP_SENDRECV;
+            }
+        }
+        
+        if (compare_var(event, channel, kCODEC) ||
+            compare_var(event, channel, kPTIME) ||
+            compare_var(event, channel, kPT) ||
+           compare_var(event, channel, kRATE)) {
+               /* Reset codec */
+               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Switching codec updating \n");
+
+               if (switch_core_codec_init(&tech_pvt->read_codec,
+                                       codec,
+                                       NULL,
+                                       rate,
+                                       ptime,
+                                       1,
+                                       /*SWITCH_CODEC_FLAG_ENCODE |*/ SWITCH_CODEC_FLAG_DECODE,
+                                       NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+                       goto fail;
+               } else {
+                       if (switch_core_codec_init(&tech_pvt->write_codec,
+                                               codec,
+                                               NULL,
+                                               rate,
+                                               ptime,
+                                               1,
+                                               SWITCH_CODEC_FLAG_ENCODE /*| SWITCH_CODEC_FLAG_DECODE*/, 
+                                               NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+                               goto fail;
+                       }
+               }
+
+               if (switch_core_session_set_read_codec(session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n");
+                       goto fail;
+               }
+
+               if (switch_core_session_set_write_codec(session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n");
+                       goto fail;
+               }
+
+               switch_rtp_set_default_payload(tech_pvt->rtp_session, pt);
+               switch_rtp_set_recv_pt(tech_pvt->rtp_session, pt);
+       }
+        
+        if (compare_var(event, channel, kRFC2833PT)) {
+            const char *szpt = switch_channel_get_variable(channel, kRFC2833PT);
+            int pt = !zstr(szpt) ? atoi(szpt) : 0;
+            
+            switch_channel_set_variable(channel, kRFC2833PT, szpt);
+            switch_rtp_set_telephony_event(tech_pvt->rtp_session, pt);
+        }
+    
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received unknown command [%s] in event.\n", !command ? "null" : command);
+    }
+
+    return SWITCH_STATUS_SUCCESS;
+fail:
+     if (tech_pvt) {
+        if (tech_pvt->read_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->read_codec);
+               }
+               
+               if (tech_pvt->write_codec.implementation) {
+                       switch_core_codec_destroy(&tech_pvt->write_codec);
+               }
+    }
+    
+    if (session) {
+        switch_core_session_destroy(&session);
+    }
+    return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
+
+}
+
+static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
+{
+    crtp_private_t *tech_pvt = NULL;
+    
+       tech_pvt = switch_core_session_get_private(session);
+       assert(tech_pvt != NULL);
+    
+    switch (msg->message_id) {
+        case SWITCH_MESSAGE_INDICATE_DEBUG_AUDIO:
+        {
+            if (switch_rtp_ready(tech_pvt->rtp_session) && !zstr(msg->string_array_arg[0]) && !zstr(msg->string_array_arg[1])) {
+                int32_t flags = 0;
+                if (!strcasecmp(msg->string_array_arg[0], "read")) {
+                    flags |= SWITCH_RTP_FLAG_DEBUG_RTP_READ;
+                } else if (!strcasecmp(msg->string_array_arg[0], "write")) {
+                    flags |= SWITCH_RTP_FLAG_DEBUG_RTP_WRITE;
+                } else if (!strcasecmp(msg->string_array_arg[0], "both")) {
+                    flags |= SWITCH_RTP_FLAG_DEBUG_RTP_READ | SWITCH_RTP_FLAG_DEBUG_RTP_WRITE;
+                }
+                
+                if (flags) {
+                    if (switch_true(msg->string_array_arg[1])) {
+                        switch_rtp_set_flag(tech_pvt->rtp_session, flags);
+                    } else {
+                        switch_rtp_clear_flag(tech_pvt->rtp_session, flags);
+                    }
+                } else {
+                    switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Options\n");
+                }
+            }
+            break;
+        }
+        case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
+            if (switch_rtp_ready(tech_pvt->rtp_session)) {
+                rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_ONCE);
+            }
+            break;
+        case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER:
+               {
+                       if (switch_rtp_ready(tech_pvt->rtp_session)) {
+                               int len = 0, maxlen = 0, qlen = 0, maxqlen = 50, max_drift = 0;
+                
+                               if (msg->string_arg) {
+                                       char *p, *q;
+                                       const char *s;
+                    
+                                       if (!strcasecmp(msg->string_arg, "pause")) {
+                                               switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_TRUE);
+                                               goto end;
+                                       } else if (!strcasecmp(msg->string_arg, "resume")) {
+                                               switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_FALSE);
+                                               goto end;
+                                       } else if (!strncasecmp(msg->string_arg, "debug:", 6)) {
+                                               s = msg->string_arg + 6;
+                                               if (s && !strcmp(s, "off")) {
+                                                       s = NULL;
+                                               }
+                        switch_rtp_debug_jitter_buffer(tech_pvt->rtp_session, s);
+                                               goto end;
+                                       }
+                    
+                                       
+                                       if ((len = atoi(msg->string_arg))) {
+                                               qlen = len / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000);
+                                               if (qlen < 1) {
+                                                       qlen = 3;
+                                               }
+                                       }
+                                       
+                                       if (qlen) {
+                                               if ((p = strchr(msg->string_arg, ':'))) {
+                                                       p++;
+                                                       maxlen = atol(p);
+                                                       if ((q = strchr(p, ':'))) {
+                                                               q++;
+                                                               max_drift = abs(atol(q));
+                                                       }
+                                               }
+                                       }
+                    
+                    
+                                       if (maxlen) {
+                                               maxqlen = maxlen / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000);
+                                       }
+                               }
+                
+                               if (qlen) {
+                                       if (maxqlen < qlen) {
+                                               maxqlen = qlen * 5;
+                                       }
+                                       if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen,
+                                                                                                                 tech_pvt->read_codec.implementation->samples_per_packet, 
+                                                                                                                 tech_pvt->read_codec.implementation->samples_per_second, max_drift) == SWITCH_STATUS_SUCCESS) {
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), 
+                                                                                 SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames) (%d max drift)\n", 
+                                                                                 len, qlen, maxqlen, max_drift);
+                                               switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER);
+                                               if (!switch_false(switch_channel_get_variable(tech_pvt->channel, "sip_jitter_buffer_plc"))) {
+                                                       switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER_PLC);
+                                               }
+                                       } else {
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), 
+                                                                                 SWITCH_LOG_WARNING, "Error Setting Jitterbuffer to %dms (%d frames)\n", len, qlen);
+                                       }
+                                       
+                               } else {
+                                       switch_rtp_deactivate_jitter_buffer(tech_pvt->rtp_session);
+                               }
+                       }
+               }
+            break;
+
+        default:
+            break;
+    }
+end:
+    return SWITCH_STATUS_SUCCESS;
+}
+