[Freeswitch-branches] [commit] r1745 - in freeswitch/branches/stkn: . src/mod/endpoints/mod_pjsip src/mod/endpoints/mod_pjsip/pjsdp src/mod/endpoints/mod_pjsip/pjsdp/build src/mod/endpoints/mod_pjsip/pjsdp/pjsdp src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/output src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/lib src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src

Freeswitch SVN stkn at freeswitch.org
Mon Jul 3 22:03:34 EDT 2006


Author: stkn
Date: Mon Jul  3 22:03:31 2006
New Revision: 1745

Added:
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/Makefile
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip_sdp.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/Makefile
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-gcc.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-vc.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/common.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-mingw.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-unix.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-win32.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-alpha.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-i386.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-m68k.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-mpc860.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-powerpc.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-sparc.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-darwinos.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux-kernel.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-palmos.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-rtems.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-sunos.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-win32.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/rules.mak
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/configure   (contents, props changed)
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/Makefile
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/output/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/config.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/errno.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp_neg.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/types.h
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/lib/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/errno.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_cmp.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_neg.c
   freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/user.mak
Modified:
   freeswitch/branches/stkn/modules.conf.in

Log:
Add initial version of mod_pjsip, early development version, partially working for me (tm). Download pjproject-0.5.6.tar.gz from pjproject.org and put it into the toplevel libs folder.

Modified: freeswitch/branches/stkn/modules.conf.in
==============================================================================
--- freeswitch/branches/stkn/modules.conf.in	(original)
+++ freeswitch/branches/stkn/modules.conf.in	Mon Jul  3 22:03:31 2006
@@ -17,7 +17,8 @@
 #dialplans/mod_dialplan_directory
 dialplans/mod_dialplan_xml
 #directories/mod_ldap
