Added pjsip-apps top level projects

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@254 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2006-03-01 19:31:18 +00:00
parent cbf3740e9b
commit 8df5b021c5
11 changed files with 2828 additions and 3 deletions

View File

@ -1,7 +1,7 @@
include build.mak
include build/host-$(HOST_NAME).mak
DIRS = pjlib pjlib-util pjmedia pjsip
DIRS = pjlib pjlib-util pjmedia pjsip pjsip-apps
ifdef MINSIZE
MAKE_FLAGS := MINSIZE=1
@ -21,9 +21,12 @@ all clean dep depend distclean doc print realclean:
LIBS = pjlib/lib/libpj-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjlib-util/lib/libpjlib-util-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjmedia/lib/libpjmedia-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjmedia/lib/libpjmedia-codec-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjsip/lib/libpjsip-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjsip/lib/libpjsip-ua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a
BINS = pjsip/bin/pjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE)
pjsip/lib/libpjsip-ua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjsip/lib/libpjsip-simple-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a \
pjsip/lib/libpjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).a
BINS = pjsip-apps/bin/pjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE)
size:
@echo -n 'Date: '

80
pjsip-apps/build/Makefile Normal file
View File

@ -0,0 +1,80 @@
include ../../build/common.mak
RULES_MAK := ../../build/rules.mak
PJLIB_LIB:=../../pjlib/lib/libpj-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJMEDIA_LIB:=../../pjmedia/lib/libpjmedia-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJMEDIA_CODEC_LIB:=../../pjmedia/lib/libpjmedia-codec-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJSIP_LIB:=../../pjsip/lib/libpjsip-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJSIP_UA_LIB:=../../pjsip/lib/libpjsip-ua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJSIP_SIMPLE_LIB:=../../pjsip/lib/libpjsip-simple-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
PJSUA_LIB_LIB=../../pjsip/lib/libpjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT)
###############################################################################
# Gather all flags.
#
export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \
$(CFLAGS) $(CC_INC)../../pjsip/include $(CC_INC)../../pjlib/include \
$(CC_INC)../../pjlib-util/include $(CC_INC)../../pjmedia/include
export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \
$(HOST_CXXFLAGS) $(CXXFLAGS)
export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJSUA_LIB_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_UA_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_SIMPLE_LIB)) \
$(subst /,$(HOST_PSEP),$(PJSIP_LIB)) \
$(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \
$(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \
$(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \
$(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \
$(LDFLAGS) -lm
###############################################################################
# Defines for building PJSUA
#
export PJSUA_SRCDIR = ../src/pjsua
export PJSUA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
main.o
export PJSUA_CFLAGS += $(_CFLAGS)
export PJSUA_LDFLAGS += $(_LDFLAGS)
export PJSUA_EXE:=../bin/pjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE)
export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
###############################################################################
# Main entry
#
#
TARGETS := pjsua
.PHONY: $(TARGETS)
all: $(TARGETS)
doc:
cd .. && doxygen docs/doxygen.cfg
dep: depend
distclean: realclean
.PHONY: dep depend pjsua clean realclean distclean
pjsua:
$(MAKE) -f $(RULES_MAK) APP=PJSUA app=pjsua $(PJSUA_EXE)
.PHONY: ../lib/pjsua.ko
../lib/pjsua.ko:
$(MAKE) -f $(RULES_MAK) APP=PJSUA app=pjsua $@
clean depend realclean:
$(MAKE) -f $(RULES_MAK) APP=PJSUA app=pjsua $@
@if test "$@" == "depend"; then \
echo '$(PJSUA_EXE): $(PJSIP_LIB) $(PJSIP_UA_LIB) $(PJSIP_SIMPLE) $(PJSUA_LIB_LIB) $(PJLIB_LIB) $(PJLIB_UTIL_LIB) $(PJMEDIA_LIB) $(PJMEDIA_CODEC_LIB)' >> .pjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).depend; \
fi

View File

@ -0,0 +1,2 @@
export LDFLAGS += -lwinmm

View File

@ -0,0 +1,185 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "pjlib"="..\..\pjlib\build\pjlib.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjlib_util"="..\..\pjlib-util\build\pjlib_util.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjmedia"="..\..\pjmedia\build\pjmedia.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjmedia_codec"="..\..\pjmedia\build\pjmedia_codec.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjsip_core"="..\..\pjsip\build\pjsip_core.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjsip_perf"=".\pjsip_perf.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name pjlib
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjlib_util
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjmedia
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjmedia_codec
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_core
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_simple
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_ua
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsua_lib
End Project Dependency
}}}
###############################################################################
Project: "pjsip_simple"="..\..\pjsip\build\pjsip_simple.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjsip_ua"="..\..\pjsip\build\pjsip_ua.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "pjsua"=".\pjsua.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name pjlib
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjlib_util
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjmedia
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjmedia_codec
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_core
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_simple
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsip_ua
End Project Dependency
Begin Project Dependency
Project_Dep_Name pjsua_lib
End Project Dependency
}}}
###############################################################################
Project: "pjsua_lib"="..\..\pjsip\build\pjsua_lib.dsp" - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,114 @@
# Microsoft Developer Studio Project File - Name="pjsip_perf" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=pjsip_perf - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "pjsip_perf.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "pjsip_perf.mak" CFG="pjsip_perf - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "pjsip_perf - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "pjsip_perf - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "pjsip_perf - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir ".\output\pjsip-perf-i386-win32-vc6-release"
# PROP BASE Intermediate_Dir ".\output\pjsip-perf-i386-win32-vc6-release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir ".\output\pjsip-perf-i386-win32-vc6-release"
# PROP Intermediate_Dir ".\output\pjsip-perf-i386-win32-vc6-release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../pjsip/include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\bin\pjsip-perf-i386-win32-vc6d.exe"
!ELSEIF "$(CFG)" == "pjsip_perf - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir ".\output\pjsip-perf-i386-win32-vc6-debug"
# PROP BASE Intermediate_Dir ".\output\pjsip-perf-i386-win32-vc6-debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir ".\output\pjsip-perf-i386-win32-vc6-debug"
# PROP Intermediate_Dir ".\output\pjsip-perf-i386-win32-vc6-debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\bin\pjsip-perf-i386-win32-vc6d.exe" /pdbtype:sept
!ENDIF
# Begin Target
# Name "pjsip_perf - Win32 Release"
# Name "pjsip_perf - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE="..\src\pjsip-perf\handler_call.c"
# End Source File
# Begin Source File
SOURCE="..\src\pjsip-perf\handler_options.c"
# End Source File
# Begin Source File
SOURCE="..\src\pjsip-perf\main.c"
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE="..\src\pjsip-perf\pjsip_perf.h"
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

105
pjsip-apps/build/pjsua.dsp Normal file
View File

