From e56ea14ab8531ee3cec375460577d1b89bf62e26 Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Thu, 16 Jan 2014 05:30:46 +0000 Subject: Closed #1723: Merging pjsua2 branch into trunk git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4704 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/build/Makefile | 56 +- pjsip/build/pjsua2_lib.vcproj | 3220 +++++++++++++++++++++++++++++++++++ pjsip/docs/doxygen.cfg | 2 +- pjsip/include/pjsip-simple/evsub.h | 9 +- pjsip/include/pjsua-lib/pjsua.h | 34 +- pjsip/include/pjsua2.hpp | 29 + pjsip/include/pjsua2/account.hpp | 1557 +++++++++++++++++ pjsip/include/pjsua2/call.hpp | 1713 +++++++++++++++++++ pjsip/include/pjsua2/config.hpp | 47 + pjsip/include/pjsua2/doxygen.hpp | 52 + pjsip/include/pjsua2/endpoint.hpp | 1322 ++++++++++++++ pjsip/include/pjsua2/json.hpp | 116 ++ pjsip/include/pjsua2/media.hpp | 1162 +++++++++++++ pjsip/include/pjsua2/persistent.hpp | 689 ++++++++ pjsip/include/pjsua2/presence.hpp | 296 ++++ pjsip/include/pjsua2/siptypes.hpp | 910 ++++++++++ pjsip/include/pjsua2/types.hpp | 266 +++ pjsip/src/pjsip/sip_endpoint.c | 2 + pjsip/src/pjsua-lib/pjsua_aud.c | 3 + pjsip/src/pjsua-lib/pjsua_core.c | 60 +- pjsip/src/pjsua2-test/main.cpp | 38 + pjsip/src/pjsua2/account.cpp | 799 +++++++++ pjsip/src/pjsua2/call.cpp | 719 ++++++++ pjsip/src/pjsua2/endpoint.cpp | 1612 ++++++++++++++++++ pjsip/src/pjsua2/json.cpp | 544 ++++++ pjsip/src/pjsua2/media.cpp | 779 +++++++++ pjsip/src/pjsua2/persistent.cpp | 227 +++ pjsip/src/pjsua2/presence.cpp | 190 +++ pjsip/src/pjsua2/siptypes.cpp | 590 +++++++ pjsip/src/pjsua2/types.cpp | 95 ++ pjsip/src/pjsua2/util.hpp | 45 + 31 files changed, 17158 insertions(+), 25 deletions(-) create mode 100644 pjsip/build/pjsua2_lib.vcproj create mode 100644 pjsip/include/pjsua2.hpp create mode 100644 pjsip/include/pjsua2/account.hpp create mode 100644 pjsip/include/pjsua2/call.hpp create mode 100644 pjsip/include/pjsua2/config.hpp create mode 100644 pjsip/include/pjsua2/doxygen.hpp create mode 100644 pjsip/include/pjsua2/endpoint.hpp create mode 100644 pjsip/include/pjsua2/json.hpp create mode 100644 pjsip/include/pjsua2/media.hpp create mode 100644 pjsip/include/pjsua2/persistent.hpp create mode 100644 pjsip/include/pjsua2/presence.hpp create mode 100644 pjsip/include/pjsua2/siptypes.hpp create mode 100644 pjsip/include/pjsua2/types.hpp create mode 100644 pjsip/src/pjsua2-test/main.cpp create mode 100644 pjsip/src/pjsua2/account.cpp create mode 100644 pjsip/src/pjsua2/call.cpp create mode 100644 pjsip/src/pjsua2/endpoint.cpp create mode 100644 pjsip/src/pjsua2/json.cpp create mode 100644 pjsip/src/pjsua2/media.cpp create mode 100644 pjsip/src/pjsua2/persistent.cpp create mode 100644 pjsip/src/pjsua2/presence.cpp create mode 100644 pjsip/src/pjsua2/siptypes.cpp create mode 100644 pjsip/src/pjsua2/types.cpp create mode 100644 pjsip/src/pjsua2/util.hpp (limited to 'pjsip') diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index a10fdc7d..a6e474e3 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -21,6 +21,7 @@ export PJSIP_LIB:=libpjsip-$(TARGET_NAME)$(LIBEXT) export PJSIP_UA_LIB:=libpjsip-ua-$(TARGET_NAME)$(LIBEXT) export PJSIP_SIMPLE_LIB:=libpjsip-simple-$(TARGET_NAME)$(LIBEXT) export PJSUA_LIB_LIB:=libpjsua-$(TARGET_NAME)$(LIBEXT) +export PJSUA2_LIB_LIB=../lib/libpjsua2-$(TARGET_NAME)$(LIBEXT) ifeq ($(PJ_SHARED_LIBRARIES),) else @@ -32,6 +33,8 @@ export PJSIP_SIMPLE_SONAME := libpjsip-simple.$(SHLIB_SUFFIX) export PJSIP_SIMPLE_SHLIB := $(PJSIP_SIMPLE_SONAME).$(PJ_VERSION_MAJOR) export PJSUA_LIB_SONAME := libpjsua.$(SHLIB_SUFFIX) export PJSUA_LIB_SHLIB := $(PJSUA_LIB_SONAME).$(PJ_VERSION_MAJOR) +export PJSUA2_LIB_SONAME := ../lib/libpjsua2.$(SHLIB_SUFFIX) +export PJSUA2_LIB_SHLIB := $(PJSUA2_LIB_SONAME).$(PJ_VERSION_MAJOR) endif ############################################################################### @@ -129,6 +132,31 @@ export PJSUA_LIB_LDFLAGS += $(PJSIP_UA_LDLIB) \ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT +############################################################################### +# Defines for building pjsua2 library +# +export PJSUA2_LIB_SRCDIR = ../src/pjsua2 +export PJSUA2_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ + account.o endpoint.o json.o persistent.o types.o \ + siptypes.o call.o presence.o media.o +export PJSUA2_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) +export PJSUA2_LIB_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) + + +############################################################################### +# Defines for building pjsua2-test application +# +export PJSUA2_TEST_SRCDIR = ../src/pjsua2-test +export PJSUA2_TEST_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ + main.o +export PJSUA2_TEST_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) +export PJSUA2_TEST_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) +export PJSUA2_TEST_LDFLAGS += -lpjsua2-$(TARGET_NAME) -lstdc++ $(PJ_LDFLAGS) $(PJ_LDLIBS) $(LDFLAGS) +export PJSUA2_TEST_EXE := pjsua2-test-$(TARGET_NAME)$(HOST_EXE) + +export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT + + ############################################################################### # Defines for building test application # @@ -166,7 +194,9 @@ TARGETS := $(PJSIP_LIB) $(PJSIP_SONAME) \ $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME) \ $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME) \ $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) \ - $(TEST_EXE) + $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME) \ + $(TEST_EXE) \ + $(PJSUA2_TEST_EXE) all: $(TARGETS) @@ -190,7 +220,9 @@ distclean: realclean .PHONY: $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME) .PHONY: $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME) .PHONY: $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) +.PHONY: $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME) .PHONY: $(TEST_EXE) +.PHONY: $(PJSUA2_TEST_EXE) pjsip: $(PJSIP_LIB) $(PJSIP_SONAME): $(PJSIP_LIB) @@ -220,6 +252,14 @@ $(TEST_EXE): $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) $(TEST_EXE): $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(subst /,$(HOST_PSEP),$(BINDIR)/$@) +pjsua2-test: $(PJSUA2_TEST_EXE) +$(PJSUA2_TEST_EXE): $(PJSIP_LIB) $(PJSIP_SONAME) +$(PJSUA2_TEST_EXE): $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME) +$(PJSUA2_TEST_EXE): $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME) +$(PJSUA2_TEST_EXE): $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) +$(PJSUA2_TEST_EXE): $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME) + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $(subst /,$(HOST_PSEP),$(BINDIR)/$@) + .PHONY: pjsip.ko pjsip.ko: echo Making $@ @@ -240,31 +280,45 @@ pjsua-lib.ko: echo Making $@ $(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) +.PHONY: pjsua2-lib.ko +pjsua2-lib.ko: + echo Making $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) + clean: $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@ depend: $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@ echo '$(BINDIR)/$(TEST_EXE): $(PJMEDIA_LIB) $(LIBDIR)/$(PJSUA_LIB_LIB) $(LIBDIR)/$(PJSIP_SIMPLE_LIB) $(LIBDIR)/$(PJSIP_UA_LIB) $(LIBDIR)/$(PJSIP_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjsip-test-$(TARGET_NAME).depend + echo '$(BINDIR)/$(PJSUA2_TEST_EXE): $(PJSUA2_LIB_LIB) $(PJMEDIA_LIB) $(LIBDIR)/$(PJSUA_LIB_LIB) $(LIBDIR)/$(PJSIP_SIMPLE_LIB) $(LIBDIR)/$(PJSIP_UA_LIB) $(LIBDIR)/$(PJSIP_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjsua2-test-$(TARGET_NAME).depend realclean: $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-ua-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsip-simple-$(TARGET_NAME).depend),$(HOST_RMR)) $(subst @@,$(subst /,$(HOST_PSEP),.pjsua-lib-$(TARGET_NAME).depend),$(HOST_RMR)) + $(subst @@,$(subst /,$(HOST_PSEP),.pjsua2-lib-$(TARGET_NAME).depend),$(HOST_RMR)) + $(subst @@,$(subst /,$(HOST_PSEP),.pjsua2-test-$(TARGET_NAME).depend),$(HOST_RMR)) $(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@ $(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@ $(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@ $(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@ + $(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@ diff --git a/pjsip/build/pjsua2_lib.vcproj b/pjsip/build/pjsua2_lib.vcproj new file mode 100644 index 00000000..61f725ac --- /dev/null +++ b/pjsip/build/pjsua2_lib.vcproj @@ -0,0 +1,3220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pjsip/docs/doxygen.cfg b/pjsip/docs/doxygen.cfg index cd3f8e4c..e3e6dc9f 100644 --- a/pjsip/docs/doxygen.cfg +++ b/pjsip/docs/doxygen.cfg @@ -356,7 +356,7 @@ INPUT = docs include # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl -FILE_PATTERNS = *.h +FILE_PATTERNS = *.h *.hpp # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h index b0159bff..2dc4d69c 100644 --- a/pjsip/include/pjsip-simple/evsub.h +++ b/pjsip/include/pjsip-simple/evsub.h @@ -58,7 +58,7 @@ typedef struct pjsip_evsub pjsip_evsub; * will be set to PJSIP_EVSUB_STATE_UNKNOWN, and the token will be kept * in state_str member of the susbcription structure. */ -enum pjsip_evsub_state +typedef enum pjsip_evsub_state { PJSIP_EVSUB_STATE_NULL, /**< State is NULL. */ PJSIP_EVSUB_STATE_SENT, /**< Client has sent SUBSCRIBE request. */ @@ -70,12 +70,7 @@ enum pjsip_evsub_state PJSIP_EVSUB_STATE_UNKNOWN, /**< Subscription state can not be determined. Application can query the state by calling #pjsip_evsub_get_state_name().*/ -}; - -/** - * @see pjsip_evsub_state - */ -typedef enum pjsip_evsub_state pjsip_evsub_state; +} pjsip_evsub_state; /** diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index bca343ba..7ee3d6cf 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -251,7 +251,10 @@ PJ_BEGIN_DECL */ /** Constant to identify invalid ID for all sorts of IDs. */ -#define PJSUA_INVALID_ID (-1) +enum pjsua_invalid_id_const_ +{ + PJSUA_INVALID_ID = -1 +}; /** Disabled features temporarily for media reorganization */ #define DISABLED_FOR_TICKET_1185 0 @@ -1879,6 +1882,26 @@ PJ_DECL(pj_status_t) pjsua_destroy2(unsigned flags); PJ_DECL(int) pjsua_handle_events(unsigned msec_timeout); +/** + * Register a thread to poll for events. This function should be + * called by an external worker thread, and it will block polling + * for events until the library is destroyed. + * + * @return PJ_SUCCESS if things are working correctly + * or an error polling cannot be done for some + * reason. + */ +PJ_DECL(pj_status_t) pjsua_register_worker_thread(const char *name); + + +/** + * Signal all worker threads to quit. This will only wait until internal + * threads are done. For external threads, application must perform + * its own waiting for the external threads to quit from + * pjsua_register_worker_thread() function. + */ +PJ_DECL(void) pjsua_stop_worker_threads(void); + /** * Create memory pool to be used by the application. Once application * finished using the pool, it must be released with pj_pool_release(). @@ -5777,6 +5800,9 @@ typedef struct pjsua_conf_port_info /** Port name. */ pj_str_t name; + /** Format. */ + pjmedia_format format; + /** Clock rate. */ unsigned clock_rate; @@ -5789,6 +5815,12 @@ typedef struct pjsua_conf_port_info /** Bits per sample */ unsigned bits_per_sample; + /** Tx level adjustment. */ + float tx_level_adj; + + /** Rx level adjustment. */ + float rx_level_adj; + /** Number of listeners in the array. */ unsigned listener_cnt; diff --git a/pjsip/include/pjsua2.hpp b/pjsip/include/pjsua2.hpp new file mode 100644 index 00000000..d1a12ed6 --- /dev/null +++ b/pjsip/include/pjsua2.hpp @@ -0,0 +1,29 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_HPP__ +#define __PJSUA2_HPP__ + +#include +#include +#include +#include +#include +#include + +#endif diff --git a/pjsip/include/pjsua2/account.hpp b/pjsip/include/pjsua2/account.hpp new file mode 100644 index 00000000..2a7199f2 --- /dev/null +++ b/pjsip/include/pjsua2/account.hpp @@ -0,0 +1,1557 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_ACCOUNT_HPP__ +#define __PJSUA2_ACCOUNT_HPP__ + +/** + * @file pjsua2/account.hpp + * @brief PJSUA2 Account operations + */ +#include +#include +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_ACC Account + * @ingroup PJSUA2_Ref + * @{ + */ + +using std::string; + +/** + * Account registration config. This will be specified in AccountConfig. + */ +struct AccountRegConfig : public PersistentObject +{ + /** + * This is the URL to be put in the request URI for the registration, + * and will look something like "sip:serviceprovider". + * + * This field should be specified if registration is desired. If the + * value is empty, no account registration will be performed. + */ + string registrarUri; + + /** + * Specify whether the account should register as soon as it is + * added to the UA. Application can set this to PJ_FALSE and control + * the registration manually with pjsua_acc_set_registration(). + * + * Default: True + */ + bool registerOnAdd; + + /** + * The optional custom SIP headers to be put in the registration + * request. + */ + SipHeaderVector headers; + + /** + * Optional interval for registration, in seconds. If the value is zero, + * default interval will be used (PJSUA_REG_INTERVAL, 300 seconds). + */ + unsigned timeoutSec; + + /** + * Specify interval of auto registration retry upon registration failure + * (including caused by transport problem), in second. Set to 0 to + * disable auto re-registration. Note that if the registration retry + * occurs because of transport failure, the first retry will be done + * after \a firstRetryIntervalSec seconds instead. Also note that + * the interval will be randomized slightly by approximately +/- ten + * seconds to avoid all clients re-registering at the same time. + * + * See also \a firstRetryIntervalSec setting. + * + * Default: PJSUA_REG_RETRY_INTERVAL + */ + unsigned retryIntervalSec; + + /** + * This specifies the interval for the first registration retry. The + * registration retry is explained in \a retryIntervalSec. Note that + * the value here will also be randomized by +/- ten seconds. + * + * Default: 0 + */ + unsigned firstRetryIntervalSec; + + /** + * Specify the number of seconds to refresh the client registration + * before the registration expires. + * + * Default: PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH, 5 seconds + */ + unsigned delayBeforeRefreshSec; + + /** + * Specify whether calls of the configured account should be dropped + * after registration failure and an attempt of re-registration has + * also failed. + * + * Default: FALSE (disabled) + */ + bool dropCallsOnFail; + + /** + * Specify the maximum time to wait for unregistration requests to + * complete during library shutdown sequence. + * + * Default: PJSUA_UNREG_TIMEOUT + */ + unsigned unregWaitSec; + + /** + * Specify how the registration uses the outbound and account proxy + * settings. This controls if and what Route headers will appear in + * the REGISTER request of this account. The value is bitmask combination + * of PJSUA_REG_USE_OUTBOUND_PROXY and PJSUA_REG_USE_ACC_PROXY bits. + * If the value is set to 0, the REGISTER request will not use any proxy + * (i.e. it will not have any Route headers). + * + * Default: 3 (PJSUA_REG_USE_OUTBOUND_PROXY | PJSUA_REG_USE_ACC_PROXY) + */ + unsigned proxyUse; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); + +}; + +/** Array of SIP credentials */ +typedef std::vector AuthCredInfoVector; + +/** + * Various SIP settings for the account. This will be specified in + * AccountConfig. + */ +struct AccountSipConfig : public PersistentObject +{ + /** + * Array of credentials. If registration is desired, normally there should + * be at least one credential specified, to successfully authenticate + * against the service provider. More credentials can be specified, for + * example when the requests are expected to be challenged by the + * proxies in the route set. + */ + AuthCredInfoVector authCreds; + + /** + * Array of proxy servers to visit for outgoing requests. Each of the + * entry is translated into one Route URI. + */ + StringVector proxies; + + /** + * Optional URI to be put as Contact for this account. It is recommended + * that this field is left empty, so that the value will be calculated + * automatically based on the transport address. + */ + string contactForced; + + /** + * Additional parameters that will be appended in the Contact header + * for this account. This will affect the Contact header in all SIP + * messages sent on behalf of this account, including but not limited to + * REGISTER, INVITE, and SUBCRIBE requests or responses. + * + * The parameters should be preceeded by semicolon, and all strings must + * be properly escaped. Example: + * ";my-param=X;another-param=Hi%20there" + */ + string contactParams; + + /** + * Additional URI parameters that will be appended in the Contact URI + * for this account. This will affect the Contact URI in all SIP + * messages sent on behalf of this account, including but not limited to + * REGISTER, INVITE, and SUBCRIBE requests or responses. + * + * The parameters should be preceeded by semicolon, and all strings must + * be properly escaped. Example: + * ";my-param=X;another-param=Hi%20there" + */ + string contactUriParams; + + + /** + * If this flag is set, the authentication client framework will + * send an empty Authorization header in each initial request. + * Default is no. + */ + bool authInitialEmpty; + + /** + * Specify the algorithm to use when empty Authorization header + * is to be sent for each initial request (see above) + */ + string authInitialAlgorithm; + + /** + * Optionally bind this account to specific transport. This normally is + * not a good idea, as account should be able to send requests using + * any available transports according to the destination. But some + * application may want to have explicit control over the transport to + * use, so in that case it can set this field. + * + * Default: -1 (PJSUA_INVALID_ID) + * + * @see Account::setTransport() + */ + TransportId transportId; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account's call settings. This will be specified in AccountConfig. + */ +struct AccountCallConfig : public PersistentObject +{ + /** + * Specify how to offer call hold to remote peer. Please see the + * documentation on pjsua_call_hold_type for more info. + * + * Default: PJSUA_CALL_HOLD_TYPE_DEFAULT + */ + pjsua_call_hold_type holdType; + + /** + * Specify how support for reliable provisional response (100rel/ + * PRACK) should be used for all sessions in this account. See the + * documentation of pjsua_100rel_use enumeration for more info. + * + * Default: PJSUA_100REL_NOT_USED + */ + pjsua_100rel_use prackUse; + + /** + * Specify the usage of Session Timers for all sessions. See the + * pjsua_sip_timer_use for possible values. + * + * Default: PJSUA_SIP_TIMER_OPTIONAL + */ + pjsua_sip_timer_use timerUse; + + /** + * Specify minimum Session Timer expiration period, in seconds. + * Must not be lower than 90. Default is 90. + */ + unsigned timerMinSESec; + + /** + * Specify Session Timer expiration period, in seconds. + * Must not be lower than timerMinSE. Default is 1800. + */ + unsigned timerSessExpiresSec; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account presence config. This will be specified in AccountConfig. + */ +struct AccountPresConfig : public PersistentObject +{ + /** + * The optional custom SIP headers to be put in the presence + * subscription request. + */ + SipHeaderVector headers; + + /** + * If this flag is set, the presence information of this account will + * be PUBLISH-ed to the server where the account belongs. + * + * Default: PJ_FALSE + */ + bool publishEnabled; + + /** + * Specify whether the client publication session should queue the + * PUBLISH request should there be another PUBLISH transaction still + * pending. If this is set to false, the client will return error + * on the PUBLISH request if there is another PUBLISH transaction still + * in progress. + * + * Default: PJSIP_PUBLISHC_QUEUE_REQUEST (TRUE) + */ + bool publishQueue; + + /** + * Maximum time to wait for unpublication transaction(s) to complete + * during shutdown process, before sending unregistration. The library + * tries to wait for the unpublication (un-PUBLISH) to complete before + * sending REGISTER request to unregister the account, during library + * shutdown process. If the value is set too short, it is possible that + * the unregistration is sent before unpublication completes, causing + * unpublication request to fail. + * + * Value is in milliseconds. + * + * Default: PJSUA_UNPUBLISH_MAX_WAIT_TIME_MSEC (2000) + */ + unsigned publishShutdownWaitMsec; + + /** + * Optional PIDF tuple ID for outgoing PUBLISH and NOTIFY. If this value + * is not specified, a random string will be used. + */ + string pidfTupleId; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account MWI (Message Waiting Indication) settings. This will be specified + * in AccountConfig. + */ +struct AccountMwiConfig : public PersistentObject +{ + /** + * Subscribe to message waiting indication events (RFC 3842). + * + * See also UaConfig.mwiUnsolicitedEnabled setting. + * + * Default: FALSE + */ + bool enabled; + + /** + * Specify the default expiration time (in seconds) for Message + * Waiting Indication (RFC 3842) event subscription. This must not + * be zero. + * + * Default: PJSIP_MWI_DEFAULT_EXPIRES (3600) + */ + unsigned expirationSec; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account's NAT (Network Address Translation) settings. This will be + * specified in AccountConfig. + */ +struct AccountNatConfig : public PersistentObject +{ + /** + * Control the use of STUN for the SIP signaling. + * + * Default: PJSUA_STUN_USE_DEFAULT + */ + pjsua_stun_use sipStunUse; + + /** + * Control the use of STUN for the media transports. + * + * Default: PJSUA_STUN_USE_DEFAULT + */ + pjsua_stun_use mediaStunUse; + + /** + * Enable ICE for the media transport. + * + * Default: False + */ + bool iceEnabled; + + /** + * Set the maximum number of ICE host candidates. + * + * Default: -1 (maximum not set) + */ + int iceMaxHostCands; + + /** + * Specify whether to use aggressive nomination. + * + * Default: True + */ + bool iceAggressiveNomination; + + /** + * For controlling agent if it uses regular nomination, specify the delay + * to perform nominated check (connectivity check with USE-CANDIDATE + * attribute) after all components have a valid pair. + * + * Default value is PJ_ICE_NOMINATED_CHECK_DELAY. + */ + unsigned iceNominatedCheckDelayMsec; + + /** + * For a controlled agent, specify how long it wants to wait (in + * milliseconds) for the controlling agent to complete sending + * connectivity check with nominated flag set to true for all components + * after the controlled agent has found that all connectivity checks in + * its checklist have been completed and there is at least one successful + * (but not nominated) check for every component. + * + * Default value for this option is + * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable + * this timer. + */ + int iceWaitNominationTimeoutMsec; + + /** + * Disable RTCP component. + * + * Default: False + */ + bool iceNoRtcp; + + /** + * Always send re-INVITE/UPDATE after ICE negotiation regardless of whether + * the default ICE transport address is changed or not. When this is set + * to False, re-INVITE/UPDATE will be sent only when the default ICE + * transport address is changed. + * + * Default: yes + */ + bool iceAlwaysUpdate; + + /** + * Enable TURN candidate in ICE. + */ + bool turnEnabled; + + /** + * Specify TURN domain name or host name, in in "DOMAIN:PORT" or + * "HOST:PORT" format. + */ + string turnServer; + + /** + * Specify the connection type to be used to the TURN server. Valid + * values are PJ_TURN_TP_UDP or PJ_TURN_TP_TCP. + * + * Default: PJ_TURN_TP_UDP + */ + pj_turn_tp_type turnConnType; + + /** + * Specify the username to authenticate with the TURN server. + */ + string turnUserName; + + /** + * Specify the type of password. Currently this must be zero to + * indicate plain-text password will be used in the password. + */ + int turnPasswordType; + + /** + * Specify the password to authenticate with the TURN server. + */ + string turnPassword; + + /** + * This option is used to update the transport address and the Contact + * header of REGISTER request. When this option is enabled, the library + * will keep track of the public IP address from the response of REGISTER + * request. Once it detects that the address has changed, it will + * unregister current Contact, update the Contact with transport address + * learned from Via header, and register a new Contact to the registrar. + * This will also update the public name of UDP transport if STUN is + * configured. + * + * See also contactRewriteMethod field. + * + * Default: TRUE + */ + int contactRewriteUse; + + /** + * Specify how Contact update will be done with the registration, if + * \a contactRewriteEnabled is enabled. The value is bitmask combination of + * \a pjsua_contact_rewrite_method. See also pjsua_contact_rewrite_method. + * + * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior. + * + * Default value: PJSUA_CONTACT_REWRITE_METHOD + * (PJSUA_CONTACT_REWRITE_NO_UNREG | PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) + */ + int contactRewriteMethod; + + /** + * This option is used to overwrite the "sent-by" field of the Via header + * for outgoing messages with the same interface address as the one in + * the REGISTER request, as long as the request uses the same transport + * instance as the previous REGISTER request. + * + * Default: TRUE + */ + int viaRewriteUse; + + /** + * This option controls whether the IP address in SDP should be replaced + * with the IP address found in Via header of the REGISTER response, ONLY + * when STUN and ICE are not used. If the value is FALSE (the original + * behavior), then the local IP address will be used. If TRUE, and when + * STUN and ICE are disabled, then the IP address found in registration + * response will be used. + * + * Default: PJ_FALSE (no) + */ + int sdpNatRewriteUse; + + /** + * Control the use of SIP outbound feature. SIP outbound is described in + * RFC 5626 to enable proxies or registrar to send inbound requests back + * to UA using the same connection initiated by the UA for its + * registration. This feature is highly useful in NAT-ed deployemtns, + * hence it is enabled by default. + * + * Note: currently SIP outbound can only be used with TCP and TLS + * transports. If UDP is used for the registration, the SIP outbound + * feature will be silently ignored for the account. + * + * Default: TRUE + */ + int sipOutboundUse; + + /** + * Specify SIP outbound (RFC 5626) instance ID to be used by this + * account. If empty, an instance ID will be generated based on + * the hostname of this agent. If application specifies this parameter, the + * value will look like "" + * without the double-quotes. + * + * Default: empty + */ + string sipOutboundInstanceId; + + /** + * Specify SIP outbound (RFC 5626) registration ID. The default value + * is empty, which would cause the library to automatically generate + * a suitable value. + * + * Default: empty + */ + string sipOutboundRegId; + + /** + * Set the interval for periodic keep-alive transmission for this account. + * If this value is zero, keep-alive will be disabled for this account. + * The keep-alive transmission will be sent to the registrar's address, + * after successful registration. + * + * Default: 15 (seconds) + */ + unsigned udpKaIntervalSec; + + /** + * Specify the data to be transmitted as keep-alive packets. + * + * Default: CR-LF + */ + string udpKaData; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account media config (applicable for both audio and video). This will be + * specified in AccountConfig. + */ +struct AccountMediaConfig : public PersistentObject +{ + /** + * Media transport (RTP) configuration. + */ + TransportConfig transportConfig; + + /** + * If remote sends SDP answer containing more than one format or codec in + * the media line, send re-INVITE or UPDATE with just one codec to lock + * which codec to use. + * + * Default: True (Yes). + */ + bool lockCodecEnabled; + + /** + * Specify whether stream keep-alive and NAT hole punching with + * non-codec-VAD mechanism (see PJMEDIA_STREAM_ENABLE_KA) is enabled + * for this account. + * + * Default: False + */ + bool streamKaEnabled; + + /** + * Specify whether secure media transport should be used for this account. + * Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and + * PJMEDIA_SRTP_MANDATORY. + * + * Default: PJSUA_DEFAULT_USE_SRTP + */ + pjmedia_srtp_use srtpUse; + + /** + * Specify whether SRTP requires secure signaling to be used. This option + * is only used when \a use_srtp option above is non-zero. + * + * Valid values are: + * 0: SRTP does not require secure signaling + * 1: SRTP requires secure transport such as TLS + * 2: SRTP requires secure end-to-end transport (SIPS) + * + * Default: PJSUA_DEFAULT_SRTP_SECURE_SIGNALING + */ + int srtpSecureSignaling; + + /** + * Specify whether IPv6 should be used on media. Default is not used. + */ + pjsua_ipv6_use ipv6Use; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account video config. This will be specified in AccountConfig. + */ +struct AccountVideoConfig : public PersistentObject +{ + /** + * Specify whether incoming video should be shown to screen by default. + * This applies to incoming call (INVITE), incoming re-INVITE, and + * incoming UPDATE requests. + * + * Regardless of this setting, application can detect incoming video + * by implementing \a on_call_media_state() callback and enumerating + * the media stream(s) with pjsua_call_get_info(). Once incoming + * video is recognised, application may retrieve the window associated + * with the incoming video and show or hide it with + * pjsua_vid_win_set_show(). + * + * Default: False + */ + bool autoShowIncoming; + + /** + * Specify whether outgoing video should be activated by default when + * making outgoing calls and/or when incoming video is detected. This + * applies to incoming and outgoing calls, incoming re-INVITE, and + * incoming UPDATE. If the setting is non-zero, outgoing video + * transmission will be started as soon as response to these requests + * is sent (or received). + * + * Regardless of the value of this setting, application can start and + * stop outgoing video transmission with pjsua_call_set_vid_strm(). + * + * Default: False + */ + bool autoTransmitOutgoing; + + /** + * Specify video window's flags. The value is a bitmask combination of + * pjmedia_vid_dev_wnd_flag. + * + * Default: 0 + */ + unsigned windowFlags; + + /** + * Specify the default capture device to be used by this account. If + * vidOutAutoTransmit is enabled, this device will be used for + * capturing video. + * + * Default: PJMEDIA_VID_DEFAULT_CAPTURE_DEV + */ + pjmedia_vid_dev_index defaultCaptureDevice; + + /** + * Specify the default rendering device to be used by this account. + * + * Default: PJMEDIA_VID_DEFAULT_RENDER_DEV + */ + pjmedia_vid_dev_index defaultRenderDevice; + + /** + * Rate control method. + * + * Default: PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING. + */ + pjmedia_vid_stream_rc_method rateControlMethod; + + /** + * Upstream/outgoing bandwidth. If this is set to zero, the video stream + * will use codec maximum bitrate setting. + * + * Default: 0 (follow codec maximum bitrate). + */ + unsigned rateControlBandwidth; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * Account configuration. + */ +struct AccountConfig : public PersistentObject +{ + /** + * Account priority, which is used to control the order of matching + * incoming/outgoing requests. The higher the number means the higher + * the priority is, and the account will be matched first. + */ + int priority; + + /** + * The Address of Record or AOR, that is full SIP URL that identifies the + * account. The value can take name address or URL format, and will look + * something like "sip:account@serviceprovider". + * + * This field is mandatory. + */ + string idUri; + + /** + * Registration settings. + */ + AccountRegConfig regConfig; + + /** + * SIP settings. + */ + AccountSipConfig sipConfig; + + /** + * Call settings. + */ + AccountCallConfig callConfig; + + /** + * Presence settings. + */ + AccountPresConfig presConfig; + + /** + * MWI (Message Waiting Indication) settings. + */ + AccountMwiConfig mwiConfig; + + /** + * NAT settings. + */ + AccountNatConfig natConfig; + + /** + * Media settings (applicable for both audio and video). + */ + AccountMediaConfig mediaConfig; + + /** + * Video settings. + */ + AccountVideoConfig videoConfig; + +public: + /** + * Default constructor will initialize with default values. + */ + AccountConfig(); + + /** + * This will return a temporary pjsua_acc_config instance, which contents + * are only valid as long as this AccountConfig structure remains valid + * AND no modifications are done to it AND no further toPj() function call + * is made. Any call to toPj() function will invalidate the content of + * temporary pjsua_acc_config that was returned by the previous call. + */ + void toPj(pjsua_acc_config &cfg) const; + + /** + * Initialize from pjsip. + */ + void fromPj(const pjsua_acc_config &prm, const pjsua_media_config *mcfg); + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +/** + * Account information. Application can query the account information + * by calling Account::getInfo(). + */ +struct AccountInfo +{ + /** + * The account ID. + */ + pjsua_acc_id id; + + /** + * Flag to indicate whether this is the default account. + */ + bool isDefault; + + /** + * Account URI + */ + string uri; + + /** + * Flag to tell whether this account has registration setting + * (reg_uri is not empty). + */ + bool regIsConfigured; + + /** + * Flag to tell whether this account is currently registered + * (has active registration session). + */ + bool regIsActive; + + /** + * An up to date expiration interval for account registration session. + */ + int regExpiresSec; + + /** + * Last registration status code. If status code is zero, the account + * is currently not registered. Any other value indicates the SIP + * status code of the registration. + */ + pjsip_status_code regStatus; + + /** + * String describing the registration status. + */ + string regStatusText; + + /** + * Last registration error code. When the status field contains a SIP + * status code that indicates a registration failure, last registration + * error code contains the error code that causes the failure. In any + * other case, its value is zero. + */ + pj_status_t regLastErr; + + /** + * Presence online status for this account. + */ + bool onlineStatus; + + /** + * Presence online status text. + */ + string onlineStatusText; + +public: + /** Import from pjsip data */ + void fromPj(const pjsua_acc_info &pai); +}; + +/** + * This structure contains parameters for onIncomingCall() account callback. + */ +struct OnIncomingCallParam +{ + /** + * The library call ID allocated for the new call. + */ + int callId; + + /** + * The incoming INVITE request. + */ + SipRxData rdata; +}; + +/** + * This structure contains parameters for onRegStarted() account callback. + */ +struct OnRegStartedParam +{ + /** + * True for registration and False for unregistration. + */ + bool renew; +}; + +/** + * This structure contains parameters for onRegState() account callback. + */ +struct OnRegStateParam +{ + /** + * Registration operation status. + */ + pj_status_t status; + + /** + * SIP status code received. + */ + pjsip_status_code code; + + /** + * SIP reason phrase received. + */ + string reason; + + /** + * The incoming message. + */ + SipRxData rdata; + + /** + * Next expiration interval. + */ + int expiration; +}; + +/** + * This structure contains parameters for onIncomingSubscribe() callback. + */ +struct OnIncomingSubscribeParam +{ + /** + * Server presence subscription instance. If application delays + * the acceptance of the request, it will need to specify this object + * when calling Account::presNotify(). + */ + void *srvPres; + + /** + * Sender URI. + */ + string fromUri; + + /** + * The incoming message. + */ + SipRxData rdata; + + /** + * The status code to respond to the request. The default value is 200. + * Application may set this to other final status code to accept or + * reject the request. + */ + pjsip_status_code code; + + /** + * The reason phrase to respond to the request. + */ + string reason; + + /** + * Additional data to be sent with the response, if any. + */ + SipTxOption txOption; +}; + +/** + * Parameters for onInstantMessage() account callback. + */ +struct OnInstantMessageParam +{ + /** + * Sender From URI. + */ + string fromUri; + + /** + * To URI of the request. + */ + string toUri; + + /** + * Contact URI of the sender. + */ + string contactUri; + + /** + * MIME type of the message body. + */ + string contentType; + + /** + * The message body. + */ + string msgBody; + + /** + * The whole message. + */ + SipRxData rdata; +}; + +/** + * Parameters for onInstantMessageStatus() account callback. + */ +struct OnInstantMessageStatusParam +{ + /** + * Token or a user data that was associated with the pager + * transmission. + */ + Token userData; + + /** + * Destination URI. + */ + string toUri; + + /** + * The message body. + */ + string msgBody; + + /** + * The SIP status code of the transaction. + */ + pjsip_status_code code; + + /** + * The reason phrase of the transaction. + */ + string reason; + + /** + * The incoming response that causes this callback to be called. + * If the transaction fails because of time out or transport error, + * the content will be empty. + */ + SipRxData rdata; +}; + +/** + * Parameters for onTypingIndication() account callback. + */ +struct OnTypingIndicationParam +{ + /** + * Sender/From URI. + */ + string fromUri; + + /** + * To URI. + */ + string toUri; + + /** + * The Contact URI. + */ + string contactUri; + + /** + * Boolean to indicate if sender is typing. + */ + bool isTyping; + + /** + * The whole message buffer. + */ + SipRxData rdata; +}; + +/** + * Parameters for onMwiInfo() account callback. + */ +struct OnMwiInfoParam +{ + /** + * MWI subscription state. + */ + pjsip_evsub_state state; + + /** + * The whole message buffer. + */ + SipRxData rdata; +}; + +/** + * Parameters for presNotify() account method. + */ +struct PresNotifyParam +{ + /** + * Server presence subscription instance. + */ + void *srvPres; + + /** + * Server presence subscription state to set. + */ + pjsip_evsub_state state; + + /** + * Optionally specify the state string name, if state is not "active", + * "pending", or "terminated". + */ + string stateStr; + + /** + * If the new state is PJSIP_EVSUB_STATE_TERMINATED, optionally specify + * the termination reason. + */ + string reason; + + /** + * If the new state is PJSIP_EVSUB_STATE_TERMINATED, this specifies + * whether the NOTIFY request should contain message body containing + * account's presence information. + */ + bool withBody; + + /** + * Optional list of headers to be sent with the NOTIFY request. + */ + SipTxOption txOption; +}; + + +/** + * Wrapper class for Buddy matching algo. + * + * Default algo is a simple substring lookup of search-token in the + * Buddy URIs, with case sensitive. Application can implement its own + * matching algo by overriding this class and specifying its instance + * in Account::findBuddy(). + */ +class FindBuddyMatch +{ +public: + /** + * Default algo implementation. + */ + virtual bool match(const string &token, const Buddy &buddy) + { + BuddyInfo bi = buddy.getInfo(); + return bi.uri.find(token) != string::npos; + } + + /** + * Destructor. + */ + virtual ~FindBuddyMatch() {} +}; + + +/** + * Account. + */ +class Account +{ +public: + /** + * Constructor. + */ + Account(); + + /** + * Destructor. Note that if the account is deleted, it will also delete + * the corresponding account in the PJSUA-LIB. + */ + virtual ~Account(); + + /** + * Create the account. + * + * @param cfg The account config. + * @param make_default Make this the default account. + */ + void create(const AccountConfig &cfg, + bool make_default=false) throw(Error); + + /** + * Modify the account to use the specified account configuration. + * Depending on the changes, this may cause unregistration or + * reregistration on the account. + * + * @param cfg New account config to be applied to the + * account. + */ + void modify(const AccountConfig &cfg) throw(Error); + + /** + * Check if this account is still valid. + * + * @return True if it is. + */ + bool isValid() const; + + /** + * Set this as default account to be used when incoming and outgoing + * requests don't match any accounts. + * + * @return PJ_SUCCESS on success. + */ + void setDefault() throw(Error); + + /** + * Check if this account is the default account. Default account will be + * used for incoming and outgoing requests that don't match any other + * accounts. + * + * @return True if this is the default account. + */ + bool isDefault() const; + + /** + * Get PJSUA-LIB account ID or index associated with this account. + * + * @return Integer greater than or equal to zero. + */ + int getId() const; + + /** + * Get the Account class for the specified account Id. + * + * @param acc_id The account ID to lookup + * + * @return The Account instance or NULL if not found. + */ + static Account *lookup(int acc_id); + + /** + * Get account info. + * + * @return Account info. + */ + AccountInfo getInfo() const throw(Error); + + /** + * Update registration or perform unregistration. Application normally + * only needs to call this function if it wants to manually update the + * registration or to unregister from the server. + * + * @param renew If False, this will start unregistration + * process. + */ + void setRegistration(bool renew) throw(Error); + + /** + * Set or modify account's presence online status to be advertised to + * remote/presence subscribers. This would trigger the sending of + * outgoing NOTIFY request if there are server side presence subscription + * for this account, and/or outgoing PUBLISH if presence publication is + * enabled for this account. + * + * @param pres_st Presence online status. + */ + void setOnlineStatus(const PresenceStatus &pres_st) throw(Error); + + /** + * Lock/bind this account to a specific transport/listener. Normally + * application shouldn't need to do this, as transports will be selected + * automatically by the library according to the destination. + * + * When account is locked/bound to a specific transport, all outgoing + * requests from this account will use the specified transport (this + * includes SIP registration, dialog (call and event subscription), and + * out-of-dialog requests such as MESSAGE). + * + * Note that transport id may be specified in AccountConfig too. + * + * @param tp_id The transport ID. + */ + void setTransport(TransportId tp_id) throw(Error); + + /** + * Send NOTIFY to inform account presence status or to terminate server + * side presence subscription. If application wants to reject the incoming + * request, it should set the param \a PresNotifyParam.state to + * PJSIP_EVSUB_STATE_TERMINATED. + * + * @param prm The sending NOTIFY parameter. + */ + void presNotify(const PresNotifyParam &prm) throw(Error); + + /** + * Enumerate all buddies of the account. + * + * @return The buddy list. + */ + const BuddyVector& enumBuddies() const throw(Error); + + /** + * Find a buddy in the buddy list with the specified URI. + * + * Exception: if buddy is not found, PJ_ENOTFOUND will be thrown. + * + * @param uri The buddy URI. + * @param buddy_match The buddy match algo. + * + * @return The pointer to buddy. + */ + Buddy* findBuddy(string uri, FindBuddyMatch *buddy_match = NULL) const + throw(Error); + + /** + * An internal function to add a Buddy to Account buddy list. + * This function must never be used by application. + */ + void addBuddy(Buddy *buddy); + + /** + * An internal function to remove a Buddy from Account buddy list. + * This function must never be used by application. + */ + void removeBuddy(Buddy *buddy); + +public: + /* + * Callbacks + */ + /** + * Notify application on incoming call. + * + * @param prm Callback parameter. + */ + virtual void onIncomingCall(OnIncomingCallParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when registration or unregistration has been + * initiated. Note that this only notifies the initial registration + * and unregistration. Once registration session is active, subsequent + * refresh will not cause this callback to be called. + * + * @param prm Callback parameter. + */ + virtual void onRegStarted(OnRegStartedParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when registration status has changed. + * Application may then query the account info to get the + * registration details. + * + * @param prm Callback parameter. + */ + virtual void onRegState(OnRegStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notification when incoming SUBSCRIBE request is received. Application + * may use this callback to authorize the incoming subscribe request + * (e.g. ask user permission if the request should be granted). + * + * If this callback is not implemented, all incoming presence subscription + * requests will be accepted. + * + * If this callback is implemented, application has several choices on + * what to do with the incoming request: + * - it may reject the request immediately by specifying non-200 class + * final response in the IncomingSubscribeParam.code parameter. + * - it may immediately accept the request by specifying 200 as the + * IncomingSubscribeParam.code parameter. This is the default value if + * application doesn't set any value to the IncomingSubscribeParam.code + * parameter. In this case, the library will automatically send NOTIFY + * request upon returning from this callback. + * - it may delay the processing of the request, for example to request + * user permission whether to accept or reject the request. In this + * case, the application MUST set the IncomingSubscribeParam.code + * argument to 202, then IMMEDIATELY calls presNotify() with + * state PJSIP_EVSUB_STATE_PENDING and later calls presNotify() + * again to accept or reject the subscription request. + * + * Any IncomingSubscribeParam.code other than 200 and 202 will be treated + * as 200. + * + * Application MUST return from this callback immediately (e.g. it must + * not block in this callback while waiting for user confirmation). + * + * @param prm Callback parameter. + */ + virtual void onIncomingSubscribe(OnIncomingSubscribeParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application on incoming instant message or pager (i.e. MESSAGE + * request) that was received outside call context. + * + * @param prm Callback parameter. + */ + virtual void onInstantMessage(OnInstantMessageParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application about the delivery status of outgoing pager/instant + * message (i.e. MESSAGE) request. + * + * @param prm Callback parameter. + */ + virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application about typing indication. + * + * @param prm Callback parameter. + */ + virtual void onTypingIndication(OnTypingIndicationParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notification about MWI (Message Waiting Indication) status change. + * This callback can be called upon the status change of the + * SUBSCRIBE request (for example, 202/Accepted to SUBSCRIBE is received) + * or when a NOTIFY reqeust is received. + * + * @param prm Callback parameter. + */ + virtual void onMwiInfo(OnMwiInfoParam &prm) + { PJ_UNUSED_ARG(prm); } + +protected: + friend class Endpoint; + +private: + pjsua_acc_id id; + string tmpReason; // for saving response's reason + BuddyVector buddyList; +}; + +/** + * @} // PJSUA2_ACC + */ + +} // namespace pj + +#endif /* __PJSUA2_ACCOUNT_HPP__ */ + diff --git a/pjsip/include/pjsua2/call.hpp b/pjsip/include/pjsua2/call.hpp new file mode 100644 index 00000000..f16db153 --- /dev/null +++ b/pjsip/include/pjsua2/call.hpp @@ -0,0 +1,1713 @@ +/* $Id$ */ +/* + * Copyright (C) 2012-2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_CALL_HPP__ +#define __PJSUA2_CALL_HPP__ + +/** + * @file pjsua2/call.hpp + * @brief PJSUA2 Call manipulation + */ +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_CALL Call + * @ingroup PJSUA2_Ref + */ + +/** + * @defgroup PJSUA2_Call_Data_Structure Call Related Types + * @ingroup PJSUA2_DS + * @{ + */ + +using std::string; +using std::vector; + +////////////////////////////////////////////////////////////////////////////// + +/** + * Codec parameters, corresponds to pjmedia_codec_param or + * pjmedia_vid_codec_param. + */ +typedef void *CodecParam; + +/** + * Media stream, corresponds to pjmedia_stream + */ +typedef void *MediaStream; + +/** + * Media transport, corresponds to pjmedia_transport + */ +typedef void *MediaTransport; + +/** + * This structure describes statistics state. + */ +struct MathStat +{ + int n; /**< number of samples */ + int max; /**< maximum value */ + int min; /**< minimum value */ + int last; /**< last value */ + int mean; /**< mean */ + +public: + /** + * Default constructor + */ + MathStat(); + + /** + * Convert from pjsip + */ + void fromPj(const pj_math_stat &prm); +}; + +/** + * Unidirectional RTP stream statistics. + */ +struct RtcpStreamStat +{ + TimeValue update; /**< Time of last update. */ + unsigned updateCount;/**< Number of updates (to calculate avg) */ + unsigned pkt; /**< Total number of packets */ + unsigned bytes; /**< Total number of payload/bytes */ + unsigned discard; /**< Total number of discarded packets. */ + unsigned loss; /**< Total number of packets lost */ + unsigned reorder; /**< Total number of out of order packets */ + unsigned dup; /**< Total number of duplicates packets */ + + MathStat lossPeriodUsec; /**< Loss period statistics */ + + struct { + unsigned burst; /**< Burst/sequential packet lost detected */ + unsigned random; /**< Random packet lost detected. */ + } lossType; /**< Types of loss detected. */ + + MathStat jitterUsec; /**< Jitter statistics */ + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_rtcp_stream_stat &prm); +}; + +/** + * RTCP SDES structure. + */ +struct RtcpSdes +{ + string cname; /**< RTCP SDES type CNAME. */ + string name; /**< RTCP SDES type NAME. */ + string email; /**< RTCP SDES type EMAIL. */ + string phone; /**< RTCP SDES type PHONE. */ + string loc; /**< RTCP SDES type LOC. */ + string tool; /**< RTCP SDES type TOOL. */ + string note; /**< RTCP SDES type NOTE. */ + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_rtcp_sdes &prm); +}; + +/** + * Bidirectional RTP stream statistics. + */ +struct RtcpStat +{ + TimeValue start; /**< Time when session was created */ + + RtcpStreamStat txStat; /**< Encoder stream statistics. */ + RtcpStreamStat rxStat; /**< Decoder stream statistics. */ + + MathStat rttUsec; /**< Round trip delay statistic. */ + + pj_uint32_t rtpTxLastTs; /**< Last TX RTP timestamp. */ + pj_uint16_t rtpTxLastSeq; /**< Last TX RTP sequence. */ + + MathStat rxIpdvUsec; /**< Statistics of IP packet delay + variation in receiving + direction. It is only used when + PJMEDIA_RTCP_STAT_HAS_IPDV is + set to non-zero. */ + + MathStat rxRawJitterUsec;/**< Statistic of raw jitter in + receiving direction. It is only + used when + PJMEDIA_RTCP_STAT_HAS_RAW_JITTER + is set to non-zero. */ + + RtcpSdes peerSdes; /**< Peer SDES. */ + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_rtcp_stat &prm); +}; + +/** + * This structure describes jitter buffer state. + */ +struct JbufState +{ + /* Setting */ + unsigned frameSize; /**< Individual frame size, in bytes. */ + unsigned minPrefetch; /**< Minimum allowed prefetch, in frms. */ + unsigned maxPrefetch; /**< Maximum allowed prefetch, in frms. */ + + /* Status */ + unsigned burst; /**< Current burst level, in frames */ + unsigned prefetch; /**< Current prefetch value, in frames */ + unsigned size; /**< Current buffer size, in frames. */ + + /* Statistic */ + unsigned avgDelayMsec; /**< Average delay, in ms. */ + unsigned minDelayMsec; /**< Minimum delay, in ms. */ + unsigned maxDelayMsec; /**< Maximum delay, in ms. */ + unsigned devDelayMsec; /**< Standard deviation of delay, in ms.*/ + unsigned avgBurst; /**< Average burst, in frames. */ + unsigned lost; /**< Number of lost frames. */ + unsigned discard; /**< Number of discarded frames. */ + unsigned empty; /**< Number of empty on GET events. */ + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_jb_state &prm); +}; + +/** + * This structure describes SDP session description. It corresponds to the + * pjmedia_sdp_session structure. + */ +struct SdpSession +{ + /** + * The whole SDP as a string. + */ + string wholeSdp; + + /** + * Pointer to its original pjmedia_sdp_session. Only valid when the struct + * is converted from PJSIP's pjmedia_sdp_session. + */ + void *pjSdpSession; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_sdp_session &sdp); +}; + +/** + * This structure describes a media format changed event. + */ +struct MediaFmtChangedEvent +{ + unsigned newWidth; /**< The new width. */ + unsigned newHeight; /**< The new height. */ +}; + +/** + * This structure describes a media event. It corresponds to the + * pjmedia_event structure. + */ +struct MediaEvent +{ + /** + * The event type. + */ + pjmedia_event_type type; + + /** + * Additional data/parameters about the event. The type of data + * will be specific to the event type being reported. + */ + union { + /** + * Media format changed event data. + */ + MediaFmtChangedEvent fmtChanged; + + /** + * Pointer to storage to user event data, if it's outside + * this struct + */ + GenericData ptr; + } data; + + /** + * Pointer to original pjmedia_event. Only valid when the struct + * is converted from PJSIP's pjmedia_event. + */ + void *pjMediaEvent; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_event &ev); +}; + +/** + * This structure describes media transport informations. It corresponds to the + * pjmedia_transport_info structure. + */ +struct MediaTransportInfo +{ + /** + * Remote address where RTP originated from. + */ + SocketAddress srcRtpName; + + /** + * Remote address where RTCP originated from. + */ + SocketAddress srcRtcpName; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjmedia_transport_info &info); +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * Call settings. + */ +struct CallSetting +{ + /** + * Bitmask of pjsua_call_flag constants. + * + * Default: PJSUA_CALL_INCLUDE_DISABLED_MEDIA + */ + unsigned flag; + + /** + * This flag controls what methods to request keyframe are allowed on + * the call. Value is bitmask of pjsua_vid_req_keyframe_method. + * + * Default: PJSUA_VID_REQ_KEYFRAME_SIP_INFO | + * PJSUA_VID_REQ_KEYFRAME_RTCP_PLI + */ + unsigned reqKeyframeMethod; + + /** + * Number of simultaneous active audio streams for this call. Setting + * this to zero will disable audio in this call. + * + * Default: 1 + */ + unsigned audioCount; + + /** + * Number of simultaneous active video streams for this call. Setting + * this to zero will disable video in this call. + * + * Default: 1 (if video feature is enabled, otherwise it is zero) + */ + unsigned videoCount; + +public: + /** + * Default constructor initializes with empty or default values. + */ + CallSetting(pj_bool_t useDefaultValues = false); + + /** + * Check if the settings are set with empty values. + * + * @return True if the settings are empty. + */ + bool isEmpty() const; + + /** + * Convert from pjsip + */ + void fromPj(const pjsua_call_setting &prm); + + /** + * Convert to pjsip + */ + pjsua_call_setting toPj() const; +}; + +/** + * Call media information. + */ +struct CallMediaInfo +{ + /** + * Media index in SDP. + */ + unsigned index; + + /** + * Media type. + */ + pjmedia_type type; + + /** + * Media direction. + */ + pjmedia_dir dir; + + /** + * Call media status. + */ + pjsua_call_media_status status; + + /** + * The conference port number for the call. Only valid if the media type + * is audio. + */ + int audioConfSlot; + + /** + * The window id for incoming video, if any, or + * PJSUA_INVALID_ID. Only valid if the media type is video. + */ + pjsua_vid_win_id videoIncomingWindowId; + + /** + * The video capture device for outgoing transmission, if any, + * or PJMEDIA_VID_INVALID_DEV. Only valid if the media type is video. + */ + pjmedia_vid_dev_index videoCapDev; + +public: + /** + * Default constructor + */ + CallMediaInfo(); + + /** + * Convert from pjsip + */ + void fromPj(const pjsua_call_media_info &prm); +}; + +/** Array of call media info */ +typedef std::vector CallMediaInfoVector; + +/** + * Call information. Application can query the call information + * by calling Call::getInfo(). + */ +struct CallInfo +{ + /** + * Call identification. + */ + pjsua_call_id id; + + /** + * Initial call role (UAC == caller) + */ + pjsip_role_e role; + + /** + * The account ID where this call belongs. + */ + pjsua_acc_id accId; + + /** + * Local URI + */ + string localUri; + + /** + * Local Contact + */ + string localContact; + + /** + * Remote URI + */ + string remoteUri; + + /** + * Remote contact + */ + string remoteContact; + + /** + * Dialog Call-ID string. + */ + string callIdString; + + /** + * Call setting + */ + CallSetting setting; + + /** + * Call state + */ + pjsip_inv_state state; + + /** + * Text describing the state + */ + string stateText; + + /** + * Last status code heard, which can be used as cause code + */ + pjsip_status_code lastStatusCode; + + /** + * The reason phrase describing the last status. + */ + string lastReason; + + /** + * Array of active media information. + */ + CallMediaInfoVector media; + + /** + * Array of provisional media information. This contains the media info + * in the provisioning state, that is when the media session is being + * created/updated (SDP offer/answer is on progress). + */ + CallMediaInfoVector provMedia; + + /** + * Up-to-date call connected duration (zero when call is not + * established) + */ + TimeValue connectDuration; + + /** + * Total call duration, including set-up time + */ + TimeValue totalDuration; + + /** + * Flag if remote was SDP offerer + */ + bool remOfferer; + + /** + * Number of audio streams offered by remote + */ + unsigned remAudioCount; + + /** + * Number of video streams offered by remote + */ + unsigned remVideoCount; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjsua_call_info &pci); +}; + +/** + * Media stream info. + */ +struct StreamInfo +{ + /** + * Media type of this stream. + */ + pjmedia_type type; + + /** + * Transport protocol (RTP/AVP, etc.) + */ + pjmedia_tp_proto proto; + + /** + * Media direction. + */ + pjmedia_dir dir; + + /** + * Remote RTP address + */ + SocketAddress remoteRtpAddress; + + /** + * Optional remote RTCP address + */ + SocketAddress remoteRtcpAddress; + + /** + * Outgoing codec payload type. + */ + unsigned txPt; + + /** + * Incoming codec payload type. + */ + unsigned rxPt; + + /** + * Codec name. + */ + string codecName; + + /** + * Codec clock rate. + */ + unsigned codecClockRate; + + /** + * Optional codec param. + */ + CodecParam codecParam; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjsua_stream_info &info); +}; + +/** + * Media stream statistic. + */ +struct StreamStat +{ + /** + * RTCP statistic. + */ + RtcpStat rtcp; + + /** + * Jitter buffer statistic. + */ + JbufState jbuf; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pjsua_stream_stat &prm); +}; + +/** + * This structure contains parameters for Call::onCallState() callback. + */ +struct OnCallStateParam +{ + /** + * Event which causes the call state to change. + */ + SipEvent e; +}; + +/** + * This structure contains parameters for Call::onCallTsxState() callback. + */ +struct OnCallTsxStateParam +{ + /** + * Transaction event that caused the state change. + */ + SipEvent e; +}; + +/** + * This structure contains parameters for Call::onCallMediaState() callback. + */ +struct OnCallMediaStateParam +{ +}; + +/** + * This structure contains parameters for Call::onCallSdpCreated() callback. + */ +struct OnCallSdpCreatedParam +{ + /** + * The SDP has just been created. + */ + SdpSession sdp; + + /** + * The remote SDP, will be empty if local is SDP offerer. + */ + SdpSession remSdp; +}; + +/** + * This structure contains parameters for Call::onStreamCreated() + * callback. + */ +struct OnStreamCreatedParam +{ + /** + * Media stream. + */ + MediaStream stream; + + /** + * Stream index in the media session. + */ + unsigned streamIdx; + + /** + * On input, it specifies the media port of the stream. Application + * may modify this pointer to point to different media port to be + * registered to the conference bridge. + */ + MediaPort pPort; +}; + +/** + * This structure contains parameters for Call::onStreamDestroyed() + * callback. + */ +struct OnStreamDestroyedParam +{ + /** + * Media stream. + */ + MediaStream stream; + + /** + * Stream index in the media session. + */ + unsigned streamIdx; +}; + +/** + * This structure contains parameters for Call::onDtmfDigit() + * callback. + */ +struct OnDtmfDigitParam +{ + /** + * DTMF ASCII digit. + */ + string digit; +}; + +/** + * This structure contains parameters for Call::onCallTransferRequest() + * callback. + */ +struct OnCallTransferRequestParam +{ + /** + * The destination where the call will be transfered to. + */ + string dstUri; + + /** + * Status code to be returned for the call transfer request. On input, + * it contains status code 200. + */ + pjsip_status_code statusCode; + + /** + * The current call setting, application can update this setting + * for the call being transfered. + */ + CallSetting opt; +}; + +/** + * This structure contains parameters for Call::onCallTransferStatus() + * callback. + */ +struct OnCallTransferStatusParam +{ + /** + * Status progress of the transfer request. + */ + pjsip_status_code statusCode; + + /** + * Status progress reason. + */ + string reason; + + /** + * If true, no further notification will be reported. The statusCode + * specified in this callback is the final status. + */ + bool finalNotify; + + /** + * Initially will be set to true, application can set this to false + * if it no longer wants to receive further notification (for example, + * after it hangs up the call). + */ + bool cont; +}; + +/** + * This structure contains parameters for Call::onCallReplaceRequest() + * callback. + */ +struct OnCallReplaceRequestParam +{ + /** + * The incoming INVITE request to replace the call. + */ + SipRxData rdata; + + /** + * Status code to be set by application. Application should only + * return a final status (200-699) + */ + pjsip_status_code statusCode; + + /** + * Optional status text to be set by application. + */ + string reason; + + /** + * The current call setting, application can update this setting for + * the call being replaced. + */ + CallSetting opt; +}; + +/** + * This structure contains parameters for Call::onCallReplaced() callback. + */ +struct OnCallReplacedParam +{ + /** + * The new call id. + */ + pjsua_call_id newCallId; +}; + +/** + * This structure contains parameters for Call::onCallRxOffer() callback. + */ +struct OnCallRxOfferParam +{ + /** + * The new offer received. + */ + SdpSession offer; + + /** + * Status code to be returned for answering the offer. On input, + * it contains status code 200. Currently, valid values are only + * 200 and 488. + */ + pjsip_status_code statusCode; + + /** + * The current call setting, application can update this setting for + * answering the offer. + */ + CallSetting opt; +}; + +/** + * This structure contains parameters for Call::onCallRedirected() callback. + */ +struct OnCallRedirectedParam +{ + /** + * The current target to be tried. + */ + string targetUri; + + /** + * The event that caused this callback to be called. + * This could be the receipt of 3xx response, or 4xx/5xx response + * received for the INVITE sent to subsequent targets, or empty + * (e.type == PJSIP_EVENT_UNKNOWN) + * if this callback is called from within Call::processRedirect() + * context. + */ + SipEvent e; +}; + +/** + * This structure contains parameters for Call::onCallMediaEvent() callback. + */ +struct OnCallMediaEventParam +{ + /** + * The media stream index. + */ + unsigned medIdx; + + /** + * The media event. + */ + MediaEvent ev; +}; + +/** + * This structure contains parameters for Call::onCallMediaTransportState() + * callback. + */ +struct OnCallMediaTransportStateParam +{ + /** + * The media index. + */ + unsigned medIdx; + + /** + * The media transport state + */ + pjsua_med_tp_st state; + + /** + * The last error code related to the media transport state. + */ + pj_status_t status; + + /** + * Optional SIP error code. + */ + int sipErrorCode; +}; + +/** + * This structure contains parameters for Call::onCreateMediaTransport() + * callback. + */ +struct OnCreateMediaTransportParam +{ + /** + * The media index in the SDP for which this media transport will be used. + */ + unsigned mediaIdx; + + /** + * The media transport which otherwise will be used by the call has this + * callback not been implemented. Application can change this to its own + * instance of media transport to be used by the call. + */ + MediaTransport mediaTp; + + /** + * Bitmask from pjsua_create_media_transport_flag. + */ + unsigned flags; +}; + +/** + * @} // PJSUA2_Call_Data_Structure + */ + +/** + * @addtogroup PJSUA2_CALL + * @{ + */ + +/** + * This structure contains parameters for Call::answer(), Call::hangup(), + * Call::reinvite(), Call::update(), Call::xfer(), Call::xferReplaces(), + * Call::setHold(). + */ +struct CallOpParam +{ + /** + * The call setting. + */ + CallSetting opt; + + /** + * Status code. + */ + pjsip_status_code statusCode; + + /** + * Reason phrase. + */ + string reason; + + /** + * Options. + */ + unsigned options; + + /** + * List of headers etc to be added to outgoing response message. + * Note that this message data will be persistent in all next + * answers/responses for this INVITE request. + */ + SipTxOption txOption; + +public: + /** + * Default constructor initializes with zero/empty values. + * Setting useDefaultCallSetting to true will initialize opt with default + * call setting values. + */ + CallOpParam(bool useDefaultCallSetting = false); +}; + +/** + * This structure contains parameters for Call::sendRequest() + */ +struct CallSendRequestParam +{ + /** + * SIP method of the request. + */ + string method; + + /** + * Message body and/or list of headers etc to be included in + * outgoing request. + */ + SipTxOption txOption; + +public: + /** + * Default constructor initializes with zero/empty values. + */ + CallSendRequestParam(); +}; + +/** + * This structure contains parameters for Call::vidSetStream() + */ +struct CallVidSetStreamParam +{ + /** + * Specify the media stream index. This can be set to -1 to denote + * the default video stream in the call, which is the first active + * video stream or any first video stream if none is active. + * + * This field is valid for all video stream operations, except + * PJSUA_CALL_VID_STRM_ADD. + * + * Default: -1 (first active video stream, or any first video stream + * if none is active) + */ + int medIdx; + + /** + * Specify the media stream direction. + * + * This field is valid for the following video stream operations: + * PJSUA_CALL_VID_STRM_ADD and PJSUA_CALL_VID_STRM_CHANGE_DIR. + * + * Default: PJMEDIA_DIR_ENCODING_DECODING + */ + pjmedia_dir dir; + + /** + * Specify the video capture device ID. This can be set to + * PJMEDIA_VID_DEFAULT_CAPTURE_DEV to specify the default capture + * device as configured in the account. + * + * This field is valid for the following video stream operations: + * PJSUA_CALL_VID_STRM_ADD and PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV. + * + * Default: PJMEDIA_VID_DEFAULT_CAPTURE_DEV. + */ + pjmedia_vid_dev_index capDev; + +public: + /** + * Default constructor + */ + CallVidSetStreamParam(); +}; + +/** + * Call. + */ +class Call +{ +public: + /** + * Constructor. + */ + Call(Account& acc, int call_id = PJSUA_INVALID_ID); + + /** + * Destructor. + */ + virtual ~Call(); + + /** + * Obtain detail information about this call. + * + * @return Call info. + */ + CallInfo getInfo() const throw(Error); + + /** + * Check if this call has active INVITE session and the INVITE + * session has not been disconnected. + * + * @return True if call is active. + */ + bool isActive() const; + + /** + * Get PJSUA-LIB call ID or index associated with this call. + * + * @return Integer greater than or equal to zero. + */ + int getId() const; + + /** + * Get the Call class for the specified call Id. + * + * @param call_id The call ID to lookup + * + * @return The Call instance or NULL if not found. + */ + static Call *lookup(int call_id); + + /** + * Check if call has an active media session. + * + * @return True if yes. + */ + bool hasMedia() const; + + /** + * Get media for the specified media index. + * + * @param med_idx Media index. + * + * @return The media or NULL if invalid or inactive. + */ + Media *getMedia(unsigned med_idx) const; + + /** + * Check if remote peer support the specified capability. + * + * @param htype The header type (pjsip_hdr_e) to be checked, which + * value may be: + * - PJSIP_H_ACCEPT + * - PJSIP_H_ALLOW + * - PJSIP_H_SUPPORTED + * @param hname If htype specifies PJSIP_H_OTHER, then the header + * name must be supplied in this argument. Otherwise + * the value must be set to empty string (""). + * @param token The capability token to check. For example, if \a + * htype is PJSIP_H_ALLOW, then \a token specifies the + * method names; if \a htype is PJSIP_H_SUPPORTED, then + * \a token specifies the extension names such as + * "100rel". + * + * @return PJSIP_DIALOG_CAP_SUPPORTED if the specified + * capability is explicitly supported, see + * pjsip_dialog_cap_status for more info. + */ + pjsip_dialog_cap_status remoteHasCap(int htype, + const string &hname, + const string &token) const; + + /** + * Attach application specific data to the call. Application can then + * inspect this data by calling getUserData(). + * + * @param user_data Arbitrary data to be attached to the call. + */ + void setUserData(Token user_data); + + /** + * Get user data attached to the call, which has been previously set with + * setUserData(). + * + * @return The user data. + */ + Token getUserData() const; + + /** + * Get the NAT type of remote's endpoint. This is a proprietary feature + * of PJSUA-LIB which sends its NAT type in the SDP when \a natTypeInSdp + * is set in UaConfig. + * + * This function can only be called after SDP has been received from remote, + * which means for incoming call, this function can be called as soon as + * call is received as long as incoming call contains SDP, and for outgoing + * call, this function can be called only after SDP is received (normally in + * 200/OK response to INVITE). As a general case, application should call + * this function after or in \a onCallMediaState() callback. + * + * @return The NAT type. + * + * @see Endpoint::natGetType(), natTypeInSdp + */ + pj_stun_nat_type getRemNatType() throw(Error); + + /** + * Make outgoing call to the specified URI. + * + * @param dst_uri URI to be put in the To header (normally is the same + * as the target URI). + * @param prm.opt Optional call setting. + * @param prm.txOption Optional headers etc to be added to outgoing INVITE + * request. + */ + void makeCall(const string &dst_uri, const CallOpParam &prm) throw(Error); + + /** + * Send response to incoming INVITE request with call setting param. + * Depending on the status code specified as parameter, this function may + * send provisional response, establish the call, or terminate the call. + * Notes about call setting: + * - if call setting is changed in the subsequent call to this function, + * only the first call setting supplied will applied. So normally + * application will not supply call setting before getting confirmation + * from the user. + * - if no call setting is supplied when SDP has to be sent, i.e: answer + * with status code 183 or 2xx, the default call setting will be used, + * check CallSetting for its default values. + * + * @param prm.opt Optional call setting. + * @param prm.statusCode Status code, (100-699). + * @param prm.reason Optional reason phrase. If empty, default text + * will be used. + * @param prm.txOption Optional list of headers etc to be added to outgoing + * response message. Note that this message data will + * be persistent in all next answers/responses for this + * INVITE request. + */ + void answer(const CallOpParam &prm) throw(Error); + + /** + * Hangup call by using method that is appropriate according to the + * call state. This function is different than answering the call with + * 3xx-6xx response (with answer()), in that this function + * will hangup the call regardless of the state and role of the call, + * while answer() only works with incoming calls on EARLY + * state. + * + * @param prm.statusCode + * Optional status code to be sent when we're rejecting + * incoming call. If the value is zero, "603/Decline" + * will be sent. + * @param prm.reason Optional reason phrase to be sent when we're + * rejecting incoming call. If empty, default text + * will be used. + * @param prm.txOption Optional list of headers etc to be added to outgoing + * request/response message. + */ + void hangup(const CallOpParam &prm) throw(Error); + + /** + * Put the specified call on hold. This will send re-INVITE with the + * appropriate SDP to inform remote that the call is being put on hold. + * The final status of the request itself will be reported on the + * \a onCallMediaState() callback, which inform the application that + * the media state of the call has changed. + * + * @param prm.options Bitmask of pjsua_call_flag constants. Currently, + * only the flag PJSUA_CALL_UPDATE_CONTACT can be used. + * @param prm.txOption Optional message components to be sent with + * the request. + */ + void setHold(const CallOpParam &prm) throw(Error); + + /** + * Send re-INVITE to release hold. + * The final status of the request itself will be reported on the + * \a onCallMediaState() callback, which inform the application that + * the media state of the call has changed. + * + * @param prm.opt Optional call setting, if empty, the current call + * setting will remain unchanged. + * @param prm.txOption Optional message components to be sent with + * the request. + */ + void reinvite(const CallOpParam &prm) throw(Error); + + /** + * Send UPDATE request. + * + * @param prm.opt Optional call setting, if empty, the current call + * setting will remain unchanged. + * @param prm.txOption Optional message components to be sent with + * the request. + */ + void update(const CallOpParam &prm) throw(Error); + + /** + * Initiate call transfer to the specified address. This function will send + * REFER request to instruct remote call party to initiate a new INVITE + * session to the specified destination/target. + * + * If application is interested to monitor the successfulness and + * the progress of the transfer request, it can implement + * \a onCallTransferStatus() callback which will report the progress + * of the call transfer request. + * + * @param dest URI of new target to be contacted. The URI may be + * in name address or addr-spec format. + * @param prm.txOption Optional message components to be sent with + * the request. + */ + void xfer(const string &dest, const CallOpParam &prm) throw(Error); + + /** + * Initiate attended call transfer. This function will send REFER request + * to instruct remote call party to initiate new INVITE session to the URL + * of \a destCall. The party at \a dest_call then should "replace" + * the call with us with the new call from the REFER recipient. + * + * @param dest_call The call to be replaced. + * @param prm.options Application may specify + * PJSUA_XFER_NO_REQUIRE_REPLACES to suppress the + * inclusion of "Require: replaces" in + * the outgoing INVITE request created by the REFER + * request. + * @param prm.txOption Optional message components to be sent with + * the request. + */ + void xferReplaces(const Call& dest_call, + const CallOpParam &prm) throw(Error); + + /** + * Accept or reject redirection response. Application MUST call this + * function after it signaled PJSIP_REDIRECT_PENDING in the + * \a onCallRedirected() callback, + * to notify the call whether to accept or reject the redirection + * to the current target. Application can use the combination of + * PJSIP_REDIRECT_PENDING command in \a onCallRedirected() callback and + * this function to ask for user permission before redirecting the call. + * + * Note that if the application chooses to reject or stop redirection (by + * using PJSIP_REDIRECT_REJECT or PJSIP_REDIRECT_STOP respectively), the + * call disconnection callback will be called before this function returns. + * And if the application rejects the target, the \a onCallRedirected() + * callback may also be called before this function returns if there is + * another target to try. + * + * @param cmd Redirection operation to be applied to the current + * target. The semantic of this argument is similar + * to the description in the \a onCallRedirected() + * callback, except that the PJSIP_REDIRECT_PENDING is + * not accepted here. + */ + void processRedirect(pjsip_redirect_op cmd) throw(Error); + + /** + * Send DTMF digits to remote using RFC 2833 payload formats. + * + * @param digits DTMF string digits to be sent. + */ + void dialDtmf(const string &digits) throw(Error); + + /** + * Send instant messaging inside INVITE session. + * + * @param prm.contentType + * MIME type. + * @param prm.content The message content. + * @param prm.txOption Optional list of headers etc to be included in + * outgoing request. The body descriptor in the + * txOption is ignored. + * @param prm.userData Optional user data, which will be given back when + * the IM callback is called. + */ + void sendInstantMessage(const SendInstantMessageParam& prm) throw(Error); + + /** + * Send IM typing indication inside INVITE session. + * + * @param prm.isTyping True to indicate to remote that local person is + * currently typing an IM. + * @param prm.txOption Optional list of headers etc to be included in + * outgoing request. + */ + void sendTypingIndication(const SendTypingIndicationParam &prm) + throw(Error); + + /** + * Send arbitrary request with the call. This is useful for example to send + * INFO request. Note that application should not use this function to send + * requests which would change the invite session's state, such as + * re-INVITE, UPDATE, PRACK, and BYE. + * + * @param prm.method SIP method of the request. + * @param prm.txOption Optional message body and/or list of headers to be + * included in outgoing request. + */ + void sendRequest(const CallSendRequestParam &prm) throw(Error); + + /** + * Dump call and media statistics to string. + * + * @param with_media True to include media information too. + * @param indent Spaces for left indentation. + * + * @return Call dump and media statistics string. + */ + string dump(bool with_media, const string indent) throw(Error); + + /** + * Get the media stream index of the default video stream in the call. + * Typically this will just retrieve the stream index of the first + * activated video stream in the call. If none is active, it will return + * the first inactive video stream. + * + * @return The media stream index or -1 if no video stream + * is present in the call. + */ + int vidGetStreamIdx() const; + + /** + * Determine if video stream for the specified call is currently running + * (i.e. has been created, started, and not being paused) for the specified + * direction. + * + * @param med_idx Media stream index, or -1 to specify default video + * media. + * @param dir The direction to be checked. + * + * @return True if stream is currently running for the + * specified direction. + */ + bool vidStreamIsRunning(int med_idx, pjmedia_dir dir) const; + + /** + * Add, remove, modify, and/or manipulate video media stream for the + * specified call. This may trigger a re-INVITE or UPDATE to be sent + * for the call. + * + * @param op The video stream operation to be performed, + * possible values are pjsua_call_vid_strm_op. + * @param param The parameters for the video stream operation + * (see CallVidSetStreamParam). + */ + void vidSetStream(pjsua_call_vid_strm_op op, + const CallVidSetStreamParam ¶m) throw(Error); + + /** + * Get media stream info for the specified media index. + * + * @param med_idx Media stream index. + * + * @return The stream info. + */ + StreamInfo getStreamInfo(unsigned med_idx) const throw(Error); + + /** + * Get media stream statistic for the specified media index. + * + * @param med_idx Media stream index. + * + * @return The stream statistic. + */ + StreamStat getStreamStat(unsigned med_idx) const throw(Error); + + /** + * Get media transport info for the specified media index. + * + * @param med_idx Media stream index. + * + * @return The transport info. + */ + MediaTransportInfo getMedTransportInfo(unsigned med_idx) const throw(Error); + + /** + * Internal function (callled by Endpoint( to process update to call + * medias when call media state changes. + */ + void processMediaUpdate(OnCallMediaStateParam &prm); + + /** + * Internal function (called by Endpoint) to process call state change. + */ + void processStateChange(OnCallStateParam &prm); + +public: + /* + * Callbacks + */ + /** + * Notify application when call state has changed. + * Application may then query the call info to get the + * detail call states by calling getInfo() function. + * + * @param prm Callback parameter. + */ + virtual void onCallState(OnCallStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * This is a general notification callback which is called whenever + * a transaction within the call has changed state. Application can + * implement this callback for example to monitor the state of + * outgoing requests, or to answer unhandled incoming requests + * (such as INFO) with a final response. + * + * @param prm Callback parameter. + */ + virtual void onCallTsxState(OnCallTsxStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when media state in the call has changed. + * Normal application would need to implement this callback, e.g. + * to connect the call's media to sound device. When ICE is used, + * this callback will also be called to report ICE negotiation + * failure. + * + * @param prm Callback parameter. + */ + virtual void onCallMediaState(OnCallMediaStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when a call has just created a local SDP (for + * initial or subsequent SDP offer/answer). Application can implement + * this callback to modify the SDP, before it is being sent and/or + * negotiated with remote SDP, for example to apply per account/call + * basis codecs priority or to add custom/proprietary SDP attributes. + * + * @param prm Callback parameter. + */ + virtual void onCallSdpCreated(OnCallSdpCreatedParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when media session is created and before it is + * registered to the conference bridge. Application may return different + * media port if it has added media processing port to the stream. This + * media port then will be added to the conference bridge instead. + * + * @param prm Callback parameter. + */ + virtual void onStreamCreated(OnStreamCreatedParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when media session has been unregistered from the + * conference bridge and about to be destroyed. + * + * @param prm Callback parameter. + */ + virtual void onStreamDestroyed(OnStreamDestroyedParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application upon incoming DTMF digits. + * + * @param prm Callback parameter. + */ + virtual void onDtmfDigit(OnDtmfDigitParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application on call being transfered (i.e. REFER is received). + * Application can decide to accept/reject transfer request + * by setting the code (default is 202). When this callback + * is not implemented, the default behavior is to accept the + * transfer. + * + * @param prm Callback parameter. + */ + virtual void onCallTransferRequest(OnCallTransferRequestParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application of the status of previously sent call + * transfer request. Application can monitor the status of the + * call transfer request, for example to decide whether to + * terminate existing call. + * + * @param prm Callback parameter. + */ + virtual void onCallTransferStatus(OnCallTransferStatusParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application about incoming INVITE with Replaces header. + * Application may reject the request by setting non-2xx code. + * + * @param prm Callback parameter. + */ + virtual void onCallReplaceRequest(OnCallReplaceRequestParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application that an existing call has been replaced with + * a new call. This happens when PJSUA-API receives incoming INVITE + * request with Replaces header. + * + * After this callback is called, normally PJSUA-API will disconnect + * this call and establish a new call \a newCallId. + * + * @param prm Callback parameter. + */ + virtual void onCallReplaced(OnCallReplacedParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application when call has received new offer from remote + * (i.e. re-INVITE/UPDATE with SDP is received). Application can + * decide to accept/reject the offer by setting the code (default + * is 200). If the offer is accepted, application can update the + * call setting to be applied in the answer. When this callback is + * not implemented, the default behavior is to accept the offer using + * current call setting. + * + * @param prm Callback parameter. + */ + virtual void onCallRxOffer(OnCallRxOfferParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application on incoming MESSAGE request. + * + * @param prm Callback parameter. + */ + virtual void onInstantMessage(OnInstantMessageParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application about the delivery status of outgoing MESSAGE + * request. + * + * @param prm Callback parameter. + */ + virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notify application about typing indication. + * + * @param prm Callback parameter. + */ + virtual void onTypingIndication(OnTypingIndicationParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * This callback is called when the call is about to resend the + * INVITE request to the specified target, following the previously + * received redirection response. + * + * Application may accept the redirection to the specified target, + * reject this target only and make the session continue to try the next + * target in the list if such target exists, stop the whole + * redirection process altogether and cause the session to be + * disconnected, or defer the decision to ask for user confirmation. + * + * This callback is optional, + * the default behavior is to NOT follow the redirection response. + * + * @param prm Callback parameter. + * + * @return Action to be performed for the target. Set this + * parameter to one of the value below: + * - PJSIP_REDIRECT_ACCEPT: immediately accept the + * redirection. When set, the call will immediately + * resend INVITE request to the target. + * - PJSIP_REDIRECT_ACCEPT_REPLACE: immediately accept + * the redirection and replace the To header with the + * current target. When set, the call will immediately + * resend INVITE request to the target. + * - PJSIP_REDIRECT_REJECT: immediately reject this + * target. The call will continue retrying with + * next target if present, or disconnect the call + * if there is no more target to try. + * - PJSIP_REDIRECT_STOP: stop the whole redirection + * process and immediately disconnect the call. The + * onCallState() callback will be called with + * PJSIP_INV_STATE_DISCONNECTED state immediately + * after this callback returns. + * - PJSIP_REDIRECT_PENDING: set to this value if + * no decision can be made immediately (for example + * to request confirmation from user). Application + * then MUST call processRedirect() + * to either accept or reject the redirection upon + * getting user decision. + */ + virtual pjsip_redirect_op onCallRedirected(OnCallRedirectedParam &prm) + { + PJ_UNUSED_ARG(prm); + return PJSIP_REDIRECT_STOP; + } + + /** + * This callback is called when media transport state is changed. + * + * @param prm Callback parameter. + */ + virtual void onCallMediaTransportState(OnCallMediaTransportStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Notification about media events such as video notifications. This + * callback will most likely be called from media threads, thus + * application must not perform heavy processing in this callback. + * Especially, application must not destroy the call or media in this + * callback. If application needs to perform more complex tasks to + * handle the event, it should post the task to another thread. + * + * @param prm Callback parameter. + */ + virtual void onCallMediaEvent(OnCallMediaEventParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * This callback can be used by application to implement custom media + * transport adapter for the call, or to replace the media transport + * with something completely new altogether. + * + * This callback is called when a new call is created. The library has + * created a media transport for the call, and it is provided as the + * \a mediaTp argument of this callback. The callback may change it + * with the instance of media transport to be used by the call. + * + * @param prm Callback parameter. + */ + virtual void + onCreateMediaTransport(OnCreateMediaTransportParam &prm) + { PJ_UNUSED_ARG(prm); } + +private: + Account &acc; + pjsua_call_id id; + Token userData; + std::vector medias; +}; + +/** + * @} // PJSUA2_CALL + */ + +} // namespace pj + +#endif /* __PJSUA2_CALL_HPP__ */ + diff --git a/pjsip/include/pjsua2/config.hpp b/pjsip/include/pjsua2/config.hpp new file mode 100644 index 00000000..743a7deb --- /dev/null +++ b/pjsip/include/pjsua2/config.hpp @@ -0,0 +1,47 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_CONFIG_HPP__ +#define __PJSUA2_CONFIG_HPP__ + +/** + * @file pjsua2/config.hpp + * @brief PJSUA2 Base Agent Operation + */ +#include + +/** + * @defgroup PJSUA2_CFG_Compile Compile time settings + * @ingroup PJSUA2_DS + * @{ + */ + +/** + * Specify if the Error exception info should contain operation and source + * file information. + */ +#ifndef PJSUA2_ERROR_HAS_EXTRA_INFO +# define PJSUA2_ERROR_HAS_EXTRA_INFO 1 +#endif + + +/** + * @} PJSUA2_CFG + */ + +#endif /* __PJSUA2_CONFIG_HPP__ */ diff --git a/pjsip/include/pjsua2/doxygen.hpp b/pjsip/include/pjsua2/doxygen.hpp new file mode 100644 index 00000000..c9952c44 --- /dev/null +++ b/pjsip/include/pjsua2/doxygen.hpp @@ -0,0 +1,52 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_DOXYGEN_HPP__ +#define __PJSUA2_DOXYGEN_HPP__ + +/** + * @file pjsua2/doxygen.hpp + * @brief PJSUA2 Doxygen documentation + */ + +/** +@mainpage pjsua2 API + +This documentation contains the API Reference for pjsua2. Please +go to Modules section above for navigation. + +For more info regarding pjsua2, including some tutorials, please +see The PJSIP Book. +*/ + + +/** + * @defgroup PJSUA2_Ref pjsua2 API Reference + */ + +/** + * @defgroup PJSUA2_DS Data Structure + * @ingroup PJSUA2_Ref + */ + + +/** + * @} PJSUA2_CFG + */ + +#endif /* __PJSUA2_DOXYGEN_HPP__ */ diff --git a/pjsip/include/pjsua2/endpoint.hpp b/pjsip/include/pjsua2/endpoint.hpp new file mode 100644 index 00000000..5ccd533b --- /dev/null +++ b/pjsip/include/pjsua2/endpoint.hpp @@ -0,0 +1,1322 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_UA_HPP__ +#define __PJSUA2_UA_HPP__ + +/** + * @file pjsua2/endpoint.hpp + * @brief PJSUA2 Base Agent Operation + */ +#include +#include +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_UA Endpoint + * @ingroup PJSUA2_Ref + * @{ + */ + +using std::string; +using std::vector; + + +////////////////////////////////////////////////////////////////////////////// + +/** + * Argument to Endpoint::onNatDetectionComplete() callback. + */ +struct OnNatDetectionCompleteParam +{ + /** + * Status of the detection process. If this value is not PJ_SUCCESS, + * the detection has failed and \a nat_type field will contain + * PJ_STUN_NAT_TYPE_UNKNOWN. + */ + pj_status_t status; + + /** + * The text describing the status, if the status is not PJ_SUCCESS. + */ + string reason; + + /** + * This contains the NAT type as detected by the detection procedure. + * This value is only valid when the \a status is PJ_SUCCESS. + */ + pj_stun_nat_type natType; + + /** + * Text describing that NAT type. + */ + string natTypeName; + +}; + +/** + * Argument to Endpoint::onNatCheckStunServersComplete() callback. + */ +struct OnNatCheckStunServersCompleteParam +{ + /** + * Arbitrary user data that was passed to Endpoint::natCheckStunServers() + * function. + */ + Token userData; + + /** + * This will contain PJ_SUCCESS if at least one usable STUN server + * is found, otherwise it will contain the last error code during + * the operation. + */ + pj_status_t status; + + /** + * The server name that yields successful result. This will only + * contain value if status is successful. + */ + string name; + + /** + * The server IP address and port in "IP:port" format. This will only + * contain value if status is successful. + */ + SocketAddress addr; +}; + +/** + * Parameter of Endpoint::onTimer() callback. + */ +struct OnTimerParam +{ + /** + * Arbitrary user data that was passed to Endpoint::utilTimerSchedule() + * function. + */ + Token userData; + + /** + * The interval of this timer, in miliseconds. + */ + unsigned msecDelay; +}; + +/** + * Parameter of Endpoint::onTransportState() callback. + */ +struct OnTransportStateParam +{ + /** + * The transport handle. + */ + TransportHandle hnd; + + /** + * Transport current state. + */ + pjsip_transport_state state; + + /** + * The last error code related to the transport state. + */ + pj_status_t lastError; +}; + +/** + * Parameter of Endpoint::onSelectAccount() callback. + */ +struct OnSelectAccountParam +{ + /** + * The incoming request. + */ + SipRxData rdata; + + /** + * The account index to be used to handle the request. + * Upon entry, this will be filled by the account index + * chosen by the library. Application may change it to + * another value to use another account. + */ + int accountIndex; +}; + +////////////////////////////////////////////////////////////////////////////// +/** + * SIP User Agent related settings. + */ +struct UaConfig : public PersistentObject +{ + /** + * Maximum calls to support (default: 4). The value specified here + * must be smaller than the compile time maximum settings + * PJSUA_MAX_CALLS, which by default is 32. To increase this + * limit, the library must be recompiled with new PJSUA_MAX_CALLS + * value. + */ + unsigned maxCalls; + + /** + * Number of worker threads. Normally application will want to have at + * least one worker thread, unless when it wants to poll the library + * periodically, which in this case the worker thread can be set to + * zero. + */ + unsigned threadCnt; + + /** + * When this flag is non-zero, all callbacks that come from thread + * other than main thread will be posted to the main thread and + * to be executed by Endpoint::libHandleEvents() function. This + * includes the logging callback. Note that this will only work if + * threadCnt is set to zero and Endpoint::libHandleEvents() is + * performed by main thread. By default, the main thread is set + * from the thread that invoke Endpoint::libCreate() + * + * Default: false + */ + bool mainThreadOnly; + + /** + * Array of nameservers to be used by the SIP resolver subsystem. + * The order of the name server specifies the priority (first name + * server will be used first, unless it is not reachable). + */ + StringVector nameserver; + + /** + * Optional user agent string (default empty). If it's empty, no + * User-Agent header will be sent with outgoing requests. + */ + string userAgent; + + /** + * Array of STUN servers to try. The library will try to resolve and + * contact each of the STUN server entry until it finds one that is + * usable. Each entry may be a domain name, host name, IP address, and + * it may contain an optional port number. For example: + * - "pjsip.org" (domain name) + * - "sip.pjsip.org" (host name) + * - "pjsip.org:33478" (domain name and a non-standard port number) + * - "10.0.0.1:3478" (IP address and port number) + * + * When nameserver is configured in the \a pjsua_config.nameserver field, + * if entry is not an IP address, it will be resolved with DNS SRV + * resolution first, and it will fallback to use DNS A resolution if this + * fails. Port number may be specified even if the entry is a domain name, + * in case the DNS SRV resolution should fallback to a non-standard port. + * + * When nameserver is not configured, entries will be resolved with + * pj_gethostbyname() if it's not an IP address. Port number may be + * specified if the server is not listening in standard STUN port. + */ + StringVector stunServer; + + /** + * This specifies if the library startup should ignore failure with the + * STUN servers. If this is set to PJ_FALSE, the library will refuse to + * start if it fails to resolve or contact any of the STUN servers. + * + * Default: TRUE + */ + bool stunIgnoreFailure; + + /** + * Support for adding and parsing NAT type in the SDP to assist + * troubleshooting. The valid values are: + * - 0: no information will be added in SDP, and parsing is disabled. + * - 1: only the NAT type number is added. + * - 2: add both NAT type number and name. + * + * Default: 1 + */ + int natTypeInSdp; + + /** + * Handle unsolicited NOTIFY requests containing message waiting + * indication (MWI) info. Unsolicited MWI is incoming NOTIFY requests + * which are not requested by client with SUBSCRIBE request. + * + * If this is enabled, the library will respond 200/OK to the NOTIFY + * request and forward the request to Endpoint::onMwiInfo() callback. + * + * See also AccountMwiConfig.enabled. + * + * Default: PJ_TRUE + */ + bool mwiUnsolicitedEnabled; + +public: + /** + * Default constructor to initialize with default values. + */ + UaConfig(); + + /** + * Construct from pjsua_config. + */ + void fromPj(const pjsua_config &ua_cfg); + + /** + * Export to pjsua_config + */ + pjsua_config toPj() const; + + /** + * Read this object from a container. + * + * @param node Container to write values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); + +}; + + +/** + * Data containing log entry to be written by the LogWriter. + */ +struct LogEntry +{ + /** Log verbosity level of this message */ + int level; + + /** The log message */ + string msg; + + /** ID of current thread */ + long threadId; + + /** The name of the thread that writes this log */ + string threadName; +}; + + +/** + * Interface for writing log messages. Applications can inherit this class + * and supply it in the LogConfig structure to implement custom log + * writing facility. + */ +class LogWriter +{ +public: + /** Destructor */ + virtual ~LogWriter() {} + + /** Write a log entry. */ + virtual void write(const LogEntry &entry) = 0; +}; + + +/** + * Logging configuration, which can be (optionally) specified when calling + * Lib::init(). + */ +struct LogConfig : public PersistentObject +{ + /** Log incoming and outgoing SIP message? Yes! */ + unsigned msgLogging; + + /** Input verbosity level. Value 5 is reasonable. */ + unsigned level; + + /** Verbosity level for console. Value 4 is reasonable. */ + unsigned consoleLevel; + + /** Log decoration. */ + unsigned decor; + + /** Optional log filename if app wishes the library to write to log file. + */ + string filename; + + /** + * Additional flags to be given to pj_file_open() when opening + * the log file. By default, the flag is PJ_O_WRONLY. Application + * may set PJ_O_APPEND here so that logs are appended to existing + * file instead of overwriting it. + * + * Default is 0. + */ + unsigned fileFlags; + + /** + * Custom log writer, if required. This instance will be destroyed + * by the endpoint when the endpoint is destroyed. + */ + LogWriter *writer; + +public: + /** Default constructor initialises with default values */ + LogConfig(); + + /** Construct from pjsua_logging_config */ + void fromPj(const pjsua_logging_config &lc); + + /** Generate pjsua_logging_config. */ + pjsua_logging_config toPj() const; + + /** + * Read this object from a container. + * + * @param node Container to write values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +/** + * This structure describes media configuration, which will be specified + * when calling Lib::init(). + */ +struct MediaConfig : public PersistentObject +{ +public: + /** + * Clock rate to be applied to the conference bridge. + * If value is zero, default clock rate will be used + * (PJSUA_DEFAULT_CLOCK_RATE, which by default is 16KHz). + */ + unsigned clockRate; + + /** + * Clock rate to be applied when opening the sound device. + * If value is zero, conference bridge clock rate will be used. + */ + unsigned sndClockRate; + + /** + * Channel count be applied when opening the sound device and + * conference bridge. + */ + unsigned channelCount; + + /** + * Specify audio frame ptime. The value here will affect the + * samples per frame of both the sound device and the conference + * bridge. Specifying lower ptime will normally reduce the + * latency. + * + * Default value: PJSUA_DEFAULT_AUDIO_FRAME_PTIME + */ + unsigned audioFramePtime; + + /** + * Specify maximum number of media ports to be created in the + * conference bridge. Since all media terminate in the bridge + * (calls, file player, file recorder, etc), the value must be + * large enough to support all of them. However, the larger + * the value, the more computations are performed. + * + * Default value: PJSUA_MAX_CONF_PORTS + */ + unsigned maxMediaPorts; + + /** + * Specify whether the media manager should manage its own + * ioqueue for the RTP/RTCP sockets. If yes, ioqueue will be created + * and at least one worker thread will be created too. If no, + * the RTP/RTCP sockets will share the same ioqueue as SIP sockets, + * and no worker thread is needed. + * + * Normally application would say yes here, unless it wants to + * run everything from a single thread. + */ + bool hasIoqueue; + + /** + * Specify the number of worker threads to handle incoming RTP + * packets. A value of one is recommended for most applications. + */ + unsigned threadCnt; + + /** + * Media quality, 0-10, according to this table: + * 5-10: resampling use large filter, + * 3-4: resampling use small filter, + * 1-2: resampling use linear. + * The media quality also sets speex codec quality/complexity to the + * number. + * + * Default: 5 (PJSUA_DEFAULT_CODEC_QUALITY). + */ + unsigned quality; + + /** + * Specify default codec ptime. + * + * Default: 0 (codec specific) + */ + unsigned ptime; + + /** + * Disable VAD? + * + * Default: 0 (no (meaning VAD is enabled)) + */ + bool noVad; + + /** + * iLBC mode (20 or 30). + * + * Default: 30 (PJSUA_DEFAULT_ILBC_MODE) + */ + unsigned ilbcMode; + + /** + * Percentage of RTP packet to drop in TX direction + * (to simulate packet lost). + * + * Default: 0 + */ + unsigned txDropPct; + + /** + * Percentage of RTP packet to drop in RX direction + * (to simulate packet lost). + * + * Default: 0 + */ + unsigned rxDropPct; + + /** + * Echo canceller options (see pjmedia_echo_create()) + * + * Default: 0. + */ + unsigned ecOptions; + + /** + * Echo canceller tail length, in miliseconds. Setting this to zero + * will disable echo cancellation. + * + * Default: PJSUA_DEFAULT_EC_TAIL_LEN + */ + unsigned ecTailLen; + + /** + * Audio capture buffer length, in milliseconds. + * + * Default: PJMEDIA_SND_DEFAULT_REC_LATENCY + */ + unsigned sndRecLatency; + + /** + * Audio playback buffer length, in milliseconds. + * + * Default: PJMEDIA_SND_DEFAULT_PLAY_LATENCY + */ + unsigned sndPlayLatency; + + /** + * Jitter buffer initial prefetch delay in msec. The value must be + * between jb_min_pre and jb_max_pre below. + * + * Default: -1 (to use default stream settings, currently 150 msec) + */ + int jbInit; + + /** + * Jitter buffer minimum prefetch delay in msec. + * + * Default: -1 (to use default stream settings, currently 60 msec) + */ + int jbMinPre; + + /** + * Jitter buffer maximum prefetch delay in msec. + * + * Default: -1 (to use default stream settings, currently 240 msec) + */ + int jbMaxPre; + + /** + * Set maximum delay that can be accomodated by the jitter buffer msec. + * + * Default: -1 (to use default stream settings, currently 360 msec) + */ + int jbMax; + + /** + * Specify idle time of sound device before it is automatically closed, + * in seconds. Use value -1 to disable the auto-close feature of sound + * device + * + * Default : 1 + */ + int sndAutoCloseTime; + + /** + * Specify whether built-in/native preview should be used if available. + * In some systems, video input devices have built-in capability to show + * preview window of the device. Using this built-in preview is preferable + * as it consumes less CPU power. If built-in preview is not available, + * the library will perform software rendering of the input. If this + * field is set to PJ_FALSE, software preview will always be used. + * + * Default: PJ_TRUE + */ + bool vidPreviewEnableNative; + +public: + /** Default constructor initialises with default values */ + MediaConfig(); + + /** Construct from pjsua_media_config. */ + void fromPj(const pjsua_media_config &mc); + + /** Export */ + pjsua_media_config toPj() const; + + /** + * Read this object from a container. + * + * @param node Container to write values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +/** + * Endpoint configuration + */ +struct EpConfig : public PersistentObject +{ + /** UA config */ + UaConfig uaConfig; + + /** Logging config */ + LogConfig logConfig; + + /** Media config */ + MediaConfig medConfig; + + /** + * Read this object from a container. + * + * @param node Container to write values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); + +}; + +/* This represents posted job */ +struct PendingJob +{ + /** Perform the job */ + virtual void execute(bool is_pending) = 0; + + /** Virtual destructor */ + virtual ~PendingJob() {} +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * Endpoint represents an instance of pjsua library. There can only be + * one instance of pjsua library in an application, hence this class + * is a singleton. + */ +class Endpoint +{ +public: + /** Retrieve the singleton instance of the endpoint */ + static Endpoint &instance() throw(Error); + + /** Default constructor */ + Endpoint(); + + /** Virtual destructor */ + virtual ~Endpoint(); + + + /************************************************************************* + * Base library operations + */ + + /** + * Get library version. + */ + Version libVersion() const; + + /** + * Instantiate pjsua application. Application must call this function before + * calling any other functions, to make sure that the underlying libraries + * are properly initialized. Once this function has returned success, + * application must call destroy() before quitting. + */ + void libCreate() throw(Error); + + /** + * Get library state. + * + * @return library state. + */ + pjsua_state libGetState() const; + + /** + * Initialize pjsua with the specified settings. All the settings are + * optional, and the default values will be used when the config is not + * specified. + * + * Note that create() MUST be called before calling this function. + * + * @param prmEpConfig Endpoint configurations + */ + void libInit( const EpConfig &prmEpConfig) throw(Error); + + /** + * Call this function after all initialization is done, so that the + * library can do additional checking set up. Application may call this + * function any time after init(). + */ + void libStart() throw(Error); + + /** + * Register a thread to poll for events. This function should be + * called by an external worker thread, and it will block polling + * for events until the library is destroyed. + */ + void libRegisterWorkerThread(const string &name) throw(Error); + + /** + * Stop all worker threads. + */ + void libStopWorkerThreads(); + + /** + * Poll pjsua for events, and if necessary block the caller thread for + * the specified maximum interval (in miliseconds). + * + * Application doesn't normally need to call this function if it has + * configured worker thread (\a thread_cnt field) in pjsua_config + * structure, because polling then will be done by these worker threads + * instead. + * + * If EpConfig::UaConfig::mainThreadOnly is enabled and this function + * is called from the main thread (by default the main thread is thread + * that calls libCreate()), this function will also scan and run any + * pending jobs in the list. + * + * @param msec_timeout Maximum time to wait, in miliseconds. + * + * @return The number of events that have been handled during the + * poll. Negative value indicates error, and application + * can retrieve the error as (status = -return_value). + */ + int libHandleEvents(unsigned msec_timeout); + + /** + * Destroy pjsua. Application is recommended to perform graceful shutdown + * before calling this function (such as unregister the account from the + * SIP server, terminate presense subscription, and hangup active calls), + * however, this function will do all of these if it finds there are + * active sessions that need to be terminated. This function will + * block for few seconds to wait for replies from remote. + * + * Application.may safely call this function more than once if it doesn't + * keep track of it's state. + * + * @param prmFlags Combination of pjsua_destroy_flag enumeration. + */ + void libDestroy(unsigned prmFlags=0) throw(Error); + + + /************************************************************************* + * Utilities + */ + + /** + * Retrieve the error string for the specified status code. + * + * @param prmErr The error code. + */ + string utilStrError(pj_status_t prmErr); + + /** + * Write a log message. + * + * @param prmLevel Log verbosity level (1-5) + * @param prmSender The log sender. + * @param prmMsg The log message. + */ + void utilLogWrite(int prmLevel, + const string &prmSender, + const string &prmMsg); + + /** + * Write a log entry. + * + * @param e The log entry. + */ + void utilLogWrite(LogEntry &e); + + /** + * This is a utility function to verify that valid SIP url is given. If the + * URL is a valid SIP/SIPS scheme, PJ_SUCCESS will be returned. + * + * @param prmUri The URL string. + * + * @return PJ_SUCCESS on success, or the appropriate error + * code. + * + * @see utilVerifyUri() + */ + pj_status_t utilVerifySipUri(const string &prmUri); + + /** + * This is a utility function to verify that valid URI is given. Unlike + * utilVerifySipUri(), this function will return PJ_SUCCESS if tel: URI + * is given. + * + * @param prmUri The URL string. + * + * @return PJ_SUCCESS on success, or the appropriate error + * code. + * + * @see pjsua_verify_sip_url() + */ + pj_status_t utilVerifyUri(const string &prmUri); + + /** + * Schedule a timer with the specified interval and user data. When the + * interval elapsed, onTimer() callback will be + * called. Note that the callback may be executed by different thread, + * depending on whether worker thread is enabled or not. + * + * @param prmMsecDelay The time interval in msec. + * @param prmUserData Arbitrary user data, to be given back to + * application in the callback. + * + * @return Token to identify the timer, which could be + * given to utilTimerCancel(). + */ + Token utilTimerSchedule(unsigned prmMsecDelay, + Token prmUserData) throw (Error); + + /** + * Cancel previously scheduled timer with the specified timer token. + * + * @param prmToken The timer token, which was returned from + * previous utilTimerSchedule() call. + */ + void utilTimerCancel(Token prmToken); + + /** + * Utility to register a pending job to be executed by main thread. + * If EpConfig::UaConfig::mainThreadOnly is false, the job will be + * executed immediately. + * + * @param job The job class. + */ + void utilAddPendingJob(PendingJob *job); + + /** + * Get cipher list supported by SSL/TLS backend. + */ + IntVector utilSslGetAvailableCiphers() throw (Error); + + /************************************************************************* + * NAT operations + */ + /** + * This is a utility function to detect NAT type in front of this endpoint. + * Once invoked successfully, this function will complete asynchronously + * and report the result in onNatDetectionComplete(). + * + * After NAT has been detected and the callback is called, application can + * get the detected NAT type by calling natGetType(). Application + * can also perform NAT detection by calling natDetectType() + * again at later time. + * + * Note that STUN must be enabled to run this function successfully. + */ + void natDetectType(void) throw(Error); + + /** + * Get the NAT type as detected by natDetectType() function. This + * function will only return useful NAT type after natDetectType() + * has completed successfully and onNatDetectionComplete() + * callback has been called. + * + * Exception: if this function is called while detection is in progress, + * PJ_EPENDING exception will be raised. + */ + pj_stun_nat_type natGetType() throw(Error); + + /** + * Auxiliary function to resolve and contact each of the STUN server + * entries (sequentially) to find which is usable. The libInit() must + * have been called before calling this function. + * + * @param prmServers Array of STUN servers to try. The endpoint + * will try to resolve and contact each of the + * STUN server entry until it finds one that is + * usable. Each entry may be a domain name, host + * name, IP address, and it may contain an + * optional port number. For example: + * - "pjsip.org" (domain name) + * - "sip.pjsip.org" (host name) + * - "pjsip.org:33478" (domain name and a non- + * standard port number) + * - "10.0.0.1:3478" (IP address and port number) + * @param prmWait Specify if the function should block until + * it gets the result. In this case, the function + * will block while the resolution is being done, + * and the callback will be called before this + * function returns. + * @param prmUserData Arbitrary user data to be passed back to + * application in the callback. + * + * @see natCancelCheckStunServers() + */ + void natCheckStunServers(const StringVector &prmServers, + bool prmWait, + Token prmUserData) throw(Error); + + /** + * Cancel pending STUN resolution which match the specified token. + * + * @param token The token to match. This token was given to + * natCheckStunServers() + * @param notify_cb Boolean to control whether the callback should + * be called for cancelled resolutions. When the + * callback is called, the status in the result + * will be set as PJ_ECANCELLED. + * + * Exception: PJ_ENOTFOUND if there is no matching one, or other error. + */ + void natCancelCheckStunServers(Token token, + bool notify_cb = false) throw(Error); + + /************************************************************************* + * Transport operations + */ + + /** + * Create and start a new SIP transport according to the specified + * settings. + * + * @param type Transport type. + * @param cfg Transport configuration. + * + * @return The transport ID. + */ + TransportId transportCreate(pjsip_transport_type_e type, + const TransportConfig &cfg) throw(Error); + + /** + * Enumerate all transports currently created in the system. This + * function will return all transport IDs, and application may then + * call transportGetInfo() function to retrieve detailed information + * about the transport. + * + * @return Array of transport IDs. + */ + IntVector transportEnum() throw(Error); + + /** + * Get information about transport. + * + * @param id Transport ID. + * + * @return Transport info. + */ + TransportInfo transportGetInfo(TransportId id) throw(Error); + + /** + * Disable a transport or re-enable it. By default transport is always + * enabled after it is created. Disabling a transport does not necessarily + * close the socket, it will only discard incoming messages and prevent + * the transport from being used to send outgoing messages. + * + * @param id Transport ID. + * @param enabled Enable or disable the transport. + * + */ + void transportSetEnable(TransportId id, bool enabled) throw(Error); + + /** + * Close the transport. The system will wait until all transactions are + * closed while preventing new users from using the transport, and will + * close the transport when its usage count reaches zero. + * + * @param id Transport ID. + */ + void transportClose(TransportId id) throw(Error); + + /************************************************************************* + * Call operations + */ + + /** + * Terminate all calls. This will initiate call hangup for all + * currently active calls. + */ + void hangupAllCalls(void); + + /************************************************************************* + * Media operations + */ + + /** + * Add media to the media list. + * + * @param media media to be added. + */ + void mediaAdd(AudioMedia &media); + + /** + * Remove media from the media list. + * + * @param media media to be removed. + */ + void mediaRemove(AudioMedia &media); + + /** + * Check if media has been added to the media list. + * + * @param media media to be check. + * + * @return True if media has been added, false otherwise. + */ + bool mediaExists(const AudioMedia &media) const; + + /** + * Get maximum number of media port. + * + * @return Maximum number of media port in the conference bridge. + */ + unsigned mediaMaxPorts() const; + + /** + * Get current number of active media port in the bridge. + * + * @return The number of active media port. + */ + unsigned mediaActivePorts() const; + + /** + * Enumerate all media port. + * + * @return The list of media port. + */ + const AudioMediaVector &mediaEnumPorts() const throw(Error); + + /** + * Get the instance of Audio Device Manager. + * + * @return The Audio Device Manager. + */ + AudDevManager &audDevManager(); + + /************************************************************************* + * Codec management operations + */ + + /** + * Enum all supported codecs in the system. + * + * @return Array of codec info. + */ + const CodecInfoVector &codecEnum() throw(Error); + + /** + * Change codec priority. + * + * @param codec_id Codec ID, which is a string that uniquely identify + * the codec (such as "speex/8000"). + * @param priority Codec priority, 0-255, where zero means to disable + * the codec. + * + */ + void codecSetPriority(const string &codec_id, + pj_uint8_t priority) throw(Error); + + /** + * Get codec parameters. + * + * @param codec_id Codec ID. + * + * @return Codec parameters. If codec is not found, Error + * will be thrown. + * + */ + CodecParam codecGetParam(const string &codec_id) const throw(Error); + + /** + * Set codec parameters. + * + * @param codec_id Codec ID. + * @param param Codec parameter to set. Set to NULL to reset + * codec parameter to library default settings. + * + */ + void codecSetParam(const string &codec_id, + const CodecParam param) throw(Error); + + +public: + /* + * Overrideables callbacks + */ + + /** + * Callback when the Endpoint has finished performing NAT type + * detection that is initiated with natDetectType(). + * + * @param prm Callback parameters containing the detection + * result. + */ + virtual void onNatDetectionComplete( + const OnNatDetectionCompleteParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Callback when the Endpoint has finished performing STUN server + * checking that is initiated with natCheckStunServers(). + * + * @param prm Callback parameters. + */ + virtual void onNatCheckStunServersComplete( + const OnNatCheckStunServersCompleteParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * This callback is called when transport state has changed. + * + * @param prm Callback parameters. + */ + virtual void onTransportState( + const OnTransportStateParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * Callback when a timer has fired. The timer was scheduled by + * utilTimerSchedule(). + * + * @param prm Callback parameters. + */ + virtual void onTimer(const OnTimerParam &prm) + { PJ_UNUSED_ARG(prm); } + + /** + * This callback can be used by application to override the account + * to be used to handle an incoming message. Initially, the account to + * be used will be calculated automatically by the library. This initial + * account will be used if application does not implement this callback, + * or application sets an invalid account upon returning from this + * callback. + * + * Note that currently the incoming messages requiring account assignment + * are INVITE, MESSAGE, SUBSCRIBE, and unsolicited NOTIFY. This callback + * may be called before the callback of the SIP event itself, i.e: + * incoming call, pager, subscription, or unsolicited-event. + * + * @param prm Callback parameters. + */ + virtual void onSelectAccount(OnSelectAccountParam &prm) + { PJ_UNUSED_ARG(prm); } + +private: + static Endpoint *instance_; // static instance + LogWriter *writer; // Custom writer, if any + AudioMediaVector mediaList; + AudDevManager audioDevMgr; + CodecInfoVector codecInfoList; + + /* Pending logging */ + bool mainThreadOnly; + void *mainThread; + unsigned pendingJobSize; + std::list pendingJobs; + + void performPendingJobs(); + + /* Endpoint static callbacks */ + static void logFunc(int level, const char *data, int len); + static void stun_resolve_cb(const pj_stun_resolve_result *result); + static void on_timer(pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry); + static void on_nat_detect(const pj_stun_nat_detect_result *res); + static void on_transport_state(pjsip_transport *tp, + pjsip_transport_state state, + const pjsip_transport_state_info *info); + +private: + /* + * Account & Call lookups + */ + static Account *lookupAcc(int acc_id, const char *op); + static Call *lookupCall(int call_id, const char *op); + + /* static callbacks */ + static void on_incoming_call(pjsua_acc_id acc_id, + pjsua_call_id call_id, + pjsip_rx_data *rdata); + static void on_reg_started(pjsua_acc_id acc_id, + pj_bool_t renew); + static void on_reg_state2(pjsua_acc_id acc_id, + pjsua_reg_info *info); + static void on_incoming_subscribe(pjsua_acc_id acc_id, + pjsua_srv_pres *srv_pres, + pjsua_buddy_id buddy_id, + const pj_str_t *from, + pjsip_rx_data *rdata, + pjsip_status_code *code, + pj_str_t *reason, + pjsua_msg_data *msg_data); + static void on_pager2(pjsua_call_id call_id, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + const pj_str_t *mime_type, + const pj_str_t *body, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id); + static void on_pager_status2(pjsua_call_id call_id, + const pj_str_t *to, + const pj_str_t *body, + void *user_data, + pjsip_status_code status, + const pj_str_t *reason, + pjsip_tx_data *tdata, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id); + static void on_typing2(pjsua_call_id call_id, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + pj_bool_t is_typing, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id); + static void on_mwi_info(pjsua_acc_id acc_id, + pjsua_mwi_info *mwi_info); + + static void on_buddy_state(pjsua_buddy_id buddy_id); + // Call callbacks + static void on_call_state(pjsua_call_id call_id, pjsip_event *e); + static void on_call_tsx_state(pjsua_call_id call_id, + pjsip_transaction *tsx, + pjsip_event *e); + static void on_call_media_state(pjsua_call_id call_id); + static void on_call_sdp_created(pjsua_call_id call_id, + pjmedia_sdp_session *sdp, + pj_pool_t *pool, + const pjmedia_sdp_session *rem_sdp); + static void on_stream_created(pjsua_call_id call_id, + pjmedia_stream *strm, + unsigned stream_idx, + pjmedia_port **p_port); + static void on_stream_destroyed(pjsua_call_id call_id, + pjmedia_stream *strm, + unsigned stream_idx); + static void on_dtmf_digit(pjsua_call_id call_id, int digit); + static void on_call_transfer_request(pjsua_call_id call_id, + const pj_str_t *dst, + pjsip_status_code *code); + static void on_call_transfer_request2(pjsua_call_id call_id, + const pj_str_t *dst, + pjsip_status_code *code, + pjsua_call_setting *opt); + static void on_call_transfer_status(pjsua_call_id call_id, + int st_code, + const pj_str_t *st_text, + pj_bool_t final, + pj_bool_t *p_cont); + static void on_call_replace_request(pjsua_call_id call_id, + pjsip_rx_data *rdata, + int *st_code, + pj_str_t *st_text); + static void on_call_replace_request2(pjsua_call_id call_id, + pjsip_rx_data *rdata, + int *st_code, + pj_str_t *st_text, + pjsua_call_setting *opt); + static void on_call_replaced(pjsua_call_id old_call_id, + pjsua_call_id new_call_id); + static void on_call_rx_offer(pjsua_call_id call_id, + const pjmedia_sdp_session *offer, + void *reserved, + pjsip_status_code *code, + pjsua_call_setting *opt); + static pjsip_redirect_op on_call_redirected(pjsua_call_id call_id, + const pjsip_uri *target, + const pjsip_event *e); + static pj_status_t + on_call_media_transport_state(pjsua_call_id call_id, + const pjsua_med_tp_state_info *info); + static void on_call_media_event(pjsua_call_id call_id, + unsigned med_idx, + pjmedia_event *event); + static pjmedia_transport* + on_create_media_transport(pjsua_call_id call_id, + unsigned media_idx, + pjmedia_transport *base_tp, + unsigned flags); + +private: + void clearCodecInfoList(); + +}; + + + +/** + * @} PJSUA2_UA + */ + +} +/* End pj namespace */ + + +#endif /* __PJSUA2_UA_HPP__ */ + diff --git a/pjsip/include/pjsua2/json.hpp b/pjsip/include/pjsua2/json.hpp new file mode 100644 index 00000000..0c28c6a8 --- /dev/null +++ b/pjsip/include/pjsua2/json.hpp @@ -0,0 +1,116 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_JSON_HPP__ +#define __PJSUA2_JSON_HPP__ + +/** + * @file pjsua2/persistent.hpp + * @brief PJSUA2 Persistent Services + */ +#include +#include +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_JSON JSON Persistent Support + * @ingroup PJSUA2_PERSISTENT + * @{ + * Provides object serialization and deserialization to/from JSON document. + */ + +using std::string; + +/** + * Persistent document (file) with JSON format. + */ +class JsonDocument : public PersistentDocument +{ +public: + /** Default constructor */ + JsonDocument(); + + /** Destructor */ + ~JsonDocument(); + + /** + * Load this document from a file. + * + * @param filename The file name. + */ + virtual void loadFile(const string &filename) throw(Error); + + /** + * Load this document from string. + * + * @param input The string. + */ + virtual void loadString(const string &input) throw(Error); + + /** + * Write this document to a file. + * + * @param filename The file name. + */ + virtual void saveFile(const string &filename) throw(Error); + + /** + * Write this document to string. + */ + virtual string saveString() throw(Error); + + /** + * Get the root container node for this document + */ + virtual ContainerNode & getRootContainer() const; + + /** + * An internal function to create JSON element. + */ + pj_json_elem* allocElement() const; + + /** + * An internal function to get the pool. + */ + pj_pool_t* getPool(); + +private: + pj_caching_pool cp; + mutable ContainerNode rootNode; + mutable pj_json_elem *root; + mutable pj_pool_t *pool; + + void initRoot() const; +}; + + + + +/** + * @} PJSUA2 + */ + +} // namespace pj + + +#endif /* __PJSUA2_JSON_HPP__ */ diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp new file mode 100644 index 00000000..4c4e65f4 --- /dev/null +++ b/pjsip/include/pjsua2/media.hpp @@ -0,0 +1,1162 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_MEDIA_HPP__ +#define __PJSUA2_MEDIA_HPP__ + +/** + * @file pjsua2/media.hpp + * @brief PJSUA2 media operations + */ +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_MED Media + * @ingroup PJSUA2_Ref + * @{ + */ + +using std::string; +using std::vector; + +/** + * This structure contains all the information needed to completely describe + * a media. + */ +struct MediaFormat +{ + /** + * The format id that specifies the audio sample or video pixel format. + * Some well known formats ids are declared in pjmedia_format_id + * enumeration. + * + * @see pjmedia_format_id + */ + pj_uint32_t id; + + /** + * The top-most type of the media, as an information. + */ + pjmedia_type type; +}; + +/** + * This structure describe detail information about an audio media. + */ +struct MediaFormatAudio : public MediaFormat +{ + unsigned clockRate; /**< Audio clock rate in samples or Hz. */ + unsigned channelCount; /**< Number of channels. */ + unsigned frameTimeUsec; /**< Frame interval, in microseconds. */ + unsigned bitsPerSample; /**< Number of bits per sample. */ + pj_uint32_t avgBps; /**< Average bitrate */ + pj_uint32_t maxBps; /**< Maximum bitrate */ + + /** + * Construct from pjmedia_format. + */ + void fromPj(const pjmedia_format &format); + + /** + * Export to pjmedia_format. + */ + pjmedia_format toPj() const; +}; + +/** + * This structure describe detail information about an video media. + */ +struct MediaFormatVideo : public MediaFormat +{ + unsigned width; /**< Video width. */ + unsigned height; /**< Video height. */ + int fpsNum; /**< Frames per second numerator. */ + int fpsDenum; /**< Frames per second denumerator. */ + pj_uint32_t avgBps; /**< Average bitrate. */ + pj_uint32_t maxBps; /**< Maximum bitrate. */ +}; + +/** Array of MediaFormat */ +typedef std::vector MediaFormatVector; + +/** + * This structure descibes information about a particular media port that + * has been registered into the conference bridge. + */ +struct ConfPortInfo +{ + /** + * Conference port number. + */ + int portId; + + /** + * Port name. + */ + string name; + + /** + * Media audio format information + */ + MediaFormatAudio format; + + /** + * Tx level adjustment. Value 1.0 means no adjustment, value 0 means + * the port is muted, value 2.0 means the level is amplified two times. + */ + float txLevelAdj; + + /** + * Rx level adjustment. Value 1.0 means no adjustment, value 0 means + * the port is muted, value 2.0 means the level is amplified two times. + */ + float rxLevelAdj; + + /** + * Array of listeners (in other words, ports where this port is + * transmitting to. + */ + IntVector listeners; + +public: + /** + * Construct from pjsua_conf_port_info. + */ + void fromPj(const pjsua_conf_port_info &port_info); +}; + +/** + * Media port, corresponds to pjmedia_port + */ +typedef void *MediaPort; + +/** + * Media. + */ +class Media +{ +public: + /** + * Virtual destructor. + */ + virtual ~Media(); + + /** + * Get type of the media. + * + * @return The media type. + */ + pjmedia_type getType() const; + +protected: + /** + * Constructor. + */ + Media(pjmedia_type med_type); + +private: + /** + * Media type. + */ + pjmedia_type type; +}; + +/** + * Audio Media. + */ +class AudioMedia : public Media +{ +public: + /** + * Get information about the specified conference port. + */ + ConfPortInfo getPortInfo() const throw(Error); + + /** + * Get port Id. + */ + int getPortId() const; + + /** + * Get information from specific port id. + */ + static ConfPortInfo getPortInfoFromId(int port_id) throw(Error); + + /** + * Establish unidirectional media flow to sink. This media port + * will act as a source, and it may transmit to multiple destinations/sink. + * And if multiple sources are transmitting to the same sink, the media + * will be mixed together. Source and sink may refer to the same Media, + * effectively looping the media. + * + * If bidirectional media flow is desired, application needs to call + * this method twice, with the second one called from the opposite source + * media. + * + * @param sink The destination Media. + */ + void startTransmit(const AudioMedia &sink) const throw(Error); + + /** + * Stop media flow to destination/sink port. + * + * @param sink The destination media. + * + */ + void stopTransmit(const AudioMedia &sink) const throw(Error); + + /** + * Adjust the signal level to be transmitted from the bridge to this + * media port by making it louder or quieter. + * + * @param level Signal level adjustment. Value 1.0 means no + * level adjustment, while value 0 means to mute + * the port. + */ + void adjustRxLevel(float level) throw(Error); + + /** + * Adjust the signal level to be received from this media port (to + * the bridge) by making it louder or quieter. + * + * @param level Signal level adjustment. Value 1.0 means no + * level adjustment, while value 0 means to mute + * the port. + */ + void adjustTxLevel(float level) throw(Error); + + /** + * Get the last received signal level. + * + * @return Signal level in percent. + */ + unsigned getRxLevel() const throw(Error); + + /** + * Get the last transmitted signal level. + * + * @return Signal level in percent. + */ + unsigned getTxLevel() const throw(Error); + + /** + * Typecast from base class Media. This is useful for application written + * in language that does not support downcasting such as Python. + * + * @param media The object to be downcasted + * + * @return The object as AudioMedia instance + */ + static AudioMedia* typecastFromMedia(Media *media); + + /** + * Virtual Destructor + */ + virtual ~AudioMedia(); + +protected: + /** + * Conference port Id. + */ + int id; + +protected: + /** + * Default Constructor. + */ + AudioMedia(); + + /** + * This method needs to be called by descendants of this class to register + * the media port created to the conference bridge and Endpoint's + * media list. + * + * param port the media port to be registered to the conference bridge. + * + */ + void registerMediaPort(MediaPort port) throw(Error); + + /** + * This method needs to be called by descendants of this class to remove + * the media port from the conference bridge and Endpoint's media list. + * Descendant should only call this method if it has registered the media + * with the previous call to registerMediaPort(). + */ + void unregisterMediaPort(); + +private: + pj_caching_pool mediaCachingPool; + pj_pool_t *mediaPool; + +private: + unsigned getSignalLevel(bool is_rx = true) const throw(Error); +}; + +/** Array of Audio Media */ +typedef std::vector AudioMediaVector; + +/** + * Audio Media Player. + */ +class AudioMediaPlayer : public AudioMedia +{ +public: + /** + * Constructor. + */ + AudioMediaPlayer(); + + /** + * Create a file player, and automatically add this + * player to the conference bridge. + * + * @param file_name The filename to be played. Currently only + * WAV files are supported, and the WAV file MUST be + * formatted as 16bit PCM mono/single channel (any + * clock rate is supported). + * @param options Optional option flag. Application may specify + * PJMEDIA_FILE_NO_LOOP to prevent playback loop. + */ + void createPlayer(const string &file_name, + unsigned options=PJMEDIA_FILE_NO_LOOP) throw(Error); + + /** + * Create a file playlist media port, and automatically add the port + * to the conference bridge. + * + * @param file_names Array of file names to be added to the play list. + * Note that the files must have the same clock rate, + * number of channels, and number of bits per sample. + * @param label Optional label to be set for the media port. + * @param options Optional option flag. Application may specify + * PJMEDIA_FILE_NO_LOOP to prevent looping. + */ + void createPlaylist(const StringVector &file_names, + const string &label="", + unsigned options=PJMEDIA_FILE_NO_LOOP) throw(Error); + + /** + * Set playback position. This operation is not valid for playlist. + */ + void setPos(pj_uint32_t samples) throw(Error); + + /** + * Typecast from base class AudioMedia. This is useful for application + * written in language that does not support downcasting such as Python. + * + * @param media The object to be downcasted + * + * @return The object as AudioMediaPlayer instance + */ + static AudioMediaPlayer* typecastFromAudioMedia(AudioMedia *media); + + /** + * Virtual destructor. + */ + virtual ~AudioMediaPlayer(); + +private: + /** + * Player Id. + */ + int playerId; + +}; + +/** + * Audio Media Recorder. + */ +class AudioMediaRecorder : public AudioMedia +{ +public: + /** + * Constructor. + */ + AudioMediaRecorder(); + + /** + * Create a file recorder, and automatically connect this recorder to + * the conference bridge. The recorder currently supports recording WAV + * file. The type of the recorder to use is determined by the extension of + * the file (e.g. ".wav"). + * + * @param file_name Output file name. The function will determine the + * default format to be used based on the file extension. + * Currently ".wav" is supported on all platforms. + * @param enc_type Optionally specify the type of encoder to be used to + * compress the media, if the file can support different + * encodings. This value must be zero for now. + * @param max_size Maximum file size. Specify zero or -1 to remove size + * limitation. This value must be zero or -1 for now. + * @param options Optional options. + */ + void createRecorder(const string &file_name, + unsigned enc_type=0, + pj_ssize_t max_size=0, + unsigned options=PJMEDIA_FILE_WRITE_PCM) throw(Error); + + /** + * Typecast from base class AudioMedia. This is useful for application + * written in language that does not support downcasting such as Python. + * + * @param media The object to be downcasted + * + * @return The object as AudioMediaRecorder instance + */ + static AudioMediaRecorder* typecastFromAudioMedia(AudioMedia *media); + + /** + * Virtual destructor. + */ + virtual ~AudioMediaRecorder(); + +private: + /** + * Recorder Id. + */ + int recorderId; +}; + +/************************************************************************* +* Sound device management +*/ + +/** + * Audio device information structure. + */ +struct AudioDevInfo +{ + /** + * The device name + */ + string name; + + /** + * Maximum number of input channels supported by this device. If the + * value is zero, the device does not support input operation (i.e. + * it is a playback only device). + */ + unsigned inputCount; + + /** + * Maximum number of output channels supported by this device. If the + * value is zero, the device does not support output operation (i.e. + * it is an input only device). + */ + unsigned outputCount; + + /** + * Default sampling rate. + */ + unsigned defaultSamplesPerSec; + + /** + * The underlying driver name + */ + string driver; + + /** + * Device capabilities, as bitmask combination of pjmedia_aud_dev_cap. + */ + unsigned caps; + + /** + * Supported audio device routes, as bitmask combination of + * pjmedia_aud_dev_route. The value may be zero if the device + * does not support audio routing. + */ + unsigned routes; + + /** + * Array of supported extended audio formats + */ + MediaFormatVector extFmt; + + /** + * Construct from pjmedia_aud_dev_info. + */ + void fromPj(const pjmedia_aud_dev_info &dev_info); + + /** + * Destructor. + */ + ~AudioDevInfo(); +}; + +/** Array of audio device info */ +typedef std::vector AudioDevInfoVector; + +/** + * Audio device manager. + */ +class AudDevManager +{ +public: + /** + * Get currently active capture sound devices. If sound devices has not been + * created, it is possible that the function returns -1 as device IDs. + * + * @return Device ID of the capture device. + */ + int getCaptureDev() const throw(Error); + + /** + * Get the AudioMedia of the capture audio device. + * + * @return Audio media for the capture device. + */ + AudioMedia &getCaptureDevMedia() throw(Error); + + /** + * Get currently active playback sound devices. If sound devices has not + * been created, it is possible that the function returns -1 as device IDs. + * + * @return Device ID of the playback device. + */ + int getPlaybackDev() const throw(Error); + + /** + * Get the AudioMedia of the speaker/playback audio device. + * + * @return Audio media for the speaker/playback device. + */ + AudioMedia &getPlaybackDevMedia() throw(Error); + + /** + * Select or change capture sound device. Application may call this + * function at any time to replace current sound device. + * + * @param capture_dev Device ID of the capture device. + */ + void setCaptureDev(int capture_dev) const throw(Error); + + /** + * Select or change playback sound device. Application may call this + * function at any time to replace current sound device. + * + * @param playback_dev Device ID of the playback device. + */ + void setPlaybackDev(int playback_dev) const throw(Error); + + /** + * Enum all audio devices installed in the system. + * + * @return The list of audio device info. + */ + const AudioDevInfoVector &enumDev() throw(Error); + + /** + * Set pjsua to use null sound device. The null sound device only provides + * the timing needed by the conference bridge, and will not interract with + * any hardware. + * + */ + void setNullDev() throw(Error); + + /** + * Disconnect the main conference bridge from any sound devices, and let + * application connect the bridge to it's own sound device/master port. + * + * @return The port interface of the conference bridge, + * so that application can connect this to it's + * own sound device or master port. + */ + MediaPort *setNoDev(); + + /** + * Change the echo cancellation settings. + * + * The behavior of this function depends on whether the sound device is + * currently active, and if it is, whether device or software AEC is + * being used. + * + * If the sound device is currently active, and if the device supports AEC, + * this function will forward the change request to the device and it will + * be up to the device on whether support the request. If software AEC is + * being used (the software EC will be used if the device does not support + * AEC), this function will change the software EC settings. In all cases, + * the setting will be saved for future opening of the sound device. + * + * If the sound device is not currently active, this will only change the + * default AEC settings and the setting will be applied next time the + * sound device is opened. + * + * @param tail_msec The tail length, in miliseconds. Set to zero to + * disable AEC. + * @param options Options to be passed to pjmedia_echo_create(). + * Normally the value should be zero. + * + */ + void setEcOptions(unsigned tail_msec, unsigned options) throw(Error); + + /** + * Get current echo canceller tail length. + * + * @return The EC tail length in milliseconds, + * If AEC is disabled, the value will be zero. + */ + unsigned getEcTail() const throw(Error); + + /** + * Check whether the sound device is currently active. The sound device + * may be inactive if the application has set the auto close feature to + * non-zero (the sndAutoCloseTime setting in MediaConfig), or + * if null sound device or no sound device has been configured via the + * setNoDev() function. + */ + bool sndIsActive() const; + + /** + * Refresh the list of sound devices installed in the system. This method + * will only refresh the list of audio device so all active audio streams + * will be unaffected. After refreshing the device list, application MUST + * make sure to update all index references to audio devices before calling + * any method that accepts audio device index as its parameter. + * + */ + void refreshDevs() throw(Error); + + /** + * Get the number of sound devices installed in the system. + * + * @return The number of sound devices installed in the + * system. + * + */ + unsigned getDevCount() const; + + /** + * Get device information. + * + * @param id The audio device ID. + * + * @return The device information which will be filled in + * by this method once it returns successfully. + */ + AudioDevInfo getDevInfo(int id) const throw(Error); + + /** + * Lookup device index based on the driver and device name. + * + * @param drv_name The driver name. + * @param dev_name The device name. + * + * @return The device ID. If the device is not found, + * Error will be thrown. + */ + int lookupDev(const string &drv_name, + const string &dev_name) const throw(Error); + + /** + * Get string info for the specified capability. + * + * @param cap The capability ID. + * + * @return Capability name. + */ + string capName(pjmedia_aud_dev_cap cap) const; + + /** + * This will configure audio format capability (other than PCM) to the + * sound device being used. If sound device is currently active, the method + * will forward the setting to the sound device instance to be applied + * immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param format The audio format. + * @param keep Specify whether the setting is to be kept for + * future use. + * + */ + void + setExtFormat(const MediaFormatAudio &format, bool keep=true) throw(Error); + + /** + * Get the audio format capability (other than PCM) of the sound device + * being used. If sound device is currently active, the method will forward + * the request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * @return The audio format. + * + */ + MediaFormatAudio getExtFormat() const throw(Error); + + /** + * This will configure audio input latency control or query capability to + * the sound device being used. If sound device is currently active, + * the method will forward the setting to the sound device instance to be + * applied immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param latency_msec The input latency. + * @param keep Specify whether the setting is to be kept + * for future use. + */ + void + setInputLatency(unsigned latency_msec, bool keep=true) throw(Error); + + /** + * Get the audio input latency control or query capability of the sound + * device being used. If sound device is currently active, the method will + * forward the request to the sound device. If sound device is currently + * inactive, and if application had previously set the setting and mark the + * setting as kept, then that setting will be returned. Otherwise, this + * method will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * @return The audio input latency. + * + */ + unsigned getInputLatency() const throw(Error); + + /** + * This will configure audio output latency control or query capability to + * the sound device being used. If sound device is currently active, + * the method will forward the setting to the sound device instance to be + * applied immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param latency_msec The output latency. + * @param keep Specify whether the setting is to be kept + * for future use. + * + */ + void + setOutputLatency(unsigned latency_msec, bool keep=true) throw(Error); + + /** + * Get the audio output latency control or query capability of the sound + * device being used. If sound device is currently active, the method will + * forward the request to the sound device. If sound device is currently + * inactive, and if application had previously set the setting and mark the + * setting as kept, then that setting will be returned. Otherwise, this + * method will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags, + * otherwise Error will be thrown. + * + * @return The audio output latency. + * + */ + unsigned getOutputLatency() const throw(Error); + + /** + * This will configure audio input volume level capability to the + * sound device being used. + * If sound device is currently active, the method will forward the + * setting to the sound device instance to be applied immediately, + * if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param volume The input volume level, in percent. + * @param keep Specify whether the setting is to be kept for + * future use. + * + */ + void setInputVolume(unsigned volume, bool keep=true) throw(Error); + + /** + * Get the audio input volume level capability of the sound device being + * used. If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. * + + * @return The audio input volume level, in percent. + * + */ + unsigned getInputVolume() const throw(Error); + + /** + * This will configure audio output volume level capability to the sound + * device being used. If sound device is currently active, the method will + * forward the setting to the sound device instance to be applied + * immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param volume The output volume level, in percent. + * @param keep Specify whether the setting is to be kept + * for future use. + * + */ + void setOutputVolume(unsigned volume, bool keep=true) throw(Error); + + /** + * Get the audio output volume level capability of the sound device being + * used. If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * @return The audio output volume level, in percent. + * + */ + unsigned getOutputVolume() const throw(Error); + + /** + * Get the audio input signal level capability of the sound device being + * used. If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * @return The audio input signal level, in percent. + * + */ + unsigned getInputSignal() const throw(Error); + + /** + * Get the audio output signal level capability of the sound device being + * used. If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * @return The audio output signal level, in percent. + * + */ + unsigned getOutputSignal() const throw(Error); + + /** + * This will configure audio input route capability to the sound device + * being used. If sound device is currently active, the method will + * forward the setting to the sound device instance to be applied + * immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param route The audio input route. + * @param keep Specify whether the setting is to be kept + * for future use. + * + */ + void + setInputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error); + + /** + * Get the audio input route capability of the sound device being used. + * If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * @return The audio input route. + * + */ + pjmedia_aud_dev_route getInputRoute() const throw(Error); + + /** + * This will configure audio output route capability to the sound device + * being used. If sound device is currently active, the method will + * forward the setting to the sound device instance to be applied + * immediately, if it supports it. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param route The audio output route. + * @param keep Specify whether the setting is to be kept + * for future use. + * + */ + void + setOutputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error); + + /** + * Get the audio output route capability of the sound device being used. + * If sound device is currently active, the method will forward the + * request to the sound device. If sound device is currently inactive, + * and if application had previously set the setting and mark the setting + * as kept, then that setting will be returned. Otherwise, this method + * will raise error. + * + * This method is only valid if the device has + * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps + * flags, otherwise Error will be thrown. + * + * @return The audio output route. + * + */ + pjmedia_aud_dev_route getOutputRoute() const throw(Error); + + /** + * This will configure audio voice activity detection capability to + * the sound device being used. If sound device is currently active, + * the method will forward the setting to the sound device instance + * to be applied immediately, if it supports it. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param enable Enable/disable voice activity detection + * feature. Set true to enable. + * @param keep Specify whether the setting is to be kept for + * future use. + * + */ + void setVad(bool enable, bool keep=true) throw(Error); + + /** + * Get the audio voice activity detection capability of the sound device + * being used. If sound device is currently active, the method will + * forward the request to the sound device. If sound device is currently + * inactive, and if application had previously set the setting and mark + * the setting as kept, then that setting will be returned. Otherwise, + * this method will raise error. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * @return The audio voice activity detection feature. + * + */ + bool getVad() const throw(Error); + + /** + * This will configure audio comfort noise generation capability to + * the sound device being used. If sound device is currently active, + * the method will forward the setting to the sound device instance + * to be applied immediately, if it supports it. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param enable Enable/disable comfort noise generation + * feature. Set true to enable. + * @param keep Specify whether the setting is to be kept for + * future use. + * + */ + void setCng(bool enable, bool keep=true) throw(Error); + + /** + * Get the audio comfort noise generation capability of the sound device + * being used. If sound device is currently active, the method will + * forward the request to the sound device. If sound device is currently + * inactive, and if application had previously set the setting and mark + * the setting as kept, then that setting will be returned. Otherwise, + * this method will raise error. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * @return The audio comfort noise generation feature. + * + */ + bool getCng() const throw(Error); + + /** + * This will configure audio packet loss concealment capability to + * the sound device being used. If sound device is currently active, + * the method will forward the setting to the sound device instance + * to be applied immediately, if it supports it. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * Note that in case the setting is kept for future use, it will be applied + * to any devices, even when application has changed the sound device to be + * used. + * + * @param enable Enable/disable packet loss concealment + * feature. Set true to enable. + * @param keep Specify whether the setting is to be kept for + * future use. + * + */ + void setPlc(bool enable, bool keep=true) throw(Error); + + /** + * Get the audio packet loss concealment capability of the sound device + * being used. If sound device is currently active, the method will + * forward the request to the sound device. If sound device is currently + * inactive, and if application had previously set the setting and mark + * the setting as kept, then that setting will be returned. Otherwise, + * this method will raise error. + * + * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC + * capability in AudioDevInfo.caps flags, otherwise Error will be thrown. + * + * @return The audio packet loss concealment feature. + * + */ + bool getPlc() const throw(Error); + +private: + AudioDevInfoVector audioDevList; + AudioMedia *devMedia; + + /** + * Constructor. + */ + AudDevManager(); + + /** + * Destructor. + */ + ~AudDevManager(); + + void clearAudioDevList(); + int getActiveDev(bool is_capture) const throw(Error); + + friend class Endpoint; +}; + +/************************************************************************* +* Codec management +*/ + +/** + * This structure describes codec information. + */ +struct CodecInfo +{ + /** + * Codec unique identification. + */ + string codecId; + + /** + * Codec priority (integer 0-255). + */ + pj_uint8_t priority; + + /** + * Codec description. + */ + string desc; + + /** + * Construct from pjsua_codec_info. + */ + void fromPj(const pjsua_codec_info &codec_info); +}; + +/** Array of codec info */ +typedef std::vector CodecInfoVector; + +/** + * Codec parameters, corresponds to pjmedia_codec_param or + * pjmedia_vid_codec_param. + */ +typedef void *CodecParam; + + +/** + * @} // PJSUA2_MED + */ + +} // namespace pj + +#endif /* __PJSUA2_MEDIA_HPP__ */ diff --git a/pjsip/include/pjsua2/persistent.hpp b/pjsip/include/pjsua2/persistent.hpp new file mode 100644 index 00000000..c086b01d --- /dev/null +++ b/pjsip/include/pjsua2/persistent.hpp @@ -0,0 +1,689 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_PERSISTENT_HPP__ +#define __PJSUA2_PERSISTENT_HPP__ + +/** + * @file pjsua2/persistent.hpp + * @brief PJSUA2 Persistent Services + */ +#include + +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_PERSISTENT Persistent API + * @ingroup PJSUA2_Ref + * @{ + * The persistent API provides functionality to read/write data from/to + * a document (string or file). The data can be simple data types such + * as boolean, number, string, and string arrays, or a user defined object. + * Currently the implementation supports reading and writing from/to JSON + * document, but the framework allows application to extend the API to + * support other document formats. + */ + +using std::string; +using std::vector; + +/* Forward declaration for ContainerNode */ +class ContainerNode; + +/** + * This is the abstract base class of objects that can be serialized to/from + * persistent document. + */ +class PersistentObject +{ +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error) = 0; + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error) = 0; +}; + + +/** + * This a the abstract base class for a persistent document. A document + * is created either by loading from a string or a file, or by constructing + * it manually when writing data to it. The document then can be saved + * to either string or to a file. A document contains one root ContainerNode + * where all data are stored under. + * + * Document is read and written serially, hence the order of reading must be + * the same as the order of writing. The PersistentDocument class provides + * API to read and write to the root node, but for more flexible operations + * application can use the ContainerNode methods instead. Indeed the read + * and write API in PersistentDocument is just a shorthand which calls the + * relevant methods in the ContainerNode. As a tip, normally application only + * uses the readObject() and writeObject() methods declared here to read/write + * top level objects, and use the macros that are explained in ContainerNode + * documentation to read/write more detailed data. + */ +class PersistentDocument +{ +public: + /** + * Virtual destructor + */ + virtual ~PersistentDocument() + {} + + /** + * Load this document from a file. + * + * @param filename The file name. + */ + virtual void loadFile(const string &filename) throw(Error) = 0; + + /** + * Load this document from string. + * + * @param input The string. + */ + virtual void loadString(const string &input) throw(Error) = 0; + + /** + * Write this document to a file. + * + * @param filename The file name. + */ + virtual void saveFile(const string &filename) throw(Error) = 0; + + /** + * Write this document to string. + * + * @return The string document. + */ + virtual string saveString() throw(Error) = 0; + + /** + * Get the root container node for this document + * + * @return The root node. + */ + virtual ContainerNode & getRootContainer() const = 0; + + + /* + * Shorthand functions for reading and writing from/to the root container + */ + + + /** + * Determine if there is unread element. If yes, then app can use one of + * the readXxx() functions to read it. + * + * @return True if there is. + */ + bool hasUnread() const; + + /** + * Get the name of the next unread element. It will throw Error if there + * is no more element to read. + * + * @return The name of the next element . + */ + string unreadName() const throw(Error); + + /** + * Read an integer value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + int readInt(const string &name="") const throw(Error); + + /** + * Read a float value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + float readNumber(const string &name="") const throw(Error); + + /** + * Read a boolean value from the container and return the value. + * This will throw Error if the current element is not a boolean. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + bool readBool(const string &name="") const throw(Error); + + /** + * Read a string value from the container and return the value. + * This will throw Error if the current element is not a string. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + string readString(const string &name="") const throw(Error); + + /** + * Read a string array from the container. This will throw Error + * if the current element is not a string array. The read position + * will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + StringVector readStringVector(const string &name="") const + throw(Error); + + /** + * Read the specified object from the container. This is equal to + * calling PersistentObject.readObject(ContainerNode); + * + * @param obj The object to read. + */ + void readObject(PersistentObject &obj) const throw(Error); + + /** + * Read a container from the container. This will throw Error if the + * current element is not an object. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readContainer(const string &name="") const + throw(Error); + + /** + * Read array container from the container. This will throw Error if the + * current element is not an array. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readArray(const string &name="") const + throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeNumber(const string &name, + float num) throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeInt(const string &name, + int num) throw(Error); + + /** + * Write a boolean value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeBool(const string &name, + bool value) throw(Error); + + /** + * Write a string value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeString(const string &name, + const string &value) throw(Error); + + /** + * Write string vector to the container. + * + * @param name The name for the value in the container. + * @param arr The vector to be written. + */ + void writeStringVector(const string &name, + const StringVector &arr) + throw(Error); + + /** + * Write an object to the container. This is equal to calling + * PersistentObject.writeObject(ContainerNode); + * + * @param obj The object to be written + */ + void writeObject(const PersistentObject &obj) throw(Error); + + /** + * Create and write an empty Object node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the new container in the container. + * + * @return A sub-container. + */ + ContainerNode writeNewContainer(const string &name) + throw(Error); + + /** + * Create and write an empty array node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the array. + * + * @return A sub-container. + */ + ContainerNode writeNewArray(const string &name) + throw(Error); +}; + + +/** + * Forward declaration of container_node_op. + */ +struct container_node_op; + + +/** + * Internal data for ContainerNode. See ContainerNode implementation notes + * for more info. + */ +struct container_node_internal_data +{ + void *doc; /**< The document. */ + void *data1; /**< Internal data 1 */ + void *data2; /**< Internal data 2 */ +}; + +/** + * A container node is a placeholder for storing other data elements, which + * could be boolean, number, string, array of strings, or another container. + * Each data in the container is basically a name/value pair, with a type + * internally associated with it so that written data can be read in the + * correct type. Data is read and written serially, hence the order of + * reading must be the same as the order of writing. + * + * Application can read data from it by using the various read methods, and + * write data to it using the various write methods. Alternatively, it + * may be more convenient to use the provided macros below to read and write + * the data, because these macros set the name automatically: + * - NODE_READ_BOOL(node,item) + * - NODE_READ_UNSIGNED(node,item) + * - NODE_READ_INT(node,item) + * - NODE_READ_FLOAT(node,item) + * - NODE_READ_NUM_T(node,type,item) + * - NODE_READ_STRING(node,item) + * - NODE_READ_STRINGV(node,item) + * - NODE_READ_OBJ(node,item) + * - NODE_WRITE_BOOL(node,item) + * - NODE_WRITE_UNSIGNED(node,item) + * - NODE_WRITE_INT(node,item) + * - NODE_WRITE_FLOAT(node,item) + * - NODE_WRITE_NUM_T(node,type,item) + * - NODE_WRITE_STRING(node,item) + * - NODE_WRITE_STRINGV(node,item) + * - NODE_WRITE_OBJ(node,item) + * + * Implementation notes: + * + * The ContainerNode class is subclass-able, but not in the usual C++ way. + * With the usual C++ inheritance, some methods will be made pure virtual + * and must be implemented by the actual class. However, doing so will + * require dynamic instantiation of the ContainerNode class, which means + * we will need to pass around the class as pointer, for example as the + * return value of readContainer() and writeNewContainer() methods. Then + * we will need to establish who needs or how to delete these objects, or + * use shared pointer mechanism, each of which is considered too inconvenient + * or complicated for the purpose. + * + * So hence we use C style "inheritance", where the methods are declared in + * container_node_op and the data in container_node_internal_data structures. + * An implementation of ContainerNode class will need to set up these members + * with values that makes sense to itself. The methods in container_node_op + * contains the pointer to the actual implementation of the operation, which + * would be specific according to the format of the document. The methods in + * this ContainerNode class are just thin wrappers which call the + * implementation in the container_node_op structure. + * + */ +class ContainerNode +{ +public: + /** + * Determine if there is unread element. If yes, then app can use one of + * the readXxx() functions to read it. + */ + bool hasUnread() const; + + /** + * Get the name of the next unread element. + */ + string unreadName() const throw(Error); + + /** + * Read an integer value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + int readInt(const string &name="") const throw(Error); + + /** + * Read a number value from the document and return the value. + * This will throw Error if the current element is not a number. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + float readNumber(const string &name="") const throw(Error); + + /** + * Read a boolean value from the container and return the value. + * This will throw Error if the current element is not a boolean. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + bool readBool(const string &name="") const throw(Error); + + /** + * Read a string value from the container and return the value. + * This will throw Error if the current element is not a string. + * The read position will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + string readString(const string &name="") const throw(Error); + + /** + * Read a string array from the container. This will throw Error + * if the current element is not a string array. The read position + * will be advanced to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return The value. + */ + StringVector readStringVector(const string &name="") const + throw(Error); + + /** + * Read the specified object from the container. This is equal to + * calling PersistentObject.readObject(ContainerNode); + * + * @param obj The object to read. + */ + void readObject(PersistentObject &obj) const throw(Error); + + /** + * Read a container from the container. This will throw Error if the + * current element is not a container. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readContainer(const string &name="") const + throw(Error); + + /** + * Read array container from the container. This will throw Error if the + * current element is not an array. The read position will be advanced + * to the next element. + * + * @param name If specified, then the function will check if the + * name of the next element matches the specified + * name and throw Error if it doesn't match. + * + * @return Container object. + */ + ContainerNode readArray(const string &name="") const + throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeNumber(const string &name, + float num) throw(Error); + + /** + * Write a number value to the container. + * + * @param name The name for the value in the container. + * @param num The value to be written. + */ + void writeInt(const string &name, + int num) throw(Error); + + /** + * Write a boolean value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeBool(const string &name, + bool value) throw(Error); + + /** + * Write a string value to the container. + * + * @param name The name for the value in the container. + * @param value The value to be written. + */ + void writeString(const string &name, + const string &value) throw(Error); + + /** + * Write string vector to the container. + * + * @param name The name for the value in the container. + * @param arr The vector to be written. + */ + void writeStringVector(const string &name, + const StringVector &arr) + throw(Error); + + /** + * Write an object to the container. This is equal to calling + * PersistentObject.writeObject(ContainerNode); + * + * @param obj The object to be written + */ + void writeObject(const PersistentObject &obj) throw(Error); + + /** + * Create and write an empty Object node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the new container in the container. + * + * @return A sub-container. + */ + ContainerNode writeNewContainer(const string &name) + throw(Error); + + /** + * Create and write an empty array node that can be used as parent + * for subsequent write operations. + * + * @param name The name for the array. + * + * @return A sub-container. + */ + ContainerNode writeNewArray(const string &name) + throw(Error); + +public: + /* internal data */ + container_node_op *op; /**< Method table. */ + container_node_internal_data data; /**< Internal data */ +}; + + +/** + * Pointer to actual ContainerNode implementation. See ContainerNode + * implementation notes for more info. + */ +//! @cond Doxygen_Suppress +struct container_node_op +{ + bool (*hasUnread)(const ContainerNode*); + string (*unreadName)(const ContainerNode*) + throw(Error); + float (*readNumber)(const ContainerNode*, + const string&) + throw(Error); + bool (*readBool)(const ContainerNode*, + const string&) + throw(Error); + string (*readString)(const ContainerNode*, + const string&) + throw(Error); + StringVector (*readStringVector)(const ContainerNode*, + const string&) + throw(Error); + ContainerNode (*readContainer)(const ContainerNode*, + const string &) + throw(Error); + ContainerNode (*readArray)(const ContainerNode*, + const string &) + throw(Error); + void (*writeNumber)(ContainerNode*, + const string &name, + float num) + throw(Error); + void (*writeBool)(ContainerNode*, + const string &name, + bool value) + throw(Error); + void (*writeString)(ContainerNode*, + const string &name, + const string &value) + throw(Error); + void (*writeStringVector)(ContainerNode*, + const string &name, + const StringVector &value) + throw(Error); + ContainerNode (*writeNewContainer)(ContainerNode*, + const string &name) + throw(Error); + ContainerNode (*writeNewArray)(ContainerNode*, + const string &name) + throw(Error); +}; + +/* + * Convenient macros. + */ +#define NODE_READ_BOOL(node,item) item = node.readBool(#item) +#define NODE_READ_UNSIGNED(node,item) item = (unsigned)node.readNumber(#item) +#define NODE_READ_INT(node,item) item = (int) node.readNumber(#item) +#define NODE_READ_FLOAT(node,item) item = node.readNumber(#item) +#define NODE_READ_NUM_T(node,T,item) item = (T)(int)node.readNumber(#item) +#define NODE_READ_STRING(node,item) item = node.readString(#item) +#define NODE_READ_STRINGV(node,item) item = node.readStringVector(#item) +#define NODE_READ_OBJ(node,item) node.readObject(item) + +#define NODE_WRITE_BOOL(node,item) node.writeBool(#item, item) +#define NODE_WRITE_UNSIGNED(node,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_INT(node,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_NUM_T(node,T,item) node.writeNumber(#item, (float)item) +#define NODE_WRITE_FLOAT(node,item) node.writeNumber(#item, item) +#define NODE_WRITE_STRING(node,item) node.writeString(#item, item) +#define NODE_WRITE_STRINGV(node,item) node.writeStringVector(#item, item) +#define NODE_WRITE_OBJ(node,item) node.writeObject(item) + +//! @endcond + +/** + * @} PJSUA2 + */ + +} // namespace pj + + + +#endif /* __PJSUA2_PERSISTENT_HPP__ */ diff --git a/pjsip/include/pjsua2/presence.hpp b/pjsip/include/pjsua2/presence.hpp new file mode 100644 index 00000000..5106344a --- /dev/null +++ b/pjsip/include/pjsua2/presence.hpp @@ -0,0 +1,296 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_PRESENCE_HPP__ +#define __PJSUA2_PRESENCE_HPP__ + +/** + * @file pjsua2/presence.hpp + * @brief PJSUA2 Presence Operations + */ +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_PRES Presence + * @ingroup PJSUA2_Ref + * @{ + */ + +using std::string; +using std::vector; + + +/** + * This describes presence status. + */ +struct PresenceStatus +{ + /** + * Buddy's online status. + */ + pjsua_buddy_status status; + + /** + * Text to describe buddy's online status. + */ + string statusText; + + /** + * Activity type. + */ + pjrpid_activity activity; + + /** + * Optional text describing the person/element. + */ + string note; + + /** + * Optional RPID ID string. + */ + string rpidId; + +public: + /** + * Constructor. + */ + PresenceStatus(); +}; + + +/** + * This structure describes buddy configuration when adding a buddy to + * the buddy list with Buddy::create(). + */ +struct BuddyConfig : public PersistentObject +{ + /** + * Buddy URL or name address. + */ + string uri; + + /** + * Specify whether presence subscription should start immediately. + */ + bool subscribe; + +public: + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +/** + * This structure describes buddy info, which can be retrieved by via + * Buddy::getInfo(). + */ +struct BuddyInfo +{ + /** + * The full URI of the buddy, as specified in the configuration. + */ + string uri; + + /** + * Buddy's Contact, only available when presence subscription has + * been established to the buddy. + */ + string contact; + + /** + * Flag to indicate that we should monitor the presence information for + * this buddy (normally yes, unless explicitly disabled). + */ + bool presMonitorEnabled; + + /** + * If \a presMonitorEnabled is true, this specifies the last state of + * the presence subscription. If presence subscription session is currently + * active, the value will be PJSIP_EVSUB_STATE_ACTIVE. If presence + * subscription request has been rejected, the value will be + * PJSIP_EVSUB_STATE_TERMINATED, and the termination reason will be + * specified in \a subTermReason. + */ + pjsip_evsub_state subState; + + /** + * String representation of subscription state. + */ + string subStateName; + + /** + * Specifies the last presence subscription termination code. This would + * return the last status of the SUBSCRIBE request. If the subscription + * is terminated with NOTIFY by the server, this value will be set to + * 200, and subscription termination reason will be given in the + * \a subTermReason field. + */ + pjsip_status_code subTermCode; + + /** + * Specifies the last presence subscription termination reason. If + * presence subscription is currently active, the value will be empty. + */ + string subTermReason; + + /** + * Presence status. + */ + PresenceStatus presStatus; + +public: + /** Import from pjsip structure */ + void fromPj(const pjsua_buddy_info &pbi); +}; + + +/** + * Buddy. + */ +class Buddy +{ +public: + /** + * Constructor. + */ + Buddy(); + + /** + * Destructor. Note that if the Buddy instance is deleted, it will also + * delete the corresponding buddy in the PJSUA-LIB. + */ + virtual ~Buddy(); + + /** + * Create buddy and register the buddy to PJSUA-LIB. + * + * @param acc The account for this buddy. + * @param cfg The buddy config. + */ + void create(Account &acc, const BuddyConfig &cfg) throw(Error); + + /** + * Check if this buddy is valid. + * + * @return True if it is. + */ + bool isValid() const; + + /** + * Get detailed buddy info. + * + * @return Buddy info. + */ + BuddyInfo getInfo() const throw(Error); + + /** + * Enable/disable buddy's presence monitoring. Once buddy's presence is + * subscribed, application will be informed about buddy's presence status + * changed via \a onBuddyState() callback. + * + * @param subscribe Specify true to activate presence + * subscription. + */ + void subscribePresence(bool subscribe) throw(Error); + + /** + * Update the presence information for the buddy. Although the library + * periodically refreshes the presence subscription for all buddies, + * some application may want to refresh the buddy's presence subscription + * immediately, and in this case it can use this function to accomplish + * this. + * + * Note that the buddy's presence subscription will only be initiated + * if presence monitoring is enabled for the buddy. See + * subscribePresence() for more info. Also if presence subscription for + * the buddy is already active, this function will not do anything. + * + * Once the presence subscription is activated successfully for the buddy, + * application will be notified about the buddy's presence status in the + * \a onBuddyState() callback. + */ + void updatePresence(void) throw(Error); + + /** + * Send instant messaging outside dialog, using this buddy's specified + * account for route set and authentication. + * + * @param prm Sending instant message parameter. + */ + void sendInstantMessage(const SendInstantMessageParam &prm) throw(Error); + + /** + * Send typing indication outside dialog. + * + * @param prm Sending instant message parameter. + */ + void sendTypingIndication(const SendTypingIndicationParam &prm) + throw(Error); + +public: + /* + * Callbacks + */ + + /** + * Notify application when the buddy state has changed. + * Application may then query the buddy info to get the details. + */ + virtual void onBuddyState() + {} + +private: + /** + * Buddy ID. + */ + pjsua_buddy_id id; + + /** + * Account. + */ + Account *acc; +}; + + +/** Array of buddies */ +typedef std::vector BuddyVector; + + +/** + * @} // PJSUA2_PRES + */ + +} // namespace pj + +#endif /* __PJSUA2_PRESENCE_HPP__ */ diff --git a/pjsip/include/pjsua2/siptypes.hpp b/pjsip/include/pjsua2/siptypes.hpp new file mode 100644 index 00000000..a5e7550f --- /dev/null +++ b/pjsip/include/pjsua2/siptypes.hpp @@ -0,0 +1,910 @@ +/* $Id$ */ +/* + * Copyright (C) 2032 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_SIPTYPES_HPP__ +#define __PJSUA2_SIPTYPES_HPP__ + +/** + * @file pjsua2/types.hpp + * @brief PJSUA2 Base Types + */ +#include +#include + +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_SIP_Types SIP Types + * @ingroup PJSUA2_DS + * @{ + */ + +/** + * Credential information. Credential contains information to authenticate + * against a service. + */ +struct AuthCredInfo : public PersistentObject +{ + /** + * The authentication scheme (e.g. "digest"). + */ + string scheme; + + /** + * Realm on which this credential is to be used. Use "*" to make + * a credential that can be used to authenticate against any challenges. + */ + string realm; + + /** + * Authentication user name. + */ + string username; + + /** + * Type of data that is contained in the "data" field. Use 0 if the data + * contains plain text password. + */ + int dataType; + + /** + * The data, which can be a plain text password or a hashed digest. + */ + string data; + + /* + * Digest AKA credential information. Note that when AKA credential + * is being used, the \a data field of this pjsip_cred_info is + * not used, but it still must be initialized to an empty string. + * Please see PJSIP_AUTH_AKA_API for more information. + */ + + /** Permanent subscriber key. */ + string akaK; + + /** Operator variant key. */ + string akaOp; + + /** Authentication Management Field */ + string akaAmf; + +public: + /** Default constructor */ + AuthCredInfo(); + + /** Construct a credential with the specified parameters */ + AuthCredInfo(const string &scheme, + const string &realm, + const string &user_name, + const int data_type, + const string data); + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +////////////////////////////////////////////////////////////////////////////// + +/** + * TLS transport settings, to be specified in TransportConfig. + */ +struct TlsConfig : public PersistentObject +{ + /** + * Certificate of Authority (CA) list file. + */ + string CaListFile; + + /** + * Public endpoint certificate file, which will be used as client- + * side certificate for outgoing TLS connection, and server-side + * certificate for incoming TLS connection. + */ + string certFile; + + /** + * Optional private key of the endpoint certificate to be used. + */ + string privKeyFile; + + /** + * Password to open private key. + */ + string password; + + /** + * TLS protocol method from pjsip_ssl_method. + * + * Default is PJSIP_SSL_UNSPECIFIED_METHOD (0), which in turn will + * use PJSIP_SSL_DEFAULT_METHOD, which default value is + * PJSIP_TLSV1_METHOD. + */ + pjsip_ssl_method method; + + /** + * Ciphers and order preference. The Endpoint::utilSslGetAvailableCiphers() + * can be used to check the available ciphers supported by backend. + * If the array is empty, then default cipher list of the backend + * will be used. + */ + IntVector ciphers; + + /** + * Specifies TLS transport behavior on the server TLS certificate + * verification result: + * - If \a verifyServer is disabled, TLS transport will just notify + * the application via pjsip_tp_state_callback with state + * PJSIP_TP_STATE_CONNECTED regardless TLS verification result. + * - If \a verifyServer is enabled, TLS transport will be shutdown + * and application will be notified with state + * PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification + * error, otherwise PJSIP_TP_STATE_CONNECTED will be notified. + * + * In any cases, application can inspect pjsip_tls_state_info in the + * callback to see the verification detail. + * + * Default value is false. + */ + bool verifyServer; + + /** + * Specifies TLS transport behavior on the client TLS certificate + * verification result: + * - If \a verifyClient is disabled, TLS transport will just notify + * the application via pjsip_tp_state_callback with state + * PJSIP_TP_STATE_CONNECTED regardless TLS verification result. + * - If \a verifyClient is enabled, TLS transport will be shutdown + * and application will be notified with state + * PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification + * error, otherwise PJSIP_TP_STATE_CONNECTED will be notified. + * + * In any cases, application can inspect pjsip_tls_state_info in the + * callback to see the verification detail. + * + * Default value is PJ_FALSE. + */ + bool verifyClient; + + /** + * When acting as server (incoming TLS connections), reject incoming + * connection if client doesn't supply a TLS certificate. + * + * This setting corresponds to SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag. + * Default value is PJ_FALSE. + */ + bool requireClientCert; + + /** + * TLS negotiation timeout to be applied for both outgoing and incoming + * connection, in milliseconds. If zero, the SSL negotiation doesn't + * have a timeout. + * + * Default: zero + */ + unsigned msecTimeout; + + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qosParam fields since this is more portable. + * + * Default value is PJ_QOS_TYPE_BEST_EFFORT. + */ + pj_qos_type qosType; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qosType field and may not be + * supported on all platforms. + * + * By default all settings in this structure are disabled. + */ + pj_qos_params qosParams; + + /** + * Specify if the transport should ignore any errors when setting the QoS + * traffic type/parameters. + * + * Default: PJ_TRUE + */ + bool qosIgnoreError; + +public: + /** Default constructor initialises with default values */ + TlsConfig(); + + /** Convert to pjsip */ + pjsip_tls_setting toPj() const; + + /** Convert from pjsip */ + void fromPj(const pjsip_tls_setting &prm); + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + + +/** + * Parameters to create a transport instance. + */ +struct TransportConfig : public PersistentObject +{ + /** + * UDP port number to bind locally. This setting MUST be specified + * even when default port is desired. If the value is zero, the + * transport will be bound to any available port, and application + * can query the port by querying the transport info. + */ + unsigned port; + + /** + * Specify the port range for socket binding, relative to the start + * port number specified in \a port. Note that this setting is only + * applicable when the start port number is non zero. + * + * Default value is zero. + */ + unsigned portRange; + + /** + * Optional address to advertise as the address of this transport. + * Application can specify any address or hostname for this field, + * for example it can point to one of the interface address in the + * system, or it can point to the public address of a NAT router + * where port mappings have been configured for the application. + * + * Note: this option can be used for both UDP and TCP as well! + */ + string publicAddress; + + /** + * Optional address where the socket should be bound to. This option + * SHOULD only be used to selectively bind the socket to particular + * interface (instead of 0.0.0.0), and SHOULD NOT be used to set the + * published address of a transport (the public_addr field should be + * used for that purpose). + * + * Note that unlike public_addr field, the address (or hostname) here + * MUST correspond to the actual interface address in the host, since + * this address will be specified as bind() argument. + */ + string boundAddress; + + /** + * This specifies TLS settings for TLS transport. It is only be used + * when this transport config is being used to create a SIP TLS + * transport. + */ + TlsConfig tlsConfig; + + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qosParam fields since this is more portable. + * + * Default is QoS not set. + */ + pj_qos_type qosType; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qosType field and may not be + * supported on all platforms. + * + * Default is QoS not set. + */ + pj_qos_params qosParams; + +public: + /** Default constructor initialises with default values */ + TransportConfig(); + + /** Convert from pjsip */ + void fromPj(const pjsua_transport_config &prm); + + /** Convert to pjsip */ + pjsua_transport_config toPj() const; + + /** + * Read this object from a container node. + * + * @param node Container to read values from. + */ + virtual void readObject(const ContainerNode &node) throw(Error); + + /** + * Write this object to a container node. + * + * @param node Container to write values to. + */ + virtual void writeObject(ContainerNode &node) const throw(Error); +}; + +/** + * This structure describes transport information returned by + * Endpoint::transportGetInfo() function. + */ +struct TransportInfo +{ + /** PJSUA transport identification. */ + TransportId id; + + /** Transport type. */ + pjsip_transport_type_e type; + + /** Transport type name. */ + string typeName; + + /** Transport string info/description. */ + string info; + + /** Transport flags (see pjsip_transport_flags_e). */ + unsigned flags; + + /** Local/bound address. */ + SocketAddress localAddress; + + /** Published address (or transport address name). */ + SocketAddress localName; + + /** Current number of objects currently referencing this transport. */ + unsigned usageCount; + +public: + /** Construct from pjsua_transport_info */ + void fromPj(const pjsua_transport_info &info); +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * This structure describes an incoming SIP message. It corresponds to the + * pjsip_rx_data structure in PJSIP library. + */ +struct SipRxData +{ + /** + * A short info string describing the request, which normally contains + * the request method and its CSeq. + */ + string info; + + /** + * The whole message data as a string, containing both the header section + * and message body section. + */ + string wholeMsg; + + /** + * Source address of the message. + */ + SocketAddress srcAddress; + + /** + * Pointer to original pjsip_rx_data. Only valid when the struct + * is constructed from PJSIP's pjsip_rx_data. + */ + void *pjRxData; + +public: + /** + * Default constructor. + */ + SipRxData(); + + /** + * Construct from PJSIP's pjsip_rx_data + */ + void fromPj(pjsip_rx_data &rdata); +}; + +/** + * This structure describes an outgoing SIP message. It corresponds to the + * pjsip_tx_data structure in PJSIP library. + */ +struct SipTxData +{ + /** + * A short info string describing the request, which normally contains + * the request method and its CSeq. + */ + string info; + + /** + * The whole message data as a string, containing both the header section + * and message body section. + */ + string wholeMsg; + + /** + * Destination address of the message. + */ + SocketAddress dstAddress; + + /** + * Pointer to original pjsip_tx_data. Only valid when the struct + * is constructed from PJSIP's pjsip_tx_data. + */ + void *pjTxData; + +public: + /** + * Default constructor. + */ + SipTxData(); + + /** + * Construct from PJSIP's pjsip_tx_data + */ + void fromPj(pjsip_tx_data &tdata); +}; + +/** + * This structure describes SIP transaction object. It corresponds to the + * pjsip_transaction structure in PJSIP library. + */ +struct SipTransaction +{ + /* Transaction identification. */ + pjsip_role_e role; /**< Role (UAS or UAC) */ + string method; /**< The method. */ + + /* State and status. */ + int statusCode; /**< Last status code seen. */ + string statusText; /**< Last reason phrase. */ + pjsip_tsx_state_e state; /**< State. */ + + /* Messages and timer. */ + SipTxData lastTx; /**< Msg kept for retrans. */ + + /* Original pjsip_transaction. */ + void *pjTransaction; /**< pjsip_transaction. */ + +public: + /** + * Default constructor. + */ + SipTransaction(); + + /** + * Construct from PJSIP's pjsip_transaction + */ + void fromPj(pjsip_transaction &tsx); +}; + +/** + * This structure describes timer event. + */ +struct TimerEvent +{ + TimerEntry entry; /**< The timer entry. */ +}; + +/** + * This structure describes transaction state changed event. + */ +struct TsxStateEvent +{ + struct + { + SipRxData rdata; /**< The incoming message. */ + SipTxData tdata; /**< The outgoing message. */ + TimerEntry timer; /**< The timer. */ + pj_status_t status; /**< Transport error status. */ + GenericData data; /**< Generic data. */ + } src; /**< Event source. */ + SipTransaction tsx; /**< The transaction. */ + pjsip_tsx_state_e prevState; /**< Previous state. */ + pjsip_event_id_e type; /**< Type of event source: + * - PJSIP_EVENT_TX_MSG + * - PJSIP_EVENT_RX_MSG, + * - PJSIP_EVENT_TRANSPORT_ERROR + * - PJSIP_EVENT_TIMER + * - PJSIP_EVENT_USER + */ +}; + +/** + * This structure describes message transmission event. + */ +struct TxMsgEvent +{ + SipTxData tdata; /**< The transmit data buffer. */ +}; + +/** + * This structure describes transmission error event. + */ +struct TxErrorEvent +{ + SipTxData tdata; /**< The transmit data. */ + SipTransaction tsx; /**< The transaction. */ +}; + +/** + * This structure describes message arrival event. + */ +struct RxMsgEvent +{ + SipRxData rdata; /**< The receive data buffer. */ +}; + +/** + * This structure describes user event. + */ +struct UserEvent +{ + GenericData user1; /**< User data 1. */ + GenericData user2; /**< User data 2. */ + GenericData user3; /**< User data 3. */ + GenericData user4; /**< User data 4. */ +}; + +/** + * This structure describe event descriptor to fully identify a SIP event. It + * corresponds to the pjsip_event structure in PJSIP library. + */ +struct SipEvent +{ + /** + * The event type, can be any value of \b pjsip_event_id_e. + */ + pjsip_event_id_e type; + + /** + * The event body, which fields depends on the event type. + */ + struct + { + /** + * Timer event. + */ + TimerEvent timer; + + /** + * Transaction state has changed event. + */ + TsxStateEvent tsxState; + + /** + * Message transmission event. + */ + TxMsgEvent txMsg; + + /** + * Transmission error event. + */ + TxErrorEvent txError; + + /** + * Message arrival event. + */ + RxMsgEvent rxMsg; + + /** + * User event. + */ + UserEvent user; + + } body; + + /** + * Pointer to its original pjsip_event. Only valid when the struct is + * constructed from PJSIP's pjsip_event. + */ + void *pjEvent; + +public: + /** + * Default constructor. + */ + SipEvent(); + + /** + * Construct from PJSIP's pjsip_event + */ + void fromPj(const pjsip_event &ev); +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * SIP media type containing type and subtype. For example, for + * "application/sdp", the type is "application" and the subtype is "sdp". + */ +struct SipMediaType +{ + /** Media type. */ + string type; + + /** Media subtype. */ + string subType; + +public: + /** + * Construct from PJSIP's pjsip_media_type + */ + void fromPj(const pjsip_media_type &prm); + + /** + * Convert to PJSIP's pjsip_media_type. + */ + pjsip_media_type toPj() const; +}; + +/** + * Simple SIP header. + */ +struct SipHeader +{ + /** + * Header name. + */ + string hName; + + /** + * Header value. + */ + string hValue; + +public: + /** + * Initiaize from PJSIP header. + */ + void fromPj(const pjsip_hdr *) throw(Error); + + /** + * Convert to PJSIP header. + */ + pjsip_generic_string_hdr &toPj() const; + +private: + /** Interal buffer for conversion to PJSIP header */ + mutable pjsip_generic_string_hdr pjHdr; +}; + + +/** Array of strings */ +typedef std::vector SipHeaderVector; + +/** + * This describes each multipart part. + */ +struct SipMultipartPart +{ + /** + * Optional headers to be put in this multipart part. + */ + SipHeaderVector headers; + + /** + * The MIME type of the body part of this multipart part. + */ + SipMediaType contentType; + + /** + * The body part of tthis multipart part. + */ + string body; + +public: + /** + * Initiaize from PJSIP's pjsip_multipart_part. + */ + void fromPj(const pjsip_multipart_part &prm) throw(Error); + + /** + * Convert to PJSIP's pjsip_multipart_part. + */ + pjsip_multipart_part& toPj() const; + +private: + /** Interal buffer for conversion to PJSIP pjsip_multipart_part */ + mutable pjsip_multipart_part pjMpp; + mutable pjsip_msg_body pjMsgBody; +}; + +/** Array of multipart parts */ +typedef std::vector SipMultipartPartVector; + +/** + * Additional options when sending outgoing SIP message. This corresponds to + * pjsua_msg_data structure in PJSIP library. + */ +struct SipTxOption +{ + /** + * Optional remote target URI (i.e. Target header). If empty (""), the + * target will be set to the remote URI (To header). At the moment this + * field is only used when sending initial INVITE and MESSAGE requests. + */ + string targetUri; + + /** + * Additional message headers to be included in the outgoing message. + */ + SipHeaderVector headers; + + /** + * MIME type of the message body, if application specifies the messageBody + * in this structure. + */ + string contentType; + + /** + * Optional message body to be added to the message, only when the + * message doesn't have a body. + */ + string msgBody; + + /** + * Content type of the multipart body. If application wants to send + * multipart message bodies, it puts the parts in multipartParts and set + * the content type in multipartContentType. If the message already + * contains a body, the body will be added to the multipart bodies. + */ + SipMediaType multipartContentType; + + /** + * Array of multipart parts. If application wants to send multipart + * message bodies, it puts the parts in \a parts and set the content + * type in \a multipart_ctype. If the message already contains a body, + * the body will be added to the multipart bodies. + */ + SipMultipartPartVector multipartParts; + +public: + /** + * Check if the options are empty. If the options are set with empty + * values, there will be no additional information sent with outgoing + * SIP message. + * + * @return True if the options are empty. + */ + bool isEmpty() const; + + /** + * Initiaize from PJSUA's pjsua_msg_data. + */ + void fromPj(const pjsua_msg_data &prm) throw(Error); + + /** + * Convert to PJSUA's pjsua_msg_data. + */ + void toPj(pjsua_msg_data &msg_data) const; +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * This structure contains parameters for sending instance message methods, + * e.g: Buddy::sendInstantMessage(), Call:sendInstantMessage(). + */ +struct SendInstantMessageParam +{ + /** + * MIME type. Default is "text/plain". + */ + string contentType; + + /** + * The message content. + */ + string content; + + /** + * List of headers etc to be included in outgoing request. + */ + SipTxOption txOption; + + /** + * User data, which will be given back when the IM callback is called. + */ + Token userData; + +public: + /** + * Default constructor initializes with zero/empty values. + */ + SendInstantMessageParam(); +}; + + +/** + * This structure contains parameters for sending typing indication methods, + * e.g: Buddy::sendTypingIndication(), Call:sendTypingIndication(). + */ +struct SendTypingIndicationParam +{ + /** + * True to indicate to remote that local person is currently typing an IM. + */ + bool isTyping; + + /** + * List of headers etc to be included in outgoing request. + */ + SipTxOption txOption; + +public: + /** + * Default constructor initializes with zero/empty values. + */ + SendTypingIndicationParam(); +}; + + +/* Utilities */ +#ifndef SWIG +//! @cond Doxygen_Suppress +void readIntVector( ContainerNode &node, + const string &array_name, + IntVector &v) throw(Error); +void writeIntVector(ContainerNode &node, + const string &array_name, + const IntVector &v) throw(Error); +void readQosParams( ContainerNode &node, + pj_qos_params &qos) throw(Error); +void writeQosParams( ContainerNode &node, + const pj_qos_params &qos) throw(Error); +void readSipHeaders( const ContainerNode &node, + const string &array_name, + SipHeaderVector &headers) throw(Error); +void writeSipHeaders(ContainerNode &node, + const string &array_name, + const SipHeaderVector &headers) throw(Error); +//! @endcond +#endif // SWIG + +/** + * @} PJSUA2 + */ + +} // namespace pj + + + +#endif /* __PJSUA2_SIPTYPES_HPP__ */ diff --git a/pjsip/include/pjsua2/types.hpp b/pjsip/include/pjsua2/types.hpp new file mode 100644 index 00000000..129ac4a6 --- /dev/null +++ b/pjsip/include/pjsua2/types.hpp @@ -0,0 +1,266 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 __PJSUA2_TYPES_HPP__ +#define __PJSUA2_TYPES_HPP__ + +#ifdef _MSC_VER +# pragma warning( disable : 4290 ) // exception spec ignored +# pragma warning( disable : 4512 ) // can't generate assignment op +#endif + +/** + * @file pjsua2/types.hpp + * @brief PJSUA2 Base Types + */ +#include + +#include +#include + +/** PJSUA2 API is inside pj namespace */ +namespace pj +{ + +/** + * @defgroup PJSUA2_TYPES General Data Structure + * @ingroup PJSUA2_DS + * @{ + */ + +using std::string; +using std::vector; + +/** Array of strings */ +typedef std::vector StringVector; + +/** Array of integers */ +typedef std::vector IntVector; + +/** + * Type of token, i.e. arbitrary application user data + */ +typedef void *Token; + +/** + * Socket address, encoded as string. The socket address contains host + * and port number in "host[:port]" format. The host part may contain + * hostname, domain name, IPv4 or IPv6 address. For IPv6 address, the + * address will be enclosed with square brackets, e.g. "[::1]:5060". + */ +typedef string SocketAddress; + +/** + * Transport ID is an integer. + */ +typedef int TransportId; + +/** + * Transport handle, corresponds to pjsip_transport instance. + */ +typedef void *TransportHandle; + +/** + * Timer entry, corresponds to pj_timer_entry + */ +typedef void *TimerEntry; + +/** + * Generic data + */ +typedef void *GenericData; + +/* + * Forward declaration of Account and Call to be used + * by Endpoint. + */ +class Account; +class Call; + + +/** + * Constants + */ +enum +{ + /** Invalid ID, equal to PJSUA_INVALID_ID */ + INVALID_ID = -1, + + /** Success, equal to PJ_SUCCESS */ + SUCCESS = 0 +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * This structure contains information about an error that is thrown + * as an exception. + */ +struct Error +{ + /** The error code. */ + pj_status_t status; + + /** The PJSUA API operation that throws the error. */ + string title; + + /** The error message */ + string reason; + + /** The PJSUA source file that throws the error */ + string srcFile; + + /** The line number of PJSUA source file that throws the error */ + int srcLine; + + /** Build error string. */ + string info(bool multi_line=false) const; + + /** Default constructor */ + Error(); + + /** + * Construct an Error instance from the specified parameters. If + * \a prm_reason is empty, it will be filled with the error description + * for the status code. + */ + Error(pj_status_t prm_status, + const string &prm_title, + const string &prm_reason, + const string &prm_src_file, + int prm_src_line); +}; + + +/* + * Error utilities. + */ +#if PJSUA2_ERROR_HAS_EXTRA_INFO +# define PJSUA2_RAISE_ERROR(status) \ + PJSUA2_RAISE_ERROR2(status, __FUNCTION__) + +# define PJSUA2_RAISE_ERROR2(status,op) \ + PJSUA2_RAISE_ERROR3(status, op, string()) + +# define PJSUA2_RAISE_ERROR3(status,op,txt) \ + do { \ + Error err_ = Error(status, op, txt, __FILE__, __LINE__); \ + PJ_LOG(1,(THIS_FILE, "%s", err_.info().c_str())); \ + throw err_; \ + } while (0) + +#else + /** Raise Error exception */ +# define PJSUA2_RAISE_ERROR(status) \ + PJSUA2_RAISE_ERROR2(status, string()) + +/** Raise Error exception */ +# define PJSUA2_RAISE_ERROR2(status,op) \ + PJSUA2_RAISE_ERROR3(status, op, string()) + +/** Raise Error exception */ +# define PJSUA2_RAISE_ERROR3(status,op,txt) \ + do { \ + Error err_ = Error(status, op, txt, string(), 0); \ + PJ_LOG(1,(THIS_FILE, "%s", err_.info().c_str())); \ + throw err_; \ + } while (0) + +#endif + +/** Raise Error exception if the expression fails */ +#define PJSUA2_CHECK_RAISE_ERROR2(status, op) \ + do { \ + if (status != PJ_SUCCESS) { \ + PJSUA2_RAISE_ERROR2(status, op); \ + } \ + } while (0) + +/** Raise Error exception if the status fails */ +#define PJSUA2_CHECK_RAISE_ERROR(status) \ + PJSUA2_CHECK_RAISE_ERROR2(status, "") + +/** Raise Error exception if the expression fails */ +#define PJSUA2_CHECK_EXPR(expr) \ + do { \ + pj_status_t the_status = expr; \ + PJSUA2_CHECK_RAISE_ERROR2(the_status, #expr); \ + } while (0) + +////////////////////////////////////////////////////////////////////////////// +/** + * Version information. + */ +struct Version +{ + /** Major number */ + int major; + + /** Minor number */ + int minor; + + /** Additional revision number */ + int rev; + + /** Version suffix (e.g. "-svn") */ + string suffix; + + /** The full version info (e.g. "2.1.0-svn") */ + string full; + + /** + * PJLIB version number as three bytes with the following format: + * 0xMMIIRR00, where MM: major number, II: minor number, RR: revision + * number, 00: always zero for now. + */ + unsigned numeric; +}; + +////////////////////////////////////////////////////////////////////////////// + +/** + * Representation of time value. + */ +struct TimeValue +{ + /** + * The seconds part of the time. + */ + long sec; + + /** + * The miliseconds fraction of the time. + */ + long msec; + +public: + /** + * Convert from pjsip + */ + void fromPj(const pj_time_val &prm); +}; + +/** + * @} PJSUA2 + */ + +} // namespace pj + + + +#endif /* __PJSUA2_TYPES_HPP__ */ diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 7a3a3a38..d1f66b8c 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -923,6 +923,8 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt, pjsip_process_rdata_param proc_prm; pj_bool_t handled = PJ_FALSE; + PJ_UNUSED_ARG(msg); + if (status != PJ_SUCCESS) { char info[30]; char errmsg[PJ_ERR_MSG_SIZE]; diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index e15c54aa..0f5172e9 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -741,10 +741,13 @@ PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, pj_bzero(info, sizeof(*info)); info->slot_id = id; info->name = cinfo.name; + pjmedia_format_copy(&info->format, &cinfo.format); info->clock_rate = cinfo.clock_rate; info->channel_count = cinfo.channel_count; info->samples_per_frame = cinfo.samples_per_frame; info->bits_per_sample = cinfo.bits_per_sample; + info->tx_level_adj = ((float)cinfo.tx_adj_level) / 128 + 1; + info->rx_level_adj = ((float)cinfo.rx_adj_level) / 128 + 1; /* Build array of listeners */ info->listener_cnt = cinfo.listener_cnt; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 8b0f0e62..e4893401 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -699,6 +699,50 @@ static int worker_thread(void *arg) return 0; } +PJ_DEF(pj_status_t) pjsua_register_worker_thread(const char *name) +{ + pj_thread_desc desc; + pj_thread_t *thread; + pj_status_t status; + + if (pjsua_var.thread_quit_flag) + return PJ_EGONE; + + status = pj_thread_register(NULL, desc, &thread); + if (status != PJ_SUCCESS) + return status; + + if (name) + PJ_LOG(4,(THIS_FILE, "Worker thread %s started", name)); + + worker_thread(NULL); + + if (name) + PJ_LOG(4,(THIS_FILE, "Worker thread %s stopped", name)); + + return PJ_SUCCESS; +} + +PJ_DEF(void) pjsua_stop_worker_threads(void) +{ + unsigned i; + + pjsua_var.thread_quit_flag = 1; + + /* Wait worker threads to quit: */ + for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) { + if (pjsua_var.thread[i]) { + pj_status_t status; + status = pj_thread_join(pjsua_var.thread[i]); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(THIS_FILE, status, "Error joining worker thread")); + pj_thread_sleep(1000); + } + pj_thread_destroy(pjsua_var.thread[i]); + pjsua_var.thread[i] = NULL; + } + } +} /* Init random seed */ static void init_random_seed(void) @@ -1478,21 +1522,7 @@ PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) } /* Signal threads to quit: */ - pjsua_var.thread_quit_flag = 1; - - /* Wait worker threads to quit: */ - for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) { - if (pjsua_var.thread[i]) { - pj_status_t status; - status = pj_thread_join(pjsua_var.thread[i]); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(THIS_FILE, status, "Error joining worker thread")); - pj_thread_sleep(1000); - } - pj_thread_destroy(pjsua_var.thread[i]); - pjsua_var.thread[i] = NULL; - } - } + pjsua_stop_worker_threads(); if (pjsua_var.endpt) { unsigned max_wait; diff --git a/pjsip/src/pjsua2-test/main.cpp b/pjsip/src/pjsua2-test/main.cpp new file mode 100644 index 00000000..c76811bb --- /dev/null +++ b/pjsip/src/pjsua2-test/main.cpp @@ -0,0 +1,38 @@ +/* $Id$ */ +/* + * Copyright (C) 2012 Teluu Inc. (http://www.teluu.com) + * + * 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 + +using namespace pj; + +int main(int argc, char *argv[]) +{ + Endpoint &ep = Endpoint::instance(); + + EpConfig epCfg; + + epCfg.uaConfig.userAgent = "pjsua++-test"; + + ep.libCreate(); + ep.libInit(epCfg); + ep.libStart(); + ep.libDestroy(); + + return 0; +} + diff --git a/pjsip/src/pjsua2/account.cpp b/pjsip/src/pjsua2/account.cpp new file mode 100644 index 00000000..247c4a4a --- /dev/null +++ b/pjsip/src/pjsua2/account.cpp @@ -0,0 +1,799 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "account.cpp" + +/////////////////////////////////////////////////////////////////////////////// + +void AccountRegConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("AccountRegConfig"); + + NODE_READ_STRING (this_node, registrarUri); + NODE_READ_BOOL (this_node, registerOnAdd); + NODE_READ_UNSIGNED (this_node, timeoutSec); + NODE_READ_UNSIGNED (this_node, retryIntervalSec); + NODE_READ_UNSIGNED (this_node, firstRetryIntervalSec); + NODE_READ_UNSIGNED (this_node, delayBeforeRefreshSec); + NODE_READ_BOOL (this_node, dropCallsOnFail); + NODE_READ_UNSIGNED (this_node, unregWaitSec); + NODE_READ_UNSIGNED (this_node, proxyUse); + + readSipHeaders(this_node, "headers", headers); +} + +void AccountRegConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("AccountRegConfig"); + + NODE_WRITE_STRING (this_node, registrarUri); + NODE_WRITE_BOOL (this_node, registerOnAdd); + NODE_WRITE_UNSIGNED (this_node, timeoutSec); + NODE_WRITE_UNSIGNED (this_node, retryIntervalSec); + NODE_WRITE_UNSIGNED (this_node, firstRetryIntervalSec); + NODE_WRITE_UNSIGNED (this_node, delayBeforeRefreshSec); + NODE_WRITE_BOOL (this_node, dropCallsOnFail); + NODE_WRITE_UNSIGNED (this_node, unregWaitSec); + NODE_WRITE_UNSIGNED (this_node, proxyUse); + + writeSipHeaders(this_node, "headers", headers); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AccountSipConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("AccountSipConfig"); + + NODE_READ_STRINGV (this_node, proxies); + NODE_READ_STRING (this_node, contactForced); + NODE_READ_STRING (this_node, contactParams); + NODE_READ_STRING (this_node, contactUriParams); + NODE_READ_BOOL (this_node, authInitialEmpty); + NODE_READ_STRING (this_node, authInitialAlgorithm); + NODE_READ_INT (this_node, transportId); + + ContainerNode creds_node = this_node.readArray("authCreds"); + authCreds.resize(0); + while (creds_node.hasUnread()) { + AuthCredInfo cred; + cred.readObject(creds_node); + authCreds.push_back(cred); + } +} + +void AccountSipConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("AccountSipConfig"); + + NODE_WRITE_STRINGV (this_node, proxies); + NODE_WRITE_STRING (this_node, contactForced); + NODE_WRITE_STRING (this_node, contactParams); + NODE_WRITE_STRING (this_node, contactUriParams); + NODE_WRITE_BOOL (this_node, authInitialEmpty); + NODE_WRITE_STRING (this_node, authInitialAlgorithm); + NODE_WRITE_INT (this_node, transportId); + + ContainerNode creds_node = this_node.writeNewArray("authCreds"); + for (unsigned i=0; i PJ_ARRAY_SIZE(ret.cred_info)) + PJSUA2_RAISE_ERROR(PJ_ETOOMANY); + for (i=0; irealm = str2Pj(src.realm); + dst->scheme = str2Pj(src.scheme); + dst->username = str2Pj(src.username); + dst->data_type = src.dataType; + dst->data = str2Pj(src.data); + dst->ext.aka.k = str2Pj(src.akaK); + dst->ext.aka.op = str2Pj(src.akaOp); + dst->ext.aka.amf= str2Pj(src.akaAmf); + + ret.cred_count++; + } + ret.proxy_cnt = 0; + if (sipConfig.proxies.size() > PJ_ARRAY_SIZE(ret.proxy)) + PJSUA2_RAISE_ERROR(PJ_ETOOMANY); + for (i=0; inext; + } + + // AccountSipConfig + sipConfig.authCreds.clear(); + for (i=0; inext; + } + presConfig.publishEnabled = PJ2BOOL(prm.publish_enabled); + presConfig.publishQueue = PJ2BOOL(prm.publish_opt.queue_request); + presConfig.publishShutdownWaitMsec = prm.unpublish_max_wait_time_msec; + presConfig.pidfTupleId = pj2Str(prm.pidf_tuple_id); + + // AccountMwiConfig + mwiConfig.enabled = PJ2BOOL(prm.mwi_enabled); + mwiConfig.expirationSec = prm.mwi_expires; + + // AccountNatConfig + natConfig.sipStunUse = prm.sip_stun_use; + natConfig.mediaStunUse = prm.media_stun_use; + if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) { + natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice); + natConfig.iceMaxHostCands = prm.ice_cfg.ice_max_host_cands; + natConfig.iceAggressiveNomination = PJ2BOOL(prm.ice_cfg.ice_opt.aggressive); + natConfig.iceNominatedCheckDelayMsec = prm.ice_cfg.ice_opt.nominated_check_delay; + natConfig.iceWaitNominationTimeoutMsec = prm.ice_cfg.ice_opt.controlled_agent_want_nom_timeout; + natConfig.iceNoRtcp = PJ2BOOL(prm.ice_cfg.ice_no_rtcp); + natConfig.iceAlwaysUpdate = PJ2BOOL(prm.ice_cfg.ice_always_update); + } else { + pjsua_media_config default_mcfg; + if (!mcfg) { + pjsua_media_config_default(&default_mcfg); + mcfg = &default_mcfg; + } + natConfig.iceEnabled = PJ2BOOL(mcfg->enable_ice); + natConfig.iceMaxHostCands= mcfg->ice_max_host_cands; + natConfig.iceAggressiveNomination = PJ2BOOL(mcfg->ice_opt.aggressive); + natConfig.iceNominatedCheckDelayMsec = mcfg->ice_opt.nominated_check_delay; + natConfig.iceWaitNominationTimeoutMsec = mcfg->ice_opt.controlled_agent_want_nom_timeout; + natConfig.iceNoRtcp = PJ2BOOL(mcfg->ice_no_rtcp); + natConfig.iceAlwaysUpdate = PJ2BOOL(mcfg->ice_always_update); + } + + if (prm.turn_cfg_use == PJSUA_TURN_CONFIG_USE_CUSTOM) { + natConfig.turnEnabled = PJ2BOOL(prm.turn_cfg.enable_turn); + natConfig.turnServer = pj2Str(prm.turn_cfg.turn_server); + natConfig.turnConnType = prm.turn_cfg.turn_conn_type; + natConfig.turnUserName = pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.username); + natConfig.turnPasswordType = prm.turn_cfg.turn_auth_cred.data.static_cred.data_type; + natConfig.turnPassword = pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.data); + } else { + pjsua_media_config default_mcfg; + if (!mcfg) { + pjsua_media_config_default(&default_mcfg); + mcfg = &default_mcfg; + } + natConfig.turnEnabled = PJ2BOOL(mcfg->enable_turn); + natConfig.turnServer = pj2Str(mcfg->turn_server); + natConfig.turnConnType = mcfg->turn_conn_type; + natConfig.turnUserName = pj2Str(mcfg->turn_auth_cred.data.static_cred.username); + natConfig.turnPasswordType = mcfg->turn_auth_cred.data.static_cred.data_type; + natConfig.turnPassword = pj2Str(mcfg->turn_auth_cred.data.static_cred.data); + } + natConfig.contactRewriteUse = prm.allow_contact_rewrite; + natConfig.contactRewriteMethod = prm.contact_rewrite_method; + natConfig.viaRewriteUse = prm.allow_via_rewrite; + natConfig.sdpNatRewriteUse = prm.allow_sdp_nat_rewrite; + natConfig.sipOutboundUse = prm.use_rfc5626; + natConfig.sipOutboundInstanceId = pj2Str(prm.rfc5626_instance_id); + natConfig.sipOutboundRegId = pj2Str(prm.rfc5626_reg_id); + natConfig.udpKaIntervalSec = prm.ka_interval; + natConfig.udpKaData = pj2Str(prm.ka_data); + + // AccountMediaConfig + mediaConfig.transportConfig.fromPj(prm.rtp_cfg); + mediaConfig.lockCodecEnabled= PJ2BOOL(prm.lock_codec); +#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0) + mediaConfig.streamKaEnabled = PJ2BOOL(prm.use_stream_ka); +#else + mediaConfig.streamKaEnabled = false; +#endif + mediaConfig.srtpUse = prm.use_srtp; + mediaConfig.srtpSecureSignaling = prm.srtp_secure_signaling; + mediaConfig.ipv6Use = prm.ipv6_media_use; + + // AccountVideoConfig + videoConfig.autoShowIncoming = PJ2BOOL(prm.vid_in_auto_show); + videoConfig.autoTransmitOutgoing = PJ2BOOL(prm.vid_out_auto_transmit); + videoConfig.windowFlags = prm.vid_wnd_flags; + videoConfig.defaultCaptureDevice = prm.vid_cap_dev; + videoConfig.defaultRenderDevice = prm.vid_rend_dev; + videoConfig.rateControlMethod = prm.vid_stream_rc_cfg.method; + videoConfig.rateControlBandwidth = prm.vid_stream_rc_cfg.bandwidth; +} + +void AccountConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("AccountConfig"); + + NODE_READ_INT ( this_node, priority); + NODE_READ_STRING ( this_node, idUri); + NODE_READ_OBJ ( this_node, regConfig); + NODE_READ_OBJ ( this_node, sipConfig); + NODE_READ_OBJ ( this_node, callConfig); + NODE_READ_OBJ ( this_node, presConfig); + NODE_READ_OBJ ( this_node, mwiConfig); + NODE_READ_OBJ ( this_node, natConfig); + NODE_READ_OBJ ( this_node, mediaConfig); + NODE_READ_OBJ ( this_node, videoConfig); +} + +void AccountConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("AccountConfig"); + + NODE_WRITE_INT ( this_node, priority); + NODE_WRITE_STRING ( this_node, idUri); + NODE_WRITE_OBJ ( this_node, regConfig); + NODE_WRITE_OBJ ( this_node, sipConfig); + NODE_WRITE_OBJ ( this_node, callConfig); + NODE_WRITE_OBJ ( this_node, presConfig); + NODE_WRITE_OBJ ( this_node, mwiConfig); + NODE_WRITE_OBJ ( this_node, natConfig); + NODE_WRITE_OBJ ( this_node, mediaConfig); + NODE_WRITE_OBJ ( this_node, videoConfig); +} + + +/////////////////////////////////////////////////////////////////////////////// + +void AccountInfo::fromPj(const pjsua_acc_info &pai) +{ + id = pai.id; + isDefault = pai.is_default != 0; + uri = pj2Str(pai.acc_uri); + regIsConfigured = pai.has_registration != 0; + regIsActive = pai.has_registration && pai.expires > 0 && + (pai.status / 100 == 2); + regExpiresSec = pai.expires; + regStatus = pai.status; + regStatusText = pj2Str(pai.status_text); + regLastErr = pai.reg_last_err; + onlineStatus = pai.online_status != 0; + onlineStatusText = pj2Str(pai.online_status_text); +} + +/////////////////////////////////////////////////////////////////////////////// + +Account::Account() +: id(PJSUA_INVALID_ID) +{ +} + +Account::~Account() +{ + /* If this instance is deleted, also delete the corresponding account in + * PJSUA library. + */ + if (isValid() && pjsua_get_state() < PJSUA_STATE_CLOSING) { + // Cleanup buddies in the buddy list + while(buddyList.size() > 0) { + Buddy *b = buddyList[0]; + delete b; /* this will remove itself from the list */ + } + + pjsua_acc_set_user_data(id, NULL); + pjsua_acc_del(id); + } +} + +void Account::create(const AccountConfig &acc_cfg, + bool make_default) throw(Error) +{ + pjsua_acc_config pj_acc_cfg; + + acc_cfg.toPj(pj_acc_cfg); + pj_acc_cfg.user_data = (void*)this; + PJSUA2_CHECK_EXPR( pjsua_acc_add(&pj_acc_cfg, make_default, &id) ); +} + +void Account::modify(const AccountConfig &acc_cfg) throw(Error) +{ + pjsua_acc_config pj_acc_cfg; + + acc_cfg.toPj(pj_acc_cfg); + pj_acc_cfg.user_data = (void*)this; + PJSUA2_CHECK_EXPR( pjsua_acc_modify(id, &pj_acc_cfg) ); +} + +bool Account::isValid() const +{ + return pjsua_acc_is_valid(id) != 0; +} + +void Account::setDefault() throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_acc_set_default(id) ); +} + +bool Account::isDefault() const +{ + return pjsua_acc_get_default() == id; +} + +int Account::getId() const +{ + return id; +} + +Account *Account::lookup(int acc_id) +{ + return (Account*)pjsua_acc_get_user_data(acc_id); +} + +AccountInfo Account::getInfo() const throw(Error) +{ + pjsua_acc_info pj_ai; + AccountInfo ai; + + PJSUA2_CHECK_EXPR( pjsua_acc_get_info(id, &pj_ai) ); + ai.fromPj(pj_ai); + return ai; +} + +void Account::setRegistration(bool renew) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_acc_set_registration(id, renew) ); +} + +void +Account::setOnlineStatus(const PresenceStatus &pres_st) throw(Error) +{ + pjrpid_element pj_rpid; + + pj_bzero(&pj_rpid, sizeof(pj_rpid)); + pj_rpid.type = PJRPID_ELEMENT_TYPE_PERSON; + pj_rpid.activity = pres_st.activity; + pj_rpid.id = str2Pj(pres_st.rpidId); + pj_rpid.note = str2Pj(pres_st.note); + + PJSUA2_CHECK_EXPR( pjsua_acc_set_online_status2( + id, pres_st.status == PJSUA_BUDDY_STATUS_ONLINE, + &pj_rpid) ); +} + +void Account::setTransport(TransportId tp_id) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_acc_set_transport(id, tp_id) ); +} + +void Account::presNotify(const PresNotifyParam &prm) throw(Error) +{ + pj_str_t pj_state_str = str2Pj(prm.stateStr); + pj_str_t pj_reason = str2Pj(prm.reason); + pjsua_msg_data msg_data; + prm.txOption.toPj(msg_data); + + PJSUA2_CHECK_EXPR( pjsua_pres_notify(id, (pjsua_srv_pres*)prm.srvPres, + prm.state, &pj_state_str, + &pj_reason, prm.withBody, + &msg_data) ); +} + +const BuddyVector& Account::enumBuddies() const throw(Error) +{ + return buddyList; +} + +Buddy* Account::findBuddy(string uri, FindBuddyMatch *buddy_match) const + throw(Error) +{ + if (!buddy_match) { + static FindBuddyMatch def_bm; + buddy_match = &def_bm; + } + + for (unsigned i = 0; i < buddyList.size(); i++) { + if (buddy_match->match(uri, *buddyList[i])) + return buddyList[i]; + } + PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); +} + +void Account::addBuddy(Buddy *buddy) +{ + pj_assert(buddy); + + buddyList.push_back(buddy); +} + +void Account::removeBuddy(Buddy *buddy) +{ + pj_assert(buddy); + + BuddyVector::iterator it; + for (it = buddyList.begin(); it != buddyList.end(); it++) { + if (*it == buddy) { + buddyList.erase(it); + return; + } + } + + pj_assert(!"Bug! Buddy to be removed is not in the buddy list!"); +} diff --git a/pjsip/src/pjsua2/call.cpp b/pjsip/src/pjsua2/call.cpp new file mode 100644 index 00000000..69a91829 --- /dev/null +++ b/pjsip/src/pjsua2/call.cpp @@ -0,0 +1,719 @@ +/* $Id$ */ +/* + * Copyright (C) 2012-2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "call.cpp" + +/////////////////////////////////////////////////////////////////////////////// + +#define SDP_BUFFER_SIZE 1024 + +MathStat::MathStat() +: n(0), max(0), min(0), last(0), mean(0) +{ +} + +void MathStat::fromPj(const pj_math_stat &prm) +{ + this->n = prm.n; + this->max = prm.max; + this->min = prm.min; + this->last = prm.last; + this->mean = prm.mean; +} + +void RtcpStreamStat::fromPj(const pjmedia_rtcp_stream_stat &prm) +{ + this->update.fromPj(prm.update); + this->updateCount = prm.update_cnt; + this->pkt = (unsigned)prm.pkt; + this->bytes = (unsigned)prm.bytes; + this->discard = prm.discard; + this->loss = prm.loss; + this->reorder = prm.loss; + this->dup = prm.dup; + this->lossPeriodUsec.fromPj(prm.loss_period); + this->lossType.burst = prm.loss_type.burst; + this->lossType.random = prm.loss_type.random; + this->jitterUsec.fromPj(prm.jitter); +} + +void RtcpSdes::fromPj(const pjmedia_rtcp_sdes &prm) +{ + this->cname = pj2Str(prm.cname); + this->name = pj2Str(prm.name); + this->email = pj2Str(prm.email); + this->phone = pj2Str(prm.phone); + this->loc = pj2Str(prm.loc); + this->tool = pj2Str(prm.tool); + this->note = pj2Str(prm.note); +} + +void RtcpStat::fromPj(const pjmedia_rtcp_stat &prm) +{ + this->start.fromPj(prm.start); + this->txStat.fromPj(prm.tx); + this->rxStat.fromPj(prm.rx); + this->rttUsec.fromPj(prm.rtt); + this->rtpTxLastTs = prm.rtp_tx_last_ts; + this->rtpTxLastSeq = prm.rtp_tx_last_seq; +#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 + this->rxIpdvUsec.fromPj(prm.rx_ipdv); +#endif +#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && \ + PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 + this->rxRawJitterUsec.fromPj(prm.rx_raw_jitter); +#endif + this->peerSdes.fromPj(prm.peer_sdes); +} + +void JbufState::fromPj(const pjmedia_jb_state &prm) +{ + this->frameSize = prm.frame_size; + this->minPrefetch = prm.min_prefetch; + this->maxPrefetch = prm.max_prefetch; + this->burst = prm.burst; + this->prefetch = prm.prefetch; + this->size = prm.size; + this->avgDelayMsec = prm.avg_delay; + this->minDelayMsec = prm.min_delay; + this->maxDelayMsec = prm.max_delay; + this->devDelayMsec = prm.dev_delay; + this->avgBurst = prm.avg_burst; + this->lost = prm.lost; + this->discard = prm.discard; + this->empty = prm.empty; +} + +void SdpSession::fromPj(const pjmedia_sdp_session &sdp) +{ + char buf[SDP_BUFFER_SIZE]; + int len; + + len = pjmedia_sdp_print(&sdp, buf, sizeof(buf)); + wholeSdp = (len > -1? string(buf, len): ""); + pjSdpSession = (void *)&sdp; +} + +void MediaEvent::fromPj(const pjmedia_event &ev) +{ + type = ev.type; + if (type == PJMEDIA_EVENT_FMT_CHANGED) { + data.fmtChanged.newWidth = ev.data.fmt_changed.new_fmt.det.vid.size.w; + data.fmtChanged.newHeight = ev.data.fmt_changed.new_fmt.det.vid.size.h; + } + pjMediaEvent = (void *)&ev; +} + +void MediaTransportInfo::fromPj(const pjmedia_transport_info &info) +{ + char straddr[PJ_INET6_ADDRSTRLEN+10]; + + pj_sockaddr_print(&info.src_rtp_name, straddr, sizeof(straddr), 3); + srcRtpName = straddr; + pj_sockaddr_print(&info.src_rtcp_name, straddr, sizeof(straddr), 3); + srcRtcpName = straddr; +} + +////////////////////////////////////////////////////////////////////////////// + +/* Call Audio Media. */ +class CallAudioMedia : public AudioMedia +{ +public: + /* + * Set the conference port identification associated with the + * call audio media. + */ + void setPortId(int id); +}; + + +void CallAudioMedia::setPortId(int id) +{ + this->id = id; +} + +CallOpParam::CallOpParam(bool useDefaultCallSetting) +: statusCode(pjsip_status_code(0)), reason(""), options(0) +{ + if (useDefaultCallSetting) + opt = CallSetting(true); +} + +CallSendRequestParam::CallSendRequestParam() +: method("") +{ +} + +CallVidSetStreamParam::CallVidSetStreamParam() +{ +#if PJSUA_HAS_VIDEO + pjsua_call_vid_strm_op_param prm; + + pjsua_call_vid_strm_op_param_default(&prm); + this->medIdx = prm.med_idx; + this->dir = prm.dir; + this->capDev = prm.cap_dev; +#endif +} + +CallSetting::CallSetting(pj_bool_t useDefaultValues) +{ + if (useDefaultValues) { + pjsua_call_setting setting; + + pjsua_call_setting_default(&setting); + fromPj(setting); + } else { + flag = 0; + reqKeyframeMethod = 0; + audioCount = 0; + videoCount = 0; + } +} + +bool CallSetting::isEmpty() const +{ + return (flag == 0 && reqKeyframeMethod == 0 && audioCount == 0 && + videoCount == 0); +} + +void CallSetting::fromPj(const pjsua_call_setting &prm) +{ + this->flag = prm.flag; + this->reqKeyframeMethod = prm.req_keyframe_method; + this->audioCount = prm.aud_cnt; + this->videoCount = prm.vid_cnt; +} + +pjsua_call_setting CallSetting::toPj() const +{ + pjsua_call_setting setting; + + setting.flag = this->flag; + setting.req_keyframe_method = this->reqKeyframeMethod; + setting.aud_cnt = this->audioCount; + setting.vid_cnt = this->videoCount; + + return setting; +} + + +CallMediaInfo::CallMediaInfo() +{ +} + +void CallMediaInfo::fromPj(const pjsua_call_media_info &prm) +{ + this->index = prm.index; + this->type = prm.type; + this->dir = prm.dir; + this->status = prm.status; + if (this->type == PJMEDIA_TYPE_AUDIO) { + this->audioConfSlot = (int)prm.stream.aud.conf_slot; + } else if (this->type == PJMEDIA_TYPE_VIDEO) { + this->videoIncomingWindowId = prm.stream.vid.win_in; + this->videoCapDev = prm.stream.vid.cap_dev; + } +} + +void CallInfo::fromPj(const pjsua_call_info &pci) +{ + unsigned mi; + + id = pci.id; + role = pci.role; + accId = pci.acc_id; + localUri = pj2Str(pci.local_info); + localContact = pj2Str(pci.local_contact); + remoteUri = pj2Str(pci.remote_info); + remoteContact = pj2Str(pci.remote_contact); + callIdString = pj2Str(pci.call_id); + setting.fromPj(pci.setting); + state = pci.state; + stateText = pj2Str(pci.state_text); + lastStatusCode = pci.last_status; + lastReason = pj2Str(pci.last_status_text); + connectDuration.fromPj(pci.connect_duration); + totalDuration.fromPj(pci.total_duration); + remOfferer = PJ2BOOL(pci.rem_offerer); + remAudioCount = pci.rem_aud_cnt; + remVideoCount = pci.rem_vid_cnt; + + for (mi = 0; mi < pci.media_cnt; mi++) { + CallMediaInfo med; + + med.fromPj(pci.media[mi]); + media.push_back(med); + } + for (mi = 0; mi < pci.prov_media_cnt; mi++) { + CallMediaInfo med; + + med.fromPj(pci.prov_media[mi]); + provMedia.push_back(med); + } +} + +void StreamInfo::fromPj(const pjsua_stream_info &info) +{ + char straddr[PJ_INET6_ADDRSTRLEN+10]; + + type = info.type; + if (type == PJMEDIA_TYPE_AUDIO) { + proto = info.info.aud.proto; + dir = info.info.aud.dir; + pj_sockaddr_print(&info.info.aud.rem_addr, straddr, sizeof(straddr), 3); + remoteRtpAddress = straddr; + pj_sockaddr_print(&info.info.aud.rem_rtcp, straddr, sizeof(straddr), 3); + remoteRtcpAddress = straddr; + txPt = info.info.aud.tx_pt; + rxPt = info.info.aud.rx_pt; + codecName = pj2Str(info.info.aud.fmt.encoding_name); + codecClockRate = info.info.aud.fmt.clock_rate; + codecParam = info.info.aud.param; + } else if (type == PJMEDIA_TYPE_VIDEO) { + proto = info.info.vid.proto; + dir = info.info.vid.dir; + pj_sockaddr_print(&info.info.vid.rem_addr, straddr, sizeof(straddr), 3); + remoteRtpAddress = straddr; + pj_sockaddr_print(&info.info.vid.rem_rtcp, straddr, sizeof(straddr), 3); + remoteRtcpAddress = straddr; + txPt = info.info.vid.tx_pt; + rxPt = info.info.vid.rx_pt; + codecName = pj2Str(info.info.vid.codec_info.encoding_name); + codecClockRate = info.info.vid.codec_info.clock_rate; + codecParam = info.info.vid.codec_param; + } +} + +void StreamStat::fromPj(const pjsua_stream_stat &prm) +{ + rtcp.fromPj(prm.rtcp); + jbuf.fromPj(prm.jbuf); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct call_param +{ + pjsua_msg_data msg_data; + pjsua_msg_data *p_msg_data; + pjsua_call_setting opt; + pjsua_call_setting *p_opt; + pj_str_t reason; + pj_str_t *p_reason; + +public: + /** + * Default constructors with specified parameters. + */ + call_param(const SipTxOption &tx_option); + call_param(const SipTxOption &tx_option, const CallSetting &setting, + const string &reason_str); +}; + +call_param::call_param(const SipTxOption &tx_option) +{ + if (tx_option.isEmpty()) { + p_msg_data = NULL; + } else { + tx_option.toPj(msg_data); + p_msg_data = &msg_data; + } + + p_opt = NULL; + p_reason = NULL; +} + +call_param::call_param(const SipTxOption &tx_option, const CallSetting &setting, + const string &reason_str) +{ + if (tx_option.isEmpty()) { + p_msg_data = NULL; + } else { + tx_option.toPj(msg_data); + p_msg_data = &msg_data; + } + + if (setting.isEmpty()) { + p_opt = NULL; + } else { + opt = setting.toPj(); + p_opt = &opt; + } + + reason = str2Pj(reason_str); + p_reason = (reason.slen == 0? NULL: &reason); +} + +Call::Call(Account& account, int call_id) +: acc(account), id(call_id) +{ + if (call_id != PJSUA_INVALID_ID) + pjsua_call_set_user_data(call_id, this); +} + +Call::~Call() +{ + /** + * If this instance is deleted, also hangup the corresponding call in + * PJSUA library. + */ + if (id != PJSUA_INVALID_ID && pjsua_get_state() < PJSUA_STATE_CLOSING) { + pjsua_call_set_user_data(id, NULL); + if (isActive()) { + CallOpParam prm; + hangup(prm); + } + } +} + +CallInfo Call::getInfo() const throw(Error) +{ + pjsua_call_info pj_ci; + CallInfo ci; + + PJSUA2_CHECK_EXPR( pjsua_call_get_info(id, &pj_ci) ); + ci.fromPj(pj_ci); + return ci; +} + +bool Call::isActive() const +{ + if (id == PJSUA_INVALID_ID) + return false; + + return (pjsua_call_is_active(id) != 0); +} + +int Call::getId() const +{ + return id; +} + +Call *Call::lookup(int call_id) +{ + Call *call = (Call*)pjsua_call_get_user_data(call_id); + if (call) + call->id = call_id; + return call; +} + +bool Call::hasMedia() const +{ + return (pjsua_call_has_media(id) != 0); +} + +Media *Call::getMedia(unsigned med_idx) const +{ + /* Check if the media index is valid and if the media has a valid port ID */ + if (med_idx >= medias.size() || + (medias[med_idx] && medias[med_idx]->getType() == PJMEDIA_TYPE_AUDIO && + ((AudioMedia *)medias[med_idx])->getPortId() == PJSUA_INVALID_ID)) + { + return NULL; + } + + return medias[med_idx]; +} + +pjsip_dialog_cap_status Call::remoteHasCap(int htype, + const string &hname, + const string &token) const +{ + pj_str_t pj_hname = str2Pj(hname); + pj_str_t pj_token = str2Pj(token); + + return pjsua_call_remote_has_cap(id, htype, + (htype == PJSIP_H_OTHER)? &pj_hname: NULL, + &pj_token); +} + +void Call::setUserData(Token user_data) +{ + userData = user_data; +} + +Token Call::getUserData() const +{ + return userData; +} + +pj_stun_nat_type Call::getRemNatType() throw(Error) +{ + pj_stun_nat_type nat; + + PJSUA2_CHECK_EXPR( pjsua_call_get_rem_nat_type(id, &nat) ); + + return nat; +} + +void Call::makeCall(const string &dst_uri, const CallOpParam &prm) throw(Error) +{ + pj_str_t pj_dst_uri = str2Pj(dst_uri); + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_make_call(acc.getId(), &pj_dst_uri, + param.p_opt, this, + param.p_msg_data, &id) ); +} + +void Call::answer(const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_answer2(id, param.p_opt, prm.statusCode, + param.p_reason, param.p_msg_data) ); +} + +void Call::hangup(const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_hangup(id, prm.statusCode, param.p_reason, + param.p_msg_data) ); +} + +void Call::setHold(const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_set_hold2(id, prm.options, param.p_msg_data)); +} + +void Call::reinvite(const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_reinvite2(id, param.p_opt, param.p_msg_data)); +} + +void Call::update(const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption, prm.opt, prm.reason); + + PJSUA2_CHECK_EXPR( pjsua_call_update2(id, param.p_opt, param.p_msg_data) ); +} + +void Call::xfer(const string &dest, const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption); + pj_str_t pj_dest = str2Pj(dest); + + PJSUA2_CHECK_EXPR( pjsua_call_xfer(id, &pj_dest, param.p_msg_data) ); +} + +void Call::xferReplaces(const Call& dest_call, + const CallOpParam &prm) throw(Error) +{ + call_param param(prm.txOption); + + PJSUA2_CHECK_EXPR(pjsua_call_xfer_replaces(id, dest_call.getId(), + prm.options, param.p_msg_data) ); +} + +void Call::processRedirect(pjsip_redirect_op cmd) throw(Error) +{ + PJSUA2_CHECK_EXPR(pjsua_call_process_redirect(id, cmd)); +} + +void Call::dialDtmf(const string &digits) throw(Error) +{ + pj_str_t pj_digits = str2Pj(digits); + + PJSUA2_CHECK_EXPR(pjsua_call_dial_dtmf(id, &pj_digits)); +} + +void Call::sendInstantMessage(const SendInstantMessageParam& prm) + throw(Error) +{ + pj_str_t mime_type = str2Pj(prm.contentType); + pj_str_t content = str2Pj(prm.content); + call_param param(prm.txOption); + + PJSUA2_CHECK_EXPR(pjsua_call_send_im(id, &mime_type, &content, + param.p_msg_data, prm.userData) ); +} + +void Call::sendTypingIndication(const SendTypingIndicationParam &prm) + throw(Error) +{ + call_param param(prm.txOption); + + PJSUA2_CHECK_EXPR(pjsua_call_send_typing_ind(id, + (prm.isTyping? + PJ_TRUE: PJ_FALSE), + param.p_msg_data) ); +} + +void Call::sendRequest(const CallSendRequestParam &prm) throw(Error) +{ + pj_str_t method = str2Pj(prm.method); + call_param param(prm.txOption); + + PJSUA2_CHECK_EXPR(pjsua_call_send_request(id, &method, param.p_msg_data) ); +} + +string Call::dump(bool with_media, const string indent) throw(Error) +{ +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + char buffer[1024 * 10]; +#else + char buffer[1024 * 3]; +#endif + + PJSUA2_CHECK_EXPR(pjsua_call_dump(id, (with_media? PJ_TRUE: PJ_FALSE), + buffer, sizeof(buffer), + indent.c_str())); + + return buffer; +} + +int Call::vidGetStreamIdx() const +{ +#if PJSUA_HAS_VIDEO + return pjsua_call_get_vid_stream_idx(id); +#else + return PJSUA_INVALID_ID; +#endif +} + +bool Call::vidStreamIsRunning(int med_idx, pjmedia_dir dir) const +{ +#if PJSUA_HAS_VIDEO + return pjsua_call_vid_stream_is_running(id, med_idx, dir); +#else + PJ_UNUSED_ARG(med_idx); + PJ_UNUSED_ARG(dir); + return false; +#endif +} + +void Call::vidSetStream(pjsua_call_vid_strm_op op, + const CallVidSetStreamParam ¶m) throw(Error) +{ +#if PJSUA_HAS_VIDEO + pjsua_call_vid_strm_op_param prm; + + prm.med_idx = param.medIdx; + prm.dir = param.dir; + prm.cap_dev = param.capDev; + PJSUA2_CHECK_EXPR( pjsua_call_set_vid_strm(id, op, &prm) ); +#else + PJ_UNUSED_ARG(op); + PJ_UNUSED_ARG(param); + PJSUA2_RAISE_ERROR(PJ_EINVALIDOP); +#endif +} + +StreamInfo Call::getStreamInfo(unsigned med_idx) const throw(Error) +{ + pjsua_stream_info pj_si; + StreamInfo si; + + PJSUA2_CHECK_EXPR( pjsua_call_get_stream_info(id, med_idx, &pj_si) ); + si.fromPj(pj_si); + return si; +} + +StreamStat Call::getStreamStat(unsigned med_idx) const throw(Error) +{ + pjsua_stream_stat pj_ss; + StreamStat ss; + + PJSUA2_CHECK_EXPR( pjsua_call_get_stream_stat(id, med_idx, &pj_ss) ); + ss.fromPj(pj_ss); + return ss; +} + +MediaTransportInfo Call::getMedTransportInfo(unsigned med_idx) const + throw(Error) +{ + pjmedia_transport_info pj_mti; + MediaTransportInfo mti; + + PJSUA2_CHECK_EXPR( pjsua_call_get_med_transport_info(id, med_idx, + &pj_mti) ); + mti.fromPj(pj_mti); + return mti; +} + +void Call::processMediaUpdate(OnCallMediaStateParam &prm) +{ + pjsua_call_info pj_ci; + unsigned mi; + + if (pjsua_call_get_info(id, &pj_ci) == PJ_SUCCESS) { + for (mi = 0; mi < pj_ci.media_cnt; mi++) { + if (mi >= medias.size()) { + if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) { + medias.push_back(new CallAudioMedia); + } else { + medias.push_back(NULL); + } + } + + if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) { + CallAudioMedia *aud_med = (CallAudioMedia *)medias[mi]; + + aud_med->setPortId(pj_ci.media[mi].stream.aud.conf_slot); + /* Add media if the conference slot ID is valid. */ + if (pj_ci.media[mi].stream.aud.conf_slot != PJSUA_INVALID_ID) + { + Endpoint::instance().mediaAdd((AudioMedia &)*aud_med); + } else { + Endpoint::instance().mediaRemove((AudioMedia &)*aud_med); + } + } + } + } + + /* Call media state callback. */ + onCallMediaState(prm); +} + +void Call::processStateChange(OnCallStateParam &prm) +{ + pjsua_call_info pj_ci; + unsigned mi; + + if (pjsua_call_get_info(id, &pj_ci) == PJ_SUCCESS && + pj_ci.state == PJSIP_INV_STATE_DISCONNECTED) + { + /* Clear medias. */ + for (mi = 0; mi < medias.size(); mi++) { + if (medias[mi]) + delete medias[mi]; + } + medias.clear(); + } + + onCallState(prm); + /* If the state is DISCONNECTED, this call may have already been deleted + * by the application in the callback, so do not access it anymore here. + */ +} diff --git a/pjsip/src/pjsua2/endpoint.cpp b/pjsip/src/pjsua2/endpoint.cpp new file mode 100644 index 00000000..d3ba3719 --- /dev/null +++ b/pjsip/src/pjsua2/endpoint.cpp @@ -0,0 +1,1612 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include +#include +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#include +#include + +#define THIS_FILE "endpoint.cpp" +#define MAX_STUN_SERVERS 32 +#define TIMER_SIGNATURE 0x600D878A +#define MAX_CODEC_NUM 64 + +struct UserTimer +{ + pj_uint32_t signature; + OnTimerParam prm; + pj_timer_entry entry; +}; + +Endpoint *Endpoint::instance_; + +/////////////////////////////////////////////////////////////////////////////// + +UaConfig::UaConfig() +{ + pjsua_config ua_cfg; + + pjsua_config_default(&ua_cfg); + fromPj(ua_cfg); +} + +void UaConfig::fromPj(const pjsua_config &ua_cfg) +{ + unsigned i; + + this->maxCalls = ua_cfg.max_calls; + this->threadCnt = ua_cfg.thread_cnt; + this->userAgent = pj2Str(ua_cfg.user_agent); + + for (i=0; inameserver.push_back(pj2Str(ua_cfg.nameserver[i])); + } + + for (i=0; istunServer.push_back(pj2Str(ua_cfg.stun_srv[i])); + } + + this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure); + this->natTypeInSdp = ua_cfg.nat_type_in_sdp; + this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi); +} + +pjsua_config UaConfig::toPj() const +{ + unsigned i; + pjsua_config pua_cfg; + + pjsua_config_default(&pua_cfg); + + pua_cfg.max_calls = this->maxCalls; + pua_cfg.thread_cnt = this->threadCnt; + pua_cfg.user_agent = str2Pj(this->userAgent); + + for (i=0; inameserver.size() && inameserver[i]); + } + pua_cfg.nameserver_count = i; + + for (i=0; istunServer.size() && istunServer[i]); + } + pua_cfg.stun_srv_cnt = i; + + pua_cfg.nat_type_in_sdp = this->natTypeInSdp; + pua_cfg.enable_unsolicited_mwi = this->mwiUnsolicitedEnabled; + + return pua_cfg; +} + +void UaConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("UaConfig"); + + NODE_READ_UNSIGNED( this_node, maxCalls); + NODE_READ_UNSIGNED( this_node, threadCnt); + NODE_READ_BOOL ( this_node, mainThreadOnly); + NODE_READ_STRINGV ( this_node, nameserver); + NODE_READ_STRING ( this_node, userAgent); + NODE_READ_STRINGV ( this_node, stunServer); + NODE_READ_BOOL ( this_node, stunIgnoreFailure); + NODE_READ_INT ( this_node, natTypeInSdp); + NODE_READ_BOOL ( this_node, mwiUnsolicitedEnabled); +} + +void UaConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("UaConfig"); + + NODE_WRITE_UNSIGNED( this_node, maxCalls); + NODE_WRITE_UNSIGNED( this_node, threadCnt); + NODE_WRITE_BOOL ( this_node, mainThreadOnly); + NODE_WRITE_STRINGV ( this_node, nameserver); + NODE_WRITE_STRING ( this_node, userAgent); + NODE_WRITE_STRINGV ( this_node, stunServer); + NODE_WRITE_BOOL ( this_node, stunIgnoreFailure); + NODE_WRITE_INT ( this_node, natTypeInSdp); + NODE_WRITE_BOOL ( this_node, mwiUnsolicitedEnabled); +} + +/////////////////////////////////////////////////////////////////////////////// + +LogConfig::LogConfig() +{ + pjsua_logging_config lc; + + pjsua_logging_config_default(&lc); + fromPj(lc); +} + +void LogConfig::fromPj(const pjsua_logging_config &lc) +{ + this->msgLogging = lc.msg_logging; + this->level = lc.level; + this->consoleLevel = lc.console_level; + this->decor = lc.decor; + this->filename = pj2Str(lc.log_filename); + this->fileFlags = lc.log_file_flags; + this->writer = NULL; +} + +pjsua_logging_config LogConfig::toPj() const +{ + pjsua_logging_config lc; + + pjsua_logging_config_default(&lc); + + lc.msg_logging = this->msgLogging; + lc.level = this->level; + lc.console_level = this->consoleLevel; + lc.decor = this->decor; + lc.log_file_flags = this->fileFlags; + lc.log_filename = str2Pj(this->filename); + + return lc; +} + +void LogConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("LogConfig"); + + NODE_READ_UNSIGNED( this_node, msgLogging); + NODE_READ_UNSIGNED( this_node, level); + NODE_READ_UNSIGNED( this_node, consoleLevel); + NODE_READ_UNSIGNED( this_node, decor); + NODE_READ_STRING ( this_node, filename); + NODE_READ_UNSIGNED( this_node, fileFlags); +} + +void LogConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("LogConfig"); + + NODE_WRITE_UNSIGNED( this_node, msgLogging); + NODE_WRITE_UNSIGNED( this_node, level); + NODE_WRITE_UNSIGNED( this_node, consoleLevel); + NODE_WRITE_UNSIGNED( this_node, decor); + NODE_WRITE_STRING ( this_node, filename); + NODE_WRITE_UNSIGNED( this_node, fileFlags); +} + +/////////////////////////////////////////////////////////////////////////////// + +MediaConfig::MediaConfig() +{ + pjsua_media_config mc; + + pjsua_media_config_default(&mc); + fromPj(mc); +} + +void MediaConfig::fromPj(const pjsua_media_config &mc) +{ + this->clockRate = mc.clock_rate; + this->sndClockRate = mc.snd_clock_rate; + this->channelCount = mc.channel_count; + this->audioFramePtime = mc.audio_frame_ptime; + this->maxMediaPorts = mc.max_media_ports; + this->hasIoqueue = PJ2BOOL(mc.has_ioqueue); + this->threadCnt = mc.thread_cnt; + this->quality = mc.quality; + this->ptime = mc.ptime; + this->noVad = PJ2BOOL(mc.no_vad); + this->ilbcMode = mc.ilbc_mode; + this->txDropPct = mc.tx_drop_pct; + this->rxDropPct = mc.rx_drop_pct; + this->ecOptions = mc.ec_options; + this->ecTailLen = mc.ec_tail_len; + this->sndRecLatency = mc.snd_rec_latency; + this->sndPlayLatency = mc.snd_play_latency; + this->jbInit = mc.jb_init; + this->jbMinPre = mc.jb_min_pre; + this->jbMaxPre = mc.jb_max_pre; + this->jbMax = mc.jb_max; + this->sndAutoCloseTime = mc.snd_auto_close_time; + this->vidPreviewEnableNative = PJ2BOOL(mc.vid_preview_enable_native); +} + +pjsua_media_config MediaConfig::toPj() const +{ + pjsua_media_config mcfg; + + pjsua_media_config_default(&mcfg); + + mcfg.clock_rate = this->clockRate; + mcfg.snd_clock_rate = this->sndClockRate; + mcfg.channel_count = this->channelCount; + mcfg.audio_frame_ptime = this->audioFramePtime; + mcfg.max_media_ports = this->maxMediaPorts; + mcfg.has_ioqueue = this->hasIoqueue; + mcfg.thread_cnt = this->threadCnt; + mcfg.quality = this->quality; + mcfg.ptime = this->ptime; + mcfg.no_vad = this->noVad; + mcfg.ilbc_mode = this->ilbcMode; + mcfg.tx_drop_pct = this->txDropPct; + mcfg.rx_drop_pct = this->rxDropPct; + mcfg.ec_options = this->ecOptions; + mcfg.ec_tail_len = this->ecTailLen; + mcfg.snd_rec_latency = this->sndRecLatency; + mcfg.snd_play_latency = this->sndPlayLatency; + mcfg.jb_init = this->jbInit; + mcfg.jb_min_pre = this->jbMinPre; + mcfg.jb_max_pre = this->jbMaxPre; + mcfg.jb_max = this->jbMax; + mcfg.snd_auto_close_time = this->sndAutoCloseTime; + mcfg.vid_preview_enable_native = this->vidPreviewEnableNative; + + return mcfg; +} + +void MediaConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("MediaConfig"); + + NODE_READ_UNSIGNED( this_node, clockRate); + NODE_READ_UNSIGNED( this_node, sndClockRate); + NODE_READ_UNSIGNED( this_node, channelCount); + NODE_READ_UNSIGNED( this_node, audioFramePtime); + NODE_READ_UNSIGNED( this_node, maxMediaPorts); + NODE_READ_BOOL ( this_node, hasIoqueue); + NODE_READ_UNSIGNED( this_node, threadCnt); + NODE_READ_UNSIGNED( this_node, quality); + NODE_READ_UNSIGNED( this_node, ptime); + NODE_READ_BOOL ( this_node, noVad); + NODE_READ_UNSIGNED( this_node, ilbcMode); + NODE_READ_UNSIGNED( this_node, txDropPct); + NODE_READ_UNSIGNED( this_node, rxDropPct); + NODE_READ_UNSIGNED( this_node, ecOptions); + NODE_READ_UNSIGNED( this_node, ecTailLen); + NODE_READ_UNSIGNED( this_node, sndRecLatency); + NODE_READ_UNSIGNED( this_node, sndPlayLatency); + NODE_READ_INT ( this_node, jbInit); + NODE_READ_INT ( this_node, jbMinPre); + NODE_READ_INT ( this_node, jbMaxPre); + NODE_READ_INT ( this_node, jbMax); + NODE_READ_INT ( this_node, sndAutoCloseTime); + NODE_READ_BOOL ( this_node, vidPreviewEnableNative); +} + +void MediaConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("MediaConfig"); + + NODE_WRITE_UNSIGNED( this_node, clockRate); + NODE_WRITE_UNSIGNED( this_node, sndClockRate); + NODE_WRITE_UNSIGNED( this_node, channelCount); + NODE_WRITE_UNSIGNED( this_node, audioFramePtime); + NODE_WRITE_UNSIGNED( this_node, maxMediaPorts); + NODE_WRITE_BOOL ( this_node, hasIoqueue); + NODE_WRITE_UNSIGNED( this_node, threadCnt); + NODE_WRITE_UNSIGNED( this_node, quality); + NODE_WRITE_UNSIGNED( this_node, ptime); + NODE_WRITE_BOOL ( this_node, noVad); + NODE_WRITE_UNSIGNED( this_node, ilbcMode); + NODE_WRITE_UNSIGNED( this_node, txDropPct); + NODE_WRITE_UNSIGNED( this_node, rxDropPct); + NODE_WRITE_UNSIGNED( this_node, ecOptions); + NODE_WRITE_UNSIGNED( this_node, ecTailLen); + NODE_WRITE_UNSIGNED( this_node, sndRecLatency); + NODE_WRITE_UNSIGNED( this_node, sndPlayLatency); + NODE_WRITE_INT ( this_node, jbInit); + NODE_WRITE_INT ( this_node, jbMinPre); + NODE_WRITE_INT ( this_node, jbMaxPre); + NODE_WRITE_INT ( this_node, jbMax); + NODE_WRITE_INT ( this_node, sndAutoCloseTime); + NODE_WRITE_BOOL ( this_node, vidPreviewEnableNative); +} + +/////////////////////////////////////////////////////////////////////////////// + +void EpConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("EpConfig"); + NODE_READ_OBJ( this_node, uaConfig); + NODE_READ_OBJ( this_node, logConfig); + NODE_READ_OBJ( this_node, medConfig); +} + +void EpConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("EpConfig"); + NODE_WRITE_OBJ( this_node, uaConfig); + NODE_WRITE_OBJ( this_node, logConfig); + NODE_WRITE_OBJ( this_node, medConfig); +} + +/////////////////////////////////////////////////////////////////////////////// +/* Class to post log to main thread */ +struct PendingLog : public PendingJob +{ + LogEntry entry; + virtual void execute(bool is_pending) + { + PJ_UNUSED_ARG(is_pending); + Endpoint::instance().utilLogWrite(entry); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +/* + * Endpoint instance + */ +Endpoint::Endpoint() +: writer(NULL), mainThreadOnly(false), mainThread(NULL), pendingJobSize(0) +{ + if (instance_) { + PJSUA2_RAISE_ERROR(PJ_EEXISTS); + } + + instance_ = this; +} + +Endpoint& Endpoint::instance() throw(Error) +{ + if (!instance_) { + PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); + } + return *instance_; +} + +Endpoint::~Endpoint() +{ + while (!pendingJobs.empty()) { + delete pendingJobs.front(); + pendingJobs.pop_front(); + } + + while(mediaList.size() > 0) { + AudioMedia *cur_media = mediaList[0]; + delete cur_media; /* this will remove itself from the list */ + } + + clearCodecInfoList(); + + try { + libDestroy(); + } catch (Error &err) { + // Ignore + PJ_UNUSED_ARG(err); + } + + instance_ = NULL; +} + +void Endpoint::utilAddPendingJob(PendingJob *job) +{ + enum { + MAX_PENDING_JOBS = 1024 + }; + + /* See if we can execute immediately */ + if (!mainThreadOnly || pj_thread_this()==mainThread) { + job->execute(false); + delete job; + return; + } + + if (pendingJobSize > MAX_PENDING_JOBS) { + enum { NUMBER_TO_DISCARD = 5 }; + + pj_enter_critical_section(); + for (unsigned i=0; ientry = entry; + utilAddPendingJob(job); + } else { + writer->write(entry); + } +} + +/* Run pending jobs only in main thread */ +void Endpoint::performPendingJobs() +{ + if (pj_thread_this() != mainThread) + return; + + if (pendingJobSize == 0) + return; + + for (;;) { + PendingJob *job = NULL; + + pj_enter_critical_section(); + if (pendingJobSize != 0) { + job = pendingJobs.front(); + pendingJobs.pop_front(); + pendingJobSize--; + } + pj_leave_critical_section(); + + if (job) { + job->execute(true); + delete job; + } else + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Endpoint static callbacks + */ +void Endpoint::logFunc(int level, const char *data, int len) +{ + Endpoint &ep = Endpoint::instance(); + + if (!ep.writer) + return; + + LogEntry entry; + entry.level = level; + entry.msg = string(data, len); + entry.threadId = (long)pj_thread_this(); + entry.threadName = string(pj_thread_get_name(pj_thread_this())); + + ep.utilLogWrite(entry); +} + +void Endpoint::stun_resolve_cb(const pj_stun_resolve_result *res) +{ + Endpoint &ep = Endpoint::instance(); + + if (!res) + return; + + OnNatCheckStunServersCompleteParam prm; + + prm.userData = res->token; + prm.status = res->status; + if (res->status == PJ_SUCCESS) { + char straddr[PJ_INET6_ADDRSTRLEN+10]; + + prm.name = string(res->name.ptr, res->name.slen); + pj_sockaddr_print(&res->addr, straddr, sizeof(straddr), 3); + prm.addr = straddr; + } + + ep.onNatCheckStunServersComplete(prm); +} + +void Endpoint::on_timer(pj_timer_heap_t *timer_heap, + pj_timer_entry *entry) +{ + PJ_UNUSED_ARG(timer_heap); + + Endpoint &ep = Endpoint::instance(); + UserTimer *ut = (UserTimer*) entry->user_data; + + if (ut->signature != TIMER_SIGNATURE) + return; + + ep.onTimer(ut->prm); +} + +void Endpoint::on_nat_detect(const pj_stun_nat_detect_result *res) +{ + Endpoint &ep = Endpoint::instance(); + + if (!res) + return; + + OnNatDetectionCompleteParam prm; + + prm.status = res->status; + prm.reason = res->status_text; + prm.natType = res->nat_type; + prm.natTypeName = res->nat_type_name; + + ep.onNatDetectionComplete(prm); +} + +void Endpoint::on_transport_state( pjsip_transport *tp, + pjsip_transport_state state, + const pjsip_transport_state_info *info) +{ + Endpoint &ep = Endpoint::instance(); + + OnTransportStateParam prm; + + prm.hnd = (TransportHandle)tp; + prm.state = state; + prm.lastError = info ? info->status : PJ_SUCCESS; + + ep.onTransportState(prm); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Account static callbacks + */ + +Account *Endpoint::lookupAcc(int acc_id, const char *op) +{ + Account *acc = Account::lookup(acc_id); + if (!acc) { + PJ_LOG(1,(THIS_FILE, + "Error: cannot find Account instance for account id %d in " + "%s", acc_id, op)); + } + + return acc; +} + +Call *Endpoint::lookupCall(int call_id, const char *op) +{ + Call *call = Call::lookup(call_id); + if (!call) { + PJ_LOG(1,(THIS_FILE, + "Error: cannot find Call instance for call id %d in " + "%s", call_id, op)); + } + + return call; +} + +void Endpoint::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, + pjsip_rx_data *rdata) +{ + Account *acc = lookupAcc(acc_id, "on_incoming_call()"); + if (!acc) { + pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL); + return; + } + + /* call callback */ + OnIncomingCallParam prm; + prm.callId = call_id; + prm.rdata.fromPj(*rdata); + + acc->onIncomingCall(prm); + + /* disconnect if callback doesn't handle the call */ + pjsua_call_info ci; + + pjsua_call_get_info(call_id, &ci); + if (!pjsua_call_get_user_data(call_id) && + ci.state != PJSIP_INV_STATE_DISCONNECTED) + { + pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL); + } +} + +void Endpoint::on_reg_started(pjsua_acc_id acc_id, pj_bool_t renew) +{ + Account *acc = lookupAcc(acc_id, "on_reg_started()"); + if (!acc) { + return; + } + + OnRegStartedParam prm; + prm.renew = PJ2BOOL(renew); + acc->onRegStarted(prm); +} + +void Endpoint::on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info) +{ + Account *acc = lookupAcc(acc_id, "on_reg_state2()"); + if (!acc) { + return; + } + + OnRegStateParam prm; + prm.status = info->cbparam->status; + prm.code = (pjsip_status_code) info->cbparam->code; + prm.reason = pj2Str(info->cbparam->reason); + if (info->cbparam->rdata) + prm.rdata.fromPj(*info->cbparam->rdata); + prm.expiration = info->cbparam->expiration; + + acc->onRegState(prm); +} + +void Endpoint::on_incoming_subscribe(pjsua_acc_id acc_id, + pjsua_srv_pres *srv_pres, + pjsua_buddy_id buddy_id, + const pj_str_t *from, + pjsip_rx_data *rdata, + pjsip_status_code *code, + pj_str_t *reason, + pjsua_msg_data *msg_data) +{ + PJ_UNUSED_ARG(buddy_id); + PJ_UNUSED_ARG(srv_pres); + + Account *acc = lookupAcc(acc_id, "on_incoming_subscribe()"); + if (!acc) { + /* default behavior should apply */ + return; + } + + OnIncomingSubscribeParam prm; + prm.srvPres = srv_pres; + prm.fromUri = pj2Str(*from); + prm.rdata.fromPj(*rdata); + prm.code = *code; + prm.reason = pj2Str(*reason); + prm.txOption.fromPj(*msg_data); + + acc->onIncomingSubscribe(prm); + + *code = prm.code; + acc->tmpReason = prm.reason; + *reason = str2Pj(acc->tmpReason); + prm.txOption.toPj(*msg_data); +} + +void Endpoint::on_pager2(pjsua_call_id call_id, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + const pj_str_t *mime_type, + const pj_str_t *body, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id) +{ + OnInstantMessageParam prm; + prm.fromUri = pj2Str(*from); + prm.toUri = pj2Str(*to); + prm.contactUri = pj2Str(*contact); + prm.contentType = pj2Str(*mime_type); + prm.msgBody = pj2Str(*body); + prm.rdata.fromPj(*rdata); + + if (call_id != PJSUA_INVALID_ID) { + Call *call = lookupCall(call_id, "on_pager2()"); + if (!call) { + /* Ignored */ + return; + } + + call->onInstantMessage(prm); + } else { + Account *acc = lookupAcc(acc_id, "on_pager2()"); + if (!acc) { + /* Ignored */ + return; + } + + acc->onInstantMessage(prm); + } +} + +void Endpoint::on_pager_status2( pjsua_call_id call_id, + const pj_str_t *to, + const pj_str_t *body, + void *user_data, + pjsip_status_code status, + const pj_str_t *reason, + pjsip_tx_data *tdata, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id) +{ + PJ_UNUSED_ARG(tdata); + + OnInstantMessageStatusParam prm; + prm.userData = user_data; + prm.toUri = pj2Str(*to); + prm.msgBody = pj2Str(*body); + prm.code = status; + prm.reason = pj2Str(*reason); + if (rdata) + prm.rdata.fromPj(*rdata); + + if (call_id != PJSUA_INVALID_ID) { + Call *call = lookupCall(call_id, "on_pager_status2()"); + if (!call) { + /* Ignored */ + return; + } + + call->onInstantMessageStatus(prm); + } else { + Account *acc = lookupAcc(acc_id, "on_pager_status2()"); + if (!acc) { + /* Ignored */ + return; + } + + acc->onInstantMessageStatus(prm); + } +} + +void Endpoint::on_typing2( pjsua_call_id call_id, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + pj_bool_t is_typing, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id) +{ + OnTypingIndicationParam prm; + prm.fromUri = pj2Str(*from); + prm.toUri = pj2Str(*to); + prm.contactUri = pj2Str(*contact); + prm.isTyping = is_typing != 0; + prm.rdata.fromPj(*rdata); + + if (call_id != PJSUA_INVALID_ID) { + Call *call = lookupCall(call_id, "on_typing2()"); + if (!call) { + /* Ignored */ + return; + } + + call->onTypingIndication(prm); + } else { + Account *acc = lookupAcc(acc_id, "on_typing2()"); + if (!acc) { + /* Ignored */ + return; + } + + acc->onTypingIndication(prm); + } +} + +void Endpoint::on_mwi_info(pjsua_acc_id acc_id, + pjsua_mwi_info *mwi_info) +{ + OnMwiInfoParam prm; + prm.state = pjsip_evsub_get_state(mwi_info->evsub); + prm.rdata.fromPj(*mwi_info->rdata); + + Account *acc = lookupAcc(acc_id, "on_mwi_info()"); + if (!acc) { + /* Ignored */ + return; + } + + acc->onMwiInfo(prm); +} + +void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id) +{ + Buddy *buddy = (Buddy*)pjsua_buddy_get_user_data(buddy_id); + if (!buddy || !buddy->isValid()) { + /* Ignored */ + return; + } + + buddy->onBuddyState(); +} + +// Call callbacks +void Endpoint::on_call_state(pjsua_call_id call_id, pjsip_event *e) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallStateParam prm; + prm.e.fromPj(*e); + + call->processStateChange(prm); + /* If the state is DISCONNECTED, call may have already been deleted + * by the application in the callback, so do not access it anymore here. + */ +} + +void Endpoint::on_call_tsx_state(pjsua_call_id call_id, + pjsip_transaction *tsx, + pjsip_event *e) +{ + PJ_UNUSED_ARG(tsx); + + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallTsxStateParam prm; + prm.e.fromPj(*e); + + call->onCallTsxState(prm); +} + +void Endpoint::on_call_media_state(pjsua_call_id call_id) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallMediaStateParam prm; + call->processMediaUpdate(prm); +} + +void Endpoint::on_call_sdp_created(pjsua_call_id call_id, + pjmedia_sdp_session *sdp, + pj_pool_t *pool, + const pjmedia_sdp_session *rem_sdp) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallSdpCreatedParam prm; + string orig_sdp; + + prm.sdp.fromPj(*sdp); + orig_sdp = prm.sdp.wholeSdp; + if (rem_sdp) + prm.remSdp.fromPj(*rem_sdp); + + call->onCallSdpCreated(prm); + + /* Check if application modifies the SDP */ + if (orig_sdp != prm.sdp.wholeSdp) { + pjmedia_sdp_parse(pool, (char*)prm.sdp.wholeSdp.c_str(), + prm.sdp.wholeSdp.size(), &sdp); + } +} + +void Endpoint::on_stream_created(pjsua_call_id call_id, + pjmedia_stream *strm, + unsigned stream_idx, + pjmedia_port **p_port) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnStreamCreatedParam prm; + prm.stream = strm; + prm.streamIdx = stream_idx; + prm.pPort = (void *)*p_port; + + call->onStreamCreated(prm); + + if (prm.pPort != (void *)*p_port) + *p_port = (pjmedia_port *)prm.pPort; +} + +void Endpoint::on_stream_destroyed(pjsua_call_id call_id, + pjmedia_stream *strm, + unsigned stream_idx) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnStreamDestroyedParam prm; + prm.stream = strm; + prm.streamIdx = stream_idx; + + call->onStreamDestroyed(prm); +} + +struct PendingOnDtmfDigitCallback : public PendingJob +{ + int call_id; + OnDtmfDigitParam prm; + + virtual void execute(bool is_pending) + { + PJ_UNUSED_ARG(is_pending); + + Call *call = Call::lookup(call_id); + if (!call) + return; + + call->onDtmfDigit(prm); + } +}; + +void Endpoint::on_dtmf_digit(pjsua_call_id call_id, int digit) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + PendingOnDtmfDigitCallback *job = new PendingOnDtmfDigitCallback; + job->call_id = call_id; + char buf[10]; + pj_ansi_sprintf(buf, "%c", digit); + job->prm.digit = (string)buf; + + Endpoint::instance().utilAddPendingJob(job); +} + +void Endpoint::on_call_transfer_request2(pjsua_call_id call_id, + const pj_str_t *dst, + pjsip_status_code *code, + pjsua_call_setting *opt) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallTransferRequestParam prm; + prm.dstUri = pj2Str(*dst); + prm.statusCode = *code; + prm.opt.fromPj(*opt); + + call->onCallTransferRequest(prm); + + *code = prm.statusCode; + *opt = prm.opt.toPj(); +} + +void Endpoint::on_call_transfer_status(pjsua_call_id call_id, + int st_code, + const pj_str_t *st_text, + pj_bool_t final, + pj_bool_t *p_cont) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallTransferStatusParam prm; + prm.statusCode = (pjsip_status_code)st_code; + prm.reason = pj2Str(*st_text); + prm.finalNotify = PJ2BOOL(final); + prm.cont = PJ2BOOL(*p_cont); + + call->onCallTransferStatus(prm); + + *p_cont = prm.cont; +} + +void Endpoint::on_call_replace_request2(pjsua_call_id call_id, + pjsip_rx_data *rdata, + int *st_code, + pj_str_t *st_text, + pjsua_call_setting *opt) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallReplaceRequestParam prm; + prm.rdata.fromPj(*rdata); + prm.statusCode = (pjsip_status_code)*st_code; + prm.reason = pj2Str(*st_text); + prm.opt.fromPj(*opt); + + call->onCallReplaceRequest(prm); + + *st_code = prm.statusCode; + *st_text = str2Pj(prm.reason); + *opt = prm.opt.toPj(); +} + +void Endpoint::on_call_replaced(pjsua_call_id old_call_id, + pjsua_call_id new_call_id) +{ + Call *call = Call::lookup(old_call_id); + if (!call) { + return; + } + + OnCallReplacedParam prm; + prm.newCallId = new_call_id; + + call->onCallReplaced(prm); +} + +void Endpoint::on_call_rx_offer(pjsua_call_id call_id, + const pjmedia_sdp_session *offer, + void *reserved, + pjsip_status_code *code, + pjsua_call_setting *opt) +{ + PJ_UNUSED_ARG(reserved); + + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + OnCallRxOfferParam prm; + prm.offer.fromPj(*offer); + prm.statusCode = *code; + prm.opt.fromPj(*opt); + + call->onCallRxOffer(prm); + + *code = prm.statusCode; + *opt = prm.opt.toPj(); +} + +pjsip_redirect_op Endpoint::on_call_redirected(pjsua_call_id call_id, + const pjsip_uri *target, + const pjsip_event *e) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return PJSIP_REDIRECT_STOP; + } + + OnCallRedirectedParam prm; + char uristr[PJSIP_MAX_URL_SIZE]; + int len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr, + sizeof(uristr)); + if (len < 1) { + pj_ansi_strcpy(uristr, "--URI too long--"); + } + prm.targetUri = string(uristr); + if (e) + prm.e.fromPj(*e); + else + prm.e.type = PJSIP_EVENT_UNKNOWN; + + return call->onCallRedirected(prm); +} + + +struct PendingOnMediaTransportCallback : public PendingJob +{ + int call_id; + OnCallMediaTransportStateParam prm; + + virtual void execute(bool is_pending) + { + PJ_UNUSED_ARG(is_pending); + + Call *call = Call::lookup(call_id); + if (!call) + return; + + call->onCallMediaTransportState(prm); + } +}; + +pj_status_t +Endpoint::on_call_media_transport_state(pjsua_call_id call_id, + const pjsua_med_tp_state_info *info) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return PJ_SUCCESS; + } + + PendingOnMediaTransportCallback *job = new PendingOnMediaTransportCallback; + + job->call_id = call_id; + job->prm.medIdx = info->med_idx; + job->prm.state = info->state; + job->prm.status = info->status; + job->prm.sipErrorCode = info->sip_err_code; + + Endpoint::instance().utilAddPendingJob(job); + + return PJ_SUCCESS; +} + +struct PendingOnMediaEventCallback : public PendingJob +{ + int call_id; + OnCallMediaEventParam prm; + + virtual void execute(bool is_pending) + { + Call *call = Call::lookup(call_id); + if (!call) + return; + + if (is_pending) { + /* Can't do this anymore, pointer is invalid */ + prm.ev.pjMediaEvent = NULL; + } + + call->onCallMediaEvent(prm); + } +}; + +void Endpoint::on_call_media_event(pjsua_call_id call_id, + unsigned med_idx, + pjmedia_event *event) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return; + } + + PendingOnMediaEventCallback *job = new PendingOnMediaEventCallback; + + job->call_id = call_id; + job->prm.medIdx = med_idx; + job->prm.ev.fromPj(*event); + + Endpoint::instance().utilAddPendingJob(job); +} + +pjmedia_transport* +Endpoint::on_create_media_transport(pjsua_call_id call_id, + unsigned media_idx, + pjmedia_transport *base_tp, + unsigned flags) +{ + Call *call = Call::lookup(call_id); + if (!call) { + return base_tp; + } + + OnCreateMediaTransportParam prm; + prm.mediaIdx = media_idx; + prm.mediaTp = base_tp; + prm.flags = flags; + + call->onCreateMediaTransport(prm); + + return (pjmedia_transport *)prm.mediaTp; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Endpoint library operations + */ +Version Endpoint::libVersion() const +{ + Version ver; + ver.major = PJ_VERSION_NUM_MAJOR; + ver.minor = PJ_VERSION_NUM_MINOR; + ver.rev = PJ_VERSION_NUM_REV; + ver.suffix = PJ_VERSION_NUM_EXTRA; + ver.full = pj_get_version(); + ver.numeric = PJ_VERSION_NUM; + return ver; +} + +void Endpoint::libCreate() throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_create() ); + mainThread = pj_thread_this(); +} + +pjsua_state Endpoint::libGetState() const +{ + return pjsua_get_state(); +} + +void Endpoint::libInit(const EpConfig &prmEpConfig) throw(Error) +{ + pjsua_config ua_cfg; + pjsua_logging_config log_cfg; + pjsua_media_config med_cfg; + + ua_cfg = prmEpConfig.uaConfig.toPj(); + log_cfg = prmEpConfig.logConfig.toPj(); + med_cfg = prmEpConfig.medConfig.toPj(); + + /* Setup log callback */ + if (prmEpConfig.logConfig.writer) { + this->writer = prmEpConfig.logConfig.writer; + log_cfg.cb = &Endpoint::logFunc; + } + mainThreadOnly = prmEpConfig.uaConfig.mainThreadOnly; + + /* Setup UA callbacks */ + pj_bzero(&ua_cfg.cb, sizeof(ua_cfg.cb)); + ua_cfg.cb.on_nat_detect = &Endpoint::on_nat_detect; + ua_cfg.cb.on_transport_state = &Endpoint::on_transport_state; + + ua_cfg.cb.on_incoming_call = &Endpoint::on_incoming_call; + ua_cfg.cb.on_reg_started = &Endpoint::on_reg_started; + ua_cfg.cb.on_reg_state2 = &Endpoint::on_reg_state2; + ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe; + ua_cfg.cb.on_pager2 = &Endpoint::on_pager2; + ua_cfg.cb.on_pager_status2 = &Endpoint::on_pager_status2; + ua_cfg.cb.on_typing2 = &Endpoint::on_typing2; + ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info; + ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state; + + /* Call callbacks */ + ua_cfg.cb.on_call_state = &Endpoint::on_call_state; + ua_cfg.cb.on_call_tsx_state = &Endpoint::on_call_tsx_state; + ua_cfg.cb.on_call_media_state = &Endpoint::on_call_media_state; + ua_cfg.cb.on_call_sdp_created = &Endpoint::on_call_sdp_created; + ua_cfg.cb.on_stream_created = &Endpoint::on_stream_created; + ua_cfg.cb.on_stream_destroyed = &Endpoint::on_stream_destroyed; + ua_cfg.cb.on_dtmf_digit = &Endpoint::on_dtmf_digit; + ua_cfg.cb.on_call_transfer_request2 = &Endpoint::on_call_transfer_request2; + ua_cfg.cb.on_call_transfer_status = &Endpoint::on_call_transfer_status; + ua_cfg.cb.on_call_replace_request2 = &Endpoint::on_call_replace_request2; + ua_cfg.cb.on_call_replaced = &Endpoint::on_call_replaced; + ua_cfg.cb.on_call_rx_offer = &Endpoint::on_call_rx_offer; + ua_cfg.cb.on_call_redirected = &Endpoint::on_call_redirected; + ua_cfg.cb.on_call_media_transport_state = + &Endpoint::on_call_media_transport_state; + ua_cfg.cb.on_call_media_event = &Endpoint::on_call_media_event; + ua_cfg.cb.on_create_media_transport = &Endpoint::on_create_media_transport; + + /* Init! */ + PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) ); +} + +void Endpoint::libStart() throw(Error) +{ + PJSUA2_CHECK_EXPR(pjsua_start()); +} + +void Endpoint::libRegisterWorkerThread(const string &name) throw(Error) +{ + PJSUA2_CHECK_EXPR(pjsua_register_worker_thread(name.c_str())); +} + +void Endpoint::libStopWorkerThreads() +{ + pjsua_stop_worker_threads(); +} + +int Endpoint::libHandleEvents(unsigned msec_timeout) +{ + performPendingJobs(); + return pjsua_handle_events(msec_timeout); +} + +void Endpoint::libDestroy(unsigned flags) throw(Error) +{ + pj_status_t status; + + status = pjsua_destroy2(flags); + + delete this->writer; + this->writer = NULL; + + if (pj_log_get_log_func() == &Endpoint::logFunc) { + pj_log_set_log_func(NULL); + } + + PJSUA2_CHECK_RAISE_ERROR(status); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Endpoint Utilities + */ +string Endpoint::utilStrError(pj_status_t prmErr) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(prmErr, errmsg, sizeof(errmsg)); + return errmsg; +} + +static void ept_log_write(int level, const char *sender, + const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(sender, level, format, arg ); + va_end(arg); +} + +void Endpoint::utilLogWrite(int prmLevel, + const string &prmSender, + const string &prmMsg) +{ + ept_log_write(prmLevel, prmSender.c_str(), "%s", prmMsg.c_str()); +} + +pj_status_t Endpoint::utilVerifySipUri(const string &prmUri) +{ + return pjsua_verify_sip_url(prmUri.c_str()); +} + +pj_status_t Endpoint::utilVerifyUri(const string &prmUri) +{ + return pjsua_verify_url(prmUri.c_str()); +} + +Token Endpoint::utilTimerSchedule(unsigned prmMsecDelay, + Token prmUserData) throw (Error) +{ + UserTimer *ut; + pj_time_val delay; + pj_status_t status; + + ut = new UserTimer; + ut->signature = TIMER_SIGNATURE; + ut->prm.msecDelay = prmMsecDelay; + ut->prm.userData = prmUserData; + pj_timer_entry_init(&ut->entry, 1, ut, &Endpoint::on_timer); + + delay.sec = 0; + delay.msec = prmMsecDelay; + pj_time_val_normalize(&delay); + + status = pjsua_schedule_timer(&ut->entry, &delay); + if (status != PJ_SUCCESS) { + delete ut; + PJSUA2_CHECK_RAISE_ERROR(status); + } + + return (Token)ut; +} + +void Endpoint::utilTimerCancel(Token prmTimerToken) +{ + UserTimer *ut = (UserTimer*)(void*)prmTimerToken; + + if (ut->signature != TIMER_SIGNATURE) { + PJ_LOG(1,(THIS_FILE, + "Invalid timer token in Endpoint::utilTimerCancel()")); + return; + } + + ut->entry.id = 0; + ut->signature = 0xFFFFFFFE; + pjsua_cancel_timer(&ut->entry); + + delete ut; +} + +IntVector Endpoint::utilSslGetAvailableCiphers() throw (Error) +{ +#if PJ_HAS_SSL_SOCK + pj_ssl_cipher ciphers[64]; + unsigned count = PJ_ARRAY_SIZE(ciphers); + + PJSUA2_CHECK_EXPR( pj_ssl_cipher_get_availables(ciphers, &count) ); + + return IntVector(ciphers, ciphers + count); +#else + return IntVector(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Endpoint NAT operations + */ +void Endpoint::natDetectType(void) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_detect_nat_type() ); +} + +pj_stun_nat_type Endpoint::natGetType() throw(Error) +{ + pj_stun_nat_type type; + + PJSUA2_CHECK_EXPR( pjsua_get_nat_type(&type) ); + + return type; +} + +void Endpoint::natCheckStunServers(const StringVector &servers, + bool wait, + Token token) throw(Error) +{ + pj_str_t srv[MAX_STUN_SERVERS]; + unsigned i, count = 0; + + for (i=0; ifromPj(pj_codec[i]); + codecInfoList.push_back(codec_info); + } + pj_leave_critical_section(); + return codecInfoList; +} + +void Endpoint::codecSetPriority(const string &codec_id, + pj_uint8_t priority) throw(Error) +{ + pj_str_t codec_str = str2Pj(codec_id); + PJSUA2_CHECK_EXPR( pjsua_codec_set_priority(&codec_str, priority) ); +} + +CodecParam Endpoint::codecGetParam(const string &codec_id) const throw(Error) +{ + pjmedia_codec_param *pj_param = NULL; + pj_str_t codec_str = str2Pj(codec_id); + + PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, pj_param) ); + + return pj_param; +} + +void Endpoint::codecSetParam(const string &codec_id, + const CodecParam param) throw(Error) +{ + pj_str_t codec_str = str2Pj(codec_id); + pjmedia_codec_param *pj_param = (pjmedia_codec_param*)param; + + PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, pj_param) ); +} + +void Endpoint::clearCodecInfoList() +{ + for (unsigned i=0;i +#include +#include +#include "util.hpp" + +#define THIS_FILE "json.cpp" + +using namespace pj; +using namespace std; + +/* Json node operations */ +static bool jsonNode_hasUnread(const ContainerNode*); +static string jsonNode_unreadName(const ContainerNode*n) + throw(Error); +static float jsonNode_readNumber(const ContainerNode*, + const string&) + throw(Error); +static bool jsonNode_readBool(const ContainerNode*, + const string&) + throw(Error); +static string jsonNode_readString(const ContainerNode*, + const string&) + throw(Error); +static StringVector jsonNode_readStringVector(const ContainerNode*, + const string&) + throw(Error); +static ContainerNode jsonNode_readContainer(const ContainerNode*, + const string &) + throw(Error); +static ContainerNode jsonNode_readArray(const ContainerNode*, + const string &) + throw(Error); +static void jsonNode_writeNumber(ContainerNode*, + const string &name, + float num) + throw(Error); +static void jsonNode_writeBool(ContainerNode*, + const string &name, + bool value) + throw(Error); +static void jsonNode_writeString(ContainerNode*, + const string &name, + const string &value) + throw(Error); +static void jsonNode_writeStringVector(ContainerNode*, + const string &name, + const StringVector &value) + throw(Error); +static ContainerNode jsonNode_writeNewContainer(ContainerNode*, + const string &name) + throw(Error); +static ContainerNode jsonNode_writeNewArray(ContainerNode*, + const string &name) + throw(Error); + +static container_node_op json_op = +{ + &jsonNode_hasUnread, + &jsonNode_unreadName, + &jsonNode_readNumber, + &jsonNode_readBool, + &jsonNode_readString, + &jsonNode_readStringVector, + &jsonNode_readContainer, + &jsonNode_readArray, + &jsonNode_writeNumber, + &jsonNode_writeBool, + &jsonNode_writeString, + &jsonNode_writeStringVector, + &jsonNode_writeNewContainer, + &jsonNode_writeNewArray +}; + +/////////////////////////////////////////////////////////////////////////////// +JsonDocument::JsonDocument() +: root(NULL) +{ + pj_caching_pool_init(&cp, NULL, 0); + pool = pj_pool_create(&cp.factory, "jsondoc", 512, 512, NULL); + if (!pool) + PJSUA2_RAISE_ERROR(PJ_ENOMEM); +} + +JsonDocument::~JsonDocument() +{ + if (pool) + pj_pool_release(pool); + pj_caching_pool_destroy(&cp); +} + +void JsonDocument::initRoot() const +{ + rootNode.op = &json_op; + rootNode.data.doc = (void*)this; + rootNode.data.data1 = (void*)root; + rootNode.data.data2 = root->value.children.next; +} + +void JsonDocument::loadFile(const string &filename) throw(Error) +{ + if (root) + PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()", + "Document already initialized"); + + if (!pj_file_exists(filename.c_str())) + PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); + + pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str()); + pj_status_t status; + + char *buffer = (char*)pj_pool_alloc(pool, size+1); + pj_oshandle_t fd = 0; + unsigned parse_size; + char err_msg[120]; + pj_json_err_info err_info; + + err_msg[0] = '\0'; + + status = pj_file_open(pool, filename.c_str(), PJ_O_RDONLY, &fd); + if (status != PJ_SUCCESS) + goto on_error; + + status = pj_file_read(fd, buffer, &size); + if (status != PJ_SUCCESS) + goto on_error; + + pj_file_close(fd); + fd = NULL; + + if (size <= 0) { + status = PJ_EEOF; + goto on_error; + } + + parse_size = (unsigned)size; + root = pj_json_parse(pool, buffer, &parse_size, &err_info); + if (root == NULL) { + pj_ansi_snprintf(err_msg, sizeof(err_msg), + "JSON parsing failed: syntax error in file '%s' at " + "line %d column %d", + filename.c_str(), err_info.line, err_info.col); + PJ_LOG(1,(THIS_FILE, err_msg)); + status = PJLIB_UTIL_EINJSON; + goto on_error; + } + + initRoot(); + return; + +on_error: + if (fd) + pj_file_close(fd); + if (err_msg[0]) + PJSUA2_RAISE_ERROR3(status, "loadFile()", err_msg); + else + PJSUA2_RAISE_ERROR(status); +} + +void JsonDocument::loadString(const string &input) throw(Error) +{ + if (root) + PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()", + "Document already initialized"); + + unsigned size = input.size(); + char *buffer = (char*)pj_pool_alloc(pool, size+1); + unsigned parse_size = (unsigned)size; + pj_json_err_info err_info; + + pj_memcpy(buffer, input.c_str(), size); + + root = pj_json_parse(pool, buffer, &parse_size, &err_info); + if (root == NULL) { + char err_msg[80]; + + pj_ansi_snprintf(err_msg, sizeof(err_msg), + "JSON parsing failed at line %d column %d", + err_info.line, err_info.col); + PJ_LOG(1,(THIS_FILE, err_msg)); + PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "loadString()", err_msg); + } + initRoot(); +} + +struct save_file_data +{ + pj_oshandle_t fd; +}; + +static pj_status_t json_file_writer(const char *s, + unsigned size, + void *user_data) +{ + save_file_data *sd = (save_file_data*)user_data; + pj_ssize_t ssize = (pj_ssize_t)size; + return pj_file_write(sd->fd, s, &ssize); +} + +void JsonDocument::saveFile(const string &filename) throw(Error) +{ + struct save_file_data sd; + pj_status_t status; + + /* Make sure root container has been created */ + getRootContainer(); + + status = pj_file_open(pool, filename.c_str(), PJ_O_WRONLY, &sd.fd); + if (status != PJ_SUCCESS) + PJSUA2_RAISE_ERROR(status); + + status = pj_json_writef(root, &json_file_writer, &sd.fd); + pj_file_close(sd.fd); + + if (status != PJ_SUCCESS) + PJSUA2_RAISE_ERROR(status); +} + +struct save_string_data +{ + string output; +}; + +static pj_status_t json_string_writer(const char *s, + unsigned size, + void *user_data) +{ + save_string_data *sd = (save_string_data*)user_data; + sd->output.append(s, size); + return PJ_SUCCESS; +} + +string JsonDocument::saveString() throw(Error) +{ + struct save_string_data sd; + pj_status_t status; + + /* Make sure root container has been created */ + getRootContainer(); + + status = pj_json_writef(root, &json_string_writer, &sd); + if (status != PJ_SUCCESS) + PJSUA2_RAISE_ERROR(status); + + return sd.output; +} + +ContainerNode & JsonDocument::getRootContainer() const +{ + if (!root) { + root = allocElement(); + pj_json_elem_obj(root, NULL); + initRoot(); + } + + return rootNode; +} + +pj_json_elem* JsonDocument::allocElement() const +{ + return (pj_json_elem*)pj_pool_alloc(pool, sizeof(pj_json_elem)); +} + +pj_pool_t *JsonDocument::getPool() +{ + return pool; +} + +/////////////////////////////////////////////////////////////////////////////// +struct json_node_data +{ + JsonDocument *doc; + pj_json_elem *jnode; + pj_json_elem *childPtr; +}; + +static bool jsonNode_hasUnread(const ContainerNode *node) +{ + json_node_data *jdat = (json_node_data*)&node->data; + return jdat->childPtr != (pj_json_elem*)&jdat->jnode->value.children; +} + +static void json_verify(struct json_node_data *jdat, + const char *op, + const string &name, + pj_json_val_type type) +{ + if (jdat->childPtr == (pj_json_elem*)&jdat->jnode->value.children) + PJSUA2_RAISE_ERROR3(PJ_EEOF, op, "No unread element"); + + /* If name is specified, then check if the names match, except + * when the node name itself is empty and the parent node is + * an array, then ignore the checking (JSON doesn't allow array + * elements to have name). + */ + if (name.size() && name.compare(0, name.size(), + jdat->childPtr->name.ptr, + jdat->childPtr->name.slen) && + jdat->childPtr->name.slen && + jdat->jnode->type != PJ_JSON_VAL_ARRAY) + { + char err_msg[80]; + pj_ansi_snprintf(err_msg, sizeof(err_msg), + "Name mismatch: expecting '%s' got '%.*s'", + name.c_str(), (int)jdat->childPtr->name.slen, + jdat->childPtr->name.ptr); + PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg); + } + + if (type != PJ_JSON_VAL_NULL && jdat->childPtr->type != type) { + char err_msg[80]; + pj_ansi_snprintf(err_msg, sizeof(err_msg), + "Type mismatch: expecting %d got %d", + type, jdat->childPtr->type); + PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg); + } +} + +static string jsonNode_unreadName(const ContainerNode *node) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "unreadName()", "", PJ_JSON_VAL_NULL); + return pj2Str(jdat->childPtr->name); +} + +static float jsonNode_readNumber(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readNumber()", name, PJ_JSON_VAL_NUMBER); + jdat->childPtr = jdat->childPtr->next; + return jdat->childPtr->prev->value.num; +} + +static bool jsonNode_readBool(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL); + jdat->childPtr = jdat->childPtr->next; + return PJ2BOOL(jdat->childPtr->prev->value.is_true); +} + +static string jsonNode_readString(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readString()", name, PJ_JSON_VAL_STRING); + jdat->childPtr = jdat->childPtr->next; + return pj2Str(jdat->childPtr->prev->value.str); +} + +static StringVector jsonNode_readStringVector(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readStringVector()", name, PJ_JSON_VAL_ARRAY); + + StringVector result; + pj_json_elem *child = jdat->childPtr->value.children.next; + while (child != (pj_json_elem*)&jdat->childPtr->value.children) { + if (child->type != PJ_JSON_VAL_STRING) { + char err_msg[80]; + pj_ansi_snprintf(err_msg, sizeof(err_msg), + "Elements not string but type %d", + child->type); + PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "readStringVector()", + err_msg); + } + result.push_back(pj2Str(child->value.str)); + child = child->next; + } + + jdat->childPtr = jdat->childPtr->next; + return result; +} + +static ContainerNode jsonNode_readContainer(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readContainer()", name, PJ_JSON_VAL_OBJ); + + ContainerNode json_node; + + json_node.op = &json_op; + json_node.data.doc = (void*)jdat->doc; + json_node.data.data1 = (void*)jdat->childPtr; + json_node.data.data2 = (void*)jdat->childPtr->value.children.next; + + jdat->childPtr = jdat->childPtr->next; + return json_node; +} + +static ContainerNode jsonNode_readArray(const ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + json_verify(jdat, "readArray()", name, PJ_JSON_VAL_ARRAY); + + ContainerNode json_node; + + json_node.op = &json_op; + json_node.data.doc = (void*)jdat->doc; + json_node.data.data1 = (void*)jdat->childPtr; + json_node.data.data2 = (void*)jdat->childPtr->value.children.next; + + jdat->childPtr = jdat->childPtr->next; + return json_node; +} + +static pj_str_t alloc_name(JsonDocument *doc, const string &name) +{ + pj_str_t new_name; + pj_strdup2(doc->getPool(), &new_name, name.c_str()); + return new_name; +} + +static void jsonNode_writeNumber(ContainerNode *node, + const string &name, + float num) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + pj_json_elem_number(el, &nm, num); + pj_json_elem_add(jdat->jnode, el); +} + +static void jsonNode_writeBool(ContainerNode *node, + const string &name, + bool value) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + pj_json_elem_bool(el, &nm, value); + pj_json_elem_add(jdat->jnode, el); +} + +static void jsonNode_writeString(ContainerNode *node, + const string &name, + const string &value) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + pj_str_t new_val; + pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str()); + pj_json_elem_string(el, &nm, &new_val); + + pj_json_elem_add(jdat->jnode, el); +} + +static void jsonNode_writeStringVector(ContainerNode *node, + const string &name, + const StringVector &value) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + + pj_json_elem_array(el, &nm); + for (unsigned i=0; idoc->getPool(), &new_val, value[i].c_str()); + pj_json_elem *child = jdat->doc->allocElement(); + pj_json_elem_string(child, NULL, &new_val); + pj_json_elem_add(el, child); + } + + pj_json_elem_add(jdat->jnode, el); +} + +static ContainerNode jsonNode_writeNewContainer(ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + + pj_json_elem_obj(el, &nm); + pj_json_elem_add(jdat->jnode, el); + + ContainerNode json_node; + + json_node.op = &json_op; + json_node.data.doc = (void*)jdat->doc; + json_node.data.data1 = (void*)el; + json_node.data.data2 = (void*)el->value.children.next; + + return json_node; +} + +static ContainerNode jsonNode_writeNewArray(ContainerNode *node, + const string &name) + throw(Error) +{ + json_node_data *jdat = (json_node_data*)&node->data; + pj_json_elem *el = jdat->doc->allocElement(); + pj_str_t nm = alloc_name(jdat->doc, name); + + pj_json_elem_array(el, &nm); + pj_json_elem_add(jdat->jnode, el); + + ContainerNode json_node; + + json_node.op = &json_op; + json_node.data.doc = (void*)jdat->doc; + json_node.data.data1 = (void*)el; + json_node.data.data2 = (void*)el->value.children.next; + + return json_node; +} diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp new file mode 100644 index 00000000..175d5987 --- /dev/null +++ b/pjsip/src/pjsua2/media.cpp @@ -0,0 +1,779 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "media.cpp" +#define MAX_FILE_NAMES 64 +#define MAX_DEV_COUNT 64 + +/////////////////////////////////////////////////////////////////////////////// +void MediaFormatAudio::fromPj(const pjmedia_format &format) +{ + if ((format.type != PJMEDIA_TYPE_AUDIO) && + (format.detail_type != PJMEDIA_FORMAT_DETAIL_AUDIO)) + { + type = PJMEDIA_TYPE_UNKNOWN; + return; + } + + id = format.id; + type = format.type; + + /* Detail. */ + clockRate = format.det.aud.clock_rate; + channelCount = format.det.aud.channel_count; + frameTimeUsec = format.det.aud.frame_time_usec; + bitsPerSample = format.det.aud.bits_per_sample; + avgBps = format.det.aud.avg_bps; + maxBps = format.det.aud.max_bps; +} + +pjmedia_format MediaFormatAudio::toPj() const +{ + pjmedia_format pj_format; + + pj_format.id = id; + pj_format.type = type; + + pj_format.detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO; + pj_format.det.aud.clock_rate = clockRate; + pj_format.det.aud.channel_count = channelCount; + pj_format.det.aud.frame_time_usec = frameTimeUsec; + pj_format.det.aud.bits_per_sample = bitsPerSample; + pj_format.det.aud.avg_bps = avgBps; + pj_format.det.aud.max_bps = maxBps; + + return pj_format; +} + +/////////////////////////////////////////////////////////////////////////////// +/* Audio Media operations. */ +void ConfPortInfo::fromPj(const pjsua_conf_port_info &port_info) +{ + portId = port_info.slot_id; + name = pj2Str(port_info.name); + format.fromPj(port_info.format); + txLevelAdj = port_info.tx_level_adj; + rxLevelAdj = port_info.rx_level_adj; + + /* + format.id = PJMEDIA_FORMAT_PCM; + format.type = PJMEDIA_TYPE_AUDIO; + format.clockRate = port_info.clock_rate; + format.channelCount = port_info.channel_count; + format.bitsPerSample = port_info.bits_per_sample; + format.frameTimeUsec = (port_info.samples_per_frame * + 1000000) / + (port_info.clock_rate * + port_info.channel_count); + + format.avgBps = format.maxBps = port_info.clock_rate * + port_info.channel_count * + port_info.bits_per_sample; + */ + listeners.clear(); + for (unsigned i=0; i(media); +} + +/////////////////////////////////////////////////////////////////////////////// + +AudioMediaPlayer::AudioMediaPlayer() +: playerId(PJSUA_INVALID_ID) +{ + +} + +AudioMediaPlayer::~AudioMediaPlayer() +{ + if (playerId != PJSUA_INVALID_ID) + pjsua_player_destroy(playerId); +} + +void AudioMediaPlayer::createPlayer(const string &file_name, + unsigned options) + throw(Error) +{ + pj_str_t pj_name = str2Pj(file_name); + + PJSUA2_CHECK_EXPR( pjsua_player_create(&pj_name, + options, + &playerId) ); + + /* Get media port id. */ + id = pjsua_player_get_conf_port(playerId); + + registerMediaPort(NULL); +} + +void AudioMediaPlayer::createPlaylist(const StringVector &file_names, + const string &label, + unsigned options) + throw(Error) +{ + pj_str_t pj_files[MAX_FILE_NAMES]; + unsigned i, count = 0; + pj_str_t pj_lbl = str2Pj(label); + + count = PJ_ARRAY_SIZE(pj_files); + + for(i=0; i(media); +} + +/////////////////////////////////////////////////////////////////////////////// +AudioMediaRecorder::AudioMediaRecorder() +: recorderId(PJSUA_INVALID_ID) +{ + +} + +AudioMediaRecorder::~AudioMediaRecorder() +{ + if (recorderId != PJSUA_INVALID_ID) + pjsua_recorder_destroy(recorderId); +} + +void AudioMediaRecorder::createRecorder(const string &file_name, + unsigned enc_type, + pj_ssize_t max_size, + unsigned options) + throw(Error) +{ + PJ_UNUSED_ARG(max_size); + + pj_str_t pj_name = str2Pj(file_name); + + PJSUA2_CHECK_EXPR( pjsua_recorder_create(&pj_name, + enc_type, + NULL, + -1, + options, + &recorderId) ); + + /* Get media port id. */ + id = pjsua_recorder_get_conf_port(recorderId); + + registerMediaPort(NULL); +} + +AudioMediaRecorder* AudioMediaRecorder::typecastFromAudioMedia( + AudioMedia *media) +{ + return static_cast(media); +} + +/////////////////////////////////////////////////////////////////////////////// +void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info) +{ + name = dev_info.name; + inputCount = dev_info.input_count; + outputCount = dev_info.output_count; + defaultSamplesPerSec = dev_info.default_samples_per_sec; + driver = dev_info.driver; + caps = dev_info.caps; + routes = dev_info.routes; + + for (unsigned i=0; ifromPj(dev_info.ext_fmt[i]); + if (format->type == PJMEDIA_TYPE_AUDIO) + extFmt.push_back(format); + } +} + +AudioDevInfo::~AudioDevInfo() +{ + for(unsigned i=0;iid = 0; + registerMediaPort(NULL); +} + +DevAudioMedia::~DevAudioMedia() +{ + /* Avoid removing this port (conf port id=0) from conference */ + this->id = PJSUA_INVALID_ID; +} + +/////////////////////////////////////////////////////////////////////////////// +/* Audio device operations. */ + +AudDevManager::AudDevManager() +: devMedia(NULL) +{ +} + +AudDevManager::~AudDevManager() +{ + // At this point, devMedia should have been cleaned up by Endpoint, + // as AudDevManager destructor is called after Endpoint destructor. + //delete devMedia; + + clearAudioDevList(); +} + +int AudDevManager::getCaptureDev() const throw(Error) +{ + return getActiveDev(true); +} + +AudioMedia &AudDevManager::getCaptureDevMedia() throw(Error) +{ + if (!devMedia) + devMedia = new DevAudioMedia; + return *devMedia; +} + +int AudDevManager::getPlaybackDev() const throw(Error) +{ + return getActiveDev(false); +} + +AudioMedia &AudDevManager::getPlaybackDevMedia() throw(Error) +{ + if (!devMedia) + devMedia = new DevAudioMedia; + return *devMedia; +} + +void AudDevManager::setCaptureDev(int capture_dev) const throw(Error) +{ + int playback_dev = getPlaybackDev(); + + PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) ); +} + +void AudDevManager::setPlaybackDev(int playback_dev) const throw(Error) +{ + int capture_dev = getCaptureDev(); + + PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) ); +} + +const AudioDevInfoVector &AudDevManager::enumDev() throw(Error) +{ + pjmedia_aud_dev_info pj_info[MAX_DEV_COUNT]; + unsigned count; + + PJSUA2_CHECK_EXPR( pjsua_enum_aud_devs(pj_info, &count) ); + + pj_enter_critical_section(); + clearAudioDevList(); + for (unsigned i = 0; (ifromPj(pj_info[i]); + audioDevList.push_back(dev_info); + } + pj_leave_critical_section(); + return audioDevList; +} + +void AudDevManager::setNullDev() throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_set_null_snd_dev() ); +} + +MediaPort *AudDevManager::setNoDev() +{ + return (MediaPort*)pjsua_set_no_snd_dev(); +} + +void AudDevManager::setEcOptions(unsigned tail_msec, + unsigned options) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_set_ec(tail_msec, options) ); +} + +unsigned AudDevManager::getEcTail() const throw(Error) +{ + unsigned tail_msec = 0; + + PJSUA2_CHECK_EXPR( pjsua_get_ec_tail(&tail_msec) ); + + return tail_msec; +} + +bool AudDevManager::sndIsActive() const +{ + return PJ2BOOL(pjsua_snd_is_active()); +} + +void AudDevManager::refreshDevs() throw(Error) +{ + PJSUA2_CHECK_EXPR( pjmedia_aud_dev_refresh() ); +} + +unsigned AudDevManager::getDevCount() const +{ + return pjmedia_aud_dev_count(); +} + +AudioDevInfo +AudDevManager::getDevInfo(int id) const throw(Error) +{ + AudioDevInfo dev_info; + pjmedia_aud_dev_info pj_info; + + PJSUA2_CHECK_EXPR( pjmedia_aud_dev_get_info(id, &pj_info) ); + + dev_info.fromPj(pj_info); + return dev_info; +} + +int AudDevManager::lookupDev(const string &drv_name, + const string &dev_name) const throw(Error) +{ + pjmedia_aud_dev_index pj_idx = 0; + + PJSUA2_CHECK_EXPR( pjmedia_aud_dev_lookup(drv_name.c_str(), + dev_name.c_str(), + &pj_idx) ); + + return pj_idx; +} + + +string AudDevManager::capName(pjmedia_aud_dev_cap cap) const +{ + return pjmedia_aud_dev_cap_name(cap, NULL); +} + +void +AudDevManager::setExtFormat(const MediaFormatAudio &format, + bool keep) throw(Error) +{ + pjmedia_format pj_format = format.toPj(); + + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT, + &pj_format, + keep) ); +} + +MediaFormatAudio AudDevManager::getExtFormat() const throw(Error) +{ + pjmedia_format pj_format; + MediaFormatAudio format; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT, + &pj_format) ); + + format.fromPj(pj_format); + + return format; +} + +void AudDevManager::setInputLatency(unsigned latency_msec, + bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, + &latency_msec, + keep) ); +} + +unsigned AudDevManager::getInputLatency() const throw(Error) +{ + unsigned latency_msec = 0; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, + &latency_msec) ); + + return latency_msec; +} + +void +AudDevManager::setOutputLatency(unsigned latency_msec, + bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, + &latency_msec, + keep) ); +} + +unsigned AudDevManager::getOutputLatency() const throw(Error) +{ + unsigned latency_msec = 0; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, + &latency_msec) ); + + return latency_msec; +} + +void AudDevManager::setInputVolume(unsigned volume, bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( + pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, + &volume, + keep) ); +} + +unsigned AudDevManager::getInputVolume() const throw(Error) +{ + unsigned volume = 0; + + PJSUA2_CHECK_EXPR( + pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, + &volume) ); + + return volume; +} + +void AudDevManager::setOutputVolume(unsigned volume, bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( + pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, + &volume, + keep) ); +} + +unsigned AudDevManager::getOutputVolume() const throw(Error) +{ + unsigned volume = 0; + + PJSUA2_CHECK_EXPR( + pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, + &volume) ); + + return volume; +} + +unsigned AudDevManager::getInputSignal() const throw(Error) +{ + unsigned signal = 0; + + PJSUA2_CHECK_EXPR( + pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER, + &signal) ); + + return signal; +} + +unsigned AudDevManager::getOutputSignal() const throw(Error) +{ + unsigned signal = 0; + + PJSUA2_CHECK_EXPR( + pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER, + &signal) ); + + return signal; +} + +void +AudDevManager::setInputRoute(pjmedia_aud_dev_route route, + bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, + &route, + keep) ); +} + +pjmedia_aud_dev_route AudDevManager::getInputRoute() const throw(Error) +{ + pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, + &route) ); + + return route; +} + +void +AudDevManager::setOutputRoute(pjmedia_aud_dev_route route, + bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, + &route, + keep) ); +} + +pjmedia_aud_dev_route AudDevManager::getOutputRoute() const throw(Error) +{ + pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, + &route) ); + + return route; +} + +void AudDevManager::setVad(bool enable, bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_VAD, + &enable, + keep) ); +} + +bool AudDevManager::getVad() const throw(Error) +{ + bool enable = false; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_VAD, + &enable) ); + + return enable; +} + +void AudDevManager::setCng(bool enable, bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_CNG, + &enable, + keep) ); +} + +bool AudDevManager::getCng() const throw(Error) +{ + bool enable = false; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_CNG, + &enable) ); + + return enable; +} + +void AudDevManager::setPlc(bool enable, bool keep) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_PLC, + &enable, + keep) ); +} + +bool AudDevManager::getPlc() const throw(Error) +{ + bool enable = false; + + PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_PLC, + &enable) ); + + return enable; +} + +void AudDevManager::clearAudioDevList() +{ + for(unsigned i=0;i + +using namespace pj; +using namespace std; + + +bool PersistentDocument::hasUnread() const +{ + return getRootContainer().hasUnread(); +} + +string PersistentDocument::unreadName() const throw(Error) +{ + return getRootContainer().unreadName(); +} + +int PersistentDocument::readInt(const string &name) const throw(Error) +{ + return (int)getRootContainer().readNumber(name); +} + +float PersistentDocument::readNumber(const string &name) const throw(Error) +{ + return getRootContainer().readNumber(name); +} + +bool PersistentDocument::readBool(const string &name) const throw(Error) +{ + return getRootContainer().readBool(name); +} + +string PersistentDocument::readString(const string &name) const throw(Error) +{ + return getRootContainer().readString(name); +} + +StringVector PersistentDocument::readStringVector(const string &name) const + throw(Error) +{ + return getRootContainer().readStringVector(name); +} + +void PersistentDocument::readObject(PersistentObject &obj) const throw(Error) +{ + getRootContainer().readObject(obj); +} + +ContainerNode PersistentDocument::readContainer(const string &name) const + throw(Error) +{ + return getRootContainer().readContainer(name); +} + +ContainerNode PersistentDocument::readArray(const string &name) const + throw(Error) +{ + return getRootContainer().readArray(name); +} + +void PersistentDocument::writeNumber(const string &name, + float num) throw(Error) +{ + getRootContainer().writeNumber(name, num); +} + +void PersistentDocument::writeInt(const string &name, + int num) throw(Error) +{ + getRootContainer().writeNumber(name, (float)num); +} + +void PersistentDocument::writeBool(const string &name, + bool value) throw(Error) +{ + getRootContainer().writeBool(name, value); +} + +void PersistentDocument::writeString(const string &name, + const string &value) throw(Error) +{ + getRootContainer().writeString(name, value); +} + +void PersistentDocument::writeStringVector(const string &name, + const StringVector &value) + throw(Error) +{ + getRootContainer().writeStringVector(name, value); +} + +void PersistentDocument::writeObject(const PersistentObject &obj) throw(Error) +{ + getRootContainer().writeObject(obj); +} + +ContainerNode PersistentDocument::writeNewContainer(const string &name) + throw(Error) +{ + return getRootContainer().writeNewContainer(name); +} + +ContainerNode PersistentDocument::writeNewArray(const string &name) + throw(Error) +{ + return getRootContainer().writeNewArray(name); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ContainerNode::hasUnread() const +{ + return op->hasUnread(this); +} + +string ContainerNode::unreadName() const throw(Error) +{ + return op->unreadName(this); +} + +int ContainerNode::readInt(const string &name) const throw(Error) +{ + return (int)op->readNumber(this, name); +} + +float ContainerNode::readNumber(const string &name) const throw(Error) +{ + return op->readNumber(this, name); +} + +bool ContainerNode::readBool(const string &name) const throw(Error) +{ + return op->readBool(this, name); +} + +string ContainerNode::readString(const string &name) const throw(Error) +{ + return op->readString(this, name); +} + +StringVector ContainerNode::readStringVector(const string &name) const + throw(Error) +{ + return op->readStringVector(this, name); +} + +void ContainerNode::readObject(PersistentObject &obj) const throw(Error) +{ + obj.readObject(*this); +} + +ContainerNode ContainerNode::readContainer(const string &name) const + throw(Error) +{ + return op->readContainer(this, name); +} + +ContainerNode ContainerNode::readArray(const string &name) const + throw(Error) +{ + return op->readArray(this, name); +} + +void ContainerNode::writeNumber(const string &name, + float num) throw(Error) +{ + return op->writeNumber(this, name, num); +} + +void ContainerNode::writeInt(const string &name, + int num) throw(Error) +{ + return op->writeNumber(this, name, (float)num); +} + +void ContainerNode::writeBool(const string &name, + bool value) throw(Error) +{ + return op->writeBool(this, name, value); +} + +void ContainerNode::writeString(const string &name, + const string &value) throw(Error) +{ + return op->writeString(this, name, value); +} + +void ContainerNode::writeStringVector(const string &name, + const StringVector &value) + throw(Error) +{ + return op->writeStringVector(this, name, value); +} + +void ContainerNode::writeObject(const PersistentObject &obj) throw(Error) +{ + obj.writeObject(*this); +} + +ContainerNode ContainerNode::writeNewContainer(const string &name) + throw(Error) +{ + return op->writeNewContainer(this, name); +} + +ContainerNode ContainerNode::writeNewArray(const string &name) + throw(Error) +{ + return op->writeNewArray(this, name); +} diff --git a/pjsip/src/pjsua2/presence.cpp b/pjsip/src/pjsua2/presence.cpp new file mode 100644 index 00000000..205a85ee --- /dev/null +++ b/pjsip/src/pjsua2/presence.cpp @@ -0,0 +1,190 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "presence.cpp" + + +/////////////////////////////////////////////////////////////////////////////// + +PresenceStatus::PresenceStatus() +: status(PJSUA_BUDDY_STATUS_UNKNOWN), activity(PJRPID_ACTIVITY_UNKNOWN) +{ +} + + +/////////////////////////////////////////////////////////////////////////////// + +void BuddyConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("BuddyConfig"); + + NODE_READ_STRING ( this_node, uri); + NODE_READ_BOOL ( this_node, subscribe); +} + +void BuddyConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("BuddyConfig"); + + NODE_WRITE_STRING ( this_node, uri); + NODE_WRITE_BOOL ( this_node, subscribe); +} + +////////////////////////////////////////////////////////////////////////////// + +void BuddyInfo::fromPj(const pjsua_buddy_info &pbi) +{ + uri = pj2Str(pbi.uri); + contact = pj2Str(pbi.contact); + presMonitorEnabled = PJ2BOOL(pbi.monitor_pres); + subState = pbi.sub_state; + subStateName = string(pbi.sub_state_name); + subTermCode = (pjsip_status_code)pbi.sub_term_code; + subTermReason = pj2Str(pbi.sub_term_reason); + + /* Presence status */ + presStatus.status = pbi.status; + presStatus.statusText = pj2Str(pbi.status_text); + presStatus.activity = pbi.rpid.activity; + presStatus.note = pj2Str(pbi.rpid.note); + presStatus.rpidId = pj2Str(pbi.rpid.id); +} + +////////////////////////////////////////////////////////////////////////////// + +/* + * Constructor. + */ +Buddy::Buddy() +: id(PJSUA_INVALID_ID) +{ +} + +/* + * Destructor. + */ +Buddy::~Buddy() +{ + if (isValid()) { + pjsua_buddy_set_user_data(id, NULL); + pjsua_buddy_del(id); + + /* Remove from account buddy list */ + acc->removeBuddy(this); + } +} + +/* + * Create buddy and register the buddy to PJSUA-LIB. + */ +void Buddy::create(Account &account, const BuddyConfig &cfg) throw(Error) +{ + pjsua_buddy_config pj_cfg; + pjsua_buddy_config_default(&pj_cfg); + + if (!account.isValid()) + PJSUA2_RAISE_ERROR3(PJ_EINVAL, "Buddy::create()", "Invalid account"); + + pj_cfg.uri = str2Pj(cfg.uri); + pj_cfg.subscribe = cfg.subscribe; + pj_cfg.user_data = (void*)this; + PJSUA2_CHECK_EXPR( pjsua_buddy_add(&pj_cfg, &id) ); + + acc = &account; + acc->addBuddy(this); +} + +/* + * Check if this buddy is valid. + */ +bool Buddy::isValid() const +{ + return PJ2BOOL( pjsua_buddy_is_valid(id) ); +} + +/* + * Get detailed buddy info. + */ +BuddyInfo Buddy::getInfo() const throw(Error) +{ + pjsua_buddy_info pj_bi; + BuddyInfo bi; + + PJSUA2_CHECK_EXPR( pjsua_buddy_get_info(id, &pj_bi) ); + bi.fromPj(pj_bi); + return bi; +} + +/* + * Enable/disable buddy's presence monitoring. + */ +void Buddy::subscribePresence(bool subscribe) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_buddy_subscribe_pres(id, subscribe) ); +} + + +/* + * Update the presence information for the buddy. + */ +void Buddy::updatePresence(void) throw(Error) +{ + PJSUA2_CHECK_EXPR( pjsua_buddy_update_pres(id) ); +} + +/* + * Send instant messaging outside dialog. + */ +void Buddy::sendInstantMessage(const SendInstantMessageParam &prm) throw(Error) +{ + BuddyInfo bi = getInfo(); + + pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact); + pj_str_t mime_type = str2Pj(prm.contentType); + pj_str_t content = str2Pj(prm.content); + void *user_data = (void*)prm.userData; + pjsua_msg_data msg_data; + prm.txOption.toPj(msg_data); + + PJSUA2_CHECK_EXPR( pjsua_im_send(acc->getId(), &to, &mime_type, &content, + &msg_data, user_data) ); +} + +/* + * Send typing indication outside dialog. + */ +void Buddy::sendTypingIndication(const SendTypingIndicationParam &prm) + throw(Error) +{ + BuddyInfo bi = getInfo(); + + pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact); + pjsua_msg_data msg_data; + prm.txOption.toPj(msg_data); + + PJSUA2_CHECK_EXPR( pjsua_im_typing(acc->getId(), &to, prm.isTyping, + &msg_data) ); +} + diff --git a/pjsip/src/pjsua2/siptypes.cpp b/pjsip/src/pjsua2/siptypes.cpp new file mode 100644 index 00000000..f1d8bf2b --- /dev/null +++ b/pjsip/src/pjsua2/siptypes.cpp @@ -0,0 +1,590 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "siptypes.cpp" + +/////////////////////////////////////////////////////////////////////////////// +namespace pj +{ +void readIntVector( ContainerNode &node, + const string &array_name, + IntVector &v) throw(Error) +{ + ContainerNode array_node = node.readArray(array_name); + v.resize(0); + while (array_node.hasUnread()) { + v.push_back((int)array_node.readNumber()); + } +} + +void writeIntVector(ContainerNode &node, + const string &array_name, + const IntVector &v) throw(Error) +{ + ContainerNode array_node = node.writeNewArray(array_name); + for (unsigned i=0; ifromPj(ts); +} + +pjsip_tls_setting TlsConfig::toPj() const +{ + pjsip_tls_setting ts; + + ts.ca_list_file = str2Pj(this->CaListFile); + ts.cert_file = str2Pj(this->certFile); + ts.privkey_file = str2Pj(this->privKeyFile); + ts.password = str2Pj(this->password); + ts.method = this->method; + ts.ciphers_num = this->ciphers.size(); + // The following will only work if sizeof(enum)==sizeof(int) + pj_assert(sizeof(ts.ciphers[0]) == sizeof(int)); + ts.ciphers = ts.ciphers_num? + (pj_ssl_cipher*)&this->ciphers[0] : NULL; + ts.verify_server = this->verifyServer; + ts.verify_client = this->verifyClient; + ts.require_client_cert = this->requireClientCert; + ts.timeout.sec = this->msecTimeout / 1000; + ts.timeout.msec = this->msecTimeout % 1000; + ts.qos_type = this->qosType; + ts.qos_params = this->qosParams; + ts.qos_ignore_error = this->qosIgnoreError; + + return ts; +} + +void TlsConfig::fromPj(const pjsip_tls_setting &prm) +{ + this->CaListFile = pj2Str(prm.ca_list_file); + this->certFile = pj2Str(prm.cert_file); + this->privKeyFile = pj2Str(prm.privkey_file); + this->password = pj2Str(prm.password); + this->method = (pjsip_ssl_method)prm.method; + // The following will only work if sizeof(enum)==sizeof(int) + pj_assert(sizeof(prm.ciphers[0]) == sizeof(int)); + this->ciphers = IntVector(prm.ciphers, prm.ciphers+prm.ciphers_num); + this->verifyServer = PJ2BOOL(prm.verify_server); + this->verifyClient = PJ2BOOL(prm.verify_client); + this->requireClientCert = PJ2BOOL(prm.require_client_cert); + this->msecTimeout = PJ_TIME_VAL_MSEC(prm.timeout); + this->qosType = prm.qos_type; + this->qosParams = prm.qos_params; + this->qosIgnoreError = PJ2BOOL(prm.qos_ignore_error); +} + +void TlsConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("TlsConfig"); + + NODE_READ_STRING ( this_node, CaListFile); + NODE_READ_STRING ( this_node, certFile); + NODE_READ_STRING ( this_node, privKeyFile); + NODE_READ_STRING ( this_node, password); + NODE_READ_NUM_T ( this_node, pjsip_ssl_method, method); + readIntVector ( this_node, "ciphers", ciphers); + NODE_READ_BOOL ( this_node, verifyServer); + NODE_READ_BOOL ( this_node, verifyClient); + NODE_READ_BOOL ( this_node, requireClientCert); + NODE_READ_UNSIGNED( this_node, msecTimeout); + NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); + readQosParams ( this_node, qosParams); + NODE_READ_BOOL ( this_node, qosIgnoreError); +} + +void TlsConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("TlsConfig"); + + NODE_WRITE_STRING ( this_node, CaListFile); + NODE_WRITE_STRING ( this_node, certFile); + NODE_WRITE_STRING ( this_node, privKeyFile); + NODE_WRITE_STRING ( this_node, password); + NODE_WRITE_NUM_T ( this_node, pjsip_ssl_method, method); + writeIntVector ( this_node, "ciphers", ciphers); + NODE_WRITE_BOOL ( this_node, verifyServer); + NODE_WRITE_BOOL ( this_node, verifyClient); + NODE_WRITE_BOOL ( this_node, requireClientCert); + NODE_WRITE_UNSIGNED( this_node, msecTimeout); + NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); + writeQosParams ( this_node, qosParams); + NODE_WRITE_BOOL ( this_node, qosIgnoreError); +} + +/////////////////////////////////////////////////////////////////////////////// + +TransportConfig::TransportConfig() +{ + pjsua_transport_config tc; + pjsua_transport_config_default(&tc); + this->fromPj(tc); +} + +void TransportConfig::fromPj(const pjsua_transport_config &prm) +{ + this->port = prm.port; + this->portRange = prm.port_range; + this->publicAddress = pj2Str(prm.public_addr); + this->boundAddress = pj2Str(prm.bound_addr); + this->tlsConfig.fromPj(prm.tls_setting); + this->qosType = prm.qos_type; + this->qosParams = prm.qos_params; +} + +pjsua_transport_config TransportConfig::toPj() const +{ + pjsua_transport_config tc; + + tc.port = this->port; + tc.port_range = this->portRange; + tc.public_addr = str2Pj(this->publicAddress); + tc.bound_addr = str2Pj(this->boundAddress); + tc.tls_setting = this->tlsConfig.toPj(); + tc.qos_type = this->qosType; + tc.qos_params = this->qosParams; + + return tc; +} + +void TransportConfig::readObject(const ContainerNode &node) throw(Error) +{ + ContainerNode this_node = node.readContainer("TransportConfig"); + + NODE_READ_UNSIGNED ( this_node, port); + NODE_READ_UNSIGNED ( this_node, portRange); + NODE_READ_STRING ( this_node, publicAddress); + NODE_READ_STRING ( this_node, boundAddress); + NODE_READ_NUM_T ( this_node, pj_qos_type, qosType); + readQosParams ( this_node, qosParams); + NODE_READ_OBJ ( this_node, tlsConfig); +} + +void TransportConfig::writeObject(ContainerNode &node) const throw(Error) +{ + ContainerNode this_node = node.writeNewContainer("TransportConfig"); + + NODE_WRITE_UNSIGNED ( this_node, port); + NODE_WRITE_UNSIGNED ( this_node, portRange); + NODE_WRITE_STRING ( this_node, publicAddress); + NODE_WRITE_STRING ( this_node, boundAddress); + NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType); + writeQosParams ( this_node, qosParams); + NODE_WRITE_OBJ ( this_node, tlsConfig); +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransportInfo::fromPj(const pjsua_transport_info &info) +{ + this->id = info.id; + this->type = info.type; + this->typeName = pj2Str(info.type_name); + this->info = pj2Str(info.info); + this->flags = info.flag; + + char straddr[PJ_INET6_ADDRSTRLEN+10]; + pj_sockaddr_print(&info.local_addr, straddr, sizeof(straddr), 3); + this->localAddress = straddr; + + pj_ansi_snprintf(straddr, sizeof(straddr), "%.*s:%d", + (int)info.local_name.host.slen, + info.local_name.host.ptr, + info.local_name.port); + this->localName = straddr; + this->usageCount = info.usage_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +SipRxData::SipRxData() +: pjRxData(NULL) +{ +} + +void SipRxData::fromPj(pjsip_rx_data &rdata) +{ + char straddr[PJ_INET6_ADDRSTRLEN+10]; + + info = pjsip_rx_data_get_info(&rdata); + wholeMsg = string(rdata.msg_info.msg_buf, rdata.msg_info.len); + pj_sockaddr_print(&rdata.pkt_info.src_addr, straddr, sizeof(straddr), 3); + srcAddress = straddr; + pjRxData = (void *)&rdata; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SipMediaType::fromPj(const pjsip_media_type &prm) +{ + type = pj2Str(prm.type); + subType = pj2Str(prm.subtype); +} + +pjsip_media_type SipMediaType::toPj() const +{ + pjsip_media_type pj_mt; + pj_bzero(&pj_mt, sizeof(pj_mt)); + pj_mt.type = str2Pj(type); + pj_mt.subtype = str2Pj(subType); + return pj_mt; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SipHeader::fromPj(const pjsip_hdr *hdr) throw(Error) +{ + char buf[256]; + + int len = pjsip_hdr_print_on((void*)hdr, buf, sizeof(buf)-1); + if (len <= 0) + PJSUA2_RAISE_ERROR(PJ_ETOOSMALL); + buf[len] = '\0'; + + char *pos = strchr(buf, ':'); + if (!pos) + PJSUA2_RAISE_ERROR(PJSIP_EINVALIDHDR); + + // Trim white space after header name + char *end_name = pos; + while (end_name>buf && pj_isspace(*(end_name-1))) --end_name; + + // Trim whitespaces after colon + char *start_val = pos+1; + while (*start_val && pj_isspace(*start_val)) ++start_val; + + hName = string(buf, end_name); + hValue = string(start_val); +} + +pjsip_generic_string_hdr &SipHeader::toPj() const +{ + pj_str_t hname = str2Pj(hName); + pj_str_t hvalue = str2Pj(hValue); + + pjsip_generic_string_hdr_init2(&pjHdr, &hname, &hvalue); + return pjHdr; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SipMultipartPart::fromPj(const pjsip_multipart_part &prm) throw(Error) +{ + headers.clear(); + pjsip_hdr* pj_hdr = prm.hdr.next; + while (pj_hdr != &prm.hdr) { + SipHeader sh; + sh.fromPj(pj_hdr); + headers.push_back(sh); + pj_hdr = pj_hdr->next; + } + + if (!prm.body) + PJSUA2_RAISE_ERROR(PJ_EINVAL); + + contentType.fromPj(prm.body->content_type); + body = string((char*)prm.body->data, prm.body->len); +} + +pjsip_multipart_part& SipMultipartPart::toPj() const +{ + pj_list_init(&pjMpp.hdr); + for (unsigned i = 0; i < headers.size(); i++) { + pjsip_generic_string_hdr& pj_hdr = headers[i].toPj(); + pj_list_push_back(&pjMpp.hdr, &pj_hdr); + } + + pj_bzero(&pjMsgBody, sizeof(pjMsgBody)); + pjMsgBody.content_type = contentType.toPj(); + pjMsgBody.print_body = &pjsip_print_text_body; + pjMsgBody.clone_data = &pjsip_clone_text_data; + pjMsgBody.data = (void*)body.c_str(); + pjMsgBody.len = body.size(); + pjMpp.body = &pjMsgBody; + + return pjMpp; +} + +/////////////////////////////////////////////////////////////////////////////// + +SipEvent::SipEvent() +: type(PJSIP_EVENT_UNKNOWN), pjEvent(NULL) +{ +} + +void SipEvent::fromPj(const pjsip_event &ev) +{ + type = ev.type; + if (type == PJSIP_EVENT_TIMER) { + body.timer.entry = ev.body.timer.entry; + } else if (type == PJSIP_EVENT_TSX_STATE) { + body.tsxState.prevState = (pjsip_tsx_state_e) + ev.body.tsx_state.prev_state; + body.tsxState.tsx.fromPj(*ev.body.tsx_state.tsx); + if (body.tsxState.type == PJSIP_EVENT_TX_MSG) { + if (ev.body.tsx_state.src.tdata) + body.tsxState.src.tdata.fromPj(*ev.body.tsx_state.src.tdata); + } else if (body.tsxState.type == PJSIP_EVENT_RX_MSG) { + if (ev.body.tsx_state.src.rdata) + body.tsxState.src.rdata.fromPj(*ev.body.tsx_state.src.rdata); + } else if (body.tsxState.type == PJSIP_EVENT_TRANSPORT_ERROR) { + body.tsxState.src.status = ev.body.tsx_state.src.status; + } else if (body.tsxState.type == PJSIP_EVENT_TIMER) { + body.tsxState.src.timer = ev.body.tsx_state.src.timer; + } else if (body.tsxState.type == PJSIP_EVENT_USER) { + body.tsxState.src.data = ev.body.tsx_state.src.data; + } + } else if (type == PJSIP_EVENT_TX_MSG) { + if (ev.body.tx_msg.tdata) + body.txMsg.tdata.fromPj(*ev.body.tx_msg.tdata); + } else if (type == PJSIP_EVENT_RX_MSG) { + if (ev.body.rx_msg.rdata) + body.rxMsg.rdata.fromPj(*ev.body.rx_msg.rdata); + } else if (type == PJSIP_EVENT_TRANSPORT_ERROR) { + if (ev.body.tx_error.tdata) + body.txError.tdata.fromPj(*ev.body.tx_error.tdata); + if (ev.body.tx_error.tsx) + body.txError.tsx.fromPj(*ev.body.tx_error.tsx); + } else if (type == PJSIP_EVENT_USER) { + body.user.user1 = ev.body.user.user1; + body.user.user2 = ev.body.user.user2; + body.user.user3 = ev.body.user.user3; + body.user.user4 = ev.body.user.user4; + } + pjEvent = (void *)&ev; +} + +SipTxData::SipTxData() +: pjTxData(NULL) +{ +} + +void SipTxData::fromPj(pjsip_tx_data &tdata) +{ + char straddr[PJ_INET6_ADDRSTRLEN+10]; + + info = pjsip_tx_data_get_info(&tdata); + pjsip_tx_data_encode(&tdata); + wholeMsg = string(tdata.buf.start, tdata.buf.end - tdata.buf.start); + if (pj_sockaddr_has_addr(&tdata.tp_info.dst_addr)) { + pj_sockaddr_print(&tdata.tp_info.dst_addr, straddr, sizeof(straddr), 3); + dstAddress = straddr; + } else { + dstAddress = ""; + } + pjTxData = (void *)&tdata; +} + +SipTransaction::SipTransaction() +: role(PJSIP_ROLE_UAC), statusCode(0), pjTransaction(NULL) +{ +} + +void SipTransaction::fromPj(pjsip_transaction &tsx) +{ + this->role = tsx.role; + this->method = pj2Str(tsx.method.name); + this->statusCode = tsx.status_code; + this->statusText = pj2Str(tsx.status_text); + if (tsx.last_tx) + this->lastTx.fromPj(*tsx.last_tx); + else + this->lastTx.pjTxData = NULL; + this->pjTransaction = (void *)&tsx; +} + +bool SipTxOption::isEmpty() const +{ + return (targetUri == "" && headers.size() == 0 && contentType == "" && + msgBody == "" && multipartContentType.type == "" && + multipartContentType.subType == "" && multipartParts.size() == 0); +} + +void SipTxOption::fromPj(const pjsua_msg_data &prm) throw(Error) +{ + targetUri = pj2Str(prm.target_uri); + + headers.clear(); + pjsip_hdr* pj_hdr = prm.hdr_list.next; + while (pj_hdr != &prm.hdr_list) { + SipHeader sh; + sh.fromPj(pj_hdr); + headers.push_back(sh); + pj_hdr = pj_hdr->next; + } + + contentType = pj2Str(prm.content_type); + msgBody = pj2Str(prm.msg_body); + multipartContentType.fromPj(prm.multipart_ctype); + + multipartParts.clear(); + pjsip_multipart_part* pj_mp = prm.multipart_parts.next; + while (pj_mp != &prm.multipart_parts) { + SipMultipartPart smp; + smp.fromPj(*pj_mp); + multipartParts.push_back(smp); + pj_mp = pj_mp->next; + } +} + +void SipTxOption::toPj(pjsua_msg_data &msg_data) const +{ + unsigned i; + + pjsua_msg_data_init(&msg_data); + + msg_data.target_uri = str2Pj(targetUri); + + pj_list_init(&msg_data.hdr_list); + for (i = 0; i < headers.size(); i++) { + pjsip_generic_string_hdr& pj_hdr = headers[i].toPj(); + pj_list_push_back(&msg_data.hdr_list, &pj_hdr); + } + + msg_data.content_type = str2Pj(contentType); + msg_data.msg_body = str2Pj(msgBody); + msg_data.multipart_ctype = multipartContentType.toPj(); + + pj_list_init(&msg_data.multipart_parts); + for (i = 0; i < multipartParts.size(); i++) { + pjsip_multipart_part& pj_part = multipartParts[i].toPj(); + pj_list_push_back(&msg_data.multipart_parts, &pj_part); + } +} + +////////////////////////////////////////////////////////////////////////////// + +SendInstantMessageParam::SendInstantMessageParam() +: contentType("text/plain"), content(""), userData(NULL) +{ +} + +SendTypingIndicationParam::SendTypingIndicationParam() +: isTyping(false) +{ +} diff --git a/pjsip/src/pjsua2/types.cpp b/pjsip/src/pjsua2/types.cpp new file mode 100644 index 00000000..26740cc7 --- /dev/null +++ b/pjsip/src/pjsua2/types.cpp @@ -0,0 +1,95 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include "util.hpp" + +using namespace pj; +using namespace std; + +#define THIS_FILE "types.cpp" + +/////////////////////////////////////////////////////////////////////////////// + +Error::Error() +: status(PJ_SUCCESS), srcLine(0) +{ +} + +Error::Error( pj_status_t prm_status, + const string &prm_title, + const string &prm_reason, + const string &prm_src_file, + int prm_src_line) +: status(prm_status), title(prm_title), reason(prm_reason), + srcFile(prm_src_file), srcLine(prm_src_line) +{ + if (this->status != PJ_SUCCESS && prm_reason.empty()) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(this->status, errmsg, sizeof(errmsg)); + this->reason = errmsg; + } +} + +string Error::info(bool multi_line) const +{ + string output; + + if (status==PJ_SUCCESS) { + output = "No error"; + } else if (!multi_line) { + char temp[80]; + + if (!title.empty()) { + output += title + " error: "; + } + snprintf(temp, sizeof(temp), " (status=%d)", status); + output += reason + temp; + if (!srcFile.empty()) { + output += " ["; + output += srcFile; + snprintf(temp, sizeof(temp), ":%d]", srcLine); + output += temp; + } + } else { + char temp[80]; + + if (!title.empty()) { + output += string("Title: ") + title + "\n"; + } + + snprintf(temp, sizeof(temp), "%d\n", status); + output += string("Code: ") + temp; + output += string("Description: ") + reason + "\n"; + if (!srcFile.empty()) { + snprintf(temp, sizeof(temp), ":%d\n", srcLine); + output += string("Location: ") + srcFile + temp; + } + } + + return output; +} + +/////////////////////////////////////////////////////////////////////////////// + +void TimeValue::fromPj(const pj_time_val &prm) +{ + this->sec = prm.sec; + this->msec = prm.msec; +} + diff --git a/pjsip/src/pjsua2/util.hpp b/pjsip/src/pjsua2/util.hpp new file mode 100644 index 00000000..ae72af63 --- /dev/null +++ b/pjsip/src/pjsua2/util.hpp @@ -0,0 +1,45 @@ +/* $Id$ */ +/* + * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) + * + * 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 +#include + +#define PJ2BOOL(var) ((var) != PJ_FALSE) + +namespace pj +{ +using std::string; + +inline pj_str_t str2Pj(const string &input_str) +{ + pj_str_t output_str; + output_str.ptr = (char*)input_str.c_str(); + output_str.slen = input_str.size(); + return output_str; +} + +inline string pj2Str(const pj_str_t &input_str) +{ + if (input_str.ptr) + return string(input_str.ptr, input_str.slen); + return string(); +} + + +} // namespace -- cgit v1.2.3