[Freeswitch-svn] [commit] r2398 - in freeswitch/trunk/src: . mod/endpoints/mod_sofia

Freeswitch SVN anthm at freeswitch.org
Fri Aug 25 19:55:59 EDT 2006


Author: anthm
Date: Fri Aug 25 19:55:59 2006
New Revision: 2398

Added:
   freeswitch/trunk/src/mod/endpoints/mod_sofia/
   freeswitch/trunk/src/mod/endpoints/mod_sofia/Makefile
   freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.c
Modified:
   freeswitch/trunk/src/switch_ivr.c

Log:
Adding mod_sofia to the tree so we can work on it easier....

I am not adding it to the examples or to the modules.conf because it's not really ready for that yet.
This is only 1.5 days old from scratch at this point but the brave hearted who want to play with it can do the following:

Add this to modules.conf:
-----------------------------------------------------------------------------
endpoints/mod_sofia
-----------------------------------------------------------------------------

Add this to freeswitch.xml in the configuration/modules.conf area
-----------------------------------------------------------------------------
<load module="mod_sofia"/>
-----------------------------------------------------------------------------

Add this to freeswitch.xml in the configuration section

-----------------------------------------------------------------------------
    <configuration name="sofia.conf" description="sofia Endpoint">
      <!-- You may have multiple profiles -->
      <profile name="test">
        <param name="rfc2833-pt" value="101"/>
        <param name="sip-port" value="5060"/>
        <param name="dialplan" value="XML"/>
        <param name="dtmf-duration" value="100"/>
        <param name="codec-prefs" value="PCMU"/>
        <param name="use-rtp-timer" value="true"/>
        <param name="rtp-ip" value="127.0.0.1"/>
        <param name="sip-ip" value="127.0.0.1"/>
        <!-- optional ; -->
        <!-- <param name="ext-rtp-ip" value="stun:stun.server.com"/>-->
        <!-- <param name="ext-rtp-ip" value="100.101.102.103"/> -->

        <!-- VAD choose one (out is a good choice); -->
        <!-- <param name="vad" value="in"/> -->
        <!-- <param name="vad" value="out"/> -->
        <!-- <param name="vad" value="both"/> -->
	<!--<param name="alias" value="sip:208.64.200.40:5555"/>-->
      </profile>
    </configuration>
-----------------------------------------------------------------------------

The call string to use profile test would be:

sofia/test/1000 at 1.2.3.4

as in:

<action application="bridge" data="sofia/test/1000 at 1.2.3.4"/>