@ -0,0 +1,105 @@
# Microsoft Developer Studio Project File - Name="pjsua" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=pjsua - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "pjsua.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "pjsua.mak" CFG="pjsua - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "pjsua - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "pjsua - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/pjproject/pjsip/build", RIAAAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "pjsua - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /map /machine:I386 /out:"../bin/pjsua_vc6.exe" /fixed:no
# SUBTRACT LINK32 /pdb:none /debug
!ELSEIF "$(CFG)" == "pjsua - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjsua_vc6d.exe" /pdbtype:sept
!ENDIF
# Begin Target
# Name "pjsua - Win32 Release"
# Name "pjsua - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=..\src\pjsua\main.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

View File

@ -0,0 +1,412 @@
/* $Id$ */
/*
* Copyright (C) 2003-2006 Benny Prijono <benny@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 "pjsip_perf.h"
/*
* This file handles call generation and incoming calls.
*/
#define THIS_FILE "handler_call.c"
/*
* Dummy SDP.
*/
static pjmedia_sdp_session *local_sdp;
#define TIMER_ID 1234
/* Call data, to be attached to invite session. */
struct call_data
{
pjsip_inv_session *inv;
pj_bool_t confirmed;
pj_timer_entry bye_timer;
void *test_data;
void (*completion_cb)(void*,pj_bool_t);
};
/****************************************************************************
*
* INCOMING CALL HANDLER
*
****************************************************************************
*/
static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
/* The module instance. */
static pjsip_module mod_call =
{
NULL, NULL, /* prev, next. */
{ "mod-perf-call", 13 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&mod_call_on_rx_request, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
/*
* Handle incoming requests.
* Because this module is registered to the INVITE module too, this
* callback may be called for requests inside a dialog.
*/
static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
{
pjsip_msg *msg = rdata->msg_info.msg;
pjsip_dialog *dlg;
pjsip_inv_session *inv;
pjsip_tx_data *response;
struct call_data *call_data;
unsigned options;
pj_status_t status;
/* Don't want to handle anything but INVITE */
if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
return PJ_FALSE;
/* Don't want to handle request that's already associated with
* existing dialog or transaction.
*/
if (pjsip_rdata_get_dlg(rdata) || pjsip_rdata_get_tsx(rdata))
return PJ_FALSE;
/* Verify that we can handle the request. */
options = 0;
status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
settings.endpt, &response);
if (status != PJ_SUCCESS) {
/*
* No we can't handle the incoming INVITE request.
*/
if (response) {
pjsip_response_addr res_addr;
pjsip_get_response_addr(response->pool, rdata, &res_addr);
pjsip_endpt_send_response(settings.endpt, &res_addr, response,
NULL, NULL);
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(settings.endpt, rdata, 500, NULL,
NULL, NULL);
}
return PJ_TRUE;
}
/*
* Yes we can handle the incoming INVITE request.
*/
/* Create dialog. */
status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dlg);
if (status != PJ_SUCCESS) {
pjsip_dlg_respond(dlg, rdata, 500, NULL);
return PJ_TRUE;
}
/* Create invite session: */
status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &inv);
if (status != PJ_SUCCESS) {
pjsip_dlg_respond(dlg, rdata, 500, NULL);
// TODO: Need to delete dialog
return PJ_TRUE;
}
/* Create and associate call data. */
call_data = pj_pool_zalloc(inv->pool, sizeof(struct call_data));
call_data->inv = inv;
call_data->bye_timer.user_data = call_data;
inv->mod_data[mod_call.id] = call_data;
/* Answer with 200 straight away. */
status = pjsip_inv_initial_answer(inv, rdata, 200,
NULL, NULL, &response);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create 200 response", status);
pjsip_dlg_respond(dlg, rdata, 500, NULL);
// TODO: Need to delete dialog
} else {
status = pjsip_inv_send_msg(inv, response, NULL);
if (status != PJ_SUCCESS)
app_perror(THIS_FILE, "Unable to send 100 response", status);
}
return PJ_TRUE;
}
/****************************************************************************
*
* OUTGOING CALL GENERATOR
*
****************************************************************************
*/
/**
* Make outgoing call.
*/
pj_status_t call_spawn_test( const pj_str_t *target,
const pj_str_t *from,
const pj_str_t *to,
unsigned cred_cnt,
const pjsip_cred_info cred[],
const pjsip_route_hdr *route_set,
void *test_data,
void (*completion_cb)(void*,pj_bool_t))
{
pjsip_dialog *dlg;
pjsip_inv_session *inv;
pjsip_tx_data *tdata;
struct call_data *call_data;
pj_status_t status;
/* Create outgoing dialog: */
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
from, NULL,
to, target,
&dlg);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Dialog creation failed", status);
return status;
}
/* Create the INVITE session: */
status = pjsip_inv_create_uac( dlg, local_sdp, 0, &inv);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Invite session creation failed", status);
goto on_error;
}
/* Set dialog Route-Set: */
if (route_set)
pjsip_dlg_set_route_set(dlg, route_set);
/* Set credentials: */
pjsip_auth_clt_set_credentials( &dlg->auth_sess, cred_cnt, cred);
/* Create initial INVITE: */
status = pjsip_inv_invite(inv, &tdata);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create initial INVITE request",
status);
goto on_error;
}
/* Create and associate our call data */
call_data = pj_pool_zalloc(inv->pool, sizeof(struct call_data));
call_data->inv = inv;
call_data->test_data = test_data;
call_data->bye_timer.user_data = call_data;
call_data->completion_cb = completion_cb;
inv->mod_data[mod_call.id] = call_data;
/* Send initial INVITE: */
status = pjsip_inv_send_msg(inv, tdata, NULL);
if (status != PJ_SUCCESS) {
app_perror( THIS_FILE, "Unable to send initial INVITE request",
status);
goto on_error;
}
return PJ_SUCCESS;
on_error:
PJ_TODO(DESTROY_DIALOG_ON_FAIL);
return status;
}
/* Timer callback to send BYE. */
static void bye_callback( pj_timer_heap_t *ht, pj_timer_entry *e)
{
struct call_data *call_data = e->user_data;
pjsip_tx_data *tdata;
pj_status_t status;
e->id = 0;
status = pjsip_inv_end_session(call_data->inv, PJSIP_SC_REQUEST_TIMEOUT,
NULL, &tdata);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create BYE", status);
return;
}
status = pjsip_inv_send_msg(call_data->inv, tdata, NULL);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to send BYE", status);
return;
}
}
/*
* This callback receives notification from invite session when the
* session state has changed.
*/
static void call_on_state_changed( pjsip_inv_session *inv, pjsip_event *e)
{
struct call_data *call_data;
call_data = inv->mod_data[mod_call.id];
if (call_data == NULL)
return;
/* Once call has been confirmed, schedule timer to terminate the call. */
if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
pj_time_val interval;
call_data->confirmed = PJ_TRUE;
/* For UAC, schedule time to send BYE.
* For UAS, schedule time to disconnect INVITE, just in case BYE
* is not received.
*/
if (inv->role == PJSIP_ROLE_UAC)
interval.sec = settings.duration, interval.msec = 0;
else
interval.sec = settings.duration+5, interval.msec = 0;
call_data->bye_timer.id = TIMER_ID;
call_data->bye_timer.cb = &bye_callback;
pjsip_endpt_schedule_timer(settings.endpt, &call_data->bye_timer,
&interval);
}
/* If call has been terminated, cancel our timer, if any.
* And call tester's callback.
*/
else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
/* Cancel timer, if any. */
if (call_data->bye_timer.id == TIMER_ID) {
call_data->bye_timer.id = 0;
pjsip_endpt_cancel_timer(settings.endpt, &call_data->bye_timer);
}
/* Detach call data from the invite session. */
inv->mod_data[mod_call.id] = NULL;
/* Call tester callback. */
if (call_data->completion_cb) {
(*call_data->completion_cb)(call_data->test_data,
call_data->confirmed);
}
}
}
/*
* This callback is called by invite session framework when UAC session
* has forked.
*/
static void call_on_forked( pjsip_inv_session *inv, pjsip_event *e)
{
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
PJ_TODO(HANDLE_FORKED_DIALOG);
}
/****************************************************************************
*
* INITIALIZATION
*
****************************************************************************
*/
pj_status_t call_handler_init(void)
{
pjsip_inv_callback inv_cb;
pjmedia_sock_info skinfo;
pj_status_t status;
/* Register incoming call handler. */
status = pjsip_endpt_register_module(settings.endpt, &mod_call);
if (status != PJ_SUCCESS) {
app_perror( THIS_FILE, "Unable to register call handler",
status);
return status;
}
/* Invite session callback: */
pj_memset(&inv_cb, 0, sizeof(inv_cb));
inv_cb.on_state_changed = &call_on_state_changed;
inv_cb.on_new_session = &call_on_forked;
/* Initialize invite session module: */
status = pjsip_inv_usage_init(settings.endpt, &mod_call, &inv_cb);
if (status != PJ_SUCCESS) {
app_perror( THIS_FILE, "Unable to initialize INVITE session module",
status);
return status;
}
/* Create dummy SDP. */
pj_memset(&skinfo, 0, sizeof(skinfo));
pj_sockaddr_in_init(&skinfo.rtp_addr_name, pj_gethostname(), 4000);
pj_sockaddr_in_init(&skinfo.rtcp_addr_name, pj_gethostname(), 4001);
status = pjmedia_endpt_create_sdp( settings.med_endpt, settings.pool,
1, &skinfo, &local_sdp);
if (status != PJ_SUCCESS) {
app_perror( THIS_FILE, "Unable to generate local SDP",
status);
return status;
}
return PJ_SUCCESS;
}

