summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Parker <jparker@digium.com>2013-03-11 15:09:56 -0500
committerJason Parker <jparker@digium.com>2013-03-11 15:09:56 -0500
commit483805f79570115ab95c69698792d238c1719b1b (patch)
tree6b53ab2fd2b2478f864ccc8bd1b0bfaedc4d2050
parentf3ab456a17af1c89a6e3be4d20c5944853df1cb0 (diff)
Import pjproject-2.1
-rwxr-xr-xaconfigure300
-rw-r--r--aconfigure.ac130
-rw-r--r--build.symbian/pjlibU.def385
-rwxr-xr-xconfigure-bb102
-rwxr-xr-xconfigure-iphone2
-rw-r--r--pjlib-util/build/Makefile2
-rw-r--r--pjlib-util/include/pjlib-util/stun_simple.h77
-rw-r--r--pjlib-util/src/pjlib-util/resolver.c150
-rw-r--r--pjlib-util/src/pjlib-util/scanner.c3
-rw-r--r--pjlib-util/src/pjlib-util/stun_simple_client.c39
-rw-r--r--pjlib/build/Makefile2
-rw-r--r--pjlib/include/pj/activesock.h9
-rw-r--r--pjlib/include/pj/addr_resolv.h27
-rw-r--r--pjlib/include/pj/config.h22
-rw-r--r--pjlib/include/pj/config_site_sample.h6
-rw-r--r--pjlib/include/pj/errno.h7
-rw-r--r--pjlib/include/pj/guid.h21
-rw-r--r--pjlib/include/pj/hash.h40
-rw-r--r--pjlib/include/pj/ioqueue.h15
-rw-r--r--pjlib/include/pj/lock.h254
-rw-r--r--pjlib/include/pj/pool.h4
-rw-r--r--pjlib/include/pj/pool_i.h4
-rw-r--r--pjlib/include/pj/sock.h20
-rw-r--r--pjlib/include/pj/timer.h68
-rw-r--r--pjlib/include/pj/types.h5
-rw-r--r--pjlib/src/pj/activesock.c58
-rw-r--r--pjlib/src/pj/errno.c5
-rw-r--r--pjlib/src/pj/guid.c23
-rw-r--r--pjlib/src/pj/hash.c95
-rw-r--r--pjlib/src/pj/ioqueue_common_abs.c116
-rw-r--r--pjlib/src/pj/ioqueue_common_abs.h3
-rw-r--r--pjlib/src/pj/ioqueue_epoll.c20
-rw-r--r--pjlib/src/pj/ioqueue_select.c83
-rw-r--r--pjlib/src/pj/ioqueue_symbian.cpp15
-rw-r--r--pjlib/src/pj/ip_helper_generic.c20
-rw-r--r--pjlib/src/pj/lock.c519
-rw-r--r--pjlib/src/pj/os_core_unix.c123
-rw-r--r--pjlib/src/pj/os_info.c5
-rw-r--r--pjlib/src/pj/pool.c6
-rw-r--r--pjlib/src/pj/pool_caching.c10
-rw-r--r--pjlib/src/pj/sock_bsd.c7
-rw-r--r--pjlib/src/pj/sock_common.c111
-rw-r--r--pjlib/src/pj/ssl_sock_ossl.c436
-rw-r--r--pjlib/src/pj/timer.c119
-rw-r--r--pjlib/src/pj/timer_symbian.cpp31
-rw-r--r--pjlib/src/pjlib-test/activesock.c3
-rw-r--r--pjlib/src/pjlib-test/ioq_tcp.c14
-rw-r--r--pjlib/src/pjlib-test/ioq_udp.c7
-rw-r--r--pjlib/src/pjlib-test/ssl_sock.c25
-rw-r--r--pjmedia/build/Makefile6
-rw-r--r--pjmedia/build/os-auto.mak.in11
-rw-r--r--pjmedia/build/pjmedia_codec.vcproj12
-rw-r--r--pjmedia/include/pjmedia-audiodev/audiodev.h8
-rw-r--r--pjmedia/include/pjmedia-codec.h5
-rw-r--r--pjmedia/include/pjmedia-codec/config.h48
-rw-r--r--pjmedia/include/pjmedia-codec/config_auto.h.in11
-rw-r--r--pjmedia/include/pjmedia-codec/opencore_amr.h146
-rw-r--r--pjmedia/include/pjmedia-codec/opencore_amrnb.h89
-rw-r--r--pjmedia/include/pjmedia-codec/silk.h133
-rw-r--r--pjmedia/include/pjmedia-codec/types.h30
-rw-r--r--pjmedia/include/pjmedia-videodev/config.h13
-rw-r--r--pjmedia/include/pjmedia/codec.h15
-rw-r--r--pjmedia/include/pjmedia/config.h34
-rw-r--r--pjmedia/include/pjmedia/sdp.h19
-rw-r--r--pjmedia/include/pjmedia/transport.h27
-rw-r--r--pjmedia/include/pjmedia/transport_ice.h8
-rw-r--r--pjmedia/src/pjmedia-audiodev/alsa_dev.c4
-rw-r--r--pjmedia/src/pjmedia-audiodev/bb10_dev.c364
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp58
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp62
-rw-r--r--pjmedia/src/pjmedia-codec/audio_codecs.c15
-rw-r--r--pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c17
-rw-r--r--pjmedia/src/pjmedia-codec/opencore_amr.c (renamed from pjmedia/src/pjmedia-codec/opencore_amrnb.c)402
-rw-r--r--pjmedia/src/pjmedia-codec/silk.c953
-rw-r--r--pjmedia/src/pjmedia-videodev/dshow_dev.c17
-rw-r--r--pjmedia/src/pjmedia-videodev/sdl_dev.c6
-rw-r--r--pjmedia/src/pjmedia-videodev/v4l2_dev.c5
-rw-r--r--pjmedia/src/pjmedia/codec.c52
-rw-r--r--pjmedia/src/pjmedia/conference.c6
-rw-r--r--pjmedia/src/pjmedia/converter.c4
-rw-r--r--pjmedia/src/pjmedia/endpoint.c10
-rw-r--r--pjmedia/src/pjmedia/g711.c7
-rw-r--r--pjmedia/src/pjmedia/jbuf.c9
-rw-r--r--pjmedia/src/pjmedia/rtcp.c5
-rw-r--r--pjmedia/src/pjmedia/rtp.c4
-rw-r--r--pjmedia/src/pjmedia/sdp.c14
-rw-r--r--pjmedia/src/pjmedia/sdp_neg.c4
-rw-r--r--pjmedia/src/pjmedia/stream.c104
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c8
-rw-r--r--pjmedia/src/pjmedia/transport_srtp.c69
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c8
-rw-r--r--pjmedia/src/pjmedia/types.c6
-rw-r--r--pjmedia/src/pjmedia/vid_codec_util.c290
-rw-r--r--pjmedia/src/pjmedia/vid_port.c13
-rw-r--r--pjmedia/src/pjmedia/vid_stream.c21
-rw-r--r--pjmedia/src/pjmedia/vid_stream_info.c5
-rw-r--r--pjmedia/src/test/mips_test.c45
-rw-r--r--pjnath/build/Makefile4
-rw-r--r--pjnath/build/pjnath_test.vcproj58
-rw-r--r--pjnath/include/pjnath/config.h13
-rw-r--r--pjnath/include/pjnath/ice_session.h8
-rw-r--r--pjnath/include/pjnath/stun_config.h10
-rw-r--r--pjnath/include/pjnath/stun_session.h27
-rw-r--r--pjnath/include/pjnath/stun_sock.h27
-rw-r--r--pjnath/include/pjnath/stun_transaction.h15
-rw-r--r--pjnath/include/pjnath/turn_session.h8
-rw-r--r--pjnath/include/pjnath/turn_sock.h34
-rw-r--r--pjnath/src/pjnath-test/concur_test.c367
-rw-r--r--pjnath/src/pjnath-test/ice_test.c241
-rw-r--r--pjnath/src/pjnath-test/sess_auth.c6
-rw-r--r--pjnath/src/pjnath-test/stun_sock_test.c14
-rw-r--r--pjnath/src/pjnath-test/test.c35
-rw-r--r--pjnath/src/pjnath-test/test.h6
-rw-r--r--pjnath/src/pjnath/ice_session.c409
-rw-r--r--pjnath/src/pjnath/ice_strans.c211
-rw-r--r--pjnath/src/pjnath/nat_detect.c4
-rw-r--r--pjnath/src/pjnath/stun_session.c377
-rw-r--r--pjnath/src/pjnath/stun_sock.c217
-rw-r--r--pjnath/src/pjnath/stun_transaction.c157
-rw-r--r--pjnath/src/pjnath/turn_session.c184
-rw-r--r--pjnath/src/pjnath/turn_sock.c173
-rw-r--r--pjnath/src/pjturn-srv/allocation.c4
-rw-r--r--pjnath/src/pjturn-srv/server.c5
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c359
-rw-r--r--pjsip-apps/src/python/setup.py7
-rw-r--r--pjsip-apps/src/samples/icedemo.c3
-rw-r--r--pjsip-apps/src/samples/pjsip-perf.c14
-rw-r--r--pjsip-apps/src/samples/proxy.h8
-rw-r--r--pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp1
-rw-r--r--pjsip/build/Makefile2
-rw-r--r--pjsip/include/pjsip/sip_auth.h79
-rw-r--r--pjsip/include/pjsip/sip_config.h50
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h73
-rw-r--r--pjsip/include/pjsip/sip_transport.h110
-rw-r--r--pjsip/include/pjsip/sip_transport_tls.h42
-rw-r--r--pjsip/include/pjsip/sip_types.h7
-rw-r--r--pjsip/include/pjsip/sip_uri.h10
-rw-r--r--pjsip/include/pjsip/sip_util.h9
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h346
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h31
-rw-r--r--pjsip/src/pjsip-simple/publishc.c9
-rw-r--r--pjsip/src/pjsip-ua/sip_100rel.c6
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c100
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c11
-rw-r--r--pjsip/src/pjsip-ua/sip_replaces.c19
-rw-r--r--pjsip/src/pjsip-ua/sip_timer.c29
-rw-r--r--pjsip/src/pjsip/sip_auth_client.c17
-rw-r--r--pjsip/src/pjsip/sip_auth_server.c47
-rw-r--r--pjsip/src/pjsip/sip_config.c4
-rw-r--r--pjsip/src/pjsip/sip_dialog.c23
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c160
-rw-r--r--pjsip/src/pjsip/sip_parser.c11
-rw-r--r--pjsip/src/pjsip/sip_tel_uri.c6
-rw-r--r--pjsip/src/pjsip/sip_transaction.c43
-rw-r--r--pjsip/src/pjsip/sip_transport.c243
-rw-r--r--pjsip/src/pjsip/sip_transport_tcp.c174
-rw-r--r--pjsip/src/pjsip/sip_transport_tls.c191
-rw-r--r--pjsip/src/pjsip/sip_ua_layer.c50
-rw-r--r--pjsip/src/pjsip/sip_uri.c7
-rw-r--r--pjsip/src/pjsip/sip_util_proxy.c4
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c290
-rw-r--r--pjsip/src/pjsua-lib/pjsua_aud.c36
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c724
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c178
-rw-r--r--pjsip/src/pjsua-lib/pjsua_dump.c30
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c767
-rw-r--r--pjsip/src/pjsua-lib/pjsua_pres.c76
-rw-r--r--pjsip/src/pjsua-lib/pjsua_vid.c32
-rw-r--r--pjsip/src/test/tsx_uac_test.c34
-rw-r--r--pjsip/src/test/tsx_uas_test.c76
-rw-r--r--pjsip/src/test/uri_test.c24
-rw-r--r--pkgconfig.py4
-rw-r--r--tests/pjsua/scripts-sipp/uas-reinv-with-less-media.py8
-rw-r--r--tests/pjsua/scripts-sipp/uas-reinv-with-less-media.xml155
-rw-r--r--third_party/BaseClasses/wxutil.cpp6
-rw-r--r--version.mak4
176 files changed, 10759 insertions, 3254 deletions
diff --git a/aconfigure b/aconfigure
index ac15b36..33ed0be 100755
--- a/aconfigure
+++ b/aconfigure
@@ -623,8 +623,16 @@ LIBOBJS
ac_main_obj
ac_host
ac_linux_poll
+silk_present
+silk_h_present
+ac_no_silk
+opencore_amrwb_dec_present
+opencore_amrwb_dec_h_present
+opencore_amrwb_enc_present
+opencore_amrwb_enc_h_present
opencore_amrnb_present
opencore_amrnb_h_present
+ac_no_opencore_amrwb
ac_no_opencore_amrnb
libcrypto_present
libssl_present
@@ -778,7 +786,11 @@ with_ipp_arch
with_ssl
enable_ssl
with_opencore_amrnb
-enable_opencore_amrnb
+with_opencore_amr
+with_opencore_amrwbenc
+enable_opencore_amr
+with_silk
+enable_silk
'
ac_precious_vars='build_alias
host_alias
@@ -1435,10 +1447,12 @@ Optional Features:
--with-ipp-samples options
--disable-ssl Exclude SSL support the build (default: autodetect)
- --disable-opencore-amrnb
- Exclude OpenCORE AMR-NB support from the build
+ --disable-opencore-amr Exclude OpenCORE AMR support from the build
(default: autodetect)
+ --disable-silk Exclude SILK support from the build (default:
+ autodetect)
+
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1466,7 +1480,12 @@ Optional Packages:
"em64t. Default is blank for IA32"
--with-ssl=DIR Specify alternate libssl prefix
--with-opencore-amrnb=DIR
- Specify alternate libopencore-amrnb prefix
+ This option is obsolete and replaced by
+ --with-opencore-amr=DIR
+ --with-opencore-amr=DIR Specify alternate libopencore-amr prefix
+ --with-opencore-amrwbenc=DIR
+ Specify alternate libvo-amrwbenc prefix
+ --with-silk=DIR Specify alternate SILK prefix
Some influential environment variables:
CC C compiler command
@@ -7100,38 +7119,70 @@ fi
# Check whether --with-opencore-amrnb was given.
if test "${with_opencore_amrnb+set}" = set; then :
- withval=$with_opencore_amrnb;
+ withval=$with_opencore_amrnb; as_fn_error $? "This option is obsolete and replaced by --with-opencore-amr=DIR" "$LINENO" 5
+fi
+
+
+
+# Check whether --with-opencore-amr was given.
+if test "${with_opencore_amr+set}" = set; then :
+ withval=$with_opencore_amr;
else
- with_opencore_amrnb=no
+ with_opencore_amr=no
fi
-if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amrnb" = "xno"; then
- enable_opencore_amrnb=no
+if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amr" = "xno"; then
+ enable_opencore_amr=no
+fi
+
+
+# Check whether --with-opencore-amrwbenc was given.
+if test "${with_opencore_amrwbenc+set}" = set; then :
+ withval=$with_opencore_amrwbenc;
+else
+ with_opencore_amrwbenc=no
+
fi
-# Check whether --enable-opencore_amrnb was given.
-if test "${enable_opencore_amrnb+set}" = set; then :
- enableval=$enable_opencore_amrnb;
- if test "$enable_opencore_amrnb" = "no"; then
+if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amrwbenc" = "xno"; then
+ enable_opencore_amrwbenc=no
+fi
+
+
+
+
+# Check whether --enable-opencore_amr was given.
+if test "${enable_opencore_amr+set}" = set; then :
+ enableval=$enable_opencore_amr;
+ if test "$enable_opencore_amr" = "no"; then
ac_no_opencore_amrnb=1
+ ac_no_opencore_amrwb=1
$as_echo "#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0" >>confdefs.h
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if OpenCORE AMR-NB support is disabled... yes" >&5
-$as_echo "Checking if OpenCORE AMR-NB support is disabled... yes" >&6; }
+ $as_echo "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if OpenCORE AMR support is disabled... yes" >&5
+$as_echo "Checking if OpenCORE AMR support is disabled... yes" >&6; }
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenCORE AMR-NB installations.." >&5
-$as_echo "checking for OpenCORE AMR-NB installations.." >&6; }
- if test "x$with_opencore_amrnb" != "xno" -a "x$with_opencore_amrnb" != "x"; then
- CFLAGS="$CFLAGS -I$with_opencore_amrnb/include"
- LDFLAGS="$LDFLAGS -L$with_opencore_amrnb/lib"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMR-NB prefix... $with_opencore_amrnb" >&5
-$as_echo "Using OpenCORE AMR-NB prefix... $with_opencore_amrnb" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for OpenCORE AMR installations.." >&5
+$as_echo "checking for OpenCORE AMR installations.." >&6; }
+ if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then
+ CFLAGS="$CFLAGS -I$with_opencore_amr/include"
+ LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMR prefix... $with_opencore_amr" >&5
+$as_echo "Using OpenCORE AMR prefix... $with_opencore_amr" >&6; }
+ fi
+ if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then
+ CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include"
+ LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc" >&5
+$as_echo "Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc" >&6; }
fi
@@ -7192,9 +7243,216 @@ $as_echo "OpenCORE AMR-NB library found, AMR-NB support enabled" >&6; }
fi
+
+
+
+ ac_fn_c_check_header_mongrel "$LINENO" "vo-amrwbenc/enc_if.h" "ac_cv_header_vo_amrwbenc_enc_if_h" "$ac_includes_default"
+if test "x$ac_cv_header_vo_amrwbenc_enc_if_h" = xyes; then :
+ opencore_amrwb_enc_h_present=1
fi
+ ac_fn_c_check_header_mongrel "$LINENO" "opencore-amrwb/dec_if.h" "ac_cv_header_opencore_amrwb_dec_if_h" "$ac_includes_default"
+if test "x$ac_cv_header_opencore_amrwb_dec_if_h" = xyes; then :
+ opencore_amrwb_dec_h_present=1
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for D_IF_init in -lopencore-amrwb" >&5
+$as_echo_n "checking for D_IF_init in -lopencore-amrwb... " >&6; }
+if ${ac_cv_lib_opencore_amrwb_D_IF_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lopencore-amrwb $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char D_IF_init ();
+int
+main ()
+{
+return D_IF_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_opencore_amrwb_D_IF_init=yes
+else
+ ac_cv_lib_opencore_amrwb_D_IF_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_opencore_amrwb_D_IF_init" >&5
+$as_echo "$ac_cv_lib_opencore_amrwb_D_IF_init" >&6; }
+if test "x$ac_cv_lib_opencore_amrwb_D_IF_init" = xyes; then :
+ opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb"
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for E_IF_init in -lvo-amrwbenc" >&5
+$as_echo_n "checking for E_IF_init in -lvo-amrwbenc... " >&6; }
+if ${ac_cv_lib_vo_amrwbenc_E_IF_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lvo-amrwbenc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char E_IF_init ();
+int
+main ()
+{
+return E_IF_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_vo_amrwbenc_E_IF_init=yes
+else
+ ac_cv_lib_vo_amrwbenc_E_IF_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_vo_amrwbenc_E_IF_init" >&5
+$as_echo "$ac_cv_lib_vo_amrwbenc_E_IF_init" >&6; }
+if test "x$ac_cv_lib_vo_amrwbenc_E_IF_init" = xyes; then :
+ opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc"
+fi
+
+ if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: OpenCORE AMR-WB library found, AMR-WB support enabled" >&5
+$as_echo "OpenCORE AMR-WB library found, AMR-WB support enabled" >&6; }
+ $as_echo "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 1" >>confdefs.h
+
+ else
+ ac_no_opencore_amrwb=1
+ $as_echo "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0" >>confdefs.h
+
+ fi
+
+
+fi
+
+
+
+# Check whether --with-silk was given.
+if test "${with_silk+set}" = set; then :
+ withval=$with_silk;
+else
+ with_silk=no
+
+fi
+
+
+if test "x$ac_cross_compile" != "x" -a "x$with_silk" = "xno"; then
+ enable_silk=no
+fi
+
+
+# Check whether --enable-silk was given.
+if test "${enable_silk+set}" = set; then :
+ enableval=$enable_silk;
+ if test "$enable_silk" = "no"; then
+ ac_no_silk=1
+ $as_echo "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if SILK support is disabled... yes" >&5
+$as_echo "Checking if SILK support is disabled... yes" >&6; }
+ fi
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for SILK installations.." >&5
+$as_echo "checking for SILK installations.." >&6; }
+ if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then
+ CFLAGS="$CFLAGS -I$with_silk/interface"
+ CPPFLAGS="$CPPFLAGS -I$with_silk/interface"
+ LDFLAGS="$LDFLAGS -L$with_silk"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using SILK prefix... $with_silk" >&5
+$as_echo "Using SILK prefix... $with_silk" >&6; }
+ fi
+
+
+ ac_fn_c_check_header_mongrel "$LINENO" "SKP_Silk_SDK_API.h" "ac_cv_header_SKP_Silk_SDK_API_h" "$ac_includes_default"
+if test "x$ac_cv_header_SKP_Silk_SDK_API_h" = xyes; then :
+ silk_h_present=1
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK" >&5
+$as_echo_n "checking for SKP_Silk_SDK_get_version in -lSKP_SILK_SDK... " >&6; }
+if ${ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lSKP_SILK_SDK $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SKP_Silk_SDK_get_version ();
+int
+main ()
+{
+return SKP_Silk_SDK_get_version ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version=yes
+else
+ ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" >&5
+$as_echo "$ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" >&6; }
+if test "x$ac_cv_lib_SKP_SILK_SDK_SKP_Silk_SDK_get_version" = xyes; then :
+ silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK"
+fi
+
+ if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: SILK library found, SILK support enabled" >&5
+$as_echo "SILK library found, SILK support enabled" >&6; }
+ $as_echo "#define PJMEDIA_HAS_SILK_CODEC 1" >>confdefs.h
+
+ else
+ ac_no_silk=1
+ $as_echo "#define PJMEDIA_HAS_SILK_CODEC 0" >>confdefs.h
+
+ fi
+
+fi
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if select() needs correct nfds" >&5
diff --git a/aconfigure.ac b/aconfigure.ac
index be2f2ae..9afce8d 100644
--- a/aconfigure.ac
+++ b/aconfigure.ac
@@ -1320,39 +1320,69 @@ AC_ARG_ENABLE(ssl,
fi
])
-dnl # opencore-amrnb alt prefix
+dnl # Obsolete option --with-opencore-amrnb
AC_ARG_WITH(opencore-amrnb,
AC_HELP_STRING([--with-opencore-amrnb=DIR],
- [Specify alternate libopencore-amrnb prefix]),
+ [This option is obsolete and replaced by --with-opencore-amr=DIR]),
+ [AC_MSG_ERROR(This option is obsolete and replaced by --with-opencore-amr=DIR)],
+ []
+ )
+
+dnl # opencore-amr alt prefix
+AC_ARG_WITH(opencore-amr,
+ AC_HELP_STRING([--with-opencore-amr=DIR],
+ [Specify alternate libopencore-amr prefix]),
+ [],
+ [with_opencore_amr=no]
+ )
+
+dnl # Do not use default opencore-amr installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amr" = "xno"; then
+ enable_opencore_amr=no
+fi
+
+dnl # vo-amrwbenc alt prefix
+AC_ARG_WITH(opencore-amrwbenc,
+ AC_HELP_STRING([--with-opencore-amrwbenc=DIR],
+ [Specify alternate libvo-amrwbenc prefix]),
[],
- [with_opencore_amrnb=no]
+ [with_opencore_amrwbenc=no]
)
-dnl # Do not use default opencore-amrnb installation if we are cross-compiling
-if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amrnb" = "xno"; then
- enable_opencore_amrnb=no
+dnl # Do not use default vo-amrwbenc installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_opencore_amrwbenc" = "xno"; then
+ enable_opencore_amrwbenc=no
fi
-dnl # Include opencore-amrnb support
+
+dnl # Include opencore-amr support
AC_SUBST(ac_no_opencore_amrnb)
-AC_ARG_ENABLE(opencore_amrnb,
- AC_HELP_STRING([--disable-opencore-amrnb],
- [Exclude OpenCORE AMR-NB support from the build (default: autodetect)])
+AC_SUBST(ac_no_opencore_amrwb)
+AC_ARG_ENABLE(opencore_amr,
+ AC_HELP_STRING([--disable-opencore-amr],
+ [Exclude OpenCORE AMR support from the build (default: autodetect)])
,
[
- if test "$enable_opencore_amrnb" = "no"; then
+ if test "$enable_opencore_amr" = "no"; then
[ac_no_opencore_amrnb=1]
+ [ac_no_opencore_amrwb=1]
AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0)
- AC_MSG_RESULT([Checking if OpenCORE AMR-NB support is disabled... yes])
+ AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0)
+ AC_MSG_RESULT([Checking if OpenCORE AMR support is disabled... yes])
fi
],
[
- AC_MSG_RESULT([checking for OpenCORE AMR-NB installations..])
- if test "x$with_opencore_amrnb" != "xno" -a "x$with_opencore_amrnb" != "x"; then
- CFLAGS="$CFLAGS -I$with_opencore_amrnb/include"
- LDFLAGS="$LDFLAGS -L$with_opencore_amrnb/lib"
- AC_MSG_RESULT([Using OpenCORE AMR-NB prefix... $with_opencore_amrnb])
- fi
+ AC_MSG_RESULT([checking for OpenCORE AMR installations..])
+ if test "x$with_opencore_amr" != "xno" -a "x$with_opencore_amr" != "x"; then
+ CFLAGS="$CFLAGS -I$with_opencore_amr/include"
+ LDFLAGS="$LDFLAGS -L$with_opencore_amr/lib"
+ AC_MSG_RESULT([Using OpenCORE AMR prefix... $with_opencore_amr])
+ fi
+ if test "x$with_opencore_amrwbenc" != "xno" -a "x$with_opencore_amrwbenc" != "x"; then
+ CFLAGS="$CFLAGS -I$with_opencore_amrwbenc/include"
+ LDFLAGS="$LDFLAGS -L$with_opencore_amrwbenc/lib"
+ AC_MSG_RESULT([Using OpenCORE AMRWB-enc prefix... $with_opencore_amrwbenc])
+ fi
AC_SUBST(opencore_amrnb_h_present)
AC_SUBST(opencore_amrnb_present)
AC_CHECK_HEADER(opencore-amrnb/interf_enc.h,[opencore_amrnb_h_present=1])
@@ -1364,8 +1394,72 @@ AC_ARG_ENABLE(opencore_amrnb,
[ac_no_opencore_amrnb=1]
AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC,0)
fi
+ AC_SUBST(opencore_amrwb_enc_h_present)
+ AC_SUBST(opencore_amrwb_enc_present)
+ AC_SUBST(opencore_amrwb_dec_h_present)
+ AC_SUBST(opencore_amrwb_dec_present)
+ AC_CHECK_HEADER(vo-amrwbenc/enc_if.h,[opencore_amrwb_enc_h_present=1])
+ AC_CHECK_HEADER(opencore-amrwb/dec_if.h,[opencore_amrwb_dec_h_present=1])
+ AC_CHECK_LIB(opencore-amrwb,D_IF_init,[opencore_amrwb_dec_present=1 && LIBS="$LIBS -lopencore-amrwb"])
+ AC_CHECK_LIB(vo-amrwbenc,E_IF_init,[opencore_amrwb_enc_present=1 && LIBS="$LIBS -lvo-amrwbenc"])
+ if test "x$opencore_amrwb_enc_h_present" = "x1" -a "x$opencore_amrwb_dec_h_present" = "x1" -a "x$opencore_amrwb_enc_present" = "x1" -a "x$opencore_amrwb_dec_present" = "x1"; then
+ AC_MSG_RESULT([OpenCORE AMR-WB library found, AMR-WB support enabled])
+ AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,1)
+ else
+ [ac_no_opencore_amrwb=1]
+ AC_DEFINE(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC,0)
+ fi
+
+ ])
+
+dnl # SILK prefix
+AC_ARG_WITH(silk,
+ AC_HELP_STRING([--with-silk=DIR],
+ [Specify alternate SILK prefix]),
+ [],
+ [with_silk=no]
+ )
+
+dnl # Do not use default SILK installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_silk" = "xno"; then
+ enable_silk=no
+fi
+
+dnl # Include SILK support
+AC_SUBST(ac_no_silk)
+AC_ARG_ENABLE(silk,
+ AC_HELP_STRING([--disable-silk],
+ [Exclude SILK support from the build (default: autodetect)])
+ ,
+ [
+ if test "$enable_silk" = "no"; then
+ [ac_no_silk=1]
+ AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0)
+ AC_MSG_RESULT([Checking if SILK support is disabled... yes])
+ fi
+ ],
+ [
+ AC_MSG_RESULT([checking for SILK installations..])
+ if test "x$with_silk" != "xno" -a "x$with_silk" != "x"; then
+ CFLAGS="$CFLAGS -I$with_silk/interface"
+ CPPFLAGS="$CPPFLAGS -I$with_silk/interface"
+ LDFLAGS="$LDFLAGS -L$with_silk"
+ AC_MSG_RESULT([Using SILK prefix... $with_silk])
+ fi
+ AC_SUBST(silk_h_present)
+ AC_SUBST(silk_present)
+ AC_CHECK_HEADER(SKP_Silk_SDK_API.h,[silk_h_present=1])
+ AC_CHECK_LIB(SKP_SILK_SDK,SKP_Silk_SDK_get_version,[silk_present=1 && LIBS="$LIBS -lSKP_SILK_SDK"])
+ if test "x$silk_h_present" = "x1" -a "x$silk_present" = "x1"; then
+ AC_MSG_RESULT([SILK library found, SILK support enabled])
+ AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,1)
+ else
+ [ac_no_silk=1]
+ AC_DEFINE(PJMEDIA_HAS_SILK_CODEC,0)
+ fi
])
+
dnl ##########################################
dnl #
dnl # MANUAL CONFIG
diff --git a/build.symbian/pjlibU.def b/build.symbian/pjlibU.def
index 3ddcb40..6dbffdf 100644
--- a/build.symbian/pjlibU.def
+++ b/build.symbian/pjlibU.def
@@ -130,194 +130,197 @@ EXPORTS
pj_ioqueue_recv @ 129 NONAME
pj_ioqueue_recvfrom @ 130 NONAME
pj_ioqueue_register_sock @ 131 NONAME
- pj_ioqueue_send @ 132 NONAME
- pj_ioqueue_sendto @ 133 NONAME
- pj_ioqueue_set_lock @ 134 NONAME
- pj_ioqueue_set_user_data @ 135 NONAME
- pj_ioqueue_unregister @ 136 NONAME
- pj_leave_critical_section @ 137 NONAME
- pj_list_erase @ 138 NONAME
- pj_list_find_node @ 139 NONAME
- pj_list_insert_after @ 140 NONAME
- pj_list_insert_before @ 141 NONAME
- pj_list_insert_nodes_after @ 142 NONAME
- pj_list_insert_nodes_before @ 143 NONAME
- pj_list_merge_first @ 144 NONAME
- pj_list_merge_last @ 145 NONAME
- pj_list_search @ 146 NONAME
- pj_list_size @ 147 NONAME
- pj_lock_acquire @ 148 NONAME
- pj_lock_create_null_mutex @ 149 NONAME
- pj_lock_create_recursive_mutex @ 150 NONAME
- pj_lock_create_semaphore @ 151 NONAME
- pj_lock_create_simple_mutex @ 152 NONAME
- pj_lock_destroy @ 153 NONAME
- pj_lock_release @ 154 NONAME
- pj_lock_tryacquire @ 155 NONAME
- pj_log @ 156 NONAME
- pj_log_1 @ 157 NONAME
- pj_log_2 @ 158 NONAME
- pj_log_3 @ 159 NONAME
- pj_log_4 @ 160 NONAME
- pj_log_5 @ 161 NONAME
- pj_log_get_decor @ 162 NONAME
- pj_log_get_level @ 163 NONAME
- pj_log_get_log_func @ 164 NONAME
- pj_log_set_decor @ 165 NONAME
- pj_log_set_level @ 166 NONAME
- pj_log_set_log_func @ 167 NONAME
- pj_log_write @ 168 NONAME
- pj_mutex_create @ 169 NONAME
- pj_mutex_create_recursive @ 170 NONAME
- pj_mutex_create_simple @ 171 NONAME
- pj_mutex_destroy @ 172 NONAME
- pj_mutex_lock @ 173 NONAME
- pj_mutex_trylock @ 174 NONAME
- pj_mutex_unlock @ 175 NONAME
- pj_ntohl @ 176 NONAME
- pj_ntohs @ 177 NONAME
- pj_pool_alloc @ 178 NONAME
- pj_pool_alloc_from_block @ 179 NONAME
- pj_pool_allocate_find @ 180 NONAME
- pj_pool_calloc @ 181 NONAME
- pj_pool_create @ 182 NONAME
- pj_pool_create_int @ 183 NONAME
- pj_pool_create_on_buf @ 184 NONAME
- pj_pool_destroy_int @ 185 NONAME
- pj_pool_factory_default_policy @ 186 NONAME
- pj_pool_factory_get_default_policy @ 187 NONAME
- pj_pool_get_capacity @ 188 NONAME
- pj_pool_get_used_size @ 189 NONAME
- pj_pool_getobjname @ 190 NONAME
- pj_pool_init_int @ 191 NONAME
- pj_pool_release @ 192 NONAME
- pj_pool_reset @ 193 NONAME
- pj_rand @ 194 NONAME
- pj_rbtree_erase @ 195 NONAME
- pj_rbtree_find @ 196 NONAME
- pj_rbtree_first @ 197 NONAME
- pj_rbtree_init @ 198 NONAME
- pj_rbtree_insert @ 199 NONAME
- pj_rbtree_last @ 200 NONAME
- pj_rbtree_max_height @ 201 NONAME
- pj_rbtree_min_height @ 202 NONAME
- pj_rbtree_next @ 203 NONAME
- pj_rbtree_prev @ 204 NONAME
- pj_register_strerror @ 205 NONAME
- pj_rwmutex_create @ 206 NONAME
- pj_rwmutex_destroy @ 207 NONAME
- pj_rwmutex_lock_read @ 208 NONAME
- pj_rwmutex_lock_write @ 209 NONAME
- pj_rwmutex_unlock_read @ 210 NONAME
- pj_rwmutex_unlock_write @ 211 NONAME
- pj_sem_create @ 212 NONAME
- pj_sem_destroy @ 213 NONAME
- pj_sem_post @ 214 NONAME
- pj_sem_trywait @ 215 NONAME
- pj_sem_wait @ 216 NONAME
- pj_set_netos_error @ 217 NONAME
- pj_set_os_error @ 218 NONAME
- pj_shutdown @ 219 NONAME
- pj_sock_accept @ 220 NONAME
- pj_sock_bind @ 221 NONAME
- pj_sock_bind_in @ 222 NONAME
- pj_sock_close @ 223 NONAME
- pj_sock_connect @ 224 NONAME
- pj_sock_getpeername @ 225 NONAME
- pj_sock_getsockname @ 226 NONAME
- pj_sock_getsockopt @ 227 NONAME
- pj_sock_listen @ 228 NONAME
- pj_sock_recv @ 229 NONAME
- pj_sock_recvfrom @ 230 NONAME
- pj_sock_select @ 231 NONAME
- pj_sock_send @ 232 NONAME
- pj_sock_sendto @ 233 NONAME
- pj_sock_setsockopt @ 234 NONAME
- pj_sock_shutdown @ 235 NONAME
- pj_sock_socket @ 236 NONAME
- pj_sockaddr_cmp @ 237 NONAME
- pj_sockaddr_copy_addr @ 238 NONAME
- pj_sockaddr_get_addr @ 239 NONAME
- pj_sockaddr_get_addr_len @ 240 NONAME
- pj_sockaddr_get_len @ 241 NONAME
- pj_sockaddr_get_port @ 242 NONAME
- pj_sockaddr_has_addr @ 243 NONAME
- pj_sockaddr_in_get_addr @ 244 NONAME
- pj_sockaddr_in_get_port @ 245 NONAME
- pj_sockaddr_in_init @ 246 NONAME
- pj_sockaddr_in_set_addr @ 247 NONAME
- pj_sockaddr_in_set_port @ 248 NONAME
- pj_sockaddr_in_set_str_addr @ 249 NONAME
- pj_sockaddr_init @ 250 NONAME
- pj_sockaddr_print @ 251 NONAME
- pj_sockaddr_set_port @ 252 NONAME
- pj_sockaddr_set_str_addr @ 253 NONAME
- pj_srand @ 254 NONAME
- pj_str @ 255 NONAME
- pj_strassign @ 256 NONAME
- pj_strcat @ 257 NONAME
- pj_strcat2 @ 258 NONAME
- pj_strcmp @ 259 NONAME
- pj_strcmp2 @ 260 NONAME
- pj_strcpy @ 261 NONAME
- pj_strcpy2 @ 262 NONAME
- pj_strdup @ 263 NONAME
- pj_strdup2 @ 264 NONAME
- pj_strdup2_with_null @ 265 NONAME
- pj_strdup3 @ 266 NONAME
- pj_strdup_with_null @ 267 NONAME
- pj_strerror @ 268 NONAME
- pj_stricmp @ 269 NONAME
- pj_stricmp2 @ 270 NONAME
- pj_strltrim @ 271 NONAME
- pj_strncmp @ 272 NONAME
- pj_strncmp2 @ 273 NONAME
- pj_strncpy @ 274 NONAME
- pj_strncpy_with_null @ 275 NONAME
- pj_strnicmp @ 276 NONAME
- pj_strnicmp2 @ 277 NONAME
- pj_strrtrim @ 278 NONAME
- pj_strtoul @ 279 NONAME
- pj_strtoul2 @ 280 NONAME
- pj_strtrim @ 281 NONAME
- pj_symbianos_poll @ 282 NONAME
- pj_symbianos_set_params @ 283 NONAME
- pj_thread_check_stack @ 284 NONAME
- pj_thread_create @ 285 NONAME
- pj_thread_destroy @ 286 NONAME
- pj_thread_get_name @ 287 NONAME
- pj_thread_get_os_handle @ 288 NONAME
- pj_thread_get_stack_info @ 289 NONAME
- pj_thread_get_stack_max_usage @ 290 NONAME
- pj_thread_is_registered @ 291 NONAME
- pj_thread_join @ 292 NONAME
- pj_thread_local_alloc @ 293 NONAME
- pj_thread_local_free @ 294 NONAME
- pj_thread_local_get @ 295 NONAME
- pj_thread_local_set @ 296 NONAME
- pj_thread_register @ 297 NONAME
- pj_thread_resume @ 298 NONAME
- pj_thread_sleep @ 299 NONAME
- pj_thread_this @ 300 NONAME
- pj_time_decode @ 301 NONAME
- pj_time_encode @ 302 NONAME
- pj_time_gmt_to_local @ 303 NONAME
- pj_time_local_to_gmt @ 304 NONAME
- pj_time_val_normalize @ 305 NONAME
- pj_timer_entry_init @ 306 NONAME
- pj_timer_heap_cancel @ 307 NONAME
- pj_timer_heap_count @ 308 NONAME
- pj_timer_heap_create @ 309 NONAME
- pj_timer_heap_destroy @ 310 NONAME
- pj_timer_heap_earliest_time @ 311 NONAME
- pj_timer_heap_mem_size @ 312 NONAME
- pj_timer_heap_poll @ 313 NONAME
- pj_timer_heap_schedule @ 314 NONAME
- pj_timer_heap_set_lock @ 315 NONAME
- pj_timer_heap_set_max_timed_out_per_poll @ 316 NONAME
- pj_unicode_to_ansi @ 317 NONAME
- pj_utoa @ 318 NONAME
- pj_utoa_pad @ 319 NONAME
- platform_strerror @ 320 NONAME
- snprintf @ 321 NONAME
- vsnprintf @ 322 NONAME
+ pj_ioqueue_register_sock2 @ 132 NONAME
+ pj_ioqueue_send @ 133 NONAME
+ pj_ioqueue_sendto @ 134 NONAME
+ pj_ioqueue_set_lock @ 135 NONAME
+ pj_ioqueue_set_user_data @ 136 NONAME
+ pj_ioqueue_unregister @ 137 NONAME
+ pj_leave_critical_section @ 138 NONAME
+ pj_list_erase @ 139 NONAME
+ pj_list_find_node @ 140 NONAME
+ pj_list_insert_after @ 141 NONAME
+ pj_list_insert_before @ 142 NONAME
+ pj_list_insert_nodes_after @ 143 NONAME
+ pj_list_insert_nodes_before @ 144 NONAME
+ pj_list_merge_first @ 145 NONAME
+ pj_list_merge_last @ 146 NONAME
+ pj_list_search @ 147 NONAME
+ pj_list_size @ 148 NONAME
+ pj_lock_acquire @ 149 NONAME
+ pj_lock_create_null_mutex @ 150 NONAME
+ pj_lock_create_recursive_mutex @ 151 NONAME
+ pj_lock_create_semaphore @ 152 NONAME
+ pj_lock_create_simple_mutex @ 153 NONAME
+ pj_lock_destroy @ 154 NONAME
+ pj_lock_release @ 155 NONAME
+ pj_lock_tryacquire @ 156 NONAME
+ pj_log @ 157 NONAME
+ pj_log_1 @ 158 NONAME
+ pj_log_2 @ 159 NONAME
+ pj_log_3 @ 160 NONAME
+ pj_log_4 @ 161 NONAME
+ pj_log_5 @ 162 NONAME
+ pj_log_get_decor @ 163 NONAME
+ pj_log_get_level @ 164 NONAME
+ pj_log_get_log_func @ 165 NONAME
+ pj_log_set_decor @ 166 NONAME
+ pj_log_set_level @ 167 NONAME
+ pj_log_set_log_func @ 168 NONAME
+ pj_log_write @ 169 NONAME
+ pj_mutex_create @ 170 NONAME
+ pj_mutex_create_recursive @ 171 NONAME
+ pj_mutex_create_simple @ 172 NONAME
+ pj_mutex_destroy @ 173 NONAME
+ pj_mutex_lock @ 174 NONAME
+ pj_mutex_trylock @ 175 NONAME
+ pj_mutex_unlock @ 176 NONAME
+ pj_ntohl @ 177 NONAME
+ pj_ntohs @ 178 NONAME
+ pj_pool_alloc @ 179 NONAME
+ pj_pool_alloc_from_block @ 180 NONAME
+ pj_pool_allocate_find @ 181 NONAME
+ pj_pool_calloc @ 182 NONAME
+ pj_pool_create @ 183 NONAME
+ pj_pool_create_int @ 184 NONAME
+ pj_pool_create_on_buf @ 185 NONAME
+ pj_pool_destroy_int @ 186 NONAME
+ pj_pool_factory_default_policy @ 187 NONAME
+ pj_pool_factory_get_default_policy @ 188 NONAME
+ pj_pool_get_capacity @ 189 NONAME
+ pj_pool_get_used_size @ 190 NONAME
+ pj_pool_getobjname @ 191 NONAME
+ pj_pool_init_int @ 192 NONAME
+ pj_pool_release @ 193 NONAME
+ pj_pool_reset @ 194 NONAME
+ pj_rand @ 195 NONAME
+ pj_rbtree_erase @ 196 NONAME
+ pj_rbtree_find @ 197 NONAME
+ pj_rbtree_first @ 198 NONAME
+ pj_rbtree_init @ 199 NONAME
+ pj_rbtree_insert @ 200 NONAME
+ pj_rbtree_last @ 201 NONAME
+ pj_rbtree_max_height @ 202 NONAME
+ pj_rbtree_min_height @ 203 NONAME
+ pj_rbtree_next @ 204 NONAME
+ pj_rbtree_prev @ 205 NONAME
+ pj_register_strerror @ 206 NONAME
+ pj_rwmutex_create @ 207 NONAME
+ pj_rwmutex_destroy @ 208 NONAME
+ pj_rwmutex_lock_read @ 209 NONAME
+ pj_rwmutex_lock_write @ 210 NONAME
+ pj_rwmutex_unlock_read @ 211 NONAME
+ pj_rwmutex_unlock_write @ 212 NONAME
+ pj_sem_create @ 213 NONAME
+ pj_sem_destroy @ 214 NONAME
+ pj_sem_post @ 215 NONAME
+ pj_sem_trywait @ 216 NONAME
+ pj_sem_wait @ 217 NONAME
+ pj_set_netos_error @ 218 NONAME
+ pj_set_os_error @ 219 NONAME
+ pj_shutdown @ 220 NONAME
+ pj_sock_accept @ 221 NONAME
+ pj_sock_bind @ 222 NONAME
+ pj_sock_bind_in @ 223 NONAME
+ pj_sock_close @ 224 NONAME
+ pj_sock_connect @ 225 NONAME
+ pj_sock_getpeername @ 226 NONAME
+ pj_sock_getsockname @ 227 NONAME
+ pj_sock_getsockopt @ 228 NONAME
+ pj_sock_listen @ 229 NONAME
+ pj_sock_recv @ 230 NONAME
+ pj_sock_recvfrom @ 231 NONAME
+ pj_sock_select @ 232 NONAME
+ pj_sock_send @ 233 NONAME
+ pj_sock_sendto @ 234 NONAME
+ pj_sock_setsockopt @ 235 NONAME
+ pj_sock_shutdown @ 236 NONAME
+ pj_sock_socket @ 237 NONAME
+ pj_sockaddr_cmp @ 238 NONAME
+ pj_sockaddr_copy_addr @ 239 NONAME
+ pj_sockaddr_get_addr @ 240 NONAME
+ pj_sockaddr_get_addr_len @ 241 NONAME
+ pj_sockaddr_get_len @ 242 NONAME
+ pj_sockaddr_get_port @ 243 NONAME
+ pj_sockaddr_has_addr @ 244 NONAME
+ pj_sockaddr_in_get_addr @ 245 NONAME
+ pj_sockaddr_in_get_port @ 246 NONAME
+ pj_sockaddr_in_init @ 247 NONAME
+ pj_sockaddr_in_set_addr @ 248 NONAME
+ pj_sockaddr_in_set_port @ 249 NONAME
+ pj_sockaddr_in_set_str_addr @ 250 NONAME
+ pj_sockaddr_init @ 251 NONAME
+ pj_sockaddr_print @ 252 NONAME
+ pj_sockaddr_set_port @ 253 NONAME
+ pj_sockaddr_set_str_addr @ 254 NONAME
+ pj_srand @ 255 NONAME
+ pj_str @ 256 NONAME
+ pj_strassign @ 257 NONAME
+ pj_strcat @ 258 NONAME
+ pj_strcat2 @ 259 NONAME
+ pj_strcmp @ 260 NONAME
+ pj_strcmp2 @ 261 NONAME
+ pj_strcpy @ 262 NONAME
+ pj_strcpy2 @ 263 NONAME
+ pj_strdup @ 264 NONAME
+ pj_strdup2 @ 265 NONAME
+ pj_strdup2_with_null @ 266 NONAME
+ pj_strdup3 @ 267 NONAME
+ pj_strdup_with_null @ 268 NONAME
+ pj_strerror @ 269 NONAME
+ pj_stricmp @ 270 NONAME
+ pj_stricmp2 @ 271 NONAME
+ pj_strltrim @ 272 NONAME
+ pj_strncmp @ 273 NONAME
+ pj_strncmp2 @ 274 NONAME
+ pj_strncpy @ 275 NONAME
+ pj_strncpy_with_null @ 276 NONAME
+ pj_strnicmp @ 277 NONAME
+ pj_strnicmp2 @ 278 NONAME
+ pj_strrtrim @ 279 NONAME
+ pj_strtoul @ 280 NONAME
+ pj_strtoul2 @ 281 NONAME
+ pj_strtrim @ 282 NONAME
+ pj_symbianos_poll @ 283 NONAME
+ pj_symbianos_set_params @ 284 NONAME
+ pj_thread_check_stack @ 285 NONAME
+ pj_thread_create @ 286 NONAME
+ pj_thread_destroy @ 287 NONAME
+ pj_thread_get_name @ 288 NONAME
+ pj_thread_get_os_handle @ 289 NONAME
+ pj_thread_get_stack_info @ 290 NONAME
+ pj_thread_get_stack_max_usage @ 291 NONAME
+ pj_thread_is_registered @ 292 NONAME
+ pj_thread_join @ 293 NONAME
+ pj_thread_local_alloc @ 294 NONAME
+ pj_thread_local_free @ 295 NONAME
+ pj_thread_local_get @ 296 NONAME
+ pj_thread_local_set @ 297 NONAME
+ pj_thread_register @ 298 NONAME
+ pj_thread_resume @ 299 NONAME
+ pj_thread_sleep @ 300 NONAME
+ pj_thread_this @ 301 NONAME
+ pj_time_decode @ 302 NONAME
+ pj_time_encode @ 303 NONAME
+ pj_time_gmt_to_local @ 304 NONAME
+ pj_time_local_to_gmt @ 305 NONAME
+ pj_time_val_normalize @ 306 NONAME
+ pj_timer_entry_init @ 307 NONAME
+ pj_timer_heap_cancel @ 308 NONAME
+ pj_timer_heap_cancel_if_active @ 309 NONAME
+ pj_timer_heap_count @ 310 NONAME
+ pj_timer_heap_create @ 311 NONAME
+ pj_timer_heap_destroy @ 312 NONAME
+ pj_timer_heap_earliest_time @ 313 NONAME
+ pj_timer_heap_mem_size @ 314 NONAME
+ pj_timer_heap_poll @ 315 NONAME
+ pj_timer_heap_schedule @ 316 NONAME
+ pj_timer_heap_schedule_w_grp_lock @ 317 NONAME
+ pj_timer_heap_set_lock @ 318 NONAME
+ pj_timer_heap_set_max_timed_out_per_poll @ 319 NONAME
+ pj_unicode_to_ansi @ 320 NONAME
+ pj_utoa @ 321 NONAME
+ pj_utoa_pad @ 322 NONAME
+ platform_strerror @ 323 NONAME
+ snprintf @ 324 NONAME
+ vsnprintf @ 325 NONAME \ No newline at end of file
diff --git a/configure-bb10 b/configure-bb10
index 4d1a5dd..e29f52f 100755
--- a/configure-bb10
+++ b/configure-bb10
@@ -32,7 +32,7 @@ RANLIB="${QNX_HOST}/usr/bin/nto${TARGET_ARCH}-ranlib "
CPP="${QNX_HOST}/usr/bin/qcc -V4.6.3,gcc_nto${TARGET_ARCHEND}_cpp -E "
CC="${QNX_HOST}/usr/bin/qcc -V4.6.3,gcc_nto${TARGET_ARCHEND}_cpp "
LD="${QNX_HOST}/usr/bin/nto${TARGET_ARCH}-ld "
-export LDFLAGS=" -L${QNX_TARGET}/${LIBDIR}/usr/lib -L${QNX_TARGET}/${LIBDIR}/lib -L${QNX_HOST}/usr/lib/gcc/${TARGET_HOST}/4.6.3 -lgcc -lasound "
+export LDFLAGS=" -L${QNX_TARGET}/${LIBDIR}/usr/lib -L${QNX_TARGET}/${LIBDIR}/lib -L${QNX_HOST}/usr/lib/gcc/${TARGET_HOST}/4.6.3 -lgcc -lasound -laudio_manager"
export CFLAGS=" -g -fPIC -DPJMEDIA_AUDIO_DEV_HAS_BB10=1 "
./configure --host=${TARGET_HOST} --disable-oss $*
diff --git a/configure-iphone b/configure-iphone
index 742dce0..79bf226 100755
--- a/configure-iphone
+++ b/configure-iphone
@@ -133,7 +133,7 @@ if test "1" = "1"; then
fi
# And finally invoke the configure script itself
-./aconfigure --host=arm-apple-darwin9 --disable-floating-point --disable-sdl $*
+./aconfigure --host=arm-apple-darwin9 --disable-sdl $*
if test "$?" = "0"; then
echo "Done configuring for `basename $SDKPATH`"
diff --git a/pjlib-util/build/Makefile b/pjlib-util/build/Makefile
index fba93bb..d2ad65a 100644
--- a/pjlib-util/build/Makefile
+++ b/pjlib-util/build/Makefile
@@ -74,7 +74,7 @@ distclean: realclean
pjlib-util:
$(MAKE) -f $(RULES_MAK) APP=PJLIB_UTIL app=pjlib-util $(PJLIB_UTIL_LIB)
-pjlib-util-test:
+pjlib-util-test: pjlib-util
$(MAKE) -f $(RULES_MAK) APP=UTIL_TEST app=pjlib-util-test $(UTIL_TEST_EXE)
.PHONY: ../lib/pjlib-util.ko
diff --git a/pjlib-util/include/pjlib-util/stun_simple.h b/pjlib-util/include/pjlib-util/stun_simple.h
index 3d10379..a0b1b69 100644
--- a/pjlib-util/include/pjlib-util/stun_simple.h
+++ b/pjlib-util/include/pjlib-util/stun_simple.h
@@ -1,4 +1,4 @@
-/* $Id: stun_simple.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_simple.h 4224 2012-08-09 05:21:25Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -199,6 +199,81 @@ PJ_DECL(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
const pj_str_t *srv2, int port2,
pj_sockaddr_in mapped_addr[]);
+
+/*
+ * This structre describes configurable setting for requesting mapped address.
+ */
+typedef struct pjstun_setting
+{
+ /**
+ * Specifies whether STUN request generated by old STUN library should
+ * insert magic cookie (specified in RFC 5389) in the transaction ID.
+ */
+ pj_bool_t use_stun2;
+
+ /**
+ * Host name or IP address string of the first STUN server.
+ */
+ pj_str_t srv1;
+
+ /**
+ * The port number of the first STUN server.
+ */
+ int port1;
+
+ /**
+ * Host name or IP address string of the second STUN server.
+ */
+ pj_str_t srv2;
+
+ /**
+ * The port number of the second STUN server.
+ */
+ int port2;
+
+} pjstun_setting;
+
+
+/**
+ * Another version of mapped address resolution of local sockets to multiple
+ * STUN servers configured in #pjstun_setting. This function is able to find
+ * the mapped addresses of multiple sockets simultaneously, and for each
+ * socket, two requests will be sent to two different STUN servers to see if
+ * both servers get the same public address for the same socket. (Note that
+ * application can specify the same address for the two servers, but still
+ * two requests will be sent for each server).
+ *
+ * This function will perform necessary retransmissions of the requests if
+ * response is not received within a predetermined period. When all responses
+ * have been received, the function will compare the mapped addresses returned
+ * by the servers, and when both are equal, the address will be returned in
+ * \a mapped_addr argument.
+ *
+ * @param pf The pool factory where memory will be allocated from.
+ * @param opt The STUN settings.
+ * @param sock_cnt Number of sockets in the socket array.
+ * @param sock Array of local UDP sockets which public addresses are
+ * to be queried from the STUN servers.
+ * @param mapped_addr Array to receive the mapped public address of the local
+ * UDP sockets, when the function returns PJ_SUCCESS.
+ *
+ * @return This functions returns PJ_SUCCESS if responses are
+ * received from all servers AND all servers returned the
+ * same mapped public address. Otherwise this function may
+ * return one of the following error codes:
+ * - PJLIB_UTIL_ESTUNNOTRESPOND: no respons from servers.
+ * - PJLIB_UTIL_ESTUNSYMMETRIC: different mapped addresses
+ * are returned by servers.
+ * - etc.
+ *
+ */
+PJ_DECL(pj_status_t) pjstun_get_mapped_addr2( pj_pool_factory *pf,
+ const pjstun_setting *opt,
+ int sock_cnt,
+ pj_sock_t sock[],
+ pj_sockaddr_in mapped_addr[]);
+
+
PJ_END_DECL
/**
diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
index 4be5350..25c6067 100644
--- a/pjlib-util/src/pjlib-util/resolver.c
+++ b/pjlib-util/src/pjlib-util/resolver.c
@@ -1,4 +1,4 @@
-/* $Id: resolver.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: resolver.c 4333 2013-01-23 09:53:39Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -180,7 +180,8 @@ struct pj_dns_resolver
unsigned char udp_rx_pkt[UDPSZ];/**< UDP receive buffer. */
unsigned char udp_tx_pkt[UDPSZ];/**< UDP receive buffer. */
pj_ssize_t udp_len; /**< Length of received packet. */
- pj_ioqueue_op_key_t udp_op_key; /**< UDP read operation key. */
+ pj_ioqueue_op_key_t udp_op_rx_key; /**< UDP read operation key. */
+ pj_ioqueue_op_key_t udp_op_tx_key; /**< UDP write operation key. */
pj_sockaddr_in udp_src_addr; /**< Source address of packet */
int udp_addr_len; /**< Source address length. */
@@ -223,6 +224,63 @@ static pj_status_t select_nameservers(pj_dns_resolver *resolver,
unsigned servers[]);
+/* Close UDP socket */
+static void close_sock(pj_dns_resolver *resv)
+{
+ /* Close existing socket */
+ if (resv->udp_key != NULL) {
+ pj_ioqueue_unregister(resv->udp_key);
+ resv->udp_key = NULL;
+ resv->udp_sock = PJ_INVALID_SOCKET;
+ } else if (resv->udp_sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(resv->udp_sock);
+ resv->udp_sock = PJ_INVALID_SOCKET;
+ }
+}
+
+
+/* Initialize UDP socket */
+static pj_status_t init_sock(pj_dns_resolver *resv)
+{
+ pj_ioqueue_callback socket_cb;
+ pj_status_t status;
+
+ /* Create the UDP socket */
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Bind to any address/port */
+ status = pj_sock_bind_in(resv->udp_sock, 0, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Register to ioqueue */
+ pj_bzero(&socket_cb, sizeof(socket_cb));
+ socket_cb.on_read_complete = &on_read_complete;
+ status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue,
+ resv->udp_sock, resv, &socket_cb,
+ &resv->udp_key);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_ioqueue_op_key_init(&resv->udp_op_rx_key, sizeof(resv->udp_op_rx_key));
+ pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key));
+
+ /* Start asynchronous read to the UDP socket */
+ resv->udp_len = sizeof(resv->udp_rx_pkt);
+ resv->udp_addr_len = sizeof(resv->udp_src_addr);
+ status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key,
+ resv->udp_rx_pkt, &resv->udp_len,
+ PJ_IOQUEUE_ALWAYS_ASYNC,
+ &resv->udp_src_addr, &resv->udp_addr_len);
+ if (status != PJ_EPENDING)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
+
/* Initialize DNS settings with default values */
PJ_DEF(void) pj_dns_settings_default(pj_dns_settings *s)
{
@@ -247,7 +305,6 @@ PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf,
{
pj_pool_t *pool;
pj_dns_resolver *resv;
- pj_ioqueue_callback socket_cb;
pj_status_t status;
/* Sanity check */
@@ -302,37 +359,11 @@ PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf,
resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE);
pj_list_init(&resv->query_free_nodes);
- /* Create the UDP socket */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- /* Bind to any address/port */
- status = pj_sock_bind_in(resv->udp_sock, 0, 0);
+ /* Initialize the UDP socket */
+ status = init_sock(resv);
if (status != PJ_SUCCESS)
goto on_error;
- /* Register to ioqueue */
- pj_bzero(&socket_cb, sizeof(socket_cb));
- socket_cb.on_read_complete = &on_read_complete;
- status = pj_ioqueue_register_sock(pool, resv->ioqueue, resv->udp_sock,
- resv, &socket_cb, &resv->udp_key);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- pj_ioqueue_op_key_init(&resv->udp_op_key, sizeof(resv->udp_op_key));
-
- /* Start asynchronous read to the UDP socket */
- resv->udp_len = sizeof(resv->udp_rx_pkt);
- resv->udp_addr_len = sizeof(resv->udp_src_addr);
- status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_key,
- resv->udp_rx_pkt, &resv->udp_len,
- PJ_IOQUEUE_ALWAYS_ASYNC,
- &resv->udp_src_addr, &resv->udp_addr_len);
- if (status != PJ_EPENDING)
- goto on_error;
-
-
/* Looks like everything is okay */
*p_resolver = resv;
return PJ_SUCCESS;
@@ -392,14 +423,7 @@ PJ_DEF(pj_status_t) pj_dns_resolver_destroy( pj_dns_resolver *resolver,
resolver->timer = NULL;
}
- if (resolver->udp_key != NULL) {
- pj_ioqueue_unregister(resolver->udp_key);
- resolver->udp_key = NULL;
- resolver->udp_sock = PJ_INVALID_SOCKET;
- } else if (resolver->udp_sock != PJ_INVALID_SOCKET) {
- pj_sock_close(resolver->udp_sock);
- resolver->udp_sock = PJ_INVALID_SOCKET;
- }
+ close_sock(resolver);
if (resolver->own_ioqueue && resolver->ioqueue) {
pj_ioqueue_destroy(resolver->ioqueue);
@@ -561,15 +585,6 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
pj_time_val delay;
pj_status_t status;
- /* Create DNS query packet */
- pkt_size = sizeof(resolver->udp_tx_pkt);
- name = pj_str(q->key.name);
- status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size,
- q->id, q->key.qtype, &name);
- if (status != PJ_SUCCESS) {
- return status;
- }
-
/* Select which nameserver(s) to send requests to. */
server_cnt = PJ_ARRAY_SIZE(servers);
status = select_nameservers(resolver, &server_cnt, servers);
@@ -595,6 +610,28 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
return status;
}
+ /* Check if the socket is available for sending */
+ if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)) {
+ ++q->transmit_cnt;
+ PJ_LOG(4,(resolver->name.ptr,
+ "Socket busy in transmitting DNS %s query for %s%s",
+ pj_dns_get_type_name(q->key.qtype),
+ q->key.name,
+ (q->transmit_cnt < resolver->settings.qretr_count?
+ ", will try again later":"")));
+ return PJ_SUCCESS;
+ }
+
+ /* Create DNS query packet */
+ pkt_size = sizeof(resolver->udp_tx_pkt);
+ name = pj_str(q->key.name);
+ status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size,
+ q->id, q->key.qtype, &name);
+ if (status != PJ_SUCCESS) {
+ pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
+ return status;
+ }
+
/* Get current time. */
pj_gettimeofday(&now);
@@ -603,13 +640,16 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
pj_ssize_t sent = (pj_ssize_t) pkt_size;
struct nameserver *ns = &resolver->ns[servers[i]];
- pj_sock_sendto(resolver->udp_sock, resolver->udp_tx_pkt, &sent, 0,
- &resolver->ns[servers[i]].addr, sizeof(pj_sockaddr_in));
+ status = pj_ioqueue_sendto(resolver->udp_key,
+ &resolver->udp_op_tx_key,
+ resolver->udp_tx_pkt, &sent, 0,
+ &resolver->ns[servers[i]].addr,
+ sizeof(pj_sockaddr_in));
- PJ_LOG(4,(resolver->name.ptr,
+ PJ_PERROR(4,(resolver->name.ptr, status,
"%s %d bytes to NS %d (%s:%d): DNS %s query for %s",
(q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
- (int)sent, servers[i],
+ (int)pkt_size, servers[i],
pj_inet_ntoa(ns->addr.sin_addr),
(int)pj_ntohs(ns->addr.sin_port),
pj_dns_get_type_name(q->key.qtype),
@@ -1248,6 +1288,9 @@ static void on_timeout( pj_timer_heap_t *timer_heap,
pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL);
pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL);
+ /* Workaround for deadlock problem in #1565 (similar to #1108) */
+ pj_mutex_unlock(resolver->mutex);
+
/* Call application callback, if any. */
if (q->cb)
(*q->cb)(q->user_data, PJ_ETIMEDOUT, NULL);
@@ -1260,6 +1303,9 @@ static void on_timeout( pj_timer_heap_t *timer_heap,
cq = cq->next;
}
+ /* Workaround for deadlock problem in #1565 (similar to #1108) */
+ pj_mutex_lock(resolver->mutex);
+
/* Clear data */
q->timer_entry.id = 0;
q->user_data = NULL;
diff --git a/pjlib-util/src/pjlib-util/scanner.c b/pjlib-util/src/pjlib-util/scanner.c
index d8e1c8e..ba1152e 100644
--- a/pjlib-util/src/pjlib-util/scanner.c
+++ b/pjlib-util/src/pjlib-util/scanner.c
@@ -1,4 +1,4 @@
-/* $Id: scanner.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: scanner.c 4209 2012-07-18 10:21:00Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -393,7 +393,6 @@ PJ_DEF(void) pj_scan_get_quotes(pj_scanner *scanner,
}
/* break from main loop if we have odd number of backslashes */
if (((unsigned)(q-r) & 0x01) == 1) {
- ++s;
break;
}
++s;
diff --git a/pjlib-util/src/pjlib-util/stun_simple_client.c b/pjlib-util/src/pjlib-util/stun_simple_client.c
index 345d121..2a59ae1 100644
--- a/pjlib-util/src/pjlib-util/stun_simple_client.c
+++ b/pjlib-util/src/pjlib-util/stun_simple_client.c
@@ -1,4 +1,4 @@
-/* $Id: stun_simple_client.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: stun_simple_client.c 4297 2012-11-13 08:46:42Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -42,7 +42,27 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
const pj_str_t *srv2, int port2,
pj_sockaddr_in mapped_addr[])
{
+ pjstun_setting opt;
+
+ pj_bzero(&opt, sizeof(opt));
+ opt.use_stun2 = PJ_FALSE;
+ opt.srv1 = *srv1;
+ opt.port1 = port1;
+ opt.srv2 = *srv2;
+ opt.port2 = port2;
+
+ return pjstun_get_mapped_addr2(pf, &opt, sock_cnt, sock, mapped_addr);
+}
+
+PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
+ const pjstun_setting *opt,
+ int sock_cnt,
+ pj_sock_t sock[],
+ pj_sockaddr_in mapped_addr[])
+{
unsigned srv_cnt;
+ const pj_str_t *srv1, *srv2;
+ int port1, port2;
pj_sockaddr_in srv_addr[2];
int i, send_cnt = 0, nfds;
pj_pool_t *pool;
@@ -59,6 +79,11 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
PJ_CHECK_STACK();
+ srv1 = &opt->srv1;
+ port1 = opt->port1;
+ srv2 = &opt->srv1;
+ port2 = opt->port2;
+
TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()"));
/* Create pool. */
@@ -82,6 +107,12 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Insert magic cookie (specified in RFC 5389) when requested to. */
+ if (opt->use_stun2) {
+ pjstun_msg_hdr *hdr = (pjstun_msg_hdr*)out_msg;
+ hdr->tsx[0] = pj_htonl(STUN_MAGIC);
+ }
+
TRACE_((THIS_FILE, " Binding request created."));
/* Resolve servers. */
@@ -162,15 +193,15 @@ PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
TRACE_((THIS_FILE, " Request(s) sent, counter=%d", send_cnt));
/* Calculate time of next retransmission. */
- pj_gettimeofday(&next_tx);
+ pj_gettickcount(&next_tx);
next_tx.sec += (stun_timer[send_cnt]/1000);
next_tx.msec += (stun_timer[send_cnt]%1000);
pj_time_val_normalize(&next_tx);
- for (pj_gettimeofday(&now), select_rc=1;
+ for (pj_gettickcount(&now), select_rc=1;
status==PJ_SUCCESS && select_rc>=1 && wait_resp>0
&& PJ_TIME_VAL_LT(now, next_tx);
- pj_gettimeofday(&now))
+ pj_gettickcount(&now))
{
pj_time_val timeout;
diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
index 9183644..a36f5f4 100644
--- a/pjlib/build/Makefile
+++ b/pjlib/build/Makefile
@@ -87,7 +87,7 @@ pjlib: ../include/pj/config_site.h
../include/pj/config_site.h:
touch ../include/pj/config_site.h
-pjlib-test:
+pjlib-test: pjlib
$(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $(TEST_EXE)
.PHONY: ../lib/pjlib.ko
diff --git a/pjlib/include/pj/activesock.h b/pjlib/include/pj/activesock.h
index 0c30c01..6d3b0d0 100644
--- a/pjlib/include/pj/activesock.h
+++ b/pjlib/include/pj/activesock.h
@@ -1,4 +1,4 @@
-/* $Id: activesock.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: activesock.h 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -174,6 +174,11 @@ typedef struct pj_activesock_cb
typedef struct pj_activesock_cfg
{
/**
+ * Optional group lock to be assigned to the ioqueue key.
+ */
+ pj_grp_lock_t *grp_lock;
+
+ /**
* Number of concurrent asynchronous operations that is to be supported
* by the active socket. This value only affects socket receive and
* accept operations -- the active socket will issue one or more
@@ -290,7 +295,6 @@ PJ_DECL(pj_status_t) pj_activesock_create_udp(pj_pool_t *pool,
pj_activesock_t **p_asock,
pj_sockaddr *bound_addr);
-
/**
* Close the active socket. This will unregister the socket from the
* ioqueue and ultimately close the socket.
@@ -548,6 +552,7 @@ PJ_DECL(pj_status_t) pj_activesock_start_connect(pj_activesock_t *asock,
const pj_sockaddr_t *remaddr,
int addr_len);
+
#endif /* PJ_HAS_TCP */
/**
diff --git a/pjlib/include/pj/addr_resolv.h b/pjlib/include/pj/addr_resolv.h
index ae10337..41eacc8 100644
--- a/pjlib/include/pj/addr_resolv.h
+++ b/pjlib/include/pj/addr_resolv.h
@@ -1,4 +1,4 @@
-/* $Id: addr_resolv.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: addr_resolv.h 4218 2012-08-07 02:18:15Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -117,6 +117,31 @@ PJ_DECL(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr);
/**
+ * Get the interface IP address to send data to the specified destination.
+ *
+ * @param af The desired address family to query. Valid values
+ * are pj_AF_INET() or pj_AF_INET6().
+ * @param dst The destination host.
+ * @param itf_addr On successful resolution, the address family and address
+ * part of this socket address will be filled up with the host
+ * IP address, in network byte order. Other parts of the socket
+ * address should be ignored.
+ * @param allow_resolve If \a dst may contain hostname (instead of IP
+ * address), specify whether hostname resolution should
+ * be performed. If not, default interface address will
+ * be returned.
+ * @param p_dst_addr If not NULL, it will be filled with the IP address of
+ * the destination host.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_getipinterface(int af,
+ const pj_str_t *dst,
+ pj_sockaddr *itf_addr,
+ pj_bool_t allow_resolve,
+ pj_sockaddr *p_dst_addr);
+
+/**
* Get the IP address of the default interface. Default interface is the
* interface of the default route.
*
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index 629fa44..120e6ce 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 4189 2012-07-03 03:11:24Z ming $ */
+/* $Id: config.h 4415 2013-03-05 08:34:45Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -488,6 +488,14 @@
/**
+ * Set this to 1 to enable debugging on the group lock. Default: 0
+ */
+#ifndef PJ_GRP_LOCK_DEBUG
+# define PJ_GRP_LOCK_DEBUG 0
+#endif
+
+
+/**
* Specify this as \a stack_size argument in #pj_thread_create() to specify
* that thread should use default stack size for the current platform.
*
@@ -1122,6 +1130,14 @@
#endif
/**
+ * Simulate race condition by sleeping the thread in strategic locations.
+ * Default: no!
+ */
+#ifndef PJ_RACE_ME
+# define PJ_RACE_ME(x)
+#endif
+
+/**
* Function attributes to inform that the function may throw exception.
*
* @param x The exception list, enclosed in parenthesis.
@@ -1167,10 +1183,10 @@ PJ_BEGIN_DECL
#define PJ_VERSION_NUM_MAJOR 2
/** PJLIB version minor number. */
-#define PJ_VERSION_NUM_MINOR 0
+#define PJ_VERSION_NUM_MINOR 1
/** PJLIB version revision number. */
-#define PJ_VERSION_NUM_REV 1
+#define PJ_VERSION_NUM_REV 0
/**
* Extra suffix for the version (e.g. "-trunk"), or empty for
diff --git a/pjlib/include/pj/config_site_sample.h b/pjlib/include/pj/config_site_sample.h
index 0c7832f..845d1bb 100644
--- a/pjlib/include/pj/config_site_sample.h
+++ b/pjlib/include/pj/config_site_sample.h
@@ -302,8 +302,10 @@
* PJLIB settings.
*/
- /* Disable floating point support */
- #define PJ_HAS_FLOATING_POINT 0
+ /* Both armv6 and armv7 has FP hardware support.
+ * See https://trac.pjsip.org/repos/ticket/1589 for more info
+ */
+ #define PJ_HAS_FLOATING_POINT 1
/*
* PJMEDIA settings
diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h
index 98a735a..75aadbe 100644
--- a/pjlib/include/pj/errno.h
+++ b/pjlib/include/pj/errno.h
@@ -1,4 +1,4 @@
-/* $Id: errno.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: errno.h 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -422,6 +422,11 @@ PJ_DECL(pj_status_t) pj_register_strerror(pj_status_t start_code,
* Unsupported address family
*/
#define PJ_EAFNOTSUP (PJ_ERRNO_START_STATUS + 22)/* 70022 */
+/**
+ * @hideinitializer
+ * Object no longer exists
+ */
+#define PJ_EGONE (PJ_ERRNO_START_STATUS + 23)/* 70023 */
/** @} */ /* pj_errnum */
diff --git a/pjlib/include/pj/guid.h b/pjlib/include/pj/guid.h
index 991ce53..2a76af6 100644
--- a/pjlib/include/pj/guid.h
+++ b/pjlib/include/pj/guid.h
@@ -1,4 +1,4 @@
-/* $Id: guid.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: guid.h 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -83,6 +83,17 @@ PJ_DECL(unsigned) pj_GUID_STRING_LENGTH(void);
PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
/**
+ * Create a globally unique string in lowercase, which length is
+ * PJ_GUID_STRING_LENGTH characters. Caller is responsible for preallocating
+ * the storage used in the string.
+ *
+ * @param str The string to store the result.
+ *
+ * @return The string.
+ */
+PJ_DECL(pj_str_t*) pj_generate_unique_string_lower(pj_str_t *str);
+
+/**
* Generate a unique string.
*
* @param pool Pool to allocate memory from.
@@ -90,6 +101,14 @@ PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str);
*/
PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str);
+/**
+ * Generate a unique string in lowercase.
+ *
+ * @param pool Pool to allocate memory from.
+ * @param str The string.
+ */
+PJ_DECL(void) pj_create_unique_string_lower(pj_pool_t *pool, pj_str_t *str);
+
/**
* @}
diff --git a/pjlib/include/pj/hash.h b/pjlib/include/pj/hash.h
index 75b46c1..5d9a2d9 100644
--- a/pjlib/include/pj/hash.h
+++ b/pjlib/include/pj/hash.h
@@ -1,4 +1,4 @@
-/* $Id: hash.h 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: hash.h 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -75,8 +75,8 @@ PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval,
* string is stored in \c result.
*
* @param hval The initial hash value, normally zero.
- * @param result Buffer to store the result, which must be enough to hold
- * the string.
+ * @param result Optional. Buffer to store the result, which must be enough
+ * to hold the string.
* @param key The input key to be converted and calculated.
*
* @return The hash value.
@@ -116,6 +116,17 @@ PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht,
/**
+ * Variant of #pj_hash_get() with the key being converted to lowercase when
+ * calculating the hash value.
+ *
+ * @see pj_hash_get()
+ */
+PJ_DECL(void *) pj_hash_get_lower( pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t *hval );
+
+
+/**
* Associate/disassociate a value with the specified key. If value is not
* NULL and entry already exists, the entry's value will be overwritten.
* If value is not NULL and entry does not exist, a new one will be created
@@ -142,6 +153,17 @@ PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
/**
+ * Variant of #pj_hash_set() with the key being converted to lowercase when
+ * calculating the hash value.
+ *
+ * @see pj_hash_set()
+ */
+PJ_DECL(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t hval, void *value );
+
+
+/**
* Associate/disassociate a value with the specified key. This function works
* like #pj_hash_set(), except that it doesn't use pool (hence the np -- no
* pool suffix). If new entry needs to be allocated, it will use the entry_buf.
@@ -165,6 +187,18 @@ PJ_DECL(void) pj_hash_set_np(pj_hash_table_t *ht,
void *value);
/**
+ * Variant of #pj_hash_set_np() with the key being converted to lowercase
+ * when calculating the hash value.
+ *
+ * @see pj_hash_set_np()
+ */
+PJ_DECL(void) pj_hash_set_np_lower(pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t hval,
+ pj_hash_entry_buf entry_buf,
+ void *value);
+
+/**
* Get the total number of entries in the hash table.
*
* @param ht the hash table.
diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h
index 068e7ba..853bf48 100644
--- a/pjlib/include/pj/ioqueue.h
+++ b/pjlib/include/pj/ioqueue.h
@@ -1,4 +1,4 @@
-/* $Id: ioqueue.h 3553 2011-05-05 06:14:19Z nanang $
+/* $Id: ioqueue.h 4359 2013-02-21 11:18:36Z bennylp $
*/
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
@@ -405,6 +405,19 @@ PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
pj_ioqueue_key_t **key );
/**
+ * Variant of pj_ioqueue_register_sock() with additional group lock parameter.
+ * If group lock is set for the key, the key will add the reference counter
+ * when the socket is registered and decrease it when it is destroyed.
+ */
+PJ_DECL(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ pj_grp_lock_t *grp_lock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **key );
+
+/**
* Unregister from the I/O Queue framework. Caller must make sure that
* the key doesn't have any pending operations before calling this function,
* by calling #pj_ioqueue_is_pending() for all previously submitted
diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h
index f0d3bc5..e9b26c7 100644
--- a/pjlib/include/pj/lock.h
+++ b/pjlib/include/pj/lock.h
@@ -1,4 +1,4 @@
-/* $Id: lock.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: lock.h 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -147,6 +147,258 @@ PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock );
/** @} */
+
+/**
+ * @defgroup PJ_GRP_LOCK Group Lock
+ * @ingroup PJ_LOCK
+ * @{
+ *
+ * Group lock is a synchronization object to manage concurrency among members
+ * within the same logical group. Example of such groups are:
+ *
+ * - dialog, which has members such as the dialog itself, an invite session,
+ * and several transactions
+ * - ICE, which has members such as ICE stream transport, ICE session, STUN
+ * socket, TURN socket, and down to ioqueue key
+ *
+ * Group lock has three functions:
+ *
+ * - mutual exclusion: to protect resources from being accessed by more than
+ * one threads at the same time
+ * - session management: to make sure that the resource is not destroyed
+ * while others are still using or about to use it.
+ * - lock coordinator: to provide uniform lock ordering among more than one
+ * lock objects, which is necessary to avoid deadlock.
+ *
+ * The requirements of the group lock are:
+ *
+ * - must satisfy all the functions above
+ * - must allow members to join or leave the group (for example,
+ * transaction may be added or removed from a dialog)
+ * - must be able to synchronize with external lock (for example, a dialog
+ * lock must be able to sync itself with PJSUA lock)
+ *
+ * Please see https://trac.pjsip.org/repos/wiki/Group_Lock for more info.
+ */
+
+/**
+ * Settings for creating the group lock.
+ */
+typedef struct pj_grp_lock_config
+{
+ /**
+ * Creation flags, currently must be zero.
+ */
+ unsigned flags;
+
+} pj_grp_lock_config;
+
+
+/**
+ * Initialize the config with the default values.
+ *
+ * @param cfg The config to be initialized.
+ */
+PJ_DECL(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg);
+
+/**
+ * Create a group lock object. Initially the group lock will have reference
+ * counter of one.
+ *
+ * @param pool The group lock only uses the pool parameter to get
+ * the pool factory, from which it will create its own
+ * pool.
+ * @param cfg Optional configuration.
+ * @param p_grp_lock Pointer to receive the newly created group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_create(pj_pool_t *pool,
+ const pj_grp_lock_config *cfg,
+ pj_grp_lock_t **p_grp_lock);
+
+/**
+ * Forcibly destroy the group lock, ignoring the reference counter value.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock);
+
+/**
+ * Move the contents of the old lock to the new lock and destroy the
+ * old lock.
+ *
+ * @param old_lock The old group lock to be destroyed.
+ * @param new_lock The new group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_replace(pj_grp_lock_t *old_lock,
+ pj_grp_lock_t *new_lock);
+
+/**
+ * Acquire lock on the specified group lock.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock);
+
+/**
+ * Acquire lock on the specified group lock if it is available, otherwise
+ * return immediately wihout waiting.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock);
+
+/**
+ * Release the previously held lock. This may cause the group lock
+ * to be destroyed if it is the last one to hold the reference counter.
+ * In that case, the function will return PJ_EGONE.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock);
+
+/**
+ * Add a destructor handler, to be called by the group lock when it is
+ * about to be destroyed.
+ *
+ * @param grp_lock The group lock.
+ * @param pool Pool to allocate memory for the handler.
+ * @param member A pointer to be passed to the handler.
+ * @param handler The destroy handler.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_add_handler(pj_grp_lock_t *grp_lock,
+ pj_pool_t *pool,
+ void *member,
+ void (*handler)(void *member));
+
+/**
+ * Remove previously registered handler. All parameters must be the same
+ * as when the handler was added.
+ *
+ * @param grp_lock The group lock.
+ * @param member A pointer to be passed to the handler.
+ * @param handler The destroy handler.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_del_handler(pj_grp_lock_t *grp_lock,
+ void *member,
+ void (*handler)(void *member));
+
+/**
+ * Increment reference counter to prevent the group lock grom being destroyed.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+#if !PJ_GRP_LOCK_DEBUG
+PJ_DECL(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *grp_lock);
+
+#define pj_grp_lock_add_ref_dbg(grp_lock, x, y) pj_grp_lock_add_ref(grp_lock)
+
+#else
+
+#define pj_grp_lock_add_ref(g) pj_grp_lock_add_ref_dbg(g, __FILE__, __LINE__)
+
+PJ_DECL(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *grp_lock,
+ const char *file,
+ int line);
+#endif
+
+/**
+ * Decrement the reference counter. When the counter value reaches zero, the
+ * group lock will be destroyed and all destructor handlers will be called.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+#if !PJ_GRP_LOCK_DEBUG
+PJ_DECL(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *grp_lock);
+
+#define pj_grp_lock_dec_ref_dbg(grp_lock, x, y) pj_grp_lock_dec_ref(grp_lock)
+#else
+
+#define pj_grp_lock_dec_ref(g) pj_grp_lock_dec_ref_dbg(g, __FILE__, __LINE__)
+
+PJ_DECL(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *grp_lock,
+ const char *file,
+ int line);
+
+#endif
+
+/**
+ * Get current reference count value. This normally is only used for
+ * debugging purpose.
+ *
+ * @param grp_lock The group lock.
+ *
+ * @return The reference count value.
+ */
+PJ_DECL(int) pj_grp_lock_get_ref(pj_grp_lock_t *grp_lock);
+
+
+/**
+ * Dump group lock info for debugging purpose. If group lock debugging is
+ * enabled (via PJ_GRP_LOCK_DEBUG) macro, this will print the group lock
+ * reference counter value along with the source file and line. If
+ * debugging is disabled, this will only print the reference counter.
+ *
+ * @param grp_lock The group lock.
+ */
+PJ_DECL(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock);
+
+
+/**
+ * Synchronize an external lock with the group lock, by adding it to the
+ * list of locks to be acquired by the group lock when the group lock is
+ * acquired.
+ *
+ * The ''pos'' argument specifies the lock order and also the relative
+ * position with regard to lock ordering against the group lock. Locks with
+ * lower ''pos'' value will be locked first, and those with negative value
+ * will be locked before the group lock (the group lock's ''pos'' value is
+ * zero).
+ *
+ * @param grp_lock The group lock.
+ * @param ext_lock The external lock
+ * @param pos The position.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_chain_lock(pj_grp_lock_t *grp_lock,
+ pj_lock_t *ext_lock,
+ int pos);
+
+/**
+ * Remove an external lock from group lock's list of synchronized locks.
+ *
+ * @param grp_lock The group lock.
+ * @param ext_lock The external lock
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_grp_lock_unchain_lock(pj_grp_lock_t *grp_lock,
+ pj_lock_t *ext_lock);
+
+
+/** @} */
+
+
PJ_END_DECL
diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h
index 5738d4f..73c8473 100644
--- a/pjlib/include/pj/pool.h
+++ b/pjlib/include/pj/pool.h
@@ -1,4 +1,4 @@
-/* $Id: pool.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: pool.h 4298 2012-11-22 05:00:01Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -509,7 +509,7 @@ PJ_INLINE(void*) pj_pool_zalloc(pj_pool_t *pool, pj_size_t size)
* Internal functions
*/
PJ_IDECL(void*) pj_pool_alloc_from_block(pj_pool_block *block, pj_size_t size);
-PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size);
+PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size);
diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h
index ea4fa2d..376fd6a 100644
--- a/pjlib/include/pj/pool_i.h
+++ b/pjlib/include/pj/pool_i.h
@@ -1,4 +1,4 @@
-/* $Id: pool_i.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: pool_i.h 4298 2012-11-22 05:00:01Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -47,7 +47,7 @@ PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_block *block, pj_size_t size )
if (size & (PJ_POOL_ALIGNMENT-1)) {
size = (size + PJ_POOL_ALIGNMENT) & ~(PJ_POOL_ALIGNMENT-1);
}
- if ((unsigned)(block->end - block->cur) >= size) {
+ if ((pj_size_t)(block->end - block->cur) >= size) {
void *ptr = block->cur;
block->cur += size;
return ptr;
diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h
index 2fcd896..5692d61 100644
--- a/pjlib/include/pj/sock.h
+++ b/pjlib/include/pj/sock.h
@@ -1,4 +1,4 @@
-/* $Id: sock.h 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: sock.h 4343 2013-02-07 09:35:34Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -1166,6 +1166,24 @@ PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd,
pj_uint32_t addr,
pj_uint16_t port);
+/**
+ * Bind the IP socket sockfd to the given address and a random port in the
+ * specified range.
+ *
+ * @param sockfd The socket desriptor.
+ * @param addr The local address and port to bind the socket to.
+ * @param port_range The port range, relative the to start port number
+ * specified in port field in #addr. Note that if the
+ * port is zero, this param will be ignored.
+ * @param max_try Maximum retries.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ pj_uint16_t port_range,
+ pj_uint16_t max_try);
+
#if PJ_HAS_TCP
/**
* Listen for incoming connection. This function only applies to connection
diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h
index 1cc11cf..a89c30b 100644
--- a/pjlib/include/pj/timer.h
+++ b/pjlib/include/pj/timer.h
@@ -1,4 +1,4 @@
-/* $Id: timer.h 4154 2012-06-05 10:41:17Z bennylp $ */
+/* $Id: timer.h 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* 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
@@ -24,6 +24,7 @@
*/
#include <pj/types.h>
+#include <pj/lock.h>
PJ_BEGIN_DECL
@@ -118,6 +119,12 @@ typedef struct pj_timer_entry
*/
pj_time_val _timer_value;
+ /**
+ * Internal: the group lock used by this entry, set when
+ * pj_timer_heap_schedule_w_lock() is used.
+ */
+ pj_grp_lock_t *_grp_lock;
+
#if PJ_TIMER_DEBUG
const char *src_file;
int src_line;
@@ -229,7 +236,46 @@ PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
#endif /* PJ_TIMER_DEBUG */
/**
- * Cancel a previously registered timer.
+ * Schedule a timer entry which will expire AFTER the specified delay, and
+ * increment the reference counter of the group lock while the timer entry
+ * is active. The group lock reference counter will automatically be released
+ * after the timer callback is called or when the timer is cancelled.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be registered.
+ * @param id_val The value to be set to the "id" field of the timer entry
+ * once the timer is scheduled.
+ * @param delay The interval to expire.
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+# define pj_timer_heap_schedule_w_grp_lock(ht,e,d,id,g) \
+ pj_timer_heap_schedule_w_grp_lock_dbg(ht,e,d,id,g,__FILE__,__LINE__)
+
+ PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock,
+ const char *src_file,
+ int src_line);
+#else
+PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock);
+#endif /* PJ_TIMER_DEBUG */
+
+
+/**
+ * Cancel a previously registered timer. This will also decrement the
+ * reference counter of the group lock associated with the timer entry,
+ * if the entry was scheduled with one.
*
* @param ht The timer heap.
* @param entry The entry to be cancelled.
@@ -241,6 +287,24 @@ PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
pj_timer_entry *entry);
/**
+ * Cancel only if the previously registered timer is active. This will
+ * also decrement the reference counter of the group lock associated
+ * with the timer entry, if the entry was scheduled with one. In any
+ * case, set the "id" to the specified value.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be cancelled.
+ * @param id_val Value to be set to "id"
+ *
+ * @return The number of timer cancelled, which should be one if the
+ * entry has really been registered, or zero if no timer was
+ * cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int id_val);
+
+/**
* Get the number of timer entries.
*
* @param ht The timer heap.
diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h
index 99c54db..a615585 100644
--- a/pjlib/include/pj/types.h
+++ b/pjlib/include/pj/types.h
@@ -1,4 +1,4 @@
-/* $Id: types.h 4154 2012-06-05 10:41:17Z bennylp $ */
+/* $Id: types.h 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -231,6 +231,9 @@ typedef struct pj_thread_t pj_thread_t;
/** Lock object. */
typedef struct pj_lock_t pj_lock_t;
+/** Group lock */
+typedef struct pj_grp_lock_t pj_grp_lock_t;
+
/** Mutex handle. */
typedef struct pj_mutex_t pj_mutex_t;
diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c
index 5c91383..3eaf027 100644
--- a/pjlib/src/pj/activesock.c
+++ b/pjlib/src/pj/activesock.c
@@ -1,4 +1,4 @@
-/* $Id: activesock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: activesock.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -43,6 +43,13 @@ enum read_type
TYPE_RECV_FROM
};
+enum shutdown_dir
+{
+ SHUT_NONE = 0,
+ SHUT_RX = 1,
+ SHUT_TX = 2
+};
+
struct read_op
{
pj_ioqueue_op_key_t op_key;
@@ -77,6 +84,7 @@ struct pj_activesock_t
pj_ioqueue_t *ioqueue;
void *user_data;
unsigned async_count;
+ unsigned shutdown;
unsigned max_loop;
pj_activesock_cb cb;
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
@@ -209,8 +217,9 @@ PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool,
ioq_cb.on_accept_complete = &ioqueue_on_accept_complete;
#endif
- status = pj_ioqueue_register_sock(pool, ioqueue, sock, asock,
- &ioq_cb, &asock->key);
+ status = pj_ioqueue_register_sock2(pool, ioqueue, sock,
+ (opt? opt->grp_lock : NULL),
+ asock, &ioq_cb, &asock->key);
if (status != PJ_SUCCESS) {
pj_activesock_close(asock);
return status;
@@ -283,10 +292,10 @@ PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool,
return PJ_SUCCESS;
}
-
PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock)
{
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
+ asock->shutdown = SHUT_RX | SHUT_TX;
if (asock->key) {
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
@@ -448,6 +457,10 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+ /* Ignore if we've been shutdown */
+ if (asock->shutdown & SHUT_RX)
+ return;
+
do {
unsigned flags;
@@ -569,6 +582,10 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
if (!ret)
return;
+ /* Also stop further read if we've been shutdown */
+ if (asock->shutdown & SHUT_RX)
+ return;
+
/* Only stream oriented socket may leave data in the packet */
if (asock->stream_oriented) {
r->size = remainder;
@@ -648,6 +665,9 @@ PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock,
{
PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL);
+ if (asock->shutdown & SHUT_TX)
+ return PJ_EINVALIDOP;
+
send_key->activesock_data = NULL;
if (asock->whole_data) {
@@ -698,6 +718,9 @@ PJ_DEF(pj_status_t) pj_activesock_sendto( pj_activesock_t *asock,
PJ_ASSERT_RETURN(asock && send_key && data && size && addr && addr_len,
PJ_EINVAL);
+ if (asock->shutdown & SHUT_TX)
+ return PJ_EINVALIDOP;
+
return pj_ioqueue_sendto(asock->key, send_key, data, size, flags,
addr, addr_len);
}
@@ -711,6 +734,13 @@ static void ioqueue_on_write_complete(pj_ioqueue_key_t *key,
asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+ /* Ignore if we've been shutdown. This may cause data to be partially
+ * sent even when 'wholedata' was requested if the OS only sent partial
+ * buffer.
+ */
+ if (asock->shutdown & SHUT_TX)
+ return;
+
if (bytes_sent > 0 && op_key->activesock_data) {
/* whole_data is requested. Make sure we send all the data */
struct send_data *sd = (struct send_data*)op_key->activesock_data;
@@ -756,6 +786,10 @@ PJ_DEF(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock,
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
PJ_ASSERT_RETURN(asock->accept_op==NULL, PJ_EINVALIDOP);
+ /* Ignore if we've been shutdown */
+ if (asock->shutdown)
+ return PJ_EINVALIDOP;
+
asock->accept_op = (struct accept_op*)
pj_pool_calloc(pool, asock->async_count,
sizeof(struct accept_op));
@@ -798,6 +832,10 @@ static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
PJ_UNUSED_ARG(new_sock);
+ /* Ignore if we've been shutdown */
+ if (asock->shutdown)
+ return;
+
do {
if (status == asock->last_err && status != PJ_SUCCESS) {
asock->err_counter++;
@@ -835,6 +873,10 @@ static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
pj_sock_close(accept_op->new_sock);
}
+ /* Don't start another accept() if we've been shutdown */
+ if (asock->shutdown)
+ return;
+
/* Prepare next accept() */
accept_op->new_sock = PJ_INVALID_SOCKET;
accept_op->rem_addr_len = sizeof(accept_op->rem_addr);
@@ -853,6 +895,10 @@ PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock,
int addr_len)
{
PJ_UNUSED_ARG(pool);
+
+ if (asock->shutdown)
+ return PJ_EINVALIDOP;
+
return pj_ioqueue_connect(asock->key, remaddr, addr_len);
}
@@ -861,6 +907,10 @@ static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key,
{
pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+ /* Ignore if we've been shutdown */
+ if (asock->shutdown)
+ return;
+
if (asock->cb.on_connect_complete) {
pj_bool_t ret;
diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c
index 1cb8e72..a0f7406 100644
--- a/pjlib/src/pj/errno.c
+++ b/pjlib/src/pj/errno.c
@@ -1,4 +1,4 @@
-/* $Id: errno.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: errno.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -77,7 +77,8 @@ static const struct
PJ_BUILD_ERR(PJ_ETOOSMALL, "Size is too short"),
PJ_BUILD_ERR(PJ_EIGNORED, "Ignored"),
PJ_BUILD_ERR(PJ_EIPV6NOTSUP, "IPv6 is not supported"),
- PJ_BUILD_ERR(PJ_EAFNOTSUP, "Unsupported address family")
+ PJ_BUILD_ERR(PJ_EAFNOTSUP, "Unsupported address family"),
+ PJ_BUILD_ERR(PJ_EGONE, "Object no longer exists")
};
#endif /* PJ_HAS_ERROR_STRING */
diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c
index 8ad5f78..88b6c37 100644
--- a/pjlib/src/pj/guid.c
+++ b/pjlib/src/pj/guid.c
@@ -1,4 +1,4 @@
-/* $Id: guid.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: guid.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -17,11 +17,32 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <pj/ctype.h>
#include <pj/guid.h>
#include <pj/pool.h>
+PJ_DEF(pj_str_t*) pj_generate_unique_string_lower(pj_str_t *str)
+{
+ int i;
+
+ pj_generate_unique_string(str);
+ for (i = 0; i < str->slen; i++)
+ str->ptr[i] = (char)pj_tolower(str->ptr[i]);
+
+ return str;
+}
+
PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
{
str->ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
pj_generate_unique_string(str);
}
+
+PJ_DEF(void) pj_create_unique_string_lower(pj_pool_t *pool, pj_str_t *str)
+{
+ int i;
+
+ pj_create_unique_string(pool, str);
+ for (i = 0; i < str->slen; i++)
+ str->ptr[i] = (char)pj_tolower(str->ptr[i]);
+}
diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c
index 00f167f..b37e8ff 100644
--- a/pjlib/src/pj/hash.c
+++ b/pjlib/src/pj/hash.c
@@ -1,4 +1,4 @@
-/* $Id: hash.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: hash.c 4296 2012-11-07 04:56:26Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -79,16 +79,21 @@ PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
#if defined(PJ_HASH_USE_OWN_TOLOWER) && PJ_HASH_USE_OWN_TOLOWER != 0
for (i=0; i<key->slen; ++i) {
pj_uint8_t c = key->ptr[i];
+ char lower;
if (c & 64)
- result[i] = (char)(c | 32);
+ lower = (char)(c | 32);
else
- result[i] = (char)c;
- hval = hval * PJ_HASH_MULTIPLIER + result[i];
+ lower = (char)c;
+ if (result)
+ result[i] = lower;
+ hval = hval * PJ_HASH_MULTIPLIER + lower;
}
#else
for (i=0; i<key->slen; ++i) {
- result[i] = (char)pj_tolower(key->ptr[i]);
- hval = hval * PJ_HASH_MULTIPLIER + result[i];
+ char lower = (char)pj_tolower(key->ptr[i]);
+ if (result)
+ result[i] = lower;
+ hval = hval * PJ_HASH_MULTIPLIER + lower;
}
#endif
@@ -128,7 +133,7 @@ PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
const void *key, unsigned keylen,
void *val, pj_uint32_t *hval,
- void *entry_buf)
+ void *entry_buf, pj_bool_t lower)
{
pj_uint32_t hash;
pj_hash_entry **p_entry, *entry;
@@ -146,14 +151,20 @@ static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
if (keylen==PJ_HASH_KEY_STRING) {
const pj_uint8_t *p = (const pj_uint8_t*)key;
for ( ; *p; ++p ) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
+ if (lower)
+ hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
+ else
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
}
keylen = p - (const unsigned char*)key;
} else {
const pj_uint8_t *p = (const pj_uint8_t*)key,
*end = p + keylen;
for ( ; p!=end; ++p) {
- hash = hash * PJ_HASH_MULTIPLIER + *p;
+ if (lower)
+ hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
+ else
+ hash = hash * PJ_HASH_MULTIPLIER + *p;
}
}
@@ -168,9 +179,11 @@ static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht,
p_entry = &entry->next, entry = *p_entry)
{
if (entry->hash==hash && entry->keylen==keylen &&
- pj_memcmp(entry->key, key, keylen)==0)
+ ((lower && pj_ansi_strnicmp((const char*)entry->key,
+ (const char*)key, keylen)==0) ||
+ (!lower && pj_memcmp(entry->key, key, keylen)==0)))
{
- break;
+ break;
}
}
@@ -214,17 +227,27 @@ PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
pj_uint32_t *hval)
{
pj_hash_entry *entry;
- entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL);
+ entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_FALSE);
return entry ? entry->value : NULL;
}
-PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
- const void *key, unsigned keylen, pj_uint32_t hval,
- void *value )
+PJ_DEF(void *) pj_hash_get_lower( pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t *hval)
+{
+ pj_hash_entry *entry;
+ entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_TRUE);
+ return entry ? entry->value : NULL;
+}
+
+static void hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen, pj_uint32_t hval,
+ void *value, void *entry_buf, pj_bool_t lower )
{
pj_hash_entry **p_entry;
- p_entry = find_entry( pool, ht, key, keylen, value, &hval, NULL);
+ p_entry = find_entry( pool, ht, key, keylen, value, &hval, entry_buf,
+ lower);
if (*p_entry) {
if (value == NULL) {
/* delete entry */
@@ -241,29 +264,35 @@ PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
}
}
+PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen, pj_uint32_t hval,
+ void *value )
+{
+ hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_FALSE);
+}
+
+PJ_DEF(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t hval, void *value )
+{
+ hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_TRUE);
+}
+
PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht,
const void *key, unsigned keylen,
pj_uint32_t hval, pj_hash_entry_buf entry_buf,
void *value)
{
- pj_hash_entry **p_entry;
+ hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_FALSE);
+}
- p_entry = find_entry( NULL, ht, key, keylen, value, &hval,
- (void*)entry_buf );
- if (*p_entry) {
- if (value == NULL) {
- /* delete entry */
- PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
- *p_entry = (*p_entry)->next;
- --ht->count;
-
- } else {
- /* overwrite */
- (*p_entry)->value = value;
- PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht,
- *p_entry, value));
- }
- }
+PJ_DEF(void) pj_hash_set_np_lower( pj_hash_table_t *ht,
+ const void *key, unsigned keylen,
+ pj_uint32_t hval,
+ pj_hash_entry_buf entry_buf,
+ void *value)
+{
+ hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_TRUE);
}
PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c
index ee4506d..b8036a5 100644
--- a/pjlib/src/pj/ioqueue_common_abs.c
+++ b/pjlib/src/pj/ioqueue_common_abs.c
@@ -1,4 +1,4 @@
-/* $Id: ioqueue_common_abs.c 3666 2011-07-19 08:40:20Z nanang $ */
+/* $Id: ioqueue_common_abs.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -70,6 +70,7 @@ static pj_status_t ioqueue_init_key( pj_pool_t *pool,
pj_ioqueue_t *ioqueue,
pj_ioqueue_key_t *key,
pj_sock_t sock,
+ pj_grp_lock_t *grp_lock,
void *user_data,
const pj_ioqueue_callback *cb)
{
@@ -114,10 +115,18 @@ static pj_status_t ioqueue_init_key( pj_pool_t *pool,
/* Create mutex for the key. */
#if !PJ_IOQUEUE_HAS_SAFE_UNREG
- rc = pj_mutex_create_simple(pool, NULL, &key->mutex);
+ rc = pj_lock_create_simple_mutex(poll, NULL, &key->lock);
#endif
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ /* Group lock */
+ key->grp_lock = grp_lock;
+ if (key->grp_lock) {
+ pj_grp_lock_add_ref_dbg(key->grp_lock, "ioqueue", 0);
+ }
- return rc;
+ return PJ_SUCCESS;
}
/*
@@ -189,10 +198,10 @@ PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key)
void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
{
/* Lock the key. */
- pj_mutex_lock(h->mutex);
+ pj_ioqueue_lock_key(h);
if (IS_CLOSING(h)) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
return;
}
@@ -261,7 +270,7 @@ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
* save it to a flag.
*/
has_lock = PJ_FALSE;
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
} else {
has_lock = PJ_TRUE;
}
@@ -272,7 +281,7 @@ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
/* Unlock if we still hold the lock */
if (has_lock) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
/* Done. */
@@ -379,7 +388,8 @@ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
* save it to a flag.
*/
has_lock = PJ_FALSE;
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
+ PJ_RACE_ME(5);
} else {
has_lock = PJ_TRUE;
}
@@ -392,11 +402,11 @@ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
}
if (has_lock) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
} else {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
/* Done. */
@@ -406,7 +416,7 @@ void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
* are signalled for the same event, but only one thread eventually
* able to process the event.
*/
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
}
@@ -415,10 +425,10 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
pj_status_t rc;
/* Lock the key. */
- pj_mutex_lock(h->mutex);
+ pj_ioqueue_lock_key(h);
if (IS_CLOSING(h)) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
return;
}
@@ -453,7 +463,8 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
* save it to a flag.
*/
has_lock = PJ_FALSE;
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
+ PJ_RACE_ME(5);
} else {
has_lock = PJ_TRUE;
}
@@ -466,7 +477,7 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
}
if (has_lock) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
}
else
@@ -567,7 +578,8 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
* save it to a flag.
*/
has_lock = PJ_FALSE;
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
+ PJ_RACE_ME(5);
} else {
has_lock = PJ_TRUE;
}
@@ -580,7 +592,7 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
}
if (has_lock) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
} else {
@@ -589,7 +601,7 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
* are signalled for the same event, but only one thread eventually
* able to process the event.
*/
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
}
@@ -599,19 +611,19 @@ void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
{
pj_bool_t has_lock;
- pj_mutex_lock(h->mutex);
+ pj_ioqueue_lock_key(h);
if (!h->connecting) {
/* It is possible that more than one thread was woken up, thus
* the remaining thread will see h->connecting as zero because
* it has been processed by other thread.
*/
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
return;
}
if (IS_CLOSING(h)) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
return;
}
@@ -629,7 +641,8 @@ void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
* save it to a flag.
*/
has_lock = PJ_FALSE;
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
+ PJ_RACE_ME(5);
} else {
has_lock = PJ_TRUE;
}
@@ -651,7 +664,7 @@ void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue,
}
if (has_lock) {
- pj_mutex_unlock(h->mutex);
+ pj_ioqueue_unlock_key(h);
}
}
@@ -713,18 +726,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
read_op->size = *length;
read_op->flags = flags;
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous check
* in multithreaded app. If we add bad handle to the set it will
* corrupt the ioqueue set. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
pj_list_insert_before(&key->read_list, read_op);
ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
}
@@ -789,18 +802,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
read_op->rmt_addr = addr;
read_op->rmt_addrlen = addrlen;
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous check
* in multithreaded app. If we add bad handle to the set it will
* corrupt the ioqueue set. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
pj_list_insert_before(&key->read_list, read_op);
ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
}
@@ -903,18 +916,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
write_op->written = 0;
write_op->flags = flags;
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous check
* in multithreaded app. If we add bad handle to the set it will
* corrupt the ioqueue set. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
pj_list_insert_before(&key->write_list, write_op);
ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
}
@@ -1050,18 +1063,18 @@ retry_on_restart:
pj_memcpy(&write_op->rmt_addr, addr, addrlen);
write_op->rmt_addrlen = addrlen;
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous check
* in multithreaded app. If we add bad handle to the set it will
* corrupt the ioqueue set. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
pj_list_insert_before(&key->write_list, write_op);
ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
}
@@ -1127,18 +1140,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
accept_op->addrlen= addrlen;
accept_op->local_addr = local;
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous check
* in multithreaded app. If we add bad handle to the set it will
* corrupt the ioqueue set. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
pj_list_insert_before(&key->accept_list, accept_op);
ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
}
@@ -1171,18 +1184,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
} else {
if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) {
/* Pending! */
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Check again. Handle may have been closed after the previous
* check in multithreaded app. See #913
*/
if (IS_CLOSING(key)) {
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_ECANCELLED;
}
key->connecting = PJ_TRUE;
ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
ioqueue_add_to_set(key->ioqueue, key, EXCEPTION_EVENT);
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EPENDING;
} else {
/* Error! */
@@ -1228,7 +1241,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
* Find the operation key in all pending operation list to
* really make sure that it's still there; then call the callback.
*/
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Find the operation in the pending read list. */
op_rec = (struct generic_operation*)key->read_list.next;
@@ -1236,7 +1249,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
if (op_rec == (void*)op_key) {
pj_list_erase(op_rec);
op_rec->op = PJ_IOQUEUE_OP_NONE;
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
(*key->cb.on_read_complete)(key, op_key, bytes_status);
return PJ_SUCCESS;
@@ -1250,7 +1263,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
if (op_rec == (void*)op_key) {
pj_list_erase(op_rec);
op_rec->op = PJ_IOQUEUE_OP_NONE;
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
(*key->cb.on_write_complete)(key, op_key, bytes_status);
return PJ_SUCCESS;
@@ -1264,7 +1277,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
if (op_rec == (void*)op_key) {
pj_list_erase(op_rec);
op_rec->op = PJ_IOQUEUE_OP_NONE;
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
(*key->cb.on_accept_complete)(key, op_key,
PJ_INVALID_SOCKET,
@@ -1274,7 +1287,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
op_rec = op_rec->next;
}
- pj_mutex_unlock(key->mutex);
+ pj_ioqueue_unlock_key(key);
return PJ_EINVALIDOP;
}
@@ -1304,11 +1317,18 @@ PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key,
PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key)
{
- return pj_mutex_lock(key->mutex);
+ if (key->grp_lock)
+ return pj_grp_lock_acquire(key->grp_lock);
+ else
+ return pj_lock_acquire(key->lock);
}
PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
{
- return pj_mutex_unlock(key->mutex);
+ if (key->grp_lock)
+ return pj_grp_lock_release(key->grp_lock);
+ else
+ return pj_lock_release(key->lock);
}
+
diff --git a/pjlib/src/pj/ioqueue_common_abs.h b/pjlib/src/pj/ioqueue_common_abs.h
index 3a41051..3bdbb52 100644
--- a/pjlib/src/pj/ioqueue_common_abs.h
+++ b/pjlib/src/pj/ioqueue_common_abs.h
@@ -101,7 +101,8 @@ union operation_key
#define DECLARE_COMMON_KEY \
PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t); \
pj_ioqueue_t *ioqueue; \
- pj_mutex_t *mutex; \
+ pj_grp_lock_t *grp_lock; \
+ pj_lock_t *lock; \
pj_bool_t inside_callback; \
pj_bool_t destroy_requested; \
pj_bool_t allow_concurrent; \
diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c
index 27845f5..c2564d1 100644
--- a/pjlib/src/pj/ioqueue_epoll.c
+++ b/pjlib/src/pj/ioqueue_epoll.c
@@ -1,4 +1,4 @@
-/* $Id: ioqueue_epoll.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ioqueue_epoll.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -262,11 +262,11 @@ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
key = PJ_POOL_ALLOC_T(pool, pj_ioqueue_key_t);
key->ref_count = 0;
- rc = pj_mutex_create_recursive(pool, NULL, &key->mutex);
+ rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock);
if (rc != PJ_SUCCESS) {
key = ioqueue->free_list.next;
while (key != &ioqueue->free_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
pj_mutex_destroy(ioqueue->ref_cnt_mutex);
@@ -323,19 +323,19 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
/* Destroy reference counters */
key = ioqueue->active_list.next;
while (key != &ioqueue->active_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
key = ioqueue->closing_list.next;
while (key != &ioqueue->closing_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
key = ioqueue->free_list.next;
while (key != &ioqueue->free_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
@@ -422,7 +422,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
if (status < 0) {
rc = pj_get_os_error();
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = NULL;
TRACE_((THIS_FILE,
"pj_ioqueue_register_sock error: os_epoll_ctl rc=%d",
@@ -497,7 +497,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
* the key. We need to lock the key before ioqueue here to prevent
* deadlock.
*/
- pj_mutex_lock(key->mutex);
+ pj_lock_acquire(key->lock);
/* Also lock ioqueue */
pj_lock_acquire(ioqueue->lock);
@@ -531,9 +531,9 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
decrement_counter(key);
/* Done. */
- pj_mutex_unlock(key->mutex);
+ pj_lock_release(key->lock);
#else
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
#endif
return PJ_SUCCESS;
diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c
index 74b87d8..1b08d28 100644
--- a/pjlib/src/pj/ioqueue_select.c
+++ b/pjlib/src/pj/ioqueue_select.c
@@ -1,4 +1,4 @@
-/* $Id: ioqueue_select.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ioqueue_select.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -39,6 +39,7 @@
#include <pj/sock_select.h>
#include <pj/sock_qos.h>
#include <pj/errno.h>
+#include <pj/rand.h>
/* Now that we have access to OS'es <sys/select>, lets check again that
* PJ_IOQUEUE_MAX_HANDLES is not greater than FD_SETSIZE
@@ -237,11 +238,11 @@ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
key = PJ_POOL_ALLOC_T(pool, pj_ioqueue_key_t);
key->ref_count = 0;
- rc = pj_mutex_create_recursive(pool, NULL, &key->mutex);
+ rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock);
if (rc != PJ_SUCCESS) {
key = ioqueue->free_list.next;
while (key != &ioqueue->free_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
pj_mutex_destroy(ioqueue->ref_cnt_mutex);
@@ -284,19 +285,19 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
/* Destroy reference counters */
key = ioqueue->active_list.next;
while (key != &ioqueue->active_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
key = ioqueue->closing_list.next;
while (key != &ioqueue->closing_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
key = ioqueue->free_list.next;
while (key != &ioqueue->free_list) {
- pj_mutex_destroy(key->mutex);
+ pj_lock_destroy(key->lock);
key = key->next;
}
@@ -312,9 +313,10 @@ PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
*
* Register socket handle to ioqueue.
*/
-PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
pj_ioqueue_t *ioqueue,
pj_sock_t sock,
+ pj_grp_lock_t *grp_lock,
void *user_data,
const pj_ioqueue_callback *cb,
pj_ioqueue_key_t **p_key)
@@ -358,7 +360,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
#endif
- rc = ioqueue_init_key(pool, ioqueue, key, sock, user_data, cb);
+ rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb);
if (rc != PJ_SUCCESS) {
key = NULL;
goto on_return;
@@ -386,12 +388,27 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
on_return:
/* On error, socket may be left in non-blocking mode. */
+ if (rc != PJ_SUCCESS) {
+ if (key->grp_lock)
+ pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0);
+ }
*p_key = key;
pj_lock_release(ioqueue->lock);
return rc;
}
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data,
+ cb, p_key);
+}
+
#if PJ_IOQUEUE_HAS_SAFE_UNREG
/* Increment key's reference counter */
static void increment_counter(pj_ioqueue_key_t *key)
@@ -446,7 +463,7 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
* the key. We need to lock the key before ioqueue here to prevent
* deadlock.
*/
- pj_mutex_lock(key->mutex);
+ pj_ioqueue_lock_key(key);
/* Also lock ioqueue */
pj_lock_acquire(ioqueue->lock);
@@ -485,9 +502,34 @@ PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
decrement_counter(key);
/* Done. */
- pj_mutex_unlock(key->mutex);
+ if (key->grp_lock) {
+ /* just dec_ref and unlock. we will set grp_lock to NULL
+ * elsewhere */
+ pj_grp_lock_t *grp_lock = key->grp_lock;
+ // Don't set grp_lock to NULL otherwise the other thread
+ // will crash. Just leave it as dangling pointer, but this
+ // should be safe
+ //key->grp_lock = NULL;
+ pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+ pj_grp_lock_release(grp_lock);
+ } else {
+ pj_ioqueue_unlock_key(key);
+ }
#else
- pj_mutex_destroy(key->mutex);
+ if (key->grp_lock) {
+ /* set grp_lock to NULL and unlock */
+ pj_grp_lock_t *grp_lock = key->grp_lock;
+ // Don't set grp_lock to NULL otherwise the other thread
+ // will crash. Just leave it as dangling pointer, but this
+ // should be safe
+ //key->grp_lock = NULL;
+ pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+ pj_grp_lock_release(grp_lock);
+ } else {
+ pj_ioqueue_unlock_key(key);
+ }
+
+ pj_lock_destroy(key->lock);
#endif
return PJ_SUCCESS;
@@ -620,6 +662,10 @@ static void scan_closing_keys(pj_ioqueue_t *ioqueue)
if (PJ_TIME_VAL_GTE(now, h->free_time)) {
pj_list_erase(h);
+ // Don't set grp_lock to NULL otherwise the other thread
+ // will crash. Just leave it as dangling pointer, but this
+ // should be safe
+ //h->grp_lock = NULL;
pj_list_push_back(&ioqueue->free_list, h);
}
h = next;
@@ -781,7 +827,7 @@ on_error:
PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
{
pj_fd_set_t rfdset, wfdset, xfdset;
- int count, counter;
+ int count, i, counter;
pj_ioqueue_key_t *h;
struct event
{
@@ -892,8 +938,17 @@ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
#endif
}
+ for (i=0; i<counter; ++i) {
+ if (event[i].key->grp_lock)
+ pj_grp_lock_add_ref_dbg(event[i].key->grp_lock, "ioqueue", 0);
+ }
+
+ PJ_RACE_ME(5);
+
pj_lock_release(ioqueue->lock);
+ PJ_RACE_ME(5);
+
count = counter;
/* Now process all events. The dispatch functions will take care
@@ -918,6 +973,10 @@ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
#if PJ_IOQUEUE_HAS_SAFE_UNREG
decrement_counter(event[counter].key);
#endif
+
+ if (event[counter].key->grp_lock)
+ pj_grp_lock_dec_ref_dbg(event[counter].key->grp_lock,
+ "ioqueue", 0);
}
diff --git a/pjlib/src/pj/ioqueue_symbian.cpp b/pjlib/src/pj/ioqueue_symbian.cpp
index 01f4db5..0b310d1 100644
--- a/pjlib/src/pj/ioqueue_symbian.cpp
+++ b/pjlib/src/pj/ioqueue_symbian.cpp
@@ -1,4 +1,4 @@
-/* $Id: ioqueue_symbian.cpp 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: ioqueue_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -528,6 +528,19 @@ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
return PJ_SUCCESS;
}
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ pj_sock_t sock,
+ pj_grp_lock_t *grp_lock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **p_key)
+{
+ PJ_UNUSED_ARG(grp_lock);
+
+ return pj_ioqueue_register_sock(pool, ioqueue, sock, user_data, cb, p_key);
+}
+
/*
* Unregister from the I/O Queue framework.
*/
diff --git a/pjlib/src/pj/ip_helper_generic.c b/pjlib/src/pj/ip_helper_generic.c
index 3a43423..94d5a1f 100644
--- a/pjlib/src/pj/ip_helper_generic.c
+++ b/pjlib/src/pj/ip_helper_generic.c
@@ -1,4 +1,4 @@
-/* $Id: ip_helper_generic.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ip_helper_generic.c 4355 2013-02-19 16:27:37Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -165,9 +165,6 @@ static pj_status_t if_enum_by_af(int af,
return PJ_RETURN_OS_ERROR(oserr);
}
- /* Done with socket */
- pj_sock_close(sock);
-
/* Interface interfaces */
ifr = (struct ifreq*) ifc.ifc_req;
count = ifc.ifc_len / sizeof(struct ifreq);
@@ -177,6 +174,7 @@ static pj_status_t if_enum_by_af(int af,
*p_cnt = 0;
for (i=0; i<count; ++i) {
struct ifreq *itf = &ifr[i];
+ struct ifreq iff = *itf;
struct sockaddr *ad = &itf->ifr_addr;
TRACE_((THIS_FILE, " checking interface %s", itf->ifr_name));
@@ -188,13 +186,19 @@ static pj_status_t if_enum_by_af(int af,
continue;
}
- if ((itf->ifr_flags & IFF_UP)==0) {
+ if (ioctl(sock, SIOCGIFFLAGS, &iff) != 0) {
+ TRACE_((THIS_FILE, " ioctl(SIOCGIFFLAGS) failed: %s",
+ get_os_errmsg()));
+ continue; /* Failed to get flags, continue */
+ }
+
+ if ((iff.ifr_flags & IFF_UP)==0) {
TRACE_((THIS_FILE, " interface is down"));
continue; /* Skip when interface is down */
}
#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
- if (itf->ifr_flags & IFF_LOOPBACK) {
+ if (iff.ifr_flags & IFF_LOOPBACK) {
TRACE_((THIS_FILE, " loopback interface"));
continue; /* Skip loopback interface */
}
@@ -220,10 +224,14 @@ static pj_status_t if_enum_by_af(int af,
(*p_cnt)++;
}
+ /* Done with socket */
+ pj_sock_close(sock);
+
TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt));
return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND;
}
+
#elif defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0
/* Note: this does not work with IPv6 */
static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[])
diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c
index a7879af..34e2d1e 100644
--- a/pjlib/src/pj/lock.c
+++ b/pjlib/src/pj/lock.c
@@ -1,4 +1,4 @@
-/* $Id: lock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: lock.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -20,10 +20,12 @@
#include <pj/lock.h>
#include <pj/os.h>
#include <pj/assert.h>
+#include <pj/log.h>
#include <pj/pool.h>
#include <pj/string.h>
#include <pj/errno.h>
+#define THIS_FILE "lock.c"
typedef void LOCK_OBJ;
@@ -196,3 +198,518 @@ PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
return (*lock->destroy)(lock->lock_object);
}
+
+/******************************************************************************
+ * Group lock
+ */
+
+/* Individual lock in the group lock */
+typedef struct grp_lock_item
+{
+ PJ_DECL_LIST_MEMBER(struct grp_lock_item);
+ int prio;
+ pj_lock_t *lock;
+
+} grp_lock_item;
+
+/* Destroy callbacks */
+typedef struct grp_destroy_callback
+{
+ PJ_DECL_LIST_MEMBER(struct grp_destroy_callback);
+ void *comp;
+ void (*handler)(void*);
+} grp_destroy_callback;
+
+#if PJ_GRP_LOCK_DEBUG
+/* Store each add_ref caller */
+typedef struct grp_lock_ref
+{
+ PJ_DECL_LIST_MEMBER(struct grp_lock_ref);
+ const char *file;
+ int line;
+} grp_lock_ref;
+#endif
+
+/* The group lock */
+struct pj_grp_lock_t
+{
+ pj_lock_t base;
+
+ pj_pool_t *pool;
+ pj_atomic_t *ref_cnt;
+ pj_lock_t *own_lock;
+
+ pj_thread_t *owner;
+ int owner_cnt;
+
+ grp_lock_item lock_list;
+ grp_destroy_callback destroy_list;
+
+#if PJ_GRP_LOCK_DEBUG
+ grp_lock_ref ref_list;
+ grp_lock_ref ref_free_list;
+#endif
+};
+
+
+PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg)
+{
+ pj_bzero(cfg, sizeof(*cfg));
+}
+
+static void grp_lock_set_owner_thread(pj_grp_lock_t *glock)
+{
+ if (!glock->owner) {
+ glock->owner = pj_thread_this();
+ glock->owner_cnt = 1;
+ } else {
+ pj_assert(glock->owner == pj_thread_this());
+ glock->owner_cnt++;
+ }
+}
+
+static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock)
+{
+ pj_assert(glock->owner == pj_thread_this());
+ pj_assert(glock->owner_cnt > 0);
+ if (--glock->owner_cnt <= 0) {
+ glock->owner = NULL;
+ glock->owner_cnt = 0;
+ }
+}
+
+static pj_status_t grp_lock_acquire(LOCK_OBJ *p)
+{
+ pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+ grp_lock_item *lck;
+
+ pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
+
+ lck = glock->lock_list.next;
+ while (lck != &glock->lock_list) {
+ pj_lock_acquire(lck->lock);
+ lck = lck->next;
+ }
+ grp_lock_set_owner_thread(glock);
+ pj_grp_lock_add_ref(glock);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p)
+{
+ pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+ grp_lock_item *lck;
+
+ pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
+
+ lck = glock->lock_list.next;
+ while (lck != &glock->lock_list) {
+ pj_status_t status = pj_lock_tryacquire(lck->lock);
+ if (status != PJ_SUCCESS) {
+ lck = lck->prev;
+ while (lck != &glock->lock_list) {
+ pj_lock_release(lck->lock);
+ lck = lck->prev;
+ }
+ return status;
+ }
+ lck = lck->next;
+ }
+ grp_lock_set_owner_thread(glock);
+ pj_grp_lock_add_ref(glock);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_release(LOCK_OBJ *p)
+{
+ pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+ grp_lock_item *lck;
+
+ grp_lock_unset_owner_thread(glock);
+
+ lck = glock->lock_list.prev;
+ while (lck != &glock->lock_list) {
+ pj_lock_release(lck->lock);
+ lck = lck->prev;
+ }
+ return pj_grp_lock_dec_ref(glock);
+}
+
+static pj_status_t grp_lock_destroy(LOCK_OBJ *p)
+{
+ pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+ pj_pool_t *pool = glock->pool;
+ grp_lock_item *lck;
+ grp_destroy_callback *cb;
+
+ if (!glock->pool) {
+ /* already destroyed?! */
+ return PJ_EINVAL;
+ }
+
+ /* Release all chained locks */
+ lck = glock->lock_list.next;
+ while (lck != &glock->lock_list) {
+ if (lck->lock != glock->own_lock) {
+ int i;
+ for (i=0; i<glock->owner_cnt; ++i)
+ pj_lock_release(lck->lock);
+ }
+ lck = lck->next;
+ }
+
+ /* Call callbacks */
+ cb = glock->destroy_list.next;
+ while (cb != &glock->destroy_list) {
+ grp_destroy_callback *next = cb->next;
+ cb->handler(cb->comp);
+ cb = next;
+ }
+
+ pj_lock_destroy(glock->own_lock);
+ pj_atomic_destroy(glock->ref_cnt);
+ glock->pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool,
+ const pj_grp_lock_config *cfg,
+ pj_grp_lock_t **p_grp_lock)
+{
+ pj_grp_lock_t *glock;
+ grp_lock_item *own_lock;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL);
+
+ PJ_UNUSED_ARG(cfg);
+
+ pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t);
+ glock->base.lock_object = glock;
+ glock->base.acquire = &grp_lock_acquire;
+ glock->base.tryacquire = &grp_lock_tryacquire;
+ glock->base.release = &grp_lock_release;
+ glock->base.destroy = &grp_lock_destroy;
+
+ glock->pool = pool;
+ pj_list_init(&glock->lock_list);
+ pj_list_init(&glock->destroy_list);
+#if PJ_GRP_LOCK_DEBUG
+ pj_list_init(&glock->ref_list);
+ pj_list_init(&glock->ref_free_list);
+#endif
+
+ status = pj_atomic_create(pool, 0, &glock->ref_cnt);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+ &glock->own_lock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item);
+ own_lock->lock = glock->own_lock;
+ pj_list_push_back(&glock->lock_list, own_lock);
+
+ *p_grp_lock = glock;
+ return PJ_SUCCESS;
+
+on_error:
+ grp_lock_destroy(glock);
+ return status;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock)
+{
+ return grp_lock_destroy(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock)
+{
+ return grp_lock_acquire(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock)
+{
+ return grp_lock_tryacquire(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock)
+{
+ return grp_lock_release(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old_lock,
+ pj_grp_lock_t *new_lock)
+{
+ grp_destroy_callback *ocb;
+
+ /* Move handlers from old to new */
+ ocb = old_lock->destroy_list.next;
+ while (ocb != &old_lock->destroy_list) {
+ grp_destroy_callback *ncb;
+
+ ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback);
+ ncb->comp = ocb->comp;
+ ncb->handler = ocb->handler;
+ pj_list_push_back(&new_lock->destroy_list, ncb);
+
+ ocb = ocb->next;
+ }
+
+ pj_list_init(&old_lock->destroy_list);
+
+ grp_lock_destroy(old_lock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock,
+ pj_pool_t *pool,
+ void *comp,
+ void (*destroy)(void *comp))
+{
+ grp_destroy_callback *cb;
+
+ grp_lock_acquire(glock);
+
+ if (pool == NULL)
+ pool = glock->pool;
+
+ cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback);
+ cb->comp = comp;
+ cb->handler = destroy;
+ pj_list_push_back(&glock->destroy_list, cb);
+
+ grp_lock_release(glock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock,
+ void *comp,
+ void (*destroy)(void *comp))
+{
+ grp_destroy_callback *cb;
+
+ grp_lock_acquire(glock);
+
+ cb = glock->destroy_list.next;
+ while (cb != &glock->destroy_list) {
+ if (cb->comp == comp && cb->handler == destroy)
+ break;
+ cb = cb->next;
+ }
+
+ if (cb != &glock->destroy_list)
+ pj_list_erase(cb);
+
+ grp_lock_release(glock);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock)
+{
+ pj_atomic_inc(glock->ref_cnt);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock)
+{
+ int cnt; /* for debugging */
+ if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) {
+ grp_lock_destroy(glock);
+ return PJ_EGONE;
+ }
+ pj_assert(cnt > 0);
+ pj_grp_lock_dump(glock);
+ return PJ_SUCCESS;
+}
+
+#if PJ_GRP_LOCK_DEBUG
+PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock,
+ const char *file,
+ int line)
+{
+ grp_lock_ref *ref;
+ pj_status_t status;
+
+ pj_enter_critical_section();
+ if (!pj_list_empty(&glock->ref_free_list)) {
+ ref = glock->ref_free_list.next;
+ pj_list_erase(ref);
+ } else {
+ ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref);
+ }
+
+ ref->file = file;
+ ref->line = line;
+ pj_list_push_back(&glock->ref_list, ref);
+
+ pj_leave_critical_section();
+
+ status = grp_lock_add_ref(glock);
+
+ if (status != PJ_SUCCESS) {
+ pj_enter_critical_section();
+ pj_list_erase(ref);
+ pj_list_push_back(&glock->ref_free_list, ref);
+ pj_leave_critical_section();
+ }
+
+ return status;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock,
+ const char *file,
+ int line)
+{
+ grp_lock_ref *ref;
+
+ pj_enter_critical_section();
+ /* Find the same source file */
+ ref = glock->ref_list.next;
+ while (ref != &glock->ref_list) {
+ if (strcmp(ref->file, file) == 0) {
+ pj_list_erase(ref);
+ pj_list_push_back(&glock->ref_free_list, ref);
+ break;
+ }
+ ref = ref->next;
+ }
+ pj_leave_critical_section();
+
+ if (ref == &glock->ref_list) {
+ PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find "
+ "matching ref for %s", file));
+ }
+
+ return grp_lock_dec_ref(glock);
+}
+#else
+PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock)
+{
+ return grp_lock_add_ref(glock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock)
+{
+ return grp_lock_dec_ref(glock);
+}
+#endif
+
+PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock)
+{
+ return pj_atomic_get(glock->ref_cnt);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock,
+ pj_lock_t *lock,
+ int pos)
+{
+ grp_lock_item *lck, *new_lck;
+ int i;
+
+ grp_lock_acquire(glock);
+
+ for (i=0; i<glock->owner_cnt; ++i)
+ pj_lock_acquire(lock);
+
+ lck = glock->lock_list.next;
+ while (lck != &glock->lock_list) {
+ if (lck->prio >= pos)
+ break;
+ lck = lck->next;
+ }
+
+ new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item);
+ new_lck->prio = pos;
+ new_lck->lock = lock;
+ pj_list_insert_before(lck, new_lck);
+
+ /* this will also release the new lock */
+ grp_lock_release(glock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock,
+ pj_lock_t *lock)
+{
+ grp_lock_item *lck;
+
+ grp_lock_acquire(glock);
+
+ lck = glock->lock_list.next;
+ while (lck != &glock->lock_list) {
+ if (lck->lock == lock)
+ break;
+ lck = lck->next;
+ }
+
+ if (lck != &glock->lock_list) {
+ int i;
+
+ pj_list_erase(lck);
+ for (i=0; i<glock->owner_cnt; ++i)
+ pj_lock_release(lck->lock);
+ }
+
+ grp_lock_release(glock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock)
+{
+#if PJ_GRP_LOCK_DEBUG
+ grp_lock_ref *ref = grp_lock->ref_list.next;
+ char info_buf[1000];
+ pj_str_t info;
+
+ info.ptr = info_buf;
+ info.slen = 0;
+
+ pj_grp_lock_acquire(grp_lock);
+ pj_enter_critical_section();
+
+ while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) {
+ char *start = info.ptr + info.slen;
+ int max_len = sizeof(info_buf) - info.slen;
+ int len;
+
+ len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line);
+ if (len < 1 || len > max_len) {
+ len = strlen(ref->file);
+ if (len > max_len - 1)
+ len = max_len - 1;
+
+ memcpy(start, ref->file, len);
+ start[len++] = ' ';
+ }
+
+ info.slen += len;
+
+ ref = ref->next;
+ }
+
+ if (ref != &grp_lock->ref_list) {
+ int i;
+ for (i=0; i<4; ++i)
+ info_buf[sizeof(info_buf)-i-1] = '.';
+ }
+ info.ptr[info.slen-1] = '\0';
+
+ pj_leave_critical_section();
+ pj_grp_lock_release(grp_lock);
+
+ PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s",
+ grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr));
+#else
+ PJ_UNUSED_ARG(grp_lock);
+#endif
+}
diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
index 7810eb2..ff8ba68 100644
--- a/pjlib/src/pj/os_core_unix.c
+++ b/pjlib/src/pj/os_core_unix.c
@@ -1,4 +1,4 @@
-/* $Id: os_core_unix.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: os_core_unix.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -97,7 +97,18 @@ struct pj_sem_t
#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
struct pj_event_t
{
- char obj_name[PJ_MAX_OBJ_NAME];
+ enum event_state {
+ EV_STATE_OFF,
+ EV_STATE_SET,
+ EV_STATE_PULSED
+ } state;
+
+ pj_mutex_t mutex;
+ pthread_cond_t cond;
+
+ pj_bool_t auto_reset;
+ unsigned threads_waiting;
+ unsigned threads_to_release;
};
#endif /* PJ_HAS_EVENT_OBJ */
@@ -1700,13 +1711,48 @@ PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
pj_bool_t manual_reset, pj_bool_t initial,
pj_event_t **ptr_event)
{
- pj_assert(!"Not supported!");
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(name);
- PJ_UNUSED_ARG(manual_reset);
- PJ_UNUSED_ARG(initial);
- PJ_UNUSED_ARG(ptr_event);
- return PJ_EINVALIDOP;
+ pj_event_t *event;
+
+ event = PJ_POOL_ALLOC_T(pool, pj_event_t);
+
+ init_mutex(&event->mutex, name, PJ_MUTEX_SIMPLE);
+ pthread_cond_init(&event->cond, 0);
+ event->auto_reset = !manual_reset;
+ event->threads_waiting = 0;
+
+ if (initial) {
+ event->state = EV_STATE_SET;
+ event->threads_to_release = 1;
+ } else {
+ event->state = EV_STATE_OFF;
+ event->threads_to_release = 0;
+ }
+
+ *ptr_event = event;
+ return PJ_SUCCESS;
+}
+
+static void event_on_one_release(pj_event_t *event)
+{
+ if (event->state == EV_STATE_SET) {
+ if (event->auto_reset) {
+ event->threads_to_release = 0;
+ event->state = EV_STATE_OFF;
+ } else {
+ /* Manual reset remains on */
+ }
+ } else {
+ if (event->auto_reset) {
+ /* Only release one */
+ event->threads_to_release = 0;
+ event->state = EV_STATE_OFF;
+ } else {
+ event->threads_to_release--;
+ pj_assert(event->threads_to_release >= 0);
+ if (event->threads_to_release==0)
+ event->state = EV_STATE_OFF;
+ }
+ }
}
/*
@@ -1714,8 +1760,14 @@ PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
*/
PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pthread_mutex_lock(&event->mutex.mutex);
+ event->threads_waiting++;
+ while (event->state == EV_STATE_OFF)
+ pthread_cond_wait(&event->cond, &event->mutex.mutex);
+ event->threads_waiting--;
+ event_on_one_release(event);
+ pthread_mutex_unlock(&event->mutex.mutex);
+ return PJ_SUCCESS;
}
/*
@@ -1723,8 +1775,16 @@ PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
*/
PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pj_status_t status;
+
+ pthread_mutex_lock(&event->mutex.mutex);
+ status = event->state != EV_STATE_OFF ? PJ_SUCCESS : -1;
+ if (status==PJ_SUCCESS) {
+ event_on_one_release(event);
+ }
+ pthread_mutex_unlock(&event->mutex.mutex);
+
+ return status;
}
/*
@@ -1732,8 +1792,15 @@ PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
*/
PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pthread_mutex_lock(&event->mutex.mutex);
+ event->threads_to_release = 1;
+ event->state = EV_STATE_SET;
+ if (event->auto_reset)
+ pthread_cond_signal(&event->cond);
+ else
+ pthread_cond_broadcast(&event->cond);
+ pthread_mutex_unlock(&event->mutex.mutex);
+ return PJ_SUCCESS;
}
/*
@@ -1741,8 +1808,18 @@ PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
*/
PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pthread_mutex_lock(&event->mutex.mutex);
+ if (event->threads_waiting) {
+ event->threads_to_release = event->auto_reset ? 1 :
+ event->threads_waiting;
+ event->state = EV_STATE_PULSED;
+ if (event->threads_to_release==1)
+ pthread_cond_signal(&event->cond);
+ else
+ pthread_cond_broadcast(&event->cond);
+ }
+ pthread_mutex_unlock(&event->mutex.mutex);
+ return PJ_SUCCESS;
}
/*
@@ -1750,8 +1827,11 @@ PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
*/
PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pthread_mutex_lock(&event->mutex.mutex);
+ event->state = EV_STATE_OFF;
+ event->threads_to_release = 0;
+ pthread_mutex_unlock(&event->mutex.mutex);
+ return PJ_SUCCESS;
}
/*
@@ -1759,8 +1839,9 @@ PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
*/
PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
{
- PJ_UNUSED_ARG(event);
- return PJ_EINVALIDOP;
+ pj_mutex_destroy(&event->mutex);
+ pthread_cond_destroy(&event->cond);
+ return PJ_SUCCESS;
}
#endif /* PJ_HAS_EVENT_OBJ */
diff --git a/pjlib/src/pj/os_info.c b/pjlib/src/pj/os_info.c
index be67120..1d6e007 100644
--- a/pjlib/src/pj/os_info.c
+++ b/pjlib/src/pj/os_info.c
@@ -1,4 +1,4 @@
-/* $Id: os_info.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: os_info.c 4411 2013-03-04 04:34:38Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -242,6 +242,9 @@ PJ_DEF(const pj_sys_info*) pj_get_sys_info(void)
} else {
si.os_name = pj_str("Unknown");
}
+
+ /* Avoid compile warning on Symbian. */
+ goto get_sdk_info;
}
#endif
diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c
index 9992df7..90443b9 100644
--- a/pjlib/src/pj/pool.c
+++ b/pjlib/src/pj/pool.c
@@ -1,4 +1,4 @@
-/* $Id: pool.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: pool.c 4298 2012-11-22 05:00:01Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -88,7 +88,7 @@ static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
* If no space is available in all the blocks, a new block might be created
* (depending on whether the pool is allowed to resize).
*/
-PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
+PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size)
{
pj_pool_block *block = pool->block_list.next;
void *p;
@@ -121,7 +121,7 @@ PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size)
if (pool->increment_size <
size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT)
{
- unsigned count;
+ pj_size_t count;
count = (size + pool->increment_size + sizeof(pj_pool_block) +
PJ_POOL_ALIGNMENT) /
pool->increment_size;
diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c
index a15c3d9..60659ec 100644
--- a/pjlib/src/pj/pool_caching.c
+++ b/pjlib/src/pj/pool_caching.c
@@ -1,4 +1,4 @@
-/* $Id: pool_caching.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: pool_caching.c 4298 2012-11-22 05:00:01Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -178,7 +178,11 @@ static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
pj_pool_init_int(pool, name, increment_sz, callback);
/* Update pool manager's free capacity. */
- cp->capacity -= pj_pool_get_capacity(pool);
+ if (cp->capacity > pj_pool_get_capacity(pool)) {
+ cp->capacity -= pj_pool_get_capacity(pool);
+ } else {
+ cp->capacity = 0;
+ }
PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity));
}
@@ -199,7 +203,7 @@ static pj_pool_t* cpool_create_pool(pj_pool_factory *pf,
static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
{
pj_caching_pool *cp = (pj_caching_pool*)pf;
- unsigned pool_capacity;
+ pj_size_t pool_capacity;
unsigned i;
PJ_CHECK_STACK();
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index ed9fde9..8ed2d5d 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -1,4 +1,4 @@
-/* $Id: sock_bsd.c 4170 2012-06-19 07:40:19Z bennylp $ */
+/* $Id: sock_bsd.c 4233 2012-08-21 11:16:06Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -723,7 +723,6 @@ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
{
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
- PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL));
*len = recvfrom(sock, (char*)buf, *len, flags,
(struct sockaddr*)from, (socklen_t*)fromlen);
@@ -731,7 +730,9 @@ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
if (*len < 0)
return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
else {
- PJ_SOCKADDR_RESET_LEN(from);
+ if (from) {
+ PJ_SOCKADDR_RESET_LEN(from);
+ }
return PJ_SUCCESS;
}
}
diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c
index dd2ef6e..1528b5f 100644
--- a/pjlib/src/pj/sock_common.c
+++ b/pjlib/src/pj/sock_common.c
@@ -1,4 +1,4 @@
-/* $Id: sock_common.c 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: sock_common.c 4343 2013-02-07 09:35:34Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -24,6 +24,7 @@
#include <pj/ip_helper.h>
#include <pj/os.h>
#include <pj/addr_resolv.h>
+#include <pj/rand.h>
#include <pj/string.h>
#include <pj/compat/socket.h>
@@ -956,42 +957,54 @@ PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr)
return PJ_SUCCESS;
}
-/* Get the default IP interface */
-PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr)
+/* Get IP interface for sending to the specified destination */
+PJ_DEF(pj_status_t) pj_getipinterface(int af,
+ const pj_str_t *dst,
+ pj_sockaddr *itf_addr,
+ pj_bool_t allow_resolve,
+ pj_sockaddr *p_dst_addr)
{
+ pj_sockaddr dst_addr;
pj_sock_t fd;
- pj_str_t cp;
- pj_sockaddr a;
int len;
pj_uint8_t zero[64];
pj_status_t status;
- addr->addr.sa_family = (pj_uint16_t)af;
-
- status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd);
+ pj_sockaddr_init(af, &dst_addr, NULL, 53);
+ status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr));
if (status != PJ_SUCCESS) {
- return status;
- }
+ /* "dst" is not an IP address. */
+ if (allow_resolve) {
+ status = pj_sockaddr_init(af, &dst_addr, dst, 53);
+ } else {
+ pj_str_t cp;
- if (af == PJ_AF_INET) {
- cp = pj_str("1.1.1.1");
- } else {
- cp = pj_str("1::1");
+ if (af == PJ_AF_INET) {
+ cp = pj_str("1.1.1.1");
+ } else {
+ cp = pj_str("1::1");
+ }
+ status = pj_sockaddr_init(af, &dst_addr, &cp, 53);
+ }
+
+ if (status != PJ_SUCCESS)
+ return status;
}
- status = pj_sockaddr_init(af, &a, &cp, 53);
+
+ /* Create UDP socket and connect() to the destination IP */
+ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd);
if (status != PJ_SUCCESS) {
- pj_sock_close(fd);
return status;
}
- status = pj_sock_connect(fd, &a, pj_sockaddr_get_len(&a));
+ status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr));
if (status != PJ_SUCCESS) {
pj_sock_close(fd);
return status;
}
- len = sizeof(a);
- status = pj_sock_getsockname(fd, &a, &len);
+ len = sizeof(*itf_addr);
+ status = pj_sock_getsockname(fd, itf_addr, &len);
if (status != PJ_SUCCESS) {
pj_sock_close(fd);
return status;
@@ -1001,18 +1014,70 @@ PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr)
/* Check that the address returned is not zero */
pj_bzero(zero, sizeof(zero));
- if (pj_memcmp(pj_sockaddr_get_addr(&a), zero,
- pj_sockaddr_get_addr_len(&a))==0)
+ if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero,
+ pj_sockaddr_get_addr_len(itf_addr))==0)
{
return PJ_ENOTFOUND;
}
- pj_sockaddr_copy_addr(addr, &a);
+ if (p_dst_addr)
+ *p_dst_addr = dst_addr;
- /* Success */
return PJ_SUCCESS;
}
+/* Get the default IP interface */
+PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr)
+{
+ pj_str_t cp;
+
+ if (af == PJ_AF_INET) {
+ cp = pj_str("1.1.1.1");
+ } else {
+ cp = pj_str("1::1");
+ }
+
+ return pj_getipinterface(af, &cp, addr, PJ_FALSE, NULL);
+}
+
+
+/*
+ * Bind socket at random port.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd,
+ const pj_sockaddr_t *addr,
+ pj_uint16_t port_range,
+ pj_uint16_t max_try)
+{
+ pj_sockaddr bind_addr;
+ int addr_len;
+ pj_uint16_t base_port;
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_CHECK_STACK();
+
+ PJ_ASSERT_RETURN(addr, PJ_EINVAL);
+
+ pj_sockaddr_cp(&bind_addr, addr);
+ addr_len = pj_sockaddr_get_len(addr);
+ base_port = pj_sockaddr_get_port(addr);
+
+ if (base_port == 0 || port_range == 0) {
+ return pj_sock_bind(sockfd, &bind_addr, addr_len);
+ }
+
+ for (; max_try; --max_try) {
+ pj_uint16_t port;
+ port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1));
+ pj_sockaddr_set_port(&bind_addr, port);
+ status = pj_sock_bind(sockfd, &bind_addr, addr_len);
+ if (status == PJ_SUCCESS)
+ break;
+ }
+
+ return status;
+}
+
/* Only need to implement these in DLL build */
#if defined(PJ_DLL)
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index 82ad2a5..4b1d0a7 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -1,4 +1,4 @@
-/* $Id: ssl_sock_ossl.c 4146 2012-05-30 06:35:59Z nanang $ */
+/* $Id: ssl_sock_ossl.c 4349 2013-02-14 09:38:31Z nanang $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -105,9 +105,10 @@ typedef struct read_data_t
ssock->param.read_buffer_size)
/*
- * Structure of SSL socket write buffer.
+ * Structure of SSL socket write data.
*/
typedef struct write_data_t {
+ PJ_DECL_LIST_MEMBER(struct write_data_t);
pj_ioqueue_op_key_t key;
pj_size_t record_len;
pj_ioqueue_op_key_t *app_key;
@@ -121,23 +122,14 @@ typedef struct write_data_t {
} write_data_t;
/*
- * Structure of SSL socket write state.
+ * Structure of SSL socket write buffer (circular buffer).
*/
-typedef struct write_state_t {
+typedef struct send_buf_t {
char *buf;
pj_size_t max_len;
char *start;
pj_size_t len;
- write_data_t *last_data;
-} write_state_t;
-
-/*
- * Structure of write data pending.
- */
-typedef struct write_pending_t {
- PJ_DECL_LIST_MEMBER(struct write_pending_t);
- write_data_t data;
-} write_pending_t;
+} send_buf_t;
/*
* Secure socket structure definition.
@@ -173,10 +165,12 @@ struct pj_ssl_sock_t
void **asock_rbuf;
read_data_t *ssock_rbuf;
- write_state_t write_state;
- write_pending_t write_pending;
- write_pending_t write_pending_empty;
- pj_lock_t *write_mutex; /* protect write BIO and write_state */
+ write_data_t write_pending;/* list of pending write to OpenSSL */
+ write_data_t write_pending_empty; /* cache for write_pending */
+ pj_bool_t flushing_write_pend; /* flag of flushing is ongoing*/
+ send_buf_t send_buf;
+ write_data_t send_pending; /* list of pending write to network */
+ pj_lock_t *write_mutex; /* protect write BIO and send_buf */
SSL_CTX *ossl_ctx;
SSL *ossl_ssl;
@@ -197,6 +191,8 @@ struct pj_ssl_cert_t
};
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len);
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata);
static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
/*
@@ -1054,6 +1050,179 @@ static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
return PJ_TRUE;
}
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
+{
+ send_buf_t *send_buf = &ssock->send_buf;
+ pj_size_t avail_len, skipped_len = 0;
+ char *reg1, *reg2;
+ pj_size_t reg1_len, reg2_len;
+ write_data_t *p;
+
+ /* Check buffer availability */
+ avail_len = send_buf->max_len - send_buf->len;
+ if (avail_len < len)
+ return NULL;
+
+ /* If buffer empty, reset start pointer and return it */
+ if (send_buf->len == 0) {
+ send_buf->start = send_buf->buf;
+ send_buf->len = len;
+ p = (write_data_t*)send_buf->start;
+ goto init_send_data;
+ }
+
+ /* Free space may be wrapped/splitted into two regions, so let's
+ * analyze them if any region can hold the write data.
+ */
+ reg1 = send_buf->start + send_buf->len;
+ if (reg1 >= send_buf->buf + send_buf->max_len)
+ reg1 -= send_buf->max_len;
+ reg1_len = send_buf->max_len - send_buf->len;
+ if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
+ reg1_len = send_buf->buf + send_buf->max_len - reg1;
+ reg2 = send_buf->buf;
+ reg2_len = send_buf->start - send_buf->buf;
+ } else {
+ reg2 = NULL;
+ reg2_len = 0;
+ }
+
+ /* More buffer availability check, note that the write data must be in
+ * a contigue buffer.
+ */
+ avail_len = PJ_MAX(reg1_len, reg2_len);
+ if (avail_len < len)
+ return NULL;
+
+ /* Get the data slot */
+ if (reg1_len >= len) {
+ p = (write_data_t*)reg1;
+ } else {
+ p = (write_data_t*)reg2;
+ skipped_len = reg1_len;
+ }
+
+ /* Update buffer length */
+ send_buf->len += len + skipped_len;
+
+init_send_data:
+ /* Init the new send data */
+ pj_bzero(p, sizeof(*p));
+ pj_list_init(p);
+ pj_list_push_back(&ssock->send_pending, p);
+
+ return p;
+}
+
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
+{
+ send_buf_t *buf = &ssock->send_buf;
+ write_data_t *spl = &ssock->send_pending;
+
+ pj_assert(!pj_list_empty(&ssock->send_pending));
+
+ /* Free slot from the buffer */
+ if (spl->next == wdata && spl->prev == wdata) {
+ /* This is the only data, reset the buffer */
+ buf->start = buf->buf;
+ buf->len = 0;
+ } else if (spl->next == wdata) {
+ /* This is the first data, shift start pointer of the buffer and
+ * adjust the buffer length.
+ */
+ buf->start = (char*)wdata->next;
+ if (wdata->next > wdata) {
+ buf->len -= ((char*)wdata->next - buf->start);
+ } else {
+ /* Overlapped */
+ unsigned right_len, left_len;
+ right_len = buf->buf + buf->max_len - (char*)wdata;
+ left_len = (char*)wdata->next - buf->buf;
+ buf->len -= (right_len + left_len);
+ }
+ } else if (spl->prev == wdata) {
+ /* This is the last data, just adjust the buffer length */
+ if (wdata->prev < wdata) {
+ unsigned jump_len;
+ jump_len = (char*)wdata -
+ ((char*)wdata->prev + wdata->prev->record_len);
+ buf->len -= (wdata->record_len + jump_len);
+ } else {
+ /* Overlapped */
+ unsigned right_len, left_len;
+ right_len = buf->buf + buf->max_len -
+ ((char*)wdata->prev + wdata->prev->record_len);
+ left_len = (char*)wdata + wdata->record_len - buf->buf;
+ buf->len -= (right_len + left_len);
+ }
+ }
+ /* For data in the middle buffer, just do nothing on the buffer. The slot
+ * will be freed later when freeing the first/last data.
+ */
+
+ /* Remove the data from send pending list */
+ pj_list_erase(wdata);
+}
+
+#if 0
+/* Just for testing send buffer alloc/free */
+#include <pj/rand.h>
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
+{
+ enum { MAX_CHUNK_NUM = 20 };
+ unsigned chunk_size, chunk_cnt, i;
+ write_data_t *wdata[MAX_CHUNK_NUM] = {0};
+ pj_time_val now;
+ pj_ssl_sock_t *ssock = NULL;
+ pj_ssl_sock_param param;
+ pj_status_t status;
+
+ pj_gettimeofday(&now);
+ pj_srand((unsigned)now.sec);
+
+ pj_ssl_sock_param_default(&param);
+ status = pj_ssl_sock_create(pool, &param, &ssock);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ if (ssock->send_buf.max_len == 0) {
+ ssock->send_buf.buf = (char*)
+ pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
+ }
+
+ chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
+ chunk_cnt = 0;
+ for (i = 0; i < MAX_CHUNK_NUM; i++) {
+ wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
+ if (wdata[i])
+ chunk_cnt++;
+ else
+ break;
+ }
+
+ while (chunk_cnt) {
+ i = pj_rand() % MAX_CHUNK_NUM;
+ if (wdata[i]) {
+ free_send_data(ssock, wdata[i]);
+ wdata[i] = NULL;
+ chunk_cnt--;
+ }
+ }
+
+ if (ssock->send_buf.len != 0)
+ status = PJ_EBUG;
+
+ pj_ssl_sock_close(ssock);
+ return status;
+}
+#endif
+
+
/* Flush write BIO to network socket. Note that any access to write BIO
* MUST be serialized, so mutex protection must cover any call to OpenSSL
* API (that possibly generate data for write BIO) along with the call to
@@ -1067,76 +1236,39 @@ static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock,
{
char *data;
pj_ssize_t len;
-
- write_state_t *write_st = &ssock->write_state;
write_data_t *wdata;
- pj_size_t avail_len, needed_len, skipped_len = 0;
+ pj_size_t needed_len;
pj_status_t status;
+ pj_lock_acquire(ssock->write_mutex);
+
/* Check if there is data in write BIO, flush it if any */
- if (!BIO_pending(ssock->ossl_wbio))
+ if (!BIO_pending(ssock->ossl_wbio)) {
+ pj_lock_release(ssock->write_mutex);
return PJ_SUCCESS;
+ }
/* Get data and its length */
len = BIO_get_mem_data(ssock->ossl_wbio, &data);
- if (len == 0)
+ if (len == 0) {
+ pj_lock_release(ssock->write_mutex);
return PJ_SUCCESS;
+ }
/* Calculate buffer size needed, and align it to 8 */
needed_len = len + sizeof(write_data_t);
needed_len = ((needed_len + 7) >> 3) << 3;
- /* Check buffer availability */
- avail_len = write_st->max_len - write_st->len;
- if (avail_len < needed_len)
+ /* Allocate buffer for send data */
+ wdata = alloc_send_data(ssock, needed_len);
+ if (wdata == NULL) {
+ pj_lock_release(ssock->write_mutex);
return PJ_ENOMEM;
-
- /* More buffer availability check, note that the write data must be in
- * a contigue buffer.
- */
- if (write_st->len == 0) {
-
- write_st->start = write_st->buf;
- wdata = (write_data_t*)write_st->start;
-
- } else {
-
- char *reg1, *reg2;
- pj_size_t reg1_len, reg2_len;
-
- /* Unused slots may be wrapped/splitted into two regions, so let's
- * analyze them if any region can hold the write data.
- */
- reg1 = write_st->start + write_st->len;
- if (reg1 >= write_st->buf + write_st->max_len)
- reg1 -= write_st->max_len;
- reg1_len = write_st->max_len - write_st->len;
- if (reg1 + reg1_len > write_st->buf + write_st->max_len) {
- reg1_len = write_st->buf + write_st->max_len - reg1;
- reg2 = write_st->buf;
- reg2_len = write_st->start - write_st->buf;
- } else {
- reg2 = NULL;
- reg2_len = 0;
- }
- avail_len = PJ_MAX(reg1_len, reg2_len);
- if (avail_len < needed_len)
- return PJ_ENOMEM;
-
- /* Get write data pointer and update buffer length */
- if (reg1_len >= needed_len) {
- wdata = (write_data_t*)reg1;
- } else {
- wdata = (write_data_t*)reg2;
- /* Unused slot in region 1 is skipped as current write data
- * doesn't fit it.
- */
- skipped_len = reg1_len;
- }
}
- /* Copy the data and set its properties into the buffer */
- pj_bzero(wdata, sizeof(write_data_t));
+ /* Copy the data and set its properties into the send data */
+ pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
+ wdata->key.user_data = wdata;
wdata->app_key = send_key;
wdata->record_len = needed_len;
wdata->data_len = len;
@@ -1144,6 +1276,12 @@ static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock,
wdata->flags = flags;
pj_memcpy(&wdata->data, data, len);
+ /* Reset write BIO */
+ BIO_reset(ssock->ossl_wbio);
+
+ /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
+ pj_lock_release(ssock->write_mutex);
+
/* Send it */
if (ssock->param.sock_type == pj_SOCK_STREAM()) {
status = pj_activesock_send(ssock->asock, &wdata->key,
@@ -1157,24 +1295,13 @@ static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock,
ssock->addr_len);
}
- /* Oh no, EWOULDBLOCK! */
- if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
- /* Just return PJ_SUCCESS here, the pending data will be sent in next
- * call of this function since the data is still stored in write BIO.
+ if (status != PJ_EPENDING) {
+ /* When the sending is not pending, remove the wdata from send
+ * pending list.
*/
- return PJ_SUCCESS;
- }
-
- /* Reset write BIO after flushed */
- BIO_reset(ssock->ossl_wbio);
-
- if (status == PJ_EPENDING) {
- /* Update write state */
- pj_assert(skipped_len==0 || write_st->last_data);
- write_st->len += needed_len + skipped_len;
- if (write_st->last_data)
- write_st->last_data->record_len += skipped_len;
- write_st->last_data = wdata;
+ pj_lock_acquire(ssock->write_mutex);
+ free_send_data(ssock, wdata);
+ pj_lock_release(ssock->write_mutex);
}
return status;
@@ -1213,22 +1340,19 @@ static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
pj_status_t status;
int err;
- pj_lock_acquire(ssock->write_mutex);
-
/* Perform SSL handshake */
+ pj_lock_acquire(ssock->write_mutex);
err = SSL_do_handshake(ssock->ossl_ssl);
+ pj_lock_release(ssock->write_mutex);
/* SSL_do_handshake() may put some pending data into SSL write BIO,
* flush it if any.
*/
status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- pj_lock_release(ssock->write_mutex);
return status;
}
- pj_lock_release(ssock->write_mutex);
-
if (err < 0) {
err = SSL_get_error(ssock->ossl_ssl, err);
if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
@@ -1356,9 +1480,15 @@ static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
/* Update certificates */
update_certs_info(ssock);
- pj_lock_acquire(ssock->write_mutex);
+ // Ticket #1573: Don't hold mutex while calling
+ // PJLIB socket send().
+ //pj_lock_acquire(ssock->write_mutex);
status = flush_delayed_send(ssock);
- pj_lock_release(ssock->write_mutex);
+ //pj_lock_release(ssock->write_mutex);
+
+ /* If flushing is ongoing, treat it as success */
+ if (status == PJ_EBUSY)
+ status = PJ_SUCCESS;
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
PJ_PERROR(1,(ssock->pool->obj_name, status,
@@ -1418,11 +1548,14 @@ static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
} else if (send_key != &ssock->handshake_op_key) {
/* Some data has been sent, notify application */
- write_data_t *wdata = (write_data_t*)send_key;
+ write_data_t *wdata = (write_data_t*)send_key->user_data;
if (ssock->param.cb.on_data_sent) {
pj_bool_t ret;
+ pj_ssize_t sent_len;
+
+ sent_len = (sent > 0)? wdata->plain_data_len : sent;
ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key,
- wdata->plain_data_len);
+ sent_len);
if (!ret) {
/* We've been destroyed */
return PJ_FALSE;
@@ -1431,12 +1564,7 @@ static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
/* Update write buffer state */
pj_lock_acquire(ssock->write_mutex);
- ssock->write_state.start += wdata->record_len;
- ssock->write_state.len -= wdata->record_len;
- if (ssock->write_state.last_data == wdata) {
- pj_assert(ssock->write_state.len == 0);
- ssock->write_state.last_data = NULL;
- }
+ free_send_data(ssock, wdata);
pj_lock_release(ssock->write_mutex);
} else {
@@ -1547,13 +1675,13 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
goto on_return;
/* Prepare write/send state */
- pj_assert(ssock->write_state.max_len == 0);
- ssock->write_state.buf = (char*)
- pj_pool_alloc(ssock->pool,
- ssock->param.send_buffer_size);
- ssock->write_state.max_len = ssock->param.send_buffer_size;
- ssock->write_state.start = ssock->write_state.buf;
- ssock->write_state.len = 0;
+ pj_assert(ssock->send_buf.max_len == 0);
+ ssock->send_buf.buf = (char*)
+ pj_pool_alloc(ssock->pool,
+ ssock->param.send_buffer_size);
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
/* Start handshake timer */
if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
@@ -1626,13 +1754,13 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
goto on_return;
/* Prepare write/send state */
- pj_assert(ssock->write_state.max_len == 0);
- ssock->write_state.buf = (char*)
+ pj_assert(ssock->send_buf.max_len == 0);
+ ssock->send_buf.buf = (char*)
pj_pool_alloc(ssock->pool,
ssock->param.send_buffer_size);
- ssock->write_state.max_len = ssock->param.send_buffer_size;
- ssock->write_state.start = ssock->write_state.buf;
- ssock->write_state.len = 0;
+ ssock->send_buf.max_len = ssock->param.send_buffer_size;
+ ssock->send_buf.start = ssock->send_buf.buf;
+ ssock->send_buf.len = 0;
#ifdef SSL_set_tlsext_host_name
/* Set server name to connect */
@@ -1805,7 +1933,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
ssock->ssl_state = SSL_STATE_NULL;
pj_list_init(&ssock->write_pending);
pj_list_init(&ssock->write_pending_empty);
+ pj_list_init(&ssock->send_pending);
pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
+ pj_ioqueue_op_key_init(&ssock->handshake_op_key,
+ sizeof(pj_ioqueue_op_key_t));
/* Create secure socket mutex */
status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
@@ -2038,10 +2169,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
return PJ_ENOTSUP;
}
-/* Write plain data to SSL and flush write BIO. Note that accessing
- * write BIO must be serialized, so a call to this function must be
- * protected by write mutex of SSL socket.
- */
+/* Write plain data to SSL and flush write BIO. */
static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
pj_ioqueue_op_key_t *send_key,
const void *data,
@@ -2056,7 +2184,9 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
* negotitation may be on progress, so sending data should be delayed
* until re-negotiation is completed.
*/
+ pj_lock_acquire(ssock->write_mutex);
nwritten = SSL_write(ssock->ossl_ssl, data, size);
+ pj_lock_release(ssock->write_mutex);
if (nwritten == size) {
/* All data written, flush write BIO to network socket */
@@ -2087,56 +2217,81 @@ static pj_status_t ssl_write(pj_ssl_sock_t *ssock,
return status;
}
-/* Flush delayed data sending in the write pending list. Note that accessing
- * write pending list must be serialized, so a call to this function must be
- * protected by write mutex of SSL socket.
- */
+/* Flush delayed data sending in the write pending list. */
static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
{
+ /* Check for another ongoing flush */
+ if (ssock->flushing_write_pend)
+ return PJ_EBUSY;
+
+ pj_lock_acquire(ssock->write_mutex);
+
+ /* Again, check for another ongoing flush */
+ if (ssock->flushing_write_pend) {
+ pj_lock_release(ssock->write_mutex);
+ return PJ_EBUSY;
+ }
+
+ /* Set ongoing flush flag */
+ ssock->flushing_write_pend = PJ_TRUE;
+
while (!pj_list_empty(&ssock->write_pending)) {
- write_pending_t *wp;
+ write_data_t *wp;
pj_status_t status;
wp = ssock->write_pending.next;
- status = ssl_write(ssock, &wp->data.key, wp->data.data.ptr,
- wp->data.plain_data_len, wp->data.flags);
- if (status != PJ_SUCCESS)
+ /* Ticket #1573: Don't hold mutex while calling socket send. */
+ pj_lock_release(ssock->write_mutex);
+
+ status = ssl_write(ssock, &wp->key, wp->data.ptr,
+ wp->plain_data_len, wp->flags);
+ if (status != PJ_SUCCESS) {
+ /* Reset ongoing flush flag first. */
+ ssock->flushing_write_pend = PJ_FALSE;
return status;
+ }
+ pj_lock_acquire(ssock->write_mutex);
pj_list_erase(wp);
pj_list_push_back(&ssock->write_pending_empty, wp);
}
+ /* Reset ongoing flush flag */
+ ssock->flushing_write_pend = PJ_FALSE;
+
+ pj_lock_release(ssock->write_mutex);
+
return PJ_SUCCESS;
}
-/* Sending is delayed, push back the sending data into pending list. Note that
- * accessing write pending list must be serialized, so a call to this function
- * must be protected by write mutex of SSL socket.
- */
+/* Sending is delayed, push back the sending data into pending list. */
static pj_status_t delay_send (pj_ssl_sock_t *ssock,
pj_ioqueue_op_key_t *send_key,
const void *data,
pj_ssize_t size,
unsigned flags)
{
- write_pending_t *wp;
+ write_data_t *wp;
+
+ pj_lock_acquire(ssock->write_mutex);
/* Init write pending instance */
if (!pj_list_empty(&ssock->write_pending_empty)) {
wp = ssock->write_pending_empty.next;
pj_list_erase(wp);
} else {
- wp = PJ_POOL_ZALLOC_T(ssock->pool, write_pending_t);
+ wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
}
- wp->data.app_key = send_key;
- wp->data.plain_data_len = size;
- wp->data.data.ptr = data;
- wp->data.flags = flags;
+ wp->app_key = send_key;
+ wp->plain_data_len = size;
+ wp->data.ptr = data;
+ wp->flags = flags;
pj_list_push_back(&ssock->write_pending, wp);
+
+ pj_lock_release(ssock->write_mutex);
/* Must return PJ_EPENDING */
return PJ_EPENDING;
@@ -2156,14 +2311,15 @@ PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL);
PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
- pj_lock_acquire(ssock->write_mutex);
+ // Ticket #1573: Don't hold mutex while calling PJLIB socket send().
+ //pj_lock_acquire(ssock->write_mutex);
/* Flush delayed send first. Sending data might be delayed when
* re-negotiation is on-progress.
*/
status = flush_delayed_send(ssock);
if (status == PJ_EBUSY) {
- /* Re-negotiation is on progress, delay sending */
+ /* Re-negotiation or flushing is on progress, delay sending */
status = delay_send(ssock, send_key, data, *size, flags);
goto on_return;
} else if (status != PJ_SUCCESS) {
@@ -2178,7 +2334,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
}
on_return:
- pj_lock_release(ssock->write_mutex);
+ //pj_lock_release(ssock->write_mutex);
return status;
}
diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c
index e78cba3..7951955 100644
--- a/pjlib/src/pj/timer.c
+++ b/pjlib/src/pj/timer.c
@@ -1,4 +1,4 @@
-/* $Id: timer.c 4154 2012-06-05 10:41:17Z bennylp $ */
+/* $Id: timer.c 4359 2013-02-21 11:18:36Z bennylp $ */
/*
* The PJLIB's timer heap is based (or more correctly, copied and modied)
* from ACE library by Douglas C. Schmidt. ACE is an excellent OO framework
@@ -35,6 +35,7 @@
#include <pj/errno.h>
#include <pj/lock.h>
#include <pj/log.h>
+#include <pj/rand.h>
#define THIS_FILE "timer.c"
@@ -451,20 +452,27 @@ PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
entry->id = id;
entry->user_data = user_data;
entry->cb = cb;
+ entry->_grp_lock = NULL;
return entry;
}
#if PJ_TIMER_DEBUG
-PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay,
- const char *src_file,
- int src_line)
+static pj_status_t schedule_w_grp_lock_dbg(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ pj_bool_t set_id,
+ int id_val,
+ pj_grp_lock_t *grp_lock,
+ const char *src_file,
+ int src_line)
#else
-PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay)
+static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ pj_bool_t set_id,
+ int id_val,
+ pj_grp_lock_t *grp_lock)
#endif
{
pj_status_t status;
@@ -485,13 +493,66 @@ PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
lock_timer_heap(ht);
status = schedule_entry(ht, entry, &expires);
+ if (status == PJ_SUCCESS) {
+ if (set_id)
+ entry->id = id_val;
+ entry->_grp_lock = grp_lock;
+ if (entry->_grp_lock) {
+ pj_grp_lock_add_ref(entry->_grp_lock);
+ }
+ }
unlock_timer_heap(ht);
return status;
}
-PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry)
+
+#if PJ_TIMER_DEBUG
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ const char *src_file,
+ int src_line)
+{
+ return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_FALSE, 1, NULL,
+ src_file, src_line);
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock,
+ const char *src_file,
+ int src_line)
+{
+ return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_TRUE, id_val,
+ grp_lock, src_file, src_line);
+}
+
+#else
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay)
+{
+ return schedule_w_grp_lock(ht, entry, delay, PJ_FALSE, 1, NULL);
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock)
+{
+ return schedule_w_grp_lock(ht, entry, delay, PJ_TRUE, id_val, grp_lock);
+}
+#endif
+
+static int cancel_timer(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ pj_bool_t set_id,
+ int id_val)
{
int count;
@@ -499,11 +560,32 @@ PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
lock_timer_heap(ht);
count = cancel(ht, entry, 1);
+ if (set_id) {
+ entry->id = id_val;
+ }
+ if (entry->_grp_lock) {
+ pj_grp_lock_t *grp_lock = entry->_grp_lock;
+ entry->_grp_lock = NULL;
+ pj_grp_lock_dec_ref(grp_lock);
+ }
unlock_timer_heap(ht);
return count;
}
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry)
+{
+ return cancel_timer(ht, entry, PJ_FALSE, 0);
+}
+
+PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int id_val)
+{
+ return cancel_timer(ht, entry, PJ_TRUE, id_val);
+}
+
PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
pj_time_val *next_delay )
{
@@ -512,25 +594,38 @@ PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
PJ_ASSERT_RETURN(ht, 0);
+ lock_timer_heap(ht);
if (!ht->cur_size && next_delay) {
next_delay->sec = next_delay->msec = PJ_MAXINT32;
+ unlock_timer_heap(ht);
return 0;
}
count = 0;
pj_gettickcount(&now);
- lock_timer_heap(ht);
while ( ht->cur_size &&
PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&
count < ht->max_entries_per_poll )
{
pj_timer_entry *node = remove_node(ht, 0);
+ pj_grp_lock_t *grp_lock;
+
++count;
+ grp_lock = node->_grp_lock;
+ node->_grp_lock = NULL;
+
unlock_timer_heap(ht);
+
+ PJ_RACE_ME(5);
+
if (node->cb)
(*node->cb)(ht, node);
+
+ if (grp_lock)
+ pj_grp_lock_dec_ref(grp_lock);
+
lock_timer_heap(ht);
}
if (ht->cur_size && next_delay) {
diff --git a/pjlib/src/pj/timer_symbian.cpp b/pjlib/src/pj/timer_symbian.cpp
index 47aa984..8b18525 100644
--- a/pjlib/src/pj/timer_symbian.cpp
+++ b/pjlib/src/pj/timer_symbian.cpp
@@ -1,4 +1,4 @@
-/* $Id: timer_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: timer_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -392,6 +392,24 @@ PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
return PJ_SUCCESS;
}
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock)
+{
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(grp_lock);
+
+ status = pj_timer_heap_schedule(ht, entry, delay);
+
+ if (status == PJ_SUCCESS)
+ entry->id = id_val;
+
+ return status;
+}
+
PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
pj_timer_entry *entry)
{
@@ -411,6 +429,17 @@ PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
}
}
+PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int id_val)
+{
+ int count = pj_timer_heap_cancel(ht, entry);
+ if (count == 1)
+ entry->id = id_val;
+
+ return count;
+}
+
PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
pj_time_val *next_delay )
{
diff --git a/pjlib/src/pjlib-test/activesock.c b/pjlib/src/pjlib-test/activesock.c
index 399cc29..6fe5091 100644
--- a/pjlib/src/pjlib-test/activesock.c
+++ b/pjlib/src/pjlib-test/activesock.c
@@ -1,4 +1,4 @@
-/* $Id: activesock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: activesock.c 4238 2012-08-31 06:17:56Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -213,6 +213,7 @@ static int udp_ping_pong_test(void)
for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
pj_time_val delay = {0, 10};
#ifdef PJ_SYMBIAN
+ PJ_UNUSED_ARG(delay);
pj_symbianos_poll(-1, 100);
#else
pj_ioqueue_poll(ioqueue, &delay);
diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c
index 8ecbc0f..faf0646 100644
--- a/pjlib/src/pjlib-test/ioq_tcp.c
+++ b/pjlib/src/pjlib-test/ioq_tcp.c
@@ -1,4 +1,4 @@
-/* $Id: ioq_tcp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ioq_tcp.c 4238 2012-08-31 06:17:56Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -363,7 +363,7 @@ static int compliance_test_0(pj_bool_t allow_concur)
#ifdef PJ_SYMBIAN
callback_call_count = 0;
- pj_symbianos_poll(-1, 1000);
+ pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
status = callback_call_count;
#else
status = pj_ioqueue_poll(ioque, &timeout);
@@ -412,7 +412,7 @@ static int compliance_test_0(pj_bool_t allow_concur)
if (pending_op == 0) {
pj_time_val timeout = {1, 0};
#ifdef PJ_SYMBIAN
- status = pj_symbianos_poll(-1, 1000);
+ status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
status = pj_ioqueue_poll(ioque, &timeout);
#endif
@@ -542,7 +542,7 @@ static int compliance_test_1(pj_bool_t allow_concur)
#ifdef PJ_SYMBIAN
callback_call_count = 0;
- pj_symbianos_poll(-1, 1000);
+ pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
status = callback_call_count;
#else
status = pj_ioqueue_poll(ioque, &timeout);
@@ -576,7 +576,7 @@ static int compliance_test_1(pj_bool_t allow_concur)
if (pending_op == 0) {
pj_time_val timeout = {1, 0};
#ifdef PJ_SYMBIAN
- status = pj_symbianos_poll(-1, 1000);
+ status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
status = pj_ioqueue_poll(ioque, &timeout);
#endif
@@ -771,7 +771,7 @@ static int compliance_test_2(pj_bool_t allow_concur)
pj_time_val timeout = {1, 0};
#ifdef PJ_SYMBIAN
- status = pj_symbianos_poll(-1, 1000);
+ status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
status = pj_ioqueue_poll(ioque, &timeout);
#endif
@@ -797,7 +797,7 @@ static int compliance_test_2(pj_bool_t allow_concur)
if (pending_op == 0) {
pj_time_val timeout = {1, 0};
#ifdef PJ_SYMBIAN
- status = pj_symbianos_poll(-1, 1000);
+ status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
status = pj_ioqueue_poll(ioque, &timeout);
#endif
diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c
index 6928641..8180e3d 100644
--- a/pjlib/src/pjlib-test/ioq_udp.c
+++ b/pjlib/src/pjlib-test/ioq_udp.c
@@ -1,4 +1,4 @@
-/* $Id: ioq_udp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ioq_udp.c 4238 2012-08-31 06:17:56Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -267,7 +267,7 @@ static int compliance_test(pj_bool_t allow_concur)
TRACE_("poll...");
#ifdef PJ_SYMBIAN
- rc = pj_symbianos_poll(-1, 5000);
+ rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
rc = pj_ioqueue_poll(ioque, &timeout);
#endif
@@ -783,7 +783,7 @@ static int bench_test(pj_bool_t allow_concur, int bufsize,
do {
pj_time_val timeout = { 1, 0 };
#ifdef PJ_SYMBIAN
- rc = pj_symbianos_poll(-1, 1000);
+ rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
#else
rc = pj_ioqueue_poll(ioque, &timeout);
#endif
@@ -812,6 +812,7 @@ static int bench_test(pj_bool_t allow_concur, int bufsize,
do {
pj_time_val timeout = { 0, 10 };
#ifdef PJ_SYMBIAN
+ PJ_UNUSED_ARG(timeout);
rc = pj_symbianos_poll(-1, 100);
#else
rc = pj_ioqueue_poll(ioque, &timeout);
diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c
index d05a4db..d983eba 100644
--- a/pjlib/src/pjlib-test/ssl_sock.c
+++ b/pjlib/src/pjlib-test/ssl_sock.c
@@ -1,4 +1,4 @@
-/* $Id: ssl_sock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ssl_sock.c 4247 2012-09-07 08:58:48Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -1329,11 +1329,34 @@ on_return:
return status;
}
+#if 0 && (!defined(PJ_SYMBIAN) || PJ_SYMBIAN==0)
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool);
+static int ossl_test_send_buf()
+{
+ pj_pool_t *pool;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "send_buf", 256, 256, NULL);
+ status = pj_ssl_sock_ossl_test_send_buf(pool);
+ pj_pool_release(pool);
+ return status;
+}
+#else
+static int ossl_test_send_buf()
+{
+ return 0;
+}
+#endif
int ssl_sock_test(void)
{
int ret;
+ PJ_LOG(3,("", "..test ossl send buf"));
+ ret = ossl_test_send_buf();
+ if (ret != 0)
+ return ret;
+
PJ_LOG(3,("", "..get cipher list test"));
ret = get_cipher_list();
if (ret != 0)
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 2f97296..026d89c 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -113,7 +113,7 @@ export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec
export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o \
h263_packetizer.o h264_packetizer.o \
$(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
- ipp_codecs.o $(CODEC_OBJS) \
+ ipp_codecs.o opencore_amr.o silk.o $(CODEC_OBJS) \
g7221_sdp_match.o amr_sdp_match.o
export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \
$(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS)
@@ -173,7 +173,9 @@ pjmedia-audiodev:
pjsdp:
$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(PJSDP_LIB)
-pjmedia-test: $(PJMEDIA_LIB)
+$(PJMEDIA_LIB): pjmedia
+
+pjmedia-test: $(PJMEDIA_LIB) pjmedia
$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(PJMEDIA_TEST_EXE)
.PHONY: ../lib/pjmedia.ko
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index 98ecf28..04224e8 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -58,6 +58,7 @@ AC_NO_ILBC_CODEC=@ac_no_ilbc_codec@
AC_NO_G722_CODEC=@ac_no_g722_codec@
AC_NO_G7221_CODEC=@ac_no_g7221_codec@
AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@
+AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@
export CODEC_OBJS=
@@ -113,7 +114,15 @@ endif
ifeq ($(AC_NO_OPENCORE_AMRNB),1)
export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRNB_CODEC=0
else
-export CODEC_OBJS += opencore_amrnb.o
+export CODEC_OBJS += opencore_amr.o
+endif
+
+ifeq ($(AC_NO_OPENCORE_AMRWB),1)
+export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRWB_CODEC=0
+else
+ifeq ($(AC_NO_OPENCORE_AMRNB),1)
+export CODEC_OBJS += opencore_amr.o
+endif
endif
diff --git a/pjmedia/build/pjmedia_codec.vcproj b/pjmedia/build/pjmedia_codec.vcproj
index 158d43d..eddbc8c 100644
--- a/pjmedia/build/pjmedia_codec.vcproj
+++ b/pjmedia/build/pjmedia_codec.vcproj
@@ -2975,7 +2975,7 @@
</FileConfiguration>
</File>
<File
- RelativePath="..\src\pjmedia-codec\opencore_amrnb.c"
+ RelativePath="..\src\pjmedia-codec\opencore_amr.c"
>
</File>
<File
@@ -2983,6 +2983,10 @@
>
</File>
<File
+ RelativePath="..\src\pjmedia-codec\silk.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia-codec\speex_codec.c"
>
<FileConfiguration
@@ -3122,7 +3126,7 @@
>
</File>
<File
- RelativePath="..\include\pjmedia-codec\opencore_amrnb.h"
+ RelativePath="..\include\pjmedia-codec\opencore_amr.h"
>
</File>
<File
@@ -3134,6 +3138,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia-codec\silk.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia-codec\speex.h"
>
</File>
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h
index f8edb0d..7e13d14 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -1,4 +1,4 @@
-/* $Id: audiodev.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: audiodev.h 4243 2012-08-31 11:42:17Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -424,6 +424,12 @@ typedef struct pjmedia_aud_param
*/
pj_bool_t cng_enabled;
+ /**
+ * Enable/disable VAD. This setting is optional, and will only be used
+ * if PJMEDIA_AUD_DEV_CAP_VAD is set in the flags.
+ */
+ pj_bool_t vad_enabled;
+
} pjmedia_aud_param;
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index c666996..c63e46e 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -1,4 +1,4 @@
-/* $Id: pjmedia-codec.h 4049 2012-04-13 06:24:23Z ming $ */
+/* $Id: pjmedia-codec.h 4331 2013-01-23 06:18:18Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -34,8 +34,9 @@
#include <pjmedia-codec/g722.h>
#include <pjmedia-codec/g7221.h>
#include <pjmedia-codec/ipp_codecs.h>
+#include <pjmedia-codec/opencore_amr.h>
#include <pjmedia-codec/passthrough.h>
-#include <pjmedia-codec/opencore_amrnb.h>
+#include <pjmedia-codec/silk.h>
#endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 2c71cc9..29350db 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 4070 2012-04-23 13:48:10Z bennylp $ */
+/* $Id: config.h 4331 2013-01-23 06:18:18Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -323,8 +323,18 @@
#endif
/**
+ * Enable OpenCORE AMR-WB codec.
+ * See https://trac.pjsip.org/repos/ticket/1608 for some info.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+# define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0
+#endif
+
+/**
* Link with libopencore-amrXX via pragma comment on Visual Studio.
- * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+ * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC
* is enabled.
*
* Default: 1
@@ -335,7 +345,7 @@
/**
* Link with libopencore-amrXX.a that has been produced with gcc.
- * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+ * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC
* and PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS are enabled.
*
* Default: 1
@@ -378,6 +388,38 @@
/**
+ * Enable SILK codec.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_SILK_CODEC
+# define PJMEDIA_HAS_SILK_CODEC 0
+#endif
+
+
+/**
+ * SILK codec default complexity setting, valid values are 0 (lowest), 1,
+ * and 2.
+ *
+ * Default: 2
+ */
+#ifndef PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY
+# define PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY 2
+#endif
+
+/**
+ * SILK codec default quality setting, valid values are ranging from
+ * 0 (lowest) to 10. Please note that pjsua-lib may override this setting
+ * via its codec quality setting (i.e PJSUA_DEFAULT_CODEC_QUALITY).
+ *
+ * Default: 10
+ */
+#ifndef PJMEDIA_CODEC_SILK_DEFAULT_QUALITY
+# define PJMEDIA_CODEC_SILK_DEFAULT_QUALITY 10
+#endif
+
+
+/**
* Specify if FFMPEG codecs are available.
*
* Default: PJMEDIA_HAS_LIBAVCODEC
diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in
index 887d083..11da110 100644
--- a/pjmedia/include/pjmedia-codec/config_auto.h.in
+++ b/pjmedia/include/pjmedia-codec/config_auto.h.in
@@ -1,4 +1,4 @@
-/* $Id: config_auto.h.in 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: config_auto.h.in 4331 2013-01-23 06:18:18Z ming $ */
/*
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -74,6 +74,15 @@
#undef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
#endif
+/* OpenCORE AMR-WB codec */
+#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+#undef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+#endif
+
+/* SILK codec */
+#ifndef PJMEDIA_HAS_SILK_CODEC
+#undef PJMEDIA_HAS_SILK_CODEC
+#endif
#endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */
diff --git a/pjmedia/include/pjmedia-codec/opencore_amr.h b/pjmedia/include/pjmedia-codec/opencore_amr.h
new file mode 100644
index 0000000..3d5049b
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/opencore_amr.h
@@ -0,0 +1,146 @@
+/* $Id: opencore_amr.h 4335 2013-01-29 08:09:15Z ming $ */
+/*
+ * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CODEC_OPENCORE_AMR_H__
+#define __PJMEDIA_CODEC_OPENCORE_AMR_H__
+
+#include <pjmedia-codec/types.h>
+
+/**
+ * @defgroup PJMED_OC_AMR OpenCORE AMR Codec
+ * @ingroup PJMEDIA_CODEC_CODECS
+ * @brief AMRCodec wrapper for OpenCORE AMR codec
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * Bitmask options to be passed during AMR codec factory initialization.
+ */
+enum pjmedia_amr_options
+{
+ PJMEDIA_AMR_NO_NB = 1, /**< Disable narrowband mode. */
+ PJMEDIA_AMR_NO_WB = 2, /**< Disable wideband mode. */
+};
+
+/**
+ * Settings. Use #pjmedia_codec_opencore_amrnb/wb_set_config() to
+ * activate.
+ */
+typedef struct pjmedia_codec_amr_config
+{
+ /**
+ * Control whether to use octent align.
+ */
+ pj_bool_t octet_align;
+
+ /**
+ * Set the bitrate.
+ */
+ unsigned bitrate;
+
+} pjmedia_codec_amr_config;
+
+typedef pjmedia_codec_amr_config pjmedia_codec_amrnb_config;
+typedef pjmedia_codec_amr_config pjmedia_codec_amrwb_config;
+
+/**
+ * Initialize and register AMR codec factory to pjmedia endpoint.
+ *
+ * @param endpt The pjmedia endpoint.
+ * @param options Bitmask of pjmedia_amr_options (default=0).
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amr_init(pjmedia_endpt* endpt,
+ unsigned options);
+
+/**
+ * Initialize and register AMR codec factory using default settings to
+ * pjmedia endpoint.
+ *
+ * @param endpt The pjmedia endpoint.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_codec_opencore_amr_init_default(pjmedia_endpt* endpt);
+
+/**
+ * Unregister AMR codec factory from pjmedia endpoint and deinitialize
+ * the OpenCORE codec library.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amr_deinit(void);
+
+/**
+ * Initialize and register AMR-NB codec factory to pjmedia endpoint. Calling
+ * this function will automatically initialize AMR codec factory without
+ * the wideband mode (i.e. it is equivalent to calling
+ * #pjmedia_codec_opencore_amr_init() with PJMEDIA_AMR_NO_WB). Application
+ * should call #pjmedia_codec_opencore_amr_init() instead if wishing to use
+ * both modes.
+ *
+ * @param endpt The pjmedia endpoint.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_init(pjmedia_endpt* endpt);
+
+/**
+ * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
+ * the OpenCORE codec library.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void);
+
+
+/**
+ * Set AMR-NB parameters.
+ *
+ * @param cfg The settings;
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
+ const pjmedia_codec_amrnb_config* cfg);
+
+
+/**
+ * Set AMR-WB parameters.
+ *
+ * @param cfg The settings;
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrwb_set_config(
+ const pjmedia_codec_amrwb_config* cfg);
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ */
+
diff --git a/pjmedia/include/pjmedia-codec/opencore_amrnb.h b/pjmedia/include/pjmedia-codec/opencore_amrnb.h
deleted file mode 100644
index aedb460..0000000
--- a/pjmedia/include/pjmedia-codec/opencore_amrnb.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* $Id: opencore_amrnb.h 3841 2011-10-24 09:28:13Z ming $ */
-/*
- * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJMEDIA_CODEC_OPENCORE_AMRNB_H__
-#define __PJMEDIA_CODEC_OPENCORE_AMRNB_H__
-
-#include <pjmedia-codec/types.h>
-
-/**
- * @defgroup PJMED_OC_AMRNB OpenCORE AMR-NB Codec
- * @ingroup PJMEDIA_CODEC_CODECS
- * @brief AMRCodec wrapper for OpenCORE AMR-NB codec
- * @{
- */
-
-PJ_BEGIN_DECL
-
-/**
- * Settings. Use #pjmedia_codec_opencore_amrnb_set_config() to
- * activate.
- */
-typedef struct pjmedia_codec_amrnb_config
-{
- /**
- * Control whether to use octent align.
- */
- pj_bool_t octet_align;
-
- /**
- * Set the bitrate.
- */
- unsigned bitrate;
-
-} pjmedia_codec_amrnb_config;
-
-
-/**
- * Initialize and register AMR-NB codec factory to pjmedia endpoint.
- *
- * @param endpt The pjmedia endpoint.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_init(pjmedia_endpt* endpt);
-
-/**
- * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
- * the OpenCORE codec library.
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void);
-
-
-/**
- * Set AMR-NB parameters.
- *
- * @param cfg The settings;
- *
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
- const pjmedia_codec_amrnb_config* cfg);
-
-PJ_END_DECL
-
-
-/**
- * @}
- */
-
-#endif /* __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ */
-
diff --git a/pjmedia/include/pjmedia-codec/silk.h b/pjmedia/include/pjmedia-codec/silk.h
new file mode 100644
index 0000000..810faca
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/silk.h
@@ -0,0 +1,133 @@
+/* $Id: silk.h 4264 2012-09-24 06:58:16Z nanang $ */
+/*
+ * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
+ * Contributed by Regis Montoya (aka r3gis - www.r3gis.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_CODEC_SILK_H__
+#define __PJMEDIA_CODEC_SILK_H__
+
+/**
+ * @file silk.h
+ * @brief SILK codec.
+ */
+
+#include <pjmedia-codec/types.h>
+
+/**
+ * @defgroup PJMED_SILK SILK Codec Family
+ * @ingroup PJMEDIA_CODEC_CODECS
+ * @brief Implementation of SILK codecs (narrow/medium/wide/superwide-band).
+ * @{
+ *
+ * This section describes functions to initialize and register SILK codec
+ * factory to the codec manager. After the codec factory has been registered,
+ * application can use @ref PJMEDIA_CODEC API to manipulate the codec.
+ *
+ * The SILK codec uses multiple bit rates, and supports super wideband
+ * (24 kHz sampling rate), wideband (16 kHz sampling rate), medium (12kHz
+ * sampling rate), and narrowband (telephone quality, 8 kHz sampling rate).
+ *
+ * By default, the SILK codec factory registers two SILK codecs:
+ * "SILK/8000" narrowband codec and "SILK/16000" wideband codec. This behavior
+ * can be changed by specifying #pjmedia_codec_silk_options flags during
+ * initialization.
+ *
+ *
+ * \section codec_setting Codec Settings
+ *
+ * \subsection general_setting General Settings
+ *
+ * General codec settings for this codec such as VAD and PLC can be
+ * manipulated through the <tt>setting</tt> field in #pjmedia_codec_param.
+ * Please see the documentation of #pjmedia_codec_param for more info.
+ *
+ * \subsection specific_setting Codec Specific Settings
+ *
+ * The following settings are applicable for this codec.
+ *
+ * \subsubsection quality_vs_complexity Quality vs Complexity
+ *
+ * The SILK codec quality versus computational complexity and bandwidth
+ * requirement can be adjusted by modifying the quality and complexity
+ * setting, by calling #pjmedia_codec_silk_set_config().
+ *
+ * The default setting of quality is specified in
+ * #PJMEDIA_CODEC_SILK_DEFAULT_QUALITY. And the default setting of
+ * complexity is specified in #PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY.
+ */
+
+PJ_BEGIN_DECL
+
+typedef struct pjmedia_codec_silk_setting
+{
+ pj_bool_t enabled; /**< Enable/disable. */
+ int quality; /**< Encoding quality, or use -1 for default
+ (@see PJMEDIA_CODEC_SILK_DEFAULT_QUALITY). */
+ int complexity; /**< Encoding complexity, or use -1 for default
+ (@see PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY)*/
+} pjmedia_codec_silk_setting;
+
+
+/**
+ * Initialize and register SILK codec factory to pjmedia endpoint. By default,
+ * only narrowband (8kHz sampling rate) and wideband (16kHz sampling rate)
+ * will be enabled. Quality and complexity for those sampling rate modes
+ * will be set to the default values (see #PJMEDIA_CODEC_SILK_DEFAULT_QUALITY
+ * and #PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY), application may modify these
+ * settings via #pjmedia_codec_silk_set_config().
+ *
+ * @param endpt The pjmedia endpoint.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_silk_init(pjmedia_endpt *endpt);
+
+
+/**
+ * Change the configuration setting of the SILK codec for the specified
+ * clock rate.
+ *
+ * @param clock_rate PCM sampling rate, in Hz, valid values are 8000,
+ * 12000, 16000 and 24000.
+ * @param opt The setting to be applied for the specified
+ * clock rate.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_silk_set_config(
+ unsigned clock_rate,
+ const pjmedia_codec_silk_setting *opt);
+
+
+/**
+ * Unregister SILK codec factory from pjmedia endpoint and deinitialize
+ * the SILK codec library.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_silk_deinit(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_CODEC_SILK_H__ */
+
diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h
index a18fa30..53e71e1 100644
--- a/pjmedia/include/pjmedia-codec/types.h
+++ b/pjmedia/include/pjmedia-codec/types.h
@@ -1,4 +1,4 @@
-/* $Id: types.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: types.h 4264 2012-09-24 06:58:16Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -63,18 +63,10 @@ enum pjmedia_audio_pt
PJMEDIA_RTP_PT_SPEEX_NB, /**< Speex narrowband/8KHz */
PJMEDIA_RTP_PT_SPEEX_WB, /**< Speex wideband/16KHz */
PJMEDIA_RTP_PT_SPEEX_UWB, /**< Speex 32KHz */
- PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */
- PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */
- //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */
- //PJMEDIA_RTP_PT_L16_11KHZ_STEREO, /**< L16 @ 11KHz, stereo */
- PJMEDIA_RTP_PT_L16_16KHZ_MONO, /**< L16 @ 16KHz, mono */
- PJMEDIA_RTP_PT_L16_16KHZ_STEREO, /**< L16 @ 16KHz, stereo */
- //PJMEDIA_RTP_PT_L16_22KHZ_MONO, /**< L16 @ 22KHz, mono */
- //PJMEDIA_RTP_PT_L16_22KHZ_STEREO, /**< L16 @ 22KHz, stereo */
- //PJMEDIA_RTP_PT_L16_32KHZ_MONO, /**< L16 @ 32KHz, mono */
- //PJMEDIA_RTP_PT_L16_32KHZ_STEREO, /**< L16 @ 32KHz, stereo */
- //PJMEDIA_RTP_PT_L16_48KHZ_MONO, /**< L16 @ 48KHz, mono */
- //PJMEDIA_RTP_PT_L16_48KHZ_STEREO, /**< L16 @ 48KHz, stereo */
+ PJMEDIA_RTP_PT_SILK_NB, /**< SILK narrowband/8KHz */
+ PJMEDIA_RTP_PT_SILK_MB, /**< SILK mediumband/12KHz */
+ PJMEDIA_RTP_PT_SILK_WB, /**< SILK wideband/16KHz */
+ PJMEDIA_RTP_PT_SILK_SWB, /**< SILK 24KHz */
PJMEDIA_RTP_PT_ILBC, /**< iLBC (13.3/15.2Kbps) */
PJMEDIA_RTP_PT_AMR, /**< AMR (4.75 - 12.2Kbps) */
PJMEDIA_RTP_PT_AMRWB, /**< AMRWB (6.6 - 23.85Kbps)*/
@@ -91,6 +83,18 @@ enum pjmedia_audio_pt
PJMEDIA_RTP_PT_G7221C_48, /**< G722.1 Annex C (48Kbps)*/
PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */
PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */
+ PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */
+ PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */
+ //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */
+ //PJMEDIA_RTP_PT_L16_11KHZ_STEREO, /**< L16 @ 11KHz, stereo */
+ PJMEDIA_RTP_PT_L16_16KHZ_MONO, /**< L16 @ 16KHz, mono */
+ PJMEDIA_RTP_PT_L16_16KHZ_STEREO, /**< L16 @ 16KHz, stereo */
+ //PJMEDIA_RTP_PT_L16_22KHZ_MONO, /**< L16 @ 22KHz, mono */
+ //PJMEDIA_RTP_PT_L16_22KHZ_STEREO, /**< L16 @ 22KHz, stereo */
+ //PJMEDIA_RTP_PT_L16_32KHZ_MONO, /**< L16 @ 32KHz, mono */
+ //PJMEDIA_RTP_PT_L16_32KHZ_STEREO, /**< L16 @ 32KHz, stereo */
+ //PJMEDIA_RTP_PT_L16_48KHZ_MONO, /**< L16 @ 48KHz, mono */
+ //PJMEDIA_RTP_PT_L16_48KHZ_STEREO, /**< L16 @ 48KHz, stereo */
/* Caution!
* Ensure the value of the last pt above is <= 127.
diff --git a/pjmedia/include/pjmedia-videodev/config.h b/pjmedia/include/pjmedia-videodev/config.h
index 12a251a..ef161eb 100644
--- a/pjmedia/include/pjmedia-videodev/config.h
+++ b/pjmedia/include/pjmedia-videodev/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 4016 2012-04-04 05:05:50Z bennylp $ */
+/* $Id: config.h 4414 2013-03-05 08:21:02Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -132,6 +132,17 @@ PJ_BEGIN_DECL
# define PJMEDIA_VIDEO_DEV_HAS_AVI 1
#endif
+/**
+ * Specify the SDL library name to be linked with Visual Studio project.
+ * By default, the name is autodetected based on SDL version ("sdl.lib" or
+ * "sdl2.lib"), but application may explicitly specify the library name if this
+ * autodetection fails. Common names are: "sdl2.lib" or "sdl.lib".
+ *
+ * Default: undeclared.
+ */
+#ifndef PJMEDIA_SDL_LIB
+# undef PJMEDIA_SDL_LIB
+#endif
/**
* @}
diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h
index 62c79e0..58fa7a1 100644
--- a/pjmedia/include/pjmedia/codec.h
+++ b/pjmedia/include/pjmedia/codec.h
@@ -1,4 +1,4 @@
-/* $Id: codec.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: codec.h 4278 2012-10-05 10:04:54Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -276,6 +276,7 @@ typedef struct pjmedia_codec_param
unsigned channel_cnt; /**< Channel count. */
pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */
pj_uint32_t max_bps; /**< Maximum bandwidth in bits/sec */
+ unsigned max_rx_frame_size; /**< Maximum frame size */
pj_uint16_t frm_ptime; /**< Decoder frame ptime in msec. */
pj_uint16_t enc_ptime; /**< Encoder ptime, or zero if it's
equal to decoder ptime. */
@@ -308,6 +309,18 @@ typedef struct pjmedia_codec_param
} pjmedia_codec_param;
+/**
+ * Duplicate codec parameter.
+ *
+ * @param pool The pool.
+ * @param src The codec parameter to be duplicated.
+ *
+ * @return Duplicated codec parameter.
+ */
+PJ_DECL(pjmedia_codec_param*) pjmedia_codec_param_clone(
+ pj_pool_t *pool,
+ const pjmedia_codec_param *src);
+
/*
* Forward declaration for pjmedia_codec.
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index d5a598f..b229eca 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 4130 2012-05-17 08:35:51Z nanang $ */
+/* $Id: config.h 4240 2012-08-31 09:03:36Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -371,7 +371,7 @@
/**
- * Max packet size to support.
+ * Max packet size for transmitting direction.
*/
#ifndef PJMEDIA_MAX_MTU
# define PJMEDIA_MAX_MTU 1500
@@ -379,6 +379,14 @@
/**
+ * Max packet size for receiving direction.
+ */
+#ifndef PJMEDIA_MAX_MRU
+# define PJMEDIA_MAX_MRU 2000
+#endif
+
+
+/**
* DTMF/telephone-event duration, in timestamp.
*/
#ifndef PJMEDIA_DTMF_DURATION
@@ -677,6 +685,28 @@
/**
+ * This macro controls whether pjmedia should include SDP
+ * bandwidth modifier "TIAS" (RFC3890).
+ *
+ * Note that there is also a run-time variable to turn this setting
+ * on or off, defined in endpoint.c. To access this variable, use
+ * the following construct
+ *
+ \verbatim
+ extern pj_bool_t pjmedia_add_bandwidth_tias_in_sdp;
+
+ // Do not enable bandwidth information inclusion in sdp
+ pjmedia_add_bandwidth_tias_in_sdp = PJ_FALSE;
+ \endverbatim
+ *
+ * Default: 1 (yes)
+ */
+#ifndef PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP
+# define PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP 1
+#endif
+
+
+/**
* This macro controls whether pjmedia should include SDP rtpmap
* attribute for static payload types. SDP rtpmap for static
* payload types are optional, although they are normally included
diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h
index 2dc58d6..bababef 100644
--- a/pjmedia/include/pjmedia/sdp.h
+++ b/pjmedia/include/pjmedia/sdp.h
@@ -1,4 +1,4 @@
-/* $Id: sdp.h 3945 2012-01-27 09:12:59Z nanang $ */
+/* $Id: sdp.h 4367 2013-02-21 20:49:19Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -686,6 +686,23 @@ PJ_DECL(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp);
/**
+ * Perform semantic validation for the specified SDP session descriptor.
+ * This function perform validation beyond just syntactic verification,
+ * such as to verify the value of network type and address type, check
+ * the connection line, and verify that \a rtpmap attribute is present
+ * when dynamic payload type is used.
+ *
+ * @param sdp The SDP session descriptor to validate.
+ * @param strict Flag whether the check should be strict, i.e: allow
+ * media without connection line when port is zero.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp,
+ pj_bool_t strict);
+
+
+/**
* Clone SDP session descriptor.
*
* @param pool The pool used to clone the session.
diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h
index dca9f29..e86c974 100644
--- a/pjmedia/include/pjmedia/transport.h
+++ b/pjmedia/include/pjmedia/transport.h
@@ -1,4 +1,4 @@
-/* $Id: transport.h 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: transport.h 4345 2013-02-13 07:43:32Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -566,7 +566,7 @@ PJ_INLINE(void) pjmedia_transport_info_init(pjmedia_transport_info *info)
* for example to fill in the "c=" and "m=" line of local SDP.
*
* @param tp The transport.
- * @param info Media socket info to be initialized.
+ * @param info Media transport info to be initialized.
*
* @return PJ_SUCCESS on success.
*/
@@ -581,6 +581,29 @@ PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp,
/**
+ * Utility API to get transport type specific info from the specified media
+ * transport info.
+ *
+ * @param info Media transport info.
+ * @param type Media transport type.
+ *
+ * @return Pointer to media transport specific info, or NULL if
+ * specific info for the transport type is not found.
+ */
+PJ_INLINE(void*) pjmedia_transport_info_get_spc_info(
+ pjmedia_transport_info *info,
+ pjmedia_transport_type type)
+{
+ unsigned i;
+ for (i = 0; i < info->specific_info_cnt; ++i) {
+ if (info->spc_info[i].type == type)
+ return (void*)info->spc_info[i].buffer;
+ }
+ return NULL;
+}
+
+
+/**
* Attach callbacks to be called on receipt of incoming RTP/RTCP packets.
* This is just a simple wrapper which calls <tt>attach()</tt> member of
* the transport.
diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h
index 4500fae..417ee6d 100644
--- a/pjmedia/include/pjmedia/transport_ice.h
+++ b/pjmedia/include/pjmedia/transport_ice.h
@@ -1,4 +1,4 @@
-/* $Id: transport_ice.h 3872 2011-10-28 04:27:41Z bennylp $ */
+/* $Id: transport_ice.h 4350 2013-02-15 03:57:31Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -69,6 +69,12 @@ typedef struct pjmedia_ice_cb
typedef struct pjmedia_ice_transport_info
{
/**
+ * Specifies whether ICE is used, i.e. SDP offer and answer indicates
+ * that both parties support ICE and ICE should be used for the session.
+ */
+ pj_bool_t active;
+
+ /**
* ICE sesion state.
*/
pj_ice_strans_state sess_state;
diff --git a/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/pjmedia/src/pjmedia-audiodev/alsa_dev.c
index 72b995e..31b3b06 100644
--- a/pjmedia/src/pjmedia-audiodev/alsa_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/alsa_dev.c
@@ -1,4 +1,4 @@
-/* $Id: alsa_dev.c 4130 2012-05-17 08:35:51Z nanang $ */
+/* $Id: alsa_dev.c 4283 2012-10-12 06:19:32Z ming $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved.
@@ -232,7 +232,7 @@ static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name)
pj_bzero(adi, sizeof(*adi));
/* Set device name */
- strcpy(adi->name, dev_name);
+ strncpy(adi->name, dev_name, sizeof(adi->name));
/* Check the number of playback channels */
adi->output_count = (pb_result>=0) ? 1 : 0;
diff --git a/pjmedia/src/pjmedia-audiodev/bb10_dev.c b/pjmedia/src/pjmedia-audiodev/bb10_dev.c
index 418256d..f920080 100644
--- a/pjmedia/src/pjmedia-audiodev/bb10_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/bb10_dev.c
@@ -1,4 +1,4 @@
-/* $Id: bb10_dev.c 4151 2012-06-01 04:49:57Z ming $ */
+/* $Id: bb10_dev.c 4340 2013-02-05 05:15:01Z bennylp $ */
/*
* Copyright (C) 2008-2012 Teluu Inc. (http://www.teluu.com)
*
@@ -33,6 +33,11 @@
#if defined(PJMEDIA_AUDIO_DEV_HAS_BB10) && PJMEDIA_AUDIO_DEV_HAS_BB10 != 0
+#ifndef PJ_BBSDK_VER
+ /* Format: 0xMMNNRR: MM: major, NN: minor, RR: revision */
+# define PJ_BBSDK_VER 0x100006
+#endif
+
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
@@ -40,6 +45,9 @@
#include <pthread.h>
#include <errno.h>
#include <sys/asoundlib.h>
+#if PJ_BBSDK_VER >= 0x100006
+#include <audio/audio_manager_routing.h>
+#endif
#define THIS_FILE "bb10_dev.c"
@@ -114,8 +122,9 @@ struct bb10_stream
int quit;
/* Playback */
+ unsigned int pb_ctrl_audio_manager_handle;
snd_pcm_t *pb_pcm;
- snd_mixer_t *pb_mixer;
+ unsigned int pb_audio_manager_handle;
unsigned long pb_frames; /* samples_per_frame */
pjmedia_aud_play_cb pb_cb;
unsigned pb_buf_size;
@@ -124,7 +133,7 @@ struct bb10_stream
/* Capture */
snd_pcm_t *ca_pcm;
- snd_mixer_t *ca_mixer;
+ unsigned int ca_audio_manager_handle;
unsigned long ca_frames; /* samples_per_frame */
pjmedia_aud_rec_cb ca_cb;
unsigned ca_buf_size;
@@ -160,8 +169,7 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af)
{
pjmedia_aud_dev_info *adi;
int pb_result, ca_result;
- int card = -1;
- int dev = 0;
+ unsigned int handle;
snd_pcm_t *pcm_handle;
if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
@@ -171,20 +179,29 @@ static pj_status_t bb10_add_dev (struct bb10_factory *af)
TRACE_((THIS_FILE, "bb10_add_dev Enter"));
- if ((pb_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev,
- SND_PCM_OPEN_PLAYBACK)) >= 0)
+ if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
+ &pcm_handle,
+ &handle,
+ (char*)"voice",
+ SND_PCM_OPEN_PLAYBACK))
+ >= 0)
{
- TRACE_((THIS_FILE, "Try to open the device for playback - success"));
snd_pcm_close (pcm_handle);
+ audio_manager_free_handle(handle);
} else {
TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
}
- if ((ca_result = snd_pcm_open_preferred (&pcm_handle, &card, &dev,
- SND_PCM_OPEN_CAPTURE)) >=0)
+ if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
+ &pcm_handle,
+ &handle,
+ (char*)"voice",
+ SND_PCM_OPEN_CAPTURE))
+ >= 0)
{
- TRACE_((THIS_FILE, "Try to open the device for capture - success"));
- snd_pcm_close (pcm_handle);
+ snd_pcm_close (pcm_handle);
+ audio_manager_free_handle(handle);
+
} else {
TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
}
@@ -239,7 +256,7 @@ pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf)
static pj_status_t bb10_factory_init(pjmedia_aud_dev_factory *f)
{
pj_status_t status;
-
+
status = bb10_factory_refresh(f);
if (status != PJ_SUCCESS)
return status;
@@ -314,7 +331,7 @@ static pj_status_t bb10_factory_get_dev_info(pjmedia_aud_dev_factory *f,
pj_memcpy(info, &af->devs[index], sizeof(*info));
info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
-
+
return PJ_SUCCESS;
}
@@ -358,7 +375,7 @@ static pj_status_t bb10_factory_default_param(pjmedia_aud_dev_factory *f,
TRACE_((THIS_FILE, "bb10_factory_default_param clock = %d flags = %d"
" spf = %d", param->clock_rate, param->flags,
param->samples_per_frame));
-
+
return PJ_SUCCESS;
}
@@ -368,14 +385,16 @@ static void close_play_pcm(struct bb10_stream *stream)
if (stream != NULL && stream->pb_pcm != NULL) {
snd_pcm_close(stream->pb_pcm);
stream->pb_pcm = NULL;
- }
-}
-static void close_play_mixer(struct bb10_stream *stream)
-{
- if (stream != NULL && stream->pb_mixer != NULL) {
- snd_mixer_close(stream->pb_mixer);
- stream->pb_mixer = NULL;
+ if (stream->pb_audio_manager_handle != 0) {
+ audio_manager_free_handle(stream->pb_audio_manager_handle);
+ stream->pb_audio_manager_handle = 0;
+ }
+
+ if (stream->pb_ctrl_audio_manager_handle != 0) {
+ audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle);
+ stream->pb_ctrl_audio_manager_handle = 0;
+ }
}
}
@@ -391,14 +410,11 @@ static void close_capture_pcm(struct bb10_stream *stream)
if (stream != NULL && stream->ca_pcm != NULL) {
snd_pcm_close(stream->ca_pcm);
stream->ca_pcm = NULL;
- }
-}
-static void close_capture_mixer(struct bb10_stream *stream)
-{
- if (stream != NULL && stream->ca_mixer != NULL) {
- snd_mixer_close(stream->ca_mixer);
- stream->ca_mixer = NULL;
+ if (stream->ca_audio_manager_handle != 0) {
+ audio_manager_free_handle(stream->ca_audio_manager_handle);
+ stream->ca_audio_manager_handle = 0;
+ }
}
}
@@ -435,10 +451,9 @@ static int pb_thread_func (void *arg)
if ((result = snd_pcm_plugin_prepare(stream->pb_pcm,
SND_PCM_CHANNEL_PLAYBACK)) < 0)
{
- close_play_mixer(stream);
close_play_pcm(stream);
TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result));
- return PJ_SUCCESS;
+ return PJ_SUCCESS;
}
while (!stream->quit) {
@@ -451,6 +466,7 @@ static int pb_thread_func (void *arg)
frame.timestamp.u64 = tstamp.u64;
frame.bit_info = 0;
+ /* Read the audio from pjmedia */
result = stream->pb_cb (user_data, &frame);
if (result != PJ_SUCCESS || stream->quit)
break;
@@ -460,18 +476,45 @@ static int pb_thread_func (void *arg)
/* Write 640 to play unit */
result = snd_pcm_plugin_write(stream->pb_pcm,buf,size);
- if (result != size) {
- TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result));
+ if (result != size || result < 0) {
+ /* either the write to output device has failed or not the
+ * full amount of bytes have been written. This usually happens
+ * when audio routing is being changed by another thread
+ * Use a status variable for reading the error
+ */
+ snd_pcm_channel_status_t status;
+ status.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) {
+ /* Call has failed nothing we can do except log and
+ * continue */
+ PJ_LOG(4,(THIS_FILE,
+ "underrun: playback channel status error"));
+ } else {
+ /* The status of the error has been read
+ * RIM say these are expected so we can "re-prepare" the stream
+ */
+ PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d",
+ status.status));
+ if (status.status == SND_PCM_STATUS_READY ||
+ status.status == SND_PCM_STATUS_UNDERRUN ||
+ status.status == SND_PCM_STATUS_ERROR )
+ {
+ if (snd_pcm_plugin_prepare (stream->pb_pcm,
+ SND_PCM_CHANNEL_PLAYBACK) < 0)
+ {
+ PJ_LOG(4,(THIS_FILE,
+ "underrun: playback channel prepare error"));
+ }
+ }
+ }
}
-
tstamp.u64 += nframes;
}
flush_play(stream);
- close_play_mixer(stream);
close_play_pcm(stream);
TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
-
+
return PJ_SUCCESS;
}
@@ -513,29 +556,58 @@ static int ca_thread_func (void *arg)
if ((result = snd_pcm_plugin_prepare (stream->ca_pcm,
SND_PCM_CHANNEL_CAPTURE)) < 0)
{
- close_capture_mixer(stream);
close_capture_pcm(stream);
TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result));
- return PJ_SUCCESS;
+ return PJ_SUCCESS;
}
while (!stream->quit) {
pjmedia_frame frame;
pj_bzero (buf, size);
-
+
+ /* read the input device */
result = snd_pcm_plugin_read(stream->ca_pcm, buf,size);
- if (result == -EPIPE) {
- PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!"));
- snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE);
- continue;
- } else if (result < 0) {
- PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!"));
+ if(result <0 || result != size) {
+ /* We expect result to be size (640)
+ * It's not so we have to read the status error and "prepare"
+ * the channel. This usually happens when output audio routing
+ * has been changed by another thread.
+ * We won't "continue", instead just do what we can and leave
+ * the end of the loop to write what's in the buffer. Not entirely
+ * correct but saves a potential underrun in PJMEDIA
+ */
+ PJ_LOG (4,(THIS_FILE,
+ "snd_pcm_plugin_read ERROR read = %d required = %d",
+ result,size));
+ snd_pcm_channel_status_t status;
+ status.channel = SND_PCM_CHANNEL_CAPTURE;
+ if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0)
+ {
+ /* Should not fail but all we can do is continue */
+ PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d",
+ result));
+ } else {
+ /* RIM say these are the errors that we should "prepare"
+ * after */
+ if (status.status == SND_PCM_STATUS_READY ||
+ status.status == SND_PCM_STATUS_OVERRUN ||
+ status.status == SND_PCM_STATUS_ERROR)
+ {
+ if (snd_pcm_plugin_prepare (stream->ca_pcm,
+ SND_PCM_CHANNEL_CAPTURE) < 0)
+ {
+ PJ_LOG (4,(THIS_FILE,
+ "overrun: capture channel prepare error"));
+ }
+ }
+ }
}
if (stream->quit)
break;
+ /* Write the capture audio data to PJMEDIA */
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.buf = (void *) buf;
frame.size = size;
@@ -550,19 +622,68 @@ static int ca_thread_func (void *arg)
}
flush_capture(stream);
- close_capture_mixer(stream);
close_capture_pcm(stream);
TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
return PJ_SUCCESS;
}
+/* Audio routing, speaker/headset */
+static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream,
+ bool speaker)
+{
+ /* Although the play and capture have audio manager handles, audio routing
+ * requires a separate handle
+ */
+ int ret = PJ_SUCCESS;
+
+ if (stream->pb_ctrl_audio_manager_handle == 0) {
+ /* lazy init an audio manager handle */
+ ret = audio_manager_get_handle(AUDIO_TYPE_VIDEO_CHAT, 0, false,
+ &stream->pb_ctrl_audio_manager_handle);
+ if (ret != 0) {
+ TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+ }
+
+ /* Set for either speaker or earpiece */
+ if (speaker) {
+ ret = audio_manager_set_handle_type(
+ stream->pb_ctrl_audio_manager_handle,
+ AUDIO_TYPE_VIDEO_CHAT,
+ AUDIO_DEVICE_SPEAKER,
+ AUDIO_DEVICE_DEFAULT);
+ } else {
+ ret = audio_manager_set_handle_type(
+ stream->pb_ctrl_audio_manager_handle,
+ AUDIO_TYPE_VIDEO_CHAT,
+ AUDIO_DEVICE_HANDSET,
+ AUDIO_DEVICE_DEFAULT);
+ }
+
+ if (ret == 0) {
+ /* RIM recommend this call */
+ ret = audio_manager_set_handle_routing_conditions(
+ stream->pb_ctrl_audio_manager_handle,
+ SETTINGS_RESET_ON_DEVICE_CONNECTION);
+ if (ret != 0) {
+ TRACE_((THIS_FILE,
+ "audio_manager_set_handle_routing_conditions ret = %d",
+ ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+ } else {
+ TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+
+ return PJ_SUCCESS;
+}
static pj_status_t bb10_open_playback (struct bb10_stream *stream,
const pjmedia_aud_param *param)
{
- int card = -1;
- int dev = 0;
int ret = 0;
snd_pcm_channel_info_t pi;
snd_pcm_channel_setup_t setup;
@@ -575,34 +696,54 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream,
return PJMEDIA_EAUD_INVDEV;
}
- if ((ret = snd_pcm_open_preferred (&stream->pb_pcm, &card, &dev,
- SND_PCM_OPEN_PLAYBACK)) < 0)
+ /* Use the bb10 audio manager API to open as opposed to QNX core audio
+ * Echo cancellation built in
+ */
+ if ((ret = audio_manager_snd_pcm_open_name(
+ AUDIO_TYPE_VIDEO_CHAT,
+ &stream->pb_pcm, &stream->pb_audio_manager_handle,
+ (char*)"voice",
+ SND_PCM_OPEN_PLAYBACK)) < 0)
{
- TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret));
+ TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
return PJMEDIA_EAUD_SYSERR;
}
+ /* Required call from January 2013 gold OS release */
+ if ((ret = snd_pcm_plugin_set_disable(stream->pb_pcm,
+ PLUGIN_DISABLE_MMAP)) < 0)
+ {
+ TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+
+ /* Required call from January 2013 gold OS release */
+ if ((ret = snd_pcm_plugin_set_enable(stream->pb_pcm,
+ PLUGIN_ROUTING)) < 0)
+ {
+ TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable ret = %d", ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+
/* TODO PJ_ZERO */
memset (&pi, 0, sizeof (pi));
pi.channel = SND_PCM_CHANNEL_PLAYBACK;
if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) {
- TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
- return PJMEDIA_EAUD_SYSERR;
+ TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret));
+ return PJMEDIA_EAUD_SYSERR;
}
memset (&pp, 0, sizeof (pp));
- /* Request VoIP compatible capabilities
- * On simulator frag_size is always negotiated to 170
- */
+ /* Request VoIP compatible capabilities */
pp.mode = SND_PCM_MODE_BLOCK;
pp.channel = SND_PCM_CHANNEL_PLAYBACK;
pp.start_mode = SND_PCM_START_DATA;
pp.stop_mode = SND_PCM_STOP_ROLLOVER;
/* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
- /* Increasing this internal buffer count delays write failure in the loop */
- pp.buf.block.frags_max = 4;
+ /* RIM recommends maximum of 3 */
+ pp.buf.block.frags_max = 3;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
/* HARD CODE for the time being PJMEDIA expects 16khz */
@@ -622,7 +763,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream,
memset (&group, 0, sizeof (group));
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
setup.mixer_gid = &group.gid;
-
+
if ((ret = snd_pcm_plugin_setup (stream->pb_pcm, &setup)) < 0) {
TRACE_((THIS_FILE, "snd_pcm_plugin_setup ret = %d", ret));
return PJMEDIA_EAUD_SYSERR;
@@ -631,14 +772,6 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream,
if (group.gid.name[0] == 0) {
return PJMEDIA_EAUD_SYSERR;
}
-
- if ((ret = snd_mixer_open (&stream->pb_mixer, card,
- setup.mixer_device)) < 0)
- {
- TRACE_((THIS_FILE, "snd_mixer_open ret = %d", ret));
- return PJMEDIA_EAUD_SYSERR;
- }
-
rate = param->clock_rate;
/* Set the sound device buffer size and latency */
@@ -658,7 +791,7 @@ static pj_status_t bb10_open_playback (struct bb10_stream *stream,
TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d",
stream->pb_frames, param->clock_rate));
-
+
return PJ_SUCCESS;
}
@@ -668,8 +801,6 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream,
int ret = 0;
unsigned int rate;
unsigned long tmp_buf_size;
- int card = -1;
- int dev = 0;
int frame_size;
snd_pcm_channel_info_t pi;
snd_mixer_group_t group;
@@ -679,11 +810,27 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream,
if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
return PJMEDIA_EAUD_INVDEV;
- /* BB10 Audio init here (not prepare) */
- if ((ret = snd_pcm_open_preferred (&stream->ca_pcm, &card, &dev,
- SND_PCM_OPEN_CAPTURE)) < 0)
+ if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT,
+ &stream->ca_pcm,
+ &stream->ca_audio_manager_handle,
+ (char*)"voice",
+ SND_PCM_OPEN_CAPTURE)) < 0)
+ {
+ TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+ /* Required call from January 2013 gold OS release */
+ if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm,
+ PLUGIN_DISABLE_MMAP)) < 0)
{
- TRACE_((THIS_FILE, "snd_pcm_open_preferred ret = %d", ret));
+ TRACE_(("snd_pcm_plugin_set_disable failed: %d",ret));
+ return PJMEDIA_EAUD_SYSERR;
+ }
+ /* Required call from January 2013 gold OS release */
+ if ((ret = snd_pcm_plugin_set_enable(stream->ca_pcm,
+ PLUGIN_ROUTING)) < 0)
+ {
+ TRACE_(("snd_pcm_plugin_set_enable failed: %d",ret));
return PJMEDIA_EAUD_SYSERR;
}
@@ -707,8 +854,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream,
pp.stop_mode = SND_PCM_STOP_ROLLOVER;
/* HARD CODE for the time being PJMEDIA expects 640 for 16khz */
pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2;
- /* Not applicable for capture hence -1 */
- pp.buf.block.frags_max = -1;
+ /* From January 2013 gold OS release. RIM recommend these for capture */
+ pp.buf.block.frags_max = 1;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
/* HARD CODE for the time being PJMEDIA expects 16khz */
@@ -740,18 +887,8 @@ static pj_status_t bb10_open_capture (struct bb10_stream *stream,
} else {
}
- if ((ret = snd_mixer_open (&stream->ca_mixer, card,
- setup.mixer_device)) < 0)
- {
- TRACE_((THIS_FILE,"snd_mixer_open ret = %d",ret));
- return PJMEDIA_EAUD_SYSERR;
- }
-
- /* frag_size should be 160 */
frame_size = setup.buf.block.frag_size;
- /* END BB10 init */
-
/* Set clock rate */
rate = param->clock_rate;
stream->ca_frames = (unsigned long) param->samples_per_frame /
@@ -820,7 +957,6 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
status = bb10_open_capture (stream, param);
if (status != PJ_SUCCESS) {
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
- close_play_mixer(stream);
close_play_pcm(stream);
}
pj_pool_release (pool);
@@ -828,12 +964,24 @@ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f,
}
}
+ /* Part of the play functionality but the RIM/Truphone loopback sample
+ * initialializes after the play and capture
+ * "false" is default/earpiece for output
+ */
+ status = bb10_initialize_playback_ctrl(stream,false);
+ if (status != PJ_SUCCESS) {
+ return PJMEDIA_EAUD_SYSERR;
+ }
+
*p_strm = &stream->base;
return PJ_SUCCESS;
}
-/* API: get running parameter */
+/*
+ * API: get running parameter
+ * based on ALSA template
+ */
static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s,
pjmedia_aud_param *pi)
{
@@ -847,7 +995,10 @@ static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s,
}
-/* API: get capability */
+/*
+ * API: get capability
+ * based on ALSA template
+*/
static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s,
pjmedia_aud_dev_cap cap,
void *pval)
@@ -862,28 +1013,51 @@ static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s,
/* Recording latency */
*(unsigned*)pval = stream->param.input_latency_ms;
return PJ_SUCCESS;
+
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
(stream->param.dir & PJMEDIA_DIR_PLAYBACK))
{
/* Playback latency */
*(unsigned*)pval = stream->param.output_latency_ms;
return PJ_SUCCESS;
+
} else {
return PJMEDIA_EAUD_INVCAP;
}
}
-/* API: set capability */
+/*
+ * API: set capability
+ * Currently just supporting toggle between speaker and earpiece
+ */
static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
const void *value)
{
- PJ_UNUSED_ARG(strm);
- PJ_UNUSED_ARG(cap);
- PJ_UNUSED_ARG(value);
+ pj_status_t ret = PJ_SUCCESS;
+ struct bb10_stream *stream = (struct bb10_stream*)strm;
+
+ if (cap != PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE || value == NULL) {
+ TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP"));
+ return PJMEDIA_EAUD_INVCAP;
+
+ } else {
+ pjmedia_aud_dev_route route = *((pjmedia_aud_dev_route*)value);
+ /* Use the initialization function which lazy-inits the
+ * handle for routing
+ */
+ if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) {
+ ret = bb10_initialize_playback_ctrl(stream,true);
+ } else {
+ ret = bb10_initialize_playback_ctrl(stream,false);
+ }
+ }
- return PJMEDIA_EAUD_INVCAP;
+ if (ret != PJ_SUCCESS) {
+ TRACE_((THIS_FILE,"bb10_stream_set_cap() = %d",ret));
+ }
+ return ret;
}
@@ -952,7 +1126,7 @@ static pj_status_t bb10_stream_stop (pjmedia_aud_stream *s)
static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s)
{
struct bb10_stream *stream = (struct bb10_stream*)s;
-
+
TRACE_((THIS_FILE,"bb10_stream_destroy()"));
bb10_stream_stop (s);
diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
index d2c6564..9b60ae1 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
@@ -1,4 +1,4 @@
-/* $Id: symb_aps_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: symb_aps_dev.cpp 4243 2012-08-31 11:42:17Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -973,7 +973,8 @@ static void RecCb(TAPSCommBuffer &buf, void *user_data)
{
unsigned samples_got;
- samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
+ samples_got =
+ strm->param.ext_fmt.det.aud.avg_bps == 15200? 160 : 240;
/* Check if we got a normal frame. */
if (buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0) {
@@ -1171,9 +1172,9 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
- pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
+ pj_assert((strm->param.ext_fmt.det.aud.avg_bps == 15200 &&
samples_cnt == 160) ||
- (strm->param.ext_fmt.bitrate != 15200 &&
+ (strm->param.ext_fmt.det.aud.avg_bps != 15200 &&
samples_cnt == 240));
if (sf->data && sf->bitlen) {
@@ -1391,34 +1392,41 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
}
if (supported) {
+ pjmedia_format ext_fmt;
+
switch(i) {
case 0: /* AMRNB */
- af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_AMR;
- af->dev_info.ext_fmt[fmt_cnt].bitrate = 7400;
- af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_AMR,
+ 8000, 1, 16, 20, 7400, 12200);
+ af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
++fmt_cnt;
break;
case 1: /* G.711 */
- af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
- af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
- af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMU,
+ 8000, 1, 16, 20, 64000, 64000);
+ af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
++fmt_cnt;
- af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
- af->dev_info.ext_fmt[fmt_cnt].bitrate = 64000;
- af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMA,
+ 8000, 1, 16, 20, 64000, 64000);
+ af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
++fmt_cnt;
g711_supported = PJ_TRUE;
break;
case 2: /* G.729 */
- af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_G729;
- af->dev_info.ext_fmt[fmt_cnt].bitrate = 8000;
- af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_G729,
+ 8000, 1, 16, 20, 8000, 8000);
+ af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_FALSE;
++fmt_cnt;
break;
case 3: /* iLBC */
- af->dev_info.ext_fmt[fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
- af->dev_info.ext_fmt[fmt_cnt].bitrate = 13333;
- af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_ILBC,
+ 8000, 1, 16, 30, 13333, 15200);
+ af->dev_info.ext_fmt[fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[fmt_cnt].vad = PJ_TRUE;
++fmt_cnt;
break;
}
@@ -1570,18 +1578,18 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
/* Set audio engine mode. */
if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
{
- aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.bitrate;
+ aps_setting.mode = (TAPSCodecMode)strm->param.ext_fmt.det.aud.avg_bps;
}
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU ||
strm->param.ext_fmt.id == PJMEDIA_FORMAT_L16 ||
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
- strm->param.ext_fmt.bitrate != 15200))
+ strm->param.ext_fmt.det.aud.avg_bps != 15200))
{
aps_setting.mode = EULawOr30ms;
}
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
(strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC &&
- strm->param.ext_fmt.bitrate == 15200))
+ strm->param.ext_fmt.det.aud.avg_bps == 15200))
{
aps_setting.mode = EALawOr20ms;
}
@@ -1596,11 +1604,13 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
{
aps_setting.vad = EFalse;
} else {
- aps_setting.vad = strm->param.ext_fmt.vad;
+ aps_setting.vad = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_VAD) &&
+ strm->param.vad_enabled;
}
/* Set other audio engine attributes. */
- aps_setting.plc = strm->param.plc_enabled;
+ aps_setting.plc = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_PLC) &&
+ strm->param.plc_enabled;
aps_setting.cng = aps_setting.vad;
aps_setting.loudspk =
strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
diff --git a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
index 5b94903..70fe3b3 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
@@ -1,4 +1,4 @@
-/* $Id: symb_vas_dev.cpp 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: symb_vas_dev.cpp 4243 2012-08-31 11:42:17Z nanang $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -1015,7 +1015,8 @@ static void RecCb(CVoIPDataBuffer *buf, void *user_data)
{
unsigned samples_got;
- samples_got = strm->param.ext_fmt.bitrate == 15200? 160 : 240;
+ samples_got =
+ strm->param.ext_fmt.det.aud.avg_bps == 15200? 160 : 240;
/* Check if we got a normal or SID frame. */
if (buffer[0] != 0) {
@@ -1214,9 +1215,9 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
sf = pjmedia_frame_ext_get_subframe(frame, 0);
samples_cnt = frame->samples_cnt / frame->subframe_cnt;
- pj_assert((strm->param.ext_fmt.bitrate == 15200 &&
+ pj_assert((strm->param.ext_fmt.det.aud.avg_bps == 15200 &&
samples_cnt == 160) ||
- (strm->param.ext_fmt.bitrate != 15200 &&
+ (strm->param.ext_fmt.det.aud.avg_bps != 15200 &&
samples_cnt == 240));
if (sf->data && sf->bitlen) {
@@ -1230,7 +1231,8 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
buffer.Append(0);
/* VAS iLBC frame is 20ms or 30ms */
- frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
+ frame_len =
+ strm->param.ext_fmt.det.aud.avg_bps == 15200? 38 : 50;
buffer.AppendFill(0, frame_len);
}
@@ -1244,7 +1246,8 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
buffer.Append(0);
/* VAS iLBC frame is 20ms or 30ms */
- frame_len = strm->param.ext_fmt.bitrate == 15200? 38 : 50;
+ frame_len =
+ strm->param.ext_fmt.det.aud.avg_bps == 15200? 38 : 50;
buffer.AppendFill(0, frame_len);
}
@@ -1408,40 +1411,47 @@ static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
vas_factory_ = NULL;
for (TInt i = 0; i < dnlink_formats.Count(); i++) {
+ pjmedia_format ext_fmt;
+
/* Format must be supported by both downlink & uplink. */
if (uplink_formats.Find(dnlink_formats[i]) == KErrNotFound)
continue;
switch (dnlink_formats[i]) {
case EAMR_NB:
- af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_AMR;
- af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 7400;
- af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_AMR,
+ 8000, 1, 16, 20, 7400, 12200);
+ af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
break;
case EG729:
- af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_G729;
- af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 8000;
- af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_G729,
+ 8000, 1, 16, 20, 8000, 8000);
+ af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
break;
case EILBC:
- af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_ILBC;
- af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 13333;
- af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_ILBC,
+ 8000, 1, 16, 30, 13333, 15200);
+ af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_TRUE;
break;
case EG711:
#if PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION==2
case EG711_10MS:
#endif
- af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMU;
- af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
- af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMU,
+ 8000, 1, 16, 20, 64000, 64000);
+ af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
++ext_fmt_cnt;
- af->dev_info.ext_fmt[ext_fmt_cnt].id = PJMEDIA_FORMAT_PCMA;
- af->dev_info.ext_fmt[ext_fmt_cnt].bitrate = 64000;
- af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
+ pjmedia_format_init_audio(&ext_fmt, PJMEDIA_FORMAT_PCMA,
+ 8000, 1, 16, 20, 64000, 64000);
+ af->dev_info.ext_fmt[ext_fmt_cnt] = ext_fmt;
+ //af->dev_info.ext_fmt[ext_fmt_cnt].vad = PJ_FALSE;
break;
default:
@@ -1616,7 +1626,7 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
}
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_AMR)
{
- vas_setting.mode = strm->param.ext_fmt.bitrate;
+ vas_setting.mode = strm->param.ext_fmt.det.aud.avg_bps;
}
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_PCMU)
{
@@ -1628,7 +1638,7 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
}
else if (strm->param.ext_fmt.id == PJMEDIA_FORMAT_ILBC)
{
- if (strm->param.ext_fmt.bitrate == 15200)
+ if (strm->param.ext_fmt.det.aud.avg_bps == 15200)
vas_setting.mode = CVoIPFormatIntfc::EiLBC20mSecFrame;
else
vas_setting.mode = CVoIPFormatIntfc::EiLBC30mSecFrame;
@@ -1647,11 +1657,13 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
{
vas_setting.vad = EFalse;
} else {
- vas_setting.vad = strm->param.ext_fmt.vad;
+ vas_setting.vad = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_VAD) &&
+ strm->param.vad_enabled;
}
/* Set other audio engine attributes. */
- vas_setting.plc = strm->param.plc_enabled;
+ vas_setting.plc = (strm->param.flags & PJMEDIA_AUD_DEV_CAP_PLC) &&
+ strm->param.plc_enabled;
vas_setting.cng = vas_setting.vad;
vas_setting.loudspk =
strm->param.output_route==PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c
index caf63e5..0c21821 100644
--- a/pjmedia/src/pjmedia-codec/audio_codecs.c
+++ b/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -1,4 +1,4 @@
-/* $Id: audio_codecs.c 3910 2011-12-15 06:34:25Z nanang $ */
+/* $Id: audio_codecs.c 4335 2013-01-29 08:09:15Z ming $ */
/*
* Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -107,9 +107,16 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
return status;
#endif /* PJMEDIA_HAS_L16_CODEC */
-#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
- /* Register OpenCORE AMR-NB */
- status = pjmedia_codec_opencore_amrnb_init(endpt);
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC || PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+ /* Register OpenCORE AMR */
+ status = pjmedia_codec_opencore_amr_init(endpt, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
+#if PJMEDIA_HAS_SILK_CODEC
+ /* Register SILK */
+ status = pjmedia_codec_silk_init(endpt);
if (status != PJ_SUCCESS)
return status;
#endif
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
index d0e87ef..a71a2cc 100644
--- a/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c
@@ -1,4 +1,4 @@
-/* $Id: ffmpeg_vid_codecs.c 4089 2012-04-26 07:27:06Z nanang $ */
+/* $Id: ffmpeg_vid_codecs.c 4311 2012-12-20 06:45:09Z nanang $ */
/*
* Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -62,11 +62,16 @@
#endif
#if LIBAVCODEC_VER_AT_LEAST(53,61)
-/* Not sure when AVCodec::encode2 is introduced. It appears in
- * libavcodec 53.61 where some codecs actually still use AVCodec::encode
- * (e.g: H263, H264).
- */
-# define AVCODEC_HAS_ENCODE(c) (c->encode || c->encode2)
+# if LIBAVCODEC_VER_AT_LEAST(54,59)
+ /* Not sure when AVCodec::encode is obsoleted/removed. */
+# define AVCODEC_HAS_ENCODE(c) (c->encode2)
+# else
+ /* Not sure when AVCodec::encode2 is introduced. It appears in
+ * libavcodec 53.61 where some codecs actually still use AVCodec::encode
+ * (e.g: H263, H264).
+ */
+# define AVCODEC_HAS_ENCODE(c) (c->encode || c->encode2)
+# endif
# define AV_OPT_SET(obj,name,val,opt) (av_opt_set(obj,name,val,opt)==0)
# define AV_OPT_SET_INT(obj,name,val) (av_opt_set_int(obj,name,val,0)==0)
#else
diff --git a/pjmedia/src/pjmedia-codec/opencore_amrnb.c b/pjmedia/src/pjmedia-codec/opencore_amr.c
index 9b6daa9..ccfcc2e 100644
--- a/pjmedia/src/pjmedia-codec/opencore_amrnb.c
+++ b/pjmedia/src/pjmedia-codec/opencore_amr.c
@@ -1,6 +1,6 @@
-/* $Id: opencore_amrnb.c 3939 2012-01-10 05:38:40Z nanang $ */
+/* $Id: opencore_amr.c 4348 2013-02-14 02:00:13Z ming $ */
/*
- * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2011-2013 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,7 @@
*/
/*
- * AMR-NB codec implementation with OpenCORE AMRNB library
+ * AMR codec implementation with OpenCORE AMR library
*/
#include <pjmedia-codec/g722.h>
#include <pjmedia-codec/amr_sdp_match.h>
@@ -38,13 +38,30 @@
#if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \
(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0)
+#define USE_AMRNB
+#endif
+
+#if defined(PJMEDIA_HAS_OPENCORE_AMRWB_CODEC) && \
+ (PJMEDIA_HAS_OPENCORE_AMRWB_CODEC != 0)
+#define USE_AMRWB
+#endif
+#if defined(USE_AMRNB) || defined(USE_AMRWB)
+
+#ifdef USE_AMRNB
#include <opencore-amrnb/interf_enc.h>
#include <opencore-amrnb/interf_dec.h>
+#endif
+
+#ifdef USE_AMRWB
+#include <vo-amrwbenc/enc_if.h>
+#include <opencore-amrwb/dec_if.h>
+#endif
+
#include <pjmedia-codec/amr_helper.h>
-#include <pjmedia-codec/opencore_amrnb.h>
+#include <pjmedia-codec/opencore_amr.h>
-#define THIS_FILE "opencore_amrnb.c"
+#define THIS_FILE "opencore_amr.c"
/* Tracing */
#define PJ_TRACE 0
@@ -58,9 +75,10 @@
/* Use PJMEDIA PLC */
#define USE_PJMEDIA_PLC 1
+#define FRAME_LENGTH_MS 20
-/* Prototypes for AMR-NB factory */
+/* Prototypes for AMR factory */
static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory,
const pjmedia_codec_info *id );
static pj_status_t amr_default_attr(pjmedia_codec_factory *factory,
@@ -75,7 +93,7 @@ static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory,
static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory,
pjmedia_codec *codec );
-/* Prototypes for AMR-NB implementation. */
+/* Prototypes for AMR implementation. */
static pj_status_t amr_codec_init(pjmedia_codec *codec,
pj_pool_t *pool );
static pj_status_t amr_codec_open(pjmedia_codec *codec,
@@ -103,7 +121,7 @@ static pj_status_t amr_codec_recover(pjmedia_codec *codec,
-/* Definition for AMR-NB codec operations. */
+/* Definition for AMR codec operations. */
static pjmedia_codec_op amr_op =
{
&amr_codec_init,
@@ -116,7 +134,7 @@ static pjmedia_codec_op amr_op =
&amr_codec_recover
};
-/* Definition for AMR-NB codec factory operations. */
+/* Definition for AMR codec factory operations. */
static pjmedia_codec_factory_op amr_factory_op =
{
&amr_test_alloc,
@@ -124,23 +142,25 @@ static pjmedia_codec_factory_op amr_factory_op =
&amr_enum_codecs,
&amr_alloc_codec,
&amr_dealloc_codec,
- &pjmedia_codec_opencore_amrnb_deinit
+ &pjmedia_codec_opencore_amr_deinit
};
-/* AMR-NB factory */
+/* AMR factory */
static struct amr_codec_factory
{
pjmedia_codec_factory base;
pjmedia_endpt *endpt;
pj_pool_t *pool;
+ pj_bool_t init[2];
} amr_codec_factory;
-/* AMR-NB codec private data. */
+/* AMR codec private data. */
struct amr_data
{
pj_pool_t *pool;
+ unsigned clock_rate;
void *encoder;
void *decoder;
pj_bool_t plc_enabled;
@@ -154,18 +174,38 @@ struct amr_data
pj_timestamp last_tx;
};
-static pjmedia_codec_amrnb_config def_config =
+/* Index for AMR tables. */
+enum
{
+ IDX_AMR_NB, /* Index for narrowband. */
+ IDX_AMR_WB /* Index for wideband. */
+};
+
+static pjmedia_codec_amr_config def_config[2] =
+{{ /* AMR-NB */
PJ_FALSE, /* octet align */
5900 /* bitrate */
-};
+ },
+ { /* AMR-WB */
+ PJ_FALSE, /* octet align */
+ 12650 /* bitrate */
+ }};
+static const pj_uint16_t* amr_bitrates[2] =
+ {pjmedia_codec_amrnb_bitrates, pjmedia_codec_amrwb_bitrates};
+
+static const unsigned amr_bitrates_size[2] =
+{
+ PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates),
+ PJ_ARRAY_SIZE(pjmedia_codec_amrwb_bitrates)
+};
/*
- * Initialize and register AMR-NB codec factory to pjmedia endpoint.
+ * Initialize and register AMR codec factory to pjmedia endpoint.
*/
-PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_init( pjmedia_endpt *endpt,
+ unsigned options)
{
pjmedia_codec_mgr *codec_mgr;
pj_str_t codec_name;
@@ -174,12 +214,22 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
if (amr_codec_factory.pool != NULL)
return PJ_SUCCESS;
- /* Create AMR-NB codec factory. */
+ /* Create AMR codec factory. */
amr_codec_factory.base.op = &amr_factory_op;
amr_codec_factory.base.factory_data = NULL;
amr_codec_factory.endpt = endpt;
+#ifdef USE_AMRNB
+ amr_codec_factory.init[IDX_AMR_NB] = ((options & PJMEDIA_AMR_NO_NB) == 0);
+#else
+ amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
+#endif
+#ifdef USE_AMRWB
+ amr_codec_factory.init[IDX_AMR_WB] = ((options & PJMEDIA_AMR_NO_WB) == 0);
+#else
+ amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
+#endif
- amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amrnb", 1000,
+ amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amr", 1000,
1000);
if (!amr_codec_factory.pool)
return PJ_ENOMEM;
@@ -214,16 +264,30 @@ on_error:
return status;
}
+PJ_DEF(pj_status_t)
+pjmedia_codec_opencore_amr_init_default( pjmedia_endpt *endpt )
+{
+ return pjmedia_codec_opencore_amr_init(endpt, 0);
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
+{
+ return pjmedia_codec_opencore_amr_init(endpt, PJMEDIA_AMR_NO_WB);
+}
+
/*
- * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
- * the AMR-NB codec library.
+ * Unregister AMR codec factory from pjmedia endpoint and deinitialize
+ * the AMR codec library.
*/
-PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amr_deinit(void)
{
pjmedia_codec_mgr *codec_mgr;
pj_status_t status;
+ amr_codec_factory.init[IDX_AMR_NB] = PJ_FALSE;
+ amr_codec_factory.init[IDX_AMR_WB] = PJ_FALSE;
+
if (amr_codec_factory.pool == NULL)
return PJ_SUCCESS;
@@ -235,7 +299,7 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
return PJ_EINVALIDOP;
}
- /* Unregister AMR-NB codec factory. */
+ /* Unregister AMR codec factory. */
status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
&amr_codec_factory.base);
@@ -246,50 +310,93 @@ PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
return status;
}
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
+{
+ if (amr_codec_factory.init[IDX_AMR_NB] &&
+ amr_codec_factory.init[IDX_AMR_WB])
+ {
+ PJ_LOG(4, (THIS_FILE, "Should call "
+ "pjmedia_codec_opencore_amr_deinit() instead"));
+
+ return PJ_EINVALIDOP;
+ }
+
+ return pjmedia_codec_opencore_amr_deinit();
+}
-PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
- const pjmedia_codec_amrnb_config *config)
+static pj_status_t
+amr_set_config(unsigned idx, const pjmedia_codec_amr_config *config)
{
unsigned nbitrates;
-
- def_config = *config;
+ def_config[idx] = *config;
/* Normalize bitrate. */
- nbitrates = PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates);
- if (def_config.bitrate < pjmedia_codec_amrnb_bitrates[0])
- def_config.bitrate = pjmedia_codec_amrnb_bitrates[0];
- else if (def_config.bitrate > pjmedia_codec_amrnb_bitrates[nbitrates-1])
- def_config.bitrate = pjmedia_codec_amrnb_bitrates[nbitrates-1];
- else
+ nbitrates = amr_bitrates_size[idx];
+ if (def_config[idx].bitrate < amr_bitrates[idx][0]) {
+ def_config[idx].bitrate = amr_bitrates[idx][0];
+ } else if (def_config[idx].bitrate > amr_bitrates[idx][nbitrates-1]) {
+ def_config[idx].bitrate = amr_bitrates[idx][nbitrates-1];
+ } else
{
unsigned i;
for (i = 0; i < nbitrates; ++i) {
- if (def_config.bitrate <= pjmedia_codec_amrnb_bitrates[i])
+ if (def_config[idx].bitrate <= amr_bitrates[idx][i])
break;
}
- def_config.bitrate = pjmedia_codec_amrnb_bitrates[i];
+ def_config[idx].bitrate = amr_bitrates[idx][i];
}
return PJ_SUCCESS;
}
-/*
- * Check if factory can allocate the specified codec.
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
+ const pjmedia_codec_amrnb_config *config)
+{
+ return amr_set_config(IDX_AMR_NB, (const pjmedia_codec_amr_config *)config);
+}
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrwb_set_config(
+ const pjmedia_codec_amrwb_config *config)
+{
+ return amr_set_config(IDX_AMR_WB, (const pjmedia_codec_amr_config *)config);
+}
+
+/*
+ * Check if factory can allocate the specified codec.
*/
static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory,
const pjmedia_codec_info *info )
{
+ const pj_str_t amr_tag = { "AMR", 3};
+ const pj_str_t amrwb_tag = { "AMR-WB", 6};
PJ_UNUSED_ARG(factory);
+ /* Type MUST be audio. */
+ if (info->type != PJMEDIA_TYPE_AUDIO)
+ return PJMEDIA_CODEC_EUNSUP;
+
/* Check payload type. */
- if (info->pt != PJMEDIA_RTP_PT_AMR)
+ if (info->pt != PJMEDIA_RTP_PT_AMR && info->pt != PJMEDIA_RTP_PT_AMRWB)
return PJMEDIA_CODEC_EUNSUP;
+
+ /* Check encoding name. */
+ if (pj_stricmp(&info->encoding_name, &amr_tag) != 0 &&
+ pj_stricmp(&info->encoding_name, &amrwb_tag) != 0)
+ {
+ return PJMEDIA_CODEC_EUNSUP;
+ }
+
+ /* Check clock-rate */
+ if ((info->clock_rate == 8000 && amr_codec_factory.init[IDX_AMR_NB]) ||
+ (info->clock_rate == 16000 && amr_codec_factory.init[IDX_AMR_WB]))
+ {
+ return PJ_SUCCESS;
+ }
- /* Ignore the rest, since it's static payload type. */
-
- return PJ_SUCCESS;
+ /* Unsupported or disabled. */
+ return PJMEDIA_CODEC_EUNSUP;
}
/*
@@ -299,23 +406,25 @@ static pj_status_t amr_default_attr( pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
pjmedia_codec_param *attr )
{
+ unsigned idx;
+
PJ_UNUSED_ARG(factory);
- PJ_UNUSED_ARG(id);
+ idx = (id->clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
pj_bzero(attr, sizeof(pjmedia_codec_param));
- attr->info.clock_rate = 8000;
+ attr->info.clock_rate = (id->clock_rate <= 8000? 8000: 16000);
attr->info.channel_cnt = 1;
- attr->info.avg_bps = def_config.bitrate;
- attr->info.max_bps = pjmedia_codec_amrnb_bitrates[7];
+ attr->info.avg_bps = def_config[idx].bitrate;
+ attr->info.max_bps = amr_bitrates[idx][amr_bitrates_size[idx]-1];
attr->info.pcm_bits_per_sample = 16;
attr->info.frm_ptime = 20;
- attr->info.pt = PJMEDIA_RTP_PT_AMR;
+ attr->info.pt = (pj_uint8_t)id->pt;
attr->setting.frm_per_pkt = 2;
attr->setting.vad = 1;
attr->setting.plc = 1;
- if (def_config.octet_align) {
+ if (def_config[idx].octet_align) {
attr->setting.dec_fmtp.cnt = 1;
attr->setting.dec_fmtp.param[0].name = pj_str("octet-align");
attr->setting.dec_fmtp.param[0].val = pj_str("1");
@@ -328,7 +437,7 @@ static pj_status_t amr_default_attr( pjmedia_codec_factory *factory,
/*
- * Enum codecs supported by this factory (i.e. only AMR-NB!).
+ * Enum codecs supported by this factory (i.e. AMR-NB and AMR-WB).
*/
static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory,
unsigned *count,
@@ -337,21 +446,34 @@ static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory,
PJ_UNUSED_ARG(factory);
PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
- pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
- codecs[0].encoding_name = pj_str("AMR");
- codecs[0].pt = PJMEDIA_RTP_PT_AMR;
- codecs[0].type = PJMEDIA_TYPE_AUDIO;
- codecs[0].clock_rate = 8000;
- codecs[0].channel_cnt = 1;
+ *count = 0;
- *count = 1;
+ if (amr_codec_factory.init[IDX_AMR_NB]) {
+ pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+ codecs[*count].encoding_name = pj_str("AMR");
+ codecs[*count].pt = PJMEDIA_RTP_PT_AMR;
+ codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+ codecs[*count].clock_rate = 8000;
+ codecs[*count].channel_cnt = 1;
+ (*count)++;
+ }
+
+ if (amr_codec_factory.init[IDX_AMR_WB]) {
+ pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+ codecs[*count].encoding_name = pj_str("AMR-WB");
+ codecs[*count].pt = PJMEDIA_RTP_PT_AMRWB;
+ codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+ codecs[*count].clock_rate = 16000;
+ codecs[*count].channel_cnt = 1;
+ (*count)++;
+ }
return PJ_SUCCESS;
}
/*
- * Allocate a new AMR-NB codec instance.
+ * Allocate a new AMR codec instance.
*/
static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory,
const pjmedia_codec_info *id,
@@ -365,7 +487,7 @@ static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory,
PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
- pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amrnb-inst",
+ pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amr-inst",
512, 512);
codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
@@ -379,7 +501,9 @@ static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory,
#if USE_PJMEDIA_PLC
/* Create PLC */
- status = pjmedia_plc_create(pool, 8000, 160, 0, &amr_data->plc);
+ status = pjmedia_plc_create(pool, id->clock_rate,
+ id->clock_rate * FRAME_LENGTH_MS / 1000, 0,
+ &amr_data->plc);
if (status != PJ_SUCCESS) {
return status;
}
@@ -437,12 +561,14 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec,
pj_uint8_t octet_align = 0;
pj_int8_t enc_mode;
const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+ unsigned idx;
PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+ idx = (attr->info.clock_rate <= 8000? IDX_AMR_NB: IDX_AMR_WB);
enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
- pj_assert(enc_mode >= 0 && enc_mode <= 7);
+ pj_assert(enc_mode >= 0 && enc_mode < amr_bitrates_size[idx]);
/* Check octet-align */
for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
@@ -476,7 +602,7 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec,
p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
while (l--) {
- if (*p>='0' && *p<='7') {
+ if (*p>='0' && *p<=('0'+amr_bitrates_size[idx]-1)) {
pj_int8_t tmp = *p - '0' - enc_mode;
if (PJ_ABS(diff) > PJ_ABS(tmp) ||
@@ -496,38 +622,56 @@ static pj_status_t amr_codec_open( pjmedia_codec *codec,
}
}
+ amr_data->clock_rate = attr->info.clock_rate;
amr_data->vad_enabled = (attr->setting.vad != 0);
amr_data->plc_enabled = (attr->setting.plc != 0);
amr_data->enc_mode = enc_mode;
- amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+ if (idx == IDX_AMR_NB) {
+#ifdef USE_AMRNB
+ amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+#endif
+ } else {
+#ifdef USE_AMRWB
+ amr_data->encoder = E_IF_init();
+#endif
+ }
if (amr_data->encoder == NULL) {
- TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
+ TRACE_((THIS_FILE, "Encoder initialization failed"));
amr_codec_close(codec);
return PJMEDIA_CODEC_EFAILED;
}
setting = &amr_data->enc_setting;
pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
- setting->amr_nb = 1;
+ setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
setting->reorder = 0;
setting->octet_aligned = octet_align;
setting->cmr = 15;
- amr_data->decoder = Decoder_Interface_init();
+ if (idx == IDX_AMR_NB) {
+#ifdef USE_AMRNB
+ amr_data->decoder = Decoder_Interface_init();
+#endif
+ } else {
+#ifdef USE_AMRWB
+ amr_data->decoder = D_IF_init();
+#endif
+ }
if (amr_data->decoder == NULL) {
- TRACE_((THIS_FILE, "Decoder_Interface_init() failed"));
+ TRACE_((THIS_FILE, "Decoder initialization failed"));
amr_codec_close(codec);
return PJMEDIA_CODEC_EFAILED;
}
setting = &amr_data->dec_setting;
pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
- setting->amr_nb = 1;
+ setting->amr_nb = (idx == IDX_AMR_NB? 1: 0);
setting->reorder = 0;
setting->octet_aligned = octet_align;
- TRACE_((THIS_FILE, "AMR-NB codec allocated: vad=%d, plc=%d, bitrate=%d",
+ TRACE_((THIS_FILE, "AMR codec allocated: clockrate=%d vad=%d, plc=%d,"
+ " bitrate=%d", amr_data->clock_rate,
amr_data->vad_enabled, amr_data->plc_enabled,
- pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
+ amr_bitrates[idx][amr_data->enc_mode]));
return PJ_SUCCESS;
}
@@ -545,16 +689,32 @@ static pj_status_t amr_codec_close( pjmedia_codec *codec )
PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
if (amr_data->encoder) {
- Encoder_Interface_exit(amr_data->encoder);
+ if (amr_data->enc_setting.amr_nb) {
+#ifdef USE_AMRNB
+ Encoder_Interface_exit(amr_data->encoder);
+#endif
+ } else {
+#ifdef USE_AMRWB
+ E_IF_exit(amr_data->encoder);
+#endif
+ }
amr_data->encoder = NULL;
}
if (amr_data->decoder) {
- Decoder_Interface_exit(amr_data->decoder);
+ if (amr_data->dec_setting.amr_nb) {
+#ifdef USE_AMRNB
+ Decoder_Interface_exit(amr_data->decoder);
+#endif
+ } else {
+#ifdef USE_AMRWB
+ D_IF_exit(amr_data->decoder);
+#endif
+ }
amr_data->decoder = NULL;
}
- TRACE_((THIS_FILE, "AMR-NB codec closed"));
+ TRACE_((THIS_FILE, "AMR codec closed"));
return PJ_SUCCESS;
}
@@ -575,11 +735,15 @@ static pj_status_t amr_codec_modify( pjmedia_codec *codec,
amr_data->vad_enabled = (attr->setting.vad != 0);
amr_data->plc_enabled = (attr->setting.plc != 0);
- if (prev_vad_state != amr_data->vad_enabled) {
+ if (amr_data->enc_setting.amr_nb &&
+ prev_vad_state != amr_data->vad_enabled)
+ {
/* Reinit AMR encoder to update VAD setting */
TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
+#ifdef USE_AMRNB
Encoder_Interface_exit(amr_data->encoder);
amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+#endif
if (amr_data->encoder == NULL) {
TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
amr_codec_close(codec);
@@ -587,7 +751,7 @@ static pj_status_t amr_codec_modify( pjmedia_codec *codec,
}
}
- TRACE_((THIS_FILE, "AMR-NB codec modified: vad=%d, plc=%d",
+ TRACE_((THIS_FILE, "AMR codec modified: vad=%d, plc=%d",
amr_data->vad_enabled, amr_data->plc_enabled));
return PJ_SUCCESS;
}
@@ -606,6 +770,7 @@ static pj_status_t amr_codec_parse( pjmedia_codec *codec,
struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
pj_uint8_t cmr;
pj_status_t status;
+ unsigned idx = (amr_data->enc_setting.amr_nb? 0: 1);
status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting,
frames, frame_cnt, &cmr);
@@ -613,11 +778,11 @@ static pj_status_t amr_codec_parse( pjmedia_codec *codec,
return status;
/* Check for Change Mode Request. */
- if (cmr <= 7 && amr_data->enc_mode != cmr) {
+ if (cmr < amr_bitrates_size[idx] && amr_data->enc_mode != cmr) {
amr_data->enc_mode = cmr;
- TRACE_((THIS_FILE, "AMR-NB encoder switched mode to %d (%dbps)",
- amr_data->enc_mode,
- pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
+ TRACE_((THIS_FILE, "AMR encoder switched mode to %d (%dbps)",
+ amr_data->enc_mode,
+ amr_bitrates[idx][amr_data->enc_mode]));
}
return PJ_SUCCESS;
@@ -649,7 +814,7 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec,
PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
nsamples = input->size >> 1;
- samples_per_frame = 160;
+ samples_per_frame = amr_data->clock_rate * FRAME_LENGTH_MS / 1000;
PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0,
PJMEDIA_CODEC_EPCMFRMINLEN);
@@ -661,20 +826,30 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec,
speech = (pj_int16_t*)input->buf;
bitstream = (unsigned char*)output->buf;
while (nsamples >= samples_per_frame) {
- size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode,
- speech, bitstream, 0);
+ if (amr_data->enc_setting.amr_nb) {
+#ifdef USE_AMRNB
+ size = Encoder_Interface_Encode (amr_data->encoder,
+ amr_data->enc_mode,
+ speech, bitstream, 0);
+#endif
+ } else {
+#ifdef USE_AMRWB
+ size = E_IF_encode (amr_data->encoder, amr_data->enc_mode,
+ speech, bitstream, 0);
+#endif
+ }
if (size == 0) {
output->size = 0;
output->buf = NULL;
output->type = PJMEDIA_FRAME_TYPE_NONE;
- TRACE_((THIS_FILE, "AMR-NB encode() failed"));
+ TRACE_((THIS_FILE, "AMR encode() failed"));
return PJMEDIA_CODEC_EFAILED;
}
- nsamples -= 160;
+ nsamples -= samples_per_frame;
speech += samples_per_frame;
bitstream += size;
out_size += size;
- TRACE_((THIS_FILE, "AMR-NB encode(): mode=%d, size=%d",
+ TRACE_((THIS_FILE, "AMR encode(): mode=%d, size=%d",
amr_data->enc_mode, out_size));
}
@@ -690,8 +865,13 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec,
info->mode = (pj_int8_t)amr_data->enc_mode;
info->start_bit = 0;
frames[i].buf = p + 1;
- frames[i].size = (info->frame_type <= 8)?
- pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
+ if (amr_data->enc_setting.amr_nb) {
+ frames[i].size = (info->frame_type <= 8)?
+ pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
+ } else {
+ frames[i].size = (info->frame_type <= 9)?
+ pjmedia_codec_amrwb_framelen[info->frame_type] : 0;
+ }
p += frames[i].size + 1;
/* Count the number of SID and DTX frames */
@@ -713,7 +893,8 @@ static pj_status_t amr_codec_encode( pjmedia_codec *codec,
dtx_duration = pj_timestamp_diff32(&amr_data->last_tx,
&input->timestamp);
if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
- dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)
+ dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*
+ amr_data->clock_rate/1000)
{
output->size = 0;
output->type = PJMEDIA_FRAME_TYPE_NONE;
@@ -755,31 +936,43 @@ static pj_status_t amr_codec_decode( pjmedia_codec *codec,
struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
pjmedia_frame input_;
pjmedia_codec_amr_bit_info *info;
- /* VA AMR-NB decoding buffer: AMR-NB max frame size + 1 byte header. */
- unsigned char bitstream[32];
+ unsigned out_size;
+ /* AMR decoding buffer: AMR max frame size + 1 byte header. */
+ unsigned char bitstream[61];
pj_assert(amr_data != NULL);
PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
- if (output_buf_len < 320)
+ out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
+ if (output_buf_len < out_size)
return PJMEDIA_CODEC_EPCMTOOSHORT;
input_.buf = &bitstream[1];
- input_.size = 31; /* AMR-NB max frame size */
+ /* AMR max frame size */
+ input_.size = (amr_data->dec_setting.amr_nb? 31: 60);
pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
- /* VA AMRNB decoder requires frame info in the first byte. */
+ /* VA AMR decoder requires frame info in the first byte. */
bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
- TRACE_((THIS_FILE, "AMR-NB decode(): mode=%d, ft=%d, size=%d",
+ TRACE_((THIS_FILE, "AMR decode(): mode=%d, ft=%d, size=%d",
info->mode, info->frame_type, input_.size));
/* Decode */
- Decoder_Interface_Decode(amr_data->decoder, bitstream,
- (pj_int16_t*)output->buf, 0);
+ if (amr_data->dec_setting.amr_nb) {
+#ifdef USE_AMRNB
+ Decoder_Interface_Decode(amr_data->decoder, bitstream,
+ (pj_int16_t*)output->buf, 0);
+#endif
+ } else {
+#ifdef USE_AMRWB
+ D_IF_decode(amr_data->decoder, bitstream,
+ (pj_int16_t*)output->buf, 0);
+#endif
+ }
- output->size = 320;
+ output->size = out_size;
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
output->timestamp = input->timestamp;
@@ -804,16 +997,17 @@ static pj_status_t amr_codec_recover( pjmedia_codec *codec,
struct pjmedia_frame *output)
{
struct amr_data *amr_data = codec->codec_data;
+ unsigned out_size = amr_data->clock_rate * FRAME_LENGTH_MS / 1000 * 2;
TRACE_((THIS_FILE, "amr_codec_recover"));
PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
- PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
+ PJ_ASSERT_RETURN(output_buf_len >= out_size, PJMEDIA_CODEC_EPCMTOOSHORT);
pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
- output->size = 320;
+ output->size = out_size;
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
return PJ_SUCCESS;
@@ -821,11 +1015,17 @@ static pj_status_t amr_codec_recover( pjmedia_codec *codec,
#endif
#if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS
-# if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
-# pragma comment( lib, "libopencore-amrnb.a")
-# else
-# error Unsupported OpenCORE AMR library, fix here
-# endif
+# if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
+# ifdef USE_AMRNB
+# pragma comment( lib, "libopencore-amrnb.a")
+# endif
+# ifdef USE_AMRWB
+# pragma comment( lib, "libopencore-amrwb.a")
+# pragma comment( lib, "libvo-amrwbenc.a")
+# endif
+# else
+# error Unsupported OpenCORE AMR library, fix here
+# endif
#endif
#endif
diff --git a/pjmedia/src/pjmedia-codec/silk.c b/pjmedia/src/pjmedia-codec/silk.c
new file mode 100644
index 0000000..75ee7ca
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/silk.c
@@ -0,0 +1,953 @@
+/* $Id: silk.c 4339 2013-01-31 05:23:46Z ming $ */
+/*
+ * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
+ * Contributed by Regis Montoya (aka r3gis - www.r3gis.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia-codec/silk.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/delaybuf.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_SILK_CODEC) && (PJMEDIA_HAS_SILK_CODEC!=0)
+
+#include "SKP_Silk_SDK_API.h"
+
+#define THIS_FILE "silk.c"
+
+#ifndef PJMEDIA_SILK_DELAY_BUF_OPTIONS
+ #define PJMEDIA_SILK_DELAY_BUF_OPTIONS PJMEDIA_DELAY_BUF_SIMPLE_FIFO
+#endif
+
+#define FRAME_LENGTH_MS 20
+#define SILK_ENC_CTL_PACKET_LOSS_PCT 10
+#define SILK_MIN_BITRATE 5000
+#define CALC_BITRATE_QUALITY(quality, max_br) \
+ (quality * max_br / 10)
+#define CALC_BITRATE(max_br) \
+ CALC_BITRATE_QUALITY(PJMEDIA_CODEC_SILK_DEFAULT_QUALITY, \
+ max_br);
+
+
+/* Prototypes for SILK factory */
+static pj_status_t silk_test_alloc( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id );
+static pj_status_t silk_default_attr( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec_param *attr );
+static pj_status_t silk_enum_codecs ( pjmedia_codec_factory *factory,
+ unsigned *count,
+ pjmedia_codec_info codecs[]);
+static pj_status_t silk_alloc_codec( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec **p_codec);
+static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory,
+ pjmedia_codec *codec );
+
+/* Prototypes for SILK implementation. */
+static pj_status_t silk_codec_init( pjmedia_codec *codec,
+ pj_pool_t *pool );
+static pj_status_t silk_codec_open( pjmedia_codec *codec,
+ pjmedia_codec_param *attr );
+static pj_status_t silk_codec_close( pjmedia_codec *codec );
+static pj_status_t silk_codec_modify( pjmedia_codec *codec,
+ const pjmedia_codec_param *attr );
+static pj_status_t silk_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *timestamp,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
+static pj_status_t silk_codec_encode( pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+static pj_status_t silk_codec_decode( pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+static pj_status_t silk_codec_recover( pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+
+
+typedef enum
+{
+ PARAM_NB, /* Index for narrowband parameter. */
+ PARAM_MB, /* Index for medium parameter. */
+ PARAM_WB, /* Index for wideband parameter. */
+ PARAM_SWB, /* Index for super-wideband parameter */
+} silk_mode;
+
+
+/* Silk default parameter */
+typedef struct silk_param
+{
+ int enabled; /* Is this mode enabled? */
+ int pt; /* Payload type. */
+ unsigned clock_rate; /* Default sampling rate to be used. */
+ pj_uint16_t ptime; /* packet length (in ms). */
+ pj_uint32_t bitrate; /* Bit rate for current mode. */
+ pj_uint32_t max_bitrate; /* Max bit rate for current mode. */
+ int complexity; /* Complexity mode: 0/lowest to 2. */
+} silk_param;
+
+
+/* Definition for SILK codec operations. */
+static pjmedia_codec_op silk_op =
+{
+ &silk_codec_init,
+ &silk_codec_open,
+ &silk_codec_close,
+ &silk_codec_modify,
+ &silk_codec_parse,
+ &silk_codec_encode,
+ &silk_codec_decode,
+ &silk_codec_recover
+};
+
+/* Definition for SILK codec factory operations. */
+static pjmedia_codec_factory_op silk_factory_op =
+{
+ &silk_test_alloc,
+ &silk_default_attr,
+ &silk_enum_codecs,
+ &silk_alloc_codec,
+ &silk_dealloc_codec,
+ &pjmedia_codec_silk_deinit
+};
+
+
+/* SILK factory private data */
+static struct silk_factory
+{
+ pjmedia_codec_factory base;
+ pjmedia_endpt *endpt;
+ pj_pool_t *pool;
+ pj_mutex_t *mutex;
+ struct silk_param silk_param[4];
+} silk_factory;
+
+
+/* SILK codec private data. */
+typedef struct silk_private
+{
+ silk_mode mode; /**< Silk mode. */
+ pj_pool_t *pool; /**< Pool for each instance. */
+ unsigned samples_per_frame;
+ pj_uint8_t pcm_bytes_per_sample;
+
+ pj_bool_t enc_ready;
+ SKP_SILK_SDK_EncControlStruct enc_ctl;
+ void *enc_st;
+
+ pj_bool_t dec_ready;
+ SKP_SILK_SDK_DecControlStruct dec_ctl;
+ void *dec_st;
+
+ /* Buffer to hold decoded frames. */
+ void *dec_buf[SILK_MAX_FRAMES_PER_PACKET-1];
+ SKP_int16 dec_buf_size[SILK_MAX_FRAMES_PER_PACKET-1];
+ pj_size_t dec_buf_sz;
+ unsigned dec_buf_cnt;
+ pj_uint32_t pkt_info; /**< Packet info for buffered frames. */
+} silk_private;
+
+
+silk_mode silk_get_mode_from_clock_rate(unsigned clock_rate) {
+ if (clock_rate <= silk_factory.silk_param[PARAM_NB].clock_rate) {
+ return PARAM_NB;
+ } else if (clock_rate <= silk_factory.silk_param[PARAM_MB].clock_rate) {
+ return PARAM_MB;
+ } else if (clock_rate <= silk_factory.silk_param[PARAM_WB].clock_rate) {
+ return PARAM_WB;
+ }
+ return PARAM_SWB;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_silk_init(pjmedia_endpt *endpt)
+{
+ pjmedia_codec_mgr *codec_mgr;
+ silk_param *sp;
+ pj_status_t status;
+
+ if (silk_factory.endpt != NULL) {
+ /* Already initialized. */
+ return PJ_SUCCESS;
+ }
+
+ /* Init factory */
+ pj_bzero(&silk_factory, sizeof(silk_factory));
+ silk_factory.base.op = &silk_factory_op;
+ silk_factory.base.factory_data = NULL;
+ silk_factory.endpt = endpt;
+
+ /* Create pool */
+ silk_factory.pool = pjmedia_endpt_create_pool(endpt, "silk", 4000, 4000);
+ if (!silk_factory.pool)
+ return PJ_ENOMEM;
+
+ /* Create mutex. */
+ status = pj_mutex_create_simple(silk_factory.pool, "silk",
+ &silk_factory.mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Initialize default codec params */
+
+ /* From SILK docs:
+ - SILK bitrate tables:
+ +----------------+---------+-----------+
+ | | fs (Hz) | BR (kbps) |
+ +----------------+---------+-----------+
+ | Narrowband | 8000 | 6 - 20 |
+ | Mediumband | 12000 | 7 - 25 |
+ | Wideband | 16000 | 8 - 30 |
+ | Super Wideband | 24000 | 12 - 40 |
+ +----------------+---------+-----------+
+ - The upper limits of the bit rate ranges in this table are
+ recommended values.
+ */
+
+ sp = &silk_factory.silk_param[PARAM_NB];
+ sp->pt = PJMEDIA_RTP_PT_SILK_NB;
+ sp->clock_rate = 8000;
+ sp->max_bitrate = 22000;
+ sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+ sp->ptime = FRAME_LENGTH_MS;
+ sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+ sp->enabled = 1;
+
+ sp = &silk_factory.silk_param[PARAM_MB];
+ sp->pt = PJMEDIA_RTP_PT_SILK_MB;
+ sp->clock_rate = 12000;
+ sp->max_bitrate = 28000;
+ sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+ sp->ptime = FRAME_LENGTH_MS;
+ sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+ sp->enabled = 0;
+
+ sp = &silk_factory.silk_param[PARAM_WB];
+ sp->pt = PJMEDIA_RTP_PT_SILK_WB;
+ sp->clock_rate = 16000;
+ sp->max_bitrate = 36000;
+ sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+ sp->ptime = FRAME_LENGTH_MS;
+ sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+ sp->enabled = 1;
+
+ sp = &silk_factory.silk_param[PARAM_SWB];
+ sp->pt = PJMEDIA_RTP_PT_SILK_SWB;
+ sp->clock_rate = 24000;
+ sp->max_bitrate = 46000;
+ sp->bitrate = CALC_BITRATE(sp->max_bitrate);
+ sp->ptime = FRAME_LENGTH_MS;
+ sp->complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+ sp->enabled = 0;
+
+
+ /* Get the codec manager. */
+ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+ if (!codec_mgr) {
+ return PJ_EINVALIDOP;
+ }
+
+ /* Register codec factory to endpoint. */
+ status = pjmedia_codec_mgr_register_factory(codec_mgr,
+ &silk_factory.base);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ PJ_LOG(4,(THIS_FILE, "SILK codec version %s initialized",
+ SKP_Silk_SDK_get_version()));
+ return PJ_SUCCESS;
+
+on_error:
+ if (silk_factory.mutex) {
+ pj_mutex_destroy(silk_factory.mutex);
+ silk_factory.mutex = NULL;
+ }
+ if (silk_factory.pool) {
+ pj_pool_release(silk_factory.pool);
+ silk_factory.pool = NULL;
+ }
+
+ return status;
+}
+
+
+/*
+ * Change the configuration setting of the SILK codec for the specified
+ * clock rate.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_silk_set_config(
+ unsigned clock_rate,
+ const pjmedia_codec_silk_setting *opt)
+{
+ unsigned i;
+
+ /* Look up in factory modes table */
+ for (i = 0; i < sizeof(silk_factory.silk_param)/
+ sizeof(silk_factory.silk_param[0]); ++i)
+ {
+ if (silk_factory.silk_param[i].clock_rate == clock_rate) {
+ int quality = PJMEDIA_CODEC_SILK_DEFAULT_QUALITY;
+ int complexity = PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY;
+
+ silk_factory.silk_param[i].enabled = opt->enabled;
+ if (opt->complexity >= 0)
+ complexity = opt->complexity;
+ silk_factory.silk_param[i].complexity = complexity;
+ if (opt->quality >= 0)
+ quality = opt->quality;
+ silk_factory.silk_param[i].bitrate =
+ CALC_BITRATE_QUALITY(quality,
+ silk_factory.silk_param[i].max_bitrate);
+ if (silk_factory.silk_param[i].bitrate < SILK_MIN_BITRATE)
+ silk_factory.silk_param[i].bitrate = SILK_MIN_BITRATE;
+
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+
+/*
+ * Unregister SILK codec factory from pjmedia endpoint and deinitialize
+ * the SILK codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_silk_deinit(void)
+{
+ pjmedia_codec_mgr *codec_mgr;
+ pj_status_t status;
+
+ if (silk_factory.endpt == NULL) {
+ /* Not registered. */
+ return PJ_SUCCESS;
+ }
+
+ /* Lock mutex. */
+ pj_mutex_lock(silk_factory.mutex);
+
+ /* Get the codec manager. */
+ codec_mgr = pjmedia_endpt_get_codec_mgr(silk_factory.endpt);
+ if (!codec_mgr) {
+ silk_factory.endpt = NULL;
+ pj_mutex_unlock(silk_factory.mutex);
+ return PJ_EINVALIDOP;
+ }
+
+ /* Unregister silk codec factory. */
+ status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+ &silk_factory.base);
+ silk_factory.endpt = NULL;
+
+ /* Destroy mutex. */
+ pj_mutex_destroy(silk_factory.mutex);
+ silk_factory.mutex = NULL;
+
+
+ /* Release pool. */
+ pj_pool_release(silk_factory.pool);
+ silk_factory.pool = NULL;
+
+ return status;
+}
+
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t silk_test_alloc(pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *info )
+{
+ const pj_str_t silk_tag = {"SILK", 4};
+ unsigned i;
+
+ PJ_UNUSED_ARG(factory);
+ PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+
+ /* Type MUST be audio. */
+ if (info->type != PJMEDIA_TYPE_AUDIO)
+ return PJMEDIA_CODEC_EUNSUP;
+
+ /* Check encoding name. */
+ if (pj_stricmp(&info->encoding_name, &silk_tag) != 0)
+ return PJMEDIA_CODEC_EUNSUP;
+
+ /* Channel count must be one */
+ if (info->channel_cnt != 1)
+ return PJMEDIA_CODEC_EUNSUP;
+
+ /* Check clock-rate */
+ for (i=0; i<PJ_ARRAY_SIZE(silk_factory.silk_param); ++i) {
+ silk_param *sp = &silk_factory.silk_param[i];
+ if (sp->enabled && info->clock_rate == sp->clock_rate)
+ {
+ return PJ_SUCCESS;
+ }
+ }
+ /* Clock rate not supported */
+ return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t silk_default_attr( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec_param *attr )
+{
+ silk_param *sp;
+ int i;
+
+ PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+
+ i = silk_get_mode_from_clock_rate(id->clock_rate);
+ pj_assert(i >= PARAM_NB && i <= PARAM_SWB);
+
+ sp = &silk_factory.silk_param[i];
+
+ pj_bzero(attr, sizeof(pjmedia_codec_param));
+ attr->info.channel_cnt = 1;
+ attr->info.clock_rate = sp->clock_rate;
+ attr->info.avg_bps = sp->bitrate;
+ attr->info.max_bps = sp->max_bitrate;
+ attr->info.frm_ptime = sp->ptime;
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.pt = (pj_uint8_t) sp->pt;
+ attr->setting.frm_per_pkt = 1;
+ attr->setting.vad = 0; /* DTX is not recommended for quality reason */
+ attr->setting.plc = 1;
+
+ i = 0;
+ attr->setting.dec_fmtp.param[i].name = pj_str("useinbandfec");
+ attr->setting.dec_fmtp.param[i++].val = pj_str("0");
+ /*
+ attr->setting.dec_fmtp.param[i].name = pj_str("maxaveragebitrate");
+ attr->setting.dec_fmtp.param[i++].val = pj_str(mode->bitrate_str);
+ */
+ attr->setting.dec_fmtp.cnt = (pj_uint8_t)i;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t silk_enum_codecs(pjmedia_codec_factory *factory,
+ unsigned *count,
+ pjmedia_codec_info codecs[])
+{
+ unsigned max;
+ int i;
+
+ PJ_ASSERT_RETURN(factory==&silk_factory.base, PJ_EINVAL);
+ PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+ max = *count;
+ *count = 0;
+
+ for (i = 0; i<PJ_ARRAY_SIZE(silk_factory.silk_param) && *count<max; ++i)
+ {
+ silk_param *sp = &silk_factory.silk_param[i];
+
+ if (!sp->enabled)
+ continue;
+
+ pj_bzero(&codecs[*count], sizeof(pjmedia_codec_info));
+ codecs[*count].encoding_name = pj_str("SILK");
+ codecs[*count].pt = sp->pt;
+ codecs[*count].type = PJMEDIA_TYPE_AUDIO;
+ codecs[*count].clock_rate = sp->clock_rate;
+ codecs[*count].channel_cnt = 1;
+
+ ++*count;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new SILK codec instance.
+ */
+static pj_status_t silk_alloc_codec(pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec **p_codec)
+{
+ pj_pool_t *pool;
+ pjmedia_codec *codec;
+ silk_private *silk;
+
+ PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL);
+
+ /* Create pool for codec instance */
+ pool = pjmedia_endpt_create_pool(silk_factory.endpt, "silk", 512, 512);
+
+ /* Allocate codec */
+ codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+ codec->op = &silk_op;
+ codec->factory = factory;
+ codec->codec_data = PJ_POOL_ZALLOC_T(pool, silk_private);
+ silk = (silk_private*) codec->codec_data;
+ silk->pool = pool;
+ silk->enc_ready = PJ_FALSE;
+ silk->dec_ready = PJ_FALSE;
+ silk->mode = silk_get_mode_from_clock_rate(id->clock_rate);
+
+ *p_codec = codec;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t silk_dealloc_codec( pjmedia_codec_factory *factory,
+ pjmedia_codec *codec )
+{
+ silk_private *silk;
+
+ PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL);
+
+ silk = (silk_private*)codec->codec_data;
+
+ /* Close codec, if it's not closed. */
+ if (silk->enc_ready == PJ_TRUE || silk->dec_ready == PJ_TRUE) {
+ silk_codec_close(codec);
+ }
+
+ pj_pool_release(silk->pool);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Init codec.
+ */
+static pj_status_t silk_codec_init(pjmedia_codec *codec,
+ pj_pool_t *pool )
+{
+ PJ_UNUSED_ARG(codec);
+ PJ_UNUSED_ARG(pool);
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Open codec.
+ */
+static pj_status_t silk_codec_open(pjmedia_codec *codec,
+ pjmedia_codec_param *attr )
+{
+
+ silk_private *silk;
+ silk_param *sp;
+ SKP_int st_size, err;
+ pj_bool_t enc_use_fec;
+ unsigned enc_bitrate, i;
+
+ PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+
+ silk = (silk_private*)codec->codec_data;
+ sp = &silk_factory.silk_param[silk->mode];
+
+ /* Already opened? */
+ if (silk->enc_ready || silk->dec_ready)
+ return PJ_SUCCESS;
+
+ /* Allocate and initialize encoder */
+ err = SKP_Silk_SDK_Get_Encoder_Size(&st_size);
+ if (err) {
+ PJ_LOG(3,(THIS_FILE, "Failed to get encoder state size (err=%d)",
+ err));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ silk->enc_st = pj_pool_zalloc(silk->pool, st_size);
+ err = SKP_Silk_SDK_InitEncoder(silk->enc_st, &silk->enc_ctl);
+ if (err) {
+ PJ_LOG(3,(THIS_FILE, "Failed to init encoder (err=%d)", err));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+
+ /* Check fmtp params */
+ enc_use_fec = PJ_TRUE;
+ enc_bitrate = sp->bitrate;
+ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+ pjmedia_codec_fmtp *fmtp = &attr->setting.enc_fmtp;
+ const pj_str_t STR_USEINBANDFEC = {"useinbandfec", 12};
+ const pj_str_t STR_MAXAVERAGEBITRATE = {"maxaveragebitrate", 17};
+
+ if (!pj_stricmp(&fmtp->param[i].name, &STR_USEINBANDFEC)) {
+ enc_use_fec = pj_strtoul(&fmtp->param[i].val) != 0;
+ } else if (!pj_stricmp(&fmtp->param[i].name, &STR_MAXAVERAGEBITRATE)) {
+ enc_bitrate = pj_strtoul(&fmtp->param[i].val);
+ if (enc_bitrate > sp->max_bitrate) {
+ enc_bitrate = sp->max_bitrate;
+ }
+ }
+ }
+
+ /* Setup encoder control for encoding process */
+ silk->enc_ready = PJ_TRUE;
+ silk->samples_per_frame = FRAME_LENGTH_MS *
+ attr->info.clock_rate / 1000;
+ silk->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8;
+
+ silk->enc_ctl.API_sampleRate = attr->info.clock_rate;
+ silk->enc_ctl.maxInternalSampleRate = attr->info.clock_rate;
+ silk->enc_ctl.packetSize = attr->setting.frm_per_pkt *
+ silk->samples_per_frame;
+ /* For useInBandFEC setting to be useful, we need to set
+ * packetLossPercentage greater than LBRR_LOSS_THRES (1)
+ */
+ silk->enc_ctl.packetLossPercentage = SILK_ENC_CTL_PACKET_LOSS_PCT;
+ silk->enc_ctl.useInBandFEC = enc_use_fec;
+ silk->enc_ctl.useDTX = attr->setting.vad;
+ silk->enc_ctl.complexity = sp->complexity;
+ silk->enc_ctl.bitRate = enc_bitrate;
+
+
+ /* Allocate and initialize decoder */
+ err = SKP_Silk_SDK_Get_Decoder_Size(&st_size);
+ if (err) {
+ PJ_LOG(3,(THIS_FILE, "Failed to get decoder state size (err=%d)",
+ err));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ silk->dec_st = pj_pool_zalloc(silk->pool, st_size);
+ err = SKP_Silk_SDK_InitDecoder(silk->dec_st);
+ if (err) {
+ PJ_LOG(3,(THIS_FILE, "Failed to init decoder (err=%d)", err));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+
+ /* Setup decoder control for decoding process */
+ silk->dec_ctl.API_sampleRate = attr->info.clock_rate;
+ silk->dec_ctl.framesPerPacket = 1; /* for proper PLC at start */
+ silk->dec_ready = PJ_TRUE;
+ silk->dec_buf_sz = attr->info.clock_rate * attr->info.channel_cnt *
+ attr->info.frm_ptime / 1000 *
+ silk->pcm_bytes_per_sample;
+
+ /* Inform the stream to prepare a larger buffer since we cannot parse
+ * SILK packets and split it into individual frames.
+ */
+ attr->info.max_rx_frame_size = attr->info.max_bps *
+ attr->info.frm_ptime / 8 / 1000;
+ if ((attr->info.max_bps * attr->info.frm_ptime) % 8000 != 0)
+ {
+ ++attr->info.max_rx_frame_size;
+ }
+ attr->info.max_rx_frame_size *= SILK_MAX_FRAMES_PER_PACKET;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t silk_codec_close( pjmedia_codec *codec )
+{
+ silk_private *silk;
+ silk = (silk_private*)codec->codec_data;
+
+ silk->enc_ready = PJ_FALSE;
+ silk->dec_ready = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t silk_codec_modify(pjmedia_codec *codec,
+ const pjmedia_codec_param *attr )
+{
+ PJ_TODO(implement_silk_codec_modify);
+
+ PJ_UNUSED_ARG(codec);
+ PJ_UNUSED_ARG(attr);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Encode frame.
+ */
+static pj_status_t silk_codec_encode(pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ silk_private *silk;
+ SKP_int err;
+ unsigned nsamples;
+ SKP_int16 out_size;
+
+ PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+ silk = (silk_private*)codec->codec_data;
+
+ /* Check frame in size */
+ nsamples = input->size >> 1;
+ PJ_ASSERT_RETURN(nsamples % silk->samples_per_frame == 0,
+ PJMEDIA_CODEC_EPCMFRMINLEN);
+
+ /* Encode */
+ output->size = 0;
+ out_size = (SKP_int16)output_buf_len;
+ err = SKP_Silk_SDK_Encode(silk->enc_st, &silk->enc_ctl,
+ (SKP_int16*)input->buf, nsamples,
+ (SKP_uint8*)output->buf, &out_size);
+ if (err) {
+ PJ_LOG(3, (THIS_FILE, "Failed to encode frame (err=%d)", err));
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ if (err == SKP_SILK_ENC_PAYLOAD_BUF_TOO_SHORT)
+ return PJMEDIA_CODEC_EFRMTOOSHORT;
+ return PJMEDIA_CODEC_EFAILED;
+ }
+
+ output->size = out_size;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ output->timestamp = input->timestamp;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t silk_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
+{
+ silk_private *silk;
+ SKP_Silk_TOC_struct toc;
+ unsigned i, count;
+
+ PJ_ASSERT_RETURN(codec && ts && frames && frame_cnt, PJ_EINVAL);
+ silk = (silk_private*)codec->codec_data;
+
+ SKP_Silk_SDK_get_TOC(pkt, pkt_size, &toc);
+ count = toc.framesInPacket;
+ pj_assert(count <= SILK_MAX_FRAMES_PER_PACKET);
+
+ for (i = 0; i < count; i++) {
+ frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) |
+ (((unsigned)pkt & 0xFF) << 8) |
+ (toc.framesInPacket << 4) | i;
+ frames[i].buf = pkt;
+ frames[i].size = pkt_size;
+ frames[i].timestamp.u64 = ts->u64 + i * silk->samples_per_frame;
+ }
+
+ *frame_cnt = count;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t silk_codec_decode(pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ silk_private *silk;
+ SKP_int16 out_size;
+ SKP_Silk_TOC_struct toc;
+ SKP_int err = 0;
+ unsigned pkt_info, frm_info;
+
+ PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL);
+ silk = (silk_private*)codec->codec_data;
+ PJ_ASSERT_RETURN(output_buf_len >= silk->dec_buf_sz, PJ_ETOOSMALL);
+
+ SKP_Silk_SDK_get_TOC(input->buf, input->size, &toc);
+ pkt_info = input->bit_info & 0xFFFFFF00;
+ frm_info = input->bit_info & 0xF;
+
+ if (toc.framesInPacket == 0) {
+ /* In SILK ARM version, the table of content can indicate
+ * that the number of frames in the packet is 0.
+ * Try to get the number of frames in packet that we save
+ * in the frame instead.
+ */
+ toc.framesInPacket = (input->bit_info & 0xF0) >> 4;
+ }
+
+ if (toc.framesInPacket == 0) {
+ output->size = 0;
+ } else if (silk->pkt_info != pkt_info || input->bit_info == 0) {
+ unsigned i;
+ SKP_int16 nsamples;
+
+ silk->pkt_info = pkt_info;
+ nsamples = (SKP_int16)silk->dec_buf_sz / silk->pcm_bytes_per_sample;
+
+ if (toc.framesInPacket-1 > (SKP_int)silk->dec_buf_cnt) {
+ /* Grow the buffer */
+ for (i = silk->dec_buf_cnt+1; i < (unsigned)toc.framesInPacket;
+ i++)
+ {
+ silk->dec_buf[i-1] = pj_pool_alloc(silk->pool,
+ silk->dec_buf_sz);
+ }
+ silk->dec_buf_cnt = toc.framesInPacket-1;
+ }
+
+ /* We need to decode all the frames in the packet. */
+ for (i = 0; i < (unsigned)toc.framesInPacket;) {
+ void *buf;
+ SKP_int16 *size;
+
+ if (i == 0 || i == frm_info) {
+ buf = output->buf;
+ size = &out_size;
+ } else {
+ buf = silk->dec_buf[i-1];
+ size = &silk->dec_buf_size[i-1];
+ }
+
+ *size = nsamples;
+ err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl,
+ 0, /* Normal frame flag */
+ input->buf, input->size,
+ buf, size);
+ if (err) {
+ PJ_LOG(3, (THIS_FILE, "Failed to decode frame (err=%d)",
+ err));
+ *size = 0;
+ } else {
+ *size = *size * silk->pcm_bytes_per_sample;
+ }
+
+ if (i == frm_info) {
+ output->size = *size;
+ }
+
+ i++;
+ if (!silk->dec_ctl.moreInternalDecoderFrames &&
+ i < (unsigned)toc.framesInPacket)
+ {
+ /* It turns out that the packet does not have
+ * the number of frames as mentioned in the TOC.
+ */
+ for (; i < (unsigned)toc.framesInPacket; i++) {
+ silk->dec_buf_size[i-1] = 0;
+ if (i == frm_info) {
+ output->size = 0;
+ }
+ }
+ }
+ }
+ } else {
+ /* We have already decoded this packet. */
+ if (frm_info == 0 || silk->dec_buf_size[frm_info-1] == 0) {
+ /* The decoding was a failure. */
+ output->size = 0;
+ } else {
+ /* Copy the decoded frame from the buffer. */
+ pj_assert(frm_info-1 < silk->dec_buf_cnt);
+ if (frm_info-1 >= silk->dec_buf_cnt) {
+ output->size = 0;
+ } else {
+ pj_memcpy(output->buf, silk->dec_buf[frm_info-1],
+ silk->dec_buf_size[frm_info-1]);
+ output->size = silk->dec_buf_size[frm_info-1];
+ }
+ }
+ }
+
+ if (output->size == 0) {
+ output->type = PJMEDIA_TYPE_NONE;
+ output->buf = NULL;
+ return PJMEDIA_CODEC_EFAILED;
+ }
+
+ output->type = PJMEDIA_TYPE_AUDIO;
+ output->timestamp = input->timestamp;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+static pj_status_t silk_codec_recover(pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ silk_private *silk;
+ SKP_int16 out_size;
+ SKP_int err;
+
+ PJ_ASSERT_RETURN(codec && output_buf_len && output, PJ_EINVAL);
+ silk = (silk_private*)codec->codec_data;
+
+ out_size = (SKP_int16)output_buf_len / silk->pcm_bytes_per_sample;
+ err = SKP_Silk_SDK_Decode(silk->dec_st, &silk->dec_ctl,
+ 1, /* Lost frame flag */
+ NULL,
+ 0,
+ output->buf,
+ &out_size);
+ if (err) {
+ PJ_LOG(3, (THIS_FILE, "Failed to conceal lost frame (err=%d)", err));
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ output->buf = NULL;
+ output->size = 0;
+ return PJMEDIA_CODEC_EFAILED;
+ }
+
+ output->size = out_size * silk->pcm_bytes_per_sample;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ return PJ_SUCCESS;
+}
+
+#if defined(_MSC_VER)
+# if PJ_DEBUG
+# pragma comment(lib, "SKP_Silk_FLP_Win32_debug.lib")
+# else
+# pragma comment(lib, "SKP_Silk_FLP_Win32_mt.lib")
+# endif
+#endif
+
+
+#endif /* PJMEDIA_HAS_SILK_CODEC */
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index 6fb7c0b..f05d94a 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -1,4 +1,4 @@
-/* $Id: dshow_dev.c 3953 2012-02-16 08:49:33Z ming $ */
+/* $Id: dshow_dev.c 4253 2012-09-13 08:35:24Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -33,6 +33,7 @@
#include <windows.h>
#define COBJMACROS
#include <DShow.h>
+#include <wmsdkidl.h>
#ifdef _MSC_VER
# pragma warning(pop)
@@ -64,14 +65,16 @@ typedef struct dshow_fmt_info
{
pjmedia_format_id pjmedia_format;
const GUID *dshow_format;
+ pj_bool_t enabled;
} dshow_fmt_info;
static dshow_fmt_info dshow_fmts[] =
{
- {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
- {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
- {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
- {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV} ,
+ {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2, PJ_FALSE} ,
+ {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24, PJ_FALSE} ,
+ {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32, PJ_FALSE} ,
+ {PJMEDIA_FORMAT_IYUV, &MEDIASUBTYPE_IYUV, PJ_FALSE} ,
+ {PJMEDIA_FORMAT_I420, &WMMEDIASUBTYPE_I420, PJ_FALSE}
};
/* dshow_ device info */
@@ -329,6 +332,8 @@ static void enum_dev_cap(IBaseFilter *filter,
&rpcstatus2) == 0 &&
rpcstatus2 == RPC_S_OK)
{
+ if (!dshow_fmt)
+ dshow_fmts[j].enabled = PJ_TRUE;
if (sup_fmt)
sup_fmt[j] = PJ_TRUE;
if (pSrcpin) {
@@ -640,7 +645,7 @@ static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
unsigned i;
for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
- if (dshow_fmts[i].pjmedia_format == id)
+ if (dshow_fmts[i].pjmedia_format == id && dshow_fmts[i].enabled)
return &dshow_fmts[i];
}
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
index 7e40da1..9c0944b 100644
--- a/pjmedia/src/pjmedia-videodev/sdl_dev.c
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -1,4 +1,4 @@
-/* $Id: sdl_dev.c 4157 2012-06-06 09:37:25Z nanang $ */
+/* $Id: sdl_dev.c 4414 2013-03-05 08:21:02Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -1418,7 +1418,9 @@ static pj_status_t job_queue_destroy(job_queue *jq)
}
#ifdef _MSC_VER
-# if SDL_VERSION_ATLEAST(2,0,0)
+# if defined(PJMEDIA_SDL_LIB)
+# pragma comment( lib, PJMEDIA_SDL_LIB)
+# elif SDL_VERSION_ATLEAST(2,0,0)
# pragma comment( lib, "sdl2.lib")
# elif SDL_VERSION_ATLEAST(1,3,0)
# pragma comment( lib, "sdl.lib")
diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
index 880d47b..5b9c5e0 100644
--- a/pjmedia/src/pjmedia-videodev/v4l2_dev.c
+++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
@@ -1,4 +1,4 @@
-/* $Id: v4l2_dev.c 3901 2011-12-07 10:43:28Z nanang $ */
+/* $Id: v4l2_dev.c 4310 2012-12-19 05:38:28Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -670,6 +670,7 @@ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
struct v4l2_buffer buf;
pj_time_val time;
pj_status_t status = PJ_SUCCESS;
+ unsigned tmp_idx;
pj_bzero(&buf, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -697,9 +698,11 @@ static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused);
on_return:
+ tmp_idx = buf.index;
pj_bzero(&buf, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = tmp_idx;
xioctl(stream->fd, VIDIOC_QBUF, &buf);
return status;
diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c
index db8cad2..5107c8c 100644
--- a/pjmedia/src/pjmedia/codec.c
+++ b/pjmedia/src/pjmedia/codec.c
@@ -1,4 +1,4 @@
-/* $Id: codec.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: codec.c 4254 2012-09-14 04:06:29Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -41,6 +41,39 @@ static void sort_codecs(pjmedia_codec_mgr *mgr);
/*
+ * Duplicate codec parameter.
+ */
+PJ_DEF(pjmedia_codec_param*) pjmedia_codec_param_clone(
+ pj_pool_t *pool,
+ const pjmedia_codec_param *src)
+{
+ pjmedia_codec_param *p;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && src, NULL);
+
+ p = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param);
+
+ /* Update codec param */
+ pj_memcpy(p, src, sizeof(pjmedia_codec_param));
+ for (i = 0; i < src->setting.dec_fmtp.cnt; ++i) {
+ pj_strdup(pool, &p->setting.dec_fmtp.param[i].name,
+ &src->setting.dec_fmtp.param[i].name);
+ pj_strdup(pool, &p->setting.dec_fmtp.param[i].val,
+ &src->setting.dec_fmtp.param[i].val);
+ }
+ for (i = 0; i < src->setting.enc_fmtp.cnt; ++i) {
+ pj_strdup(pool, &p->setting.enc_fmtp.param[i].name,
+ &src->setting.enc_fmtp.param[i].name);
+ pj_strdup(pool, &p->setting.enc_fmtp.param[i].val,
+ &src->setting.enc_fmtp.param[i].val);
+ }
+
+ return p;
+}
+
+
+/*
* Initialize codec manager.
*/
PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr,
@@ -600,22 +633,11 @@ PJ_DEF(pj_status_t) pjmedia_codec_mgr_set_default_param(
codec_desc->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_default_param);
p = codec_desc->param;
p->pool = pool;
- p->param = PJ_POOL_ZALLOC_T(pool, pjmedia_codec_param);
/* Update codec param */
- pj_memcpy(p->param, param, sizeof(pjmedia_codec_param));
- for (i = 0; i < param->setting.dec_fmtp.cnt; ++i) {
- pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].name,
- &param->setting.dec_fmtp.param[i].name);
- pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].val,
- &param->setting.dec_fmtp.param[i].val);
- }
- for (i = 0; i < param->setting.enc_fmtp.cnt; ++i) {
- pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].name,
- &param->setting.enc_fmtp.param[i].name);
- pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].val,
- &param->setting.enc_fmtp.param[i].val);
- }
+ p->param = pjmedia_codec_param_clone(pool, param);
+ if (!p->param)
+ return PJ_EINVAL;
pj_mutex_unlock(mgr->mutex);
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index a08e7b0..391e4e3 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -1,4 +1,4 @@
-/* $Id: conference.c 4162 2012-06-11 04:17:54Z nanang $ */
+/* $Id: conference.c 4198 2012-07-05 10:25:46Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -1810,11 +1810,11 @@ static pj_status_t get_frame(pjmedia_port *this_port,
/* Var "ci" is to count how many ports have been visited so far. */
++ci;
- /* Reset buffer (only necessary if more than one transmitter) and
+ /* Reset buffer (only necessary if the port has transmitter) and
* reset auto adjustment level for mixed signal.
*/
conf_port->mix_adj = NORMAL_LEVEL;
- if (conf_port->transmitter_cnt > 1) {
+ if (conf_port->transmitter_cnt) {
pj_bzero(conf_port->mix_buf,
conf->samples_per_frame*sizeof(conf_port->mix_buf[0]));
}
diff --git a/pjmedia/src/pjmedia/converter.c b/pjmedia/src/pjmedia/converter.c
index 50fbcc8..19a8a8c 100644
--- a/pjmedia/src/pjmedia/converter.c
+++ b/pjmedia/src/pjmedia/converter.c
@@ -1,4 +1,4 @@
-/* $Id: converter.c 4087 2012-04-26 03:39:24Z ming $ */
+/* $Id: converter.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2010-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -39,7 +39,9 @@ PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool,
pjmedia_converter_mgr **p_mgr)
{
pjmedia_converter_mgr *mgr;
+#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
pj_status_t status = PJ_SUCCESS;
+#endif
mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr);
pj_list_init(&mgr->factory_list);
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index e20a5a4..b61152e 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -1,4 +1,4 @@
-/* $Id: endpoint.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: endpoint.c 4240 2012-08-31 09:03:36Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -49,6 +49,10 @@ static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
pj_bool_t pjmedia_add_rtpmap_for_static_pt =
PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT;
+/* Config to control use of RFC3890 TIAS */
+pj_bool_t pjmedia_add_bandwidth_tias_in_sdp =
+ PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP;
+
/* Worker thread proc. */
@@ -551,7 +555,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
/* Put bandwidth info in media level using bandwidth modifier "TIAS"
* (RFC3890).
*/
- if (max_bitrate) {
+ if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) {
const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
pjmedia_sdp_bandw *b;
@@ -715,7 +719,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
/* Put bandwidth info in media level using bandwidth modifier "TIAS"
* (RFC3890).
*/
- if (max_bitrate) {
+ if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) {
const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
pjmedia_sdp_bandw *b;
diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c
index 0a61a47..2e3ec7b 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -1,4 +1,4 @@
-/* $Id: g711.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: g711.c 4266 2012-09-26 05:55:18Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -331,7 +331,8 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory,
#if !PLC_DISABLED
/* Create PLC, always with 10ms ptime */
- status = pjmedia_plc_create(g711_factory.pool, 8000, 80,
+ status = pjmedia_plc_create(g711_factory.pool, 8000,
+ SAMPLES_PER_FRAME,
0, &codec_priv->plc);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(g711_factory.mutex);
@@ -341,7 +342,7 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory,
/* Create VAD */
status = pjmedia_silence_det_create(g711_factory.pool,
- 8000, 80,
+ 8000, SAMPLES_PER_FRAME,
&codec_priv->vad);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(g711_factory.mutex);
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 74f6469..8c54074 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -1,4 +1,4 @@
-/* $Id: jbuf.c 4100 2012-04-26 16:57:47Z nanang $ */
+/* $Id: jbuf.c 4369 2013-02-21 21:55:54Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -167,7 +167,7 @@ struct pjmedia_jbuf
/* Enabling this would log the jitter buffer state about once per
* second.
*/
-#if 1
+#if 0
# define TRACE__(args) PJ_LOG(5,args)
#else
# define TRACE__(args)
@@ -599,6 +599,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb,
jb->jb_min_prefetch = jb->jb_max_prefetch =
jb->jb_prefetch = jb->jb_init_prefetch = prefetch;
+ pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_NONE);
return PJ_SUCCESS;
}
@@ -724,6 +725,8 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
jb->jb_prefetch = jb->jb_eff_level;
if (jb->jb_prefetch < jb->jb_min_prefetch)
jb->jb_prefetch = jb->jb_min_prefetch;
+ if (jb->jb_prefetch > jb->jb_max_prefetch)
+ jb->jb_prefetch = jb->jb_max_prefetch;
}
/* Reset history */
@@ -747,6 +750,8 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
jb->jb_prefetch = jb->jb_eff_level;
if (jb->jb_prefetch > jb->jb_max_prefetch)
jb->jb_prefetch = jb->jb_max_prefetch;
+ if (jb->jb_prefetch < jb->jb_min_prefetch)
+ jb->jb_prefetch = jb->jb_min_prefetch;
}
jb->jb_stable_hist = 0;
diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index f57797c..fbfae13 100644
--- a/pjmedia/src/pjmedia/rtcp.c
+++ b/pjmedia/src/pjmedia/rtcp.c
@@ -1,4 +1,4 @@
-/* $Id: rtcp.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: rtcp.c 4283 2012-10-12 06:19:32Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -752,7 +752,8 @@ static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
/* Check and get BYE reason */
if (size > 8) {
- reason.slen = *((pj_uint8_t*)pkt+8);
+ reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_),
+ *((pj_uint8_t*)pkt+8));
pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
reason.slen);
reason.ptr = sess->stat.peer_sdes_buf_;
diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c
index 7fe6f89..c9ea53c 100644
--- a/pjmedia/src/pjmedia/rtp.c
+++ b/pjmedia/src/pjmedia/rtp.c
@@ -1,4 +1,4 @@
-/* $Id: rtp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: rtp.c 4235 2012-08-24 03:15:42Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -118,8 +118,6 @@ PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses,
int payload_len, int ts_len,
const void **rtphdr, int *hdrlen )
{
- PJ_UNUSED_ARG(payload_len);
-
/* Update timestamp */
ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c
index e54bfcf..c0d43d8 100644
--- a/pjmedia/src/pjmedia/sdp.c
+++ b/pjmedia/src/pjmedia/sdp.c
@@ -1,4 +1,4 @@
-/* $Id: sdp.c 3945 2012-01-27 09:12:59Z nanang $ */
+/* $Id: sdp.c 4367 2013-02-21 20:49:19Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -1423,6 +1423,14 @@ static pj_status_t validate_sdp_conn(const pjmedia_sdp_conn *c)
/* Validate SDP session descriptor. */
PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
{
+ return pjmedia_sdp_validate2(sdp, PJ_TRUE);
+}
+
+
+/* Validate SDP session descriptor. */
+PJ_DEF(pj_status_t) pjmedia_sdp_validate2(const pjmedia_sdp_session *sdp,
+ pj_bool_t strict)
+{
unsigned i;
const pj_str_t STR_RTPMAP = { "rtpmap", 6 };
@@ -1471,7 +1479,8 @@ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
*/
if (m->conn == NULL) {
if (sdp->conn == NULL)
- return PJMEDIA_SDP_EMISSINGCONN;
+ if (strict || m->desc.port != 0)
+ return PJMEDIA_SDP_EMISSINGCONN;
}
/* Verify payload type. */
@@ -1505,6 +1514,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_validate(const pjmedia_sdp_session *sdp)
return PJ_SUCCESS;
}
+
PJ_DEF(pj_status_t) pjmedia_sdp_transport_cmp( const pj_str_t *t1,
const pj_str_t *t2)
{
diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c
index bdce18d..61e6d93 100644
--- a/pjmedia/src/pjmedia/sdp_neg.c
+++ b/pjmedia/src/pjmedia/sdp_neg.c
@@ -1,4 +1,4 @@
-/* $Id: sdp_neg.c 3980 2012-03-20 09:23:20Z ming $ */
+/* $Id: sdp_neg.c 4367 2013-02-21 20:49:19Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -138,7 +138,7 @@ PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool,
*p_neg = NULL;
/* Validate remote offer and initial answer */
- status = pjmedia_sdp_validate(remote);
+ status = pjmedia_sdp_validate2(remote, PJ_FALSE);
if (status != PJ_SUCCESS)
return status;
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 5c7c873..840b06c 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -1,4 +1,4 @@
-/* $Id: stream.c 4120 2012-05-12 07:18:09Z ming $ */
+/* $Id: stream.c 4336 2013-01-29 08:15:02Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -73,6 +73,8 @@
# define PJMEDIA_STREAM_INC 1000
#endif
+/* Number of DTMF E bit transmissions */
+#define DTMF_EBIT_RETRANSMIT_CNT 3
/**
* Media channel.
@@ -94,6 +96,7 @@ struct dtmf
{
int event;
pj_uint32_t duration;
+ int ebit_cnt; /**< # of E bit transmissions */
};
/**
@@ -876,7 +879,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
*/
static void create_dtmf_payload(pjmedia_stream *stream,
struct pjmedia_frame *frame_out,
- int *first, int *last)
+ int forced_last, int *first, int *last)
{
pjmedia_rtp_dtmf_event *event;
struct dtmf *digit = &stream->tx_dtmf_buf[0];
@@ -896,25 +899,33 @@ static void create_dtmf_payload(pjmedia_stream *stream,
}
digit->duration += PJMEDIA_PIA_SPF(&stream->port.info);
+ if (digit->duration >= PJMEDIA_DTMF_DURATION)
+ digit->duration = PJMEDIA_DTMF_DURATION;
event->event = (pj_uint8_t)digit->event;
event->e_vol = 10;
event->duration = pj_htons((pj_uint16_t)digit->duration);
+ if (forced_last) {
+ digit->duration = PJMEDIA_DTMF_DURATION;
+ }
if (digit->duration >= PJMEDIA_DTMF_DURATION) {
event->e_vol |= 0x80;
- *last = 1;
- /* Prepare next digit. */
- pj_mutex_lock(stream->jb_mutex);
+ if (++digit->ebit_cnt >= DTMF_EBIT_RETRANSMIT_CNT) {
+ *last = 1;
- pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]),
- stream->tx_dtmf_count, 0);
- --stream->tx_dtmf_count;
+ /* Prepare next digit. */
+ pj_mutex_lock(stream->jb_mutex);
- pj_mutex_unlock(stream->jb_mutex);
+ pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]),
+ stream->tx_dtmf_count, 0);
+ --stream->tx_dtmf_count;
+
+ pj_mutex_unlock(stream->jb_mutex);
+ }
}
frame_out->size = 4;
@@ -1217,7 +1228,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
if (stream->tx_dtmf_count) {
int first=0, last=0;
- create_dtmf_payload(stream, &frame_out, &first, &last);
+ create_dtmf_payload(stream, &frame_out, 0, &first, &last);
/* Encapsulate into RTP packet. Note that:
* - RTP marker should be set on the beginning of a new event
@@ -1235,7 +1246,9 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
* Increment the RTP timestamp of the RTP session, for next
* RTP packets.
*/
- inc_timestamp = PJMEDIA_DTMF_DURATION - rtp_ts_len;
+ inc_timestamp = PJMEDIA_DTMF_DURATION +
+ ((DTMF_EBIT_RETRANSMIT_CNT-1) * samples_per_frame)
+ - rtp_ts_len;
}
@@ -1765,7 +1778,11 @@ static void on_rx_rtp( void *data,
peer_frm_ts_diff == (frm_ts_span>>1)))
{
if (peer_frm_ts_diff < stream->rtp_rx_ts_len_per_frame)
+ {
stream->rtp_rx_ts_len_per_frame = peer_frm_ts_diff;
+ /* Done, stop the check immediately */
+ stream->rtp_rx_check_cnt = 1;
+ }
if (--stream->rtp_rx_check_cnt == 0) {
PJ_LOG(4, (THIS_FILE, "G722 codec used, remote"
@@ -1985,6 +2002,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
stream->own_pool = own_pool;
pj_memcpy(&stream->si, info, sizeof(*info));
+ stream->si.param = pjmedia_codec_param_clone(pool, info->param);
+ pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name);
/* Init stream/port name */
name.ptr = (char*) pj_pool_alloc(pool, M);
@@ -2159,12 +2178,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
}
/* Get the frame size */
- stream->frame_size = stream->codec_param.info.max_bps *
- stream->codec_param.info.frm_ptime / 8 / 1000;
- if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime)
- % 8000 != 0)
- {
- ++stream->frame_size;
+ if (stream->codec_param.info.max_rx_frame_size > 0) {
+ stream->frame_size = stream->codec_param.info.max_rx_frame_size;
+ } else {
+ stream->frame_size = stream->codec_param.info.max_bps *
+ stream->codec_param.info.frm_ptime / 8 / 1000;
+ if ((stream->codec_param.info.max_bps *
+ stream->codec_param.info.frm_ptime) % 8000 != 0)
+ {
+ ++stream->frame_size;
+ }
}
/* How many consecutive PLC frames can be generated */
@@ -2172,7 +2195,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->codec_param.info.frm_ptime;
#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0)
- stream->rtp_rx_check_cnt = 5;
+ stream->rtp_rx_check_cnt = 50;
stream->has_g722_mpeg_bug = PJ_FALSE;
stream->rtp_rx_last_ts = 0;
stream->rtp_rx_last_cnt = 0;
@@ -2410,6 +2433,8 @@ err_cleanup:
*/
PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
{
+ pj_status_t status;
+
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
/* Send RTCP BYE (also SDES & XR) */
@@ -2417,6 +2442,48 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_TRUE);
}
+ /* If we're in the middle of transmitting DTMF digit, send one last
+ * RFC 2833 RTP packet with 'End' flag set.
+ */
+ if (stream->tx_dtmf_count && stream->tx_dtmf_buf[0].duration != 0) {
+ pjmedia_frame frame_out;
+ pjmedia_channel *channel = stream->enc;
+ int first=0, last=0;
+ void *rtphdr;
+ int rtphdrlen;
+
+ pj_bzero(&frame_out, sizeof(frame_out));
+ frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
+ frame_out.size = 0;
+
+ create_dtmf_payload(stream, &frame_out, 1, &first, &last);
+
+ /* Encapsulate into RTP packet. Note that:
+ * - RTP marker should be set on the beginning of a new event
+ * - RTP timestamp is constant for the same packet.
+ */
+ status = pjmedia_rtp_encode_rtp( &channel->rtp,
+ stream->tx_event_pt, first,
+ frame_out.size, 0,
+ (const void**)&rtphdr,
+ &rtphdrlen);
+ if (status == PJ_SUCCESS) {
+ /* Copy RTP header to the beginning of packet */
+ pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
+
+ /* Send the RTP packet to the transport. */
+ status = pjmedia_transport_send_rtp(stream->transport,
+ channel->out_pkt,
+ frame_out.size +
+ sizeof(pjmedia_rtp_hdr));
+ }
+
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(stream->port.info.name.ptr, status,
+ "Error sending RTP/DTMF end packet"));
+ }
+ }
+
/* Detach from transport
* MUST NOT hold stream mutex while detaching from transport, as
* it may cause deadlock. See ticket #460 for the details.
@@ -2692,6 +2759,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream,
stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt;
stream->tx_dtmf_buf[stream->tx_dtmf_count+i].duration = 0;
+ stream->tx_dtmf_buf[stream->tx_dtmf_count+i].ebit_cnt = 0;
}
if (status != PJ_SUCCESS)
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 40bb71b..7c3deb2 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -1,4 +1,4 @@
-/* $Id: transport_ice.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: transport_ice.c 4350 2013-02-15 03:57:31Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -285,7 +285,9 @@ static void set_no_ice(struct transport_ice *tp_ice, const char *reason,
"Stopping ICE, reason=%s", reason));
}
- pj_ice_strans_stop_ice(tp_ice->ice_st);
+ if (tp_ice->ice_st) {
+ pj_ice_strans_stop_ice(tp_ice->ice_st);
+ }
tp_ice->use_ice = PJ_FALSE;
}
@@ -1541,6 +1543,8 @@ static pj_status_t transport_get_info(pjmedia_transport *tp,
ii = (pjmedia_ice_transport_info*) tsi->buffer;
pj_bzero(ii, sizeof(*ii));
+ ii->active = tp_ice->use_ice;
+
if (pj_ice_strans_has_sess(tp_ice->ice_st))
ii->role = pj_ice_strans_get_role(tp_ice->ice_st);
else
diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c
index 1321e75..a661c37 100644
--- a/pjmedia/src/pjmedia/transport_srtp.c
+++ b/pjmedia/src/pjmedia/transport_srtp.c
@@ -1,4 +1,4 @@
-/* $Id: transport_srtp.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: transport_srtp.c 4366 2013-02-21 20:41:31Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -34,10 +34,12 @@
#define THIS_FILE "transport_srtp.c"
-/* Maximum size of packet */
-#define MAX_RTP_BUFFER_LEN 1500
-#define MAX_RTCP_BUFFER_LEN 1500
-#define MAX_KEY_LEN 32
+/* Maximum size of outgoing packet */
+#define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU
+#define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU
+
+/* Maximum SRTP crypto key length */
+#define MAX_KEY_LEN 128
/* Initial value of probation counter. When probation counter > 0,
* it means SRTP is in probation state, and it may restart when
@@ -611,19 +613,47 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
/* Declare SRTP session initialized */
srtp->session_inited = PJ_TRUE;
- PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr,
- octet_string_hex_string(tx->key.ptr, tx->key.slen)));
- if (srtp->tx_policy.flags) {
- PJ_LOG(5,(srtp->pool->obj_name,"TX: disable%s%s", (cr_tx_idx?"":" enc"),
- (au_tx_idx?"":" auth")));
- }
+ /* Logging stuffs */
+#if PJ_LOG_MAX_LEVEL >= 5
+ {
+ char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)];
+ int b64_len;
+
+ /* TX crypto and key */
+ b64_len = sizeof(b64);
+ status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen,
+ b64, &b64_len);
+ if (status != PJ_SUCCESS)
+ b64_len = pj_ansi_sprintf(b64, "--key too long--");
+ else
+ b64[b64_len] = '\0';
+
+ PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s",
+ srtp->tx_policy.name.ptr, b64));
+ if (srtp->tx_policy.flags) {
+ PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s",
+ (cr_tx_idx?"":" enc"),
+ (au_tx_idx?"":" auth")));
+ }
- PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr,
- octet_string_hex_string(rx->key.ptr, rx->key.slen)));
- if (srtp->rx_policy.flags) {
- PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"),
- (au_rx_idx?"":" auth")));
+ /* RX crypto and key */
+ b64_len = sizeof(b64);
+ status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen,
+ b64, &b64_len);
+ if (status != PJ_SUCCESS)
+ b64_len = pj_ansi_sprintf(b64, "--key too long--");
+ else
+ b64[b64_len] = '\0';
+
+ PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s",
+ srtp->rx_policy.name.ptr, b64));
+ if (srtp->rx_policy.flags) {
+ PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s",
+ (cr_rx_idx?"":" enc"),
+ (au_rx_idx?"":" auth")));
+ }
}
+#endif
on_return:
pj_lock_release(srtp->mutex);
@@ -777,7 +807,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
if (srtp->bypass_srtp)
return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size);
- if (size > sizeof(srtp->rtp_tx_buffer))
+ if (size > sizeof(srtp->rtp_tx_buffer) - 10)
return PJ_ETOOBIG;
pj_memcpy(srtp->rtp_tx_buffer, pkt, size);
@@ -791,7 +821,8 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
pj_lock_release(srtp->mutex);
if (err == err_status_ok) {
- status = pjmedia_transport_send_rtp(srtp->member_tp, srtp->rtp_tx_buffer, len);
+ status = pjmedia_transport_send_rtp(srtp->member_tp,
+ srtp->rtp_tx_buffer, len);
} else {
status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
@@ -822,7 +853,7 @@ static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
pkt, size);
}
- if (size > sizeof(srtp->rtcp_tx_buffer))
+ if (size > sizeof(srtp->rtcp_tx_buffer) - 10)
return PJ_ETOOBIG;
pj_memcpy(srtp->rtcp_tx_buffer, pkt, size);
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index 76f9bbb..458424d 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -1,4 +1,4 @@
-/* $Id: transport_udp.c 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: transport_udp.c 4197 2012-07-05 07:26:29Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -29,7 +29,7 @@
/* Maximum size of incoming RTP packet */
-#define RTP_LEN PJMEDIA_MAX_MTU
+#define RTP_LEN PJMEDIA_MAX_MRU
/* Maximum size of incoming RTCP packet */
#define RTCP_LEN 600
@@ -42,7 +42,7 @@ static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
/* Pending write buffer */
typedef struct pending_write
{
- char buffer[RTP_LEN];
+ char buffer[PJMEDIA_MAX_MTU];
pj_ioqueue_op_key_t op_key;
} pending_write;
@@ -752,7 +752,7 @@ static pj_status_t transport_send_rtp( pjmedia_transport *tp,
PJ_ASSERT_RETURN(udp->attached, PJ_EINVALIDOP);
/* Check that the size is supported */
- PJ_ASSERT_RETURN(size <= RTP_LEN, PJ_ETOOBIG);
+ PJ_ASSERT_RETURN(size <= PJMEDIA_MAX_MTU, PJ_ETOOBIG);
/* Simulate packet lost on TX direction */
if (udp->tx_drop_pct) {
diff --git a/pjmedia/src/pjmedia/types.c b/pjmedia/src/pjmedia/types.c
index b280ea8..732f9b5 100644
--- a/pjmedia/src/pjmedia/types.c
+++ b/pjmedia/src/pjmedia/types.c
@@ -1,4 +1,4 @@
-/* $Id: types.c 3715 2011-08-19 09:35:25Z nanang $ */
+/* $Id: types.c 4411 2013-03-04 04:34:38Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -37,10 +37,10 @@ PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t)
"unknown"
};
- pj_assert(t < PJ_ARRAY_SIZE(type_names));
+ pj_assert(t < (int)PJ_ARRAY_SIZE(type_names));
pj_assert(PJMEDIA_TYPE_UNKNOWN == 4);
- if (t < PJ_ARRAY_SIZE(type_names))
+ if (t < (int)PJ_ARRAY_SIZE(type_names))
return type_names[t];
else
return "??";
diff --git a/pjmedia/src/pjmedia/vid_codec_util.c b/pjmedia/src/pjmedia/vid_codec_util.c
index e51fff4..8cc241b 100644
--- a/pjmedia/src/pjmedia/vid_codec_util.c
+++ b/pjmedia/src/pjmedia/vid_codec_util.c
@@ -1,4 +1,4 @@
-/* $Id: vid_codec_util.c 3995 2012-03-29 10:54:01Z nanang $ */
+/* $Id: vid_codec_util.c 4362 2013-02-21 14:51:56Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -38,6 +38,14 @@
*/
#define H264_STRICT_SDP_NEGO 0
+/* Default frame rate, if not specified */
+#define DEFAULT_H264_FPS_NUM 15
+#define DEFAULT_H264_FPS_DENUM 1
+
+/* Default aspect ratio, if not specified */
+#define DEFAULT_H264_RATIO_NUM 16
+#define DEFAULT_H264_RATIO_DENUM 9
+
/* ITU resolution definition */
struct mpi_resolution_t
@@ -55,6 +63,10 @@ mpi_resolutions [] =
};
+#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16))
+#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum
+
+
/* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */
static pj_status_t parse_custom_res_fmtp(const pj_str_t *fmtp_val,
pjmedia_rect_size *size,
@@ -291,6 +303,75 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h263_apply_fmtp(
}
+/* Declaration of H.264 level info */
+typedef struct h264_level_info_t
+{
+ unsigned id; /* Level id. */
+ unsigned max_mbps; /* Max macroblocks per second. */
+ unsigned max_mb; /* Max macroblocks. */
+ unsigned max_br; /* Max bitrate (kbps). */
+} h264_level_info_t;
+
+
+/* Init H264 parameters based on profile-level-id */
+static pj_status_t init_h264_profile(const pj_str_t *profile,
+ pjmedia_vid_codec_h264_fmtp *fmtp)
+{
+ const h264_level_info_t level_info[] =
+ {
+ { 10, 1485, 99, 64 },
+ { 9, 1485, 99, 128 }, /*< level 1b */
+ { 11, 3000, 396, 192 },
+ { 12, 6000, 396, 384 },
+ { 13, 11880, 396, 768 },
+ { 20, 11880, 396, 2000 },
+ { 21, 19800, 792, 4000 },
+ { 22, 20250, 1620, 4000 },
+ { 30, 40500, 1620, 10000 },
+ { 31, 108000, 3600, 14000 },
+ { 32, 216000, 5120, 20000 },
+ { 40, 245760, 8192, 20000 },
+ { 41, 245760, 8192, 50000 },
+ { 42, 522240, 8704, 50000 },
+ { 50, 589824, 22080, 135000 },
+ { 51, 983040, 36864, 240000 },
+ };
+ unsigned i, tmp;
+ pj_str_t endst;
+ const h264_level_info_t *li = NULL;
+
+ if (profile->slen != 6)
+ return PJMEDIA_SDP_EINFMTP;
+
+ tmp = pj_strtoul2(profile, &endst, 16);
+ if (endst.slen)
+ return PJMEDIA_SDP_EINFMTP;
+
+ fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF);
+ fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF);
+ fmtp->level = (pj_uint8_t)(tmp & 0xFF);
+
+ for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) {
+ if (level_info[i].id == fmtp->level) {
+ li = &level_info[i];
+ break;
+ }
+ }
+ if (li == NULL)
+ return PJMEDIA_SDP_EINFMTP;
+
+ /* Init profile level spec */
+ if (fmtp->max_br == 0)
+ fmtp->max_br = li->max_br;
+ if (fmtp->max_mbps == 0)
+ fmtp->max_mbps = li->max_mbps;
+ if (fmtp->max_fs == 0)
+ fmtp->max_fs = li->max_mb;
+
+ return PJ_SUCCESS;
+}
+
+
/* H264 fmtp parser */
PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
const pjmedia_codec_fmtp *fmtp,
@@ -305,24 +386,17 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
const pj_str_t PACKETIZATION_MODE = {"packetization-mode", 18};
const pj_str_t SPROP_PARAMETER_SETS = {"sprop-parameter-sets", 20};
unsigned i;
+ pj_status_t status;
pj_bzero(h264_fmtp, sizeof(*h264_fmtp));
for (i=0; i<fmtp->cnt; ++i) {
unsigned tmp;
if (pj_stricmp(&fmtp->param[i].name, &PROFILE_LEVEL_ID)==0) {
- pj_str_t endst;
-
- if (fmtp->param[i].val.slen != 6)
- return PJMEDIA_SDP_EINFMTP;
-
- tmp = pj_strtoul2(&fmtp->param[i].val, &endst, 16);
- if (endst.slen)
- return PJMEDIA_SDP_EINFMTP;
-
- h264_fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF);
- h264_fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF);
- h264_fmtp->level = (pj_uint8_t)(tmp & 0xFF);
+ /* Init H264 parameters based on level, if not set yet */
+ status = init_h264_profile(&fmtp->param[i].val, h264_fmtp);
+ if (status != PJ_SUCCESS)
+ return status;
} else if (pj_stricmp(&fmtp->param[i].name, &PACKETIZATION_MODE)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
if (tmp >= 0 && tmp <= 2)
@@ -331,19 +405,19 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
return PJMEDIA_SDP_EINFMTP;
} else if (pj_stricmp(&fmtp->param[i].name, &MAX_MBPS)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
- h264_fmtp->max_mbps = tmp;
+ h264_fmtp->max_mbps = PJ_MAX(tmp, h264_fmtp->max_mbps);
} else if (pj_stricmp(&fmtp->param[i].name, &MAX_FS)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
- h264_fmtp->max_fs = tmp;
+ h264_fmtp->max_fs = PJ_MAX(tmp, h264_fmtp->max_fs);
} else if (pj_stricmp(&fmtp->param[i].name, &MAX_CPB)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
- h264_fmtp->max_cpb = tmp;
+ h264_fmtp->max_cpb = PJ_MAX(tmp, h264_fmtp->max_cpb);
} else if (pj_stricmp(&fmtp->param[i].name, &MAX_DPB)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
- h264_fmtp->max_dpb = tmp;
+ h264_fmtp->max_dpb = PJ_MAX(tmp, h264_fmtp->max_dpb);
} else if (pj_stricmp(&fmtp->param[i].name, &MAX_BR)==0) {
tmp = pj_strtoul(&fmtp->param[i].val);
- h264_fmtp->max_br = tmp;
+ h264_fmtp->max_br = PJ_MAX(tmp, h264_fmtp->max_br);
} else if (pj_stricmp(&fmtp->param[i].name, &SPROP_PARAMETER_SETS)==0)
{
pj_str_t sps_st;
@@ -507,66 +581,80 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_match_sdp(pj_pool_t *pool,
}
-/* Declaration of H.264 level info */
-typedef struct h264_level_info_t
-{
- unsigned id; /* Level id. */
- unsigned max_mbps; /* Max macroblocks per second. */
- unsigned max_mb; /* Max macroblocks. */
- unsigned bitrate; /* Max bitrate (kbps). */
- unsigned def_w; /* Default width. */
- unsigned def_h; /* Default height. */
- unsigned def_fps; /* Default fps. */
-} h264_level_info_t;
-
+/* Find greatest common divisor (GCD) */
+static unsigned gcd (unsigned a, unsigned b) {
+ unsigned c;
+ while (b) {
+ c = a % b;
+ a = b;
+ b = c;
+ }
+ return a;
+}
-/* Get H.264 level info from specified level ID */
-static pj_status_t get_h264_level_info(unsigned id, h264_level_info_t *level)
+/* Find highest resolution possible for the specified H264 fmtp and
+ * aspect ratio.
+ */
+static pj_status_t find_highest_res(pjmedia_vid_codec_h264_fmtp *fmtp,
+ const pjmedia_ratio *fps,
+ const pjmedia_ratio *ratio,
+ pjmedia_rect_size *size)
{
- unsigned i;
- const h264_level_info_t level_info[] =
- {
- { 10, 1485, 99, 64, 176, 144, 15 },
- { 9, 1485, 99, 128, 176, 144, 15 }, /*< level 1b */
- { 11, 3000, 396, 192, 320, 240, 10 },
- { 12, 6000, 396, 384, 352, 288, 15 },
- { 13, 11880, 396, 768, 352, 288, 15 },
- { 20, 11880, 396, 2000, 352, 288, 30 },
- { 21, 19800, 792, 4000, 352, 288, 30 },
- { 22, 20250, 1620, 4000, 352, 288, 30 },
- { 30, 40500, 1620, 10000, 720, 480, 30 },
- { 31, 108000, 3600, 14000, 1280, 720, 30 },
- { 32, 216000, 5120, 20000, 1280, 720, 30 },
- { 40, 245760, 8192, 20000, 1920, 1080, 30 },
- { 41, 245760, 8192, 50000, 1920, 1080, 30 },
- { 42, 522240, 8704, 50000, 1920, 1080, 30 },
- { 50, 589824, 22080, 135000, 1920, 1080, 30 },
- { 51, 983040, 36864, 240000, 1920, 1080, 30 },
- };
+ pjmedia_ratio def_ratio = { DEFAULT_H264_RATIO_NUM,
+ DEFAULT_H264_RATIO_DENUM };
+ pjmedia_ratio def_fps = { DEFAULT_H264_FPS_NUM,
+ DEFAULT_H264_FPS_DENUM };
+ pjmedia_ratio asp_ratio, the_fps;
+ unsigned max_fs, g, scale;
+
+ pj_assert(size);
+
+ /* Get the ratio, or just use default if not provided. */
+ if (ratio && ratio->num && ratio->denum) {
+ asp_ratio = *ratio;
+ } else {
+ asp_ratio = def_ratio;
+ }
- for (i = 0; i < PJ_ARRAY_SIZE(level_info); ++i) {
- if (level_info[i].id == id) {
- *level = level_info[i];
- return PJ_SUCCESS;
- }
+ /* Normalize the aspect ratio */
+ g = gcd(asp_ratio.num, asp_ratio.denum);
+ asp_ratio.num /= g;
+ asp_ratio.denum /= g;
+
+ /* Get the frame rate, or just use default if not provided. */
+ if (fps && fps->num && fps->denum) {
+ the_fps = *fps;
+ } else {
+ the_fps = def_fps;
}
- return PJ_ENOTFOUND;
-}
+ /* Calculate maximum size (in macroblocks) */
+ max_fs = fmtp->max_mbps * fps->denum / fps->num;
+ max_fs = PJ_MIN(max_fs, fmtp->max_fs);
-#define CALC_H264_MB_NUM(size) (((size.w+15)/16)*((size.h+15)/16))
-#define CALC_H264_MBPS(size,fps) CALC_H264_MB_NUM(size)*fps.num/fps.denum
+ /* Check if the specified ratio is using big numbers
+ * (not normalizable), override it with default ratio!
+ */
+ if ((int)max_fs < asp_ratio.num * asp_ratio.denum)
+ asp_ratio = def_ratio;
+
+ /* Calculate the scale factor for size */
+ scale = pj_isqrt(max_fs / asp_ratio.denum / asp_ratio.num);
+
+ /* Calculate the size, note that the frame size is in macroblock units */
+ size->w = asp_ratio.num * scale * 16;
+ size->h = asp_ratio.denum * scale * 16;
+
+ return PJ_SUCCESS;
+}
PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
pjmedia_vid_codec_param *param)
{
- const unsigned default_fps = 30;
-
if (param->dir & PJMEDIA_DIR_ENCODING) {
pjmedia_vid_codec_h264_fmtp fmtp;
pjmedia_video_format_detail *vfd;
- h264_level_info_t level_info;
pj_status_t status;
/* Get remote param */
@@ -575,36 +663,42 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
if (status != PJ_SUCCESS)
return status;
- status = get_h264_level_info(fmtp.level, &level_info);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Size and fps for encoding direction must conform to H.264 level
+ /* Adjust fps, size, and bitrate to conform to H.264 level
* specified by remote SDP fmtp.
*/
vfd = pjmedia_format_get_video_format_detail(&param->enc_fmt,
PJ_TRUE);
+
+ if (vfd->fps.num == 0 || vfd->fps.denum == 0) {
+ vfd->fps.num = DEFAULT_H264_FPS_NUM;
+ vfd->fps.denum = DEFAULT_H264_FPS_DENUM;
+ }
+
if (vfd->size.w && vfd->size.h) {
unsigned mb, mbps;
- if (vfd->fps.num == 0 || vfd->fps.denum == 0) {
- vfd->fps.num = default_fps;
- vfd->fps.denum = 1;
- }
+ /* Scale down the resolution if it exceeds profile spec */
mb = CALC_H264_MB_NUM(vfd->size);
mbps = CALC_H264_MBPS(vfd->size, vfd->fps);
- if (mb > level_info.max_mb || mbps > level_info.max_mbps) {
- vfd->size.w = level_info.def_w;
- vfd->size.h = level_info.def_h;
- vfd->fps.num = level_info.def_fps;
- vfd->fps.denum = 1;
+ if (mb > fmtp.max_fs || mbps > fmtp.max_mbps) {
+ pjmedia_ratio r;
+ r.num = vfd->size.w;
+ r.denum = vfd->size.h;
+ find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size);
}
} else {
- vfd->size.w = level_info.def_w;
- vfd->size.h = level_info.def_h;
- vfd->fps.num = level_info.def_fps;
- vfd->fps.denum = 1;
+ /* When not specified, just use the highest res possible*/
+ pjmedia_ratio r;
+ r.num = vfd->size.w;
+ r.denum = vfd->size.h;
+ find_highest_res(&fmtp, &vfd->fps, &r, &vfd->size);
}
+
+ /* Encoding bitrate must not be higher than H264 level spec */
+ if (vfd->avg_bps > fmtp.max_br * 1000)
+ vfd->avg_bps = fmtp.max_br * 1000;
+ if (vfd->max_bps > fmtp.max_br * 1000)
+ vfd->max_bps = fmtp.max_br * 1000;
}
if (param->dir & PJMEDIA_DIR_DECODING) {
@@ -613,7 +707,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
*/
pjmedia_vid_codec_h264_fmtp fmtp;
pjmedia_video_format_detail *vfd;
- h264_level_info_t level_info;
+ pjmedia_ratio r;
+ pjmedia_rect_size highest_size;
pj_status_t status;
status = pjmedia_vid_codec_h264_parse_fmtp(&param->dec_fmtp,
@@ -621,22 +716,29 @@ PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_apply_fmtp(
if (status != PJ_SUCCESS)
return status;
- status = get_h264_level_info(fmtp.level, &level_info);
- if (status != PJ_SUCCESS)
- return status;
-
vfd = pjmedia_format_get_video_format_detail(&param->dec_fmt,
PJ_TRUE);
- if (vfd->size.w * vfd->size.h < level_info.def_w * level_info.def_h) {
- vfd->size.w = level_info.def_w;
- vfd->size.h = level_info.def_h;
- }
-
if (vfd->fps.num == 0 || vfd->fps.denum == 0) {
- vfd->fps.num = default_fps;
- vfd->fps.denum = 1;
+ vfd->fps.num = DEFAULT_H264_FPS_NUM;
+ vfd->fps.denum = DEFAULT_H264_FPS_DENUM;
}
+
+ /* Normalize decoding resolution, i.e: it must not be lower than
+ * the H264 profile level setting used, as this may be used by
+ * app to allocate buffer.
+ */
+ r.num = vfd->size.w;
+ r.denum = vfd->size.h;
+ find_highest_res(&fmtp, &vfd->fps, &r, &highest_size);
+ if (vfd->size.w * vfd->size.h < highest_size.w * highest_size.h)
+ vfd->size = highest_size;
+
+ /* Normalize decoding bitrate based on H264 level spec */
+ if (vfd->avg_bps < fmtp.max_br * 1000)
+ vfd->avg_bps = fmtp.max_br * 1000;
+ if (vfd->max_bps < fmtp.max_br * 1000)
+ vfd->max_bps = fmtp.max_br * 1000;
}
return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/vid_port.c b/pjmedia/src/pjmedia/vid_port.c
index 289e3e5..017e867 100644
--- a/pjmedia/src/pjmedia/vid_port.c
+++ b/pjmedia/src/pjmedia/vid_port.c
@@ -1,4 +1,4 @@
-/* $Id: vid_port.c 4168 2012-06-18 05:59:08Z ming $ */
+/* $Id: vid_port.c 4290 2012-11-01 03:06:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -332,9 +332,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
PJMEDIA_SIG_VID_PORT,
prm->vidparam.dir, &prm->vidparam.fmt);
- if (vp->stream_role == ROLE_ACTIVE) {
- need_frame_buf = PJ_TRUE;
- }
+ need_frame_buf = PJ_TRUE;
}
if (need_frame_buf) {
@@ -654,8 +652,10 @@ static pj_status_t convert_frame(pjmedia_vid_port *vp,
pj_status_t status = PJ_SUCCESS;
if (vp->conv.conv) {
- dst_frame->buf = vp->conv.conv_buf;
- dst_frame->size = vp->conv.conv_buf_size;
+ if (!dst_frame->buf || dst_frame->size < vp->conv.conv_buf_size) {
+ dst_frame->buf = vp->conv.conv_buf;
+ dst_frame->size = vp->conv.conv_buf_size;
+ }
status = pjmedia_converter_convert(vp->conv.conv,
src_frame, dst_frame);
}
@@ -922,6 +922,7 @@ static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port,
pj_status_t status;
pjmedia_frame frame_;
+ pj_bzero(&frame_, sizeof(frame_));
status = convert_frame(vp, frame, &frame_);
if (status != PJ_SUCCESS)
return status;
diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c
index d899408..e101d7b 100644
--- a/pjmedia/src/pjmedia/vid_stream.c
+++ b/pjmedia/src/pjmedia/vid_stream.c
@@ -1,4 +1,4 @@
-/* $Id: vid_stream.c 4123 2012-05-14 11:17:31Z ming $ */
+/* $Id: vid_stream.c 4197 2012-07-05 07:26:29Z nanang $ */
/*
* Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
*
@@ -68,6 +68,13 @@
# define PJMEDIA_VSTREAM_INC 1000
#endif
+/* Due to network MTU limitation, a picture bitstream may be splitted into
+ * several chunks for RTP delivery. The chunk number may vary depend on the
+ * picture resolution and MTU. This constant specifies the minimum chunk
+ * number to be allocated to store a picture bitstream in decoding direction.
+ */
+#define MIN_CHUNKS_PER_FRM 30
+
/* Video stream keep-alive feature is currently disabled. */
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
# undef PJMEDIA_STREAM_ENABLE_KA
@@ -1357,7 +1364,6 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
int frm_ptime, chunks_per_frm;
pjmedia_video_format_detail *vfd_enc, *vfd_dec;
char *p;
- unsigned dec_mtu;
pj_status_t status;
if (!pool) {
@@ -1410,9 +1416,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
if (info->codec_param->enc_mtu > PJMEDIA_MAX_MTU)
info->codec_param->enc_mtu = PJMEDIA_MAX_MTU;
- /* MTU estimation for decoding direction */
- dec_mtu = PJMEDIA_MAX_MTU;
-
+ /* Packet size estimation for decoding direction */
vfd_enc = pjmedia_format_get_video_format_detail(
&info->codec_param->enc_fmt, PJ_TRUE);
vfd_dec = pjmedia_format_get_video_format_detail(
@@ -1528,8 +1532,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Init jitter buffer parameters: */
frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num;
- chunks_per_frm = stream->frame_size / dec_mtu;
- if (chunks_per_frm == 0) chunks_per_frm = 1;
+ chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MRU;
+ if (chunks_per_frm < MIN_CHUNKS_PER_FRM)
+ chunks_per_frm = MIN_CHUNKS_PER_FRM;
/* JB max count, default 500ms */
if (info->jb_max >= frm_ptime)
@@ -1564,7 +1569,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
/* Create jitter buffer */
status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name,
- dec_mtu + PJMEDIA_STREAM_RESV_PAYLOAD_LEN,
+ PJMEDIA_MAX_MRU,
1000 * vfd_enc->fps.denum / vfd_enc->fps.num,
jb_max, &stream->jb);
if (status != PJ_SUCCESS)
diff --git a/pjmedia/src/pjmedia/vid_stream_info.c b/pjmedia/src/pjmedia/vid_stream_info.c
index 51b688f..37dae5b 100644
--- a/pjmedia/src/pjmedia/vid_stream_info.c
+++ b/pjmedia/src/pjmedia/vid_stream_info.c
@@ -1,4 +1,4 @@
-/* $Id: vid_stream_info.c 3982 2012-03-22 09:56:52Z bennylp $ */
+/* $Id: vid_stream_info.c 4257 2012-09-17 03:11:44Z ming $ */
/*
* Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
*
@@ -22,6 +22,7 @@
#include <pj/ctype.h>
#include <pj/rand.h>
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
static const pj_str_t ID_VIDEO = { "video", 5};
static const pj_str_t ID_IN = { "IN", 2 };
@@ -382,4 +383,4 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
return status;
}
-
+#endif /* PJMEDIA_HAS_VIDEO */
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index b0b146d..df68511 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -1,4 +1,4 @@
-/* $Id: mips_test.c 3982 2012-03-22 09:56:52Z bennylp $ */
+/* $Id: mips_test.c 4335 2013-01-29 08:09:15Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -943,6 +943,23 @@ static pjmedia_port* amr_encode_decode(pj_pool_t *pool,
}
#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */
+#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+/* AMR-WB benchmark benchmark */
+static pjmedia_port* amrwb_encode_decode(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ return codec_encode_decode(pool, "AMR/16000",
+ &pjmedia_codec_opencore_amr_init_default,
+ &pjmedia_codec_opencore_amr_deinit,
+ clock_rate, channel_count,
+ samples_per_frame, flags, te);
+}
+#endif /* PJMEDIA_HAS_OPENCORE_AMRWB_CODEC */
+
#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
static pj_status_t init_l16_default(pjmedia_endpt *endpt)
{
@@ -2024,7 +2041,25 @@ static pjmedia_port* create_stream_amr( pj_pool_t *pool,
clock_rate, channel_count,
samples_per_frame, flags, te);
}
-#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */
+#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */
+
+/* AMR-WB stream */
+#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+static pjmedia_port* create_stream_amrwb( pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ return create_stream(pool, "AMR/16000",
+ &pjmedia_codec_opencore_amr_init_default,
+ &pjmedia_codec_opencore_amr_deinit,
+ PJ_FALSE, PJ_FALSE, PJ_FALSE,
+ clock_rate, channel_count,
+ samples_per_frame, flags, te);
+}
+#endif /* PJMEDIA_HAS_OPENCORE_AMRWB_CODEC */
/***************************************************************************/
/* Delay buffer */
@@ -2419,6 +2454,9 @@ int mips_test(void)
#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
{ "codec encode/decode - AMR-NB", OP_PUT, K8, &amr_encode_decode},
#endif
+#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+ { "codec encode/decode - AMR-WB", OP_PUT, K16, &amrwb_encode_decode},
+#endif
#if PJMEDIA_HAS_L16_CODEC
{ "codec encode/decode - L16/8000/1", OP_PUT, K8, &l16_8_encode_decode},
{ "codec encode/decode - L16/16000/1", OP_PUT, K16, &l16_16_encode_decode},
@@ -2447,6 +2485,9 @@ int mips_test(void)
#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
{ "stream TX/RX - AMR-NB", OP_PUT_GET, K8, &create_stream_amr},
#endif
+#if PJMEDIA_HAS_OPENCORE_AMRWB_CODEC
+ { "stream TX/RX - AMR-WB", OP_PUT_GET, K16, &create_stream_amrwb},
+#endif
};
unsigned i, c, k[3] = {K8, K16, K32}, clock_rates[3] = {8000, 16000, 32000};
diff --git a/pjnath/build/Makefile b/pjnath/build/Makefile
index 573d957..32b0b50 100644
--- a/pjnath/build/Makefile
+++ b/pjnath/build/Makefile
@@ -40,7 +40,7 @@ export PJNATH_CFLAGS += $(_CFLAGS)
# Defines for building test application
#
export PJNATH_TEST_SRCDIR = ../src/pjnath-test
-export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o \
+export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o \
stun_sock_test.o turn_sock_test.o test.o
export PJNATH_TEST_CFLAGS += $(_CFLAGS)
export PJNATH_TEST_LDFLAGS += $(_LDFLAGS)
@@ -97,6 +97,8 @@ distclean: realclean
pjnath:
$(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $(PJNATH_LIB)
+$$(PJNATH_LIB): pjnath
+
pjnath-test: $(PJLIB_LIB) $(PJLIB_UTIL_LIB) $(PJNATH_LIB)
$(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $(PJNATH_TEST_EXE)
diff --git a/pjnath/build/pjnath_test.vcproj b/pjnath/build/pjnath_test.vcproj
index ced2508..4ec5bbf 100644
--- a/pjnath/build/pjnath_test.vcproj
+++ b/pjnath/build/pjnath_test.vcproj
@@ -2999,6 +2999,64 @@
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
+ RelativePath="..\src\pjnath-test\concur_test.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug-Static|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release-Dynamic|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug-Dynamic|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release-Static|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ />
+ </FileConfiguration>
+ </File>
+ <File
RelativePath="..\src\pjnath-test\ice_test.c"
>
<FileConfiguration
diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h
index 06368df..3ee5d91 100644
--- a/pjnath/include/pjnath/config.h
+++ b/pjnath/include/pjnath/config.h
@@ -1,4 +1,4 @@
-/* $Id: config.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: config.h 4199 2012-07-05 10:52:55Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -487,6 +487,17 @@
# define PJNATH_POOL_INC_TURN_SOCK 1000
#endif
+/** Default STUN software name */
+#ifndef PJNATH_STUN_SOFTWARE_NAME
+# define PJNATH_MAKE_SW_NAME(a,b,c,d) "pjnath-" #a "." #b "." #c d
+# define PJNATH_MAKE_SW_NAME2(a,b,c,d) PJNATH_MAKE_SW_NAME(a,b,c,d)
+# define PJNATH_STUN_SOFTWARE_NAME PJNATH_MAKE_SW_NAME2( \
+ PJ_VERSION_NUM_MAJOR, \
+ PJ_VERSION_NUM_MINOR, \
+ PJ_VERSION_NUM_REV, \
+ PJ_VERSION_NUM_EXTRA)
+#endif
+
/**
* @}
*/
diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h
index 1e65943..e21f520 100644
--- a/pjnath/include/pjnath/ice_session.h
+++ b/pjnath/include/pjnath/ice_session.h
@@ -1,4 +1,4 @@
-/* $Id: ice_session.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ice_session.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -612,13 +612,14 @@ struct pj_ice_sess
pj_pool_t *pool; /**< Pool instance. */
void *user_data; /**< App. data. */
- pj_mutex_t *mutex; /**< Mutex. */
+ pj_grp_lock_t *grp_lock; /**< Group lock */
pj_ice_sess_role role; /**< ICE role. */
pj_ice_sess_options opt; /**< Options */
pj_timestamp tie_breaker; /**< Tie breaker value */
pj_uint8_t *prefs; /**< Type preference. */
pj_bool_t is_nominating; /**< Nominating stage */
pj_bool_t is_complete; /**< Complete? */
+ pj_bool_t is_destroying; /**< Destroy is called */
pj_status_t ice_status; /**< Error status. */
pj_timer_entry timer; /**< ICE timer. */
pj_ice_sess_cb cb; /**< Callback. */
@@ -729,6 +730,8 @@ PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt);
* the value is NULL, a random string will be
* generated.
* @param local_passwd Optional string to be used as local password.
+ * @param grp_lock Optional group lock to be used by this session.
+ * If NULL, the session will create one itself.
* @param p_ice Pointer to receive the ICE session instance.
*
* @return PJ_SUCCESS if ICE session is created successfully.
@@ -740,6 +743,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
const pj_ice_sess_cb *cb,
const pj_str_t *local_ufrag,
const pj_str_t *local_passwd,
+ pj_grp_lock_t *grp_lock,
pj_ice_sess **p_ice);
/**
diff --git a/pjnath/include/pjnath/stun_config.h b/pjnath/include/pjnath/stun_config.h
index 199c452..e5a0b98 100644
--- a/pjnath/include/pjnath/stun_config.h
+++ b/pjnath/include/pjnath/stun_config.h
@@ -1,4 +1,4 @@
-/* $Id: stun_config.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_config.h 4199 2012-07-05 10:52:55Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -81,6 +81,13 @@ typedef struct pj_stun_config
*/
unsigned res_cache_msec;
+ /**
+ * Software name to be included in all STUN requests and responses.
+ *
+ * Default: PJNATH_STUN_SOFTWARE_NAME.
+ */
+ pj_str_t software_name;
+
} pj_stun_config;
@@ -102,6 +109,7 @@ PJ_INLINE(void) pj_stun_config_init(pj_stun_config *cfg,
cfg->timer_heap = timer_heap;
cfg->rto_msec = PJ_STUN_RTO_VALUE;
cfg->res_cache_msec = PJ_STUN_RES_CACHE_DURATION;
+ cfg->software_name = pj_str((char*)PJNATH_STUN_SOFTWARE_NAME);
}
diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h
index 29efe1a..dd12e91 100644
--- a/pjnath/include/pjnath/stun_session.h
+++ b/pjnath/include/pjnath/stun_session.h
@@ -1,4 +1,4 @@
-/* $Id: stun_session.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_session.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -30,6 +30,7 @@
#include <pjnath/stun_config.h>
#include <pjnath/stun_transaction.h>
#include <pj/list.h>
+#include <pj/lock.h>
#include <pj/timer.h>
PJ_BEGIN_DECL
@@ -384,6 +385,8 @@ typedef enum pj_stun_sess_msg_log_flag
* name will be used for example for logging purpose.
* @param cb Session callback.
* @param fingerprint Enable message fingerprint for outgoing messages.
+ * @param grp_lock Optional group lock to be used by this session.
+ * If NULL, the session will create one itself.
* @param p_sess Pointer to receive STUN session instance.
*
* @return PJ_SUCCESS on success, or the appropriate error code.
@@ -392,6 +395,7 @@ PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_config *cfg,
const char *name,
const pj_stun_session_cb *cb,
pj_bool_t fingerprint,
+ pj_grp_lock_t *grp_lock,
pj_stun_session **p_sess);
/**
@@ -431,22 +435,6 @@ PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess,
PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess);
/**
- * Change the lock object used by the STUN session. By default, the STUN
- * session uses a mutex to protect its internal data. If application already
- * protects access to STUN session with higher layer lock, it may disable
- * the mutex protection in the STUN session by changing the STUN session
- * lock to a NULL mutex.
- *
- * @param sess The STUN session instance.
- * @param lock New lock instance to be used by the STUN session.
- * @param auto_del Specify whether STUN session should destroy this
- * lock instance when it's destroyed.
- */
-PJ_DECL(pj_status_t) pj_stun_session_set_lock(pj_stun_session *sess,
- pj_lock_t *lock,
- pj_bool_t auto_del);
-
-/**
* Set SOFTWARE name to be included in all requests and responses.
*
* @param sess The STUN session instance.
@@ -682,6 +670,8 @@ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess,
*
* @param sess The STUN session instance.
* @param tdata The request message previously sent.
+ * @param mod_count Boolean flag to indicate whether transmission count
+ * needs to be incremented.
*
* @return PJ_SUCCESS on success, or the appropriate error.
* This function will return PJNATH_ESTUNDESTROYED if
@@ -689,7 +679,8 @@ PJ_DECL(pj_status_t) pj_stun_session_cancel_req(pj_stun_session *sess,
* callback.
*/
PJ_DECL(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
- pj_stun_tx_data *tdata);
+ pj_stun_tx_data *tdata,
+ pj_bool_t mod_count);
/**
diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h
index decba9a..c18741a 100644
--- a/pjnath/include/pjnath/stun_sock.h
+++ b/pjnath/include/pjnath/stun_sock.h
@@ -1,4 +1,4 @@
-/* $Id: stun_sock.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_sock.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -27,6 +27,7 @@
#include <pjnath/stun_config.h>
#include <pjlib-util/resolver.h>
#include <pj/ioqueue.h>
+#include <pj/lock.h>
#include <pj/sock.h>
#include <pj/sock_qos.h>
@@ -218,7 +219,17 @@ typedef struct pj_stun_sock_info
typedef struct pj_stun_sock_cfg
{
/**
- * Packet buffer size. Default value is PJ_STUN_SOCK_PKT_LEN.
+ * The group lock to be used by the STUN socket. If NULL, the STUN socket
+ * will create one internally.
+ *
+ * Default: NULL
+ */
+ pj_grp_lock_t *grp_lock;
+
+ /**
+ * Packet buffer size.
+ *
+ * Default value is PJ_STUN_SOCK_PKT_LEN.
*/
unsigned max_pkt_size;
@@ -236,11 +247,21 @@ typedef struct pj_stun_sock_cfg
* address is zero, socket will be bound to INADDR_ANY. If the address
* is non-zero, socket will be bound to this address only, and the
* transport will have only one address alias (the \a alias_cnt field
- * in #pj_stun_sock_info structure.
+ * in #pj_stun_sock_info structure. If the port is set to zero, the
+ * socket will bind at any port (chosen by the OS).
*/
pj_sockaddr bound_addr;
/**
+ * Specify the port range for STUN socket binding, relative to the start
+ * port number specified in \a bound_addr. Note that this setting is only
+ * applicable when the start port number is non zero.
+ *
+ * Default value is zero.
+ */
+ pj_uint16_t port_range;
+
+ /**
* Specify the STUN keep-alive duration, in seconds. The STUN transport
* does keep-alive by sending STUN Binding request to the STUN server.
* If this value is zero, the PJ_STUN_KEEP_ALIVE_SEC value will be used.
diff --git a/pjnath/include/pjnath/stun_transaction.h b/pjnath/include/pjnath/stun_transaction.h
index 526186f..2acb56c 100644
--- a/pjnath/include/pjnath/stun_transaction.h
+++ b/pjnath/include/pjnath/stun_transaction.h
@@ -1,4 +1,4 @@
-/* $Id: stun_transaction.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_transaction.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -27,6 +27,7 @@
#include <pjnath/stun_msg.h>
#include <pjnath/stun_config.h>
+#include <pj/lock.h>
PJ_BEGIN_DECL
@@ -124,6 +125,7 @@ typedef struct pj_stun_tsx_cb
* @param cfg The STUN endpoint, which will be used to retrieve
* various settings for the transaction.
* @param pool Pool to be used to allocate memory from.
+ * @param grp_lock Group lock to synchronize.
* @param cb Callback structure, to be used by the transaction
* to send message and to notify the application about
* the completion of the transaction.
@@ -133,6 +135,7 @@ typedef struct pj_stun_tsx_cb
*/
PJ_DECL(pj_status_t) pj_stun_client_tsx_create( pj_stun_config *cfg,
pj_pool_t *pool,
+ pj_grp_lock_t *grp_lock,
const pj_stun_tsx_cb *cb,
pj_stun_client_tsx **p_tsx);
@@ -159,15 +162,14 @@ pj_stun_client_tsx_schedule_destroy(pj_stun_client_tsx *tsx,
/**
- * Destroy a STUN client transaction immediately. This function can be
- * called at any time to stop the transaction and destroy it.
+ * Stop the client transaction.
*
* @param tsx The STUN transaction.
*
* @return PJ_SUCCESS on success or PJ_EINVAL if the parameter
* is NULL.
*/
-PJ_DECL(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx);
+PJ_DECL(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx);
/**
@@ -234,13 +236,16 @@ PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
* but this functionality is needed by ICE.
*
* @param tsx The STUN client transaction instance.
+ * @param mod_count Boolean flag to indicate whether transmission count
+ * needs to be incremented.
*
* @return PJ_SUCCESS on success, or PJNATH_ESTUNDESTROYED
* when the user has destroyed the transaction in
* \a on_send_msg() callback, or any other error code
* as returned by \a on_send_msg() callback.
*/
-PJ_DECL(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx);
+PJ_DECL(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx,
+ pj_bool_t mod_count);
/**
diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h
index 0eeb6e3..eb6d16b 100644
--- a/pjnath/include/pjnath/turn_session.h
+++ b/pjnath/include/pjnath/turn_session.h
@@ -1,4 +1,4 @@
-/* $Id: turn_session.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: turn_session.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -417,7 +417,10 @@ PJ_DECL(const char*) pj_turn_state_name(pj_turn_state_t state);
* @param name Optional name to identify this session in the log.
* @param af Address family of the client connection. Currently
* pj_AF_INET() and pj_AF_INET6() are supported.
- * @param conn_type Connection type to the TURN server.
+ * @param conn_type Connection type to the TURN server.
+ * @param grp_lock Optional group lock object to be used by this session.
+ * If this value is NULL, the session will create
+ * a group lock internally.
* @param cb Callback to receive events from the TURN session.
* @param options Option flags, currently this value must be zero.
* @param user_data Arbitrary application data to be associated with
@@ -432,6 +435,7 @@ PJ_DECL(pj_status_t) pj_turn_session_create(const pj_stun_config *cfg,
const char *name,
int af,
pj_turn_tp_type conn_type,
+ pj_grp_lock_t *grp_lock,
const pj_turn_session_cb *cb,
unsigned options,
void *user_data,
diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h
index 1a75a64..b13057c 100644
--- a/pjnath/include/pjnath/turn_sock.h
+++ b/pjnath/include/pjnath/turn_sock.h
@@ -1,4 +1,4 @@
-/* $Id: turn_sock.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: turn_sock.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -109,6 +109,21 @@ typedef struct pj_turn_sock_cb
typedef struct pj_turn_sock_cfg
{
/**
+ * The group lock to be used by the STUN socket. If NULL, the STUN socket
+ * will create one internally.
+ *
+ * Default: NULL
+ */
+ pj_grp_lock_t *grp_lock;
+
+ /**
+ * Packet buffer size.
+ *
+ * Default value is PJ_TURN_MAX_PKT_LEN.
+ */
+ unsigned max_pkt_size;
+
+ /**
* 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 qos_param fields since this is more portable.
@@ -134,6 +149,23 @@ typedef struct pj_turn_sock_cfg
*/
pj_bool_t qos_ignore_error;
+ /**
+ * Specify the interface where the socket should be bound to. If the
+ * address is zero, socket will be bound to INADDR_ANY. If the address
+ * is non-zero, socket will be bound to this address only. If the port is
+ * set to zero, the socket will bind at any port (chosen by the OS).
+ */
+ pj_sockaddr bound_addr;
+
+ /**
+ * Specify the port range for TURN socket binding, relative to the start
+ * port number specified in \a bound_addr. Note that this setting is only
+ * applicable when the start port number is non zero.
+ *
+ * Default value is zero.
+ */
+ pj_uint16_t port_range;
+
} pj_turn_sock_cfg;
diff --git a/pjnath/src/pjnath-test/concur_test.c b/pjnath/src/pjnath-test/concur_test.c
new file mode 100644
index 0000000..bf54e94
--- /dev/null
+++ b/pjnath/src/pjnath-test/concur_test.c
@@ -0,0 +1,367 @@
+/* $Id: concur_test.c 4412 2013-03-05 03:12:32Z riza $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#if INCLUDE_CONCUR_TEST
+
+#define THIS_FILE "concur_test.c"
+
+/****************************************************************************/
+#define WORKER_THREAD_CNT 4
+#define SERVER_THREAD_CNT 4
+#define MAX_SOCK_CLIENTS 80
+
+struct stun_test_session
+{
+ pj_stun_config stun_cfg;
+
+ pj_lock_t *lock;
+
+ pj_thread_t *worker_threads[WORKER_THREAD_CNT];
+
+ pj_sock_t server_sock;
+ int server_port;
+ pj_thread_t *server_threads[SERVER_THREAD_CNT];
+ pj_event_t *server_event;
+
+ pj_bool_t thread_quit_flag;
+
+ /* Test parameters: */
+ struct {
+ int client_got_response;
+
+ pj_bool_t server_wait_for_event;
+ pj_bool_t server_drop_request;
+ int client_sleep_after_start;
+ int client_sleep_before_destroy;
+ } param;
+};
+
+static int server_thread_proc(void *p)
+{
+ struct stun_test_session *test_sess = (struct stun_test_session*)p;
+ pj_pool_t *pool;
+ pj_status_t status;
+
+ PJ_LOG(4,(THIS_FILE, "Server thread running"));
+
+ pool = pj_pool_create(test_sess->stun_cfg.pf, "server", 512, 512, NULL);
+
+ while (!test_sess->thread_quit_flag) {
+ pj_time_val timeout = {0, 10};
+ pj_fd_set_t rdset;
+ int n;
+
+ /* Serve client */
+ PJ_FD_ZERO(&rdset);
+ PJ_FD_SET(test_sess->server_sock, &rdset);
+ n = pj_sock_select(test_sess->server_sock+1, &rdset,
+ NULL, NULL, &timeout);
+ if (n==1 && PJ_FD_ISSET(test_sess->server_sock, &rdset)) {
+ pj_uint8_t pkt[512];
+ pj_ssize_t pkt_len;
+ pj_size_t res_len;
+ pj_sockaddr client_addr;
+ int addr_len;
+
+ pj_stun_msg *stun_req, *stun_res;
+
+ pj_pool_reset(pool);
+
+ /* Got query */
+ pkt_len = sizeof(pkt);
+ addr_len = sizeof(client_addr);
+ status = pj_sock_recvfrom(test_sess->server_sock, pkt, &pkt_len,
+ 0, &client_addr, &addr_len);
+ if (status != PJ_SUCCESS) {
+ continue;
+ }
+
+ status = pj_stun_msg_decode(pool, pkt, pkt_len,
+ PJ_STUN_IS_DATAGRAM,
+ &stun_req, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "STUN request decode error"));
+ continue;
+ }
+
+ status = pj_stun_msg_create_response(pool, stun_req,
+ PJ_STUN_SC_BAD_REQUEST, NULL,
+ &stun_res);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "STUN create response error"));
+ continue;
+ }
+
+ status = pj_stun_msg_encode(stun_res, pkt, sizeof(pkt), 0,
+ NULL, &res_len);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "STUN encode error"));
+ continue;
+ }
+
+ /* Ignore request */
+ if (test_sess->param.server_drop_request)
+ continue;
+
+ /* Wait for signal to continue */
+ if (test_sess->param.server_wait_for_event)
+ pj_event_wait(test_sess->server_event);
+
+ pkt_len = res_len;
+ pj_sock_sendto(test_sess->server_sock, pkt, &pkt_len, 0,
+ &client_addr, pj_sockaddr_get_len(&client_addr));
+ }
+ }
+
+ pj_pool_release(pool);
+
+ PJ_LOG(4,(THIS_FILE, "Server thread quitting"));
+ return 0;
+}
+
+static int worker_thread_proc(void *p)
+{
+ struct stun_test_session *test_sess = (struct stun_test_session*)p;
+
+ PJ_LOG(4,(THIS_FILE, "Worker thread running"));
+
+ while (!test_sess->thread_quit_flag) {
+ pj_time_val timeout = {0, 10};
+ pj_timer_heap_poll(test_sess->stun_cfg.timer_heap, NULL);
+ pj_ioqueue_poll(test_sess->stun_cfg.ioqueue, &timeout);
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Worker thread quitting"));
+ return 0;
+}
+
+static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
+ pj_stun_sock_op op,
+ pj_status_t status)
+{
+ struct stun_test_session *test_sess = (struct stun_test_session*)pj_stun_sock_get_user_data(stun_sock);
+
+ PJ_UNUSED_ARG(op);
+ PJ_UNUSED_ARG(status);
+
+ test_sess->param.client_got_response++;
+ return PJ_TRUE;
+}
+
+static int stun_destroy_test_session(struct stun_test_session *test_sess)
+{
+
+ unsigned i;
+ pj_stun_sock_cb stun_cb;
+ pj_status_t status;
+ pj_stun_sock *stun_sock[MAX_SOCK_CLIENTS];
+
+ pj_bzero(&stun_cb, sizeof(stun_cb));
+ stun_cb.on_status = &stun_sock_on_status;
+
+ pj_event_reset(test_sess->server_event);
+
+ /* Create all clients first */
+ for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
+ char name[10];
+ sprintf(name, "stun%02d", i);
+ status = pj_stun_sock_create(&test_sess->stun_cfg, name, pj_AF_INET(),
+ &stun_cb, NULL, test_sess,
+ &stun_sock[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error creating stun socket"));
+ return -10;
+ }
+ }
+
+ /* Start resolution */
+ for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
+ pj_str_t server_ip = pj_str("127.0.0.1");
+ status = pj_stun_sock_start(stun_sock[i], &server_ip,
+ (pj_uint16_t)test_sess->server_port, NULL);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error starting stun socket"));
+ return -20;
+ }
+ }
+
+ /* settle down */
+ pj_thread_sleep(test_sess->param.client_sleep_after_start);
+
+ /* Resume server threads */
+ pj_event_set(test_sess->server_event);
+
+ pj_thread_sleep(test_sess->param.client_sleep_before_destroy);
+
+ /* Destroy clients */
+ for (i=0; i<MAX_SOCK_CLIENTS; ++i) {
+ status = pj_stun_sock_destroy(stun_sock[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error destroying stun socket"));
+ }
+ }
+
+ return 0;
+}
+
+static int stun_destroy_test(void)
+{
+ enum { LOOP = 500 };
+ struct stun_test_session test_sess;
+ pj_sockaddr bind_addr;
+ int addr_len;
+ pj_caching_pool cp;
+ pj_pool_t *pool;
+ unsigned i;
+ pj_status_t status;
+ int rc = 0;
+
+ PJ_LOG(3,(THIS_FILE, " STUN destroy concurrency test"));
+
+ pj_bzero(&test_sess, sizeof(test_sess));
+
+ pj_caching_pool_init(&cp, NULL, 0);
+ pool = pj_pool_create(&cp.factory, "testsess", 512, 512, NULL);
+
+ pj_stun_config_init(&test_sess.stun_cfg, &cp.factory, 0, NULL, NULL);
+
+ status = pj_timer_heap_create(pool, 1023, &test_sess.stun_cfg.timer_heap);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pj_lock_create_recursive_mutex(pool, NULL, &test_sess.lock);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_timer_heap_set_lock(test_sess.stun_cfg.timer_heap, test_sess.lock, PJ_TRUE);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pj_ioqueue_create(pool, 512, &test_sess.stun_cfg.ioqueue);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &test_sess.server_sock);
+ pj_sockaddr_init(pj_AF_INET(), &bind_addr, NULL, 0);
+ status = pj_sock_bind(test_sess.server_sock, &bind_addr, pj_sockaddr_get_len(&bind_addr));
+ pj_assert(status == PJ_SUCCESS);
+
+ addr_len = sizeof(bind_addr);
+ status = pj_sock_getsockname(test_sess.server_sock, &bind_addr, &addr_len);
+ pj_assert(status == PJ_SUCCESS);
+
+ test_sess.server_port = pj_sockaddr_get_port(&bind_addr);
+
+ status = pj_event_create(pool, NULL, PJ_TRUE, PJ_FALSE, &test_sess.server_event);
+ pj_assert(status == PJ_SUCCESS);
+
+ for (i=0; i<SERVER_THREAD_CNT; ++i) {
+ status = pj_thread_create(pool, NULL,
+ &server_thread_proc, &test_sess,
+ 0, 0, &test_sess.server_threads[i]);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ for (i=0; i<WORKER_THREAD_CNT; ++i) {
+ status = pj_thread_create(pool, NULL,
+ &worker_thread_proc, &test_sess,
+ 0, 0, &test_sess.worker_threads[i]);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ /* Test 1: Main thread calls destroy while callback is processing response */
+ PJ_LOG(3,(THIS_FILE, " Destroy in main thread while callback is running"));
+ for (i=0; i<LOOP; ++i) {
+ int sleep = pj_rand() % 5;
+
+ PJ_LOG(3,(THIS_FILE, " Try %-3d of %d", i+1, LOOP));
+
+ /* Test 1: destroy at the same time when receiving response */
+ pj_bzero(&test_sess.param, sizeof(test_sess.param));
+ test_sess.param.client_sleep_after_start = 20;
+ test_sess.param.client_sleep_before_destroy = sleep;
+ test_sess.param.server_wait_for_event = PJ_TRUE;
+ stun_destroy_test_session(&test_sess);
+ PJ_LOG(3,(THIS_FILE,
+ " stun test a: sleep delay:%d: clients with response: %d",
+ sleep, test_sess.param.client_got_response));
+
+ /* Test 2: destroy at the same time with STUN retransmit timer */
+ test_sess.param.server_drop_request = PJ_TRUE;
+ test_sess.param.client_sleep_after_start = 0;
+ test_sess.param.client_sleep_before_destroy = PJ_STUN_RTO_VALUE;
+ test_sess.param.server_wait_for_event = PJ_FALSE;
+ stun_destroy_test_session(&test_sess);
+ PJ_LOG(3,(THIS_FILE, " stun test b: retransmit concurrency"));
+
+ /* Test 3: destroy at the same time with receiving response
+ * AND STUN retransmit timer */
+ test_sess.param.client_got_response = 0;
+ test_sess.param.server_drop_request = PJ_FALSE;
+ test_sess.param.client_sleep_after_start = PJ_STUN_RTO_VALUE;
+ test_sess.param.client_sleep_before_destroy = 0;
+ test_sess.param.server_wait_for_event = PJ_TRUE;
+ stun_destroy_test_session(&test_sess);
+ PJ_LOG(3,(THIS_FILE,
+ " stun test c: clients with response: %d",
+ test_sess.param.client_got_response));
+
+ pj_thread_sleep(10);
+
+ ice_one_conc_test(&test_sess.stun_cfg, PJ_FALSE);
+
+ pj_thread_sleep(10);
+ }
+
+ /* Avoid compiler warning */
+ goto on_return;
+
+
+on_return:
+ test_sess.thread_quit_flag = PJ_TRUE;
+
+ for (i=0; i<SERVER_THREAD_CNT; ++i) {
+ pj_thread_join(test_sess.server_threads[i]);
+ }
+
+ for (i=0; i<WORKER_THREAD_CNT; ++i) {
+ pj_thread_join(test_sess.worker_threads[i]);
+ }
+
+ pj_event_destroy(test_sess.server_event);
+ pj_sock_close(test_sess.server_sock);
+ pj_ioqueue_destroy(test_sess.stun_cfg.ioqueue);
+ pj_timer_heap_destroy(test_sess.stun_cfg.timer_heap);
+
+ pj_pool_release(pool);
+ pj_caching_pool_destroy(&cp);
+
+ PJ_LOG(3,(THIS_FILE, " Done. rc=%d", rc));
+ return rc;
+}
+
+
+int concur_test(void)
+{
+ int rc = 0;
+
+ rc += stun_destroy_test();
+
+ return 0;
+}
+
+#endif /* INCLUDE_CONCUR_TEST */
diff --git a/pjnath/src/pjnath-test/ice_test.c b/pjnath/src/pjnath-test/ice_test.c
index fe0ee8d..2b74811 100644
--- a/pjnath/src/pjnath-test/ice_test.c
+++ b/pjnath/src/pjnath-test/ice_test.c
@@ -1,4 +1,4 @@
-/* $Id: ice_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: ice_test.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -29,7 +29,9 @@ enum
#define NODELAY 0xFFFFFFFF
#define SRV_DOMAIN "pjsip.lab.domain"
+#define MAX_THREADS 16
+#define THIS_FILE "ice_test.c"
#define INDENT " "
/* Client flags */
@@ -48,7 +50,7 @@ struct test_result
unsigned rx_cnt[4]; /* Number of data received */
};
-
+/* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
/* Test session configuration */
struct test_cfg
{
@@ -60,8 +62,8 @@ struct test_cfg
unsigned client_flag; /* Client flags */
unsigned answer_delay; /* Delay before sending SDP */
- unsigned send_delay; /* Delay before sending data */
- unsigned destroy_delay; /* Delay before destroy() */
+ unsigned send_delay; /* unused */
+ unsigned destroy_delay; /* unused */
struct test_result expected;/* Expected result */
@@ -79,6 +81,17 @@ struct ice_ept
pj_str_t pass; /* password */
};
+/* Session param */
+struct sess_param
+{
+ unsigned worker_cnt;
+ unsigned worker_timeout;
+ pj_bool_t worker_quit;
+
+ pj_bool_t destroy_after_create;
+ pj_bool_t destroy_after_one_done;
+};
+
/* The test session */
struct test_sess
{
@@ -86,8 +99,12 @@ struct test_sess
pj_stun_config *stun_cfg;
pj_dns_resolver *resolver;
+ struct sess_param *param;
+
test_server *server;
+ pj_thread_t *worker_threads[MAX_THREADS];
+
unsigned server_flag;
struct ice_ept caller;
struct ice_ept callee;
@@ -190,6 +207,7 @@ static int create_sess(pj_stun_config *stun_cfg,
unsigned server_flag,
struct test_cfg *caller_cfg,
struct test_cfg *callee_cfg,
+ struct sess_param *test_param,
struct test_sess **p_sess)
{
pj_pool_t *pool;
@@ -204,6 +222,7 @@ static int create_sess(pj_stun_config *stun_cfg,
sess = PJ_POOL_ZALLOC_T(pool, struct test_sess);
sess->pool = pool;
sess->stun_cfg = stun_cfg;
+ sess->param = test_param;
pj_memcpy(&sess->caller.cfg, caller_cfg, sizeof(*caller_cfg));
sess->caller.result.init_status = sess->caller.result.nego_status = PJ_EPENDING;
@@ -261,6 +280,8 @@ static int create_sess(pj_stun_config *stun_cfg,
/* Destroy test session */
static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
{
+ unsigned i;
+
if (sess->caller.ice) {
pj_ice_strans_destroy(sess->caller.ice);
sess->caller.ice = NULL;
@@ -271,6 +292,12 @@ static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
sess->callee.ice = NULL;
}
+ sess->param->worker_quit = PJ_TRUE;
+ for (i=0; i<sess->param->worker_cnt; ++i) {
+ if (sess->worker_threads[i])
+ pj_thread_join(sess->worker_threads[i]);
+ }
+
poll_events(sess->stun_cfg, wait_msec, PJ_FALSE);
if (sess->resolver) {
@@ -326,6 +353,9 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
case PJ_ICE_STRANS_OP_NEGOTIATION:
ept->result.nego_status = status;
break;
+ case PJ_ICE_STRANS_OP_KEEP_ALIVE:
+ /* keep alive failed? */
+ break;
default:
pj_assert(!"Unknown op");
}
@@ -384,20 +414,20 @@ static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1);
if (c1 == NULL) {
- PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice1 "
+ PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice1 "
"component %d", i+1));
return start_err - 2;
}
c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1);
if (c2 == NULL) {
- PJ_LOG(3,("", INDENT "err: unable to get valid pair for ice2 "
+ PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice2 "
"component %d", i+1));
return start_err - 4;
}
if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) {
- PJ_LOG(3,("", INDENT "err: candidate pair does not match "
+ PJ_LOG(3,(THIS_FILE, INDENT "err: candidate pair does not match "
"for component %d", i+1));
return start_err - 6;
}
@@ -408,14 +438,14 @@ static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
if (ept1->cfg.comp_cnt>i &&
pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL)
{
- PJ_LOG(3,("", INDENT "err: ice1 shouldn't have valid pair "
+ PJ_LOG(3,(THIS_FILE, INDENT "err: ice1 shouldn't have valid pair "
"for component %d", i+1));
return start_err - 8;
}
if (ept2->cfg.comp_cnt>i &&
pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL)
{
- PJ_LOG(3,("", INDENT "err: ice2 shouldn't have valid pair "
+ PJ_LOG(3,(THIS_FILE, INDENT "err: ice2 shouldn't have valid pair "
"for component %d", i+1));
return start_err - 9;
}
@@ -436,26 +466,44 @@ static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
rc = PJ_SUCCESS; \
break; \
} \
- if (t.sec - t0.sec > (timeout)) break; \
+ PJ_TIME_VAL_SUB(t, t0); \
+ if ((unsigned)PJ_TIME_VAL_MSEC(t) >= (timeout)) \
+ break; \
} \
}
+int worker_thread_proc(void *data)
+{
+ pj_status_t rc;
+ struct test_sess *sess = (struct test_sess *) data;
+ pj_stun_config *stun_cfg = sess->stun_cfg;
+
+ /* Wait until negotiation is complete on both endpoints */
+#define ALL_DONE (sess->param->worker_quit || \
+ (sess->caller.result.nego_status!=PJ_EPENDING && \
+ sess->callee.result.nego_status!=PJ_EPENDING))
+ WAIT_UNTIL(sess->param->worker_timeout, ALL_DONE, rc);
+
+ return 0;
+}
-static int perform_test(const char *title,
- pj_stun_config *stun_cfg,
- unsigned server_flag,
- struct test_cfg *caller_cfg,
- struct test_cfg *callee_cfg)
+static int perform_test2(const char *title,
+ pj_stun_config *stun_cfg,
+ unsigned server_flag,
+ struct test_cfg *caller_cfg,
+ struct test_cfg *callee_cfg,
+ struct sess_param *test_param)
{
pjlib_state pjlib_state;
struct test_sess *sess;
+ unsigned i;
int rc;
- PJ_LOG(3,("", INDENT "%s", title));
+ PJ_LOG(3,(THIS_FILE, INDENT "%s", title));
capture_pjlib_state(stun_cfg, &pjlib_state);
- rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, &sess);
+ rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, test_param, &sess);
if (rc != 0)
return rc;
@@ -463,10 +511,10 @@ static int perform_test(const char *title,
sess->callee.result.init_status!=PJ_EPENDING)
/* Wait until both ICE transports are initialized */
- WAIT_UNTIL(30, ALL_READY, rc);
+ WAIT_UNTIL(30000, ALL_READY, rc);
if (!ALL_READY) {
- PJ_LOG(3,("", INDENT "err: init timed-out"));
+ PJ_LOG(3,(THIS_FILE, INDENT "err: init timed-out"));
destroy_sess(sess, 500);
return -100;
}
@@ -489,7 +537,6 @@ static int perform_test(const char *title,
rc = 0;
goto on_return;
}
-
/* Init ICE on caller */
rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role,
&sess->caller.ufrag, &sess->caller.pass);
@@ -507,17 +554,14 @@ static int perform_test(const char *title,
destroy_sess(sess, 500);
return -110;
}
-
/* Start ICE on callee */
rc = start_ice(&sess->callee, &sess->caller);
if (rc != PJ_SUCCESS) {
destroy_sess(sess, 500);
return -120;
}
-
/* Wait for callee's answer_delay */
poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE);
-
/* Start ICE on caller */
rc = start_ice(&sess->caller, &sess->callee);
if (rc != PJ_SUCCESS) {
@@ -525,13 +569,37 @@ static int perform_test(const char *title,
return -130;
}
- /* Wait until negotiation is complete on both endpoints */
-#define ALL_DONE (sess->caller.result.nego_status!=PJ_EPENDING && \
- sess->callee.result.nego_status!=PJ_EPENDING)
- WAIT_UNTIL(30, ALL_DONE, rc);
+ for (i=0; i<sess->param->worker_cnt; ++i) {
+ pj_status_t status;
+ status = pj_thread_create(sess->pool, "worker_thread",
+ worker_thread_proc, sess, 0, 0,
+ &sess->worker_threads[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3,(THIS_FILE, INDENT "err: create thread"));
+ destroy_sess(sess, 500);
+ return -135;
+ }
+ }
+
+ if (sess->param->destroy_after_create)
+ goto on_destroy;
+
+ if (sess->param->destroy_after_one_done) {
+ while (sess->caller.result.init_status==PJ_EPENDING &&
+ sess->callee.result.init_status==PJ_EPENDING)
+ {
+ if (sess->param->worker_cnt)
+ pj_thread_sleep(0);
+ else
+ poll_events(stun_cfg, 0, PJ_FALSE);
+ }
+ goto on_destroy;
+ }
+
+ WAIT_UNTIL(30000, ALL_DONE, rc);
if (!ALL_DONE) {
- PJ_LOG(3,("", INDENT "err: negotiation timed-out"));
+ PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out"));
destroy_sess(sess, 500);
return -140;
}
@@ -561,6 +629,7 @@ static int perform_test(const char *title,
}
/* Looks like everything is okay */
+on_destroy:
/* Destroy ICE stream transports first to let it de-allocate
* TURN relay (otherwise there'll be timer/memory leak, unless
@@ -578,7 +647,7 @@ static int perform_test(const char *title,
on_return:
/* Wait.. */
- poll_events(stun_cfg, 500, PJ_FALSE);
+ poll_events(stun_cfg, 200, PJ_FALSE);
/* Now destroy everything */
destroy_sess(sess, 500);
@@ -591,7 +660,20 @@ on_return:
return rc;
}
- return 0;
+ return rc;
+}
+
+static int perform_test(const char *title,
+ pj_stun_config *stun_cfg,
+ unsigned server_flag,
+ struct test_cfg *caller_cfg,
+ struct test_cfg *callee_cfg)
+{
+ struct sess_param test_param;
+
+ pj_bzero(&test_param, sizeof(test_param));
+ return perform_test2(title, stun_cfg, server_flag, caller_cfg,
+ callee_cfg, &test_param);
}
#define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED
@@ -680,7 +762,7 @@ int ice_test(void)
if (rc != 0)
goto on_return;
}
-
+
/* Simple test first with srflx candidate */
if (1) {
struct sess_cfg_t cfg =
@@ -744,7 +826,7 @@ int ice_test(void)
{ROLE2, 2, NO, YES, NO, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}}
};
- rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
+ rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
&cfg.ua1, &cfg.ua2);
if (rc != 0)
goto on_return;
@@ -785,6 +867,7 @@ int ice_test(void)
goto on_return;
}
+
/* STUN failure, testing TURN deallocation */
if (1) {
struct sess_cfg_t cfg =
@@ -792,11 +875,11 @@ int ice_test(void)
"STUN failure, testing TURN deallocation",
0xFFFF & (~(CREATE_STUN_SERVER)),
/* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
- {ROLE1, 2, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}},
- {ROLE2, 2, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}}
+ {ROLE1, 1, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}},
+ {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJNATH_ESTUNTIMEDOUT, -1}}
};
- rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
+ rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
&cfg.ua1, &cfg.ua2);
if (rc != 0)
goto on_return;
@@ -818,7 +901,7 @@ int ice_test(void)
unsigned delay[] = { 50, 2000 };
unsigned d;
- PJ_LOG(3,("", " %s", cfg->title));
+ PJ_LOG(3,(THIS_FILE, " %s", cfg->title));
/* For each test item, test with various answer delay */
for (d=0; d<PJ_ARRAY_SIZE(delay); ++d) {
@@ -876,3 +959,89 @@ on_return:
return rc;
}
+int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit)
+{
+ struct sess_cfg_t {
+ const char *title;
+ unsigned server_flag;
+ struct test_cfg ua1;
+ struct test_cfg ua2;
+ } cfg =
+ {
+ "Concurrency test",
+ 0xFFFF,
+ /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
+ {ROLE1, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}},
+ {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS}}
+ };
+ struct sess_param test_param;
+ int rc;
+
+
+ /* test a: destroy as soon as nego starts */
+ cfg.title = " ice test a: immediate destroy";
+ pj_bzero(&test_param, sizeof(test_param));
+ test_param.worker_cnt = 4;
+ test_param.worker_timeout = 1000;
+ test_param.destroy_after_create = PJ_TRUE;
+
+ rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
+ &cfg.ua1, &cfg.ua2, &test_param);
+ if (rc != 0 && err_quit)
+ return rc;
+
+ /* test b: destroy as soon as one is done */
+ cfg.title = " ice test b: destroy after 1 success";
+ test_param.destroy_after_create = PJ_FALSE;
+ test_param.destroy_after_one_done = PJ_TRUE;
+
+ rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
+ &cfg.ua1, &cfg.ua2, &test_param);
+ if (rc != 0 && err_quit)
+ return rc;
+
+ /* test c: normal */
+ cfg.title = " ice test c: normal flow";
+ pj_bzero(&test_param, sizeof(test_param));
+ test_param.worker_cnt = 4;
+ test_param.worker_timeout = 1000;
+
+ rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
+ &cfg.ua1, &cfg.ua2, &test_param);
+ if (rc != 0 && err_quit)
+ return rc;
+
+ return 0;
+}
+
+int ice_conc_test(void)
+{
+ const unsigned LOOP = 100;
+ pj_pool_t *pool;
+ pj_stun_config stun_cfg;
+ unsigned i;
+ int rc;
+
+ pool = pj_pool_create(mem, NULL, 512, 512, NULL);
+ rc = create_stun_config(pool, &stun_cfg);
+ if (rc != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return -7;
+ }
+
+ for (i = 0; i < LOOP; i++) {
+ PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP));
+ rc = ice_one_conc_test(&stun_cfg, PJ_TRUE);
+ if (rc)
+ break;
+ }
+
+ /* Avoid compiler warning */
+ goto on_return;
+
+on_return:
+ destroy_stun_config(&stun_cfg);
+ pj_pool_release(pool);
+
+ return rc;
+}
diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c
index 05a6209..2364260 100644
--- a/pjnath/src/pjnath-test/sess_auth.c
+++ b/pjnath/src/pjnath-test/sess_auth.c
@@ -1,4 +1,4 @@
-/* $Id: sess_auth.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sess_auth.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -247,7 +247,7 @@ static int create_std_server(pj_stun_auth_type auth_type,
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_rx_request = &server_on_rx_request;
sess_cb.on_send_msg = &server_send_msg;
- status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, &server->sess);
+ status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, NULL, &server->sess);
if (status != PJ_SUCCESS) {
destroy_server();
return -10;
@@ -479,7 +479,7 @@ static int run_client_test(const char *title,
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_request_complete = &client_on_request_complete;
sess_cb.on_send_msg = &client_send_msg;
- status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, &client->sess);
+ status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, NULL, &client->sess);
if (status != PJ_SUCCESS) {
destroy_client_server();
return -200;
diff --git a/pjnath/src/pjnath-test/stun_sock_test.c b/pjnath/src/pjnath-test/stun_sock_test.c
index 7a309ea..3c3cecc 100644
--- a/pjnath/src/pjnath-test/stun_sock_test.c
+++ b/pjnath/src/pjnath-test/stun_sock_test.c
@@ -1,4 +1,4 @@
-/* $Id: stun_sock_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: stun_sock_test.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -298,7 +298,7 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
struct stun_client *client;
pj_str_t srv_addr;
pj_time_val timeout, t;
- int ret = 0;
+ int i, ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " timeout test [%d]", destroy_on_err));
@@ -359,6 +359,8 @@ static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
on_return:
destroy_server(srv);
destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
return ret;
}
@@ -373,7 +375,7 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
struct stun_client *client;
pj_str_t srv_addr;
pj_time_val timeout, t;
- int ret = 0;
+ int i, ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " missing attribute test [%d]", destroy_on_err));
@@ -426,6 +428,8 @@ static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
on_return:
destroy_server(srv);
destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
return ret;
}
@@ -440,7 +444,7 @@ static int keep_alive_test(pj_stun_config *cfg)
pj_stun_sock_info info;
pj_str_t srv_addr;
pj_time_val timeout, t;
- int ret = 0;
+ int i, ret = 0;
pj_status_t status;
PJ_LOG(3,(THIS_FILE, " normal operation"));
@@ -791,6 +795,8 @@ static int keep_alive_test(pj_stun_config *cfg)
on_return:
destroy_server(srv);
destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
return ret;
}
diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c
index 081df25..5277585 100644
--- a/pjnath/src/pjnath-test/test.c
+++ b/pjnath/src/pjnath-test/test.c
@@ -1,4 +1,4 @@
-/* $Id: test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: test.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -34,6 +34,7 @@ pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg)
{
pj_ioqueue_t *ioqueue;
pj_timer_heap_t *timer_heap;
+ pj_lock_t *lock;
pj_status_t status;
status = pj_ioqueue_create(pool, 64, &ioqueue);
@@ -49,6 +50,9 @@ pj_status_t create_stun_config(pj_pool_t *pool, pj_stun_config *stun_cfg)
return status;
}
+ pj_lock_create_recursive_mutex(pool, NULL, &lock);
+ pj_timer_heap_set_lock(timer_heap, lock, PJ_TRUE);
+
pj_stun_config_init(stun_cfg, mem, 0, ioqueue, timer_heap);
return PJ_SUCCESS;
@@ -105,7 +109,7 @@ void capture_pjlib_state(pj_stun_config *cfg, struct pjlib_state *st)
st->timer_cnt = pj_timer_heap_count(cfg->timer_heap);
- cp = (pj_caching_pool*)mem;
+ cp = (pj_caching_pool*)cfg->pf;
st->pool_used_cnt = cp->used_count;
}
@@ -120,6 +124,10 @@ int check_pjlib_state(pj_stun_config *cfg,
if (current_state.timer_cnt > initial_st->timer_cnt) {
PJ_LOG(3,("", " error: possibly leaking timer"));
rc |= ERR_TIMER_LEAK;
+
+#if PJ_TIMER_DEBUG
+ pj_timer_heap_dump(cfg->timer_heap);
+#endif
}
if (current_state.pool_used_cnt > initial_st->pool_used_cnt) {
@@ -148,6 +156,18 @@ pj_pool_factory *mem;
int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
PJ_LOG_HAS_MICRO_SEC;
+pj_log_func *orig_log_func;
+FILE *log_file;
+
+static void test_log_func(int level, const char *data, int len)
+{
+ if (log_file) {
+ fwrite(data, len, 1, log_file);
+ }
+ if (level <= 3)
+ orig_log_func(level, data, len);
+}
+
static int test_inner(void)
{
pj_caching_pool caching_pool;
@@ -158,6 +178,11 @@ static int test_inner(void)
#if 1
pj_log_set_level(3);
pj_log_set_decor(param_log_decor);
+#elif 1
+ log_file = fopen("pjnath-test.log", "wt");
+ pj_log_set_level(5);
+ orig_log_func = pj_log_get_log_func();
+ pj_log_set_log_func(&test_log_func);
#endif
rc = pj_init();
@@ -189,7 +214,13 @@ static int test_inner(void)
DO_TEST(turn_sock_test());
#endif
+#if INCLUDE_CONCUR_TEST
+ DO_TEST(concur_test());
+#endif
+
on_return:
+ if (log_file)
+ fclose(log_file);
return rc;
}
diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h
index bbba992..504f2f7 100644
--- a/pjnath/src/pjnath-test/test.h
+++ b/pjnath/src/pjnath-test/test.h
@@ -1,4 +1,4 @@
-/* $Id: test.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: test.h 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -25,17 +25,21 @@
#define INCLUDE_ICE_TEST 1
#define INCLUDE_STUN_SOCK_TEST 1
#define INCLUDE_TURN_SOCK_TEST 1
+#define INCLUDE_CONCUR_TEST 1
int stun_test(void);
int sess_auth_test(void);
int stun_sock_test(void);
int turn_sock_test(void);
int ice_test(void);
+int concur_test(void);
int test_main(void);
extern void app_perror(const char *title, pj_status_t rc);
extern pj_pool_factory *mem;
+int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit);
+
////////////////////////////////////
/*
* Utilities
diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
index 05f39bc..30a40f6 100644
--- a/pjnath/src/pjnath/ice_session.c
+++ b/pjnath/src/pjnath/ice_session.c
@@ -1,4 +1,4 @@
-/* $Id: ice_session.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: ice_session.c 4365 2013-02-21 18:06:51Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -97,6 +97,7 @@ static pj_uint8_t cand_type_prefs[4] =
#endif
};
+#define THIS_FILE "ice_session.c"
#define CHECK_NAME_LEN 128
#define LOG4(expr) PJ_LOG(4,expr)
#define LOG5(expr) PJ_LOG(4,expr)
@@ -134,6 +135,7 @@ typedef struct timer_data
static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te);
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now);
+static void ice_on_destroy(void *obj);
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason);
static pj_status_t start_periodic_check(pj_timer_heap_t *th,
@@ -288,6 +290,7 @@ static pj_status_t init_comp(pj_ice_sess *ice,
/* Create STUN session for this candidate */
status = pj_stun_session_create(&ice->stun_cfg, NULL,
&sess_cb, PJ_TRUE,
+ ice->grp_lock,
&comp->stun_sess);
if (status != PJ_SUCCESS)
return status;
@@ -332,6 +335,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
const pj_ice_sess_cb *cb,
const pj_str_t *local_ufrag,
const pj_str_t *local_passwd,
+ pj_grp_lock_t *grp_lock,
pj_ice_sess **p_ice)
{
pj_pool_t *pool;
@@ -359,13 +363,20 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
name, ice);
- status = pj_mutex_create_recursive(pool, ice->obj_name,
- &ice->mutex);
- if (status != PJ_SUCCESS) {
- destroy_ice(ice, status);
- return status;
+ if (grp_lock) {
+ ice->grp_lock = grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &ice->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
}
+ pj_grp_lock_add_ref(ice->grp_lock);
+ pj_grp_lock_add_handler(ice->grp_lock, pool, ice,
+ &ice_on_destroy);
+
pj_memcpy(&ice->cb, cb, sizeof(*cb));
pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg));
@@ -444,6 +455,21 @@ PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
/*
+ * Callback to really destroy the session
+ */
+static void ice_on_destroy(void *obj)
+{
+ pj_ice_sess *ice = (pj_ice_sess*) obj;
+
+ if (ice->pool) {
+ pj_pool_t *pool = ice->pool;
+ ice->pool = NULL;
+ pj_pool_release(pool);
+ }
+ LOG4((THIS_FILE, "ICE session %p destroyed", ice));
+}
+
+/*
* Destroy
*/
static void destroy_ice(pj_ice_sess *ice,
@@ -452,21 +478,21 @@ static void destroy_ice(pj_ice_sess *ice,
unsigned i;
if (reason == PJ_SUCCESS) {
- LOG4((ice->obj_name, "Destroying ICE session"));
+ LOG4((ice->obj_name, "Destroying ICE session %p", ice));
}
- /* Let other callbacks finish */
- if (ice->mutex) {
- pj_mutex_lock(ice->mutex);
- pj_mutex_unlock(ice->mutex);
- }
+ pj_grp_lock_acquire(ice->grp_lock);
- if (ice->timer.id) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap,
- &ice->timer);
- ice->timer.id = PJ_FALSE;
+ if (ice->is_destroying) {
+ pj_grp_lock_release(ice->grp_lock);
+ return;
}
+ ice->is_destroying = PJ_TRUE;
+
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+ &ice->timer, PJ_FALSE);
+
for (i=0; i<ice->comp_cnt; ++i) {
if (ice->comp[i].stun_sess) {
pj_stun_session_destroy(ice->comp[i].stun_sess);
@@ -474,21 +500,12 @@ static void destroy_ice(pj_ice_sess *ice,
}
}
- if (ice->clist.timer.id) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
- ice->clist.timer.id = PJ_FALSE;
- }
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+ &ice->clist.timer,
+ PJ_FALSE);
- if (ice->mutex) {
- pj_mutex_destroy(ice->mutex);
- ice->mutex = NULL;
- }
-
- if (ice->pool) {
- pj_pool_t *pool = ice->pool;
- ice->pool = NULL;
- pj_pool_release(pool);
- }
+ pj_grp_lock_dec_ref(ice->grp_lock);
+ pj_grp_lock_release(ice->grp_lock);
}
@@ -701,13 +718,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
{
pj_ice_sess_cand *lcand;
pj_status_t status = PJ_SUCCESS;
+ char address[PJ_INET6_ADDRSTRLEN];
PJ_ASSERT_RETURN(ice && comp_id &&
foundation && addr && base_addr && addr_len,
PJ_EINVAL);
PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
status = PJ_ETOOMANY;
@@ -720,13 +738,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
lcand->type = type;
pj_strdup(ice->pool, &lcand->foundation, foundation);
lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id);
- pj_memcpy(&lcand->addr, addr, addr_len);
- pj_memcpy(&lcand->base_addr, base_addr, addr_len);
+ pj_sockaddr_cp(&lcand->addr, addr);
+ pj_sockaddr_cp(&lcand->base_addr, base_addr);
if (rel_addr == NULL)
rel_addr = base_addr;
pj_memcpy(&lcand->rel_addr, rel_addr, addr_len);
- pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
+ pj_ansi_strcpy(ice->tmp.txt, pj_sockaddr_print(&lcand->addr, address,
+ sizeof(address), 0));
LOG4((ice->obj_name,
"Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, "
"addr=%s:%d, base=%s:%d, prio=0x%x (%u)",
@@ -736,9 +755,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
(int)lcand->foundation.slen,
lcand->foundation.ptr,
ice->tmp.txt,
- (int)pj_ntohs(lcand->addr.ipv4.sin_port),
- pj_inet_ntoa(lcand->base_addr.ipv4.sin_addr),
- (int)pj_htons(lcand->base_addr.ipv4.sin_port),
+ pj_sockaddr_get_port(&lcand->addr),
+ pj_sockaddr_print(&lcand->base_addr, address, sizeof(address), 0),
+ pj_sockaddr_get_port(&lcand->base_addr),
lcand->prio, lcand->prio));
if (p_cand_id)
@@ -747,7 +766,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
++ice->lcand_cnt;
on_error:
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return status;
}
@@ -764,7 +783,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
*cand_id = -1;
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
/* First find in valid list if we have nominated pair */
for (i=0; i<ice->valid_list.count; ++i) {
@@ -772,7 +791,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
if (check->lcand->comp_id == comp_id) {
*cand_id = GET_LCAND_ID(check->lcand);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
}
@@ -784,7 +803,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
lcand->type == PJ_ICE_CAND_TYPE_RELAYED)
{
*cand_id = GET_LCAND_ID(lcand);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
}
@@ -797,7 +816,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
lcand->type == PJ_ICE_CAND_TYPE_PRFLX))
{
*cand_id = GET_LCAND_ID(lcand);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
}
@@ -809,13 +828,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
lcand->type == PJ_ICE_CAND_TYPE_HOST)
{
*cand_id = GET_LCAND_ID(lcand);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
}
/* Still no candidate is found! :( */
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_assert(!"Should have a candidate by now");
return PJ_EBUG;
@@ -875,25 +894,24 @@ static const char *dump_check(char *buffer, unsigned bufsize,
{
const pj_ice_sess_cand *lcand = check->lcand;
const pj_ice_sess_cand *rcand = check->rcand;
- char laddr[PJ_INET6_ADDRSTRLEN];
+ char laddr[PJ_INET6_ADDRSTRLEN], raddr[PJ_INET6_ADDRSTRLEN];
int len;
PJ_CHECK_STACK();
- pj_ansi_strcpy(laddr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
-
- if (lcand->addr.addr.sa_family == pj_AF_INET()) {
- len = pj_ansi_snprintf(buffer, bufsize,
- "%d: [%d] %s:%d-->%s:%d",
- (int)GET_CHECK_ID(clist, check),
- check->lcand->comp_id,
- laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port),
- pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
- (int)pj_ntohs(rcand->addr.ipv4.sin_port));
- } else {
- len = pj_ansi_snprintf(buffer, bufsize, "IPv6->IPv6");
- }
+ pj_ansi_strcpy(laddr, pj_sockaddr_print(&lcand->addr, laddr,
+ sizeof(laddr), 0));
+ len = pj_ansi_snprintf(buffer, bufsize,
+ "%d: [%d] %s:%d-->%s:%d",
+ (int)GET_CHECK_ID(clist, check),
+ check->lcand->comp_id,
+ pj_sockaddr_print(&lcand->addr, laddr,
+ sizeof(laddr), 0),
+ pj_sockaddr_get_port(&lcand->addr),
+ pj_sockaddr_print(&rcand->addr, raddr,
+ sizeof(raddr), 0),
+ pj_sockaddr_get_port(&rcand->addr));
if (len < 0)
len = 0;
@@ -964,6 +982,7 @@ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist)
}
}
+ pj_assert(clist->count > 0);
for (i=0; i<clist->count-1; ++i) {
unsigned j, highest = i;
@@ -996,32 +1015,6 @@ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist)
}
}
-enum
-{
- SOCKADDR_EQUAL = 0,
- SOCKADDR_NOT_EQUAL = 1
-};
-
-/* Utility: compare sockaddr.
- * Returns 0 if equal.
- */
-static int sockaddr_cmp(const pj_sockaddr *a1, const pj_sockaddr *a2)
-{
- if (a1->addr.sa_family != a2->addr.sa_family)
- return SOCKADDR_NOT_EQUAL;
-
- if (a1->addr.sa_family == pj_AF_INET()) {
- return !(a1->ipv4.sin_addr.s_addr == a2->ipv4.sin_addr.s_addr &&
- a1->ipv4.sin_port == a2->ipv4.sin_port);
- } else if (a1->addr.sa_family == pj_AF_INET6()) {
- return pj_memcmp(&a1->ipv6, &a2->ipv6, sizeof(a1->ipv6));
- } else {
- pj_assert(!"Invalid address family!");
- return SOCKADDR_NOT_EQUAL;
- }
-}
-
-
/* Prune checklist, this must have been done after the checklist
* is sorted.
*/
@@ -1053,7 +1046,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
if (host->type != PJ_ICE_CAND_TYPE_HOST)
continue;
- if (sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) {
+ if (pj_sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) {
/* Replace this SRFLX with its BASE */
clist->checks[i].lcand = host;
break;
@@ -1061,11 +1054,13 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
}
if (j==ice->lcand_cnt) {
+ char baddr[PJ_INET6_ADDRSTRLEN];
/* Host candidate not found this this srflx! */
LOG4((ice->obj_name,
"Base candidate %s:%d not found for srflx candidate %d",
- pj_inet_ntoa(srflx->base_addr.ipv4.sin_addr),
- pj_ntohs(srflx->base_addr.ipv4.sin_port),
+ pj_sockaddr_print(&srflx->base_addr, baddr,
+ sizeof(baddr), 0),
+ pj_sockaddr_get_port(&srflx->base_addr),
GET_LCAND_ID(clist->checks[i].lcand)));
return PJNATH_EICENOHOSTCAND;
}
@@ -1093,7 +1088,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
if ((licand == ljcand) && (ricand == rjcand)) {
reason = "duplicate found";
} else if ((rjcand == ricand) &&
- (sockaddr_cmp(&ljcand->base_addr,
+ (pj_sockaddr_cmp(&ljcand->base_addr,
&licand->base_addr)==0))
{
reason = "equal base";
@@ -1124,14 +1119,20 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
{
pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
enum timer_type type = (enum timer_type)te->id;
- pj_bool_t has_mutex = PJ_TRUE;
PJ_UNUSED_ARG(th);
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
te->id = TIMER_NONE;
+ if (ice->is_destroying) {
+ /* Stray timer, could happen when destroy is invoked while callback
+ * is pending. */
+ pj_grp_lock_release(ice->grp_lock);
+ return;
+ }
+
switch (type) {
case TIMER_CONTROLLED_WAIT_NOM:
LOG4((ice->obj_name,
@@ -1154,8 +1155,6 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
/* Release mutex in case app destroy us in the callback */
ice_status = ice->ice_status;
on_ice_complete = ice->cb.on_ice_complete;
- has_mutex = PJ_FALSE;
- pj_mutex_unlock(ice->mutex);
/* Notify app about ICE completion*/
if (on_ice_complete)
@@ -1173,8 +1172,7 @@ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
break;
}
- if (has_mutex)
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
}
/* Send keep-alive */
@@ -1232,8 +1230,10 @@ done:
ice->comp_cnt;
pj_time_val_normalize(&delay);
- ice->timer.id = TIMER_KEEP_ALIVE;
- pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_KEEP_ALIVE,
+ ice->grp_lock);
} else {
pj_assert(!"Not expected any timer active");
@@ -1247,10 +1247,8 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
ice->is_complete = PJ_TRUE;
ice->ice_status = status;
- if (ice->timer.id != TIMER_NONE) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
- ice->timer.id = TIMER_NONE;
- }
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+ TIMER_NONE);
/* Log message */
LOG4((ice->obj_name, "ICE process complete, status=%s",
@@ -1263,9 +1261,10 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
if (ice->cb.on_ice_complete) {
pj_time_val delay = {0, 0};
- ice->timer.id = TIMER_COMPLETION_CALLBACK;
- pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
- &ice->timer, &delay);
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_COMPLETION_CALLBACK,
+ ice->grp_lock);
}
}
}
@@ -1493,10 +1492,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
delay.msec = ice->opt.controlled_agent_want_nom_timeout;
pj_time_val_normalize(&delay);
- ice->timer.id = TIMER_CONTROLLED_WAIT_NOM;
- pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
- &ice->timer,
- &delay);
+ pj_timer_heap_schedule_w_grp_lock(
+ ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_CONTROLLED_WAIT_NOM,
+ ice->grp_lock);
LOG5((ice->obj_name,
"All checks have completed. Controlled agent now "
@@ -1572,10 +1572,8 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
"Scheduling nominated check in %d ms",
ice->opt.nominated_check_delay));
- if (ice->timer.id != TIMER_NONE) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
- ice->timer.id = TIMER_NONE;
- }
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+ TIMER_NONE);
/* All components have valid pair. Let connectivity checks run for
* a little bit more time, then start our nominated check.
@@ -1584,8 +1582,10 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
delay.msec = ice->opt.nominated_check_delay;
pj_time_val_normalize(&delay);
- ice->timer.id = TIMER_START_NOMINATED_CHECK;
- pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_START_NOMINATED_CHECK,
+ ice->grp_lock);
return PJ_FALSE;
}
@@ -1615,7 +1615,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND,
PJ_ETOOMANY);
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
/* Save credentials */
username.ptr = buf;
@@ -1663,7 +1663,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
pj_ice_sess_check *chk = &clist->checks[clist->count];
if (clist->count >= PJ_ICE_MAX_CHECKS) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_ETOOMANY;
}
@@ -1688,13 +1688,20 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
}
}
+ /* This could happen if candidates have no matching address families */
+ if (clist->count == 0) {
+ LOG4((ice->obj_name, "Error: no checklist can be created"));
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_ENOTFOUND;
+ }
+
/* Sort checklist based on priority */
sort_checklist(ice, clist);
/* Prune the checklist */
status = prune_checklist(ice, clist);
if (status != PJ_SUCCESS) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return status;
}
@@ -1721,7 +1728,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
/* Log checklist */
dump_checklist("Checklist created:", ice, clist);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
@@ -1809,7 +1816,8 @@ static pj_status_t perform_check(pj_ice_sess *ice,
/* Initiate STUN transaction to send the request */
status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE,
PJ_TRUE, &rcand->addr,
- sizeof(pj_sockaddr_in), check->tdata);
+ pj_sockaddr_get_len(&rcand->addr),
+ check->tdata);
if (status != PJ_SUCCESS) {
check->tdata = NULL;
pjnath_perror(ice->obj_name, "Error sending STUN request", status);
@@ -1840,7 +1848,12 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
ice = td->ice;
clist = td->clist;
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_SUCCESS;
+ }
/* Set timer ID to FALSE first */
te->id = PJ_FALSE;
@@ -1860,7 +1873,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
status = perform_check(ice, clist, i, ice->is_nominating);
if (status != PJ_SUCCESS) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_log_pop_indent();
return status;
}
@@ -1880,7 +1893,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
status = perform_check(ice, clist, i, ice->is_nominating);
if (status != PJ_SUCCESS) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_log_pop_indent();
return status;
}
@@ -1897,12 +1910,12 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
/* Schedule for next timer */
pj_time_val timeout = {0, PJ_ICE_TA_VAL};
- te->id = PJ_TRUE;
pj_time_val_normalize(&timeout);
- pj_timer_heap_schedule(th, te, &timeout);
+ pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE,
+ ice->grp_lock);
}
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_log_pop_indent();
return PJ_SUCCESS;
}
@@ -1922,8 +1935,8 @@ static void start_nominated_check(pj_ice_sess *ice)
/* Stop our timer if it's active */
if (ice->timer.id == TIMER_START_NOMINATED_CHECK) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
- ice->timer.id = TIMER_NONE;
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+ TIMER_NONE);
}
/* For each component, set the check state of valid check with
@@ -1951,18 +1964,15 @@ static void start_nominated_check(pj_ice_sess *ice)
}
/* And (re)start the periodic check */
- if (ice->clist.timer.id) {
- pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
- ice->clist.timer.id = PJ_FALSE;
- }
+ pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+ &ice->clist.timer, PJ_FALSE);
- ice->clist.timer.id = PJ_TRUE;
delay.sec = delay.msec = 0;
- status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
- &ice->clist.timer, &delay);
- if (status != PJ_SUCCESS) {
- ice->clist.timer.id = PJ_FALSE;
- } else {
+ status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->clist.timer, &delay,
+ PJ_TRUE,
+ ice->grp_lock);
+ if (status == PJ_SUCCESS) {
LOG5((ice->obj_name, "Periodic timer rescheduled.."));
}
@@ -2012,7 +2022,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
PJ_ASSERT_RETURN(ice->clist.count > 0, PJ_EINVALIDOP);
/* Lock session */
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
LOG4((ice->obj_name, "Starting ICE check.."));
pj_log_push_indent();
@@ -2042,7 +2052,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
}
if (i == clist->count) {
pj_assert(!"Unable to find checklist for component 1");
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_log_pop_indent();
return PJNATH_EICEINCOMPID;
}
@@ -2096,15 +2106,15 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
* instead to reduce stack usage:
* return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
*/
- clist->timer.id = PJ_TRUE;
delay.sec = delay.msec = 0;
- status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
- &clist->timer, &delay);
+ status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &clist->timer, &delay,
+ PJ_TRUE, ice->grp_lock);
if (status != PJ_SUCCESS) {
clist->timer.id = PJ_FALSE;
}
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
pj_log_pop_indent();
return status;
}
@@ -2125,9 +2135,22 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess,
stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
pj_ice_sess *ice = sd->ice;
pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
+ pj_status_t status;
- return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
- pkt, pkt_size, dst_addr, addr_len);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ /* Stray retransmit timer that could happen while
+ * we're being destroyed */
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
+ pkt, pkt_size, dst_addr, addr_len);
+
+ pj_grp_lock_release(ice->grp_lock);
+ return status;
}
@@ -2162,7 +2185,13 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
pj_assert(tdata == check->tdata);
check->tdata = NULL;
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ /* Not sure if this is possible but just in case */
+ pj_grp_lock_release(ice->grp_lock);
+ return;
+ }
/* Init lcand to NULL. lcand will be found from the mapped address
* found in the response.
@@ -2213,7 +2242,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
perform_check(ice, clist, msg_data->data.req.ckid,
check->nominated || ice->is_nominating);
pj_log_pop_indent();
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
@@ -2228,7 +2257,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
on_check_complete(ice, check);
pj_log_pop_indent();
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
@@ -2241,7 +2270,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
* the response match the source IP address and port that the Binding
* Request was sent from.
*/
- if (sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr) != 0) {
+ if (pj_sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr)!=0)
+ {
status = PJNATH_EICEINSRCADDR;
LOG4((ice->obj_name,
"Check %s%s: connectivity check FAILED: source address mismatch",
@@ -2252,7 +2282,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status);
on_check_complete(ice, check);
pj_log_pop_indent();
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
@@ -2285,14 +2315,14 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
PJNATH_ESTUNNOMAPPEDADDR);
on_check_complete(ice, check);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
/* Find local candidate that matches the XOR-MAPPED-ADDRESS */
pj_assert(lcand == NULL);
for (i=0; i<ice->lcand_cnt; ++i) {
- if (sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) {
+ if (pj_sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) {
/* Match */
lcand = &ice->lcand[i];
break;
@@ -2328,12 +2358,13 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
&xaddr->sockaddr,
&check->lcand->base_addr,
&check->lcand->base_addr,
- sizeof(pj_sockaddr_in), &cand_id);
+ pj_sockaddr_get_len(&xaddr->sockaddr),
+ &cand_id);
if (status != PJ_SUCCESS) {
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED,
status);
on_check_complete(ice, check);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
@@ -2393,11 +2424,11 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
*/
if (on_check_complete(ice, check)) {
/* ICE complete! */
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return;
}
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
}
@@ -2438,7 +2469,12 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
sd = (stun_data*) pj_stun_session_get_user_data(sess);
ice = sd->ice;
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_EINVALIDOP;
+ }
/*
* Note:
@@ -2453,7 +2489,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0);
if (prio_attr == NULL) {
LOG5((ice->obj_name, "Received Binding request with no PRIORITY"));
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
@@ -2498,7 +2534,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT,
NULL, token, PJ_TRUE,
src_addr, src_addr_len);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
@@ -2510,7 +2546,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT,
NULL, token, PJ_TRUE,
src_addr, src_addr_len);
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
} else {
/* Switch role to controlled */
@@ -2525,7 +2561,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
*/
status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata);
if (status != PJ_SUCCESS) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return status;
}
@@ -2562,7 +2598,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
rcheck->comp_id = sd->comp_id;
rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id;
rcheck->src_addr_len = src_addr_len;
- pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len);
+ pj_sockaddr_cp(&rcheck->src_addr, src_addr);
rcheck->use_candidate = (uc_attr != NULL);
rcheck->priority = prio_attr->value;
rcheck->role_attr = role_attr;
@@ -2577,7 +2613,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
handle_incoming_check(ice, rcheck);
}
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_SUCCESS;
}
@@ -2600,7 +2636,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
* the request.
*/
for (i=0; i<ice->rcand_cnt; ++i) {
- if (sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0)
+ if (pj_sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0)
break;
}
@@ -2610,6 +2646,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
* candidate.
*/
if (i == ice->rcand_cnt) {
+ char raddr[PJ_INET6_ADDRSTRLEN];
if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) {
LOG4((ice->obj_name,
"Unable to add new peer reflexive candidate: too many "
@@ -2621,7 +2658,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
rcand->comp_id = (pj_uint8_t)rcheck->comp_id;
rcand->type = PJ_ICE_CAND_TYPE_PRFLX;
rcand->prio = rcheck->priority;
- pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len);
+ pj_sockaddr_cp(&rcand->addr, &rcheck->src_addr);
/* Foundation is random, unique from other foundation */
rcand->foundation.ptr = (char*) pj_pool_alloc(ice->pool, 36);
@@ -2630,9 +2667,9 @@ static void handle_incoming_check(pj_ice_sess *ice,
rcand->foundation.ptr);
LOG4((ice->obj_name,
- "Added new remote candidate from the request: %s:%d",
- pj_inet_ntoa(rcand->addr.ipv4.sin_addr),
- (int)pj_ntohs(rcand->addr.ipv4.sin_port)));
+ "Added new remote candidate from the request: %s:%d",
+ pj_sockaddr_print(&rcand->addr, raddr, sizeof(raddr), 0),
+ pj_sockaddr_get_port(&rcand->addr)));
} else {
/* Remote candidate found */
@@ -2649,7 +2686,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
for (i=0; i<ice->clist.count; ++i) {
pj_ice_sess_check *c = &ice->clist.checks[i];
if (/*c->lcand == lcand ||*/
- sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0)
+ pj_sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0)
{
lcand = c->lcand;
break;
@@ -2732,7 +2769,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
LOG5((ice->obj_name, "Triggered check for check %d not performed "
"because it's in progress. Retransmitting", i));
pj_log_push_indent();
- pj_stun_session_retransmit_req(comp->stun_sess, c->tdata);
+ pj_stun_session_retransmit_req(comp->stun_sess, c->tdata, PJ_FALSE);
pj_log_pop_indent();
} else if (c->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) {
@@ -2866,18 +2903,23 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
return PJNATH_EICEINCOMPID;
}
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_EINVALIDOP;
+ }
comp = find_comp(ice, comp_id);
if (comp == NULL) {
status = PJNATH_EICEINCOMPID;
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
goto on_return;
}
if (comp->valid_check == NULL) {
status = PJNATH_EICEINPROGRESS;
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
goto on_return;
}
@@ -2886,12 +2928,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
pj_sockaddr_cp(&addr, &comp->valid_check->rcand->addr);
/* Release the mutex now to avoid deadlock (see ticket #1451). */
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
+
+ PJ_RACE_ME(5);
status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id,
data, data_len,
&addr,
- sizeof(pj_sockaddr_in));
+ pj_sockaddr_get_len(&addr));
on_return:
return status;
@@ -2913,11 +2957,16 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
- pj_mutex_lock(ice->mutex);
+ pj_grp_lock_acquire(ice->grp_lock);
+
+ if (ice->is_destroying) {
+ pj_grp_lock_release(ice->grp_lock);
+ return PJ_EINVALIDOP;
+ }
comp = find_comp(ice, comp_id);
if (comp == NULL) {
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJNATH_EICEINCOMPID;
}
@@ -2930,7 +2979,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
}
if (msg_data == NULL) {
pj_assert(!"Invalid transport ID");
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
return PJ_EINVAL;
}
@@ -2950,12 +2999,14 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
LOG4((ice->obj_name, "Error processing incoming message: %s",
ice->tmp.errmsg));
}
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
} else {
/* Not a STUN packet. Call application's callback instead, but release
* the mutex now or otherwise we may get deadlock.
*/
- pj_mutex_unlock(ice->mutex);
+ pj_grp_lock_release(ice->grp_lock);
+
+ PJ_RACE_ME(5);
(*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size,
src_addr, src_addr_len);
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
index 8ae2a90..2df77bf 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -1,4 +1,4 @@
-/* $Id: ice_strans.c 4133 2012-05-21 14:00:17Z bennylp $ */
+/* $Id: ice_strans.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -31,8 +31,9 @@
#include <pj/string.h>
#include <pj/compat/socket.h>
+#define ENABLE_TRACE 0
-#if 0
+#if defined(ENABLE_TRACE) && (ENABLE_TRACE != 0)
# define TRACE_PKT(expr) PJ_LOG(5,expr)
#else
# define TRACE_PKT(expr)
@@ -126,13 +127,11 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
/* Forward decls */
+static void ice_st_on_destroy(void *obj);
static void destroy_ice_st(pj_ice_strans *ice_st);
#define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc)
static void sess_init_update(pj_ice_strans *ice_st);
-static void sess_add_ref(pj_ice_strans *ice_st);
-static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st);
-
/**
* This structure describes an ICE stream transport component. A component
* in ICE stream transport typically corresponds to a single socket created
@@ -172,7 +171,7 @@ struct pj_ice_strans
void *user_data; /**< Application data. */
pj_ice_strans_cfg cfg; /**< Configuration. */
pj_ice_strans_cb cb; /**< Application callback. */
- pj_lock_t *init_lock; /**< Initialization mutex. */
+ pj_grp_lock_t *grp_lock; /**< Group lock. */
pj_ice_strans_state state; /**< Session state. */
pj_ice_sess *ice; /**< ICE session. */
@@ -183,7 +182,6 @@ struct pj_ice_strans
pj_timer_entry ka_timer; /**< STUN keep-alive timer. */
- pj_atomic_t *busy_cnt; /**< To prevent destroy */
pj_bool_t destroy_req;/**< Destroy has been called? */
pj_bool_t cb_called; /**< Init error callback called?*/
};
@@ -503,6 +501,13 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
add_update_turn(ice_st, comp);
}
+ /* It's possible that we end up without any candidates */
+ if (comp->cand_cnt == 0) {
+ PJ_LOG(4,(ice_st->obj_name,
+ "Error: no candidate is created due to settings"));
+ return PJ_EINVAL;
+ }
+
return PJ_SUCCESS;
}
@@ -544,23 +549,22 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
comp_cnt));
pj_log_push_indent();
- pj_ice_strans_cfg_copy(pool, &ice_st->cfg, cfg);
- pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
-
- status = pj_atomic_create(pool, 0, &ice_st->busy_cnt);
+ status = pj_grp_lock_create(pool, NULL, &ice_st->grp_lock);
if (status != PJ_SUCCESS) {
- destroy_ice_st(ice_st);
- return status;
- }
-
- status = pj_lock_create_recursive_mutex(pool, ice_st->obj_name,
- &ice_st->init_lock);
- if (status != PJ_SUCCESS) {
- destroy_ice_st(ice_st);
+ pj_pool_release(pool);
pj_log_pop_indent();
return status;
}
+ pj_grp_lock_add_ref(ice_st->grp_lock);
+ pj_grp_lock_add_handler(ice_st->grp_lock, pool, ice_st,
+ &ice_st_on_destroy);
+
+ pj_ice_strans_cfg_copy(pool, &ice_st->cfg, cfg);
+ ice_st->cfg.stun.cfg.grp_lock = ice_st->grp_lock;
+ ice_st->cfg.turn.cfg.grp_lock = ice_st->grp_lock;
+ pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
+
ice_st->comp_cnt = comp_cnt;
ice_st->comp = (pj_ice_strans_comp**)
pj_pool_calloc(pool, comp_cnt, sizeof(pj_ice_strans_comp*));
@@ -571,12 +575,12 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
/* Acquire initialization mutex to prevent callback to be
* called before we finish initialization.
*/
- pj_lock_acquire(ice_st->init_lock);
+ pj_grp_lock_acquire(ice_st->grp_lock);
for (i=0; i<comp_cnt; ++i) {
status = create_comp(ice_st, i+1);
if (status != PJ_SUCCESS) {
- pj_lock_release(ice_st->init_lock);
+ pj_grp_lock_release(ice_st->grp_lock);
destroy_ice_st(ice_st);
pj_log_pop_indent();
return status;
@@ -584,9 +588,9 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
}
/* Done with initialization */
- pj_lock_release(ice_st->init_lock);
+ pj_grp_lock_release(ice_st->grp_lock);
- PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created"));
+ PJ_LOG(4,(ice_st->obj_name, "ICE stream transport %p created", ice_st));
*p_ice_st = ice_st;
@@ -598,14 +602,35 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
return PJ_SUCCESS;
}
+/* REALLY destroy ICE */
+static void ice_st_on_destroy(void *obj)
+{
+ pj_ice_strans *ice_st = (pj_ice_strans*)obj;
+
+ PJ_LOG(4,(ice_st->obj_name, "ICE stream transport %p destroyed", obj));
+
+ /* Done */
+ pj_pool_release(ice_st->pool);
+}
+
/* Destroy ICE */
static void destroy_ice_st(pj_ice_strans *ice_st)
{
unsigned i;
- PJ_LOG(5,(ice_st->obj_name, "ICE stream transport destroying.."));
+ PJ_LOG(5,(ice_st->obj_name, "ICE stream transport %p destroy request..",
+ ice_st));
pj_log_push_indent();
+ pj_grp_lock_acquire(ice_st->grp_lock);
+
+ if (ice_st->destroy_req) {
+ pj_grp_lock_release(ice_st->grp_lock);
+ return;
+ }
+
+ ice_st->destroy_req = PJ_TRUE;
+
/* Destroy ICE if we have ICE */
if (ice_st->ice) {
pj_ice_sess_destroy(ice_st->ice);
@@ -616,38 +641,19 @@ static void destroy_ice_st(pj_ice_strans *ice_st)
for (i=0; i<ice_st->comp_cnt; ++i) {
if (ice_st->comp[i]) {
if (ice_st->comp[i]->stun_sock) {
- pj_stun_sock_set_user_data(ice_st->comp[i]->stun_sock, NULL);
pj_stun_sock_destroy(ice_st->comp[i]->stun_sock);
ice_st->comp[i]->stun_sock = NULL;
}
if (ice_st->comp[i]->turn_sock) {
- pj_turn_sock_set_user_data(ice_st->comp[i]->turn_sock, NULL);
pj_turn_sock_destroy(ice_st->comp[i]->turn_sock);
ice_st->comp[i]->turn_sock = NULL;
}
}
}
- ice_st->comp_cnt = 0;
-
- /* Destroy mutex */
- if (ice_st->init_lock) {
- pj_lock_acquire(ice_st->init_lock);
- pj_lock_release(ice_st->init_lock);
- pj_lock_destroy(ice_st->init_lock);
- ice_st->init_lock = NULL;
- }
-
- /* Destroy reference counter */
- if (ice_st->busy_cnt) {
- pj_assert(pj_atomic_get(ice_st->busy_cnt)==0);
- pj_atomic_destroy(ice_st->busy_cnt);
- ice_st->busy_cnt = NULL;
- }
- PJ_LOG(4,(ice_st->obj_name, "ICE stream transport destroyed"));
+ pj_grp_lock_dec_ref(ice_st->grp_lock);
+ pj_grp_lock_release(ice_st->grp_lock);
- /* Done */
- pj_pool_release(ice_st->pool);
pj_log_pop_indent();
}
@@ -732,45 +738,12 @@ static void sess_init_update(pj_ice_strans *ice_st)
*/
PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st)
{
- PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
-
- ice_st->destroy_req = PJ_TRUE;
- if (pj_atomic_get(ice_st->busy_cnt) > 0) {
- PJ_LOG(5,(ice_st->obj_name,
- "ICE strans object is busy, will destroy later"));
- return PJ_EPENDING;
- }
-
destroy_ice_st(ice_st);
return PJ_SUCCESS;
}
/*
- * Increment busy counter.
- */
-static void sess_add_ref(pj_ice_strans *ice_st)
-{
- pj_atomic_inc(ice_st->busy_cnt);
-}
-
-/*
- * Decrement busy counter. If the counter has reached zero and destroy
- * has been requested, destroy the object and return FALSE.
- */
-static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st)
-{
- int count = pj_atomic_dec_and_get(ice_st->busy_cnt);
- pj_assert(count >= 0);
- if (count==0 && ice_st->destroy_req) {
- pj_ice_strans_destroy(ice_st);
- return PJ_FALSE;
- } else {
- return PJ_TRUE;
- }
-}
-
-/*
* Get user data
*/
PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st)
@@ -833,7 +806,9 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
/* Create! */
status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role,
ice_st->comp_cnt, &ice_cb,
- local_ufrag, local_passwd, &ice_st->ice);
+ local_ufrag, local_passwd,
+ ice_st->grp_lock,
+ &ice_st->ice);
if (status != PJ_SUCCESS)
return status;
@@ -1145,6 +1120,8 @@ pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st,
*/
PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st)
{
+ PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
+
if (ice_st->ice) {
pj_ice_sess_destroy(ice_st->ice);
ice_st->ice = NULL;
@@ -1246,7 +1223,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
pj_time_val t;
unsigned msec;
- sess_add_ref(ice_st);
+ pj_grp_lock_add_ref(ice_st->grp_lock);
pj_gettimeofday(&t);
PJ_TIME_VAL_SUB(t, ice_st->start_time);
@@ -1328,7 +1305,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
}
- sess_dec_ref(ice_st);
+ pj_grp_lock_dec_ref(ice_st->grp_lock);
}
/*
@@ -1344,17 +1321,20 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
pj_ice_strans_comp *comp;
pj_status_t status;
+#if defined(ENABLE_TRACE) && (ENABLE_TRACE != 0)
+ char daddr[PJ_INET6_ADDRSTRLEN];
+#endif
PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
comp = ice_st->comp[comp_id-1];
TRACE_PKT((comp->ice_st->obj_name,
- "Component %d TX packet to %s:%d with transport %d",
- comp_id,
- pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr),
- (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port),
- transport_id));
+ "Component %d TX packet to %s:%d with transport %d",
+ comp_id,
+ pj_sockaddr_print(dst_addr, daddr, sizeof(addr), 0),
+ pj_sockaddr_get_port(dst_addr),
+ transport_id));
if (transport_id == TP_TURN) {
if (comp->turn_sock) {
@@ -1417,7 +1397,7 @@ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
ice_st = comp->ice_st;
- sess_add_ref(ice_st);
+ pj_grp_lock_add_ref(ice_st->grp_lock);
if (ice_st->ice == NULL) {
/* The ICE session is gone, but we're still receiving packets.
@@ -1442,7 +1422,7 @@ static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock,
}
}
- return sess_dec_ref(ice_st);
+ return pj_grp_lock_dec_ref(ice_st->grp_lock) ? PJ_FALSE : PJ_TRUE;
}
/* Notifification when asynchronous send operation to the STUN socket
@@ -1473,10 +1453,10 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock);
ice_st = comp->ice_st;
- sess_add_ref(ice_st);
+ pj_grp_lock_add_ref(ice_st->grp_lock);
/* Wait until initialization completes */
- pj_lock_acquire(ice_st->init_lock);
+ pj_grp_lock_acquire(ice_st->grp_lock);
/* Find the srflx cancidate */
for (i=0; i<comp->cand_cnt; ++i) {
@@ -1486,14 +1466,14 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
}
}
- pj_lock_release(ice_st->init_lock);
+ pj_grp_lock_release(ice_st->grp_lock);
/* It is possible that we don't have srflx candidate even though this
* callback is called. This could happen when we cancel adding srflx
* candidate due to initialization error.
*/
if (cand == NULL) {
- return sess_dec_ref(ice_st);
+ return pj_grp_lock_dec_ref(ice_st->grp_lock) ? PJ_FALSE : PJ_TRUE;
}
switch (op) {
@@ -1546,7 +1526,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
if (comp->default_cand > idx) {
--comp->default_cand;
} else if (comp->default_cand == idx) {
- comp->default_cand = !idx;
+ comp->default_cand = 0;
}
/* Remove srflx candidate */
@@ -1574,7 +1554,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
/* May not have cand, e.g. when error during init */
if (cand)
cand->status = status;
- if (!ice_st->cfg.stun.ignore_stun_error) {
+ if (!ice_st->cfg.stun.ignore_stun_error || comp->cand_cnt==1) {
sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT,
"STUN binding request failed", status);
} else {
@@ -1609,7 +1589,7 @@ static pj_bool_t stun_on_status(pj_stun_sock *stun_sock,
break;
}
- return sess_dec_ref(ice_st);
+ return pj_grp_lock_dec_ref(ice_st->grp_lock)? PJ_FALSE : PJ_TRUE;
}
/* Callback when TURN socket has received a packet */
@@ -1628,7 +1608,7 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock,
return;
}
- sess_add_ref(comp->ice_st);
+ pj_grp_lock_add_ref(comp->ice_st->grp_lock);
if (comp->ice_st->ice == NULL) {
/* The ICE session is gone, but we're still receiving packets.
@@ -1655,7 +1635,7 @@ static void turn_on_rx_data(pj_turn_sock *turn_sock,
}
}
- sess_dec_ref(comp->ice_st);
+ pj_grp_lock_dec_ref(comp->ice_st->grp_lock);
}
@@ -1677,7 +1657,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
pj_turn_state_name(old_state), pj_turn_state_name(new_state)));
pj_log_push_indent();
- sess_add_ref(comp->ice_st);
+ pj_grp_lock_add_ref(comp->ice_st->grp_lock);
if (new_state == PJ_TURN_STATE_READY) {
pj_turn_session_info rel_info;
@@ -1691,7 +1671,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
pj_turn_sock_get_info(turn_sock, &rel_info);
/* Wait until initialization completes */
- pj_lock_acquire(comp->ice_st->init_lock);
+ pj_grp_lock_acquire(comp->ice_st->grp_lock);
/* Find relayed candidate in the component */
for (i=0; i<comp->cand_cnt; ++i) {
@@ -1702,7 +1682,7 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
}
pj_assert(cand != NULL);
- pj_lock_release(comp->ice_st->init_lock);
+ pj_grp_lock_release(comp->ice_st->grp_lock);
/* Update candidate */
pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr);
@@ -1735,22 +1715,27 @@ static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
pj_turn_sock_set_user_data(turn_sock, NULL);
comp->turn_sock = NULL;
- /* Set session to fail if we're still initializing */
- if (comp->ice_st->state < PJ_ICE_STRANS_STATE_READY) {
- sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT,
- "TURN allocation failed", info.last_status);
- } else if (comp->turn_err_cnt > 1) {
- sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_KEEP_ALIVE,
- "TURN refresh failed", info.last_status);
- } else {
- PJ_PERROR(4,(comp->ice_st->obj_name, info.last_status,
- "Comp %d: TURN allocation failed, retrying",
- comp->comp_id));
- add_update_turn(comp->ice_st, comp);
+ /* Set session to fail on error. last_status PJ_SUCCESS means normal
+ * deallocation, which should not trigger sess_fail as it may have
+ * been initiated by ICE destroy
+ */
+ if (info.last_status != PJ_SUCCESS) {
+ if (comp->ice_st->state < PJ_ICE_STRANS_STATE_READY) {
+ sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT,
+ "TURN allocation failed", info.last_status);
+ } else if (comp->turn_err_cnt > 1) {
+ sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_KEEP_ALIVE,
+ "TURN refresh failed", info.last_status);
+ } else {
+ PJ_PERROR(4,(comp->ice_st->obj_name, info.last_status,
+ "Comp %d: TURN allocation failed, retrying",
+ comp->comp_id));
+ add_update_turn(comp->ice_st, comp);
+ }
}
}
- sess_dec_ref(comp->ice_st);
+ pj_grp_lock_dec_ref(comp->ice_st->grp_lock);
pj_log_pop_indent();
}
diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
index 86ac694..0eaf9bf 100644
--- a/pjnath/src/pjnath/nat_detect.c
+++ b/pjnath/src/pjnath/nat_detect.c
@@ -1,4 +1,4 @@
-/* $Id: nat_detect.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: nat_detect.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -307,7 +307,7 @@ PJ_DEF(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server,
sess_cb.on_request_complete = &on_request_complete;
sess_cb.on_send_msg = &on_send_msg;
status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb,
- PJ_FALSE, &sess->stun_sess);
+ PJ_FALSE, NULL, &sess->stun_sess);
if (status != PJ_SUCCESS)
goto on_error;
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c
index 45d5313..4018b93 100644
--- a/pjnath/src/pjnath/stun_session.c
+++ b/pjnath/src/pjnath/stun_session.c
@@ -1,4 +1,4 @@
-/* $Id: stun_session.c 3843 2011-10-24 14:13:35Z bennylp $ */
+/* $Id: stun_session.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -25,13 +25,10 @@ struct pj_stun_session
{
pj_stun_config *cfg;
pj_pool_t *pool;
- pj_lock_t *lock;
- pj_bool_t delete_lock;
+ pj_grp_lock_t *grp_lock;
pj_stun_session_cb cb;
void *user_data;
-
- pj_atomic_t *busy;
- pj_bool_t destroy_request;
+ pj_bool_t is_destroying;
pj_bool_t use_fingerprint;
@@ -55,14 +52,15 @@ struct pj_stun_session
};
#define SNAME(s_) ((s_)->pool->obj_name)
+#define THIS_FILE "stun_session.c"
-#if PJ_LOG_MAX_LEVEL >= 5
+#if 1
# define TRACE_(expr) PJ_LOG(5,expr)
#else
# define TRACE_(expr)
#endif
-#define LOG_ERR_(sess,title,rc) pjnath_perror(sess->pool->obj_name,title,rc)
+#define LOG_ERR_(sess,title,rc) PJ_PERROR(3,(sess->pool->obj_name,rc,title))
#define TDATA_POOL_SIZE PJNATH_POOL_LEN_STUN_TDATA
#define TDATA_POOL_INC PJNATH_POOL_INC_STUN_TDATA
@@ -77,6 +75,7 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
const void *stun_pkt,
pj_size_t pkt_size);
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx);
+static void stun_sess_on_destroy(void *comp);
static pj_stun_tsx_cb tsx_cb =
{
@@ -148,31 +147,38 @@ static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx)
pj_stun_tx_data *tdata;
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
- tsx_erase(tdata->sess, tdata);
+ pj_stun_client_tsx_stop(tsx);
+ if (tdata) {
+ tsx_erase(tdata->sess, tdata);
+ pj_pool_release(tdata->pool);
+ }
- pj_stun_client_tsx_destroy(tsx);
- pj_pool_release(tdata->pool);
+ TRACE_((THIS_FILE, "STUN transaction %p destroyed", tsx));
}
static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
{
+ TRACE_((THIS_FILE, "tdata %p destroy request, force=%d, tsx=%p", tdata,
+ force, tdata->client_tsx));
+
if (tdata->res_timer.id != PJ_FALSE) {
- pj_timer_heap_cancel(tdata->sess->cfg->timer_heap,
- &tdata->res_timer);
- tdata->res_timer.id = PJ_FALSE;
+ pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
+ &tdata->res_timer, PJ_FALSE);
pj_list_erase(tdata);
}
if (force) {
+ pj_list_erase(tdata);
if (tdata->client_tsx) {
- tsx_erase(tdata->sess, tdata);
- pj_stun_client_tsx_destroy(tdata->client_tsx);
+ pj_stun_client_tsx_stop(tdata->client_tsx);
+ pj_stun_client_tsx_set_data(tdata->client_tsx, NULL);
}
pj_pool_release(tdata->pool);
} else {
if (tdata->client_tsx) {
- pj_time_val delay = {2, 0};
+ /* "Probably" this is to absorb retransmission */
+ pj_time_val delay = {0, 300};
pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
} else {
@@ -206,7 +212,7 @@ static void on_cache_timeout(pj_timer_heap_t *timer_heap,
PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
pj_list_erase(tdata);
- pj_stun_msg_destroy_tdata(tdata->sess, tdata);
+ destroy_tdata(tdata, PJ_FALSE);
}
static pj_status_t apply_msg_options(pj_stun_session *sess,
@@ -419,8 +425,12 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
sess = tdata->sess;
/* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_grp_lock_release(sess->grp_lock);
+ return;
+ }
/* Handle authentication challenge */
handle_auth_challenge(sess, tdata, response, src_addr,
@@ -434,15 +444,13 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
/* Destroy the transmit data. This will remove the transaction
* from the pending list too.
*/
- pj_stun_msg_destroy_tdata(sess, tdata);
+ if (status == PJNATH_ESTUNTIMEDOUT)
+ destroy_tdata(tdata, PJ_TRUE);
+ else
+ destroy_tdata(tdata, PJ_FALSE);
tdata = NULL;
- pj_lock_release(sess->lock);
-
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return;
- }
+ pj_grp_lock_release(sess->grp_lock);
}
static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
@@ -457,20 +465,21 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
sess = tdata->sess;
/* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ /* Stray timer */
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt,
pkt_size, tdata->dst_addr,
tdata->addr_len);
- pj_lock_release(sess->lock);
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return PJNATH_ESTUNDESTROYED;
- } else {
- return status;
- }
+ return status;
}
/* **************************************************************************/
@@ -479,6 +488,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
const char *name,
const pj_stun_session_cb *cb,
pj_bool_t fingerprint,
+ pj_grp_lock_t *grp_lock,
pj_stun_session **p_sess)
{
pj_pool_t *pool;
@@ -500,49 +510,38 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
pj_memcpy(&sess->cb, cb, sizeof(*cb));
sess->use_fingerprint = fingerprint;
sess->log_flag = 0xFFFF;
-
- sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32);
- sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32,
- "pjnath-%s", pj_get_version());
- sess->rx_pool = pj_pool_create(sess->cfg->pf, name,
- PJNATH_POOL_LEN_STUN_TDATA,
+ if (grp_lock) {
+ sess->grp_lock = grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &sess->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+ }
+
+ pj_grp_lock_add_ref(sess->grp_lock);
+ pj_grp_lock_add_handler(sess->grp_lock, pool, sess,
+ &stun_sess_on_destroy);
+
+ pj_stun_session_set_software_name(sess, &cfg->software_name);
+
+ sess->rx_pool = pj_pool_create(sess->cfg->pf, name,
+ PJNATH_POOL_LEN_STUN_TDATA,
PJNATH_POOL_INC_STUN_TDATA, NULL);
pj_list_init(&sess->pending_request_list);
pj_list_init(&sess->cached_response_list);
- status = pj_lock_create_recursive_mutex(pool, name, &sess->lock);
- if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
- return status;
- }
- sess->delete_lock = PJ_TRUE;
-
- status = pj_atomic_create(pool, 0, &sess->busy);
- if (status != PJ_SUCCESS) {
- pj_lock_destroy(sess->lock);
- pj_pool_release(pool);
- return status;
- }
-
*p_sess = sess;
return PJ_SUCCESS;
}
-PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
+static void stun_sess_on_destroy(void *comp)
{
- PJ_ASSERT_RETURN(sess, PJ_EINVAL);
-
- pj_lock_acquire(sess->lock);
-
- /* Can't destroy if we're in a callback */
- sess->destroy_request = PJ_TRUE;
- if (pj_atomic_get(sess->busy)) {
- pj_lock_release(sess->lock);
- return PJ_EPENDING;
- }
+ pj_stun_session *sess = (pj_stun_session*)comp;
while (!pj_list_empty(&sess->pending_request_list)) {
pj_stun_tx_data *tdata = sess->pending_request_list.next;
@@ -553,11 +552,6 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
pj_stun_tx_data *tdata = sess->cached_response_list.next;
destroy_tdata(tdata, PJ_TRUE);
}
- pj_lock_release(sess->lock);
-
- if (sess->delete_lock) {
- pj_lock_destroy(sess->lock);
- }
if (sess->rx_pool) {
pj_pool_release(sess->rx_pool);
@@ -566,6 +560,47 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
pj_pool_release(sess->pool);
+ TRACE_((THIS_FILE, "STUN session %p destroyed", sess));
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
+{
+ pj_stun_tx_data *tdata;
+
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ TRACE_((SNAME(sess), "STUN session %p destroy request, ref_cnt=%d",
+ sess, pj_grp_lock_get_ref(sess->grp_lock)));
+
+ pj_grp_lock_acquire(sess->grp_lock);
+
+ if (sess->is_destroying) {
+ /* Prevent from decrementing the ref counter more than once */
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ sess->is_destroying = PJ_TRUE;
+
+ /* We need to stop transactions and cached response because they are
+ * holding the group lock's reference counter while retransmitting.
+ */
+ tdata = sess->pending_request_list.next;
+ while (tdata != &sess->pending_request_list) {
+ if (tdata->client_tsx)
+ pj_stun_client_tsx_stop(tdata->client_tsx);
+ tdata = tdata->next;
+ }
+
+ tdata = sess->cached_response_list.next;
+ while (tdata != &sess->cached_response_list) {
+ pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
+ &tdata->res_timer, PJ_FALSE);
+ tdata = tdata->next;
+ }
+
+ pj_grp_lock_dec_ref(sess->grp_lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -574,9 +609,9 @@ PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
void *user_data)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
sess->user_data = user_data;
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -586,35 +621,16 @@ PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess)
return sess->user_data;
}
-PJ_DEF(pj_status_t) pj_stun_session_set_lock( pj_stun_session *sess,
- pj_lock_t *lock,
- pj_bool_t auto_del)
-{
- pj_lock_t *old_lock = sess->lock;
- pj_bool_t old_del;
-
- PJ_ASSERT_RETURN(sess && lock, PJ_EINVAL);
-
- pj_lock_acquire(old_lock);
- sess->lock = lock;
- old_del = sess->delete_lock;
- sess->delete_lock = auto_del;
- pj_lock_release(old_lock);
-
- if (old_lock)
- pj_lock_destroy(old_lock);
-
- return PJ_SUCCESS;
-}
-
PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
const pj_str_t *sw)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
if (sw && sw->slen)
pj_strdup(sess->pool, &sess->srv_name, sw);
else
sess->srv_name.slen = 0;
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -624,6 +640,7 @@ PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
sess->auth_type = auth_type;
if (cred) {
pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
@@ -631,6 +648,7 @@ PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
sess->auth_type = PJ_STUN_AUTH_NONE;
pj_bzero(&sess->cred, sizeof(sess->cred));
}
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -707,17 +725,21 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
status = create_tdata(sess, &tdata);
if (status != PJ_SUCCESS)
- return status;
+ goto on_error;
/* Create STUN message */
status = pj_stun_msg_create(tdata->pool, method, magic,
tsx_id, &tdata->msg);
- if (status != PJ_SUCCESS) {
- pj_pool_release(tdata->pool);
- return status;
- }
+ if (status != PJ_SUCCESS)
+ goto on_error;
/* copy the request's transaction ID as the transaction key. */
pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
@@ -733,10 +755,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
} else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
/* MUST put authentication in request */
status = get_auth(sess, tdata);
- if (status != PJ_SUCCESS) {
- pj_pool_release(tdata->pool);
- return status;
- }
+ if (status != PJ_SUCCESS)
+ goto on_error;
} else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
/* Only put authentication information if we've received
@@ -744,22 +764,27 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
*/
if (sess->next_nonce.slen != 0) {
status = get_auth(sess, tdata);
- if (status != PJ_SUCCESS) {
- pj_pool_release(tdata->pool);
- return status;
- }
+ if (status != PJ_SUCCESS)
+ goto on_error;
tdata->auth_info.nonce = sess->next_nonce;
tdata->auth_info.realm = sess->server_realm;
}
} else {
pj_assert(!"Invalid authentication type");
- pj_pool_release(tdata->pool);
- return PJ_EBUG;
+ status = PJ_EBUG;
+ goto on_error;
}
*p_tdata = tdata;
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
+
+on_error:
+ if (tdata)
+ pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
}
PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
@@ -771,9 +796,17 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
status = create_tdata(sess, &tdata);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
return status;
+ }
/* Create STUN message */
msg_type |= PJ_STUN_INDICATION_BIT;
@@ -781,10 +814,13 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
NULL, &tdata->msg);
if (status != PJ_SUCCESS) {
pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
*p_tdata = tdata;
+
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -800,15 +836,24 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
pj_status_t status;
pj_stun_tx_data *tdata = NULL;
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
status = create_tdata(sess, &tdata);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
return status;
+ }
/* Create STUN response message */
status = pj_stun_msg_create_response(tdata->pool, rdata->msg,
err_code, err_msg, &tdata->msg);
if (status != PJ_SUCCESS) {
pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -823,6 +868,8 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
*p_tdata = tdata;
+ pj_grp_lock_release(sess->grp_lock);
+
return PJ_SUCCESS;
}
@@ -869,6 +916,13 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
pj_log_push_indent();
/* Allocate packet */
@@ -878,10 +932,6 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
tdata->token = token;
tdata->retransmit = retransmit;
- /* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
-
/* Apply options */
status = apply_msg_options(sess, tdata->pool, &tdata->auth_info,
tdata->msg);
@@ -911,7 +961,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
/* Create STUN client transaction */
- status = pj_stun_client_tsx_create(sess->cfg, tdata->pool,
+ status = pj_stun_client_tsx_create(sess->cfg, tdata->pool,
+ sess->grp_lock,
&tsx_cb, &tdata->client_tsx);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
@@ -941,17 +992,17 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
pj_time_val timeout;
pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
- pj_timer_entry_init(&tdata->res_timer, PJ_TRUE, tdata,
+ pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata,
&on_cache_timeout);
timeout.sec = sess->cfg->res_cache_msec / 1000;
timeout.msec = sess->cfg->res_cache_msec % 1000;
- status = pj_timer_heap_schedule(sess->cfg->timer_heap,
- &tdata->res_timer,
- &timeout);
+ status = pj_timer_heap_schedule_w_grp_lock(sess->cfg->timer_heap,
+ &tdata->res_timer,
+ &timeout, PJ_TRUE,
+ sess->grp_lock);
if (status != PJ_SUCCESS) {
- tdata->res_timer.id = PJ_FALSE;
pj_stun_msg_destroy_tdata(sess, tdata);
LOG_ERR_(sess, "Error scheduling response timer", status);
goto on_return;
@@ -977,15 +1028,10 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
}
on_return:
- pj_lock_release(sess->lock);
-
pj_log_pop_indent();
- /* Check if application has called destroy() in the callback */
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return PJNATH_ESTUNDESTROYED;
- }
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
return status;
}
@@ -1007,14 +1053,25 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
pj_str_t reason;
pj_stun_tx_data *tdata;
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
status = pj_stun_session_create_res(sess, rdata, code,
(errmsg?pj_cstr(&reason,errmsg):NULL),
&tdata);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
return status;
+ }
- return pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
- dst_addr, addr_len, tdata);
+ status = pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
+ dst_addr, addr_len, tdata);
+
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
}
@@ -1031,8 +1088,11 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
/* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
if (notify) {
(sess->cb.on_request_complete)(sess, notify_status, tdata->token,
@@ -1042,12 +1102,7 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
/* Just destroy tdata. This will destroy the transaction as well */
pj_stun_msg_destroy_tdata(sess, tdata);
- pj_lock_release(sess->lock);
-
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return PJNATH_ESTUNDESTROYED;
- }
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -1056,7 +1111,8 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
* Explicitly request retransmission of the request.
*/
PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
- pj_stun_tx_data *tdata)
+ pj_stun_tx_data *tdata,
+ pj_bool_t mod_count)
{
pj_status_t status;
@@ -1064,17 +1120,15 @@ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
/* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
-
- status = pj_stun_client_tsx_retransmit(tdata->client_tsx);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
- pj_lock_release(sess->lock);
+ status = pj_stun_client_tsx_retransmit(tdata->client_tsx, mod_count);
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return PJNATH_ESTUNDESTROYED;
- }
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -1362,11 +1416,15 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
- pj_log_push_indent();
-
/* Lock the session and prevent user from destroying us in the callback */
- pj_atomic_inc(sess->busy);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
+
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ pj_log_push_indent();
/* Reset pool */
pj_pool_reset(sess->rx_pool);
@@ -1419,17 +1477,10 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
}
on_return:
- pj_lock_release(sess->lock);
-
pj_log_pop_indent();
- /* If we've received destroy request while we're on the callback,
- * destroy the session now.
- */
- if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
- pj_stun_session_destroy(sess);
- return PJNATH_ESTUNDESTROYED;
- }
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
return status;
}
diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c
index ff7dc16..cc7bd2c 100644
--- a/pjnath/src/pjnath/stun_sock.c
+++ b/pjnath/src/pjnath/stun_sock.c
@@ -1,4 +1,4 @@
-/* $Id: stun_sock.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: stun_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -28,16 +28,24 @@
#include <pj/assert.h>
#include <pj/ip_helper.h>
#include <pj/log.h>
+#include <pj/os.h>
#include <pj/pool.h>
#include <pj/rand.h>
+#if 1
+# define TRACE_(x) PJ_LOG(5,x)
+#else
+# define TRACE_(x)
+#endif
+
+enum { MAX_BIND_RETRY = 100 };
struct pj_stun_sock
{
char *obj_name; /* Log identification */
pj_pool_t *pool; /* Pool */
void *user_data; /* Application user data */
-
+ pj_bool_t is_destroying; /* Destroy already called */
int af; /* Address family */
pj_stun_config stun_cfg; /* STUN config (ioqueue etc)*/
pj_stun_sock_cb cb; /* Application callbacks */
@@ -56,13 +64,16 @@ struct pj_stun_sock
pj_uint16_t tsx_id[6]; /* .. to match STUN msg */
pj_stun_session *stun_sess; /* STUN session */
-
+ pj_grp_lock_t *grp_lock; /* Session group lock */
};
/*
* Prototypes for static functions
*/
+/* Destructor for group lock */
+static void stun_sock_destructor(void *obj);
+
/* This callback is called by the STUN session to send packet */
static pj_status_t sess_on_send_msg(pj_stun_session *sess,
void *token,
@@ -162,7 +173,9 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
pj_pool_t *pool;
pj_stun_sock *stun_sock;
pj_stun_sock_cfg default_cfg;
+ pj_sockaddr bound_addr;
unsigned i;
+ pj_uint16_t max_bind_retry;
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && cb && p_stun_sock, PJ_EINVAL);
@@ -198,6 +211,20 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
if (stun_sock->ka_interval == 0)
stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
+ if (cfg && cfg->grp_lock) {
+ stun_sock->grp_lock = cfg->grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+ }
+
+ pj_grp_lock_add_ref(stun_sock->grp_lock);
+ pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
+ &stun_sock_destructor);
+
/* Create socket and bind socket */
status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd);
if (status != PJ_SUCCESS)
@@ -211,17 +238,17 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
goto on_error;
/* Bind socket */
- if (pj_sockaddr_has_addr(&cfg->bound_addr)) {
- status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr,
- pj_sockaddr_get_len(&cfg->bound_addr));
- } else {
- pj_sockaddr bound_addr;
-
- pj_sockaddr_init(af, &bound_addr, NULL, 0);
- status = pj_sock_bind(stun_sock->sock_fd, &bound_addr,
- pj_sockaddr_get_len(&bound_addr));
+ max_bind_retry = MAX_BIND_RETRY;
+ if (cfg->port_range && cfg->port_range < max_bind_retry)
+ max_bind_retry = cfg->port_range;
+ pj_sockaddr_init(af, &bound_addr, NULL, 0);
+ if (cfg->bound_addr.addr.sa_family == pj_AF_INET() ||
+ cfg->bound_addr.addr.sa_family == pj_AF_INET6())
+ {
+ pj_sockaddr_cp(&bound_addr, &cfg->bound_addr);
}
-
+ status = pj_sock_bind_random(stun_sock->sock_fd, &bound_addr,
+ cfg->port_range, max_bind_retry);
if (status != PJ_SUCCESS)
goto on_error;
@@ -248,6 +275,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
pj_activesock_cb activesock_cb;
pj_activesock_cfg_default(&activesock_cfg);
+ activesock_cfg.grp_lock = stun_sock->grp_lock;
activesock_cfg.async_cnt = cfg->async_cnt;
activesock_cfg.concurrency = 0;
@@ -286,6 +314,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg,
status = pj_stun_session_create(&stun_sock->stun_cfg,
stun_sock->obj_name,
&sess_cb, PJ_FALSE,
+ stun_sock->grp_lock,
&stun_sock->stun_sess);
if (status != PJ_SUCCESS)
goto on_error;
@@ -328,6 +357,8 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Check whether the domain contains IP address */
stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af;
status = pj_inet_pton(stun_sock->af, domain,
@@ -356,7 +387,6 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
&stun_sock->q);
/* Processing will resume when the DNS SRV callback is called */
- return status;
} else {
@@ -374,40 +404,29 @@ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock,
pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port);
/* Start sending Binding request */
- return get_mapped_addr(stun_sock);
+ status = get_mapped_addr(stun_sock);
}
+
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return status;
}
-/* Destroy */
-PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+/* Destructor */
+static void stun_sock_destructor(void *obj)
{
+ pj_stun_sock *stun_sock = (pj_stun_sock*)obj;
+
if (stun_sock->q) {
pj_dns_srv_cancel_query(stun_sock->q, PJ_FALSE);
stun_sock->q = NULL;
}
- /* Destroy the active socket first just in case we'll get
- * stray callback.
- */
- if (stun_sock->active_sock != NULL) {
- pj_activesock_close(stun_sock->active_sock);
- stun_sock->active_sock = NULL;
- stun_sock->sock_fd = PJ_INVALID_SOCKET;
- } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
- pj_sock_close(stun_sock->sock_fd);
- stun_sock->sock_fd = PJ_INVALID_SOCKET;
- }
-
- if (stun_sock->ka_timer.id != 0) {
- pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer);
- stun_sock->ka_timer.id = 0;
- }
-
+ /*
if (stun_sock->stun_sess) {
pj_stun_session_destroy(stun_sock->stun_sess);
stun_sock->stun_sess = NULL;
}
+ */
if (stun_sock->pool) {
pj_pool_t *pool = stun_sock->pool;
@@ -415,6 +434,40 @@ PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
pj_pool_release(pool);
}
+ TRACE_(("", "STUN sock %p destroyed", stun_sock));
+
+}
+
+/* Destroy */
+PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+{
+ TRACE_((stun_sock->obj_name, "STUN sock %p request, ref_cnt=%d",
+ stun_sock, pj_grp_lock_get_ref(stun_sock->grp_lock)));
+
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+ if (stun_sock->is_destroying) {
+ /* Destroy already called */
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ stun_sock->is_destroying = PJ_TRUE;
+ pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer, 0);
+
+ if (stun_sock->active_sock != NULL) {
+ stun_sock->sock_fd = PJ_INVALID_SOCKET;
+ pj_activesock_close(stun_sock->active_sock);
+ } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
+ pj_sock_close(stun_sock->sock_fd);
+ stun_sock->sock_fd = PJ_INVALID_SOCKET;
+ }
+
+ if (stun_sock->stun_sess) {
+ pj_stun_session_destroy(stun_sock->stun_sess);
+ }
+ pj_grp_lock_dec_ref(stun_sock->grp_lock);
+ pj_grp_lock_release(stun_sock->grp_lock);
return PJ_SUCCESS;
}
@@ -458,12 +511,15 @@ static void dns_srv_resolver_cb(void *user_data,
{
pj_stun_sock *stun_sock = (pj_stun_sock*) user_data;
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Clear query */
stun_sock->q = NULL;
/* Handle error */
if (status != PJ_SUCCESS) {
sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status);
+ pj_grp_lock_release(stun_sock->grp_lock);
return;
}
@@ -480,6 +536,8 @@ static void dns_srv_resolver_cb(void *user_data,
/* Start sending Binding request */
get_mapped_addr(stun_sock);
+
+ pj_grp_lock_release(stun_sock->grp_lock);
}
@@ -523,6 +581,8 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Copy STUN server address and mapped address */
pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
sizeof(pj_sockaddr));
@@ -533,8 +593,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
addr_len = sizeof(info->bound_addr);
status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
&addr_len);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
/* If socket is bound to a specific interface, then only put that
* interface in the alias list. Otherwise query all the interfaces
@@ -550,8 +612,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
/* Get the default address */
status = pj_gethostip(stun_sock->af, &def_addr);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
pj_sockaddr_set_port(&def_addr, port);
@@ -559,8 +623,10 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
info->alias_cnt = PJ_ARRAY_SIZE(info->aliases);
status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt,
info->aliases);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return status;
+ }
/* Set the port number for each address.
*/
@@ -580,6 +646,7 @@ PJ_DEF(pj_status_t) pj_stun_sock_get_info( pj_stun_sock *stun_sock,
}
}
+ pj_grp_lock_release(stun_sock->grp_lock);
return PJ_SUCCESS;
}
@@ -593,14 +660,29 @@ PJ_DEF(pj_status_t) pj_stun_sock_sendto( pj_stun_sock *stun_sock,
unsigned addr_len)
{
pj_ssize_t size;
+ pj_status_t status;
+
PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
+ if (!stun_sock->active_sock) {
+ /* We have been shutdown, but this callback may still get called
+ * by retransmit timer.
+ */
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
if (send_key==NULL)
send_key = &stun_sock->send_key;
size = pkt_len;
- return pj_activesock_sendto(stun_sock->active_sock, send_key,
- pkt, &size, flag, dst_addr, addr_len);
+ status = pj_activesock_sendto(stun_sock->active_sock, send_key,
+ pkt, &size, flag, dst_addr, addr_len);
+
+ pj_grp_lock_release(stun_sock->grp_lock);
+ return status;
}
/* This callback is called by the STUN session to send packet */
@@ -615,12 +697,18 @@ static pj_status_t sess_on_send_msg(pj_stun_session *sess,
pj_ssize_t size;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+ if (!stun_sock || !stun_sock->active_sock) {
+ /* We have been shutdown, but this callback may still get called
+ * by retransmit timer.
+ */
+ return PJ_EINVALIDOP;
+ }
pj_assert(token==INTERNAL_MSG_TOKEN);
PJ_UNUSED_ARG(token);
size = pkt_size;
- return pj_activesock_sendto(stun_sock->active_sock,
+ return pj_activesock_sendto(stun_sock->active_sock,
&stun_sock->int_send_key,
pkt, &size, 0, dst_addr, addr_len);
}
@@ -643,6 +731,8 @@ static void sess_on_request_complete(pj_stun_session *sess,
pj_bool_t resched = PJ_TRUE;
stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
+ if (!stun_sock)
+ return;
PJ_UNUSED_ARG(tdata);
PJ_UNUSED_ARG(token);
@@ -712,25 +802,20 @@ on_return:
/* Schedule keep-alive timer */
static void start_ka_timer(pj_stun_sock *stun_sock)
{
- if (stun_sock->ka_timer.id != 0) {
- pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer);
- stun_sock->ka_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer, 0);
pj_assert(stun_sock->ka_interval != 0);
- if (stun_sock->ka_interval > 0) {
+ if (stun_sock->ka_interval > 0 && !stun_sock->is_destroying) {
pj_time_val delay;
delay.sec = stun_sock->ka_interval;
delay.msec = 0;
- if (pj_timer_heap_schedule(stun_sock->stun_cfg.timer_heap,
- &stun_sock->ka_timer,
- &delay) == PJ_SUCCESS)
- {
- stun_sock->ka_timer.id = PJ_TRUE;
- }
+ pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer,
+ &delay, PJ_TRUE,
+ stun_sock->grp_lock);
}
}
@@ -742,14 +827,18 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
stun_sock = (pj_stun_sock *) te->user_data;
PJ_UNUSED_ARG(th);
+ pj_grp_lock_acquire(stun_sock->grp_lock);
/* Time to send STUN Binding request */
- if (get_mapped_addr(stun_sock) != PJ_SUCCESS)
+ if (get_mapped_addr(stun_sock) != PJ_SUCCESS) {
+ pj_grp_lock_release(stun_sock->grp_lock);
return;
+ }
/* Next keep-alive timer will be scheduled once the request
* is complete.
*/
+ pj_grp_lock_release(stun_sock->grp_lock);
}
/* Callback from active socket when incoming packet is received */
@@ -765,6 +854,8 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
pj_uint16_t type;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+ if (!stun_sock)
+ return PJ_FALSE;
/* Log socket error */
if (status != PJ_SUCCESS) {
@@ -772,6 +863,8 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
return PJ_TRUE;
}
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* Check that this is STUN message */
status = pj_stun_msg_check((const pj_uint8_t*)data, size,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET);
@@ -807,7 +900,10 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock,
status = pj_stun_session_on_rx_pkt(stun_sock->stun_sess, data, size,
PJ_STUN_IS_DATAGRAM, NULL, NULL,
src_addr, addr_len);
- return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE;
+
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
process_app_data:
if (stun_sock->cb.on_rx_data) {
@@ -815,10 +911,12 @@ process_app_data:
ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size,
src_addr, addr_len);
- return ret;
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
}
- return PJ_TRUE;
+ status = pj_grp_lock_release(stun_sock->grp_lock);
+ return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
}
/* Callback from active socket about send status */
@@ -829,6 +927,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
pj_stun_sock *stun_sock;
stun_sock = (pj_stun_sock*) pj_activesock_get_user_data(asock);
+ if (!stun_sock)
+ return PJ_FALSE;
/* Don't report to callback if this is internal message */
if (send_key == &stun_sock->int_send_key) {
@@ -839,6 +939,8 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
if (stun_sock->cb.on_data_sent) {
pj_bool_t ret;
+ pj_grp_lock_acquire(stun_sock->grp_lock);
+
/* If app gives NULL send_key in sendto() function, then give
* NULL in the callback too
*/
@@ -848,6 +950,7 @@ static pj_bool_t on_data_sent(pj_activesock_t *asock,
/* Call callback */
ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent);
+ pj_grp_lock_release(stun_sock->grp_lock);
return ret;
}
diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c
index d714ecf..58eca26 100644
--- a/pjnath/src/pjnath/stun_transaction.c
+++ b/pjnath/src/pjnath/stun_transaction.c
@@ -1,4 +1,4 @@
-/* $Id: stun_transaction.c 3753 2011-09-18 14:59:56Z bennylp $ */
+/* $Id: stun_transaction.c 4413 2013-03-05 06:29:15Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -26,6 +26,8 @@
#include <pj/timer.h>
+#define THIS_FILE "stun_transaction.c"
+#define TIMER_INACTIVE 0
#define TIMER_ACTIVE 1
@@ -34,6 +36,7 @@ struct pj_stun_client_tsx
char obj_name[PJ_MAX_OBJ_NAME];
pj_stun_tsx_cb cb;
void *user_data;
+ pj_grp_lock_t *grp_lock;
pj_bool_t complete;
@@ -51,18 +54,24 @@ struct pj_stun_client_tsx
};
+#if 1
+# define TRACE_(expr) PJ_LOG(5,expr)
+#else
+# define TRACE_(expr)
+#endif
+
+
static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
pj_timer_entry *timer);
static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
pj_timer_entry *timer);
-#define stun_perror(tsx,msg,rc) pjnath_perror(tsx->obj_name, msg, rc)
-
/*
* Create a STUN client transaction.
*/
PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
pj_pool_t *pool,
+ pj_grp_lock_t *grp_lock,
const pj_stun_tsx_cb *cb,
pj_stun_client_tsx **p_tsx)
{
@@ -74,6 +83,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx);
tsx->rto_msec = cfg->rto_msec;
tsx->timer_heap = cfg->timer_heap;
+ tsx->grp_lock = grp_lock;
pj_memcpy(&tsx->cb, cb, sizeof(*cb));
tsx->retransmit_timer.cb = &retransmit_timer_callback;
@@ -82,7 +92,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
tsx->destroy_timer.cb = &destroy_timer_callback;
tsx->destroy_timer.user_data = tsx;
- pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx);
+ pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx);
*p_tsx = tsx;
@@ -100,26 +110,30 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL);
PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL);
+ pj_grp_lock_acquire(tsx->grp_lock);
+
/* Cancel previously registered timer */
- if (tsx->destroy_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
- tsx->destroy_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
+ TIMER_INACTIVE);
/* Stop retransmission, just in case */
- if (tsx->retransmit_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+ TIMER_INACTIVE);
- status = pj_timer_heap_schedule(tsx->timer_heap,
- &tsx->destroy_timer, delay);
- if (status != PJ_SUCCESS)
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->destroy_timer, delay,
+ TIMER_ACTIVE, tsx->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(tsx->grp_lock);
return status;
+ }
- tsx->destroy_timer.id = TIMER_ACTIVE;
tsx->cb.on_complete = NULL;
+ pj_grp_lock_release(tsx->grp_lock);
+
+ TRACE_((tsx->obj_name, "STUN transaction %p schedule destroy", tsx));
+
return PJ_SUCCESS;
}
@@ -127,20 +141,21 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
/*
* Destroy transaction immediately.
*/
-PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx)
+PJ_DEF(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx)
{
PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
- if (tsx->retransmit_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
- }
- if (tsx->destroy_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
- tsx->destroy_timer.id = 0;
- }
+ /* Don't call grp_lock_acquire() because we might be called on
+ * group lock's destructor.
+ */
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+ TIMER_INACTIVE);
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
+ TIMER_INACTIVE);
+
+ PJ_LOG(5,(tsx->obj_name, "STUN client transaction %p stopped, ref_cnt=%d",
+ tsx, pj_grp_lock_get_ref(tsx->grp_lock)));
- PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed"));
return PJ_SUCCESS;
}
@@ -180,14 +195,15 @@ PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx)
/*
* Transmit message.
*/
-static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
+static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx,
+ pj_bool_t mod_count)
{
pj_status_t status;
- PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0 ||
+ PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE ||
!tsx->require_retransmit, PJ_EBUSY);
- if (tsx->require_retransmit) {
+ if (tsx->require_retransmit && mod_count) {
/* Calculate retransmit/timeout delay */
if (tsx->transmit_count == 0) {
tsx->retransmit_time.sec = 0;
@@ -210,18 +226,20 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
* cancel it (as opposed to when schedule_timer() failed we cannot
* cancel transmission).
*/;
- status = pj_timer_heap_schedule(tsx->timer_heap,
- &tsx->retransmit_timer,
- &tsx->retransmit_time);
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->retransmit_timer,
+ &tsx->retransmit_time,
+ TIMER_ACTIVE,
+ tsx->grp_lock);
if (status != PJ_SUCCESS) {
- tsx->retransmit_timer.id = 0;
+ tsx->retransmit_timer.id = TIMER_INACTIVE;
return status;
}
- tsx->retransmit_timer.id = TIMER_ACTIVE;
}
- tsx->transmit_count++;
+ if (mod_count)
+ tsx->transmit_count++;
PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
tsx->transmit_count));
@@ -233,12 +251,12 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
if (status == PJNATH_ESTUNDESTROYED) {
/* We've been destroyed, don't access the object. */
} else if (status != PJ_SUCCESS) {
- if (tsx->retransmit_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap,
- &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
+ if (mod_count) {
+ pj_timer_heap_cancel_if_active( tsx->timer_heap,
+ &tsx->retransmit_timer,
+ TIMER_INACTIVE);
}
- stun_perror(tsx, "STUN error sending message", status);
+ PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message"));
}
pj_log_pop_indent();
@@ -259,6 +277,8 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL);
PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY);
+ pj_grp_lock_acquire(tsx->grp_lock);
+
/* Encode message */
tsx->last_pkt = pkt;
tsx->last_pkt_size = pkt_len;
@@ -284,27 +304,29 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
* cancel it (as opposed to when schedule_timer() failed we cannot
* cancel transmission).
*/;
- status = pj_timer_heap_schedule(tsx->timer_heap,
- &tsx->retransmit_timer,
- &tsx->retransmit_time);
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->retransmit_timer,
+ &tsx->retransmit_time,
+ TIMER_ACTIVE,
+ tsx->grp_lock);
if (status != PJ_SUCCESS) {
- tsx->retransmit_timer.id = 0;
+ tsx->retransmit_timer.id = TIMER_INACTIVE;
+ pj_grp_lock_release(tsx->grp_lock);
return status;
}
- tsx->retransmit_timer.id = TIMER_ACTIVE;
}
/* Send the message */
- status = tsx_transmit_msg(tsx);
+ status = tsx_transmit_msg(tsx, PJ_TRUE);
if (status != PJ_SUCCESS) {
- if (tsx->retransmit_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap,
- &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(tsx->timer_heap,
+ &tsx->retransmit_timer,
+ TIMER_INACTIVE);
+ pj_grp_lock_release(tsx->grp_lock);
return status;
}
+ pj_grp_lock_release(tsx->grp_lock);
return PJ_SUCCESS;
}
@@ -317,8 +339,12 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
pj_status_t status;
PJ_UNUSED_ARG(timer_heap);
+ pj_grp_lock_acquire(tsx->grp_lock);
if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
+ /* tsx may be destroyed when calling the callback below */
+ pj_grp_lock_t *grp_lock = tsx->grp_lock;
+
/* Retransmission count exceeded. Transaction has failed */
tsx->retransmit_timer.id = 0;
PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response"));
@@ -329,16 +355,15 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0);
}
}
+ pj_grp_lock_release(grp_lock);
/* We might have been destroyed, don't try to access the object */
pj_log_pop_indent();
return;
}
tsx->retransmit_timer.id = 0;
- status = tsx_transmit_msg(tsx);
- if (status == PJNATH_ESTUNDESTROYED) {
- /* We've been destroyed, don't try to access the object */
- } else if (status != PJ_SUCCESS) {
+ status = tsx_transmit_msg(tsx, PJ_TRUE);
+ if (status != PJ_SUCCESS) {
tsx->retransmit_timer.id = 0;
if (!tsx->complete) {
tsx->complete = PJ_TRUE;
@@ -346,25 +371,26 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
tsx->cb.on_complete(tsx, status, NULL, NULL, 0);
}
}
- /* We might have been destroyed, don't try to access the object */
}
+
+ pj_grp_lock_release(tsx->grp_lock);
+ /* We might have been destroyed, don't try to access the object */
}
/*
* Request to retransmit the request.
*/
-PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx)
+PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx,
+ pj_bool_t mod_count)
{
if (tsx->destroy_timer.id != 0) {
return PJ_SUCCESS;
}
- if (tsx->retransmit_timer.id != 0) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+ TIMER_INACTIVE);
- return tsx_transmit_msg(tsx);
+ return tsx_transmit_msg(tsx, mod_count);
}
/* Timer callback to destroy transaction */
@@ -376,6 +402,7 @@ static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
PJ_UNUSED_ARG(timer_heap);
tsx->destroy_timer.id = PJ_FALSE;
+
tsx->cb.on_destroy(tsx);
/* Don't access transaction after this */
}
@@ -405,10 +432,8 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
/* We have a response with matching transaction ID.
* We can cancel retransmit timer now.
*/
- if (tsx->retransmit_timer.id) {
- pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
- tsx->retransmit_timer.id = 0;
- }
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+ TIMER_INACTIVE);
/* Find STUN error code attribute */
err_attr = (pj_stun_errcode_attr*)
diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
index cbe8f5c..7031cd3 100644
--- a/pjnath/src/pjnath/turn_session.c
+++ b/pjnath/src/pjnath/turn_session.c
@@ -1,4 +1,4 @@
-/* $Id: turn_session.c 3844 2011-10-24 15:03:43Z bennylp $ */
+/* $Id: turn_session.c 4368 2013-02-21 21:53:28Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -112,8 +112,9 @@ struct pj_turn_session
pj_turn_session_cb cb;
void *user_data;
pj_stun_config stun_cfg;
+ pj_bool_t is_destroying;
- pj_lock_t *lock;
+ pj_grp_lock_t *grp_lock;
int busy;
pj_turn_state_t state;
@@ -161,6 +162,7 @@ struct pj_turn_session
*/
static void sess_shutdown(pj_turn_session *sess,
pj_status_t status);
+static void turn_sess_on_destroy(void *comp);
static void do_destroy(pj_turn_session *sess);
static void send_refresh(pj_turn_session *sess, int lifetime);
static pj_status_t stun_on_send_msg(pj_stun_session *sess,
@@ -236,6 +238,7 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
const char *name,
int af,
pj_turn_tp_type conn_type,
+ pj_grp_lock_t *grp_lock,
const pj_turn_session_cb *cb,
unsigned options,
void *user_data,
@@ -244,7 +247,6 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
pj_pool_t *pool;
pj_turn_session *sess;
pj_stun_session_cb stun_cb;
- pj_lock_t *null_lock;
pj_status_t status;
PJ_ASSERT_RETURN(cfg && cfg->pf && cb && p_sess, PJ_EINVAL);
@@ -281,13 +283,20 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
sess->perm_table = pj_hash_create(pool, PJ_TURN_PERM_HTABLE_SIZE);
/* Session lock */
- status = pj_lock_create_recursive_mutex(pool, sess->obj_name,
- &sess->lock);
- if (status != PJ_SUCCESS) {
- do_destroy(sess);
- return status;
+ if (grp_lock) {
+ sess->grp_lock = grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &sess->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
}
+ pj_grp_lock_add_ref(sess->grp_lock);
+ pj_grp_lock_add_handler(sess->grp_lock, pool, sess,
+ &turn_sess_on_destroy);
+
/* Timer */
pj_timer_entry_init(&sess->timer, TIMER_NONE, sess, &on_timer_event);
@@ -297,7 +306,7 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
stun_cb.on_request_complete = &stun_on_request_complete;
stun_cb.on_rx_indication = &stun_on_rx_indication;
status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
- PJ_FALSE, &sess->stun);
+ PJ_FALSE, sess->grp_lock, &sess->stun);
if (status != PJ_SUCCESS) {
do_destroy(sess);
return status;
@@ -306,16 +315,6 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
/* Attach ourself to STUN session */
pj_stun_session_set_user_data(sess->stun, sess);
- /* Replace mutex in STUN session with a NULL mutex, since access to
- * STUN session is serialized.
- */
- status = pj_lock_create_null_mutex(pool, name, &null_lock);
- if (status != PJ_SUCCESS) {
- do_destroy(sess);
- return status;
- }
- pj_stun_session_set_lock(sess->stun, null_lock, PJ_TRUE);
-
/* Done */
PJ_LOG(4,(sess->obj_name, "TURN client session created"));
@@ -325,32 +324,9 @@ PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
}
-/* Destroy */
-static void do_destroy(pj_turn_session *sess)
+static void turn_sess_on_destroy(void *comp)
{
- /* Lock session */
- if (sess->lock) {
- pj_lock_acquire(sess->lock);
- }
-
- /* Cancel pending timer, if any */
- if (sess->timer.id != TIMER_NONE) {
- pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
- sess->timer.id = TIMER_NONE;
- }
-
- /* Destroy STUN session */
- if (sess->stun) {
- pj_stun_session_destroy(sess->stun);
- sess->stun = NULL;
- }
-
- /* Destroy lock */
- if (sess->lock) {
- pj_lock_release(sess->lock);
- pj_lock_destroy(sess->lock);
- sess->lock = NULL;
- }
+ pj_turn_session *sess = (pj_turn_session*) comp;
/* Destroy pool */
if (sess->pool) {
@@ -363,6 +339,26 @@ static void do_destroy(pj_turn_session *sess)
}
}
+/* Destroy */
+static void do_destroy(pj_turn_session *sess)
+{
+ PJ_LOG(4,(sess->obj_name, "TURN session destroy request, ref_cnt=%d",
+ pj_grp_lock_get_ref(sess->grp_lock)));
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return;
+ }
+
+ sess->is_destroying = PJ_TRUE;
+ pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer, TIMER_NONE);
+ pj_stun_session_destroy(sess->stun);
+
+ pj_grp_lock_dec_ref(sess->grp_lock);
+ pj_grp_lock_release(sess->grp_lock);
+}
+
/* Set session state */
static void set_state(pj_turn_session *sess, enum pj_turn_state_t state)
@@ -421,7 +417,10 @@ static void sess_shutdown(pj_turn_session *sess,
/* This may recursively call this function again with
* state==PJ_TURN_STATE_DEALLOCATED.
*/
+ /* No need to deallocate as we're already deallocating!
+ * See https://trac.pjsip.org/repos/ticket/1551
send_refresh(sess, 0);
+ */
break;
case PJ_TURN_STATE_DEALLOCATED:
case PJ_TURN_STATE_DESTROYING:
@@ -434,13 +433,11 @@ static void sess_shutdown(pj_turn_session *sess,
set_state(sess, PJ_TURN_STATE_DESTROYING);
- if (sess->timer.id != TIMER_NONE) {
- pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
- sess->timer.id = TIMER_NONE;
- }
-
- sess->timer.id = TIMER_DESTROY;
- pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
+ pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer,
+ TIMER_NONE);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &delay, TIMER_DESTROY,
+ sess->grp_lock);
}
}
@@ -452,11 +449,11 @@ PJ_DEF(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess)
{
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
sess_shutdown(sess, PJ_SUCCESS);
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -550,9 +547,9 @@ PJ_DEF(pj_status_t) pj_turn_session_set_software_name( pj_turn_session *sess,
{
pj_status_t status;
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
status = pj_stun_session_set_software_name(sess->stun, sw);
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -573,7 +570,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL);
PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_NULL, PJ_EINVALIDOP);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
/* See if "domain" contains just IP address */
tmp_addr.addr.sa_family = sess->af;
@@ -673,7 +670,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
}
on_return:
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -687,11 +684,11 @@ PJ_DEF(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess,
PJ_ASSERT_RETURN(sess && cred, PJ_EINVAL);
PJ_ASSERT_RETURN(sess->stun, PJ_EINVALIDOP);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
pj_stun_session_set_credential(sess->stun, PJ_STUN_AUTH_LONG_TERM, cred);
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -712,7 +709,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
sess->state<=PJ_TURN_STATE_RESOLVED,
PJ_EINVALIDOP);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
if (param && param != &sess->alloc_param)
pj_turn_alloc_param_copy(sess->pool, &sess->alloc_param, param);
@@ -723,7 +720,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
PJ_LOG(4,(sess->obj_name, "Pending ALLOCATE in state %s",
state_names[sess->state]));
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
}
@@ -735,7 +732,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST,
PJ_STUN_MAGIC, NULL, &tdata);
if (status != PJ_SUCCESS) {
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -775,7 +772,7 @@ PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
set_state(sess, PJ_TURN_STATE_RESOLVED);
}
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -796,14 +793,14 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess,
PJ_ASSERT_RETURN(sess && addr_cnt && addr, PJ_EINVAL);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
/* Create a bare CreatePermission request */
status = pj_stun_session_create_req(sess->stun,
PJ_STUN_CREATE_PERM_REQUEST,
PJ_STUN_MAGIC, NULL, &tdata);
if (status != PJ_SUCCESS) {
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -854,7 +851,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess,
goto on_error;
}
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return PJ_SUCCESS;
on_error:
@@ -871,7 +868,7 @@ on_error:
if (perm->req_token == req_token)
invalidate_perm(sess, perm);
}
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -942,7 +939,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
}
/* Lock session now */
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
/* Lookup permission first */
perm = lookup_perm(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE);
@@ -957,7 +954,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr,
0);
if (status != PJ_SUCCESS) {
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
}
@@ -1032,7 +1029,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
}
on_return:
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -1052,7 +1049,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
PJ_ASSERT_RETURN(sess && peer_adr && addr_len, PJ_EINVAL);
PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
/* Create blank ChannelBind request */
status = pj_stun_session_create_req(sess->stun,
@@ -1095,7 +1092,7 @@ PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
tdata);
on_return:
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -1118,7 +1115,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
*/
/* Start locking the session */
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
is_datagram = (sess->conn_type==PJ_TURN_TP_UDP);
@@ -1190,7 +1187,7 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
}
on_return:
- pj_lock_release(sess->lock);
+ pj_grp_lock_release(sess->grp_lock);
return status;
}
@@ -1382,20 +1379,22 @@ static void on_allocate_success(pj_turn_session *sess,
/* Cancel existing keep-alive timer, if any */
pj_assert(sess->timer.id != TIMER_DESTROY);
-
- if (sess->timer.id != TIMER_NONE) {
- pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
- sess->timer.id = TIMER_NONE;
+ if (sess->timer.id == TIMER_KEEP_ALIVE) {
+ pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer,
+ TIMER_NONE);
}
/* Start keep-alive timer once allocation succeeds */
- timeout.sec = sess->ka_interval;
- timeout.msec = 0;
+ if (sess->state < PJ_TURN_STATE_DEALLOCATING) {
+ timeout.sec = sess->ka_interval;
+ timeout.msec = 0;
- sess->timer.id = TIMER_KEEP_ALIVE;
- pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &timeout);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &timeout, TIMER_KEEP_ALIVE,
+ sess->grp_lock);
- set_state(sess, PJ_TURN_STATE_READY);
+ set_state(sess, PJ_TURN_STATE_READY);
+ }
}
/*
@@ -1945,7 +1944,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
PJ_UNUSED_ARG(th);
- pj_lock_acquire(sess->lock);
+ pj_grp_lock_acquire(sess->grp_lock);
eid = (enum timer_id_t) e->id;
e->id = TIMER_NONE;
@@ -1956,6 +1955,11 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
pj_bool_t resched = PJ_TRUE;
pj_bool_t pkt_sent = PJ_FALSE;
+ if (sess->state >= PJ_TURN_STATE_DEALLOCATING) {
+ /* Ignore if we're deallocating */
+ goto on_return;
+ }
+
pj_gettimeofday(&now);
/* Refresh allocation if it's time to do so */
@@ -2022,19 +2026,19 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
delay.sec = sess->ka_interval;
delay.msec = 0;
- sess->timer.id = TIMER_KEEP_ALIVE;
- pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &delay, TIMER_KEEP_ALIVE,
+ sess->grp_lock);
}
- pj_lock_release(sess->lock);
-
} else if (eid == TIMER_DESTROY) {
/* Time to destroy */
- pj_lock_release(sess->lock);
do_destroy(sess);
} else {
pj_assert(!"Unknown timer event");
- pj_lock_release(sess->lock);
}
+
+on_return:
+ pj_grp_lock_release(sess->grp_lock);
}
diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
index 799b557..970a955 100644
--- a/pjnath/src/pjnath/turn_sock.c
+++ b/pjnath/src/pjnath/turn_sock.c
@@ -1,4 +1,4 @@
-/* $Id: turn_sock.c 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: turn_sock.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -32,6 +32,10 @@ enum
TIMER_DESTROY
};
+
+enum { MAX_BIND_RETRY = 100 };
+
+
#define INIT 0x1FFFFFFF
struct pj_turn_sock
@@ -42,13 +46,13 @@ struct pj_turn_sock
pj_turn_sock_cb cb;
void *user_data;
- pj_lock_t *lock;
+ pj_bool_t is_destroying;
+ pj_grp_lock_t *grp_lock;
pj_turn_alloc_param alloc_param;
pj_stun_config cfg;
pj_turn_sock_cfg setting;
- pj_bool_t destroy_request;
pj_timer_entry timer;
int af;
@@ -89,6 +93,7 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
+static void turn_sock_on_destroy(void *comp);
static void destroy(pj_turn_sock *turn_sock);
static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e);
@@ -97,10 +102,12 @@ static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e);
PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg)
{
pj_bzero(cfg, sizeof(*cfg));
+ cfg->max_pkt_size = PJ_TURN_MAX_PKT_LEN;
cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT;
cfg->qos_ignore_error = PJ_TRUE;
}
+
/*
* Create.
*/
@@ -162,14 +169,21 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
pj_memcpy(&turn_sock->cb, cb, sizeof(*cb));
}
- /* Create lock */
- status = pj_lock_create_recursive_mutex(pool, turn_sock->obj_name,
- &turn_sock->lock);
- if (status != PJ_SUCCESS) {
- destroy(turn_sock);
- return status;
+ /* Session lock */
+ if (setting && setting->grp_lock) {
+ turn_sock->grp_lock = setting->grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &turn_sock->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
}
+ pj_grp_lock_add_ref(turn_sock->grp_lock);
+ pj_grp_lock_add_handler(turn_sock->grp_lock, pool, turn_sock,
+ &turn_sock_on_destroy);
+
/* Init timer */
pj_timer_entry_init(&turn_sock->timer, TIMER_NONE, turn_sock, &timer_cb);
@@ -180,7 +194,8 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
sess_cb.on_rx_data = &turn_on_rx_data;
sess_cb.on_state = &turn_on_state;
status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type,
- &sess_cb, 0, turn_sock, &turn_sock->sess);
+ turn_sock->grp_lock, &sess_cb, 0,
+ turn_sock, &turn_sock->sess);
if (status != PJ_SUCCESS) {
destroy(turn_sock);
return status;
@@ -197,41 +212,45 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
/*
* Destroy.
*/
-static void destroy(pj_turn_sock *turn_sock)
+static void turn_sock_on_destroy(void *comp)
{
- if (turn_sock->lock) {
- pj_lock_acquire(turn_sock->lock);
- }
-
- if (turn_sock->sess) {
- pj_turn_session_set_user_data(turn_sock->sess, NULL);
- pj_turn_session_shutdown(turn_sock->sess);
- turn_sock->sess = NULL;
- }
-
- if (turn_sock->active_sock) {
- pj_activesock_close(turn_sock->active_sock);
- turn_sock->active_sock = NULL;
- }
-
- if (turn_sock->lock) {
- pj_lock_release(turn_sock->lock);
- pj_lock_destroy(turn_sock->lock);
- turn_sock->lock = NULL;
- }
+ pj_turn_sock *turn_sock = (pj_turn_sock*) comp;
if (turn_sock->pool) {
pj_pool_t *pool = turn_sock->pool;
+ PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroyed"));
turn_sock->pool = NULL;
pj_pool_release(pool);
}
}
+static void destroy(pj_turn_sock *turn_sock)
+{
+ PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroy request, ref_cnt=%d",
+ pj_grp_lock_get_ref(turn_sock->grp_lock)));
+
+ pj_grp_lock_acquire(turn_sock->grp_lock);
+ if (turn_sock->is_destroying) {
+ pj_grp_lock_release(turn_sock->grp_lock);
+ return;
+ }
+
+ turn_sock->is_destroying = PJ_TRUE;
+ if (turn_sock->sess)
+ pj_turn_session_shutdown(turn_sock->sess);
+ if (turn_sock->active_sock)
+ pj_activesock_close(turn_sock->active_sock);
+ pj_grp_lock_dec_ref(turn_sock->grp_lock);
+ pj_grp_lock_release(turn_sock->grp_lock);
+}
PJ_DEF(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock)
{
- pj_lock_acquire(turn_sock->lock);
- turn_sock->destroy_request = PJ_TRUE;
+ pj_grp_lock_acquire(turn_sock->grp_lock);
+ if (turn_sock->is_destroying) {
+ pj_grp_lock_release(turn_sock->grp_lock);
+ return;
+ }
if (turn_sock->sess) {
pj_turn_session_shutdown(turn_sock->sess);
@@ -239,12 +258,11 @@ PJ_DEF(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock)
* session state is DESTROYING we will schedule a timer to
* destroy ourselves.
*/
- pj_lock_release(turn_sock->lock);
} else {
- pj_lock_release(turn_sock->lock);
destroy(turn_sock);
}
+ pj_grp_lock_release(turn_sock->grp_lock);
}
@@ -260,7 +278,6 @@ static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e)
switch (eid) {
case TIMER_DESTROY:
- PJ_LOG(5,(turn_sock->obj_name, "Destroying TURN"));
destroy(turn_sock);
break;
default:
@@ -330,7 +347,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
*/
PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
{
- return pj_lock_acquire(turn_sock->lock);
+ return pj_grp_lock_acquire(turn_sock->grp_lock);
}
/**
@@ -338,7 +355,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
*/
PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock)
{
- return pj_lock_release(turn_sock->lock);
+ return pj_grp_lock_release(turn_sock->grp_lock);
}
/*
@@ -374,6 +391,8 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
PJ_ASSERT_RETURN(turn_sock && domain, PJ_EINVAL);
PJ_ASSERT_RETURN(turn_sock->sess, PJ_EINVALIDOP);
+ pj_grp_lock_acquire(turn_sock->grp_lock);
+
/* Copy alloc param. We will call session_alloc() only after the
* server address has been resolved.
*/
@@ -388,6 +407,7 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
status = pj_turn_session_set_credential(turn_sock->sess, cred);
if (status != PJ_SUCCESS) {
sess_fail(turn_sock, "Error setting credential", status);
+ pj_grp_lock_release(turn_sock->grp_lock);
return status;
}
}
@@ -397,13 +417,14 @@ PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
resolver);
if (status != PJ_SUCCESS) {
sess_fail(turn_sock, "Error setting TURN server", status);
+ pj_grp_lock_release(turn_sock->grp_lock);
return status;
}
/* Done for now. The next work will be done when session state moved
* to RESOLVED state.
*/
-
+ pj_grp_lock_release(turn_sock->grp_lock);
return PJ_SUCCESS;
}
@@ -462,9 +483,23 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
pj_turn_sock *turn_sock;
turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
+ if (!turn_sock)
+ return PJ_FALSE;
+
+ pj_grp_lock_acquire(turn_sock->grp_lock);
+
+ /* TURN session may have already been destroyed here.
+ * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557).
+ */
+ if (!turn_sock->sess) {
+ sess_fail(turn_sock, "TURN session already destroyed", status);
+ pj_grp_lock_release(turn_sock->grp_lock);
+ return PJ_FALSE;
+ }
if (status != PJ_SUCCESS) {
sess_fail(turn_sock, "TCP connect() error", status);
+ pj_grp_lock_release(turn_sock->grp_lock);
return PJ_FALSE;
}
@@ -474,7 +509,7 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
/* Kick start pending read operation */
status = pj_activesock_start_read(asock, turn_sock->pool,
- PJ_TURN_MAX_PKT_LEN, 0);
+ turn_sock->setting.max_pkt_size, 0);
/* Init send_key */
pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key));
@@ -483,9 +518,11 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param);
if (status != PJ_SUCCESS) {
sess_fail(turn_sock, "Error sending ALLOCATE", status);
+ pj_grp_lock_release(turn_sock->grp_lock);
return PJ_FALSE;
}
+ pj_grp_lock_release(turn_sock->grp_lock);
return PJ_TRUE;
}
@@ -545,9 +582,9 @@ static pj_bool_t on_data_read(pj_activesock_t *asock,
pj_bool_t ret = PJ_TRUE;
turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
- pj_lock_acquire(turn_sock->lock);
+ pj_grp_lock_acquire(turn_sock->grp_lock);
- if (status == PJ_SUCCESS && turn_sock->sess) {
+ if (status == PJ_SUCCESS && turn_sock->sess && !turn_sock->is_destroying) {
/* Report incoming packet to TURN session, repeat while we have
* "packet" in the buffer (required for stream-oriented transports)
*/
@@ -597,7 +634,7 @@ static pj_bool_t on_data_read(pj_activesock_t *asock,
}
on_return:
- pj_lock_release(turn_sock->lock);
+ pj_grp_lock_release(turn_sock->grp_lock);
return ret;
}
@@ -617,7 +654,7 @@ static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
pj_ssize_t len = pkt_len;
pj_status_t status;
- if (turn_sock == NULL) {
+ if (turn_sock == NULL || turn_sock->is_destroying) {
/* We've been destroyed */
// https://trac.pjsip.org/repos/ticket/1316
//pj_assert(!"We should shutdown gracefully");
@@ -663,7 +700,7 @@ static void turn_on_rx_data(pj_turn_session *sess,
{
pj_turn_sock *turn_sock = (pj_turn_sock*)
pj_turn_session_get_user_data(sess);
- if (turn_sock == NULL) {
+ if (turn_sock == NULL || turn_sock->is_destroying) {
/* We've been destroyed */
return;
}
@@ -712,7 +749,10 @@ static void turn_on_state(pj_turn_session *sess,
char addrtxt[PJ_INET6_ADDRSTRLEN+8];
int sock_type;
pj_sock_t sock;
+ pj_activesock_cfg asock_cfg;
pj_activesock_cb asock_cb;
+ pj_sockaddr bound_addr, *cfg_bind_addr;
+ pj_uint16_t max_bind_retry;
/* Close existing connection, if any. This happens when
* we're switching to alternate TURN server when either TCP
@@ -738,7 +778,29 @@ static void turn_on_state(pj_turn_session *sess,
return;
}
- /* Apply QoS, if specified */
+ /* Bind socket */
+ cfg_bind_addr = &turn_sock->setting.bound_addr;
+ max_bind_retry = MAX_BIND_RETRY;
+ if (turn_sock->setting.port_range &&
+ turn_sock->setting.port_range < max_bind_retry)
+ {
+ max_bind_retry = turn_sock->setting.port_range;
+ }
+ pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0);
+ if (cfg_bind_addr->addr.sa_family == pj_AF_INET() ||
+ cfg_bind_addr->addr.sa_family == pj_AF_INET6())
+ {
+ pj_sockaddr_cp(&bound_addr, cfg_bind_addr);
+ }
+ status = pj_sock_bind_random(sock, &bound_addr,
+ turn_sock->setting.port_range,
+ max_bind_retry);
+ if (status != PJ_SUCCESS) {
+ pj_turn_sock_destroy(turn_sock);
+ return;
+ }
+
+ /* Apply QoS, if specified */
status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type,
&turn_sock->setting.qos_params,
(turn_sock->setting.qos_ignore_error?2:1),
@@ -749,11 +811,14 @@ static void turn_on_state(pj_turn_session *sess,
}
/* Create active socket */
+ pj_activesock_cfg_default(&asock_cfg);
+ asock_cfg.grp_lock = turn_sock->grp_lock;
+
pj_bzero(&asock_cb, sizeof(asock_cb));
asock_cb.on_data_read = &on_data_read;
asock_cb.on_connect_complete = &on_connect_complete;
status = pj_activesock_create(turn_sock->pool, sock,
- sock_type, NULL,
+ sock_type, &asock_cfg,
turn_sock->cfg.ioqueue, &asock_cb,
turn_sock,
&turn_sock->active_sock);
@@ -794,14 +859,12 @@ static void turn_on_state(pj_turn_session *sess,
turn_sock->sess = NULL;
pj_turn_session_set_user_data(sess, NULL);
- if (turn_sock->timer.id) {
- pj_timer_heap_cancel(turn_sock->cfg.timer_heap, &turn_sock->timer);
- turn_sock->timer.id = 0;
- }
-
- turn_sock->timer.id = TIMER_DESTROY;
- pj_timer_heap_schedule(turn_sock->cfg.timer_heap, &turn_sock->timer,
- &delay);
+ pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap,
+ &turn_sock->timer, 0);
+ pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap,
+ &turn_sock->timer,
+ &delay, TIMER_DESTROY,
+ turn_sock->grp_lock);
}
}
diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
index 9371635..f267bf8 100644
--- a/pjnath/src/pjturn-srv/allocation.c
+++ b/pjnath/src/pjturn-srv/allocation.c
@@ -1,4 +1,4 @@
-/* $Id: allocation.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: allocation.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -338,7 +338,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport,
sess_cb.on_rx_request = &stun_on_rx_request;
sess_cb.on_rx_indication = &stun_on_rx_indication;
status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name,
- &sess_cb, PJ_FALSE, &alloc->sess);
+ &sess_cb, PJ_FALSE, NULL, &alloc->sess);
if (status != PJ_SUCCESS) {
goto on_error;
}
diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c
index 3732898..f27d3a9 100644
--- a/pjnath/src/pjturn-srv/server.c
+++ b/pjnath/src/pjturn-srv/server.c
@@ -1,4 +1,4 @@
-/* $Id: server.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: server.c 4360 2013-02-21 11:26:35Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -155,7 +155,8 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
sess_cb.on_send_msg = &on_tx_stun_msg;
status = pj_stun_session_create(&srv->core.stun_cfg, srv->obj_name,
- &sess_cb, PJ_FALSE, &srv->core.stun_sess);
+ &sess_cb, PJ_FALSE, NULL,
+ &srv->core.stun_sess);
if (status != PJ_SUCCESS) {
goto on_error;
}
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 6a4f9a1..c7264c9 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_app.c 4129 2012-05-17 08:27:46Z nanang $ */
+/* $Id: pjsua_app.c 4358 2013-02-20 21:00:42Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -170,7 +170,6 @@ static char some_buf[SOME_BUF_SIZE];
#ifdef STEREO_DEMO
static void stereo_demo();
#endif
-static pj_status_t create_ipv6_media_transports(void);
pj_status_t app_destroy(void);
static void ringback_start(pjsua_call_id call_id);
@@ -194,9 +193,13 @@ void keepAliveFunction(int timeout)
if (!pjsua_acc_is_valid(i))
continue;
- if (app_config.acc_cfg[i].reg_timeout < timeout)
+ if (app_config.acc_cfg[i].reg_timeout < timeout) {
+ pjsua_acc_get_config(i, &app_config.acc_cfg[i]);
app_config.acc_cfg[i].reg_timeout = timeout;
- pjsua_acc_set_registration(i, PJ_TRUE);
+ pjsua_acc_modify(i, &app_config.acc_cfg[i]);
+ } else {
+ pjsua_acc_set_registration(i, PJ_TRUE);
+ }
}
}
#endif
@@ -224,14 +227,11 @@ static void usage(void)
puts ("");
puts ("SIP Account options:");
- puts (" --use-ims Enable 3GPP/IMS related settings on this account");
-#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
- puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory,");
- puts (" 3:optional by duplicating media offer (def:0)");
- puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 2:sips (def:1)");
-#endif
puts (" --registrar=url Set the URL of registrar server");
puts (" --id=url Set the URL of local ID (used in From header)");
+ puts (" --realm=string Set realm");
+ puts (" --username=string Set authentication username");
+ puts (" --password=string Set authentication password");
puts (" --contact=url Optionally override the Contact information");
puts (" --contact-params=S Append the specified parameters S in Contact header");
puts (" --contact-uri-params=S Append the specified parameters S in Contact URI");
@@ -243,11 +243,14 @@ static void usage(void)
PJSUA_REG_RETRY_INTERVAL);
puts (" --reg-use-proxy=N Control the use of proxy settings in REGISTER.");
puts (" 0=no proxy, 1=outbound only, 2=acc only, 3=all (default)");
- puts (" --realm=string Set realm");
- puts (" --username=string Set authentication username");
- puts (" --password=string Set authentication password");
puts (" --publish Send presence PUBLISH for this account");
puts (" --mwi Subscribe to message summary/waiting indication");
+ puts (" --use-ims Enable 3GPP/IMS related settings on this account");
+#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+ puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory,");
+ puts (" 3:optional by duplicating media offer (def:0)");
+ puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 2:sips (def:1)");
+#endif
puts (" --use-100rel Require reliable provisional response (100rel)");
puts (" --use-timer=N Use SIP session timers? (default=1)");
puts (" 0:inactive, 1:optional, 2:mandatory, 3:always");
@@ -257,6 +260,7 @@ static void usage(void)
puts (" --outb-rid=string Set SIP outbound reg-id (default:1)");
puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
puts (" symmetric NAT (default 1)");
+ puts (" --disable-stun Disable STUN for this account");
puts (" --next-cred Add another credentials");
puts ("");
puts ("SIP Account Control:");
@@ -340,7 +344,7 @@ static void usage(void)
puts ("Video Options:");
puts (" --video Enable video");
puts (" --vcapture-dev=id Video capture device ID (default=-1)");
- puts (" --vrender-dev=id Video render device ID (default=-1)");
+ puts (" --vrender-dev=id Video render device ID (default=-2)");
puts (" --play-avi=FILE Load this AVI as virtual capture device");
puts (" --auto-play-avi Automatically play the AVI media to call");
#endif
@@ -373,7 +377,8 @@ static void usage(void)
puts (" --use-compact-form Minimize SIP message size");
puts (" --no-force-lr Allow strict-route to be used (i.e. do not force lr)");
puts (" --accept-redirect=N Specify how to handle call redirect (3xx) response.");
- puts (" 0: reject, 1: follow automatically (default), 2: ask");
+ puts (" 0: reject, 1: follow automatically,");
+ puts (" 2: follow + replace To header (default), 3: ask");
puts ("");
puts ("When URL is specified, pjsua will immediately initiate call to that URL");
@@ -400,7 +405,7 @@ static void default_config(struct app_config *cfg)
cfg->udp_cfg.port = 5060;
pjsua_transport_config_default(&cfg->rtp_cfg);
cfg->rtp_cfg.port = 4000;
- cfg->redir_op = PJSIP_REDIRECT_ACCEPT;
+ cfg->redir_op = PJSIP_REDIRECT_ACCEPT_REPLACE;
cfg->duration = NO_LIMIT;
cfg->wav_id = PJSUA_INVALID_ID;
cfg->rec_id = PJSUA_INVALID_ID;
@@ -589,7 +594,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_STDOUT_NO_BUF,
#endif
OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
- OPT_NO_FORCE_LR,
+ OPT_DISABLE_STUN, OPT_NO_FORCE_LR,
OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE,
OPT_VIDEO, OPT_EXTRA_AUDIO,
OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI
@@ -629,6 +634,7 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "contact-params",1,0, OPT_CONTACT_PARAMS},
{ "contact-uri-params",1,0, OPT_CONTACT_URI_PARAMS},
{ "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
+ { "disable-stun",0,0, OPT_DISABLE_STUN},
{ "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
{ "accept-redirect", 1, 0, OPT_ACCEPT_REDIRECT},
{ "no-force-lr",0, 0, OPT_NO_FORCE_LR},
@@ -874,8 +880,8 @@ static pj_status_t parse_args(int argc, char *argv[],
break;
case OPT_NO_UDP: /* no-udp */
- if (cfg->no_tcp) {
- PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
+ if (cfg->no_tcp && !cfg->use_tls) {
+ PJ_LOG(1,(THIS_FILE,"Error: cannot disable both TCP and UDP"));
return PJ_EINVAL;
}
@@ -887,8 +893,8 @@ static pj_status_t parse_args(int argc, char *argv[],
break;
case OPT_NO_TCP: /* no-tcp */
- if (cfg->no_udp) {
- PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
+ if (cfg->no_udp && !cfg->use_tls) {
+ PJ_LOG(1,(THIS_FILE,"Error: cannot disable both TCP and UDP"));
return PJ_EINVAL;
}
@@ -1021,6 +1027,11 @@ static pj_status_t parse_args(int argc, char *argv[],
cur_acc->allow_contact_rewrite = pj_strtoul(pj_cstr(&tmp, pj_optarg));
break;
+ case OPT_DISABLE_STUN:
+ cur_acc->sip_stun_use = PJSUA_STUN_USE_DISABLED;
+ cur_acc->media_stun_use = PJSUA_STUN_USE_DISABLED;
+ break;
+
case OPT_USE_COMPACT_FORM:
/* enable compact form - from Ticket #342 */
{
@@ -1171,42 +1182,54 @@ static pj_status_t parse_args(int argc, char *argv[],
break;
case OPT_USE_ICE:
- cfg->media_cfg.enable_ice = PJ_TRUE;
+ cfg->media_cfg.enable_ice =
+ cur_acc->ice_cfg.enable_ice = PJ_TRUE;
break;
case OPT_ICE_REGULAR:
- cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
+ cfg->media_cfg.ice_opt.aggressive =
+ cur_acc->ice_cfg.ice_opt.aggressive = PJ_FALSE;
break;
case OPT_USE_TURN:
- cfg->media_cfg.enable_turn = PJ_TRUE;
+ cfg->media_cfg.enable_turn =
+ cur_acc->turn_cfg.enable_turn = PJ_TRUE;
break;
case OPT_ICE_MAX_HOSTS:
- cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
+ cfg->media_cfg.ice_max_host_cands =
+ cur_acc->ice_cfg.ice_max_host_cands = my_atoi(pj_optarg);
break;
case OPT_ICE_NO_RTCP:
- cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
+ cfg->media_cfg.ice_no_rtcp =
+ cur_acc->ice_cfg.ice_no_rtcp = PJ_TRUE;
break;
case OPT_TURN_SRV:
- cfg->media_cfg.turn_server = pj_str(pj_optarg);
+ cfg->media_cfg.turn_server =
+ cur_acc->turn_cfg.turn_server = pj_str(pj_optarg);
break;
case OPT_TURN_TCP:
- cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
+ cfg->media_cfg.turn_conn_type =
+ cur_acc->turn_cfg.turn_conn_type = PJ_TURN_TP_TCP;
break;
case OPT_TURN_USER:
- cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
- cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
- cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
+ cfg->media_cfg.turn_auth_cred.type =
+ cur_acc->turn_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+ cfg->media_cfg.turn_auth_cred.data.static_cred.realm =
+ cur_acc->turn_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
+ cfg->media_cfg.turn_auth_cred.data.static_cred.username =
+ cur_acc->turn_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
break;
case OPT_TURN_PASSWD:
- cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
- cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
+ cfg->media_cfg.turn_auth_cred.data.static_cred.data_type =
+ cur_acc->turn_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+ cfg->media_cfg.turn_auth_cred.data.static_cred.data =
+ cur_acc->turn_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
break;
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
@@ -1577,6 +1600,13 @@ static pj_status_t parse_args(int argc, char *argv[],
acfg->cred_count++;
}
+ if (acfg->ice_cfg.enable_ice) {
+ acfg->ice_cfg_use = PJSUA_ICE_CONFIG_USE_CUSTOM;
+ }
+ if (acfg->turn_cfg.enable_turn) {
+ acfg->turn_cfg_use = PJSUA_TURN_CONFIG_USE_CUSTOM;
+ }
+
/* When IMS mode is enabled for the account, verify that settings
* are okay.
*/
@@ -1771,6 +1801,55 @@ static void write_account_settings(int acc_index, pj_str_t *result)
/* MWI */
if (acc_cfg->mwi_enabled)
pj_strcat2(result, "--mwi\n");
+
+ if (acc_cfg->sip_stun_use != PJSUA_STUN_USE_DEFAULT ||
+ acc_cfg->media_stun_use != PJSUA_STUN_USE_DEFAULT)
+ {
+ pj_strcat2(result, "--disable-stun\n");
+ }
+
+ /* Media Transport*/
+ if (acc_cfg->ice_cfg.enable_ice)
+ pj_strcat2(result, "--use-ice\n");
+
+ if (acc_cfg->ice_cfg.ice_opt.aggressive == PJ_FALSE)
+ pj_strcat2(result, "--ice-regular\n");
+
+ if (acc_cfg->turn_cfg.enable_turn)
+ pj_strcat2(result, "--use-turn\n");
+
+ if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) {
+ pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
+ acc_cfg->ice_cfg.ice_max_host_cands);
+ pj_strcat2(result, line);
+ }
+
+ if (acc_cfg->ice_cfg.ice_no_rtcp)
+ pj_strcat2(result, "--ice-no-rtcp\n");
+
+ if (acc_cfg->turn_cfg.turn_server.slen) {
+ pj_ansi_sprintf(line, "--turn-srv %.*s\n",
+ (int)acc_cfg->turn_cfg.turn_server.slen,
+ acc_cfg->turn_cfg.turn_server.ptr);
+ pj_strcat2(result, line);
+ }
+
+ if (acc_cfg->turn_cfg.turn_conn_type == PJ_TURN_TP_TCP)
+ pj_strcat2(result, "--turn-tcp\n");
+
+ if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen) {
+ pj_ansi_sprintf(line, "--turn-user %.*s\n",
+ (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen,
+ acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.ptr);
+ pj_strcat2(result, line);
+ }
+
+ if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen) {
+ pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
+ (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen,
+ acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.ptr);
+ pj_strcat2(result, line);
+ }
}
@@ -1967,49 +2046,6 @@ static int write_settings(const struct app_config *config,
}
#endif
- /* Media Transport*/
- if (config->media_cfg.enable_ice)
- pj_strcat2(&cfg, "--use-ice\n");
-
- if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
- pj_strcat2(&cfg, "--ice-regular\n");
-
- if (config->media_cfg.enable_turn)
- pj_strcat2(&cfg, "--use-turn\n");
-
- if (config->media_cfg.ice_max_host_cands >= 0) {
- pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
- config->media_cfg.ice_max_host_cands);
- pj_strcat2(&cfg, line);
- }
-
- if (config->media_cfg.ice_no_rtcp)
- pj_strcat2(&cfg, "--ice-no-rtcp\n");
-
- if (config->media_cfg.turn_server.slen) {
- pj_ansi_sprintf(line, "--turn-srv %.*s\n",
- (int)config->media_cfg.turn_server.slen,
- config->media_cfg.turn_server.ptr);
- pj_strcat2(&cfg, line);
- }
-
- if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
- pj_strcat2(&cfg, "--turn-tcp\n");
-
- if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
- pj_ansi_sprintf(line, "--turn-user %.*s\n",
- (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
- config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
- pj_strcat2(&cfg, line);
- }
-
- if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
- pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
- (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
- config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
- pj_strcat2(&cfg, line);
- }
-
/* Media */
if (config->null_audio)
pj_strcat2(&cfg, "--null-audio\n");
@@ -2206,7 +2242,7 @@ static int write_settings(const struct app_config *config,
}
/* accept-redirect */
- if (config->redir_op != PJSIP_REDIRECT_ACCEPT) {
+ if (config->redir_op != PJSIP_REDIRECT_ACCEPT_REPLACE) {
pj_ansi_sprintf(line, "--accept-redirect %d\n",
config->redir_op);
pj_strcat2(&cfg, line);
@@ -3016,8 +3052,8 @@ static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id,
}
PJ_LOG(3,(THIS_FILE, "Call %d is being redirected to %.*s. "
- "Press 'Ra' to accept, 'Rr' to reject, or 'Rd' to "
- "disconnect.",
+ "Press 'Ra' to accept+replace To header, 'RA' to accept, "
+ "'Rr' to reject, or 'Rd' to disconnect.",
call_id, len, uristr));
}
@@ -4339,7 +4375,7 @@ static void vid_handle_menu(char *menuin)
status = pjsua_vid_codec_set_param(&cid, &cp);
}
if (status != PJ_SUCCESS)
- PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
+ PJ_PERROR(1,(THIS_FILE, status, "Set codec size error"));
} else
goto on_error;
} else
@@ -4366,7 +4402,7 @@ static void app_config_init_video(pjsua_acc_config *acc_cfg)
*/
void console_app_main(const pj_str_t *uri_to_call)
{
- char menuin[32];
+ char menuin[80];
char buf[128];
char text[128];
int i, count;
@@ -5346,6 +5382,9 @@ void console_app_main(const pj_str_t *uri_to_call)
PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
} else if (menuin[1] == 'a') {
pjsua_call_process_redirect(current_call,
+ PJSIP_REDIRECT_ACCEPT_REPLACE);
+ } else if (menuin[1] == 'A') {
+ pjsua_call_process_redirect(current_call,
PJSIP_REDIRECT_ACCEPT);
} else if (menuin[1] == 'r') {
pjsua_call_process_redirect(current_call,
@@ -5875,6 +5914,8 @@ pj_status_t app_init(int argc, char *argv[])
pjsua_acc_config acc_cfg;
pjsua_acc_get_config(aid, &acc_cfg);
app_config_init_video(&acc_cfg);
+ if (app_config.ipv6)
+ acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
pjsua_acc_modify(aid, &acc_cfg);
}
//pjsua_acc_set_transport(aid, transport_id);
@@ -5913,6 +5954,33 @@ pj_status_t app_init(int argc, char *argv[])
}
+ /* Add TCP IPv6 transport unless it's disabled. */
+ if (!app_config.no_tcp && app_config.ipv6) {
+ pjsua_acc_id aid;
+ pjsip_transport_type_e type = PJSIP_TRANSPORT_TCP6;
+
+ tcp_cfg.port += 10;
+
+ status = pjsua_transport_create(type,
+ &tcp_cfg,
+ &transport_id);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Add local account */
+ pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
+ if (PJMEDIA_HAS_VIDEO) {
+ pjsua_acc_config acc_cfg;
+ pjsua_acc_get_config(aid, &acc_cfg);
+ app_config_init_video(&acc_cfg);
+ if (app_config.ipv6)
+ acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
+ pjsua_acc_modify(aid, &acc_cfg);
+ }
+ //pjsua_acc_set_transport(aid, transport_id);
+ pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+ }
+
#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
/* Add TLS transport when application wants one */
@@ -5944,6 +6012,34 @@ pj_status_t app_init(int argc, char *argv[])
}
pjsua_acc_set_online_status(acc_id, PJ_TRUE);
}
+
+ /* Add TLS IPv6 transport unless it's disabled. */
+ if (app_config.use_tls && app_config.ipv6) {
+ pjsua_acc_id aid;
+ pjsip_transport_type_e type = PJSIP_TRANSPORT_TLS6;
+
+ tcp_cfg.port += 10;
+
+ status = pjsua_transport_create(type,
+ &tcp_cfg,
+ &transport_id);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Add local account */
+ pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
+ if (PJMEDIA_HAS_VIDEO) {
+ pjsua_acc_config acc_cfg;
+ pjsua_acc_get_config(aid, &acc_cfg);
+ app_config_init_video(&acc_cfg);
+ if (app_config.ipv6)
+ acc_cfg.ipv6_media_use = PJSUA_IPV6_ENABLED;
+ pjsua_acc_modify(aid, &acc_cfg);
+ }
+ //pjsua_acc_set_transport(aid, transport_id);
+ pjsua_acc_set_online_status(current_acc, PJ_TRUE);
+ }
+
#endif
if (transport_id == -1) {
@@ -5994,16 +6090,6 @@ pj_status_t app_init(int argc, char *argv[])
#endif
}
- /* Add RTP transports */
- if (app_config.ipv6)
- status = create_ipv6_media_transports();
- #if DISABLED_FOR_TICKET_1185
- else
- status = pjsua_media_transports_create(&app_config.rtp_cfg);
- #endif
- if (status != PJ_SUCCESS)
- goto on_error;
-
/* Use null sound device? */
#ifndef STEREO_DEMO
if (app_config.null_audio) {
@@ -6219,94 +6305,3 @@ static void stereo_demo()
}
#endif
-static pj_status_t create_ipv6_media_transports(void)
-{
- pjsua_media_transport tp[PJSUA_MAX_CALLS];
- pj_status_t status;
- int port = app_config.rtp_cfg.port;
- unsigned i;
-
- for (i=0; i<app_config.cfg.max_calls; ++i) {
- enum { MAX_RETRY = 10 };
- pj_sock_t sock[2];
- pjmedia_sock_info si;
- unsigned j;
-
- /* Get rid of uninitialized var compiler warning with MSVC */
- status = PJ_SUCCESS;
-
- for (j=0; j<MAX_RETRY; ++j) {
- unsigned k;
-
- for (k=0; k<2; ++k) {
- pj_sockaddr bound_addr;
-
- status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
- if (status != PJ_SUCCESS)
- break;
-
- status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
- &app_config.rtp_cfg.bound_addr,
- (unsigned short)(port+k));
- if (status != PJ_SUCCESS)
- break;
-
- status = pj_sock_bind(sock[k], &bound_addr,
- pj_sockaddr_get_len(&bound_addr));
- if (status != PJ_SUCCESS)
- break;
- }
- if (status != PJ_SUCCESS) {
- if (k==1)
- pj_sock_close(sock[0]);
-
- if (port != 0)
- port += 10;
- else
- break;
-
- continue;
- }
-
- pj_bzero(&si, sizeof(si));
- si.rtp_sock = sock[0];
- si.rtcp_sock = sock[1];
-
- pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name,
- &app_config.rtp_cfg.public_addr,
- (unsigned short)(port));
- pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name,
- &app_config.rtp_cfg.public_addr,
- (unsigned short)(port+1));
-
- status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
- NULL,
- &si,
- 0,
- &tp[i].transport);
- if (port != 0)
- port += 10;
- else
- break;
-
- if (status == PJ_SUCCESS)
- break;
- }
-
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error creating IPv6 UDP media transport",
- status);
- for (j=0; j<i; ++j) {
- pjmedia_transport_close(tp[j].transport);
- }
- return status;
- }
- }
-
-#if DISABLED_FOR_TICKET_1185
- return pjsua_media_transports_attach(tp, i, PJ_TRUE);
-#else
- return PJ_ENOTSUP;
-#endif
-}
-
diff --git a/pjsip-apps/src/python/setup.py b/pjsip-apps/src/python/setup.py
index ae22491..9eaed9d 100644
--- a/pjsip-apps/src/python/setup.py
+++ b/pjsip-apps/src/python/setup.py
@@ -1,4 +1,4 @@
-# $Id: setup.py 4122 2012-05-14 11:04:46Z bennylp $
+# $Id: setup.py 4232 2012-08-20 06:01:41Z ming $
#
# pjsua Setup script.
#
@@ -87,8 +87,9 @@ f.close()
if platform.system() == 'Darwin':
extra_link_args = ["-framework", "CoreFoundation",
"-framework", "AudioToolbox"]
- # OS X Lion support
- if platform.mac_ver()[0].startswith("10.7"):
+ version = platform.mac_ver()[0].split(".")
+ # OS X Lion (10.7.x) or above support
+ if version[0] == '10' and int(version[1]) >= 7:
extra_link_args += ["-framework", "AudioUnit"]
else:
extra_link_args = []
diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c
index 08292ee..598ab1b 100644
--- a/pjsip-apps/src/samples/icedemo.c
+++ b/pjsip-apps/src/samples/icedemo.c
@@ -1,4 +1,4 @@
-/* $Id: icedemo.c 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: icedemo.c 4217 2012-07-27 17:24:12Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -614,6 +614,7 @@ static int encode_session(char buffer[], unsigned maxlen)
}
/* Enumerate all candidates for this component */
+ cand_cnt = PJ_ARRAY_SIZE(cand);
status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
&cand_cnt, cand);
if (status != PJ_SUCCESS)
diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c
index f1b7cb9..8918dd9 100644
--- a/pjsip-apps/src/samples/pjsip-perf.c
+++ b/pjsip-apps/src/samples/pjsip-perf.c
@@ -1,4 +1,4 @@
-/* $Id: pjsip-perf.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: pjsip-perf.c 4370 2013-02-26 05:30:00Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -219,8 +219,8 @@ static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
- /* Only want to receive SIP scheme */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri))
+ /* Only want to receive SIP/SIPS scheme */
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
return PJ_FALSE;
sip_uri = (pjsip_sip_uri*) uri;
@@ -280,8 +280,8 @@ static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
- /* Only want to receive SIP scheme */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri))
+ /* Only want to receive SIP/SIPS scheme */
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
return PJ_FALSE;
sip_uri = (pjsip_sip_uri*) uri;
@@ -417,8 +417,8 @@ static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
- /* Only want to receive SIP scheme */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri))
+ /* Only want to receive SIP/SIPS scheme */
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
return PJ_FALSE;
sip_uri = (pjsip_sip_uri*) uri;
diff --git a/pjsip-apps/src/samples/proxy.h b/pjsip-apps/src/samples/proxy.h
index 2e1b383..257fd1f 100644
--- a/pjsip-apps/src/samples/proxy.h
+++ b/pjsip-apps/src/samples/proxy.h
@@ -1,4 +1,4 @@
-/* $Id: proxy.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: proxy.h 4370 2013-02-26 05:30:00Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -364,9 +364,11 @@ static pj_status_t proxy_verify_request(pjsip_rx_data *rdata)
*/
/* 2. URI scheme.
- * We only want to support "sip:" URI scheme for this simple proxy.
+ * We only want to support "sip:"/"sips:" URI scheme for this simple proxy.
*/
- if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.msg->line.req.uri)) {
+ if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.msg->line.req.uri) &&
+ !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
+ {
pjsip_endpt_respond_stateless(global.endpt, rdata,
PJSIP_SC_UNSUPPORTED_URI_SCHEME, NULL,
NULL, NULL);
diff --git a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
index 79e81f2..a0a5431 100644
--- a/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
+++ b/pjsip-apps/src/symbian_ua_gui/src/symbian_ua.cpp
@@ -34,7 +34,6 @@ static RConnection aConn;
static pjsua_acc_id g_acc_id = PJSUA_INVALID_ID;
static pjsua_call_id g_call_id = PJSUA_INVALID_ID;
-static pjsua_buddy_id g_buddy_id = PJSUA_INVALID_ID;
static symbian_ua_info_cb_t g_cb = {NULL, NULL, NULL, NULL, NULL};
diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile
index 2605be0..0f8fd13 100644
--- a/pjsip/build/Makefile
+++ b/pjsip/build/Makefile
@@ -138,7 +138,7 @@ pjsip-simple:
pjsua-lib:
$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $(PJSUA_LIB_LIB)
-pjsip-test:
+pjsip-test: pjsip
$(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(TEST_EXE)
.PHONY: ../lib/pjsip.ko
diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h
index 613e0d5..cd59372 100644
--- a/pjsip/include/pjsip/sip_auth.h
+++ b/pjsip/include/pjsip/sip_auth.h
@@ -1,4 +1,4 @@
-/* $Id: sip_auth.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_auth.h 4214 2012-07-25 14:29:28Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -275,6 +275,37 @@ typedef pj_status_t pjsip_auth_lookup_cred( pj_pool_t *pool,
const pj_str_t *acc_name,
pjsip_cred_info *cred_info );
+
+/**
+ * This structure describes input param for credential lookup.
+ */
+typedef struct pjsip_auth_lookup_cred_param
+{
+ pj_str_t realm; /**< Realm to find the account. */
+ pj_str_t acc_name; /**< Account name to look for. */
+ pjsip_rx_data *rdata; /**< Incoming request to be authenticated. */
+
+} pjsip_auth_lookup_cred_param;
+
+
+/**
+ * Type of function to lookup credential for the specified name.
+ *
+ * @param pool Pool to initialize the credential info.
+ * @param param The input param for credential lookup.
+ * @param cred_info The structure to put the credential when it's found.
+ *
+ * @return The function MUST return PJ_SUCCESS when it found
+ * a correct credential for the specified account and
+ * realm. Otherwise it may return PJSIP_EAUTHACCNOTFOUND
+ * or PJSIP_EAUTHACCDISABLED.
+ */
+typedef pj_status_t pjsip_auth_lookup_cred2(
+ pj_pool_t *pool,
+ const pjsip_auth_lookup_cred_param *param,
+ pjsip_cred_info *cred_info );
+
+
/** Flag to specify that server is a proxy. */
#define PJSIP_AUTH_SRV_IS_PROXY 1
@@ -286,7 +317,8 @@ typedef struct pjsip_auth_srv
pj_str_t realm; /**< Realm to serve. */
pj_bool_t is_proxy; /**< Will issue 407 instead of 401 */
pjsip_auth_lookup_cred *lookup; /**< Lookup function. */
-
+ pjsip_auth_lookup_cred2 *lookup2; /**< Lookup function with additional
+ info in its input param. */
} pjsip_auth_srv;
@@ -434,6 +466,49 @@ PJ_DECL(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool,
/**
+ * This structure describes initialization settings of server authorization
+ * session.
+ */
+typedef struct pjsip_auth_srv_init_param
+{
+ /**
+ * Realm to be served by the server.
+ */
+ const pj_str_t *realm;
+
+ /**
+ * Account lookup function.
+ */
+ pjsip_auth_lookup_cred2 *lookup2;
+
+ /**
+ * Options, bitmask of:
+ * - PJSIP_AUTH_SRV_IS_PROXY: to specify that the server will authorize
+ * clients as a proxy server (instead of as UAS), which means that
+ * Proxy-Authenticate will be used instead of WWW-Authenticate.
+ */
+ unsigned options;
+
+} pjsip_auth_srv_init_param;
+
+
+/**
+ * Initialize server authorization session data structure to serve the
+ * specified realm and to use lookup_func function to look for the credential
+ * info.
+ *
+ * @param pool Pool used to initialize the authentication server.
+ * @param auth_srv The authentication server structure.
+ * @param param The initialization param.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_auth_srv_init2(
+ pj_pool_t *pool,
+ pjsip_auth_srv *auth_srv,
+ const pjsip_auth_srv_init_param *param);
+
+/**
* Request the authorization server framework to verify the authorization
* information in the specified request in rdata.
*
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index 9035b1e..bde0777 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -1,4 +1,4 @@
-/* $Id: sip_config.h 4172 2012-06-19 14:35:18Z nanang $ */
+/* $Id: sip_config.h 4285 2012-10-19 04:23:57Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -73,18 +73,41 @@ typedef struct pjsip_cfg_t
/**
* Specify port number should be allowed to appear in To and From
* header. Note that RFC 3261 disallow this, see Table 1 in section
- * 19.1.1 of the RFC. Default is PJSIP_ALLOW_PORT_IN_FROMTO_HDR.
+ * 19.1.1 of the RFC.
+ *
+ * Default is PJSIP_ALLOW_PORT_IN_FROMTO_HDR.
*/
pj_bool_t allow_port_in_fromto_hdr;
/**
+ * Accept call replace in early state when invite is not initiated
+ * by the user agent. RFC 3891 Section 3 disallows this, however,
+ * for better interoperability reason, this might be ignored.
+ *
+ * Default is PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE.
+ */
+ pj_bool_t accept_replace_in_early_state;
+
+ /**
+ * Allow hash character ('#') to appear in outgoing URIs. See
+ * https://trac.pjsip.org/repos/ticket/1569.
+ *
+ * Default is PJ_FALSE.
+ */
+ pj_bool_t allow_tx_hash_in_uri;
+
+ /**
* Disable rport in request.
+ *
+ * Default is PJ_FALSE.
*/
pj_bool_t disable_rport;
/**
* Disable automatic switching from UDP to TCP if outgoing request
- * is greater than 1300 bytes. See PJSIP_DONT_SWITCH_TO_TCP.
+ * is greater than 1300 bytes.
+ *
+ * Default is PJSIP_DONT_SWITCH_TO_TCP.
*/
pj_bool_t disable_tcp_switch;
@@ -269,6 +292,21 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
/**
+ * Accept call replace in early state when invite is not initiated
+ * by the user agent. RFC 3891 Section 3 disallows this, however,
+ * for better interoperability reason, this might be ignored.
+ *
+ * This option can also be controlled at run-time by the
+ * \a accept_replace_in_early_state setting in pjsip_cfg_t.
+ *
+ * Default is 0 (no).
+ */
+#ifndef PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE
+# define PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE 0
+#endif
+
+
+/**
* This setting controls the threshold of the UDP packet, which if it's
* larger than this value the request will be sent with TCP. This setting
* is useful only when PJSIP_DONT_SWITCH_TO_TCP is set to 0.
@@ -362,9 +400,13 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
* response is received, the response will be discarded since its Via
* sent-by now contains address that is different than the transport
* address.
+ *
+ * Update:
+ * As of version 2.1, the default value is 0. This change was part of
+ * https://trac.pjsip.org/repos/ticket/1412
*/
#ifndef PJSIP_CHECK_VIA_SENT_BY
-# define PJSIP_CHECK_VIA_SENT_BY 1
+# define PJSIP_CHECK_VIA_SENT_BY 0
#endif
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index ac3f525..63c65d7 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -1,4 +1,4 @@
-/* $Id: sip_endpoint.h 4154 2012-06-05 10:41:17Z bennylp $ */
+/* $Id: sip_endpoint.h 4275 2012-10-04 06:11:58Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -213,6 +213,77 @@ PJ_DECL(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
PJ_DECL(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
pjsip_module *module );
+/**
+ * This describes additional parameters to pjsip_endpt_process_rx_data()
+ * function. Application MUST call pjsip_process_rdata_param_default() to
+ * initialize this structure.
+ */
+typedef struct pjsip_process_rdata_param
+{
+ /**
+ * Specify the minimum priority number of the modules that are allowed
+ * to process the message. Default is zero to allow all modules to
+ * process the message.
+ */
+ unsigned start_prio;
+
+ /**
+ * Specify the pointer of the module where processing will start.
+ * The default is NULL, meaning processing will start from the start
+ * of the module list.
+ */
+ void *start_mod;
+
+ /**
+ * Set to N, then processing will start at Nth module after start
+ * module (where start module can be an explicit module as specified
+ * by \a start_mod or the start of module list when \a start_mod is
+ * NULL). For example, if set to 1, then processing will start from
+ * the next module after start module. Default is zero.
+ */
+ unsigned idx_after_start;
+
+ /**
+ * Print nothing to log. Default is PJ_FALSE.
+ */
+ pj_bool_t silent;
+
+} pjsip_process_rdata_param;
+
+/**
+ * Initialize with default.
+ *
+ * @param p The param.
+ */
+PJ_DECL(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p);
+
+/**
+ * Manually distribute the specified pjsip_rx_data to registered modules.
+ * Normally application does not need to call this function because received
+ * messages will be given to endpoint automatically by transports.
+ *
+ * Application can use this function when it has postponed the processing of
+ * an incoming message, for example to perform long operations such as
+ * database operation or to consult other servers to decide what to do with
+ * the message. In this case, application clones the original rdata, return
+ * from the callback, and perform the long operation. Upon completing the
+ * long operation, it resumes pjsip's module processing by calling this
+ * function, and then free the cloned rdata.
+ *
+ * @param endpt The endpoint instance.
+ * @param rdata The rdata to be distributed.
+ * @param p Optional pointer to param to specify from which module
+ * the processing should start.
+ * @param p_handled Optional pointer to receive last return value of
+ * module's \a on_rx_request() or \a on_rx_response()
+ * callback.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_endpt_process_rx_data(pjsip_endpoint *endpt,
+ pjsip_rx_data *rdata,
+ pjsip_process_rdata_param *p,
+ pj_bool_t *p_handled);
/**
* Create pool from the endpoint. All SIP components should allocate their
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index c8ea6fd..21b2238 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport.h 4173 2012-06-20 10:39:05Z ming $ */
+/* $Id: sip_transport.h 4275 2012-10-04 06:11:58Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -427,6 +427,42 @@ struct pjsip_rx_data
*/
PJ_DECL(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata);
+/**
+ * Clone pjsip_rx_data. This will duplicate the contents of
+ * pjsip_rx_data and add reference count to the transport.
+ * Once application has finished using the cloned pjsip_rx_data,
+ * it must release it by calling #pjsip_rx_data_free_cloned().
+ *
+ * By default (if flags is set to zero), this function copies the
+ * transport pointer in \a tp_info, duplicates the \a pkt_info,
+ * perform deep clone of the \a msg_info parts of the rdata, and
+ * fills the \a endpt_info (i.e. the \a mod_data) with zeros.
+ *
+ * @param src The source to be cloned.
+ * @param flags Optional flags. Must be zero for now.
+ * @param p_rdata Pointer to receive the cloned rdata.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pjsip_rx_data_clone(const pjsip_rx_data *src,
+ unsigned flags,
+ pjsip_rx_data **p_rdata);
+
+/**
+ * Free cloned pjsip_rx_data. This function must be and must only
+ * be called for a cloned pjsip_rx_data. Specifically, it must NOT
+ * be called for the original pjsip_rx_data that is returned by
+ * transports.
+ *
+ * This function will free the memory used by the pjsip_rx_data and
+ * decrement the transport reference counter.
+ *
+ * @param rdata The receive data buffer.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata);
+
/*****************************************************************************
*
@@ -1076,6 +1112,8 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
* In this implementation, it will only select the transport based on
* the transport type in the request.
*
+ * @see pjsip_tpmgr_find_local_addr2()
+ *
* @param tpmgr The transport manager.
* @param pool Pool to allocate memory for the IP address.
* @param type Destination address to contact.
@@ -1093,6 +1131,76 @@ PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
int *port);
/**
+ * Parameter for pjsip_tpmgr_find_local_addr2() function.
+ */
+typedef struct pjsip_tpmgr_fla2_param
+{
+ /**
+ * Specify transport type to use. This must be set.
+ */
+ pjsip_transport_type_e tp_type;
+
+ /**
+ * Optional pointer to preferred transport, if any.
+ */
+ const pjsip_tpselector *tp_sel;
+
+ /**
+ * Destination host, if known. The destination host is needed
+ * if \a local_if field below is set.
+ */
+ pj_str_t dst_host;
+
+ /**
+ * Specify if the function should return which local interface
+ * to use for the specified destination in \a dst_host. By definition,
+ * the returned address will always be local interface address.
+ */
+ pj_bool_t local_if;
+
+ /**
+ * The returned address.
+ */
+ pj_str_t ret_addr;
+
+ /**
+ * The returned port.
+ */
+ pj_uint16_t ret_port;
+
+ /**
+ * Returned pointer to the transport. Only set if local_if is set.
+ */
+ const void *ret_tp;
+
+} pjsip_tpmgr_fla2_param;
+
+/**
+ * Initialize with default values.
+ *
+ * @param prm The parameter to be initialized.
+ */
+PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm);
+
+/**
+ * Find out the appropriate local address info (IP address and port) to
+ * advertise in Contact or Via header header based on the remote address
+ * to be contacted. The local address info would be the address name of the
+ * transport or listener which will be used to send the request.
+ *
+ * @see pjsip_tpmgr_find_local_addr()
+ *
+ * @param tpmgr The transport manager.
+ * @param pool Pool to allocate memory for the IP address.
+ * @param param Function input and output parameters.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr,
+ pj_pool_t *pool,
+ pjsip_tpmgr_fla2_param *prm);
+
+/**
* Return number of transports currently registered to the transport
* manager.
*
diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h
index cbc1d75..8164c63 100644
--- a/pjsip/include/pjsip/sip_transport_tls.h
+++ b/pjsip/include/pjsip/sip_transport_tls.h
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tls.h 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: sip_transport_tls.h 4262 2012-09-20 06:00:23Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -262,6 +262,8 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool,
* instance of SIP TLS transport factory and register it to the
* transport manager.
*
+ * See also #pjsip_tls_transport_start2() which supports IPv6.
+ *
* @param endpt The SIP endpoint.
* @param opt Optional TLS settings.
* @param local Optional local address to bind, or specify the
@@ -294,7 +296,43 @@ PJ_DECL(pj_status_t) pjsip_tls_transport_start(pjsip_endpoint *endpt,
unsigned async_cnt,
pjsip_tpfactory **p_factory);
-
+/**
+ * Variant of #pjsip_tls_transport_start() that supports IPv6. To instantiate
+ * IPv6 listener, set the address family of the "local" argument to IPv6
+ * (the host and port part may be left unspecified if not desired, i.e. by
+ * filling them with zeroes).
+ *
+ * @param endpt The SIP endpoint.
+ * @param opt Optional TLS settings.
+ * @param local Optional local address to bind, or specify the
+ * address to bind the server socket to. Both IP
+ * interface address and port fields are optional.
+ * If IP interface address is not specified, socket
+ * will be bound to any address. If port is not
+ * specified, socket will be bound to any port
+ * selected by the operating system.
+ * @param a_name Optional published address, which is the address to be
+ * advertised as the address of this SIP transport.
+ * If this argument is NULL, then the bound address
+ * will be used as the published address.
+ * @param async_cnt Number of simultaneous asynchronous accept()
+ * operations to be supported. It is recommended that
+ * the number here corresponds to the number of
+ * processors in the system (or the number of SIP
+ * worker threads).
+ * @param p_factory Optional pointer to receive the instance of the
+ * SIP TLS transport factory just created.
+ *
+ * @return PJ_SUCCESS when the transport has been successfully
+ * started and registered to transport manager, or
+ * the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tls_transport_start2(pjsip_endpoint *endpt,
+ const pjsip_tls_setting *opt,
+ const pj_sockaddr *local,
+ const pjsip_host_port *a_name,
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory);
PJ_END_DECL
diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h
index 8cd98ae..9809983 100644
--- a/pjsip/include/pjsip/sip_types.h
+++ b/pjsip/include/pjsip/sip_types.h
@@ -1,4 +1,4 @@
-/* $Id: sip_types.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_types.h 4262 2012-09-20 06:00:23Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -92,7 +92,10 @@ typedef enum pjsip_transport_type_e
PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6,
/** TCP over IPv6 */
- PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6
+ PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6,
+
+ /** TLS over IPv6 */
+ PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6
} pjsip_transport_type_e;
diff --git a/pjsip/include/pjsip/sip_uri.h b/pjsip/include/pjsip/sip_uri.h
index 849392f..47b63bd 100644
--- a/pjsip/include/pjsip/sip_uri.h
+++ b/pjsip/include/pjsip/sip_uri.h
@@ -1,4 +1,4 @@
-/* $Id: sip_uri.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_uri.h 4370 2013-02-26 05:30:00Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -226,12 +226,12 @@ struct pjsip_uri
};
/**
- * This macro checks that the URL is a "sip:" or "sips:" URL.
+ * This macro checks that the URL is a "sip:" URL.
* @param url The URL (pointer to)
* @return non-zero if TRUE.
*/
#define PJSIP_URI_SCHEME_IS_SIP(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0)
+ (pj_stricmp2(pjsip_uri_get_scheme(url), "sip")==0)
/**
* This macro checks that the URL is a "sips:" URL (not SIP).
@@ -239,7 +239,7 @@ struct pjsip_uri
* @return non-zero if TRUE.
*/
#define PJSIP_URI_SCHEME_IS_SIPS(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0)
+ (pj_stricmp2(pjsip_uri_get_scheme(url), "sips")==0)
/**
* This macro checks that the URL is a "tel:" URL.
@@ -247,7 +247,7 @@ struct pjsip_uri
* @return non-zero if TRUE.
*/
#define PJSIP_URI_SCHEME_IS_TEL(url) \
- (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0)
+ (pj_stricmp2(pjsip_uri_get_scheme(url), "tel")==0)
/**
diff --git a/pjsip/include/pjsip/sip_util.h b/pjsip/include/pjsip/sip_util.h
index 6efa45f..018ad87 100644
--- a/pjsip/include/pjsip/sip_util.h
+++ b/pjsip/include/pjsip/sip_util.h
@@ -1,4 +1,4 @@
-/* $Id: sip_util.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_util.h 4347 2013-02-13 10:19:25Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -92,6 +92,13 @@ typedef enum pjsip_redirect_op
PJSIP_REDIRECT_ACCEPT,
/**
+ * Accept the redirection to the current target and replace the To
+ * header in the INVITE request with the current target. The INVITE
+ * request will be resent to the current target.
+ */
+ PJSIP_REDIRECT_ACCEPT_REPLACE,
+
+ /**
* Defer the redirection decision, for example to request permission
* from the end user.
*/
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 856d853..88c0d46 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1,4 +1,4 @@
-/* $Id: pjsua.h 4180 2012-06-26 09:37:41Z ming $ */
+/* $Id: pjsua.h 4347 2013-02-13 10:19:25Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -906,9 +906,10 @@ typedef struct pjsua_callback
* 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 \a code argument to 202, and
- * later calls #pjsua_pres_notify() to accept or reject the
- * subscription request.
+ * case, the application MUST set the \a code argument to 202, then
+ * IMMEDIATELY calls #pjsua_pres_notify() with state
+ * PJSIP_EVSUB_STATE_PENDING and later calls #pjsua_pres_notify()
+ * again to accept or reject the subscription request.
*
* Any \a code other than 200 and 202 will be treated as 200.
*
@@ -1124,9 +1125,8 @@ typedef struct pjsua_callback
* INVITE request to the specified target, following the previously
* received redirection response.
*
- * Application may accept the redirection to the specified target
- * (the default behavior if this callback is implemented), reject
- * this target only and make the session continue to try the next
+ * 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.
@@ -1146,9 +1146,12 @@ typedef struct pjsua_callback
* @return Action to be performed for the target. Set this
* parameter to one of the value below:
* - PJSIP_REDIRECT_ACCEPT: immediately accept the
- * redirection (default value). When set, the
- * call will immediately resend INVITE request
- * to the target.
+ * 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
@@ -1461,6 +1464,15 @@ typedef struct pjsua_config
pj_bool_t stun_ignore_failure;
/**
+ * This specifies whether STUN requests for resolving socket mapped
+ * address should use the new format, i.e: having STUN magic cookie
+ * in its transaction ID.
+ *
+ * Default: PJ_FALSE
+ */
+ pj_bool_t stun_map_use_stun2;
+
+ /**
* 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.
@@ -2143,6 +2155,15 @@ typedef struct pjsua_transport_config
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 port_range;
+
+ /**
* 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
@@ -2566,6 +2587,147 @@ typedef enum pjsua_call_hold_type
#endif
/**
+ * This enumeration controls the use of STUN in the account.
+ */
+typedef enum pjsua_stun_use
+{
+ /**
+ * Follow the default setting in the global \a pjsua_config.
+ */
+ PJSUA_STUN_USE_DEFAULT,
+
+ /**
+ * Disable STUN. If STUN is not enabled in the global \a pjsua_config,
+ * this setting has no effect.
+ */
+ PJSUA_STUN_USE_DISABLED
+
+} pjsua_stun_use;
+
+/**
+ * This enumeration controls the use of ICE settings in the account.
+ */
+typedef enum pjsua_ice_config_use
+{
+ /**
+ * Use the default settings in the global \a pjsua_media_config.
+ */
+ PJSUA_ICE_CONFIG_USE_DEFAULT,
+
+ /**
+ * Use the custom \a pjsua_ice_config setting in the account.
+ */
+ PJSUA_ICE_CONFIG_USE_CUSTOM
+
+} pjsua_ice_config_use;
+
+/**
+ * This enumeration controls the use of TURN settings in the account.
+ */
+typedef enum pjsua_turn_config_use
+{
+ /**
+ * Use the default setting in the global \a pjsua_media_config.
+ */
+ PJSUA_TURN_CONFIG_USE_DEFAULT,
+
+ /**
+ * Use the custom \a pjsua_turn_config setting in the account.
+ */
+ PJSUA_TURN_CONFIG_USE_CUSTOM
+
+} pjsua_turn_config_use;
+
+/**
+ * ICE setting. This setting is used in the pjsua_acc_config.
+ */
+typedef struct pjsua_ice_config
+{
+ /**
+ * Enable ICE.
+ */
+ pj_bool_t enable_ice;
+
+ /**
+ * Set the maximum number of host candidates.
+ *
+ * Default: -1 (maximum not set)
+ */
+ int ice_max_host_cands;
+
+ /**
+ * ICE session options.
+ */
+ pj_ice_sess_options ice_opt;
+
+ /**
+ * Disable RTCP component.
+ *
+ * Default: no
+ */
+ pj_bool_t ice_no_rtcp;
+
+ /**
+ * Send re-INVITE/UPDATE every after ICE connectivity check regardless
+ * the default ICE transport address is changed or not. When this is set
+ * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE
+ * transport address is changed.
+ *
+ * Default: yes
+ */
+ pj_bool_t ice_always_update;
+
+} pjsua_ice_config;
+
+/**
+ * TURN setting. This setting is used in the pjsua_acc_config.
+ */
+typedef struct pjsua_turn_config
+{
+ /**
+ * Enable TURN candidate in ICE.
+ */
+ pj_bool_t enable_turn;
+
+ /**
+ * Specify TURN domain name or host name, in in "DOMAIN:PORT" or
+ * "HOST:PORT" format.
+ */
+ pj_str_t turn_server;
+
+ /**
+ * 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 turn_conn_type;
+
+ /**
+ * Specify the credential to authenticate with the TURN server.
+ */
+ pj_stun_auth_cred turn_auth_cred;
+
+} pjsua_turn_config;
+
+/**
+ * Specify how IPv6 transport should be used in account config.
+ */
+typedef enum pjsua_ipv6_use
+{
+ /**
+ * IPv6 is not used.
+ */
+ PJSUA_IPV6_DISABLED,
+
+ /**
+ * IPv6 is enabled.
+ */
+ PJSUA_IPV6_ENABLED
+
+} pjsua_ipv6_use;
+
+/**
* This structure describes account configuration to be specified when
* adding a new account with #pjsua_acc_add(). Application MUST initialize
* this structure first by calling #pjsua_acc_config_default().
@@ -2588,7 +2750,8 @@ typedef struct pjsua_acc_config
/**
* The full SIP URL for the account. The value can take name address or
- * URL format, and will look something like "sip:account@serviceprovider".
+ * URL format, and will look something like "sip:account@serviceprovider"
+ * or "\"Display Name\" <sip:account@provider>".
*
* This field is mandatory.
*/
@@ -2743,6 +2906,15 @@ typedef struct pjsua_acc_config
*/
pj_str_t proxy[PJSUA_ACC_MAX_PROXIES];
+ /**
+ * 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: 1 (Yes). Set to zero to disable.
+ */
+ unsigned lock_codec;
+
/**
* Optional interval for registration, in seconds. If the value is zero,
* default interval will be used (PJSUA_REG_INTERVAL, 300 seconds).
@@ -2957,13 +3129,60 @@ typedef struct pjsua_acc_config
pjsua_transport_config rtp_cfg;
/**
+ * Specify whether IPv6 should be used on media.
+ */
+ pjsua_ipv6_use ipv6_media_use;
+
+ /**
+ * Control the use of STUN for the SIP signaling.
+ *
+ * Default: PJSUA_STUN_USE_DEFAULT
+ */
+ pjsua_stun_use sip_stun_use;
+
+ /**
+ * Control the use of STUN for the media transports.
+ *
+ * Default: PJSUA_STUN_USE_DEFAULT
+ */
+ pjsua_stun_use media_stun_use;
+
+ /**
+ * Control the use of ICE in the account. By default, the settings in the
+ * \a pjsua_media_config will be used.
+ *
+ * Default: PJSUA_ICE_CONFIG_USE_DEFAULT
+ */
+ pjsua_ice_config_use ice_cfg_use;
+
+ /**
+ * The custom ICE setting for this account. This setting will only be
+ * used if \a ice_cfg_use is set to PJSUA_ICE_CONFIG_USE_CUSTOM
+ */
+ pjsua_ice_config ice_cfg;
+
+ /**
+ * Control the use of TURN in the account. By default, the settings in the
+ * \a pjsua_media_config will be used
+ *
+ * Default: PJSUA_TURN_CONFIG_USE_DEFAULT
+ */
+ pjsua_turn_config_use turn_cfg_use;
+
+ /**
+ * The custom TURN setting for this account. This setting will only be
+ * used if \a turn_cfg_use is set to PJSUA_TURN_CONFIG_USE_CUSTOM
+ */
+ pjsua_turn_config turn_cfg;
+
+ /**
* 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 use_srtp;
+ pjmedia_srtp_use use_srtp;
/**
* Specify whether SRTP requires secure signaling to be used. This option
@@ -3061,6 +3280,52 @@ typedef struct pjsua_acc_config
/**
+ * Initialize ICE config from a media config. If the \a pool argument
+ * is NULL, a simple memcpy() will be used.
+ *
+ * @param pool Memory to duplicate strings.
+ * @param dst Destination config.
+ * @param src Source config.
+ */
+PJ_DECL(void) pjsua_ice_config_from_media_config(pj_pool_t *pool,
+ pjsua_ice_config *dst,
+ const pjsua_media_config *src);
+
+/**
+ * Clone. If the \a pool argument is NULL, a simple memcpy() will be used.
+ *
+ * @param pool Memory to duplicate strings.
+ * @param dst Destination config.
+ * @param src Source config.
+ */
+PJ_DECL(void) pjsua_ice_config_dup( pj_pool_t *pool,
+ pjsua_ice_config *dst,
+ const pjsua_ice_config *src);
+
+/**
+ * Initialize TURN config from a media config. If the \a pool argument
+ * is NULL, a simple memcpy() will be used.
+ *
+ * @param pool Memory to duplicate strings.
+ * @param dst Destination config.
+ * @param src Source config.
+ */
+PJ_DECL(void) pjsua_turn_config_from_media_config(pj_pool_t *pool,
+ pjsua_turn_config *dst,
+ const pjsua_media_config *src);
+
+/**
+ * Clone. If the \a pool argument is NULL, a simple memcpy() will be used.
+ *
+ * @param pool Memory to duplicate strings.
+ * @param dst Destination config.
+ * @param src Source config.
+ */
+PJ_DECL(void) pjsua_turn_config_dup(pj_pool_t *pool,
+ pjsua_turn_config *dst,
+ const pjsua_turn_config *src);
+
+/**
* Call this function to initialize account config with default values.
*
* @param cfg The account config to be initialized.
@@ -4291,7 +4556,8 @@ PJ_DECL(pj_status_t) pjsua_call_update2(pjsua_call_id call_id,
* of the call transfer request.
*
* @param call_id The call id to be transfered.
- * @param dest Address of new target to be contacted.
+ * @param dest URI of new target to be contacted. The URI may be
+ * in name address or addr-spec format.
* @param msg_data Optional message components to be sent with
* the request.
*
@@ -5015,6 +5281,28 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id,
/**
+ * Specify whether the third party stream has the capability of retrieving
+ * the stream info, i.e: pjmedia_stream_get_info() and
+ * pjmedia_vid_stream_get_info(). Currently this capability is required
+ * by smart media update and call dump.
+ */
+#ifndef PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO
+# define PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO 0
+#endif
+
+
+/**
+ * Specify whether the third party stream has the capability of retrieving
+ * the stream statistics, i.e: pjmedia_stream_get_stat() and
+ * pjmedia_vid_stream_get_stat(). Currently this capability is required
+ * by call dump.
+ */
+#ifndef PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT
+# define PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT 0
+#endif
+
+
+/**
* Max ports in the conference bridge. This setting is the default value
* for pjsua_media_config.max_media_ports.
*/
@@ -5081,6 +5369,14 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id,
/**
+ * Enable/disable "c=" line in SDP session level. Set to zero to disable it.
+ */
+#ifndef PJSUA_SDP_SESS_HAS_CONN
+# define PJSUA_SDP_SESS_HAS_CONN 0
+#endif
+
+
+/**
* This structure describes media configuration, which will be specified
* when calling #pjsua_init(). Application MUST initialize this structure
* by calling #pjsua_media_config_default().
@@ -5276,6 +5572,16 @@ struct pjsua_media_config
pj_bool_t ice_no_rtcp;
/**
+ * Send re-INVITE/UPDATE every after ICE connectivity check regardless
+ * the default ICE transport address is changed or not. When this is set
+ * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE
+ * transport address is changed.
+ *
+ * Default: yes
+ */
+ pj_bool_t ice_always_update;
+
+ /**
* Enable TURN relay candidate in ICE.
*/
pj_bool_t enable_turn;
@@ -5319,6 +5625,20 @@ struct pjsua_media_config
* Default: PJ_TRUE
*/
pj_bool_t vid_preview_enable_native;
+
+ /**
+ * Disable smart media update (ticket #1568). The smart media update
+ * will check for any changes in the media properties after a successful
+ * SDP negotiation and the media will only be reinitialized when any
+ * change is found. When it is disabled, media streams will always be
+ * reinitialized after a successful SDP negotiation.
+ *
+ * Note for third party media, the smart media update requires stream info
+ * retrieval capability, see #PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO.
+ *
+ * Default: PJ_FALSE
+ */
+ pj_bool_t no_smart_media_update;
};
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index c228513..3b38679 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -1,4 +1,4 @@
-/* $Id: pjsua_internal.h 4175 2012-06-22 08:53:11Z nanang $ */
+/* $Id: pjsua_internal.h 4342 2013-02-06 13:48:45Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -155,10 +155,7 @@ struct pjsua_call
char last_text_buf_[128]; /**< Buffer for last_text. */
struct {
- pj_timer_entry reinv_timer;/**< Reinvite retry timer. */
- pj_uint32_t sdp_ver; /**< SDP version of the bad answer */
int retry_cnt; /**< Retry count. */
- pj_bool_t pending; /**< Pending until CONFIRMED state */
} lock_codec; /**< Data for codec locking when answer
contains multiple codecs. */
@@ -170,6 +167,7 @@ struct pjsua_call
union {
struct {
pjsua_msg_data *msg_data;/**< Headers for outgoing INVITE. */
+ pj_bool_t hangup; /**< Call is hangup? */
} out_call;
struct {
call_answer answers;/**< A list of call answers. */
@@ -184,6 +182,10 @@ struct pjsua_call
offer. */
unsigned rem_vid_cnt; /**< No of active video in last remote
offer. */
+
+ pj_timer_entry reinv_timer; /**< Reinvite retry timer. */
+ pj_bool_t reinv_pending;/**< Pending until CONFIRMED state. */
+ pj_bool_t reinv_ice_sent;/**< Has reinvite for ICE upd sent? */
};
@@ -336,6 +338,9 @@ typedef struct pjsua_stun_resolve
PJ_DECL_LIST_MEMBER(struct pjsua_stun_resolve);
pj_pool_t *pool; /**< Pool */
+ int ref_cnt; /**< Reference count */
+ pj_bool_t destroy_flag; /**< To be destroyed */
+ pj_bool_t has_result;
unsigned count; /**< # of entries */
pj_str_t *srv; /**< Array of entries */
unsigned idx; /**< Current index */
@@ -583,6 +588,20 @@ pj_status_t resolve_stun_server(pj_bool_t wait);
*/
pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri);
+/* acc use stun? */
+pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id);
+
+/* Get local transport address suitable to be used for Via or Contact address
+ * to send request to the specified destination URI.
+ */
+pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
+ pj_pool_t *pool,
+ const pj_str_t *dst_uri,
+ pjsip_host_port *addr,
+ pjsip_transport_type_e *p_tp_type,
+ int *p_secure,
+ const void **p_tp);
+
/**
* Handle incoming invite request.
*/
@@ -798,6 +817,10 @@ PJ_DECL(void) pjsua_vid_win_reset(pjsua_vid_win_id wid);
# define pjsua_vid_win_reset(wid)
#endif
+/*
+ * Schedule check for the need of re-INVITE/UPDATE after media update
+ */
+void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms);
PJ_END_DECL
diff --git a/pjsip/src/pjsip-simple/publishc.c b/pjsip/src/pjsip-simple/publishc.c
index efee6fb..ab007ba 100644
--- a/pjsip/src/pjsip-simple/publishc.c
+++ b/pjsip/src/pjsip-simple/publishc.c
@@ -1,4 +1,4 @@
-/* $Id: publishc.c 4173 2012-06-20 10:39:05Z ming $ */
+/* $Id: publishc.c 4206 2012-07-16 02:45:09Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -355,8 +355,11 @@ PJ_DEF(pj_status_t) pjsip_publishc_set_via_sent_by(pjsip_publishc *pubc,
if (!via_addr)
pj_bzero(&pubc->via_addr, sizeof(pubc->via_addr));
- else
- pubc->via_addr = *via_addr;
+ else {
+ if (pj_strcmp(&pubc->via_addr.host, &via_addr->host))
+ pj_strdup(pubc->pool, &pubc->via_addr.host, &via_addr->host);
+ pubc->via_addr.port = via_addr->port;
+ }
pubc->via_tp = via_tp;
return PJ_SUCCESS;
diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c
index 07122c4..f64ef19 100644
--- a/pjsip/src/pjsip-ua/sip_100rel.c
+++ b/pjsip/src/pjsip-ua/sip_100rel.c
@@ -1,4 +1,4 @@
-/* $Id: sip_100rel.c 3841 2011-10-24 09:28:13Z ming $ */
+/* $Id: sip_100rel.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -273,7 +273,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
/* Find UAC state for the specified call leg */
uac_state = dd->uac_state_list;
while (uac_state) {
- if (pj_strcmp(&uac_state->tag, to_tag)==0)
+ if (pj_stricmp(&uac_state->tag, to_tag)==0)
break;
uac_state = uac_state->next;
}
@@ -320,7 +320,7 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
/* If this response is a forked response from a different call-leg,
* update the req URI (https://trac.pjsip.org/repos/ticket/1364)
*/
- if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) {
+ if (pj_stricmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) {
const pjsip_contact_hdr *mhdr;
mhdr = (const pjsip_contact_hdr*)
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 8db5308..dec4ee5 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -1,4 +1,4 @@
-/* $Id: sip_inv.c 4156 2012-06-06 07:24:08Z bennylp $ */
+/* $Id: sip_inv.c 4367 2013-02-21 20:49:19Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -829,7 +829,7 @@ PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
sdp_info->body.slen,
&sdp_info->sdp);
if (status == PJ_SUCCESS)
- status = pjmedia_sdp_validate(sdp_info->sdp);
+ status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE);
if (status != PJ_SUCCESS) {
sdp_info->sdp = NULL;
@@ -1745,7 +1745,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
if (tsx->role == PJSIP_ROLE_UAC &&
rdata->msg_info.msg->line.status.code/100 == 2 &&
tsx_inv_data->done_early &&
- pj_strcmp(&tsx_inv_data->done_tag, &res_tag))
+ pj_stricmp(&tsx_inv_data->done_tag, &res_tag))
{
const pjmedia_sdp_session *reoffer_sdp = NULL;
@@ -2329,6 +2329,7 @@ static pj_bool_t inv_uac_recurse(pjsip_inv_session *inv, int code,
/* Check what the application wants to do now */
switch (op) {
case PJSIP_REDIRECT_ACCEPT:
+ case PJSIP_REDIRECT_ACCEPT_REPLACE:
case PJSIP_REDIRECT_STOP:
/* Must increment session counter, that's the convention of the
* pjsip_inv_process_redirect().
@@ -2394,6 +2395,7 @@ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv,
/* See what the application wants to do now */
switch (op) {
case PJSIP_REDIRECT_ACCEPT:
+ case PJSIP_REDIRECT_ACCEPT_REPLACE:
/* User accept the redirection. Reset the session and resend the
* INVITE request.
*/
@@ -2419,6 +2421,51 @@ PJ_DEF(pj_status_t) pjsip_inv_process_redirect( pjsip_inv_session *inv,
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
via->branch_param.slen = 0;
+ /* Process PJSIP_REDIRECT_ACCEPT_REPLACE */
+ if (op == PJSIP_REDIRECT_ACCEPT_REPLACE) {
+ pjsip_to_hdr *to;
+ pjsip_dialog *dlg = inv->dlg;
+ enum { TMP_LEN = 128 };
+ char tmp[TMP_LEN];
+ int len;
+
+ /* Replace To header */
+ to = PJSIP_MSG_TO_HDR(tdata->msg);
+ to->uri = (pjsip_uri*)
+ pjsip_uri_clone(tdata->pool,
+ dlg->target_set.current->uri);
+ to->tag.slen = 0;
+ pj_list_init(&to->other_param);
+
+ /* Re-init dialog remote info */
+ dlg->remote.info = (pjsip_to_hdr*)
+ pjsip_hdr_clone(dlg->pool, to);
+
+ /* Remove header param from remote info */
+ if (PJSIP_URI_SCHEME_IS_SIP(dlg->remote.info->uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(dlg->remote.info->uri))
+ {
+ pjsip_sip_uri *sip_uri = (pjsip_sip_uri *)
+ pjsip_uri_get_uri(dlg->remote.info->uri);
+ if (!pj_list_empty(&sip_uri->header_param)) {
+ /* Remove all header param */
+ pj_list_init(&sip_uri->header_param);
+ }
+ }
+
+ /* Print the remote info. */
+ len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
+ dlg->remote.info->uri, tmp, TMP_LEN);
+ if (len < 1) {
+ pj_ansi_strcpy(tmp, "<-error: uri too long->");
+ len = pj_ansi_strlen(tmp);
+ }
+ pj_strdup2_with_null(dlg->pool, &dlg->remote.info_str, tmp);
+
+ /* Secure? */
+ dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(to->uri);
+ }
+
/* Reset message destination info (see #1248). */
pj_bzero(&tdata->dest_info, sizeof(tdata->dest_info));
@@ -2572,6 +2619,7 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv,
pjsip_contact_hdr *contact_hdr = NULL;
pjsip_tx_data *tdata = NULL;
pjmedia_sdp_session *sdp_copy;
+ const pjsip_hdr *hdr;
pj_status_t status = PJ_SUCCESS;
/* Verify arguments. */
@@ -2640,13 +2688,24 @@ PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv,
pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
}
- /* Unlock dialog. */
- pjsip_dlg_dec_lock(inv->dlg);
+ /* Session Timers spec (RFC 4028) says that Supported header MUST be put
+ * in refresh requests. So here we'll just put the Supported header in
+ * all cases regardless of whether session timers is used or not, just
+ * in case this is a common behavior.
+ */
+ hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
+ if (hdr) {
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
+ pjsip_hdr_shallow_clone(tdata->pool, hdr));
+ }
status = pjsip_timer_update_req(inv, tdata);
if (status != PJ_SUCCESS)
goto on_error;
+ /* Unlock dialog. */
+ pjsip_dlg_dec_lock(inv->dlg);
+
*p_tdata = tdata;
pj_log_pop_indent();
@@ -2957,8 +3016,16 @@ static void inv_respond_incoming_update(pjsip_inv_session *inv,
neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+ /* If UPDATE doesn't contain SDP, just respond with 200/OK.
+ * This is a valid scenario according to session-timer draft.
+ */
+ if (rdata->msg_info.msg->body == NULL) {
+
+ status = pjsip_dlg_create_response(inv->dlg, rdata,
+ 200, NULL, &tdata);
+ }
/* Send 491 if we receive UPDATE while we're waiting for an answer */
- if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
+ else if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
status = pjsip_dlg_create_response(inv->dlg, rdata,
PJSIP_SC_REQUEST_PENDING, NULL,
&tdata);
@@ -2967,18 +3034,18 @@ static void inv_respond_incoming_update(pjsip_inv_session *inv,
* receive UPDATE while we haven't sent answer.
*/
else if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER ||
- neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
- status = pjsip_dlg_create_response(inv->dlg, rdata,
+ neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
+ {
+ pjsip_retry_after_hdr *ra_hdr;
+ int val;
+
+ status = pjsip_dlg_create_response(inv->dlg, rdata,
PJSIP_SC_INTERNAL_SERVER_ERROR,
NULL, &tdata);
- /* If UPDATE doesn't contain SDP, just respond with 200/OK.
- * This is a valid scenario according to session-timer draft.
- */
- } else if (rdata->msg_info.msg->body == NULL) {
-
- status = pjsip_dlg_create_response(inv->dlg, rdata,
- 200, NULL, &tdata);
+ val = (pj_rand() % 10);
+ ra_hdr = pjsip_retry_after_hdr_create(tdata->pool, val);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ra_hdr);
} else {
/* We receive new offer from remote */
@@ -3305,8 +3372,7 @@ static pj_bool_t handle_uac_tsx_response(pjsip_inv_session *inv,
((tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST &&
tsx->method.id != PJSIP_CANCEL_METHOD) ||
tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT ||
- tsx->status_code == PJSIP_SC_TSX_TIMEOUT ||
- tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR))
+ tsx->status_code == PJSIP_SC_TSX_TIMEOUT))
{
pjsip_tx_data *bye;
pj_status_t status;
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index bd1fb21..b76a1f6 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -1,4 +1,4 @@
-/* $Id: sip_reg.c 4173 2012-06-20 10:39:05Z ming $ */
+/* $Id: sip_reg.c 4319 2013-01-16 10:20:55Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -336,7 +336,7 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
pj_status_t status;
PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
- contact_cnt && contact && expires, PJ_EINVAL);
+ expires, PJ_EINVAL);
/* Copy server URL. */
pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
@@ -818,8 +818,11 @@ PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
if (!via_addr)
pj_bzero(&regc->via_addr, sizeof(regc->via_addr));
- else
- regc->via_addr = *via_addr;
+ else {
+ if (pj_strcmp(&regc->via_addr.host, &via_addr->host))
+ pj_strdup(regc->pool, &regc->via_addr.host, &via_addr->host);
+ regc->via_addr.port = via_addr->port;
+ }
regc->via_tp = via_tp;
return PJ_SUCCESS;
diff --git a/pjsip/src/pjsip-ua/sip_replaces.c b/pjsip/src/pjsip-ua/sip_replaces.c
index b961cf9..a37546f 100644
--- a/pjsip/src/pjsip-ua/sip_replaces.c
+++ b/pjsip/src/pjsip-ua/sip_replaces.c
@@ -1,4 +1,4 @@
-/* $Id: sip_replaces.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: sip_replaces.c 4268 2012-09-28 08:56:08Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -305,10 +305,19 @@ PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata,
* initiated by this UA, it returns a 481 (Call/Transaction Does Not
* Exist) response to the new INVITE.
*/
- if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) {
- code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
- warn_text = "Found early INVITE session but not initiated by this UA";
- goto on_return;
+ if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC)
+ {
+ /* Really return 481 only if call haven't reached early state or
+ * accept-replace-in-early-state (ticket #1587) is not allowed.
+ */
+ if (inv->state != PJSIP_INV_STATE_EARLY ||
+ pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE)
+ {
+ code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
+ warn_text = "Found early INVITE session but not initiated by "
+ "this UA";
+ goto on_return;
+ }
}
diff --git a/pjsip/src/pjsip-ua/sip_timer.c b/pjsip/src/pjsip-ua/sip_timer.c
index 2c70791..a48c71d 100644
--- a/pjsip/src/pjsip-ua/sip_timer.c
+++ b/pjsip/src/pjsip-ua/sip_timer.c
@@ -1,4 +1,4 @@
-/* $Id: sip_timer.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: sip_timer.c 4213 2012-07-23 13:31:26Z nanang $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -1030,6 +1030,33 @@ PJ_DEF(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv,
if (inv->timer && inv->timer->active) {
/* Add Session-Expires header and start the timer */
add_timer_headers(inv, tdata, PJ_TRUE, PJ_FALSE);
+
+ /* Add 'timer' to Require header (see ticket #1560). */
+ if (inv->timer->refresher == TR_UAC) {
+ pjsip_require_hdr *req_hdr;
+ pj_bool_t req_hdr_has_timer = PJ_FALSE;
+
+ req_hdr = (pjsip_require_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE,
+ NULL);
+ if (req_hdr == NULL) {
+ req_hdr = pjsip_require_hdr_create(tdata->pool);
+ PJ_ASSERT_RETURN(req_hdr, PJ_ENOMEM);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
+ } else {
+ unsigned i;
+ for (i = 0; i < req_hdr->count; ++i) {
+ if (pj_stricmp(&req_hdr->values[i], &STR_TIMER)) {
+ req_hdr_has_timer = PJ_TRUE;
+ break;
+ }
+ }
+ }
+ if (!req_hdr_has_timer)
+ req_hdr->values[req_hdr->count++] = STR_TIMER;
+ }
+
+ /* Finally, start timer. */
start_timer(inv);
}
}
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index ae850b1..0facc7b 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -1,4 +1,4 @@
-/* $Id: sip_auth_client.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: sip_auth_client.c 4322 2013-01-17 10:09:09Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -920,13 +920,13 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
* or add an empty authorization header.
*/
unsigned i;
- char *uri_str;
- int len;
+ pj_str_t uri;
- uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
- len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
- uri_str, PJSIP_MAX_URL_SIZE);
- if (len < 1 || len >= PJSIP_MAX_URL_SIZE)
+ uri.ptr = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
+ uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+ tdata->msg->line.req.uri,
+ uri.ptr, PJSIP_MAX_URL_SIZE);
+ if (uri.slen < 1 || uri.slen >= PJSIP_MAX_URL_SIZE)
return PJSIP_EURITOOLONG;
for (i=0; i<sess->cred_cnt; ++i) {
@@ -946,8 +946,7 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
&c->username);
pj_strdup(tdata->pool, &hs->credential.digest.realm,
&c->realm);
- pj_strdup2(tdata->pool, &hs->credential.digest.uri,
- uri_str);
+ pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);
pj_strdup(tdata->pool, &hs->credential.digest.algorithm,
&sess->pref.algorithm);
diff --git a/pjsip/src/pjsip/sip_auth_server.c b/pjsip/src/pjsip/sip_auth_server.c
index 248e6cc..c38b40c 100644
--- a/pjsip/src/pjsip/sip_auth_server.c
+++ b/pjsip/src/pjsip/sip_auth_server.c
@@ -1,4 +1,4 @@
-/* $Id: sip_auth_server.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_auth_server.c 4214 2012-07-25 14:29:28Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -40,6 +40,7 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool,
{
PJ_ASSERT_RETURN(pool && auth_srv && realm && lookup, PJ_EINVAL);
+ pj_bzero(auth_srv, sizeof(*auth_srv));
pj_strdup( pool, &auth_srv->realm, realm);
auth_srv->lookup = lookup;
auth_srv->is_proxy = (options & PJSIP_AUTH_SRV_IS_PROXY);
@@ -47,6 +48,26 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_init( pj_pool_t *pool,
return PJ_SUCCESS;
}
+/*
+ * Initialize server authorization session data structure to serve the
+ * specified realm and to use lookup_func function to look for the credential
+ * info.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_srv_init2(
+ pj_pool_t *pool,
+ pjsip_auth_srv *auth_srv,
+ const pjsip_auth_srv_init_param *param)
+{
+ PJ_ASSERT_RETURN(pool && auth_srv && param, PJ_EINVAL);
+
+ pj_bzero(auth_srv, sizeof(*auth_srv));
+ pj_strdup( pool, &auth_srv->realm, param->realm);
+ auth_srv->lookup2 = param->lookup2;
+ auth_srv->is_proxy = (param->options & PJSIP_AUTH_SRV_IS_PROXY);
+
+ return PJ_SUCCESS;
+}
+
/* Verify incoming Authorization/Proxy-Authorization header against the
* specified credential.
@@ -148,11 +169,25 @@ PJ_DEF(pj_status_t) pjsip_auth_srv_verify( pjsip_auth_srv *auth_srv,
}
/* Find the credential information for the account. */
- status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm,
- &acc_name, &cred_info);
- if (status != PJ_SUCCESS) {
- *status_code = PJSIP_SC_FORBIDDEN;
- return status;
+ if (auth_srv->lookup2) {
+ pjsip_auth_lookup_cred_param param;
+
+ pj_bzero(&param, sizeof(param));
+ param.realm = auth_srv->realm;
+ param.acc_name = acc_name;
+ param.rdata = rdata;
+ status = (*auth_srv->lookup2)(rdata->tp_info.pool, &param, &cred_info);
+ if (status != PJ_SUCCESS) {
+ *status_code = PJSIP_SC_FORBIDDEN;
+ return status;
+ }
+ } else {
+ status = (*auth_srv->lookup)(rdata->tp_info.pool, &auth_srv->realm,
+ &acc_name, &cred_info);
+ if (status != PJ_SUCCESS) {
+ *status_code = PJSIP_SC_FORBIDDEN;
+ return status;
+ }
}
/* Authenticate with the specified credential. */
diff --git a/pjsip/src/pjsip/sip_config.c b/pjsip/src/pjsip/sip_config.c
index 8742c8a..92b8805 100644
--- a/pjsip/src/pjsip/sip_config.c
+++ b/pjsip/src/pjsip/sip_config.c
@@ -1,4 +1,4 @@
-/* $Id: sip_config.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* $Id: sip_config.c 4285 2012-10-19 04:23:57Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -26,6 +26,8 @@ pjsip_cfg_t pjsip_sip_cfg_var =
/* Global settings */
{
PJSIP_ALLOW_PORT_IN_FROMTO_HDR,
+ PJSIP_ACCEPT_REPLACE_IN_EARLY_STATE,
+ 0,
0,
PJSIP_DONT_SWITCH_TO_TCP
},
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 008cad2..0e70d33 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -1,4 +1,4 @@
-/* $Id: sip_dialog.c 4173 2012-06-20 10:39:05Z ming $ */
+/* $Id: sip_dialog.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -206,8 +206,8 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
pj_create_unique_string(dlg->pool, &dlg->local.info->tag);
/* Calculate hash value of local tag. */
- dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr,
- dlg->local.info->tag.slen);
+ dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL,
+ &dlg->local.info->tag);
/* Randomize local CSeq. */
dlg->local.first_cseq = pj_rand() & 0x7FFF;
@@ -374,8 +374,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
pj_strdup(dlg->pool, &dlg->local.info_str, &tmp);
/* Calculate hash value of local tag. */
- dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr,
- dlg->local.info->tag.slen);
+ dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag);
/* Randomize local cseq */
@@ -522,8 +521,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
++dlg->tsx_count;
/* Calculate hash value of remote tag. */
- dlg->remote.tag_hval = pj_hash_calc(0, dlg->remote.info->tag.ptr,
- dlg->remote.info->tag.slen);
+ dlg->remote.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->remote.info->tag);
/* Update remote capabilities info */
pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg, PJ_TRUE);
@@ -596,8 +594,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_set_via_sent_by( pjsip_dialog *dlg,
if (!via_addr)
pj_bzero(&dlg->via_addr, sizeof(dlg->via_addr));
- else
- dlg->via_addr = *via_addr;
+ else {
+ if (pj_strcmp(&dlg->via_addr.host, &via_addr->host))
+ pj_strdup(dlg->pool, &dlg->via_addr.host, &via_addr->host);
+ dlg->via_addr.port = via_addr->port;
+ }
dlg->via_tp = via_tp;
return PJ_SUCCESS;
@@ -1814,7 +1815,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
res_code > 100 &&
res_code/100 <= 2 &&
pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
- pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag)))
+ pj_stricmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag)))
{
pjsip_contact_hdr *contact;
@@ -1823,7 +1824,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
* with To-tag or forking, apply strict update.
*/
pjsip_dlg_update_remote_cap(dlg, rdata->msg_info.msg,
- pj_strcmp(&dlg->remote.info->tag,
+ pj_stricmp(&dlg->remote.info->tag,
&rdata->msg_info.to->tag));
/* Update To tag. */
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index 2510d14..ae55990 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -1,4 +1,4 @@
-/* $Id: sip_endpoint.c 4154 2012-06-05 10:41:17Z bennylp $ */
+/* $Id: sip_endpoint.c 4411 2013-03-04 04:34:38Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -812,6 +812,104 @@ PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt)
return endpt->timer_heap;
}
+/* Init with default */
+PJ_DEF(void) pjsip_process_rdata_param_default(pjsip_process_rdata_param *p)
+{
+ pj_bzero(p, sizeof(*p));
+}
+
+/* Distribute rdata */
+PJ_DEF(pj_status_t) pjsip_endpt_process_rx_data( pjsip_endpoint *endpt,
+ pjsip_rx_data *rdata,
+ pjsip_process_rdata_param *p,
+ pj_bool_t *p_handled)
+{
+ pjsip_msg *msg;
+ pjsip_process_rdata_param def_prm;
+ pjsip_module *mod;
+ pj_bool_t handled = PJ_FALSE;
+ unsigned i;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(endpt && rdata, PJ_EINVAL);
+
+ if (p==NULL) {
+ p = &def_prm;
+ pjsip_process_rdata_param_default(p);
+ }
+
+ msg = rdata->msg_info.msg;
+
+ if (p_handled)
+ *p_handled = PJ_FALSE;
+
+ if (!p->silent) {
+ PJ_LOG(5, (THIS_FILE, "Distributing rdata to modules: %s",
+ pjsip_rx_data_get_info(rdata)));
+ pj_log_push_indent();
+ }
+
+ LOCK_MODULE_ACCESS(endpt);
+
+ /* Find start module */
+ if (p->start_mod) {
+ mod = (pjsip_module*)
+ pj_list_find_node(&endpt->module_list, p->start_mod);
+ if (!mod) {
+ status = PJ_ENOTFOUND;
+ goto on_return;
+ }
+ } else {
+ mod = endpt->module_list.next;
+ }
+
+ /* Start after the specified index */
+ for (i=0; i < p->idx_after_start && mod != &endpt->module_list; ++i) {
+ mod = mod->next;
+ }
+
+ /* Start with the specified priority */
+ while (mod != &endpt->module_list && mod->priority < (int)p->start_prio) {
+ mod = mod->next;
+ }
+
+ if (mod == &endpt->module_list) {
+ status = PJ_ENOTFOUND;
+ goto on_return;
+ }
+
+ /* Distribute */
+ if (msg->type == PJSIP_REQUEST_MSG) {
+ do {
+ if (mod->on_rx_request)
+ handled = (*mod->on_rx_request)(rdata);
+ if (handled)
+ break;
+ mod = mod->next;
+ } while (mod != &endpt->module_list);
+ } else {
+ do {
+ if (mod->on_rx_response)
+ handled = (*mod->on_rx_response)(rdata);
+ if (handled)
+ break;
+ mod = mod->next;
+ } while (mod != &endpt->module_list);
+ }
+
+ status = PJ_SUCCESS;
+
+on_return:
+ if (p_handled)
+ *p_handled = handled;
+
+ UNLOCK_MODULE_ACCESS(endpt);
+ if (!p->silent) {
+ pj_log_pop_indent();
+ }
+ return status;
+}
+
/*
* This is the callback that is called by the transport manager when it
* receives a message from the network.
@@ -820,7 +918,8 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt,
pj_status_t status,
pjsip_rx_data *rdata )
{
- pjsip_msg *msg = rdata->msg_info.msg;
+ pjsip_process_rdata_param proc_prm;
+ pj_bool_t handled = PJ_FALSE;
if (status != PJ_SUCCESS) {
char info[30];
@@ -927,57 +1026,20 @@ static void endpt_on_rx_msg( pjsip_endpoint *endpt,
}
#endif
+ pjsip_process_rdata_param_default(&proc_prm);
+ proc_prm.silent = PJ_TRUE;
- /* Distribute to modules, starting from modules with highest priority */
- LOCK_MODULE_ACCESS(endpt);
-
- if (msg->type == PJSIP_REQUEST_MSG) {
- pjsip_module *mod;
- pj_bool_t handled = PJ_FALSE;
-
- mod = endpt->module_list.next;
- while (mod != &endpt->module_list) {
- if (mod->on_rx_request)
- handled = (*mod->on_rx_request)(rdata);
- if (handled)
- break;
- mod = mod->next;
- }
-
- /* No module is able to handle the request. */
- if (!handled) {
- PJ_TODO(ENDPT_RESPOND_UNHANDLED_REQUEST);
- PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled by"
- " any modules",
- pjsip_rx_data_get_info(rdata),
- rdata->pkt_info.src_name,
- rdata->pkt_info.src_port));
- }
-
- } else {
- pjsip_module *mod;
- pj_bool_t handled = PJ_FALSE;
+ pjsip_endpt_process_rx_data(endpt, rdata, &proc_prm, &handled);
- mod = endpt->module_list.next;
- while (mod != &endpt->module_list) {
- if (mod->on_rx_response)
- handled = (*mod->on_rx_response)(rdata);
- if (handled)
- break;
- mod = mod->next;
- }
-
- if (!handled) {
- PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled"
- " by any modules",
- pjsip_rx_data_get_info(rdata),
- rdata->pkt_info.src_name,
- rdata->pkt_info.src_port));
- }
+ /* No module is able to handle the message */
+ if (!handled) {
+ PJ_LOG(4,(THIS_FILE, "%s from %s:%d was dropped/unhandled by"
+ " any modules",
+ pjsip_rx_data_get_info(rdata),
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port));
}
- UNLOCK_MODULE_ACCESS(endpt);
-
/* Must clear mod_data before returning rdata to transport, since
* rdata may be reused.
*/
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 2d46fe9..26f4aca 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -1,4 +1,4 @@
-/* $Id: sip_parser.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_parser.c 4288 2012-10-26 09:30:37Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -1522,8 +1522,15 @@ static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
/* Get the SIP-URL */
has_bracket = (*scanner->curptr == '<');
- if (has_bracket)
+ if (has_bracket) {
pj_scan_get_char(scanner);
+ } else if (name_addr->display.slen) {
+ /* Must have bracket now (2012-10-26).
+ * Allowing (invalid) name-addr to pass URI verification will
+ * cause us to send invalid URI to the wire.
+ */
+ PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
+ }
name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
if (has_bracket) {
if (pj_scan_get_char(scanner) != '>')
diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c
index 4120ae0..b98db57 100644
--- a/pjsip/src/pjsip/sip_tel_uri.c
+++ b/pjsip/src/pjsip/sip_tel_uri.c
@@ -1,4 +1,4 @@
-/* $Id: sip_tel_uri.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_tel_uri.c 4322 2013-01-17 10:09:09Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -182,7 +182,7 @@ static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
{
int printed;
char *startbuf = buf;
- char *endbuf = buf+size;
+ char *endbuf = buf+size-1;
const pjsip_parser_const_t *pc = pjsip_parser_const();
PJ_UNUSED_ARG(context);
@@ -217,6 +217,8 @@ static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
return -1;
buf += printed;
+ *buf = '\0';
+
return (buf-startbuf);
}
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 4b3dd12..1321d9f 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transaction.c 4165 2012-06-14 09:04:20Z bennylp $ */
+/* $Id: sip_transaction.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -403,7 +403,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
*/
const pj_str_t *branch = &rdata->msg_info.via->branch_param;
- if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
+ if (pj_strnicmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
/* Create transaction key. */
return create_tsx_key_3261(pool, key, role, method, branch);
@@ -548,10 +548,10 @@ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx)
* Do not use PJ_ASSERT_RETURN since it evaluates the expression
* twice!
*/
- if(pj_hash_get(mod_tsx_layer.htable,
- tsx->transaction_key.ptr,
- tsx->transaction_key.slen,
- NULL))
+ if(pj_hash_get_lower(mod_tsx_layer.htable,
+ tsx->transaction_key.ptr,
+ tsx->transaction_key.slen,
+ NULL))
{
pj_mutex_unlock(mod_tsx_layer.mutex);
PJ_LOG(2,(THIS_FILE,
@@ -568,11 +568,13 @@ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx)
/* Register the transaction to the hash table. */
#ifdef PRECALC_HASH
- pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx->hashed_key, tsx);
+ pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable,
+ tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, tsx->hashed_key, tsx);
#else
- pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, 0, tsx);
+ pj_hash_set_lower( tsx->pool, mod_tsx_layer.htable,
+ tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, 0, tsx);
#endif
/* Unlock mutex. */
@@ -604,11 +606,11 @@ static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx)
/* Register the transaction to the hash table. */
#ifdef PRECALC_HASH
- pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx->hashed_key, NULL);
+ pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, tsx->hashed_key, NULL);
#else
- pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, 0, NULL);
+ pj_hash_set_lower( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, 0, NULL);
#endif
TSX_TRACE_((THIS_FILE,
@@ -651,7 +653,8 @@ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
pj_mutex_lock(mod_tsx_layer.mutex);
tsx = (pjsip_transaction*)
- pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, &hval );
+ pj_hash_get_lower( mod_tsx_layer.htable, key->ptr, key->slen,
+ &hval );
pj_mutex_unlock(mod_tsx_layer.mutex);
TSX_TRACE_((THIS_FILE,
@@ -785,7 +788,7 @@ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata)
pj_mutex_lock( mod_tsx_layer.mutex );
tsx = (pjsip_transaction*)
- pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
+ pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
TSX_TRACE_((THIS_FILE,
@@ -834,7 +837,7 @@ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata)
pj_mutex_lock( mod_tsx_layer.mutex );
tsx = (pjsip_transaction*)
- pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
+ pj_hash_get_lower( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
TSX_TRACE_((THIS_FILE,
@@ -1299,8 +1302,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
/* Calculate hashed key value. */
#ifdef PRECALC_HASH
- tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
- tsx->transaction_key.slen);
+ tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key);
#endif
PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
@@ -1432,8 +1434,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
/* Calculate hashed key value. */
#ifdef PRECALC_HASH
- tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
- tsx->transaction_key.slen);
+ tsx->hashed_key = pj_hash_calc_tolower(0, NULL, &tsx->transaction_key);
#endif
/* Duplicate branch parameter for transaction. */
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 4d8b77e..1598241 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport.c 4094 2012-04-26 09:31:00Z bennylp $ */
+/* $Id: sip_transport.c 4295 2012-11-06 05:22:11Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -24,6 +24,7 @@
#include <pjsip/sip_private.h>
#include <pjsip/sip_errno.h>
#include <pjsip/sip_module.h>
+#include <pj/addr_resolv.h>
#include <pj/except.h>
#include <pj/os.h>
#include <pj/log.h>
@@ -197,6 +198,13 @@ struct transport_names_t
"TCP IPv6 transport",
PJSIP_TRANSPORT_RELIABLE
},
+ {
+ PJSIP_TRANSPORT_TLS6,
+ 5061,
+ {"TLS", 3},
+ "TLS IPv6 transport",
+ PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE
+ },
};
static void tp_state_callback(pjsip_transport *tp,
@@ -594,6 +602,89 @@ PJ_DEF(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata)
return rdata->msg_info.info;
}
+/* Clone pjsip_rx_data. */
+PJ_DEF(pj_status_t) pjsip_rx_data_clone( const pjsip_rx_data *src,
+ unsigned flags,
+ pjsip_rx_data **p_rdata)
+{
+ pj_pool_t *pool;
+ pjsip_rx_data *dst;
+ pjsip_hdr *hdr;
+
+ PJ_ASSERT_RETURN(src && flags==0 && p_rdata, PJ_EINVAL);
+
+ pool = pj_pool_create(src->tp_info.pool->factory,
+ "rtd%p",
+ PJSIP_POOL_RDATA_LEN,
+ PJSIP_POOL_RDATA_INC,
+ NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ dst = PJ_POOL_ZALLOC_T(pool, pjsip_rx_data);
+
+ /* Parts of tp_info */
+ dst->tp_info.pool = pool;
+ dst->tp_info.transport = (pjsip_transport*)src->tp_info.transport;
+
+ /* pkt_info can be memcopied */
+ pj_memcpy(&dst->pkt_info, &src->pkt_info, sizeof(src->pkt_info));
+
+ /* msg_info needs deep clone */
+ dst->msg_info.msg_buf = dst->pkt_info.packet;
+ dst->msg_info.len = src->msg_info.len;
+ dst->msg_info.msg = pjsip_msg_clone(pool, src->msg_info.msg);
+ pj_list_init(&dst->msg_info.parse_err);
+
+#define GET_MSG_HDR2(TYPE, type, var) \
+ case PJSIP_H_##TYPE: \
+ if (!dst->msg_info.var) { \
+ dst->msg_info.var = (pjsip_##type##_hdr*)hdr; \
+ } \
+ break
+#define GET_MSG_HDR(TYPE, var_type) GET_MSG_HDR2(TYPE, var_type, var_type)
+
+ hdr = dst->msg_info.msg->hdr.next;
+ while (hdr != &dst->msg_info.msg->hdr) {
+ switch (hdr->type) {
+ GET_MSG_HDR(CALL_ID, cid);
+ GET_MSG_HDR(FROM, from);
+ GET_MSG_HDR(TO, to);
+ GET_MSG_HDR(VIA, via);
+ GET_MSG_HDR(CSEQ, cseq);
+ GET_MSG_HDR(MAX_FORWARDS, max_fwd);
+ GET_MSG_HDR(ROUTE, route);
+ GET_MSG_HDR2(RECORD_ROUTE, rr, record_route);
+ GET_MSG_HDR(CONTENT_TYPE, ctype);
+ GET_MSG_HDR(CONTENT_LENGTH, clen);
+ GET_MSG_HDR(REQUIRE, require);
+ GET_MSG_HDR(SUPPORTED, supported);
+ default:
+ break;
+ }
+ hdr = hdr->next;
+ }
+
+#undef GET_MSG_HDR
+#undef GET_MSG_HDR2
+
+ *p_rdata = dst;
+
+ /* Finally add transport ref */
+ return pjsip_transport_add_ref(dst->tp_info.transport);
+}
+
+/* Free previously cloned pjsip_rx_data. */
+PJ_DEF(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata)
+{
+ PJ_ASSERT_RETURN(rdata, PJ_EINVAL);
+
+ pjsip_transport_dec_ref(rdata->tp_info.transport);
+ pj_pool_release(rdata->tp_info.pool);
+
+ return PJ_SUCCESS;
+}
+
/*****************************************************************************
*
* TRANSPORT KEY
@@ -1080,6 +1171,10 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
return PJ_SUCCESS;
}
+PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm)
+{
+ pj_bzero(prm, sizeof(*prm));
+}
/*****************************************************************************
*
@@ -1138,7 +1233,27 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
return PJ_SUCCESS;
}
+/* Get the interface to send packet to the specified address */
+static pj_status_t get_net_interface(pjsip_transport_type_e tp_type,
+ const pj_str_t *dst,
+ pj_str_t *itf_str_addr)
+{
+ int af;
+ pj_sockaddr itf_addr;
+ pj_status_t status;
+
+ af = (tp_type & PJSIP_TRANSPORT_IPV6)? PJ_AF_INET6 : PJ_AF_INET;
+ status = pj_getipinterface(af, dst, &itf_addr, PJ_FALSE, NULL);
+ if (status != PJ_SUCCESS)
+ return status;
+ /* Print address */
+ pj_sockaddr_print(&itf_addr, itf_str_addr->ptr,
+ PJ_INET6_ADDRSTRLEN, 0);
+ itf_str_addr->slen = pj_ansi_strlen(itf_str_addr->ptr);
+
+ return PJ_SUCCESS;
+}
/*
* Find out the appropriate local address info (IP address and port) to
@@ -1149,46 +1264,66 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
* In this implementation, it will only select the transport based on
* the transport type in the request.
*/
-PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
+PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr,
pj_pool_t *pool,
- pjsip_transport_type_e type,
- const pjsip_tpselector *sel,
- pj_str_t *ip_addr,
- int *port)
+ pjsip_tpmgr_fla2_param *prm)
{
+ char tmp_buf[PJ_INET6_ADDRSTRLEN+10];
+ pj_str_t tmp_str;
pj_status_t status = PJSIP_EUNSUPTRANSPORT;
unsigned flag;
/* Sanity checks */
- PJ_ASSERT_RETURN(tpmgr && pool && ip_addr && port, PJ_EINVAL);
+ PJ_ASSERT_RETURN(tpmgr && pool && prm, PJ_EINVAL);
- ip_addr->slen = 0;
- *port = 0;
+ pj_strset(&tmp_str, tmp_buf, 0);
+ prm->ret_addr.slen = 0;
+ prm->ret_port = 0;
+ prm->ret_tp = NULL;
- flag = pjsip_transport_get_flag_from_type(type);
+ flag = pjsip_transport_get_flag_from_type(prm->tp_type);
- if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT &&
- sel->u.transport)
+ if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_TRANSPORT &&
+ prm->tp_sel->u.transport)
{
- pj_strdup(pool, ip_addr, &sel->u.transport->local_name.host);
- *port = sel->u.transport->local_name.port;
+ const pjsip_transport *tp = prm->tp_sel->u.transport;
+ if (prm->local_if) {
+ status = get_net_interface((pjsip_transport_type_e)tp->key.type,
+ &prm->dst_host, &tmp_str);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ pj_strdup(pool, &prm->ret_addr, &tmp_str);
+ prm->ret_port = pj_sockaddr_get_port(&tp->local_addr);
+ prm->ret_tp = tp;
+ } else {
+ pj_strdup(pool, &prm->ret_addr, &tp->local_name.host);
+ prm->ret_port = (pj_uint16_t)tp->local_name.port;
+ }
status = PJ_SUCCESS;
- } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
- sel->u.listener)
+ } else if (prm->tp_sel && prm->tp_sel->type == PJSIP_TPSELECTOR_LISTENER &&
+ prm->tp_sel->u.listener)
{
- pj_strdup(pool, ip_addr, &sel->u.listener->addr_name.host);
- *port = sel->u.listener->addr_name.port;
+ if (prm->local_if) {
+ status = get_net_interface(prm->tp_sel->u.listener->type,
+ &prm->dst_host, &tmp_str);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ pj_strdup(pool, &prm->ret_addr, &tmp_str);
+ } else {
+ pj_strdup(pool, &prm->ret_addr,
+ &prm->tp_sel->u.listener->addr_name.host);
+ }
+ prm->ret_port = (pj_uint16_t)prm->tp_sel->u.listener->addr_name.port;
status = PJ_SUCCESS;
} else if ((flag & PJSIP_TRANSPORT_DATAGRAM) != 0) {
-
pj_sockaddr remote;
int addr_len;
pjsip_transport *tp;
pj_bzero(&remote, sizeof(remote));
- if (type & PJSIP_TRANSPORT_IPV6) {
+ if (prm->tp_type & PJSIP_TRANSPORT_IPV6) {
addr_len = sizeof(pj_sockaddr_in6);
remote.addr.sa_family = pj_AF_INET6();
} else {
@@ -1196,13 +1331,23 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
remote.addr.sa_family = pj_AF_INET();
}
- status = pjsip_tpmgr_acquire_transport(tpmgr, type, &remote,
+ status = pjsip_tpmgr_acquire_transport(tpmgr, prm->tp_type, &remote,
addr_len, NULL, &tp);
if (status == PJ_SUCCESS) {
- pj_strdup(pool, ip_addr, &tp->local_name.host);
- *port = tp->local_name.port;
- status = PJ_SUCCESS;
+ if (prm->local_if) {
+ status = get_net_interface((pjsip_transport_type_e)
+ tp->key.type,
+ &prm->dst_host, &tmp_str);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+ pj_strdup(pool, &prm->ret_addr, &tmp_str);
+ prm->ret_port = pj_sockaddr_get_port(&tp->local_addr);
+ prm->ret_tp = tp;
+ } else {
+ pj_strdup(pool, &prm->ret_addr, &tp->local_name.host);
+ prm->ret_port = (pj_uint16_t)tp->local_name.port;
+ }
pjsip_transport_dec_ref(tp);
}
@@ -1215,22 +1360,66 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
f = tpmgr->factory_list.next;
while (f != &tpmgr->factory_list) {
- if (f->type == type)
+ if (f->type == prm->tp_type)
break;
f = f->next;
}
if (f != &tpmgr->factory_list) {
- pj_strdup(pool, ip_addr, &f->addr_name.host);
- *port = f->addr_name.port;
+ if (prm->local_if) {
+ status = get_net_interface(f->type, &prm->dst_host,
+ &tmp_str);
+ if (status == PJ_SUCCESS) {
+ pj_strdup(pool, &prm->ret_addr, &tmp_str);
+ } else {
+ /* It could fail "normally" on certain cases, e.g.
+ * when connecting to IPv6 link local address, it
+ * will wail with EINVAL.
+ * In this case, fallback to use the default interface
+ * rather than failing the call.
+ */
+ PJ_PERROR(5,(THIS_FILE, status, "Warning: unable to "
+ "determine local interface"));
+ pj_strdup(pool, &prm->ret_addr, &f->addr_name.host);
+ status = PJ_SUCCESS;
+ }
+ } else {
+ pj_strdup(pool, &prm->ret_addr, &f->addr_name.host);
+ }
+ prm->ret_port = (pj_uint16_t)f->addr_name.port;
status = PJ_SUCCESS;
}
pj_lock_release(tpmgr->lock);
}
+on_return:
return status;
}
+PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pjsip_tpselector *sel,
+ pj_str_t *ip_addr,
+ int *port)
+{
+ pjsip_tpmgr_fla2_param prm;
+ pj_status_t status;
+
+ pjsip_tpmgr_fla2_param_default(&prm);
+ prm.tp_type = type;
+ prm.tp_sel = sel;
+
+ status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &prm);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ *ip_addr = prm.ret_addr;
+ *port = prm.ret_port;
+
+ return PJ_SUCCESS;
+}
+
/*
* Return number of transports currently registered to the transport
* manager.
diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
index 141434b..808cee9 100644
--- a/pjsip/src/pjsip/sip_transport_tcp.c
+++ b/pjsip/src/pjsip/sip_transport_tcp.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tcp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_transport_tcp.c 4294 2012-11-06 05:02:10Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -57,6 +57,7 @@ struct tcp_listener
pjsip_endpoint *endpt;
pjsip_tpmgr *tpmgr;
pj_activesock_t *asock;
+ pj_sockaddr bound_addr;
pj_qos_type qos_type;
pj_qos_params qos_params;
};
@@ -73,6 +74,7 @@ struct delayed_tdata
{
PJ_DECL_LIST_MEMBER(struct delayed_tdata);
pjsip_tx_data_op_key *tdata_op_key;
+ pj_time_val timeout;
};
@@ -140,8 +142,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
static pj_status_t tcp_create(struct tcp_listener *listener,
pj_pool_t *pool,
pj_sock_t sock, pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
+ const pj_sockaddr *local,
+ const pj_sockaddr *remote,
struct tcp_transport **p_tcp);
@@ -158,10 +160,10 @@ static void tcp_perror(const char *sender, const char *title,
static void sockaddr_to_host_port( pj_pool_t *pool,
pjsip_host_port *host_port,
- const pj_sockaddr_in *addr )
+ const pj_sockaddr *addr )
{
host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4);
- pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 2);
+ pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0);
host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
host_port->port = pj_sockaddr_get_port(addr);
}
@@ -266,17 +268,21 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
listener->factory.pool = pool;
- listener->factory.type = PJSIP_TRANSPORT_TCP;
- listener->factory.type_name = "tcp";
+ listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP :
+ PJSIP_TRANSPORT_TCP6;
+ listener->factory.type_name = (char*)
+ pjsip_transport_get_type_name(listener->factory.type);
listener->factory.flag =
- pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP);
+ pjsip_transport_get_flag_from_type(listener->factory.type);
listener->qos_type = cfg->qos_type;
pj_memcpy(&listener->qos_params, &cfg->qos_params,
sizeof(cfg->qos_params));
pj_ansi_strcpy(listener->factory.obj_name, "tcplis");
+ if (listener->factory.type==PJSIP_TRANSPORT_TCP6)
+ pj_ansi_strcat(listener->factory.obj_name, "6");
- status = pj_lock_create_recursive_mutex(pool, "tcplis",
+ status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
&listener->factory.lock);
if (status != PJ_SUCCESS)
goto on_error;
@@ -292,6 +298,11 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
2, listener->factory.obj_name,
"SIP TCP listener socket");
+ /* Bind address may be different than factory.local_addr because
+ * factory.local_addr will be resolved below.
+ */
+ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr);
+
/* Bind socket */
listener_addr = &listener->factory.local_addr;
pj_sockaddr_cp(listener_addr, &cfg->bind_addr);
@@ -326,19 +337,18 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
if (!pj_sockaddr_has_addr(listener_addr)) {
pj_sockaddr hostip;
- status = pj_gethostip(pj_AF_INET(), &hostip);
+ status = pj_gethostip(listener->bound_addr.addr.sa_family,
+ &hostip);
if (status != PJ_SUCCESS)
goto on_error;
- pj_memcpy(pj_sockaddr_get_addr(listener_addr),
- pj_sockaddr_get_addr(&hostip),
- pj_sockaddr_get_addr_len(&hostip));
+ pj_sockaddr_copy_addr(listener_addr, &hostip);
}
/* Save the address name */
sockaddr_to_host_port(listener->factory.pool,
&listener->factory.addr_name,
- (pj_sockaddr_in*)listener_addr);
+ listener_addr);
}
/* If port is zero, get the bound port */
@@ -535,8 +545,8 @@ static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
static pj_status_t tcp_create( struct tcp_listener *listener,
pj_pool_t *pool,
pj_sock_t sock, pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
+ const pj_sockaddr *local,
+ const pj_sockaddr *remote,
struct tcp_transport **p_tcp)
{
struct tcp_transport *tcp;
@@ -544,6 +554,7 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
pj_activesock_cfg asock_cfg;
pj_activesock_cb tcp_callback;
const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA;
+ char print_addr[PJ_INET6_ADDRSTRLEN+10];
pj_status_t status;
@@ -579,18 +590,21 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
goto on_error;
}
- tcp->base.key.type = PJSIP_TRANSPORT_TCP;
- pj_memcpy(&tcp->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
- tcp->base.type_name = "tcp";
- tcp->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP);
+ tcp->base.key.type = listener->factory.type;
+ pj_sockaddr_cp(&tcp->base.key.rem_addr, remote);
+ tcp->base.type_name = (char*)pjsip_transport_get_type_name(
+ (pjsip_transport_type_e)tcp->base.key.type);
+ tcp->base.flag = pjsip_transport_get_flag_from_type(
+ (pjsip_transport_type_e)tcp->base.key.type);
tcp->base.info = (char*) pj_pool_alloc(pool, 64);
- pj_ansi_snprintf(tcp->base.info, 64, "TCP to %s:%d",
- pj_inet_ntoa(remote->sin_addr),
- (int)pj_ntohs(remote->sin_port));
+ pj_ansi_snprintf(tcp->base.info, 64, "%s to %s",
+ tcp->base.type_name,
+ pj_sockaddr_print(remote, print_addr,
+ sizeof(print_addr), 3));
- tcp->base.addr_len = sizeof(pj_sockaddr_in);
- pj_memcpy(&tcp->base.local_addr, local, sizeof(pj_sockaddr_in));
+ tcp->base.addr_len = pj_sockaddr_get_len(remote);
+ pj_sockaddr_cp(&tcp->base.local_addr, local);
sockaddr_to_host_port(pool, &tcp->base.local_name, local);
sockaddr_to_host_port(pool, &tcp->base.remote_name, remote);
tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING;
@@ -601,7 +615,6 @@ static pj_status_t tcp_create( struct tcp_listener *listener,
tcp->base.do_shutdown = &tcp_shutdown;
tcp->base.destroy = &tcp_destroy_transport;
-
/* Create active socket */
pj_activesock_cfg_default(&asock_cfg);
asock_cfg.async_cnt = 1;
@@ -649,6 +662,9 @@ on_error:
/* Flush all delayed transmision once the socket is connected. */
static void tcp_flush_pending_tx(struct tcp_transport *tcp)
{
+ pj_time_val now;
+
+ pj_gettickcount(&now);
pj_lock_acquire(tcp->base.lock);
while (!pj_list_empty(&tcp->delayed_list)) {
struct delayed_tdata *pending_tx;
@@ -663,12 +679,20 @@ static void tcp_flush_pending_tx(struct tcp_transport *tcp)
tdata = pending_tx->tdata_op_key->tdata;
op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+ if (pending_tx->timeout.sec > 0 &&
+ PJ_TIME_VAL_GT(now, pending_tx->timeout))
+ {
+ continue;
+ }
+
/* send! */
size = tdata->buf.cur - tdata->buf.start;
status = pj_activesock_send(tcp->asock, op_key, tdata->buf.start,
&size, 0);
if (status != PJ_EPENDING) {
+ pj_lock_release(tcp->base.lock);
on_data_sent(tcp->asock, op_key, size);
+ pj_lock_acquire(tcp->base.lock);
}
}
@@ -791,7 +815,7 @@ static pj_status_t tcp_start_read(struct tcp_transport *tcp)
{
pj_pool_t *pool;
pj_ssize_t size;
- pj_sockaddr_in *rem_addr;
+ pj_sockaddr *rem_addr;
void *readbuf[1];
pj_status_t status;
@@ -814,11 +838,11 @@ static pj_status_t tcp_start_read(struct tcp_transport *tcp)
sizeof(pj_ioqueue_op_key_t));
tcp->rdata.pkt_info.src_addr = tcp->base.key.rem_addr;
- tcp->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in);
- rem_addr = (pj_sockaddr_in*) &tcp->base.key.rem_addr;
- pj_ansi_strcpy(tcp->rdata.pkt_info.src_name,
- pj_inet_ntoa(rem_addr->sin_addr));
- tcp->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port);
+ tcp->rdata.pkt_info.src_addr_len = sizeof(tcp->rdata.pkt_info.src_addr);
+ rem_addr = &tcp->base.key.rem_addr;
+ pj_sockaddr_print(rem_addr, tcp->rdata.pkt_info.src_name,
+ sizeof(tcp->rdata.pkt_info.src_name), 0);
+ tcp->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr);
size = sizeof(tcp->rdata.pkt_info.packet);
readbuf[0] = tcp->rdata.pkt_info.packet;
@@ -848,23 +872,25 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
struct tcp_listener *listener;
struct tcp_transport *tcp;
pj_sock_t sock;
- pj_sockaddr_in local_addr;
+ pj_sockaddr local_addr;
pj_status_t status;
/* Sanity checks */
PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr &&
addr_len && p_transport, PJ_EINVAL);
- /* Check that address is a sockaddr_in */
- PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() &&
- addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL);
+ /* Check that address is a sockaddr_in or sockaddr_in6*/
+ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() &&
+ addr_len == sizeof(pj_sockaddr_in)) ||
+ (rem_addr->addr.sa_family == pj_AF_INET6() &&
+ addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL);
listener = (struct tcp_listener*)factory;
-
/* Create socket */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(),
+ 0, &sock);
if (status != PJ_SUCCESS)
return status;
@@ -874,15 +900,20 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
2, listener->factory.obj_name,
"outgoing SIP TCP socket");
- /* Bind to any port */
- status = pj_sock_bind_in(sock, 0, 0);
+ /* Bind to listener's address and any port */
+ pj_bzero(&local_addr, sizeof(local_addr));
+ pj_sockaddr_cp(&local_addr, &listener->bound_addr);
+ pj_sockaddr_set_port(&local_addr, 0);
+
+ status = pj_sock_bind(sock, &local_addr,
+ pj_sockaddr_get_len(&local_addr));
if (status != PJ_SUCCESS) {
pj_sock_close(sock);
return status;
}
/* Get the local port */
- addr_len = sizeof(pj_sockaddr_in);
+ addr_len = sizeof(local_addr);
status = pj_sock_getsockname(sock, &local_addr, &addr_len);
if (status != PJ_SUCCESS) {
pj_sock_close(sock);
@@ -890,12 +921,13 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
}
/* Initially set the address from the listener's address */
- local_addr.sin_addr.s_addr =
- ((pj_sockaddr_in*)&listener->factory.local_addr)->sin_addr.s_addr;
+ if (!pj_sockaddr_has_addr(&local_addr)) {
+ pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr);
+ }
/* Create the transport descriptor */
status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr,
- (pj_sockaddr_in*)rem_addr, &tcp);
+ rem_addr, &tcp);
if (status != PJ_SUCCESS)
return status;
@@ -903,7 +935,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
/* Start asynchronous connect() operation */
tcp->has_pending_connect = PJ_TRUE;
status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr,
- sizeof(pj_sockaddr_in));
+ addr_len);
if (status == PJ_SUCCESS) {
on_connect_complete(tcp->asock, PJ_SUCCESS);
} else if (status != PJ_EPENDING) {
@@ -915,18 +947,17 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
/* Update (again) local address, just in case local address currently
* set is different now that asynchronous connect() is started.
*/
- addr_len = sizeof(pj_sockaddr_in);
+ addr_len = sizeof(local_addr);
if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) {
- pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tcp->base.local_addr;
+ pj_sockaddr *tp_addr = &tcp->base.local_addr;
/* Some systems (like old Win32 perhaps) may not set local address
* properly before socket is fully connected.
*/
- if (tp_addr->sin_addr.s_addr != local_addr.sin_addr.s_addr &&
- local_addr.sin_addr.s_addr != 0)
+ if (pj_sockaddr_cmp(tp_addr, &local_addr) &&
+ pj_sockaddr_get_port(&local_addr) != 0)
{
- tp_addr->sin_addr.s_addr = local_addr.sin_addr.s_addr;
- tp_addr->sin_port = local_addr.sin_port;
+ pj_sockaddr_cp(tp_addr, &local_addr);
sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name,
&local_addr);
}
@@ -962,6 +993,7 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock,
struct tcp_transport *tcp;
char addr[PJ_INET6_ADDRSTRLEN+10];
pjsip_tp_state_callback state_cb;
+ pj_sockaddr tmp_src_addr;
pj_status_t status;
PJ_UNUSED_ARG(src_addr_len);
@@ -985,13 +1017,19 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock,
2, listener->factory.obj_name,
"incoming SIP TCP socket");
+ /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var,
+ * just in case.
+ */
+ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr));
+ pj_sockaddr_cp(&tmp_src_addr, src_addr);
+
/*
* Incoming connection!
* Create TCP transport for the new socket.
*/
status = tcp_create( listener, NULL, sock, PJ_TRUE,
- (const pj_sockaddr_in*)&listener->factory.local_addr,
- (const pj_sockaddr_in*)src_addr, &tcp);
+ &listener->factory.local_addr,
+ &tmp_src_addr, &tcp);
if (status == PJ_SUCCESS) {
status = tcp_start_read(tcp);
if (status != PJ_SUCCESS) {
@@ -1095,9 +1133,9 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
/* Check the address is supported */
- PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);
-
-
+ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) ||
+ addr_len==sizeof(pj_sockaddr_in6)),
+ PJ_EINVAL);
/* Init op key. */
tdata->op_key.tdata = tdata;
@@ -1122,10 +1160,19 @@ static pj_status_t tcp_send_msg(pjsip_transport *transport,
/*
* connect() is still in progress. Put the transmit data to
* the delayed list.
+ * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583),
+ * we also add timeout value for the transmit data. When the
+ * connect() is completed, the timeout value will be checked to
+ * determine whether the transmit data needs to be sent.
*/
- delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool,
- struct delayed_tdata);
+ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool,
+ struct delayed_tdata);
delayed_tdata->tdata_op_key = &tdata->op_key;
+ if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
+ pj_gettickcount(&delayed_tdata->timeout);
+ delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td;
+ pj_time_val_normalize(&delayed_tdata->timeout);
+ }
pj_list_push_back(&tcp->delayed_list, delayed_tdata);
status = PJ_EPENDING;
@@ -1269,7 +1316,7 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
pj_status_t status)
{
struct tcp_transport *tcp;
- pj_sockaddr_in addr;
+ pj_sockaddr addr;
int addrlen;
pjsip_tp_state_callback state_cb;
@@ -1314,15 +1361,14 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock,
* set is different now that the socket is connected (could happen
* on some systems, like old Win32 probably?).
*/
- addrlen = sizeof(pj_sockaddr_in);
+ addrlen = sizeof(addr);
if (pj_sock_getsockname(tcp->sock, &addr, &addrlen)==PJ_SUCCESS) {
- pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tcp->base.local_addr;
+ pj_sockaddr *tp_addr = &tcp->base.local_addr;
if (pj_sockaddr_has_addr(&addr) &&
- tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr)
+ pj_sockaddr_cmp(&addr, tp_addr) != 0)
{
- tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr;
- tp_addr->sin_port = addr.sin_port;
+ pj_sockaddr_cp(tp_addr, &addr);
sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name,
tp_addr);
}
diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
index 878b6db..2f244cc 100644
--- a/pjsip/src/pjsip/sip_transport_tls.c
+++ b/pjsip/src/pjsip/sip_transport_tls.c
@@ -1,4 +1,4 @@
-/* $Id: sip_transport_tls.c 4146 2012-05-30 06:35:59Z nanang $ */
+/* $Id: sip_transport_tls.c 4411 2013-03-04 04:34:38Z nanang $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -55,6 +55,7 @@ struct tls_listener
pjsip_endpoint *endpt;
pjsip_tpmgr *tpmgr;
pj_ssl_sock_t *ssock;
+ pj_sockaddr bound_addr;
pj_ssl_cert_t *cert;
pjsip_tls_setting tls_setting;
};
@@ -71,6 +72,7 @@ struct delayed_tdata
{
PJ_DECL_LIST_MEMBER(struct delayed_tdata);
pjsip_tx_data_op_key *tdata_op_key;
+ pj_time_val timeout;
};
@@ -146,8 +148,8 @@ static pj_status_t tls_create(struct tls_listener *listener,
pj_pool_t *pool,
pj_ssl_sock_t *ssock,
pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
+ const pj_sockaddr *local,
+ const pj_sockaddr *remote,
const pj_str_t *remote_name,
struct tls_transport **p_tls);
@@ -165,10 +167,10 @@ static void tls_perror(const char *sender, const char *title,
static void sockaddr_to_host_port( pj_pool_t *pool,
pjsip_host_port *host_port,
- const pj_sockaddr_in *addr )
+ const pj_sockaddr *addr )
{
host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4);
- pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 2);
+ pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0);
host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
host_port->port = pj_sockaddr_get_port(addr);
}
@@ -234,29 +236,50 @@ static void tls_init_shutdown(struct tls_transport *tls, pj_status_t status)
*/
PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
const pjsip_tls_setting *opt,
- const pj_sockaddr_in *local,
+ const pj_sockaddr_in *local_in,
const pjsip_host_port *a_name,
unsigned async_cnt,
pjsip_tpfactory **p_factory)
{
+ pj_sockaddr local;
+
+ if (local_in)
+ pj_sockaddr_cp(&local, local_in);
+
+ return pjsip_tls_transport_start2(endpt, opt, (local_in? &local : NULL),
+ a_name, async_cnt, p_factory);
+}
+
+PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
+ const pjsip_tls_setting *opt,
+ const pj_sockaddr *local,
+ const pjsip_host_port *a_name,
+ unsigned async_cnt,
+ pjsip_tpfactory **p_factory)
+{
pj_pool_t *pool;
+ pj_bool_t is_ipv6;
+ int af;
struct tls_listener *listener;
pj_ssl_sock_param ssock_param;
- pj_sockaddr_in *listener_addr;
+ pj_sockaddr *listener_addr;
pj_bool_t has_listener;
pj_status_t status;
/* Sanity check */
PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL);
+ is_ipv6 = (local && local->addr.sa_family == pj_AF_INET6());
+ af = is_ipv6 ? pj_AF_INET6() : pj_AF_INET();
+
/* Verify that address given in a_name (if any) is valid */
if (a_name && a_name->host.slen) {
- pj_sockaddr_in tmp;
+ pj_sockaddr tmp;
- status = pj_sockaddr_in_init(&tmp, &a_name->host,
- (pj_uint16_t)a_name->port);
- if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY ||
- tmp.sin_addr.s_addr == PJ_INADDR_NONE)
+ status = pj_sockaddr_init(af, &tmp, &a_name->host,
+ (pj_uint16_t)a_name->port);
+ if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
+ (!is_ipv6 && tmp.ipv4.sin_addr.s_addr == PJ_INADDR_NONE))
{
/* Invalid address */
return PJ_EINVAL;
@@ -269,19 +292,25 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener);
listener->factory.pool = pool;
- listener->factory.type = PJSIP_TRANSPORT_TLS;
- listener->factory.type_name = "tls";
+ if (is_ipv6)
+ listener->factory.type = PJSIP_TRANSPORT_TLS6;
+ else
+ listener->factory.type = PJSIP_TRANSPORT_TLS;
+ listener->factory.type_name = (char*)
+ pjsip_transport_get_type_name(listener->factory.type);
listener->factory.flag =
- pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
+ pjsip_transport_get_flag_from_type(listener->factory.type);
pj_ansi_strcpy(listener->factory.obj_name, "tlslis");
+ if (is_ipv6)
+ pj_ansi_strcat(listener->factory.obj_name, "6");
if (opt)
pjsip_tls_setting_copy(pool, &listener->tls_setting, opt);
else
pjsip_tls_setting_default(&listener->tls_setting);
- status = pj_lock_create_recursive_mutex(pool, "tlslis",
+ status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
&listener->factory.lock);
if (status != PJ_SUCCESS)
goto on_error;
@@ -291,6 +320,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
/* Build SSL socket param */
pj_ssl_sock_param_default(&ssock_param);
+ ssock_param.sock_af = af;
ssock_param.cb.on_accept_complete = &on_accept_complete;
ssock_param.cb.on_data_read = &on_data_read;
ssock_param.cb.on_data_sent = &on_data_sent;
@@ -337,12 +367,17 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
if (status != PJ_SUCCESS)
goto on_error;
- listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr;
+ /* Bind address may be different than factory.local_addr because
+ * factory.local_addr will be resolved below.
+ */
+ listener_addr = &listener->factory.local_addr;
if (local) {
pj_sockaddr_cp((pj_sockaddr_t*)listener_addr,
(const pj_sockaddr_t*)local);
+ pj_sockaddr_cp(&listener->bound_addr, local);
} else {
- pj_sockaddr_in_init(listener_addr, NULL, 0);
+ pj_sockaddr_init(af, listener_addr, NULL, 0);
+ pj_sockaddr_init(af, &listener->bound_addr, NULL, 0);
}
/* Check if certificate/CA list for SSL socket is set */
@@ -400,14 +435,14 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
/* If the address returns 0.0.0.0, use the default
* interface address as the transport's address.
*/
- if (listener_addr->sin_addr.s_addr == 0) {
+ if (!pj_sockaddr_has_addr(listener_addr)) {
pj_sockaddr hostip;
- status = pj_gethostip(pj_AF_INET(), &hostip);
+ status = pj_gethostip(af, &hostip);
if (status != PJ_SUCCESS)
goto on_error;
- listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr;
+ pj_sockaddr_copy_addr(listener_addr, &hostip);
}
/* Save the address name */
@@ -417,7 +452,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt,
/* If port is zero, get the bound port */
if (listener->factory.addr_name.port == 0) {
- listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port);
+ listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
}
pj_ansi_snprintf(listener->factory.obj_name,
@@ -534,13 +569,14 @@ static pj_status_t tls_create( struct tls_listener *listener,
pj_pool_t *pool,
pj_ssl_sock_t *ssock,
pj_bool_t is_server,
- const pj_sockaddr_in *local,
- const pj_sockaddr_in *remote,
+ const pj_sockaddr *local,
+ const pj_sockaddr *remote,
const pj_str_t *remote_name,
struct tls_transport **p_tls)
{
struct tls_transport *tls;
const pj_str_t ka_pkt = PJSIP_TLS_KEEP_ALIVE_DATA;
+ char print_addr[PJ_INET6_ADDRSTRLEN+10];
pj_status_t status;
@@ -578,17 +614,21 @@ static pj_status_t tls_create( struct tls_listener *listener,
if (remote_name)
pj_strdup(pool, &tls->remote_name, remote_name);
- tls->base.key.type = PJSIP_TRANSPORT_TLS;
- pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in));
- tls->base.type_name = "tls";
- tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS);
+ tls->base.key.type = listener->factory.type;
+ pj_sockaddr_cp(&tls->base.key.rem_addr, remote);
+ tls->base.type_name = (char*)pjsip_transport_get_type_name(
+ (pjsip_transport_type_e)tls->base.key.type);
+ tls->base.flag = pjsip_transport_get_flag_from_type(
+ (pjsip_transport_type_e)tls->base.key.type);
tls->base.info = (char*) pj_pool_alloc(pool, 64);
- pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d",
- pj_inet_ntoa(remote->sin_addr),
- (int)pj_ntohs(remote->sin_port));
+ pj_ansi_snprintf(tls->base.info, 64, "%s to %s",
+ tls->base.type_name,
+ pj_sockaddr_print(remote, print_addr,
+ sizeof(print_addr), 3));
- tls->base.addr_len = sizeof(pj_sockaddr_in);
+
+ tls->base.addr_len = pj_sockaddr_get_len(remote);
tls->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING;
/* Set initial local address */
@@ -599,11 +639,10 @@ static pj_status_t tls_create( struct tls_listener *listener,
pj_sockaddr_cp(&tls->base.local_addr, local);
}
- sockaddr_to_host_port(pool, &tls->base.local_name,
- (pj_sockaddr_in*)&tls->base.local_addr);
+ sockaddr_to_host_port(pool, &tls->base.local_name, &tls->base.local_addr);
if (tls->remote_name.slen) {
tls->base.remote_name.host = tls->remote_name;
- tls->base.remote_name.port = pj_sockaddr_in_get_port(remote);
+ tls->base.remote_name.port = pj_sockaddr_get_port(remote);
} else {
sockaddr_to_host_port(pool, &tls->base.remote_name, remote);
}
@@ -647,6 +686,9 @@ on_error:
/* Flush all delayed transmision once the socket is connected. */
static void tls_flush_pending_tx(struct tls_transport *tls)
{
+ pj_time_val now;
+
+ pj_gettickcount(&now);
pj_lock_acquire(tls->base.lock);
while (!pj_list_empty(&tls->delayed_list)) {
struct delayed_tdata *pending_tx;
@@ -661,13 +703,21 @@ static void tls_flush_pending_tx(struct tls_transport *tls)
tdata = pending_tx->tdata_op_key->tdata;
op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+ if (pending_tx->timeout.sec > 0 &&
+ PJ_TIME_VAL_GT(now, pending_tx->timeout))
+ {
+ continue;
+ }
+
/* send! */
size = tdata->buf.cur - tdata->buf.start;
status = pj_ssl_sock_send(tls->ssock, op_key, tdata->buf.start,
&size, 0);
if (status != PJ_EPENDING) {
+ pj_lock_release(tls->base.lock);
on_data_sent(tls->ssock, op_key, size);
+ pj_lock_acquire(tls->base.lock);
}
}
pj_lock_release(tls->base.lock);
@@ -784,7 +834,7 @@ static pj_status_t tls_start_read(struct tls_transport *tls)
{
pj_pool_t *pool;
pj_ssize_t size;
- pj_sockaddr_in *rem_addr;
+ pj_sockaddr *rem_addr;
void *readbuf[1];
pj_status_t status;
@@ -807,11 +857,11 @@ static pj_status_t tls_start_read(struct tls_transport *tls)
sizeof(pj_ioqueue_op_key_t));
tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr;
- tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in);
- rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr;
- pj_ansi_strcpy(tls->rdata.pkt_info.src_name,
- pj_inet_ntoa(rem_addr->sin_addr));
- tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port);
+ tls->rdata.pkt_info.src_addr_len = sizeof(tls->rdata.pkt_info.src_addr);
+ rem_addr = &tls->base.key.rem_addr;
+ pj_sockaddr_print(rem_addr, tls->rdata.pkt_info.src_name,
+ sizeof(tls->rdata.pkt_info.src_name), 0);
+ tls->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr);
size = sizeof(tls->rdata.pkt_info.packet);
readbuf[0] = tls->rdata.pkt_info.packet;
@@ -844,7 +894,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
pj_pool_t *pool;
pj_ssl_sock_t *ssock;
pj_ssl_sock_param ssock_param;
- pj_sockaddr_in local_addr;
+ pj_sockaddr local_addr;
pj_str_t remote_name;
pj_status_t status;
@@ -852,9 +902,11 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr &&
addr_len && p_transport, PJ_EINVAL);
- /* Check that address is a sockaddr_in */
- PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() &&
- addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL);
+ /* Check that address is a sockaddr_in or sockaddr_in6*/
+ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() &&
+ addr_len == sizeof(pj_sockaddr_in)) ||
+ (rem_addr->addr.sa_family == pj_AF_INET6() &&
+ addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL);
listener = (struct tls_listener*)factory;
@@ -871,6 +923,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
/* Build SSL socket param */
pj_ssl_sock_param_default(&ssock_param);
+ ssock_param.sock_af = (factory->type & PJSIP_TRANSPORT_IPV6) ?
+ pj_AF_INET6() : pj_AF_INET();
ssock_param.cb.on_connect_complete = &on_connect_complete;
ssock_param.cb.on_data_read = &on_data_read;
ssock_param.cb.on_data_sent = &on_data_sent;
@@ -921,12 +975,14 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
return status;
}
- /* Initially set bind address to PJ_INADDR_ANY port 0 */
- pj_sockaddr_in_init(&local_addr, NULL, 0);
+ /* Initially set bind address to listener's bind address */
+ pj_sockaddr_init(listener->bound_addr.addr.sa_family,
+ &local_addr, NULL, 0);
+ pj_sockaddr_copy_addr(&local_addr, &listener->bound_addr);
/* Create the transport descriptor */
status = tls_create(listener, pool, ssock, PJ_FALSE, &local_addr,
- (pj_sockaddr_in*)rem_addr, &remote_name, &tls);
+ rem_addr, &remote_name, &tls);
if (status != PJ_SUCCESS)
return status;
@@ -973,7 +1029,7 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
}
sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
- (pj_sockaddr_in*)&tls->base.local_addr);
+ &tls->base.local_addr);
}
PJ_LOG(4,(tls->base.obj_name,
@@ -1007,6 +1063,7 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
pj_ssl_sock_info ssl_info;
char addr[PJ_INET6_ADDRSTRLEN+10];
pjsip_tp_state_callback state_cb;
+ pj_sockaddr tmp_src_addr;
pj_bool_t is_shutdown;
pj_status_t status;
@@ -1034,13 +1091,17 @@ static pj_bool_t on_accept_complete(pj_ssl_sock_t *ssock,
return PJ_TRUE;
}
+ /* Copy to larger buffer, just in case */
+ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr));
+ pj_sockaddr_cp(&tmp_src_addr, src_addr);
+
/*
* Incoming connection!
* Create TLS transport for the new socket.
*/
status = tls_create( listener, NULL, new_ssock, PJ_TRUE,
- (const pj_sockaddr_in*)&listener->factory.local_addr,
- (const pj_sockaddr_in*)src_addr, NULL, &tls);
+ &listener->factory.local_addr,
+ &tmp_src_addr, NULL, &tls);
if (status != PJ_SUCCESS)
return PJ_TRUE;
@@ -1190,9 +1251,9 @@ static pj_status_t tls_send_msg(pjsip_transport *transport,
PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
/* Check the address is supported */
- PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL);
-
-
+ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) ||
+ addr_len==sizeof(pj_sockaddr_in6)),
+ PJ_EINVAL);
/* Init op key. */
tdata->op_key.tdata = tdata;
@@ -1217,10 +1278,19 @@ static pj_status_t tls_send_msg(pjsip_transport *transport,
/*
* connect() is still in progress. Put the transmit data to
* the delayed list.
+ * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583),
+ * we also add timeout value for the transmit data. When the
+ * connect() is completed, the timeout value will be checked to
+ * determine whether the transmit data needs to be sent.
*/
- delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool,
- struct delayed_tdata);
+ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool,
+ struct delayed_tdata);
delayed_tdata->tdata_op_key = &tdata->op_key;
+ if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
+ pj_gettickcount(&delayed_tdata->timeout);
+ delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td;
+ pj_time_val_normalize(&delayed_tdata->timeout);
+ }
pj_list_push_back(&tls->delayed_list, delayed_tdata);
status = PJ_EPENDING;
@@ -1365,7 +1435,7 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
{
struct tls_transport *tls;
pj_ssl_sock_info ssl_info;
- pj_sockaddr_in addr, *tp_addr;
+ pj_sockaddr addr, *tp_addr;
pjsip_tp_state_callback state_cb;
pj_bool_t is_shutdown;
@@ -1403,12 +1473,11 @@ static pj_bool_t on_connect_complete(pj_ssl_sock_t *ssock,
* set is different now that the socket is connected (could happen
* on some systems, like old Win32 probably?).
*/
- tp_addr = (pj_sockaddr_in*)&tls->base.local_addr;
+ tp_addr = &tls->base.local_addr;
pj_sockaddr_cp((pj_sockaddr_t*)&addr,
(pj_sockaddr_t*)&ssl_info.local_addr);
- if (tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) {
- tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr;
- tp_addr->sin_port = addr.sin_port;
+ if (pj_sockaddr_cmp(tp_addr, &addr) != 0) {
+ pj_sockaddr_cp(tp_addr, &addr);
sockaddr_to_host_port(tls->base.pool, &tls->base.local_name,
tp_addr);
}
diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c
index 7128891..723c061 100644
--- a/pjsip/src/pjsip/sip_ua_layer.c
+++ b/pjsip/src/pjsip/sip_ua_layer.c
@@ -1,4 +1,4 @@
-/* $Id: sip_ua_layer.c 3664 2011-07-19 03:42:28Z nanang $ */
+/* $Id: sip_ua_layer.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -302,9 +302,10 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua,
struct dlg_set *dlg_set;
dlg_set = (struct dlg_set*)
- pj_hash_get( mod_ua.dlg_table, dlg->local.info->tag.ptr,
- dlg->local.info->tag.slen,
- &dlg->local.tag_hval);
+ pj_hash_get_lower( mod_ua.dlg_table,
+ dlg->local.info->tag.ptr,
+ dlg->local.info->tag.slen,
+ &dlg->local.tag_hval);
if (dlg_set) {
/* This is NOT the first dialog in the dialog set.
@@ -326,9 +327,11 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua,
dlg->dlg_set = dlg_set;
/* Register the dialog set in the hash table. */
- pj_hash_set_np(mod_ua.dlg_table,
- dlg->local.info->tag.ptr, dlg->local.info->tag.slen,
- dlg->local.tag_hval, dlg_set->ht_entry, dlg_set);
+ pj_hash_set_np_lower(mod_ua.dlg_table,
+ dlg->local.info->tag.ptr,
+ dlg->local.info->tag.slen,
+ dlg->local.tag_hval, dlg_set->ht_entry,
+ dlg_set);
}
} else {
@@ -341,9 +344,10 @@ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua,
dlg->dlg_set = dlg_set;
- pj_hash_set_np(mod_ua.dlg_table,
- dlg->local.info->tag.ptr, dlg->local.info->tag.slen,
- dlg->local.tag_hval, dlg_set->ht_entry, dlg_set);
+ pj_hash_set_np_lower(mod_ua.dlg_table,
+ dlg->local.info->tag.ptr,
+ dlg->local.info->tag.slen,
+ dlg->local.tag_hval, dlg_set->ht_entry, dlg_set);
}
/* Unlock user agent. */
@@ -387,8 +391,9 @@ PJ_DEF(pj_status_t) pjsip_ua_unregister_dlg( pjsip_user_agent *ua,
/* If dialog list is empty, remove the dialog set from the hash table. */
if (pj_list_empty(&dlg_set->dlg_list)) {
- pj_hash_set(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr,
- dlg->local.info->tag.slen, dlg->local.tag_hval, NULL);
+ pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr,
+ dlg->local.info->tag.slen, dlg->local.tag_hval,
+ NULL);
/* Return dlg_set to free nodes. */
pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set);
@@ -449,8 +454,8 @@ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id,
/* Lookup the dialog set. */
dlg_set = (struct dlg_set*)
- pj_hash_get(mod_ua.dlg_table, local_tag->ptr, local_tag->slen,
- NULL);
+ pj_hash_get_lower(mod_ua.dlg_table, local_tag->ptr,
+ local_tag->slen, NULL);
if (dlg_set == NULL) {
/* Not found */
pj_mutex_unlock(mod_ua.mutex);
@@ -462,7 +467,7 @@ PJ_DEF(pjsip_dialog*) pjsip_ua_find_dialog(const pj_str_t *call_id,
*/
dlg = dlg_set->dlg_list.next;
while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) {
- if (pj_strcmp(&dlg->remote.info->tag, remote_tag) == 0)
+ if (pj_stricmp(&dlg->remote.info->tag, remote_tag) == 0)
break;
dlg = dlg->next;
}
@@ -563,7 +568,8 @@ static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata )
/* Lookup the dialog set. */
dlg_set = (struct dlg_set*)
- pj_hash_get(mod_ua.dlg_table, tag->ptr, tag->slen, NULL);
+ pj_hash_get_lower(mod_ua.dlg_table, tag->ptr, tag->slen,
+ NULL);
return dlg_set;
}
}
@@ -624,7 +630,7 @@ retry_on_deadlock:
dlg = dlg_set->dlg_list.next;
while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) {
- if (pj_strcmp(&dlg->remote.info->tag, from_tag) == 0)
+ if (pj_stricmp(&dlg->remote.info->tag, from_tag) == 0)
break;
dlg = dlg->next;
@@ -761,10 +767,10 @@ retry_on_deadlock:
/* Get the dialog set. */
dlg_set = (struct dlg_set*)
- pj_hash_get(mod_ua.dlg_table,
- rdata->msg_info.from->tag.ptr,
- rdata->msg_info.from->tag.slen,
- NULL);
+ pj_hash_get_lower(mod_ua.dlg_table,
+ rdata->msg_info.from->tag.ptr,
+ rdata->msg_info.from->tag.slen,
+ NULL);
if (!dlg_set) {
/* Unlock dialog hash table. */
@@ -812,7 +818,7 @@ retry_on_deadlock:
break;
/* Otherwise find the one with matching To tag. */
- if (pj_strcmp(to_tag, &dlg->remote.info->tag) == 0)
+ if (pj_stricmp(to_tag, &dlg->remote.info->tag) == 0)
break;
dlg = dlg->next;
diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c
index fba163c..4d54e2e 100644
--- a/pjsip/src/pjsip/sip_uri.c
+++ b/pjsip/src/pjsip/sip_uri.c
@@ -1,4 +1,4 @@
-/* $Id: sip_uri.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_uri.c 4228 2012-08-13 07:26:03Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -269,7 +269,10 @@ static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context,
/* Print "user:password@", if any. */
if (url->user.slen) {
- copy_advance_escape(buf, url->user, pc->pjsip_USER_SPEC);
+ const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ?
+ &pc->pjsip_USER_SPEC_LENIENT :
+ &pc->pjsip_USER_SPEC;
+ copy_advance_escape(buf, url->user, *spec);
if (url->passwd.slen) {
*buf++ = ':';
copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC);
diff --git a/pjsip/src/pjsip/sip_util_proxy.c b/pjsip/src/pjsip/sip_util_proxy.c
index 7a34534..02069b9 100644
--- a/pjsip/src/pjsip/sip_util_proxy.c
+++ b/pjsip/src/pjsip/sip_util_proxy.c
@@ -1,4 +1,4 @@
-/* $Id: sip_util_proxy.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: sip_util_proxy.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -345,7 +345,7 @@ PJ_DEF(pj_str_t) pjsip_calculate_branch_id( pjsip_rx_data *rdata )
/* If incoming request does not have RFC 3261 branch value, create
* a branch value from GUID .
*/
- if (pj_strncmp(&rdata->msg_info.via->branch_param,
+ if (pj_strnicmp(&rdata->msg_info.via->branch_param,
&rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) != 0 )
{
pj_str_t tmp;
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index c5278c4..5d7695d 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_acc.c 4185 2012-06-28 14:16:05Z ming $ */
+/* $Id: pjsua_acc.c 4318 2013-01-16 09:51:45Z bennylp $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -133,6 +133,9 @@ PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool,
pjsua_transport_config_dup(pool, &dst->rtp_cfg, &src->rtp_cfg);
+ pjsua_ice_config_dup(pool, &dst->ice_cfg, &src->ice_cfg);
+ pjsua_turn_config_dup(pool, &dst->turn_cfg, &src->turn_cfg);
+
pj_strdup(pool, &dst->ka_data, &src->ka_data);
}
@@ -283,7 +286,7 @@ static pj_status_t initialize_acc(unsigned acc_id)
* contact params.
*/
#if PJSUA_ADD_ICE_TAGS
- if (pjsua_var.media_cfg.enable_ice) {
+ if (acc_cfg->ice_cfg.enable_ice) {
unsigned new_len;
pj_str_t new_prm;
@@ -348,6 +351,18 @@ static pj_status_t initialize_acc(unsigned acc_id)
}
}
+ /* If account's ICE and TURN customization is not set, then
+ * initialize it with the settings from the global media config.
+ */
+ if (acc->cfg.ice_cfg_use == PJSUA_ICE_CONFIG_USE_DEFAULT) {
+ pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
+ &pjsua_var.media_cfg);
+ }
+ if (acc->cfg.turn_cfg_use == PJSUA_TURN_CONFIG_USE_DEFAULT) {
+ pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
+ &pjsua_var.media_cfg);
+ }
+
/* Mark account as valid */
pjsua_var.acc[acc_id].valid = PJ_TRUE;
@@ -470,6 +485,10 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
/* Otherwise subscribe to MWI, if it's enabled */
if (pjsua_var.acc[id].cfg.mwi_enabled)
pjsua_start_mwi(id, PJ_TRUE);
+
+ /* Start publish too */
+ if (acc->cfg.publish_enabled)
+ pjsua_pres_init_publish_acc(id);
}
pj_log_pop_indent();
@@ -1152,6 +1171,70 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
update_reg = PJ_TRUE;
}
+ /* Video settings */
+ acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show;
+ acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit;
+ acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags;
+ acc->cfg.vid_cap_dev = cfg->vid_cap_dev;
+ acc->cfg.vid_rend_dev = cfg->vid_rend_dev;
+ acc->cfg.vid_stream_rc_cfg = cfg->vid_stream_rc_cfg;
+
+ /* Media settings */
+ if (pj_stricmp(&acc->cfg.rtp_cfg.public_addr, &cfg->rtp_cfg.public_addr) ||
+ pj_stricmp(&acc->cfg.rtp_cfg.bound_addr, &cfg->rtp_cfg.bound_addr))
+ {
+ pjsua_transport_config_dup(acc->pool, &acc->cfg.rtp_cfg,
+ &cfg->rtp_cfg);
+ } else {
+ /* ..to save memory by not using the pool */
+ acc->cfg.rtp_cfg = cfg->rtp_cfg;
+ }
+
+ acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
+
+ /* STUN and Media customization */
+ if (acc->cfg.sip_stun_use != cfg->sip_stun_use) {
+ acc->cfg.sip_stun_use = cfg->sip_stun_use;
+ update_reg = PJ_TRUE;
+ }
+ acc->cfg.media_stun_use = cfg->media_stun_use;
+
+ /* ICE settings */
+ acc->cfg.ice_cfg_use = cfg->ice_cfg_use;
+ switch (acc->cfg.ice_cfg_use) {
+ case PJSUA_ICE_CONFIG_USE_DEFAULT:
+ /* Copy ICE settings from media settings so that we don't need to
+ * check the media config if we look for ICE config.
+ */
+ pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
+ &pjsua_var.media_cfg);
+ break;
+ case PJSUA_ICE_CONFIG_USE_CUSTOM:
+ pjsua_ice_config_dup(acc->pool, &acc->cfg.ice_cfg, &cfg->ice_cfg);
+ break;
+ }
+
+ /* TURN settings */
+ acc->cfg.turn_cfg_use = cfg->turn_cfg_use;
+ switch (acc->cfg.turn_cfg_use) {
+ case PJSUA_TURN_CONFIG_USE_DEFAULT:
+ /* Copy TURN settings from media settings so that we don't need to
+ * check the media config if we look for TURN config.
+ */
+ pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
+ &pjsua_var.media_cfg);
+ break;
+ case PJSUA_TURN_CONFIG_USE_CUSTOM:
+ pjsua_turn_config_dup(acc->pool, &acc->cfg.turn_cfg,
+ &cfg->turn_cfg);
+ break;
+ }
+
+ acc->cfg.use_srtp = cfg->use_srtp;
+
+ /* Call hold type */
+ acc->cfg.call_hold_type = cfg->call_hold_type;
+
/* Unregister first */
if (unreg_first) {
pjsua_acc_set_registration(acc->index, PJ_FALSE);
@@ -1174,16 +1257,6 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
pjsua_start_mwi(acc_id, PJ_TRUE);
}
- /* Video settings */
- acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show;
- acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit;
- acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags;
- acc->cfg.vid_cap_dev = cfg->vid_cap_dev;
- acc->cfg.vid_rend_dev = cfg->vid_rend_dev;
-
- /* Call hold type */
- acc->cfg.call_hold_type = cfg->call_hold_type;
-
on_return:
PJSUA_UNLOCK();
pj_log_pop_indent();
@@ -1515,6 +1588,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
const char *ob = ";ob";
char *tmp;
const char *beginquote, *endquote;
+ char transport_param[32];
int len;
/* Enclose IPv6 address in square brackets */
@@ -1525,9 +1599,21 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
beginquote = endquote = "";
}
+ /* Don't add transport parameter if it's UDP */
+ if (tp->key.type != PJSIP_TRANSPORT_UDP &&
+ tp->key.type != PJSIP_TRANSPORT_UDP6)
+ {
+ pj_ansi_snprintf(transport_param, sizeof(transport_param),
+ ";transport=%s",
+ pjsip_transport_get_type_name(
+ (pjsip_transport_type_e)tp->key.type));
+ } else {
+ transport_param[0] = '\0';
+ }
+
tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
- "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s%s>%.*s",
+ "<sip:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
(int)acc->user_part.slen,
acc->user_part.ptr,
(acc->user_part.slen? "@" : ""),
@@ -1536,7 +1622,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
via_addr->ptr,
endquote,
rport,
- tp->type_name,
+ transport_param,
(int)acc->cfg.contact_uri_params.slen,
acc->cfg.contact_uri_params.ptr,
(acc->cfg.use_rfc5626? ob: ""),
@@ -1776,9 +1862,26 @@ static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
/* Save transport and destination address. */
acc->ka_transport = param->rdata->tp_info.transport;
pjsip_transport_add_ref(acc->ka_transport);
- pj_memcpy(&acc->ka_target, &param->rdata->pkt_info.src_addr,
- param->rdata->pkt_info.src_addr_len);
- acc->ka_target_len = param->rdata->pkt_info.src_addr_len;
+
+ /* https://trac.pjsip.org/repos/ticket/1607:
+ * Calculate the destination address from the original request. Some
+ * (broken) servers send the response using different source address
+ * than the one that receives the request, which is forbidden by RFC
+ * 3581.
+ */
+ {
+ pjsip_transaction *tsx;
+ pjsip_tx_data *req;
+
+ tsx = pjsip_rdata_get_tsx(param->rdata);
+ PJ_ASSERT_ON_FAIL(tsx, return);
+
+ req = tsx->last_tx;
+
+ pj_memcpy(&acc->ka_target, &req->tp_info.dst_addr,
+ req->tp_info.dst_addr_len);
+ acc->ka_target_len = req->tp_info.dst_addr_len;
+ }
/* Setup and start the timer */
acc->ka_timer.cb = &keep_alive_timer_cb;
@@ -2146,6 +2249,13 @@ static pj_status_t pjsua_regc_init(int acc_id)
return PJ_SUCCESS;
}
+pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id)
+{
+ pjsua_acc *acc = &pjsua_var.acc[acc_id];
+
+ return acc->cfg.sip_stun_use != PJSUA_STUN_USE_DISABLED &&
+ pjsua_var.ua_cfg.stun_srv_cnt != 0;
+}
/*
* Update registration or perform unregistration.
@@ -2153,6 +2263,7 @@ static pj_status_t pjsua_regc_init(int acc_id)
PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
pj_bool_t renew)
{
+ pjsua_acc *acc;
pj_status_t status = 0;
pjsip_tx_data *tdata = 0;
@@ -2166,6 +2277,8 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
PJSUA_LOCK();
+ acc = &pjsua_var.acc[acc_id];
+
/* Cancel any re-registration timer */
if (pjsua_var.acc[acc_id].auto_rereg.timer.id) {
pjsua_var.acc[acc_id].auto_rereg.timer.id = PJ_FALSE;
@@ -2232,6 +2345,15 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id,
pjsip_regc_set_via_sent_by(pjsua_var.acc[acc_id].regc,
&pjsua_var.acc[acc_id].via_addr,
pjsua_var.acc[acc_id].via_tp);
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN
+ */
+ pjsua_acc_get_uac_addr(acc_id, tdata->pool,
+ &acc->cfg.reg_uri,
+ &tdata->via_addr,
+ NULL, NULL,
+ &tdata->via_tp);
}
//pjsua_process_msg_data(tdata, NULL);
@@ -2605,6 +2727,15 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
{
tdata->via_addr = pjsua_var.acc[acc_id].via_addr;
tdata->via_tp = pjsua_var.acc[acc_id].via_tp;
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN
+ */
+ pjsua_acc_get_uac_addr(acc_id, tdata->pool,
+ target,
+ &tdata->via_addr,
+ NULL, NULL,
+ &tdata->via_tp);
}
/* Done */
@@ -2612,46 +2743,40 @@ PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
return PJ_SUCCESS;
}
-
-PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
- pj_str_t *contact,
- pjsua_acc_id acc_id,
- const pj_str_t *suri)
+/* Get local transport address suitable to be used for Via or Contact address
+ * to send request to the specified destination URI.
+ */
+pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
+ pj_pool_t *pool,
+ const pj_str_t *dst_uri,
+ pjsip_host_port *addr,
+ pjsip_transport_type_e *p_tp_type,
+ int *secure,
+ const void **p_tp)
{
pjsua_acc *acc;
pjsip_sip_uri *sip_uri;
pj_status_t status;
pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
- pj_str_t local_addr;
- pjsip_tpselector tp_sel;
unsigned flag;
- int secure;
- int local_port;
- const char *beginquote, *endquote;
- char transport_param[32];
- const char *ob = ";ob";
+ pjsip_tpselector tp_sel;
+ pjsip_tpmgr *tpmgr;
+ pjsip_tpmgr_fla2_param tfla2_prm;
-
PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
acc = &pjsua_var.acc[acc_id];
- /* If force_contact is configured, then use use it */
- if (acc->cfg.force_contact.slen) {
- *contact = acc->cfg.force_contact;
- return PJ_SUCCESS;
- }
-
- /* If route-set is configured for the account, then URI is the
+ /* If route-set is configured for the account, then URI is the
* first entry of the route-set.
*/
if (!pj_list_empty(&acc->route_set)) {
- sip_uri = (pjsip_sip_uri*)
+ sip_uri = (pjsip_sip_uri*)
pjsip_uri_get_uri(acc->route_set.next->name_addr.uri);
} else {
pj_str_t tmp;
pjsip_uri *uri;
- pj_strdup_with_null(pool, &tmp, suri);
+ pj_strdup_with_null(pool, &tmp, dst_uri);
uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
if (uri == NULL)
@@ -2671,7 +2796,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
tp_type = PJSIP_TRANSPORT_UDP;
} else
tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
-
+
if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
return PJSIP_EUNSUPTRANSPORT;
@@ -2682,15 +2807,66 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
flag = pjsip_transport_get_flag_from_type(tp_type);
- secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
/* Init transport selector. */
- pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
+ pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
/* Get local address suitable to send request from */
- status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
- pool, tp_type, &tp_sel,
- &local_addr, &local_port);
+ pjsip_tpmgr_fla2_param_default(&tfla2_prm);
+ tfla2_prm.tp_type = tp_type;
+ tfla2_prm.tp_sel = &tp_sel;
+ tfla2_prm.dst_host = sip_uri->host;
+ tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
+ (flag & PJSIP_TRANSPORT_RELIABLE));
+
+ tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
+ status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ addr->host = tfla2_prm.ret_addr;
+ addr->port = tfla2_prm.ret_port;
+
+ if (p_tp_type)
+ *p_tp_type = tp_type;
+
+ if (secure) {
+ *secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+ }
+
+ if (p_tp)
+ *p_tp = tfla2_prm.ret_tp;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
+ pj_str_t *contact,
+ pjsua_acc_id acc_id,
+ const pj_str_t *suri)
+{
+ pjsua_acc *acc;
+ pj_status_t status;
+ pjsip_transport_type_e tp_type;
+ pjsip_host_port addr;
+ int secure;
+ const char *beginquote, *endquote;
+ char transport_param[32];
+ const char *ob = ";ob";
+
+
+ PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+ acc = &pjsua_var.acc[acc_id];
+
+ /* If force_contact is configured, then use use it */
+ if (acc->cfg.force_contact.slen) {
+ *contact = acc->cfg.force_contact;
+ return PJ_SUCCESS;
+ }
+
+ status = pjsua_acc_get_uac_addr(acc_id, pool, suri, &addr,
+ &tp_type, &secure, NULL);
if (status != PJ_SUCCESS)
return status;
@@ -2725,10 +2901,10 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
acc->user_part.ptr,
(acc->user_part.slen?"@":""),
beginquote,
- (int)local_addr.slen,
- local_addr.ptr,
+ (int)addr.host.slen,
+ addr.host.ptr,
endquote,
- local_port,
+ addr.port,
transport_param,
(int)acc->cfg.contact_uri_params.slen,
acc->cfg.contact_uri_params.ptr,
@@ -2760,6 +2936,8 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
pj_str_t local_addr;
pjsip_tpselector tp_sel;
+ pjsip_tpmgr *tpmgr;
+ pjsip_tpmgr_fla2_param tfla2_prm;
unsigned flag;
int secure;
int local_port;
@@ -2847,12 +3025,22 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
/* Get local address suitable to send request from */
- status = pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
- pool, tp_type, &tp_sel,
- &local_addr, &local_port);
+ pjsip_tpmgr_fla2_param_default(&tfla2_prm);
+ tfla2_prm.tp_type = tp_type;
+ tfla2_prm.tp_sel = &tp_sel;
+ tfla2_prm.dst_host = sip_uri->host;
+ tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
+ (flag & PJSIP_TRANSPORT_RELIABLE));
+
+ tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
+ status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
if (status != PJ_SUCCESS)
return status;
+ local_addr = tfla2_prm.ret_addr;
+ local_port = tfla2_prm.ret_port;
+
+
/* Enclose IPv6 address in square brackets */
if (tp_type & PJSIP_TRANSPORT_IPV6) {
beginquote = "[";
diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c
index 557a147..1163e08 100644
--- a/pjsip/src/pjsua-lib/pjsua_aud.c
+++ b/pjsip/src/pjsua-lib/pjsua_aud.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_aud.c 4145 2012-05-22 23:13:22Z bennylp $ */
+/* $Id: pjsua_aud.c 4336 2013-01-29 08:15:02Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -291,7 +291,8 @@ pj_status_t pjsua_aud_subsys_init()
/* Init the passthrough codec with supported formats only */
codec_cfg.passthrough.setting.fmt_cnt = ext_fmt_cnt;
codec_cfg.passthrough.setting.fmts = ext_fmts;
- codec_cfg.passthrough.setting.ilbc_mode = cfg->ilbc_mode;
+ codec_cfg.passthrough.setting.ilbc_mode =
+ pjsua_var.media_cfg.ilbc_mode;
}
#endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
@@ -559,7 +560,12 @@ static void dtmf_callback(pjmedia_stream *strm, void *user_data,
pj_log_pop_indent();
}
-
+/* Internal function: update audio channel after SDP negotiation.
+ * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov,
+ * for creating stream, etc, as after SDP negotiation and when
+ * the SDP media is not changed, the stream should remain running
+ * while the temporary/flip-flop pool may be released.
+ */
pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med,
pj_pool_t *tmp_pool,
pjmedia_stream_info *si,
@@ -583,20 +589,6 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med,
/* Check if no media is active */
if (si->dir != PJMEDIA_DIR_NONE) {
- /* Override ptime, if this option is specified. */
- if (pjsua_var.media_cfg.ptime != 0) {
- si->param->setting.frm_per_pkt = (pj_uint8_t)
- (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime);
- if (si->param->setting.frm_per_pkt == 0)
- si->param->setting.frm_per_pkt = 1;
- }
-
- /* Disable VAD, if this option is specified. */
- if (pjsua_var.media_cfg.no_vad) {
- si->param->setting.vad = 0;
- }
-
-
/* Optionally, application may modify other stream settings here
* (such as jitter buffer parameters, codec ptime, etc.)
*/
@@ -678,7 +670,7 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med,
port_name = pj_str("call");
}
status = pjmedia_conf_add_port( pjsua_var.mconf,
- call->inv->pool_prov,
+ call->inv->pool,
media_port,
&port_name,
(unsigned*)
@@ -1571,6 +1563,14 @@ static pj_status_t create_aud_param(pjmedia_aud_param *param,
param->flags &= ~(PJMEDIA_AUD_DEV_CAP_EC|PJMEDIA_AUD_DEV_CAP_EC_TAIL);
}
+ /* VAD settings */
+ if (pjsua_var.media_cfg.no_vad) {
+ param->flags &= ~PJMEDIA_AUD_DEV_CAP_VAD;
+ } else {
+ param->flags |= PJMEDIA_AUD_DEV_CAP_VAD;
+ param->vad_enabled = PJ_TRUE;
+ }
+
return PJ_SUCCESS;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 6f14709..7329333 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_call.c 4176 2012-06-23 03:06:52Z nanang $ */
+/* $Id: pjsua_call.c 4411 2013-03-04 04:34:38Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -106,6 +106,12 @@ static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
+static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry);
+
+/* Check and send reinvite for lock codec and ICE update */
+static pj_status_t process_pending_reinvite(pjsua_call *call);
+
/*
* Reset call descriptor.
*/
@@ -128,6 +134,8 @@ static void reset_call(pjsua_call_id id)
call_med->tp_auto_del = PJ_TRUE;
}
pjsua_call_setting_default(&call->opt);
+ pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,
+ (void*)(pj_size_t)id, &reinv_timer_cb);
}
@@ -179,6 +187,10 @@ pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
NULL, 1, &str_norefersub);
+ /* Add "INFO" in Allow header, for DTMF and video key frame request. */
+ pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
+ NULL, 1, &pjsip_info_method.name);
+
return status;
}
@@ -359,6 +371,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id,
pjsip_dialog *dlg = call->async_call.dlg;
unsigned options = 0;
pjsip_tx_data *tdata;
+ pj_bool_t cb_called = PJ_FALSE;
pj_status_t status = (info? info->status: PJ_SUCCESS);
PJSUA_LOCK();
@@ -387,9 +400,16 @@ on_make_call_med_tp_complete(pjsua_call_id call_id,
goto on_error;
}
- /* pjsua_media_channel_deinit() has been called. */
- if (call->async_call.med_ch_deinit)
+ /* pjsua_media_channel_deinit() has been called or
+ * call has been hung up.
+ */
+ if (call->async_call.med_ch_deinit ||
+ call->async_call.call_var.out_call.hangup)
+ {
+ PJ_LOG(4,(THIS_FILE, "Call has been hung up or media channel has "
+ "been deinitialized"));
goto on_error;
+ }
/* Create offer */
status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
@@ -476,8 +496,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id,
status = pjsip_inv_send_msg(inv, tdata);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
- status);
+ cb_called = PJ_TRUE;
/* Upon failure to send first request, the invite
* session would have been cleared.
@@ -487,6 +506,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id,
}
/* Done. */
+ call->med_ch_cb = NULL;
pjsip_dlg_dec_lock(dlg);
PJSUA_UNLOCK();
@@ -494,8 +514,11 @@ on_make_call_med_tp_complete(pjsua_call_id call_id,
return PJ_SUCCESS;
on_error:
- if (inv == NULL && call_id != -1 && pjsua_var.ua_cfg.cb.on_call_state)
+ if (inv == NULL && call_id != -1 && !cb_called &&
+ pjsua_var.ua_cfg.cb.on_call_state)
+ {
(*pjsua_var.ua_cfg.cb.on_call_state)(call_id, NULL);
+ }
if (dlg) {
/* This may destroy the dialog */
@@ -511,6 +534,10 @@ on_error:
pjsua_media_channel_deinit(call_id);
}
+ call->med_ch_cb = NULL;
+
+ pjsua_check_snd_dev_idle();
+
PJSUA_UNLOCK();
return status;
}
@@ -547,35 +574,23 @@ static pj_status_t apply_call_setting(pjsua_call *call,
pj_assert(opt->vid_cnt == 0);
#endif
+ call->opt = *opt;
+
/* If call is established, reinit media channel */
if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
- pjsua_call_setting old_opt;
+ pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC;
pj_status_t status;
- old_opt = call->opt;
- call->opt = *opt;
-
- /* Reinit media channel when media count is changed or we are the
- * answerer (as remote offer may 'extremely' modify the existing
- * media session, e.g: media type order).
- */
- if (rem_sdp ||
- opt->aud_cnt!=old_opt.aud_cnt || opt->vid_cnt!=old_opt.vid_cnt)
- {
- pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC;
- status = pjsua_media_channel_init(call->index, role,
- call->secure_level,
- call->inv->pool_prov,
- rem_sdp, NULL,
- PJ_FALSE, NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error re-initializing media channel",
- status);
- return status;
- }
+ status = pjsua_media_channel_init(call->index, role,
+ call->secure_level,
+ call->inv->pool_prov,
+ rem_sdp, NULL,
+ PJ_FALSE, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error re-initializing media channel",
+ status);
+ return status;
}
- } else {
- call->opt = *opt;
}
return PJ_SUCCESS;
@@ -811,8 +826,8 @@ static pj_status_t process_incoming_call_replace(pjsua_call *call,
{
pjsip_inv_session *replaced_inv;
struct pjsua_call *replaced_call;
- pjsip_tx_data *tdata;
- pj_status_t status;
+ pjsip_tx_data *tdata = NULL;
+ pj_status_t status = PJ_SUCCESS;
/* Get the invite session in the dialog */
replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
@@ -825,12 +840,29 @@ static pj_status_t process_incoming_call_replace(pjsua_call *call,
pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
call->index);
- PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
- call->index));
+ if (replaced_call->inv->state <= PJSIP_INV_STATE_EARLY &&
+ replaced_call->inv->role != PJSIP_ROLE_UAC)
+ {
+ if (replaced_call->last_code > 100 && replaced_call->last_code < 200)
+ {
+ pjsip_status_code code = replaced_call->last_code;
+ pj_str_t *text = &replaced_call->last_text;
+
+ PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with %d/%.*s",
+ call->index, code, text->slen, text->ptr));
+
+ /* Answer the new call with last response in the replaced call */
+ status = pjsip_inv_answer(call->inv, code, text, NULL, &tdata);
+ }
+ } else {
+ PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
+ call->index));
+
+ /* Answer the new call with 200 response */
+ status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata);
+ }
- /* Answer the new call with 200 response */
- status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata);
- if (status == PJ_SUCCESS)
+ if (status == PJ_SUCCESS && tdata)
status = pjsip_inv_send_msg(call->inv, tdata);
if (status != PJ_SUCCESS)
@@ -997,7 +1029,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
int acc_id;
pjsua_call *call;
int call_id = -1;
- int sip_err_code;
+ int sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
pjmedia_sdp_session *offer=NULL;
pj_status_t status;
@@ -1182,7 +1214,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
options |= PJSIP_INV_SUPPORT_TIMER;
if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY)
options |= PJSIP_INV_REQUIRE_100REL;
- if (pjsua_var.media_cfg.enable_ice)
+ if (pjsua_var.acc[acc_id].cfg.ice_cfg.enable_ice)
options |= PJSIP_INV_SUPPORT_ICE;
if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
options |= PJSIP_INV_REQUIRE_TIMER;
@@ -1488,6 +1520,7 @@ pj_status_t acquire_call(const char *title,
pj_bool_t has_pjsua_lock = PJ_FALSE;
pj_status_t status = PJ_SUCCESS;
pj_time_val time_start, timeout;
+ pjsip_dialog *dlg = NULL;
pj_gettimeofday(&time_start);
timeout.sec = 0;
@@ -1515,14 +1548,18 @@ pj_status_t acquire_call(const char *title,
has_pjsua_lock = PJ_TRUE;
call = &pjsua_var.calls[call_id];
+ if (call->inv)
+ dlg = call->inv->dlg;
+ else
+ dlg = call->async_call.dlg;
- if (call->inv == NULL) {
+ if (dlg == NULL) {
PJSUA_UNLOCK();
PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
return PJSIP_ESESSIONTERMINATED;
}
- status = pjsip_dlg_try_inc_lock(call->inv->dlg);
+ status = pjsip_dlg_try_inc_lock(dlg);
if (status != PJ_SUCCESS) {
PJSUA_UNLOCK();
pj_thread_sleep(retry/10);
@@ -1547,7 +1584,7 @@ pj_status_t acquire_call(const char *title,
}
*p_call = call;
- *p_dlg = call->inv->dlg;
+ *p_dlg = dlg;
return PJ_SUCCESS;
}
@@ -1992,7 +2029,7 @@ PJ_DEF(pj_status_t) pjsua_call_answer2(pjsua_call_id call_id,
* answer code 183 or 2xx is issued
*/
if (!call->med_ch_cb &&
- (call->opt_inited || (code==183 && code/100==2)) &&
+ (call->opt_inited || (code==183 || code/100==2)) &&
(!call->inv->neg ||
pjmedia_sdp_neg_get_state(call->inv->neg) ==
PJMEDIA_SDP_NEG_STATE_NULL))
@@ -2120,6 +2157,25 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
if (status != PJ_SUCCESS)
goto on_return;
+ /* If media transport creation is not yet completed, we will hangup
+ * the call in the media transport creation callback instead.
+ */
+ if (call->med_ch_cb && !call->inv) {
+ PJ_LOG(4,(THIS_FILE, "Pending call %d hangup upon completion "
+ "of media transport", call_id));
+ call->async_call.call_var.out_call.hangup = PJ_TRUE;
+ if (code == 0)
+ call->last_code = PJSIP_SC_REQUEST_TERMINATED;
+ else
+ call->last_code = (pjsip_status_code)code;
+ if (reason) {
+ pj_strncpy(&call->last_text, reason,
+ sizeof(call->last_text_buf_));
+ }
+
+ goto on_return;
+ }
+
if (code==0) {
if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
code = PJSIP_SC_OK;
@@ -2156,11 +2212,10 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
goto on_return;
}
- /* Stop lock codec timer, if it is active */
- if (call->lock_codec.reinv_timer.id) {
- pjsip_endpt_cancel_timer(pjsua_var.endpt,
- &call->lock_codec.reinv_timer);
- call->lock_codec.reinv_timer.id = PJ_FALSE;
+ /* Stop reinvite timer, if it is active */
+ if (call->reinv_timer.id) {
+ pjsua_cancel_timer(&call->reinv_timer);
+ call->reinv_timer.id = PJ_FALSE;
}
on_return:
@@ -2337,7 +2392,7 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id,
goto on_return;
}
- if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &
+ if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &&
pjsua_acc_is_valid(call->acc_id))
{
new_contact = &pjsua_var.acc[call->acc_id].contact;
@@ -2433,7 +2488,7 @@ PJ_DEF(pj_status_t) pjsua_call_update2(pjsua_call_id call_id,
goto on_return;
}
- if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &
+ if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &&
pjsua_acc_is_valid(call->acc_id))
{
new_contact = &pjsua_var.acc[call->acc_id].contact;
@@ -2850,12 +2905,8 @@ PJ_DEF(void) pjsua_call_hangup_all(void)
}
-/* Proto */
-static pj_status_t perform_lock_codec(pjsua_call *call);
-
-/* Timer callback to send re-INVITE or UPDATE to lock codec */
-static void reinv_timer_cb(pj_timer_heap_t *th,
- pj_timer_entry *entry)
+/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
+static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
{
pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
pjsip_dialog *dlg;
@@ -2864,21 +2915,27 @@ static void reinv_timer_cb(pj_timer_heap_t *th,
PJ_UNUSED_ARG(th);
- pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
+ pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE;
+
+ pj_log_push_indent();
status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
- if (status != PJ_SUCCESS)
+ if (status != PJ_SUCCESS) {
+ pj_log_pop_indent();
return;
+ }
- status = perform_lock_codec(call);
+ process_pending_reinvite(call);
pjsip_dlg_dec_lock(dlg);
+
+ pj_log_pop_indent();
}
/* Check if the specified format can be skipped in counting codecs */
static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
- const pj_str_t *fmt)
+ const pj_str_t *fmt)
{
const pj_str_t STR_TEL = {"telephone-event", 15};
unsigned pt;
@@ -2911,62 +2968,62 @@ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
}
-/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
- * out of several codecs presented by callee in his answer.
+/* Schedule check for the need of re-INVITE/UPDATE after media update, cases:
+ * - lock codec if remote answerer has given us more than one codecs
+ * - update ICE default transport address if it has changed after ICE
+ * connectivity check.
*/
-static pj_status_t perform_lock_codec(pjsua_call *call)
+void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms)
+{
+ pj_time_val delay;
+
+ /* Stop reinvite timer, if it is active */
+ if (call->reinv_timer.id)
+ pjsua_cancel_timer(&call->reinv_timer);
+
+ delay.sec = 0;
+ delay.msec = delay_ms;
+ pj_time_val_normalize(&delay);
+ call->reinv_timer.id = PJ_TRUE;
+ pjsua_schedule_timer(&call->reinv_timer, &delay);
+}
+
+
+/* Check if lock codec is needed */
+static pj_bool_t check_lock_codec(pjsua_call *call)
{
- const pj_str_t STR_UPDATE = {"UPDATE", 6};
- const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
+ const pjmedia_sdp_session *local_sdp, *remote_sdp;
+ pj_bool_t has_mult_fmt = PJ_FALSE;
unsigned i;
- pj_bool_t rem_can_update;
- pj_bool_t need_lock_codec = PJ_FALSE;
- pjsip_tx_data *tdata;
pj_status_t status;
- PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
- PJ_EINVALIDOP);
+ /* Check if lock codec is disabled */
+ if (!pjsua_var.acc[call->acc_id].cfg.lock_codec)
+ return PJ_FALSE;
- /* Verify if another SDP negotiation is in progress, e.g: session timer
- * or another re-INVITE.
- */
- if (call->inv==NULL || call->inv->neg==NULL ||
- pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
- {
- return PJMEDIA_SDPNEG_EINSTATE;
- }
+ /* Check lock codec retry count */
+ if (call->lock_codec.retry_cnt >= LOCK_CODEC_MAX_RETRY)
+ return PJ_FALSE;
- /* Don't do this if call is disconnecting! */
- if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
- call->inv->cause >= 200)
- {
- return PJ_EINVALIDOP;
- }
+ /* Check if we are the answerer, we shouldn't need to lock codec */
+ if (!call->inv->neg || !pjmedia_sdp_neg_was_answer_remote(call->inv->neg))
+ return PJ_FALSE;
- /* Verify if another SDP negotiation has been completed by comparing
- * the SDP version.
- */
+ /* Check if remote answerer has given us more than one codecs. */
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
if (status != PJ_SUCCESS)
- return status;
- if (local_sdp->origin.version > call->lock_codec.sdp_ver)
- return PJMEDIA_SDP_EINVER;
-
- PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
-
- /* Update the new offer so it contains only a codec. Note that formats
- * order in the offer should have been matched to the answer, so we can
- * just directly update the offer without looking-up the answer.
- */
- new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
+ return PJ_FALSE;
+ status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
+ if (status != PJ_SUCCESS)
+ return PJ_FALSE;
- for (i = 0; i < call->med_cnt; ++i) {
- unsigned j = 0, codec_cnt = 0;
- const pjmedia_sdp_media *ref_m;
- pjmedia_sdp_media *m;
+ for (i = 0; i < call->med_cnt && !has_mult_fmt; ++i) {
pjsua_call_media *call_med = &call->media[i];
+ const pjmedia_sdp_media *rem_m, *loc_m;
+ unsigned codec_cnt = 0;
+ unsigned j;
- /* Verify if media is deactivated */
+ /* Skip this if the media is inactive or error */
if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
call_med->state == PJSUA_CALL_MEDIA_ERROR ||
call_med->dir == PJMEDIA_DIR_NONE)
@@ -2974,192 +3031,336 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
continue;
}
- ref_m = local_sdp->media[i];
- m = new_sdp->media[i];
-
- /* Verify that media must be active. */
- pj_assert(ref_m->desc.port);
+ /* Remote may answer with less media lines. */
+ if (i >= remote_sdp->media_count)
+ continue;
- while (j < m->desc.fmt_count) {
- pjmedia_sdp_attr *a;
- pj_str_t *fmt = &m->desc.fmt[j];
+ rem_m = remote_sdp->media[i];
+ loc_m = local_sdp->media[i];
- if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
- ++j;
- continue;
- }
+ /* Verify that media must be active. */
+ pj_assert(loc_m->desc.port && rem_m->desc.port);
- /* Remove format */
- a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
- if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
- a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
- if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
- pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
- m->desc.fmt_count, j);
- --m->desc.fmt_count;
+ /* Count the formats in the answer. */
+ for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
+ if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]) && ++codec_cnt > 1)
+ has_mult_fmt = PJ_TRUE;
}
-
- need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
}
- /* Last check if SDP trully needs to be updated. It is possible that OA
- * negotiations have completed and SDP has changed but we didn't
- * increase the SDP version (should not happen!).
- */
- if (!need_lock_codec)
- return PJ_SUCCESS;
+ /* Reset retry count when remote answer has one codec */
+ if (!has_mult_fmt)
+ call->lock_codec.retry_cnt = 0;
- /* Send UPDATE or re-INVITE */
- rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
- PJSIP_H_ALLOW,
- NULL, &STR_UPDATE) ==
- PJSIP_DIALOG_CAP_SUPPORTED;
- if (rem_can_update) {
- status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
- } else {
- status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
- }
+ return has_mult_fmt;
+}
- if (status==PJ_EINVALIDOP &&
- ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
- {
- /* Ups, let's reschedule again */
- pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
- pj_time_val_normalize(&delay);
- call->lock_codec.reinv_timer.id = PJ_TRUE;
- pjsip_endpt_schedule_timer(pjsua_var.endpt,
- &call->lock_codec.reinv_timer, &delay);
- return status;
- } else if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
- status);
- return status;
- }
+/* Check if ICE setup is complete and if it needs to send reinvite */
+static pj_bool_t check_ice_complete(pjsua_call *call, pj_bool_t *need_reinv)
+{
+ pj_bool_t ice_need_reinv = PJ_FALSE;
+ pj_bool_t ice_complete = PJ_TRUE;
+ unsigned i;
- /* Send the UPDATE/re-INVITE request */
- status = pjsip_inv_send_msg(call->inv, tdata);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
- status);
- return status;
- }
+ /* Check if ICE setup is complete and if it needs reinvite */
+ for (i = 0; i < call->med_cnt; ++i) {
+ pjsua_call_media *call_med = &call->media[i];
+ pjmedia_transport_info tpinfo;
+ pjmedia_ice_transport_info *ice_info;
+
+ if (call_med->tp_st == PJSUA_MED_TP_NULL ||
+ call_med->tp_st == PJSUA_MED_TP_DISABLED ||
+ call_med->state == PJSUA_CALL_MEDIA_ERROR)
+ {
+ continue;
+ }
+
+ pjmedia_transport_info_init(&tpinfo);
+ pjmedia_transport_get_info(call_med->tp, &tpinfo);
+ ice_info = (pjmedia_ice_transport_info*)
+ pjmedia_transport_info_get_spc_info(
+ &tpinfo, PJMEDIA_TRANSPORT_TYPE_ICE);
+
+ /* Check if ICE is active */
+ if (!ice_info || !ice_info->active)
+ continue;
- return status;
+ /* Check if ICE setup not completed yet */
+ if (ice_info->sess_state < PJ_ICE_STRANS_STATE_RUNNING) {
+ ice_complete = PJ_FALSE;
+ break;
+ }
+
+ /* Check if ICE needs to send reinvite */
+ if (!ice_need_reinv &&
+ ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING &&
+ ice_info->role == PJ_ICE_SESS_ROLE_CONTROLLING)
+ {
+ pjsua_ice_config *cfg=&pjsua_var.acc[call->acc_id].cfg.ice_cfg;
+ if ((cfg->ice_always_update && !call->reinv_ice_sent) ||
+ pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
+ &call_med->rtp_addr))
+ {
+ ice_need_reinv = PJ_TRUE;
+ }
+ }
+ }
+
+ if (ice_complete && need_reinv)
+ *need_reinv = ice_need_reinv;
+
+ return ice_complete;
}
-/* Check if remote answerer has given us more than one codecs. If so,
- * create another offer with one codec only to lock down the codec.
- */
-static pj_status_t lock_codec(pjsua_call *call)
+/* Check and send reinvite for lock codec and ICE update */
+static pj_status_t process_pending_reinvite(pjsua_call *call)
{
+ const pj_str_t ST_UPDATE = {"UPDATE", 6};
+ pj_pool_t *pool = call->inv->pool_prov;
pjsip_inv_session *inv = call->inv;
- const pjmedia_sdp_session *local_sdp, *remote_sdp;
- pj_time_val delay = {0, 0};
- const pj_str_t st_update = {"UPDATE", 6};
+ pj_bool_t ice_need_reinv;
+ pj_bool_t ice_completed;
+ pj_bool_t need_lock_codec;
+ pj_bool_t rem_can_update;
+ pjmedia_sdp_session *new_offer;
+ pjsip_tx_data *tdata;
unsigned i;
- pj_bool_t has_mult_fmt = PJ_FALSE;
pj_status_t status;
- /* Stop lock codec timer, if it is active */
- if (call->lock_codec.reinv_timer.id) {
- pjsip_endpt_cancel_timer(pjsua_var.endpt,
- &call->lock_codec.reinv_timer);
- call->lock_codec.reinv_timer.id = PJ_FALSE;
+ /* Verify if another SDP negotiation is in progress, e.g: session timer
+ * or another re-INVITE.
+ */
+ if (inv==NULL || inv->neg==NULL ||
+ pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
+ {
+ return PJMEDIA_SDPNEG_EINSTATE;
}
- /* Skip this if we are the answerer */
- if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
- return PJ_SUCCESS;
+ /* Don't do this if call is disconnecting! */
+ if (inv->state > PJSIP_INV_STATE_CONFIRMED || inv->cause >= 200)
+ {
+ return PJ_EINVALIDOP;
}
/* Delay this when the SDP negotiation done in call state EARLY and
* remote does not support UPDATE method.
*/
if (inv->state == PJSIP_INV_STATE_EARLY &&
- pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
+ pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &ST_UPDATE)!=
PJSIP_DIALOG_CAP_SUPPORTED)
{
- call->lock_codec.pending = PJ_TRUE;
- return PJ_SUCCESS;
+ call->reinv_pending = PJ_TRUE;
+ return PJ_EPENDING;
}
- status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
- if (status != PJ_SUCCESS)
- return status;
- status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
- if (status != PJ_SUCCESS)
- return status;
+ /* Check if ICE setup is complete and if it needs reinvite */
+ ice_completed = check_ice_complete(call, &ice_need_reinv);
+ if (!ice_completed)
+ return PJ_EPENDING;
- /* Find multiple codecs answer in all media */
- for (i = 0; i < call->med_cnt; ++i) {
- pjsua_call_media *call_med = &call->media[i];
- const pjmedia_sdp_media *rem_m, *loc_m;
- unsigned codec_cnt = 0;
+ /* Check if we need to lock codec */
+ need_lock_codec = check_lock_codec(call);
- /* Skip this if the media is inactive or error */
- if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
- call_med->state == PJSUA_CALL_MEDIA_ERROR ||
- call_med->dir == PJMEDIA_DIR_NONE)
- {
- continue;
- }
+ /* Check if reinvite is really needed */
+ if (!need_lock_codec && !ice_need_reinv)
+ return PJ_SUCCESS;
- /* Remote may answer with less media lines. */
- if (i >= remote_sdp->media_count)
- continue;
+
+ /* Okay! So we need to send re-INVITE/UPDATE */
- rem_m = remote_sdp->media[i];
- loc_m = local_sdp->media[i];
+ /* Check if remote support UPDATE */
+ rem_can_update = pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL,
+ &ST_UPDATE) ==
+ PJSIP_DIALOG_CAP_SUPPORTED;
- /* Verify that media must be active. */
- pj_assert(loc_m->desc.port && rem_m->desc.port);
+ /* Logging stuff */
+ {
+ const char *ST_ICE_UPDATE = "ICE transport address after "
+ "ICE negotiation";
+ const char *ST_LOCK_CODEC = "media session to use only one codec";
+ PJ_LOG(4,(THIS_FILE, "Call %d sending %s for updating %s%s%s",
+ call->index,
+ (rem_can_update? "UPDATE" : "re-INVITE"),
+ (ice_need_reinv? ST_ICE_UPDATE : ST_LOCK_CODEC),
+ (ice_need_reinv && need_lock_codec? " and " : ""),
+ (ice_need_reinv && need_lock_codec? ST_LOCK_CODEC : "")
+ ));
+ }
+
+ /* Generate SDP re-offer */
+ status = pjsua_media_channel_create_sdp(call->index, pool, NULL,
+ &new_offer, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+ return status;
+ }
- /* Count the formats in the answer. */
- if (rem_m->desc.fmt_count==1) {
- codec_cnt = 1;
- } else {
- unsigned j;
- for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
- if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
- ++codec_cnt;
- }
+ /* Update the new offer so it contains only a codec. Note that
+ * SDP nego has removed unmatched codecs from the offer and the codec
+ * order in the offer has been matched to the answer, so we'll override
+ * the codecs in the just generated SDP with the ones from the active
+ * local SDP and leave just one codec for the next SDP re-offer.
+ */
+ if (need_lock_codec) {
+ const pjmedia_sdp_session *ref_sdp;
+
+ /* Get local active SDP as reference */
+ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &ref_sdp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Verify media count. Note that remote may add/remove media line
+ * in the answer. When answer has less media, it must have been
+ * handled by pjsua_media_channel_update() as disabled media.
+ * When answer has more media, it must have been ignored (treated
+ * as non-exist) anywhere. Local media count should not be updated
+ * at this point, as modifying media count operation (i.e: reinvite,
+ * update, vid_set_strm) is currently blocking, protected with
+ * dialog mutex, and eventually reset SDP nego state to LOCAL OFFER.
+ */
+ if (call->med_cnt != ref_sdp->media_count ||
+ ref_sdp->media_count != new_offer->media_count)
+ {
+ /* Anyway, just in case, let's just return error */
+ return PJMEDIA_SDPNEG_EINSTATE;
}
- if (codec_cnt > 1) {
- has_mult_fmt = PJ_TRUE;
- break;
+ for (i = 0; i < call->med_cnt; ++i) {
+ unsigned j, codec_cnt = 0;
+ const pjmedia_sdp_media *ref_m = ref_sdp->media[i];
+ pjmedia_sdp_media *m = new_offer->media[i];
+ pjsua_call_media *call_med = &call->media[i];
+
+ /* Verify if media is deactivated */
+ if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
+ call_med->state == PJSUA_CALL_MEDIA_ERROR ||
+ call_med->dir == PJMEDIA_DIR_NONE)
+ {
+ continue;
+ }
+
+ /* Reset formats */
+ m->desc.fmt_count = 0;
+ pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtpmap");
+ pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "fmtp");
+
+ /* Copy only the first format + any non-AV formats from
+ * the active local SDP.
+ */
+ for (j = 0; j < ref_m->desc.fmt_count; ++j) {
+ const pj_str_t *fmt = &ref_m->desc.fmt[j];
+
+ if (is_non_av_fmt(ref_m, fmt) || (++codec_cnt == 1)) {
+ pjmedia_sdp_attr *a;
+
+ m->desc.fmt[m->desc.fmt_count++] = *fmt;
+ a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr,
+ "rtpmap", fmt);
+ if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
+ a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr,
+ "fmtp", fmt);
+ if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
+ }
+ }
}
}
- /* Each media in the answer already contains single codec. */
- if (!has_mult_fmt) {
- call->lock_codec.retry_cnt = 0;
- return PJ_SUCCESS;
- }
+ /* Put back original direction and "c=0.0.0.0" line */
+ {
+ const pjmedia_sdp_session *cur_sdp;
+
+ /* Get local active SDP */
+ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &cur_sdp);
+ if (status != PJ_SUCCESS)
+ return status;
- /* Remote keeps answering with multiple codecs, let's just give up
- * locking codec to avoid infinite retry loop.
- */
- if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
- return PJ_SUCCESS;
+ /* Make sure media count has not been changed */
+ if (call->med_cnt != cur_sdp->media_count)
+ return PJMEDIA_SDPNEG_EINSTATE;
+
+ for (i = 0; i < call->med_cnt; ++i) {
+ const pjmedia_sdp_media *m = cur_sdp->media[i];
+ pjmedia_sdp_media *new_m = new_offer->media[i];
+ pjsua_call_media *call_med = &call->media[i];
+ pjmedia_sdp_attr *a = NULL;
+
+ /* Update direction to the current dir */
+ pjmedia_sdp_media_remove_all_attr(new_m, "sendrecv");
+ pjmedia_sdp_media_remove_all_attr(new_m, "sendonly");
+ pjmedia_sdp_media_remove_all_attr(new_m, "recvonly");
+ pjmedia_sdp_media_remove_all_attr(new_m, "inactive");
+
+ if (call_med->dir == PJMEDIA_DIR_ENCODING_DECODING) {
+ a = pjmedia_sdp_attr_create(pool, "sendrecv", NULL);
+ } else if (call_med->dir == PJMEDIA_DIR_ENCODING) {
+ a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
+ } else if (call_med->dir == PJMEDIA_DIR_DECODING) {
+ a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
+ } else {
+ const pjmedia_sdp_conn *conn;
+ a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
+
+ /* Also check if the original c= line address is zero */
+ conn = m->conn;
+ if (!conn)
+ conn = cur_sdp->conn;
+ if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
+ pj_strcmp2(&conn->addr, "0")==0)
+ {
+ if (!new_m->conn) {
+ new_m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ }
+
+ if (pj_strcmp2(&new_m->conn->addr, "0.0.0.0")) {
+ new_m->conn->net_type = pj_str("IN");
+ new_m->conn->addr_type = pj_str("IP4");
+ new_m->conn->addr = pj_str("0.0.0.0");
+ }
+ }
+ }
- PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
- "updating media session to use only one codec.."));
+ pj_assert(a);
+ pjmedia_sdp_media_add_attr(new_m, a);
+ }
+ }
- call->lock_codec.sdp_ver = local_sdp->origin.version;
+
+ if (rem_can_update) {
+ status = pjsip_inv_update(inv, NULL, new_offer, &tdata);
+ } else {
+ status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata);
+ }
- /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
- * See: https://trac.pjsip.org/repos/ticket/1149
- */
- pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
- (void*)(pj_size_t)call->index,
- &reinv_timer_cb);
- pjsip_endpt_schedule_timer(pjsua_var.endpt,
- &call->lock_codec.reinv_timer, &delay);
+ if (status==PJ_EINVALIDOP &&
+ ++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY)
+ {
+ /* Ups, let's reschedule again */
+ pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL);
+ return PJ_SUCCESS;
+ } else if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE",
+ status);
+ return status;
+ }
+ /* Send the UPDATE/re-INVITE request */
+ status = pjsip_inv_send_msg(inv, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE",
+ status);
+ return status;
+ }
+
+ /* Update flags */
+ if (ice_need_reinv)
+ call->reinv_ice_sent = PJ_TRUE;
+ if (need_lock_codec)
+ ++call->lock_codec.retry_cnt;
+
return PJ_SUCCESS;
}
+
/*
* This callback receives notification from invite session when the
* session state has changed.
@@ -3194,16 +3395,12 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
case PJSIP_INV_STATE_CONFIRMED:
pj_gettimeofday(&call->conn_time);
- /* See if lock codec was pended as media update was done in the
+ /* See if auto reinvite was pended as media update was done in the
* EARLY state and remote does not support UPDATE.
*/
- if (call->lock_codec.pending) {
- pj_status_t status;
- status = lock_codec(call);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to lock codec", status);
- }
- call->lock_codec.pending = PJ_FALSE;
+ if (call->reinv_pending) {
+ call->reinv_pending = PJ_FALSE;
+ pjsua_call_schedule_reinvite_check(call, 0);
}
break;
case PJSIP_INV_STATE_DISCONNECTED:
@@ -3225,11 +3422,10 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
sizeof(call->last_text_buf_));
}
- /* Stop lock codec timer, if it is active */
- if (call->lock_codec.reinv_timer.id) {
- pjsip_endpt_cancel_timer(pjsua_var.endpt,
- &call->lock_codec.reinv_timer);
- call->lock_codec.reinv_timer.id = PJ_FALSE;
+ /* Stop reinvite timer, if it is active */
+ if (call->reinv_timer.id) {
+ pjsua_cancel_timer(&call->reinv_timer);
+ call->reinv_timer.id = PJ_FALSE;
}
break;
default:
@@ -3307,6 +3503,15 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
}
}
+ /* Ticket #1627: Invoke on_call_tsx_state() when call is disconnected. */
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED &&
+ e->type == PJSIP_EVENT_TSX_STATE &&
+ call->inv &&
+ pjsua_var.ua_cfg.cb.on_call_tsx_state)
+ {
+ (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index,
+ e->body.tsx_state.tsx, e);
+ }
if (pjsua_var.ua_cfg.cb.on_call_state)
(*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
@@ -3512,10 +3717,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
}
/* Ticket #476: make sure only one codec is specified in the answer. */
- status = lock_codec(call);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to lock codec", status);
- }
+ pjsua_call_schedule_reinvite_check(call, 0);
/* Call application callback, if any */
if (pjsua_var.ua_cfg.cb.on_call_media_state)
@@ -4221,11 +4423,7 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
goto on_return;
if (call->inv == NULL) {
- /* Shouldn't happen. It happens only when we don't terminate the
- * server subscription caused by REFER after the call has been
- * transfered (and this call has been disconnected), and we
- * receive another REFER for this call.
- */
+ /* Call has been disconnected. */
goto on_return;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index f4f9508..e352238 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_core.c 4173 2012-06-20 10:39:05Z ming $ */
+/* $Id: pjsua_core.c 4370 2013-02-26 05:30:00Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -196,12 +196,65 @@ PJ_DEF(void) pjsua_transport_config_dup(pj_pool_t *pool,
pjsua_transport_config *dst,
const pjsua_transport_config *src)
{
+ pj_memcpy(dst, src, sizeof(*src));
+ pj_strdup(pool, &dst->public_addr, &src->public_addr);
+ pj_strdup(pool, &dst->bound_addr, &src->bound_addr);
+}
+
+PJ_DEF(void) pjsua_ice_config_from_media_config( pj_pool_t *pool,
+ pjsua_ice_config *dst,
+ const pjsua_media_config *src)
+{
+ PJ_UNUSED_ARG(pool);
+
+ dst->enable_ice = src->enable_ice;
+ dst->ice_max_host_cands = src->ice_max_host_cands;
+ dst->ice_opt = src->ice_opt;
+ dst->ice_no_rtcp = src->ice_no_rtcp;
+ dst->ice_always_update = src->ice_always_update;
+}
+
+PJ_DEF(void) pjsua_ice_config_dup( pj_pool_t *pool,
+ pjsua_ice_config *dst,
+ const pjsua_ice_config *src)
+{
PJ_UNUSED_ARG(pool);
pj_memcpy(dst, src, sizeof(*src));
}
+PJ_DEF(void) pjsua_turn_config_from_media_config(pj_pool_t *pool,
+ pjsua_turn_config *dst,
+ const pjsua_media_config *src)
+{
+ dst->enable_turn = src->enable_turn;
+ dst->turn_conn_type = src->turn_conn_type;
+ if (pool == NULL) {
+ dst->turn_server = src->turn_server;
+ dst->turn_auth_cred = src->turn_auth_cred;
+ } else {
+ if (pj_stricmp(&dst->turn_server, &src->turn_server))
+ pj_strdup(pool, &dst->turn_server, &src->turn_server);
+ pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred,
+ &src->turn_auth_cred);
+ }
+}
+
+PJ_DEF(void) pjsua_turn_config_dup(pj_pool_t *pool,
+ pjsua_turn_config *dst,
+ const pjsua_turn_config *src)
+{
+ pj_memcpy(dst, src, sizeof(*src));
+ if (pool) {
+ pj_strdup(pool, &dst->turn_server, &src->turn_server);
+ pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred,
+ &src->turn_auth_cred);
+ }
+}
+
PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
{
+ pjsua_media_config med_cfg;
+
pj_bzero(cfg, sizeof(*cfg));
cfg->reg_timeout = PJSUA_REG_INTERVAL;
@@ -215,6 +268,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
cfg->require_100rel = pjsua_var.ua_cfg.require_100rel;
cfg->use_timer = pjsua_var.ua_cfg.use_timer;
cfg->timer_setting = pjsua_var.ua_cfg.timer_setting;
+ cfg->lock_codec = 1;
cfg->ka_interval = 15;
cfg->ka_data = pj_str("\r\n");
cfg->vid_cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
@@ -223,6 +277,11 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
pjmedia_vid_stream_rc_config_default(&cfg->vid_stream_rc_cfg);
#endif
pjsua_transport_config_default(&cfg->rtp_cfg);
+
+ pjsua_media_config_default(&med_cfg);
+ pjsua_ice_config_from_media_config(NULL, &cfg->ice_cfg, &med_cfg);
+ pjsua_turn_config_from_media_config(NULL, &cfg->turn_cfg, &med_cfg);
+
cfg->use_srtp = pjsua_var.ua_cfg.use_srtp;
cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling;
cfg->srtp_optional_dup_offer = pjsua_var.ua_cfg.srtp_optional_dup_offer;
@@ -266,6 +325,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
cfg->snd_auto_close_time = 1;
cfg->ice_max_host_cands = -1;
+ cfg->ice_always_update = PJ_TRUE;
pj_ice_sess_options_default(&cfg->ice_opt);
cfg->turn_conn_type = PJ_TURN_TP_UDP;
@@ -898,7 +958,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
if (pjsua_var.ua_cfg.force_lr) {
pjsip_sip_uri *sip_url;
if (!PJSIP_URI_SCHEME_IS_SIP(r->name_addr.uri) &&
- !PJSIP_URI_SCHEME_IS_SIP(r->name_addr.uri))
+ !PJSIP_URI_SCHEME_IS_SIPS(r->name_addr.uri))
{
status = PJSIP_EINVALIDSCHEME;
goto on_error;
@@ -1017,7 +1077,7 @@ static void busy_sleep(unsigned msec)
{
pj_time_val timeout, now;
- pj_gettimeofday(&timeout);
+ pj_gettickcount(&timeout);
timeout.msec += msec;
pj_time_val_normalize(&timeout);
@@ -1026,15 +1086,21 @@ static void busy_sleep(unsigned msec)
i = msec / 10;
while (pjsua_handle_events(10) > 0 && i > 0)
--i;
- pj_gettimeofday(&now);
+ pj_gettickcount(&now);
} while (PJ_TIME_VAL_LT(now, timeout));
}
-/* Internal function to destroy STUN resolution session
- * (pj_stun_resolve).
- */
+static void stun_resolve_add_ref(pjsua_stun_resolve *sess)
+{
+ ++sess->ref_cnt;
+}
+
static void destroy_stun_resolve(pjsua_stun_resolve *sess)
{
+ sess->destroy_flag = PJ_TRUE;
+ if (sess->ref_cnt > 0)
+ return;
+
PJSUA_LOCK();
pj_list_erase(sess);
PJSUA_UNLOCK();
@@ -1043,6 +1109,14 @@ static void destroy_stun_resolve(pjsua_stun_resolve *sess)
pj_pool_release(sess->pool);
}
+static void stun_resolve_dec_ref(pjsua_stun_resolve *sess)
+{
+ --sess->ref_cnt;
+ if (sess->ref_cnt <= 0 && sess->destroy_flag)
+ destroy_stun_resolve(sess);
+}
+
+
/* This is the internal function to be called when STUN resolution
* session (pj_stun_resolve) has completed.
*/
@@ -1050,11 +1124,15 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess)
{
pj_stun_resolve_result result;
+ if (sess->has_result)
+ goto on_return;
+
pj_bzero(&result, sizeof(result));
result.token = sess->token;
result.status = sess->status;
result.name = sess->srv[sess->idx];
pj_memcpy(&result.addr, &sess->addr, sizeof(result.addr));
+ sess->has_result = PJ_TRUE;
if (result.status == PJ_SUCCESS) {
char addr[PJ_INET6_ADDRSTRLEN+10];
@@ -1070,8 +1148,11 @@ static void stun_resolve_complete(pjsua_stun_resolve *sess)
PJ_LOG(1,(THIS_FILE, "STUN resolution failed: %s", errmsg));
}
+ stun_resolve_add_ref(sess);
sess->cb(&result);
+ stun_resolve_dec_ref(sess);
+on_return:
if (!sess->blocking) {
destroy_stun_resolve(sess);
}
@@ -1133,22 +1214,27 @@ static pj_bool_t test_stun_on_status(pj_stun_sock *stun_sock,
*/
static void resolve_stun_entry(pjsua_stun_resolve *sess)
{
+ stun_resolve_add_ref(sess);
+
/* Loop while we have entry to try */
for (; sess->idx < sess->count; ++sess->idx) {
const int af = pj_AF_INET();
+ char target[64];
pj_str_t hostpart;
pj_uint16_t port;
pj_stun_sock_cb stun_sock_cb;
pj_assert(sess->idx < sess->count);
+ pj_ansi_snprintf(target, sizeof(target), "%.*s",
+ (int)sess->srv[sess->idx].slen,
+ sess->srv[sess->idx].ptr);
+
/* Parse the server entry into host:port */
sess->status = pj_sockaddr_parse2(af, 0, &sess->srv[sess->idx],
&hostpart, &port, NULL);
if (sess->status != PJ_SUCCESS) {
- PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %.*s",
- (int)sess->srv[sess->idx].slen,
- sess->srv[sess->idx].ptr));
+ PJ_LOG(2,(THIS_FILE, "Invalid STUN server entry %s", target));
continue;
}
@@ -1158,10 +1244,8 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
pj_assert(sess->stun_sock == NULL);
- PJ_LOG(4,(THIS_FILE, "Trying STUN server %.*s (%d of %d)..",
- (int)sess->srv[sess->idx].slen,
- sess->srv[sess->idx].ptr,
- sess->idx+1, sess->count));
+ PJ_LOG(4,(THIS_FILE, "Trying STUN server %s (%d of %d)..",
+ target, sess->idx+1, sess->count));
/* Use STUN_sock to test this entry */
pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb));
@@ -1173,9 +1257,8 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(sess->status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE,
- "Error creating STUN socket for %.*s: %s",
- (int)sess->srv[sess->idx].slen,
- sess->srv[sess->idx].ptr, errmsg));
+ "Error creating STUN socket for %s: %s",
+ target, errmsg));
continue;
}
@@ -1186,19 +1269,20 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(sess->status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE,
- "Error starting STUN socket for %.*s: %s",
- (int)sess->srv[sess->idx].slen,
- sess->srv[sess->idx].ptr, errmsg));
+ "Error starting STUN socket for %s: %s",
+ target, errmsg));
- pj_stun_sock_destroy(sess->stun_sock);
- sess->stun_sock = NULL;
+ if (sess->stun_sock) {
+ pj_stun_sock_destroy(sess->stun_sock);
+ sess->stun_sock = NULL;
+ }
continue;
}
/* Done for now, testing will resume/complete asynchronously in
* stun_sock_cb()
*/
- return;
+ goto on_return;
}
if (sess->idx >= sess->count) {
@@ -1207,6 +1291,9 @@ static void resolve_stun_entry(pjsua_stun_resolve *sess)
sess->status = PJ_EUNKNOWN);
stun_resolve_complete(sess);
}
+
+on_return:
+ stun_resolve_dec_ref(sess);
}
@@ -1837,6 +1924,8 @@ static pj_status_t create_sip_udp_sock(int af,
pj_sockaddr_set_port(p_pub_addr, (pj_uint16_t)port);
} else if (stun_srv.slen) {
+ pjstun_setting stun_opt;
+
/*
* STUN is specified, resolve the address with STUN.
*/
@@ -1846,10 +1935,13 @@ static pj_status_t create_sip_udp_sock(int af,
return PJ_EAFNOTSUP;
}
- status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
- &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
- &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
- &p_pub_addr->ipv4);
+ pj_bzero(&stun_opt, sizeof(stun_opt));
+ stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+ stun_opt.srv1 = stun_opt.srv2 = stun_srv;
+ stun_opt.port1 = stun_opt.port2 =
+ pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+ status = pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
+ 1, &sock, &p_pub_addr->ipv4);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error contacting STUN server", status);
pj_sock_close(sock);
@@ -1977,8 +2069,10 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
pjsua_transport_config config;
pjsip_tpfactory *tcp;
pjsip_tcp_transport_cfg tcp_cfg;
+ int af;
- pjsip_tcp_transport_cfg_default(&tcp_cfg, pj_AF_INET());
+ af = (type==PJSIP_TRANSPORT_TCP6) ? pj_AF_INET6() : pj_AF_INET();
+ pjsip_tcp_transport_cfg_default(&tcp_cfg, af);
/* Supply default config if it's not specified */
if (cfg == NULL) {
@@ -2028,14 +2122,15 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
#endif /* PJ_HAS_TCP */
#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
- } else if (type == PJSIP_TRANSPORT_TLS) {
+ } else if (type == PJSIP_TRANSPORT_TLS || type == PJSIP_TRANSPORT_TLS6) {
/*
* Create TLS transport.
*/
pjsua_transport_config config;
pjsip_host_port a_name;
pjsip_tpfactory *tls;
- pj_sockaddr_in local_addr;
+ pj_sockaddr local_addr;
+ int af;
/* Supply default config if it's not specified */
if (cfg == NULL) {
@@ -2045,13 +2140,15 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
}
/* Init local address */
- pj_sockaddr_in_init(&local_addr, 0, 0);
+ af = (type==PJSIP_TRANSPORT_TLS) ? pj_AF_INET() : pj_AF_INET6();
+ pj_sockaddr_init(af, &local_addr, NULL, 0);
if (cfg->port)
- local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port);
+ pj_sockaddr_set_port(&local_addr, (pj_uint16_t)cfg->port);
if (cfg->bound_addr.slen) {
- status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr);
+ status = pj_sockaddr_set_str_addr(af, &local_addr,
+ &cfg->bound_addr);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to resolve transport bound address",
@@ -2065,9 +2162,9 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
if (cfg->public_addr.slen)
a_name.host = cfg->public_addr;
- status = pjsip_tls_transport_start(pjsua_var.endpt,
- &cfg->tls_setting,
- &local_addr, &a_name, 1, &tls);
+ status = pjsip_tls_transport_start2(pjsua_var.endpt,
+ &cfg->tls_setting,
+ &local_addr, &a_name, 1, &tls);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating SIP TLS listener",
status);
@@ -2087,7 +2184,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
}
/* Set transport state callback */
- if (pjsua_var.ua_cfg.cb.on_transport_state) {
+ {
pjsip_tp_state_callback tpcb;
pjsip_tpmgr *tpmgr;
@@ -2743,7 +2840,7 @@ pj_status_t normalize_route_uri(pj_pool_t *pool, pj_str_t *uri)
}
if (!PJSIP_URI_SCHEME_IS_SIP(uri_obj) &&
- !PJSIP_URI_SCHEME_IS_SIP(uri_obj))
+ !PJSIP_URI_SCHEME_IS_SIPS(uri_obj))
{
PJ_LOG(1,(THIS_FILE, "Route URI must be SIP URI: %.*s",
(int)uri->slen, uri->ptr));
@@ -2804,6 +2901,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail)
PJ_LOG(3,(THIS_FILE, "Dumping media transports:"));
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
pjsua_call *call = &pjsua_var.calls[i];
+ pjsua_acc_config *acc_cfg;
pjmedia_transport *tp[PJSUA_MAX_CALL_MEDIA*2];
unsigned tp_cnt = 0;
unsigned j;
@@ -2829,6 +2927,8 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail)
}
}
+ acc_cfg = &pjsua_var.acc[call->acc_id].cfg;
+
/* Dump the media transports in this call */
for (j = 0; j < tp_cnt; ++j) {
pjmedia_transport_info tpinfo;
@@ -2837,7 +2937,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail)
pjmedia_transport_info_init(&tpinfo);
pjmedia_transport_get_info(tp[j], &tpinfo);
PJ_LOG(3,(THIS_FILE, " %s: %s",
- (pjsua_var.media_cfg.enable_ice ? "ICE" : "UDP"),
+ (acc_cfg->ice_cfg.enable_ice ? "ICE" : "UDP"),
pj_sockaddr_print(&tpinfo.sock_info.rtp_addr_name,
addr_buf,
sizeof(addr_buf), 3)));
diff --git a/pjsip/src/pjsua-lib/pjsua_dump.c b/pjsip/src/pjsua-lib/pjsua_dump.c
index 0b23a97..a24b71a 100644
--- a/pjsip/src/pjsua-lib/pjsua_dump.c
+++ b/pjsip/src/pjsua-lib/pjsua_dump.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_dump.c 4085 2012-04-25 07:45:22Z nanang $ */
+/* $Id: pjsua_dump.c 4300 2012-11-26 02:04:17Z ming $ */
/*
* Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -212,6 +212,10 @@ static unsigned dump_media_stat(const char *indent,
/* Dump media session */
+#if PJSUA_MEDIA_HAS_PJMEDIA || \
+ (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && \
+ PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT)
+
static void dump_media_session(const char *indent,
char *buf, unsigned maxlen,
pjsua_call *call)
@@ -858,6 +862,24 @@ static void dump_media_session(const char *indent,
}
}
+#else /* PJSUA_MEDIA_HAS_PJMEDIA ||
+ (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO &&
+ PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */
+
+static void dump_media_session(const char *indent,
+ char *buf, unsigned maxlen,
+ pjsua_call *call)
+{
+ PJ_UNUSED_ARG(indent);
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(maxlen);
+ PJ_UNUSED_ARG(call);
+}
+
+#endif /* PJSUA_MEDIA_HAS_PJMEDIA ||
+ (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO &&
+ PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */
+
/* Print call info */
void print_call(const char *title,
@@ -866,11 +888,12 @@ void print_call(const char *title,
{
int len;
pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
- pjsip_dialog *dlg = inv->dlg;
+ pjsip_dialog *dlg;
char userinfo[128];
/* Dump invite sesion info. */
+ dlg = (inv? inv->dlg: pjsua_var.calls[call_id].async_call.dlg);
len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
if (len < 0)
pj_ansi_strcpy(userinfo, "<--uri too long-->");
@@ -879,7 +902,8 @@ void print_call(const char *title,
len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
title,
- pjsip_inv_state_name(inv->state),
+ pjsip_inv_state_name(inv? inv->state:
+ PJSIP_INV_STATE_DISCONNECTED),
userinfo);
if (len < 1 || len >= (int)size) {
pj_ansi_strcpy(buf, "<--uri too long-->");
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 8bcb0da..27069a1 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_media.c 4182 2012-06-27 07:12:23Z ming $ */
+/* $Id: pjsua_media.c 4412 2013-03-05 03:12:32Z riza $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -165,9 +165,11 @@ pj_status_t pjsua_media_subsys_start(void)
#endif
/* Perform NAT detection */
- status = pjsua_detect_nat_type();
- if (status != PJ_SUCCESS) {
- PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
+ if (pjsua_var.ua_cfg.stun_srv_cnt) {
+ status = pjsua_detect_nat_type();
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
+ }
}
pj_log_pop_indent();
@@ -226,24 +228,33 @@ pj_status_t pjsua_media_subsys_destroy(unsigned flags)
* Create RTP and RTCP socket pair, and possibly resolve their public
* address via STUN.
*/
-static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
+static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
+ const pjsua_transport_config *cfg,
pjmedia_sock_info *skinfo)
{
enum {
RTP_RETRY = 100
};
int i;
- pj_sockaddr_in bound_addr;
- pj_sockaddr_in mapped_addr[2];
+ pj_bool_t use_ipv6;
+ int af;
+ pj_sockaddr bound_addr;
+ pj_sockaddr mapped_addr[2];
pj_status_t status = PJ_SUCCESS;
- char addr_buf[PJ_INET6_ADDRSTRLEN+2];
+ char addr_buf[PJ_INET6_ADDRSTRLEN+10];
pj_sock_t sock[2];
+ use_ipv6 = (pjsua_var.acc[call_med->call->acc_id].cfg.ipv6_media_use !=
+ PJSUA_IPV6_DISABLED);
+ af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
+
/* Make sure STUN server resolution has completed */
- status = resolve_stun_server(PJ_TRUE);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
- return status;
+ if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id)) {
+ status = resolve_stun_server(PJ_TRUE);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+ return status;
+ }
}
if (next_rtp_port == 0)
@@ -255,9 +266,9 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
for (i=0; i<2; ++i)
sock[i] = PJ_INVALID_SOCKET;
- bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
+ pj_sockaddr_init(af, &bound_addr, NULL, 0);
if (cfg->bound_addr.slen) {
- status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
+ status = pj_sockaddr_set_str_addr(af, &bound_addr, &cfg->bound_addr);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
status);
@@ -269,7 +280,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
for (i=0; i<RTP_RETRY; ++i, next_rtp_port += 2) {
/* Create RTP socket. */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[0]);
+ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[0]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "socket() error", status);
return status;
@@ -281,8 +292,9 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
2, THIS_FILE, "RTP socket");
/* Bind RTP socket */
- status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr),
- next_rtp_port);
+ pj_sockaddr_set_port(&bound_addr, next_rtp_port);
+ status=pj_sock_bind(sock[0], &bound_addr,
+ pj_sockaddr_get_len(&bound_addr));
if (status != PJ_SUCCESS) {
pj_sock_close(sock[0]);
sock[0] = PJ_INVALID_SOCKET;
@@ -290,7 +302,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
}
/* Create RTCP socket. */
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[1]);
+ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[1]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "socket() error", status);
pj_sock_close(sock[0]);
@@ -303,8 +315,9 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
2, THIS_FILE, "RTCP socket");
/* Bind RTCP socket */
- status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr),
- (pj_uint16_t)(next_rtp_port+1));
+ pj_sockaddr_set_port(&bound_addr, (pj_uint16_t)(next_rtp_port+1));
+ status=pj_sock_bind(sock[1], &bound_addr,
+ pj_sockaddr_get_len(&bound_addr));
if (status != PJ_SUCCESS) {
pj_sock_close(sock[0]);
sock[0] = PJ_INVALID_SOCKET;
@@ -318,26 +331,55 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
* If we're configured to use STUN, then find out the mapped address,
* and make sure that the mapped RTCP port is adjacent with the RTP.
*/
- if (pjsua_var.stun_srv.addr.sa_family != 0) {
+ if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id) &&
+ pjsua_var.stun_srv.addr.sa_family != 0)
+ {
char ip_addr[32];
pj_str_t stun_srv;
+ pj_sockaddr_in resolved_addr[2];
+ pjstun_setting stun_opt;
pj_ansi_strcpy(ip_addr,
pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
stun_srv = pj_str(ip_addr);
- status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock,
- &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
- &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
- mapped_addr);
+ pj_bzero(&stun_opt, sizeof(stun_opt));
+ stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
+ stun_opt.srv1 = stun_opt.srv2 = stun_srv;
+ stun_opt.port1 = stun_opt.port2 =
+ pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
+ status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
+ 2, sock, resolved_addr);
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ /* Handle EPIPE (Broken Pipe) error, which happens on UDP socket
+ * after app wakes up from suspended state. In this case, simply
+ * just retry.
+ * P.S.: The magic status is PJ_STATUS_FROM_OS(EPIPE)
+ */
+ if (status == 120032) {
+ PJ_LOG(4,(THIS_FILE, "Got EPIPE error, retrying.."));
+ pj_sock_close(sock[0]);
+ sock[0] = PJ_INVALID_SOCKET;
+
+ pj_sock_close(sock[1]);
+ sock[1] = PJ_INVALID_SOCKET;
+
+ continue;
+ }
+ else
+#endif
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STUN resolve error", status);
goto on_error;
}
+ pj_sockaddr_cp(&mapped_addr[0], &resolved_addr[0]);
+ pj_sockaddr_cp(&mapped_addr[1], &resolved_addr[1]);
+
#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
- if (pj_ntohs(mapped_addr[1].sin_port) ==
- pj_ntohs(mapped_addr[0].sin_port)+1)
+ if (pj_sockaddr_get_port(&mapped_addr[1]) ==
+ pj_sockaddr_get_port(&mapped_addr[0])+1)
{
/* Success! */
break;
@@ -349,14 +391,14 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
pj_sock_close(sock[1]);
sock[1] = PJ_INVALID_SOCKET;
#else
- if (pj_ntohs(mapped_addr[1].sin_port) !=
- pj_ntohs(mapped_addr[0].sin_port)+1)
+ if (pj_sockaddr_get_port(&mapped_addr[1]) !=
+ pj_sockaddr_get_port(&mapped_addr[0])+1)
{
PJ_LOG(4,(THIS_FILE,
"Note: STUN mapped RTCP port %d is not adjacent"
" to RTP port %d",
- pj_ntohs(mapped_addr[1].sin_port),
- pj_ntohs(mapped_addr[0].sin_port)));
+ pj_sockaddr_get_port(&mapped_addr[1]),
+ pj_sockaddr_get_port(&mapped_addr[0])));
}
/* Success! */
break;
@@ -364,13 +406,13 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
} else if (cfg->public_addr.slen) {
- status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
- (pj_uint16_t)next_rtp_port);
+ status = pj_sockaddr_init(af, &mapped_addr[0], &cfg->public_addr,
+ (pj_uint16_t)next_rtp_port);
if (status != PJ_SUCCESS)
goto on_error;
- status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
- (pj_uint16_t)(next_rtp_port+1));
+ status = pj_sockaddr_init(af, &mapped_addr[1], &cfg->public_addr,
+ (pj_uint16_t)(next_rtp_port+1));
if (status != PJ_SUCCESS)
goto on_error;
@@ -378,24 +420,24 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
} else {
- if (bound_addr.sin_addr.s_addr == 0) {
+ if (!pj_sockaddr_has_addr(&bound_addr)) {
pj_sockaddr addr;
/* Get local IP address. */
- status = pj_gethostip(pj_AF_INET(), &addr);
+ status = pj_gethostip(af, &addr);
if (status != PJ_SUCCESS)
goto on_error;
- bound_addr.sin_addr.s_addr = addr.ipv4.sin_addr.s_addr;
+ pj_sockaddr_copy_addr(&bound_addr, &addr);
}
for (i=0; i<2; ++i) {
- pj_sockaddr_in_init(&mapped_addr[i], NULL, 0);
- mapped_addr[i].sin_addr.s_addr = bound_addr.sin_addr.s_addr;
+ pj_sockaddr_init(af, &mapped_addr[i], NULL, 0);
+ pj_sockaddr_copy_addr(&mapped_addr[i], &bound_addr);
+ pj_sockaddr_set_port(&mapped_addr[i],
+ (pj_uint16_t)(next_rtp_port+i));
}
- mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port);
- mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1));
break;
}
}
@@ -408,12 +450,10 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
skinfo->rtp_sock = sock[0];
- pj_memcpy(&skinfo->rtp_addr_name,
- &mapped_addr[0], sizeof(pj_sockaddr_in));
+ pj_sockaddr_cp(&skinfo->rtp_addr_name, &mapped_addr[0]);
skinfo->rtcp_sock = sock[1];
- pj_memcpy(&skinfo->rtcp_addr_name,
- &mapped_addr[1], sizeof(pj_sockaddr_in));
+ pj_sockaddr_cp(&skinfo->rtcp_addr_name, &mapped_addr[1]);
PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
@@ -440,7 +480,7 @@ static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
pjmedia_sock_info skinfo;
pj_status_t status;
- status = create_rtp_rtcp_sock(cfg, &skinfo);
+ status = create_rtp_rtcp_sock(call_med, cfg, &skinfo);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
status);
@@ -512,21 +552,59 @@ on_error:
}
#endif
-static void med_tp_timer_cb(void *user_data)
+/* Deferred callback to notify ICE init complete */
+static void ice_init_complete_cb(void *user_data)
{
pjsua_call_media *call_med = (pjsua_call_media*)user_data;
- pjsua_call *call = NULL;
- pjsip_dialog *dlg = NULL;
- acquire_call("med_tp_timer_cb", call_med->call->index, &call, &dlg);
+ if (call_med->call == NULL)
+ return;
+ /* No need to acquire_call() if we only change the tp_ready flag
+ * (i.e. transport is being created synchronously). Otherwise
+ * calling acquire_call() here may cause deadlock. See
+ * https://trac.pjsip.org/repos/ticket/1578
+ */
call_med->tp_ready = call_med->tp_result;
- if (call_med->med_create_cb)
+
+ if (call_med->med_create_cb) {
+ pjsua_call *call = NULL;
+ pjsip_dialog *dlg = NULL;
+
+ if (acquire_call("ice_init_complete_cb", call_med->call->index,
+ &call, &dlg) != PJ_SUCCESS)
+ {
+ /* Call have been terminated */
+ return;
+ }
+
(*call_med->med_create_cb)(call_med, call_med->tp_ready,
call_med->call->secure_level, NULL);
+ if (dlg)
+ pjsip_dlg_dec_lock(dlg);
+ }
+}
+
+/* Deferred callback to notify ICE negotiation failure */
+static void ice_failed_nego_cb(void *user_data)
+{
+ int call_id = (int)(long)user_data;
+ pjsua_call *call = NULL;
+ pjsip_dialog *dlg = NULL;
+
+ if (acquire_call("ice_failed_nego_cb", call_id,
+ &call, &dlg) != PJ_SUCCESS)
+ {
+ /* Call have been terminated */
+ return;
+ }
+
+ pjsua_var.ua_cfg.cb.on_call_media_state(call_id);
+
if (dlg)
- pjsip_dlg_dec_lock(dlg);
+ pjsip_dlg_dec_lock(dlg);
+
}
/* This callback is called when ICE negotiation completes */
@@ -535,73 +613,43 @@ static void on_ice_complete(pjmedia_transport *tp,
pj_status_t result)
{
pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
+ pjsua_call *call;
if (!call_med)
return;
+ call = call_med->call;
+
switch (op) {
case PJ_ICE_STRANS_OP_INIT:
call_med->tp_result = result;
- pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1);
+ pjsua_schedule_timer2(&ice_init_complete_cb, call_med, 1);
break;
case PJ_ICE_STRANS_OP_NEGOTIATION:
- if (result != PJ_SUCCESS) {
+ if (result == PJ_SUCCESS) {
+ /* Update RTP address */
+ pjmedia_transport_info tpinfo;
+ pjmedia_transport_info_init(&tpinfo);
+ pjmedia_transport_get_info(call_med->tp, &tpinfo);
+ pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
+ } else {
call_med->state = PJSUA_CALL_MEDIA_ERROR;
call_med->dir = PJMEDIA_DIR_NONE;
-
- if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
- pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
- }
- } else if (call_med->call) {
- /* Send UPDATE if default transport address is different than
- * what was advertised (ticket #881)
- */
- pjmedia_transport_info tpinfo;
- pjmedia_ice_transport_info *ii = NULL;
- unsigned i;
-
- pjmedia_transport_info_init(&tpinfo);
- pjmedia_transport_get_info(tp, &tpinfo);
- for (i=0; i<tpinfo.specific_info_cnt; ++i) {
- if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
- ii = (pjmedia_ice_transport_info*)
- tpinfo.spc_info[i].buffer;
- break;
- }
- }
-
- if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
- pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
- &call_med->rtp_addr))
- {
- pj_bool_t use_update;
- const pj_str_t STR_UPDATE = { "UPDATE", 6 };
- pjsip_dialog_cap_status support_update;
- pjsip_dialog *dlg;
-
- dlg = call_med->call->inv->dlg;
- support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
- NULL, &STR_UPDATE);
- use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
-
- PJ_LOG(4,(THIS_FILE,
- "ICE default transport address has changed for "
- "call %d, sending %s",
- call_med->call->index,
- (use_update ? "UPDATE" : "re-INVITE")));
-
- if (use_update)
- pjsua_call_update(call_med->call->index, 0, NULL);
- else
- pjsua_call_reinvite(call_med->call->index, 0, NULL);
+ if (call && pjsua_var.ua_cfg.cb.on_call_media_state) {
+ /* Defer the callback to a timer */
+ pjsua_schedule_timer2(&ice_failed_nego_cb,
+ (void*)(long)call->index, 1);
}
- }
+ }
+ /* Check if default ICE transport address is changed */
+ call->reinv_ice_sent = PJ_FALSE;
+ pjsua_call_schedule_reinvite_check(call, 0);
break;
case PJ_ICE_STRANS_OP_KEEP_ALIVE:
if (result != PJ_SUCCESS) {
PJ_PERROR(4,(THIS_FILE, result,
"ICE keep alive failure for transport %d:%d",
- call_med->call->index, call_med->idx));
+ call->index, call_med->idx));
}
if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
pjsua_med_tp_state_info info;
@@ -612,10 +660,10 @@ static void on_ice_complete(pjmedia_transport *tp,
info.status = result;
info.ext_info = &op;
(*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
- call_med->call->index, &info);
+ call->index, &info);
}
if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
- pjsua_call_id id = call_med->call->index;
+ pjsua_call_id id = call->index;
(*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
NULL);
}
@@ -657,12 +705,15 @@ static pj_status_t create_ice_media_transport(
pj_bool_t async)
{
char stunip[PJ_INET6_ADDRSTRLEN];
+ pjsua_acc_config *acc_cfg;
pj_ice_strans_cfg ice_cfg;
pjmedia_ice_cb ice_cb;
char name[32];
unsigned comp_cnt;
pj_status_t status;
+ acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
+
/* Make sure STUN server resolution has completed */
status = resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
@@ -679,7 +730,7 @@ static pj_status_t create_ice_media_transport(
ice_cfg.af = pj_AF_INET();
ice_cfg.resolver = pjsua_var.resolver;
- ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
+ ice_cfg.opt = acc_cfg->ice_cfg.ice_opt;
/* Configure STUN settings */
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
@@ -687,8 +738,16 @@ static pj_status_t create_ice_media_transport(
ice_cfg.stun.server = pj_str(stunip);
ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
}
- if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
- ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
+ if (acc_cfg->ice_cfg.ice_max_host_cands >= 0)
+ ice_cfg.stun.max_host_cands = acc_cfg->ice_cfg.ice_max_host_cands;
+
+ /* Copy binding port setting to STUN setting */
+ pj_sockaddr_init(ice_cfg.af, &ice_cfg.stun.cfg.bound_addr,
+ &cfg->bound_addr, (pj_uint16_t)cfg->port);
+ ice_cfg.stun.cfg.port_range = (pj_uint16_t)cfg->port_range;
+ if (cfg->port != 0 && ice_cfg.stun.cfg.port_range == 0)
+ ice_cfg.stun.cfg.port_range =
+ (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
/* Copy QoS setting to STUN setting */
ice_cfg.stun.cfg.qos_type = cfg->qos_type;
@@ -696,8 +755,8 @@ static pj_status_t create_ice_media_transport(
sizeof(cfg->qos_params));
/* Configure TURN settings */
- if (pjsua_var.media_cfg.enable_turn) {
- status = parse_host_port(&pjsua_var.media_cfg.turn_server,
+ if (acc_cfg->turn_cfg.enable_turn) {
+ status = parse_host_port(&acc_cfg->turn_cfg.turn_server,
&ice_cfg.turn.server,
&ice_cfg.turn.port);
if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
@@ -706,24 +765,36 @@ static pj_status_t create_ice_media_transport(
}
if (ice_cfg.turn.port == 0)
ice_cfg.turn.port = 3479;
- ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
+ ice_cfg.turn.conn_type = acc_cfg->turn_cfg.turn_conn_type;
pj_memcpy(&ice_cfg.turn.auth_cred,
- &pjsua_var.media_cfg.turn_auth_cred,
+ &acc_cfg->turn_cfg.turn_auth_cred,
sizeof(ice_cfg.turn.auth_cred));
/* Copy QoS setting to TURN setting */
ice_cfg.turn.cfg.qos_type = cfg->qos_type;
pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
sizeof(cfg->qos_params));
+
+ /* Copy binding port setting to TURN setting */
+ pj_sockaddr_init(ice_cfg.af, &ice_cfg.turn.cfg.bound_addr,
+ &cfg->bound_addr, (pj_uint16_t)cfg->port);
+ ice_cfg.turn.cfg.port_range = (pj_uint16_t)cfg->port_range;
+ if (cfg->port != 0 && ice_cfg.turn.cfg.port_range == 0)
+ ice_cfg.turn.cfg.port_range =
+ (pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
}
+ /* Configure packet size for STUN and TURN sockets */
+ ice_cfg.stun.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+ ice_cfg.turn.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+
pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
ice_cb.on_ice_complete = &on_ice_complete;
pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
call_med->tp_ready = PJ_EPENDING;
comp_cnt = 1;
- if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp)
+ if (PJMEDIA_ADVERTISE_RTCP && !acc_cfg->ice_cfg.ice_no_rtcp)
++comp_cnt;
status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
@@ -761,7 +832,7 @@ static pj_status_t create_ice_media_transport(
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
pjsua_var.media_cfg.rx_drop_pct);
-
+
return PJ_SUCCESS;
on_error:
@@ -851,7 +922,7 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create(
pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
/* Create the transports */
- if (pjsua_var.media_cfg.enable_ice) {
+ if (pjsua_var.ice_cfg.enable_ice) {
status = create_ice_media_transports(&cfg);
} else {
status = create_udp_media_transports(&cfg);
@@ -1237,7 +1308,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
- if (pjsua_var.media_cfg.enable_ice) {
+ if (pjsua_var.acc[call_med->call->acc_id].cfg.ice_cfg.enable_ice) {
status = create_ice_media_transport(tcfg, call_med, async);
if (async && status == PJ_EPENDING) {
/* We will resume call media initialization in the
@@ -1245,7 +1316,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
*/
call_med->med_create_cb = &call_media_init_cb;
call_med->med_init_cb = cb;
-
+
return PJ_EPENDING;
}
} else {
@@ -1344,7 +1415,7 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
}
if (call_med->use_custom_med_tp) {
- unsigned custom_med_tp_flags = 0;
+ unsigned custom_med_tp_flags = PJSUA_MED_TP_CLOSE_MEMBER;
/* Use custom media transport returned by the application */
call_med->tp =
@@ -1388,12 +1459,12 @@ on_return:
/* Clean up media transports in provisional media that is not used
* by call media.
*/
-void pjsua_media_prov_clean_up(pjsua_call_id call_id)
+static void media_prov_clean_up(pjsua_call_id call_id, int idx)
{
pjsua_call *call = &pjsua_var.calls[call_id];
unsigned i;
- for (i = 0; i < call->med_prov_cnt; ++i) {
+ for (i = idx; i < call->med_prov_cnt; ++i) {
pjsua_call_media *call_med = &call->media_prov[i];
unsigned j;
pj_bool_t used = PJ_FALSE;
@@ -1420,6 +1491,11 @@ void pjsua_media_prov_clean_up(pjsua_call_id call_id)
}
}
+void pjsua_media_prov_clean_up(pjsua_call_id call_id)
+{
+ media_prov_clean_up(call_id, 0);
+}
+
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
pjsip_role_e role,
@@ -1452,8 +1528,10 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
* (e.g. in reinvites, updates, etc).
*/
- if (pjsua_get_state() != PJSUA_STATE_RUNNING)
+ if (pjsua_get_state() != PJSUA_STATE_RUNNING) {
+ if (sip_err_code) *sip_err_code = PJSIP_SC_SERVICE_UNAVAILABLE;
return PJ_EBUSY;
+ }
if (async) {
pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
@@ -1831,6 +1909,8 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
if (rem_sdp && mi >= rem_sdp->media_count) {
/* Remote might have removed some media lines. */
+ media_prov_clean_up(call->index, rem_sdp->media_count);
+ call->med_prov_cnt = rem_sdp->media_count;
break;
}
@@ -1848,10 +1928,6 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
m->desc.transport = pj_str("RTP/AVP");
m->desc.fmt_count = 1;
- m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
- m->conn->net_type = pj_str("IN");
- m->conn->addr_type = pj_str("IP4");
- m->conn->addr = pj_str("127.0.0.1");
switch (call_med->type) {
case PJMEDIA_TYPE_AUDIO:
@@ -1881,6 +1957,24 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
}
}
+ /* Add connection line, if none */
+ if (m->conn == NULL && sdp->conn == NULL) {
+ pj_bool_t use_ipv6;
+
+ use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
+ PJSUA_IPV6_DISABLED);
+
+ m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ m->conn->net_type = pj_str("IN");
+ if (use_ipv6) {
+ m->conn->addr_type = pj_str("IP6");
+ m->conn->addr = pj_str("::1");
+ } else {
+ m->conn->addr_type = pj_str("IP4");
+ m->conn->addr = pj_str("127.0.0.1");
+ }
+ }
+
sdp->media[sdp->media_count++] = m;
continue;
}
@@ -1919,12 +2013,14 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
return status;
}
+#if PJSUA_SDP_SESS_HAS_CONN
/* Copy c= line of the first media to session level,
* if there's none.
*/
if (sdp->conn == NULL) {
sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
}
+#endif
/* Find media bandwidth info */
@@ -2049,63 +2145,73 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
}
-static void stop_media_session(pjsua_call_id call_id)
+static void stop_media_stream(pjsua_call *call, unsigned med_idx)
{
- pjsua_call *call = &pjsua_var.calls[call_id];
- unsigned mi;
+ pjsua_call_media *call_med = &call->media[med_idx];
- pj_log_push_indent();
+ /* Check if stream does not exist */
+ if (med_idx >= call->med_cnt)
+ return;
- for (mi=0; mi<call->med_cnt; ++mi) {
- pjsua_call_media *call_med = &call->media[mi];
+ pj_log_push_indent();
- if (call_med->type == PJMEDIA_TYPE_AUDIO) {
- pjsua_aud_stop_stream(call_med);
- }
+ if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+ pjsua_aud_stop_stream(call_med);
+ }
#if PJMEDIA_HAS_VIDEO
- else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
- pjsua_vid_stop_stream(call_med);
- }
+ else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+ pjsua_vid_stop_stream(call_med);
+ }
#endif
- PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
- call_id, mi));
- call_med->prev_state = call_med->state;
- call_med->state = PJSUA_CALL_MEDIA_NONE;
+ PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed",
+ call->index, med_idx));
+ call_med->prev_state = call_med->state;
+ call_med->state = PJSUA_CALL_MEDIA_NONE;
- /* Try to sync recent changes to provisional media */
- if (mi<call->med_prov_cnt && call->media_prov[mi].tp==call_med->tp)
- {
- pjsua_call_media *prov_med = &call->media_prov[mi];
+ /* Try to sync recent changes to provisional media */
+ if (med_idx < call->med_prov_cnt &&
+ call->media_prov[med_idx].tp == call_med->tp)
+ {
+ pjsua_call_media *prov_med = &call->media_prov[med_idx];
- /* Media state */
- prov_med->prev_state = call_med->prev_state;
- prov_med->state = call_med->state;
+ /* Media state */
+ prov_med->prev_state = call_med->prev_state;
+ prov_med->state = call_med->state;
- /* RTP seq/ts */
- prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
- prov_med->rtp_tx_seq = call_med->rtp_tx_seq;
- prov_med->rtp_tx_ts = call_med->rtp_tx_ts;
+ /* RTP seq/ts */
+ prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
+ prov_med->rtp_tx_seq = call_med->rtp_tx_seq;
+ prov_med->rtp_tx_ts = call_med->rtp_tx_ts;
- /* Stream */
- if (call_med->type == PJMEDIA_TYPE_AUDIO) {
- prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
- prov_med->strm.a.stream = call_med->strm.a.stream;
- }
+ /* Stream */
+ if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+ prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
+ prov_med->strm.a.stream = call_med->strm.a.stream;
+ }
#if PJMEDIA_HAS_VIDEO
- else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
- prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
- prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
- prov_med->strm.v.stream = call_med->strm.v.stream;
- }
-#endif
+ else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+ prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
+ prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
+ prov_med->strm.v.stream = call_med->strm.v.stream;
}
+#endif
}
pj_log_pop_indent();
}
+static void stop_media_session(pjsua_call_id call_id)
+{
+ pjsua_call *call = &pjsua_var.calls[call_id];
+ unsigned mi;
+
+ for (mi=0; mi<call->med_cnt; ++mi) {
+ stop_media_stream(call, mi);
+ }
+}
+
pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
{
pjsua_call *call = &pjsua_var.calls[call_id];
@@ -2153,6 +2259,197 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
}
+/* Match codec fmtp. This will compare the values and the order. */
+static pj_bool_t match_codec_fmtp(const pjmedia_codec_fmtp *fmtp1,
+ const pjmedia_codec_fmtp *fmtp2)
+{
+ unsigned i;
+
+ if (fmtp1->cnt != fmtp2->cnt)
+ return PJ_FALSE;
+
+ for (i = 0; i < fmtp1->cnt; ++i) {
+ if (pj_stricmp(&fmtp1->param[i].name, &fmtp2->param[i].name))
+ return PJ_FALSE;
+ if (pj_stricmp(&fmtp1->param[i].val, &fmtp2->param[i].val))
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO
+
+static pj_bool_t is_ice_running(pjmedia_transport *tp)
+{
+ pjmedia_transport_info tpinfo;
+ pjmedia_ice_transport_info *ice_info;
+
+ pjmedia_transport_info_init(&tpinfo);
+ pjmedia_transport_get_info(tp, &tpinfo);
+ ice_info = (pjmedia_ice_transport_info*)
+ pjmedia_transport_info_get_spc_info(&tpinfo,
+ PJMEDIA_TRANSPORT_TYPE_ICE);
+ return (ice_info && ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING);
+}
+
+
+static pj_bool_t is_media_changed(const pjsua_call *call,
+ unsigned med_idx,
+ const pjsua_stream_info *new_si_)
+{
+ const pjsua_call_media *call_med = &call->media[med_idx];
+
+ /* Check for newly added media */
+ if (med_idx >= call->med_cnt)
+ return PJ_TRUE;
+
+ /* Compare media type */
+ if (call_med->type != new_si_->type)
+ return PJ_TRUE;
+
+ /* Audio update checks */
+ if (call_med->type == PJMEDIA_TYPE_AUDIO) {
+ pjmedia_stream_info the_old_si;
+ const pjmedia_stream_info *old_si = NULL;
+ const pjmedia_stream_info *new_si = &new_si_->info.aud;
+ const pjmedia_codec_info *old_ci = NULL;
+ const pjmedia_codec_info *new_ci = &new_si->fmt;
+ const pjmedia_codec_param *old_cp = NULL;
+ const pjmedia_codec_param *new_cp = new_si->param;
+
+ /* Compare media direction */
+ if (call_med->dir != new_si->dir)
+ return PJ_TRUE;
+
+ /* Get current active stream info */
+ if (call_med->strm.a.stream) {
+ pjmedia_stream_get_info(call_med->strm.a.stream, &the_old_si);
+ old_si = &the_old_si;
+ old_ci = &old_si->fmt;
+ old_cp = old_si->param;
+ } else {
+ /* The stream is inactive. */
+ return (new_si->dir != PJMEDIA_DIR_NONE);
+ }
+
+ /* Compare remote RTP address. If ICE is running, change in default
+ * address can happen after negotiation, this can be handled
+ * internally by ICE and does not need to cause media restart.
+ */
+ if (!is_ice_running(call_med->tp) &&
+ pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
+ {
+ return PJ_TRUE;
+ }
+
+ /* Compare codec info */
+ if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
+ old_ci->clock_rate != new_ci->clock_rate ||
+ old_ci->channel_cnt != new_ci->channel_cnt ||
+ old_si->rx_pt != new_si->rx_pt ||
+ old_si->tx_pt != new_si->tx_pt ||
+ old_si->rx_event_pt != new_si->tx_event_pt ||
+ old_si->tx_event_pt != new_si->tx_event_pt)
+ {
+ return PJ_TRUE;
+ }
+
+ /* Compare codec param */
+ if (old_cp->setting.frm_per_pkt != new_cp->setting.frm_per_pkt ||
+ old_cp->setting.vad != new_cp->setting.vad ||
+ old_cp->setting.cng != new_cp->setting.cng ||
+ old_cp->setting.plc != new_cp->setting.plc ||
+ old_cp->setting.penh != new_cp->setting.penh ||
+ !match_codec_fmtp(&old_cp->setting.dec_fmtp,
+ &new_cp->setting.dec_fmtp) ||
+ !match_codec_fmtp(&old_cp->setting.enc_fmtp,
+ &new_cp->setting.enc_fmtp))
+ {
+ return PJ_TRUE;
+ }
+ }
+
+#if PJMEDIA_HAS_VIDEO
+ else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+ pjmedia_vid_stream_info the_old_si;
+ const pjmedia_vid_stream_info *old_si = NULL;
+ const pjmedia_vid_stream_info *new_si = &new_si_->info.vid;
+ const pjmedia_vid_codec_info *old_ci = NULL;
+ const pjmedia_vid_codec_info *new_ci = &new_si->codec_info;
+ const pjmedia_vid_codec_param *old_cp = NULL;
+ const pjmedia_vid_codec_param *new_cp = new_si->codec_param;
+
+ /* Compare media direction */
+ if (call_med->dir != new_si->dir)
+ return PJ_TRUE;
+
+ /* Get current active stream info */
+ if (call_med->strm.v.stream) {
+ pjmedia_vid_stream_get_info(call_med->strm.v.stream, &the_old_si);
+ old_si = &the_old_si;
+ old_ci = &old_si->codec_info;
+ old_cp = old_si->codec_param;
+ } else {
+ /* The stream is inactive. */
+ return (new_si->dir != PJMEDIA_DIR_NONE);
+ }
+
+ /* Compare remote RTP address. If ICE is running, change in default
+ * address can happen after negotiation, this can be handled
+ * internally by ICE and does not need to cause media restart.
+ */
+ if (!is_ice_running(call_med->tp) &&
+ pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
+ {
+ return PJ_TRUE;
+ }
+
+ /* Compare codec info */
+ if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
+ old_si->rx_pt != new_si->rx_pt ||
+ old_si->tx_pt != new_si->tx_pt)
+ {
+ return PJ_TRUE;
+ }
+
+ /* Compare codec param */
+ if (/* old_cp->enc_mtu != new_cp->enc_mtu || */
+ pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det,
+ sizeof(pjmedia_video_format_detail)) ||
+ !match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) ||
+ !match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp))
+ {
+ return PJ_TRUE;
+ }
+ }
+
+#endif
+
+ else {
+ /* Just return PJ_TRUE for other media type */
+ return PJ_TRUE;
+ }
+
+ return PJ_FALSE;
+}
+
+#else /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
+
+static pj_bool_t is_media_changed(const pjsua_call *call,
+ unsigned med_idx,
+ const pjsua_stream_info *new_si_)
+{
+ PJ_UNUSED_ARG(call);
+ PJ_UNUSED_ARG(med_idx);
+ PJ_UNUSED_ARG(new_si_);
+ /* Always assume that media has been changed */
+ return PJ_TRUE;
+}
+
+#endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
+
+
pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
const pjmedia_sdp_session *local_sdp,
const pjmedia_sdp_session *remote_sdp)
@@ -2181,7 +2478,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
pj_log_push_indent();
/* Destroy existing media session, if any. */
- stop_media_session(call->index);
+ //stop_media_session(call->index);
/* Call media count must be at least equal to SDP media. Note that
* it may not be equal when remote removed any SDP media line.
@@ -2235,6 +2532,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
/* Process each media stream */
for (mi=0; mi < call->med_prov_cnt; ++mi) {
pjsua_call_media *call_med = &call->media_prov[mi];
+ pj_bool_t media_changed = PJ_FALSE;
if (mi >= local_sdp->media_count ||
mi >= remote_sdp->media_count)
@@ -2242,8 +2540,12 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
/* This may happen when remote removed any SDP media lines in
* its re-offer.
*/
+
+ /* Stop stream */
+ stop_media_stream(call, mi);
+
+ /* Close the media transport */
if (call_med->tp) {
- /* Close the media transport */
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
pjmedia_transport_close(call_med->tp);
call_med->tp = call_med->tp_orig = NULL;
@@ -2258,11 +2560,14 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
#endif
}
+ /* Apply media update action */
if (call_med->type==PJMEDIA_TYPE_AUDIO) {
pjmedia_stream_info the_si, *si = &the_si;
+ pjsua_stream_info stream_info;
- status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
- local_sdp, remote_sdp, mi);
+ status = pjmedia_stream_info_from_sdp(
+ si, tmp_pool, pjsua_var.med_endpt,
+ local_sdp, remote_sdp, mi);
if (status != PJ_SUCCESS) {
PJ_PERROR(1,(THIS_FILE, status,
"pjmedia_stream_info_from_sdp() failed "
@@ -2271,14 +2576,43 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
continue;
}
+ /* Override ptime, if this option is specified. */
+ if (pjsua_var.media_cfg.ptime != 0) {
+ si->param->setting.frm_per_pkt = (pj_uint8_t)
+ (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime);
+ if (si->param->setting.frm_per_pkt == 0)
+ si->param->setting.frm_per_pkt = 1;
+ }
+
+ /* Disable VAD, if this option is specified. */
+ if (pjsua_var.media_cfg.no_vad) {
+ si->param->setting.vad = 0;
+ }
+
+ /* Check if this media is changed */
+ stream_info.type = PJMEDIA_TYPE_AUDIO;
+ stream_info.info.aud = the_si;
+ if (pjsua_var.media_cfg.no_smart_media_update ||
+ is_media_changed(call, mi, &stream_info))
+ {
+ media_changed = PJ_TRUE;
+ /* Stop the media */
+ stop_media_stream(call, mi);
+ } else {
+ PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.",
+ call_id, mi));
+ }
+
/* Check if no media is active */
if (si->dir == PJMEDIA_DIR_NONE) {
+
/* Update call media state and direction */
call_med->state = PJSUA_CALL_MEDIA_NONE;
call_med->dir = PJMEDIA_DIR_NONE;
} else {
pjmedia_transport_info tp_info;
+ pjmedia_srtp_info *srtp_info;
/* Start/restart media transport based on info in SDP */
status = pjmedia_transport_media_start(call_med->tp,
@@ -2297,17 +2631,24 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
/* Get remote SRTP usage policy */
pjmedia_transport_info_init(&tp_info);
pjmedia_transport_get_info(call_med->tp, &tp_info);
- if (tp_info.specific_info_cnt > 0) {
- unsigned i;
- for (i = 0; i < tp_info.specific_info_cnt; ++i) {
- if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
- {
- pjmedia_srtp_info *srtp_info =
- (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
-
- call_med->rem_srtp_use = srtp_info->peer_use;
- break;
- }
+ srtp_info = (pjmedia_srtp_info*)
+ pjmedia_transport_info_get_spc_info(
+ &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
+ if (srtp_info) {
+ call_med->rem_srtp_use = srtp_info->peer_use;
+ }
+
+ /* Update audio channel */
+ if (media_changed) {
+ status = pjsua_aud_channel_update(call_med,
+ call->inv->pool, si,
+ local_sdp, remote_sdp);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status,
+ "pjsua_aud_channel_update() failed "
+ "for call_id %d media %d",
+ call_id, mi));
+ continue;
}
}
@@ -2323,17 +2664,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
}
- /* Call implementation */
- status = pjsua_aud_channel_update(call_med, tmp_pool, si,
- local_sdp, remote_sdp);
- if (status != PJ_SUCCESS) {
- PJ_PERROR(1,(THIS_FILE, status,
- "pjsua_aud_channel_update() failed "
- "for call_id %d media %d",
- call_id, mi));
- continue;
- }
-
/* Print info. */
if (status == PJ_SUCCESS) {
char info[80];
@@ -2378,9 +2708,11 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
} else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
pjmedia_vid_stream_info the_si, *si = &the_si;
+ pjsua_stream_info stream_info;
- status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
- local_sdp, remote_sdp, mi);
+ status = pjmedia_vid_stream_info_from_sdp(
+ si, tmp_pool, pjsua_var.med_endpt,
+ local_sdp, remote_sdp, mi);
if (status != PJ_SUCCESS) {
PJ_PERROR(1,(THIS_FILE, status,
"pjmedia_vid_stream_info_from_sdp() failed "
@@ -2389,14 +2721,28 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
continue;
}
+ /* Check if this media is changed */
+ stream_info.type = PJMEDIA_TYPE_VIDEO;
+ stream_info.info.vid = the_si;
+ if (is_media_changed(call, mi, &stream_info)) {
+ media_changed = PJ_TRUE;
+ /* Stop the media */
+ stop_media_stream(call, mi);
+ } else {
+ PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.",
+ call_id, mi));
+ }
+
/* Check if no media is active */
if (si->dir == PJMEDIA_DIR_NONE) {
+
/* Update call media state and direction */
call_med->state = PJSUA_CALL_MEDIA_NONE;
call_med->dir = PJMEDIA_DIR_NONE;
} else {
pjmedia_transport_info tp_info;
+ pjmedia_srtp_info *srtp_info;
/* Start/restart media transport */
status = pjmedia_transport_media_start(call_med->tp,
@@ -2415,17 +2761,24 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
/* Get remote SRTP usage policy */
pjmedia_transport_info_init(&tp_info);
pjmedia_transport_get_info(call_med->tp, &tp_info);
- if (tp_info.specific_info_cnt > 0) {
- unsigned i;
- for (i = 0; i < tp_info.specific_info_cnt; ++i) {
- if (tp_info.spc_info[i].type ==
- PJMEDIA_TRANSPORT_TYPE_SRTP)
- {
- pjmedia_srtp_info *sri;
- sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer;
- call_med->rem_srtp_use = sri->peer_use;
- break;
- }
+ srtp_info = (pjmedia_srtp_info*)
+ pjmedia_transport_info_get_spc_info(
+ &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
+ if (srtp_info) {
+ call_med->rem_srtp_use = srtp_info->peer_use;
+ }
+
+ /* Update audio channel */
+ if (media_changed) {
+ status = pjsua_vid_channel_update(call_med,
+ call->inv->pool, si,
+ local_sdp, remote_sdp);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status,
+ "pjsua_vid_channel_update() failed "
+ "for call_id %d media %d",
+ call_id, mi));
+ continue;
}
}
@@ -2441,16 +2794,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
}
- status = pjsua_vid_channel_update(call_med, tmp_pool, si,
- local_sdp, remote_sdp);
- if (status != PJ_SUCCESS) {
- PJ_PERROR(1,(THIS_FILE, status,
- "pjsua_vid_channel_update() failed "
- "for call_id %d media %d",
- call_id, mi));
- continue;
- }
-
/* Print info. */
{
char info[80];
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index 4551c91..591f65b 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_pres.c 4186 2012-06-29 01:37:50Z ming $ */
+/* $Id: pjsua_pres.c 4294 2012-11-06 05:02:10Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -864,8 +864,31 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
return PJ_TRUE;
}
- if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN. See https://trac.pjsip.org/repos/ticket/1412
+ */
+ char target_buf[PJSIP_MAX_URL_SIZE];
+ pj_str_t target;
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ target.ptr = target_buf;
+ target.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+ dlg->target,
+ target_buf, sizeof(target_buf));
+ if (target.slen < 0) target.slen = 0;
+
+ if (pjsua_acc_get_uac_addr(acc_id, dlg->pool, &target,
+ &via_addr, NULL, NULL,
+ &via_tp) == PJ_SUCCESS)
+ {
+ pjsip_dlg_set_via_sent_by(dlg, &via_addr,
+ (pjsip_transport*)via_tp);
+ }
+ }
/* Set credentials and preference. */
pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
@@ -1236,6 +1259,20 @@ static pj_status_t send_publish(int acc_id, pj_bool_t active)
if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
pjsip_publishc_set_via_sent_by(acc->publish_sess, &acc->via_addr,
acc->via_tp);
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN. See https://trac.pjsip.org/repos/ticket/1412
+ */
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ if (pjsua_acc_get_uac_addr(acc_id, acc->pool, &acc_cfg->id,
+ &via_addr, NULL, NULL,
+ &via_tp) == PJ_SUCCESS)
+ {
+ pjsip_publishc_set_via_sent_by(acc->publish_sess, &via_addr,
+ (pjsip_transport*)via_tp);
+ }
}
/* Send the PUBLISH request */
@@ -1789,8 +1826,24 @@ static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
*/
pjsip_dlg_inc_lock(buddy->dlg);
- if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
pjsip_dlg_set_via_sent_by(buddy->dlg, &acc->via_addr, acc->via_tp);
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN. See https://trac.pjsip.org/repos/ticket/1412
+ */
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ if (pjsua_acc_get_uac_addr(acc_id, buddy->dlg->pool, &buddy->uri,
+ &via_addr, NULL, NULL,
+ &via_tp) == PJ_SUCCESS)
+ {
+ pjsip_dlg_set_via_sent_by(buddy->dlg, &via_addr,
+ (pjsip_transport*)via_tp);
+ }
+ }
+
status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
@@ -2114,8 +2167,23 @@ pj_status_t pjsua_start_mwi(pjsua_acc_id acc_id, pj_bool_t force_renew)
*/
pjsip_dlg_inc_lock(acc->mwi_dlg);
- if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
+ if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0) {
pjsip_dlg_set_via_sent_by(acc->mwi_dlg, &acc->via_addr, acc->via_tp);
+ } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+ /* Choose local interface to use in Via if acc is not using
+ * STUN. See https://trac.pjsip.org/repos/ticket/1412
+ */
+ pjsip_host_port via_addr;
+ const void *via_tp;
+
+ if (pjsua_acc_get_uac_addr(acc_id, acc->mwi_dlg->pool, &acc->cfg.id,
+ &via_addr, NULL, NULL,
+ &via_tp) == PJ_SUCCESS)
+ {
+ pjsip_dlg_set_via_sent_by(acc->mwi_dlg, &via_addr,
+ (pjsip_transport*)via_tp);
+ }
+ }
/* Create UAC subscription */
status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb,
diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c
index eb74ee1..c92ec56 100644
--- a/pjsip/src/pjsua-lib/pjsua_vid.c
+++ b/pjsip/src/pjsua-lib/pjsua_vid.c
@@ -1,4 +1,4 @@
-/* $Id: pjsua_vid.c 4071 2012-04-24 05:40:32Z nanang $ */
+/* $Id: pjsua_vid.c 4341 2013-02-05 12:21:30Z nanang $ */
/*
* Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
*
@@ -705,7 +705,12 @@ pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med)
return PJ_SUCCESS;
}
-/* Internal function: update video channel after SDP negotiation */
+/* Internal function: update video channel after SDP negotiation.
+ * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov,
+ * for creating stream, etc, as after SDP negotiation and when
+ * the SDP media is not changed, the stream should remain running
+ * while the temporary/flip-flop pool may be released.
+ */
pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med,
pj_pool_t *tmp_pool,
pjmedia_vid_stream_info *si,
@@ -1743,11 +1748,13 @@ static pj_status_t call_modify_video(pjsua_call *call,
sdp->media[med_idx] = sdp_m;
- /* Update SDP media line by media transport */
- status = pjmedia_transport_encode_sdp(call_med->tp, pool,
- sdp, NULL, call_med->idx);
- if (status != PJ_SUCCESS)
- goto on_error;
+ if (call_med->dir == PJMEDIA_DIR_NONE) {
+ /* Update SDP media line by media transport */
+ status = pjmedia_transport_encode_sdp(call_med->tp, pool,
+ sdp, NULL, call_med->idx);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
on_error:
if (status != PJ_SUCCESS) {
@@ -2029,6 +2036,7 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
const pjsua_call_vid_strm_op_param *param)
{
pjsua_call *call;
+ pjsip_dialog *dlg;
pjsua_call_vid_strm_op_param param_;
pj_status_t status;
@@ -2040,9 +2048,9 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
call_id, op));
pj_log_push_indent();
- PJSUA_LOCK();
-
- call = &pjsua_var.calls[call_id];
+ status = acquire_call("pjsua_call_set_vid_strm()", call_id, &call, &dlg);
+ if (status != PJ_SUCCESS)
+ goto on_return;
if (param) {
param_ = *param;
@@ -2097,9 +2105,9 @@ PJ_DEF(pj_status_t) pjsua_call_set_vid_strm (
break;
}
- PJSUA_UNLOCK();
+on_return:
+ if (dlg) pjsip_dlg_dec_lock(dlg);
pj_log_pop_indent();
-
return status;
}
diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c
index c8f3074..eec92fc 100644
--- a/pjsip/src/test/tsx_uac_test.c
+++ b/pjsip/src/test/tsx_uac_test.c
@@ -1,4 +1,4 @@
-/* $Id: tsx_uac_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: tsx_uac_test.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -159,7 +159,7 @@ static struct my_timer
*/
static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
{
- if (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) {
/*
* Transaction with TEST1_BRANCH_ID should terminate with transaction
* timeout status.
@@ -213,7 +213,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
}
- } else if (pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) {
/*
* Transaction with TEST2_BRANCH_ID should terminate with transport error.
*/
@@ -231,7 +231,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
test_complete = 1;
}
- } else if (pj_strcmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
/*
* This test terminates the transaction while resolver is still
* running.
@@ -256,7 +256,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST4_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) {
/*
* This test simulates transport failure after several
* retransmissions.
@@ -284,7 +284,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST5_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) {
/*
* This test simulates transport failure after several
* retransmissions.
@@ -312,7 +312,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST6_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) {
/*
* Successfull non-INVITE transaction.
*/
@@ -355,7 +355,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0) {
/*
* Successfull non-INVITE transaction.
*/
@@ -408,7 +408,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST8_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) {
/*
* Failed INVITE transaction.
*/
@@ -468,7 +468,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
- } else if (pj_strcmp2(&tsx->branch, TEST9_BRANCH_ID)==0) {
+ } else if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) {
/*
* Failed INVITE transaction with provisional response.
*/
@@ -583,7 +583,7 @@ static void terminate_tsx_callback( pj_timer_heap_t *timer_heap,
*/
static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
{
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST1_BRANCH_ID) == 0) {
/*
* The TEST1_BRANCH_ID test performs the verifications for transaction
* retransmission mechanism. It will not answer the incoming request
@@ -651,7 +651,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
return PJ_TRUE;
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST4_BRANCH_ID) == 0) {
/*
* The TEST4_BRANCH_ID test simulates transport failure after several
* retransmissions.
@@ -672,7 +672,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST5_BRANCH_ID) == 0) {
/*
* The TEST5_BRANCH_ID test simulates user terminating the transaction
* after several retransmissions.
@@ -703,7 +703,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
return PJ_TRUE;
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST6_BRANCH_ID) == 0) {
/*
* The TEST6_BRANCH_ID test successfull non-INVITE transaction.
*/
@@ -728,7 +728,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST7_BRANCH_ID) == 0) {
/*
* The TEST7_BRANCH_ID test successfull non-INVITE transaction
* with provisional response.
@@ -778,7 +778,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST8_BRANCH_ID) == 0) {
/*
* The TEST8_BRANCH_ID test failed INVITE transaction.
*/
@@ -841,7 +841,7 @@ static pj_bool_t msg_receiver_on_rx_request(pjsip_rx_data *rdata)
} else
- if (pj_strcmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&rdata->msg_info.via->branch_param, TEST9_BRANCH_ID) == 0) {
/*
* The TEST9_BRANCH_ID test failed INVITE transaction with
* provisional response.
diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c
index 045e958..6da1c4c 100644
--- a/pjsip/src/test/tsx_uas_test.c
+++ b/pjsip/src/test/tsx_uas_test.c
@@ -1,4 +1,4 @@
-/* $Id: tsx_uas_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: tsx_uas_test.c 4208 2012-07-18 07:52:33Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -352,8 +352,8 @@ static void schedule_terminate_tsx( pjsip_transaction *tsx,
*/
static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
{
- if (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0 ||
- pj_strcmp2(&tsx->branch, TEST2_BRANCH_ID)==0)
+ if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 ||
+ pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0)
{
/*
* TEST1_BRANCH_ID tests that non-INVITE transaction transmits final
@@ -362,7 +362,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
*
* TEST2_BRANCH_ID does similar test for non-2xx final response.
*/
- int status_code = (pj_strcmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ?
+ int status_code = (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ?
TEST1_STATUS_CODE : TEST2_STATUS_CODE;
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
@@ -392,7 +392,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
else
- if (pj_strcmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
/*
* TEST3_BRANCH_ID tests sending provisional response.
*/
@@ -455,7 +455,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
} else
- if (pj_strcmp2(&tsx->branch, TEST4_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) {
/*
* TEST4_BRANCH_ID tests receiving retransmissions in TRYING state.
*/
@@ -488,7 +488,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
} else
- if (pj_strcmp2(&tsx->branch, TEST5_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) {
/*
* TEST5_BRANCH_ID tests receiving retransmissions in PROCEEDING state
*/
@@ -525,7 +525,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
}
} else
- if (pj_strcmp2(&tsx->branch, TEST6_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) {
/*
* TEST6_BRANCH_ID tests receiving retransmissions in COMPLETED state
*/
@@ -560,8 +560,8 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
} else
- if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID)==0 ||
- pj_strcmp2(&tsx->branch, TEST8_BRANCH_ID)==0)
+ if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0 ||
+ pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0)
{
/*
* TEST7_BRANCH_ID and TEST8_BRANCH_ID test retransmission of
@@ -569,7 +569,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
*/
int code;
- if (pj_strcmp2(&tsx->branch, TEST7_BRANCH_ID) == 0)
+ if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID) == 0)
code = TEST7_STATUS_CODE;
else
code = TEST8_STATUS_CODE;
@@ -637,7 +637,7 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
} else
- if (pj_strcmp2(&tsx->branch, TEST9_BRANCH_ID)==0) {
+ if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0) {
/*
* TEST9_BRANCH_ID tests that retransmission of INVITE final response
* must cease when ACK is received.
@@ -701,9 +701,9 @@ static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
} else
- if (pj_strcmp2(&tsx->branch, TEST10_BRANCH_ID)==0 ||
- pj_strcmp2(&tsx->branch, TEST11_BRANCH_ID)==0 ||
- pj_strcmp2(&tsx->branch, TEST12_BRANCH_ID)==0)
+ if (pj_stricmp2(&tsx->branch, TEST10_BRANCH_ID)==0 ||
+ pj_stricmp2(&tsx->branch, TEST11_BRANCH_ID)==0 ||
+ pj_stricmp2(&tsx->branch, TEST12_BRANCH_ID)==0)
{
if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
@@ -739,8 +739,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
pj_str_t branch_param = rdata->msg_info.via->branch_param;
pj_status_t status;
- if (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST2_BRANCH_ID) == 0)
+ if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0)
{
/*
* TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx
@@ -749,7 +749,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
*
* TEST2_BRANCH_ID performs similar test for non-2xx final response.
*/
- int status_code = (pj_strcmp2(&branch_param, TEST1_BRANCH_ID) == 0) ?
+ int status_code = (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0) ?
TEST1_STATUS_CODE : TEST2_STATUS_CODE;
if (msg->type == PJSIP_REQUEST_MSG) {
@@ -789,7 +789,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
}
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST3_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST3_BRANCH_ID) == 0) {
/* TEST3_BRANCH_ID tests provisional response. */
@@ -838,9 +838,9 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
}
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0)
+ } else if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0)
{
/* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state. */
@@ -863,12 +863,12 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
- if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
- } else if (pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
send_response(rdata, tsx, TEST5_PROVISIONAL_CODE);
- } else if (pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
PJ_LOG(4,(THIS_FILE, " sending provisional response"));
send_response(rdata, tsx, TEST6_PROVISIONAL_CODE);
PJ_LOG(4,(THIS_FILE, " sending final response"));
@@ -882,11 +882,11 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
++recv_count;
- if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
PJ_LOG(3,(THIS_FILE, " error: not expecting response!"));
test_complete = -132;
- } else if (pj_strcmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) {
PJ_LOG(3,(THIS_FILE, " error: incorrect status code!"));
@@ -898,7 +898,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
test_complete = -134;
}
- } else if (pj_strcmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
int code = rdata->msg_info.msg->line.status.code;
@@ -927,8 +927,8 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST8_BRANCH_ID) == 0)
+ } else if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST8_BRANCH_ID) == 0)
{
/*
@@ -950,7 +950,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
- if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) {
+ if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) {
send_response(rdata, tsx, TEST7_STATUS_CODE);
@@ -965,7 +965,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
++recv_count;
- if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0)
+ if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0)
code = TEST7_STATUS_CODE;
else
code = TEST8_STATUS_CODE;
@@ -1013,7 +1013,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
}
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST9_BRANCH_ID) == 0) {
+ } else if (pj_stricmp2(&branch_param, TEST9_BRANCH_ID) == 0) {
/*
* TEST9_BRANCH_ID tests that the retransmission of INVITE final
@@ -1118,15 +1118,15 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
}
return PJ_TRUE;
- } else if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0 ||
- pj_strcmp2(&branch_param, TEST12_BRANCH_ID) == 0)
+ } else if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0 ||
+ pj_stricmp2(&branch_param, TEST12_BRANCH_ID) == 0)
{
int test_num, code1, code2;
- if (pj_strcmp2(&branch_param, TEST10_BRANCH_ID) == 0)
+ if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0)
test_num=10, code1 = 100, code2 = 0;
- else if (pj_strcmp2(&branch_param, TEST11_BRANCH_ID) == 0)
+ else if (pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0)
test_num=11, code1 = 100, code2 = 200;
else
test_num=12, code1 = 200, code2 = 0;
diff --git a/pjsip/src/test/uri_test.c b/pjsip/src/test/uri_test.c
index 8527faa..c8b0b3e 100644
--- a/pjsip/src/test/uri_test.c
+++ b/pjsip/src/test/uri_test.c
@@ -1,4 +1,4 @@
-/* $Id: uri_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* $Id: uri_test.c 4210 2012-07-19 01:00:07Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
@@ -67,6 +67,7 @@ static pjsip_uri *create_uri14( pj_pool_t *pool );
static pjsip_uri *create_uri15( pj_pool_t *pool );
static pjsip_uri *create_uri16( pj_pool_t *pool );
static pjsip_uri *create_uri17( pj_pool_t *pool );
+static pjsip_uri *create_uri18( pj_pool_t *pool );
static pjsip_uri *create_uri25( pj_pool_t *pool );
static pjsip_uri *create_uri26( pj_pool_t *pool );
static pjsip_uri *create_uri27( pj_pool_t *pool );
@@ -81,6 +82,7 @@ static pjsip_uri *create_uri35( pj_pool_t *pool );
static pjsip_uri *create_uri36( pj_pool_t *pool );
static pjsip_uri *create_uri37( pj_pool_t *pool );
static pjsip_uri *create_uri38( pj_pool_t *pool );
+static pjsip_uri *create_uri39( pj_pool_t *pool );
static pjsip_uri *create_dummy( pj_pool_t *pool );
#define ERR_NOT_EQUAL -1001
@@ -349,6 +351,12 @@ struct uri_test
"\xC0\x81 <sip:localhost>",
&create_uri38,
"\"\xC0\x81\" <sip:localhost>"
+ },
+ {
+ /* Even number of backslash before end quote in display name. */
+ PJ_SUCCESS,
+ "\"User\\\\\" <sip:localhost>",
+ &create_uri39,
}
};
@@ -759,6 +767,20 @@ static pjsip_uri *create_uri38( pj_pool_t *pool )
}
+/* "\"User\\\\\" <sip:localhost>" */
+static pjsip_uri *create_uri39(pj_pool_t *pool)
+{
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_sip_uri *url;
+
+ url = pjsip_sip_uri_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "User\\\\");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
static pjsip_uri *create_dummy(pj_pool_t *pool)
{
PJ_UNUSED_ARG(pool);
diff --git a/pkgconfig.py b/pkgconfig.py
index 496e311..ee06884 100644
--- a/pkgconfig.py
+++ b/pkgconfig.py
@@ -163,7 +163,9 @@ if __name__ == "__main__":
continue
if REMOVE_THESE.count(opt) != 0:
continue
- if filtered_opts.count(opt) != 0:
+ if opt != '-framework' and opt != '--framework' and filtered_opts.count(opt) != 0:
+ if len(filtered_opts) and (filtered_opts[-1] == '-framework' or filtered_opts[-1] == '--framework'):
+ filtered_opts.pop()
continue
filtered_opts.append(opt)
diff --git a/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.py b/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.py
new file mode 100644
index 0000000..eaa15e6
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.py
@@ -0,0 +1,8 @@
+# $Id: uas-reinv-with-less-media.py 4373 2013-02-27 06:44:44Z ming $
+#
+import inc_const as const
+
+PJSUA = ["--null-audio --extra-audio --max-calls=1 $SIPP_URI"]
+
+# Send hold after remote holds (double hold)
+PJSUA_EXPECTS = [[0, const.MEDIA_HOLD, "H"]]
diff --git a/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.xml b/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.xml
new file mode 100644
index 0000000..33ae6a2
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-reinv-with-less-media.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- 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 -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="Sending OK and re-INVITE with less media (#16xx)">
+ <!-- By adding rrs="true" (Record Route Sets), the route sets -->
+ <!-- are saved and used for following messages sent. Useful to test -->
+ <!-- against stateful SIP proxies/B2BUAs. -->
+
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+ <ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+ <assign assign_to="4" variable="5" />
+ </action>
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4000 RTP/AVP 0 96
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:96 telephone-event/8000
+ a=sendrecv
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ INVITE sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=z9hG4bK-same-branch
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 2 INVITE
+ Max-Forwards: 70
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4000 RTP/AVP 0 96
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:96 telephone-event/8000
+ a=sendonly
+
+ ]]>
+ </send>
+
+ <recv response="200" rtd="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ ACK sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=z9hG4bK-same-branch
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 1 ACK
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4000 RTP/AVP 0 96
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:96 telephone-event/8000
+ a=inactive
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/third_party/BaseClasses/wxutil.cpp b/third_party/BaseClasses/wxutil.cpp
index 8ff8de4..59474ed 100644
--- a/third_party/BaseClasses/wxutil.cpp
+++ b/third_party/BaseClasses/wxutil.cpp
@@ -58,7 +58,7 @@ BOOL CAMMsgEvent::WaitMsg(DWORD dwTimeout)
// timeout (in MS) to expire. allow SENT messages
// to be processed while we wait
DWORD dwWait;
- DWORD dwStartTime;
+ DWORD dwStartTime = 0;
// set the waiting period.
DWORD dwWaitTime = dwTimeout;
@@ -603,8 +603,8 @@ DWORD WINAPI WaitDispatchingMessages(
{
BOOL bPeeked = FALSE;
DWORD dwResult;
- DWORD dwStart;
- DWORD dwThreadPriority;
+ DWORD dwStart = 0;
+ DWORD dwThreadPriority = THREAD_PRIORITY_HIGHEST;
static UINT uMsgId = 0;
diff --git a/version.mak b/version.mak
index 5cdbd7f..d3448e5 100644
--- a/version.mak
+++ b/version.mak
@@ -1,7 +1,7 @@
# Don't change the "export PJ_VERSION_xxx" style, they are parsed by setup.py
export PJ_VERSION_MAJOR := 2
-export PJ_VERSION_MINOR := 0
-export PJ_VERSION_REV := 1
+export PJ_VERSION_MINOR := 1
+export PJ_VERSION_REV := 0
export PJ_VERSION_SUFFIX :=
export PJ_VERSION := $(PJ_VERSION_MAJOR).$(PJ_VERSION_MINOR)