diff options
author | Jason Parker <jparker@digium.com> | 2013-03-11 15:09:56 -0500 |
---|---|---|
committer | Jason Parker <jparker@digium.com> | 2013-03-11 15:09:56 -0500 |
commit | 483805f79570115ab95c69698792d238c1719b1b (patch) | |
tree | 6b53ab2fd2b2478f864ccc8bd1b0bfaedc4d2050 | |
parent | f3ab456a17af1c89a6e3be4d20c5944853df1cb0 (diff) |
Import pjproject-2.1
176 files changed, 10759 insertions, 3254 deletions
@@ -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(¶m); + status = pj_ssl_sock_create(pool, ¶m, &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, - ¶m->setting.dec_fmtp.param[i].name); - pj_strdup(pool, &p->param->setting.dec_fmtp.param[i].val, - ¶m->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, - ¶m->setting.enc_fmtp.param[i].name); - pj_strdup(pool, &p->param->setting.enc_fmtp.param[i].val, - ¶m->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(¶m->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(¶m->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(¶m->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, ®c->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(®c->via_addr, sizeof(regc->via_addr)); - else - regc->via_addr = *via_addr; + else { + if (pj_strcmp(®c->via_addr.host, &via_addr->host)) + pj_strdup(regc->pool, ®c->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(¶m, sizeof(param)); + param.realm = auth_srv->realm; + param.acc_name = acc_name; + param.rdata = rdata; + status = (*auth_srv->lookup2)(rdata->tp_info.pool, ¶m, &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, ¶m->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) |