View File

@ -0,0 +1,148 @@
/* $Id$ */
/*
* Copyright (C) 2003-2006 Benny Prijono <benny@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 "pjsip_perf.h"
/*
* This file handles OPTIONS generator and incoming OPTIONS requests.
*/
#define THIS_FILE "handler_options.c"
/****************************************************************************
*
* INCOMING OPTIONS HANDLER
*
****************************************************************************
*/
static pj_bool_t mod_options_on_rx_request(pjsip_rx_data *rdata);
/* The module instance. */
static pjsip_module mod_perf_options =
{
NULL, NULL, /* prev, next. */
{ "mod-perf-options", 16 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&mod_options_on_rx_request, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
static pj_bool_t mod_options_on_rx_request(pjsip_rx_data *rdata)
{
pjsip_msg *msg = rdata->msg_info.msg;
if (msg->line.req.method.id == PJSIP_OPTIONS_METHOD) {
if (settings.stateless) {
pjsip_endpt_respond_stateless( settings.endpt, rdata, 200, NULL,
NULL, NULL);
} else {
pjsip_endpt_respond( settings.endpt, NULL, rdata, 200, NULL,
NULL, NULL, NULL);
}
return PJ_TRUE;
}
return PJ_FALSE;
}
/****************************************************************************
*
* OUTGOING OPTIONS GENERATOR.
*
****************************************************************************
*/
struct callback_data
{
void *test_data;
void (*completion_cb)(void*,pj_bool_t);
};
static void options_callback(void *token, const pjsip_event *e)
{
struct callback_data *cb_data = token;
if (e->type == PJSIP_EVENT_TSX_STATE) {
(*cb_data->completion_cb)(cb_data->test_data,
e->body.tsx_state.tsx->status_code/100==2);
}
}
pj_status_t options_spawn_test(const pj_str_t *target,
const pj_str_t *from,
const pj_str_t *to,
unsigned cred_cnt,
const pjsip_cred_info cred[],
const pjsip_route_hdr *route_set,
void *test_data,
void (*completion_cb)(void*,pj_bool_t))
{
pj_status_t status;
struct callback_data *cb_data;
pjsip_tx_data *tdata;
status = pjsip_endpt_create_request( settings.endpt,
&pjsip_options_method,
target,
from,
to,
NULL, NULL, -1, NULL,
&tdata);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create request", status);
return status;
}
cb_data = pj_pool_alloc(tdata->pool, sizeof(struct callback_data));
cb_data->test_data = test_data;
cb_data->completion_cb = completion_cb;
status = pjsip_endpt_send_request( settings.endpt, tdata, -1,
cb_data, &options_callback);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to send request", status);
return status;
}
return PJ_SUCCESS;
}
pj_status_t options_handler_init(void)
{
return pjsip_endpt_register_module(settings.endpt, &mod_perf_options);
}

View File

