From 89565386ac1fe2c9c610342ef488c36a83158eba Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 10 Dec 2014 16:33:12 +0100 Subject: [PATCH 01/74] Working file transfer in Python. - Add the LinphoneBuffer object so that file transfer callbacks do not pass out parameters - Use the LinphoneBuffer object in the file transfer callbacks - Implement the file_transfer_send callback in the Python file transfer test --- build/android/Android.mk | 1 + build/wp8/LibLinphone.vcxproj | 2 + coreapi/CMakeLists.txt | 2 + coreapi/Makefile.am | 2 + coreapi/buffer.c | 105 ++++++++++++++++++ coreapi/buffer.h | 147 +++++++++++++++++++++++++ coreapi/chat.c | 16 ++- coreapi/linphonecore.h | 22 ++-- coreapi/private.h | 9 ++ tools/genapixml.py | 11 +- tools/python/apixml2python.py | 3 + tools/python/apixml2python/linphone.py | 84 ++++++++++---- tools/python/unittests/test_message.py | 25 ++++- 13 files changed, 381 insertions(+), 48 deletions(-) create mode 100644 coreapi/buffer.c create mode 100644 coreapi/buffer.h 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/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index 92689dcb8..1a4f48665 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 @@ -153,6 +154,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/buffer.c b/coreapi/buffer.c new file mode 100644 index 000000000..8e44f832b --- /dev/null +++ b/coreapi/buffer.c @@ -0,0 +1,105 @@ +/* +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); + memcpy(buffer->content, content, size); +} + +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/chat.c b/coreapi/chat.c index 7bec11b01..d80f133b1 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); @@ -1205,7 +1211,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); @@ -1279,7 +1287,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/linphonecore.h b/coreapi/linphonecore.h index 81d9e4ab0..5c24d14a7 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -366,12 +366,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" @@ -1350,30 +1352,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. diff --git a/coreapi/private.h b/coreapi/private.h index a8816b04b..fb84eab14 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -936,6 +936,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); /***************************************************************************** @@ -1012,6 +1020,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/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..ff62efd30 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -38,6 +38,9 @@ blacklisted_events = [ 'LinphoneCoreFileTransferSendCb' # missing LinphoneContent ] blacklisted_functions = [ + 'linphone_buffer_new_from_data', + 'linphone_buffer_get_content', + 'linphone_buffer_set_content', 'linphone_call_log_get_local_stats', # missing rtp_stats_t 'linphone_call_log_get_remote_stats', # missing rtp_stats_t 'linphone_call_params_get_privacy', # missing LinphonePrivacyMask diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index 93f5f4c31..19f4cd6f7 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -238,24 +238,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: @@ -473,6 +456,27 @@ 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" + body += "\tconst char *pyret_fmt;\n" + return body + def parse_method_node(self): if self.method_node is not None: self.xml_method_return = self.method_node.find('./return') @@ -778,6 +782,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,7 +801,7 @@ 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): if self.class_['event_class'] == 'Core': @@ -832,6 +837,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 +858,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\tcresult = {convertfunc}(pyresult);\n".format(convertfunc=argument_type.convert_func) 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 +918,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 diff --git a/tools/python/unittests/test_message.py b/tools/python/unittests/test_message.py index ebd53fc66..a17b8f47e 100644 --- a/tools/python/unittests/test_message.py +++ b/tools/python/unittests/test_message.py @@ -3,11 +3,17 @@ from copy import deepcopy import linphone from linphonetester import * import os +import os.path import time class TestMessage: + @classmethod + def teardown_class(cls): + if os.path.exists('receive_file.dump'): + os.remove('receive_file.dump') + @classmethod def msg_state_changed(cls, msg, state): stats = msg.chat_room.core.user_data.stats @@ -24,18 +30,23 @@ class TestMessage: linphonetester_logger.error("[TESTER] Unexpected state [{state}] for message [{msg}]".format(msg=msg, state=linphone.ChatMessageState.string(state))) @classmethod - def file_transfer_received(cls, msg, content, buf, size): - print buf, size + def file_transfer_send(cls, msg, content, offset, size): + if offset >= len(msg.user_data): + return linphone.Buffer.new() # end of file + return linphone.Buffer.new_from_string(msg.user_data[offset:offset+size]) + + @classmethod + def file_transfer_recv(cls, msg, content, buf): stats = msg.chat_room.core.user_data.stats if msg.user_data is None: msg.user_data = open('receive_file.dump', 'wb') - msg.user_data.write(buf) + msg.user_data.write(buf.string_content) else: - if size == 0: # Transfer complete + if buf.size == 0: # Transfer complete stats.number_of_LinphoneMessageExtBodyReceived += 1 msg.user_data.close() else: # Store content - msg.user_data.write(buf) + msg.user_data.write(buf.string_content) def wait_for_server_to_purge_messages(self, manager1, manager2): # Wait a little bit just to have time to purge message stored in the server @@ -87,14 +98,16 @@ class TestMessage: content.size = len(big_file) # total size to be transfered content.name = 'bigfile.txt' message = chat_room.create_file_transfer_message(content) + message.user_data = big_file # Store the file in the user data of the chat message self.wait_for_server_to_purge_messages(marie, pauline) message.callbacks.msg_state_changed = TestMessage.msg_state_changed + message.callbacks.file_transfer_send = TestMessage.file_transfer_send chat_room.send_chat_message(message) assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: marie.stats.number_of_LinphoneMessageReceivedWithFile == 1), True) if marie.stats.last_received_chat_message is not None: cbs = marie.stats.last_received_chat_message.callbacks cbs.msg_state_changed = TestMessage.msg_state_changed - cbs.file_transfer_recv = TestMessage.file_transfer_received + cbs.file_transfer_recv = TestMessage.file_transfer_recv marie.stats.last_received_chat_message.download_file() assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: marie.stats.number_of_LinphoneMessageExtBodyReceived == 1), True) assert_equals(pauline.stats.number_of_LinphoneMessageInProgress, 1) From 099d846fbeffc65a4039ace21006b44655117e10 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 10 Dec 2014 16:52:56 +0100 Subject: [PATCH 02/74] Fix file transfer tests. --- tester/liblinphone_tester.h | 4 ++-- tester/message_tester.c | 34 +++++++++++----------------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2c926b27d..2921a4349 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -257,8 +257,8 @@ 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); 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/message_tester.c b/tester/message_tester.c index 784acb49b..62bf70302 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); @@ -76,12 +76,12 @@ void file_transfer_received(LinphoneChatMessage *message, const LinphoneContent* /*next chunk*/ file = (FILE*)linphone_chat_message_get_user_data(message); - if (size==0) { /* tranfer complete */ + 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(buff,size,1,file)==-1){ + 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 +93,10 @@ 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); - } - *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)); +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); } /** @@ -443,6 +427,7 @@ 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)); if (marie->stat.last_received_chat_message ) { @@ -505,6 +490,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, 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 +552,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, 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 +683,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, 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); From e568e7e21db6cc03c6c3eadecf504c643846bda9 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Wed, 10 Dec 2014 17:33:30 +0100 Subject: [PATCH 03/74] Don't use 'tester' audio unit in iOS, since linphone is now intelligent enough to not kill the audio unit in between ringing and actual call. This allows tests to run more closely to the real soudncard usage --- mediastreamer2 | 2 +- tester/tester.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index faabba0fb..a9b195ade 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit faabba0fb7d388cf20c620c103e4955e8afc903f +Subproject commit a9b195ade60a094240700dc6cbe6d72663f2e524 diff --git a/tester/tester.c b/tester/tester.c index 7cb44fc36..2d39e7e67 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -263,8 +263,6 @@ 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){ From 4f155a034728a8890f9a667484a6e43990a423b9 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Wed, 10 Dec 2014 17:46:44 +0100 Subject: [PATCH 04/74] Minor MacOS README updates --- README.macos.md | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/README.macos.md b/README.macos.md index cfe0a40a9..563a5b407 100644 --- a/README.macos.md +++ b/README.macos.md @@ -1,12 +1,16 @@ -# 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/). -### 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 +22,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,16 +39,23 @@ 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 install automake intltool libtool pkg-config coreutils \ + yasm nasm wget imagemagick speex ffmpeg + + #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 +##### Linphone UI (GTK version) + + brew install gettext pygtk gtk+ + brew link gettext --force + #readline is required from linphonec.c otherwise compilation will fail + brew link readline --force + + ### Building Linphone The next pieces need to be compiled manually. @@ -58,7 +68,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 @@ -117,6 +127,7 @@ The libvpx build isn't able to produce dual architecture files. To workaround th * Install on the system sudo make install + You are done. ### Generate portable bundle @@ -126,7 +137,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`. From 26620d4c0ff8627525949b40adc69fe647799a9e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 09:35:52 +0100 Subject: [PATCH 05/74] Fix compilation of file transfer tutorial. --- coreapi/help/filetransfer.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/coreapi/help/filetransfer.c b/coreapi/help/filetransfer.c index c592c501b..b3863c248 100644 --- a/coreapi/help/filetransfer.c +++ b/coreapi/help/filetransfer.c @@ -63,7 +63,7 @@ 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*/ @@ -72,16 +72,14 @@ static void file_transfer_received(LinphoneChatMessage *message, const LinphoneC } else { /*next chunk*/ file = (FILE*)linphone_chat_message_get_user_data(message); - - if (size==0) { - + 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(buff,size,1,file)==-1){ + 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)); } } @@ -89,30 +87,14 @@ static void file_transfer_received(LinphoneChatMessage *message, const LinphoneC } 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); } /* From 95de57a6c6752fcd436799d9a59ea98a2a5b4cf2 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 11 Dec 2014 11:16:15 +0100 Subject: [PATCH 06/74] Fix ec calibration crash in Android --- coreapi/linphonecore_jni.cc | 39 +++++++++++-------- .../org/linphone/core/LinphoneCore.java | 5 ++- .../linphone/core/LinphoneCoreListener.java | 20 +++++----- .../org/linphone/core/LinphoneCoreImpl.java | 5 ++- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 00e2c3692..98f14bff9 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -763,17 +763,24 @@ 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 ecData = env->NewGlobalRef((jobject) data); + jobject state = env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass, lcData->ecCalibratorStatusFromIntId, (jint)status); + env->CallVoidMethod(lcData->listener + ,lcData->ecCalibrationStatusId + ,lcData->core + ,state + ,delay_ms + ,ecData); + env->DeleteGlobalRef(ecData); + } + if (status != LinphoneEcCalibratorInProgress) { + linphone_core_v_table_destroy(table); + } } } @@ -1727,11 +1734,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); } diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 93c5fe1cf..4b8f42a1b 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; @@ -1057,10 +1058,10 @@ public interface LinphoneCore { /** * Start an echo calibration of the sound devices, in order to find adequate settings for the echo canceler automatically. * status is notified to {@link LinphoneCoreListener#ecCalibrationStatus(EcCalibratorStatus, int, Object)} - * @param User object + * @param listener the LinphoneEchoCalibrationListener to call when the calibration is done * @throws LinphoneCoreException if operation is still in progress; **/ - void startEchoCalibration(Object data) throws LinphoneCoreException; + void startEchoCalibration(LinphoneEchoCalibrationListener listener) throws LinphoneCoreException; /** * Returns true if echo calibration is recommended. diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index d92743ce1..4be9ff799 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -75,15 +75,6 @@ public interface LinphoneCoreListener { */ void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf); - /** - * Invoked when echo cancalation calibration is completed - * @param lc LinphoneCore - * @param status - * @param delay_ms echo delay - * @param data - */ - void ecCalibrationStatus(LinphoneCore lc,LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); - /** * Report Notified message received for this identity. * @param lc LinphoneCore @@ -243,5 +234,16 @@ public interface LinphoneCoreListener { */ void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr); } + + public interface LinphoneEchoCalibrationListener extends LinphoneCoreListener { + /** + * Invoked when echo cancalation calibration is completed + * @param lc LinphoneCore + * @param status + * @param delay_ms echo delay + * @param data + */ + void ecCalibrationStatus(LinphoneCore lc,LinphoneCore.EcCalibratorStatus status, int delay_ms, Object data); + } } diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index ea8899883..21a356490 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCoreListener.LinphoneEchoCalibrationListener; import org.linphone.mediastream.Log; import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.Hacks; @@ -558,8 +559,8 @@ class LinphoneCoreImpl implements LinphoneCore { public synchronized boolean isKeepAliveEnabled() { return isKeepAliveEnabled(nativePtr); } - public synchronized void startEchoCalibration(Object data) throws LinphoneCoreException { - startEchoCalibration(nativePtr, data); + public synchronized void startEchoCalibration(LinphoneEchoCalibrationListener listener) throws LinphoneCoreException { + startEchoCalibration(nativePtr, listener); } public synchronized Transports getSignalingTransportPorts() { From f3c7c993eacd8f9eccb8088e53875ccbd50f346b Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 11 Dec 2014 11:19:37 +0100 Subject: [PATCH 07/74] Fix mistake in previous commit --- coreapi/linphonecore_jni.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 98f14bff9..a56e1d14b 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -768,15 +768,13 @@ public: if (table) { LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table); if (lcData->ecCalibrationStatusId) { - jobject ecData = env->NewGlobalRef((jobject) data); jobject state = env->CallStaticObjectMethod(lcData->ecCalibratorStatusClass, lcData->ecCalibratorStatusFromIntId, (jint)status); env->CallVoidMethod(lcData->listener ,lcData->ecCalibrationStatusId ,lcData->core ,state ,delay_ms - ,ecData); - env->DeleteGlobalRef(ecData); + ,NULL); } if (status != LinphoneEcCalibratorInProgress) { linphone_core_v_table_destroy(table); From b0f621ad13b8f827b79bbd2ae6f6e73ba632606e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 11:40:28 +0100 Subject: [PATCH 08/74] Use real file for file upload test and check that the received file is the same as the sent one. --- tester/liblinphone_tester.h | 1 + tester/message_tester.c | 84 ++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 2921a4349..d7bbb64e9 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -259,6 +259,7 @@ void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const Linph void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage* message); 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/message_tester.c b/tester/message_tester.c index 62bf70302..bce4c2989 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -94,6 +94,25 @@ 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 * */ 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); + fread(buf, size_to_send, 1, file_to_send); + lb = linphone_buffer_new_from_data(buf, size_to_send); + ms_free(buf); + return lb; +} + +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); @@ -386,24 +405,56 @@ 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); + fread(buf1, size1, 1, f1); + fread(buf2, size2, 1, f2); + 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"); @@ -414,11 +465,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; @@ -430,6 +482,7 @@ static void file_transfer_message(void) { 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); @@ -441,10 +494,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 */ @@ -490,7 +546,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, file_transfer_send); + 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 ) { @@ -552,7 +608,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, file_transfer_send); + 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); @@ -683,7 +739,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, file_transfer_send); + 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); From 78a06d3e5474885f7c10ccf95aebae2548b3a964 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 11:46:08 +0100 Subject: [PATCH 09/74] Fix return value in callbacks of Python module. --- tools/python/apixml2python/linphone.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index 19f4cd6f7..ce333a475 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -804,18 +804,23 @@ class EventCallbackMethodDefinition(MethodDefinition): 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' From 24f4d44496c57f13d124b6769980dae703152068 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 14:34:45 +0100 Subject: [PATCH 10/74] There is no longer a dummy call to the file_transfer_recv callback at the beginning of a file download. --- coreapi/help/filetransfer.c | 25 ++++++++++++------------- tester/message_tester.c | 21 ++++++++++----------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/coreapi/help/filetransfer.c b/coreapi/help/filetransfer.c index b3863c248..570e22692 100644 --- a/coreapi/help/filetransfer.c +++ b/coreapi/help/filetransfer.c @@ -69,19 +69,18 @@ static void file_transfer_received(LinphoneChatMessage *message, const LinphoneC /*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 (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)); - } + } + + 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)); } } } diff --git a/tester/message_tester.c b/tester/message_tester.c index bce4c2989..7ec828ccc 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -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 (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)); - } + 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)); } } } From 8b33ed404ebbe56157e455c50b8ffaa4e71174f6 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 15:58:58 +0100 Subject: [PATCH 11/74] Always store a NULL character at the end of a LinphoneBuffer. --- coreapi/buffer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coreapi/buffer.c b/coreapi/buffer.c index 8e44f832b..9db65798e 100644 --- a/coreapi/buffer.c +++ b/coreapi/buffer.c @@ -78,8 +78,9 @@ const uint8_t * linphone_buffer_get_content(const LinphoneBuffer *buffer) { 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); + 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) { From 340d927c3c73d5a7cebeb30bac2d9bf5f608b747 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 11 Dec 2014 15:59:29 +0100 Subject: [PATCH 12/74] File transfer with binary content working in Python. --- tools/python/apixml2python.py | 6 +- .../handwritten_declarations.mustache | 3 + .../handwritten_definitions.mustache | 145 +++++++++------- tools/python/unittests/test_message.py | 157 ++++++++++++++---- 4 files changed, 219 insertions(+), 92 deletions(-) diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py index ff62efd30..d6746ad08 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -38,9 +38,6 @@ blacklisted_events = [ 'LinphoneCoreFileTransferSendCb' # missing LinphoneContent ] blacklisted_functions = [ - 'linphone_buffer_new_from_data', - 'linphone_buffer_get_content', - 'linphone_buffer_set_content', 'linphone_call_log_get_local_stats', # missing rtp_stats_t 'linphone_call_log_get_remote_stats', # missing rtp_stats_t 'linphone_call_params_get_privacy', # missing LinphonePrivacyMask @@ -74,7 +71,8 @@ blacklisted_functions = [ 'lp_config_section_to_dict' # missing LinphoneDictionary ] hand_written_functions = [ - HandWrittenInstanceMethod('ChatRoom', 'send_message2', 'linphone_chat_room_send_message2'), + HandWrittenClassMethod('Buffer', 'new_from_data', 'linphone_buffer_new_from_data'), + HandWrittenProperty('Buffer', 'content', 'linphone_buffer_get_content', 'linphone_buffer_set_content'), 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), diff --git a/tools/python/apixml2python/handwritten_declarations.mustache b/tools/python/apixml2python/handwritten_declarations.mustache index 78fcebc22..a9c2d1ee6 100644 --- a/tools/python/apixml2python/handwritten_declarations.mustache +++ b/tools/python/apixml2python/handwritten_declarations.mustache @@ -25,5 +25,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..6b2e176f1 100644 --- a/tools/python/apixml2python/handwritten_definitions.mustache +++ b/tools/python/apixml2python/handwritten_definitions.mustache @@ -259,66 +259,6 @@ static PyObject * pylinphone_Core_class_method_new_with_config(PyObject *cls, Py } -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(); - - pylinphone_trace(-1, "[PYLINPHONE] <<< %s -> None", __FUNCTION__); - Py_RETURN_NONE; -} - - static void pylinphone_VideoSize_dealloc(PyObject *self) { pylinphone_trace(1, "[PYLINPHONE] >>> %s(%p)", __FUNCTION__, self); @@ -647,6 +587,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/unittests/test_message.py b/tools/python/unittests/test_message.py index a17b8f47e..d4b83971e 100644 --- a/tools/python/unittests/test_message.py +++ b/tools/python/unittests/test_message.py @@ -1,5 +1,6 @@ from nose.tools import assert_equals from copy import deepcopy +import filecmp import linphone from linphonetester import * import os @@ -9,11 +10,6 @@ import time class TestMessage: - @classmethod - def teardown_class(cls): - if os.path.exists('receive_file.dump'): - os.remove('receive_file.dump') - @classmethod def msg_state_changed(cls, msg, state): stats = msg.chat_room.core.user_data.stats @@ -29,24 +25,63 @@ class TestMessage: else: linphonetester_logger.error("[TESTER] Unexpected state [{state}] for message [{msg}]".format(msg=msg, state=linphone.ChatMessageState.string(state))) + @classmethod + def file_transfer_progress_indication(cls, msg, content, offset, total): + stats = msg.chat_room.core.user_data.stats + progress = int((offset * 100) / total) + direction = 'received' + tofrom = 'from' + address = msg.from_address + if msg.outgoing: + direction = 'sent' + tofrom = 'to' + address = msg.to_address + linphonetester_logger.info("[TESTER] File transfer [{progress}%] {direction} of type [{type}/{subtype}] {tofrom} {address}".format( + progress=progress, direction=direction, type=content.type, subtype=content.subtype, tofrom=tofrom, address=address.as_string())); + stats.progress_of_LinphoneFileTransfer = progress + @classmethod def file_transfer_send(cls, msg, content, offset, size): - if offset >= len(msg.user_data): + send_filepath = msg.user_data + send_filesize = os.path.getsize(send_filepath) + if offset >= send_filesize: return linphone.Buffer.new() # end of file - return linphone.Buffer.new_from_string(msg.user_data[offset:offset+size]) + f = open(send_filepath, 'rb') + f.seek(offset, 0) + if (send_filesize - offset) < size: + size = send_filesize - offset + lb = linphone.Buffer.new_from_data(bytearray(f.read(size))) + f.close() + return lb @classmethod def file_transfer_recv(cls, msg, content, buf): + receive_filepath = msg.user_data stats = msg.chat_room.core.user_data.stats - if msg.user_data is None: - msg.user_data = open('receive_file.dump', 'wb') - msg.user_data.write(buf.string_content) - else: - if buf.size == 0: # Transfer complete - stats.number_of_LinphoneMessageExtBodyReceived += 1 - msg.user_data.close() - else: # Store content - msg.user_data.write(buf.string_content) + if buf.empty: # Transfer complete + stats.number_of_LinphoneMessageExtBodyReceived += 1 + else: # Store content + f = open(receive_filepath, 'ab') + f.write(buf.content) + f.close() + + @classmethod + def memory_file_transfer_send(cls, msg, content, offset, size): + send_buf = msg.user_data + send_size = len(send_buf) + if offset >= send_size: + return linphone.Buffer.new() + if (send_size - offset) < size: + size = send_size - offset + return linphone.Buffer.new_from_string(send_buf[offset:offset+size]) + + @classmethod + def memory_file_transfer_recv(cls, msg, content, buf): + stats = msg.chat_room.core.user_data.stats + if buf.empty: # Transfer complete + stats.number_of_LinphoneMessageExtBodyReceived += 1 + else: # Store content + msg.user_data += buf.string_content def wait_for_server_to_purge_messages(self, manager1, manager2): # Wait a little bit just to have time to purge message stored in the server @@ -81,24 +116,19 @@ class TestMessage: pauline.stop() def test_file_transfer_message(self): - big_file = "big file" marie = CoreManager('marie_rc') pauline = CoreManager('pauline_rc') - while len(big_file) < 128000: - big_file += big_file - l = list(big_file) - l[0] = 'S' - l[-1] = 'E' - big_file = ''.join(l) + send_filepath = os.path.join(tester_resources_path, 'images', 'nowebcamCIF.jpg') + receive_filepath = 'receive_file.dump' pauline.lc.file_transfer_server = "https://www.linphone.org:444/lft.php" chat_room = pauline.lc.get_chat_room(marie.identity) content = pauline.lc.create_content() - content.type = 'text' - content.subtype = 'plain' - content.size = len(big_file) # total size to be transfered - content.name = 'bigfile.txt' + content.type = 'image' + content.subtype = 'jpeg' + content.size = os.path.getsize(send_filepath) # total size to be transfered + content.name = 'nowebcamCIF.jpg' message = chat_room.create_file_transfer_message(content) - message.user_data = big_file # Store the file in the user data of the chat message + message.user_data = send_filepath self.wait_for_server_to_purge_messages(marie, pauline) message.callbacks.msg_state_changed = TestMessage.msg_state_changed message.callbacks.file_transfer_send = TestMessage.file_transfer_send @@ -108,8 +138,79 @@ class TestMessage: cbs = marie.stats.last_received_chat_message.callbacks cbs.msg_state_changed = TestMessage.msg_state_changed cbs.file_transfer_recv = TestMessage.file_transfer_recv + marie.stats.last_received_chat_message.user_data = receive_filepath marie.stats.last_received_chat_message.download_file() assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: marie.stats.number_of_LinphoneMessageExtBodyReceived == 1), True) assert_equals(pauline.stats.number_of_LinphoneMessageInProgress, 1) assert_equals(pauline.stats.number_of_LinphoneMessageDelivered, 1) assert_equals(marie.stats.number_of_LinphoneMessageExtBodyReceived, 1) + assert_equals(filecmp.cmp(send_filepath, receive_filepath, shallow=False), True) + if os.path.exists(receive_filepath): + os.remove(receive_filepath) + + def test_small_file_transfer_message(self): + send_buf = "small file" + marie = CoreManager('marie_rc') + pauline = CoreManager('pauline_rc') + while len(send_buf) < 160: + send_buf += send_buf + l = list(send_buf[0:160]) + l[0] = 'S' + l[-1] = 'E' + send_buf = ''.join(l) + pauline.lc.file_transfer_server = "https://www.linphone.org:444/lft.php" + chat_room = pauline.lc.get_chat_room(marie.identity) + content = pauline.lc.create_content() + content.type = 'text' + content.subtype = 'plain' + content.size = len(send_buf) # total size to be transfered + content.name = 'small_file.txt' + message = chat_room.create_file_transfer_message(content) + message.user_data = send_buf + self.wait_for_server_to_purge_messages(marie, pauline) + message.callbacks.msg_state_changed = TestMessage.msg_state_changed + message.callbacks.file_transfer_send = TestMessage.memory_file_transfer_send + chat_room.send_chat_message(message) + assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: marie.stats.number_of_LinphoneMessageReceivedWithFile == 1), True) + if marie.stats.last_received_chat_message is not None: + cbs = marie.stats.last_received_chat_message.callbacks + cbs.msg_state_changed = TestMessage.msg_state_changed + cbs.file_transfer_recv = TestMessage.memory_file_transfer_recv + marie.stats.last_received_chat_message.user_data = '' + marie.stats.last_received_chat_message.download_file() + assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: marie.stats.number_of_LinphoneMessageExtBodyReceived == 1), True) + assert_equals(pauline.stats.number_of_LinphoneMessageInProgress, 1) + assert_equals(pauline.stats.number_of_LinphoneMessageDelivered, 1) + assert_equals(marie.stats.number_of_LinphoneMessageExtBodyReceived, 1) + assert_equals(send_buf, marie.stats.last_received_chat_message.user_data) + + def test_file_transfer_message_upload_cancelled(self): + send_buf = "big file" + marie = CoreManager('marie_rc') + pauline = CoreManager('pauline_rc') + while len(send_buf) < 128000: + send_buf += send_buf + l = list(send_buf[0:128000]) + l[0] = 'S' + l[-1] = 'E' + send_buf = ''.join(l) + pauline.lc.file_transfer_server = "https://www.linphone.org:444/lft.php" + chat_room = pauline.lc.get_chat_room(marie.identity) + content = pauline.lc.create_content() + content.type = 'text' + content.subtype = 'plain' + content.size = len(send_buf) # total size to be transfered + content.name = 'big_file.txt' + message = chat_room.create_file_transfer_message(content) + message.user_data = send_buf + self.wait_for_server_to_purge_messages(marie, pauline) + message.callbacks.msg_state_changed = TestMessage.msg_state_changed + message.callbacks.file_transfer_send = TestMessage.memory_file_transfer_send + message.callbacks.file_transfer_progress_indication = TestMessage.file_transfer_progress_indication + chat_room.send_chat_message(message) + # Wait for file to be at least 50% uploaded and cancel the transfer + assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: pauline.stats.progress_of_LinphoneFileTransfer >= 50), True) + message.cancel_file_transfer() + assert_equals(CoreManager.wait_for(pauline, marie, lambda pauline, marie: pauline.stats.number_of_LinphoneMessageNotDelivered == 1), True) + assert_equals(pauline.stats.number_of_LinphoneMessageNotDelivered, 1) + assert_equals(marie.stats.number_of_LinphoneMessageExtBodyReceived, 0) From 11b9ed01d93e4f21861edfb36615d1029abb0218 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 11 Dec 2014 17:37:22 +0100 Subject: [PATCH 13/74] Improved LocalRef (see issue 1925) on Android JNI layer --- coreapi/linphonecore_jni.cc | 60 +++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index a56e1d14b..76cbbad5c 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; } @@ -577,7 +577,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 +592,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 +620,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 +640,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 +662,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; From 84e3d60838ed0e6a819d51a83229d42e2d3658a5 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Thu, 11 Dec 2014 17:39:25 +0100 Subject: [PATCH 14/74] fix the tests for ios --- tester/call_tester.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tester/call_tester.c b/tester/call_tester.c index 4e8a323c3..5a3a1c112 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -326,12 +326,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); @@ -2925,7 +2945,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); From db5fc6ea89802168e61a73d34cffa9582872d6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 12 Dec 2014 12:11:51 +0100 Subject: [PATCH 15/74] Fix the reading of .linpohne.ecstate --- coreapi/linphonecall.c | 10 ++++++---- coreapi/lpconfig.c | 29 +++++++++++++++-------------- coreapi/lpconfig.h | 8 +++++--- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index ce9bfa38b..9cf05f1a2 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"; +static const size_t EC_STATE_MAX_LEN = 1048576; // 1Mo static void linphone_call_stats_uninit(LinphoneCallStats *stats); @@ -1581,14 +1582,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); + char statestr[EC_STATE_MAX_LEN]; 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); - ms_free(statestr); + if (audiostream->ec) { + 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); + } } } audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc)); diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index a940fb696..ee87ac023 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(fgets(data, max_length, file) == NULL) { + 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 } From 1cf049cabdea59c22b8ddbdbb53d7c3dbaea8687 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 16:29:54 +0100 Subject: [PATCH 16/74] fix leak of RtpTransport when call is updated/paused. Fix reporting of bandwidth, which was displayed even if the stream was inactive. --- coreapi/linphonecall.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 9cf05f1a2..a1c3ad003 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1550,6 +1550,8 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ AudioStream *audiostream; const char *location; int dscp; + RtpTransport *meta_rtp=NULL; + RtpTransport *meta_rtcp=NULL; if (call->audiostream != NULL) return; if (call->sessions[0].rtp_session==NULL){ @@ -1600,12 +1602,11 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ } audio_stream_set_features(audiostream,linphone_core_get_audio_features(lc)); - - if (lc->rtptf){ + rtp_session_get_transports(audiostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (lc->rtptf && (meta_rtp==NULL && meta_rtcp==NULL)){ + /*the transport just need to be created once, then they are kept into the RtpSession, which is the same for the entire call duration.*/ RtpTransport *artp=lc->rtptf->audio_rtp_func(lc->rtptf->audio_rtp_func_data, call->media_ports[0].rtp_port); RtpTransport *artcp=lc->rtptf->audio_rtcp_func(lc->rtptf->audio_rtcp_func_data, call->media_ports[0].rtcp_port); - RtpTransport *meta_rtp; - RtpTransport *meta_rtcp; meta_rtp_transport_new(&meta_rtp,TRUE,artp, 0); meta_rtp_transport_new(&meta_rtcp,FALSE,artcp, 0); rtp_session_set_transports(audiostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); @@ -2781,14 +2782,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, From a6da440cfd453b93a0f0ea01ee86e693b58ad8cf Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 16:41:48 +0100 Subject: [PATCH 17/74] add test to check absence of SDP in ACK in a scenario of INVITE without SDP. --- tester/call_tester.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tester/call_tester.c b/tester/call_tester.c index 5a3a1c112..2d186e1e1 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -914,6 +914,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; @@ -3490,6 +3510,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 }, From 5863df792e94e9dc183fd7a138069867f2b61fb2 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 16:42:27 +0100 Subject: [PATCH 18/74] Make the preview window a real gtk window. Move camera preview test within the settings. --- NEWS | 11 +++++++ coreapi/linphonecore.c | 16 ++++++++++ coreapi/linphonecore.h | 1 + gtk/linphone.h | 1 + gtk/main.c | 22 ++----------- gtk/parameters.ui | 30 +++++++++++++----- gtk/videowindow.c | 71 +++++++++++++++++++++++++++++++++++++----- 7 files changed, 118 insertions(+), 34 deletions(-) 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/coreapi/linphonecore.c b/coreapi/linphonecore.c index c3a838aef..b0fa65e12 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -5640,6 +5640,22 @@ 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}; + if (lc->previewstream){ + ret=video_preview_get_current_size(lc->previewstream); + } + return ret; +} + /** * Sets the preview video size by its name. See linphone_core_set_preview_video_size() for more information about this feature. * diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 5c24d14a7..155170338 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -2811,6 +2811,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/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 2bb9d5446..4303a770c 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -827,9 +827,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)); } @@ -1023,13 +1020,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){ @@ -1041,7 +1031,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); } @@ -1838,10 +1827,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 @@ -1852,13 +1842,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 @@ -1969,6 +1952,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)); + } +} + From f48780782c2c6acfed277753d5de9c642e917f65 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 16:54:30 +0100 Subject: [PATCH 19/74] fix compilation --- coreapi/linphonecall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index a1c3ad003..e6329dbce 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2790,7 +2790,7 @@ static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *v 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_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; From 4292c1fc150ee25bad69e112d464a514d2e8b822 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 17:02:52 +0100 Subject: [PATCH 20/74] fix compilation errors --- tester/message_tester.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tester/message_tester.c b/tester/message_tester.c index 7ec828ccc..254737a04 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -105,7 +105,9 @@ LinphoneBuffer * file_transfer_send(LinphoneChatMessage *message, const Linphone fseek(file_to_send, offset, SEEK_SET); size_to_send = MIN(size, file_size - offset); buf = ms_malloc(size_to_send); - fread(buf, size_to_send, 1, file_to_send); + if (fread(buf, size_to_send, 1, file_to_send)!=size_to_send){ + ms_error("fread error"); + } lb = linphone_buffer_new_from_data(buf, size_to_send); ms_free(buf); return lb; @@ -425,8 +427,12 @@ static bool_t compare_files(const char *path1, const char *path2) { } buf1 = ms_malloc(size1); buf2 = ms_malloc(size2); - fread(buf1, size1, 1, f1); - fread(buf2, size2, 1, f2); + 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; From d997814f823d2a2bdfa6fe3427ca2fc9e53291da Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Fri, 12 Dec 2014 17:05:56 +0100 Subject: [PATCH 21/74] Better hinting for soundcard --- coreapi/linphonecore.c | 4 +--- mediastreamer2 | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b0fa65e12..694ad2695 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3938,10 +3938,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; } @@ -6484,8 +6484,6 @@ int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call) } lc->calls = the_calls; - linphone_core_soundcard_hint_check(lc); - return 0; } diff --git a/mediastreamer2 b/mediastreamer2 index a9b195ade..323dcac7f 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit a9b195ade60a094240700dc6cbe6d72663f2e524 +Subproject commit 323dcac7fd1b8f2fc5c2acaa1fe73dc3d4ce15eb From 1ac1cd5fe8aba5299771faffae8c408ded1ad035 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 12 Dec 2014 18:55:47 +0100 Subject: [PATCH 22/74] fix RtpTransport leak on video side as well --- coreapi/linphonecall.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index e6329dbce..2697724f5 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1621,6 +1621,8 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ void linphone_call_init_video_stream(LinphoneCall *call){ #ifdef VIDEO_ENABLED LinphoneCore *lc=call->core; + RtpTransport *meta_rtp=NULL; + RtpTransport *meta_rtcp=NULL; if (call->videostream == NULL){ int video_recv_buf_size=lp_config_get_int(lc->config,"video","recv_buf_size",0); @@ -1644,11 +1646,11 @@ 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){ + rtp_session_get_transports(call->videostream->ms.sessions.rtp_session,&meta_rtp,&meta_rtcp); + if (lc->rtptf && (meta_rtp==NULL && meta_rtcp==NULL)){ RtpTransport *vrtp=lc->rtptf->video_rtp_func(lc->rtptf->video_rtp_func_data, call->media_ports[1].rtp_port); RtpTransport *vrtcp=lc->rtptf->video_rtcp_func(lc->rtptf->video_rtcp_func_data, call->media_ports[1].rtcp_port); - RtpTransport *meta_rtp; - RtpTransport *meta_rtcp; + meta_rtp_transport_new(&meta_rtp,TRUE,vrtp, 0); meta_rtp_transport_new(&meta_rtcp,FALSE,vrtcp, 0); rtp_session_set_transports(call->videostream->ms.sessions.rtp_session,meta_rtp,meta_rtcp); From d9bc65287c1e77de3f2d27db46e683bfec43aa6e Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 15 Dec 2014 11:10:44 +0100 Subject: [PATCH 23/74] Enable XML output for liblinphone tester --- tester/liblinphone_tester.c | 20 ++++++++ tester/liblinphone_tester.h | 5 ++ tester/tester.c | 96 +++++++++++++++++++++++-------------- 3 files changed, 86 insertions(+), 35 deletions(-) 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 d7bbb64e9..0ae708b70 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 diff --git a/tester/tester.c b/tester/tester.c index 2d39e7e67..125ebd5df 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__) @@ -500,49 +504,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 */ @@ -601,4 +613,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; +} + + + From 04a66a165c4a53a2dc5708d7730a12ff1af8d3aa Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 15 Dec 2014 12:33:57 +0100 Subject: [PATCH 24/74] Fix lpconfig test on read-only storage --- tester/setup_tester.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tester/setup_tester.c b/tester/setup_tester.c index 21fcfd091..63a8616d5 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -132,7 +132,9 @@ 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"); CU_ASSERT_STRING_EQUAL(lp_config_get_string(conf,"test","non_zero_len",""),"test"); From 3e56dcac160b45562af0898898c84c918848fffc Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 15 Dec 2014 12:36:18 +0100 Subject: [PATCH 25/74] Remove (now) unneeded use_files and fix the way we manage use_files in the linphone_core_manager to include a ringback tone --- tester/call_tester.c | 66 +--------------------------------------- tester/flexisip_tester.c | 35 ++------------------- tester/setup_tester.c | 2 ++ tester/tester.c | 6 +++- 4 files changed, 11 insertions(+), 98 deletions(-) diff --git a/tester/call_tester.c b/tester/call_tester.c index 2d186e1e1..df3b8636b 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -188,7 +188,6 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr stats initial_caller=caller_mgr->stat; 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{ @@ -388,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); @@ -468,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" ); @@ -479,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)); @@ -519,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" ); @@ -530,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 ) ); @@ -1839,7 +1815,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; @@ -1857,12 +1832,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); @@ -2295,7 +2264,6 @@ 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; @@ -2311,11 +2279,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); @@ -2359,7 +2322,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; @@ -2376,11 +2338,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)); @@ -2598,9 +2555,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); @@ -2766,7 +2720,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"); @@ -2781,13 +2734,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)); @@ -2912,9 +2858,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; @@ -2923,9 +2869,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; @@ -2933,18 +2876,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); diff --git a/tester/flexisip_tester.c b/tester/flexisip_tester.c index 63ae8b79d..282894f09 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); diff --git a/tester/setup_tester.c b/tester/setup_tester.c index 63a8616d5..bab077623 100644 --- a/tester/setup_tester.c +++ b/tester/setup_tester.c @@ -137,6 +137,8 @@ static void linphone_lpconfig_from_file_zerolen_value(){ 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/tester.c b/tester/tester.c index 125ebd5df..8b73026b4 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -269,11 +269,15 @@ LinphoneCoreManager* linphone_core_manager_new2(const char* rc_file, int check_f #if TARGET_OS_IPHONE linphone_core_set_ringer_device( mgr->lc, "AQ: Audio Queue Device"); linphone_core_set_ringback(mgr->lc, NULL); +#endif + 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); } -#endif if (proxy_count) wait_for_until(mgr->lc,NULL,&mgr->stat.number_of_LinphoneRegistrationOk,proxy_count,5000*proxy_count); From 697a6d4a89c22241a19d23359ac6ac786d7caeb9 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 15 Dec 2014 14:49:42 +0100 Subject: [PATCH 26/74] Fix the soundcard hint so that it trigger only when _all_ calls are paused or pausing (instead of just one call suffice). Also increase the test delay. --- coreapi/linphonecore.c | 8 ++++---- tester/call_tester.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 694ad2695..9798e4636 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6438,20 +6438,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); } diff --git a/tester/call_tester.c b/tester/call_tester.c index df3b8636b..426282097 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -2581,8 +2581,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)); From 42ddf0cf814494e5dda5b236c84a01dd77f7564d Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 15 Dec 2014 15:54:59 +0100 Subject: [PATCH 27/74] Improve Python module documentation. --- tools/python/apixml2python.py | 14 ++--- tools/python/apixml2python/linphone.py | 54 ++++++++++++++---- .../apixml2python/linphone_module.mustache | 17 ++++-- tools/python/doc/Makefile | 6 +- tools/python/doc/generate_enums.py | 28 ++++++++++ tools/python/doc/source/_static/pylinphone.js | 52 +++++++++++++++++ tools/python/doc/source/api_reference.rst | 24 ++++++++ tools/python/doc/source/conf.py | 6 +- tools/python/doc/source/getting_started.rst | 44 +++++++++++++++ tools/python/doc/source/index.rst | 56 +------------------ 10 files changed, 224 insertions(+), 77 deletions(-) create mode 100644 tools/python/doc/generate_enums.py create mode 100644 tools/python/doc/source/_static/pylinphone.js create mode 100644 tools/python/doc/source/api_reference.rst create mode 100644 tools/python/doc/source/getting_started.rst diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py index d6746ad08..2c32a24f6 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -71,13 +71,13 @@ blacklisted_functions = [ 'lp_config_section_to_dict' # missing LinphoneDictionary ] hand_written_functions = [ - HandWrittenClassMethod('Buffer', 'new_from_data', 'linphone_buffer_new_from_data'), - HandWrittenProperty('Buffer', 'content', 'linphone_buffer_get_content', 'linphone_buffer_set_content'), - 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', '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") ] def generate(apixmlfile, outputfile): diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index ce333a475..c1d37858d 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,28 @@ 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 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 @@ -947,18 +961,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']) @@ -1012,10 +1043,12 @@ 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, HandWrittenProperty): p = {} @@ -1028,6 +1061,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: diff --git a/tools/python/apixml2python/linphone_module.mustache b/tools/python/apixml2python/linphone_module.mustache index e31451cfa..13e0c1c6b 100644 --- a/tools/python/apixml2python/linphone_module.mustache +++ b/tools/python/apixml2python/linphone_module.mustache @@ -148,14 +148,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 +183,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 +308,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 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 = $('