[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