@ -0,0 +1,668 @@
/* $Id$ */
/*
* Copyright (C) 2003-2006 Benny Prijono <benny@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 "pjsip_perf.h"
#include <pjsua-lib/getopt.h>
#include <stdlib.h> /* atoi */
#define THIS_FILE "main.c"
pjsip_perf_settings settings;
/* Show error message. */
void app_perror(const char *sender, const char *title, pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
}
/* Init default settings. */
static void init_settings(void)
{
pj_status_t status;
settings.stateless = 1;
settings.start_rate = 10;
settings.max_capacity = 64;
settings.duration = 0;
settings.thread_cnt = 1;
settings.local_port = 5060;
pjsip_method_set(&settings.method, PJSIP_OPTIONS_METHOD);
pj_init();
/* Create caching pool. */
pj_caching_pool_init(&settings.cp, &pj_pool_factory_default_policy,
4 * 1024 * 1024);
/* Create application pool. */
settings.pool = pj_pool_create(&settings.cp.factory, "pjsip-perf", 1024,
1024, NULL);
/* Create endpoint. */
status = pjsip_endpt_create(&settings.cp.factory, NULL, &settings.endpt);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create endpoint", status);
return;
}
}
/* Poll function. */
static int PJ_THREAD_FUNC poll_pjsip(void *arg)
{
pj_status_t last_err = 0;
PJ_UNUSED_ARG(arg);
do {
pj_time_val timeout = { 0, 10 };
pj_status_t status;
status = pjsip_endpt_handle_events (settings.endpt, &timeout);
if (status != last_err) {
last_err = status;
app_perror(THIS_FILE, "handle_events() returned error", status);
}
} while (!settings.quit_flag);
return 0;
}
/* Initialize */
static pj_status_t initialize(void)
{
pj_sockaddr_in addr;
int i;
pj_status_t status;
/* Create UDP transport. */
pj_memset(&addr, 0, sizeof(addr));
addr.sin_family = PJ_AF_INET;
addr.sin_port = pj_htons((pj_uint16_t)settings.local_port);
status = pjsip_udp_transport_start(settings.endpt, &addr, NULL,
settings.thread_cnt, NULL);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to start UDP transport", status);
return status;
}
/* Initialize transaction layer: */
status = pjsip_tsx_layer_init_module(settings.endpt);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Transaction layer initialization error",
status);
return status;
}
/* Initialize UA layer module: */
pjsip_ua_init_module( settings.endpt, NULL );
/* Init core SIMPLE module : */
pjsip_evsub_init_module(settings.endpt);
/* Init presence module: */
pjsip_pres_init_module( settings.endpt, pjsip_evsub_instance());
/* Init xfer/REFER module */
pjsip_xfer_init_module( settings.endpt );
/* Init multimedia endpoint. */
status = pjmedia_endpt_create(&settings.cp.factory, &settings.med_endpt);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create media endpoint",
status);
return status;
}
/* Init OPTIONS test handler */
status = options_handler_init();
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create OPTIONS handler", status);
return status;
}
/* Init call test handler */
status = call_handler_init();
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to initialize call handler", status);
return status;
}
/* Start worker thread. */
for (i=0; i<settings.thread_cnt; ++i) {
status = pj_thread_create(settings.pool, "pjsip-perf", &poll_pjsip,
NULL, 0, 0, &settings.thread[i]);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Unable to create thread", status);
return status;
}
}
pj_log_set_level(3);
return PJ_SUCCESS;
}
/* Shutdown */
static void shutdown(void)
{
int i;
/* Signal and wait worker thread to quit. */
settings.quit_flag = 1;
for (i=0; i<settings.thread_cnt; ++i) {
pj_thread_join(settings.thread[i]);
pj_thread_destroy(settings.thread[i]);
}
pjsip_endpt_destroy(settings.endpt);
pj_caching_pool_destroy(&settings.cp);
}
/* Verify that valid SIP url is given. */
pj_status_t verify_sip_url(const char *c_url)
{
pjsip_uri *p;
pj_pool_t *pool;
char *url;
int len = (c_url ? pj_ansi_strlen(c_url) : 0);
if (!len) return -1;
pool = pj_pool_create(&settings.cp.factory, "check%p", 1024, 0, NULL);
if (!pool) return -1;
url = pj_pool_alloc(pool, len+1);
pj_ansi_strcpy(url, c_url);
p = pjsip_parse_uri(pool, url, len, 0);
if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
p = NULL;
pj_pool_release(pool);
return p ? 0 : -1;
}
/* Usage */
static void usage(void)
{
puts("Usage:");
puts(" pjsip-perf [options] [target]");
puts("where");
puts(" target Optional default target URL");
puts("");
puts("General options:");
puts(" --help Display this help screen");
puts(" --version Display version info");
puts("");
puts("SIP options:");
puts(" --local-port=N SIP local port");
puts(" --stateless Handle incoming request statelessly if possible");
puts(" --thread-cnt=N Number of worker threads (default=1)");
puts("");
puts("Rate control:");
puts(" --start-rate=N Start rate in tasks per seconds (default=1)");
puts("");
puts("Capacity control:");
puts(" --max-capacity=N Maximum outstanding sessions (default=64)");
puts("");
puts("Duration control:");
puts(" --duration=secs Sessions duration (default=0)");
puts("");
}
/* Read options. */
static pj_status_t parse_options(int argc, char *argv[])
{
enum {
OPT_HELP,
OPT_VERSION,
OPT_LOCAL_PORT,
OPT_STATELESS,
OPT_THREAD_CNT,
OPT_START_RATE,
OPT_MAX_CAPACITY,
OPT_DURATION
};
struct option long_opts[] = {
{ "help", 0, 0, OPT_HELP},
{ "version", 0, 0, OPT_VERSION},
{ "local-port", 1, 0, OPT_LOCAL_PORT},
{ "stateless", 0, 0, OPT_STATELESS},
{ "thread-cnt", 1, 0, OPT_THREAD_CNT},
{ "start-rate", 1, 0, OPT_START_RATE},
{ "max-capacity", 1, 0, OPT_MAX_CAPACITY},
{ "duration", 1, 0, OPT_DURATION},
{ NULL, 0, 0, 0}
};
int c, option_index;
optind = 0;
while ((c=getopt_long(argc, argv, "", long_opts, &option_index)) != -1) {
switch (c) {
case OPT_HELP:
usage();
return PJ_EINVAL;
case OPT_VERSION:
pj_dump_config();
return PJ_EINVAL;
case OPT_LOCAL_PORT:
settings.local_port = atoi(optarg);
if (settings.local_port < 1 || settings.local_port > 65535) {
PJ_LOG(1,(THIS_FILE,"Invalid --local-port %s", optarg));
return PJ_EINVAL;
}
break;
case OPT_STATELESS:
settings.stateless = 1;
break;
case OPT_THREAD_CNT:
settings.thread_cnt = atoi(optarg);
if (settings.thread_cnt < 1 ||
settings.thread_cnt > PJ_ARRAY_SIZE(settings.thread))
{
PJ_LOG(1,(THIS_FILE,"Invalid --thread-cnt %s", optarg));
return PJ_EINVAL;
}
break;
case OPT_START_RATE:
settings.start_rate = atoi(optarg);
if (settings.start_rate < 1 || settings.start_rate > 1000000) {
PJ_LOG(1,(THIS_FILE,"Invalid --start-rate %s", optarg));
return PJ_EINVAL;
}
break;
case OPT_MAX_CAPACITY:
settings.max_capacity = atoi(optarg);
if (settings.max_capacity < 1 || settings.max_capacity > 65000) {
PJ_LOG(1,(THIS_FILE,
"Invalid --max-capacity %s (range=1-65000)",
optarg));
return PJ_EINVAL;
}
break;
case OPT_DURATION:
settings.duration = atoi(optarg);
if (settings.duration < 0 || settings.duration > 1000000) {
PJ_LOG(1,(THIS_FILE,"Invalid --duration %s", optarg));
return PJ_EINVAL;
}
break;
}
}
if (optind != argc) {
if (verify_sip_url(argv[optind]) != PJ_SUCCESS) {
PJ_LOG(3,(THIS_FILE, "Invalid SIP URL %s", argv[optind]));
return PJ_EINVAL;
}
settings.target = pj_str(argv[optind]);
++optind;
}
if (optind != argc) {
printf("Error: unknown options %s\n", argv[optind]);
return PJ_EINVAL;
}
return PJ_SUCCESS;
}
static void spawn_batch( pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry );
/* Completion callback. */
static void completion_cb(void *token, pj_bool_t success)
{
batch *batch = token;
if (success)
batch->success++;
else
batch->failed++;
if (batch->success+batch->failed == batch->rate) {
pj_time_val elapsed, sess_elapsed;
unsigned msec;
pj_gettimeofday(&batch->end_time);
elapsed = sess_elapsed = batch->end_time;
PJ_TIME_VAL_SUB(elapsed, batch->start_time);
PJ_TIME_VAL_SUB(sess_elapsed, settings.session->start_time);
msec = PJ_TIME_VAL_MSEC(elapsed);
if (msec == 0) msec = 1;
PJ_LOG(3,(THIS_FILE, "%02d:%02d:%02d: %d tasks in %d.%ds (%d tasks/sec)",
(sess_elapsed.sec / 3600),
(sess_elapsed.sec % 3600) / 60,
(sess_elapsed.sec % 60),
batch->rate,
elapsed.sec, elapsed.msec,
batch->rate * 1000 / msec));
if (!settings.session->stopping) {
pj_time_val interval;
if (msec >= 1000)
interval.sec = interval.msec = 0;
else
interval.sec = 0, interval.msec = 1000-msec;
settings.timer.cb = &spawn_batch;
pjsip_endpt_schedule_timer( settings.endpt, &settings.timer, &interval);
} else {
PJ_LOG(3,(THIS_FILE, "%.*s test session completed",
(int)settings.session->method.name.slen,
settings.session->method.name.ptr));
pj_pool_release(settings.session->pool);
settings.session = NULL;
}
}
}
/* Spawn new batch. */
static void spawn_batch( pj_timer_heap_t *timer_heap,
struct pj_timer_entry *entry )
{
session *sess = settings.session;
batch *batch;
pj_status_t status = PJ_SUCCESS;
pjsip_cred_info cred_info[1];
pj_time_val now, spawn_time, sess_time;
unsigned i;
if (!pj_list_empty(&sess->free_list)) {
batch = sess->free_list.next;
pj_list_erase(batch);
} else {
batch = pj_pool_alloc(sess->pool, sizeof(struct batch));
}
pj_gettimeofday(&batch->start_time);
batch->rate = settings.cur_rate;
batch->started = 0;
batch->success = 0;
batch->failed = 0;
pj_list_push_back(&sess->active_list, batch);
for (i=0; i<batch->rate; ++i) {
pj_str_t from = { "sip:user@127.0.0.1", 18};
if (sess->method.id == PJSIP_OPTIONS_METHOD) {
status = options_spawn_test(&settings.target, &from,
&settings.target,
0, cred_info, NULL, batch,
&completion_cb);
} else if (sess->method.id == PJSIP_INVITE_METHOD) {
status = call_spawn_test( &settings.target, &from,
&settings.target,
0, cred_info, NULL, batch,
&completion_cb);
}
if (status != PJ_SUCCESS)
break;
batch->started++;
}
pj_gettimeofday(&now);
spawn_time = sess_time = now;
PJ_TIME_VAL_SUB(spawn_time, batch->start_time);
PJ_TIME_VAL_SUB(sess_time, sess->start_time);
sess->total_created += batch->started;
batch = sess->active_list.next;
sess->outstanding = 0;
while (batch != &sess->active_list) {
sess->outstanding += (batch->started - batch->success - batch->failed);
if (batch->started == batch->success + batch->failed) {
struct batch *next = batch->next;
pj_list_erase(batch);
pj_list_push_back(&sess->free_list, batch);
batch = next;
} else {
batch = batch->next;
}
}
}
/* Start new session */
static void start_session(pj_bool_t auto_repeat)
{
pj_time_val interval = { 1, 0 };
pj_pool_t *pool;
session *sess;
pool = pjsip_endpt_create_pool(settings.endpt, "session", 4000, 4000);
if (!pool) {
app_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM);
return;
}
sess = pj_pool_zalloc(pool, sizeof(session));
sess->pool = pool;
sess->stopping = auto_repeat ? 0 : 1;
sess->method = settings.method;
pj_list_init(&sess->active_list);
pj_list_init(&sess->free_list);
pj_gettimeofday(&sess->start_time);
settings.session = sess;
spawn_batch(NULL, NULL);
}
/* Dump state */
static void dump(pj_bool_t detail)
{
pjsip_endpt_dump(settings.endpt, detail);
pjsip_tsx_layer_dump(detail);
pjsip_ua_dump(detail);
}
/* help screen */
static void help_screen(void)
{
puts ("+============================================================================+");
printf("| Current mode: %-10s Current rate: %-5d Call Capacity: %-7d |\n",
settings.method.name.ptr, settings.cur_rate, settings.max_capacity);
printf("| Call Duration: %-7d |\n",
settings.duration);
puts ("+--------------------------------------+-------------------------------------+");
puts ("| Test Settings | Misc Commands: |");
puts ("| | |");
puts ("| m Change mode | |");
puts ("| + - Increment/decrement rate by 10 | d Dump status |");
puts ("| * / Increment/decrement rate by 100 | d1 Dump detailed (e.g. tables) |");
puts ("+--------------------------------------+-------------------------------------+");
puts ("| Test Commands |");
puts ("| |");
puts ("| s Start single test batch |");
puts ("| sc Start continuous test x Stop continuous tests |");
puts ("+----------------------------------------------------------------------------+");
puts ("| q: Quit |");
puts ("+============================================================================+");
puts ("");
}
/*
* Input simple string
*/
static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
{
char *p;
printf("%s (empty to cancel): ", title); fflush(stdout);
fgets(buf, len, stdin);
/* Remove trailing newlines. */
for (p=buf; ; ++p) {
if (*p=='\r' || *p=='\n') *p='\0';
else if (!*p) break;
}
if (!*buf)
return PJ_FALSE;
return PJ_TRUE;
}
/* Main input loop */
static void test_main(void)
{
char menuin[10];
char input[80];
settings.cur_rate = settings.start_rate;
help_screen();
for (;;) {
printf(">>>> "); fflush(stdout);
fgets(menuin, sizeof(menuin), stdin);
switch (menuin[0]) {
case 's':
if (settings.session != NULL) {
PJ_LOG(3,(THIS_FILE,"Error: another session is in progress"));
} else if (settings.target.slen == 0) {
PJ_LOG(3,(THIS_FILE,"Error: target URL is not configured"));
} else {
start_session(menuin[1]=='c');
}
break;
case 'x':
if (settings.session) {
settings.session->stopping = 1;
} else {
PJ_LOG(3,(THIS_FILE,"Error: no sessions"));
}
break;
case 'm':
if (!simple_input("Change method [OPTIONS,INVITE]", input, sizeof(input)))
continue;
if (pj_ansi_stricmp(input, "OPTIONS")==0)
pjsip_method_set(&settings.method, PJSIP_OPTIONS_METHOD);
else if (pj_ansi_stricmp(input, "INVITE")==0)
pjsip_method_set(&settings.method, PJSIP_INVITE_METHOD);
else {
puts("Error: invalid method");
}
break;
case 'd':
dump(menuin[1]=='1');
break;
case '+':
settings.cur_rate += 10;
PJ_LOG(3,(THIS_FILE, "Rate is now %d", settings.cur_rate));
break;
case '-':
if (settings.cur_rate > 10) {
settings.cur_rate -= 10;
PJ_LOG(3,(THIS_FILE, "Rate is now %d", settings.cur_rate));
}
break;
case '*':
settings.cur_rate += 100;
PJ_LOG(3,(THIS_FILE, "Rate is now %d", settings.cur_rate));
break;
case '/':
if (settings.cur_rate > 100) {
settings.cur_rate -= 100;
PJ_LOG(3,(THIS_FILE, "Rate is now %d", settings.cur_rate));
}
break;
case 'q':
return;
default:
help_screen();
break;
}
}
}
/* main() */
int main(int argc, char *argv[])
{
pj_status_t status;
init_settings();
status = parse_options(argc, argv);
if (status != PJ_SUCCESS)
return 1;
status = initialize();
if (status != PJ_SUCCESS)
return 1;
test_main();
shutdown();
return 0;
}

