diff --git a/CMakeLists.txt b/CMakeLists.txt index f1664f68d..89333bde4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,15 @@ if(ENABLE_NOTIFY) set(ENABLE_NOTIFY OFF CACHE BOOL "Enable libnotify support." FORCE) endif() endif() +if(ENABLE_ASSISTANT) + find_package(Soup) + if(SOUP_FOUND) + set(BUILD_WIZARD 1) + else() + message(WARNING "Could not find the soup library!") + set(ENABLE_ASSISTANT OFF CACHE BOOL "Turn on assistant compiling." FORCE) + endif() +endif() find_package(Gettext) @@ -120,6 +129,9 @@ endif() if(ENABLE_TUNNEL) include_directories(${TUNNEL_INCLUDE_DIRS}) endif() +if(ENABLE_ASSISTANT) + include_directories(${SOUP_INCLUDE_DIRS}) +endif() if(MSVC) include_directories(${CMAKE_PREFIX_PATH}/include/MSVC) diff --git a/NEWS b/NEWS index 6fada612f..bb1d573a0 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,14 @@ +linphone-3.8.0 -- Date to be defined + Application level improvements: + * The video window has now controls in order to switch fullscreen mode and terminate call. + * The out of call video preview feature (to test camera) is moved into the settings and is no longer linked to the in-call video preview feature. + * Lots of updated translations. + + Liblinphone level improvements: + * Support for RTP/AVPF (RFCxxxx) for video streams, allowing fast transmission error recovery with VP8 codec only. + * API enhancements, most objects can be ref-counted. + * Call video recording feature, in mkv format (H264 streams only for the moment) + linphone-3.7.0 -- February 20th, 2014 Application level improvements: * It is now possible to configure multiple proxy accounts with different transports (UDP, TCP, TLS) diff --git a/README.macos.md b/README.macos.md index cfe0a40a9..381efcf2f 100644 --- a/README.macos.md +++ b/README.macos.md @@ -1,12 +1,18 @@ -# Compiling Linphone on MacOS X +# Linphone on MacOS X -## Dependencies +## Build prerequisite * Xcode (download from apple or using appstore application) -* Java SE -* [HomeBrew](http://brew.sh) or [Macports](http://www.macports.org/). +* [Java SE](http://www.oracle.com/technetwork/java/javase/downloads/index.html) or openJDK + This is required to generate a C sourcefile from SIP grammar using [antlr3](http://www.antlr3.org/) generator. + * [HomeBrew](http://brew.sh) or [Macports](http://www.macports.org/). + * [XQuartz](https://xquartz.macosforge.org) for GTK version. -### Multiple MacOS version support +### Dependencies + +#### Using MacPorts + +##### Multiple MacOS version support In order to enable generation of bundle for multiple MacOS version and 32 bit processors, it is recommended to: @@ -18,17 +24,16 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr > +universal -### Build time dependencies -#### Using MacPorts +##### Linphone library (liblinphone) -* Linphone core dependencies - - sudo port install automake autoconf libtool intltool wget cunit \ + sudo port install automake autoconf libtool pkgconfig intltool wget cunit \ antlr3 speex libvpx readline sqlite3 libsoup openldap libupnp \ ffmpeg-devel -gpl2 -* UI dependencies: install `GTK`. It is recommended to use the `quartz` backend for better integration. +##### Linphone UI (GTK version) + +Install `GTK`. It is recommended to use the `quartz` backend for better integration. sudo port install gtk2 +quartz +no_x11 sudo port install gtk-osx-application -python27 @@ -36,15 +41,21 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr #### Using HomeBrew - brew install automake intltool libtool pkg-config coreutils \ - yasm nasm wget imagemagick gettext gtk+ speex ffmpeg pygtk - brew link gettext --force - # readline is required from linphonec.c otherwise compilation will fail - brew link readline --force +##### Linphone library (liblinphone) - # then you have to install some dependencies from a tap. brew tap Gui13/linphone - brew install antlr3.2 libantlr3.4c mattintosh4/gtk-mac-integration/gtk-mac-integration + brew install intltool libtool wget pkg-config automake libantlr3.4c \ + antlr3.2 gettext speex ffmpeg readline libvpx opus + ln -s /usr/local/bin/glibtoolize /usr/local/bin/libtoolize + brew link --force gettext + +##### Linphone UI (GTK version) + + brew install cairo --without-x11 + brew install gtk+ --without-x11 + brew install gettext gtk-mac-integration libsoup + #readline is required from linphonec.c otherwise compilation will fail + brew link readline --force ### Building Linphone @@ -58,7 +69,7 @@ The next pieces need to be compiled manually. export CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" export LDFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5 -Wl,-headerpad_max_install_names -Wl,-read_only_relocs -Wl,suppress" -* Install libantlr3c (library used by belle-sip for parsing) +* (MacPorts only) Install libantlr3c (library used by belle-sip for parsing) git clone -b linphone git://git.linphone.org/antlr3.git cd antlr3/runtime/C @@ -112,11 +123,12 @@ The libvpx build isn't able to produce dual architecture files. To workaround th If you got the source code from git, run `./autogen.sh` first. Then or otherwise, : - PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make + PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make * Install on the system sudo make install + You are done. ### Generate portable bundle @@ -126,7 +138,7 @@ If you want to generate a portable bundle, then install `gtk-mac-bundler`: git clone https://github.com/jralls/gtk-mac-bundler.git cd gtk-mac-bundler && make install export PATH=$PATH:~/.local/bin - #make this dummy charset.alias file for the bundler to be happy: + # make this dummy charset.alias file for the bundler to be happy: sudo touch /opt/local/lib/charset.alias The bundler file in `build/MacOS/linphone.bundle` expects some plugins to be installed in `/opt/local/lib/mediastreamer/plugins`. diff --git a/build/android/Android.mk b/build/android/Android.mk index adc78454c..9690877ee 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -40,6 +40,7 @@ LOCAL_SRC_FILES := \ bellesip_sal/sal_op_publish.c \ bellesip_sal/sal_op_registration.c \ bellesip_sal/sal_sdp.c \ + buffer.c \ callbacks.c \ call_log.c \ call_params.c \ diff --git a/build/wp8/LibLinphone.vcxproj b/build/wp8/LibLinphone.vcxproj index 6a3d8e034..7e692e325 100644 --- a/build/wp8/LibLinphone.vcxproj +++ b/build/wp8/LibLinphone.vcxproj @@ -110,6 +110,7 @@ + @@ -145,6 +146,7 @@ + diff --git a/cmake/FindNotify.cmake b/cmake/FindNotify.cmake index 26bd5bac1..f77e0885f 100644 --- a/cmake/FindNotify.cmake +++ b/cmake/FindNotify.cmake @@ -22,9 +22,9 @@ # # - Find the notify include file and library # -# NOTIFY_FOUND - system has linphone -# NOTIFY_INCLUDE_DIRS - the linphone include directory -# NOTIFY_LIBRARIES - The libraries needed to use linphone +# NOTIFY_FOUND - system has libnotify +# NOTIFY_INCLUDE_DIRS - the libnotify include directory +# NOTIFY_LIBRARIES - The libraries needed to use libnotify set(_NOTIFY_ROOT_PATHS ${WITH_NOTIFY} diff --git a/cmake/FindSoup.cmake b/cmake/FindSoup.cmake new file mode 100644 index 000000000..82735a317 --- /dev/null +++ b/cmake/FindSoup.cmake @@ -0,0 +1,63 @@ +############################################################################ +# FindSoup.cmake +# Copyright (C) 2014 Belledonne Communications, Grenoble France +# +############################################################################ +# +# 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. +# +############################################################################ +# +# - Find the soup include file and library +# +# SOUP_FOUND - system has libsoup +# SOUP_INCLUDE_DIRS - the libsoup include directory +# SOUP_LIBRARIES - The libraries needed to use libsoup + +find_package(GTK2 2.18 REQUIRED gtk) + +set(_SOUP_ROOT_PATHS + ${WITH_SOUP} + ${CMAKE_INSTALL_PREFIX} +) + +find_path(SOUP_INCLUDE_DIRS + NAMES libsoup/soup.h + HINTS _SOUP_ROOT_PATHS + PATH_SUFFIXES include/libsoup-2.4 +) + +if(SOUP_INCLUDE_DIRS) + set(HAVE_LIBSOUP_SOUP_H 1) + list(APPEND SOUP_INCLUDE_DIRS ${GTK2_INCLUDE_DIRS}) +endif() + +find_library(SOUP_LIBRARIES + NAMES soup-2.4 + HINTS ${_SOUP_ROOT_PATHS} + PATH_SUFFIXES bin lib +) + +if(SOUP_LIBRARIES) + list(APPEND SOUP_LIBRARIES ${GTK2_LIBRARIES}) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Soup + DEFAULT_MSG + SOUP_INCLUDE_DIRS SOUP_LIBRARIES HAVE_LIBSOUP_SOUP_H +) + +mark_as_advanced(SOUP_INCLUDE_DIRS SOUP_LIBRARIES HAVE_LIBSOUP_SOUP_H) diff --git a/config.h.cmake b/config.h.cmake index 9690bff27..0d442cb36 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -36,6 +36,7 @@ #define PACKAGE_DATA_DIR "${PACKAGE_DATA_DIR}" #define PACKAGE_SOUND_DIR "${PACKAGE_SOUND_DIR}" +#cmakedefine BUILD_WIZARD #cmakedefine HAVE_NOTIFY4 #cmakedefine HAVE_CU_GET_SUITE 1 #cmakedefine HAVE_CU_CURSES 1 \ No newline at end of file diff --git a/configure.ac b/configure.ac index 94aced0f9..7fa090c03 100644 --- a/configure.ac +++ b/configure.ac @@ -158,13 +158,16 @@ dnl AC_CHECK_LIB(intl,libintl_gettext) AC_CHECK_FUNCS([get_current_dir_name strndup stpcpy] ) AC_ARG_ENABLE(x11, - [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=no)])], + [AS_HELP_STRING([--disable-x11], [Disable X11 support (default=yes for MacOSX, no otherwise)])], [case "${enableval}" in yes) enable_x11=true ;; no) enable_x11=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;; esac], - [enable_x11=true] + [case "$target_os" in + *darwin*) enable_x11=false ;; #disable x11 on MacOS by default + *) enable_x11=true ;; + esac] ) dnl conditional build of LDAP support @@ -792,8 +795,6 @@ AC_ARG_ENABLE(msg-storage, [enable_msg_storage=auto] ) -AM_CONDITIONAL(BUILD_MSG_STORAGE, test x$enable_msg_storage = xtrue) - if test x$enable_msg_storage != xfalse; then PKG_CHECK_MODULES(SQLITE3,[sqlite3 >= 3.6.0],[found_sqlite=yes],[found_sqlite=no]) if test "$found_sqlite" = "no"; then @@ -802,6 +803,7 @@ if test x$enable_msg_storage != xfalse; then fi if test "$found_sqlite" = "yes"; then SQLITE3_CFLAGS+="-DMSG_STORAGE_ENABLED" + enable_msg_storage=true else if test x$enable_msg_storage = xtrue; then AC_MSG_ERROR([sqlite3, required for message storage, not found]) @@ -813,7 +815,7 @@ if test x$enable_msg_storage != xfalse; then AC_SUBST(SQLITE3_LIBS) fi - +AM_CONDITIONAL(BUILD_MSG_STORAGE, test x$enable_msg_storage = xtrue) PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.3.1]) diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index 92689dcb8..541e0d670 100644 --- a/coreapi/CMakeLists.txt +++ b/coreapi/CMakeLists.txt @@ -42,6 +42,7 @@ set(SOURCE_FILES bellesip_sal/sal_op_publish.c bellesip_sal/sal_op_registration.c bellesip_sal/sal_sdp.c + buffer.c callbacks.c call_log.c call_params.c @@ -96,6 +97,9 @@ if(ENABLE_TUNNEL) else() list(APPEND SOURCE_FILES linphone_tunnel_stubs.c) endif() +if(ENABLE_ASSISTANT) + list(APPEND SOURCE_FILES sipwizard.c) +endif() set(GENERATED_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/liblinphone_gitversion.h @@ -123,6 +127,9 @@ endif() if(ENABLE_TUNNEL) list(APPEND LIBS ${TUNNEL_LIBRARIES}) endif() +if(ENABLE_ASSISTANT) + list(APPEND LIBS ${SOUP_LIBRARIES}) +endif() if(WIN32) list(APPEND LIBS shlwapi) endif() @@ -153,6 +160,7 @@ install(TARGETS linphone set(HEADER_FILES + buffer.h call_log.h call_params.h content.h diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index f7d163749..160d0a420 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -25,6 +25,7 @@ CLEANFILES=$(GITVERSION_FILE) linphone_includedir=$(includedir)/linphone linphone_include_HEADERS=\ + buffer.h \ call_log.h \ call_params.h \ content.h \ @@ -44,6 +45,7 @@ lib_LTLIBRARIES=liblinphone.la liblinphone_la_SOURCES=\ address.c \ authentication.c \ + buffer.c \ callbacks.c \ call_log.c \ call_params.c \ diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 19753f6c1..4208bd105 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -1077,27 +1077,27 @@ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const * Parse a directory to get a certificate with the given subject common name * */ -void sal_certificates_chain_parse_directory(unsigned char **certificate_pem, unsigned char **key_pem, unsigned char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { +void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint) { belle_sip_certificates_chain_t *certificate = NULL; belle_sip_signing_key_t *key = NULL; *certificate_pem = NULL; *key_pem = NULL; if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key, (belle_sip_certificate_raw_format_t)format) == 0) { - *certificate_pem = belle_sip_get_certificates_pem(certificate); - *key_pem = belle_sip_get_key_pem(key); + *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); + *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Retrieve certificate with CN=%s successful\n", subject); } else { if (generate_certificate == TRUE) { if ( belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) { - *certificate_pem = belle_sip_get_certificates_pem(certificate); - *key_pem = belle_sip_get_key_pem(key); + *certificate_pem = belle_sip_certificates_chain_get_pem(certificate); + *key_pem = belle_sip_signing_key_get_pem(key); ms_message("Generate self-signed certificate with CN=%s successful\n", subject); } } } /* generate the fingerprint as described in RFC4572 if needed */ if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) { - *fingerprint = belle_sip_generate_certificate_fingerprint(certificate); + *fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate); } /* free key and certificate */ diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index c2e9a4a64..921184cf6 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -62,16 +62,19 @@ static void sdp_process(SalOp *h){ strcpy(h->result->addr,h->base.remote_media->addr); h->result->bandwidth=h->base.remote_media->bandwidth; - for(i=0;iresult);++i){ - strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); - h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; - h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; - h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; - strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr); - h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port; + for(i=0;iresult->nb_streams;++i){ + /*copy back parameters from remote description that we need in our result description*/ + if (h->result->streams[i].rtp_port!=0){ /*if stream was accepted*/ + strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr); + h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime; + h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth; + h->result->streams[i].rtp_port=h->base.remote_media->streams[i].rtp_port; + strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_addr); + h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port; - if ((h->result->streams[i].proto == SalProtoRtpSavpf) || (h->result->streams[i].proto == SalProtoRtpSavp)) { - h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; + if ((h->result->streams[i].proto == SalProtoRtpSavpf) || (h->result->streams[i].proto == SalProtoRtpSavp)) { + h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; + } } } } diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 6023ef7e4..8bade6e7f 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -252,10 +252,8 @@ static void stream_description_to_sdp ( belle_sdp_session_description_t *session } belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("fingerprint",stream->dtls_fingerprint)); } - - /*belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute));*/ + belle_sdp_media_description_add_attribute(media_desc, belle_sdp_attribute_create("ssrc",ssrc_attribute)); /* truc de Jehan a virer? */ ms_free(ssrc_attribute); - } switch ( stream->dir ) { @@ -771,13 +769,13 @@ static SalStreamDescription * sdp_to_stream_description(SalMediaDescription *md, stream->dtls_role = SalDtlsRoleIsServer; } - if (stream->dtls_role != SalDtlsRoleInvalid) { + if (stream->dtls_role != SalDtlsRoleInvalid || md->dtls_role != SalDtlsRoleInvalid) { attribute=belle_sdp_media_description_get_attribute(media_desc,"fingerprint"); if (attribute && (value=belle_sdp_attribute_get_value(attribute))!=NULL){ - strncpy(stream->dtls_fingerprint, value, strlen(value)+1); + strncpy(stream->dtls_fingerprint, value, sizeof(stream->dtls_fingerprint)-1); } else { /* no valid stream attributes, get them from session */ - stream->dtls_role = md->dtls_role; + if (stream->dtls_role == SalDtlsRoleInvalid) stream->dtls_role = md->dtls_role; strncpy(stream->dtls_fingerprint, md->dtls_fingerprint, strlen(md->dtls_fingerprint)+1); } } @@ -858,17 +856,11 @@ int sdp_to_media_description ( belle_sdp_session_description_t *session_desc, S } } - if (desc->dtls_role != SalDtlsRoleInvalid) { - value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); - if (value){ - strncpy(desc->dtls_fingerprint, value, strlen(value)+1); - } else { - desc->dtls_role = SalDtlsRoleInvalid; - } + value=belle_sdp_session_description_get_attribute_value(session_desc,"fingerprint"); + if (value){ + strncpy(desc->dtls_fingerprint, value, sizeof(desc->dtls_fingerprint)-1); } - - /* Get ICE remote ufrag and remote pwd, and ice_lite flag */ value=belle_sdp_session_description_get_attribute_value(session_desc,"ice-ufrag"); if (value) strncpy(desc->ice_ufrag, value, sizeof(desc->ice_ufrag) - 1); diff --git a/coreapi/buffer.c b/coreapi/buffer.c new file mode 100644 index 000000000..9db65798e --- /dev/null +++ b/coreapi/buffer.c @@ -0,0 +1,106 @@ +/* +linphone +Copyright (C) 2010-2014 Belledonne Communications SARL + +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 "linphonecore.h" +#include "private.h" + + + +static void linphone_buffer_destroy(LinphoneBuffer *buffer) { + if (buffer->content) belle_sip_free(buffer->content); +} + +BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneBuffer); + +BELLE_SIP_INSTANCIATE_VPTR(LinphoneBuffer, belle_sip_object_t, + (belle_sip_object_destroy_t)linphone_buffer_destroy, + NULL, // clone + NULL, // marshal + TRUE +); + + +LinphoneBuffer * linphone_buffer_new(void) { + LinphoneBuffer *buffer = belle_sip_object_new(LinphoneBuffer); + belle_sip_object_ref(buffer); + return buffer; +} + +LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size) { + LinphoneBuffer *buffer = linphone_buffer_new(); + linphone_buffer_set_content(buffer, data, size); + return buffer; +} + +LinphoneBuffer * linphone_buffer_new_from_string(const char *data) { + LinphoneBuffer *buffer = linphone_buffer_new(); + linphone_buffer_set_string_content(buffer, data); + return buffer; +} + +LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer) { + belle_sip_object_ref(buffer); + return buffer; +} + +void linphone_buffer_unref(LinphoneBuffer *buffer) { + belle_sip_object_unref(buffer); +} + +void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer) { + return buffer->user_data; +} + +void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud) { + buffer->user_data = ud; +} + +const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer) { + return buffer->content; +} + +void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size) { + buffer->size = size; + if (buffer->content) belle_sip_free(buffer->content); + buffer->content = belle_sip_malloc(size + 1); + memcpy(buffer->content, content, size); + ((char *)buffer->content)[size] = '\0'; +} + +const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer) { + return (const char *)buffer->content; +} + +void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content) { + buffer->size = strlen(content); + if (buffer->content) belle_sip_free(buffer->content); + buffer->content = (uint8_t *)belle_sip_strdup(content); +} + +size_t linphone_buffer_get_size(const LinphoneBuffer *buffer) { + return buffer->size; +} + +void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size) { + buffer->size = size; +} + +bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer) { + return (buffer->size == 0) ? TRUE : FALSE; +} diff --git a/coreapi/buffer.h b/coreapi/buffer.h new file mode 100644 index 000000000..0d22e9ad2 --- /dev/null +++ b/coreapi/buffer.h @@ -0,0 +1,147 @@ +/* +buffer.h +Copyright (C) 2010-2014 Belledonne Communications SARL + +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 LINPHONE_BUFFER_H_ +#define LINPHONE_BUFFER_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @addtogroup misc + * @{ + */ + +/** + * The LinphoneContent object representing a data buffer. +**/ +typedef struct _LinphoneBuffer LinphoneBuffer; + + +/** + * Create a new empty LinphoneBuffer object. + * @return A new LinphoneBuffer object. + */ +LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new(void); + +/** + * Create a new LinphoneBuffer object from existing data. + * @param[in] data The initial data to store in the LinphoneBuffer. + * @param[in] size The size of the initial data to stroe in the LinphoneBuffer. + * @return A new LinphoneBuffer object. + */ +LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new_from_data(const uint8_t *data, size_t size); + +/** + * Create a new LinphoneBuffer object from a string. + * @param[in] data The initial string content of the LinphoneBuffer. + * @return A new LinphoneBuffer object. + */ +LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_new_from_string(const char *data); + +/** + * Acquire a reference to the buffer. + * @param[in] buffer LinphoneBuffer object. + * @return The same LinphoneBuffer object. +**/ +LINPHONE_PUBLIC LinphoneBuffer * linphone_buffer_ref(LinphoneBuffer *buffer); + +/** + * Release reference to the buffer. + * @param[in] buffer LinphoneBuffer object. +**/ +LINPHONE_PUBLIC void linphone_buffer_unref(LinphoneBuffer *buffer); + +/** + * Retrieve the user pointer associated with the buffer. + * @param[in] buffer LinphoneBuffer object. + * @return The user pointer associated with the buffer. +**/ +LINPHONE_PUBLIC void *linphone_buffer_get_user_data(const LinphoneBuffer *buffer); + +/** + * Assign a user pointer to the buffer. + * @param[in] buffer LinphoneBuffer object. + * @param[in] ud The user pointer to associate with the buffer. +**/ +LINPHONE_PUBLIC void linphone_buffer_set_user_data(LinphoneBuffer *buffer, void *ud); + +/** + * Get the content of the data buffer. + * @param[in] buffer LinphoneBuffer object. + * @return The content of the data buffer. + */ +LINPHONE_PUBLIC const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer); + +/** + * Set the content of the data buffer. + * @param[in] buffer LinphoneBuffer object. + * @param[in] content The content of the data buffer. + * @param[in] size The size of the content of the data buffer. + */ +LINPHONE_PUBLIC void linphone_buffer_set_content(LinphoneBuffer *buffer, const uint8_t *content, size_t size); + +/** + * Get the string content of the data buffer. + * @param[in] buffer LinphoneBuffer object + * @return The string content of the data buffer. + */ +LINPHONE_PUBLIC const char * linphone_buffer_get_string_content(const LinphoneBuffer *buffer); + +/** + * Set the string content of the data buffer. + * @param[in] buffer LinphoneBuffer object. + * @param[in] content The string content of the data buffer. + */ +LINPHONE_PUBLIC void linphone_buffer_set_string_content(LinphoneBuffer *buffer, const char *content); + +/** + * Get the size of the content of the data buffer. + * @param[in] buffer LinphoneBuffer object. + * @return The size of the content of the data buffer. + */ +LINPHONE_PUBLIC size_t linphone_buffer_get_size(const LinphoneBuffer *buffer); + +/** + * Set the size of the content of the data buffer. + * @param[in] buffer LinphoneBuffer object + * @param[in] size The size of the content of the data buffer. + */ +LINPHONE_PUBLIC void linphone_buffer_set_size(LinphoneBuffer *buffer, size_t size); + +/** + * Tell whether the LinphoneBuffer is empty. + * @param[in] buffer LinphoneBuffer object + * @return A boolean value telling whether the LinphoneBuffer is empty or not. + */ +LINPHONE_PUBLIC bool_t linphone_buffer_is_empty(const LinphoneBuffer *buffer); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LINPHONE_CONTENT_H_ */ diff --git a/coreapi/call_log.c b/coreapi/call_log.c index 1449c1920..d1750df56 100644 --- a/coreapi/call_log.c +++ b/coreapi/call_log.c @@ -217,7 +217,7 @@ char * linphone_call_log_to_str(LinphoneCallLog *cl){ default: status=_("unknown"); } - tmp=ortp_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"), + tmp=ms_strdup_printf(_("%s at %s\nFrom: %s\nTo: %s\nStatus: %s\nDuration: %i mn %i sec\n"), (cl->dir==LinphoneCallIncoming) ? _("Incoming call") : _("Outgoing call"), cl->start_date, from, diff --git a/coreapi/chat.c b/coreapi/chat.c index 7bec11b01..4bdf5e133 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -246,7 +246,13 @@ static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_ if (offsetfile_transfer_information)){ /* get data from call back */ if (linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)) { - linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, buf, size); + LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, offset, *size); + if (lb == NULL) *size = 0; + else { + *size = linphone_buffer_get_size(lb); + memcpy(buffer, linphone_buffer_get_content(lb), *size); + linphone_buffer_unref(lb); + } } else { /* Legacy */ linphone_core_notify_file_transfer_send(lc, chatMsg, chatMsg->file_transfer_information, buf, size); @@ -287,6 +293,11 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co /* create a user body handler to take care of the file and add the content disposition and content-type headers */ if (msg->file_transfer_filepath != NULL) { first_part_bh=(belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath,NULL,msg); + } else if (linphone_content_get_buffer(msg->file_transfer_information) != NULL) { + first_part_bh=(belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer( + linphone_content_get_buffer(msg->file_transfer_information), + linphone_content_get_size(msg->file_transfer_information), + NULL,msg); } else { first_part_bh=(belle_sip_body_handler_t *)belle_sip_user_body_handler_new(linphone_content_get_size(msg->file_transfer_information),NULL,NULL,linphone_chat_message_file_transfer_on_send_body,msg); } @@ -578,8 +589,13 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM belle_http_request_listener_t *l; belle_generic_uri_t *uri; belle_http_request_t *req; + const char *transfer_server = linphone_core_get_file_transfer_server(cr->lc); - uri=belle_generic_uri_parse(linphone_core_get_file_transfer_server(cr->lc)); + if (transfer_server == NULL) { + ms_warning("Cannot send file transfer message: no file transfer server configured."); + return; + } + uri=belle_generic_uri_parse(transfer_server); req=belle_http_request_create("POST", uri, @@ -1205,7 +1221,9 @@ static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t return; } if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) { - linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, (char *)buffer, size); + LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size); + linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb); + linphone_buffer_unref(lb); } else { /* Legacy: call back given by application level */ linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, (char *)buffer, size); @@ -1262,10 +1280,12 @@ static void linphone_chat_process_response_headers_from_get_file(void *data, con (belle_sip_body_handler_t*)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress,on_recv_body,NULL,message) ); } else { - belle_sip_message_set_body_handler( - (belle_sip_message_t *)event->response, - (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(message->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, message) - ); + belle_sip_body_handler_t *bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(message->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, message); + if (belle_sip_body_handler_get_size(bh) == 0) { + /* If the size of the body has not been initialized from the file stat, use the one from the file_transfer_information. */ + belle_sip_body_handler_set_size(bh, body_size); + } + belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, bh); } } } @@ -1279,7 +1299,9 @@ static void linphone_chat_process_response_from_get_file(void *data, const belle LinphoneCore *lc = chatMsg->chat_room->lc; /* file downloaded succesfully, call again the callback with size at zero */ if (linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)) { - linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, NULL, 0); + LinphoneBuffer *lb = linphone_buffer_new(); + linphone_chat_message_cbs_get_file_transfer_recv(chatMsg->callbacks)(chatMsg, chatMsg->file_transfer_information, lb); + linphone_buffer_unref(lb); } else { linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, NULL, 0); } diff --git a/coreapi/content.c b/coreapi/content.c index 74d04ebb7..332789d2a 100644 --- a/coreapi/content.c +++ b/coreapi/content.c @@ -115,7 +115,7 @@ void linphone_content_set_buffer(LinphoneContent *content, const void *buffer, s ((char *)content->lcp.data)[size] = '\0'; } -char * linphone_content_get_string_buffer(const LinphoneContent *content) { +const char * linphone_content_get_string_buffer(const LinphoneContent *content) { return (char *)content->lcp.data; } diff --git a/coreapi/content.h b/coreapi/content.h index 400f5f5fb..e3e0166ce 100644 --- a/coreapi/content.h +++ b/coreapi/content.h @@ -165,7 +165,7 @@ LINPHONE_PUBLIC void linphone_content_set_buffer(LinphoneContent *content, const * @param[in] content LinphoneContent object * @return The string content data buffer. */ -LINPHONE_PUBLIC char * linphone_content_get_string_buffer(const LinphoneContent *content); +LINPHONE_PUBLIC const char * linphone_content_get_string_buffer(const LinphoneContent *content); /** * Set the string content data buffer. diff --git a/coreapi/help/filetransfer.c b/coreapi/help/filetransfer.c index c592c501b..570e22692 100644 --- a/coreapi/help/filetransfer.c +++ b/coreapi/help/filetransfer.c @@ -63,56 +63,37 @@ static void file_transfer_progress_indication(LinphoneChatMessage *message, cons /** * function invoked when a file transfer is received. **/ -static void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size){ +static void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const LinphoneBuffer *buffer){ FILE* file=NULL; if (!linphone_chat_message_get_user_data(message)) { /*first chunk, creating file*/ file = fopen("receive_file.dump","wb"); linphone_chat_message_set_user_data(message,(void*)file); /*store fd for next chunks*/ - } else { - /*next chunk*/ - file = (FILE*)linphone_chat_message_get_user_data(message); + } - if (size==0) { - - printf("File transfert completed\n"); - linphone_chat_room_destroy(linphone_chat_message_get_chat_room(message)); - linphone_chat_message_destroy(message); - fclose(file); - running=FALSE; - } else { /* store content on a file*/ - if (fwrite(buff,size,1,file)==-1){ - ms_warning("file_transfer_received() write failed: %s",strerror(errno)); - } + file = (FILE*)linphone_chat_message_get_user_data(message); + if (linphone_buffer_is_empty(buffer)) { + printf("File transfert completed\n"); + linphone_chat_room_destroy(linphone_chat_message_get_chat_room(message)); + linphone_chat_message_destroy(message); + fclose(file); + running=FALSE; + } else { /* store content on a file*/ + if (fwrite(linphone_buffer_get_content(buffer),linphone_buffer_get_size(buffer),1,file)==-1){ + ms_warning("file_transfer_received() write failed: %s",strerror(errno)); } } } char big_file [128000]; + /* * function called when the file transfer is initiated. file content should be feed into object LinphoneContent * */ -static void file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size){ - int offset=-1; - - if (!linphone_chat_message_get_user_data(message)) { - /*first chunk*/ - offset=0; - } else { - /*subsequent chunk*/ - offset = (int)((long)(linphone_chat_message_get_user_data(message))&0x00000000FFFFFFFF); - } - *size = MIN(*size,sizeof(big_file)-offset); /*updating content->size with minimun between remaining data and requested size*/ - - if (*size==0) { - /*end of file*/ - return; - } - memcpy(buff,big_file+offset,*size); - - /*store offset for next chunk*/ - linphone_chat_message_set_user_data(message,(void*)(offset+*size)); - +static LinphoneBuffer * file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size){ + size_t size_to_send = MIN(size, sizeof(big_file) - offset); + if (size == 0) return linphone_buffer_new(); /*end of file*/ + return linphone_buffer_new_from_data((uint8_t *)big_file + offset, size_to_send); } /* diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index a1db1a25d..ef5dd2fd1 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -30,6 +30,7 @@ import org.linphone.core.LinphoneContent; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; @@ -322,4 +323,17 @@ public class TutorialBuddyStatus implements LinphoneListener { // TODO Auto-generated method stub return 0; } + + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index eff31682d..046c23ada 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -30,6 +30,7 @@ import org.linphone.core.LinphoneContent; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; @@ -241,5 +242,18 @@ public class TutorialChatRoom implements LinphoneListener, LinphoneChatMessage.S return 0; } + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 8806b8da1..2e2e50b0b 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -30,6 +30,7 @@ import org.linphone.core.LinphoneContent; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; @@ -243,5 +244,18 @@ public class TutorialHelloWorld implements LinphoneListener { return 0; } + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } + + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } + } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index afc2fe16c..ce985d288 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -30,6 +30,7 @@ import org.linphone.core.LinphoneContent; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore.EcCalibratorStatus; import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.LogCollectionUploadState; import org.linphone.core.LinphoneCore.RegistrationState; import org.linphone.core.LinphoneCore.RemoteProvisioningState; import org.linphone.core.LinphoneCoreException; @@ -274,6 +275,16 @@ public class TutorialRegistration implements LinphoneListener { return 0; } + @Override + public void uploadProgressIndication(LinphoneCore lc, int offset, int total) { + // TODO Auto-generated method stub + + } - + @Override + public void uploadStateChanged(LinphoneCore lc, + LogCollectionUploadState state, String info) { + // TODO Auto-generated method stub + + } } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 0f2f02383..bc7a8c9ee 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/mssndcard.h" static const char EC_STATE_STORE[] = ".linphone.ecstate"; +#define EC_STATE_MAX_LEN 1048576 // 1Mo static void linphone_call_stats_uninit(LinphoneCallStats *stats); @@ -785,7 +786,7 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, c if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionDTLS; } - if ((sal_media_description_has_srtp(md) == TRUE) && (media_stream_srtp_supported() == TRUE)) { + if ((sal_media_description_has_srtp(md) == TRUE) && (ms_srtp_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionSRTP; } } @@ -1631,7 +1632,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ rtp_session_set_symmetric_rtp(audiostream->ms.sessions.rtp_session,linphone_core_symmetric_rtp_enabled(lc)); if (call->params->media_encryption==LinphoneMediaEncryptionDTLS) { MSDtlsSrtpParams params; - unsigned char *certificate, *key; + char *certificate, *key; memset(¶ms,0,sizeof(MSDtlsSrtpParams)); /* TODO : search for a certificate with CNAME=sip uri(retrieved from variable me) or defautl celui par default linphone-dtls-default-identity */ /* This will parse the directory to find a matching fingerprint or generate it if not found */ @@ -1684,13 +1685,15 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ audio_stream_enable_gain_control(audiostream,TRUE); if (linphone_core_echo_cancellation_enabled(lc)){ int len,delay,framesize; - char *statestr=lp_config_read_relative_file(lc->config, EC_STATE_STORE); len=lp_config_get_int(lc->config,"sound","ec_tail_len",0); delay=lp_config_get_int(lc->config,"sound","ec_delay",0); framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0); audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize); - if (statestr && audiostream->ec){ - ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr); + if (audiostream->ec) { + char *statestr=ms_malloc0(EC_STATE_MAX_LEN); + if (lp_config_read_relative_file(lc->config, EC_STATE_STORE, statestr, EC_STATE_MAX_LEN) == 0) { + ms_filter_call_method(audiostream->ec, MS_ECHO_CANCELLER_SET_STATE_STRING, statestr); + } ms_free(statestr); } } @@ -1754,6 +1757,7 @@ void linphone_call_init_video_stream(LinphoneCall *call){ if (display_filter != NULL) video_stream_set_display_filter_name(call->videostream,display_filter); video_stream_set_event_callback(call->videostream,video_stream_event_cb, call); + if (lc->rtptf){ RtpTransport *meta_rtp; RtpTransport *meta_rtcp; @@ -2076,7 +2080,9 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b snprintf(rtcp_tool,sizeof(rtcp_tool)-1,"%s-%s",linphone_core_get_user_agent_name(),linphone_core_get_user_agent_version()); stream = sal_media_description_find_best_stream(call->resultdesc, SalAudio); + ms_message("DTLS: call_start_audio_stream, stream is %s", stream==NULL?"NULL":"not NULL"); if (stream && stream->dir!=SalStreamInactive && stream->rtp_port!=0){ + ms_message("DTLS: call_start_audio_stream : we have stream and all stuff to start it"); playcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard; captcard=lc->sound_conf.capt_sndcard; @@ -2085,6 +2091,7 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b call->audio_profile=make_profile(call,call->resultdesc,stream,&used_pt); if (used_pt!=-1){ + ms_message("DTLS: call_start_audio_stream : we made a profile and have used_pt != -1"); call->current_params->audio_codec = rtp_profile_get_payload(call->audio_profile, used_pt); if (playcard==NULL) { ms_warning("No card defined for playback !"); @@ -2143,8 +2150,8 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, bool_t muted, b crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, stream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key(&call->audiostream->ms,stream->crypto[0].algo,stream->crypto[0].master_key, TRUE); - media_stream_set_srtp_send_key(&call->audiostream->ms,stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key, TRUE); + media_stream_set_srtp_recv_key_b64(&call->audiostream->ms,stream->crypto[0].algo,stream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&call->audiostream->ms,stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } else { ms_warning("Failed to find local crypto algo with tag: %d", stream->crypto_local_tag); } @@ -2262,8 +2269,8 @@ static void linphone_call_start_video_stream(LinphoneCall *call, bool_t all_inpu if (sal_stream_description_has_srtp(vstream) == TRUE) { int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag); if (crypto_idx >= 0) { - media_stream_set_srtp_recv_key(&call->videostream->ms,vstream->crypto[0].algo,vstream->crypto[0].master_key, TRUE); - media_stream_set_srtp_send_key(&call->videostream->ms,vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key, TRUE); + media_stream_set_srtp_recv_key_b64(&call->videostream->ms,vstream->crypto[0].algo,vstream->crypto[0].master_key); + media_stream_set_srtp_send_key_b64(&call->videostream->ms,vstream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); } } configure_rtp_session_for_rtcp_xr(lc, call, SalVideo); @@ -2326,6 +2333,8 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut if (call->audiostream!=NULL) { linphone_call_start_audio_stream(call,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); + } else { + ms_warning("DTLS no audio stream!"); } call->current_params->has_video=FALSE; if (call->videostream!=NULL) { @@ -2397,9 +2406,9 @@ static bool_t update_stream_crypto_params(LinphoneCall *call, const SalStreamDes int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, new_stream->crypto_local_tag); if (crypto_idx >= 0) { if (call->localdesc_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_KEYS_CHANGED) - media_stream_set_srtp_send_key(ms,new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key, TRUE); + media_stream_set_srtp_send_key_b64(ms,new_stream->crypto[0].algo,local_st_desc->crypto[crypto_idx].master_key); if (strcmp(old_stream->crypto[0].master_key,new_stream->crypto[0].master_key)!=0){ - media_stream_set_srtp_recv_key(ms,new_stream->crypto[0].algo,new_stream->crypto[0].master_key,TRUE); + media_stream_set_srtp_recv_key_b64(ms,new_stream->crypto[0].algo,new_stream->crypto[0].master_key); } return TRUE; } else { @@ -2906,14 +2915,17 @@ void linphone_call_stop_recording(LinphoneCall *call){ **/ static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *vs){ - call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as!=NULL) ? (media_stream_get_down_bw(as)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as!=NULL) ? (media_stream_get_up_bw(as)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs!=NULL) ? (media_stream_get_down_bw(vs)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs!=NULL) ? (media_stream_get_up_bw(vs)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_download_bandwidth=(as!=NULL) ? (media_stream_get_rtcp_down_bw(as)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_upload_bandwidth=(as!=NULL) ? (media_stream_get_rtcp_up_bw(as)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_download_bandwidth=(vs!=NULL) ? (media_stream_get_rtcp_down_bw(vs)*1e-3) : 0; - call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_upload_bandwidth=(vs!=NULL) ? (media_stream_get_rtcp_up_bw(vs)*1e-3) : 0; + bool_t as_active = as ? (media_stream_get_state(as) == MSStreamStarted) : FALSE; + bool_t vs_active = vs ? (media_stream_get_state(vs) == MSStreamStarted) : FALSE; + + call->stats[LINPHONE_CALL_STATS_AUDIO].download_bandwidth=(as_active) ? (media_stream_get_down_bw(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_AUDIO].upload_bandwidth=(as_active) ? (media_stream_get_up_bw(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].download_bandwidth=(vs_active) ? (media_stream_get_down_bw(vs)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].upload_bandwidth=(vs_active) ? (media_stream_get_up_bw(vs)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_download_bandwidth=(as_active) ? (media_stream_get_rtcp_down_bw(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_AUDIO].rtcp_upload_bandwidth=(as_active) ? (media_stream_get_rtcp_up_bw(as)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_download_bandwidth=(vs_active) ? (media_stream_get_rtcp_down_bw(vs)*1e-3) : 0; + call->stats[LINPHONE_CALL_STATS_VIDEO].rtcp_upload_bandwidth=(vs_active) ? (media_stream_get_rtcp_up_bw(vs)*1e-3) : 0; ms_message("Bandwidth usage for call [%p]: audio[ rtp]=[d=%.1f,u=%.1f], video[ rtp]=[d=%.1f,u=%.1f] kbit/sec", call, diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 66bef51c8..057e3be32 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2131,7 +2131,7 @@ void linphone_core_set_use_rfc2833_for_dtmf(LinphoneCore *lc,bool_t use_rfc2833) /** * Returns the UDP port used by SIP. * - * Deprecated: use linphone_core_get_sip_transports() instead. + * @deprecated use linphone_core_get_sip_transports() instead. * @ingroup network_parameters **/ int linphone_core_get_sip_port(LinphoneCore *lc){ @@ -2299,7 +2299,7 @@ void linphone_core_get_sip_transports_used(LinphoneCore *lc, LCSipTransports *tr /** * Sets the UDP port to be used by SIP. * - * Deprecated: use linphone_core_set_sip_transports() instead. + * @deprecated use linphone_core_set_sip_transports() instead. * @ingroup network_parameters **/ void linphone_core_set_sip_port(LinphoneCore *lc,int port) @@ -3944,10 +3944,10 @@ int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call) linphone_core_notify_display_warning(lc,_("Could not pause the call")); } lc->current_call=NULL; - linphone_call_set_state(call,LinphoneCallPausing,"Pausing call"); linphone_core_notify_display_status(lc,_("Pausing the current call...")); if (call->audiostream || call->videostream) linphone_call_stop_media_streams (call); + linphone_call_set_state(call,LinphoneCallPausing,"Pausing call"); call->paused_by_app=FALSE; return 0; } @@ -5631,6 +5631,26 @@ MSVideoSize linphone_core_get_preview_video_size(const LinphoneCore *lc){ return lc->video_conf.preview_vsize; } +/** + * Returns the effective video size for the captured video as provided by the camera. + * When preview is disabled or not yet started, this function returns a zeroed video size. + * @see linphone_core_set_preview_video_size() + * @ingroup media_parameters + * @param lc the core + * @return a MSVideoSize +**/ +MSVideoSize linphone_core_get_current_preview_video_size(const LinphoneCore *lc){ + MSVideoSize ret={0}; +#ifndef VIDEO_ENABLED + ms_error("linphone_core_get_current_preview_video_size() fail. Support for video is disabled"); +#else + if (lc->previewstream){ + ret=video_preview_get_current_size(lc->previewstream); + } +#endif + return ret; +} + /** * Sets the preview video size by its name. See linphone_core_set_preview_video_size() for more information about this feature. * @@ -6416,20 +6436,20 @@ static void notify_soundcard_usage(LinphoneCore *lc, bool_t used){ void linphone_core_soundcard_hint_check( LinphoneCore* lc){ MSList* the_calls = lc->calls; LinphoneCall* call = NULL; - bool_t remaining_paused = FALSE; + bool_t dont_need_sound = TRUE; /* check if the remaining calls are paused */ while( the_calls ){ call = the_calls->data; - if( call->state == LinphoneCallPausing || call->state == LinphoneCallPaused ){ - remaining_paused = TRUE; + if( call->state != LinphoneCallPausing && call->state != LinphoneCallPaused ){ + dont_need_sound = FALSE; break; } the_calls = the_calls->next; } /* if no more calls or all calls are paused, we can free the soundcard */ - if ( (lc->calls==NULL || remaining_paused) && !lc->use_files){ + if ( (lc->calls==NULL || dont_need_sound) && !lc->use_files){ ms_message("Notifying soundcard that we don't need it anymore for calls."); notify_soundcard_usage(lc,FALSE); } @@ -6462,8 +6482,6 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call) } lc->calls = the_calls; - linphone_core_soundcard_hint_check(lc); - return 0; } @@ -6814,7 +6832,7 @@ const char *linphone_media_encryption_to_string(LinphoneMediaEncryption menc){ bool_t linphone_core_media_encryption_supported(const LinphoneCore *lc, LinphoneMediaEncryption menc){ switch(menc){ case LinphoneMediaEncryptionSRTP: - return media_stream_srtp_supported(); + return ms_srtp_supported(); case LinphoneMediaEncryptionDTLS: return ms_dtls_available(); case LinphoneMediaEncryptionZRTP: @@ -6829,7 +6847,7 @@ int linphone_core_set_media_encryption(LinphoneCore *lc, LinphoneMediaEncryption const char *type="none"; int ret=0; if (menc == LinphoneMediaEncryptionSRTP){ - if (!media_stream_srtp_supported()){ + if (!ms_srtp_supported()){ ms_warning("SRTP not supported by library."); type="none"; ret=-1; @@ -7074,7 +7092,7 @@ int linphone_payload_type_get_normal_bitrate(const LinphonePayloadType *pt) { return pt->normal_bitrate; } -char * linphone_payload_type_get_mime_type(const LinphonePayloadType *pt) { +const char * linphone_payload_type_get_mime_type(const LinphonePayloadType *pt) { return pt->mime_type; } @@ -7087,9 +7105,6 @@ LinphoneCoreVTable *linphone_core_v_table_new() { } void linphone_core_v_table_set_user_data(LinphoneCoreVTable *table, void *data) { - if (table->user_data) { - ms_free(table->user_data); - } table->user_data = data; } @@ -7098,9 +7113,6 @@ void* linphone_core_v_table_get_user_data(LinphoneCoreVTable *table) { } void linphone_core_v_table_destroy(LinphoneCoreVTable* table) { - if (table->user_data) { - ms_free(table->user_data); - } ms_free(table); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1fd683071..ee44009f0 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -257,7 +257,7 @@ LINPHONE_PUBLIC int linphone_payload_type_get_normal_bitrate(const LinphonePaylo * @param[in] pt LinphonePayloadType object * @return The mime type. */ -LINPHONE_PUBLIC char * linphone_payload_type_get_mime_type(const LinphonePayloadType *pt); +LINPHONE_PUBLIC const char * linphone_payload_type_get_mime_type(const LinphonePayloadType *pt); /** * Get the number of channels. @@ -367,12 +367,14 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy); #ifdef IN_LINPHONE +#include "buffer.h" #include "call_log.h" #include "call_params.h" #include "content.h" #include "event.h" #include "linphonefriend.h" #else +#include "linphone/buffer.h" #include "linphone/call_log.h" #include "linphone/call_params.h" #include "linphone/content.h" @@ -1351,30 +1353,24 @@ typedef void (*LinphoneChatMessageCbsMsgStateChangedCb)(LinphoneChatMessage* msg /** * File transfer receive callback prototype. This function is called by the core upon an incoming File transfer is started. This function may be call several time for the same file in case of large file. - * * @param message #LinphoneChatMessage message from which the body is received. * @param content #LinphoneContent incoming content information - * @param buff pointer to the received data - * @param size number of bytes to be read from buff. 0 means end of file. - * + * @param buffer #LinphoneBuffer holding the received data. Empty buffer means end of file. */ -typedef void (*LinphoneChatMessageCbsFileTransferRecvCb)(LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size); +typedef void (*LinphoneChatMessageCbsFileTransferRecvCb)(LinphoneChatMessage *message, const LinphoneContent* content, const LinphoneBuffer *buffer); /** - * File transfer send callback prototype. This function is called by the core upon an outgoing File transfer is started. This function is called until size is set to 0. - *
a #LinphoneContent with a size equal zero - * + * File transfer send callback prototype. This function is called by the core when an outgoing file transfer is started. This function is called until size is set to 0. * @param message #LinphoneChatMessage message from which the body is received. * @param content #LinphoneContent outgoing content - * @param buff pointer to the buffer where data chunk shall be written by the app - * @param size as input value, it represents the number of bytes expected by the framework. As output value, it means the number of bytes wrote by the application in the buffer. 0 means end of file. - * + * @param offset the offset in the file from where to get the data to be sent + * @param size the number of bytes expected by the framework + * @return A LinphoneBuffer object holding the data written by the application. An empty buffer means end of file. */ -typedef void (*LinphoneChatMessageCbsFileTransferSendCb)(LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size); +typedef LinphoneBuffer * (*LinphoneChatMessageCbsFileTransferSendCb)(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size); /** * File transfer progress indication callback prototype. - * * @param message #LinphoneChatMessage message from which the body is received. * @param content #LinphoneContent incoming content information * @param offset The number of bytes sent/received since the beginning of the transfer. @@ -2816,6 +2812,7 @@ LINPHONE_PUBLIC void linphone_core_set_preferred_video_size(LinphoneCore *lc, MS LINPHONE_PUBLIC void linphone_core_set_preview_video_size(LinphoneCore *lc, MSVideoSize vsize); LINPHONE_PUBLIC void linphone_core_set_preview_video_size_by_name(LinphoneCore *lc, const char *name); LINPHONE_PUBLIC MSVideoSize linphone_core_get_preview_video_size(const LinphoneCore *lc); +LINPHONE_PUBLIC MSVideoSize linphone_core_get_current_preview_video_size(const LinphoneCore *lc); LINPHONE_PUBLIC MSVideoSize linphone_core_get_preferred_video_size(const LinphoneCore *lc); /** diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 00e2c3692..76d68cc60 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -170,7 +170,7 @@ jobject getProxy(JNIEnv *env, LinphoneProxyConfig *proxy, jobject core){ jobject jobj=0; if (proxy!=NULL){ - jclass proxyClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneProxyConfigImpl")); + jclass proxyClass = (jclass)env->FindClass("org/linphone/core/LinphoneProxyConfigImpl"); jmethodID proxyCtrId = env->GetMethodID(proxyClass,"", "(Lorg/linphone/core/LinphoneCoreImpl;J)V"); void *up=linphone_proxy_config_get_user_data(proxy); @@ -188,7 +188,7 @@ jobject getProxy(JNIEnv *env, LinphoneProxyConfig *proxy, jobject core){ linphone_proxy_config_set_user_data(proxy,(void*)env->NewWeakGlobalRef(jobj)); } } - env->DeleteGlobalRef(proxyClass); + env->DeleteLocalRef(proxyClass); } return jobj; } @@ -197,7 +197,7 @@ jobject getCall(JNIEnv *env, LinphoneCall *call){ jobject jobj=0; if (call!=NULL){ - jclass callClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCallImpl")); + jclass callClass = (jclass)env->FindClass("org/linphone/core/LinphoneCallImpl"); jmethodID callCtrId = env->GetMethodID(callClass,"", "(J)V"); void *up=linphone_call_get_user_pointer(call); @@ -210,7 +210,7 @@ jobject getCall(JNIEnv *env, LinphoneCall *call){ }else{ jobj=(jobject)up; } - env->DeleteGlobalRef(callClass); + env->DeleteLocalRef(callClass); } return jobj; } @@ -219,7 +219,7 @@ jobject getChatMessage(JNIEnv *env, LinphoneChatMessage *msg){ jobject jobj = 0; if (msg != NULL){ - jclass chatMessageClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessageImpl")); + jclass chatMessageClass = (jclass)env->FindClass("org/linphone/core/LinphoneChatMessageImpl"); jmethodID chatMessageCtrId = env->GetMethodID(chatMessageClass,"", "(J)V"); void *up = linphone_chat_message_get_user_data(msg); @@ -231,7 +231,7 @@ jobject getChatMessage(JNIEnv *env, LinphoneChatMessage *msg){ } else { jobj = (jobject)up; } - env->DeleteGlobalRef(chatMessageClass); + env->DeleteLocalRef(chatMessageClass); } return jobj; } @@ -240,14 +240,14 @@ jobject getEvent(JNIEnv *env, LinphoneEvent *lev){ if (lev==NULL) return NULL; jobject jev=(jobject)linphone_event_get_user_data(lev); if (jev==NULL){ - jclass linphoneEventClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneEventImpl")); + jclass linphoneEventClass = (jclass)env->FindClass("org/linphone/core/LinphoneEventImpl"); jmethodID linphoneEventCtrId = env->GetMethodID(linphoneEventClass,"", "(J)V"); jev=env->NewObject(linphoneEventClass,linphoneEventCtrId,(jlong)linphone_event_ref(lev)); jev=env->NewGlobalRef(jev); linphone_event_set_user_data(lev,jev); - env->DeleteGlobalRef(linphoneEventClass); + env->DeleteLocalRef(linphoneEventClass); } return jev; } @@ -420,6 +420,19 @@ public: if (fileTransferRecvId) { vTable->file_transfer_recv = fileTransferRecv; } + + logCollectionUploadStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneCore$LogCollectionUploadState")); + logCollectionUploadStateFromIntId = env->GetStaticMethodID(logCollectionUploadStateClass, "fromInt", "(I)Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;"); + logCollectionUploadProgressId = env->GetMethodID(listenerClass, "uploadProgressIndication", "(Lorg/linphone/core/LinphoneCore;II)V"); + env->ExceptionClear(); + if (logCollectionUploadProgressId) { + vTable->log_collection_upload_progress_indication = logCollectionUploadProgressIndication; + } + logCollectionUploadStateId = env->GetMethodID(listenerClass, "uploadStateChanged", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneCore$LogCollectionUploadState;Ljava/lang/String;)V"); + env->ExceptionClear(); + if (logCollectionUploadStateId) { + vTable->log_collection_upload_state_changed = logCollectionUploadStateChange; + } chatMessageStateClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneChatMessage$State")); chatMessageStateFromIntId = env->GetStaticMethodID(chatMessageStateClass,"fromInt","(I)Lorg/linphone/core/LinphoneChatMessage$State;"); @@ -479,6 +492,7 @@ public: env->DeleteGlobalRef(linphoneEventClass); env->DeleteGlobalRef(subscriptionStateClass); env->DeleteGlobalRef(subscriptionDirClass); + env->DeleteGlobalRef(logCollectionUploadStateClass); } jobject core; jobject listener; @@ -566,6 +580,11 @@ public: jmethodID fileTransferSendId; jmethodID fileTransferRecvId; + jclass logCollectionUploadStateClass; + jmethodID logCollectionUploadStateId; + jmethodID logCollectionUploadStateFromIntId; + jmethodID logCollectionUploadProgressId; + LinphoneCoreVTable vTable; static void displayStatusCb(LinphoneCore *lc, const char *message) { @@ -577,7 +596,11 @@ public: } LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); - env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,message ? env->NewStringUTF(message) : NULL); + jstring msg = message ? env->NewStringUTF(message) : NULL; + env->CallVoidMethod(lcData->listener,lcData->displayStatusId,lcData->core,msg); + if (msg) { + env->DeleteLocalRef(msg); + } } static void authInfoRequested(LinphoneCore *lc, const char *realm, const char *username, const char *domain) { JNIEnv *env = 0; @@ -588,12 +611,24 @@ public: } LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + jstring r = realm ? env->NewStringUTF(realm) : NULL; + jstring u = username ? env->NewStringUTF(username) : NULL; + jstring d = domain ? env->NewStringUTF(domain) : NULL; env->CallVoidMethod(lcData->listener, lcData->authInfoRequestedId, lcData->core, - realm ? env->NewStringUTF(realm):NULL, - username ? env->NewStringUTF(username) : NULL, - domain ? env->NewStringUTF(domain) : NULL); + r, + u, + d); + if (r) { + env->DeleteLocalRef(r); + } + if (u) { + env->DeleteLocalRef(u); + } + if (d) { + env->DeleteLocalRef(d); + } } static void globalStateChange(LinphoneCore *lc, LinphoneGlobalState gstate,const char* message) { JNIEnv *env = 0; @@ -604,11 +639,15 @@ public: } LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + jstring msg = message ? env->NewStringUTF(message) : NULL; env->CallVoidMethod(lcData->listener ,lcData->globalStateId ,lcData->core ,env->CallStaticObjectMethod(lcData->globalStateClass,lcData->globalStateFromIntId,(jint)gstate), - message ? env->NewStringUTF(message) : NULL); + msg); + if (msg) { + env->DeleteLocalRef(msg); + } } static void registrationStateChange(LinphoneCore *lc, LinphoneProxyConfig* proxy,LinphoneRegistrationState state,const char* message) { JNIEnv *env = 0; @@ -620,12 +659,16 @@ public: } LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + jstring msg = message ? env->NewStringUTF(message) : NULL; env->CallVoidMethod(lcData->listener ,lcData->registrationStateId ,lcData->core ,(jproxy=getProxy(env,proxy,lcData->core)) ,env->CallStaticObjectMethod(lcData->registrationStateClass,lcData->registrationStateFromIntId,(jint)state), - message ? env->NewStringUTF(message) : NULL); + msg); + if (msg) { + env->DeleteLocalRef(msg); + } } static void callStateChange(LinphoneCore *lc, LinphoneCall* call,LinphoneCallState state,const char* message) { @@ -638,16 +681,20 @@ public: } LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + jstring msg = message ? env->NewStringUTF(message) : NULL; env->CallVoidMethod(lcData->listener ,lcData->callStateId ,lcData->core ,(jcall=getCall(env,call)) ,env->CallStaticObjectMethod(lcData->callStateClass,lcData->callStateFromIntId,(jint)state), - message ? env->NewStringUTF(message) : NULL); - if (state==LinphoneCallReleased){ + msg); + if (state==LinphoneCallReleased) { linphone_call_set_user_pointer(call,NULL); env->DeleteGlobalRef(jcall); } + if (msg) { + env->DeleteLocalRef(msg); + } } static void callEncryptionChange(LinphoneCore *lc, LinphoneCall* call, bool_t encrypted,const char* authentication_token) { JNIEnv *env = 0; @@ -763,17 +810,22 @@ public: ms_error("cannot attach VM"); return; } - LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); - LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); - env->CallVoidMethod(lcData->listener - ,lcData->ecCalibrationStatusId - ,lcData->core - ,env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass,lcData->ecCalibratorStatusFromIntId,(jint)status) - ,delay_ms - ,data ? data : NULL); - if (data != NULL &&status !=LinphoneEcCalibratorInProgress ) { - //final state, releasing global ref - env->DeleteGlobalRef((jobject)data); + + LinphoneCoreVTable *table = (LinphoneCoreVTable*) data; + if (table) { + LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table); + if (lcData->ecCalibrationStatusId) { + jobject state = env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass, lcData->ecCalibratorStatusFromIntId, (jint)status); + env->CallVoidMethod(lcData->listener + ,lcData->ecCalibrationStatusId + ,lcData->core + ,state + ,delay_ms + ,NULL); + } + if (status != LinphoneEcCalibratorInProgress) { + linphone_core_v_table_destroy(table); + } } } @@ -968,6 +1020,40 @@ public: jbytes, size); } + static void logCollectionUploadProgressIndication(LinphoneCore *lc, size_t offset, size_t total) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM"); + return; + } + LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + env->CallVoidMethod(lcData->listener + ,lcData->logCollectionUploadProgressId + ,lcData->core + ,(jlong)offset + ,(jlong)total); + } + static void logCollectionUploadStateChange(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + if (result != 0) { + ms_error("cannot attach VM"); + return; + } + LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc); + LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table); + jstring msg = info ? env->NewStringUTF(info) : NULL; + env->CallVoidMethod(lcData->listener + ,lcData->logCollectionUploadStateId + ,lcData->core + ,env->CallStaticObjectMethod(lcData->logCollectionUploadStateClass,lcData->logCollectionUploadStateFromIntId,(jint)state), + msg); + if (msg) { + env->DeleteLocalRef(msg); + } + } }; extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* env @@ -1049,6 +1135,22 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_removeListener(JNIEnv* e env->DeleteGlobalRef(listener); } + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_uploadLogCollection(JNIEnv* env, jobject thiz, jlong lc) { + LinphoneCore *core = (LinphoneCore*)lc; + linphone_core_upload_log_collection(core); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_enableLogCollection(JNIEnv* env, jclass cls, jboolean enable) { + linphone_core_enable_log_collection(enable ? LinphoneLogCollectionEnabledWithoutPreviousLogHandler : LinphoneLogCollectionDisabled); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setLogCollectionPath(JNIEnv* env, jclass cls, jstring jpath) { + const char* path = env->GetStringUTFChars(jpath, NULL); + linphone_core_set_log_collection_path(path); + env->ReleaseStringUTFChars(jpath, path); +} + extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_migrateToMultiTransport(JNIEnv* env ,jobject thiz ,jlong lc) { @@ -1727,11 +1829,11 @@ extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_startEchoCalibration(JNI ,jobject thiz ,jlong lc ,jobject data) { - return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc - , LinphoneCoreData::ecCalibrationStatus - , NULL - , NULL - , data?env->NewGlobalRef(data):NULL); + LinphoneCoreVTable *vTable = linphone_core_v_table_new(); + LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, data); + linphone_core_v_table_set_user_data(vTable, ldata); + + return (jint)linphone_core_start_echo_calibration((LinphoneCore*)lc, ldata->ecCalibrationStatus, NULL, NULL, vTable); } @@ -2855,11 +2957,13 @@ extern "C" jbyteArray Java_org_linphone_core_LinphoneChatMessageImpl_getText(JNI ,jobject thiz ,jlong ptr) { const char *message = linphone_chat_message_get_text((LinphoneChatMessage*)ptr); - size_t length = strlen(message); - jbyteArray array = env->NewByteArray(length); - env->SetByteArrayRegion(array, 0, length, (const jbyte*)message); - - return array; + if (message){ + size_t length = strlen(message); + jbyteArray array = env->NewByteArray(length); + env->SetByteArrayRegion(array, 0, length, (const jbyte*)message); + return array; + } + return NULL; } extern "C" jint Java_org_linphone_core_LinphoneChatMessageImpl_getReason(JNIEnv* env diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index a940fb696..e6b7b4098 100644 --- a/coreapi/lpconfig.c +++ b/coreapi/lpconfig.c @@ -734,25 +734,26 @@ void lp_config_write_relative_file(const LpConfig *lpconfig, const char *filenam } } -char *lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename) { +int lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename, char *data, size_t max_length) { char *dir = _lp_config_dirname(lpconfig->filename); char *filepath = ms_strdup_printf("%s/%s", dir, filename); - char *result = NULL; - if(ortp_file_exist(filepath) == 0) { - FILE *file = fopen(filepath, "r"); - if(file != NULL) { - result = ms_new0(char, MAX_LEN); - if(fgets(result, MAX_LEN, file) == NULL) { - ms_error("%s could not be loaded", filepath); - } - fclose(file); - } else { - ms_error("Could not open %s for read", filepath); + FILE *file = fopen(filepath, "r"); + if(file != NULL) { + if(fread(data, 1, max_length, file)<=0) { + ms_error("%s could not be loaded. %s", filepath, strerror(errno)); + goto err; } + fclose(file); } else { - ms_message("%s does not exist", filepath); + ms_error("Could not open %s for read. %s", filepath, strerror(errno)); + goto err; } ms_free(dir); ms_free(filepath); - return result; + return 0; + +err: + ms_free(dir); + ms_free(filepath); + return -1; } diff --git a/coreapi/lpconfig.h b/coreapi/lpconfig.h index 3498f00d2..65fd613f4 100644 --- a/coreapi/lpconfig.h +++ b/coreapi/lpconfig.h @@ -281,12 +281,14 @@ LINPHONE_PUBLIC void lp_config_unref(LpConfig *lpconfig); LINPHONE_PUBLIC void lp_config_write_relative_file(const LpConfig *lpconfig, const char *filename, const char *data); /** - * @brief Read a string from a file placed relatively with the Linphone configuration file + * @brief Read a string from a file placed beside the Linphone configuration file * @param lpconfig LpConfig instance used as a reference * @param filename Name of the file where data will be read from. The name is relative to the place of the config file - * @return The read string + * @param data Buffer where read string will be stored + * @param max_length Length of the buffer + * @return 0 on success, -1 on failure */ -LINPHONE_PUBLIC char *lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename); +LINPHONE_PUBLIC int lp_config_read_relative_file(const LpConfig *lpconfig, const char *filename, char *data, size_t max_length); #ifdef __cplusplus } diff --git a/coreapi/private.h b/coreapi/private.h index 4d94267c2..2aa0f6dd9 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -254,7 +254,7 @@ struct _LinphoneCall{ char *dtmf_sequence; /*DTMF sequence needed to be sent using #dtmfs_timer*/ belle_sip_source_t *dtmfs_timer; /*DTMF timer needed to send a DTMF sequence*/ - unsigned char *dtls_certificate_fingerprint; /**> This fingerprint is computed during stream init and is stored in call to be used when making local media description */ + char *dtls_certificate_fingerprint; /**> This fingerprint is computed during stream init and is stored in call to be used when making local media description */ bool_t refer_pending; bool_t expect_media_in_ack; bool_t audio_muted; @@ -925,6 +925,14 @@ struct _LinphoneContent { BELLE_SIP_DECLARE_VPTR(LinphoneContent); +struct _LinphoneBuffer { + belle_sip_object_t base; + void *user_data; + uint8_t *content; /**< A pointer to the buffer content */ + size_t size; /**< The size of the buffer content */ +}; + +BELLE_SIP_DECLARE_VPTR(LinphoneBuffer); /***************************************************************************** @@ -1001,6 +1009,7 @@ const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc); */ BELLE_SIP_DECLARE_TYPES_BEGIN(linphone,10000) +BELLE_SIP_TYPE_ID(LinphoneBuffer), BELLE_SIP_TYPE_ID(LinphoneContactProvider), BELLE_SIP_TYPE_ID(LinphoneContactSearch), BELLE_SIP_TYPE_ID(LinphoneCall), diff --git a/gtk/CMakeLists.txt b/gtk/CMakeLists.txt index 89d5805a6..7af375971 100644 --- a/gtk/CMakeLists.txt +++ b/gtk/CMakeLists.txt @@ -65,6 +65,10 @@ set(SOURCE_FILES videowindow.c ) +if(ENABLE_ASSISTANT) + list(APPEND SOURCE_FILES setupwizard.c) +endif() + if(GETTEXT_FOUND) add_definitions("-DENABLE_NLS") endif() @@ -77,6 +81,9 @@ if(ENABLE_NOTIFY) target_include_directories(linphone-gtk PUBLIC ${NOTIFY_INCLUDE_DIRS}) target_link_libraries(linphone-gtk ${NOTIFY_LIBRARIES}) endif() +if(ENABLE_ASSISTANT) + target_link_libraries(linphone-gtk ${SOUP_LIBRARIES}) +endif() install(TARGETS linphone-gtk RUNTIME DESTINATION bin diff --git a/gtk/linphone.h b/gtk/linphone.h index 70f1c58e3..cfb52f5e7 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -199,4 +199,5 @@ void linphone_gtk_close_config_fetching(GtkWidget *w, LinphoneConfiguringState s const char *linphone_gtk_get_sound_path(const char *file); void linphone_gtk_in_call_show_video(LinphoneCall *call); char *linphone_gtk_address(const LinphoneAddress *addr);/*return human readable identifier for a LinphoneAddress */ +GtkWidget *linphone_gtk_get_camera_preview_window(void); diff --git a/gtk/main.c b/gtk/main.c index b5ab17e26..8162927fa 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -832,9 +832,6 @@ bool_t linphone_gtk_video_enabled(void){ void linphone_gtk_show_main_window(){ GtkWidget *w=linphone_gtk_get_main_window(); - LinphoneCore *lc=linphone_gtk_get_core(); - linphone_core_enable_video_preview(lc,linphone_gtk_get_ui_config_int("videoselfview", - VIDEOSELFVIEW_DEFAULT)); gtk_widget_show(w); gtk_window_present(GTK_WINDOW(w)); } @@ -1028,13 +1025,6 @@ void _linphone_gtk_enable_video(gboolean val){ linphone_core_enable_video_capture(linphone_gtk_get_core(), TRUE); linphone_core_enable_video_display(linphone_gtk_get_core(), TRUE); linphone_core_set_video_policy(linphone_gtk_get_core(),&policy); - - if (val){ - linphone_core_enable_video_preview(linphone_gtk_get_core(), - linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT)); - }else{ - linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE); - } } void linphone_gtk_enable_video(GtkWidget *w){ @@ -1046,7 +1036,6 @@ void linphone_gtk_enable_video(GtkWidget *w){ void linphone_gtk_enable_self_view(GtkWidget *w){ gboolean val=gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); LinphoneCore *lc=linphone_gtk_get_core(); - linphone_core_enable_video_preview(lc,val); linphone_core_enable_self_view(lc,val); linphone_gtk_set_ui_config_int("videoselfview",val); } @@ -1843,10 +1832,11 @@ void linphone_gtk_manage_login(void){ gboolean linphone_gtk_close(GtkWidget *mw){ /*shutdown calls if any*/ LinphoneCore *lc=linphone_gtk_get_core(); + GtkWidget *camera_preview=linphone_gtk_get_camera_preview_window(); if (linphone_core_in_call(lc)){ linphone_core_terminate_all_calls(lc); } - linphone_core_enable_video_preview(lc,FALSE); + if (camera_preview) gtk_widget_destroy(camera_preview); #ifdef __APPLE__ /*until with have a better option*/ gtk_window_iconify(GTK_WINDOW(mw)); #else @@ -1857,13 +1847,6 @@ gboolean linphone_gtk_close(GtkWidget *mw){ #ifdef HAVE_GTK_OSX static gboolean on_window_state_event(GtkWidget *w, GdkEventWindowState *event){ - bool_t video_enabled=linphone_gtk_video_enabled(); - if ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) ||(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ){ - linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE); - }else{ - linphone_core_enable_video_preview(linphone_gtk_get_core(), - linphone_gtk_get_ui_config_int("videoselfview",VIDEOSELFVIEW_DEFAULT) && video_enabled); - } return FALSE; } #endif @@ -1974,6 +1957,7 @@ static void linphone_gtk_init_main_window(){ g_signal_connect(G_OBJECT(main_window), "window-state-event",G_CALLBACK(on_window_state_event), NULL); #endif linphone_gtk_check_menu_items(); + linphone_core_enable_video_preview(linphone_gtk_get_core(),FALSE); } void linphone_gtk_log_handler(OrtpLogLevel lev, const char *fmt, va_list args){ diff --git a/gtk/parameters.ui b/gtk/parameters.ui index aa8480aa9..8c2483ef7 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -1233,7 +1233,7 @@ True False - 3 + 4 2 @@ -1330,6 +1330,22 @@ GTK_EXPAND + + + Show camera preview + True + True + True + False + + + + 2 + 3 + 4 + GTK_EXPAND + + @@ -2188,7 +2204,7 @@ 1 2 GTK_FILL - + @@ -2250,7 +2266,7 @@ 2 3 GTK_FILL - + @@ -2265,7 +2281,7 @@ 2 3 GTK_FILL - + @@ -2538,7 +2554,7 @@ True False - label + label 1 @@ -2550,7 +2566,7 @@ True False - label + label 1 @@ -2564,7 +2580,7 @@ True False - label + label 1 diff --git a/gtk/videowindow.c b/gtk/videowindow.c index f2247e573..220569077 100644 --- a/gtk/videowindow.c +++ b/gtk/videowindow.c @@ -94,7 +94,7 @@ static gboolean drag_drop(GtkWidget *widget, GdkDragContext *drag_context, gint return TRUE; } -unsigned long get_native_handle(GdkWindow *gdkw){ +static unsigned long get_native_handle(GdkWindow *gdkw){ #ifdef GDK_WINDOWING_X11 return (unsigned long)GDK_WINDOW_XID(gdkw); #elif defined(WIN32) @@ -106,6 +106,15 @@ unsigned long get_native_handle(GdkWindow *gdkw){ return 0; } +static void _resize_video_window(GtkWidget *video_window, MSVideoSize vsize){ + MSVideoSize cur; + gtk_window_get_size(GTK_WINDOW(video_window),&cur.width,&cur.height); + if (vsize.width*vsize.height > cur.width*cur.height || + ms_video_size_get_orientation(vsize)!=ms_video_size_get_orientation(cur) ){ + gtk_window_resize(GTK_WINDOW(video_window),vsize.width,vsize.height); + } +} + static gint resize_video_window(LinphoneCall *call){ const LinphoneCallParams *params=linphone_call_get_current_params(call); if (params){ @@ -114,13 +123,7 @@ static gint resize_video_window(LinphoneCall *call){ GtkWidget *callview=(GtkWidget*)linphone_call_get_user_pointer(call); GtkWidget *video_window=(GtkWidget*)g_object_get_data(G_OBJECT(callview),"video_window"); if (video_window){ - MSVideoSize cur; - gtk_window_get_size(GTK_WINDOW(video_window),&cur.width,&cur.height); - if (vsize.width*vsize.height > cur.width*cur.height || - ms_video_size_get_orientation(vsize)!=ms_video_size_get_orientation(cur) ){ - g_message("Resized to %ix%i",vsize.width,vsize.height); - gtk_window_resize(GTK_WINDOW(video_window),vsize.width,vsize.height); - } + _resize_video_window(video_window,vsize); } } } @@ -308,3 +311,55 @@ void linphone_gtk_in_call_show_video(LinphoneCall *call){ } } } + +static void on_video_preview_destroyed(GtkWidget *video_preview, GtkWidget *mw){ + LinphoneCore *lc=linphone_gtk_get_core(); + guint timeout_id=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(video_preview),"timeout-id")); + g_object_set_data(G_OBJECT(mw),"video_preview",NULL); + linphone_core_enable_video_preview(lc,FALSE); + linphone_core_set_native_preview_window_id(lc,-1); + g_source_remove(timeout_id); +} + +GtkWidget *linphone_gtk_get_camera_preview_window(void){ + return (GtkWidget *)g_object_get_data(G_OBJECT(linphone_gtk_get_main_window()),"video_preview"); +} + +static gboolean check_preview_size(GtkWidget *video_preview){ + MSVideoSize vsize=linphone_core_get_current_preview_video_size(linphone_gtk_get_core()); + if (vsize.width && vsize.height){ + MSVideoSize cur; + gtk_window_get_size(GTK_WINDOW(video_preview),&cur.width,&cur.height); + if (cur.width!=vsize.width || cur.height!=vsize.height){ + gtk_window_resize(GTK_WINDOW(video_preview),vsize.width,vsize.height); + } + } + return TRUE; +} + +void linphone_gtk_show_camera_preview_clicked(GtkButton *button){ + GtkWidget *mw=linphone_gtk_get_main_window(); + GtkWidget *video_preview=(GtkWidget *)g_object_get_data(G_OBJECT(mw),"video_preview"); + + if (!video_preview){ + gchar *title; + LinphoneCore *lc=linphone_gtk_get_core(); + GdkColor color; + guint tid; + + video_preview=gtk_window_new(GTK_WINDOW_TOPLEVEL); + title=g_strdup_printf("%s - Video preview",linphone_gtk_get_ui_config("title","Linphone")); + gtk_window_set_title(GTK_WINDOW(video_preview),title); + gdk_color_parse("black",&color); + gtk_widget_modify_bg(video_preview,GTK_STATE_NORMAL,&color); + g_free(title); + g_object_set_data(G_OBJECT(mw),"video_preview",video_preview); + g_signal_connect(video_preview,"destroy",(GCallback)on_video_preview_destroyed,mw); + gtk_widget_show(video_preview); + linphone_core_set_native_preview_window_id(lc,get_native_handle(gtk_widget_get_window(video_preview))); + linphone_core_enable_video_preview(lc,TRUE); + tid=g_timeout_add(100,(GSourceFunc)check_preview_size,video_preview); + g_object_set_data(G_OBJECT(video_preview),"timeout-id",GINT_TO_POINTER(tid)); + } +} + diff --git a/include/sal/sal.h b/include/sal/sal.h index b7238b9b4..e2ce603cc 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -540,7 +540,7 @@ void sal_signing_key_parse_file(SalAuthInfo* auth_info, const char* path, const * @param[in] generate_certificate if true, if matching certificate and key can't be found, generate it and store it into the given dir, filename will be subject.pem * @param[in] generate_dtls_fingerprint if true and we have a certificate, generate the dtls fingerprint as described in rfc4572 */ -void sal_certificates_chain_parse_directory(unsigned char **certificate_pem, unsigned char **key_pem, unsigned char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint); +void sal_certificates_chain_parse_directory(char **certificate_pem, char **key_pem, char **fingerprint, const char* path, const char *subject, SalCertificateRawFormat format, bool_t generate_certificate, bool_t generate_dtls_fingerprint); void sal_certificates_chain_delete(SalCertificatesChain *chain); void sal_signing_key_delete(SalSigningKey *key); diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 93c5fe1cf..0e5c36d6d 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -20,6 +20,7 @@ package org.linphone.core; import java.util.Vector; +import org.linphone.core.LinphoneCoreListener.LinphoneEchoCalibrationListener; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration; @@ -429,6 +430,45 @@ public interface LinphoneCore { return mStringValue; } } + /** + * linphone log collection upload states + */ + static public class LogCollectionUploadState { + + static private Vector values = new Vector(); + /** + * Delivery in progress + */ + static public LogCollectionUploadState LogCollectionUploadStateInProgress = new LogCollectionUploadState(0,"LinphoneCoreLogCollectionUploadStateInProgress"); + /** + * Log collection upload successfully delivered and acknowledged by remote end point + */ + static public LogCollectionUploadState LogCollectionUploadStateDelivered = new LogCollectionUploadState(1,"LinphoneCoreLogCollectionUploadStateDelivered"); + /** + * Log collection upload was not delivered + */ + static public LogCollectionUploadState LogCollectionUploadStateNotDelivered = new LogCollectionUploadState(2,"LinphoneCoreLogCollectionUploadStateNotDelivered"); + + private final int mValue; + private final String mStringValue; + + private LogCollectionUploadState(int value, String stringValue) { + mValue = value; + values.addElement(this); + mStringValue=stringValue; + } + public static LogCollectionUploadState fromInt(int value) { + + for (int i=0; istat; stats initial_callee=callee_mgr->stat; bool_t result=FALSE; - char hellopath[256]; LinphoneCallParams *caller_params = caller_test_params->base; LinphoneCallParams *callee_params = callee_test_params->base; bool_t did_received_call; @@ -196,10 +195,6 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr sal_default_enable_sdp_removal(caller_mgr->lc->sal, caller_test_params->sdp_removal); sal_default_enable_sdp_removal(callee_mgr->lc->sal, callee_test_params->sdp_removal); - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (callee_mgr->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(callee_mgr->lc,hellopath); if (!caller_params){ CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address(caller_mgr->lc,callee_mgr->identity)); }else{ @@ -326,12 +321,32 @@ static void simple_call(void) { LinphoneCoreManager* pauline; const LinphoneAddress *from; LinphoneCall *pauline_call; + LinphoneProxyConfig* marie_cfg; + const char* marie_id = NULL; belle_sip_object_enable_leak_detector(TRUE); begin=belle_sip_object_get_object_count(); marie = linphone_core_manager_new( "marie_rc"); pauline = linphone_core_manager_new( "pauline_rc"); + + /* with the account manager, we might lose the identity */ + marie_cfg = linphone_core_get_default_proxy_config(marie->lc); + marie_id = linphone_proxy_config_get_identity(marie_cfg); + { + LinphoneAddress* marie_addr = linphone_address_new(marie_id); + char* marie_tmp_id = NULL; + linphone_address_set_display_name(marie_addr, "Super Marie"); + marie_tmp_id = linphone_address_as_string(marie_addr); + + linphone_proxy_config_edit(marie_cfg); + linphone_proxy_config_set_identity(marie_cfg,marie_tmp_id); + linphone_proxy_config_done(marie_cfg); + + ms_free(marie_tmp_id); + linphone_address_unref(marie_addr); + } + CU_ASSERT_TRUE(call(marie,pauline)); pauline_call=linphone_core_get_current_call(pauline->lc); CU_ASSERT_PTR_NOT_NULL(pauline_call); @@ -368,15 +383,9 @@ static void direct_call_over_ipv6(){ if (liblinphone_tester_ipv6_available()){ LCSipTransports pauline_transports; LinphoneAddress* pauline_dest = linphone_address_new("sip:[::1];transport=tcp"); - char hellopath[256]; marie = linphone_core_manager_new( "marie_rc"); pauline = linphone_core_manager_new( "pauline_tcp_rc"); - /*use playfile for callee to avoid locking on capture card*/ - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - linphone_core_use_files (pauline->lc,TRUE); - linphone_core_enable_ipv6(marie->lc,TRUE); linphone_core_enable_ipv6(pauline->lc,TRUE); linphone_core_set_default_proxy_config(marie->lc,NULL); @@ -448,7 +457,6 @@ static void multiple_answers_call() { /* Scenario is this: pauline calls marie, which is registered 2 times. Both linphones answer at the same time, and only one should get the call running, the other should be terminated */ - char ringbackpath[256]; LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc" ); LinphoneCoreManager* marie1 = linphone_core_manager_new( "marie_rc" ); LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc" ); @@ -459,11 +467,6 @@ static void multiple_answers_call() { lcs = ms_list_append(lcs,marie1->lc); lcs = ms_list_append(lcs,marie2->lc); - linphone_core_use_files(pauline->lc, TRUE); - linphone_core_use_files(marie1->lc, TRUE); - linphone_core_use_files(marie2->lc, TRUE); - - snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix); CU_ASSERT_TRUE(wait_for_until(pauline->lc, NULL, &pauline->stat.number_of_LinphoneRegistrationOk, 1, 2000)); @@ -499,7 +502,6 @@ static void multiple_answers_call_with_media_relay() { /* Scenario is this: pauline calls marie, which is registered 2 times. * Both linphones answer at the same time, and only one should get the * call running, the other should be terminated */ - char ringbackpath[256]; LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc" ); LinphoneCoreManager* marie1 = linphone_core_manager_new( "marie_rc" ); LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc" ); @@ -510,16 +512,10 @@ static void multiple_answers_call_with_media_relay() { lcs = ms_list_append(lcs,marie1->lc); lcs = ms_list_append(lcs,marie2->lc); - linphone_core_use_files(pauline->lc, TRUE); - linphone_core_use_files(marie1->lc, TRUE); - linphone_core_use_files(marie2->lc, TRUE); - linphone_core_set_user_agent(pauline->lc, "Natted Linphone", NULL); linphone_core_set_user_agent(marie1->lc, "Natted Linphone", NULL); linphone_core_set_user_agent(marie2->lc, "Natted Linphone", NULL); - snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix); - CU_ASSERT_TRUE(wait_for_until(pauline->lc, NULL, &pauline->stat.number_of_LinphoneRegistrationOk, 1, 2000)); CU_ASSERT_PTR_NOT_NULL( linphone_core_invite_address(pauline->lc, marie1->identity ) ); @@ -894,6 +890,26 @@ static void call_with_no_sdp(void) { linphone_core_manager_destroy(pauline); } +static void call_with_no_sdp_ack_without_sdp(void){ + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCall *call; + + linphone_core_enable_sdp_200_ack(marie->lc,TRUE); + + linphone_core_invite_address(marie->lc,pauline->identity); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallIncomingReceived,1)); + call=linphone_core_get_current_call(pauline->lc); + if (call){ + sal_call_enable_sdp_removal(call->op, TRUE); /*this will have the effect that the SDP received in the ACK will be ignored*/ + linphone_core_accept_call(pauline->lc, call); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallError,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); + } + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + static bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, LinphoneIceState state) { LinphoneCall *c1,*c2; @@ -1249,6 +1265,32 @@ static void call_paused_resumed_from_callee(void) { linphone_core_manager_destroy(pauline); } +static void audio_call_with_ice_no_matching_audio_codecs(void) { + LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_rc"); + LinphoneCall *out_call; + + linphone_core_enable_payload_type(marie->lc, linphone_core_find_payload_type(marie->lc, "PCMU", 8000, 1), FALSE); /* Disable PCMU */ + linphone_core_enable_payload_type(marie->lc, linphone_core_find_payload_type(marie->lc, "PCMA", 8000, 1), TRUE); /* Enable PCMA */ + linphone_core_set_firewall_policy(marie->lc, LinphonePolicyUseIce); + linphone_core_set_stun_server(marie->lc, "stun.linphone.org"); + linphone_core_set_firewall_policy(pauline->lc, LinphonePolicyUseIce); + linphone_core_set_stun_server(pauline->lc, "stun.linphone.org"); + + out_call = linphone_core_invite_address(marie->lc, pauline->identity); + linphone_call_ref(out_call); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingInit, 1)); + + /* flexisip will retain the 488 until the "urgent reply" timeout arrives. */ + CU_ASSERT_TRUE(wait_for_until(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallError, 1, 6000)); + CU_ASSERT_EQUAL(linphone_call_get_reason(out_call), LinphoneReasonNotAcceptable); + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallIncomingReceived, 0); + + linphone_call_unref(out_call); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + #ifdef VIDEO_ENABLED static LinphoneCall* setup_video(LinphoneCoreManager* caller,LinphoneCoreManager* callee) { LinphoneVideoPolicy caller_policy; @@ -1495,6 +1537,7 @@ static void video_call_base(LinphoneCoreManager* pauline,LinphoneCoreManager* ma LinphoneCall* marie_call; LinphoneCall* pauline_call; LinphoneVideoPolicy marie_policy, pauline_policy; + linphone_core_enable_video_capture(marie->lc, TRUE); linphone_core_enable_video_display(marie->lc, TRUE); linphone_core_enable_video_capture(pauline->lc, TRUE); @@ -1615,27 +1658,51 @@ static void call_with_ice_video_added(void) { linphone_core_manager_destroy(pauline); } -static void video_call_with_ice_no_matching_audio_codecs(void) { +static void video_call_with_early_media_no_matching_audio_codecs(void) { LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_rc"); LinphoneCall *out_call; - + LinphoneVideoPolicy vpol={0}; + + linphone_core_enable_video_capture(marie->lc, TRUE); + linphone_core_enable_video_display(marie->lc, TRUE); + linphone_core_enable_video_capture(pauline->lc, TRUE); + linphone_core_enable_video_display(pauline->lc, FALSE); + + vpol.automatically_initiate=TRUE; + vpol.automatically_accept=TRUE; + linphone_core_set_video_policy(pauline->lc,&vpol); + linphone_core_set_video_policy(marie->lc,&vpol); + linphone_core_enable_payload_type(marie->lc, linphone_core_find_payload_type(marie->lc, "PCMU", 8000, 1), FALSE); /* Disable PCMU */ linphone_core_enable_payload_type(marie->lc, linphone_core_find_payload_type(marie->lc, "PCMA", 8000, 1), TRUE); /* Enable PCMA */ - linphone_core_set_firewall_policy(marie->lc, LinphonePolicyUseIce); - linphone_core_set_stun_server(marie->lc, "stun.linphone.org"); - linphone_core_set_firewall_policy(pauline->lc, LinphonePolicyUseIce); - linphone_core_set_stun_server(pauline->lc, "stun.linphone.org"); out_call = linphone_core_invite_address(marie->lc, pauline->identity); linphone_call_ref(out_call); CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingInit, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1)); - /* flexisip will retain the 488 until the "urgent reply" timeout arrives. */ - CU_ASSERT_TRUE(wait_for_until(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallError, 1, 6000)); - CU_ASSERT_EQUAL(linphone_call_get_reason(out_call), LinphoneReasonNotAcceptable); - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallIncomingReceived, 0); + linphone_core_accept_early_media(pauline->lc,linphone_core_get_current_call(pauline->lc)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingEarlyMedia, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia, 1)); + /*audio stream shall not have been requested to start*/ + CU_ASSERT_PTR_NULL(linphone_core_get_current_call(pauline->lc)->audiostream->soundread); + + CU_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(out_call))==TRUE); + CU_ASSERT_TRUE(linphone_call_params_video_enabled(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc)))==TRUE); + + linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + + linphone_core_terminate_call(marie->lc, out_call); + + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + CU_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallEnd, 1)); + linphone_call_unref(out_call); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); @@ -1799,7 +1866,6 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc"); - char hellopath[256]; MSList *iterator; MSList* lcs; LinphoneCall* pauline_called_by_marie; @@ -1817,12 +1883,6 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) { CU_ASSERT_TRUE(call_with_caller_params(marie,pauline,marie_params)); pauline_called_by_marie=linphone_core_get_current_call(pauline->lc); - - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (laure->lc,TRUE); - linphone_core_use_files (marie->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(laure->lc,hellopath); if (enable_caller_privacy) linphone_call_params_set_privacy(laure_params,LinphonePrivacyId); @@ -2043,7 +2103,9 @@ static void call_with_file_player(void) { CU_ASSERT_TRUE(linphone_player_open(player,hellopath,on_eof,marie)==0); CU_ASSERT_TRUE(linphone_player_start(player)==0); } - CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&marie->stat.number_of_player_eof,1,12000)); + + /* This assert should be modified to be at least as long as the hello8000.wav file */ + CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&marie->stat.number_of_player_eof,1,30000)); /*just to sleep*/ linphone_core_terminate_all_calls(marie->lc); @@ -2272,14 +2334,13 @@ static void early_media_call(void) { } static void early_media_call_with_ringing(void){ - char hellopath[256]; LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_rc"); MSList* lcs = NULL; LinphoneCall* marie_call; LinphoneCallLog *marie_call_log; - time_t connected_time=0; - time_t ended_time=0; + uint64_t connected_time=0; + uint64_t ended_time=0; int dummy=0; lcs = ms_list_append(lcs,marie->lc); @@ -2288,11 +2349,6 @@ static void early_media_call_with_ringing(void){ Marie calls Pauline, and after the call has rung, transitions to an early_media session */ - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (pauline->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - marie_call = linphone_core_invite_address(marie->lc, pauline->identity); marie_call_log = linphone_call_get_call_log(marie_call); @@ -2313,7 +2369,7 @@ static void early_media_call_with_ringing(void){ linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); - connected_time=time(NULL); + connected_time=ms_get_cur_time_ms(); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); CU_ASSERT_EQUAL(marie_call, linphone_core_get_current_call(marie->lc)); @@ -2326,8 +2382,8 @@ static void early_media_call_with_ringing(void){ CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000)); CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,1000)); - ended_time=time(NULL); - CU_ASSERT_TRUE (labs (linphone_call_log_get_duration(marie_call_log) - (ended_time - connected_time)) <1 ); + ended_time=ms_get_cur_time_ms(); + CU_ASSERT_TRUE( labs((linphone_call_log_get_duration(marie_call_log)*1000) - (int64_t)(ended_time - connected_time)) <=1000 ); ms_list_free(lcs); } @@ -2336,7 +2392,6 @@ static void early_media_call_with_ringing(void){ } static void early_media_call_with_update_base(bool_t media_change){ - char hellopath[256]; LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_rc"); MSList* lcs = NULL; @@ -2353,11 +2408,6 @@ static void early_media_call_with_update_base(bool_t media_change){ Marie calls Pauline, and after the call has rung, transitions to an early_media session */ - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (pauline->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - marie_call = linphone_core_invite_address(marie->lc, pauline->identity); CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,1000)); @@ -2575,9 +2625,6 @@ static void call_transfer_existing_call_outgoing_call(void) { MSList* lcs=ms_list_append(NULL,marie->lc); const MSList* calls; - linphone_core_use_files (pauline->lc,TRUE); - linphone_core_use_files (laure->lc,TRUE); - lcs=ms_list_append(lcs,pauline->lc); lcs=ms_list_append(lcs,laure->lc); @@ -2604,8 +2651,8 @@ static void call_transfer_existing_call_outgoing_call(void) { CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallRefered,1,2000)); /*pauline pausing marie*/ - CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPausing,1,2000)); - CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPaused,1,2000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPausing,1,4000)); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallPaused,1,4000)); /*pauline calling laure*/ CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingProgress,1,2000)); CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallOutgoingInit,1,2000)); @@ -2743,7 +2790,6 @@ static void call_established_with_rejected_incoming_reinvite(void) { } static void call_redirect(void){ - char hellopath[256]; LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_rc"); LinphoneCoreManager* laure = linphone_core_manager_new("laure_rc"); @@ -2758,13 +2804,6 @@ static void call_redirect(void){ Marie calls Pauline, which will redirect the call to Laure via a 302 */ - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (pauline->lc,TRUE); - linphone_core_use_files (laure->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - linphone_core_set_play_file(laure->lc,hellopath); - marie_call = linphone_core_invite_address(marie->lc, pauline->identity); CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,1000)); @@ -2889,9 +2928,9 @@ static void call_rejected_without_403_because_wrong_credentials_no_auth_req_cb() #ifdef VIDEO_ENABLED /*this is call forking with early media managed at client side (not by flexisip server)*/ static void multiple_early_media(void) { + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); LinphoneCoreManager* marie1 = linphone_core_manager_new("marie_early_rc"); LinphoneCoreManager* marie2 = linphone_core_manager_new("marie_early_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); MSList *lcs=NULL; LinphoneCallParams *params=linphone_core_create_default_call_parameters(pauline->lc); LinphoneVideoPolicy pol; @@ -2900,9 +2939,6 @@ static void multiple_early_media(void) { LinphoneCall *pauline_call; LinphoneInfoMessage *info; int dummy=0; - char ringbackpath[256]; - snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix); - pol.automatically_accept=1; pol.automatically_initiate=1; @@ -2910,18 +2946,11 @@ static void multiple_early_media(void) { linphone_core_enable_video(marie1->lc,TRUE,TRUE); linphone_core_set_video_policy(marie1->lc,&pol); - /*use playfile for marie1 to avoid locking on capture card*/ - linphone_core_use_files(marie1->lc,TRUE); - linphone_core_set_play_file(marie1->lc,ringbackpath); linphone_core_enable_video(marie2->lc,TRUE,TRUE); linphone_core_set_video_policy(marie2->lc,&pol); linphone_core_set_audio_port_range(marie2->lc,40200,40300); linphone_core_set_video_port_range(marie2->lc,40400,40500); - /*use playfile for marie2 to avoid locking on capture card*/ - linphone_core_use_files(marie2->lc,TRUE); - linphone_core_set_play_file(marie2->lc,ringbackpath); - lcs=ms_list_append(lcs,marie1->lc); lcs=ms_list_append(lcs,marie2->lc); @@ -2942,7 +2971,7 @@ static void multiple_early_media(void) { marie2_call=linphone_core_get_current_call(marie2->lc); /*wait a bit that streams are established*/ - wait_for_list(lcs,&dummy,1,6000); + wait_for_list(lcs,&dummy,1,3000); CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>70); CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>70); CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>70); @@ -3487,6 +3516,7 @@ test_t call_tests[] = { { "Early-media call with updated codec", early_media_call_with_codec_update}, { "Call terminated by caller", call_terminated_by_caller }, { "Call without SDP", call_with_no_sdp}, + { "Call without SDP and ACK without SDP", call_with_no_sdp_ack_without_sdp}, { "Call paused resumed", call_paused_resumed }, { "Call paused resumed with loss", call_paused_resumed_with_loss }, { "Call paused resumed from callee", call_paused_resumed_from_callee }, @@ -3496,6 +3526,7 @@ test_t call_tests[] = { { "SRTP call with declined srtp", call_with_declined_srtp }, { "Call with file player", call_with_file_player}, { "Call with mkv file player", call_with_mkv_file_player}, + { "Audio call with ICE no matching audio codecs", audio_call_with_ice_no_matching_audio_codecs }, #ifdef VIDEO_ENABLED { "Simple video call",video_call}, { "Simple video call using policy",video_call_using_policy}, @@ -3511,9 +3542,9 @@ test_t call_tests[] = { { "Call with multiple early media", multiple_early_media }, { "Call with ICE from video to non-video", call_with_ice_video_to_novideo}, { "Call with ICE and video added", call_with_ice_video_added }, - { "Video call with ICE no matching audio codecs", video_call_with_ice_no_matching_audio_codecs }, { "Video call recording", video_call_recording_test }, { "Snapshot", video_call_snapshot }, + { "Video call with early media and no matching audio codecs", video_call_with_early_media_no_matching_audio_codecs }, #endif { "SRTP ice call", srtp_ice_call }, { "ZRTP ice call", zrtp_ice_call }, diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 63ae8b79d..4b310be88 100644 --- a/tester/flexisip_tester.c +++ b/tester/flexisip_tester.c @@ -307,7 +307,6 @@ static void call_forking_cancelled(void){ } static void call_forking_declined(bool_t declined_globaly){ - char hellopath[256]; LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc"); @@ -323,11 +322,6 @@ static void call_forking_declined(bool_t declined_globaly){ linphone_core_set_user_agent(marie3->lc,"Natted Linphone",NULL); linphone_core_set_user_agent(pauline->lc,"Natted Linphone",NULL); - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (pauline->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - linphone_core_invite_address(pauline->lc,marie->identity); /*pauline should hear ringback*/ CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingRinging,1,3000)); @@ -376,7 +370,6 @@ static void call_forking_declined_localy(void){ } static void call_forking_with_push_notification_single(void){ - char hellopath[256]; MSList* lcs; LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); @@ -391,11 +384,6 @@ static void call_forking_with_push_notification_single(void){ /*unfortunately marie gets unreachable due to crappy 3G operator or iOS bug...*/ linphone_core_set_network_reachable(marie->lc,FALSE); - /*use playfile for callee to avoid locking on capture card*/ - linphone_core_use_files (pauline->lc,TRUE); - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_set_play_file(pauline->lc,hellopath); - linphone_core_invite_address(pauline->lc,marie->identity); /*the server is expected to send a push notification to marie, this will wake up linphone, that will reconnect:*/ @@ -425,7 +413,6 @@ static void call_forking_with_push_notification_single(void){ } static void call_forking_with_push_notification_multiple(void){ - char hellopath[256]; LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc"); LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* marie2 = linphone_core_manager_new( "marie_rc"); @@ -442,13 +429,6 @@ static void call_forking_with_push_notification_multiple(void){ /*unfortunately marie gets unreachable due to crappy 3G operator or iOS bug...*/ linphone_core_set_network_reachable(marie2->lc,FALSE); - /*use playfile for callee to avoid locking on capture card*/ - snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - linphone_core_use_files (marie->lc,TRUE); - linphone_core_set_play_file(marie->lc,hellopath); - linphone_core_use_files (marie2->lc,TRUE); - linphone_core_set_play_file(marie2->lc,hellopath); - linphone_core_invite_address(pauline->lc,marie->identity); /*marie1 will ring*/ @@ -523,9 +503,9 @@ void call_forking_not_responded(void){ } static void early_media_call_forking(void) { + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); LinphoneCoreManager* marie1 = linphone_core_manager_new("marie_early_rc"); LinphoneCoreManager* marie2 = linphone_core_manager_new("marie_early_rc"); - LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); MSList *lcs=NULL; LinphoneCallParams *params=linphone_core_create_default_call_parameters(pauline->lc); LinphoneVideoPolicy pol; @@ -533,9 +513,7 @@ static void early_media_call_forking(void) { LinphoneCall *marie2_call; LinphoneCall *pauline_call; int dummy=0; - char ringbackpath[256]; - snprintf(ringbackpath,sizeof(ringbackpath), "%s/sounds/hello8000.wav" /*use hello because rinback is too short*/, liblinphone_tester_file_prefix); - + pol.automatically_accept=1; pol.automatically_initiate=1; @@ -547,18 +525,11 @@ static void early_media_call_forking(void) { linphone_core_enable_video(marie1->lc,TRUE,TRUE); linphone_core_set_video_policy(marie1->lc,&pol); - /*use playfile for marie1 to avoid locking on capture card*/ - linphone_core_use_files (marie1->lc,TRUE); - linphone_core_set_play_file(marie1->lc,ringbackpath); - + linphone_core_enable_video(marie2->lc,TRUE,TRUE); linphone_core_set_video_policy(marie2->lc,&pol); linphone_core_set_audio_port_range(marie2->lc,40200,40300); linphone_core_set_video_port_range(marie2->lc,40400,40500); - /*use playfile for marie2 to avoid locking on capture card*/ - linphone_core_use_files (marie2->lc,TRUE); - linphone_core_set_play_file(marie2->lc,ringbackpath); - lcs=ms_list_append(lcs,marie1->lc); lcs=ms_list_append(lcs,marie2->lc); @@ -581,12 +552,12 @@ static void early_media_call_forking(void) { /*wait a bit that streams are established*/ wait_for_list(lcs,&dummy,1,3000); - CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>70 - && linphone_call_get_audio_stats(pauline_call)->download_bandwidth<90); - CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>70 - && linphone_call_get_audio_stats(marie1_call)->download_bandwidth<90); - CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>70 - && linphone_call_get_audio_stats(marie2_call)->download_bandwidth<90); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>60 + && linphone_call_get_audio_stats(pauline_call)->download_bandwidth<99); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>60 + && linphone_call_get_audio_stats(marie1_call)->download_bandwidth<99); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie2_call)->download_bandwidth>60 + && linphone_call_get_audio_stats(marie2_call)->download_bandwidth<99); linphone_core_accept_call(marie1->lc,linphone_core_get_current_call(marie1->lc)); CU_ASSERT_TRUE(wait_for_list(lcs,&marie1->stat.number_of_LinphoneCallStreamsRunning,1,3000)); @@ -597,10 +568,10 @@ static void early_media_call_forking(void) { /*wait a bit that streams are established*/ wait_for_list(lcs,&dummy,1,3000); - CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>71 - && linphone_call_get_audio_stats(pauline_call)->download_bandwidth<91 ); - CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>71 - && linphone_call_get_audio_stats(marie1_call)->download_bandwidth<91 ); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(pauline_call)->download_bandwidth>60 + && linphone_call_get_audio_stats(pauline_call)->download_bandwidth<99 ); + CU_ASSERT_TRUE(linphone_call_get_audio_stats(marie1_call)->download_bandwidth>60 + && linphone_call_get_audio_stats(marie1_call)->download_bandwidth<99 ); linphone_core_terminate_all_calls(pauline->lc); CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000)); diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index addf99566..aa4f78f29 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -135,6 +135,8 @@ void helper(const char *name) { #if HAVE_CU_CURSES "\t\t\t--curses\n" #endif + "\t\t\t--xml\n" + "\t\t\t--xml-file \n" , name); } @@ -153,6 +155,8 @@ int main (int argc, char *argv[]) int ret; const char *suite_name=NULL; const char *test_name=NULL; + const char *xml_file=NULL; + int xml = 0; FILE* log_file=NULL; #if defined(ANDROID) linphone_core_set_log_handler(linphone_android_ortp_log_handler); @@ -198,6 +202,11 @@ int main (int argc, char *argv[]) suite_name = argv[i]; liblinphone_tester_list_suite_tests(suite_name); return 0; + } else if (strcmp(argv[i], "--xml-file") == 0){ + CHECK_ARG("--xml-file", ++i, argc); + xml_file = argv[i]; + } else if (strcmp(argv[i], "--xml") == 0){ + xml = 1; } else if (strcmp(argv[i],"--log-file")==0){ CHECK_ARG("--log-file", ++i, argc); log_file=fopen(argv[i],"w"); @@ -216,6 +225,17 @@ int main (int argc, char *argv[]) } } + if( xml && (suite_name || test_name) ){ + printf("Cannot use both xml and specific test suite\n"); + return -1; + } + + if( xml_file != NULL ){ + liblinphone_tester_set_xml_output(xml_file); + } + liblinphone_tester_enable_xml(xml); + + ret = liblinphone_tester_run_tests(suite_name, test_name); liblinphone_tester_uninit(); return ret; diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 0c4e658c5..dded33942 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -79,6 +79,11 @@ extern void liblinphone_tester_set_fileprefix(const char* file_prefix); extern void liblinphone_tester_set_writable_dir_prefix(const char* writable_dir_prefix); extern int liblinphone_tester_ipv6_available(void); + +extern void liblinphone_tester_enable_xml( bool_t enable ); +extern void liblinphone_tester_set_xml_output(const char *xml_path ); +extern const char* liblinphone_tester_get_xml_output(void); + /** * @brief Tells the tester whether or not to clean the accounts it has created between runs. * @details Setting this to 1 will not clear the list of created accounts between successive @@ -257,8 +262,9 @@ void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, void notify_presence_received(LinphoneCore *lc, LinphoneFriend * lf); void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const LinphoneAddress *from_address, const char *message); void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage* message); -void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size); -void file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size); +void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const LinphoneBuffer *buffer); +LinphoneBuffer * file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size); +LinphoneBuffer * memory_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size); void file_transfer_progress_indication(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total); void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room); void info_message_received(LinphoneCore *lc, LinphoneCall *call, const LinphoneInfoMessage *msg); diff --git a/tester/log_collection_tester.c b/tester/log_collection_tester.c index 4f18f1224..c7b43beb7 100644 --- a/tester/log_collection_tester.c +++ b/tester/log_collection_tester.c @@ -100,8 +100,15 @@ LinphoneCoreManager* setup(bool_t enable_logs) { return marie; } -time_t check_file(char * filepath) { - time_t time_curr = -1; +time_t check_file(LinphoneCoreManager* mgr) { + + time_t last_log = ms_time(NULL); + char* filepath = linphone_core_compress_log_collection(mgr->lc); + time_t time_curr = -1; + uint32_t timediff = 0; + + CU_ASSERT_PTR_NOT_NULL(filepath); + if (filepath != NULL) { int line_count = 0; FILE *file = fopen(filepath, "r"); @@ -134,6 +141,13 @@ time_t check_file(char * filepath) { fclose(file); ms_free(filepath); } + + timediff = labs((long int)time_curr - (long int)last_log); + + CU_ASSERT_TRUE( timediff <= 1 ); + if( !(timediff <= 1) ){ + ms_error("time_curr: %ld, last_log: %ld timediff: %d", time_curr, last_log, timediff ); + } // return latest time in file return time_curr; } @@ -159,38 +173,28 @@ static void collect_files_disabled() { static void collect_files_filled() { LinphoneCoreManager* marie = setup(TRUE); - char * filepath = linphone_core_compress_log_collection(marie->lc); - CU_ASSERT_PTR_NOT_NULL(filepath); - CU_ASSERT_EQUAL(check_file(filepath), ms_time(0)); + check_file(marie); linphone_core_manager_destroy(marie); } static void collect_files_small_size() { LinphoneCoreManager* marie = setup(TRUE); - char * filepath; linphone_core_set_log_collection_max_file_size(5000); - filepath = linphone_core_compress_log_collection(marie->lc); - CU_ASSERT_PTR_NOT_NULL(filepath); - CU_ASSERT_EQUAL(check_file(filepath), ms_time(0)); + check_file(marie); linphone_core_manager_destroy(marie); } static void collect_files_changing_size() { LinphoneCoreManager* marie = setup(TRUE); - char * filepath; int waiting = 100; - filepath = linphone_core_compress_log_collection(marie->lc); - CU_ASSERT_PTR_NOT_NULL(filepath); - CU_ASSERT_EQUAL(check_file(filepath), ms_time(0)); + check_file(marie); linphone_core_set_log_collection_max_file_size(5000); // Generate some logs while (--waiting) ms_error("(test error)Waiting %d...", waiting); - filepath = linphone_core_compress_log_collection(marie->lc); - CU_ASSERT_PTR_NOT_NULL(filepath); - CU_ASSERT_EQUAL(check_file(filepath), ms_time(0)); + check_file(marie); linphone_core_manager_destroy(marie); } diff --git a/tester/message_tester.c b/tester/message_tester.c index 784acb49b..254737a04 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -62,7 +62,7 @@ void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMess /** * function invoked when a file transfer is received. * */ -void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size){ +void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* content, const LinphoneBuffer *buffer){ FILE* file=NULL; char receive_file[256]; LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(message); @@ -72,18 +72,17 @@ void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* /*first chunk, creating file*/ file = fopen(receive_file,"wb"); linphone_chat_message_set_user_data(message,(void*)file); /*store fd for next chunks*/ - } else { - /*next chunk*/ - file = (FILE*)linphone_chat_message_get_user_data(message); + } - if (size==0) { /* tranfer complete */ - stats* counters = get_stats(lc); - counters->number_of_LinphoneMessageExtBodyReceived++; - fclose(file); - } else { /* store content on a file*/ - if (fwrite(buff,size,1,file)==-1){ - ms_error("file_transfer_received(): write() failed: %s",strerror(errno)); - } + file = (FILE*)linphone_chat_message_get_user_data(message); + + if (linphone_buffer_is_empty(buffer)) { /* tranfer complete */ + stats* counters = get_stats(lc); + counters->number_of_LinphoneMessageExtBodyReceived++; + fclose(file); + } else { /* store content on a file*/ + if (fwrite(linphone_buffer_get_content(buffer),linphone_buffer_get_size(buffer),1,file)==-1){ + ms_error("file_transfer_received(): write() failed: %s",strerror(errno)); } } } @@ -93,26 +92,31 @@ static char big_file [128000]; /* a buffer to simulate a big file for the file t /* * function called when the file transfer is initiated. file content should be feed into object LinphoneContent * */ -void file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size){ - int offset=-1; - - if (!linphone_chat_message_get_user_data(message)) { - /*first chunk*/ - offset=0; - } else { - /*subsequent chunk*/ - offset = (int)((long)(linphone_chat_message_get_user_data(message))&0x00000000FFFFFFFF); +LinphoneBuffer * file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size){ + LinphoneBuffer *lb; + size_t file_size; + size_t size_to_send; + FILE *file_to_send; + uint8_t *buf; + if (size == 0) return linphone_buffer_new(); /*end of file*/ + file_to_send = linphone_chat_message_get_user_data(message); + fseek(file_to_send, 0, SEEK_END); + file_size = ftell(file_to_send); + fseek(file_to_send, offset, SEEK_SET); + size_to_send = MIN(size, file_size - offset); + buf = ms_malloc(size_to_send); + if (fread(buf, size_to_send, 1, file_to_send)!=size_to_send){ + ms_error("fread error"); } - *size = MIN(*size,sizeof(big_file)-offset); /*updating content->size with minimun between remaining data and requested size*/ + lb = linphone_buffer_new_from_data(buf, size_to_send); + ms_free(buf); + return lb; +} - if (*size==0) { - /*end of file*/ - return; - } - memcpy(buff,big_file+offset,*size); - - /*store offset for next chunk*/ - linphone_chat_message_set_user_data(message,(void*)(offset+*size)); +LinphoneBuffer * memory_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t size){ + size_t size_to_send = MIN(size, sizeof(big_file) - offset); + if (size == 0) return linphone_buffer_new(); /*end of file*/ + return linphone_buffer_new_from_data((uint8_t *)big_file + offset, size_to_send); } /** @@ -402,24 +406,60 @@ static void text_message_with_external_body(void) { linphone_core_manager_destroy(pauline); } +static bool_t compare_files(const char *path1, const char *path2) { + bool_t res; + size_t size1; + size_t size2; + uint8_t *buf1; + uint8_t *buf2; + FILE *f1 = fopen(path1, "rb"); + FILE *f2 = fopen(path2, "rb"); + fseek(f1, 0, SEEK_END); + size1 = ftell(f1); + fseek(f1, 0, SEEK_SET); + fseek(f2, 0, SEEK_END); + size2 = ftell(f2); + fseek(f2, 0, SEEK_SET); + if (size1 != size2) { + fclose(f1); + fclose(f2); + return FALSE; + } + buf1 = ms_malloc(size1); + buf2 = ms_malloc(size2); + if (fread(buf1, size1, 1, f1)!=size1){ + ms_error("fread() error"); + } + if (fread(buf2, size2, 1, f2)!=size2){ + ms_error("fread() error"); + } + fclose(f1); + fclose(f2); + res = (memcmp(buf1, buf2, size1) == 0) ? TRUE : FALSE; + ms_free(buf1); + ms_free(buf2); + return res; +} + static void file_transfer_message(void) { - int i; char* to; LinphoneChatRoom* chat_room; LinphoneChatMessage* message; LinphoneChatMessageCbs *cbs; LinphoneContent* content; - const char* big_file_content="big file"; /* setting dummy file content to something */ + FILE *file_to_send = NULL; + size_t file_size; + char *send_filepath = ms_strdup_printf("%s/images/nowebcamCIF.jpg", liblinphone_tester_file_prefix); + char *receive_filepath = ms_strdup_printf("%s/receive_file.dump", liblinphone_tester_writable_dir_prefix); LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); reset_counters(&marie->stat); reset_counters(&pauline->stat); - for (i=0;ilc,"https://www.linphone.org:444/lft.php"); @@ -430,11 +470,12 @@ static void file_transfer_message(void) { ms_free(to); /* create a file transfer message */ content = linphone_core_create_content(pauline->lc); - linphone_content_set_type(content,"text"); - linphone_content_set_subtype(content,"plain"); - linphone_content_set_size(content,sizeof(big_file)); /*total size to be transfered*/ - linphone_content_set_name(content,"bigfile.txt"); + linphone_content_set_type(content,"image"); + linphone_content_set_subtype(content,"jpeg"); + linphone_content_set_size(content,file_size); /*total size to be transfered*/ + linphone_content_set_name(content,"nowebcamCIF.jpg"); message = linphone_chat_room_create_file_transfer_message(chat_room, content); + linphone_chat_message_set_user_data(message, file_to_send); cbs = linphone_chat_message_get_callbacks(message); { int dummy=0; @@ -443,8 +484,10 @@ static void file_transfer_message(void) { reset_counters(&pauline->stat); } linphone_chat_message_cbs_set_msg_state_changed(cbs,liblinphone_tester_chat_message_msg_state_changed); + linphone_chat_message_cbs_set_file_transfer_send(cbs, file_transfer_send); linphone_chat_room_send_chat_message(chat_room,message); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); + fclose(file_to_send); if (marie->stat.last_received_chat_message ) { cbs = linphone_chat_message_get_callbacks(marie->stat.last_received_chat_message); linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed); @@ -456,10 +499,13 @@ static void file_transfer_message(void) { CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageInProgress,1); CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageDelivered,1); CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageExtBodyReceived,1); + CU_ASSERT_TRUE(compare_files(send_filepath, receive_filepath)); linphone_content_unref(content); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); + ms_free(send_filepath); + ms_free(receive_filepath); } /* same than previous but with a 160 characters file */ @@ -505,6 +551,7 @@ static void small_file_transfer_message(void) { } cbs = linphone_chat_message_get_callbacks(message); linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed); + linphone_chat_message_cbs_set_file_transfer_send(cbs, memory_file_transfer_send); linphone_chat_room_send_chat_message(chat_room,message); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); if (marie->stat.last_received_chat_message ) { @@ -566,6 +613,7 @@ static void file_transfer_message_io_error_upload(void) { } cbs = linphone_chat_message_get_callbacks(message); linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed); + linphone_chat_message_cbs_set_file_transfer_send(cbs, memory_file_transfer_send); linphone_chat_message_cbs_set_file_transfer_progress_indication(cbs, file_transfer_progress_indication); linphone_chat_room_send_chat_message(chat_room,message); @@ -696,6 +744,7 @@ static void file_transfer_message_upload_cancelled(void) { } cbs = linphone_chat_message_get_callbacks(message); linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed); + linphone_chat_message_cbs_set_file_transfer_send(cbs, memory_file_transfer_send); linphone_chat_message_cbs_set_file_transfer_progress_indication(cbs, file_transfer_progress_indication); linphone_chat_room_send_chat_message(chat_room,message); diff --git a/tester/presence_tester.c b/tester/presence_tester.c index ca3c8c18e..ed39d1b74 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -336,7 +336,7 @@ static void presence_information(void) { } /* Presence timestamp. */ - current_timestamp = time(NULL); + current_timestamp = ms_time(NULL); presence = linphone_presence_model_new_with_activity(LinphonePresenceActivityShopping, NULL); linphone_core_set_presence_model(pauline->lc, presence); wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphonePresenceActivityShopping,1); diff --git a/tester/setup_tester.c b/tester/setup_tester.c index 21fcfd091..bab077623 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -132,9 +132,13 @@ static void linphone_lpconfig_from_file_zerolen_value(){ char* rc_path = ms_strdup_printf("%s/rcfiles/%s", liblinphone_tester_file_prefix, zero_rc_file); LpConfig* conf; - conf = lp_config_new(rc_path); + /* not using lp_config_new() because it expects a readable file, and iOS (for instance) + stores the app bundle in read-only */ + conf = lp_config_new_with_factory(NULL, rc_path); CU_ASSERT_STRING_EQUAL(lp_config_get_string(conf,"test","zero_len","LOL"),"LOL"); + + // non_zero_len=test -> should return test CU_ASSERT_STRING_EQUAL(lp_config_get_string(conf,"test","non_zero_len",""),"test"); lp_config_set_string(conf, "test", "non_zero_len", ""); /* should remove "non_zero_len" */ diff --git a/tester/sounds/hello8000.wav b/tester/sounds/hello8000.wav index b787b202e..b5629df7a 100644 Binary files a/tester/sounds/hello8000.wav and b/tester/sounds/hello8000.wav differ diff --git a/tester/tester.c b/tester/tester.c index 7cb44fc36..8c09b90f1 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -18,6 +18,7 @@ #include #include "CUnit/TestRun.h" +#include "CUnit/Automated.h" #include "linphonecore.h" #include "private.h" #include "liblinphone_tester.h" @@ -42,6 +43,9 @@ int liblinphone_tester_use_log_file=0; static int liblinphone_tester_keep_accounts_flag = 0; static int manager_count = 0; +static const char* liblinphone_tester_xml_file = NULL; +static int liblinphone_tester_xml_enabled = FALSE; + #if WINAPI_FAMILY_PHONE_APP const char *liblinphone_tester_file_prefix="Assets"; #elif defined(__QNX__) @@ -263,18 +267,26 @@ LinphoneCoreManager* linphone_core_manager_new2(const char* rc_file, int check_f manager_count++; #if TARGET_OS_IPHONE - linphone_core_set_playback_device( mgr->lc, "AU: Audio Unit Tester"); - linphone_core_set_capture_device( mgr->lc, "AU: Audio Unit Tester"); linphone_core_set_ringer_device( mgr->lc, "AQ: Audio Queue Device"); linphone_core_set_ringback(mgr->lc, NULL); - if( manager_count >= 2){ - ms_message("Manager for '%s' using files", rc_file ? rc_file : "--"); - linphone_core_use_files(mgr->lc, TRUE); - } #endif - if (proxy_count) - wait_for_until(mgr->lc,NULL,&mgr->stat.number_of_LinphoneRegistrationOk,proxy_count,5000*proxy_count); + if( manager_count >= 2){ + char hellopath[512]; + ms_message("Manager for '%s' using files", rc_file ? rc_file : "--"); + linphone_core_use_files(mgr->lc, TRUE); + snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); + linphone_core_set_play_file(mgr->lc,hellopath); + } + + if (proxy_count){ +#define REGISTER_TIMEOUT 20 /* seconds */ + int success = wait_for_until(mgr->lc,NULL,&mgr->stat.number_of_LinphoneRegistrationOk, + proxy_count,(REGISTER_TIMEOUT * 1000 * proxy_count)); + if( !success ){ + ms_error("Did not register after %d seconds for %d proxies", REGISTER_TIMEOUT, proxy_count); + } + } CU_ASSERT_EQUAL(mgr->stat.number_of_LinphoneRegistrationOk,proxy_count); enable_codec(mgr->lc,"PCMU",8000); @@ -502,49 +514,57 @@ int liblinphone_tester_run_tests(const char *suite_name, const char *test_name) CU_set_suite_start_handler(test_suite_start_message_handler); + if( liblinphone_tester_xml_file != NULL ){ + CU_set_output_filename(liblinphone_tester_xml_file); + } + if( liblinphone_tester_xml_enabled != 0 ){ + CU_automated_run_tests(); + } else { + #if !HAVE_CU_GET_SUITE - if( suite_name ){ - ms_warning("Tester compiled without CU_get_suite() function, running all tests instead of suite '%s'\n", suite_name); - } -#else - if (suite_name){ - CU_pSuite suite; - suite=CU_get_suite(suite_name); - if (!suite) { - ms_error("Could not find suite '%s'. Available suites are:", suite_name); - liblinphone_tester_list_suites(); - return -1; - } else if (test_name) { - CU_pTest test=CU_get_test_by_name(test_name, suite); - if (!test) { - ms_error("Could not find test '%s' in suite '%s'. Available tests are:", test_name, suite_name); - // do not use suite_name here, since this method is case sentisitive - liblinphone_tester_list_suite_tests(suite->pName); - return -2; - } else { - CU_ErrorCode err= CU_run_test(suite, test); - if (err != CUE_SUCCESS) ms_error("CU_basic_run_test error %d", err); - } - } else { - CU_run_suite(suite); + if( suite_name ){ + ms_warning("Tester compiled without CU_get_suite() function, running all tests instead of suite '%s'\n", suite_name); } - } - else -#endif - { -#if HAVE_CU_CURSES - if (curses) { - /* Run tests using the CUnit curses interface */ - CU_curses_run_tests(); +#else + if (suite_name){ + CU_pSuite suite; + suite=CU_get_suite(suite_name); + if (!suite) { + ms_error("Could not find suite '%s'. Available suites are:", suite_name); + liblinphone_tester_list_suites(); + return -1; + } else if (test_name) { + CU_pTest test=CU_get_test_by_name(test_name, suite); + if (!test) { + ms_error("Could not find test '%s' in suite '%s'. Available tests are:", test_name, suite_name); + // do not use suite_name here, since this method is case sentisitive + liblinphone_tester_list_suite_tests(suite->pName); + return -2; + } else { + CU_ErrorCode err= CU_run_test(suite, test); + if (err != CUE_SUCCESS) ms_error("CU_basic_run_test error %d", err); + } + } else { + CU_run_suite(suite); + } } else #endif { - /* Run all tests using the CUnit Basic interface */ - CU_run_all_tests(); +#if HAVE_CU_CURSES + if (curses) { + /* Run tests using the CUnit curses interface */ + CU_curses_run_tests(); + } + else +#endif + { + /* Run all tests using the CUnit Basic interface */ + CU_run_all_tests(); + } } - } + } ret=CU_get_number_of_tests_failed()!=0; /* Redisplay list of failed tests on end */ @@ -603,4 +623,18 @@ void liblinphone_tester_clear_accounts(void){ account_manager_destroy(); } +void liblinphone_tester_enable_xml( bool_t enable ){ + liblinphone_tester_xml_enabled = enable; +} + +void liblinphone_tester_set_xml_output(const char *xml_path ) { + liblinphone_tester_xml_file = xml_path; +} + +const char* liblinphone_tester_get_xml_output( void ) { + return liblinphone_tester_xml_file; +} + + + diff --git a/tools/genapixml.py b/tools/genapixml.py index 42afb2e08..656428b9e 100755 --- a/tools/genapixml.py +++ b/tools/genapixml.py @@ -433,9 +433,16 @@ class Project: if pos == -1: return None returntype = definition[0:pos].strip() - if returntype != "void": - return None returnarg = CArgument(returntype, enums = self.enums, structs = self.__structs) + returndesc = node.find("./detaileddescription/para/simplesect[@kind='return']") + if returndesc is not None: + if returnarg.ctype == 'MSList': + n = returndesc.find('.//mslist') + if n is not None: + returnarg.containedType = n.text + returnarg.description = self.__cleanDescription(returndesc) + elif returnarg.completeType != 'void': + missingDocWarning += "\tReturn value is not documented\n" definition = definition[pos + 2 :] pos = string.find(definition, "(") definition = definition[pos + 1 : -1] diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py index 8d89b79c2..da91429b5 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -23,7 +23,7 @@ import sys import xml.etree.ElementTree as ET sys.path.append(os.path.realpath(__file__)) -from apixml2python.linphone import LinphoneModule, HandWrittenClassMethod, HandWrittenInstanceMethod, HandWrittenProperty +from apixml2python.linphone import LinphoneModule, HandWrittenClassMethod, HandWrittenInstanceMethod, HandWrittenDeallocMethod, HandWrittenProperty blacklisted_classes = [ @@ -71,12 +71,16 @@ blacklisted_functions = [ 'lp_config_section_to_dict' # missing LinphoneDictionary ] hand_written_functions = [ - HandWrittenInstanceMethod('ChatRoom', 'send_message2', 'linphone_chat_room_send_message2'), - HandWrittenProperty('Content', 'buffer', 'linphone_content_get_buffer', 'linphone_content_set_buffer'), - HandWrittenProperty('Core', 'sound_devices', 'linphone_core_get_sound_devices', None), - HandWrittenProperty('Core', 'video_devices', 'linphone_core_get_video_devices', None), - HandWrittenClassMethod('Core', 'new', 'linphone_core_new'), - HandWrittenClassMethod('Core', 'new_with_config', 'linphone_core_new_with_config') + HandWrittenClassMethod('Buffer', 'new_from_data', 'linphone_buffer_new_from_data', "Create a new LinphoneBuffer object from existing data.\n\n:param data: The initial data to store in the LinphoneBuffer.\n:type data: ByteArray\n:returns: A new LinphoneBuffer object.\n:rtype: linphone.Buffer"), + HandWrittenProperty('Buffer', 'content', 'linphone_buffer_get_content', 'linphone_buffer_set_content', "[ByteArray] Set the content of the data buffer."), + HandWrittenProperty('Content', 'buffer', 'linphone_content_get_buffer', 'linphone_content_set_buffer', "[ByteArray] Set the content data buffer."), + HandWrittenProperty('Core', 'sip_transports', 'linphone_core_get_sip_transports', 'linphone_core_set_sip_transports', "[:py:class:`linphone.SipTransports`] Sets the ports to be used for each transport. A zero value port for a given transport means the transport is not used. A value of LC_SIP_TRANSPORT_RANDOM (-1) means the port is to be chosen randomly by the system."), + HandWrittenProperty('Core', 'sip_transports_used', 'linphone_core_get_sip_transports_used', None, "[:py:class:`linphone.SipTransports`] Retrieves the real port number assigned for each sip transport (udp, tcp, tls). A zero value means that the transport is not activated. If LC_SIP_TRANSPORT_RANDOM was passed to :py:attr:`linphone.Core.sip_transports`, the random port choosed by the system is returned."), + HandWrittenProperty('Core', 'sound_devices', 'linphone_core_get_sound_devices', None, "[list of string] Get the available sound devices."), + HandWrittenProperty('Core', 'video_devices', 'linphone_core_get_video_devices', None, "[list of string] Get the available video capture devices."), + HandWrittenClassMethod('Core', 'new', 'linphone_core_new', "Instantiate a LinphoneCore object.\n\n:param vtable: The callbacks.\n:type vtable: dictionary\n:param configPath: A path to a config file. If it does not exists it will be created. The config file is used to store all settings, call logs, friends, proxies... so that all these settings become persistent over the life of the LinphoneCore object. It is allowed to set to None. In that case LinphoneCore will not store any settings.\n:type configPath: string\n:param factoryConfigPath: A path to a read-only config file that can be used to store hard-coded preference such as proxy settings or internal preferences. The settings in this factory file always override the one in the normal config file. It is OPTIONAL, use None if unneeded.\n:type factoryConfigPath: string\n:rtype: linphone.Core"), + HandWrittenClassMethod('Core', 'new_with_config', 'linphone_core_new_with_config', "Instantiate a LinphoneCore object from a LpConfig.\n\n:param vtable: The callbacks.\n:type vtable: dictionary\n:param config: A LpConfig object holding the configuration of the LinphoneCore to be instantiated.\n:rtype: linphone.Core"), + HandWrittenDeallocMethod('Core', 'linphone_core_destroy') ] def generate(apixmlfile, outputfile): diff --git a/tools/python/apixml2python/handwritten_declarations.mustache b/tools/python/apixml2python/handwritten_declarations.mustache index 78fcebc22..4a4b2c1ef 100644 --- a/tools/python/apixml2python/handwritten_declarations.mustache +++ b/tools/python/apixml2python/handwritten_declarations.mustache @@ -1,3 +1,7 @@ +static PyObject * pylinphone_Core_get_sip_transports(PyObject *self, void *closure); +static int pylinphone_Core_set_sip_transports(PyObject *self, PyObject *value, void *closure); +static void pylinphone_Core_dealloc(PyObject *self); +static PyObject * pylinphone_Core_get_sip_transports_used(PyObject *self, void *closure); static PyObject * pylinphone_Core_get_sound_devices(PyObject *self, void *closure); static PyObject * pylinphone_Core_get_video_devices(PyObject *self, void *closure); @@ -25,5 +29,8 @@ PyObject * PyLinphoneSipTransports_FromLCSipTransports(LCSipTransports lcst); time_t PyDateTime_As_time_t(PyObject *obj); PyObject * PyDateTime_From_time_t(time_t t); +static PyObject * pylinphone_Buffer_get_content(PyObject *self, void *closure); +static int pylinphone_Buffer_set_content(PyObject *self, PyObject *value, void *closure); + static PyObject * pylinphone_Content_get_buffer(PyObject *self, void *closure); static int pylinphone_Content_set_buffer(PyObject *self, PyObject *value, void *closure); diff --git a/tools/python/apixml2python/handwritten_definitions.mustache b/tools/python/apixml2python/handwritten_definitions.mustache index 3ff9db17b..13e27d873 100644 --- a/tools/python/apixml2python/handwritten_definitions.mustache +++ b/tools/python/apixml2python/handwritten_definitions.mustache @@ -128,6 +128,75 @@ static PyObject * pylinphone_module_method_set_log_handler(PyObject *self, PyObj } +static PyObject * pylinphone_Core_get_sip_transports(PyObject *self, void *closure) { + PyObject *pytr; + LCSipTransports tr = { 0 }; + LinphoneCore *native_ptr = pylinphone_Core_get_native_ptr(self); + + if (native_ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "Invalid linphone.Core instance"); + return NULL; + } + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p])", __FUNCTION__, self, native_ptr); + linphone_core_get_sip_transports(native_ptr, &tr); + pylinphone_dispatch_messages(); + + pytr = PyLinphoneSipTransports_FromLCSipTransports(tr); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> %p", __FUNCTION__, pytr); + return pytr; +} + +static int pylinphone_Core_set_sip_transports(PyObject *self, PyObject *value, void *closure) { + LinphoneCore *native_ptr; + PyObject * _tr_config; + const LCSipTransports * _tr_config_native_obj; + native_ptr = pylinphone_Core_get_native_ptr(self); + if (native_ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "Invalid linphone.Core instance"); + return -1; + } + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the 'sip_transports' attribute."); + return -1; + } + if (!PyLinphoneSipTransports_Check(value)) { + PyErr_SetString(PyExc_TypeError, "The 'sip_transports' attribute value must be a linphone.SipTransports."); + return -1; + } + + _tr_config = value; + _tr_config_native_obj = PyLinphoneSipTransports_AsLCSipTransports(value); + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p], %p [%p])", __FUNCTION__, self, native_ptr, _tr_config, _tr_config_native_obj); + linphone_core_set_sip_transports(native_ptr, _tr_config_native_obj); + pylinphone_dispatch_messages(); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> 0", __FUNCTION__); + return 0; +} + +static PyObject * pylinphone_Core_get_sip_transports_used(PyObject *self, void *closure) { + PyObject *pytr; + LCSipTransports tr = { 0 }; + LinphoneCore *native_ptr = pylinphone_Core_get_native_ptr(self); + + if (native_ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "Invalid linphone.Core instance"); + return NULL; + } + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p])", __FUNCTION__, self, native_ptr); + linphone_core_get_sip_transports_used(native_ptr, &tr); + pylinphone_dispatch_messages(); + + pytr = PyLinphoneSipTransports_FromLCSipTransports(tr); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> %p", __FUNCTION__, pytr); + return pytr; +} + + + static PyObject * pylinphone_Core_get_sound_devices(PyObject *self, void *closure) { PyObject *_list; const char **_devices; @@ -178,6 +247,13 @@ static PyObject * pylinphone_Core_get_video_devices(PyObject *self, void *closur return _list; } +static void pylinphone_init_ms2_plugins(void) { + ms_init(); // Initialize mediastreamer2 before loading the plugins +#ifdef ENABLE_OPENH264 + libmsopenh264_init(); +#endif +} + static PyObject * pylinphone_Core_class_method_new(PyObject *cls, PyObject *args) { LinphoneCore * cresult; pylinphone_CoreObject *self; @@ -206,6 +282,7 @@ static PyObject * pylinphone_Core_class_method_new(PyObject *cls, PyObject *args {{/core_events}} pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p, \"%s\", \"%s\")", __FUNCTION__, _vtable_dict, _config_path, _factory_config_path); + pylinphone_init_ms2_plugins(); cresult = linphone_core_new(&_vtable, _config_path, _factory_config_path, self); self->native_ptr = cresult; @@ -248,6 +325,7 @@ static PyObject * pylinphone_Core_class_method_new_with_config(PyObject *cls, Py {{/core_events}} pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p])", __FUNCTION__, _config, _config_native_ptr); + pylinphone_init_ms2_plugins(); cresult = linphone_core_new_with_config(&_vtable, _config_native_ptr, self); self->native_ptr = cresult; @@ -258,64 +336,22 @@ static PyObject * pylinphone_Core_class_method_new_with_config(PyObject *cls, Py return pyret; } +static void pylinphone_Core_dealloc(PyObject *self) { + LinphoneCore * native_ptr = pylinphone_Core_get_native_ptr(self); + if (Py_REFCNT(self) < 0) return; + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p])", __FUNCTION__, self, native_ptr); + + if (native_ptr != NULL) { + linphone_core_destroy(native_ptr); + ms_exit(); // De-initialize mediastreamer + } -static void pylinphone_ChatRoom_callback_chat_message_state_changed(LinphoneChatMessage *msg, LinphoneChatMessageState state, void *ud) { - PyGILState_STATE pygil_state; - PyObject *pycm = NULL; - PyObject *_dict = (PyObject *)ud; - PyObject *_cb = PyDict_GetItemString(_dict, "callback"); - PyObject *_ud = PyDict_GetItemString(_dict, "user_data"); - - pygil_state = PyGILState_Ensure(); - pycm = pylinphone_ChatMessage_from_native_ptr(&pylinphone_ChatMessageType, msg); - pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p, %p [%p], %d, %p)", __FUNCTION__, pycm, msg, state, ud); - if ((_cb != NULL) && PyCallable_Check(_cb)) { - PyObject *args = Py_BuildValue("OiO", pycm, state, _ud); - if (PyEval_CallObject(_cb, args) == NULL) { - PyErr_Print(); - } - Py_DECREF(args); - } - pylinphone_trace(-1, "[PYLINPHONE] <<< %s", __FUNCTION__); - PyGILState_Release(pygil_state); -} - -static PyObject * pylinphone_ChatRoom_instance_method_send_message2(PyObject *self, PyObject *args) { - PyObject *_chat_message; - PyObject *_dict; - PyObject *_cb; - PyObject *_ud; - LinphoneChatMessage * _chat_message_native_ptr; - LinphoneChatRoom *native_ptr = pylinphone_ChatRoom_get_native_ptr(self); - - if (native_ptr == NULL) { - PyErr_SetString(PyExc_TypeError, "Invalid linphone.ChatRoom instance"); - return NULL; - } - if (!PyArg_ParseTuple(args, "OOO", &_chat_message, &_cb, &_ud)) { - return NULL; - } - if (!PyObject_IsInstance(_chat_message, (PyObject *)&pylinphone_ChatMessageType)) { - PyErr_SetString(PyExc_TypeError, "The msg argument must be a linphone.ChatMessage"); - return NULL; - } - if ((_cb != Py_None) && !PyCallable_Check(_cb)) { - PyErr_SetString(PyExc_TypeError, "The status_cb argument must be a callable"); - return NULL; - } - if ((_chat_message_native_ptr = pylinphone_ChatMessage_get_native_ptr(_chat_message)) == NULL) { - return NULL; - } - _dict = PyDict_New(); - PyDict_SetItemString(_dict, "callback", _cb); - PyDict_SetItemString(_dict, "user_data", _ud); - - pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p], %p [%p], %p, %p)", __FUNCTION__, self, native_ptr, _chat_message, _chat_message_native_ptr, _cb, _ud); - linphone_chat_room_send_message2(native_ptr, _chat_message_native_ptr, pylinphone_ChatRoom_callback_chat_message_state_changed, _dict); pylinphone_dispatch_messages(); + Py_XDECREF(((pylinphone_CoreObject *)self)->user_data); + Py_XDECREF(((pylinphone_CoreObject *)self)->vtable_dict); - pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> None", __FUNCTION__); - Py_RETURN_NONE; + self->ob_type->tp_free(self); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s", __FUNCTION__); } @@ -647,6 +683,91 @@ static PyMethodDef pylinphone_PayloadTypeType_ModuleMethods[] = { }; +static PyObject * pylinphone_Buffer_class_method_new_from_data(PyObject *cls, PyObject *args) { + LinphoneBuffer * cresult; + pylinphone_BufferObject *self; + PyObject * pyret; + PyObject * _byte_array; + + if (!PyArg_ParseTuple(args, "O", &_byte_array)) { + return NULL; + } + if (!PyByteArray_Check(_byte_array)) { + PyErr_SetString(PyExc_TypeError, "The argument must be a ByteArray"); + return NULL; + } + + self = (pylinphone_BufferObject *)PyObject_CallObject((PyObject *) &pylinphone_BufferType, NULL); + if (self == NULL) { + return NULL; + } + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p)", __FUNCTION__, _byte_array); + cresult = linphone_buffer_new_from_data((uint8_t *)PyByteArray_AsString(_byte_array), PyByteArray_Size(_byte_array)); + self->native_ptr = cresult; + + pyret = Py_BuildValue("O", self); + + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> %p", __FUNCTION__, pyret); + Py_DECREF(self); + return pyret; +} + +static PyObject * pylinphone_Buffer_get_content(PyObject *self, void *closure) { + const uint8_t * ccontent; + size_t csize; + PyObject * pyresult; + PyObject * pyret; + const char *pyret_fmt; + const LinphoneBuffer *native_ptr; + native_ptr = pylinphone_Buffer_get_native_ptr(self); + if (native_ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "Invalid linphone.Buffer instance"); + return NULL; + } + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p])", __FUNCTION__, self, native_ptr); + ccontent = linphone_buffer_get_content(native_ptr); + csize = linphone_buffer_get_size(native_ptr); + pylinphone_dispatch_messages(); + + pyresult = PyByteArray_FromStringAndSize((const char *)ccontent, csize); + pyret = Py_BuildValue("O", pyresult); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> %p", __FUNCTION__, pyret); + return pyret; +} + +static int pylinphone_Buffer_set_content(PyObject *self, PyObject *value, void *closure) { + LinphoneBuffer *native_ptr; + uint8_t * _content; + size_t _size; + LinphonePresenceModel * _presence_native_ptr = NULL; + native_ptr = pylinphone_Buffer_get_native_ptr(self); + if (native_ptr == NULL) { + PyErr_SetString(PyExc_TypeError, "Invalid linphone.Buffer instance"); + return -1; + } + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the 'content' attribute."); + return -1; + } + if ((value != Py_None) && !PyByteArray_Check(value)) { + PyErr_SetString(PyExc_TypeError, "The 'content' attribute value must be a ByteArray instance."); + return -1; + } + + _content = (uint8_t *)PyByteArray_AsString(value); + _size = PyByteArray_Size(value); + + pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p [%p], %p [%p])", __FUNCTION__, self, native_ptr, value, _content); + linphone_buffer_set_content(native_ptr, _content, _size); + pylinphone_dispatch_messages(); + pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> 0", __FUNCTION__); + return 0; +} + + static PyObject * pylinphone_Content_get_buffer(PyObject *self, void *closure) { void * cbuffer; size_t csize; diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index 93f5f4c31..16d726850 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -15,6 +15,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +import re from sets import Set import sys @@ -25,6 +26,18 @@ def strip_leading_linphone(s): else: return s +def remove_useless_enum_prefix(senum, svalue): + lenum = re.findall('[A-Z][^A-Z]*', senum) + lvalue = re.findall('[A-Z][^A-Z]*', svalue) + if len(lenum) == 0 or len(lvalue) == 0: + return svalue + if lenum[0] == lvalue[0]: + i = 0 + while i < len(lenum) and lenum[i] == lvalue[i]: + i += 1 + return ''.join(lvalue[i:]) + return svalue + def is_callback(s): return s.startswith('Linphone') and s.endswith('Cb') @@ -42,27 +55,32 @@ def compute_event_name(s, className): class HandWrittenCode: - def __init__(self, _class, name, func_list): + def __init__(self, _class, name, func_list, doc = ''): self._class = _class self.name = name self.func_list = func_list + self.doc = doc class HandWrittenInstanceMethod(HandWrittenCode): - def __init__(self, _class, name, cfunction): - HandWrittenCode.__init__(self, _class, name, [cfunction]) + def __init__(self, _class, name, cfunction, doc = ''): + HandWrittenCode.__init__(self, _class, name, [cfunction], doc) class HandWrittenClassMethod(HandWrittenCode): - def __init__(self, _class, name, cfunction): - HandWrittenCode.__init__(self, _class, name, [cfunction]) + def __init__(self, _class, name, cfunction, doc = ''): + HandWrittenCode.__init__(self, _class, name, [cfunction], doc) + +class HandWrittenDeallocMethod(HandWrittenCode): + def __init__(self, _class, cfunction): + HandWrittenCode.__init__(self, _class, 'dealloc', [cfunction], '') class HandWrittenProperty(HandWrittenCode): - def __init__(self, _class, name, getter_cfunction = None, setter_cfunction = None): + def __init__(self, _class, name, getter_cfunction = None, setter_cfunction = None, doc = ''): func_list = [] if getter_cfunction is not None: func_list.append(getter_cfunction) if setter_cfunction is not None: func_list.append(setter_cfunction) - HandWrittenCode.__init__(self, _class, name, func_list) + HandWrittenCode.__init__(self, _class, name, func_list, doc) self.getter_cfunction = getter_cfunction self.setter_cfunction = setter_cfunction @@ -74,8 +92,8 @@ class ArgumentType: self.contained_type = contained_type self.linphone_module = linphone_module self.type_str = None - self.check_func = None - self.convert_func = None + self.check_condition = None + self.convert_code = None self.convert_from_func = None self.fmt_str = 'O' self.cfmt_str = '%p' @@ -91,33 +109,33 @@ class ArgumentType: if self.basic_type == 'char': if '*' in splitted_type: self.type_str = 'string' - self.check_func = 'PyString_Check' - self.convert_func = 'PyString_AsString' + self.check_condition = "!PyString_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyString_AsString({arg_name});\n" self.fmt_str = 'z' self.cfmt_str = '\\"%s\\"' else: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsLong' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsLong({arg_name});\n" self.fmt_str = 'b' self.cfmt_str = '%08x' elif self.basic_type == 'int': if 'unsigned' in splitted_type: self.type_str = 'unsigned int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsUnsignedLongMask' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsUnsignedLongMask({arg_name});\n" self.fmt_str = 'I' self.cfmt_str = '%u' else: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AS_LONG' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AS_LONG({arg_name});\n" self.fmt_str = 'i' self.cfmt_str = '%d' elif self.basic_type in ['int8_t', 'int16_t' 'int32_t']: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AS_LONG' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AS_LONG({arg_name});\n" if self.basic_type == 'int8_t': self.fmt_str = 'c' elif self.basic_type == 'int16_t': @@ -127,8 +145,8 @@ class ArgumentType: self.cfmt_str = '%d' elif self.basic_type in ['uint8_t', 'uint16_t', 'uint32_t']: self.type_str = 'unsigned int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsUnsignedLongMask' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsUnsignedLongMask({arg_name});\n" if self.basic_type == 'uint8_t': self.fmt_str = 'b' elif self.basic_type == 'uint16_t': @@ -138,66 +156,83 @@ class ArgumentType: self.cfmt_str = '%u' elif self.basic_type == 'int64_t': self.type_str = '64bits int' - self.check_func = 'PyLong_Check' - self.convert_func = 'PyLong_AsLongLong' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(PY_LONG_LONG)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}PyLong_AsLongLong({arg_name}); +""" self.fmt_str = 'L' self.cfmt_str = '%ld' elif self.basic_type == 'uint64_t': self.type_str = '64bits unsigned int' - self.check_func = 'PyLong_Check' - self.convert_func = 'PyLong_AsUnsignedLongLong' + self.check_condition = "!PyLong_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLong_AsUnsignedLongLong({arg_name});\n" self.fmt_str = 'K' self.cfmt_str = '%lu' elif self.basic_type == 'size_t': - self.type_str = 'size_t' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsSsize_t' + self.type_str = 'int' + self.check_condition = "!PyInt_Check({arg_name}) && !PyLong_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(size_t)PyInt_AsSsize_t({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(size_t)PyLong_AsSsize_t({arg_name}); +""" self.fmt_str = 'n' self.cfmt_str = '%lu' - elif self.basic_type in ['float', 'double']: + elif self.basic_type == 'float': self.type_str = 'float' - self.check_func = 'PyFloat_Check' - self.convert_func = 'PyFloat_AsDouble' - if self.basic_type == 'float': - self.fmt_str = 'f' - elif self.basic_type == 'double': - self.fmt_str = 'd' + self.check_condition = "!PyFloat_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyLong_AsLong({arg_name}); + else if (PyFloat_Check({arg_name})) {result_name}{result_suffix} = {cast}(float)PyFloat_AsDouble({arg_name}); +""" + self.fmt_str = 'f' + self.cfmt_str = '%f' + elif self.basic_type == 'double': + self.type_str = 'float' + self.check_condition = "!PyFloat_Check({arg_name})" + self.convert_code = \ +"""if (PyInt_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyInt_AsLong({arg_name}); + else if (PyLong_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyLong_AsLong({arg_name}); + else if (PyFloat_Check({arg_name})) {result_name}{result_suffix} = {cast}(double)PyFloat_AsDouble({arg_name}); +""" + self.fmt_str = 'd' self.cfmt_str = '%f' elif self.basic_type == 'bool_t': self.type_str = 'bool' - self.check_func = 'PyBool_Check' - self.convert_func = 'PyObject_IsTrue' + self.check_condition = "!PyBool_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyObject_IsTrue({arg_name});\n" self.convert_from_func = 'PyBool_FromLong' self.fmt_str = 'O' self.cfmt_str = '%p' self.cnativefmt_str = '%u' elif self.basic_type == 'time_t': self.type_str = 'DateTime' - self.check_func = 'PyDateTime_Check' - self.convert_func = 'PyDateTime_As_time_t' + self.check_condition = "!PyDateTime_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyDateTime_As_time_t({arg_name});\n" self.convert_from_func = 'PyDateTime_From_time_t' self.fmt_str = 'O' self.cfmt_str = '%p' self.cnativefmt_str = '%ld' elif self.basic_type == 'MSList': self.type_str = 'list of linphone.' + self.contained_type - self.check_func = 'PyList_Check' - self.convert_func = 'PyList_AsMSListOf' + self.contained_type + self.check_condition = "!PyList_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyList_AsMSListOf" + self.contained_type + "({arg_name});\n" self.convert_from_func = 'PyList_FromMSListOf' + self.contained_type self.fmt_str = 'O' self.cfmt_str = '%p' elif self.basic_type == 'MSVideoSize': self.type_str = 'linphone.VideoSize' - self.check_func = 'PyLinphoneVideoSize_Check' - self.convert_func = 'PyLinphoneVideoSize_AsMSVideoSize' + self.check_condition = "!PyLinphoneVideoSize_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLinphoneVideoSize_AsMSVideoSize({arg_name});\n" self.convert_from_func = 'PyLinphoneVideoSize_FromMSVideoSize' self.fmt_str = 'O' self.cfmt_str = '%p' self.cast_convert_func_result = False elif self.basic_type == 'LCSipTransports': self.type_str = 'linphone.SipTransports' - self.check_func = 'PyLinphoneSipTransports_Check' - self.convert_func = 'PyLinphoneSipTransports_AsLCSipTransports' + self.check_condition = "!PyLinphoneSipTransports_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyLinphoneSipTransports_AsLCSipTransports({arg_name});\n" self.convert_from_func = 'PyLinphoneSipTransports_FromLCSipTransports' self.fmt_str = 'O' self.cfmt_str = '%p' @@ -205,13 +240,13 @@ class ArgumentType: else: if strip_leading_linphone(self.basic_type) in self.linphone_module.enum_names: self.type_str = 'int' - self.check_func = 'PyInt_Check' - self.convert_func = 'PyInt_AsLong' + self.check_condition = "!PyInt_Check({arg_name})" + self.convert_code = "{result_name}{result_suffix} = {cast}PyInt_AsLong({arg_name});\n" self.fmt_str = 'i' self.cfmt_str = '%d' elif is_callback(self.complete_type): self.type_str = 'callable' - self.check_func = 'PyCallable_Check' + self.check_condition = "!PyCallable_Check({arg_name})" self.cnativefmt_str = None elif '*' in splitted_type: self.type_str = 'linphone.' + strip_leading_linphone(self.basic_type) @@ -238,24 +273,7 @@ class MethodDefinition: self.method_type = 'instancemethod' def format_local_variables_definition(self): - body = '' - if self.xml_method_return is not None: - self.return_type = self.xml_method_return.get('type') - self.return_complete_type = self.xml_method_return.get('completetype') - self.return_contained_type = self.xml_method_return.get('containedtype') - if is_callback(self.return_complete_type): - body += "\tPyObject * pyresult;\n" - body += "\tPyObject * pyret;\n" - argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) - self.build_value_format = argument_type.fmt_str - elif self.return_complete_type != 'void': - body += "\t" + self.return_complete_type + " cresult;\n" - argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) - self.build_value_format = argument_type.fmt_str - if self.build_value_format == 'O': - body += "\tPyObject * pyresult;\n" - body += "\tPyObject * pyret;\n" - body += "\tconst char *pyret_fmt;\n" + body = self.format_local_return_variables_definition() if self.self_arg is not None: body += "\t" + self.self_arg.get('completetype') + "native_ptr;\n" for xml_method_arg in self.xml_method_args: @@ -270,7 +288,7 @@ class MethodDefinition: elif argument_type.fmt_str == 'O' and argument_type.use_native_pointer: body += "\tPyObject * " + arg_name + ";\n" body += "\t" + arg_complete_type + " " + arg_name + "_native_ptr = NULL;\n" - elif argument_type.fmt_str == 'O' and argument_type.convert_func is not None: + elif argument_type.fmt_str == 'O' and argument_type.convert_code is not None: body += "\tPyObject * " + arg_name + ";\n" body += "\t" + arg_complete_type + " " + arg_name + "_native_obj;\n" elif strip_leading_linphone(arg_complete_type) in self.linphone_module.enum_names: @@ -298,10 +316,8 @@ class MethodDefinition: arg_complete_type = xml_method_arg.get('completetype') arg_contained_type = xml_method_arg.get('containedtype') argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) - if argument_type.fmt_str == 'O' and argument_type.convert_func is not None: - args_conversion_code += \ -""" {arg_name}_native_obj = {convert_func}({arg_name}); -""".format(arg_name=arg_name, convert_func=argument_type.convert_func) + if argument_type.fmt_str == 'O' and argument_type.convert_code is not None: + args_conversion_code += argument_type.convert_code.format(result_name=arg_name, result_suffix='_native_obj', cast='', arg_name=arg_name) return \ """ {class_native_ptr_check_code} {parse_tuple_code} @@ -352,7 +368,7 @@ class MethodDefinition: argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) if argument_type.fmt_str == 'O' and argument_type.use_native_pointer: arg_names.append(arg_name + "_native_ptr") - elif argument_type.fmt_str == 'O' and argument_type.convert_func is not None: + elif argument_type.fmt_str == 'O' and argument_type.convert_code is not None: arg_names.append(arg_name + "_native_obj") else: arg_names.append(arg_name) @@ -370,6 +386,7 @@ class MethodDefinition: from_native_pointer_code = '' convert_from_code = '' build_value_code = '' + cfree_code = '' result_variable = '' if self.return_complete_type != 'void': if self.build_value_format == 'O': @@ -388,16 +405,20 @@ class MethodDefinition: result_variable = 'cresult' if result_variable != '': build_value_code = "pyret = Py_BuildValue(\"{fmt}\", {result_variable});".format(fmt=self.build_value_format, result_variable=result_variable) + if self.return_complete_type == 'char *': + cfree_code = 'ms_free(cresult);'; body = \ """ {c_function_call_code} pylinphone_dispatch_messages(); {from_native_pointer_code} {convert_from_code} {build_value_code} + {cfree_code} """.format(c_function_call_code=c_function_call_code, from_native_pointer_code=from_native_pointer_code, convert_from_code=convert_from_code, - build_value_code=build_value_code) + build_value_code=build_value_code, + cfree_code=cfree_code) return body def format_return_trace(self): @@ -444,11 +465,11 @@ class MethodDefinition: """.format(arg_name=arg_name, arg_type=strip_leading_linphone(arg_type), type_str=argument_type.type_str) else: body += \ -""" if (!{check_func}({arg_name})) {{ +""" if ({check_condition}) {{ PyErr_SetString(PyExc_TypeError, "The '{arg_name}' argument must be a {type_str} instance."); return NULL; }} -""".format(arg_name=arg_name, check_func=argument_type.check_func, type_str=argument_type.type_str) +""".format(arg_name=arg_name, check_condition=argument_type.check_condition.format(arg_name=arg_name), type_str=argument_type.type_str) if body != '': body = body[1:] # Remove leading '\t' return body @@ -473,6 +494,26 @@ class MethodDefinition: body = body[1:] # Remove leading '\t' return body + def format_local_return_variables_definition(self): + body = '' + if self.xml_method_return is not None: + self.return_type = self.xml_method_return.get('type') + self.return_complete_type = self.xml_method_return.get('completetype') + self.return_contained_type = self.xml_method_return.get('containedtype') + if is_callback(self.return_complete_type): + body += "\tPyObject * pyresult;\n" + body += "\tPyObject * pyret;\n" + argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) + self.build_value_format = argument_type.fmt_str + elif self.return_complete_type != 'void': + body += "\t" + self.return_complete_type + " cresult;\n" + argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) + self.build_value_format = argument_type.fmt_str + if self.build_value_format == 'O': + body += "\tPyObject * pyresult;\n" + body += "\tPyObject * pyret;\n" + return body + def parse_method_node(self): if self.method_node is not None: self.xml_method_return = self.method_node.find('./return') @@ -661,6 +702,12 @@ class DeallocMethodDefinition(MethodDefinition): def format_return_result(self): return '' + def format(self): + return \ +"""static void pylinphone_{class_name}_dealloc(PyObject *self) {{ +{method_body} +}}""".format(class_name=self.class_['class_name'], method_body=MethodDefinition.format(self)) + class GetterMethodDefinition(MethodDefinition): def __init__(self, linphone_module, class_, method_node = None): MethodDefinition.__init__(self, linphone_module, class_, method_node) @@ -670,7 +717,7 @@ class SetterMethodDefinition(MethodDefinition): MethodDefinition.__init__(self, linphone_module, class_, method_node) def format_arguments_parsing(self): - if self.first_argument_type.check_func is None: + if self.first_argument_type.check_condition is None: attribute_type_check_code = \ """if ((value != Py_None) && !PyObject_IsInstance(value, (PyObject *)&pylinphone_{class_name}Type)) {{ PyErr_SetString(PyExc_TypeError, "The '{attribute_name}' attribute value must be a linphone.{class_name} instance."); @@ -682,11 +729,11 @@ class SetterMethodDefinition(MethodDefinition): if self.first_argument_type.type_str == 'string': checknotnone = "(value != Py_None) && " attribute_type_check_code = \ -"""if ({checknotnone}!{checkfunc}(value)) {{ +"""if ({checknotnone}{check_condition}) {{ PyErr_SetString(PyExc_TypeError, "The '{attribute_name}' attribute value must be a {type_str}."); return -1; }} -""".format(checknotnone=checknotnone, checkfunc=self.first_argument_type.check_func, attribute_name=self.attribute_name, type_str=self.first_argument_type.type_str) +""".format(checknotnone=checknotnone, check_condition=self.first_argument_type.check_condition.format(arg_name='value'), attribute_name=self.attribute_name, type_str=self.first_argument_type.type_str) attribute_conversion_code = '' callback_setting_code = '' if is_callback(self.first_argument_type.complete_type): @@ -695,18 +742,17 @@ class SetterMethodDefinition(MethodDefinition): Py_INCREF(value); ((pylinphone_{class_name}Object *)self)->{callback_name} = value; """.format(class_name=self.class_['class_name'], callback_name=compute_event_name(self.first_arg_complete_type, self.class_['class_name'])) - if (self.first_argument_type.convert_func is None) or \ - (self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None): + if (self.first_argument_type.convert_code is None) or \ + (self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None): attribute_conversion_code += "{arg_name} = value;\n".format(arg_name="_" + self.first_arg_name) - if self.first_argument_type.convert_func is not None: + if self.first_argument_type.convert_code is not None: cast_code = '' suffix = '' if self.first_argument_type.cast_convert_func_result: cast_code = "({arg_type})".format(arg_type=self.first_arg_complete_type) - if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None: + if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None: suffix = '_native_obj' - attribute_conversion_code += "\t{arg_name}{suffix} = {cast_code}{convertfunc}(value);\n".format( - arg_name="_" + self.first_arg_name, suffix=suffix, cast_code=cast_code, convertfunc=self.first_argument_type.convert_func) + attribute_conversion_code += self.first_argument_type.convert_code.format(result_name="_" + self.first_arg_name, result_suffix=suffix, cast=cast_code, arg_name='value') attribute_native_ptr_check_code = '' if self.first_argument_type.use_native_pointer: attribute_native_ptr_check_code = \ @@ -743,7 +789,7 @@ class SetterMethodDefinition(MethodDefinition): suffix = '' if self.first_argument_type.fmt_str == 'O' and self.first_argument_type.use_native_pointer: suffix = '_native_ptr' - elif self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_func is not None: + elif self.first_argument_type.fmt_str == 'O' and self.first_argument_type.convert_code is not None: suffix = '_native_obj' return \ """ {method_name}(native_ptr, {arg_name}{suffix}); @@ -778,6 +824,7 @@ class EventCallbackMethodDefinition(MethodDefinition): nocallbacks_class_name = class_name if class_name.endswith('Cbs'): nocallbacks_class_name = class_name[:-3] + returnvars = self.format_local_return_variables_definition() common = \ """ pylinphone_{class_name}Object *pyself = (pylinphone_{class_name}Object *){function_prefix}get_user_data(self); PyObject *func; @@ -796,21 +843,26 @@ class EventCallbackMethodDefinition(MethodDefinition): argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self.linphone_module) if argument_type.fmt_str == 'O': specific += "\tPyObject * py" + arg_name + " = NULL;\n" - return "{common}\n{specific}".format(common=common, specific=specific) + return "{returnvars}\n{common}\n{specific}".format(returnvars=returnvars, common=common, specific=specific) def format_arguments_parsing(self): + return_str = '' + if self.return_complete_type != 'void': + argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) + if argument_type.fmt_str == 'O': + return_str = 'NULL' if self.class_['event_class'] == 'Core': return \ -""" if (Py_REFCNT(pyself) <= 0) return; +""" if (Py_REFCNT(pyself) <= 0) return {return_str}; func = PyDict_GetItemString(pyself->vtable_dict, "{name}"); pygil_state = PyGILState_Ensure(); -""".format(name=self.class_['event_name']) +""".format(name=self.class_['event_name'], return_str=return_str) else: return \ -""" if (Py_REFCNT(pyself) <= 0) return; +""" if (Py_REFCNT(pyself) <= 0) return {return_str}; func = pycbs->{event_name}; pygil_state = PyGILState_Ensure(); -""".format(event_name=self.class_['event_name']) +""".format(event_name=self.class_['event_name'], return_str=return_str) def format_enter_trace(self): fmt = '%p' @@ -832,6 +884,7 @@ class EventCallbackMethodDefinition(MethodDefinition): def format_c_function_call(self): create_python_objects_code = '' + convert_python_result_code = '' fmt = 'O' args = ['pyself'] for xml_method_arg in self.xml_method_args: @@ -852,22 +905,52 @@ class EventCallbackMethodDefinition(MethodDefinition): type_class = self.find_class_definition(arg_type) create_python_objects_code += "\t\tpy{name} = pylinphone_{arg_type}_from_native_ptr(&pylinphone_{arg_type}Type, {name});\n".format(name=arg_name, arg_type=strip_leading_linphone(arg_type)) args=', '.join(args) + if self.return_complete_type != 'void': + argument_type = ArgumentType(self.return_type, self.return_complete_type, self.return_contained_type, self.linphone_module) + if argument_type.fmt_str == 'O': + convert_python_result_code = \ +""" if ((pyresult != Py_None) && !PyObject_IsInstance(pyresult, (PyObject *)&pylinphone_{class_name}Type)) {{ + PyErr_SetString(PyExc_TypeError, "The return value must be a linphone.{class_name} instance."); + return NULL; + }} + if ((cresult = pylinphone_{class_name}_get_native_ptr(pyresult)) == NULL) {{ + return NULL; + }} +""".format(class_name=strip_leading_linphone(self.return_type)) + + else: + convert_python_result_code = '\t\t' + argument_type.convert_code.format(result_name='cresult', result_suffix='', cast='', arg_name='pyresult') return \ """ if ((func != NULL) && PyCallable_Check(func)) {{ {create_python_objects_code} args = Py_BuildValue("{fmt}", {args}); - if (PyEval_CallObject(func, args) == NULL) {{ + pyresult = PyEval_CallObject(func, args); + if (pyresult == NULL) {{ PyErr_Print(); }} Py_DECREF(args); +{convert_python_result_code} }} -""".format(fmt=fmt, args=args, create_python_objects_code=create_python_objects_code) +""".format(fmt=fmt, args=args, create_python_objects_code=create_python_objects_code, convert_python_result_code=convert_python_result_code) def format_return_trace(self): return "\tpylinphone_trace(-1, \"[PYLINPHONE] <<< %s\", __FUNCTION__);\n" def format_return_result(self): - return '\tPyGILState_Release(pygil_state);' + s = '\tPyGILState_Release(pygil_state);' + if self.return_complete_type != 'void': + s += '\n\treturn cresult;' + return s + + def format_local_return_variables_definition(self): + body = "\tPyObject * pyresult;" + if self.xml_method_return is not None: + self.return_type = self.xml_method_return.get('type') + self.return_complete_type = self.xml_method_return.get('completetype') + self.return_contained_type = self.xml_method_return.get('containedtype') + if self.return_complete_type != 'void': + body += "\n\t" + self.return_complete_type + " cresult;" + return body def format(self): body = MethodDefinition.format(self) @@ -882,10 +965,10 @@ class EventCallbackMethodDefinition(MethodDefinition): arg_complete_type = xml_method_arg.get('completetype') arguments.append(arg_complete_type + ' ' + arg_name) definition = \ -"""static void pylinphone_{class_name}_callback_{event_name}({arguments}) {{ +"""static {returntype} pylinphone_{class_name}_callback_{event_name}({arguments}) {{ {body} }} -""".format(class_name=class_name, event_name=self.class_['event_name'], arguments=', '.join(arguments), body=body) +""".format(returntype=self.return_complete_type, class_name=class_name, event_name=self.class_['event_name'], arguments=', '.join(arguments), body=body) return definition @@ -896,6 +979,7 @@ class LinphoneModule(object): self.mslist_types = Set([]) self.enums = [] self.enum_names = [] + self.cfunction2methodmap = {} hand_written_functions = [] for hand_written_code in hand_written_codes: hand_written_functions += hand_written_code.func_list @@ -906,18 +990,35 @@ class LinphoneModule(object): e = {} e['enum_name'] = strip_leading_linphone(xml_enum.get('name')) e['enum_doc'] = self.__format_doc_content(xml_enum.find('briefdescription'), xml_enum.find('detaileddescription')) - e['enum_doc'] += "\n\nValues:\n" + e['enum_doc'] = self.__replace_doc_special_chars(e['enum_doc']) + e['enum_doc'] += """ + +.. csv-table:: + :delim: | + :widths: 30, 70 + :header: Value,Description + +""" e['enum_values'] = [] + e['enum_deprecated_values'] = [] xml_enum_values = xml_enum.findall("./values/value") for xml_enum_value in xml_enum_values: if xml_enum_value.get('deprecated') == 'true': continue v = {} v['enum_value_cname'] = xml_enum_value.get('name') - v['enum_value_name'] = strip_leading_linphone(v['enum_value_cname']) + valname = strip_leading_linphone(v['enum_value_cname']) + v['enum_value_name'] = remove_useless_enum_prefix(e['enum_name'], valname) v['enum_value_doc'] = self.__format_doc(xml_enum_value.find('briefdescription'), xml_enum_value.find('detaileddescription')) - e['enum_doc'] += '\t' + v['enum_value_name'] + ': ' + v['enum_value_doc'] + '\n' + e['enum_doc'] += ' ' + v['enum_value_name'] + '|' + v['enum_value_doc'] + '\n' e['enum_values'].append(v) + if v['enum_value_name'] != valname: + # TODO: To remove. Add deprecated value name. + v = {} + v['enum_value_cname'] = xml_enum_value.get('name') + v['enum_value_name'] = strip_leading_linphone(v['enum_value_cname']) + v['enum_value_doc'] = self.__format_doc(xml_enum_value.find('briefdescription'), xml_enum_value.find('detaileddescription')) + e['enum_deprecated_values'].append(v) e['enum_doc'] = self.__replace_doc_special_chars(e['enum_doc']) self.enums.append(e) self.enum_names.append(e['enum_name']) @@ -971,11 +1072,15 @@ class LinphoneModule(object): if isinstance(hand_written_code, HandWrittenClassMethod): m = {} m['method_name'] = hand_written_code.name + m['method_doc'] = self.__replace_doc_special_chars(hand_written_code.doc) c['class_type_hand_written_methods'].append(m) elif isinstance(hand_written_code, HandWrittenInstanceMethod): m = {} m['method_name'] = hand_written_code.name + m['method_doc'] = self.__replace_doc_special_chars(hand_written_code.doc) c['class_instance_hand_written_methods'].append(m) + elif isinstance(hand_written_code, HandWrittenDeallocMethod): + c['class_has_hand_written_dealloc'] = True elif isinstance(hand_written_code, HandWrittenProperty): p = {} p['property_name'] = hand_written_code.name @@ -987,6 +1092,7 @@ class LinphoneModule(object): p['setter_reference'] = 'NULL' else: p['setter_reference'] = '(setter)pylinphone_' + c['class_name'] + '_set_' + p['property_name'] + p['property_doc'] = self.__replace_doc_special_chars(hand_written_code.doc) c['class_hand_written_properties'].append(p) xml_type_methods = xml_class.findall("./classmethods/classmethod") for xml_type_method in xml_type_methods: @@ -999,6 +1105,7 @@ class LinphoneModule(object): m['method_name'] = method_name.replace(c['class_c_function_prefix'], '') if method_name not in hand_written_functions: m['method_xml_node'] = xml_type_method + self.cfunction2methodmap[method_name] = ':py:meth:`linphone.' + c['class_name'] + '.' + m['method_name'] + '`' c['class_type_methods'].append(m) c['class_instance_methods'] = [] xml_instance_methods = xml_class.findall("./instancemethods/instancemethod") @@ -1014,6 +1121,7 @@ class LinphoneModule(object): m['method_name'] = method_name.replace(c['class_c_function_prefix'], '') if method_name not in hand_written_functions: m['method_xml_node'] = xml_instance_method + self.cfunction2methodmap[method_name] = ':py:meth:`linphone.' + c['class_name'] + '.' + m['method_name'] + '`' c['class_instance_methods'].append(m) c['class_properties'] = [] xml_properties = xml_class.findall("./properties/property") @@ -1040,6 +1148,7 @@ class LinphoneModule(object): p['getter_reference'] = "(getter)pylinphone_" + c['class_name'] + "_" + p['getter_name'] p['getter_definition_begin'] = "static PyObject * pylinphone_" + c['class_name'] + "_" + p['getter_name'] + "(PyObject *self, void *closure) {" p['getter_definition_end'] = "}" + self.cfunction2methodmap[xml_property_getter.get('name')] = ':py:attr:`linphone.' + c['class_name'] + '.' + property_name + '`' else: p['getter_reference'] = "NULL" if xml_property_setter is not None: @@ -1049,6 +1158,7 @@ class LinphoneModule(object): p['setter_reference'] = "(setter)pylinphone_" + c['class_name'] + "_" + p['setter_name'] p['setter_definition_begin'] = "static int pylinphone_" + c['class_name'] + "_" + p['setter_name'] + "(PyObject *self, PyObject *value, void *closure) {" p['setter_definition_end'] = "}" + self.cfunction2methodmap[xml_property_setter.get('name')] = ':py:attr:`linphone.' + c['class_name'] + '.' + property_name + '`' else: p['setter_reference'] = "NULL" c['class_properties'].append(p) @@ -1101,18 +1211,19 @@ class LinphoneModule(object): except Exception, e: e.args += (c['class_name'], p['property_name']) raise - try: - if c['class_refcountable']: - xml_instance_method = c['class_xml_node'].find("./instancemethods/instancemethod[@name='" + c['class_c_function_prefix'] + "unref']") - c['dealloc_body'] = DeallocMethodDefinition(self, c, xml_instance_method).format() - elif c['class_destroyable']: - xml_instance_method = c['class_xml_node'].find("./instancemethods/instancemethod[@name='" + c['class_c_function_prefix'] + "destroy']") - c['dealloc_body'] = DeallocMethodDefinition(self, c, xml_instance_method).format() - else: - c['dealloc_body'] = DeallocMethodDefinition(self, c).format() - except Exception, e: - e.args += (c['class_name'], 'dealloc_body') - raise + if not 'class_has_hand_written_dealloc' in c: + try: + if c['class_refcountable']: + xml_instance_method = c['class_xml_node'].find("./instancemethods/instancemethod[@name='" + c['class_c_function_prefix'] + "unref']") + c['dealloc_definition'] = DeallocMethodDefinition(self, c, xml_instance_method).format() + elif c['class_destroyable']: + xml_instance_method = c['class_xml_node'].find("./instancemethods/instancemethod[@name='" + c['class_c_function_prefix'] + "destroy']") + c['dealloc_definition'] = DeallocMethodDefinition(self, c, xml_instance_method).format() + else: + c['dealloc_definition'] = DeallocMethodDefinition(self, c).format() + except Exception, e: + e.args += (c['class_name'], 'dealloc_body') + raise # Convert mslist_types to a list of dictionaries for the template d = [] for mslist_type in self.mslist_types: @@ -1168,8 +1279,20 @@ class LinphoneModule(object): def __replace_doc_special_chars(self, doc): return doc.replace('"', '').encode('string-escape') + def __replace_doc_cfunction_by_method(self, doc): + for cfunction, method in self.cfunction2methodmap.iteritems(): + doc = doc.replace(cfunction + '()', method) + for cfunction, method in self.cfunction2methodmap.iteritems(): + doc = doc.replace(cfunction, method) + return doc + + def __replace_doc_keywords(self, doc): + return doc.replace('NULL', 'None') + def __format_doc(self, brief_description, detailed_description): doc = self.__format_doc_content(brief_description, detailed_description) + doc = self.__replace_doc_cfunction_by_method(doc) + doc = self.__replace_doc_keywords(doc) doc = self.__replace_doc_special_chars(doc) return doc @@ -1202,6 +1325,8 @@ class LinphoneModule(object): return_argument_type = ArgumentType(return_type, return_complete_type, return_contained_type, self) doc += '\n:returns: ' + return_doc doc += '\n:rtype: ' + return_argument_type.type_str + doc = self.__replace_doc_cfunction_by_method(doc) + doc = self.__replace_doc_keywords(doc) doc = self.__replace_doc_special_chars(doc) return doc @@ -1213,6 +1338,8 @@ class LinphoneModule(object): argument_type = ArgumentType(arg_type, arg_complete_type, arg_contained_type, self) doc = self.__format_doc_content(xml_node.find('briefdescription'), xml_node.find('detaileddescription')) doc = '[' + argument_type.type_str + '] ' + doc + doc = self.__replace_doc_cfunction_by_method(doc) + doc = self.__replace_doc_keywords(doc) doc = self.__replace_doc_special_chars(doc) return doc @@ -1224,5 +1351,7 @@ class LinphoneModule(object): return_argument_type = ArgumentType(return_type, return_complete_type, return_contained_type, self) doc = self.__format_doc_content(xml_node.find('briefdescription'), xml_node.find('detaileddescription')) doc = '[' + return_argument_type.type_str + '] ' + doc + doc = self.__replace_doc_cfunction_by_method(doc) + doc = self.__replace_doc_keywords(doc) doc = self.__replace_doc_special_chars(doc) return doc diff --git a/tools/python/apixml2python/linphone_module.mustache b/tools/python/apixml2python/linphone_module.mustache index e31451cfa..403a2491a 100644 --- a/tools/python/apixml2python/linphone_module.mustache +++ b/tools/python/apixml2python/linphone_module.mustache @@ -125,9 +125,7 @@ static int pylinphone_{{class_name}}_init(PyObject *self, PyObject *args, PyObje {{{init_body}}} } -static void pylinphone_{{class_name}}_dealloc(PyObject *self) { -{{{dealloc_body}}} -} +{{{dealloc_definition}}} {{#class_type_methods}} @@ -148,14 +146,14 @@ static PyObject * pylinphone_{{class_name}}_instance_method_{{method_name}}(PyOb static PyMethodDef pylinphone_{{class_name}}_methods[] = { /* Class methods */ {{#class_type_hand_written_methods}} - { "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "" }, + { "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "{{{method_doc}}}" }, {{/class_type_hand_written_methods}} {{#class_type_methods}} { "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "{{{method_doc}}}" }, {{/class_type_methods}} /* Instance methods */ {{#class_instance_hand_written_methods}} - { "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "" }, + { "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "{{{method_doc}}}" }, {{/class_instance_hand_written_methods}} {{#class_instance_methods}} { "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "{{{method_doc}}}" }, @@ -183,7 +181,7 @@ static PyMemberDef pylinphone_{{class_name}}_members[] = { static PyGetSetDef pylinphone_{{class_name}}_getseters[] = { {{#class_hand_written_properties}} - { "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "" }, + { "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "{{{property_doc}}}" }, {{/class_hand_written_properties}} {{#class_properties}} { "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "{{{property_doc}}}" }, @@ -308,12 +306,21 @@ PyMODINIT_FUNC initlinphone(void) { {{#enum_values}} if (PyModule_AddIntConstant(menum, "{{enum_value_name}}", {{enum_value_cname}}) < 0) return; {{/enum_values}} +{{#enum_deprecated_values}} + if (PyModule_AddIntConstant(menum, "{{enum_value_name}}", {{enum_value_cname}}) < 0) return; +{{/enum_deprecated_values}} {{/enums}} - menum = Py_InitModule3("PayloadTypeType", pylinphone_PayloadTypeType_ModuleMethods, "Type of linphone.PayloadType."); + menum = Py_InitModule3("PayloadTypeType", pylinphone_PayloadTypeType_ModuleMethods, "Type of :py:class:`linphone.PayloadType`.\n\n.. csv-table::\n :delim: |\n :header: Value,Description\n\n AudioContinuous|\n AudioPacketized|\n Video|\n Text|\n Other|\n"); if (menum == NULL) return; Py_INCREF(menum); if (PyModule_AddObject(m, "PayloadTypeType", menum) < 0) return; + if (PyModule_AddIntConstant(menum, "AudioContinuous", PAYLOAD_AUDIO_CONTINUOUS) < 0) return; + if (PyModule_AddIntConstant(menum, "AudioPacketized", PAYLOAD_AUDIO_PACKETIZED) < 0) return; + if (PyModule_AddIntConstant(menum, "Video", PAYLOAD_VIDEO) < 0) return; + if (PyModule_AddIntConstant(menum, "Text", PAYLOAD_TEXT) < 0) return; + if (PyModule_AddIntConstant(menum, "Other", PAYLOAD_OTHER) < 0) return; + // TODO: To remove. Deprecated enum values. if (PyModule_AddIntConstant(menum, "PAYLOAD_AUDIO_CONTINUOUS", PAYLOAD_AUDIO_CONTINUOUS) < 0) return; if (PyModule_AddIntConstant(menum, "PAYLOAD_AUDIO_PACKETIZED", PAYLOAD_AUDIO_PACKETIZED) < 0) return; if (PyModule_AddIntConstant(menum, "PAYLOAD_VIDEO", PAYLOAD_VIDEO) < 0) return; diff --git a/tools/python/doc/Makefile b/tools/python/doc/Makefile index b8c40c718..158616058 100644 --- a/tools/python/doc/Makefile +++ b/tools/python/doc/Makefile @@ -48,8 +48,12 @@ help: clean: rm -rf $(BUILDDIR)/* + rm -f source/enums.rst -html: +source/enums.rst: + python generate_enums.py -o source/enums.rst + +html: source/enums.rst $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/tools/python/doc/generate_enums.py b/tools/python/doc/generate_enums.py new file mode 100644 index 000000000..2f24a7a93 --- /dev/null +++ b/tools/python/doc/generate_enums.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +import argparse +import types +import sys + +def main(argv = None): + if argv is None: + argv = sys.argv + argparser = argparse.ArgumentParser(description="Generate enums documentation of the Linphone API.") + argparser.add_argument('-o', '--outputfile', metavar='outputfile', type=argparse.FileType('w'), help="Output .rst file describing the Linphone API enums.") + args = argparser.parse_args() + if args.outputfile == None: + args.outputfile = open('enums.rst', 'w') + + module = __import__('linphone', globals(), locals()) + + for name in dir(module): + if name == 'testing' or name == 'linphone': + continue + if type(getattr(module, name)) == types.ModuleType: + args.outputfile.write('linphone.' + name + '\n') + args.outputfile.write('^' * len('linphone.' + name) + '\n\n') + args.outputfile.write(getattr(module, name).__doc__) + args.outputfile.write('\n') + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/python/doc/source/_static/pylinphone.js b/tools/python/doc/source/_static/pylinphone.js new file mode 100644 index 000000000..b0b7617a0 --- /dev/null +++ b/tools/python/doc/source/_static/pylinphone.js @@ -0,0 +1,52 @@ +$(function (){ +var createList = function(selector){ + + var ul = $('