Added: freeswitch/trunk/src/mod/endpoints/mod_sofia/Makefile
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/Makefile	Fri Aug 25 19:55:59 2006
@@ -0,0 +1,23 @@
+VERSION = sofia-sip-1.12
+TARBALL = sofia-sip-1.12.1.tar.gz
+CFLAGS += -I. -I$(PREFIX)/include/$(VERSION)
+LDFLAGS += -lsofia-sip-ua
+LINKER=$(CC)
+
+all:	depends $(MODNAME).$(DYNAMIC_LIB_EXTEN)
+
+depends:
+	MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install $(TARBALL) --prefix=$(PREFIX) --with-pic
+
+%.o:  %.c
+	$(CC) -fPIC $(CFLAGS) -c -o $@ $<
+
+$(MODNAME).$(DYNAMIC_LIB_EXTEN): $(MODNAME).c
+	$(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o 
+	$(LINKER) $(SOLINK) -o $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(MODNAME).o $(LDFLAGS)
+
+clean:
+	rm -fr *.$(DYNAMIC_LIB_EXTEN) *.o *~
+
+install:
+	cp -f $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(PREFIX)/mod

Added: freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/endpoints/mod_sofia/mod_sofia.c	Fri Aug 25 19:55:59 2006
@@ -0,0 +1,1733 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct at yahoo.com>
+ *
+ * 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 <anthmct at yahoo.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthmct at yahoo.com>
+ *
+ *
+ * mod_sofia.c -- SOFIA SIP Endpoint
+ *
+ */
+
+/* Best viewed in a 160 x 60 VT100 Terminal or so the line below at least fits across your screen*/
+/*************************************************************************************************************************************************************/
+
+
+/*Defines etc..*/
+/*************************************************************************************************************************************************************/
+#define HAVE_APR
+#include <switch.h>
+
+struct sofia_profile;
+typedef struct sofia_profile sofia_profile_t;
+#define NUA_MAGIC_T sofia_profile_t
+
+struct private_object;
+typedef struct private_object private_object_t;
+#define NUA_HMAGIC_T switch_core_session_t
+
+#define MY_EVENT_REGISTER "sofia::register"
+#define MY_EVENT_EXPIRE "sofia::expire"
+#include <sofia-sip/nua.h>
+#include <sofia-sip/sip_status.h>
+#include <sofia-sip/sdp.h>
+#define DBFILE "sofia"
+
+static const char modname[] = "mod_sofia";
+#define STRLEN 15
+
+static switch_memory_pool_t *module_pool = NULL;
+
+#define set_param(ptr,val) if (ptr) {free(ptr) ; ptr = NULL;} if (val) {ptr = strdup(val);}
+#define set_anchor(t,m) if (t->Anchor) {delete t->Anchor;} t->Anchor = new SipMessage(m);
+
+
+/* Local Structures */
+/*************************************************************************************************************************************************************/
+struct sip_alias_node {
+	char *url;
+	nua_t *nua;
+	struct sip_alias_node *next;
+};
+
+typedef struct sip_alias_node sip_alias_node_t;
+
+typedef enum {
+	TFLAG_IO = (1 << 0),
+	TFLAG_INBOUND = (1 << 1),
+	TFLAG_OUTBOUND = (1 << 2),
+	TFLAG_READING = (1 << 3),
+	TFLAG_WRITING = (1 << 4),
+	TFLAG_HUP = (1 << 5),
+	TFLAG_RTP = (1 << 6),
+	TFLAG_BYE = (1 << 7),
+	TFLAG_ANS = (1 << 8),
+	TFLAG_EARLY_MEDIA = (1 << 9),
+	TFLAG_SECURE = (1 << 10),
+	TFLAG_VAD_IN = ( 1 << 11),
+	TFLAG_VAD_OUT = ( 1 << 12),
+	TFLAG_VAD = ( 1 << 13),
+	TFLAG_TIMER = (1 << 14),
+	TFLAG_READY = (1 << 15)
+} TFLAGS;
+
+static struct {
+	switch_hash_t *profile_hash;
+	switch_mutex_t *hash_mutex;
+	uint32_t callid;
+} globals;
+
+struct sofia_profile {
+	int debug;
+	char *name;
+	char *dialplan;
+	char *context;
+	char *extrtpip;
+	char *rtpip;
+	char *sipip;
+	char *extsipip;
+	char *username;
+	char *url;
+	int sip_port;
+	char *codec_string;
+	char *codec_order[SWITCH_MAX_CODECS];
+	int codec_order_last;
+	int running;
+	int codec_ms;
+	int dtmf_duration;
+	unsigned int flags;
+	uint32_t max_calls;
+	nua_t *nua;
+	switch_memory_pool_t *pool;
+	su_root_t *s_root;
+	sip_alias_node_t *aliases;
+	switch_payload_t te;
+};
+
+
+
+struct private_object {
+	uint32_t flags;
+	switch_core_session_t *session;
+	switch_frame_t read_frame;
+	const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
+	int num_codecs;
+	switch_codec_t read_codec;
+	switch_codec_t write_codec;
+	uint32_t codec_index;
+	uint32_t codec_rate;
+	uint32_t codec_ms;
+	switch_caller_profile_t *caller_profile;
+	int32_t timestamp_send;
+	int32_t timestamp_recv;
+	switch_rtp_t *rtp_session;
+	int ssrc;
+	switch_time_t last_read;
+	sofia_profile_t *profile;
+	char *local_sdp_audio_ip;
+	switch_port_t local_sdp_audio_port;
+	char *remote_sdp_audio_ip;
+	switch_port_t remote_sdp_audio_port;
+	char *adv_sdp_audio_ip;
+	switch_port_t adv_sdp_audio_port;
+	char *from_uri;
+	char *to_uri;
+	char *from_address;
+	char *to_address;
+	char *callid;
+	char *far_end_contact;
+	char *contact_url;
+	char *rm_encoding;
+	char *remote_sdp_str;
+	char *local_sdp_str;
+	char *dest;
+	unsigned long rm_rate;
+	switch_payload_t pt;
+	switch_mutex_t *flag_mutex;
+	switch_payload_t te;
+	nua_handle_t *nh;
+};
+
+/* Function Prototypes */
+/*************************************************************************************************************************************************************/
+static switch_status_t sofia_on_init(switch_core_session_t *session);
+
+static switch_status_t sofia_on_hangup(switch_core_session_t *session);
+
+static switch_status_t sofia_on_loopback(switch_core_session_t *session);
+
+static switch_status_t sofia_on_transmit(switch_core_session_t *session);
+
+static switch_status_t sofia_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile,
+											 switch_core_session_t **new_session, switch_memory_pool_t *pool);
+
+static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout,
+									   switch_io_flag_t flags, int stream_id);
+
+static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout,
+										switch_io_flag_t flags, int stream_id);
+
+static switch_status_t config_sofia(int reload);
+
+static switch_status_t sofia_kill_channel(switch_core_session_t *session, int sig);
+
+static switch_status_t activate_rtp(private_object_t *tech_pvt);
+
+static void deactivate_rtp(private_object_t *tech_pvt);
+
+static void set_local_sdp(private_object_t *tech_pvt);
+
+static void tech_set_codecs(private_object_t *tech_pvt);
+
+static void attach_private(switch_core_session_t *session,
+                           sofia_profile_t *profile,
+                           private_object_t *tech_pvt,
+                           char *channame);
+
+static void terminate_session(switch_core_session_t **session, switch_call_cause_t cause);
+
+static switch_status_t tech_choose_port(private_object_t *tech_pvt);
+
+static void do_invite(switch_core_session_t *session);
+
+static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp);
+
+static void sip_i_state(int status,
+                        char const *phrase,
+                        nua_t *nua,
+                        sofia_profile_t *profile,
+                        nua_handle_t *nh,
+                        switch_core_session_t *session,
+                        sip_t const *sip,
+                        tagi_t tags[]);
+
+static void sip_i_invite(nua_t *nua,
+                         sofia_profile_t *profile,
+                         nua_handle_t *nh,
+                         switch_core_session_t *session,
+                         sip_t const *sip,
+                         tagi_t tags[]);
+
+static void event_callback(nua_event_t   event,
+                           int           status,
+                           char const   *phrase,
+                           nua_t        *nua,
+                           sofia_profile_t  *profile,
+                           nua_handle_t *nh,
+                           switch_core_session_t *session,
+                           sip_t const  *sip,
+                           tagi_t        tags[]);
+
+
+static void *SWITCH_THREAD_FUNC profile_thread_run(switch_thread_t *thread, void *obj);
+
+static void launch_profile_thread(sofia_profile_t *profile);
+
+static switch_status_t config_sofia(int reload);
+
+
+/* BODY OF THE MODULE */
+/*************************************************************************************************************************************************************/
+static void set_local_sdp(private_object_t *tech_pvt)
+{
+	char buf[1024];
+	switch_time_t now = switch_time_now();
+
+	assert(tech_pvt != NULL);
+	
+	snprintf(buf, sizeof(buf), 
+			 "v=0\n"
+			 "o=FreeSWITCH %d%"APR_TIME_T_FMT" %d%"APR_TIME_T_FMT" IN IP4 %s\n"
+			 "s=FreeSWITCH\n"
+			 "c=IN IP4 %s\n"
+			 "t=0 0\n"
+			 "a=sendrecv\n"
+			 "m=audio %d RTP/AVP",
+			 tech_pvt->adv_sdp_audio_port,
+			 now,
+			 tech_pvt->adv_sdp_audio_port,
+			 now,
+			 tech_pvt->adv_sdp_audio_ip,
+			 tech_pvt->adv_sdp_audio_ip,
+			 tech_pvt->adv_sdp_audio_port
+			 );
+
+	if (tech_pvt->rm_encoding) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt);
+	} else if (tech_pvt->num_codecs) {
+		int i;
+		for (i = 0; i < tech_pvt->num_codecs; i++) {
+			const switch_codec_implementation_t *imp = tech_pvt->codecs[i];
+			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", imp->ianacode);
+		}
+	}
+
+	if (tech_pvt->te > 96) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->te);
+	}
+
+	snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n");
+
+	if (tech_pvt->rm_encoding) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%ld\n", tech_pvt->pt, tech_pvt->rm_encoding, tech_pvt->rm_rate);
+	} else if (tech_pvt->num_codecs) {
+		int i;
+		for (i = 0; i < tech_pvt->num_codecs; i++) {
+			const switch_codec_implementation_t *imp = tech_pvt->codecs[i];
+			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%d\n", imp->ianacode, imp->iananame, imp->samples_per_second);
+			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=ptime:%d", imp->microseconds_per_frame / 1000);
+		}
+	}
+	
+	if (tech_pvt->te > 96) {
+		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d telephone-event/8000\na=fmtp:%d 0-16", tech_pvt->te, tech_pvt->te);
+	}
+
+
+
+	
+	tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, buf);
+
+}
+
+static void tech_set_codecs(private_object_t *tech_pvt)
+{
+	if (tech_pvt->num_codecs) {
+		return;
+	}
+
+	if (tech_pvt->profile->codec_string) {
+		tech_pvt->num_codecs = switch_loadable_module_get_codecs_sorted(tech_pvt->codecs,
+																		SWITCH_MAX_CODECS,
+																		tech_pvt->profile->codec_order,
+																		tech_pvt->profile->codec_order_last);
+		
+	} else {
+		tech_pvt->num_codecs = switch_loadable_module_get_codecs(switch_core_session_get_pool(tech_pvt->session), tech_pvt->codecs,
+																 sizeof(tech_pvt->codecs) / sizeof(tech_pvt->codecs[0]));
+	}
+}
+
+static void attach_private(switch_core_session_t *session,
+						   sofia_profile_t *profile,
+						   private_object_t *tech_pvt,
+						   char *channame)
+{
+	switch_channel_t *channel;
+	char name[256];
+
+	assert(session != NULL);
+	assert(profile != NULL);
+	assert(tech_pvt != NULL);
+
+	switch_core_session_add_stream(session, NULL);
+	channel = switch_core_session_get_channel(session);
+	
+	switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+	switch_mutex_lock(tech_pvt->flag_mutex);
+	tech_pvt->flags = profile->flags;
+	switch_mutex_unlock(tech_pvt->flag_mutex);
+	tech_pvt->profile = profile;
+	tech_pvt->te = profile->te;
+	tech_pvt->session = session;
+	switch_core_session_set_private(session, tech_pvt);
+
+	tech_set_codecs(tech_pvt);
+	snprintf(name, sizeof(name), "sofia/%s/%s", profile->name, channame);
+	switch_channel_set_name(channel, name);
+}
+
+static void terminate_session(switch_core_session_t **session, switch_call_cause_t cause)
+{
+	if (*session) {
+		switch_channel_t *channel = switch_core_session_get_channel(*session);
+		switch_channel_state_t state = switch_channel_get_state(channel);
+		struct private_object *tech_pvt = NULL;
+			
+		tech_pvt = switch_core_session_get_private(*session);
+		assert(tech_pvt != NULL);
+
+		if (state > CS_INIT && state < CS_HANGUP) {
+			switch_channel_hangup(channel, cause);
+		}
+		
+		if (!switch_test_flag(tech_pvt, TFLAG_READY)) {
+			if (state > CS_INIT && state < CS_HANGUP) {
+				sofia_on_hangup(*session);
+			} 
+
+			switch_core_session_destroy(session);
+
+		}
+	}
+}
+
+
+static switch_status_t tech_choose_port(private_object_t *tech_pvt)
+{
+	char *ip = tech_pvt->profile->rtpip;
+	switch_port_t sdp_port;
+	char *err;
+	
+	if (tech_pvt->adv_sdp_audio_port) {
+		return SWITCH_STATUS_SUCCESS;
+	}
+
+	tech_pvt->local_sdp_audio_ip = ip;
+	tech_pvt->local_sdp_audio_port = switch_rtp_request_port();
+	sdp_port = tech_pvt->local_sdp_audio_port;
+
+
+	if (tech_pvt->profile->extrtpip) {
+		if (!strncasecmp(tech_pvt->profile->extrtpip, "stun:", 5)) {
+			char *stun_ip = tech_pvt->profile->extrtpip + 5;
+			if (!stun_ip) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER\n");
+				terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+				return SWITCH_STATUS_FALSE;
+			}
+			if (switch_stun_lookup(&ip,
+								   &sdp_port,
+								   stun_ip,
+								   SWITCH_STUN_DEFAULT_PORT,
+								   &err,
+								   switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip, SWITCH_STUN_DEFAULT_PORT, err);
+				terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+				return SWITCH_STATUS_FALSE;
+			}
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Stun Success [%s]:[%d]\n", ip, sdp_port);
+		} else {
+			ip = tech_pvt->profile->extrtpip;
+		}
+	}
+
+	tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, ip);
+	tech_pvt->adv_sdp_audio_port = sdp_port;
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static void do_invite(switch_core_session_t *session)
+{
+	private_object_t *tech_pvt;
+    switch_channel_t *channel = NULL;
+
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+
+    tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+    assert(tech_pvt != NULL);
+
+	tech_choose_port(tech_pvt);
+	set_local_sdp(tech_pvt);
+	switch_set_flag_locked(tech_pvt, TFLAG_READY);
+	
+	tech_pvt->nh = nua_handle(tech_pvt->profile->nua, NULL, SIPTAG_TO_STR(tech_pvt->dest), TAG_END());
+
+	nua_handle_bind(tech_pvt->nh, session);
+	nua_invite(tech_pvt->nh,
+			   SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str),
+			   SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
+			   SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
+			   TAG_END());
+	
+
+}
+
+/* 
+State methods they get called when the state changes to the specific state 
+returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
+so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
+*/
+static switch_status_t sofia_on_init(switch_core_session_t *session)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	tech_pvt->read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+
+	switch_channel_set_variable(channel, "endpoint_disposition", "INIT");
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA INIT\n");
+
+	if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+		do_invite(session);
+	}
+	
+	/* Move Channel's State Machine to RING */
+	switch_channel_set_state(channel, CS_RING);
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t sofia_on_ring(switch_core_session_t *session)
+{
+	switch_channel_t *channel = NULL;
+	private_object_t *tech_pvt = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	switch_channel_set_variable(channel, "endpoint_disposition", "RING");
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA RING\n");
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t sofia_on_execute(switch_core_session_t *session)
+{
+	switch_channel_t *channel = NULL;
+	private_object_t *tech_pvt = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	switch_channel_set_variable(channel, "endpoint_disposition", "EXECUTE");
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA EXECUTE\n");
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t sofia_on_hangup(switch_core_session_t *session)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	deactivate_rtp(tech_pvt);
+
+	if (tech_pvt->nh) {
+		if (!switch_test_flag(tech_pvt, TFLAG_BYE)) {
+			nua_bye(tech_pvt->nh, TAG_END());
+		}
+		nua_handle_bind(tech_pvt->nh, NULL);
+		nua_handle_destroy(tech_pvt->nh);
+		tech_pvt->nh = NULL;
+	}
+
+	switch_set_flag_locked(tech_pvt, TFLAG_BYE);
+	switch_clear_flag_locked(tech_pvt, TFLAG_IO);
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA HANGUP\n");
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t sofia_on_loopback(switch_core_session_t *session)
+{
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA LOOPBACK\n");
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t sofia_on_transmit(switch_core_session_t *session)
+{
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA TRANSMIT\n");
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static void deactivate_rtp(private_object_t *tech_pvt)
+{
+	int loops = 0;//, sock = -1;
+
+	if (tech_pvt->rtp_session) {
+		while (loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) {
+			switch_yield(10000);
+			loops++;
+		}
+		switch_rtp_destroy(&tech_pvt->rtp_session);
+		tech_pvt->rtp_session = NULL;
+	}
+}
+
+
+
+
+static switch_status_t activate_rtp(private_object_t *tech_pvt)
+{
+	int bw, ms;
+	switch_channel_t *channel;
+	const char *err = NULL;
+	switch_rtp_flag_t flags;
+
+	assert(tech_pvt != NULL);
+
+	channel = switch_core_session_get_channel(tech_pvt->session);
+	assert(channel != NULL);
+
+	assert(tech_pvt->codecs[tech_pvt->codec_index] != NULL);
+
+	if (tech_pvt->rtp_session) {
+	  return SWITCH_STATUS_SUCCESS;
+	}
+
+
+	if (switch_core_codec_init(&tech_pvt->read_codec,  
+							   tech_pvt->rm_encoding,
+							   tech_pvt->rm_rate,
+							   tech_pvt->codec_ms,
+							   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");
+		terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+		return SWITCH_STATUS_FALSE;
+	} else {
+		if (switch_core_codec_init(&tech_pvt->write_codec,
+								   tech_pvt->rm_encoding,
+								   tech_pvt->rm_rate,
+								   tech_pvt->codec_ms,
+								   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");
+			terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+			return SWITCH_STATUS_FALSE;
+		} else {
+			int ms;
+			tech_pvt->read_frame.rate = tech_pvt->codec_rate;
+			ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000;
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set Codec %s %s/%d %d ms\n",
+							  switch_channel_get_name(channel),
+							  tech_pvt->codecs[tech_pvt->codec_index]->iananame, tech_pvt->codec_rate, tech_pvt->codec_ms);
+			tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+				
+			switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
+			switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
+		}
+	}
+
+	
+	bw = tech_pvt->read_codec.implementation->bits_per_second;
+	ms = tech_pvt->read_codec.implementation->microseconds_per_frame;
+
+	flags = (switch_rtp_flag_t) (SWITCH_RTP_FLAG_RAW_WRITE | SWITCH_RTP_FLAG_MINI);
+	if (switch_test_flag(tech_pvt, TFLAG_TIMER)) {
+		flags = (switch_rtp_flag_t) (flags | SWITCH_RTP_FLAG_USE_TIMER);
+	}
+	
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n",
+					  switch_channel_get_name(channel),
+					  tech_pvt->local_sdp_audio_ip,
+					  tech_pvt->local_sdp_audio_port,
+					  tech_pvt->remote_sdp_audio_ip,
+					  tech_pvt->remote_sdp_audio_port,
+					  tech_pvt->read_codec.implementation->ianacode,
+					  tech_pvt->read_codec.implementation->microseconds_per_frame / 1000);
+
+
+	tech_pvt->rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip,
+										   tech_pvt->local_sdp_audio_port,
+										   tech_pvt->remote_sdp_audio_ip,
+										   tech_pvt->remote_sdp_audio_port,
+										   tech_pvt->read_codec.implementation->ianacode,
+										   tech_pvt->read_codec.implementation->encoded_bytes_per_frame,
+										   tech_pvt->codec_ms * 1000,
+										   (switch_rtp_flag_t) flags,
+										   NULL,
+										   &err,
+										   switch_core_session_get_pool(tech_pvt->session));
+	
+	if (tech_pvt->rtp_session) {
+		uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0;
+		uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0;
+		uint8_t inb = switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? 0 : 1;
+
+		tech_pvt->ssrc = switch_rtp_get_ssrc(tech_pvt->rtp_session);
+		switch_set_flag_locked(tech_pvt, TFLAG_RTP);
+		switch_set_flag_locked(tech_pvt, TFLAG_IO);
+		
+		if ((vad_in && inb) || (vad_out && !inb)) {
+			switch_rtp_enable_vad(tech_pvt->rtp_session, tech_pvt->session, &tech_pvt->read_codec, SWITCH_VAD_FLAG_TALKING);
+			switch_set_flag_locked(tech_pvt, TFLAG_VAD);
+		}
+	} else {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err);
+		terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+		switch_clear_flag_locked(tech_pvt, TFLAG_IO);
+		return SWITCH_STATUS_FALSE;
+	}
+		
+	switch_set_flag_locked(tech_pvt, TFLAG_IO);
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t sofia_answer_channel(switch_core_session_t *session)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	assert(session != NULL);
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	if (!switch_test_flag(tech_pvt, TFLAG_ANS) && !switch_channel_test_flag(channel, CF_OUTBOUND)) {
+		switch_set_flag_locked(tech_pvt, TFLAG_ANS);
+		tech_choose_port(tech_pvt);
+		set_local_sdp(tech_pvt);
+		activate_rtp(tech_pvt);
+		if (tech_pvt->nh) {
+			nua_respond(tech_pvt->nh, SIP_200_OK, SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), TAG_END());
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Local SDP:\n%s\n", tech_pvt->local_sdp_str);
+		}
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout,
+									   switch_io_flag_t flags, int stream_id)
+{
+	private_object_t *tech_pvt = NULL;
+	size_t bytes = 0, samples = 0, frames = 0, ms = 0;
+	switch_channel_t *channel = NULL;
+	int payload = 0;
+	switch_time_t now, started = switch_time_now(), last_act = switch_time_now();
+	unsigned int elapsed;
+	uint32_t hard_timeout = 60000 * 3;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	if (switch_test_flag(tech_pvt, TFLAG_HUP)) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	tech_pvt->read_frame.datalen = 0;
+	switch_set_flag_locked(tech_pvt, TFLAG_READING);
+
+
+	bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+	samples = tech_pvt->read_codec.implementation->samples_per_frame;
+	ms = tech_pvt->read_codec.implementation->microseconds_per_frame;
+	
+	if (tech_pvt->last_read) {
+		elapsed = (unsigned int)((switch_time_now() - tech_pvt->last_read) / 1000);
+		if (elapsed > 60000) {
+			return SWITCH_STATUS_TIMEOUT;
+		}
+	}
+
+	if (switch_test_flag(tech_pvt, TFLAG_IO)) {
+		switch_status_t status;
+
+		if (!switch_test_flag(tech_pvt, TFLAG_RTP)) {
+			return SWITCH_STATUS_GENERR;
+		}
+
+		assert(tech_pvt->rtp_session != NULL);
+		tech_pvt->read_frame.datalen = 0;
+
+
+		while (!switch_test_flag(tech_pvt, TFLAG_BYE) && switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
+			now = switch_time_now();
+			tech_pvt->read_frame.flags = SFF_NONE;
+
+			status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame);
+			if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+				return SWITCH_STATUS_FALSE;
+			}
+
+			
+			
+			payload = tech_pvt->read_frame.payload;
+
+
+			elapsed = (unsigned int)((switch_time_now() - started) / 1000);
+
+			if (timeout > -1) {
+				if (elapsed >= (unsigned int)timeout) {
+					return SWITCH_STATUS_BREAK;
+				}
+			}
+			
+			elapsed = (unsigned int)((switch_time_now() - last_act) / 1000);
+			if (elapsed >= hard_timeout) {
+				return SWITCH_STATUS_BREAK;
+			}
+
+			if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) {
+				char dtmf[128];
+				switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, dtmf, sizeof(dtmf));
+				switch_channel_queue_dtmf(channel, dtmf);
+			}
+
+
+			if (tech_pvt->read_frame.datalen > 0) {
+				tech_pvt->last_read = switch_time_now();
+				bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+				frames = (tech_pvt->read_frame.datalen / bytes);
+				samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+				ms = frames * tech_pvt->read_codec.implementation->microseconds_per_frame;
+				tech_pvt->timestamp_recv += (int32_t) samples;
+				tech_pvt->read_frame.samples = (int) samples;
+				break;
+			}
+
+			switch_yield(1000);
+		}
+
+	} 
+
+	switch_clear_flag_locked(tech_pvt, TFLAG_READING);
+
+	*frame = &tech_pvt->read_frame;
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout,
+										switch_io_flag_t flags, int stream_id)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+	int bytes = 0, samples = 0, frames = 0;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	if (switch_test_flag(tech_pvt, TFLAG_HUP)) {
+		return SWITCH_STATUS_FALSE;
+	}
+
+	if (!switch_test_flag(tech_pvt, TFLAG_RTP)) {
+		return SWITCH_STATUS_GENERR;
+	}
+
+	if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+		return SWITCH_STATUS_SUCCESS;
+	}
+
+	switch_set_flag_locked(tech_pvt, TFLAG_WRITING);
+
+
+	bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+	frames = ((int) frame->datalen / bytes);
+	samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+
+#if 0
+	printf("%s %s->%s send %d bytes %d samples in %d frames ts=%d\n",
+		   switch_channel_get_name(channel),
+		   tech_pvt->local_sdp_audio_ip,
+		   tech_pvt->remote_sdp_audio_ip,
+		   frame->datalen,
+		   samples,
+		   frames,
+		   tech_pvt->timestamp_send);
+#endif
+
+	switch_rtp_write_frame(tech_pvt->rtp_session, frame, samples);
+	
+	tech_pvt->timestamp_send += (int) samples;
+
+	switch_clear_flag_locked(tech_pvt, TFLAG_WRITING);
+	return status;
+}
+
+
+
+static switch_status_t sofia_kill_channel(switch_core_session_t *session, int sig)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	switch_clear_flag_locked(tech_pvt, TFLAG_IO);
+	switch_set_flag_locked(tech_pvt, TFLAG_HUP);
+
+	if (tech_pvt->rtp_session) {
+		switch_rtp_kill_socket(tech_pvt->rtp_session);
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status_t sofia_waitfor_read(switch_core_session_t *session, int ms, int stream_id)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t sofia_waitfor_write(switch_core_session_t *session, int ms, int stream_id)
+{
+	private_object_t *tech_pvt;
+	switch_channel_t *channel = NULL;
+
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+	return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status_t sofia_send_dtmf(switch_core_session_t *session, char *digits)
+{
+	private_object_t *tech_pvt;
+
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+    assert(tech_pvt != NULL);
+
+	return switch_rtp_queue_rfc2833(tech_pvt->rtp_session,
+									digits,
+									tech_pvt->profile->dtmf_duration * (tech_pvt->read_codec.implementation->samples_per_second / 1000));
+	
+}
+
+static switch_status_t sofia_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
+{
+	switch_channel_t *channel;
+	private_object_t *tech_pvt;
+			
+	channel = switch_core_session_get_channel(session);
+	assert(channel != NULL);
+			
+	tech_pvt = (private_object_t *) switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);
+
+
+	switch (msg->message_id) {
+	case SWITCH_MESSAGE_INDICATE_BRIDGE:
+	  if (tech_pvt->rtp_session && switch_test_flag(tech_pvt, TFLAG_TIMER)) {
+	    switch_rtp_clear_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
+	    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "De-activate timed RTP!\n");
+	  }
+	  break;
+	case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
+	  if (tech_pvt->rtp_session && switch_test_flag(tech_pvt, TFLAG_TIMER)) {
+	    switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER);
+	    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Re-activate timed RTP!\n");
+	  }
+	  break;
+	case SWITCH_MESSAGE_INDICATE_PROGRESS: {
+		struct private_object *tech_pvt;
+	    switch_channel_t *channel = NULL;
+		  
+	    channel = switch_core_session_get_channel(session);
+	    assert(channel != NULL);
+
+	    tech_pvt = switch_core_session_get_private(session);
+	    assert(tech_pvt != NULL);
+
+	    if (!switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Asked to send early media by %s\n", msg->from);
+			/* Transmit 183 Progress with SDP */
+			tech_choose_port(tech_pvt);
+			set_local_sdp(tech_pvt);
+			activate_rtp(tech_pvt);
+			//help me this 183 line will not work.  The resulting 183 has no sdp, sigh...
+			nua_respond(tech_pvt->nh, SIP_183_SESSION_PROGRESS, SOATAG_LOCAL_SDP_STR(tech_pvt->local_sdp_str), TAG_END());
+			//nua_respond(tech_pvt->nh, SIP_200_OK, SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), TAG_END());
+	    }
+	}
+		break;
+	default:
+		break;
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static const switch_io_routines_t sofia_io_routines = {
+	/*.outgoing_channel */ sofia_outgoing_channel,
+	/*.answer_channel */ sofia_answer_channel,
+	/*.read_frame */ sofia_read_frame,
+	/*.write_frame */ sofia_write_frame,
+	/*.kill_channel */ sofia_kill_channel,
+	/*.waitfor_read */ sofia_waitfor_read,
+	/*.waitfor_read */ sofia_waitfor_write,
+	/*.send_dtmf*/ sofia_send_dtmf,
+	/*.receive_message*/ sofia_receive_message
+};
+
+static const switch_state_handler_table_t sofia_event_handlers = {
+	/*.on_init */ sofia_on_init,
+	/*.on_ring */ sofia_on_ring,
+	/*.on_execute */ sofia_on_execute,
+	/*.on_hangup */ sofia_on_hangup,
+	/*.on_loopback */ sofia_on_loopback,
+	/*.on_transmit */ sofia_on_transmit
+};
+
+static const switch_endpoint_interface_t sofia_endpoint_interface = {
+	/*.interface_name */ "sofia",
+	/*.io_routines */ &sofia_io_routines,
+	/*.event_handlers */ &sofia_event_handlers,
+	/*.private */ NULL,
+	/*.next */ NULL
+};
+
+static const switch_loadable_module_interface_t sofia_module_interface = {
+	/*.module_name */ modname,
+	/*.endpoint_interface */ &sofia_endpoint_interface,
+	/*.timer_interface */ NULL,
+	/*.dialplan_interface */ NULL,
+	/*.codec_interface */ NULL,
+	/*.application_interface */ NULL
+};
+
+static switch_status_t sofia_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile,
+											 switch_core_session_t **new_session, switch_memory_pool_t *pool)
+{
+	switch_status_t status = SWITCH_STATUS_FALSE;
+	switch_core_session_t *nsession;
+	char *data, *profile_name, *dest;
+	sofia_profile_t *profile;
+	switch_caller_profile_t *caller_profile = NULL;
+	private_object_t *tech_pvt = NULL;
+	switch_channel_t *channel;
+
+	*new_session = NULL;
+
+	if (!(nsession = switch_core_session_request(&sofia_endpoint_interface, pool))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n");
+		goto done;
+	}
+
+	if (!(tech_pvt = (struct private_object *) switch_core_session_alloc(nsession, sizeof(*tech_pvt)))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n");
+		terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+		goto done;
+	}
+
+	data = switch_core_session_strdup(nsession, outbound_profile->destination_number);
+	profile_name = data;
+	
+	if (!(dest = strchr(profile_name, '/'))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL\n");
+        terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        goto done;
+	}
+
+	*dest++ = '\0';
+	
+	if (!(profile = (sofia_profile_t *) switch_core_hash_find(globals.profile_hash, profile_name))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n");
+        terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+        goto done;
+	}
+	
+	tech_pvt->dest = switch_core_session_alloc(nsession, strlen(dest) + 5);
+	snprintf(tech_pvt->dest, strlen(dest) + 5, "sip:%s", dest);
+	
+	channel = switch_core_session_get_channel(nsession);
+	attach_private(nsession, profile, tech_pvt, dest);	
+	caller_profile = switch_caller_profile_clone(nsession, outbound_profile);
+	switch_channel_set_caller_profile(channel, caller_profile);
+	switch_channel_set_flag(channel, CF_OUTBOUND);
+	switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
+	switch_channel_set_state(channel, CS_INIT);
+	switch_channel_set_variable(channel, "endpoint_disposition", "OUTBOUND");
+	*new_session = nsession;
+	status = SWITCH_STATUS_SUCCESS;
+ done:
+	return status;
+}
+
+
+static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t *sdp)
+{
+	uint8_t match = 0;
+	private_object_t *tech_pvt;
+	sdp_media_t *m;
+
+	tech_pvt = switch_core_session_get_private(session);
+	assert(tech_pvt != NULL);                                                                                                                               
+	
+	for (m = sdp->sdp_media; m ; m = m->m_next) {
+		if (m->m_type == sdp_media_audio) {
+			sdp_rtpmap_t *map;
+
+			for (map = m->m_rtpmaps; map; map = map->rm_next) {
+				int32_t i;
+								
+				if (!strcmp(map->rm_encoding, "telephone-event")) {
+					tech_pvt->te = (switch_payload_t)map->rm_pt;
+				}
+
+				for (i = 0; i < tech_pvt->num_codecs; i++) {
+					const switch_codec_implementation_t *imp = tech_pvt->codecs[i];
+								
+					if (map->rm_pt < 97) {
+						match = (map->rm_pt == imp->ianacode) ? 1 : 0;
+					} else {
+						match = strcasecmp(map->rm_encoding, imp->iananame) ? 0 : 1;
+					}
+								
+					if (match && (map->rm_rate == imp->samples_per_second)) {
+						tech_pvt->rm_encoding = switch_core_session_strdup(session, (char *)map->rm_encoding);
+						tech_pvt->pt = (switch_payload_t)map->rm_pt;
+						tech_pvt->rm_rate = map->rm_rate;
+						tech_pvt->codec_ms = 20;
+						tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, (char *)sdp->sdp_connection->c_address);
+						tech_pvt->remote_sdp_audio_port = (switch_port_t)m->m_port;
+						break;
+					} else {
+						match = 0;
+					}
+				}
+
+				if (match) {
+					break;
+				}
+			}
+		}
+	}
+
+	return match;
+}
+
+static void sip_i_state(int status,
+						char const *phrase, 
+						nua_t *nua,
+						sofia_profile_t *profile,
+						nua_handle_t *nh,
+						switch_core_session_t *session,
+						sip_t const *sip,
+						tagi_t tags[])
+	 
+{
+	char const *l_sdp = NULL, *r_sdp = NULL;
+	//int audio = nua_active_inactive, video = nua_active_inactive, chat = nua_active_inactive;
+	int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
+	int ss_state = nua_callstate_init;
+	switch_channel_t *channel = NULL;
+	private_object_t *tech_pvt = NULL;
+	
+	tl_gets(tags, 
+			NUTAG_CALLSTATE_REF(ss_state),
+			NUTAG_OFFER_RECV_REF(offer_recv),
+			NUTAG_ANSWER_RECV_REF(answer_recv),
+			NUTAG_OFFER_SENT_REF(offer_sent),
+			NUTAG_ANSWER_SENT_REF(answer_sent),
+			SOATAG_LOCAL_SDP_STR_REF(l_sdp),
+			SOATAG_REMOTE_SDP_STR_REF(r_sdp),
+			TAG_END()); 
+
+	if (session) {
+		channel = switch_core_session_get_channel(session);
+		assert(channel != NULL);
+
+		tech_pvt = switch_core_session_get_private(session);
+		assert(tech_pvt != NULL);
+		
+		tech_pvt->nh = nh;
+		
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel %s entering state [%s]\n", 
+						  switch_channel_get_name(channel),
+						  nua_callstate_name(ss_state));
+	}
+
+	if (r_sdp) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remote SDP:\n%s\n", r_sdp);			
+		tech_pvt->remote_sdp_str = switch_core_session_strdup(session, (char *)r_sdp);
+	}
+
+	switch ((enum nua_callstate)ss_state) {
+	case nua_callstate_init:
+		break;
+	case nua_callstate_authenticating:
+		break;
+	case nua_callstate_calling:
+		break;
+	case nua_callstate_proceeding:
+		if (session && r_sdp) {
+			su_home_t home[1] = { SU_HOME_INIT(home) };
+			sdp_parser_t *parser = sdp_parse(home, r_sdp, (int)strlen(r_sdp), 0);
+			sdp_session_t *sdp;
+			uint8_t match = 0;
+
+			if (tech_pvt->num_codecs) {
+				if ((sdp = sdp_session(parser))) {
+					match = negotiate_sdp(session, sdp);
+				}
+			}
+
+			if (parser) {
+				sdp_parser_free(parser);
+			}
+
+			if (home) {
+				su_home_deinit(home);
+			}
+
+			if (match) {
+				tech_choose_port(tech_pvt);
+                activate_rtp(tech_pvt);
+				switch_channel_set_variable(channel, "endpoint_disposition", "PROGRESS");
+                switch_channel_pre_answer(channel);
+				return;
+			}
+		}
+		switch_channel_set_variable(channel, "endpoint_disposition", "NO CODECS");
+		nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
+		break;
+	case nua_callstate_completing:
+		nua_ack(nh, TAG_END());
+		break;
+	case nua_callstate_received: 
+		if (session && r_sdp) {
+			su_home_t home[1] = { SU_HOME_INIT(home) };
+			sdp_parser_t *parser = sdp_parse(home, r_sdp, (int)strlen(r_sdp), 0);
+			sdp_session_t *sdp;
+			uint8_t match = 0;
+
+			if (tech_pvt->num_codecs) {
+				if ((sdp = sdp_session(parser))) {
+					match = negotiate_sdp(session, sdp);
+				}
+			}
+
+			if (parser) {
+				sdp_parser_free(parser);
+			}
+
+			if (home) {
+				su_home_deinit(home);
+			}
+
+			if (match) {
+				switch_channel_set_variable(channel, "endpoint_disposition", "ANSWER");
+				switch_channel_set_state(channel, CS_INIT);
+				switch_set_flag_locked(tech_pvt, TFLAG_READY);
+				switch_core_session_thread_launch(session);
+				//nua_respond(nh, SIP_180_RINGING, TAG_END());
+				return;
+			}
+		}
+		switch_channel_set_variable(channel, "endpoint_disposition", "NO CODECS");
+		nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
+		break;		
+	case nua_callstate_early:
+		break;
+	case nua_callstate_completed:
+		break;
+	case nua_callstate_ready:
+		if (session && r_sdp) {
+			su_home_t home[1] = { SU_HOME_INIT(home) };
+			sdp_parser_t *parser = sdp_parse(home, r_sdp, (int)strlen(r_sdp), 0);
+			sdp_session_t *sdp;
+			uint8_t match = 0;
+
+			if (tech_pvt->num_codecs) {
+				if ((sdp = sdp_session(parser))) {
+					match = negotiate_sdp(session, sdp);
+				}
+			}
+
+			if (parser) {
+				sdp_parser_free(parser);
+			}
+
+			if (home) {
+				su_home_deinit(home);
+			}
+
+			if (match) {
+				switch_channel_set_variable(channel, "endpoint_disposition", "ANSWER");
+				tech_choose_port(tech_pvt);
+				activate_rtp(tech_pvt);
+				switch_channel_answer(channel);
+				return;
+			}
+		}
+		switch_channel_set_variable(channel, "endpoint_disposition", "NO CODECS");
+		nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
+		break;
+	case nua_callstate_terminating:
+		break;
+	case nua_callstate_terminated:
+		if (session) {
+			switch_set_flag_locked(tech_pvt, TFLAG_BYE);
+			terminate_session(&session, SWITCH_CAUSE_NORMAL_CLEARING);
+		}
+		break;
+	}
+
+
+}
+
+static void sip_i_invite(nua_t *nua, 
+						 sofia_profile_t *profile,
+						 nua_handle_t *nh, 
+						 switch_core_session_t *session, 
+						 sip_t const *sip,
+						 tagi_t tags[])
+{
+
+	
+	if (!session) {
+		if ((session = switch_core_session_request(&sofia_endpoint_interface, NULL))) {
+			private_object_t *tech_pvt = NULL;
+			switch_channel_t *channel = NULL;
+			sip_from_t const *from = sip->sip_from;
+			sip_to_t const *to = sip->sip_to;
+			char *displayname;
+			char username[256];
+
+
+			if (!(tech_pvt = (private_object_t *) switch_core_session_alloc(session, sizeof(private_object_t)))) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n");
+				terminate_session(&session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+				return;
+			}
+
+			displayname = switch_core_session_strdup(session, (char *) from->a_display);
+			if (*displayname == '"') {
+				char *p;
+				
+				displayname++;
+				if ((p = strchr(displayname, '"'))) {
+					*p = '\0';
+				}
+			}
+			
+			snprintf(username, sizeof(username), "%s@%s", (char *) from->a_url->url_user, (char *) from->a_url->url_host);
+			attach_private(session, profile, tech_pvt, username);
+			
+			channel = switch_core_session_get_channel(session);
+			switch_channel_set_variable(channel, "endpoint_disposition", "INBOUND CALL");
+			
+			if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
+																	  (char *) from->a_url->url_user,
+																	  profile->dialplan,
+																	  displayname,
+																	  (char *) from->a_url->url_user,
+																	  (char *) from->a_url->url_host,
+																	  NULL,
+																	  NULL,
+																	  NULL,
+																	  (char *)modname,
+																	  profile->context,
+																	  (char *) to->a_url->url_user)) != 0) {
+				switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
+			}
+			switch_set_flag_locked(tech_pvt, TFLAG_INBOUND);
+			nua_handle_bind(nh, session);
+		}
+	}
+}
+
+static void event_callback(nua_event_t   event,
+						   int           status,
+						   char const   *phrase,
+						   nua_t        *nua,
+						   sofia_profile_t  *profile,
+						   nua_handle_t *nh,
+						   switch_core_session_t *session,
+						   sip_t const  *sip,
+						   tagi_t        tags[])
+{
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "event [%s] status [%d] [%s]\n",
+					  nua_event_name (event), status, phrase);
+	
+	switch (event) {
+	case nua_r_shutdown:    
+		//sip_r_shutdown(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_get_params:    
+		//sip_r_get_params(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_register:
+		//sip_r_register(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+    
+	case nua_r_unregister:
+		//sip_r_unregister(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+    
+	case nua_r_options:
+		//sip_r_options(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_invite:
+		//sip_r_invite(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_fork:
+		//sip_i_fork(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+    
+	case nua_i_invite:
+		sip_i_invite(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_prack:
+		//sip_i_prack(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_state:
+		sip_i_state(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+    
+	case nua_r_bye:
+		//sip_r_bye(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_bye:
+		//sip_i_bye(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_message:
+		//sip_r_message(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_message:
+		//sip_i_message(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_info:
+		//sip_r_info(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_info:
+		//sip_i_info(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_refer:
+		//sip_r_refer(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_refer:
+		//sip_i_refer(nua, profile, nh, session, sip, tags);
+		break;
+     
+	case nua_r_subscribe:
+		//sip_r_subscribe(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_unsubscribe:
+		//sip_r_unsubscribe(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_r_publish:
+		//sip_r_publish(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+    
+	case nua_r_notify:
+		//sip_r_notify(status, phrase, nua, profile, nh, session, sip, tags);
+		break;
+     
+	case nua_i_notify:
+		//sip_i_notify(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_cancel:
+		//sip_i_cancel(nua, profile, nh, session, sip, tags);
+		break;
+
+	case nua_i_error:
+		//sip_i_error(nua, profile, nh, session, status, phrase, tags);
+		break;
+
+	case nua_i_active:
+	case nua_i_ack:
+	case nua_i_terminated:
+		break;
+
+	default:
+		if (status > 100)
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d: %03d %s\n", 
+							  nua_event_name (event), event, status, phrase);
+		else
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d\n", nua_event_name (event), event);
+	
+		//tl_print(stdout, "", tags);
+		break;
+
+	}
+}
+
+static void *SWITCH_THREAD_FUNC profile_thread_run(switch_thread_t *thread, void *obj)
+{
+	sofia_profile_t *profile = (sofia_profile_t *) obj;
+	switch_memory_pool_t *pool;
+	sip_alias_node_t *node;
+
+	profile->s_root = su_root_create(NULL);
+	profile->nua = nua_create(profile->s_root, /* Event loop */
+							  event_callback, /* Callback for processing events */
+							  profile, /* Additional data to pass to callback */
+							  NUTAG_URL(profile->url),
+							  NUTAG_EARLY_MEDIA(1),   
+							  TAG_END()); /* Last tag should always finish the sequence */
+
+	for (node = profile->aliases; node; node = node->next) {
+		node->nua = nua_create(profile->s_root, /* Event loop */
+							   event_callback, /* Callback for processing events */
+							   profile, /* Additional data to pass to callback */
+							   NUTAG_URL(node->url),
+							   NUTAG_EARLY_MEDIA(1),
+							   TAG_END()); /* Last tag should always finish the sequence */
+
+	}
+
+	su_root_run(profile->s_root);
+	su_root_destroy(profile->s_root);
+
+	pool = profile->pool;
+	switch_core_destroy_memory_pool(&pool);
+	
+	return NULL;
+}
+
+static void launch_profile_thread(sofia_profile_t *profile)
+{
+	switch_thread_t *thread;
+	switch_threadattr_t *thd_attr = NULL;
+	
+	switch_threadattr_create(&thd_attr, profile->pool);
+	switch_threadattr_detach_set(thd_attr, 1);
+	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+	switch_mutex_lock(globals.hash_mutex);
+	switch_core_hash_insert(globals.profile_hash, profile->name, profile);
+	switch_mutex_unlock(globals.hash_mutex);
+	switch_thread_create(&thread, thd_attr, profile_thread_run, profile, profile->pool);
+}
+
+
+
+static switch_status_t config_sofia(int reload)
+{
+	char *cf = "sofia.conf";
+	switch_xml_t cfg, xml = NULL, xprofile, param;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+	
+	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf);
+		status = SWITCH_STATUS_FALSE;
+		goto done;
+	}
+
+	for (xprofile = switch_xml_child(cfg, "profile"); xprofile; xprofile = xprofile->next) {
+		char *xprofilename = (char *) switch_xml_attr_soft(xprofile, "name");
+		sofia_profile_t *profile;
+		switch_memory_pool_t *pool = NULL;
+		char url[512] = "";
+
+		/* Setup the pool */
+		if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+			goto done;
+		}
+
+		if (!(profile = (sofia_profile_t *) switch_core_alloc(pool, sizeof(*profile)))) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
+			goto done;
+		}
+
+		if (!xprofilename) {
+			xprofilename = "unnamed";
+		}
+
+		profile->pool = pool;
+
+		profile->name = switch_core_strdup(profile->pool, xprofilename);
+
+		profile->dtmf_duration = 100;		
+		for (param = switch_xml_child(xprofile, "param"); param; param = param->next) {
+			char *var = (char *) switch_xml_attr_soft(param, "name");
+			char *val = (char *) switch_xml_attr_soft(param, "value");
+
+			if (!strcmp(var, "debug")) {
+				profile->debug = atoi(val);
+			} else if (!strcmp(var, "use-rtp-timer") && switch_true(val)) {
+			  	switch_set_flag(profile, TFLAG_TIMER);
+			} else if (!strcmp(var, "rfc2833-pt")) {
+                profile->te = (switch_payload_t) atoi(val);
+			} else if (!strcmp(var, "sip-port")) {
+				profile->sip_port = atoi(val);
+			} else if (!strcmp(var, "vad")) {
+				if (!strcasecmp(val, "in")) {
+					switch_set_flag(profile, TFLAG_VAD_IN);
+				} else if (!strcasecmp(val, "out")) {
+					switch_set_flag(profile, TFLAG_VAD_OUT);
+				} else if (!strcasecmp(val, "both")) {
+					switch_set_flag(profile, TFLAG_VAD_IN);
+					switch_set_flag(profile, TFLAG_VAD_OUT);
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invald option %s for VAD\n", val);
+				}
+			} else if (!strcmp(var, "ext-rtp-ip")) {
+				profile->extrtpip = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "rtp-ip")) {
+				profile->rtpip = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "sip-ip")) {
+				profile->sipip = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "ext-sip-ip")) {
+				profile->extsipip = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "username")) {
+				profile->username = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "context")) {
+				profile->context = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "alias")) {
+				sip_alias_node_t *node;
+				if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) {
+					if ((node->url = switch_core_strdup(profile->pool, val))) {
+						node->next = profile->aliases;
+						profile->aliases = node;
+					}
+				}
+			} else if (!strcmp(var, "dialplan")) {
+				profile->dialplan = switch_core_strdup(profile->pool, val);
+			} else if (!strcmp(var, "max-calls")) {
+				profile->max_calls = atoi(val);
+			} else if (!strcmp(var, "codec-prefs")) {
+				profile->codec_string = switch_core_strdup(profile->pool, val);
+				profile->codec_order_last = switch_separate_string(profile->codec_string, ',', profile->codec_order, SWITCH_MAX_CODECS);
+			} else if (!strcmp(var, "codec-ms")) {
+				profile->codec_ms = atoi(val);
+			} else if (!strcmp(var, "dtmf-duration")) {
+				int dur = atoi(val);
+				if (dur > 10 && dur < 8000) {
+					profile->dtmf_duration = dur;
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Duration out of bounds!\n");
+				}
+			}
+		}
+
+		if (!profile->username) {
+			profile->username = switch_core_strdup(profile->pool, "FreeSWITCH");
+		}
+
+		if (!profile->rtpip) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Setting ip to '127.0.0.1'\n");
+			profile->rtpip = switch_core_strdup(profile->pool, "127.0.0.1");
+		}
+
+		if (!profile->codec_ms) {
+			profile->codec_ms = 20;
+		}
+		
+		if (!profile->sip_port) {
+			profile->sip_port = 5060;
+		}
+
+		if (!profile->dialplan) {
+			profile->dialplan = switch_core_strdup(profile->pool, "default");
+		}
+
+
+		snprintf(url, sizeof(url), "sip:%s:%d", profile->sipip, profile->sip_port);
+		profile->url = switch_core_strdup(profile->pool, url);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Started Profile %s [%s]\n", profile->name, url);
+		launch_profile_thread(profile);
+
+	}
+	
+ done:
+	if (xml) {
+		switch_xml_free(xml);
+	}
+
+	return status;
+
+}
+
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename)
+{
+
+	if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
+		return SWITCH_STATUS_TERM;
+	}
+
+	memset(&globals, 0, sizeof(globals));
+	su_init();
+
+
+	switch_core_hash_init(&globals.profile_hash, module_pool);
+	switch_mutex_init(&globals.hash_mutex, SWITCH_MUTEX_NESTED, module_pool);
+
+	config_sofia(0);
+
+
+	/* connect my internal structure to the blank pointer passed to me */
+	*module_interface = &sofia_module_interface;
+
+	/* indicate that the module should continue to be loaded */
+	return SWITCH_STATUS_SUCCESS;
+
+}
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void)
+{
+	su_deinit();
+
+	return SWITCH_STATUS_SUCCESS;
+}

Modified: freeswitch/trunk/src/switch_ivr.c
==============================================================================
--- freeswitch/trunk/src/switch_ivr.c	(original)
+++ freeswitch/trunk/src/switch_ivr.c	Fri Aug 25 19:55:59 2006
@@ -1501,9 +1501,12 @@
  endfor1:
 
 	if (session) {
-		switch_codec_t *read_codec = switch_core_session_get_read_codec(session);
+		switch_codec_t *read_codec = NULL;
+
 		switch_channel_pre_answer(caller_channel);
+		read_codec = switch_core_session_get_read_codec(session);
 
+		assert(read_codec != NULL);
 		if (switch_core_codec_init(&write_codec,
 								   "L16",
 								   read_codec->implementation->samples_per_second,



More information about the Freeswitch-svn mailing list