View File

@ -0,0 +1,164 @@
/* $Id$ */
/*
* Copyright (C) 2003-2006 Benny Prijono <benny@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 __PJSIP_PERF_H__
#define __PJSIP_PERF_H__
#include <pjsua-lib/pjsua.h>
PJ_BEGIN_DECL
typedef struct batch batch;
typedef struct session session;
/**
* A test batch.
*/
struct batch
{
PJ_DECL_LIST_MEMBER(struct batch);
unsigned rate;
unsigned started;
unsigned success;
unsigned failed;
pj_time_val start_time;
pj_time_val end_time;
};
/**
* Test session.
*/
struct session
{
pj_pool_t *pool;
pj_time_val start_time;
pj_bool_t stopping;
pjsip_method method;
struct batch active_list;
struct batch free_list;
unsigned outstanding;
unsigned total_created;
};
/**
* Request parameter.
*/
struct request_param
{
pj_str_t dst;
pj_str_t src;
pjsip_cred_info cred;
};
typedef struct request_param request_param;
void app_perror(const char *sender, const char *title, pj_status_t status);
/* OPTIONS test */
pj_status_t options_handler_init(void);
pj_status_t options_spawn_test(const pj_str_t *target,
const pj_str_t *from,
const pj_str_t *to,
unsigned cred_cnt,
const pjsip_cred_info cred[],
const pjsip_route_hdr *route_set,
void *test_data,
void (*completion_cb)(void*,pj_bool_t));
/* CALL test */
pj_status_t call_handler_init(void);
pj_status_t call_spawn_test( const pj_str_t *target,
const pj_str_t *from,
const pj_str_t *to,
unsigned cred_cnt,
const pjsip_cred_info cred[],
const pjsip_route_hdr *route_set,
void *test_data,
void (*completion_cb)(void*,pj_bool_t));
/**
* Global settings
*/
struct pjsip_perf_settings
{
/* Global */
pj_caching_pool cp;
pj_pool_t *pool;
pjsip_endpoint *endpt;
pj_mutex_t *mutex;
/* Network: */
int local_port;
/* Threads. */
pj_bool_t quit_flag;
int thread_cnt;
pj_thread_t *thread[16];
/* Outgoing request method: */
pjsip_method method;
/* Default target: */
pj_str_t target;
/* Media: */
pjmedia_endpt *med_endpt;
pjmedia_conf *mconf;
/* Handling incoming requests: */
pj_bool_t stateless;
/* Rate control. */
pj_uint32_t start_rate;
pj_uint32_t cur_rate;
/* Capacity control. */
pj_uint32_t max_capacity;
/* Duration control: */
pj_uint32_t duration;
/* Test control: */
session *session;
pj_timer_entry timer;
};
typedef struct pjsip_perf_settings pjsip_perf_settings;
extern pjsip_perf_settings settings;
PJ_END_DECL
#endif /* __PJSIP_PERF_H__ */