-endpoints/mod_exosip
+#endpoints/mod_exosip
+endpoints/mod_pjsip
 endpoints/mod_iax
 endpoints/mod_dingaling
 endpoints/mod_portaudio

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/Makefile
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/Makefile	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,55 @@
+CFLAGS += -I.
+
+ifeq ($(OSARCH),Darwin)
+	LINKER=g++
+else
+	LINKER=$(CC)
+endif
+
+PJ_PROJECT=pjproject-0.5.6
+PJ_DIR=$(BASE)/libs/$(PJ_PROJECT)
+
+ifeq ($(MAKECMDGOALS),build)
+CC_DEF=-D
+
+include $(PJ_DIR)/build.mak
+include $(PJ_DIR)/build/m-$(MACHINE_NAME).mak
+include $(PJ_DIR)/build/os-$(OS_NAME).mak
+
+CFLAGS += -I$(PJ_DIR)/pjlib/include -I$(PJ_DIR)/pjlib-util/include -I$(PJ_DIR)/pjsip/include
+CFLAGS += -I$(PJ_DIR)/pjmedia/include -I./pjsdp/pjsdp/include
+CFLAGS += $(OS_CFLAGS) $(M_CFLAGS)
+
+LDFLAGS += -L$(PJ_DIR)/pjlib/lib -L$(PJ_DIR)/pjlib-util/lib -L$(PJ_DIR)/pjsip/lib -L./pjsdp/pjsdp/lib
+LDFLAGS += -lpjsip-$(TARGET_NAME) -lpjsip-ua-$(TARGET_NAME) -lpjsip-simple-$(TARGET_NAME)
+LDFLAGS += -lpjsdp-$(TARGET_NAME) -lpjlib-util-$(TARGET_NAME) -lpj-$(TARGET_NAME)
+endif
+
+OBJS=mod_pjsip_sdp.o
+
+.PHONY: pjsdp
+
+all:	depends
+	$(MAKE) build
+
+build: pjsdp $(OBJS) $(MODNAME).$(DYNAMIC_LIB_EXTEN)
+
+pjsdp:
+	$(MAKE) CFLAGS="$(filter-out -Werror,$(CFLAGS)) -fPIC" -C pjsdp
+
+depends:
+	MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) $(PJ_PROJECT).tar.gz --prefix=$(PREFIX)
+
+%.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)  $(OBJS) $(MODNAME).o $(LDFLAGS)
+
+clean:
+	rm -fr *.$(DYNAMIC_LIB_EXTEN) *.o *~
+	$(MAKE) -C pjsdp clean
+
+install:
+	cp -f $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(DESTDIR)$(PREFIX)/mod

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,1397 @@
+/*
+ * Experimental PJSIP channel module for FreeSWITCH
+ *
+ * 
+ *
+ *
+ *
+ *
+ */
+/***** ChangeLog
+ *
+ *  2006-06-18: initial version.
+ *              99% copy'n'paste from FreeSWITCH and pjsip example code.
+ *
+ *  2006-06-20: added parts of sdp negotiation, started writing pjsip event handler code.
+ *              more code taken from mod_exosip for FS event handling.
+ */
+
+#define HAVE_APR
+#include <switch.h>
+#include <pjsip.h>
+#include <pjsdp.h>
+#include <pjsip_ua.h>
+#include <pjsip_simple.h>
+#include <pjlib-util.h>
+#include <pjlib.h>
+
+#include "mod_pjsip.h"
+
+static const char modname[] = "mod_pjsip";
+
+#define MAX_CALLS 1000
+
+static switch_memory_pool_t *module_pool = NULL;
+static pj_caching_pool cp;
+static pjsip_inv_callback invite_cb;
+static pj_pool_t *pjsip_pool;
+
+#define PJ_MAX_STRLEN(str, pjstr) ( sizeof(str) > pjstr.slen ? pjstr.slen : sizeof(str) )
+
+static struct {
+	/* */
+	switch_hash_t *call_hash;
+
+	pj_str_t local_uri;
+	pj_str_t local_contact;
+	pj_sockaddr_in sip_addr;	//<- really needed?
+
+	/* endpoints */
+	pjsip_endpoint *sip_endpt;
+	pjsip_module mod_app;
+
+	/* */
+	char local_addr[128];
+	char dialplan[50];
+	int sip_port;
+	int running;
+} globals;
+
+enum tech_flags {
+	TFLAG_OUTBOUND = (1 << 0),
+	TFLAG_INBOUND = (1 << 1),
+	TFLAG_IO = (1 << 2),
+	TFLAG_BYE = (1 << 3),
+	TFLAG_RINGING = (1 << 4),  // not used atm
+	TFLAG_ANSWERED = (1 << 5),
+	TFLAG_USING_CODEC = (1 << 6),
+
+	TFLAG_READING = (1 << 10),
+	TFLAG_WRITING = (1 << 11),
+	TFLAG_TIMER = (1 << 20)
+};
+
+static switch_status_t create_sdp( pj_pool_t *pool,
+					struct pjsip_tech_pvt *pvt,
+					pjmedia_sdp_session **p_sdp,
+					pjmedia_sdp_session *ref_sdp );
+
+static switch_status_t start_rtp( struct pjsip_tech_pvt *pvt );
+static switch_status_t stop_rtp( struct pjsip_tech_pvt *pvt );
+//static struct pjsip_tech_pvt * find_pvt_by_callid( char *id );
+
+/* BEGIN: freeswitch callbacks */
+/* event handlers */
+static switch_status_t pjsip_on_init(switch_core_session_t *session);
+static switch_status_t pjsip_on_hangup(switch_core_session_t *session);
+static switch_status_t pjsip_on_loopback(switch_core_session_t *session);
+static switch_status_t pjsip_on_transmit(switch_core_session_t *session); 
+
+/* io routines */
+static switch_status_t pjsip_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 pjsip_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout,
+                                             switch_io_flag_t flags, int stream_id); // unimplemented
+
+static switch_status_t pjsip_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout,
+                                             switch_io_flag_t flags, int stream_id); // unimplemented
+
+static switch_status_t pjsip_answer_channel(switch_core_session_t *session);
+static switch_status_t pjsip_kill_channel(switch_core_session_t *session, int sig);	
+
+static switch_status_t pjsip_waitfor_read(switch_core_session_t *session, int ms, int stream_id);
+static switch_status_t pjsip_waitfor_write(switch_core_session_t *session, int ms, int stream_id);
+
+static switch_status_t pjsip_on_ring(switch_core_session_t *session);
+
+static switch_status_t pjsip_receive_message(switch_core_session_t *session,
+						switch_core_session_message_t *msg);
+
+static const switch_io_routines_t pjsip_io_routines = {
+        /*.outgoing_channel */ &pjsip_outgoing_channel,
+        /*.answer_channel */ &pjsip_answer_channel,
+        /*.read_frame */ &pjsip_read_frame,
+        /*.write_frame */ &pjsip_write_frame,
+        /*.kill_channel */ &pjsip_kill_channel,
+        /*.waitfor_read */ &pjsip_waitfor_read,
+        /*.waitfor_read */ &pjsip_waitfor_write,
+        /*.send_dtmf*/ NULL,
+        /*.receive_message*/ &pjsip_receive_message
+};
+
+static const switch_state_handler_table_t pjsip_event_handlers = {
+        /*.on_init */ &pjsip_on_init,
+        /*.on_ring */ &pjsip_on_ring,
+        /*.on_execute */ NULL,
+        /*.on_hangup */ &pjsip_on_hangup,
+        /*.on_loopback */ &pjsip_on_loopback,
+        /*.on_transmit */ &pjsip_on_transmit
+};
+
+static const switch_endpoint_interface_t pjsip_endpoint_interface = {
+        /*.interface_name */ "pjsip",
+        /*.io_routines */ &pjsip_io_routines,
+        /*.event_handlers */ &pjsip_event_handlers,
+        /*.private */ NULL,
+        /*.next */ NULL
+};
+
+static const switch_loadable_module_interface_t pjsip_module_interface = {
+        /*.module_name */ modname,
+        /*.endpoint_interface */ &pjsip_endpoint_interface,
+        /*.timer_interface */ NULL,
+        /*.dialplan_interface */ NULL,
+        /*.codec_interface */ NULL,
+        /*.application_interface */ NULL
+};
+
+static switch_status_t pjsip_on_init(switch_core_session_t *session)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+	pj_status_t status;
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	/* register switch thread to pjsip core */
+	status = pj_thread_register( NULL, pvt->thread_desc, &pvt->thread );
+	if( status != PJ_SUCCESS ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not register switch session thread to pjlib\n" );
+		switch_core_session_destroy( &session );
+		return SWITCH_STATUS_GENERR;
+	}
+
+	if( switch_channel_test_flag( channel, CF_OUTBOUND ) )
+	{
+		pjsip_tx_data *txdata = NULL;
+		pjmedia_sdp_session *sdp = NULL;
+//		pjsip_sip_uri *dst_uri;
+		char tmp[50];
+		pj_str_t dst;
+
+		pj_ansi_sprintf( tmp, "<sip:%s:%d>", pvt->caller_profile->destination_number, 2054 );
+		pj_cstr( &dst, tmp );
+
+		pvt->sip_ua = pjsip_ua_instance();
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dst: %s, Local: %s %s\n", tmp, globals.local_uri.ptr, globals.local_contact.ptr );
+
+		/* create new UAC dialog */
+		status = pjsip_dlg_create_uac( pvt->sip_ua,
+						&globals.local_uri,
+						&globals.local_contact,
+						&dst,
+						&dst,
+						&pvt->sip_dialog );
+
+		if ( status != PJ_SUCCESS ) {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new UAC dialog for call\n" );
+			switch_core_session_destroy( &session );
+			return SWITCH_STATUS_GENERR;
+		}
+
+		/* create SDP */
+		create_sdp( pvt->sip_dialog->pool, pvt, &sdp, NULL );
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initial SDP created\n" );
+
+		/* create INVITE session */
+		status = pjsip_inv_create_uac( pvt->sip_dialog, sdp, 0, &pvt->sip_invite );
+		if ( status != PJ_SUCCESS ) {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new INVITE session for call %s\n" );
+			pjsip_dlg_terminate( pvt->sip_dialog );
+			return SWITCH_STATUS_GENERR;
+		}
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Invite UAC created\n" );
+
+		/* attach private object to pjsip session data */
+		pvt->sip_invite->mod_data[globals.mod_app.id] = pvt;
+
+		/* create invite request */
+		status = pjsip_inv_invite( pvt->sip_invite, &txdata );
+		if ( status != PJ_SUCCESS ) {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create initial INVITE request for call %s\n" );
+			pjsip_dlg_terminate( pvt->sip_dialog );
+			return SWITCH_STATUS_GENERR;
+		}
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initial INVITE created\n" );
+
+		/* send request */
+		status = pjsip_inv_send_msg( pvt->sip_invite, txdata );
+		if ( status != PJ_SUCCESS ) {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send INVITE request for call %s\n" );
+			pjsip_dlg_terminate( pvt->sip_dialog );
+			return SWITCH_STATUS_GENERR;
+		}
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INVITE sent\n" );
+
+		/* set call id */
+		strncpy( pvt->call_id, pvt->sip_invite->dlg->call_id->id.ptr, PJ_MAX_STRLEN( pvt->call_id, pvt->sip_invite->dlg->call_id->id ) );
+	} else {
+		pjsip_tx_data *txdata;
+
+		/* Send 183 Session Progress */
+		status = pjsip_inv_answer( pvt->sip_invite, 183, NULL, NULL, &txdata );
+		if( status != PJ_SUCCESS ) {
+			status = pjsip_inv_answer( pvt->sip_invite, PJSIP_SC_NOT_ACCEPTABLE, NULL, NULL, &txdata );
+
+			if( status == PJ_SUCCESS )
+				pjsip_inv_send_msg( pvt->sip_invite, txdata );
+			else
+				pjsip_inv_terminate( pvt->sip_invite, 500, PJ_FALSE );
+
+			switch_core_session_destroy( &session );
+			return SWITCH_STATUS_GENERR;
+		}
+		pjsip_inv_send_msg( pvt->sip_invite, txdata );
+	}
+
+	/* */
+	switch_set_flag( pvt, TFLAG_IO );
+
+	/* add call to hash */
+	switch_core_hash_insert( globals.call_hash, pvt->call_id, pvt );
+
+	switch_channel_set_state( channel, CS_RING );
+
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+static switch_status_t pjsip_on_ring(switch_core_session_t *session) 
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel( session );
+
+	pvt = switch_core_session_get_private( session );
+#if 0 /* fix this, pjsip_inv_* is wrong here */
+	if( !switch_channel_test_flag( channel, CF_OUTBOUND ) ) {
+		pj_status_t status;
+		pjsip_tx_data *txdata;
+
+		/* start to send 180 ring every second */
+		status = pjsip_inv_create_answer( pvt->sip_dialog, 180, NULL, &txdata );
+		if( status != PJ_SUCCESS ) {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create 183 RING message" );
+			status = pjsip_inv_end_session( pvt->sip_invite, 500, NULL, &txdata );
+			if( status == PJ_SUCCESS )
+				pjsip_inv_send_msg( pvt->sip_invite, txdata );
+			else
+				pjsip_inv_terminate( pvt->sip_invite, 500, 1 );
+
+			switch_core_session_destroy( &session );
+			return SWITCH_STATUS_GENERR;
+		}
+		pjsip_dlg_send_response( pvt->sip_dialog, pjsip_rdata_get_tsx(rxdata), txdata );
+		switch_set_flag( pvt, TFLAG_RINGING );
+	}
+#endif
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "RING %s\n", switch_channel_get_name( channel ) );
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t pjsip_on_hangup(switch_core_session_t *session)
+{ 
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+	pj_status_t status;
+	pjsip_tx_data *txdata;
+
+	assert( session != NULL );
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Hangup call %s\n", pvt->call_id );
+
+	/* remove call from hash */
+	switch_core_hash_delete( globals.call_hash, pvt->call_id );
+
+	switch_set_flag( pvt, TFLAG_BYE );
+	switch_clear_flag( pvt, TFLAG_IO );
+
+	stop_rtp( pvt );
+
+	status = pjsip_inv_end_session( pvt->sip_invite, 200, NULL, &txdata );
+	if( status != PJ_SUCCESS )
+		pjsip_inv_terminate( pvt->sip_invite, 500, PJ_TRUE );
+	else
+		pjsip_inv_send_msg( pvt->sip_invite, txdata );
+
+
+	if ( switch_test_flag( pvt, TFLAG_USING_CODEC ) ) {
+		switch_core_codec_destroy( &pvt->read_codec );
+		switch_core_codec_destroy( &pvt->write_codec );
+	}
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+static switch_status_t pjsip_on_loopback(switch_core_session_t *session)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	/* do nothing */
+
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+static switch_status_t pjsip_on_transmit(switch_core_session_t *session)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	/* do nothing */
+
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+/* answer channel */
+static switch_status_t pjsip_answer_channel(switch_core_session_t *session)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "* Answering call %s\n", pvt->call_id );
+
+	if( !switch_test_flag( pvt, TFLAG_ANSWERED ) &&
+	    !switch_channel_test_flag( channel, CF_OUTBOUND ) )
+	{
+		pjsip_tx_data *txdata;
+		pj_status_t status;
+
+		assert( pvt->sip_invite != NULL );
+
+		/* create SIP 200 with SDP */
+		status = pjsip_inv_answer( pvt->sip_invite, 200, NULL, NULL, &txdata );
+		if( status != PJ_SUCCESS ) {
+			pjsip_inv_terminate( pvt->sip_invite, 500, PJ_TRUE );
+			switch_core_session_destroy( &session );
+			return SWITCH_STATUS_GENERR;
+		}
+		status = pjsip_inv_send_msg( pvt->sip_invite, txdata );
+		if( status != PJ_SUCCESS ) {
+			pjsip_inv_terminate( pvt->sip_invite, 500, PJ_TRUE );
+			switch_core_session_destroy( &session );
+			return SWITCH_STATUS_GENERR;
+		}
+		switch_set_flag( pvt, TFLAG_ANSWERED );
+	}
+	return SWITCH_STATUS_SUCCESS;
+}
+
+/* kill channel */
+static switch_status_t pjsip_kill_channel(switch_core_session_t *session, int sig)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	switch_clear_flag( pvt, TFLAG_IO );
+
+	if( !switch_test_flag( pvt, TFLAG_BYE ) ) {
+		switch_set_flag( pvt, TFLAG_BYE );
+		switch_channel_hangup( channel, SWITCH_CAUSE_NORMAL_CLEARING );
+	}
+
+	if( pvt->rtp_session ) {
+		switch_rtp_kill_socket( pvt->rtp_session );
+	}
+	return SWITCH_STATUS_SUCCESS;
+}
+
+/* wait for read */
+static switch_status_t pjsip_waitfor_read(switch_core_session_t *session, int ms, int stream_id)
+{
+	/* do nothing */
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+/* wait for write */
+static switch_status_t pjsip_waitfor_write(switch_core_session_t *session, int ms, int stream_id)
+{
+	/* do nothing */
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+/* create new call */
+static switch_status_t pjsip_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile,
+                                             switch_core_session_t **new_session, switch_memory_pool_t *pool)
+{ 
+	struct pjsip_tech_pvt *pvt = NULL;
+	switch_channel_t *channel = NULL;
+
+	if ( !( *new_session = switch_core_session_request( &pjsip_endpoint_interface, pool ) ) ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new session for call\n" );
+		return SWITCH_STATUS_GENERR;
+	}
+	switch_core_session_add_stream( *new_session, NULL );
+
+	/* create new call */
+	pvt = (struct pjsip_tech_pvt *) switch_core_session_alloc(*new_session, sizeof(struct pjsip_tech_pvt));
+	if (!pvt) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new private object for call\n" );
+		goto err;
+	}
+	pj_memset( pvt, 0, sizeof(struct pjsip_tech_pvt) );
+	switch_core_session_set_private( *new_session, pvt );
+	channel = switch_core_session_get_channel( *new_session );
+	pvt->session = *new_session;
+
+	/* get list of all available codecs */
+	pvt->num_codecs = switch_loadable_module_get_codecs( switch_core_session_get_pool(pvt->session), pvt->codecs,
+								sizeof(pvt->codecs) / sizeof(pvt->codecs[0]) );
+
+	if( outbound_profile ) {
+		switch_caller_profile_t *caller_profile = NULL;
+//		pjsip_dialog *dialog = NULL;
+//		pj_status_t status;
+//		pj_str_t dst_uri;
+		char name[128];
+
+		/* create channel name, copy profile and parse destination number (?) */
+		snprintf( name, sizeof(name), "pjsip/%s-%04x", outbound_profile->destination_number, rand() & 0xffff );
+		switch_channel_set_name( channel, name );
+
+		caller_profile = switch_caller_profile_clone( *new_session, outbound_profile );
+		switch_channel_set_caller_profile( channel, caller_profile );
+
+		pvt->caller_profile = caller_profile;
+
+		switch_channel_set_flag( channel, CF_OUTBOUND );
+		switch_channel_set_state( channel, CS_INIT );
+
+		return SWITCH_STATUS_SUCCESS;
+	}
+err:
+	switch_core_session_destroy(new_session);
+	return SWITCH_STATUS_GENERR;
+} 
+
+static switch_status_t pjsip_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout,
+                                             switch_io_flag_t flags, int stream_id) 
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	assert( session != NULL );
+	assert( frame != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	switch_set_flag( pvt, TFLAG_READING );
+
+	pvt->read_frame.datalen = 0;
+
+	if( switch_test_flag( pvt, TFLAG_IO ) ) {
+		switch_status_t status;
+
+		assert( pvt->rtp_session != NULL );
+
+		while( !switch_test_flag( pvt, TFLAG_BYE ) &&
+		        switch_test_flag( pvt, TFLAG_IO ) &&
+		       !pvt->read_frame.datalen )
+		{
+			status = switch_rtp_zerocopy_read_frame( pvt->rtp_session, &pvt->read_frame );
+			if( status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK ) {
+				return SWITCH_STATUS_FALSE;
+			}
+
+
+			if( pvt->read_frame.datalen > 0 ) {
+				int samples, bytes, frames;
+
+				bytes	= pvt->read_codec.implementation->encoded_bytes_per_frame;
+				frames	= pvt->read_frame.datalen / bytes;
+				samples	= frames * pvt->read_codec.implementation->samples_per_frame;
+
+				pvt->read_frame.samples = (int)samples;
+				break;
+			}
+
+			switch_yield( 1000 );
+		}
+	}
+
+	switch_clear_flag( pvt, TFLAG_READING );
+
+	*frame = &pvt->read_frame;
+
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+static switch_status_t pjsip_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout,
+                                             switch_io_flag_t flags, int stream_id)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	assert( session != NULL );
+	assert( frame != NULL );
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	switch_set_flag( pvt, TFLAG_WRITING );
+
+	if( switch_test_flag( pvt, TFLAG_IO ) ) {
+//		switch_status_t status;
+		int samples, frames, bytes;
+
+		assert( pvt->rtp_session != NULL );
+
+		bytes	= pvt->read_codec.implementation->encoded_bytes_per_frame;
+		frames	= (int)frame->datalen / bytes;
+		samples	= frames * pvt->read_codec.implementation->samples_per_frame;
+
+		switch_rtp_write_frame( pvt->rtp_session, frame, samples );
+	}
+
+	switch_clear_flag( pvt, TFLAG_WRITING );
+
+	return SWITCH_STATUS_SUCCESS;
+} 
+
+/* END: freeswitch callbacks */
+
+
+/* BEGIN: pjsip callbacks */
+
+/*
+ *
+ */
+static void on_incoming_call( pjsip_rx_data *rxdata )
+{
+	switch_core_session_t *session;
+	pj_status_t status;
+
+	/* check call limit */
+#if 0
+	if( switch_core_hash_count( &globals.call_hash ) >= MAX_CALLS ) {
+		const pj_str_t reason = pj_str("Too many calls");
+		pjsip_endpt_respond_stateless( app.sip_endpt, rxdata, 
+						500, &reason,
+						NULL, NULL);
+		return;
+	}
+#endif
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "PJSIP process id %lu, thread name %s\n", pj_getpid(), pj_thread_get_name( pj_thread_this() ) );
+
+	if ((session = switch_core_session_request( &pjsip_endpoint_interface, NULL ))) {
+		struct pjsip_tech_pvt *pvt;
+		switch_channel_t *channel;
+		char name[128];
+		pjmedia_sdp_session *sdp;
+		pjsip_dialog *dialog;
+		pjsip_tx_data *txdata;
+		pj_str_t tmp, username, host, destination;
+
+		channel = switch_core_session_get_channel( session );
+
+		switch_core_session_add_stream( session, NULL );
+		if ((pvt = (struct pjsip_tech_pvt *) switch_core_session_alloc( session, sizeof(struct pjsip_tech_pvt) ))) {
+			memset( pvt, 0, sizeof(struct pjsip_tech_pvt) );
+			switch_core_session_set_private( session, pvt );
+			pvt->session = session;
+		} else {
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new channel for inbound call\n" );
+			switch_core_session_destroy( &session );
+			return;
+		}
+		/* give the new session a name */
+		pj_strdup_with_null( pjsip_pool, &tmp, &((pjsip_sip_uri *)rxdata->msg_info.from->uri)->host );
+		snprintf(name, sizeof(name), "pjsip/%s-%04x", tmp.ptr, rand() & 0xffff);
+		switch_channel_set_name( channel, name );
+
+		switch_channel_clear_flag( channel, CF_OUTBOUND );
+
+		/* need the invite data in pjsip_on_init */
+		pvt->sip_ua = pjsip_ua_instance();
+
+		/* create UAS dialog */
+		status = pjsip_dlg_create_uas( pvt->sip_ua, rxdata, NULL, &dialog );
+		if( status != PJ_SUCCESS ) {
+			const pj_str_t reason = pj_str("Unable to create dialog");
+			pjsip_endpt_respond_stateless( globals.sip_endpt, rxdata, 
+							500, &reason,
+							NULL, NULL);
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new pjsip dialog for inbound call\n" );
+			switch_core_session_destroy( &session );
+			return;
+		}
+
+		/* load codecs */
+		pvt->num_codecs = switch_loadable_module_get_codecs( switch_core_session_get_pool(pvt->session), pvt->codecs,
+									sizeof(pvt->codecs) / sizeof(pvt->codecs[0]) );
+
+		/* Create our initial sdp */
+		create_sdp( dialog->pool, pvt, &sdp, NULL );
+
+		/* Create UAS invite session */
+		status = pjsip_inv_create_uas( dialog, rxdata, sdp, 0, &pvt->sip_invite );
+		if( status != PJ_SUCCESS ) {
+			pjsip_dlg_create_response( dialog, rxdata, 500, NULL, &txdata );
+			pjsip_dlg_send_response( dialog, pjsip_rdata_get_tsx( rxdata ), txdata );
+
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new pjsip dialog for inbound call\n" );
+			switch_core_session_destroy( &session );
+			return;
+		}
+
+		pj_strdup_with_null( pjsip_pool, &username, &((pjsip_sip_uri *)rxdata->msg_info.from->uri)->user );
+		pj_strdup_with_null( pjsip_pool, &host, &((pjsip_sip_uri *)rxdata->msg_info.from->uri)->host );
+		pj_strdup_with_null( pjsip_pool, &destination, &((pjsip_sip_uri *)rxdata->msg_info.to->uri)->host );
+
+		if ( (pvt->caller_profile = switch_caller_profile_new( switch_core_session_get_pool( session ),
+													username.ptr,
+													globals.dialplan,
+													username.ptr,
+													username.ptr,
+													host.ptr,
+													NULL,
+													NULL,
+													NULL,
+													(char *)modname,
+													NULL,
+													destination.ptr)) != 0 ) {
+			switch_channel_set_caller_profile( channel, pvt->caller_profile );
+		}
+
+		/* assign vars */
+		pvt->sip_invite->mod_data[globals.mod_app.id] = pvt;
+		pvt->sip_dialog = dialog;
+
+		/* set call id */
+		strncpy( pvt->call_id, dialog->call_id->id.ptr, PJ_MAX_STRLEN(pvt->call_id, dialog->call_id->id) );
+
+		switch_set_flag( pvt, TFLAG_INBOUND );
+
+		/* Send 100 Trying */
+		status = pjsip_inv_initial_answer( pvt->sip_invite, rxdata, 100, NULL, NULL, &txdata );
+		if( status != PJ_SUCCESS ) {
+			status = pjsip_inv_initial_answer( pvt->sip_invite, rxdata, PJSIP_SC_NOT_ACCEPTABLE, NULL, NULL, &txdata );
+
+			if( status == PJ_SUCCESS )
+				pjsip_inv_send_msg( pvt->sip_invite, txdata );
+			else
+				pjsip_inv_terminate( pvt->sip_invite, 500, PJ_FALSE );
+
+			switch_core_session_destroy( &session );
+			return;
+		}
+		pjsip_inv_send_msg( pvt->sip_invite, txdata );
+
+		/* Print the initial SDP to the console / log */
+		{
+			char tmp_sdp[1024];
+			int len = pjmedia_sdp_print( sdp, tmp_sdp, sizeof(tmp_sdp)-1 );
+			if( len > 0 ) {
+				tmp_sdp[len] = '\0';
+				switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, 
+						"\n** Our initial SDP **\n%s\n*********************\n", tmp_sdp );
+			}
+		}
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Starting session thread for call %s\n", pvt->call_id );
+
+		/* set initial state */
+		switch_channel_set_state( channel, CS_INIT );
+		switch_core_session_thread_launch( session );
+		return;
+	} else {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create new channel for inbound call\n" );
+		return;
+	}
+}
+
+static pj_bool_t on_rx_request( pjsip_rx_data *rxdata )
+{ 
+	pj_str_t reason;
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s: received new request\n", __FUNCTION__ );
+
+	switch( rxdata->msg_info.msg->line.req.method.id ) {
+		case PJSIP_ACK_METHOD:
+			return PJ_FALSE;
+			;;
+		case PJSIP_INVITE_METHOD:
+			on_incoming_call( rxdata );
+			return PJ_TRUE;
+			;;
+		default:
+			reason = rxdata->msg_info.msg->line.req.method.name;
+			pjsip_endpt_respond_stateless( globals.sip_endpt, rxdata, 
+							500, &reason, NULL, NULL );
+			;;
+	}
+	return PJ_TRUE;
+}
+
+static void call_on_tsx_state_changed( pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e )
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_core_session_t *session;
+
+	assert( inv != NULL && tsx != NULL );
+
+	pvt = inv->mod_data[globals.mod_app.id];
+	assert( pvt != NULL );
+
+	session = pvt->session;
+	assert( session != NULL );
+
+	if( inv->state == PJSIP_INV_STATE_EARLY &&
+	    tsx->state == PJSIP_TSX_STATE_PROCEEDING && 
+	    tsx->status_code == PJSIP_SC_RINGING )
+	{
+		switch_channel_t *channel;
+
+		channel = switch_core_session_get_channel( session );
+		assert( channel != NULL );
+
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "* Ring received (call %s)\n", pvt->call_id );
+	}
+}
+
+static void call_on_forked( pjsip_inv_session *inv,
+					pjsip_event *evt ) { }
+
+/* pjsip callback for inv session state changes */
+static void call_on_state_changed( pjsip_inv_session *inv,
+					pjsip_event *evt )
+{ 
+	struct pjsip_tech_pvt *pvt;
+	switch_core_session_t *session;
+	switch_channel_t *channel;
+
+	assert( evt != NULL && inv != NULL );
+
+	pvt = inv->mod_data[globals.mod_app.id];
+	assert( pvt != NULL );
+
+	session = pvt->session;
+	assert( session != NULL );
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "- Call %s state has changed (event type %s)\n", pvt->call_id, pjsip_event_str( evt->type ) );
+
+	switch( inv->state )
+	{
+	case PJSIP_INV_STATE_DISCONNECTED:
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "* Call %s has been disconnected\n", pvt->call_id );
+		switch_channel_hangup( channel, SWITCH_CAUSE_NORMAL_CLEARING );
+		/* -> hangup */
+		break;
+	case PJSIP_INV_STATE_CONFIRMED:
+		/* -> connected */
+		switch_channel_answer( channel );
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "* Call %s has been connected\n", pvt->call_id );
+		break;
+	case PJSIP_INV_STATE_EARLY:
+	case PJSIP_INV_STATE_CONNECTING:
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "* Call %s, state %s\n", pvt->call_id, pjsip_inv_state_name( inv->state ) );
+		break;
+	default:
+		break;
+	}
+} 
+
+/* pjsip callback for sdp negotiation changes */
+static void call_on_media_update( pjsip_inv_session *inv,
+					pj_status_t status )
+{ 
+	struct pjsip_tech_pvt *pvt;
+	const pjmedia_sdp_session *remote_sdp, *local_sdp;
+#if 0
+	pjmedia_stream_info streaminfo;
+	pj_in_addr rem_addr;
+#endif
+	pj_pool_t *pool;
+	const switch_codec_interface_t *codec;
+	const switch_codec_implementation_t *imp;
+	switch_channel_t *channel;
+	switch_core_session_t *session;
+	switch_status_t ret;
+	int ms, rate;
+	pj_sockaddr addr;
+
+	pvt = inv->mod_data[globals.mod_app.id];
+	pool = inv->dlg->pool;
+	assert( pvt != NULL );
+	assert( pool != NULL );
+
+	session = pvt->session;
+	assert( session != NULL );
+
+	channel = switch_core_session_get_channel( pvt->session );
+	assert( channel != NULL );
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "-- SDP negotiation status change %s (state %s, status %d)\n", pvt->call_id,
+					pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state( inv->neg )), status );
+
+	if( status != PJ_SUCCESS ) {
+		char tmp[1024];
+		pj_strerror( status, tmp, sizeof(tmp) );
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SDP negotiation failed for call %s, reason: %s\n", pvt->call_id, tmp );
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+		return;
+	}
+
+	if( pvt->rtp_session ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SDP re-negotiation received %s\n", pvt->call_id );
+
+		stop_rtp( pvt );
+		switch_clear_flag( pvt, TFLAG_IO );
+
+		/* destroy codecs */
+		if ( switch_test_flag( pvt, TFLAG_USING_CODEC ) ) {
+			switch_core_codec_destroy( &pvt->read_codec );
+			switch_core_codec_destroy( &pvt->write_codec );
+		}
+	}
+
+	pjmedia_sdp_neg_get_active_local( inv->neg, &local_sdp );
+	pjmedia_sdp_neg_get_active_remote( inv->neg, &remote_sdp );
+
+	/* Print the final(?) SDPs to the console / log */
+	{
+		char tmp_sdp[1024];
+		int len;
+
+		len = pjmedia_sdp_print( local_sdp, tmp_sdp, sizeof(tmp_sdp)-1 );
+		if( len > 0 ) {
+			tmp_sdp[len] = '\0';
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, 
+					"\n** Our final SDP **\n%s\n*********************\n", tmp_sdp );
+		}
+
+		len = pjmedia_sdp_print( remote_sdp, tmp_sdp, sizeof(tmp_sdp)-1 );
+		if( len > 0 ) {
+			tmp_sdp[len] = '\0';
+			switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, 
+					"\n** Remote final SDP **\n%s\n*************************\n", tmp_sdp );
+		}
+	}
+
+	/* retrieve stream (= codec) information */
+	status = mod_pjsip_codec_from_sdp( pvt, pool, local_sdp, remote_sdp, 0, &codec, &imp );
+	if( status != PJ_SUCCESS ) {
+		/* No valid codec found */
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SDP negotation failed, codec unknown\n" );
+		switch_set_flag( pvt, TFLAG_BYE );
+		switch_clear_flag( pvt, TFLAG_IO );
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+		return;
+	}
+
+	/* valid codec + implementation found, setup codecs */
+	rate = imp->bits_per_second / 8;
+	  ms = imp->microseconds_per_frame / 1000;
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Codec %s, rate: %d, ms per frame: %d\n", codec->implementations->iananame, rate, ms );
+
+	/* initialize codecs */
+	ret = switch_core_codec_init(&pvt->read_codec, codec->implementations->iananame, rate, ms, 1,
+				SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+				NULL, switch_core_session_get_pool( session ));
+
+	if( ret != SWITCH_STATUS_SUCCESS ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize read codec\n" );
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+		return;
+	}
+
+	ret = switch_core_codec_init(&pvt->write_codec, codec->implementations->iananame, rate, ms, 1,
+				SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+				NULL, switch_core_session_get_pool( session ));
+
+	if( ret != SWITCH_STATUS_SUCCESS ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize write codec\n" );
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+		return;
+	}
+
+	pvt->read_frame.rate  = rate;
+	pvt->read_frame.codec = &pvt->read_codec;
+
+	switch_core_session_set_read_codec( session, &pvt->read_codec );
+	switch_core_session_set_write_codec( session, &pvt->write_codec );
+
+	/* update remote rtp IP and port, restart rtp */
+	status = mod_pjsip_addr_from_sdp( remote_sdp, 0, &addr );
+	if( status != PJ_SUCCESS ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get remote RTP endpoint address and port\n" );
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+	} else {
+		pj_in_addr tmp_addr;
+
+		pvt->remote_sdp_audio_port = pj_sockaddr_in_get_port( (pj_sockaddr_in *)&addr );
+		tmp_addr.s_addr = pj_htonl( pj_sockaddr_in_get_addr( (pj_sockaddr_in *)&addr ).s_addr );
+		strncpy( pvt->remote_sdp_audio_addr, pj_inet_ntoa( tmp_addr ), 
+				sizeof(pvt->remote_sdp_audio_addr) );
+	}
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Codec %s, %s:%d\n", codec->implementations->iananame, pvt->remote_sdp_audio_addr, pvt->remote_sdp_audio_port );
+
+	assert( channel != NULL );
+
+	start_rtp( pvt );
+#if 0
+	if( !switch_channel_test_flag( channel, CF_OUTBOUND ) )
+		switch_channel_set_state( channel, CS_RING );
+#endif
+}
+
+static switch_status_t pjsip_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
+{
+	struct pjsip_tech_pvt *pvt;
+	switch_channel_t *channel;
+
+	pvt = switch_core_session_get_private( session );
+	assert( pvt != NULL );
+
+	channel = switch_core_session_get_channel( session );
+	assert( channel != NULL );
+
+	switch( msg->message_id )
+	{
+	case SWITCH_MESSAGE_INDICATE_BRIDGE:
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "MSG Indicate Bridge\n" );
+		if( pvt->rtp_session && switch_test_flag( pvt, TFLAG_TIMER ) ) {
+			switch_rtp_clear_flag( pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER );
+		}
+		break;
+	case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "MSG Indicate Unbridge\n" );
+		if( pvt->rtp_session && switch_test_flag( pvt, TFLAG_TIMER ) ) {
+			switch_rtp_set_flag( pvt->rtp_session, SWITCH_RTP_FLAG_USE_TIMER );
+		}
+		break;
+	case SWITCH_MESSAGE_INDICATE_PROGRESS:
+		{
+			pj_status_t status;
+			pjsip_tx_data *txdata;
+			/* Send 183 Session Progress */
+
+			status = pjsip_inv_answer( pvt->sip_invite, 183, NULL, NULL, &txdata );
+			if( status != PJ_SUCCESS ) {
+				status = pjsip_inv_answer( pvt->sip_invite, PJSIP_SC_NOT_ACCEPTABLE, NULL, NULL, &txdata );
+
+				if( status == PJ_SUCCESS )
+					pjsip_inv_send_msg( pvt->sip_invite, txdata );
+				else
+					pjsip_inv_terminate( pvt->sip_invite, 500, PJ_FALSE );
+
+				switch_core_session_destroy( &session );
+				return SWITCH_STATUS_GENERR;
+			}
+			pjsip_inv_send_msg( pvt->sip_invite, txdata );
+		}
+		switch_channel_set_flag( channel, CF_EARLY_MEDIA );
+		break;
+	default:
+		break;
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+/* END: pjsip callbacks */
+
+/* BEGIN: mid-level functions */
+static switch_status_t create_sdp( pj_pool_t *pool,
+					struct pjsip_tech_pvt *pvt,
+					pjmedia_sdp_session **p_sdp,
+					pjmedia_sdp_session *ref_sdp )
+{
+	pjmedia_sdp_session *sdp;
+	pjmedia_sdp_media *media;
+	pjmedia_sdp_attr *attr, *rtpmap_attr;
+	pjmedia_sdp_rtpmap rtpmap;
+	int i;
+
+	if( !pool || !pvt )
+		return SWITCH_STATUS_GENERR;
+
+	/* allocate and initialize new SDP var */
+	sdp = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_session) );
+	if( !sdp )
+		return SWITCH_STATUS_GENERR;
+
+	sdp->origin.user = pj_str( "FreeSWITCH" );
+	sdp->origin.version = sdp->origin.id = 0;
+	sdp->origin.net_type = pj_str( "IN" );
+	sdp->origin.addr_type = pj_str( "IP4" );
+	sdp->origin.addr = *pj_gethostname();
+	sdp->name = pj_str( "pjsip" );
+
+	sdp->conn = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_conn) );
+	sdp->conn->net_type = pj_str( "IN" );
+	sdp->conn->addr_type = pj_str( "IP4" );
+	sdp->conn->addr = pj_str( globals.local_addr );
+
+	sdp->time.start = sdp->time.stop = 0;
+	sdp->attr_count = 0;
+
+	/* add media to SDP */
+	sdp->media_count = 1;
+	media = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_media) );
+	sdp->media[0] = media;
+
+	/* media info */
+	media->desc.media = pj_str( "audio" );
+	media->desc.port = pvt->local_sdp_audio_port = switch_rtp_request_port();
+	media->desc.port_count = 1;
+	media->desc.transport = pj_str( "RTP/AVP" );
+
+	strncpy( pvt->local_sdp_audio_addr, globals.local_addr, sizeof(pvt->local_sdp_audio_addr) );
+
+	/* codec rtpmap list */
+	media->desc.fmt_count = 0;
+	media->attr_count = 0;
+
+	if( !ref_sdp ) {
+		for( i = 0; i < pvt->num_codecs; i++ ) {
+			const switch_codec_implementation_t *imp = NULL;
+
+			imp = pvt->codecs[i]->implementations;
+
+			/* add all incarnations of this codec... */
+			while( imp != NULL ) {
+				char tmp[10];
+
+				/* copy codec id */
+				sprintf( tmp, "%d", pvt->codecs[i]->implementations->ianacode );
+				pj_strdup2( pool, &media->desc.fmt[media->desc.fmt_count], tmp );
+
+				/* other codec parameters */
+				rtpmap.pt = media->desc.fmt[media->desc.fmt_count];
+				rtpmap.clock_rate = imp->samples_per_second;
+				rtpmap.enc_name = pj_str( pvt->codecs[i]->implementations->iananame );
+				rtpmap.param.slen = 0;
+
+				/* add codec implementation to attr list */
+				pjmedia_sdp_rtpmap_to_attr( pool, &rtpmap, &rtpmap_attr );
+				media->attr[media->attr_count++] = rtpmap_attr;
+
+				/* goto next implementation of this codec */
+				imp = imp->next;
+			}
+			media->desc.fmt_count++;
+		}
+	}
+
+	/* Add sendrecv attribute */
+	attr = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_attr) );
+	attr->name = pj_str( "sendrecv" );
+	media->attr[media->attr_count++] = attr;
+
+	/*
+	 * Add support telephony event
+	 */
+	media->desc.fmt[media->desc.fmt_count++] = pj_str( "121" );
+	/* Add rtpmap. */
+	attr = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_attr) );
+	attr->name = pj_str( "rtpmap" );
+	attr->value = pj_str( ":121 telephone-event/8000" );
+	media->attr[media->attr_count++] = attr;
+	/* Add fmtp */
+	attr = pj_pool_zalloc( pool, sizeof(pjmedia_sdp_attr) );
+	attr->name = pj_str( "fmtp" );
+	attr->value = pj_str( ":121 0-15" );
+	media->attr[media->attr_count++] = attr;
+
+	/* all done */
+	*p_sdp = sdp;
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t start_rtp( struct pjsip_tech_pvt *pvt )
+{
+	int rate, ms;
+	const char *err;
+	switch_rtp_flag_t flags = 0;
+	switch_channel_t *channel;
+
+	assert( pvt != NULL );
+
+	channel = switch_core_session_get_channel( pvt->session );
+	assert( channel != NULL );
+
+	if( pvt->rtp_session ) {
+		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP already initialized %s\n", pvt->call_id );
+		return SWITCH_STATUS_SUCCESS;
+	}
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting RTP for call %s\n", pvt->call_id );
+
+	rate = pvt->read_codec.implementation->bits_per_second;
+	ms   = pvt->read_codec.implementation->microseconds_per_frame;
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP parameters for call %s: src (%s:%d) -> dst (%s:%d), using codec %s @ %d b/s, framelen %d ms\n",
+					pvt->call_id,
+					pvt->local_sdp_audio_addr,
+					pvt->local_sdp_audio_port,
+					pvt->remote_sdp_audio_addr,
+					pvt->remote_sdp_audio_port,
+					pvt->read_codec.implementation->iananame,
+					rate,
+					ms );
+
+
+	pvt->rtp_session = switch_rtp_new( pvt->local_sdp_audio_addr,
+					   pvt->local_sdp_audio_port,
+					   pvt->remote_sdp_audio_addr,
+					   pvt->remote_sdp_audio_port,
+					   pvt->read_codec.implementation->ianacode,
+					   pvt->read_codec.implementation->encoded_bytes_per_frame,
+					   pvt->read_codec.implementation->microseconds_per_frame / 1000,
+					   flags,
+					   NULL,
+					   &err, switch_core_session_get_pool( pvt->session ) );
+
+	if( !pvt->rtp_session ) {
+		switch_channel_hangup( channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER );
+		switch_set_flag( pvt, TFLAG_BYE );
+		return SWITCH_STATUS_FALSE;
+	}
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting RTP done\n" );
+
+	switch_set_flag( pvt, TFLAG_IO );
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t stop_rtp( struct pjsip_tech_pvt *pvt )
+{
+	int loop = 0;
+
+	if( !pvt->rtp_session )
+		return SWITCH_STATUS_SUCCESS;
+
+	while( loop < 10 &&
+	       ( switch_test_flag( pvt, TFLAG_READING ) ||
+		 switch_test_flag( pvt, TFLAG_WRITING ) ) )
+	{
+		switch_yield( 1000 );
+		loop++;
+	}
+
+	switch_rtp_destroy( &pvt->rtp_session );
+	pvt->rtp_session = NULL;
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+#if 0 // unused for now
+static struct pjsip_tech_pvt * find_pvt_by_callid( char *id )
+{
+	struct pjsip_tech_pvt *pvt = NULL;
+
+	assert( id != NULL );
+
+	pvt = (struct pjsip_tech_pvt *) switch_core_hash_find( globals.call_hash, id );
+
+	return pvt;
+}
+#endif
+/* END: mid-level functions */
+
+
+/* BEGIN: module managment */
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void)
+{
+        if ( globals.running ) {
+                globals.running = -1;
+                while ( globals.running ) {
+                        switch_yield( 100000 );
+                }
+        }
+        return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename)
+{
+
+        /* NOTE:  **interface is **_interface because the common lib redefines interface to struct in some situations */
+        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;
+        }
+
+        switch_core_hash_init( &globals.call_hash, module_pool );
+
+        /* connect my internal structure to the blank pointer passed to me */
+        *module_interface = &pjsip_module_interface;
+
+        /* indicate that the module should continue to be loaded */
+        return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_runtime(void)
+{
+	pj_status_t status;
+
+	globals.sip_port = 5060;
+
+	pj_memset( &globals.mod_app, 0, sizeof(globals.mod_app) );
+	globals.mod_app.name = pj_str( "mod-pjsip" );	    		/* Name.		*/
+	globals.mod_app.id = -1;					/* Id			*/
+	globals.mod_app.priority = PJSIP_MOD_PRIORITY_APPLICATION;	/* Priority		*/
+	globals.mod_app.on_rx_request = &on_rx_request;	    		/* on_rx_request()	*/
+
+	/* Set dialplan */
+	strncpy( globals.dialplan, "XML", sizeof(globals.dialplan) - 1 );
+
+	/* init pjlib */
+	status = pj_init();
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize pjlib\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* next: pjlib_util */
+	status = pjlib_util_init();
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize pjlib_util\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* initialize pjlib pool factory */
+	pj_caching_pool_init( &cp, &pj_pool_factory_default_policy, 0 );
+
+	/* create endpoint */
+	status = pjsip_endpt_create( &cp.factory, "FreeSWITCH", &globals.sip_endpt );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize sip endpoint\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* allocate pool for pjsip functions */
+	pjsip_pool = pjsip_endpt_create_pool( globals.sip_endpt, "pjsip-pool", 1024, 1024 );
+
+	/* add udp transport */
+	globals.sip_addr.sin_family = PJ_AF_INET;
+	globals.sip_addr.sin_addr.s_addr = 0;
+	globals.sip_addr.sin_port = pj_htons( 5060 );
+
+	status = pjsip_udp_transport_start( globals.sip_endpt, &globals.sip_addr, NULL, 1, NULL );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create UDP transport\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* init transaction handling */
+	status = pjsip_tsx_layer_init_module( globals.sip_endpt );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize transaction handling\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* init UA dialog handling */
+	status = pjsip_ua_init_module( globals.sip_endpt, NULL );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize UA dialog handling\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	pj_memset( &invite_cb, 0, sizeof(invite_cb) );
+	invite_cb.on_state_changed = &call_on_state_changed;
+	invite_cb.on_new_session = &call_on_forked;
+	invite_cb.on_media_update = &call_on_media_update;
+	invite_cb.on_tsx_state_changed = &call_on_tsx_state_changed;
+
+	/* initialize invite session module */
+	status = pjsip_inv_usage_init( globals.sip_endpt, &invite_cb );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to register invite session module to pjsip core\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	/* */
+	{
+		const pj_str_t *hostname;
+		pj_sockaddr_in tmp_addr;
+		char *addr;
+		char local_uri[64];
+
+		hostname = pj_gethostname();
+		pj_sockaddr_in_init( &tmp_addr, hostname, 0 );
+		addr = pj_inet_ntoa( tmp_addr.sin_addr );
+		pj_ansi_strcpy( globals.local_addr, addr );
+		pj_ansi_sprintf( local_uri, "sip:%s:%d", hostname->ptr, globals.sip_port );
+		pj_strdup2( pjsip_pool, &globals.local_uri, local_uri );
+		pj_strdup2( pjsip_pool, &globals.local_contact, local_uri );
+	}
+
+	/* register application to pjsip core */
+	status = pjsip_endpt_register_module( globals.sip_endpt, &globals.mod_app );
+	if ( status != PJ_SUCCESS ) {
+ 		switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to register mod_pjsip application to pjsip core\n" );
+		return SWITCH_STATUS_TERM;
+	}
+
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Monitor thread running\n" );
+
+	/* main loop */
+	globals.running = 1;
+	while ( globals.running > 0 ) {
+		pj_time_val timeout = { 1, 0 };
+		pjsip_endpt_handle_events( globals.sip_endpt, &timeout );
+	}
+
+	globals.running = 0;
+	switch_log_printf( SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Monitor thread exiting\n" );
+
+	return SWITCH_STATUS_TERM;
+}

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,78 @@
+#ifndef _MOD_PJSIP_H_
+#define _MOD_PJSIP_H_
+
+struct pjsip_tech_pvt {
+	/* switch */
+	switch_core_session_t *session;
+	switch_caller_profile_t *caller_profile;
+	char call_id[50];
+	int cid;
+
+	/* flags */
+	unsigned int flags;
+
+	/* net */
+	int local_sdp_audio_port;
+	char local_sdp_audio_addr[50];
+	int remote_sdp_audio_port;
+	char remote_sdp_audio_addr[50];
+	switch_rtp_t *rtp_session;
+
+	/* pjsip */
+	pjsip_user_agent *sip_ua;	/* */
+	pjsip_dialog *sip_dialog;
+	pjsip_inv_session *sip_invite;
+	pj_thread_desc thread_desc;
+	pj_thread_t *thread;
+
+	/* codec information */
+	switch_codec_t read_codec;
+	switch_codec_t write_codec;
+	switch_codec_interface_t *codecs[SWITCH_MAX_CODECS];
+	int num_codecs;
+
+	switch_frame_t read_frame;
+};
+
+/*
+ * Get the final codec from SDP after successfull negotiation
+ */
+pj_status_t mod_pjsip_codec_from_sdp(	struct pjsip_tech_pvt *pvt,
+					pj_pool_t *pool,
+					const pjmedia_sdp_session *local,
+					const pjmedia_sdp_session *remote,
+					const unsigned int stream_idx,
+					const switch_codec_interface_t **codec,
+					const switch_codec_implementation_t **imp );
+
+/*
+ * Get address from sdp
+ */
+pj_status_t mod_pjsip_addr_from_sdp(	const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					pj_sockaddr *addr );
+
+/*
+ *
+ */
+pj_status_t mod_pjsip_media_direction_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *dir );
+
+/*
+ *
+ */
+pj_status_t mod_pjsip_media_type_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *type );
+/*
+ *
+ */
+pj_status_t mod_pjsip_telephone_type_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *event_pt );
+
+#endif

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip_sdp.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/mod_pjsip_sdp.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,307 @@
+/* $Id: session.c 452 2006-05-17 17:17:39Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 
+ */
+
+#define HAVE_APR
+#include <switch.h>
+#include <pjlib.h>
+#include <pjsip_ua.h>
+#include <pjsdp.h>
+
+#include "mod_pjsip.h"
+
+#define THIS_FILE	"mod_pjsip_sdp.c"
+
+static const pj_str_t ID_AUDIO = { "audio", 5 };
+static const pj_str_t ID_VIDEO = { "video", 5 };
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3 };
+static const pj_str_t ID_IP6 = { "IP6", 3 };
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+static pj_status_t find_codec(
+				struct pjsip_tech_pvt *pvt,
+				unsigned int pt,
+				unsigned int clock_rate,
+				const switch_codec_interface_t **codec,
+				const switch_codec_implementation_t **imp )
+{
+	const switch_codec_implementation_t *tmp_imp;
+	int i;
+
+	assert( codec != NULL );
+	assert( pvt != NULL );
+	assert( imp != NULL );
+
+	tmp_imp = NULL;
+	for( i = 0; i < pvt->num_codecs; i++ ) {
+		if( pvt->codecs[i]->implementations->ianacode == pt ) {
+			/* codec found, find implementation too */
+			tmp_imp = pvt->codecs[i]->implementations;
+
+			while( tmp_imp != NULL ) {
+				if ( tmp_imp->samples_per_second == clock_rate ) {
+					*codec = pvt->codecs[i];
+					*imp = tmp_imp;
+					return PJ_SUCCESS;
+				}
+
+				tmp_imp = tmp_imp->next;
+			}
+			break;
+		}
+	}
+	return PJ_ENOTFOUND;
+}
+
+pj_status_t mod_pjsip_codec_from_sdp(
+						struct pjsip_tech_pvt *pvt,
+						pj_pool_t *pool,
+						const pjmedia_sdp_session *local,
+						const pjmedia_sdp_session *remote,
+						const unsigned int stream_idx,
+						const switch_codec_interface_t **codec,
+						const switch_codec_implementation_t **imp )
+{
+	const pjmedia_sdp_attr *attr;
+	const pjmedia_sdp_media *local_m;
+	const pjmedia_sdp_media *rem_m;
+	pjmedia_sdp_rtpmap *rtpmap;
+	unsigned int pt;
+	pj_status_t status;
+//	const switch_codec_implementation_t *tmp_imp;
+
+	assert( pvt != NULL );
+	assert( pool != NULL );
+	assert( local != NULL );
+	assert( remote != NULL );
+	assert( codec != NULL );
+	assert( imp != NULL );
+
+	assert( stream_idx < local->media_count );
+	assert( stream_idx < remote->media_count );
+
+	/* Keep SDP shortcuts */
+	local_m = local->media[stream_idx];
+	rem_m = remote->media[stream_idx];
+
+	/* And codec must be numeric! */
+	if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 
+	    !pj_isdigit(*rem_m->desc.fmt[0].ptr))
+	{
+		return PJMEDIA_EINVALIDPT;
+	}
+
+	/* Get the payload number for receive channel. */
+	pt = pj_strtoul(&local_m->desc.fmt[0]);
+
+	/* Get codec info.
+	 * For static payload types, get the info from codec manager.
+	 * For dynamic payload types, MUST get the rtpmap.
+	 */
+	if (pt < 96) {
+		pj_bool_t has_rtpmap;
+
+		rtpmap = NULL;
+		has_rtpmap = PJ_TRUE;
+
+		attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+						   &local_m->desc.fmt[0]);
+		if (attr == NULL)
+			has_rtpmap = PJ_FALSE;
+		else {
+			status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+			if (status != PJ_SUCCESS)
+				has_rtpmap = PJ_FALSE;
+		}
+
+		/* Build codec format info: */
+		if (has_rtpmap) {
+			status = find_codec( pvt, pt, rtpmap->clock_rate, codec, imp );
+			if( status != PJ_SUCCESS )
+				return PJ_ENOTFOUND;
+		} else {
+			/*
+			 * No rtpmap, use default clock_rate (8000 Hz)
+			 */
+			status = find_codec( pvt, pt, 8000, codec, imp );
+			if( status != PJ_SUCCESS )
+				return PJ_ENOTFOUND;
+		}
+	} else
+		return PJ_ENOTSUP;
+
+
+	return PJ_SUCCESS;
+}
+
+
+pj_status_t mod_pjsip_addr_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					pj_sockaddr *addr )
+{
+	const pjmedia_sdp_media *m;
+//	const pjmedia_sdp_attr *attr;
+	const pjmedia_sdp_conn *conn;
+	pj_sockaddr_in *ip4addr;
+
+	assert( sdp != NULL );
+	assert( addr != NULL );
+
+	assert( stream_idx < sdp->media_count );
+
+	/* Keep SDP shortcuts */
+	m = sdp->media[stream_idx];
+
+	conn = m->conn ? m->conn : sdp->conn;
+	if (conn == NULL)
+		return PJMEDIA_SDP_EMISSINGCONN;
+
+	/* only IPv4 for now */
+	ip4addr = (pj_sockaddr_in *)addr;
+
+	/* Set address: */
+	ip4addr->sin_family = PJ_AF_INET;
+	ip4addr->sin_port = pj_htons(m->desc.port);
+	if (pj_inet_aton(&conn->addr, &ip4addr->sin_addr) == 0) {
+
+		/* Invalid IP address. */
+		return PJMEDIA_EINVALIDIP;
+	}
+	return PJ_SUCCESS;
+}
+
+
+pj_status_t mod_pjsip_media_direction_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *dir )
+{
+	const pjmedia_sdp_media *m;
+//	const pjmedia_sdp_attr *attr;
+	const pjmedia_sdp_conn *conn;
+
+	assert( sdp != NULL );
+	assert( dir != NULL );
+
+	assert( stream_idx < sdp->media_count );
+
+	/* Keep SDP shortcuts */
+	m = sdp->media[stream_idx];
+
+	conn = m->conn ? m->conn : sdp->conn;
+	if (conn == NULL)
+		return PJMEDIA_SDP_EMISSINGCONN;
+
+	/* Media direction: */
+	if (m->desc.port == 0 || 
+		pj_inet_addr(&conn->addr).s_addr==0 ||
+		pjmedia_sdp_media_find_attr(m, &STR_INACTIVE, NULL)!=NULL)
+	{
+		/* Inactive stream. */
+		*dir = PJMEDIA_DIR_NONE;
+	} else if (pjmedia_sdp_media_find_attr(m, &STR_SENDONLY, NULL)!=NULL) {
+
+		/* Send only stream. */
+		*dir = PJMEDIA_DIR_ENCODING;
+	} else if (pjmedia_sdp_media_find_attr(m, &STR_RECVONLY, NULL)!=NULL) {
+
+		/* Recv only stream. */
+		*dir = PJMEDIA_DIR_DECODING;
+	} else {
+
+		/* Send and receive stream. */
+		*dir = PJMEDIA_DIR_ENCODING_DECODING;
+	}
+	return PJ_SUCCESS;
+}
+
+
+pj_status_t mod_pjsip_media_type_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *type )
+{
+	const pjmedia_sdp_media *m;
+//	const pjmedia_sdp_attr *attr;
+
+	assert( sdp != NULL );
+	assert( type != NULL );
+
+	assert( stream_idx < sdp->media_count );
+
+	/* Keep SDP shortcuts */
+	m = sdp->media[stream_idx];
+
+	/* Media type: */
+	if (pj_stricmp(&m->desc.media, &ID_AUDIO) == 0) {
+		*type = PJMEDIA_TYPE_AUDIO;
+
+	} else if (pj_stricmp(&m->desc.media, &ID_VIDEO) == 0) {
+		*type = PJMEDIA_TYPE_VIDEO;
+
+	} else {
+		*type = PJMEDIA_TYPE_UNKNOWN;
+		return PJMEDIA_EINVALIMEDIATYPE;
+	}
+	return PJ_SUCCESS;
+}
+
+pj_status_t mod_pjsip_telephone_type_from_sdp(
+					const pjmedia_sdp_session *sdp,
+					const int stream_idx,
+					int *event_pt )
+{
+	const pjmedia_sdp_media *m;
+	const pjmedia_sdp_attr *attr;
+	int i;
+
+	assert( sdp != NULL );
+	assert( event_pt != NULL );
+
+	assert( stream_idx < sdp->media_count );
+
+	/* Keep SDP shortcuts */
+	m = sdp->media[stream_idx];
+
+	/* Get payload type for telephone-events */
+	*event_pt = -1;
+	for (i=0; i<m->attr_count; ++i) {
+		pjmedia_sdp_rtpmap r;
+
+		attr = m->attr[i];
+		if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+			continue;
+		if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+			continue;
+		if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+			*event_pt = pj_strtoul(&r.pt);
+			break;
+		}
+	}
+	return PJ_SUCCESS;
+}

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/Makefile
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/Makefile	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,54 @@
+include build.mak
+include build/host-$(HOST_NAME).mak
+
+DIRS = pjsdp
+
+ifdef MINSIZE
+MAKE_FLAGS := MINSIZE=1
+endif
+
+all clean dep depend distclean doc print realclean:
+	for dir in $(DIRS); do \
+	   if [ -d $$dir ]; then \
+		if make $(MAKE_FLAGS) -C $$dir/build $@; then \
+		    true; \
+		else \
+		    exit 1; \
+		fi; \
+	   fi; \
+	done
+
+LIBS = 	pjsdp/lib/libpjsdp-$(TARGET_NAME).a
+BINS =
+
+size:
+	@echo -n 'Date: '
+	@date
+	@echo
+	@for lib in $(LIBS); do \
+		echo "$$lib:"; \
+		ar tv $$lib | awk '{print $$3 "\t" $$8}' | sort -n; \
+		echo -n 'Total: '; \
+		ar tv $$lib | awk '{print " + " $$3}' | xargs expr 0; \
+		echo; \
+	done
+	@echo
+	@for bin in $(BINS); do \
+		echo "size $$bin:"; \
+		size $$bin; \
+	done
+
+#dos2unix:
+#	for f in `find . | egrep '(mak|h|c|S|s|Makefile)$$'`; do \
+#		dos2unix "$$f" > dos2unix.tmp; \
+#		cp dos2unix.tmp "$$f"; \
+#	done
+#	rm -f dos2unix.tmp
+
+xhdrid:
+	for f in `find . | egrep '\.(h|c|S|s|cpp|hpp)$$'`; do \
+		echo Processing $$f...; \
+		cat $$f | sed 's/.*\$$Author\$$/ */' > /tmp/id; \
+		cp /tmp/id $$f; \
+	done
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,8 @@
+# Auto-generated build.mak
+export MACHINE_NAME := i386
+export OS_NAME := linux
+export HOST_NAME := unix
+export CC_NAME := gcc
+export TARGET_NAME := i386-linux-gcc
+export CROSS_COMPILE := 
+export LINUX_POLL := select

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-gcc.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-gcc.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,22 @@
+export CC = $(CROSS_COMPILE)gcc -c
+export AR = $(CROSS_COMPILE)ar rv 
+export LD = $(CROSS_COMPILE)gcc
+export LDOUT = -o 
+export RANLIB = $(CROSS_COMPILE)ranlib
+
+export OBJEXT := .o
+export LIBEXT := .a
+export LIBEXT2 :=
+
+export CC_OUT := -o 
+export CC_INC := -I
+export CC_DEF := -D
+export CC_OPTIMIZE := -O2
+export CC_LIB := -l
+
+export CC_SOURCES :=
+export CC_CFLAGS := -Wall 
+#export CC_CFLAGS += -Wdeclaration-after-statement
+#export CC_CXXFLAGS := -Wdeclaration-after-statement
+export CC_LDFLAGS :=
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-vc.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/cc-vc.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,20 @@
+export CC := cl /c /nologo
+export AR := lib /NOLOGO /OUT:
+export LD := cl /nologo
+export LDOUT := /Fe
+export RANLIB := echo ranlib
+
+export OBJEXT := .obj
+export LIBEXT := .lib
+export LIBEXT2 := .LIB
+
+export CC_OUT := /Fo
+export CC_INC := /I
+export CC_DEF := /D
+export CC_OPTIMIZE := /Ox
+export CC_LIB :=
+
+export CC_SOURCES :=
+export CC_CFLAGS := /W4 /MT
+export CC_CXXFLAGS := /GX
+export CC_LDFLAGS := /MT 

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/common.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/common.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,54 @@
+#
+# Include host/target/compiler selection.
+# This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables.
+#
+include ../../build.mak
+
+#
+# Include global compiler specific definitions
+#
+include ../../build/cc-$(CC_NAME).mak
+
+#
+# (Optionally) Include compiler specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include cc-$(CC_NAME).mak
+
+#
+# Include global machine specific definitions
+#
+include ../../build/m-$(MACHINE_NAME).mak
+-include m-$(MACHINE_NAME).mak
+
+#
+# Include target OS specific definitions
+#
+include ../../build/os-$(OS_NAME).mak
+
+#
+# (Optionally) Include target OS specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include os-$(OS_NAME).mak
+
+#
+# Include host specific definitions
+#
+include ../../build/host-$(HOST_NAME).mak
+
+#
+# (Optionally) Include host specific configuration that is
+# specific to this project. This configuration file is
+# located in this directory.
+#
+-include host-$(HOST_NAME).mak
+
+#
+# Include global user configuration, if any
+#
+-include ../../user.mak
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-mingw.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-mingw.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,13 @@
+export HOST_MV := mv
+export HOST_RM := rm -f @@
+export HOST_RMR := rm -rf @@
+export HOST_RMDIR := rm -rf @@
+export HOST_MKDIR := mkdir @@
+export HOST_EXE := .exe
+export HOST_PSEP := /
+
+export HOST_SOURCES :=
+export HOST_CFLAGS :=
+export HOST_CXXFLAGS :=
+export HOST_LDFLAGS := $(CC_LIB)stdc++$(LIBEXT2)
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-unix.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-unix.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,13 @@
+export HOST_MV := mv
+export HOST_RM := rm -f @@
+export HOST_RMR := rm -rf @@
+export HOST_RMDIR := rm -rf @@
+export HOST_MKDIR := mkdir -p @@
+export HOST_EXE := 
+export HOST_PSEP := /
+
+export HOST_SOURCES :=
+export HOST_CFLAGS :=
+export HOST_CXXFLAGS :=
+export HOST_LDFLAGS := 
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-win32.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/host-win32.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,12 @@
+export HOST_MV := ren
+export HOST_RM := if exist @@; del /F /Q @@
+export HOST_RMR := if exist @@; del /F /Q @@
+export HOST_RMDIR := if exist @@; rmdir @@
+export HOST_MKDIR := if not exist @@; mkdir @@
+export HOST_EXE := .exe
+export HOST_PSEP := \\
+
+export HOST_SOURCES :=
+export HOST_CFLAGS :=
+export HOST_CXXFLAGS :=
+export HOST_LDFLAGS :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-alpha.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-alpha.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,4 @@
+export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1
+export M_CXXFLAGS :=
+export M_LDFLAGS :=
+export M_SOURCES :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-i386.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-i386.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,4 @@
+export M_CFLAGS := $(CC_DEF)PJ_M_I386=1
+export M_CXXFLAGS :=
+export M_LDFLAGS :=
+export M_SOURCES :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-m68k.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-m68k.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,4 @@
+export M_CFLAGS := $(CC_DEF)PJ_M_M68K=1
+export M_CXXFLAGS :=
+export M_LDFLAGS :=
+export M_SOURCES :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-mpc860.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-mpc860.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,9 @@
+#
+# PowerPC MPC860 specific.
+# It's a PowerPC without floating point support.
+#
+export M_CFLAGS := $(CC_DEF)PJ_M_POWERPC=1 $(CC_DEF)PJ_HAS_FLOATING_POINT=0 -mcpu=860
+export M_CXXFLAGS :=
+export M_LDFLAGS := -mcpu=860
+export M_SOURCES :=
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-powerpc.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-powerpc.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,4 @@
+export M_CFLAGS := $(CC_DEF)PJ_M_POWERPC=1
+export M_CXXFLAGS :=
+export M_LDFLAGS :=
+export M_SOURCES :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-sparc.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/m-sparc.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,4 @@
+export M_CFLAGS := $(CC_DEF)PJ_M_SPARC=1
+export M_CXXFLAGS :=
+export M_LDFLAGS :=
+export M_SOURCES :=

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-darwinos.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-darwinos.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,9 @@
+export OS_CFLAGS   := $(CC_DEF)PJ_DARWINOS=1
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := $(CC_LIB)pthread$(LIBEXT2) -framework CoreAudio
+
+export OS_SOURCES  := 
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux-kernel.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux-kernel.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,43 @@
+
+include $(KERNEL_DIR)/.config
+
+#
+# Basic kernel compilation flags.
+#
+export OS_CFLAGS   := $(CC_DEF)PJ_LINUX_KERNEL=1 -D__KERNEL__  \
+		      -I$(KERNEL_DIR)/include -iwithprefix include \
+		      -nostdinc -msoft-float
+
+#
+# Additional kernel compilation flags are taken from the kernel Makefile
+# itself.
+#
+
+KERNEL_CFLAGS := \
+    $(shell cd $(KERNEL_DIR) ; \
+    make script SCRIPT='@echo $$(CFLAGS) $$(CFLAGS_MODULE)' $(KERNEL_ARCH))
+
+export OS_CFLAGS += $(KERNEL_CFLAGS)
+
+#		      -DMODULE -I$(KERNEL_DIR)/include  -nostdinc \
+#		      -Wstrict-prototypes \
+#		      -Wno-trigraphs -fno-strict-aliasing -fno-common \
+#		      -msoft-float -m32 -fno-builtin-sprintf -fno-builtin-log2\
+#		      -fno-builtin-puts -mpreferred-stack-boundary=2 \
+#		      -fno-unit-at-a-time -march=i686 -mregparm=3 \
+#		      -iwithprefix include
+
+#export OS_CFLAGS += -U__i386__ -Ui386 -D__arch_um__ -DSUBARCH=\"i386\" \
+#		    -D_LARGEFILE64_SOURCE -I$(KERNEL_DIR)/arch/um/include \
+#		    -Derrno=kernel_errno \
+#		    -I$(KERNEL_DIR)/arch/um/kernel/tt/include \
+#		    -I$(KERNEL_DIR)/arch/um/kernel/skas/include \
+		    
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  :=  
+
+export OS_SOURCES  := 
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-linux.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,9 @@
+export OS_CFLAGS   := $(CC_DEF)PJ_LINUX=1
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := $(CC_LIB)pthread$(LIBEXT2) 
+
+export OS_SOURCES  := 
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-palmos.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-palmos.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,32 @@
+#
+# make-mingw.inc: Mingw specific compilation switches.
+#
+PALM_OS_SDK_VER := 0x06000000
+PALM_OS_TARGET_HOST := TARGET_HOST_PALMOS
+PALM_OS_TARGET_PLATFORM := TARGET_PLATFORM_PALMSIM_WIN32
+PALM_OS_BUILD_TYPE := BUILD_TYPE_DEBUG
+PALM_OS_TRACE_OUTPUT := TRACE_OUTPUT_ON
+PALM_OS_CPU_TYPE := CPU_ARM
+
+export CROSS_COMPILE := 
+
+ifeq ($(CC_NAME),gcc)
+	export CFLAGS += -mno-cygwin -fexceptions -frtti
+endif
+
+export OS_CFLAGS   := 	$(CC_DEF)PJ_PALMOS=1 \
+			$(CC_DEF)__PALMOS_KERNEL__=1 \
+			$(CC_DEF)__PALMOS__=$(PALM_OS_SDK_VER) \
+			$(CC_DEF)BUILD_TYPE=$(PALM_OS_BUILD_TYPE) \
+			$(CC_DEF)TRACE_OUTPUT=$(PALM_OS_TRACE_OUTPUT) \
+			$(CC_DEF)_SUPPORTS_NAMESPACE=0 \
+			$(CC_DEF)_SUPPORTS_RTTI=0 \
+			$(CC_DEF)TARGET_HOST=$(PALM_OS_TRAGET_HOST) \
+			$(CC_DEF)TARGET_PLATFORM=$(PALM_OS_TARGET_PLATFORM)
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := 
+
+export OS_SOURCES := 
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-rtems.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-rtems.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,17 @@
+#
+# Global OS specific configurations for RTEMS OS.
+#
+# Thanks Zetron, Inc and Phil Torre <ptorre at zetron.com> for donating PJLIB
+# port to RTEMS.
+#
+export RTEMS_DEBUG := -ggdb3 -DRTEMS_DEBUG -DDEBUG -qrtems_debug 
+
+export OS_CFLAGS   := $(CC_DEF)PJ_RTEMS=1 \
+	-B$(RTEMS_LIBRARY_PATH)/lib/ -specs bsp_specs -qrtems
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := -B$(RTEMS_LIBRARY_PATH)/lib/ -specs bsp_specs -qrtems 
+
+export OS_SOURCES  := 
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-sunos.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-sunos.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,12 @@
+export OS_CFLAGS   := $(CC_DEF)PJ_SUNOS=1
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := $(CC_LIB)pthread$(LIBEXT2) \
+		      $(CC_LIB)socket$(LIBEXT2) \
+		      $(CC_LIB)rt$(LIBEXT2) \
+		      $(CC_LIB)nsl$(LIBEXT2)
+
+export OS_SOURCES  := 
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-win32.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/os-win32.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,11 @@
+export OS_CFLAGS   := $(CC_DEF)PJ_WIN32=1
+
+export OS_CXXFLAGS := 
+
+export OS_LDFLAGS  := $(CC_LIB)wsock32$(LIBEXT2) \
+		      $(CC_LIB)ws2_32$(LIBEXT2)\
+		      $(CC_LIB)ole32$(LIBEXT2)
+
+export OS_SOURCES  := 
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/rules.mak
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/build/rules.mak	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,170 @@
+LIBDIR = ../lib
+BINDIR = ../bin
+
+#
+# The full path of output lib file (e.g. ../lib/libapp.a).
+#
+LIB = $($(APP)_LIB)
+
+#
+# The full path of output executable file (e.g. ../bin/app.exe).
+#
+EXE = $($(APP)_EXE)
+
+#
+# Source directory
+#
+SRCDIR = $($(APP)_SRCDIR)
+
+#
+# Output directory for object files (i.e. output/target)
+#
+OBJDIR = output/$(app)-$(TARGET_NAME)
+
+ifeq ($(OS_NAME),linux-kernel)
+export $(APP)_CFLAGS += -DKBUILD_MODNAME=$(app) -DKBUILD_BASENAME=$(app)
+endif
+
+
+#
+# OBJS is ./output/target/file.o
+#
+OBJS = $(foreach file, $($(APP)_OBJS), $(OBJDIR)/$(file))
+OBJDIRS := $(sort $(dir $(OBJS)))
+
+#
+# FULL_SRCS is ../src/app/file1.c ../src/app/file1.S
+#
+FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S)
+
+#
+# When generating dependency (gcc -MM), ideally we use only either
+# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work.
+#
+DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS)
+
+# Dependency file
+DEP_FILE := .$(app)-$(TARGET_NAME).depend
+
+
+print_common:
+	@echo "###"
+	@echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):"
+	@echo "###"
+	@echo APP=$(APP)
+	@echo OBJDIR=$(OBJDIR)
+	@echo OBJDIRS=$(OBJDIRS)
+	@echo OBJS=$(OBJS)
+	@echo SRCDIR=$(SRCDIR)
+	@echo FULL_SRCS=$(FULL_SRCS)
+	@echo $(APP)_CFLAGS=$($(APP)_CFLAGS)
+	@echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS)
+	@echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS)
+	@echo DEPFLAGS=$(DEPFLAGS)
+
+print_bin: print_common
+	@echo EXE=$(EXE)
+	@echo BINDIR=$(BINDIR)
+
+print_lib: print_common
+	@echo LIB=$(LIB)
+	@echo LIBDIR=$(LIBDIR)
+
+$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(AR)$(LIB) $(OBJS)
+	$(RANLIB) $(LIB)
+
+$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP)
+	$(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$(EXE)) \
+	    $(subst /,$(HOST_PSEP),$(OBJS)) $($(APP)_LDFLAGS)
+
+$(OBJDIR)/$(app).o: $(OBJDIRS) $(OBJS)
+	$(CROSS_COMPILE)ld -r -o $@ $(OBJS)
+
+$(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o
+	@echo Creating kbuild Makefile...
+	@echo "# Our module name:" > $(OBJDIR)/Makefile
+	@echo 'obj-m += $(app).o' >> $(OBJDIR)/Makefile
+	@echo >> $(OBJDIR)/Makefile
+	@echo "# Object members:" >> $(OBJDIR)/Makefile
+	@echo -n '$(app)-objs += ' >> $(OBJDIR)/Makefile
+	@for file in $($(APP)_OBJS); do \
+		echo -n "$$file " >> $(OBJDIR)/Makefile; \
+	done
+	@echo >> $(OBJDIR)/Makefile
+	@echo >> $(OBJDIR)/Makefile
+	@echo "# Prevent .o files to be built by kbuild:" >> $(OBJDIR)/Makefile
+	@for file in $($(APP)_OBJS); do \
+		echo ".PHONY: `pwd`/$(OBJDIR)/$$file" >> $(OBJDIR)/Makefile; \
+	done
+	@echo >> $(OBJDIR)/Makefile
+	@echo all: >> $(OBJDIR)/Makefile
+	@echo -e "\tmake -C $(KERNEL_DIR) M=`pwd`/$(OBJDIR) modules $(KERNEL_ARCH)" >> $(OBJDIR)/Makefile
+	@echo Invoking kbuild...
+	make -C $(OBJDIR)
+
+../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko
+	cp $(OBJDIR)/$(app).ko ../lib
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c
+	$(CC) $($(APP)_CFLAGS) \
+		$(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
+		$(subst /,$(HOST_PSEP),$<) 
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S
+	$(CC) $($(APP)_CFLAGS) \
+		$(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
+		$(subst /,$(HOST_PSEP),$<) 
+
+$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp
+	$(CC) $($(APP)_CXXFLAGS) \
+		$(CC_OUT)$(subst /,$(HOST_PSEP),$@) \
+		$(subst /,$(HOST_PSEP),$<)
+
+$(OBJDIRS):
+	$(subst @@,$(subst /,$(HOST_PSEP),$@),$(HOST_MKDIR)) 
+
+$(LIBDIR):
+	$(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR))
+
+$(BINDIR):
+	$(subst @@,$(subst /,$(HOST_PSEP),$(BINDIR)),$(HOST_MKDIR))
+
+clean:
+	$(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)/*),$(HOST_RMR))
+	$(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)),$(HOST_RMDIR))
+ifeq ($(OS_NAME),linux-kernel)
+	rm -f ../lib/$(app).o
+endif
+
+gcov-report:
+	for file in $(FULL_SRCS); do \
+		gcov $$file -n -o $(OBJDIR); \
+	done
+
+realclean: clean
+	$(subst @@,$(subst /,$(HOST_PSEP),$(LIB)) $(subst /,$(HOST_PSEP),$(EXE)),$(HOST_RM))
+	$(subst @@,$(DEP_FILE),$(HOST_RM))
+ifeq ($(OS_NAME),linux-kernel)
+	rm -f ../lib/$(app).ko
+endif
+
+depend:
+	$(subst @@,$(DEP_FILE),$(HOST_RM))
+	for F in $(FULL_SRCS); do \
+	   if test -f $$F; then \
+	     echo "$(OBJDIR)/" | tr -d '\n' >> $(DEP_FILE); \
+	     if $(CC) -M $(DEPFLAGS) $$F | sed '/^#/d' >> $(DEP_FILE); then \
+		true; \
+	     else \
+		echo 'err:' >> $(DEP_FILE); \
+		rm -f $(DEP_FILE); \
+		exit 1; \
+	     fi; \
+	   fi; \
+	done;
+
+dep: depend
+
+-include $(DEP_FILE)
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/configure
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/configure	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# Detect machine, unless the choice has been made already.
+#
+if [ "$MACHINE" = "" ]; then
+	MACHINE=`uname -m`
+fi	
+
+if echo $MACHINE | grep sun4u > /dev/null; then
+    MACHINE_NAME=sparc
+elif echo $MACHINE | grep i.86 > /dev/null; then
+    MACHINE_NAME=i386
+elif echo $MACHINE | grep alpha > /dev/null; then
+    MACHINE_NAME=alpha
+elif echo $MACHINE | grep Mac > /dev/null; then
+    MACHINE_NAME=powerpc
+else
+    echo "Unable to detect processor type ('uname -m' == '$MACHINE')"
+    exit 1
+fi
+
+#
+# Detect OS and host, unless the choice has been made already
+#
+if [ "$SYSTEM" = "" ]; then
+    SYSTEM=`uname -s`
+fi	
+ 
+
+if echo $SYSTEM | grep -i sunos > /dev/null; then
+    OS_NAME=sunos
+    HOST_NAME=unix
+elif echo $SYSTEM | grep -i linux > /dev/null; then
+    OS_NAME=linux
+    HOST_NAME=unix
+    # More on linux version
+    KERNEL_VER=`uname -r`
+    if echo $KERNEL_VER | grep '^2\.4' > /dev/null; then
+	LINUX_POLL=select
+    elif echo $KERNEL_VER | grep '^2\.2' > /dev/null; then
+	LINUX_POLL=select
+    elif echo $KERNEL_VER | grep '^2\.0' > /dev/null; then
+	LINUX_EPOLL=select
+    else
+#	LINUX_POLL=epoll
+	LINUX_POLL=select
+    fi
+elif echo $SYSTEM | grep -i mingw > /dev/null; then
+    OS_NAME=win32
+    HOST_NAME=mingw
+elif echo $SYSTEM | grep -i cygwin > /dev/null; then
+    OS_NAME=win32
+    HOST_NAME=mingw
+elif echo $SYSTEM | grep -i darwin > /dev/null; then
+    OS_NAME=darwinos
+    HOST_NAME=unix
+elif echo $SYSTEM | grep -i rtems > /dev/null; then
+    OS_NAME=rtems
+    HOST_NAME=unix
+else
+    echo "Unable to detect system type ('uname -s' == '$SYSTEM')"
+    exit 1
+fi
+
+#
+# Detect gcc, unless it has been chosen already
+#
+if [ "$CC_NAME" = "" ]; then
+	if gcc --version 2>&1 > /dev/null; then
+		CC_NAME=gcc
+	else
+		echo "Unable to find gcc"
+		exit 1
+	fi
+fi	
+ 
+
+#
+# Specify TARGET_NAME, if not already choosen.
+#
+if [ "$TARGET_NAME" = "" ]; then
+   TARGET_NAME=$MACHINE_NAME-$OS_NAME-$CC_NAME
+fi
+
+
+if test -f build.mak; then
+  echo 'Saving build.mak --> build.mak.old'
+  cp -f build.mak build.mak.old
+fi
+
+echo 'Writing build.mak as follows:'
+echo " MACHINE_NAME = $MACHINE_NAME"
+echo " OS_NAME      = $OS_NAME"
+echo " HOST_NAME    = $HOST_NAME"
+echo " CC_NAME      = $CC_NAME"
+echo " TARGET_NAME  = $TARGET_NAME"
+echo " CROSS_COMPILE= $CROSS_COMPILE"
+echo " LINUX_POLL   = $LINUX_POLL"
+
+echo "# Auto-generated build.mak" > build.mak
+echo "export MACHINE_NAME := $MACHINE_NAME" >> build.mak
+echo "export OS_NAME := $OS_NAME" >> build.mak
+echo "export HOST_NAME := $HOST_NAME" >> build.mak
+echo "export CC_NAME := $CC_NAME" >> build.mak
+echo "export TARGET_NAME := $TARGET_NAME" >> build.mak
+echo "export CROSS_COMPILE := $CROSS_COMPILE" >> build.mak
+echo "export LINUX_POLL := $LINUX_POLL" >> build.mak
+
+touch user.mak
+
+echo
+echo "The configuration for current host has been written to 'build.mak'."
+echo "Customizations can be put in:"
+echo "  - 'user.mak'"
+echo "  - 'pjlib/include/pj/config_site.h'"
+echo
+echo "Next, run 'make dep && make clean && make'"
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/Makefile
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/build/Makefile	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,74 @@
+include ../../build/common.mak
+
+RULES_MAK := ../../build/rules.mak
+
+PJLIB_LIB:=/usr/lib/libpj-$(TARGET_NAME)$(LIBEXT)
+PJLIB_UTIL_LIB:=/usr/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT)
+
+export PJSDP_LIB:=../lib/libpjsdp-$(TARGET_NAME)$(LIBEXT)
+
+###############################################################################
+# Gather all flags.
+#
+export _CFLAGS 	:= $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \
+		   $(CFLAGS) $(CC_INC)../include $(CC_INC)../../pjlib/include \
+		   $(CC_INC)../../pjlib-util/include
+export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \
+		   $(HOST_CXXFLAGS) $(CXXFLAGS)
+export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJSDP_LIB)) \
+		   $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \
+		   $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \
+		   $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \
+		   $(LDFLAGS) 
+
+###############################################################################
+# Defines for building PJMEDIA library
+#
+export PJSDP_SRCDIR = ../src
+export PJSDP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+			errno.o sdp.o sdp_cmp.o sdp_neg.o
+
+export PJSDP_CFLAGS += $(_CFLAGS)
+	
+export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+###############################################################################
+# Main entry
+#
+# $(TARGET) is defined in os-$(OS_NAME).mak file in current directory.
+#
+TARGETS := pjsdp
+
+all: $(TARGETS)
+
+doc:
+	cd .. && \
+	if test -f docs/doxygen.cfg; then \
+		doxygen docs/doxygen.cfg; \
+	fi
+
+dep: depend
+distclean: realclean
+
+.PHONY: dep depend pjsdp clean realclean distclean
+
+pjsdp:
+	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(PJSDP_LIB)
+
+.PHONY: ../lib/pjsdp.ko
+../lib/pjsdp.ko:
+	echo Making $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
+
+clean:
+	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
+
+realclean:
+	$(subst @@,$(subst /,$(HOST_PSEP),.pjsdp-$(TARGET_NAME).depend),$(HOST_RMR))
+	
+	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
+
+depend:
+	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
+	echo '$(PJSDP_LIB): .pjsdp-$(TARGET_NAME).depend' >> .pjsdp-$(TARGET_NAME).depend; \
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,33 @@
+/* $Id: pjmedia.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJSDP_H__
+#define __PJSDP_H__
+
+/**
+ * @file pjsdp.h
+ * @brief PJSDP main header file.
+ */
+
+#include <pjsdp/types.h>
+#include <pjsdp/errno.h>
+#include <pjsdp/sdp.h>
+#include <pjsdp/sdp_neg.h>
+
+#endif	/* __PJSDP_H__ */
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/config.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/config.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,72 @@
+/* $Id: config.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJMEDIA_CONFIG_H__
+#define __PJMEDIA_CONFIG_H__
+
+/**
+ * @file pjmedia/config.h Compile time config
+ * @brief Contains some compile time constants.
+ */
+#include <pj/config.h>
+
+/**
+ * @defgroup PJMEDIA_BASE Base Types and Configurations
+ * @ingroup PJMEDIA
+ */
+
+/**
+ * @defgroup PJMEDIA_CONFIG Compile time configuration
+ * @ingroup PJMEDIA_BASE
+ * @brief Some compile time configuration settings.
+ * @{
+ */
+
+/**
+ * Maximum frame duration (in msec) to be supported.
+ * This (among other thing) will affect the size of buffers to be allocated
+ * for outgoing packets.
+ */
+#ifndef PJMEDIA_MAX_FRAME_DURATION_MS   
+#   define PJMEDIA_MAX_FRAME_DURATION_MS   	200
+#endif
+
+
+/**
+ * Max packet size to support.
+ */
+#ifndef PJMEDIA_MAX_MTU			
+#  define PJMEDIA_MAX_MTU			1500
+#endif
+
+
+/**
+ * DTMF/telephone-event duration, in timestamp.
+ */
+#ifndef PJMEDIA_DTMF_DURATION		
+#  define PJMEDIA_DTMF_DURATION			1600	/* in timestamp */
+#endif
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_CONFIG_H__ */
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/errno.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/errno.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,503 @@
+/* $Id: errno.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJMEDIA_ERRNO_H__
+#define __PJMEDIA_ERRNO_H__
+
+/**
+ * @file errno.h Error Codes
+ * @brief PJMEDIA specific error codes.
+ */
+
+#include <pjsdp/types.h>
+#include <pj/errno.h>
+
+/**
+ * @defgroup PJMEDIA_ERRNO Error Codes
+ * @ingroup PJMEDIA_BASE
+ * @brief PJMEDIA specific error codes.
+ * @{
+ */
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Start of error code relative to PJ_ERRNO_START_USER.
+ */
+#define PJMEDIA_ERRNO_START       (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE)
+
+
+/**
+ * Mapping from PortAudio error codes to pjmedia error space.
+ */
+#define PJMEDIA_PORTAUDIO_ERRNO_START (PJMEDIA_ERRNO_START+PJ_ERRNO_SPACE_SIZE-1000)
+
+/**
+ * Convert PortAudio error code to PJMEDIA error code.
+ */
+#define PJMEDIA_ERRNO_FROM_PORTAUDIO(err)   (err+PJMEDIA_PORTAUDIO_ERRNO_START)
+
+
+/************************************************************
+ * GENERIC/GENERAL PJMEDIA ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * General/unknown PJMEDIA error.
+ */
+#define PJMEDIA_ERROR		    (PJMEDIA_ERRNO_START+1)	/* 220001 */
+
+
+/************************************************************
+ * SDP ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Generic invalid SDP descriptor.
+ */
+#define PJMEDIA_SDP_EINSDP	    (PJMEDIA_ERRNO_START+20)    /* 220020 */
+/**
+ * @hideinitializer
+ * Invalid SDP version.
+ */
+#define PJMEDIA_SDP_EINVER	    (PJMEDIA_ERRNO_START+21)    /* 220021 */
+/**
+ * @hideinitializer
+ * Invalid SDP origin (o=) line.
+ */
+#define PJMEDIA_SDP_EINORIGIN	    (PJMEDIA_ERRNO_START+22)    /* 220022 */
+/**
+ * @hideinitializer
+ * Invalid SDP time (t=) line.
+ */
+#define PJMEDIA_SDP_EINTIME	    (PJMEDIA_ERRNO_START+23)    /* 220023 */
+/**
+ * @hideinitializer
+ * Empty SDP subject/name (s=) line.
+ */
+#define PJMEDIA_SDP_EINNAME	    (PJMEDIA_ERRNO_START+24)    /* 220024 */
+/**
+ * @hideinitializer
+ * Invalid SDP connection info (c=) line.
+ */
+#define PJMEDIA_SDP_EINCONN	    (PJMEDIA_ERRNO_START+25)    /* 220025 */
+/**
+ * @hideinitializer
+ * Missing SDP connection info line.
+ */
+#define PJMEDIA_SDP_EMISSINGCONN    (PJMEDIA_ERRNO_START+26)    /* 220026 */
+/**
+ * @hideinitializer
+ * Invalid attribute (a=) line.
+ */
+#define PJMEDIA_SDP_EINATTR	    (PJMEDIA_ERRNO_START+27)    /* 220027 */
+/**
+ * @hideinitializer
+ * Invalid rtpmap attribute.
+ */
+#define PJMEDIA_SDP_EINRTPMAP	    (PJMEDIA_ERRNO_START+28)    /* 220028 */
+/**
+ * @hideinitializer
+ * rtpmap attribute is too long.
+ */
+#define PJMEDIA_SDP_ERTPMAPTOOLONG  (PJMEDIA_ERRNO_START+29)    /* 220029 */
+/**
+ * @hideinitializer
+ * rtpmap is missing for dynamic payload type.
+ */
+#define PJMEDIA_SDP_EMISSINGRTPMAP  (PJMEDIA_ERRNO_START+30)    /* 220030 */
+/**
+ * @hideinitializer
+ * Invalid SDP media (m=) line.
+ */
+#define PJMEDIA_SDP_EINMEDIA	    (PJMEDIA_ERRNO_START+31)    /* 220031 */
+/**
+ * @hideinitializer
+ * No payload format in the media stream.
+ */
+#define PJMEDIA_SDP_ENOFMT	    (PJMEDIA_ERRNO_START+32)    /* 220032 */
+/**
+ * @hideinitializer
+ * Invalid payload type in media.
+ */
+#define PJMEDIA_SDP_EINPT	    (PJMEDIA_ERRNO_START+33)    /* 220033 */
+/**
+ * @hideinitializer
+ * Invalid fmtp attribute.
+ */
+#define PJMEDIA_SDP_EINFMTP	    (PJMEDIA_ERRNO_START+34)    /* 220034 */
+
+
+/************************************************************
+ * SDP NEGOTIATOR ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Invalid state to perform the specified operation.
+ */
+#define PJMEDIA_SDPNEG_EINSTATE	    (PJMEDIA_ERRNO_START+40)    /* 220040 */
+/**
+ * @hideinitializer
+ * No initial local SDP.
+ */
+#define PJMEDIA_SDPNEG_ENOINITIAL   (PJMEDIA_ERRNO_START+41)    /* 220041 */
+/**
+ * @hideinitializer
+ * No currently active SDP.
+ */
+#define PJMEDIA_SDPNEG_ENOACTIVE    (PJMEDIA_ERRNO_START+42)    /* 220042 */
+/**
+ * @hideinitializer
+ * No current offer or answer.
+ */
+#define PJMEDIA_SDPNEG_ENONEG	    (PJMEDIA_ERRNO_START+43)    /* 220043 */
+/**
+ * @hideinitializer
+ * Media count mismatch in offer and answer.
+ */
+#define PJMEDIA_SDPNEG_EMISMEDIA    (PJMEDIA_ERRNO_START+44)    /* 220044 */
+/**
+ * @hideinitializer
+ * Media type is different in the remote answer.
+ */
+#define PJMEDIA_SDPNEG_EINVANSMEDIA (PJMEDIA_ERRNO_START+45)    /* 220045 */
+/**
+ * @hideinitializer
+ * Transport type is different in the remote answer.
+ */
+#define PJMEDIA_SDPNEG_EINVANSTP    (PJMEDIA_ERRNO_START+46)    /* 220046 */
+/**
+ * @hideinitializer
+ * No common media payload is provided in the answer.
+ */
+#define PJMEDIA_SDPNEG_EANSNOMEDIA  (PJMEDIA_ERRNO_START+47)    /* 220047 */
+/**
+ * @hideinitializer
+ * No media is active after negotiation.
+ */
+#define PJMEDIA_SDPNEG_ENOMEDIA	    (PJMEDIA_ERRNO_START+48)    /* 220048 */
+
+
+/************************************************************
+ * SDP COMPARISON STATUS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * SDP media stream not equal.
+ */
+#define PJMEDIA_SDP_EMEDIANOTEQUAL  (PJMEDIA_ERRNO_START+60)    /* 220060 */
+/**
+ * @hideinitializer
+ * Port number in SDP media descriptor not equal.
+ */
+#define PJMEDIA_SDP_EPORTNOTEQUAL   (PJMEDIA_ERRNO_START+61)    /* 220061 */
+/**
+ * @hideinitializer
+ * Transport in SDP media descriptor not equal.
+ */
+#define PJMEDIA_SDP_ETPORTNOTEQUAL  (PJMEDIA_ERRNO_START+62)    /* 220062 */
+/**
+ * @hideinitializer
+ * Media format in SDP media descriptor not equal.
+ */
+#define PJMEDIA_SDP_EFORMATNOTEQUAL (PJMEDIA_ERRNO_START+63)    /* 220063 */
+/**
+ * @hideinitializer
+ * SDP connection description not equal.
+ */
+#define PJMEDIA_SDP_ECONNNOTEQUAL   (PJMEDIA_ERRNO_START+64)    /* 220064 */
+/**
+ * @hideinitializer
+ * SDP attributes not equal.
+ */
+#define PJMEDIA_SDP_EATTRNOTEQUAL   (PJMEDIA_ERRNO_START+65)    /* 220065 */
+/**
+ * @hideinitializer
+ * SDP media direction not equal.
+ */
+#define PJMEDIA_SDP_EDIRNOTEQUAL    (PJMEDIA_ERRNO_START+66)    /* 220066 */
+/**
+ * @hideinitializer
+ * SDP fmtp attribute not equal.
+ */
+#define PJMEDIA_SDP_EFMTPNOTEQUAL   (PJMEDIA_ERRNO_START+67)    /* 220067 */
+/**
+ * @hideinitializer
+ * SDP ftpmap attribute not equal.
+ */
+#define PJMEDIA_SDP_ERTPMAPNOTEQUAL (PJMEDIA_ERRNO_START+68)    /* 220068 */
+/**
+ * @hideinitializer
+ * SDP session descriptor not equal.
+ */
+#define PJMEDIA_SDP_ESESSNOTEQUAL   (PJMEDIA_ERRNO_START+69)    /* 220069 */
+/**
+ * @hideinitializer
+ * SDP origin not equal.
+ */
+#define PJMEDIA_SDP_EORIGINNOTEQUAL (PJMEDIA_ERRNO_START+70)    /* 220070 */
+/**
+ * @hideinitializer
+ * SDP name/subject not equal.
+ */
+#define PJMEDIA_SDP_ENAMENOTEQUAL   (PJMEDIA_ERRNO_START+71)    /* 220071 */
+/**
+ * @hideinitializer
+ * SDP time not equal.
+ */
+#define PJMEDIA_SDP_ETIMENOTEQUAL   (PJMEDIA_ERRNO_START+72)    /* 220072 */
+
+
+/************************************************************
+ * CODEC
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Unsupported codec.
+ */
+#define PJMEDIA_CODEC_EUNSUP	    (PJMEDIA_ERRNO_START+80)    /* 220080 */
+/**
+ * @hideinitializer
+ * Codec internal creation error.
+ */
+#define PJMEDIA_CODEC_EFAILED	    (PJMEDIA_ERRNO_START+81)    /* 220081 */
+/**
+ * @hideinitializer
+ * Codec frame is too short.
+ */
+#define PJMEDIA_CODEC_EFRMTOOSHORT  (PJMEDIA_ERRNO_START+82)    /* 220082 */
+/**
+ * @hideinitializer
+ * PCM buffer is too short.
+ */
+#define PJMEDIA_CODEC_EPCMTOOSHORT  (PJMEDIA_ERRNO_START+83)    /* 220083 */
+/**
+ * @hideinitializer
+ * Invalid codec frame length.
+ */
+#define PJMEDIA_CODEC_EFRMINLEN	    (PJMEDIA_ERRNO_START+84)    /* 220084 */
+
+
+/************************************************************
+ * MEDIA
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Invalid remote IP address (in SDP).
+ */
+#define PJMEDIA_EINVALIDIP	    (PJMEDIA_ERRNO_START+100)    /* 220100 */
+/**
+ * @hideinitializer
+ * Asymetric codec is not supported.
+ */
+#define PJMEDIA_EASYMCODEC	    (PJMEDIA_ERRNO_START+101)    /* 220101 */
+/**
+ * @hideinitializer
+ * Invalid payload type.
+ */
+#define PJMEDIA_EINVALIDPT	    (PJMEDIA_ERRNO_START+102)    /* 220102 */
+/**
+ * @hideinitializer
+ * Missing rtpmap.
+ */
+#define PJMEDIA_EMISSINGRTPMAP	    (PJMEDIA_ERRNO_START+103)    /* 220103 */
+/**
+ * @hideinitializer
+ * Invalid media type.
+ */
+#define PJMEDIA_EINVALIMEDIATYPE    (PJMEDIA_ERRNO_START+104)    /* 220104 */
+/**
+ * @hideinitializer
+ * Remote does not support DTMF.
+ */
+#define PJMEDIA_EREMOTENODTMF	    (PJMEDIA_ERRNO_START+105)    /* 220105 */
+/**
+ * @hideinitializer
+ * Invalid DTMF digit.
+ */
+#define PJMEDIA_RTP_EINDTMF	    (PJMEDIA_ERRNO_START+106)    /* 220106 */
+/**
+ * @hideinitializer
+ * Remote does not support RFC 2833
+ */
+#define PJMEDIA_RTP_EREMNORFC2833   (PJMEDIA_ERRNO_START+107)    /* 220107 */
+
+
+
+/************************************************************
+ * RTP SESSION ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * General invalid RTP packet error.
+ */
+#define PJMEDIA_RTP_EINPKT	    (PJMEDIA_ERRNO_START+120)    /* 220120 */
+/**
+ * @hideinitializer
+ * Invalid RTP packet packing.
+ */
+#define PJMEDIA_RTP_EINPACK	    (PJMEDIA_ERRNO_START+121)    /* 220121 */
+/**
+ * @hideinitializer
+ * Invalid RTP packet version.
+ */
+#define PJMEDIA_RTP_EINVER	    (PJMEDIA_ERRNO_START+122)    /* 220122 */
+/**
+ * @hideinitializer
+ * RTP SSRC id mismatch.
+ */
+#define PJMEDIA_RTP_EINSSRC	    (PJMEDIA_ERRNO_START+123)    /* 220123 */
+/**
+ * @hideinitializer
+ * RTP payload type mismatch.
+ */
+#define PJMEDIA_RTP_EINPT	    (PJMEDIA_ERRNO_START+124)    /* 220124 */
+/**
+ * @hideinitializer
+ * Invalid RTP packet length.
+ */
+#define PJMEDIA_RTP_EINLEN	    (PJMEDIA_ERRNO_START+125)    /* 220125 */
+/**
+ * @hideinitializer
+ * RTP session restarted.
+ */
+#define PJMEDIA_RTP_ESESSRESTART    (PJMEDIA_ERRNO_START+130)    /* 220130 */
+/**
+ * @hideinitializer
+ * RTP session in probation
+ */
+#define PJMEDIA_RTP_ESESSPROBATION  (PJMEDIA_ERRNO_START+131)    /* 220131 */
+/**
+ * @hideinitializer
+ * Bad RTP sequence number
+ */
+#define PJMEDIA_RTP_EBADSEQ	    (PJMEDIA_ERRNO_START+132)    /* 220132 */
+/**
+ * @hideinitializer
+ * RTP media port destination is not configured
+ */
+#define PJMEDIA_RTP_EBADDEST	    (PJMEDIA_ERRNO_START+133)    /* 220133 */
+/**
+ * @hideinitializer
+ * RTP is not configured.
+ */
+#define PJMEDIA_RTP_ENOCONFIG	    (PJMEDIA_ERRNO_START+134)    /* 220134 */
+
+
+/************************************************************
+ * PORT ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Generic incompatible port error.
+ */
+#define PJMEDIA_ENOTCOMPATIBLE	    (PJMEDIA_ERRNO_START+160)    /* 220160 */
+/**
+ * @hideinitializer
+ * Incompatible clock rate
+ */
+#define PJMEDIA_ENCCLOCKRATE	    (PJMEDIA_ERRNO_START+161)    /* 220161 */
+/**
+ * @hideinitializer
+ * Incompatible samples per frame
+ */
+#define PJMEDIA_ENCSAMPLESPFRAME    (PJMEDIA_ERRNO_START+162)    /* 220162 */
+/**
+ * @hideinitializer
+ * Incompatible media type
+ */
+#define PJMEDIA_ENCTYPE		    (PJMEDIA_ERRNO_START+163)    /* 220163 */
+/**
+ * @hideinitializer
+ * Incompatible bits per sample
+ */
+#define PJMEDIA_ENCBITS		    (PJMEDIA_ERRNO_START+164)    /* 220164 */
+/**
+ * @hideinitializer
+ * Incompatible bytes per frame
+ */
+#define PJMEDIA_ENCBYTES	    (PJMEDIA_ERRNO_START+165)    /* 220165 */
+/**
+ * @hideinitializer
+ * Incompatible number of channels
+ */
+#define PJMEDIA_ENCCHANNEL	    (PJMEDIA_ERRNO_START+166)    /* 220166 */
+
+
+/************************************************************
+ * FILE ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Not a valid WAVE file.
+ */
+#define PJMEDIA_ENOTVALIDWAVE	    (PJMEDIA_ERRNO_START+180)    /* 220180 */
+/**
+ * @hideinitializer
+ * Unsupported WAVE file.
+ */
+#define PJMEDIA_EWAVEUNSUPP	    (PJMEDIA_ERRNO_START+181)    /* 220181 */
+/**
+ * @hideinitializer
+ * Wave file too short.
+ */
+#define PJMEDIA_EWAVETOOSHORT	    (PJMEDIA_ERRNO_START+182)    /* 220182 */
+/**
+ * @hideinitializer
+ * Sound frame is too large for file buffer.
+ */
+#define PJMEDIA_EFRMFILETOOBIG	    (PJMEDIA_ERRNO_START+183)    /* 220183 */
+
+
+/************************************************************
+ * SOUND DEVICE ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * No suitable audio capture device.
+ */
+#define PJMEDIA_ENOSNDREC	    (PJMEDIA_ERRNO_START+200)    /* 220200 */
+/**
+ * @hideinitializer
+ * No suitable audio playback device.
+ */
+#define PJMEDIA_ENOSNDPLAY	    (PJMEDIA_ERRNO_START+201)    /* 220201 */
+/**
+ * @hideinitializer
+ * Invalid sound device ID.
+ */
+#define PJMEDIA_ESNDINDEVID	    (PJMEDIA_ERRNO_START+202)    /* 220202 */
+/**
+ * @hideinitializer
+ * Invalid sample format for sound device.
+ */
+#define PJMEDIA_ESNDINSAMPLEFMT	    (PJMEDIA_ERRNO_START+203)    /* 220203 */
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_ERRNO_H__ */
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,599 @@
+/* $Id: sdp.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJMEDIA_SDP_H__
+#define __PJMEDIA_SDP_H__
+
+/**
+ * @file sdp.h
+ * @brief SDP header file.
+ */
+#include <pjsdp/types.h>
+
+
+/**
+ * @defgroup PJMEDIA_SDP SDP Parsing and Data Structure
+ * @ingroup PJMEDIA_SESSION
+ * @{
+ *
+ * The basic SDP session descriptor and elements are described in header
+ * file <b><pjsdp/sdp.h></b>. This file contains declaration for
+ * SDP session descriptor and SDP media descriptor, along with their
+ * attributes. This file also declares functions to parse SDP message.
+ */
+
+
+PJ_BEGIN_DECL
+
+/**
+ * The PJMEDIA_MAX_SDP_FMT macro defines maximum format in a media line.
+ */
+#ifndef PJMEDIA_MAX_SDP_FMT
+#   define PJMEDIA_MAX_SDP_FMT		32
+#endif
+
+/**
+ * The PJMEDIA_MAX_SDP_ATTR macro defines maximum SDP attributes in media and
+ * session descriptor.
+ */
+#ifndef PJMEDIA_MAX_SDP_ATTR
+#   define PJMEDIA_MAX_SDP_ATTR		(PJMEDIA_MAX_SDP_FMT*2 + 4)
+#endif
+
+/**
+ * The PJMEDIA_MAX_SDP_MEDIA macro defines maximum SDP media lines in a
+ * SDP session descriptor.
+ */
+#ifndef PJMEDIA_MAX_SDP_MEDIA
+#   define PJMEDIA_MAX_SDP_MEDIA	16
+#endif
+
+
+/* **************************************************************************
+ * SDP ATTRIBUTES
+ ***************************************************************************
+ */
+
+/** 
+ * Generic representation of attribute.
+ */
+struct pjmedia_sdp_attr
+{
+    pj_str_t		name;	    /**< Attribute name.    */
+    pj_str_t		value;	    /**< Attribute value.   */
+};
+
+/**
+ * @see pjmedia_sdp_attr
+ */
+typedef struct pjmedia_sdp_attr pjmedia_sdp_attr;
+
+
+/**
+ * Create SDP attribute.
+ *
+ * @param pool		Pool to create the attribute.
+ * @param name		Attribute name.
+ * @param value		Optional attribute value.
+ *
+ * @return		The new SDP attribute.
+ */
+PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_create(pj_pool_t *pool,
+						   const char *name,
+						   const pj_str_t *value);
+
+/** 
+ * Clone attribute 
+ *
+ * @param pool		Pool to be used.
+ * @param attr		The attribute to clone.
+ *
+ * @return		New attribute as cloned from the attribute.
+ */
+PJ_DECL(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool, 
+						  const pjmedia_sdp_attr*attr);
+
+/** 
+ * Find the first attribute with the specified type.
+ *
+ * @param count		Number of attributes in the array.
+ * @param attr_array	Array of attributes.
+ * @param name		Attribute name to find.
+ * @param fmt		Optional string to indicate which payload format
+ *			to find for \a rtpmap and \a fmt attributes. For other
+ *			types of attributes, the value should be NULL.
+ *
+ * @return		The specified attribute, or NULL if it can't be found.
+ *
+ * @see pjmedia_sdp_attr_find2, pjmedia_sdp_media_find_attr, 
+ *	pjmedia_sdp_media_find_attr2
+ */
+PJ_DECL(pjmedia_sdp_attr*) 
+pjmedia_sdp_attr_find(unsigned count, 
+		      pjmedia_sdp_attr *const attr_array[],
+		      const pj_str_t *name, const pj_str_t *fmt);
+
+/** 
+ * Find the first attribute with the specified type.
+ *
+ * @param count		Number of attributes in the array.
+ * @param attr_array	Array of attributes.
+ * @param name		Attribute name to find.
+ * @param fmt		Optional string to indicate which payload format
+ *			to find for \a rtpmap and \a fmt attributes. For other
+ *			types of attributes, the value should be NULL.
+ *
+ * @return		The specified attribute, or NULL if it can't be found.
+ *
+ * @see pjmedia_sdp_attr_find, pjmedia_sdp_media_find_attr,
+ *	pjmedia_sdp_media_find_attr2
+ */
+PJ_DECL(pjmedia_sdp_attr*) 
+pjmedia_sdp_attr_find2(unsigned count, 
+		       pjmedia_sdp_attr *const attr_array[],
+		       const char *name, const pj_str_t *fmt);
+
+/**
+ * Add a new attribute to array of attributes.
+ *
+ * @param count		Number of attributes in the array.
+ * @param attr_array	Array of attributes.
+ * @param attr		The attribute to add.
+ *
+ * @return		PJ_SUCCESS or the error code.
+ *
+ * @see pjmedia_sdp_media_add_attr
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_add(unsigned *count,
+					  pjmedia_sdp_attr *attr_array[],
+					  pjmedia_sdp_attr *attr);
+
+/**
+ * Remove all attributes with the specified name in array of attributes.
+ *
+ * @param count		Number of attributes in the array.
+ * @param attr_array	Array of attributes.
+ * @param name		Attribute name to find.
+ *
+ * @return		Number of attributes removed.
+ *
+ * @see pjmedia_sdp_media_remove_all_attr
+ */
+PJ_DECL(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count,
+					      pjmedia_sdp_attr *attr_array[],
+					      const char *name);
+
+
+/**
+ * Remove the specified attribute from the attribute array.
+ *
+ * @param count		Number of attributes in the array.
+ * @param attr_array	Array of attributes.
+ * @param attr		The attribute instance to remove.
+ *
+ * @return		PJ_SUCCESS when attribute has been removed, or 
+ *			PJ_ENOTFOUND when the attribute can not be found.
+ *
+ * @see pjmedia_sdp_media_remove_attr
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_remove(unsigned *count,
+					     pjmedia_sdp_attr *attr_array[],
+					     pjmedia_sdp_attr *attr);
+
+
+/**
+ * This structure declares SDP \a rtpmap attribute.
+ */
+struct pjmedia_sdp_rtpmap
+{
+    pj_str_t		pt;	    /**< Payload type.	    */
+    pj_str_t		enc_name;   /**< Encoding name.	    */
+    unsigned		clock_rate; /**< Clock rate.	    */
+    pj_str_t		param;	    /**< Parameter.	    */
+};
+
+/**
+ * @see pjmedia_sdp_rtpmap
+ */
+typedef struct pjmedia_sdp_rtpmap pjmedia_sdp_rtpmap;
+
+
+/**
+ * Convert generic attribute to SDP \a rtpmap. This function allocates
+ * a new attribute and call #pjmedia_sdp_attr_get_rtpmap().
+ *
+ * @param pool		Pool used to create the rtpmap attribute.
+ * @param attr		Generic attribute to be converted to rtpmap, which
+ *			name must be "rtpmap".
+ * @param p_rtpmap	Pointer to receive SDP rtpmap attribute.
+ *
+ * @return		PJ_SUCCESS if the attribute can be successfully
+ *			converted to \a rtpmap type.
+ *
+ * @see pjmedia_sdp_attr_get_rtpmap
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
+						const pjmedia_sdp_attr *attr,
+						pjmedia_sdp_rtpmap **p_rtpmap);
+
+
+/**
+ * Get the rtpmap representation of the same SDP attribute.
+ *
+ * @param attr		Generic attribute to be converted to rtpmap, which
+ *			name must be "rtpmap".
+ * @param rtpmap	SDP \a rtpmap attribute to be initialized.
+ *
+ * @return		PJ_SUCCESS if the attribute can be successfully
+ *			converted to \a rtpmap attribute.
+ *
+ * @see pjmedia_sdp_attr_to_rtpmap
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_rtpmap(const pjmedia_sdp_attr *attr,
+						 pjmedia_sdp_rtpmap *rtpmap);
+
+
+/**
+ * Convert \a rtpmap attribute to generic attribute.
+ *
+ * @param pool		Pool to be used.
+ * @param rtpmap	The \a rtpmap attribute.
+ * @param p_attr	Pointer to receive the generic SDP attribute.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_rtpmap_to_attr( pj_pool_t *pool,
+			    const pjmedia_sdp_rtpmap *rtpmap,
+			    pjmedia_sdp_attr **p_attr);
+
+
+/**
+ * This structure describes SDP \a fmtp attribute.
+ */
+struct pjmedia_sdp_fmtp
+{
+    pj_str_t		fmt;	    /**< Format type.		    */
+    pj_str_t		fmt_param;  /**< Format specific parameter. */
+};
+
+
+/**
+ * @see pjmedia_sdp_fmtp
+ */
+typedef struct pjmedia_sdp_fmtp pjmedia_sdp_fmtp;
+
+
+
+/**
+ * Get the fmtp representation of the same SDP attribute.
+ *
+ * @param attr		Generic attribute to be converted to fmtp, which
+ *			name must be "fmtp".
+ * @param fmtp		SDP fmtp attribute to be initialized.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_fmtp(const pjmedia_sdp_attr *attr,
+					       pjmedia_sdp_fmtp *fmtp);
+
+
+/* **************************************************************************
+ * SDP CONNECTION INFO
+ ****************************************************************************
+ */
+
+/**
+ * This structure describes SDP connection info ("c=" line). 
+ */
+struct pjmedia_sdp_conn
+{
+    pj_str_t	net_type;	/**< Network type ("IN").		*/
+    pj_str_t	addr_type;	/**< Address type ("IP4", "IP6").	*/
+    pj_str_t	addr;		/**< The address.			*/
+};
+
+
+/**
+ * @see pjmedia_sdp_conn
+ */
+typedef struct pjmedia_sdp_conn pjmedia_sdp_conn;
+
+
+/** 
+ * Clone connection info. 
+ * 
+ * @param pool	    Pool to allocate memory for the new connection info.
+ * @param rhs	    The connection into to clone.
+ *
+ * @return	    The new connection info.
+ */
+PJ_DECL(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone(pj_pool_t *pool, 
+						  const pjmedia_sdp_conn *rhs);
+
+
+
+/* **************************************************************************
+ * SDP MEDIA INFO/LINE
+ ****************************************************************************
+ */
+
+/**
+ * This structure describes SDP media descriptor. A SDP media descriptor
+ * starts with "m=" line and contains the media attributes and optional
+ * connection line.
+ */
+struct pjmedia_sdp_media
+{
+    /** Media descriptor line ("m=" line) */
+    struct
+    {
+	pj_str_t    media;		/**< Media type ("audio", "video")  */
+	pj_uint16_t port;		/**< Port number.		    */
+	unsigned    port_count;		/**< Port count, used only when >2  */
+	pj_str_t    transport;		/**< Transport ("RTP/AVP")	    */
+	unsigned    fmt_count;		/**< Number of formats.		    */
+	pj_str_t    fmt[PJMEDIA_MAX_SDP_FMT];	/**< Media formats.	    */
+    } desc;
+
+    pjmedia_sdp_conn *conn;		/**< Optional connection info.	    */
+    unsigned	     attr_count;	/**< Number of attributes.	    */
+    pjmedia_sdp_attr*attr[PJMEDIA_MAX_SDP_ATTR];  /**< Attributes.	    */
+
+};
+
+
+/**
+ * @see pjmedia_sdp_media
+ */
+typedef struct pjmedia_sdp_media pjmedia_sdp_media;
+
+
+/** 
+ * Clone SDP media description. 
+ *
+ * @param pool	    Pool to allocate memory for the new media description.
+ * @param rhs	    The media descriptin to clone.
+ *
+ * @return	    New media description.
+ */
+PJ_DECL(pjmedia_sdp_media*) 
+pjmedia_sdp_media_clone( pj_pool_t *pool, 
+			 const pjmedia_sdp_media *rhs);
+
+/**
+ * Find the first occurence of the specified attribute name in the media 
+ * descriptor. Optionally the format may be specified.
+ *
+ * @param m		The SDP media description.
+ * @param name		Attribute name to find.
+ * @param fmt		Optional payload type to match in the
+ *			attribute list, when the attribute is \a rtpmap
+ *			or \a fmtp. For other types of SDP attributes, this
+ *			value should be NULL.
+ *
+ * @return		The first instance of the specified attribute or NULL.
+ */
+PJ_DECL(pjmedia_sdp_attr*) 
+pjmedia_sdp_media_find_attr(const pjmedia_sdp_media *m,
+			    const pj_str_t *name, const pj_str_t *fmt);
+
+
+/**
+ * Find the first occurence of the specified attribute name in the SDP media 
+ * descriptor. Optionally the format may be specified.
+ *
+ * @param m		The SDP media description.
+ * @param name		Attribute name to find.
+ * @param fmt		Optional payload type to match in the
+ *			attribute list, when the attribute is \a rtpmap
+ *			or \a fmtp. For other types of SDP attributes, this
+ *			value should be NULL.
+ *
+ * @return		The first instance of the specified attribute or NULL.
+ */
+PJ_DECL(pjmedia_sdp_attr*) 
+pjmedia_sdp_media_find_attr2(const pjmedia_sdp_media *m,
+			     const char *name, const pj_str_t *fmt);
+
+/**
+ * Add new attribute to the media descriptor.
+ *
+ * @param m		The SDP media description.
+ * @param attr		Attribute to add.
+ *
+ * @return		PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_media_add_attr(pjmedia_sdp_media *m,
+						pjmedia_sdp_attr *attr);
+
+/**
+ * Remove all attributes with the specified name from the SDP media
+ * descriptor.
+ *
+ * @param m		The SDP media description.
+ * @param name		Attribute name to remove.
+ *
+ * @return		The number of attributes removed.
+ */
+PJ_DECL(unsigned) 
+pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m,
+				  const char *name);
+
+
+/**
+ * Remove the occurence of the specified attribute from the SDP media
+ * descriptor.
+ *
+ * @param m		The SDP media descriptor.
+ * @param attr		The attribute to find and remove.
+ *
+ * @return		PJ_SUCCESS if the attribute can be found and has
+ *			been removed from the array.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m,
+			      pjmedia_sdp_attr *attr);
+
+
+/**
+ * Compare two SDP media for equality.
+ *
+ * @param sd1	    The first SDP media to compare.
+ * @param sd2	    The second SDP media to compare.
+ * @param option    Comparison option, which should be zero for now.
+ *
+ * @return	    PJ_SUCCESS when both SDP medias are equal, or the
+ *		    appropriate status code describing which part of
+ *		    the descriptors that are not equal.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_media_cmp(const pjmedia_sdp_media *sd1,
+					   const pjmedia_sdp_media *sd2,
+					   unsigned option);
+
+
+
+/* **************************************************************************
+ * SDP SESSION DESCRIPTION
+ ****************************************************************************
+ */
+
+
+/**
+ * This structure describes SDP session description. A SDP session descriptor
+ * contains complete information about a session, and normally is exchanged
+ * with remote media peer using signaling protocol such as SIP.
+ */
+struct pjmedia_sdp_session
+{
+    /** Session origin (o= line) */
+    struct
+    {
+	pj_str_t    user;	    /**< User 				*/
+	pj_uint32_t id;		    /**< Session ID			*/
+	pj_uint32_t version;	    /**< Session version		*/
+	pj_str_t    net_type;	    /**< Network type ("IN")		*/
+	pj_str_t    addr_type;	    /**< Address type ("IP4", "IP6")	*/
+	pj_str_t    addr;	    /**< The address.			*/
+    } origin;
+
+    pj_str_t	     name;	    /**< Subject line (s=)		*/
+    pjmedia_sdp_conn *conn;	    /**< Connection line (c=)		*/
+    
+    /** Session time (t= line)	*/
+    struct
+    {
+	pj_uint32_t start;	    /**< Start time.			*/
+	pj_uint32_t stop;	    /**< Stop time.			*/
+    } time;
+
+    unsigned	       attr_count;		/**< Number of attributes.  */
+    pjmedia_sdp_attr  *attr[PJMEDIA_MAX_SDP_ATTR]; /**< Attributes array.   */
+
+    unsigned	       media_count;		/**< Number of media.	    */
+    pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA];	/**< Media array.   */
+
+};
+
+/**
+ * @see pjmedia_sdp_session
+ */
+typedef struct pjmedia_sdp_session pjmedia_sdp_session;
+
+
+
+/**
+ * Parse SDP message.
+ *
+ * @param pool	    The pool to allocate SDP session description.
+ * @param buf	    The message buffer.
+ * @param len	    The length of the message.
+ * @param p_sdp	    Pointer to receive the SDP session descriptor.
+ *
+ * @return	    PJ_SUCCESS if message was successfully parsed into
+ *		    SDP session descriptor.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
+				        char *buf, pj_size_t len, 
+					pjmedia_sdp_session **p_sdp );
+
+/**
+ * Print SDP description to a buffer.
+ *
+ * @param sdp	    The SDP session description.
+ * @param buf	    The buffer.
+ * @param size	    The buffer length.
+ *
+ * @return	    the length printed, or -1 if the buffer is too
+ *		    short.
+ */
+PJ_DECL(int) pjmedia_sdp_print( const pjmedia_sdp_session *sdp, 
+				char *buf, pj_size_t size);
+
+
+/**
+ * Perform semantic validation for the specified SDP session descriptor.
+ * This function perform validation beyond just syntactic verification,
+ * such as to verify the value of network type and address type, check
+ * the connection line, and verify that \a rtpmap attribute is present
+ * when dynamic payload type is used.
+ *
+ * @param sdp	    The SDP session descriptor to validate.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp);
+
+
+/**
+ * Clone SDP session descriptor.
+ *
+ * @param pool	    The pool used to clone the session.
+ * @param sdp	    The SDP session to clone.
+ *
+ * @return	    New SDP session.
+ */
+PJ_DECL(pjmedia_sdp_session*) 
+pjmedia_sdp_session_clone( pj_pool_t *pool,
+			   const pjmedia_sdp_session *sdp);
+
+
+/**
+ * Compare two SDP session for equality.
+ *
+ * @param sd1	    The first SDP session to compare.
+ * @param sd2	    The second SDP session to compare.
+ * @param option    Must be zero for now.
+ *
+ * @return	    PJ_SUCCESS when both SDPs are equal, or otherwise
+ *		    the status code indicates which part of the session
+ *		    descriptors are not equal.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_session_cmp(const pjmedia_sdp_session *sd1,
+					     const pjmedia_sdp_session *sd2,
+					     unsigned option);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_SDP_H__ */
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp_neg.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/sdp_neg.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,613 @@
+/* $Id: sdp_neg.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJMEDIA_SDP_NEG_H__
+#define __PJMEDIA_SDP_NEG_H__
+
+
+/**
+ * @file sdp_neg.h
+ * @brief SDP negotiator header file.
+ */
+/**
+ * @defgroup PJMEDIA_SDP_NEG SDP Negotiator
+ * @ingroup PJMEDIA_SESSION
+ * @{
+ *
+ * The header file <b><pjsdp/sdp_neg.h></b> contains the declaration
+ * of SDP offer and answer negotiator. SDP offer and answer model is described
+ * in RFC 3264 <b>"An Offer/Answer Model with Session Description Protocol 
+ * (SDP)"</b>.
+ *
+ * The SDP negotiator is represented with opaque type \a pjmedia_sdp_neg.
+ * This structure contains negotiation state and several SDP session 
+ * descriptors currently being used in the negotiation.
+ *
+ *
+ * \section sdpneg_state_dia SDP Negotiator State Diagram
+ *
+ * The following diagram describes the state transition diagram of the
+ * SDP negotiator.
+ * 
+ * <pre>
+ *                                              
+ *                                              modify_local_offer()
+ *     create_w_local_offer()  +-------------+  send_local_offer()
+ *     ----------------------->| LOCAL_OFFER |<-----------------------
+ *    |                        +-------------+                        |
+ *    |                               |                               |
+ *    |           set_remote_answer() |                               |
+ *    |                               V                               |
+ * +--+---+                     +-----------+     negotiate()     +------+
+ * | NULL |                     | WAIT_NEGO |-------------------->| DONE |
+ * +------+                     +-----------+                     +------+
+ *    |                               A                               |
+ *    |            set_local_answer() |                               |
+ *    |                               |                               |
+ *    |                        +--------------+   set_remote_offer()  |
+ *     ----------------------->| REMOTE_OFFER |<----------------------
+ *     create_w_remote_offer() +--------------+
+ *
+ * </pre>
+ *
+ *
+ *
+ * \section sdpneg_offer_answer SDP Offer/Answer Model with Negotiator
+ *
+ * \subsection sdpneg_create_offer Creating Initial Offer
+ *
+ * Application creates an offer by manualy building the SDP session descriptor
+ * (pjmedia_sdp_session), or request PJMEDIA endpoint (pjmedia_endpt) to 
+ * create SDP session descriptor based on capabilities that present in the
+ * endpoint by calling #pjmedia_endpt_create_sdp().
+ *
+ * Application then creates SDP negotiator instance by calling
+ * #pjmedia_sdp_neg_create_w_local_offer(), passing the SDP offer in the
+ * function arguments. The SDP negotiator keeps a copy of current local offer,
+ * and update its state to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER.
+ *
+ * Application can then send the initial SDP offer that it creates to
+ * remote peer using signaling protocol such as SIP.
+ *
+ *
+ * \subsection sdpneg_subseq_offer Generating Subsequent Offer
+ *
+ * The negotiator can only create subsequent offer after it has finished
+ * the negotiation process of previous offer/answer session (i.e. the
+ * negotiator state is PJMEDIA_SDP_NEG_STATE_DONE).
+ *
+ * If any previous negotiation process was successfull (i.e. the return 
+ * value of #pjmedia_sdp_neg_negotiate() was PJ_SUCCESS), the negotiator
+ * keeps both active local and active remote SDP.
+ *
+ * If application does not want send modified offer, it can just send
+ * the active local SDP as the offer. In this case, application calls
+ * #pjmedia_sdp_neg_send_local_offer() to get the active local SDP.
+ * 
+ * If application wants to modify it's local offer, it MUST inform 
+ * the negotiator about the modified SDP by calling 
+ * #pjmedia_sdp_neg_modify_local_offer().
+ *
+ * In both cases, the negotiator will internally create a copy of the offer,
+ * and move it's state to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, where it
+ * waits until application passes the remote answer.
+ *
+ *
+ * \subsection sdpneg_receive_offer Receiving Initial Offer
+ *
+ * Application receives an offer in the incoming request from remote to
+ * establish multimedia session, such as incoming INVITE message with SDP
+ * body. 
+ *
+ * Initially, when the initial offer is received, application creates the 
+ * SDP negotiator by calling #pjmedia_sdp_neg_create_w_remote_offer(),
+ * specifying the remote SDP offer in one of the argument. 
+ *
+ * At this stage, application may or may not ready to create an answer.
+ * For example, a SIP B2BUA needs to make outgoing call and receive SDP
+ * from the outgoing call leg in order to create a SDP answer to the
+ * incoming call leg.
+ *
+ * If application is not ready to create an answer, it passes NULL as
+ * the local SDP when it calls #pjmedia_sdp_neg_create_w_remote_offer().
+ *
+ * The section @ref sdpneg_create_answer describes the case when 
+ * application is ready to create a SDP answer.
+ *
+ *
+ * \subsection sdpneg_subseq_offer Receiving Subsequent Offer
+ *
+ * Application passes subsequent SDP offer received from remote by
+ * calling #pjmedia_sdp_neg_set_remote_offer().
+ *
+ * The negotiator can only receive subsequent offer after it has finished
+ * the negotiation process of previous offer/answer session (i.e. the
+ * negotiator state is PJMEDIA_SDP_NEG_STATE_DONE).
+ *
+ *
+ * \subsection sdpneg_recv_answer Receiving SDP Answer
+ *
+ * When application receives SDP answer from remote, it informs the
+ * negotiator by calling #pjmedia_sdp_neg_set_remote_answer(). The
+ * negotiator validates the answer (#pjmedia_sdp_validate()), and if
+ * succeeds, it moves it's state to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO.
+ *
+ * Application then instruct the negotiator to negotiate the remote
+ * answer by calling #pjmedia_sdp_neg_negotiate(). The purpose of
+ * this negotiation is to verify remote answer, and update the initial
+ * offer according to the answer. For example, the initial offer may
+ * specify that a stream is \a sendrecv, while the answer specifies
+ * that remote stream is \a inactive. In this case, the negotiator
+ * will update the stream in the local active media as \a inactive
+ * too.
+ *
+ * If #pjmedia_sdp_neg_negotiate() returns PJ_SUCCESS, the negotiator will
+ * keep the updated local answer and remote answer internally. These two 
+ * SDPs are called active local SDP and active remote SDP, as it describes 
+ * currently active session.
+ *
+ * Application can retrieve the active local SDP by calling
+ * #pjmedia_sdp_neg_get_active_local(), and active remote SDP by calling
+ * #pjmedia_sdp_neg_get_active_remote().
+ *
+ * If #pjmedia_sdp_neg_negotiate() returns failure (i.e. not PJ_SUCCESS),
+ * it WILL NOT update its active local and active remote SDP.
+ *
+ * Regardless of the return status of the #pjmedia_sdp_neg_negotiate(), 
+ * the negotiator state will move to PJMEDIA_SDP_NEG_STATE_DONE.
+ * 
+ *
+ * \subsection sdpneg_create_answer Generating SDP Answer
+ *
+ * After remote offer has been set in the negotiator, application can 
+ * request the SDP negotiator to generate appropriate answer based on local 
+ * capability.
+ *
+ * To do this, first the application MUST have an SDP describing its local
+ * capabilities. This SDP can be built manually, or application can generate
+ * SDP to describe local media endpoint capability by calling 
+ * #pjmedia_endpt_create_sdp(). When the application is a SIP B2BUA, 
+ * application can treat the SDP received from the outgoing call leg as if
+ * it was it's local capability.
+ * 
+ * The local SDP session descriptor DOES NOT have to match the SDP offer.
+ * For example, it can have more or less media lines than the offer, or
+ * their order may be different than the offer. The negotiator is capable
+ * to match and reorder local SDP according to remote offer, and create
+ * an answer that is suitable for the offer.
+ *
+ * After local SDP capability has been acquired, application can create
+ * a SDP answer.
+ *
+ * If application does not already have the negotiator instance, it creates
+ * one by calling #pjmedia_sdp_neg_create_w_remote_offer(), specifying 
+ * both remote SDP offer and local SDP as the arguments. The SDP negotiator
+ * validates both remote and local SDP by calling #pjmedia_sdp_validate(),
+ * and if both SDPs are valid, the negotiator state will move to
+ * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO where it is ready to negotiate the
+ * offer and answer.
+ *
+ * If application already has the negotiator instance, it sets the local
+ * SDP in the negotiator by calling #pjmedia_sdp_neg_set_local_answer().
+ * The SDP negotiator then validates local SDP (#pjmedia_sdp_validate() ),
+ * and if it is  valid, the negotiator state will move to
+ * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO where it is ready to negotiate the
+ * offer and answer.
+ *
+ * After the SDP negotiator state has moved to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO,
+ * application calls #pjmedia_sdp_neg_negotiate() to instruct the SDP
+ * negotiator to negotiate both offer and answer. This function returns
+ * PJ_SUCCESS if an answer can be generated AND at least one media stream
+ * is active in the session.
+ *
+ * If #pjmedia_sdp_neg_negotiate() returns PJ_SUCCESS, the negotiator will
+ * keep the remote offer and local answer internally. These two SDPs are
+ * called active local SDP and active remote SDP, as it describes currently
+ * active session.
+ *
+ * Application can retrieve the active local SDP by calling
+ * #pjmedia_sdp_neg_get_active_local(), and send this SDP to remote as the
+ * SDP answer.
+ *
+ * If #pjmedia_sdp_neg_negotiate() returns failure (i.e. not PJ_SUCCESS),
+ * it WILL NOT update its active local and active remote SDP.
+ *
+ * Regardless of the return status of the #pjmedia_sdp_neg_negotiate(), 
+ * the negotiator state will move to PJMEDIA_SDP_NEG_STATE_DONE.
+ *
+ *
+ */
+
+#include <pjsdp/sdp.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * This enumeration describes SDP negotiation state. 
+ */
+enum pjmedia_sdp_neg_state
+{
+    /** 
+     * This is the state of SDP negoator before it is initialized. 
+     */
+    PJMEDIA_SDP_NEG_STATE_NULL,
+
+    /** 
+     * This state occurs when SDP negotiator has sent our offer to remote and
+     * it is waiting for answer. 
+     */
+    PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER,
+
+    /** 
+     * This state occurs when SDP negotiator has received offer from remote
+     * and currently waiting for local answer.
+     */
+    PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER,
+
+    /**
+     * This state occurs when an offer (either local or remote) has been 
+     * provided with answer. The SDP negotiator is ready to negotiate both
+     * session descriptors. Application can call #pjmedia_sdp_neg_negotiate()
+     * immediately to begin negotiation process.
+     */
+    PJMEDIA_SDP_NEG_STATE_WAIT_NEGO,
+
+    /**
+     * This state occurs when SDP negotiation has completed, either 
+     * successfully or not.
+     */
+    PJMEDIA_SDP_NEG_STATE_DONE,
+};
+
+
+/**
+ * @see pjmedia_sdp_neg_state
+ */
+typedef enum pjmedia_sdp_neg_state pjmedia_sdp_neg_state;
+
+
+/**
+ * Opaque declaration of SDP negotiator.
+ */
+typedef struct pjmedia_sdp_neg pjmedia_sdp_neg;
+
+
+/**
+ * Get the state string description of the specified state.
+ *
+ * @param state		Negotiator state.
+ *
+ * @return		String description of the state.
+ */
+PJ_DECL(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state);
+
+
+/**
+ * Create the SDP negotiator with local offer. The SDP negotiator then
+ * will move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER state, where it waits
+ * until it receives answer from remote. When SDP answer from remote is
+ * received, application must call #pjmedia_sdp_neg_set_remote_answer().
+ *
+ * After calling this function, application should send the local SDP offer
+ * to remote party using signaling protocol such as SIP and wait for SDP 
+ * answer.
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param local		The initial local capability.
+ * @param p_neg		Pointer to receive the negotiator instance.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error
+ *			code.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool,
+				      const pjmedia_sdp_session *local,
+				      pjmedia_sdp_neg **p_neg);
+
+/**
+ * Initialize the SDP negotiator with remote offer, and optionally
+ * specify the initial local capability, if known. Application normally 
+ * calls this function when it receives initial offer from remote. 
+ *
+ * If local media capability is specified, this capability will be set as
+ * initial local capability of the negotiator, and after this function is
+ * called, the SDP negotiator state will move to state
+ * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and the negotiation function can be 
+ * called. 
+ *
+ * If local SDP is not specified, the negotiator will not have initial local
+ * capability, and after this function is called the negotiator state will 
+ * move to PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER state. Application MUST supply
+ * local answer later with #pjmedia_sdp_neg_set_local_answer(), before
+ * calling the negotiation function.
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param initial	Optional initial local capability.
+ * @param remote	The remote offer.
+ * @param p_neg		Pointer to receive the negotiator instance.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error
+ *			code.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool,
+				      const pjmedia_sdp_session *initial,
+				      const pjmedia_sdp_session *remote,
+				      pjmedia_sdp_neg **p_neg);
+
+/**
+ * Get SDP negotiator state.
+ *
+ * @param neg		The SDP negotiator instance.
+ *
+ * @return		The negotiator state.
+ */
+PJ_DECL(pjmedia_sdp_neg_state)
+pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg );
+
+/**
+ * Get the currently active local SDP. Application can only call this
+ * function after negotiation has been done, or otherwise there won't be
+ * active SDPs. Calling this function will not change the state of the 
+ * negotiator.
+ *
+ * @param neg		The SDP negotiator instance.
+ * @param local		Pointer to receive the local active SDP.
+ *
+ * @return		PJ_SUCCESS if local active SDP is present.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session **local);
+
+/**
+ * Get the currently active remote SDP. Application can only call this
+ * function after negotiation has been done, or otherwise there won't be
+ * active SDPs. Calling this function will not change the state of the 
+ * negotiator.
+ *
+ * @param neg		The SDP negotiator instance.
+ * @param remote	Pointer to receive the remote active SDP.
+ *
+ * @return		PJ_SUCCESS if remote active SDP is present.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg,
+				   const pjmedia_sdp_session **remote);
+
+
+/**
+ * Determine whether remote sent answer (as opposed to offer) on the
+ * last negotiation. This function can only be called in state
+ * PJMEDIA_SDP_NEG_STATE_DONE.
+ *
+ * @param neg		The SDP negotiator instance.
+ *
+ * @return		Non-zero if it was remote who sent answer,
+ *			otherwise zero if it was local who supplied
+ *			answer.
+ */
+PJ_DECL(pj_bool_t)
+pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg);
+
+
+/**
+ * Get the current remote SDP offer or answer. Application can only 
+ * call this function in state PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER or
+ * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, or otherwise there won't be remote 
+ * SDP offer/answer. Calling this  function will not change the state 
+ * of the negotiator.
+ *
+ * @param neg		The SDP negotiator instance.
+ * @param remote	Pointer to receive the current remote offer or
+ *			answer.
+ *
+ * @return		PJ_SUCCESS if the negotiator currently has
+ *			remote offer or answer.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg,
+				const pjmedia_sdp_session **remote);
+
+
+/**
+ * Get the current local SDP offer or answer. Application can only 
+ * call this function in state PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER or
+ * PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, or otherwise there won't be local 
+ * SDP offer/answer. Calling this function will not change the state 
+ * of the negotiator.
+ *
+ * @param neg		The SDP negotiator instance.
+ * @param local		Pointer to receive the current local offer or
+ *			answer.
+ *
+ * @return		PJ_SUCCESS if the negotiator currently has
+ *			local offer or answer.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg,
+			       const pjmedia_sdp_session **local);
+
+/**
+ * Modify local session with a new SDP and treat this as a new offer. 
+ * This function can only be called in state PJMEDIA_SDP_NEG_STATE_DONE.
+ * After calling this function, application can send the SDP as offer 
+ * to remote party, using signaling protocol such as SIP.
+ * The negotiator state will move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER,
+ * where it waits for SDP answer from remote.
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param local		The new local SDP.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate
+ *			error code.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool,
+				    pjmedia_sdp_neg *neg,
+				    const pjmedia_sdp_session *local);
+
+/**
+ * This function can only be called in PJMEDIA_SDP_NEG_STATE_DONE state.
+ * Application calls this function to retrieve currently active
+ * local SDP, and then send the SDP to remote as an offer. The negotiator
+ * state will then move to PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, where it waits
+ * for SDP answer from remote. 
+ *
+ * When SDP answer has been received from remote, application must call 
+ * #pjmedia_sdp_neg_set_remote_answer().
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param offer		Pointer to receive active local SDP to be
+ *			offered to remote.
+ *
+ * @return		PJ_SUCCESS if local offer can be created.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool,
+			          pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session **offer);
+
+/**
+ * This function can only be called in PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER
+ * state, i.e. after application calls #pjmedia_sdp_neg_send_local_offer()
+ * function. Application calls this function when it receives SDP answer
+ * from remote. After this function is called, the negotiator state will
+ * move to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and application can call the
+ * negotiation function #pjmedia_sdp_neg_negotiate().
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param remote	The remote answer.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool,
+				   pjmedia_sdp_neg *neg,
+				   const pjmedia_sdp_session *remote);
+
+
+
+/**
+ * This function can only be called in PJMEDIA_SDP_NEG_STATE_DONE state. 
+ * Application calls this function when it receives SDP offer from remote.
+ * After this function is called, the negotiator state will move to 
+ * PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, and application MUST call the
+ * #pjmedia_sdp_neg_set_local_answer() to set local answer before it can
+ * call the negotiation function.
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param remote	The remote offer.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool,
+				  pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session *remote);
+
+
+
+/**
+ * This function can only be called in PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER
+ * state, i.e. after application calls #pjmedia_sdp_neg_set_remote_offer()
+ * function. After this function is called, the negotiator state will
+ * move to PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, and application can call the
+ * negotiation function #pjmedia_sdp_neg_negotiate().
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param local		Optional local answer. If negotiator has initial
+ *			local capability, application can specify NULL on
+ *			this argument; in this case, the negotiator will
+ *			create answer by by negotiating remote offer with
+ *			initial local capability. If negotiator doesn't have
+ *			initial local capability, application MUST specify
+ *			local answer here.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool,
+				  pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session *local);
+
+
+/**
+ * Call this function when the negotiator is in PJMEDIA_SDP_NEG_STATE_WAIT_NEGO
+ * state to see if it was local who is answering the offer (instead of
+ * remote).
+ *
+ * @param neg		The negotiator.
+ *
+ * @return		PJ_TRUE if it is local is answering an offer, PJ_FALSE
+ *			if remote has answered local offer.
+ */
+PJ_DECL(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg);
+
+
+/**
+ * Negotiate local and remote answer. Before calling this function, the
+ * SDP negotiator must be in PJMEDIA_SDP_NEG_STATE_WAIT_NEGO state.
+ * After calling this function, the negotiator state will move to
+ * PJMEDIA_SDP_NEG_STATE_DONE regardless whether the negotiation has
+ * been successfull or not.
+ *
+ * If the negotiation succeeds (i.e. the return value is PJ_SUCCESS),
+ * the active local and remote SDP will be replaced with the new SDP
+ * from the negotiation process.
+ *
+ * If the negotiation fails, the active local and remote SDP will not
+ * change.
+ *
+ * @param pool		Pool to allocate memory. The pool's lifetime needs
+ *			to be valid for the duration of the negotiator.
+ * @param neg		The SDP negotiator instance.
+ * @param allow_asym	Should be zero.
+ *
+ * @return		PJ_SUCCESS when there is at least one media
+ *			is actuve common in both offer and answer, or 
+ *			failure code when negotiation has failed.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
+					        pjmedia_sdp_neg *neg,
+						pj_bool_t allow_asym);
+
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_SDP_NEG_H__ */
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/types.h
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/include/pjsdp/types.h	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,123 @@
+/* $Id: types.h 518 2006-06-18 02:02:36Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 __PJMEDIA_TYPES_H__
+#define __PJMEDIA_TYPES_H__
+
+/**
+ * @file pjmedia/types.h Basic Types
+ * @brief Basic PJMEDIA types.
+ */
+
+#include <pjsdp/config.h>
+#include <pj/sock.h>	    /* pjmedia_sock_info	*/
+#include <pj/string.h>	    /* pj_memcpy(), pj_memset() */
+
+
+/**
+ * @defgroup PJMEDIA_FRAME_OP Frame Operations
+ * @ingroup PJMEDIA
+ */
+
+/**
+ * @defgroup PJMEDIA_MISC Misc
+ * @ingroup PJMEDIA
+ */
+
+/**
+ * @defgroup PJMEDIA_TYPES Basic Types
+ * @ingroup PJMEDIA_BASE
+ * @brief Basic PJMEDIA types and operations.
+ * @{
+ */
+
+/** 
+ * Top most media type. 
+ */
+typedef enum pjmedia_type
+{
+    /** No type. */
+    PJMEDIA_TYPE_NONE = 0,
+
+    /** The media is audio */
+    PJMEDIA_TYPE_AUDIO = 1,
+
+    /** The media is video. */
+    PJMEDIA_TYPE_VIDEO = 2,
+
+    /** Unknown media type, in this case the name will be specified in 
+     *  encoding_name.
+     */
+    PJMEDIA_TYPE_UNKNOWN = 3,
+
+} pjmedia_type;
+
+
+
+/** 
+ * Media direction. 
+ */
+typedef enum pjmedia_dir
+{
+    /** None */
+    PJMEDIA_DIR_NONE = 0,
+
+    /** Encoding (outgoing to network) stream */
+    PJMEDIA_DIR_ENCODING = 1,
+
+    /** Decoding (incoming from network) stream. */
+    PJMEDIA_DIR_DECODING = 2,
+
+    /** Incoming and outgoing stream. */
+    PJMEDIA_DIR_ENCODING_DECODING = 3,
+
+} pjmedia_dir;
+
+
+
+/* Alternate names for media direction: */
+
+/**
+ * Direction is capturing audio frames.
+ */
+#define PJMEDIA_DIR_CAPTURE	PJMEDIA_DIR_ENCODING
+
+/**
+ * Direction is playback of audio frames.
+ */
+#define PJMEDIA_DIR_PLAYBACK	PJMEDIA_DIR_DECODING
+
+/**
+ * Direction is both capture and playback.
+ */
+#define PJMEDIA_DIR_CAPTURE_PLAYBACK	PJMEDIA_DIR_ENCODING_DECODING
+
+
+/**
+ * Create 32bit port signature from ASCII characters.
+ */
+#define PJMEDIA_PORT_SIGNATURE(a,b,c,d)	    \
+	    (a<<24 | b<<16 | c<<8 | d)
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_TYPES_H__ */
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/errno.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/errno.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,155 @@
+/* $Id: errno.c 438 2006-05-13 22:46:23Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 
+ */
+#include <pjmedia/errno.h>
+#include <pjmedia/types.h>
+#include <pj/string.h>
+
+
+/* PJMEDIA's own error codes/messages 
+ * MUST KEEP THIS ARRAY SORTED!!
+ * Message must be limited to 64 chars!
+ */
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+static const struct 
+{
+    int code;
+    const char *msg;
+} err_str[] = 
+{
+    /* Generic PJMEDIA errors, shouldn't be used! */
+    { PJMEDIA_ERROR,		    "Unspecified PJMEDIA error" },
+
+    /* SDP error. */
+    { PJMEDIA_SDP_EINSDP,	    "Invalid SDP descriptor" },
+    { PJMEDIA_SDP_EINVER,	    "Invalid SDP version line" },
+    { PJMEDIA_SDP_EINORIGIN,	    "Invalid SDP origin line" },
+    { PJMEDIA_SDP_EINTIME,	    "Invalid SDP time line"},
+    { PJMEDIA_SDP_EINNAME,	    "SDP name/subject line is empty"},
+    { PJMEDIA_SDP_EINCONN,	    "Invalid SDP connection line"},
+    { PJMEDIA_SDP_EMISSINGCONN,	    "Missing SDP connection info line"},
+    { PJMEDIA_SDP_EINATTR,	    "Invalid SDP attributes"},
+    { PJMEDIA_SDP_EINRTPMAP,	    "Invalid SDP rtpmap attribute"},
+    { PJMEDIA_SDP_ERTPMAPTOOLONG,   "SDP rtpmap attribute too long"},
+    { PJMEDIA_SDP_EMISSINGRTPMAP,   "Missing SDP rtpmap for dynamic payload type"},
+    { PJMEDIA_SDP_EINMEDIA,	    "Invalid SDP media line" },
+    { PJMEDIA_SDP_ENOFMT,	    "No SDP payload format in the media line" },
+    { PJMEDIA_SDP_EINPT,	    "Invalid SDP payload type in media line" },
+    { PJMEDIA_SDP_EINFMTP,	    "Invalid SDP fmtp attribute" },
+
+    /* SDP negotiator errors. */
+    { PJMEDIA_SDPNEG_EINSTATE,	    "Invalid SDP negotiator state for operation" },
+    { PJMEDIA_SDPNEG_ENOINITIAL,    "No initial local SDP in SDP negotiator" },
+    { PJMEDIA_SDPNEG_ENOACTIVE,	    "No active SDP in SDP negotiator" },
+    { PJMEDIA_SDPNEG_ENONEG,	    "No current local/remote offer/answer" },
+    { PJMEDIA_SDPNEG_EMISMEDIA,	    "SDP media count mismatch in offer/answer" },
+    { PJMEDIA_SDPNEG_EINVANSMEDIA,  "SDP media type mismatch in offer/answer" },
+    { PJMEDIA_SDPNEG_EINVANSTP,	    "SDP media transport type mismatch in offer/answer" },
+    { PJMEDIA_SDPNEG_EANSNOMEDIA,   "No common SDP media payload in answer" },
+    { PJMEDIA_SDPNEG_ENOMEDIA,	    "No active media stream after negotiation" },
+
+    /* SDP comparison results */
+    { PJMEDIA_SDP_EMEDIANOTEQUAL,   "SDP media descriptor not equal" },
+    { PJMEDIA_SDP_EPORTNOTEQUAL,    "Port in SDP media descriptor not equal" },
+    { PJMEDIA_SDP_ETPORTNOTEQUAL,   "Transport in SDP media descriptor not equal" },
+    { PJMEDIA_SDP_EFORMATNOTEQUAL,  "Format in SDP media descriptor not equal" },
+    { PJMEDIA_SDP_ECONNNOTEQUAL,    "SDP connection line not equal" },
+    { PJMEDIA_SDP_EATTRNOTEQUAL,    "SDP attributes not equal" },
+    { PJMEDIA_SDP_EDIRNOTEQUAL,	    "SDP media direction not equal" },
+    { PJMEDIA_SDP_EFMTPNOTEQUAL,    "SDP fmtp attribute not equal" },
+    { PJMEDIA_SDP_ERTPMAPNOTEQUAL,  "SDP rtpmap attribute not equal" },
+    { PJMEDIA_SDP_ESESSNOTEQUAL,    "SDP session descriptor not equal" },
+    { PJMEDIA_SDP_EORIGINNOTEQUAL,  "SDP origin line not equal" },
+    { PJMEDIA_SDP_ENAMENOTEQUAL,    "SDP name/subject line not equal" },
+    { PJMEDIA_SDP_ETIMENOTEQUAL,    "SDP time line not equal" },
+
+    /* Media errors. */
+    { PJMEDIA_EINVALIDIP,	    "Invalid remote media (IP) address" },
+    { PJMEDIA_EASYMCODEC,	    "Asymetric media codec is not supported" },
+    { PJMEDIA_EINVALIDPT,	    "Invalid media payload type" },
+    { PJMEDIA_EMISSINGRTPMAP,	    "Missing rtpmap in media description" },
+    { PJMEDIA_EINVALIMEDIATYPE,	    "Invalid media type" },
+    { PJMEDIA_EREMOTENODTMF,	    "Remote does not support DTMF" },
+    { PJMEDIA_RTP_EINDTMF,	    "Invalid DTMF digit" },
+    { PJMEDIA_RTP_EREMNORFC2833,    "Remote does not support RFC 2833" },
+};
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+
+/*
+ * pjmedia_strerror()
+ */
+PJ_DEF(pj_str_t) pjmedia_strerror( pj_status_t statcode, 
+				   char *buf, pj_size_t bufsize )
+{
+    pj_str_t errstr;
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+    if (statcode >= PJMEDIA_ERRNO_START && 
+	       statcode < PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
+    {
+	/* Find the error in the table.
+	 * Use binary search!
+	 */
+	int first = 0;
+	int n = PJ_ARRAY_SIZE(err_str);
+
+	while (n > 0) {
+	    int half = n/2;
+	    int mid = first + half;
+
+	    if (err_str[mid].code < statcode) {
+		first = mid+1;
+		n -= (half+1);
+	    } else if (err_str[mid].code > statcode) {
+		n = half;
+	    } else {
+		first = mid;
+		break;
+	    }
+	}
+
+
+	if (PJ_ARRAY_SIZE(err_str) && err_str[first].code == statcode) {
+	    pj_str_t msg;
+	    
+	    msg.ptr = (char*)err_str[first].msg;
+	    msg.slen = pj_ansi_strlen(err_str[first].msg);
+
+	    errstr.ptr = buf;
+	    pj_strncpy_with_null(&errstr, &msg, bufsize);
+	    return errstr;
+
+	} 
+    }
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+    /* Error not found. */
+    errstr.ptr = buf;
+    errstr.slen = pj_ansi_snprintf(buf, bufsize, 
+				   "Unknown pjmedia error %d",
+				   statcode);
+
+    return errstr;
+}

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,1181 @@
+/* $Id: sdp.c 366 2006-03-30 16:44:28Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 
+ */
+#include <pjmedia/sdp.h>
+#include <pjmedia/errno.h>
+#include <pjlib-util/scanner.h>
+#include <pj/array.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+
+
+enum {
+    SKIP_WS = 0,
+    SYNTAX_ERROR = 1,
+};
+#define TOKEN		"-.!%*_=`'~"
+#define NTP_OFFSET	((pj_uint32_t)2208988800)
+#define THIS_FILE	"sdp.c"
+
+typedef struct parse_context
+{ 
+    pj_status_t last_error;
+} parse_context;
+
+
+/*
+ * Prototypes for line parser.
+ */
+static void parse_version(pj_scanner *scanner, parse_context *ctx);
+static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
+			 parse_context *ctx);
+static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
+		       parse_context *ctx);
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
+			       parse_context *ctx);
+static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
+				  parse_context *ctx);
+static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner,
+				    parse_context *ctx);
+static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
+			parse_context *ctx);
+
+
+/*
+ * Scanner character specification.
+ */
+static int is_initialized;
+static pj_cis_buf_t cis_buf;
+static pj_cis_t cs_token;
+
+static void init_sdp_parser(void)
+{
+    if (is_initialized == 0) {
+	is_initialized = 1;
+	if (is_initialized != 1) {
+	    return;
+	}
+    }
+
+    pj_cis_buf_init(&cis_buf);
+    pj_cis_init(&cis_buf, &cs_token);
+    pj_cis_add_alpha(&cs_token);
+    pj_cis_add_num(&cs_token);
+    pj_cis_add_str(&cs_token, TOKEN);
+}
+
+PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_create( pj_pool_t *pool,
+						   const char *name,
+						   const pj_str_t *value)
+{
+    pjmedia_sdp_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && name, NULL);
+
+    attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
+    pj_strdup2(pool, &attr->name, name);
+
+    if (value)
+	pj_strdup(pool, &attr->value, value);
+    else {
+	attr->value.ptr = NULL;
+	attr->value.slen = 0;
+    }
+
+    return attr;
+}
+
+PJ_DEF(pjmedia_sdp_attr*) pjmedia_sdp_attr_clone(pj_pool_t *pool, 
+						 const pjmedia_sdp_attr *rhs)
+{
+    pjmedia_sdp_attr *attr;
+    
+    PJ_ASSERT_RETURN(pool && rhs, NULL);
+
+    attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
+
+    pj_strdup(pool, &attr->name, &rhs->name);
+    pj_strdup(pool, &attr->value, &rhs->value);
+
+    return attr;
+}
+
+PJ_DEF(pjmedia_sdp_attr*) 
+pjmedia_sdp_attr_find (unsigned count, 
+		       pjmedia_sdp_attr *const attr_array[],
+		       const pj_str_t *name,
+		       const pj_str_t *c_fmt)
+{
+    char fmtbuf[16];
+    pj_str_t fmt = { NULL, 0};
+    unsigned i;
+
+    if (c_fmt) {
+	/* To search the format, we prepend the string with a colon and
+	 * append space
+	 */
+	PJ_ASSERT_RETURN(c_fmt->slen<sizeof(fmtbuf)-2, NULL);
+	fmt.ptr = fmtbuf;
+	fmt.slen = c_fmt->slen + 2;
+	fmtbuf[0] = ':';
+	pj_memcpy(fmt.ptr+1, c_fmt->ptr, c_fmt->slen);
+	fmtbuf[c_fmt->slen+1] = ' ';
+
+    } 
+
+    for (i=0; i<count; ++i) {
+	if (pj_strcmp(&attr_array[i]->name, name) == 0) {
+	    const pjmedia_sdp_attr *a = attr_array[i];
+	    if (c_fmt) {
+		if (a->value.slen > fmt.slen &&
+		    pj_strncmp(&a->value, &fmt, fmt.slen)==0)
+		{
+		    return (pjmedia_sdp_attr*)a;
+		}
+	    } else 
+		return (pjmedia_sdp_attr*)a;
+	}
+    }
+    return NULL;
+}
+
+PJ_DEF(pjmedia_sdp_attr*) 
+pjmedia_sdp_attr_find2(unsigned count, 
+		       pjmedia_sdp_attr *const attr_array[],
+		       const char *c_name,
+		       const pj_str_t *c_fmt)
+{
+    pj_str_t name;
+
+    name.ptr = (char*)c_name;
+    name.slen = pj_ansi_strlen(c_name);
+
+    return pjmedia_sdp_attr_find(count, attr_array, &name, c_fmt);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_add(unsigned *count,
+					 pjmedia_sdp_attr *attr_array[],
+					 pjmedia_sdp_attr *attr)
+{
+    PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*count < PJMEDIA_MAX_SDP_ATTR, PJ_ETOOMANY);
+
+    attr_array[*count] = attr;
+    (*count)++;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(unsigned) pjmedia_sdp_attr_remove_all(unsigned *count,
+					     pjmedia_sdp_attr *attr_array[],
+					     const char *name)
+{
+    unsigned i, removed = 0;
+    pj_str_t attr_name;
+
+    PJ_ASSERT_RETURN(count && attr_array && name, PJ_EINVAL);
+
+    attr_name.ptr = (char*)name;
+    attr_name.slen = pj_ansi_strlen(name);
+
+    for (i=0; i<*count; ) {
+	if (pj_strcmp(&attr_array[i]->name, &attr_name)==0) {
+	    pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
+			   *count, i);
+	    --(*count);
+	    ++removed;
+	} else {
+	    ++i;
+	}   
+    }
+
+    return removed;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count,
+					     pjmedia_sdp_attr *attr_array[],
+					     pjmedia_sdp_attr *attr )
+{
+    unsigned i, removed=0;
+
+    PJ_ASSERT_RETURN(count && attr_array && attr, PJ_EINVAL);
+
+    for (i=0; i<*count; ) {
+	if (attr_array[i] == attr) {
+	    pj_array_erase(attr_array, sizeof(pjmedia_sdp_attr*),
+			   *count, i);
+	    --(*count);
+	    ++removed;
+	} else {
+	    ++i;
+	}
+    }
+
+    return removed ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
+						 pjmedia_sdp_rtpmap *rtpmap)
+{
+    const char *p = attr->value.ptr;
+    const char *end = attr->value.ptr + attr->value.slen;
+    pj_str_t token;
+
+    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);
+
+    /* rtpmap sample:
+     *	a=rtpmap:98 L16/16000/2.
+     */
+
+    /* Eat the first ':' */
+    if (*p != ':') return PJMEDIA_SDP_EINRTPMAP;
+
+    /* Get ':' */
+    ++p;
+
+    /* Get payload type. */
+    token.ptr = (char*)p;
+    while (pj_isdigit(*p) && p!=end)
+	++p;
+    token.slen = p - token.ptr;
+    if (token.slen == 0)
+	return PJMEDIA_SDP_EINRTPMAP;
+
+    rtpmap->pt = token;
+
+    /* Expecting space after payload type. */
+    if (*p != ' ') return PJMEDIA_SDP_EINRTPMAP;
+
+    /* Get space. */
+    ++p;
+
+    /* Get encoding name. */
+    token.ptr = (char*)p;
+    while (*p != '/' && p != end)
+	++p;
+    token.slen = p - token.ptr;
+    if (token.slen == 0)
+	return PJMEDIA_SDP_EINRTPMAP;
+    rtpmap->enc_name = token;
+
+    /* Expecting '/' after encoding name. */
+    if (*p != '/') return PJMEDIA_SDP_EINRTPMAP;
+
+    /* Get '/' */
+    ++p;
+
+    /* Get the clock rate. */
+    token.ptr = (char*)p;
+    while (p != end && pj_isdigit(*p))
+	++p;
+    token.slen = p - token.ptr;
+    if (token.slen == 0)
+	return PJMEDIA_SDP_EINRTPMAP;
+
+    rtpmap->clock_rate = pj_strtoul(&token);
+
+    /* Expecting either '/' or EOF */
+    if (p != end && *p != '/')
+	return PJMEDIA_SDP_EINRTPMAP;
+
+    if (p != end) {
+	++p;
+	token.ptr = (char*)p;
+	token.slen = end-p;
+	rtpmap->param = token;
+    } else {
+	rtpmap->param.ptr = NULL;
+	rtpmap->param.slen = 0;
+    }
+
+    return PJ_SUCCESS;
+
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
+					       pjmedia_sdp_fmtp *fmtp)
+{
+    const char *p = attr->value.ptr;
+    const char *end = attr->value.ptr + attr->value.slen;
+    pj_str_t token;
+
+    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "fmtp")==0, PJ_EINVALIDOP);
+
+    /* fmtp BNF:
+     *	a=fmtp:<format> <format specific parameter>
+     */
+
+    /* Eat the first ':' */
+    if (*p != ':') return PJMEDIA_SDP_EINFMTP;
+
+    /* Get ':' */
+    ++p;
+
+    /* Get format. */
+    token.ptr = (char*)p;
+    while (pj_isdigit(*p) && p!=end)
+	++p;
+    token.slen = p - token.ptr;
+    if (token.slen == 0)
+	return PJMEDIA_SDP_EINFMTP;
+
+    fmtp->fmt = token;
+
+    /* Expecting space after format. */
+    if (*p != ' ') return PJMEDIA_SDP_EINFMTP;
+
+    /* Get space. */
+    ++p;
+
+    /* Set the remaining string as fmtp format parameter. */
+    fmtp->fmt_param.ptr = (char*)p;
+    fmtp->fmt_param.slen = end - p;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
+					       const pjmedia_sdp_attr *attr,
+					       pjmedia_sdp_rtpmap **p_rtpmap)
+{
+    PJ_ASSERT_RETURN(pool && attr && p_rtpmap, PJ_EINVAL);
+
+    *p_rtpmap = pj_pool_alloc(pool, sizeof(pjmedia_sdp_rtpmap));
+    PJ_ASSERT_RETURN(*p_rtpmap, PJ_ENOMEM);
+
+    return pjmedia_sdp_attr_get_rtpmap(attr, *p_rtpmap);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
+					       const pjmedia_sdp_rtpmap *rtpmap,
+					       pjmedia_sdp_attr **p_attr)
+{
+    pjmedia_sdp_attr *attr;
+    char tempbuf[64], *p, *endbuf;
+    int i;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && rtpmap && p_attr, PJ_EINVAL);
+
+    /* Check that mandatory attributes are specified. */
+    PJ_ASSERT_RETURN(rtpmap->enc_name.slen && rtpmap->clock_rate,
+		     PJMEDIA_SDP_EINRTPMAP);
+
+    /* Check size. */
+    i = rtpmap->enc_name.slen + rtpmap->param.slen + 32;
+    if (i >= sizeof(tempbuf)-1) {
+	pj_assert(!"rtpmap attribute is too long");
+	return PJMEDIA_SDP_ERTPMAPTOOLONG;
+    }
+
+    attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
+    PJ_ASSERT_RETURN(attr != NULL, PJ_ENOMEM);
+
+    attr->name.ptr = "rtpmap";
+    attr->name.slen = 6;
+
+    p = tempbuf;
+    endbuf = tempbuf+sizeof(tempbuf);
+
+    /* Add colon */
+    *p++ = ':';
+
+    /* Add payload type. */
+    pj_memcpy(p, rtpmap->pt.ptr, rtpmap->pt.slen);
+    p += rtpmap->pt.slen;
+    *p++ = ' ';
+
+    /* Add encoding name. */
+    for (i=0; i<rtpmap->enc_name.slen; ++i)
+	p[i] = rtpmap->enc_name.ptr[i];
+    p += rtpmap->enc_name.slen;
+    *p++ = '/';
+
+    /* Add clock rate. */
+    p += pj_utoa(rtpmap->clock_rate, p);
+
+    /* Add parameter if necessary. */
+    if (rtpmap->param.slen > 0) {
+	*p++ = '/';
+	for (i=0; i<rtpmap->param.slen; ++i)
+	    p[i] = rtpmap->param.ptr[i];
+	p += rtpmap->param.slen;
+    }
+
+    *p = '\0';
+
+    attr->value.slen = p-tempbuf;
+    attr->value.ptr = pj_pool_alloc(pool, attr->value.slen);
+    pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen);
+
+    *p_attr = attr;
+    return PJ_SUCCESS;
+}
+
+
+static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len)
+{
+    char *p = buf;
+
+    if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+	return -1;
+    }
+    *p++ = 'c';
+    *p++ = '=';
+    pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
+    p += c->net_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
+    p += c->addr_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, c->addr.ptr, c->addr.slen);
+    p += c->addr.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    return p-buf;
+}
+
+PJ_DEF(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone (pj_pool_t *pool, 
+						  const pjmedia_sdp_conn *rhs)
+{
+    pjmedia_sdp_conn *c = pj_pool_alloc (pool, sizeof(pjmedia_sdp_conn));
+    if (!c) return NULL;
+
+    if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
+    if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
+    if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
+
+    return c;
+}
+
+static pj_ssize_t print_attr(const pjmedia_sdp_attr *attr, 
+			     char *buf, pj_size_t len)
+{
+    char *p = buf;
+
+    if ((int)len < attr->name.slen + attr->value.slen + 10)
+	return -1;
+
+    *p++ = 'a';
+    *p++ = '=';
+    pj_memcpy(p, attr->name.ptr, attr->name.slen);
+    p += attr->name.slen;
+    
+
+    if (attr->value.slen) {
+	pj_memcpy(p, attr->value.ptr, attr->value.slen);
+	p += attr->value.slen;
+    }
+
+    *p++ = '\r';
+    *p++ = '\n';
+    return p-buf;
+}
+
+static int print_media_desc( pjmedia_sdp_media *m, char *buf, int len)
+{
+    char *p = buf;
+    char *end = buf+len;
+    unsigned i;
+    int printed;
+
+    /* check length for the "m=" line. */
+    if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
+	return -1;
+    }
+    *p++ = 'm';	    /* m= */
+    *p++ = '=';
+    pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
+    p += m->desc.media.slen;
+    *p++ = ' ';
+    printed = pj_utoa(m->desc.port, p);
+    p += printed;
+    if (m->desc.port_count > 1) {
+	*p++ = '/';
+	printed = pj_utoa(m->desc.port_count, p);
+	p += printed;
+    }
+    *p++ = ' ';
+    pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
+    p += m->desc.transport.slen;
+    for (i=0; i<m->desc.fmt_count; ++i) {
+	*p++ = ' ';
+	pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
+	p += m->desc.fmt[i].slen;
+    }
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* print connection info, if present. */
+    if (m->conn) {
+	printed = print_connection_info(m->conn, p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    /* print attributes. */
+    for (i=0; i<m->attr_count; ++i) {
+	printed = print_attr(m->attr[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    return p-buf;
+}
+
+PJ_DEF(pjmedia_sdp_media*) pjmedia_sdp_media_clone(
+						 pj_pool_t *pool, 
+						 const pjmedia_sdp_media *rhs)
+{
+    unsigned int i;
+    pjmedia_sdp_media *m = pj_pool_alloc (pool, sizeof(pjmedia_sdp_media));
+    PJ_ASSERT_RETURN(m != NULL, NULL);
+
+    pj_strdup (pool, &m->desc.media, &rhs->desc.media);
+    m->desc.port = rhs->desc.port;
+    m->desc.port_count = rhs->desc.port_count;
+    pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
+    m->desc.fmt_count = rhs->desc.fmt_count;
+    for (i=0; i<rhs->desc.fmt_count; ++i)
+	pj_strdup(pool, &m->desc.fmt[i], &rhs->desc.fmt[i]);
+
+    if (rhs->conn) {
+	m->conn = pjmedia_sdp_conn_clone (pool, rhs->conn);
+	PJ_ASSERT_RETURN(m->conn != NULL, NULL);
+    } else {
+	m->conn = NULL;
+    }
+
+    m->attr_count = rhs->attr_count;
+    for (i=0; i < rhs->attr_count; ++i) {
+	m->attr[i] = pjmedia_sdp_attr_clone (pool, rhs->attr[i]);
+	PJ_ASSERT_RETURN(m->attr[i] != NULL, NULL);
+    }
+
+    return m;
+}
+
+PJ_DEF(pjmedia_sdp_attr*) 
+pjmedia_sdp_media_find_attr(const pjmedia_sdp_media *m,
+			    const pj_str_t *name, const pj_str_t *fmt)
+{
+    PJ_ASSERT_RETURN(m && name, NULL);
+    return pjmedia_sdp_attr_find(m->attr_count, m->attr, name, fmt);
+}
+
+
+
+PJ_DEF(pjmedia_sdp_attr*) 
+pjmedia_sdp_media_find_attr2(const pjmedia_sdp_media *m,
+			     const char *name, const pj_str_t *fmt)
+{
+    PJ_ASSERT_RETURN(m && name, NULL);
+    return pjmedia_sdp_attr_find2(m->attr_count, m->attr, name, fmt);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_media_add_attr( pjmedia_sdp_media *m,
+						pjmedia_sdp_attr *attr)
+{
+    return pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
+}
+
+PJ_DEF(unsigned) 
+pjmedia_sdp_media_remove_all_attr(pjmedia_sdp_media *m,
+				  const char *name)
+{
+    return pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, name);
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m,
+			      pjmedia_sdp_attr *attr)
+{
+    return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr);
+}
+
+static int print_session(const pjmedia_sdp_session *ses, 
+			 char *buf, pj_ssize_t len)
+{
+    char *p = buf;
+    char *end = buf+len;
+    unsigned i;
+    int printed;
+
+    /* Check length for v= and o= lines. */
+    if (len < 5+ 
+	      2+ses->origin.user.slen+18+
+	      ses->origin.net_type.slen+ses->origin.addr.slen + 2)
+    {
+	return -1;
+    }
+
+    /* SDP version (v= line) */
+    pj_memcpy(p, "v=0\r\n", 5);
+    p += 5;
+
+    /* Owner (o=) line. */
+    *p++ = 'o';
+    *p++ = '=';
+    pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
+    p += ses->origin.user.slen;
+    *p++ = ' ';
+    printed = pj_utoa(ses->origin.id, p);
+    p += printed;
+    *p++ = ' ';
+    printed = pj_utoa(ses->origin.version, p);
+    p += printed;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
+    p += ses->origin.net_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
+    p += ses->origin.addr_type.slen;
+    *p++ = ' ';
+    pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
+    p += ses->origin.addr.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Session name (s=) line. */
+    if ((end-p)  < 8+ses->name.slen) {
+	return -1;
+    }
+    *p++ = 's';
+    *p++ = '=';
+    pj_memcpy(p, ses->name.ptr, ses->name.slen);
+    p += ses->name.slen;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Connection line (c=) if exist. */
+    if (ses->conn) {
+	printed = print_connection_info(ses->conn, p, end-p);
+	if (printed < 1) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+
+    /* Time */
+    if ((end-p) < 24) {
+	return -1;
+    }
+    *p++ = 't';
+    *p++ = '=';
+    printed = pj_utoa(ses->time.start, p);
+    p += printed;
+    *p++ = ' ';
+    printed = pj_utoa(ses->time.stop, p);
+    p += printed;
+    *p++ = '\r';
+    *p++ = '\n';
+
+    /* Print all attribute (a=) lines. */
+    for (i=0; i<ses->attr_count; ++i) {
+	printed = print_attr(ses->attr[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    /* Print media (m=) lines. */
+    for (i=0; i<ses->media_count; ++i) {
+	printed = print_media_desc(ses->media[i], p, end-p);
+	if (printed < 0) {
+	    return -1;
+	}
+	p += printed;
+    }
+
+    return p-buf;
+}
+
+/******************************************************************************
+ * PARSERS
+ */
+
+static void parse_version(pj_scanner *scanner, parse_context *ctx)
+{
+    ctx->last_error = PJMEDIA_SDP_EINVER;
+
+    pj_scan_advance_n(scanner, 3, SKIP_WS);
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
+			 parse_context *ctx)
+{
+    pj_str_t str;
+
+    ctx->last_error = PJMEDIA_SDP_EINORIGIN;
+
+    /* o= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* username. */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
+    pj_scan_get_char(scanner);
+
+    /* id */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->origin.id = pj_strtoul(&str);
+    pj_scan_get_char(scanner);
+
+    /* version */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->origin.version = pj_strtoul(&str);
+    pj_scan_get_char(scanner);
+
+    /* network-type */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
+    pj_scan_get_char(scanner);
+
+    /* addr-type */
+    pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
+    pj_scan_get_char(scanner);
+
+    /* address */
+    pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
+		       parse_context *ctx)
+{
+    pj_str_t str;
+
+    ctx->last_error = PJMEDIA_SDP_EINTIME;
+
+    /* t= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* start time */
+    pj_scan_get_until_ch(scanner, ' ', &str);
+    ses->time.start = pj_strtoul(&str);
+
+    pj_scan_get_char(scanner);
+
+    /* stop time */
+    pj_scan_get_until_ch(scanner, '\r', &str);
+    ses->time.stop = pj_strtoul(&str);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
+			       parse_context *ctx)
+{
+    ctx->last_error = PJMEDIA_SDP_EINSDP;
+
+    /* x= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* get anything until newline. */
+    pj_scan_get_until_ch(scanner, '\r', str);
+
+    /* newline. */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_connection_info(pj_scanner *scanner, pjmedia_sdp_conn *conn,
+				  parse_context *ctx)
+{
+    ctx->last_error = PJMEDIA_SDP_EINCONN;
+
+    /* c= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* network-type */
+    pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
+    pj_scan_get_char(scanner);
+
+    /* addr-type */
+    pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
+    pj_scan_get_char(scanner);
+
+    /* address. */
+    pj_scan_get_until_ch(scanner, '\r', &conn->addr);
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
+			parse_context *ctx)
+{
+    pj_str_t str;
+
+    ctx->last_error = PJMEDIA_SDP_EINMEDIA;
+
+    /* m= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+    /* type */
+    pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
+    pj_scan_get_char(scanner);
+
+    /* port */
+    pj_scan_get(scanner, &cs_token, &str);
+    med->desc.port = (unsigned short)pj_strtoul(&str);
+    if (*scanner->curptr == '/') {
+	/* port count */
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, &cs_token, &str);
+	med->desc.port_count = pj_strtoul(&str);
+
+    } else {
+	med->desc.port_count = 0;
+    }
+
+    if (pj_scan_get_char(scanner) != ' ') {
+	PJ_THROW(SYNTAX_ERROR);
+    }
+
+    /* transport */
+    pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
+
+    /* format list */
+    med->desc.fmt_count = 0;
+    while (*scanner->curptr == ' ') {
+	pj_scan_get_char(scanner);
+	pj_scan_get(scanner, &cs_token, &med->desc.fmt[med->desc.fmt_count++]);
+    }
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+}
+
+static void on_scanner_error(pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner);
+
+    PJ_THROW(SYNTAX_ERROR);
+}
+
+static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
+				    parse_context *ctx)
+{
+    pjmedia_sdp_attr *attr;
+
+    ctx->last_error = PJMEDIA_SDP_EINATTR;
+
+    attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
+
+    /* skip a= */
+    pj_scan_advance_n(scanner, 2, SKIP_WS);
+    
+    /* get attr name. */
+    pj_scan_get(scanner, &cs_token, &attr->name);
+
+    if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
+	/* get value */
+	pj_scan_get_until_ch(scanner, '\r', &attr->value);
+    } else {
+	attr->value.ptr = NULL;
+	attr->value.slen = 0;
+    }
+
+    /* newline */
+    pj_scan_get_newline(scanner);
+
+    return attr;
+}
+
+/*
+ * Parse SDP message.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_parse( pj_pool_t *pool,
+				       char *buf, pj_size_t len, 
+				       pjmedia_sdp_session **p_sdp)
+{
+    pj_scanner scanner;
+    pjmedia_sdp_session *session;
+    pjmedia_sdp_media *media = NULL;
+    void *attr;
+    pjmedia_sdp_conn *conn;
+    pj_str_t dummy;
+    int cur_name = 254;
+    parse_context ctx;
+    PJ_USE_EXCEPTION;
+
+    ctx.last_error = PJ_SUCCESS;
+
+    init_sdp_parser();
+
+    pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
+    session = pj_pool_calloc(pool, 1, sizeof(pjmedia_sdp_session));
+    PJ_ASSERT_RETURN(session != NULL, PJ_ENOMEM);
+
+    PJ_TRY {
+	while (!pj_scan_is_eof(&scanner)) {
+		cur_name = *scanner.curptr;
+		switch (cur_name) {
+		case 'a':
+		    attr = parse_attr(pool, &scanner, &ctx);
+		    if (attr) {
+			if (media) {
+			    media->attr[media->attr_count++] = attr;
+			} else {
+			    session->attr[session->attr_count++] = attr;
+			}
+		    }
+		    break;
+		case 'o':
+		    parse_origin(&scanner, session, &ctx);
+		    break;
+		case 's':
+		    parse_generic_line(&scanner, &session->name, &ctx);
+		    break;
+		case 'c':
+		    conn = pj_pool_calloc(pool, 1, sizeof(*conn));
+		    parse_connection_info(&scanner, conn, &ctx);
+		    if (media) {
+			media->conn = conn;
+		    } else {
+			session->conn = conn;
+		    }
+		    break;
+		case 't':
+		    parse_time(&scanner, session, &ctx);
+		    break;
+		case 'm':
+		    media = pj_pool_calloc(pool, 1, sizeof(*media));
+		    parse_media(&scanner, media, &ctx);
+		    session->media[ session->media_count++ ] = media;
+		    break;
+		case 'v':
+		    parse_version(&scanner, &ctx);
+		    break;
+		default:
+		    parse_generic_line(&scanner, &dummy, &ctx);
+		    break;
+		}
+	}
+
+	ctx.last_error = PJ_SUCCESS;
+
+    }
+    PJ_CATCH(SYNTAX_ERROR) {
+	
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(ctx.last_error, errmsg, sizeof(errmsg));
+
+	PJ_LOG(4, (THIS_FILE, "Error parsing SDP in line %d col %d: %s",
+		   scanner.line, pj_scan_get_col(&scanner),
+		   errmsg));
+
+	session = NULL;
+
+    }
+    PJ_END;
+
+    pj_scan_fini(&scanner);
+
+    *p_sdp = session;
+    return ctx.last_error;
+}
+
+/*
+ * Print SDP description.
+ */
+PJ_DEF(int) pjmedia_sdp_print( const pjmedia_sdp_session *desc, 
+			       char *buf, pj_size_t size)
+{
+    return print_session(desc, buf, size);
+}
+
+
+/*
+ * Clone session
+ */
+PJ_DEF(pjmedia_sdp_session*) 
+pjmedia_sdp_session_clone( pj_pool_t *pool,
+			   const pjmedia_sdp_session *rhs)
+{
+    pjmedia_sdp_session *sess;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(pool && rhs, NULL);
+
+    sess = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_session));
+    PJ_ASSERT_RETURN(sess != NULL, NULL);
+
+    /* Clone origin line. */
+    pj_strdup(pool, &sess->origin.user, &rhs->origin.user);
+    sess->origin.id = rhs->origin.id;
+    sess->origin.version = rhs->origin.version;
+    pj_strdup(pool, &sess->origin.net_type, &rhs->origin.net_type);
+    pj_strdup(pool, &sess->origin.addr_type, &rhs->origin.addr_type);
+    pj_strdup(pool, &sess->origin.addr, &rhs->origin.addr);
+
+    /* Clone subject line. */
+    pj_strdup(pool, &sess->name, &rhs->name);
+
+    /* Clone connection line */
+    if (rhs->conn) {
+	sess->conn = pjmedia_sdp_conn_clone(pool, rhs->conn);
+	PJ_ASSERT_RETURN(sess->conn != NULL, NULL);
+    }
+
+    /* Clone time line. */
+    sess->time.start = rhs->time.start;
+    sess->time.stop = rhs->time.stop;
+
+    /* Duplicate session attributes. */
+    sess->attr_count = rhs->attr_count;
+    for (i=0; i<rhs->attr_count; ++i) {
+	sess->attr[i] = pjmedia_sdp_attr_clone(pool, rhs->attr[i]);
+    }
+
+    /* Duplicate media descriptors. */
+    sess->media_count = rhs->media_count;
+    for (i=0; i<rhs->media_count; ++i) {
+	sess->media[i] = pjmedia_sdp_media_clone(pool, rhs->media[i]);
+    }
+
+    return sess;
+}
+
+
+#define CHECK(exp,ret)	do {			\
+			    pj_assert(exp);	\
+			    if (!(exp))		\
+				return ret;	\
+			} while (0)
+
+/* Validate SDP connetion info. */
+static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
+{
+    CHECK( c, PJ_EINVAL);
+    CHECK( pj_strcmp2(&c->net_type, "IN")==0, PJMEDIA_SDP_EINCONN);
+    CHECK( pj_strcmp2(&c->addr_type, "IP4")==0 ||
+	   pj_strcmp2(&c->addr_type, "IP6")==0, 
+	   PJMEDIA_SDP_EINCONN);
+    CHECK( c->addr.slen != 0, PJMEDIA_SDP_EINCONN);
+
+    return PJ_SUCCESS;
+}
+
+
+/* Validate SDP session descriptor. */
+PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
+{
+    unsigned i;
+    const pj_str_t STR_RTPMAP = { "rtpmap", 6 };
+
+    CHECK( sdp != NULL, PJ_EINVAL);
+
+    /* Validate origin line. */
+    CHECK( sdp->origin.user.slen != 0, PJMEDIA_SDP_EINORIGIN);
+    CHECK( pj_strcmp2(&sdp->origin.net_type, "IN")==0, 
+	   PJMEDIA_SDP_EINORIGIN);
+    CHECK( pj_strcmp2(&sdp->origin.addr_type, "IP4")==0 ||
+	   pj_strcmp2(&sdp->origin.addr_type, "IP6")==0, 
+	   PJMEDIA_SDP_EINORIGIN);
+    CHECK( sdp->origin.addr.slen != 0, PJMEDIA_SDP_EINORIGIN);
+
+    /* Validate subject line. */
+    CHECK( sdp->name.slen != 0, PJMEDIA_SDP_EINNAME);
+
+    /* Ignore start and stop time. */
+
+    /* If session level connection info is present, validate it. */
+    if (sdp->conn) {
+	pj_status_t status = validate_sdp_conn(sdp->conn);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Validate each media. */
+    for (i=0; i<sdp->media_count; ++i) {
+	const pjmedia_sdp_media *m = sdp->media[i];
+	unsigned j;
+
+	/* Validate the m= line. */
+	CHECK( m->desc.media.slen != 0, PJMEDIA_SDP_EINMEDIA);
+	CHECK( m->desc.transport.slen != 0, PJMEDIA_SDP_EINMEDIA);
+	CHECK( m->desc.fmt_count != 0, PJMEDIA_SDP_ENOFMT);
+
+	/* If media level connection info is present, validate it. */
+	if (m->conn) {
+	    pj_status_t status = validate_sdp_conn(m->conn);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+
+	/* If media doesn't have connection info, then connection info
+	 * must be present in the session.
+	 */
+	if (m->conn == NULL) {
+	    if (sdp->conn == NULL)
+		return PJMEDIA_SDP_EMISSINGCONN;
+	}
+
+	/* Verify payload type. */
+	for (j=0; j<m->desc.fmt_count; ++j) {
+
+	    /* Arrgh noo!! Payload type can be non-numeric!!
+	     * RTC based programs sends "null" for instant messaging!
+	     */
+	    if (pj_isdigit(*m->desc.fmt[j].ptr)) {
+		unsigned pt = pj_strtoul(&m->desc.fmt[j]);
+
+		/* Payload type is between 0 and 127. 
+		 */
+		CHECK( pt <= 127, PJMEDIA_SDP_EINPT);
+
+		/* If port is not zero, then for each dynamic payload type, an
+		 * rtpmap attribute must be specified.
+		 */
+		if (m->desc.port != 0 && pt >= 96) {
+		    const pjmedia_sdp_attr *a;
+
+		    a = pjmedia_sdp_media_find_attr(m, &STR_RTPMAP, 
+						    &m->desc.fmt[j]);
+		    CHECK( a != NULL, PJMEDIA_SDP_EMISSINGRTPMAP);
+		}
+	    }
+	}
+    }
+
+    /* Looks good. */
+    return PJ_SUCCESS;
+}
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_cmp.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_cmp.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,292 @@
+/* $Id: sdp_cmp.c 295 2006-03-06 13:58:54Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 
+ */
+#include <pjmedia/sdp.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+
+
+/* Compare connection line. */
+static pj_status_t compare_conn(const pjmedia_sdp_conn *c1,
+				const pjmedia_sdp_conn *c2)
+{
+    /* Compare network type. */
+    if (pj_strcmp(&c1->net_type, &c2->net_type) != 0)
+	return PJMEDIA_SDP_ECONNNOTEQUAL;
+
+    /* Compare address type. */
+    if (pj_strcmp(&c1->addr_type, &c2->addr_type) != 0)
+	return PJMEDIA_SDP_ECONNNOTEQUAL;
+
+    /* Compare address. */
+    if (pj_strcmp(&c1->addr, &c2->addr) != 0)
+	return PJMEDIA_SDP_ECONNNOTEQUAL;
+
+    return PJ_SUCCESS;
+}
+
+/* Compare attributes array. */
+static pj_status_t compare_attr_imp(unsigned count1,
+				    pjmedia_sdp_attr *const attr1[],
+				    unsigned count2,
+				    pjmedia_sdp_attr *const attr2[])
+{
+    pj_status_t status;
+    unsigned i;
+    const pj_str_t inactive = { "inactive", 8 };
+    const pj_str_t sendrecv = { "sendrecv", 8 };
+    const pj_str_t sendonly = { "sendonly", 8 };
+    const pj_str_t recvonly = { "recvonly", 8 };
+    const pj_str_t fmtp = { "fmtp", 4 };
+    const pj_str_t rtpmap = { "rtpmap", 6 };
+
+    /* For simplicity, we only compare the following attributes, and ignore
+     * the others:
+     *	- direction, eg. inactive, sendonly, recvonly, sendrecv
+     *	- fmtp for each payload.
+     *	- rtpmap for each payload.
+     */
+    for (i=0; i<count1; ++i) {
+	const pjmedia_sdp_attr *a1 = attr1[i];
+
+	if (pj_strcmp(&a1->name, &inactive) == 0 || 
+	    pj_strcmp(&a1->name, &sendrecv) == 0 ||
+	    pj_strcmp(&a1->name, &sendonly) == 0 ||
+	    pj_strcmp(&a1->name, &recvonly) == 0)
+	{
+	    /* For inactive, sendrecv, sendonly, and recvonly attributes,
+	     * the same attribute must be present on the other SDP.
+	     */
+	    const pjmedia_sdp_attr *a2;
+	    a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, NULL);
+	    if (!a2)
+		return PJMEDIA_SDP_EDIRNOTEQUAL;
+
+	} else if (pj_strcmp(&a1->name, &fmtp) == 0) {
+	    /* For fmtp attribute, find the fmtp attribute in the other SDP
+	     * for the same payload type, and compare the fmtp param/value.
+	     */
+	    pjmedia_sdp_fmtp fmtp1, fmtp2;
+	    const pjmedia_sdp_attr *a2;
+
+	    status = pjmedia_sdp_attr_get_fmtp(a1, &fmtp1);
+	    if (status != PJ_SUCCESS)
+		return PJMEDIA_SDP_EFMTPNOTEQUAL;
+
+	    a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &fmtp1.fmt);
+	    if (!a2)
+		return PJMEDIA_SDP_EFMTPNOTEQUAL;
+
+	    status = pjmedia_sdp_attr_get_fmtp(a2, &fmtp2);
+	    if (status != PJ_SUCCESS)
+		return PJMEDIA_SDP_EFMTPNOTEQUAL;
+
+	    if (pj_strcmp(&fmtp1.fmt_param, &fmtp2.fmt_param) != 0)
+		return PJMEDIA_SDP_EFMTPNOTEQUAL;
+
+	} else if (pj_strcmp(&a1->name, &rtpmap) == 0) {
+	    /* For rtpmap attribute, find rtpmap attribute on the other SDP
+	     * for the same payload type, and compare both rtpmap atribute
+	     * values.
+	     */
+	    pjmedia_sdp_rtpmap r1, r2;
+	    const pjmedia_sdp_attr *a2;
+
+	    status = pjmedia_sdp_attr_get_rtpmap(a1, &r1);
+	    if (status != PJ_SUCCESS)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+
+	    a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &r1.pt);
+	    if (!a2)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+
+	    status = pjmedia_sdp_attr_get_rtpmap(a2, &r2);
+	    if (status != PJ_SUCCESS)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+
+	    if (pj_strcmp(&r1.pt, &r2.pt) != 0)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+	    if (pj_strcmp(&r1.enc_name, &r2.enc_name) != 0)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+	    if (r1.clock_rate != r2.clock_rate)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+	    if (pj_strcmp(&r1.param, &r2.param) != 0)
+		return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Compare attributes array. */
+static pj_status_t compare_attr(unsigned count1,
+				pjmedia_sdp_attr *const attr1[],
+				unsigned count2,
+				pjmedia_sdp_attr *const attr2[])
+{
+    pj_status_t status;
+
+    status = compare_attr_imp(count1, attr1, count2, attr2);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = compare_attr_imp(count2, attr2, count1, attr1);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+/* Compare media descriptor */
+PJ_DEF(pj_status_t) pjmedia_sdp_media_cmp( const pjmedia_sdp_media *sd1,
+					   const pjmedia_sdp_media *sd2,
+					   unsigned option)
+{
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(option);
+
+    /* Compare media type. */
+    if (pj_strcmp(&sd1->desc.media, &sd2->desc.media) != 0)
+	return PJMEDIA_SDP_EMEDIANOTEQUAL;
+
+    /* Compare port number. */
+    if (sd1->desc.port != sd2->desc.port)
+	return PJMEDIA_SDP_EPORTNOTEQUAL;
+
+    /* Compare port count. */
+    if (sd1->desc.port_count != sd2->desc.port_count)
+	return PJMEDIA_SDP_EPORTNOTEQUAL;
+
+    /* Compare transports. */
+    if (pj_strcmp(&sd1->desc.transport, &sd2->desc.transport) != 0)
+	return PJMEDIA_SDP_ETPORTNOTEQUAL;
+
+    /* Compare number of formats. */
+    if (sd1->desc.fmt_count != sd2->desc.fmt_count)
+	return PJMEDIA_SDP_EFORMATNOTEQUAL;
+
+    /* Compare formats, in order. */
+    for (i=0; i<sd1->desc.fmt_count; ++i) {
+	if (pj_strcmp(&sd1->desc.fmt[i], &sd2->desc.fmt[i]) != 0)
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+    }
+
+    /* Compare connection line, if they exist. */
+    if (sd1->conn) {
+	if (!sd2->conn)
+	    return PJMEDIA_SDP_EMEDIANOTEQUAL;
+	status = compare_conn(sd1->conn, sd2->conn);
+    } else {
+	if (sd2->conn)
+	    return PJMEDIA_SDP_EMEDIANOTEQUAL;
+    }
+
+    /* Compare attributes. */
+    status = compare_attr(sd1->attr_count, sd1->attr, 
+			  sd2->attr_count, sd2->attr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Looks equal */
+    return PJ_SUCCESS;
+}
+
+/*
+ * Compare two SDP session for equality.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1,
+					     const pjmedia_sdp_session *sd2,
+					     unsigned option)
+{
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(option);
+
+    /* Compare the origin line. */
+    if (pj_strcmp(&sd1->origin.user, &sd2->origin.user) != 0)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    if (sd1->origin.id != sd2->origin.id)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    if (sd1->origin.version != sd2->origin.version)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    if (pj_strcmp(&sd1->origin.net_type, &sd2->origin.net_type) != 0)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    if (pj_strcmp(&sd1->origin.addr_type, &sd2->origin.addr_type) != 0)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    if (pj_strcmp(&sd1->origin.addr, &sd2->origin.addr) != 0)
+	return PJMEDIA_SDP_EORIGINNOTEQUAL;
+
+    
+    /* Compare the subject line. */
+    if (pj_strcmp(&sd1->name, &sd2->name) != 0)
+	return PJMEDIA_SDP_ENAMENOTEQUAL;
+
+    /* Compare connection line, when they exist */
+    if (sd1->conn) {
+	if (!sd2->conn)
+	    return PJMEDIA_SDP_ECONNNOTEQUAL;
+	status = compare_conn(sd1->conn, sd2->conn);
+	if (status != PJ_SUCCESS)
+	    return status;
+    } else {
+	if (sd2->conn)
+	    return PJMEDIA_SDP_ECONNNOTEQUAL;
+    }
+
+    /* Compare time line. */
+    if (sd1->time.start != sd2->time.start)
+	return PJMEDIA_SDP_ETIMENOTEQUAL;
+
+    if (sd1->time.stop != sd2->time.stop)
+	return PJMEDIA_SDP_ETIMENOTEQUAL;
+
+    /* Compare attributes. */
+    status = compare_attr(sd1->attr_count, sd1->attr, 
+			  sd2->attr_count, sd2->attr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Compare media lines. */
+    if (sd1->media_count != sd2->media_count)
+	return PJMEDIA_SDP_EMEDIANOTEQUAL;
+
+    for (i=0; i<sd1->media_count; ++i) {
+	status = pjmedia_sdp_media_cmp(sd1->media[i], sd2->media[i], 0);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Looks equal. */
+    return PJ_SUCCESS;
+}
+
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_neg.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/pjsdp/src/sdp_neg.c	Mon Jul  3 22:03:31 2006
@@ -0,0 +1,988 @@
+/* $Id: sdp_neg.c 527 2006-06-19 14:48:06Z bennylp $ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny at prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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 
+ */
+#include <pjmedia/sdp_neg.h>
+#include <pjmedia/sdp.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/ctype.h>
+#include <pj/array.h>
+
+/**
+ * This structure describes SDP media negotiator.
+ */
+struct pjmedia_sdp_neg
+{
+    pjmedia_sdp_neg_state state;	    /**< Negotiator state.	     */
+    pj_bool_t		  has_remote_answer;
+    pj_bool_t		  answer_was_remote;
+
+    pjmedia_sdp_session	*initial_sdp,	    /**< Initial local SDP	     */
+			*active_local_sdp,  /**< Currently active local SDP. */
+			*active_remote_sdp, /**< Currently active remote's.  */
+			*neg_local_sdp,	    /**< Temporary local SDP.	     */
+			*neg_remote_sdp;    /**< Temporary remote SDP.	     */
+};
+
+static const char *state_str[] = 
+{
+    "STATE_NULL",
+    "STATE_LOCAL_OFFER",
+    "STATE_REMOTE_OFFER",
+    "STATE_WAIT_NEGO",
+    "STATE_DONE",
+};
+
+/*
+ * Get string representation of negotiator state.
+ */
+PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state)
+{
+    if (state >=0 && state < PJ_ARRAY_SIZE(state_str))
+	return state_str[state];
+
+    return "<?UNKNOWN?>";
+}
+
+
+/*
+ * Create with local offer.
+ */
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool,
+				      const pjmedia_sdp_session *local,
+				      pjmedia_sdp_neg **p_neg)
+{
+    pjmedia_sdp_neg *neg;
+    pj_status_t status;
+
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && local && p_neg, PJ_EINVAL);
+
+    *p_neg = NULL;
+
+    /* Validate local offer. */
+    PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(local))==PJ_SUCCESS, status);
+
+    /* Create and initialize negotiator. */
+    neg = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_neg));
+    PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
+
+    neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+    neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
+    neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
+
+    *p_neg = neg;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create with remote offer and initial local offer/answer.
+ */
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool,
+				      const pjmedia_sdp_session *initial,
+				      const pjmedia_sdp_session *remote,
+				      pjmedia_sdp_neg **p_neg)
+{
+    pjmedia_sdp_neg *neg;
+    pj_status_t status;
+
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && remote && p_neg, PJ_EINVAL);
+
+    *p_neg = NULL;
+
+    /* Validate remote offer and initial answer */
+    status = pjmedia_sdp_validate(remote);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create and initialize negotiator. */
+    neg = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_neg));
+    PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
+
+    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+
+    if (initial) {
+	PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(initial))==PJ_SUCCESS, 
+			 status);
+
+	neg->initial_sdp = pjmedia_sdp_session_clone(pool, initial);
+	neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, initial);
+
+	neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+
+    } else {
+	
+	neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
+
+    }
+
+    *p_neg = neg;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get SDP negotiator state.
+ */
+PJ_DEF(pjmedia_sdp_neg_state)
+pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg )
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL);
+    return neg->state;
+}
+
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session **local)
+{
+    PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
+    PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+    *local = neg->active_local_sdp;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg,
+				   const pjmedia_sdp_session **remote)
+{
+    PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
+    PJ_ASSERT_RETURN(neg->active_remote_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+    *remote = neg->active_remote_sdp;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_bool_t)
+pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg)
+{
+    PJ_ASSERT_RETURN(neg, PJ_FALSE);
+
+    return neg->answer_was_remote;
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg,
+				const pjmedia_sdp_session **remote)
+{
+    PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
+    PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJMEDIA_SDPNEG_ENONEG);
+
+    *remote = neg->neg_remote_sdp;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg,
+			       const pjmedia_sdp_session **local)
+{
+    PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
+    PJ_ASSERT_RETURN(neg->neg_local_sdp, PJMEDIA_SDPNEG_ENONEG);
+
+    *local = neg->neg_local_sdp;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify local SDP and wait for remote answer.
+ */
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool,
+				    pjmedia_sdp_neg *neg,
+				    const pjmedia_sdp_session *local)
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
+
+    /* Can only do this in STATE_DONE. */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    /* Change state to STATE_LOCAL_OFFER */
+    neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+    neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
+    neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool,
+				  pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session **offer)
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(neg && offer, PJ_EINVAL);
+
+    *offer = NULL;
+
+    /* Can only do this in STATE_DONE or STATE_LOCAL_OFFER. */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE ||
+		     neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    if (neg->state == PJMEDIA_SDP_NEG_STATE_DONE) {
+	/* If in STATE_DONE, set the active SDP as the offer. */
+	PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+	neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+	neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, 
+						       neg->active_local_sdp);
+	*offer = neg->active_local_sdp;
+
+    } else {
+	/* We assume that we're in STATE_LOCAL_OFFER.
+	 * In this case set the neg_local_sdp as the offer.
+	 */
+	*offer = neg->neg_local_sdp;
+    }
+
+    
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool,
+				   pjmedia_sdp_neg *neg,
+				   const pjmedia_sdp_session *remote)
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
+
+    /* Can only do this in STATE_LOCAL_OFFER.
+     * If we haven't provided local offer, then rx_remote_offer() should
+     * be called instead of this function.
+     */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    /* We're ready to negotiate. */
+    neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+    neg->has_remote_answer = PJ_TRUE;
+    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+ 
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool,
+				  pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session *remote)
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
+
+    /* Can only do this in STATE_DONE.
+     * If we already provide local offer, then rx_remote_answer() should
+     * be called instead of this function.
+     */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    /* State now is STATE_REMOTE_OFFER. */
+    neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
+    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) 
+pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool,
+				  pjmedia_sdp_neg *neg,
+				  const pjmedia_sdp_session *local)
+{
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
+
+    /* Can only do this in STATE_REMOTE_OFFER.
+     * If we already provide local offer, then rx_remote_answer() should
+     * be called instead of this function.
+     */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    /* State now is STATE_WAIT_NEGO. */
+    neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+    if (local)
+	neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
+    else {
+	PJ_ASSERT_RETURN(neg->initial_sdp, PJMEDIA_SDPNEG_ENOINITIAL);
+	neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp);
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg)
+{
+    pj_assert(neg && neg->state==PJMEDIA_SDP_NEG_STATE_WAIT_NEGO);
+    return !neg->has_remote_answer;
+}
+
+
+/* Swap string. */
+static void str_swap(pj_str_t *str1, pj_str_t *str2)
+{
+    pj_str_t tmp = *str1;
+    *str1 = *str2;
+    *str2 = tmp;
+}
+
+static void remove_all_media_directions(pjmedia_sdp_media *m)
+{
+    pjmedia_sdp_media_remove_all_attr(m, "inactive");
+    pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
+    pjmedia_sdp_media_remove_all_attr(m, "sendonly");
+    pjmedia_sdp_media_remove_all_attr(m, "recvonly");
+}
+
+/* Update media direction based on peer's media direction */
+static void update_media_direction(pj_pool_t *pool,
+				   const pjmedia_sdp_media *remote,
+				   pjmedia_sdp_media *local)
+{
+   if (pjmedia_sdp_media_find_attr2(remote, "inactive", NULL) != NULL) {
+	/* If remote has "a=inactive", then local is inactive too */
+
+	pjmedia_sdp_attr *a;
+
+	remove_all_media_directions(local);
+
+	a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
+	pjmedia_sdp_media_add_attr(local, a);
+
+    } else if(pjmedia_sdp_media_find_attr2(remote, "sendonly", NULL) != NULL) {
+	/* If remote has "a=sendonly", then set local to "recvonly" if
+	 * it is currently "sendrecv".
+	 */
+    
+	pjmedia_sdp_attr *a;
+
+	remove_all_media_directions(local);
+
+	a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
+	pjmedia_sdp_media_add_attr(local, a);
+
+    } else if(pjmedia_sdp_media_find_attr2(remote, "recvonly", NULL) != NULL) {
+	/* If remote has "a=recvonly", then set local to "sendonly" if
+	 * it is currently "sendrecv".
+	 */
+    
+	pjmedia_sdp_attr *a;
+
+	remove_all_media_directions(local);
+
+	a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
+	pjmedia_sdp_media_add_attr(local, a);
+
+    } else if(pjmedia_sdp_media_find_attr2(remote, "sendrecv", NULL) != NULL) {
+
+	pjmedia_sdp_attr *a;
+
+	remove_all_media_directions(local);
+
+	a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
+	pjmedia_sdp_media_add_attr(local, a);
+
+    } else {
+	/* Otherwise remote is set to "sendrecv".
+	 */
+	remove_all_media_directions(local);
+    }
+}
+
+/* Update single local media description to after receiving answer
+ * from remote.
+ */
+static pj_status_t process_m_answer( pj_pool_t *pool,
+				     pjmedia_sdp_media *offer,
+				     pjmedia_sdp_media *answer,
+				     pj_bool_t allow_asym)
+{
+    unsigned i;
+
+    /* Check that the media type match our offer. */
+
+    if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) {
+	/* The media type in the answer is different than the offer! */
+	return PJMEDIA_SDPNEG_EINVANSMEDIA;
+    }
+
+
+    /* Chec that transport in the answer match our offer. */
+
+    if (pj_strcmp(&answer->desc.transport, 
+		  &offer->desc.transport)!=0) 
+    {
+	/* The transport in the answer is different than the offer! */
+	return PJMEDIA_SDPNEG_EINVANSTP;
+    }
+
+    /* Check if remote has rejected our offer */
+    
+    if (answer->desc.port == 0) {
+	
+	/* Remote has rejected our offer. 
+	 * Set our port to zero too in active SDP.
+	 */
+	offer->desc.port = 0;
+    }
+
+
+    /* Process direction attributes */
+    update_media_direction(pool, answer, offer);
+ 
+    /* If asymetric media is allowed, then just check that remote answer has 
+     * codecs that are within the offer. 
+     *
+     * Otherwise if asymetric media is not allowed, then we will choose only
+     * one codec in our initial offer to match the answer.
+     */
+    if (allow_asym) {
+	for (i=0; i<answer->desc.fmt_count; ++i) {
+	    unsigned j;
+	    pj_str_t *rem_fmt = &answer->desc.fmt[i];
+
+	    for (j=0; j<offer->desc.fmt_count; ++j) {
+		if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0)
+		    break;
+	    }
+
+	    if (j != offer->desc.fmt_count) {
+		/* Found at least one common codec. */
+		break;
+	    }
+	}
+
+	if (i == answer->desc.fmt_count) {
+	    /* No common codec in the answer! */
+	    return PJMEDIA_SDPNEG_EANSNOMEDIA;
+	}
+
+	PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED);
+
+    } else {
+	/* Remove all format in the offer that has no matching answer */
+	for (i=0; i<offer->desc.fmt_count;) {
+	    unsigned pt;
+	    pj_uint32_t j;
+	    pj_str_t *fmt = &offer->desc.fmt[i];
+	    
+
+	    /* Find matching answer */
+	    pt = pj_strtoul(fmt);
+
+	    if (pt < 96) {
+		for (j=0; j<answer->desc.fmt_count; ++j) {
+		    if (pj_strcmp(fmt, &answer->desc.fmt[j])==0)
+			break;
+		}
+	    } else {
+		/* This is dynamic payload type.
+		 * For dynamic payload type, we must look the rtpmap and
+		 * compare the encoding name.
+		 */
+		const pjmedia_sdp_attr *a;
+		pjmedia_sdp_rtpmap or;
+
+		/* Get the rtpmap for the payload type in the offer. */
+		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
+		if (!a) {
+		    pj_assert(!"Bug! Offer should have been validated");
+		    return PJ_EBUG;
+		}
+		pjmedia_sdp_attr_get_rtpmap(a, &or);
+
+		/* Find paylaod in answer SDP with matching 
+		 * encoding name and clock rate.
+		 */
+		for (j=0; j<answer->desc.fmt_count; ++j) {
+		    a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
+						     &answer->desc.fmt[j]);
+		    if (a) {
+			pjmedia_sdp_rtpmap ar;
+			pjmedia_sdp_attr_get_rtpmap(a, &ar);
+
+			/* See if encoding name, clock rate, and channel
+			 * count match 
+			 */
+			if (!pj_strcmp(&or.enc_name, &ar.enc_name) &&
+			    or.clock_rate == ar.clock_rate &&
+			    pj_strcmp(&or.param, &ar.param)==0)
+			{
+			    /* Match! */
+			    break;
+			}
+		    }
+		}
+	    }
+
+	    if (j == answer->desc.fmt_count) {
+		/* This format has no matching answer.
+		 * Remove it from our offer.
+		 */
+		pjmedia_sdp_attr *a;
+
+		/* Remove rtpmap associated with this format */
+		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
+		if (a)
+		    pjmedia_sdp_media_remove_attr(offer, a);
+
+		/* Remove fmtp associated with this format */
+		a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt);
+		if (a)
+		    pjmedia_sdp_media_remove_attr(offer, a);
+
+		/* Remove this format from offer's array */
+		pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]),
+			       offer->desc.fmt_count, i);
+		--offer->desc.fmt_count;
+
+	    } else {
+		++i;
+	    }
+	}
+
+	/* Arrange format in the offer so the order match the priority
+	 * in the answer
+	 */
+	for (i=0; i<answer->desc.fmt_count; ++i) {
+	    unsigned j;
+	    pj_str_t *fmt = &answer->desc.fmt[i];
+
+	    for (j=i; j<offer->desc.fmt_count; ++j) {
+		if (pj_strcmp(fmt, &offer->desc.fmt[j])==0) {
+		    str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]);
+		    break;
+		}
+	    }
+	}
+    }
+
+    /* Looks okay */
+    return PJ_SUCCESS;
+}
+
+
+/* Update local media session (offer) to create active local session
+ * after receiving remote answer.
+ */
+static pj_status_t process_answer(pj_pool_t *pool,
+				  pjmedia_sdp_session *offer,
+				  pjmedia_sdp_session *answer,
+				  pj_bool_t allow_asym,
+				  pjmedia_sdp_session **p_active)
+{
+    unsigned mi;
+    pj_bool_t has_active = PJ_FALSE;
+    pj_status_t status;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL);
+
+    /* Check that media count match between offer and answer */
+    if (offer->media_count != answer->media_count)
+	return PJMEDIA_SDPNEG_EMISMEDIA;
+
+    /* Now update each media line in the offer with the answer. */
+    for (mi=0; mi<offer->media_count; ++mi) {
+	status = process_m_answer(pool, offer->media[mi], answer->media[mi],
+				  allow_asym);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (offer->media[mi]->desc.port != 0)
+	    has_active = PJ_TRUE;
+    }
+
+    *p_active = offer;
+
+    return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA;
+}
+
+/* Try to match offer with answer. */
+static pj_bool_t match_offer(pj_pool_t *pool,
+			     const pjmedia_sdp_media *offer,
+			     const pjmedia_sdp_media *local,
+			     pjmedia_sdp_media **p_answer)
+{
+    unsigned i;
+    pj_bool_t offer_has_codec = 0,
+	      offer_has_telephone_event = 0,
+	      offer_has_other = 0,
+	      found_matching_codec = 0,
+	      found_matching_telephone_event = 0,
+	      found_matching_other = 0;
+    unsigned pt_answer_count = 0;
+    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
+    pjmedia_sdp_media *answer;
+
+    /* With the addition of telephone-event and dodgy MS RTC SDP, 
+     * the answer generation algorithm looks really shitty...
+     */
+    for (i=0; i<offer->desc.fmt_count; ++i) {
+	unsigned j;
+	
+	if (pj_isdigit(*offer->desc.fmt[i].ptr)) {
+	    /* This is normal/standard payload type, where it's identified
+	     * by payload number.
+	     */
+	    unsigned pt;
+
+	    pt = pj_strtoul(&offer->desc.fmt[i]);
+	    
+	    if (pt < 96) {
+		/* For static payload type, it's enough to compare just
+		 * the payload number.
+		 */
+
+		offer_has_codec = 1;
+
+		/* We just need to select one codec. 
+		 * Continue if we have selected matching codec for previous 
+		 * payload.
+		 */
+		if (found_matching_codec)
+		    continue;
+
+		/* Find matching codec in local descriptor. */
+		for (j=0; j<local->desc.fmt_count; ++j) {
+		    unsigned p;
+		    p = pj_strtoul(&local->desc.fmt[j]);
+		    if (p == pt && pj_isdigit(*local->desc.fmt[j].ptr)) {
+			found_matching_codec = 1;
+			pt_answer[pt_answer_count++] = local->desc.fmt[j];
+			break;
+		    }
+		}
+
+	    } else {
+		/* This is dynamic payload type.
+		 * For dynamic payload type, we must look the rtpmap and
+		 * compare the encoding name.
+		 */
+		const pjmedia_sdp_attr *a;
+		pjmedia_sdp_rtpmap or;
+		pj_bool_t is_codec;
+
+		/* Get the rtpmap for the payload type in the offer. */
+		a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", 
+						 &offer->desc.fmt[i]);
+		if (!a) {
+		    pj_assert(!"Bug! Offer should have been validated");
+		    return PJ_FALSE;
+		}
+		pjmedia_sdp_attr_get_rtpmap(a, &or);
+
+		if (!pj_strcmp2(&or.enc_name, "telephone-event")) {
+		    offer_has_telephone_event = 1;
+		    if (found_matching_telephone_event)
+			continue;
+		    is_codec = 0;
+		} else {
+		    offer_has_codec = 1;
+		    if (found_matching_codec)
+			continue;
+		    is_codec = 1;
+		}
+		
+		/* Find paylaod in our initial SDP with matching 
+		 * encoding name and clock rate.
+		 */
+		for (j=0; j<local->desc.fmt_count; ++j) {
+		    a = pjmedia_sdp_media_find_attr2(local, "rtpmap", 
+						     &local->desc.fmt[j]);
+		    if (a) {
+			pjmedia_sdp_rtpmap lr;
+			pjmedia_sdp_attr_get_rtpmap(a, &lr);
+
+			/* See if encoding name, clock rate, and
+			 * channel count  match 
+			 */
+			if (!pj_strcmp(&or.enc_name, &lr.enc_name) &&
+			    or.clock_rate == lr.clock_rate &&
+			    pj_strcmp(&or.param, &lr.param)==0) 
+			{
+			    /* Match! */
+			    if (is_codec)
+				found_matching_codec = 1;
+			    else
+				found_matching_telephone_event = 1;
+			    pt_answer[pt_answer_count++] = local->desc.fmt[j];
+			    break;
+			}
+		    }
+		}
+	    }
+
+	} else {
+	    /* This is a non-standard, brain damaged SDP where the payload
+	     * type is non-numeric. It exists e.g. in Microsoft RTC based
+	     * UA, to indicate instant messaging capability.
+	     * Example:
+	     *	- m=x-ms-message 5060 sip null
+	     */
+	    offer_has_other = 1;
+	    if (found_matching_other)
+		continue;
+
+	    for (j=0; j<local->desc.fmt_count; ++j) {
+		if (!pj_strcmp(&offer->desc.fmt[i], &local->desc.fmt[j])) {
+		    /* Match */
+		    found_matching_other = 1;
+		    pt_answer[pt_answer_count++] = local->desc.fmt[j];
+		    break;
+		}
+	    }
+	}
+    }
+
+    /* See if all types of offer can be matched. */
+#if 1
+    if ((offer_has_codec && !found_matching_codec) ||
+	(offer_has_telephone_event && !found_matching_telephone_event) ||
+	(offer_has_other && !found_matching_other))
+    {
+	/* Some of the payload in the offer has no matching local sdp */
+	return PJ_FALSE;
+    }
+#else
+    PJ_TODO(FULL_MATCHING_WITH_TELEPHONE_EVENTS);
+    if (!found_matching_codec &&
+	!found_matching_telephone_event &&
+	!found_matching_other)
+    {
+	/* Some of the payload in the offer has no matching local sdp */
+	return PJ_FALSE;
+    }
+#endif
+
+    /* Seems like everything is in order.
+     * Build the answer by cloning from local media, but rearrange the payload
+     * to suit the offer.
+     */
+    answer = pjmedia_sdp_media_clone(pool, local);
+    for (i=0; i<pt_answer_count; ++i) {
+	unsigned j;
+	for (j=i; j<answer->desc.fmt_count; ++j) {
+	    if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
+		break;
+	}
+	pj_assert(j != answer->desc.fmt_count);
+	str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
+    }
+    
+    /* Remove unwanted local formats. */
+    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
+	pjmedia_sdp_attr *a;
+
+	/* Remove rtpmap for this format */
+	a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
+					 &answer->desc.fmt[i]);
+	if (a) {
+	    pjmedia_sdp_media_remove_attr(answer, a);
+	}
+
+	/* Remove fmtp for this format */
+	a = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
+					 &answer->desc.fmt[i]);
+	if (a) {
+	    pjmedia_sdp_media_remove_attr(answer, a);
+	}
+    }
+    answer->desc.fmt_count = pt_answer_count;
+
+    /* If offer has zero port, set our answer with zero port too */
+    if (offer->desc.port==0)
+	answer->desc.port = 0;
+
+    /* Update media direction. */
+    update_media_direction(pool, offer, answer);
+
+    *p_answer = answer;
+    return PJ_TRUE;
+}
+
+/* Create complete answer for remote's offer. */
+static pj_status_t create_answer( pj_pool_t *pool,
+				  const pjmedia_sdp_session *initial,
+				  const pjmedia_sdp_session *offer,
+				  pjmedia_sdp_session **p_answer)
+{
+    pj_status_t status;
+    pj_bool_t has_active = PJ_FALSE;
+    pjmedia_sdp_session *answer;
+    char media_used[PJMEDIA_MAX_SDP_MEDIA];
+    unsigned i;
+
+    /* Validate remote offer. 
+     * This should have been validated before.
+     */
+    PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status);
+
+    /* Create initial answer by duplicating initial SDP,
+     * but clear all media lines. The media lines will be filled up later.
+     */
+    answer = pjmedia_sdp_session_clone(pool, initial);
+    PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM);
+
+    answer->media_count = 0;
+
+    pj_memset(media_used, 0, sizeof(media_used));
+
+    /* For each media line, create our answer based on our initial
+     * capability.
+     */
+    for (i=0; i<offer->media_count; ++i) {
+	const pjmedia_sdp_media *om;	/* offer */
+	const pjmedia_sdp_media *im;	/* initial media */
+	pjmedia_sdp_media *am = NULL;	/* answer/result */
+	unsigned j;
+
+	om = offer->media[i];
+
+	/* Find media description in our initial capability that matches
+	 * the media type and transport type of offer's media, has
+	 * matching codec, and has not been used to answer other offer.
+	 */
+	for (im=NULL, j=0; j<initial->media_count; ++j) {
+	    im = initial->media[j];
+	    if (pj_strcmp(&om->desc.media, &im->desc.media)==0 &&
+		pj_strcmp(&om->desc.transport, &im->desc.transport)==0 &&
+		media_used[j] == 0)
+	    {
+		/* See if it has matching codec. */
+		pj_bool_t match;
+
+		match = match_offer(pool, om, im, &am);
+		if (match) {
+		    /* Mark media as used. */
+		    media_used[j] = 1;
+		    break;
+		}
+	    }
+	}
+
+	if (j==initial->media_count) {
+	    /* No matching media.
+	     * Reject the offer by setting the port to zero in the answer.
+	     */
+	    /* For simplicity in the construction of the answer, we'll
+	     * just clone the media from the offer. Anyway receiver will
+	     * ignore anything in the media once it sees that the port
+	     * number is zero.
+	     */
+	    am = pjmedia_sdp_media_clone(pool, om);
+	    am->desc.port = 0;
+
+	    /* Match direction */
+	    update_media_direction(pool, om, am);
+
+	} else {
+	    /* The answer is in am */
+	    pj_assert(am != NULL);
+	}
+
+	/* Add the media answer */
+	answer->media[answer->media_count++] = am;
+
+	/* Check if this media is active.*/
+	if (am->desc.port != 0)
+	    has_active = PJ_TRUE;
+    }
+
+    *p_answer = answer;
+
+    return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA;
+    //return PJ_SUCCESS;
+}
+
+/* The best bit: SDP negotiation function! */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
+					       pjmedia_sdp_neg *neg,
+					       pj_bool_t allow_asym)
+{
+    pj_status_t status;
+
+    /* Check arguments are valid. */
+    PJ_ASSERT_RETURN(pool && neg, PJ_EINVAL);
+
+    /* Must be in STATE_WAIT_NEGO state. */
+    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, 
+		     PJMEDIA_SDPNEG_EINSTATE);
+
+    /* Must have remote offer. */
+    PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJ_EBUG);
+
+    if (neg->has_remote_answer) {
+	pjmedia_sdp_session *active;
+	status = process_answer(pool, neg->neg_local_sdp, neg->neg_remote_sdp,
+			        allow_asym, &active);
+	if (status == PJ_SUCCESS) {
+	    /* Only update active SDPs when negotiation is successfull */
+	    neg->active_local_sdp = active;
+	    neg->active_remote_sdp = neg->neg_remote_sdp;
+
+	}
+    } else {
+	pjmedia_sdp_session *answer = NULL;
+
+	status = create_answer(pool, neg->neg_local_sdp, neg->neg_remote_sdp,
+			       &answer);
+	if (status == PJ_SUCCESS) {
+	    pj_uint32_t active_ver;
+
+	    if (neg->active_local_sdp)
+		active_ver = neg->active_local_sdp->origin.version;
+	    else
+		active_ver = neg->initial_sdp->origin.version;
+
+	    /* Only update active SDPs when negotiation is successfull */
+	    neg->active_local_sdp = answer;
+	    neg->active_remote_sdp = neg->neg_remote_sdp;
+
+	    /* Increment SDP version */
+	    neg->active_local_sdp->origin.version = ++active_ver;
+	}
+    }
+
+    /* State is DONE regardless */
+    neg->state = PJMEDIA_SDP_NEG_STATE_DONE;
+
+    /* Save state */
+    neg->answer_was_remote = neg->has_remote_answer;
+
+    /* Clear temporary SDP */
+    neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
+    neg->has_remote_answer = PJ_FALSE;
+
+    return status;
+}
+

Added: freeswitch/branches/stkn/src/mod/endpoints/mod_pjsip/pjsdp/user.mak
==============================================================================



More information about the Freeswitch-branches mailing list