944
pjsip-apps/src/pjsua/main.c Normal file
View File

@ -0,0 +1,944 @@
/* $Id$ */
/*
* Copyright (C) 2003-2006 Benny Prijono <benny@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 <pjsua-lib/pjsua.h>
#include <stdlib.h> /* atoi */
#define THIS_FILE "main.c"
/* Current dialog */
static int current_acc;
static int current_call = -1;
/*
* Find next call.
*/
static pj_bool_t find_next_call(void)
{
int i;
for (i=current_call+1; i<(int)pjsua.max_calls; ++i) {
if (pjsua.calls[i].inv != NULL) {
current_call = i;
return PJ_TRUE;
}
}
for (i=0; i<current_call; ++i) {
if (pjsua.calls[i].inv != NULL) {
current_call = i;
return PJ_TRUE;
}
}
current_call = -1;
return PJ_FALSE;
}
/*
* Find previous call.
*/
static pj_bool_t find_prev_call(void)
{
int i;
for (i=current_call-1; i>=0; --i) {
if (pjsua.calls[i].inv != NULL) {
current_call = i;
return PJ_TRUE;
}
}
for (i=pjsua.max_calls-1; i>current_call; --i) {
if (pjsua.calls[i].inv != NULL) {
current_call = i;
return PJ_TRUE;
}
}
current_call = -1;
return PJ_FALSE;
}
/*
* Notify UI when invite state has changed.
*/
void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e)
{
pjsua_call *call = &pjsua.calls[call_index];
PJ_UNUSED_ARG(e);
PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
call_index,
pjsua_inv_state_names[call->inv->state]));
if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
call->inv = NULL;
if ((int)call->index == current_call) {
find_next_call();
}
} else {
if (call && current_call==-1)
current_call = call->index;
}
}
/**
* Notify UI when registration status has changed.
*/
void pjsua_ui_regc_on_state_changed(int code)
{
PJ_UNUSED_ARG(code);
// Log already written.
}
/*
* Print buddy list.
*/
static void print_buddy_list(void)
{
int i;
puts("Buddy list:");
if (pjsua.buddy_cnt == 0)
puts(" -none-");
else {
for (i=0; i<pjsua.buddy_cnt; ++i) {
const char *status;
if (pjsua.buddies[i].sub == NULL ||
pjsua.buddies[i].status.info_cnt==0)
{
status = " ? ";
}
else if (pjsua.buddies[i].status.info[0].basic_open)
status = " Online";
else
status = "Offline";
printf(" [%2d] <%s> %s\n",
i+1, status, pjsua.buddies[i].uri.ptr);
}
}
puts("");
}
/*
* Print account status.
*/
static void print_acc_status(int acc_index)
{
char reg_status[128];
if (pjsua.acc[acc_index].regc == NULL) {
pj_ansi_strcpy(reg_status, " -not registered to server-");
} else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
} else if (pjsua.acc[acc_index].reg_last_code>=200 &&
pjsua.acc[acc_index].reg_last_code<=699) {
pjsip_regc_info info;
pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
pj_snprintf(reg_status, sizeof(reg_status),
"%s (%.*s;expires=%d)",
pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code)->ptr,
(int)info.client_uri.slen,
info.client_uri.ptr,
info.next_reg);
} else {
pj_sprintf(reg_status, "in progress (%d)",
pjsua.acc[acc_index].reg_last_code);
}
printf("[%2d] Registration status: %s\n", acc_index, reg_status);
printf(" Online status: %s\n",
(pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
}
/*
* Show a bit of help.
*/
static void keystroke_help(void)
{
int i;
printf(">>>>\n");
for (i=0; i<pjsua.acc_cnt; ++i)
print_acc_status(i);
print_buddy_list();
//puts("Commands:");
puts("+=============================================================================+");
puts("| Call Commands: | IM & Presence: | Misc: |");
puts("| | | |");
puts("| m Make new call | i Send IM | o Send OPTIONS |");
puts("| M Make multiple calls | s Subscribe presence | rr (Re-)register |");
puts("| a Answer call | u Unsubscribe presence | ru Unregister |");
puts("| h Hangup call | t ToGgle Online status | d Dump status |");
puts("| H Hold call | | dc Dump config |");
puts("| v re-inVite (release hold) +--------------------------+-------------------+");
puts("| ] Select next dialog | Conference Command | |");
puts("| [ Select previous dialog | cl List ports | |");
puts("| x Xfer call | cc Connect port | |");
puts("| # Send DTMF string | cd Disconnect port | |");
puts("+------------------------------+--------------------------+-------------------+");
puts("| q QUIT |");
puts("+=============================================================================+");
}
/*
* Input simple string
*/
static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
{
char *p;
printf("%s (empty to cancel): ", title); fflush(stdout);
fgets(buf, len, stdin);
/* Remove trailing newlines. */
for (p=buf; ; ++p) {
if (*p=='\r' || *p=='\n') *p='\0';
else if (!*p) break;
}
if (!*buf)
return PJ_FALSE;
return PJ_TRUE;
}
#define NO_NB -2
struct input_result
{
int nb_result;
char *uri_result;
};
/*
* Input URL.
*/
static void ui_input_url(const char *title, char *buf, int len,
struct input_result *result)
{
result->nb_result = NO_NB;
result->uri_result = NULL;
print_buddy_list();
printf("Choices:\n"
" 0 For current dialog.\n"
" -1 All %d buddies in buddy list\n"
" [1 -%2d] Select from buddy list\n"
" URL An URL\n"
" <Enter> Empty input (or 'q') to cancel\n"
, pjsua.buddy_cnt, pjsua.buddy_cnt);
printf("%s: ", title);
fflush(stdout);
fgets(buf, len, stdin);
len = strlen(buf);
/* Left trim */
while (isspace(*buf)) {
++buf;
--len;
}
/* Remove trailing newlines */
while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
buf[--len] = '\0';
if (len == 0 || buf[0]=='q')
return;
if (isdigit(*buf) || *buf=='-') {
int i;
if (*buf=='-')
i = 1;
else
i = 0;
for (; i<len; ++i) {
if (!isdigit(buf[i])) {
puts("Invalid input");
return;
}
}
result->nb_result = atoi(buf);
if (result->nb_result > 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
--result->nb_result;
return;
}
if (result->nb_result == -1)
return;
puts("Invalid input");
result->nb_result = NO_NB;
return;
} else {
pj_status_t status;
if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Invalid URL", status);
return;
}
result->uri_result = buf;
}
}
static void conf_list(void)
{
unsigned i, count;
pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
printf("Conference ports:\n");
count = PJ_ARRAY_SIZE(info);
pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
for (i=0; i<count; ++i) {
char txlist[PJSUA_MAX_CALLS*4+10];
int j;
pjmedia_conf_port_info *port_info = &info[i];
txlist[0] = '\0';
for (j=0; j<pjsua.max_calls+PJSUA_CONF_MORE_PORTS; ++j) {
char s[10];
if (port_info->listener[j]) {
pj_sprintf(s, "#%d ", j);
pj_ansi_strcat(txlist, s);
}
}
printf("Port #%02d %20.*s transmitting to: %s\n",
port_info->slot,
(int)port_info->name.slen,
port_info->name.ptr,
txlist);
}
puts("");
}
static void ui_console_main(void)
{
char menuin[10];
char buf[128];
int i, count;
char *uri;
struct input_result result;
keystroke_help();
for (;;) {
printf(">>> ");
fflush(stdout);
fgets(menuin, sizeof(menuin), stdin);
switch (menuin[0]) {
case 'm':
/* Make call! : */
printf("(You currently have %d calls)\n", pjsua.call_cnt);
ui_input_url("Make call", buf, sizeof(buf), &result);
if (result.nb_result != NO_NB) {
if (result.nb_result == -1)
puts("You can't do that with make call!");
else
pjsua_make_call( current_acc,
pjsua.buddies[result.nb_result].uri.ptr,
NULL);
} else if (result.uri_result)
pjsua_make_call( current_acc, result.uri_result, NULL);
break;
case 'M':
/* Make multiple calls! : */
printf("(You currently have %d calls)\n", pjsua.call_cnt);
ui_input_url("Make call", buf, sizeof(buf), &result);
if (result.nb_result != NO_NB) {
if (result.nb_result == -1) {
puts("You can't do that with make call!");
continue;
}
uri = pjsua.buddies[result.nb_result].uri.ptr;
} else {
uri = result.uri_result;
}
if (!simple_input("Number of calls", menuin, sizeof(menuin)))
continue;
count = atoi(menuin);
if (count < 1)
continue;
for (i=0; i<atoi(menuin); ++i) {
pj_status_t status;
status = pjsua_make_call(current_acc, uri, NULL);
if (status != PJ_SUCCESS)
break;
}
break;
case 'a':
if (current_call == -1 ||
pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
{
puts("No pending incoming call");
fflush(stdout);
continue;
} else {
pj_status_t status;
pjsip_tx_data *tdata;
if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
continue;
if (atoi(buf) < 100)
continue;
/*
* Must check again!
* Call may have been disconnected while we're waiting for
* keyboard input.
*/
if (current_call == -1) {
puts("Call has been disconnected");
fflush(stdout);
continue;
}
status = pjsip_inv_answer(pjsua.calls[current_call].inv,
atoi(buf),
NULL, NULL, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
tdata, NULL);
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Unable to create/send response",
status);
}
break;
case 'h':
if (current_call == -1) {
puts("No current call");
fflush(stdout);
continue;
} else {
pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
}
break;
case ']':
case '[':
/*
* Cycle next/prev dialog.
*/
if (menuin[0] == ']') {
find_next_call();
} else {
find_prev_call();
}
if (current_call != -1) {
char url[PJSIP_MAX_URL_SIZE];
int len;
const pjsip_uri *u;
u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
len = pjsip_uri_print(0, u, url, sizeof(url)-1);
if (len < 1) {
pj_ansi_strcpy(url, "<uri is too long>");
} else {
url[len] = '\0';
}
PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
} else {
PJ_LOG(3,(THIS_FILE,"No current dialog"));
}
break;
case 'H':
/*
* Hold call.
*/
if (current_call != -1) {
pjsua_call_set_hold(current_call);
} else {
PJ_LOG(3,(THIS_FILE, "No current call"));
}
break;
case 'v':
/*
* Send re-INVITE (to release hold, etc).
*/
if (current_call != -1) {
pjsua_call_reinvite(current_call);
} else {
PJ_LOG(3,(THIS_FILE, "No current call"));
}
break;
case 'x':
/*
* Transfer call.
*/
if (current_call == -1) {
PJ_LOG(3,(THIS_FILE, "No current call"));
} else {
int call = current_call;
ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
/* Check if call is still there. */
if (call != current_call) {
puts("Call has been disconnected");
continue;
}
if (result.nb_result != NO_NB) {
if (result.nb_result == -1)
puts("You can't do that with transfer call!");
else
pjsua_call_xfer( current_call,
pjsua.buddies[result.nb_result].uri.ptr);
} else if (result.uri_result) {
pjsua_call_xfer( current_call, result.uri_result);
}
}
break;
case '#':
/*
* Send DTMF strings.
*/
if (current_call == -1) {
PJ_LOG(3,(THIS_FILE, "No current call"));
} else if (pjsua.calls[current_call].session == NULL) {
PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
} else {
pj_str_t digits;
int call = current_call;
pj_status_t status;
if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
sizeof(buf)))
{
break;
}
if (call != current_call) {
puts("Call has been disconnected");
continue;
}
digits = pj_str(buf);
status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
&digits);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
} else {
puts("DTMF digits enqueued for transmission");
}
}
break;
case 's':
case 'u':
/*
* Subscribe/unsubscribe presence.
*/
ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
if (result.nb_result != NO_NB) {
if (result.nb_result == -1) {
int i;
for (i=0; i<pjsua.buddy_cnt; ++i)
pjsua.buddies[i].monitor = (menuin[0]=='s');
} else {
pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s');
}
pjsua_pres_refresh(current_acc);
} else if (result.uri_result) {
puts("Sorry, can only subscribe to buddy's presence, "
"not arbitrary URL (for now)");
}
break;
case 'r':
switch (menuin[1]) {
case 'r':
/*
* Re-Register.
*/
pjsua_regc_update(current_acc, PJ_TRUE);
break;
case 'u':
/*
* Unregister
*/
pjsua_regc_update(current_acc, PJ_FALSE);
break;
}
break;
case 't':
pjsua.acc[current_acc].online_status =
!pjsua.acc[current_acc].online_status;
pjsua_pres_refresh(current_acc);
break;
case 'c':
switch (menuin[1]) {
case 'l':
conf_list();
break;
case 'c':
case 'd':
{
char src_port[10], dst_port[10];
pj_status_t status;
const char *src_title, *dst_title;
conf_list();
src_title = (menuin[1]=='c'?
"Connect src port #":
"Disconnect src port #");
dst_title = (menuin[1]=='c'?
"To dst port #":
"From dst port #");
if (!simple_input(src_title, src_port, sizeof(src_port)))
break;
if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
break;
if (menuin[1]=='c') {
status = pjmedia_conf_connect_port(pjsua.mconf,
atoi(src_port),
atoi(dst_port));
} else {
status = pjmedia_conf_disconnect_port(pjsua.mconf,
atoi(src_port),
atoi(dst_port));
}
if (status == PJ_SUCCESS) {
puts("Success");
} else {
puts("ERROR!!");
}
}
break;
}
break;
case 'd':
if (menuin[1] == 'c') {
char settings[2000];
int len;
len = pjsua_dump_settings(settings, sizeof(settings));
if (len < 1)
PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
else
PJ_LOG(3,(THIS_FILE,
"Dumping configuration (%d bytes):\n%s\n",
len, settings));
} else {
pjsua_dump();
}
break;
case 'q':
goto on_exit;
default:
keystroke_help();
break;
}
}
on_exit:
;
}
/*****************************************************************************
* This is a very simple PJSIP module, whose sole purpose is to display
* incoming and outgoing messages to log. This module will have priority
* higher than transport layer, which means:
*
* - incoming messages will come to this module first before reaching
* transaction layer.
*
* - outgoing messages will come to this module last, after the message
* has been 'printed' to contiguous buffer by transport layer and
* appropriate transport instance has been decided for this message.
*
*/
/* Notification on incoming messages */
static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
{
PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
"%s\n"
"--end msg--",
rdata->msg_info.len,
pjsip_rx_data_get_info(rdata),
rdata->pkt_info.src_name,
rdata->pkt_info.src_port,
rdata->msg_info.msg_buf));
/* Always return false, otherwise messages will not get processed! */
return PJ_FALSE;
}
/* Notification on outgoing messages */
static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
{
/* Important note:
* tp_info field is only valid after outgoing messages has passed
* transport layer. So don't try to access tp_info when the module
* has lower priority than transport layer.
*/
PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
"%s\n"
"--end msg--",
(tdata->buf.cur - tdata->buf.start),
pjsip_tx_data_get_info(tdata),
tdata->tp_info.dst_name,
tdata->tp_info.dst_port,
tdata->buf.start));
/* Always return success, otherwise message will not get sent! */
return PJ_SUCCESS;
}
/* The module instance. */
static pjsip_module console_msg_logger =
{
NULL, NULL, /* prev, next. */
{ "mod-pjsua-log", 13 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&console_on_rx_msg, /* on_rx_request() */
&console_on_rx_msg, /* on_rx_response() */
&console_on_tx_msg, /* on_tx_request. */
&console_on_tx_msg, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
/*****************************************************************************
* Console application custom logging:
*/
static FILE *log_file;
static void app_log_writer(int level, const char *buffer, int len)
{
/* Write to both stdout and file. */
if (level <= pjsua.app_log_level)
pj_log_write(level, buffer, len);
if (log_file) {
fwrite(buffer, len, 1, log_file);
fflush(log_file);
}
}
void app_logging_init(void)
{
/* Redirect log function to ours */
pj_log_set_log_func( &app_log_writer );
/* If output log file is desired, create the file: */
if (pjsua.log_filename)
log_file = fopen(pjsua.log_filename, "wt");
}
void app_logging_shutdown(void)
{
/* Close logging file, if any: */
if (log_file) {
fclose(log_file);
log_file = NULL;
}
}
/*****************************************************************************
* Error display:
*/
/*
* Display error message for the specified error code.
*/
void pjsua_perror(const char *sender, const char *title,
pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
}
/*****************************************************************************
* main():
*/
int main(int argc, char *argv[])
{
/* Init default settings. */
pjsua_default();
/* Initialize pjsua (to create pool etc).
*/
if (pjsua_init() != PJ_SUCCESS)
return 1;
/* Parse command line arguments: */
if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
return 1;
/* Init logging: */
app_logging_init();
/* Register message logger to print incoming and outgoing
* messages.
*/
pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
/* Start pjsua! */
if (pjsua_start() != PJ_SUCCESS) {
pjsua_destroy();
return 1;
}
/* Sleep for a while, let any messages get printed to console: */
pj_thread_sleep(500);
/* Start UI console main loop: */
ui_console_main();
/* Destroy pjsua: */
pjsua_destroy();
/* Close logging: */
app_logging_shutdown();
/* Exit... */
return 0;
}