From e5311281fcaccd2cfbb8a2fb9c672970bca7bb79 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 6 Oct 2014 11:41:48 +0200 Subject: [PATCH 001/135] Store the file transfer server in the linphonerc file. --- coreapi/chat.c | 10 +++++----- coreapi/linphonecore.c | 9 ++------- coreapi/private.h | 1 - 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index d26efcf85..0c89485f3 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -41,14 +41,14 @@ const char *multipart_boundary=MULTIPART_BOUNDARY; static void process_io_error_upload(void *data, const belle_sip_io_error_event_t *event){ LinphoneChatMessage* msg=(LinphoneChatMessage *)data; - ms_error("I/O Error during file upload to %s - msg [%p] chat room[%p]", msg->chat_room->lc->file_transfer_server, msg, msg->chat_room); + ms_error("I/O Error during file upload to %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room); if (msg->cb) { msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->chat_room->lc); } } static void process_auth_requested_upload(void *data, belle_sip_auth_event_t *event){ LinphoneChatMessage* msg=(LinphoneChatMessage *)data; - ms_error("Error during file upload : auth requested to connect %s - msg [%p] chat room[%p]", msg->chat_room->lc->file_transfer_server, msg, msg->chat_room); + ms_error("Error during file upload : auth requested to connect %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room); if (msg->cb) { msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->chat_room->lc); } @@ -148,7 +148,7 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co content_type=belle_sip_strdup_printf("multipart/form-data; boundary=%s",multipart_boundary); - uri=belle_generic_uri_parse(msg->chat_room->lc->file_transfer_server); + uri=belle_generic_uri_parse(linphone_core_get_file_transfer_server(msg->chat_room->lc)); req=belle_http_request_create("POST", uri, @@ -425,7 +425,7 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM belle_generic_uri_t *uri; belle_http_request_t *req; - uri=belle_generic_uri_parse(cr->lc->file_transfer_server); + uri=belle_generic_uri_parse(linphone_core_get_file_transfer_server(cr->lc)); req=belle_http_request_create("POST", uri, @@ -1138,7 +1138,7 @@ void linphone_chat_message_start_file_download(LinphoneChatMessage *message, Lin * @param msg #LinphoneChatMessage */ void linphone_chat_room_cancel_file_transfer(LinphoneChatMessage *msg) { - ms_message("Cancelled file transfer %s - msg [%p] chat room[%p]", (msg->external_body_url==NULL)?msg->chat_room->lc->file_transfer_server:msg->external_body_url, msg, msg->chat_room); + ms_message("Cancelled file transfer %s - msg [%p] chat room[%p]", (msg->external_body_url==NULL)?linphone_core_get_file_transfer_server(msg->chat_room->lc):msg->external_body_url, msg, msg->chat_room); /* TODO: here we shall call the cancel http request from bellesip API when it is available passing msg->http_request */ /* waiting for this API, just set to NULL the reference to the request in the message and any request */ msg->http_request = NULL; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index f322a361f..68211022a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1146,8 +1146,6 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab lc->http_verify_policy = belle_tls_verify_policy_new(); belle_http_provider_set_tls_verify_policy(lc->http_provider,lc->http_verify_policy); - lc->file_transfer_server = NULL; - certificates_config_read(lc); remote_provisioning_uri = linphone_core_get_provisioning_uri(lc); @@ -5771,9 +5769,6 @@ static void linphone_core_uninit(LinphoneCore *lc) ms_list_for_each(lc->last_recv_msg_ids,ms_free); lc->last_recv_msg_ids=ms_list_free(lc->last_recv_msg_ids); - // Free struct variable - ms_free(lc->file_transfer_server); - if(lc->zrtp_secrets_cache != NULL) { ms_free(lc->zrtp_secrets_cache); } @@ -6438,11 +6433,11 @@ bool_t linphone_core_sdp_200_ack_enabled(const LinphoneCore *lc) { } void linphone_core_set_file_transfer_server(LinphoneCore *core, const char * server_url) { - core->file_transfer_server=ms_strdup(server_url); + lp_config_set_string(core->config, "misc", "file_transfer_server_url", server_url); } const char * linphone_core_get_file_transfer_server(LinphoneCore *core) { - return core->file_transfer_server; + return lp_config_get_string(core->config, "misc", "file_transfer_server_url", NULL); } /** diff --git a/coreapi/private.h b/coreapi/private.h index 795dfb60f..a92a2b844 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -766,7 +766,6 @@ struct _LinphoneCore belle_tls_verify_policy_t *http_verify_policy; MSList *tones; LinphoneReason chat_deny_code; - char *file_transfer_server; const char **supported_formats; }; From 1d10e749b5bd3d9d495d49070d52440b0d65aa49 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 6 Oct 2014 11:45:27 +0200 Subject: [PATCH 002/135] Add API to store the logs in files and to upload them on a server. --- coreapi/linphonecore.c | 270 ++++++++++++++++++++++++++++++++++++++++- coreapi/linphonecore.h | 64 +++++++++- coreapi/private.h | 3 + 3 files changed, 331 insertions(+), 6 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 68211022a..1a017d2d2 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quality_reporting.h" #include +#include +#include #include #include #include "mediastreamer2/mediastream.h" @@ -64,6 +66,9 @@ static const char *liblinphone_version= LIBLINPHONE_VERSION #endif ; +static OrtpLogFunc liblinphone_log_func = NULL; +static bool_t liblinphone_log_collection_enabled = FALSE; +static const char * liblinphone_log_collection_path = "."; static bool_t liblinphone_serialize_logs = FALSE; static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); static void linphone_core_run_hooks(LinphoneCore *lc); @@ -127,7 +132,8 @@ const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _Lin } void linphone_core_set_log_handler(OrtpLogFunc logfunc) { - ortp_set_log_handler(logfunc); + liblinphone_log_func = logfunc; + ortp_set_log_handler(liblinphone_log_func); } void linphone_core_set_log_file(FILE *file) { @@ -144,6 +150,262 @@ void linphone_core_set_log_level(OrtpLogLevel loglevel) { } } +#define LOGFILE_MAXSIZE (10 * 1024 * 1024) + +static void linphone_core_log_collection_handler(OrtpLogLevel level, const char *fmt, va_list args) { + const char *lname="undef"; + char *msg; + char *log_filename1; + char *log_filename2; + FILE *log_file; + struct timeval tp; + struct tm *lt; + time_t tt; + struct stat statbuf; + + if (liblinphone_log_func != NULL) { + liblinphone_log_func(level, fmt, args); + } + + ortp_gettimeofday(&tp, NULL); + tt = (time_t)tp.tv_sec; + lt = localtime((const time_t*)&tt); + switch(level){ + case ORTP_DEBUG: + lname = "DEBUG"; + break; + case ORTP_MESSAGE: + lname = "MESSAGE"; + break; + case ORTP_WARNING: + lname = "WARNING"; + break; + case ORTP_ERROR: + lname = "ERROR"; + break; + case ORTP_FATAL: + lname = "FATAL"; + break; + default: + ortp_fatal("Bad level !"); + } + msg = ortp_strdup_vprintf(fmt, args); + + log_filename1 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + log_filename2 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + log_file = fopen(log_filename1, "a"); + fstat(fileno(log_file), &statbuf); + if (statbuf.st_size > LOGFILE_MAXSIZE) { + fclose(log_file); + log_file = fopen(log_filename2, "a"); + fstat(fileno(log_file), &statbuf); + if (statbuf.st_size > LOGFILE_MAXSIZE) { + fclose(log_file); + unlink(log_filename1); + rename(log_filename2, log_filename1); + log_file = fopen(log_filename2, "a"); + } + } + fprintf(log_file,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s %s\n", + 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, (int)(tp.tv_usec / 1000), lname, msg); + fflush(log_file); + fclose(log_file); + + ortp_free(log_filename1); + ortp_free(log_filename2); + ortp_free(msg); +} + +void linphone_core_set_log_collection_path(const char *path) { + liblinphone_log_collection_path = path; +} + +const char *linphone_core_get_log_collection_upload_server_url(LinphoneCore *core) { + return lp_config_get_string(core->config, "misc", "log_collection_upload_server_url", NULL); +} + +void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, const char *server_url) { + lp_config_set_string(core->config, "misc", "log_collection_upload_server_url", server_url); +} + +void linphone_core_enable_log_collection(bool_t enable) { + liblinphone_log_collection_enabled = enable; + if (liblinphone_log_collection_enabled == TRUE) { + ortp_set_log_handler(linphone_core_log_collection_handler); + } else { + ortp_set_log_handler(liblinphone_log_func); + } +} + +static void process_io_error_upload_log_collection(void *data, const belle_sip_io_error_event_t *event) { + LinphoneCore *core = (LinphoneCore *)data; + ms_error("I/O Error during log collection upload to %s", linphone_core_get_log_collection_upload_server_url(core)); + linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "I/O Error"); +} + +static void process_auth_requested_upload_log_collection(void *data, belle_sip_auth_event_t *event) { + LinphoneCore *core = (LinphoneCore *)data; + ms_error("Error during log collection upload: auth requested to connect %s", linphone_core_get_log_collection_upload_server_url(core)); + linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "Auth requested"); +} + +extern const char *multipart_boundary; + +/** + * Callback called when posting a log collection file to server (following rcs5.1 recommendation) + * + * @param[in] bh The body handler + * @param[in] msg The belle sip message + * @param[in] data The user data associated with the handler, contains the LinphoneCore object + * @param[in] offset The current position in the input buffer + * @param[in] buffer The ouput buffer where to copy the data to be uploaded + * @param[in,out] size The size in byte of the data requested, as output it will contain the effective copied size + * + */ +static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, uint8_t *buffer, size_t *size) { + LinphoneCore *core = (LinphoneCore *)data; + char *buf = (char *)buffer; + + /* If we've not reach the end of file yet, fill the buffer with more data */ + if (offset < core->log_collection_upload_information->size) { + char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + FILE *log_file = fopen(log_filename, "r"); + fseek(log_file, offset, SEEK_SET); + *size = fread(buffer, 1, *size, log_file); + fclose(log_file); + ortp_free(log_filename); + } + + return BELLE_SIP_CONTINUE; +} + +/** + * Callback called during upload of a log collection to server. + * It is just forwarding the call and some parameters to the vtable defined callback. + */ +static void log_collection_upload_on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total) { + LinphoneCore *core = (LinphoneCore *)data; + linphone_core_notify_log_collection_upload_progress_indication(core, (size_t)(((double)offset / (double)total) * 100.0)); +} + +/** + * Callback function called when we have a response from server during the upload of the log collection to the server (rcs5.1 recommandation) + * Note: The first post is empty and the server shall reply a 204 (No content) message, this will trigger a new post request to the server + * to upload the file. The server response to this second post is processed by this same function + * + * @param[in] data The user-defined pointer associated with the request, it contains the LinphoneCore object + * @param[in] event The response from server + */ +static void process_response_from_post_file_log_collection(void *data, const belle_http_response_event_t *event) { + LinphoneCore *core = (LinphoneCore *)data; + + /* Check the answer code */ + if (event->response) { + int code = belle_http_response_get_status_code(event->response); + if (code == 204) { /* This is the reply to the first post to the server - an empty file */ + /* Start uploading the file */ + belle_http_request_listener_callbacks_t cbs = { 0 }; + belle_http_request_listener_t *l; + belle_generic_uri_t *uri; + belle_http_request_t *req; + belle_sip_multipart_body_handler_t *bh; + char* ua; + char *content_type; + char *first_part_header; + belle_sip_user_body_handler_t *first_part_bh; + + linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateInProgress, NULL); + + /* Temporary storage for the Content-disposition header value */ + first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", core->log_collection_upload_information->name); + + /* Create a user body handler to take care of the file and add the content disposition and content-type headers */ + first_part_bh = belle_sip_user_body_handler_new(core->log_collection_upload_information->size, NULL, NULL, log_collection_upload_on_send_body, core); + belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, belle_sip_header_create("Content-disposition", first_part_header)); + belle_sip_free(first_part_header); + belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create(core->log_collection_upload_information->type, core->log_collection_upload_information->subtype)); + + /* Insert it in a multipart body handler which will manage the boundaries of multipart message */ + bh = belle_sip_multipart_body_handler_new(log_collection_upload_on_progress, core, (belle_sip_body_handler_t *)first_part_bh); + ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version()); + content_type = belle_sip_strdup_printf("multipart/form-data; boundary=%s", multipart_boundary); + uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); + req = belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent", ua), belle_sip_header_create("Content-type", content_type), NULL); + ms_free(ua); + belle_sip_free(content_type); + belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh)); + cbs.process_response = process_response_from_post_file_log_collection; + cbs.process_io_error = process_io_error_upload_log_collection; + cbs.process_auth_requested = process_auth_requested_upload_log_collection; + l = belle_http_request_listener_create_from_callbacks(&cbs, core); + belle_http_provider_send_request(core->http_provider, req, l); + } + if (code == 200) { /* The file has been uploaded correctly, get the server reply */ + xmlDocPtr xmlMessageBody; + xmlNodePtr cur; + xmlChar *file_url = NULL; + const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); + xmlMessageBody = xmlParseDoc((const xmlChar *)body); + cur = xmlDocGetRootElement(xmlMessageBody); + if (cur != NULL) { + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a type="file" attribute */ + xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type"); + if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */ + cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */ + while (cur != NULL) { + if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) { + file_url = xmlGetProp(cur, (const xmlChar *)"url"); + } + cur=cur->next; + } + xmlFree(typeAttribute); + break; + } + xmlFree(typeAttribute); + } + cur = cur->next; + } + } + if (file_url != NULL) { + linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateDelivered, (const char *)file_url); + } + } + } +} + +void linphone_core_upload_log_collection(LinphoneCore *core) { + if ((core->log_collection_upload_information == NULL) && (linphone_core_get_log_collection_upload_server_url(core) != NULL) && (liblinphone_log_collection_enabled == TRUE)) { + /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ + belle_http_request_listener_callbacks_t cbs = { 0 }; + belle_http_request_listener_t *l; + belle_generic_uri_t *uri; + belle_http_request_t *req; + + struct stat statbuf; + char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + FILE *log_file = fopen(log_filename, "r"); + fstat(fileno(log_file), &statbuf); + fclose(log_file); + ortp_free(log_filename); + + core->log_collection_upload_information = (LinphoneContent *)malloc(sizeof(LinphoneContent)); + memset(core->log_collection_upload_information, 0, sizeof(LinphoneContent)); + core->log_collection_upload_information->type = "text"; + core->log_collection_upload_information->subtype = "plain"; + core->log_collection_upload_information->name = "linphone_log.txt"; + core->log_collection_upload_information->size = statbuf.st_size; + uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); + req = belle_http_request_create("POST", uri, NULL, NULL, NULL); + cbs.process_response = process_response_from_post_file_log_collection; + cbs.process_io_error = process_io_error_upload_log_collection; + cbs.process_auth_requested = process_auth_requested_upload_log_collection; + l = belle_http_request_listener_create_from_callbacks(&cbs, core); + belle_http_provider_send_request(core->http_provider, req, l); + } +} + /** * Enable logs in supplied FILE*. * @@ -6632,6 +6894,12 @@ void linphone_core_notify_subscription_state_changed(LinphoneCore *lc, LinphoneE void linphone_core_notify_publish_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphonePublishState state) { NOTIFY_IF_EXIST(publish_state_changed)(lc,lev,state); } +void linphone_core_notify_log_collection_upload_state_changed(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info) { + NOTIFY_IF_EXIST(log_collection_upload_state_changed)(lc, state, info); +} +void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore *lc, size_t progress) { + NOTIFY_IF_EXIST(log_collection_upload_progress_indication)(lc, progress); +} void linphone_core_add_listener(LinphoneCore *lc, LinphoneCoreVTable *vtable) { ms_message("Vtable [%p] registered on core [%p]",lc,vtable); lc->vtables=ms_list_append(lc->vtables,vtable); diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 5609d4168..041e8756e 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1464,6 +1464,14 @@ typedef enum _LinphoneGlobalState{ const char *linphone_global_state_to_string(LinphoneGlobalState gs); +/** + * LinphoneCoreLogCollectionUploadState is used to notify if log collection upload have been succesfully delivered or not. + */ +typedef enum _LinphoneCoreLogCollectionUploadState { + LinphoneCoreLogCollectionUploadStateInProgress, /**< Delivery in progress */ + LinphoneCoreLogCollectionUploadStateDelivered, /**< Log collection upload successfully delivered and acknowledged by remote end point */ + LinphoneCoreLogCollectionUploadStateNotDelivered, /**< Log collection upload was not delivered */ +} LinphoneCoreLogCollectionUploadState; /** * Global state notification callback. @@ -1670,6 +1678,21 @@ typedef void (*LinphoneCoreConfiguringStatusCb)(LinphoneCore *lc, LinphoneConfig */ typedef void (*LinphoneCoreNetworkReachableCb)(LinphoneCore *lc, bool_t reachable); +/** + * Callback prototype for reporting log collection upload state change. + * @param[in] lc LinphoneCore object + * @param[in] state The state of the log collection upload + * @param[in] info Additional information: error message in case of error state, URL of uploaded file in case of success. + */ +typedef void (*LinphoneCoreLogCollectionUploadStateChangedCb)(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info); + +/** + * Callback prototype for reporting log collection upload progress indication. + * @param[in] lc LinphoneCore object + * @param[in] progress Percentage of the file size of the log collection already uploaded. + */ +typedef void (*LinphoneCoreLogCollectionUploadProgressIndicationCb)(LinphoneCore *lc, size_t progress); + /** * This structure holds all callbacks that the application should implement. * None is mandatory. @@ -1700,11 +1723,13 @@ typedef struct _LinphoneCoreVTable{ DisplayMessageCb display_warning;/**< @deprecated Callback to display a warning to the user */ DisplayUrlCb display_url; /**< @deprecated */ ShowInterfaceCb show; /**< @deprecated Notifies the application that it should show up*/ - LinphoneCoreTextMessageReceivedCb text_received; /** @deprecated, use #message_received instead
A text message has been received */ - LinphoneCoreFileTransferRecvCb file_transfer_recv; /** Callback to store file received attached to a #LinphoneChatMessage */ - LinphoneCoreFileTransferSendCb file_transfer_send; /** Callback to collect file chunk to be sent for a #LinphoneChatMessage */ - LinphoneCoreFileTransferProgressIndicationCb file_transfer_progress_indication; /**Callback to indicate file transfer progress*/ - LinphoneCoreNetworkReachableCb network_reachable; /** Call back to report IP network status (I.E up/down)*/ + LinphoneCoreTextMessageReceivedCb text_received; /**< @deprecated, use #message_received instead
A text message has been received */ + LinphoneCoreFileTransferRecvCb file_transfer_recv; /**< Callback to store file received attached to a #LinphoneChatMessage */ + LinphoneCoreFileTransferSendCb file_transfer_send; /**< Callback to collect file chunk to be sent for a #LinphoneChatMessage */ + LinphoneCoreFileTransferProgressIndicationCb file_transfer_progress_indication; /**< Callback to indicate file transfer progress */ + LinphoneCoreNetworkReachableCb network_reachable; /**< Callback to report IP network status (I.E up/down )*/ + LinphoneCoreLogCollectionUploadStateChangedCb log_collection_upload_state_changed; /**< Callback to upload collected logs */ + LinphoneCoreLogCollectionUploadProgressIndicationCb log_collection_upload_progress_indication; /**< Callback to indicate log collection upload progress */ } LinphoneCoreVTable; /** @@ -1752,6 +1777,35 @@ typedef void * (*LinphoneCoreWaitingCallback)(LinphoneCore *lc, void *context, L /* THE main API */ +/** + * Enable the linphone core log collection to upload logs on a server. + * @ingroup misc + * @param[in] enable Boolean value telling whether to enable log collection or not. + */ +LINPHONE_PUBLIC void linphone_core_enable_log_collection(bool_t enable); + +/** + * Set the path where the log files will be written for log collection. + * @ingroup misc + * @param[in] path The path where the log files will be written. + */ +LINPHONE_PUBLIC void linphone_core_set_log_collection_path(const char *path); + +/** + * Set the url of the server where to upload the collected log files. + * @ingroup misc + * @param[in] core LinphoneCore object + * @param[in] server_url The url of the server where to upload the collected log files. + */ +LINPHONE_PUBLIC void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, const char *server_url); + +/** + * Upload the log collection to the configured server url. + * @ingroup misc + * @param[in] core LinphoneCore object + */ +LINPHONE_PUBLIC void linphone_core_upload_log_collection(LinphoneCore *core); + /** * Define a log handler. * diff --git a/coreapi/private.h b/coreapi/private.h index a92a2b844..936c7d3e8 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -767,6 +767,7 @@ struct _LinphoneCore MSList *tones; LinphoneReason chat_deny_code; const char **supported_formats; + LinphoneContent *log_collection_upload_information; }; @@ -1018,6 +1019,8 @@ void linphone_core_notify_network_reachable(LinphoneCore *lc, bool_t reachable); void linphone_core_notify_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *notified_event, const LinphoneContent *body); void linphone_core_notify_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state); void linphone_core_notify_publish_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphonePublishState state); +void linphone_core_notify_log_collection_upload_state_changed(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info); +void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore *lc, size_t progress); void set_mic_gain_db(AudioStream *st, float gain); void set_playback_gain_db(AudioStream *st, float gain); From db528b1a7467e6b090ab5276d1d1787f9226601a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 6 Oct 2014 15:07:47 +0200 Subject: [PATCH 003/135] Add the LinphonePlayer Java interface --- build/android/Android.mk | 3 +- build/android/liblinphone_tester.mk | 3 +- coreapi/linphonecore_jni.cc | 93 +++++++++++++++++++ .../org/linphone/core/LinphoneCore.java | 12 +++ .../org/linphone/core/LinphonePlayer.java | 83 +++++++++++++++++ .../org/linphone/core/LinphoneCoreImpl.java | 16 ++++ .../org/linphone/core/LinphonePlayerImpl.java | 52 +++++++++++ tester/player_tester.c | 20 +++- 8 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 java/common/org/linphone/core/LinphonePlayer.java create mode 100644 java/impl/org/linphone/core/LinphonePlayerImpl.java diff --git a/build/android/Android.mk b/build/android/Android.mk index f972bfe14..1edc91fac 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -68,7 +68,8 @@ LOCAL_SRC_FILES := \ quality_reporting.c \ call_log.c \ call_params.c \ - player.c + player.c \ + fileplayer.c ifndef LIBLINPHONE_VERSION LIBLINPHONE_VERSION = "Devel" diff --git a/build/android/liblinphone_tester.mk b/build/android/liblinphone_tester.mk index a6a2ab5f5..b23a381bd 100644 --- a/build/android/liblinphone_tester.mk +++ b/build/android/liblinphone_tester.mk @@ -14,7 +14,8 @@ common_SRC_FILES := \ tester.c \ remote_provisioning_tester.c \ quality_reporting_tester.c \ - transport_tester.c + transport_tester.c \ + player_tester.c common_C_INCLUDES += \ $(LOCAL_PATH) \ diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index bcdf0fdf0..2b8fce197 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5208,3 +5208,96 @@ JNIEXPORT jstring JNICALL Java_org_linphone_core_ErrorInfoImpl_getDetails(JNIEnv } #endif +/* Linphone Player */ +class LinphonePlayerData { +public: + LinphonePlayerData(jobject listener, jobject jLinphonePlayer) : + mListener(listener), + mJLinphonePlayer(jLinphonePlayer) {} + + jobject mListener; + jobject mJLinphonePlayer; + + static jmethodID endOfFileMethodID; + + static void init(JNIEnv *env) { + jclass listenerClass = env->FindClass("org/linphone/core/LinphonePlayer#Listener"); + endOfFileMethodID = env->GetMethodID(listenerClass, "endOfFile", "(Lorg/linphone/core/LinphonePlayer;)V"); + } +}; + +jmethodID LinphonePlayerData::endOfFileMethodID = NULL; + +static void _eof_callback(LinphonePlayer *player, void *user_data) { + JNIEnv *env; + LinphonePlayerData *player_data = (LinphonePlayerData *)user_data; + jvm->AttachCurrentThread(&env, NULL); + if(LinphonePlayerData::endOfFileMethodID == NULL) { + LinphonePlayerData::init(env); + } + env->CallVoidMethod(player_data->mListener, LinphonePlayerData::endOfFileMethodID, player_data->mJLinphonePlayer); + jvm->DetachCurrentThread(); +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_open(JNIEnv *env, jobject jPlayer, jlong ptr, jstring filename, jobject listener) { + LinphonePlayerData *data = NULL; + LinphonePlayerEofCallback cb = NULL; + if(listener) { + listener = env->NewGlobalRef(listener); + jPlayer = env->NewGlobalRef(jPlayer); + data = new LinphonePlayerData(listener, jPlayer); + cb = _eof_callback; + } + if(linphone_player_open((LinphonePlayer *)ptr, env->GetStringUTFChars(filename, NULL), cb, &data) == -1) { + if(data) { + delete data; + env->DeleteGlobalRef(listener); + env->DeleteGlobalRef(jPlayer); + } + return -1; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_start(JNIEnv *env, jobject jobj, jlong ptr) { + return (jint)linphone_player_start((LinphonePlayer *)ptr); +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_pause(JNIEnv *env, jobject jobj, jlong ptr) { + return (jint)linphone_player_pause((LinphonePlayer *)ptr); +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_seek(JNIEnv *env, jobject jobj, jlong ptr, jint timeMs) { + return (jint)linphone_player_seek((LinphonePlayer *)ptr, timeMs); +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getState(JNIEnv *env, jobject jobj, jlong ptr) { + return (jint)linphone_player_get_state((LinphonePlayer *)ptr); +} + +JNIEXPORT void JNICALL Java_org_linphone_core_LinphonePlayer_close(JNIEnv *env, jobject playerPtr, jlong ptr) { + LinphonePlayer *player = (LinphonePlayer *)ptr; + if(player->user_data) { + LinphonePlayerData *data = (LinphonePlayerData *)player->user_data; + env->DeleteGlobalRef(data->mListener); + env->DeleteGlobalRef(data->mJLinphonePlayer); + delete data; + player->user_data = NULL; + } + linphone_player_close(player); +} + +JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneCore_createPlayer(JNIEnv *env, jobject jobj, jlong ptr) { + return (jlong)linphone_core_create_file_player((LinphoneCore *)ptr, NULL, NULL); +} + +JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCore_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { + LinphonePlayer *player = (LinphonePlayer *)playerPtr; + if(player->user_data) { + LinphonePlayerData *data = (LinphonePlayerData *)player->user_data; + env->DeleteGlobalRef(data->mListener); + env->DeleteGlobalRef(data->mJLinphonePlayer); + delete data; + } + linphone_file_player_destroy(player); +} diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 3a9e1d86d..26b721675 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1781,4 +1781,16 @@ public interface LinphoneCore { * @return the serverUrl */ public String getFileTransferServer(); + + /** + * Create a media player + * @return An object that implement LinphonePlayer + */ + public LinphonePlayer createPlayer(); + + /** + * Destroy a player + * @param player Player to destroy + */ + public void destroyPlayer(LinphonePlayer player); } diff --git a/java/common/org/linphone/core/LinphonePlayer.java b/java/common/org/linphone/core/LinphonePlayer.java new file mode 100644 index 000000000..c38340af1 --- /dev/null +++ b/java/common/org/linphone/core/LinphonePlayer.java @@ -0,0 +1,83 @@ +/** + * Interface to manipulate different media players of Linphone + */ +package org.linphone.core; + +/** + * @author François Grisez + * + */ +public interface LinphonePlayer { + /** + * States that the player can be + * @author François Grisez + * + */ + public enum State { + closed, /**< No file is open */ + paused, /**< A file is open and playback is not running */ + playing; /**< A file is open and playback is running */ + + public static State fromValue(int value) { + if(value == 0) { + return closed; + } else if(value == 1) { + return paused; + } else if(value == 2) { + return playing; + } else { + return null; + } + } + }; + + /** + * Listener for Linphone players + * @author François Grisez + * + */ + public interface Listener { + /** + * Method called when a player reaches the end of a file + * @param player The player which called the method + */ + public void endOfFile(LinphonePlayer player); + } + + /** + * Open a file + * @param filename Name of the file to open + * @return 0 on success, -1 on failure + */ + public int open(final String filename, Listener listener); + + /** + * Start playback + * @return 0 on success, -1 on failure + */ + public int start(); + + /** + * Get playback paused + * @return 0 on success, -1 on failure + */ + public int pause(); + + /** + * Go to a specific position in the timeline + * @param timeMs Time in milliseconds + * @return 0 on success, -1 on failure + */ + public int seek(int timeMs); + + /** + * Get the state of the player + * @return See State enumeration + */ + public State getState(); + + /** + * Close a file + */ + public void close(); +} diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 0c53735fc..8bd3f0d7e 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -1279,4 +1279,20 @@ class LinphoneCoreImpl implements LinphoneCore { return getFileTransferServer(nativePtr); } + private native long createPlayer(long nativePtr); + @Override + public synchronized LinphonePlayer createPlayer() { + long player = createPlayer(nativePtr); + if(player != 0) { + return new LinphonePlayerImpl(createPlayer(nativePtr)); + } else { + return null; + } + } + + private native void destroyPlayer(long playerPtr); + @Override + public synchronized void destroyPlayer(LinphonePlayer player) { + + } } diff --git a/java/impl/org/linphone/core/LinphonePlayerImpl.java b/java/impl/org/linphone/core/LinphonePlayerImpl.java new file mode 100644 index 000000000..a6c5cb2e5 --- /dev/null +++ b/java/impl/org/linphone/core/LinphonePlayerImpl.java @@ -0,0 +1,52 @@ +/** + * + */ +package org.linphone.core; + +/** + * @author François Grisez + * + */ +public class LinphonePlayerImpl implements LinphonePlayer { + private long nativePtr = 0; + + LinphonePlayerImpl(long nativePtr) { + this.nativePtr = nativePtr; + } + + private native int open(long nativePtr, final String filename, Listener listener, LinphonePlayer player); + @Override + public synchronized int open(final String filename, Listener listener) { + return open(nativePtr, filename, listener, this); + } + + private native int start(long nativePtr); + @Override + public synchronized int start() { + return start(nativePtr); + } + + private native int pause(long nativePtr); + @Override + public synchronized int pause() { + return pause(nativePtr); + } + + private native int seek(long nativePtr, int timeMs); + @Override + public synchronized int seek(int timeMs) { + return seek(nativePtr, timeMs); + } + + private native int getState(long nativePtr); + @Override + public synchronized State getState() { + return LinphonePlayer.State.fromValue(getState(nativePtr)); + } + + private native void close(long nativePtr); + @Override + public synchronized void close() { + close(nativePtr); + } +} diff --git a/tester/player_tester.c b/tester/player_tester.c index 59e648340..57a57da60 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -1,5 +1,23 @@ #include "liblinphone_tester.h" +static const char *_get_default_video_renderer(void){ +#ifdef WIN32 + return "MSDrawDibDisplay"; +#elif defined(ANDROID) + return "MSAndroidDisplay"; +#elif __APPLE__ && !defined(__ios) + return "MSOSXGLDisplay"; +#elif defined (HAVE_XV) + return "MSX11Video"; +#elif defined(HAVE_GL) + return "MSGLXVideo"; +#elif defined(__ios) + return "IOSDisplay"; +#else + return "MSVideoOut"; +#endif +} + static bool_t wait_for_eof(bool_t *eof, int *time,int time_refresh, int timeout) { while(*time < timeout && !*eof) { usleep(time_refresh * 1000U); @@ -23,7 +41,7 @@ static void play_file(const char *filename, bool_t unsupported_format) { CU_ASSERT_PTR_NOT_NULL(lc_manager); if(lc_manager == NULL) return; - player = linphone_core_create_file_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), video_stream_get_default_video_renderer()); + player = linphone_core_create_file_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), _get_default_video_renderer()); CU_ASSERT_PTR_NOT_NULL(player); if(player == NULL) goto fail; From a584a075b2fbeffda0dcdd3adc4dd4f0e7b780c9 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 6 Oct 2014 16:13:18 +0200 Subject: [PATCH 004/135] Fix compilation warning. --- coreapi/linphonecore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 1a017d2d2..0aa3829f2 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6764,7 +6764,7 @@ int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc){ * @param interval interval in seconds. **/ void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int interval){ - return lp_config_set_int(lc->config,"rtp","avpf_rr_interval",interval); + lp_config_set_int(lc->config,"rtp","avpf_rr_interval",interval); } int linphone_payload_type_get_type(const LinphonePayloadType *pt) { From ce88c5ae5a3a48eafd9b8a53aca7481b997bd242 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 6 Oct 2014 16:14:20 +0200 Subject: [PATCH 005/135] Add Visual Studio project and solution to build zlib. --- build/wp8/zlib/zconf.h | 513 ++++++++++++++++++++++++++++++++++++ build/wp8/zlib/zlib.sln | 26 ++ build/wp8/zlib/zlib.vcxproj | 135 ++++++++++ 3 files changed, 674 insertions(+) create mode 100644 build/wp8/zlib/zconf.h create mode 100644 build/wp8/zlib/zlib.sln create mode 100644 build/wp8/zlib/zlib.vcxproj diff --git a/build/wp8/zlib/zconf.h b/build/wp8/zlib/zconf.h new file mode 100644 index 000000000..a3a6b54fc --- /dev/null +++ b/build/wp8/zlib/zconf.h @@ -0,0 +1,513 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +/* #undef Z_PREFIX */ +#define Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/build/wp8/zlib/zlib.sln b/build/wp8/zlib/zlib.sln new file mode 100644 index 000000000..97b0cc6a9 --- /dev/null +++ b/build/wp8/zlib/zlib.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2012 for Windows Phone +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|Win32 = Debug|Win32 + Release|ARM = Release|ARM + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.ActiveCfg = Debug|ARM + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|ARM.Build.0 = Debug|ARM + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Debug|Win32.Build.0 = Debug|Win32 + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.ActiveCfg = Release|ARM + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|ARM.Build.0 = Release|ARM + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.ActiveCfg = Release|Win32 + {7AFAC3BB-D97B-4578-B9FE-5E1D2B94EA2F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/build/wp8/zlib/zlib.vcxproj b/build/wp8/zlib/zlib.vcxproj new file mode 100644 index 000000000..34c9aa46f --- /dev/null +++ b/build/wp8/zlib/zlib.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Debug + ARM + + + Release + Win32 + + + Release + ARM + + + + {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f} + zlib + en-US + 11.0 + + + + DynamicLibrary + true + v110_wp80 + false + + + DynamicLibrary + false + true + v110_wp80 + false + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ + + + false + + + + Level4 + $(ProjectDir);$(ProjectDir)..\..\..\..\zlib;%(AdditionalIncludeDirectories) + _WIN32;_WINDLL;_USRDLL;_CRT_SECURE_NO_WARNINGS;UNICODE;%(PreprocessorDefinitions) + Default + NotUsing + false + $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) + + + Console + false + false + $(TargetDir)$(TargetName).lib + Ws2_32.lib;%(AdditionalDependencies) + $(ProjectDir)..\..\..\..\zlib\win32\zlib.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + true + + + + + NDEBUG;%(PreprocessorDefinitions) + MaxSpeed + true + true + true + + + false + + + + + true + + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From aa298d645ccf5ba014966c75f2994ab993aba724 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 6 Oct 2014 16:15:22 +0200 Subject: [PATCH 006/135] Windows Phone 8 project depends on zlib + implementation of log compression. --- build/wp8/LibLinphone.vcxproj | 7 +- coreapi/linphonecore.c | 141 +++++++++++++++++++++++++++++++--- 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/build/wp8/LibLinphone.vcxproj b/build/wp8/LibLinphone.vcxproj index 8b0660688..7d8d4b014 100644 --- a/build/wp8/LibLinphone.vcxproj +++ b/build/wp8/LibLinphone.vcxproj @@ -53,8 +53,8 @@ Level4 - $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDIr)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;%(AdditionalIncludeDirectories) - __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR=".";UNICODE;_XKEYCHECK_H;%(PreprocessorDefinitions) + $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;%(AdditionalIncludeDirectories) + __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR=".";UNICODE;_XKEYCHECK_H;HAVE_ZLIB;%(PreprocessorDefinitions) Default NotUsing false @@ -201,6 +201,9 @@ {5dfa07b4-0be9-46a9-ba32-fdf5a55c580b} + + {7afac3bb-d97b-4578-b9fe-5e1d2b94ea2f} + diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 0aa3829f2..cac4a34b1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -55,6 +55,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "TargetConditionals.h" #endif +#ifdef HAVE_ZLIB +#ifdef WIN32 +#include +#include +#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +#define SET_BINARY_MODE(file) +#endif +#include +#endif + /*#define UNSTANDART_GSM_11K 1*/ #define ROOT_CA_FILE PACKAGE_DATA_DIR "/linphone/rootca.pem" @@ -264,12 +275,16 @@ extern const char *multipart_boundary; */ static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, uint8_t *buffer, size_t *size) { LinphoneCore *core = (LinphoneCore *)data; - char *buf = (char *)buffer; /* If we've not reach the end of file yet, fill the buffer with more data */ if (offset < core->log_collection_upload_information->size) { - char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); +#ifdef HAVE_ZLIB + char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.zlib"); + FILE *log_file = fopen(log_filename, "rb"); +#else + char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.txt"); FILE *log_file = fopen(log_filename, "r"); +#endif fseek(log_file, offset, SEEK_SET); *size = fread(buffer, 1, *size, log_file); fclose(log_file); @@ -375,6 +390,113 @@ static void process_response_from_post_file_log_collection(void *data, const bel } } +#ifdef HAVE_ZLIB + +static int compress_file(FILE *input_file, FILE *output_file, z_stream *strm) { + unsigned char in[131072]; /* 128kB */ + unsigned char out[131072]; /* 128kB */ + unsigned int have; + int flush; + int ret; + size_t output_file_size = 0; + + do { + strm->avail_in = fread(in, 1, sizeof(in), input_file); + if (ferror(input_file)) { + deflateEnd(strm); + return Z_ERRNO; + } + flush = feof(input_file) ? Z_FINISH : Z_NO_FLUSH; + strm->next_in = in; + do { + strm->avail_out = sizeof(out); + strm->next_out = out; + ret = deflate(strm, flush); + have = sizeof(out) - strm->avail_out; + if (fwrite(out, 1, have, output_file) != have || ferror(output_file)) { + deflateEnd(strm); + return Z_ERRNO; + } + output_file_size += have; + } while (strm->avail_out == 0); + } while (flush != Z_FINISH); + + return output_file_size; +} + +#else + +/** + * If zlib is not available the two log files are simply concatenated. + */ +static int compress_file(FILE *input_file, FILE *output_file) { + char buffer[131072]; /* 128kB */ + size_t output_file_size = 0; + + while ((bytes = fread(buffer, 1, sizeof(buffer), input_file) > 0) { + if (bytes < 0) return bytes; + fwrite(buffer, 1, bytes, output_file); + output_file_size += bytes; + } + return output_file_size; +} + +#endif + +static size_t prepare_log_collection_file_to_upload(const char *filename) { + char *input_filename = NULL; + char *output_filename = NULL; + FILE *input_file = NULL; + FILE *output_file = NULL; + size_t output_file_size = 0; + int ret; + +#ifdef HAVE_ZLIB + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) return ret; +#endif + + output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + output_file = fopen(output_filename, "a"); + if (output_file == NULL) goto error; + input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + input_file = fopen(input_filename, "r"); + if (input_file == NULL) goto error; +#ifdef HAVE_ZLIB + SET_BINARY_MODE(output_file); + SET_BINARY_MODE(input_file); + ret = compress_file(input_file, output_file, &strm); +#else + ret = compress_file(input_file, output_file); +#endif + if (ret < 0) goto error; + output_file_size += ret; + fclose(input_file); + ortp_free(input_filename); + input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + input_file = fopen(input_filename, "r"); + if (input_file != NULL) { +#ifdef HAVE_ZLIB + SET_BINARY_MODE(input_file); + ret = compress_file(input_file, output_file, &strm); +#else + ret = compress_file(input_file, output_file); +#endif + if (ret < 0) goto error; + output_file_size += ret; + } +error: + if (input_file != NULL) fclose(input_file); + if (output_file != NULL) fclose(output_file); + if (input_filename != NULL) ortp_free(input_filename); + if (output_filename != NULL) ortp_free(output_filename); + return output_file_size; +} + void linphone_core_upload_log_collection(LinphoneCore *core) { if ((core->log_collection_upload_information == NULL) && (linphone_core_get_log_collection_upload_server_url(core) != NULL) && (liblinphone_log_collection_enabled == TRUE)) { /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ @@ -383,19 +505,18 @@ void linphone_core_upload_log_collection(LinphoneCore *core) { belle_generic_uri_t *uri; belle_http_request_t *req; - struct stat statbuf; - char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); - FILE *log_file = fopen(log_filename, "r"); - fstat(fileno(log_file), &statbuf); - fclose(log_file); - ortp_free(log_filename); - core->log_collection_upload_information = (LinphoneContent *)malloc(sizeof(LinphoneContent)); memset(core->log_collection_upload_information, 0, sizeof(LinphoneContent)); +#ifdef HAVE_ZLIB + core->log_collection_upload_information->type = "application"; + core->log_collection_upload_information->subtype = "x-deflate"; + core->log_collection_upload_information->name = "linphone_log.zlib"; +#else core->log_collection_upload_information->type = "text"; core->log_collection_upload_information->subtype = "plain"; core->log_collection_upload_information->name = "linphone_log.txt"; - core->log_collection_upload_information->size = statbuf.st_size; +#endif + core->log_collection_upload_information->size = prepare_log_collection_file_to_upload(core->log_collection_upload_information->name); uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); req = belle_http_request_create("POST", uri, NULL, NULL, NULL); cbs.process_response = process_response_from_post_file_log_collection; From 400c26cf1ec3098c0411587a266d15a450ec2b96 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 6 Oct 2014 17:53:15 +0200 Subject: [PATCH 007/135] workaround problem with chat view not displaying as it should with chinese languages. It is a gtk bug, same as the one previously workarounded with the GtkEntry. The same workaround works for the GtkTextView. --- gtk/chat.c | 4 ++-- gtk/main.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gtk/chat.c b/gtk/chat.c index c3183ad64..940437950 100644 --- a/gtk/chat.c +++ b/gtk/chat.c @@ -379,7 +379,7 @@ GtkWidget* linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const LinphoneAddres GdkColor colorb; int idx; GtkWidget *button; - GtkWidget *entry; + GtkWidget *entry = linphone_gtk_get_widget(chat_view,"text_entry"); MSList *messages; GHashTable *table; char *with_str; @@ -423,7 +423,7 @@ GtkWidget* linphone_gtk_init_chatroom(LinphoneChatRoom *cr, const LinphoneAddres display_history_message(chat_view,messages,with); button = linphone_gtk_get_widget(chat_view,"send"); g_signal_connect_swapped(G_OBJECT(button),"clicked",(GCallback)linphone_gtk_send_text,NULL); - entry = linphone_gtk_get_widget(chat_view,"text_entry"); + g_signal_connect_swapped(G_OBJECT(entry),"activate",(GCallback)linphone_gtk_send_text,NULL); g_signal_connect_swapped(G_OBJECT(entry),"changed",(GCallback)linphone_gtk_compose_text,NULL); g_signal_connect(G_OBJECT(notebook),"switch_page",(GCallback)linphone_gtk_notebook_tab_select,NULL); diff --git a/gtk/main.c b/gtk/main.c index 60c27e43b..5dd2b046f 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -440,9 +440,9 @@ GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_n return w; } -static void entry_unmapped(GtkWidget *entry){ - g_message("Entry is unmapped, calling unrealize to workaround chinese bug."); - gtk_widget_unrealize(entry); +static void entry_unmapped(GtkWidget *widget){ + ms_message("%s is unmapped, calling unrealize to workaround chinese bug.",G_OBJECT_TYPE_NAME(widget)); + gtk_widget_unrealize(widget); } GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ @@ -459,10 +459,10 @@ GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name){ g_error("No widget named %s found in xml interface.",name); } if (workaround_gtk_entry_chinese_bug){ - if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0){ + if (strcmp(G_OBJECT_TYPE_NAME(w),"GtkEntry")==0 || strcmp(G_OBJECT_TYPE_NAME(w),"GtkTextView")==0){ if (g_object_get_data(G_OBJECT(w),"entry_bug_workaround")==NULL){ g_object_set_data(G_OBJECT(w),"entry_bug_workaround",GINT_TO_POINTER(1)); - g_message("%s is a GtkEntry",name); + ms_message("%s is a %s",name,G_OBJECT_TYPE_NAME(w)); g_signal_connect(G_OBJECT(w),"unmap",(GCallback)entry_unmapped,NULL); } } From 44c55e44e80349f3023df2f1f98e65fc4f808c4a Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 6 Oct 2014 18:49:06 +0200 Subject: [PATCH 008/135] fix multiple compilation errors --- coreapi/linphonecore.c | 41 +++++++++++++++++++++-------------------- mediastreamer2 | 2 +- tester/player_tester.c | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index cac4a34b1..e477d9a44 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -399,28 +399,28 @@ static int compress_file(FILE *input_file, FILE *output_file, z_stream *strm) { int flush; int ret; size_t output_file_size = 0; - + do { strm->avail_in = fread(in, 1, sizeof(in), input_file); - if (ferror(input_file)) { - deflateEnd(strm); - return Z_ERRNO; - } - flush = feof(input_file) ? Z_FINISH : Z_NO_FLUSH; - strm->next_in = in; + if (ferror(input_file)) { + deflateEnd(strm); + return Z_ERRNO; + } + flush = feof(input_file) ? Z_FINISH : Z_NO_FLUSH; + strm->next_in = in; do { strm->avail_out = sizeof(out); - strm->next_out = out; + strm->next_out = out; ret = deflate(strm, flush); have = sizeof(out) - strm->avail_out; - if (fwrite(out, 1, have, output_file) != have || ferror(output_file)) { - deflateEnd(strm); - return Z_ERRNO; - } - output_file_size += have; + if (fwrite(out, 1, have, output_file) != have || ferror(output_file)) { + deflateEnd(strm); + return Z_ERRNO; + } + output_file_size += have; } while (strm->avail_out == 0); } while (flush != Z_FINISH); - + return output_file_size; } @@ -432,9 +432,10 @@ static int compress_file(FILE *input_file, FILE *output_file, z_stream *strm) { static int compress_file(FILE *input_file, FILE *output_file) { char buffer[131072]; /* 128kB */ size_t output_file_size = 0; + size_t bytes; - while ((bytes = fread(buffer, 1, sizeof(buffer), input_file) > 0) { - if (bytes < 0) return bytes; + while ((bytes = fread(buffer, 1, sizeof(buffer), input_file)) > 0) { + if (bytes == 0) return bytes; fwrite(buffer, 1, bytes, output_file); output_file_size += bytes; } @@ -454,10 +455,10 @@ static size_t prepare_log_collection_file_to_upload(const char *filename) { #ifdef HAVE_ZLIB z_stream strm; strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); - if (ret != Z_OK) return ret; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) return ret; #endif output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); diff --git a/mediastreamer2 b/mediastreamer2 index 91f278207..548ccf41f 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 91f2782073c776b2d5ce3ab0a12561aea7610068 +Subproject commit 548ccf41fa0d3cd3ab48d0eff44d3bed45083312 diff --git a/tester/player_tester.c b/tester/player_tester.c index 57a57da60..a9b82b07a 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -20,7 +20,7 @@ static const char *_get_default_video_renderer(void){ static bool_t wait_for_eof(bool_t *eof, int *time,int time_refresh, int timeout) { while(*time < timeout && !*eof) { - usleep(time_refresh * 1000U); + ms_usleep(time_refresh * 1000U); *time += time_refresh; } return *time < timeout; From c2573d2bc166013b90ae6d0b18ead08517b6928c Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 7 Oct 2014 11:21:27 +0200 Subject: [PATCH 009/135] Use gzip format instead of zlib for log collection upload. --- coreapi/linphonecore.c | 113 ++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 75 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e477d9a44..a6689048c 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -279,7 +279,7 @@ static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, /* If we've not reach the end of file yet, fill the buffer with more data */ if (offset < core->log_collection_upload_information->size) { #ifdef HAVE_ZLIB - char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.zlib"); + char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); FILE *log_file = fopen(log_filename, "rb"); #else char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.txt"); @@ -391,111 +391,73 @@ static void process_response_from_post_file_log_collection(void *data, const bel } #ifdef HAVE_ZLIB - -static int compress_file(FILE *input_file, FILE *output_file, z_stream *strm) { - unsigned char in[131072]; /* 128kB */ - unsigned char out[131072]; /* 128kB */ - unsigned int have; - int flush; - int ret; - size_t output_file_size = 0; - - do { - strm->avail_in = fread(in, 1, sizeof(in), input_file); - if (ferror(input_file)) { - deflateEnd(strm); - return Z_ERRNO; - } - flush = feof(input_file) ? Z_FINISH : Z_NO_FLUSH; - strm->next_in = in; - do { - strm->avail_out = sizeof(out); - strm->next_out = out; - ret = deflate(strm, flush); - have = sizeof(out) - strm->avail_out; - if (fwrite(out, 1, have, output_file) != have || ferror(output_file)) { - deflateEnd(strm); - return Z_ERRNO; - } - output_file_size += have; - } while (strm->avail_out == 0); - } while (flush != Z_FINISH); - - return output_file_size; -} - +#define COMPRESS_FILE_PTR gzFile +#define COMPRESS_OPEN gzopen +#define COMPRESS_CLOSE gzclose #else +#define COMPRESS_FILE_PTR FILE* +#define COMPRESS_OPEN fopen +#define COMPRESS_CLOSE fclose +#endif /** * If zlib is not available the two log files are simply concatenated. */ -static int compress_file(FILE *input_file, FILE *output_file) { +static int compress_file(FILE *input_file, COMPRESS_FILE_PTR output_file) { char buffer[131072]; /* 128kB */ - size_t output_file_size = 0; - size_t bytes; + int bytes; while ((bytes = fread(buffer, 1, sizeof(buffer), input_file)) > 0) { - if (bytes == 0) return bytes; - fwrite(buffer, 1, bytes, output_file); - output_file_size += bytes; + if (bytes < 0) return bytes; +#ifdef HAVE_ZLIB + bytes = gzwrite(output_file, buffer, bytes); +#else + bytes = fwrite(buffer, 1, bytes, output_file); +#endif + if (bytes < 0) return bytes; } - return output_file_size; + return 0; } -#endif - -static size_t prepare_log_collection_file_to_upload(const char *filename) { +static int prepare_log_collection_file_to_upload(const char *filename) { char *input_filename = NULL; char *output_filename = NULL; FILE *input_file = NULL; - FILE *output_file = NULL; - size_t output_file_size = 0; - int ret; - -#ifdef HAVE_ZLIB - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); - if (ret != Z_OK) return ret; -#endif + COMPRESS_FILE_PTR output_file = NULL; + int ret = 0; output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); - output_file = fopen(output_filename, "a"); + output_file = COMPRESS_OPEN(output_filename, "a"); if (output_file == NULL) goto error; input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); input_file = fopen(input_filename, "r"); if (input_file == NULL) goto error; -#ifdef HAVE_ZLIB - SET_BINARY_MODE(output_file); - SET_BINARY_MODE(input_file); - ret = compress_file(input_file, output_file, &strm); -#else ret = compress_file(input_file, output_file); -#endif if (ret < 0) goto error; - output_file_size += ret; fclose(input_file); ortp_free(input_filename); input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); input_file = fopen(input_filename, "r"); if (input_file != NULL) { -#ifdef HAVE_ZLIB - SET_BINARY_MODE(input_file); - ret = compress_file(input_file, output_file, &strm); -#else ret = compress_file(input_file, output_file); -#endif if (ret < 0) goto error; - output_file_size += ret; } + error: if (input_file != NULL) fclose(input_file); - if (output_file != NULL) fclose(output_file); + if (output_file != NULL) COMPRESS_CLOSE(output_file); if (input_filename != NULL) ortp_free(input_filename); if (output_filename != NULL) ortp_free(output_filename); - return output_file_size; + return ret; +} + +static size_t get_size_of_file_to_upload(const char *filename) { + struct stat statbuf; + char *output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + FILE *output_file = fopen(output_filename, "rb"); + fstat(fileno(output_file), &statbuf); + fclose(output_file); + return statbuf.st_size; } void linphone_core_upload_log_collection(LinphoneCore *core) { @@ -510,14 +472,15 @@ void linphone_core_upload_log_collection(LinphoneCore *core) { memset(core->log_collection_upload_information, 0, sizeof(LinphoneContent)); #ifdef HAVE_ZLIB core->log_collection_upload_information->type = "application"; - core->log_collection_upload_information->subtype = "x-deflate"; - core->log_collection_upload_information->name = "linphone_log.zlib"; + core->log_collection_upload_information->subtype = "gzip"; + core->log_collection_upload_information->name = "linphone_log.gz"; #else core->log_collection_upload_information->type = "text"; core->log_collection_upload_information->subtype = "plain"; core->log_collection_upload_information->name = "linphone_log.txt"; #endif - core->log_collection_upload_information->size = prepare_log_collection_file_to_upload(core->log_collection_upload_information->name); + if (prepare_log_collection_file_to_upload(core->log_collection_upload_information->name) < 0) return; + core->log_collection_upload_information->size = get_size_of_file_to_upload(core->log_collection_upload_information->name); uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); req = belle_http_request_create("POST", uri, NULL, NULL, NULL); cbs.process_response = process_response_from_post_file_log_collection; From e91b1c83eda6623001597d7dc251a53b1a2f7955 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 7 Oct 2014 12:11:49 +0200 Subject: [PATCH 010/135] Add mutex for log collection + remove uploaded file when done. --- coreapi/linphonecore.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index a6689048c..5a88545bf 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -80,6 +80,7 @@ static const char *liblinphone_version= static OrtpLogFunc liblinphone_log_func = NULL; static bool_t liblinphone_log_collection_enabled = FALSE; static const char * liblinphone_log_collection_path = "."; +static ortp_mutex_t liblinphone_log_collection_mutex; static bool_t liblinphone_serialize_logs = FALSE; static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); static void linphone_core_run_hooks(LinphoneCore *lc); @@ -143,7 +144,6 @@ const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _Lin } void linphone_core_set_log_handler(OrtpLogFunc logfunc) { - liblinphone_log_func = logfunc; ortp_set_log_handler(liblinphone_log_func); } @@ -204,6 +204,7 @@ static void linphone_core_log_collection_handler(OrtpLogLevel level, const char log_filename1 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); log_filename2 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + ortp_mutex_lock(&liblinphone_log_collection_mutex); log_file = fopen(log_filename1, "a"); fstat(fileno(log_file), &statbuf); if (statbuf.st_size > LOGFILE_MAXSIZE) { @@ -221,6 +222,7 @@ static void linphone_core_log_collection_handler(OrtpLogLevel level, const char 1900 + lt->tm_year, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, (int)(tp.tv_usec / 1000), lname, msg); fflush(log_file); fclose(log_file); + ortp_mutex_unlock(&liblinphone_log_collection_mutex); ortp_free(log_filename1); ortp_free(log_filename2); @@ -240,24 +242,38 @@ void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, cons } void linphone_core_enable_log_collection(bool_t enable) { - liblinphone_log_collection_enabled = enable; - if (liblinphone_log_collection_enabled == TRUE) { + if ((enable == TRUE) && (liblinphone_log_collection_enabled == FALSE)) { + liblinphone_log_collection_enabled = TRUE; + ortp_mutex_init(&liblinphone_log_collection_mutex, NULL); + liblinphone_log_func = ortp_logv_out; ortp_set_log_handler(linphone_core_log_collection_handler); } else { + liblinphone_log_collection_enabled = FALSE; ortp_set_log_handler(liblinphone_log_func); } } +static void delete_log_collection_upload_file(void) { +#ifdef HAVE_ZLIB + char *filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); +#else + char *filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); +#endif + unlink(filename); +} + static void process_io_error_upload_log_collection(void *data, const belle_sip_io_error_event_t *event) { LinphoneCore *core = (LinphoneCore *)data; ms_error("I/O Error during log collection upload to %s", linphone_core_get_log_collection_upload_server_url(core)); linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "I/O Error"); + delete_log_collection_upload_file(); } static void process_auth_requested_upload_log_collection(void *data, belle_sip_auth_event_t *event) { LinphoneCore *core = (LinphoneCore *)data; ms_error("Error during log collection upload: auth requested to connect %s", linphone_core_get_log_collection_upload_server_url(core)); linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateNotDelivered, "Auth requested"); + delete_log_collection_upload_file(); } extern const char *multipart_boundary; @@ -386,6 +402,7 @@ static void process_response_from_post_file_log_collection(void *data, const bel if (file_url != NULL) { linphone_core_notify_log_collection_upload_state_changed(core, LinphoneCoreLogCollectionUploadStateDelivered, (const char *)file_url); } + delete_log_collection_upload_file(); } } } @@ -426,6 +443,7 @@ static int prepare_log_collection_file_to_upload(const char *filename) { COMPRESS_FILE_PTR output_file = NULL; int ret = 0; + ortp_mutex_lock(&liblinphone_log_collection_mutex); output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); output_file = COMPRESS_OPEN(output_filename, "a"); if (output_file == NULL) goto error; @@ -448,6 +466,7 @@ error: if (output_file != NULL) COMPRESS_CLOSE(output_file); if (input_filename != NULL) ortp_free(input_filename); if (output_filename != NULL) ortp_free(output_filename); + ortp_mutex_unlock(&liblinphone_log_collection_mutex); return ret; } From f89751515fdff8f89da1c51c5ca7a70bca68025f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 7 Oct 2014 13:52:18 +0200 Subject: [PATCH 011/135] Add two getter the the LinphonePlayer interface --- coreapi/fileplayer.c | 12 ++++++++++++ coreapi/linphonecore.h | 2 ++ coreapi/player.c | 18 ++++++++++++++++++ coreapi/private.h | 2 ++ mediastreamer2 | 2 +- 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/coreapi/fileplayer.c b/coreapi/fileplayer.c index 6752dc9e1..505ce2297 100644 --- a/coreapi/fileplayer.c +++ b/coreapi/fileplayer.c @@ -7,6 +7,8 @@ static int file_player_start(LinphonePlayer *obj); static int file_player_pause(LinphonePlayer *obj); static int file_player_seek(LinphonePlayer *obj, int time_ms); static MSPlayerState file_player_get_state(LinphonePlayer *obj); +static int file_player_get_duration(LinphonePlayer *obj); +static int file_player_get_current_position(LinphonePlayer *obj); static void file_player_close(LinphonePlayer *obj); static void file_player_eof_callback(void *user_data); @@ -20,6 +22,8 @@ LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *sn obj->pause = file_player_pause; obj->seek = file_player_seek; obj->get_state = file_player_get_state; + obj->get_duration = file_player_get_duration; + obj->get_position = file_player_get_current_position; obj->close = file_player_close; ms_file_player_set_eof_callback((MSFilePlayer *)obj->impl, file_player_eof_callback, obj); return obj; @@ -55,6 +59,14 @@ static MSPlayerState file_player_get_state(LinphonePlayer *obj) { return ms_file_player_get_state((MSFilePlayer *)obj->impl); } +static int file_player_get_duration(LinphonePlayer *obj) { + return ms_file_player_get_duration((MSFilePlayer *)obj->impl); +} + +static int file_player_get_current_position(LinphonePlayer *obj) { + return ms_file_player_get_current_position((MSFilePlayer *)obj->impl); +} + static void file_player_close(LinphonePlayer *obj) { ms_file_player_close((MSFilePlayer *)obj->impl); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 041e8756e..7b4f7d791 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -592,6 +592,8 @@ int linphone_player_start(LinphonePlayer *obj); int linphone_player_pause(LinphonePlayer *obj); int linphone_player_seek(LinphonePlayer *obj, int time_ms); MSPlayerState linphone_player_get_state(LinphonePlayer *obj); +int linphone_player_get_duration(LinphonePlayer *obj); +int linphone_player_get_position(LinphonePlayer *obj); void linphone_player_close(LinphonePlayer *obj); /** diff --git a/coreapi/player.c b/coreapi/player.c index ed9d4095f..ef5233a34 100644 --- a/coreapi/player.c +++ b/coreapi/player.c @@ -71,6 +71,24 @@ MSPlayerState linphone_player_get_state(LinphonePlayer *obj){ return obj->get_state(obj); } +/** + * Get the duration of the media + * @param obj the player + * @return The duration in milliseconds + */ +int linphone_player_get_duration(LinphonePlayer *obj) { + return obj->get_duration(obj); +} + +/** + * Get the position of the playback + * @param obj the player + * @return Position of the playback in milliseconds + */ +int linphone_player_get_position(LinphonePlayer *obj) { + return obj->get_position(obj); +} + /** * Close the player. * @param obj the player. diff --git a/coreapi/private.h b/coreapi/private.h index 936c7d3e8..40777e207 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -920,6 +920,8 @@ struct _LinphonePlayer{ int (*pause)(struct _LinphonePlayer* player); int (*seek)(struct _LinphonePlayer* player, int time_ms); MSPlayerState (*get_state)(struct _LinphonePlayer* player); + int (*get_duration)(struct _LinphonePlayer *player); + int (*get_position)(struct _LinphonePlayer *player); void (*close)(struct _LinphonePlayer* player); LinphonePlayerEofCallback cb; void *user_data; diff --git a/mediastreamer2 b/mediastreamer2 index 548ccf41f..636c6b928 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 548ccf41fa0d3cd3ab48d0eff44d3bed45083312 +Subproject commit 636c6b928d696cf8943653548351a5b8524491e9 From 95d4375ac83c20b9450302ddc4edc9ffe6c7135d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 7 Oct 2014 13:58:03 +0200 Subject: [PATCH 012/135] Make the FilePlayer to choose the default sound card for Android --- coreapi/linphonecore_jni.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 2b8fce197..ceccb5889 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5288,7 +5288,18 @@ JNIEXPORT void JNICALL Java_org_linphone_core_LinphonePlayer_close(JNIEnv *env, } JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneCore_createPlayer(JNIEnv *env, jobject jobj, jlong ptr) { - return (jlong)linphone_core_create_file_player((LinphoneCore *)ptr, NULL, NULL); + MSSndCard *snd_card = ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get()); + if(snd_card == NULL) { + ms_error("No default playback sound card found"); + return 0; + } + LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, NULL); + if(player == NULL) { + ms_error("Fails to create a player"); + return 0; + } else { + return (jlong)player; + } } JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCore_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { From c61b246f36ac53cc8cdad3b07dc058df01306b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 7 Oct 2014 14:21:31 +0200 Subject: [PATCH 013/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 636c6b928..31b8a4ff4 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 636c6b928d696cf8943653548351a5b8524491e9 +Subproject commit 31b8a4ff4ce093fdeb7a2af48c540fb8f3218997 From a2f8befbcf0da653d1048938b214593aa00e2778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 7 Oct 2014 14:48:23 +0200 Subject: [PATCH 014/135] Add JNI wrapper for linphone_player_get_duration and linphone_player_get_current_position --- coreapi/linphonecore.h | 2 +- coreapi/linphonecore_jni.cc | 8 ++++++++ coreapi/player.c | 2 +- java/common/org/linphone/core/LinphonePlayer.java | 12 ++++++++++++ java/impl/org/linphone/core/LinphonePlayerImpl.java | 12 ++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 7b4f7d791..5238173c0 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -593,7 +593,7 @@ int linphone_player_pause(LinphonePlayer *obj); int linphone_player_seek(LinphonePlayer *obj, int time_ms); MSPlayerState linphone_player_get_state(LinphonePlayer *obj); int linphone_player_get_duration(LinphonePlayer *obj); -int linphone_player_get_position(LinphonePlayer *obj); +int linphone_player_get_current_position(LinphonePlayer *obj); void linphone_player_close(LinphonePlayer *obj); /** diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index ceccb5889..7bec3f233 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5275,6 +5275,14 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getState(JNIEnv *en return (jint)linphone_player_get_state((LinphonePlayer *)ptr); } +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getDuration(JNIEnv *env, jobject jobj, jlong ptr) { + return (jint)linphone_player_get_duration((LinphonePlayer *)ptr); +} + +JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getCurrentPosition(JNIEnv *env, jobject jobj, jlong ptr) { + return (jint)linphone_player_get_current_position((LinphonePlayer *)ptr); +} + JNIEXPORT void JNICALL Java_org_linphone_core_LinphonePlayer_close(JNIEnv *env, jobject playerPtr, jlong ptr) { LinphonePlayer *player = (LinphonePlayer *)ptr; if(player->user_data) { diff --git a/coreapi/player.c b/coreapi/player.c index ef5233a34..54573ccb8 100644 --- a/coreapi/player.c +++ b/coreapi/player.c @@ -85,7 +85,7 @@ int linphone_player_get_duration(LinphonePlayer *obj) { * @param obj the player * @return Position of the playback in milliseconds */ -int linphone_player_get_position(LinphonePlayer *obj) { +int linphone_player_get_current_position(LinphonePlayer *obj) { return obj->get_position(obj); } diff --git a/java/common/org/linphone/core/LinphonePlayer.java b/java/common/org/linphone/core/LinphonePlayer.java index c38340af1..9a862d3b3 100644 --- a/java/common/org/linphone/core/LinphonePlayer.java +++ b/java/common/org/linphone/core/LinphonePlayer.java @@ -76,6 +76,18 @@ public interface LinphonePlayer { */ public State getState(); + /** + * Get the duration of the media + * @return The duration in milliseconds + */ + public int getDuration(); + + /** + * Get the position of the playback + * @return The position in milliseconds + */ + public int getCurrentPosition(); + /** * Close a file */ diff --git a/java/impl/org/linphone/core/LinphonePlayerImpl.java b/java/impl/org/linphone/core/LinphonePlayerImpl.java index a6c5cb2e5..eb6c18c71 100644 --- a/java/impl/org/linphone/core/LinphonePlayerImpl.java +++ b/java/impl/org/linphone/core/LinphonePlayerImpl.java @@ -44,6 +44,18 @@ public class LinphonePlayerImpl implements LinphonePlayer { return LinphonePlayer.State.fromValue(getState(nativePtr)); } + private native int getDuration(long nativePtr); + @Override + public synchronized int getDuration() { + return getDuration(nativePtr); + } + + private native int getCurrentPosition(long nativePtr); + @Override + public synchronized int getCurrentPosition() { + return getCurrentPosition(nativePtr); + } + private native void close(long nativePtr); @Override public synchronized void close() { From 6a9b1270ab6da81b2f77b2e78b3b216bb0180f39 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 7 Oct 2014 15:39:23 +0200 Subject: [PATCH 015/135] Update mediastreamer2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 31b8a4ff4..4694aca83 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 31b8a4ff4ce093fdeb7a2af48c540fb8f3218997 +Subproject commit 4694aca833fbe273420e95dddf4e04c6df433efb From 7392587689fbeb3cbc6341d7c28fdab9c99ec0da Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 7 Oct 2014 19:40:03 +0200 Subject: [PATCH 016/135] update ms2 and ortp --- mediastreamer2 | 2 +- oRTP | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mediastreamer2 b/mediastreamer2 index 4694aca83..58239c346 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4694aca833fbe273420e95dddf4e04c6df433efb +Subproject commit 58239c3467f020aa32f63b207a184843039fad50 diff --git a/oRTP b/oRTP index 540ee49bd..f4ecaee95 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 540ee49bd3f65139f7e5938cc6bc1f8a4353c3f7 +Subproject commit f4ecaee9570f411ff2689e2cb348505833320616 From 5ed38c36e5989b06571b18986b1ab55f16117502 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 8 Oct 2014 21:38:03 +0200 Subject: [PATCH 017/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 58239c346..23e88e815 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 58239c3467f020aa32f63b207a184843039fad50 +Subproject commit 23e88e815065c5a8dc358bcda88adcdd9f9a2b18 From 53135182b94a74c5ec34c43a49c1e9c088172f99 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 8 Oct 2014 21:38:57 +0200 Subject: [PATCH 018/135] fix tester compilation issue because ms_audio_diff symbol was stripped out of liblinphone, because unused internally. --- coreapi/linphonecore_jni.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 7bec3f233..ff6a92102 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" +#include "mediastreamer2/dsptools.h" } #include "mediastreamer2/msjava.h" #include "private.h" @@ -58,7 +59,8 @@ extern "C" void libmswebrtc_init(); #include #endif /*ANDROID*/ - +/*force linking of ms_audio_diff symbol because the tester requires it.*/ +static void *audiodiff=(void*)&ms_audio_diff; #define RETURN_USER_DATA_OBJECT(javaclass, funcprefix, cobj) \ { \ From eefa7551a1c2fd3aebb9a5e8149297cc63034949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 9 Oct 2014 11:40:38 +0200 Subject: [PATCH 019/135] Fix the listener of linphone player --- coreapi/linphonecore_jni.cc | 86 +++++++++---------- .../org/linphone/core/LinphonePlayer.java | 2 +- .../org/linphone/core/LinphonePlayerImpl.java | 6 +- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index ff6a92102..a676249ee 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5211,99 +5211,96 @@ JNIEXPORT jstring JNICALL Java_org_linphone_core_ErrorInfoImpl_getDetails(JNIEnv #endif /* Linphone Player */ -class LinphonePlayerData { -public: - LinphonePlayerData(jobject listener, jobject jLinphonePlayer) : - mListener(listener), - mJLinphonePlayer(jLinphonePlayer) {} +struct LinphonePlayerData { + LinphonePlayerData(JNIEnv *env, jobject listener, jobject jLinphonePlayer) : + mListener(env->NewGlobalRef(listener)), + mJLinphonePlayer(env->NewGlobalRef(jLinphonePlayer)) + { + mListenerClass = (jclass)env->NewGlobalRef(env->GetObjectClass(listener)); + mEndOfFileMethodID = env->GetMethodID(mListenerClass, "endOfFile", "(Lorg/linphone/core/LinphonePlayer;)V"); + if(mEndOfFileMethodID == NULL) { + ms_error("Could not get endOfFile method ID"); + env->ExceptionClear(); + } + } + + ~LinphonePlayerData() { + JNIEnv *env; + jvm->AttachCurrentThread(&env, NULL); + env->DeleteGlobalRef(mListener); + env->DeleteGlobalRef(mListenerClass); + env->DeleteGlobalRef(mJLinphonePlayer); + } jobject mListener; + jclass mListenerClass; jobject mJLinphonePlayer; - - static jmethodID endOfFileMethodID; - - static void init(JNIEnv *env) { - jclass listenerClass = env->FindClass("org/linphone/core/LinphonePlayer#Listener"); - endOfFileMethodID = env->GetMethodID(listenerClass, "endOfFile", "(Lorg/linphone/core/LinphonePlayer;)V"); - } + jmethodID mEndOfFileMethodID; }; -jmethodID LinphonePlayerData::endOfFileMethodID = NULL; - static void _eof_callback(LinphonePlayer *player, void *user_data) { JNIEnv *env; LinphonePlayerData *player_data = (LinphonePlayerData *)user_data; jvm->AttachCurrentThread(&env, NULL); - if(LinphonePlayerData::endOfFileMethodID == NULL) { - LinphonePlayerData::init(env); - } - env->CallVoidMethod(player_data->mListener, LinphonePlayerData::endOfFileMethodID, player_data->mJLinphonePlayer); - jvm->DetachCurrentThread(); + env->CallVoidMethod(player_data->mListener, player_data->mEndOfFileMethodID, player_data->mJLinphonePlayer); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_open(JNIEnv *env, jobject jPlayer, jlong ptr, jstring filename, jobject listener) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_open(JNIEnv *env, jobject jPlayer, jlong ptr, jstring filename, jobject listener) { LinphonePlayerData *data = NULL; LinphonePlayerEofCallback cb = NULL; if(listener) { - listener = env->NewGlobalRef(listener); - jPlayer = env->NewGlobalRef(jPlayer); - data = new LinphonePlayerData(listener, jPlayer); + data = new LinphonePlayerData(env, listener, jPlayer); cb = _eof_callback; } - if(linphone_player_open((LinphonePlayer *)ptr, env->GetStringUTFChars(filename, NULL), cb, &data) == -1) { - if(data) { - delete data; - env->DeleteGlobalRef(listener); - env->DeleteGlobalRef(jPlayer); - } + if(linphone_player_open((LinphonePlayer *)ptr, env->GetStringUTFChars(filename, NULL), cb, data) == -1) { + if(data) delete data; return -1; } return 0; } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_start(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_start(JNIEnv *env, jobject jobj, jlong ptr) { + LinphonePlayerData *player_data = (LinphonePlayerData *)((LinphonePlayer *)ptr)->user_data; return (jint)linphone_player_start((LinphonePlayer *)ptr); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_pause(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_pause(JNIEnv *env, jobject jobj, jlong ptr) { return (jint)linphone_player_pause((LinphonePlayer *)ptr); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_seek(JNIEnv *env, jobject jobj, jlong ptr, jint timeMs) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_seek(JNIEnv *env, jobject jobj, jlong ptr, jint timeMs) { return (jint)linphone_player_seek((LinphonePlayer *)ptr, timeMs); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getState(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getState(JNIEnv *env, jobject jobj, jlong ptr) { return (jint)linphone_player_get_state((LinphonePlayer *)ptr); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getDuration(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getDuration(JNIEnv *env, jobject jobj, jlong ptr) { return (jint)linphone_player_get_duration((LinphonePlayer *)ptr); } -JNIEXPORT jint JNICALL Java_org_linphone_core_LinphonePlayer_getCurrentPosition(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_getCurrentPosition(JNIEnv *env, jobject jobj, jlong ptr) { return (jint)linphone_player_get_current_position((LinphonePlayer *)ptr); } -JNIEXPORT void JNICALL Java_org_linphone_core_LinphonePlayer_close(JNIEnv *env, jobject playerPtr, jlong ptr) { +extern "C" void Java_org_linphone_core_LinphonePlayerImpl_close(JNIEnv *env, jobject playerPtr, jlong ptr) { LinphonePlayer *player = (LinphonePlayer *)ptr; if(player->user_data) { LinphonePlayerData *data = (LinphonePlayerData *)player->user_data; - env->DeleteGlobalRef(data->mListener); - env->DeleteGlobalRef(data->mJLinphonePlayer); - delete data; + if(data) delete data; player->user_data = NULL; } linphone_player_close(player); } -JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneCore_createPlayer(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *env, jobject jobj, jlong ptr) { MSSndCard *snd_card = ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get()); if(snd_card == NULL) { ms_error("No default playback sound card found"); return 0; } - LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, NULL); + LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, "MSAndroidDisplay"); if(player == NULL) { ms_error("Fails to create a player"); return 0; @@ -5312,13 +5309,10 @@ JNIEXPORT jlong JNICALL Java_org_linphone_core_LinphoneCore_createPlayer(JNIEnv } } -JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneCore_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { LinphonePlayer *player = (LinphonePlayer *)playerPtr; if(player->user_data) { - LinphonePlayerData *data = (LinphonePlayerData *)player->user_data; - env->DeleteGlobalRef(data->mListener); - env->DeleteGlobalRef(data->mJLinphonePlayer); - delete data; + delete (LinphonePlayerData *)player->user_data;; } linphone_file_player_destroy(player); } diff --git a/java/common/org/linphone/core/LinphonePlayer.java b/java/common/org/linphone/core/LinphonePlayer.java index 9a862d3b3..faba92d5d 100644 --- a/java/common/org/linphone/core/LinphonePlayer.java +++ b/java/common/org/linphone/core/LinphonePlayer.java @@ -49,7 +49,7 @@ public interface LinphonePlayer { * @param filename Name of the file to open * @return 0 on success, -1 on failure */ - public int open(final String filename, Listener listener); + public int open(String filename, Listener listener); /** * Start playback diff --git a/java/impl/org/linphone/core/LinphonePlayerImpl.java b/java/impl/org/linphone/core/LinphonePlayerImpl.java index eb6c18c71..08168e093 100644 --- a/java/impl/org/linphone/core/LinphonePlayerImpl.java +++ b/java/impl/org/linphone/core/LinphonePlayerImpl.java @@ -14,10 +14,10 @@ public class LinphonePlayerImpl implements LinphonePlayer { this.nativePtr = nativePtr; } - private native int open(long nativePtr, final String filename, Listener listener, LinphonePlayer player); + private native int open(long nativePtr, String filename, Listener listener); @Override - public synchronized int open(final String filename, Listener listener) { - return open(nativePtr, filename, listener, this); + public synchronized int open(String filename, Listener listener) { + return open(nativePtr, filename, listener); } private native int start(long nativePtr); From 58d045cda20a2ce7680c09ae1fab441fa4d60f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 9 Oct 2014 12:31:08 +0200 Subject: [PATCH 020/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 23e88e815..8a357b569 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 23e88e815065c5a8dc358bcda88adcdd9f9a2b18 +Subproject commit 8a357b56945bc1f3675d748ee237c451d3cf470e From 7e2ab369ba8d1107b72055c79bc28deb8347c674 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 9 Oct 2014 16:05:53 +0200 Subject: [PATCH 021/135] Added set_device_rotation public --- coreapi/linphonecore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 5238173c0..753030612 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -2687,7 +2687,7 @@ LINPHONE_PUBLIC void linphone_core_set_native_preview_window_id(LinphoneCore *lc LINPHONE_PUBLIC void linphone_core_use_preview_window(LinphoneCore *lc, bool_t yesno); int linphone_core_get_device_rotation(LinphoneCore *lc ); -void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation); +LINPHONE_PUBLIC void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation); /** * Get the camera sensor rotation. From cc50cd1b2bc4e71c2933f88703b9dab3db4f669d Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 9 Oct 2014 16:54:29 +0200 Subject: [PATCH 022/135] Blacklist some functions for the Python wrapper. --- tools/python/apixml2python.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py index b8f1bff2f..357816a5a 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -48,6 +48,7 @@ blacklisted_functions = [ 'linphone_chat_room_create_file_transfer_message', # missing LinphoneContent 'linphone_core_add_listener', 'linphone_core_can_we_add_call', # private function + 'linphone_core_enable_log_collection', # need to handle class properties 'linphone_core_get_audio_port_range', # to be handwritten because of result via arguments 'linphone_core_get_sip_transports', # missing LCSipTransports 'linphone_core_get_sip_transports_used', # missing LCSipTransports @@ -57,6 +58,7 @@ blacklisted_functions = [ 'linphone_core_publish', # missing LinphoneContent 'linphone_core_remove_listener', 'linphone_core_serialize_logs', # There is no use to wrap this function + 'linphone_core_set_log_collection_path', # need to handle class properties 'linphone_core_set_log_file', # There is no use to wrap this function 'linphone_core_set_log_handler', # Hand-written but put directly in the linphone module 'linphone_core_set_log_level', # There is no use to wrap this function From 1ece26b89486bac0bd2c1dd4b6f0a14059ef0901 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 10 Oct 2014 13:54:21 +0200 Subject: [PATCH 023/135] repair audio only build --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 8a357b569..2ce9d1844 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 8a357b56945bc1f3675d748ee237c451d3cf470e +Subproject commit 2ce9d184418ae68b36b3ee470df01343abcb407f From 6cccf2da3d76aadb96d887aa308aa7383f456341 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 10 Oct 2014 14:18:06 +0200 Subject: [PATCH 024/135] robustize linphone_core_accept_call_with_params() --- coreapi/linphonecore.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 5a88545bf..427b61577 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3487,8 +3487,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call){ * @param params the specific parameters for this call, for example whether video is accepted or not. Use NULL to use default parameters. * **/ -int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params) -{ +int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ SalOp *replaced; SalMediaDescription *new_md; bool_t was_ringing=FALSE; @@ -3501,11 +3500,17 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, call = (LinphoneCall*)linphone_core_get_calls(lc)->data; } - if (call->state==LinphoneCallConnected){ - /*call already accepted*/ - return -1; + switch(call->state){ + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + break; + default: + ms_error("linphone_core_accept_call_with_params() call [%p] is in state [%s], operation not permitted.", + call, linphone_call_state_to_string(call->state)); + return -1; + break; } - + /* check if this call is supposed to replace an already running one*/ replaced=sal_call_get_replaces(call->op); if (replaced){ From a24f4575cb3b59098bc2eeb7a276c1248a848eb1 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 10 Oct 2014 14:30:24 +0200 Subject: [PATCH 025/135] Update oRTP submodule. --- oRTP | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oRTP b/oRTP index f4ecaee95..05e0242a9 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit f4ecaee9570f411ff2689e2cb348505833320616 +Subproject commit 05e0242a91391747408340dffa34f9b4e1335be4 From d55e807b1b3f1449bbab754b8d99439cc7afbd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 10 Oct 2014 14:56:21 +0200 Subject: [PATCH 026/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 2ce9d1844..b75ce4033 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2ce9d184418ae68b36b3ee470df01343abcb407f +Subproject commit b75ce4033f2782ead74d346547e53424aff13664 From 56f0b091f5d660f509a5dd43051c2f33adab7b2c Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 10 Oct 2014 18:06:54 +0200 Subject: [PATCH 027/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index b75ce4033..0f0183231 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit b75ce4033f2782ead74d346547e53424aff13664 +Subproject commit 0f0183231426d5f373368a0820ba6aa8a1ad00e2 From fda6945d0dcaa35b17ef142b1861358e243cddf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 10 Oct 2014 19:05:16 +0200 Subject: [PATCH 028/135] Add licence text in fileplayer.c and player_tester.c --- coreapi/fileplayer.c | 19 +++++++++++++++++++ tester/player_tester.c | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/coreapi/fileplayer.c b/coreapi/fileplayer.c index 505ce2297..fc38f93a8 100644 --- a/coreapi/fileplayer.c +++ b/coreapi/fileplayer.c @@ -1,3 +1,22 @@ +/* +linphone +Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + #include "private.h" #include #include diff --git a/tester/player_tester.c b/tester/player_tester.c index a9b82b07a..8eae21dcd 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -1,3 +1,21 @@ +/* + liblinphone_tester - liblinphone test suite + Copyright (C) 2013 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, see . +*/ + #include "liblinphone_tester.h" static const char *_get_default_video_renderer(void){ From 01b9a97323d8d9111d435d897ec8d5959ba5b573 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 13 Oct 2014 12:07:28 +0200 Subject: [PATCH 029/135] Fix linphone_core_set_log_handler(). --- coreapi/linphonecore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 427b61577..4bfafcd0d 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -144,7 +144,7 @@ const LinphoneAddress *linphone_core_get_current_call_remote_address(struct _Lin } void linphone_core_set_log_handler(OrtpLogFunc logfunc) { - ortp_set_log_handler(liblinphone_log_func); + ortp_set_log_handler(logfunc); } void linphone_core_set_log_file(FILE *file) { From 94313a9535481d72cc3cdb4ddd1771e08d9bb1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 13 Oct 2014 13:38:46 +0200 Subject: [PATCH 030/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 0f0183231..f4c48ff76 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 0f0183231426d5f373368a0820ba6aa8a1ad00e2 +Subproject commit f4c48ff7699428655446ee41acb92c7a7f300fbb From 1b40d1a497d7ed5ddc58f2f1a569bcca1f6e0e51 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 13 Oct 2014 15:51:47 +0200 Subject: [PATCH 031/135] Fix automatic wrapping of linphone_core_set_avpf_mode() and linphone_core_set_avpf_rr_interval(). --- coreapi/linphonecore.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 4bfafcd0d..801eb7a43 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6841,6 +6841,7 @@ void linphone_core_remove_supported_tag(LinphoneCore *lc, const char *tag){ * The value set here is used for calls placed or received out of any proxy configured, or if the proxy config is configured with LinphoneAVPFDefault. * @param lc the LinphoneCore * @param mode the mode. + * @ingroup media_parameters **/ void linphone_core_set_avpf_mode(LinphoneCore *lc, LinphoneAVPFMode mode){ if (mode==LinphoneAVPFDefault) mode=LinphoneAVPFDisabled; @@ -6852,6 +6853,7 @@ void linphone_core_set_avpf_mode(LinphoneCore *lc, LinphoneAVPFMode mode){ * Return AVPF enablement. See linphone_core_set_avpf_mode() . * @param lc the core * @return the avpf enablement mode. + * @ingroup media_parameters **/ LinphoneAVPFMode linphone_core_get_avpf_mode(const LinphoneCore *lc){ return lc->rtp_conf.avpf_mode; @@ -6861,6 +6863,7 @@ LinphoneAVPFMode linphone_core_get_avpf_mode(const LinphoneCore *lc){ * Return the avpf report interval in seconds. * @param lc the LinphoneCore * @return the avpf report interval in seconds. + * @ingroup media_parameters **/ int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc){ return lp_config_get_int(lc->config,"rtp","avpf_rr_interval",5); @@ -6870,7 +6873,8 @@ int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc){ * Set the avpf report interval in seconds. * This value can be overriden by the proxy config using linphone_proxy_config_set_avpf_rr_interval(). * @param lc the core - * @param interval interval in seconds. + * @param interval interval in seconds. + * @ingroup media_parameters **/ void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int interval){ lp_config_set_int(lc->config,"rtp","avpf_rr_interval",interval); From 5d9bbeeffb1cdacfc0d42e40b3c760ceed17e209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 13 Oct 2014 17:16:40 +0200 Subject: [PATCH 032/135] Add a parameter to linphone_core_create_file_player() to specify the id of the drawing window --- coreapi/fileplayer.c | 4 ++-- coreapi/linphonecore.h | 3 ++- mediastreamer2 | 2 +- tester/player_tester.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/coreapi/fileplayer.c b/coreapi/fileplayer.c index fc38f93a8..103dd205e 100644 --- a/coreapi/fileplayer.c +++ b/coreapi/fileplayer.c @@ -31,11 +31,11 @@ static int file_player_get_current_position(LinphonePlayer *obj); static void file_player_close(LinphonePlayer *obj); static void file_player_eof_callback(void *user_data); -LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out) { +LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id) { LinphonePlayer *obj = ms_new0(LinphonePlayer, 1); if(snd_card == NULL) snd_card = lc->sound_conf.play_sndcard; if(video_out == NULL) video_out = linphone_core_get_video_display_filter(lc); - obj->impl = ms_file_player_new(snd_card, video_out); + obj->impl = ms_file_player_new(snd_card, video_out, window_id); obj->open = file_player_open; obj->start = file_player_start; obj->pause = file_player_pause; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 753030612..7dd4d8f55 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -602,9 +602,10 @@ void linphone_player_close(LinphonePlayer *obj); * @param lc A LinphoneCore * @param snd_card Playback sound card. If NULL, the sound card set in LinphoneCore will be used * @param video_out Video display. If NULL, the video display set in LinphoneCore will be used + * @param window_id Pointer on the drawing window * @return A pointer on the new instance. NULL if faild. */ -LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out); +LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id); /** * @brief Destroy a file player diff --git a/mediastreamer2 b/mediastreamer2 index f4c48ff76..9de5799b6 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit f4c48ff7699428655446ee41acb92c7a7f300fbb +Subproject commit 9de5799b612449269531ad196012bbd7d480e1c7 diff --git a/tester/player_tester.c b/tester/player_tester.c index 8eae21dcd..7c37a95de 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -59,7 +59,7 @@ static void play_file(const char *filename, bool_t unsupported_format) { CU_ASSERT_PTR_NOT_NULL(lc_manager); if(lc_manager == NULL) return; - player = linphone_core_create_file_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), _get_default_video_renderer()); + player = linphone_core_create_file_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), _get_default_video_renderer(), NULL); CU_ASSERT_PTR_NOT_NULL(player); if(player == NULL) goto fail; From 3ae7d1098a0118266c07fce1b21e6acd72e7d86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 14 Oct 2014 10:26:44 +0200 Subject: [PATCH 033/135] Modify JNI glue to pass the window ID while creating a player --- coreapi/linphonecore_jni.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index a676249ee..92b92daf3 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -28,6 +28,7 @@ extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" #include "mediastreamer2/dsptools.h" +#include "mediastreamer2/fileplayer.h" } #include "mediastreamer2/msjava.h" #include "private.h" @@ -5294,15 +5295,18 @@ extern "C" void Java_org_linphone_core_LinphonePlayerImpl_close(JNIEnv *env, job linphone_player_close(player); } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *env, jobject jobj, jlong ptr) { +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *env, jobject jobj, jlong ptr, jobject window) { + jobject window_ref = NULL; MSSndCard *snd_card = ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get()); if(snd_card == NULL) { ms_error("No default playback sound card found"); return 0; } - LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, "MSAndroidDisplay"); + window_ref = env->NewGlobalRef(window); + LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, "MSAndroidDisplay", window_ref); if(player == NULL) { ms_error("Fails to create a player"); + if(window_ref) env->DeleteGlobalRef(window_ref); return 0; } else { return (jlong)player; @@ -5312,7 +5316,11 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *en extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { LinphonePlayer *player = (LinphonePlayer *)playerPtr; if(player->user_data) { - delete (LinphonePlayerData *)player->user_data;; + delete (LinphonePlayerData *)player->user_data; + } + MSFilePlayer *ms_player = (MSFilePlayer *)player->impl; + if(ms_player->window_id) { + env->DeleteGlobalRef((jobject)ms_player->window_id); } linphone_file_player_destroy(player); } From 99d7abfd1e67367ac285c0343da7ded235e1177d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 14 Oct 2014 10:42:19 +0200 Subject: [PATCH 034/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 9de5799b6..2fa9c8e25 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 9de5799b612449269531ad196012bbd7d480e1c7 +Subproject commit 2fa9c8e258eacadc789f87d3ea3a152faea291ea From 0f6ce268ae0653553ba68e93e3d015ce59530d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 14 Oct 2014 10:49:08 +0200 Subject: [PATCH 035/135] Fix compilation error for Android --- coreapi/linphonecore_jni.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 92b92daf3..dbcc128c7 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5318,9 +5318,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *en if(player->user_data) { delete (LinphonePlayerData *)player->user_data; } - MSFilePlayer *ms_player = (MSFilePlayer *)player->impl; - if(ms_player->window_id) { - env->DeleteGlobalRef((jobject)ms_player->window_id); - } + jobject window_id = (jobject)ms_file_player_get_window_id((MSFilePlayer *)player->impl); + if(window_id) env->DeleteGlobalRef(window_id); linphone_file_player_destroy(player); } From b23d6a1d6d4cabeb9f3ea822d91f659357a61165 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 14 Oct 2014 15:21:00 +0200 Subject: [PATCH 036/135] Do not accept video call when video is disabled but video policy is set to automatically accept. --- coreapi/linphonecall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 94d2e20f4..a838fabdc 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -793,7 +793,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); /*set video support */ md=sal_call_get_remote_media_description(op); - call->params->has_video = lc->video_policy.automatically_accept; + call->params->has_video = linphone_core_video_enabled(lc) && lc->video_policy.automatically_accept; if (md) { // It is licit to receive an INVITE without SDP // In this case WE chose the media parameters according to policy. From 7bc0486b57d646ecce175280bf4f098a376eaf00 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 14 Oct 2014 15:21:43 +0200 Subject: [PATCH 037/135] Apply microphone mute to all calls. --- coreapi/linphonecall.c | 2 +- coreapi/linphonecore.c | 34 ++++++++++++++++++---------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index a838fabdc..5e7f9e5af 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -2176,7 +2176,7 @@ void linphone_call_start_media_streams(LinphoneCall *call, bool_t all_inputs_mut call, linphone_core_get_upload_bandwidth(lc),linphone_core_get_download_bandwidth(lc)); if (call->audiostream!=NULL) { - linphone_call_start_audio_stream(call,cname,all_inputs_muted,send_ringbacktone,use_arc); + linphone_call_start_audio_stream(call,cname,all_inputs_muted||call->audio_muted,send_ringbacktone,use_arc); } call->current_params->has_video=FALSE; if (call->videostream!=NULL) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 801eb7a43..fa1adf331 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -4584,26 +4584,28 @@ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){ return lc->sound_conf.ea; } +static void linphone_core_mute_audio_stream(LinphoneCore *lc, AudioStream *st, bool_t val) { + audio_stream_set_mic_gain(st, + (val==TRUE) ? 0 : pow(10,lc->sound_conf.soft_mic_lev/10)); + if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ + audio_stream_mute_rtp(st,val); + } +} + void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ - LinphoneCall *call=linphone_core_get_current_call(lc); - AudioStream *st=NULL; + LinphoneCall *call; + const MSList *list; + const MSList *elem; + if (linphone_core_is_in_conference(lc)){ lc->conf_ctx.local_muted=val; - st=lc->conf_ctx.local_participant; - }else if (call==NULL){ - ms_warning("linphone_core_mute_mic(): No current call !"); - return; - }else{ - st=call->audiostream; - call->audio_muted=val; + linphone_core_mute_audio_stream(lc, lc->conf_ctx.local_participant, val); } - if (st!=NULL){ - audio_stream_set_mic_gain(st, - (val==TRUE) ? 0 : pow(10,lc->sound_conf.soft_mic_lev/10)); - if ( linphone_core_get_rtp_no_xmit_on_audio_mute(lc) ){ - audio_stream_mute_rtp(st,val); - } - + list = linphone_core_get_calls(lc); + for (elem = list; elem != NULL; elem = elem->next) { + call = (LinphoneCall *)elem->data; + call->audio_muted = val; + linphone_core_mute_audio_stream(lc, call->audiostream, val); } } From 4e1e0df40527b8e8cba741045b90e996293850ed Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Tue, 14 Oct 2014 13:59:08 +0200 Subject: [PATCH 038/135] make sure session timer requires refresher to not be linphone --- coreapi/bellesip_sal/sal_op_call.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 9efbbb7ee..bf352d1b4 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -639,7 +639,7 @@ static void sal_op_fill_invite(SalOp *op, belle_sip_request_t* invite) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),BELLE_SIP_HEADER(create_allow(op->base.root->enable_sip_update))); if (op->base.root->session_expires!=0){ - belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "200")); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Session-expires", "600;refresher=uas")); belle_sip_message_add_header(BELLE_SIP_MESSAGE(invite),belle_sip_header_create( "Supported", "timer")); } if (op->base.local_media){ @@ -766,9 +766,10 @@ int sal_call_accept(SalOp*h){ } belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(create_allow(h->base.root->enable_sip_update))); if (h->base.root->session_expires!=0){ - if (h->supports_session_timers) { +/* if (h->supports_session_timers) {*/ belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create("Supported", "timer")); - } + belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),belle_sip_header_create( "Session-expires", "600;refresher=uac")); + /*}*/ } if ((contact_header=sal_op_create_contact(h))) { From 6e9bfc276bf60f852919064527d428e62f03ab4d Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Tue, 14 Oct 2014 15:36:43 +0200 Subject: [PATCH 039/135] add more test for presence --- tester/presence_tester.c | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tester/presence_tester.c b/tester/presence_tester.c index a1a1b6b63..ca3c8c18e 100644 --- a/tester/presence_tester.c +++ b/tester/presence_tester.c @@ -347,6 +347,62 @@ static void presence_information(void) { linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } +#define USE_PRESENCE_SERVER 0 + +#if USE_PRESENCE_SERVER +static void test_subscribe_notify_publish(void) { + + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneProxyConfig* proxy; + LinphonePresenceModel* presence; + + LpConfig *pauline_lp = linphone_core_get_config(pauline->lc); + char* lf_identity=linphone_address_as_string_uri_only(marie->identity); + LinphoneFriend *lf = linphone_core_create_friend_with_address(pauline->lc,lf_identity); + + lp_config_set_int(pauline_lp,"sip","subscribe_expires",5); + + linphone_core_add_friend(pauline->lc,lf); + + /*wait for subscribe acknowledgment*/ + wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,1,2000); + CU_ASSERT_EQUAL(LinphoneStatusOffline,linphone_friend_get_status(lf)); + + /*enable publish*/ + + linphone_core_get_default_proxy(marie->lc,&proxy); + linphone_proxy_config_edit(proxy); + + linphone_proxy_config_enable_publish(proxy,TRUE); + linphone_proxy_config_set_publish_expires(proxy,3); + linphone_proxy_config_done(proxy); + + /*wait for marie status*/ + wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,2,2000); + CU_ASSERT_EQUAL(LinphoneStatusOnline,linphone_friend_get_status(lf)); + + presence =linphone_presence_model_new_with_activity(LinphonePresenceActivityOffline,NULL); + linphone_core_set_presence_model(marie->lc,presence); + + /*wait for new status*/ + wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,3,2000); + CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + + /*wait for refresh*/ + wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); + CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + + //linphone_core_remove_friend(pauline->lc,lf); + /*wait for final notify*/ + //wait_for_until(pauline->lc,marie->lc,&pauline->stat.number_of_NotifyReceived,4,5000); + //CU_ASSERT_EQUAL(LinphonePresenceActivityOffline,linphone_friend_get_status(lf)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +#endif test_t presence_tests[] = { { "Simple Subscribe", simple_subscribe }, @@ -356,6 +412,9 @@ test_t presence_tests[] = { { "Unsubscribe while subscribing", unsubscribe_while_subscribing }, { "Presence information", presence_information }, { "App managed presence failure", subscribe_failure_handle_by_app }, +#if USE_PRESENCE_SERVER + { "Subscribe with late publish", test_subscribe_notify_publish }, +#endif }; test_suite_t presence_test_suite = { From 182d7989d8f4426c2a78e548647f00d57040c98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Tue, 14 Oct 2014 16:05:52 +0200 Subject: [PATCH 040/135] Add a window ID parameter to LinphoneCore.createFilePlayer() --- java/common/org/linphone/core/LinphoneCore.java | 2 +- java/impl/org/linphone/core/LinphoneCoreImpl.java | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index 26b721675..dfa2f6bca 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1786,7 +1786,7 @@ public interface LinphoneCore { * Create a media player * @return An object that implement LinphonePlayer */ - public LinphonePlayer createPlayer(); + public LinphonePlayer createPlayer(AndroidVideoWindowImpl window); /** * Destroy a player diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 8bd3f0d7e..750ec1903 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -25,6 +25,7 @@ import java.io.IOException; import org.linphone.core.LinphoneCall.State; import org.linphone.mediastream.Log; +import org.linphone.mediastream.video.AndroidVideoWindowImpl; import org.linphone.mediastream.video.capture.hwconf.Hacks; import android.content.Context; @@ -1279,12 +1280,12 @@ class LinphoneCoreImpl implements LinphoneCore { return getFileTransferServer(nativePtr); } - private native long createPlayer(long nativePtr); + private native long createPlayer(long nativePtr, AndroidVideoWindowImpl window); @Override - public synchronized LinphonePlayer createPlayer() { - long player = createPlayer(nativePtr); - if(player != 0) { - return new LinphonePlayerImpl(createPlayer(nativePtr)); + public synchronized LinphonePlayer createPlayer(AndroidVideoWindowImpl window) { + long playerPtr = createPlayer(nativePtr, window); + if(playerPtr != 0) { + return new LinphonePlayerImpl(playerPtr); } else { return null; } From 690b1d880069fa8bafa7a8999d21baa574706d9a Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 14 Oct 2014 21:31:39 +0200 Subject: [PATCH 041/135] codec: disable AAC on Android device without hardware AEC --- coreapi/linphonecore.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index fa1adf331..816f6b618 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1011,6 +1011,13 @@ static MSList *codec_append_if_new(MSList *l, PayloadType *pt){ return l; } +#if defined(ANDROID) +static int is_aac_eld_payload(const void* a, const void* b) { + PayloadType *pt = (PayloadType*)a; + return strncmp(pt->mime_type, "mpeg4-generic", strlen("mpeg4-generic")); +} +#endif + static void codecs_config_read(LinphoneCore *lc) { int i; @@ -1025,6 +1032,17 @@ static void codecs_config_read(LinphoneCore *lc) } } audio_codecs=add_missing_codecs(lc,SalAudio,audio_codecs); + +#if defined(ANDROID) + /* AAC-ELD requires hardware AEC */ + if (lc->sound_conf.capt_sndcard && + !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER)) { + /* Remove AAC-ELD */ + audio_codecs = ms_list_remove_custom(audio_codecs, is_aac_eld_payload, NULL); + ms_message("Disable AAC-ELD (needs hardware AEC)"); + } +#endif + for (i=0;get_codec(lc,"video_codec",i,&pt);i++){ if (pt){ if (!ms_filter_codec_supported(pt->mime_type)){ @@ -3510,7 +3528,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call, return -1; break; } - + /* check if this call is supposed to replace an already running one*/ replaced=sal_call_get_replaces(call->op); if (replaced){ From deaaf0a19b20f55ad258fb4a6b95a84af32146cc Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Tue, 14 Oct 2014 23:40:44 +0200 Subject: [PATCH 042/135] Update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 2fa9c8e25..8ae28e84a 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2fa9c8e258eacadc789f87d3ea3a152faea291ea +Subproject commit 8ae28e84aac235daa3d032bb7b35888764ebd8c6 From a7d81a117d5ec933e08bb3dccece05ce2f8a11d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 09:37:06 +0200 Subject: [PATCH 043/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 8ae28e84a..c45dca7be 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 8ae28e84aac235daa3d032bb7b35888764ebd8c6 +Subproject commit c45dca7bedea220fe3bc718b89bd36a21c97fa2f From 08539a08958f2fdab6fbf01723d76db8dd4eddbe Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 15 Oct 2014 10:57:08 +0200 Subject: [PATCH 044/135] atomically sync the lpconfig on disk --- coreapi/lpconfig.c | 7 ++++++- coreapi/misc.c | 2 ++ mediastreamer2 | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index c1ffea0fc..5c556acc7 100644 --- a/coreapi/lpconfig.c +++ b/coreapi/lpconfig.c @@ -77,6 +77,7 @@ struct _LpConfig{ int refcnt; FILE *file; char *filename; + char *tmpfilename; MSList *sections; int modified; int readonly; @@ -349,6 +350,7 @@ LpConfig *lp_config_new_with_factory(const char *config_filename, const char *fa if (config_filename!=NULL){ ms_message("Using (r/w) config information from %s", config_filename); lpconfig->filename=ortp_strdup(config_filename); + lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",config_filename); lpconfig->file=fopen(config_filename,"r+"); if (lpconfig->file!=NULL){ lp_config_parse(lpconfig,lpconfig->file); @@ -583,7 +585,7 @@ int lp_config_sync(LpConfig *lpconfig){ /* don't create group/world-accessible files */ (void) umask(S_IRWXG | S_IRWXO); #endif - file=fopen(lpconfig->filename,"w"); + file=fopen(lpconfig->tmpfilename,"w"); if (file==NULL){ ms_warning("Could not write %s ! Maybe it is read-only. Configuration will not be saved.",lpconfig->filename); lpconfig->readonly=1; @@ -591,6 +593,9 @@ int lp_config_sync(LpConfig *lpconfig){ } ms_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)file); fclose(file); + if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){ + ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno)); + } lpconfig->modified=0; return 0; } diff --git a/coreapi/misc.c b/coreapi/misc.c index 30100a304..c89111333 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -966,6 +966,8 @@ unsigned int linphone_core_get_audio_features(LinphoneCore *lc){ else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF; else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO; else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING; + else if (strcasecmp(name,"LOCAL_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_LOCAL_PLAYING; + else if (strcasecmp(name,"REMOTE_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_REMOTE_PLAYING; else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL; else if (strcasecmp(name,"NONE")==0) ret=0; else ms_error("Unsupported audio feature %s requested in config file.",name); diff --git a/mediastreamer2 b/mediastreamer2 index c45dca7be..2054fd609 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit c45dca7bedea220fe3bc718b89bd36a21c97fa2f +Subproject commit 2054fd60911b585458b62d52092894a5d1b59503 From 40a82f05f5f8a01227d6d6a3a71743bc3c047d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 12:10:03 +0200 Subject: [PATCH 045/135] Rename fileplayer.c into localplayer.c --- coreapi/Makefile.am | 2 +- coreapi/fileplayer.c | 96 ------------------------------------- coreapi/linphonecore.h | 10 ++-- coreapi/linphonecore_jni.cc | 6 +-- coreapi/localplayer.c | 96 +++++++++++++++++++++++++++++++++++++ mediastreamer2 | 2 +- tester/player_tester.c | 6 +-- 7 files changed, 109 insertions(+), 109 deletions(-) delete mode 100644 coreapi/fileplayer.c create mode 100644 coreapi/localplayer.c diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index e2a6beb4f..2bb8498d9 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -62,7 +62,7 @@ liblinphone_la_SOURCES=\ call_log.c \ call_params.c \ player.c \ - fileplayer.c \ + localplayer.c \ $(GITVERSION_FILE) if BUILD_UPNP diff --git a/coreapi/fileplayer.c b/coreapi/fileplayer.c deleted file mode 100644 index 103dd205e..000000000 --- a/coreapi/fileplayer.c +++ /dev/null @@ -1,96 +0,0 @@ -/* -linphone -Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "private.h" -#include -#include - -static int file_player_open(LinphonePlayer *obj, const char *filename); -static int file_player_start(LinphonePlayer *obj); -static int file_player_pause(LinphonePlayer *obj); -static int file_player_seek(LinphonePlayer *obj, int time_ms); -static MSPlayerState file_player_get_state(LinphonePlayer *obj); -static int file_player_get_duration(LinphonePlayer *obj); -static int file_player_get_current_position(LinphonePlayer *obj); -static void file_player_close(LinphonePlayer *obj); -static void file_player_eof_callback(void *user_data); - -LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id) { - LinphonePlayer *obj = ms_new0(LinphonePlayer, 1); - if(snd_card == NULL) snd_card = lc->sound_conf.play_sndcard; - if(video_out == NULL) video_out = linphone_core_get_video_display_filter(lc); - obj->impl = ms_file_player_new(snd_card, video_out, window_id); - obj->open = file_player_open; - obj->start = file_player_start; - obj->pause = file_player_pause; - obj->seek = file_player_seek; - obj->get_state = file_player_get_state; - obj->get_duration = file_player_get_duration; - obj->get_position = file_player_get_current_position; - obj->close = file_player_close; - ms_file_player_set_eof_callback((MSFilePlayer *)obj->impl, file_player_eof_callback, obj); - return obj; -} - -void linphone_file_player_destroy(LinphonePlayer *obj) { - ms_file_player_free((MSFilePlayer *)obj->impl); - ms_free(obj); -} - -bool_t linphone_file_player_matroska_supported(void) { - return ms_file_player_matroska_supported(); -} - -static int file_player_open(LinphonePlayer *obj, const char *filename) { - return ms_file_player_open((MSFilePlayer *)obj->impl, filename) ? 0 : -1; -} - -static int file_player_start(LinphonePlayer *obj) { - return ms_file_player_start((MSFilePlayer *)obj->impl) ? 0 : -1; -} - -static int file_player_pause(LinphonePlayer *obj) { - ms_file_player_pause((MSFilePlayer *)obj->impl); - return 0; -} - -static int file_player_seek(LinphonePlayer *obj, int time_ms) { - return ms_file_player_seek((MSFilePlayer *)obj->impl, time_ms) ? 0 : -1; -} - -static MSPlayerState file_player_get_state(LinphonePlayer *obj) { - return ms_file_player_get_state((MSFilePlayer *)obj->impl); -} - -static int file_player_get_duration(LinphonePlayer *obj) { - return ms_file_player_get_duration((MSFilePlayer *)obj->impl); -} - -static int file_player_get_current_position(LinphonePlayer *obj) { - return ms_file_player_get_current_position((MSFilePlayer *)obj->impl); -} - -static void file_player_close(LinphonePlayer *obj) { - ms_file_player_close((MSFilePlayer *)obj->impl); -} - -static void file_player_eof_callback(void *user_data) { - LinphonePlayer *obj = (LinphonePlayer *)user_data; - obj->cb(obj, obj->user_data); -} diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 7dd4d8f55..2f1036678 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -599,25 +599,25 @@ void linphone_player_close(LinphonePlayer *obj); /** * @brief Create an independent media file player. * This player support WAVE and MATROSKA formats. - * @param lc A LinphoneCore + * @param lc A LinphoneCore object * @param snd_card Playback sound card. If NULL, the sound card set in LinphoneCore will be used * @param video_out Video display. If NULL, the video display set in LinphoneCore will be used * @param window_id Pointer on the drawing window * @return A pointer on the new instance. NULL if faild. */ -LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_file_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id); +LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id); /** - * @brief Destroy a file player + * @brief Destroy a local player * @param obj File player to destroy */ -LINPHONE_PUBLIC void linphone_file_player_destroy(LinphonePlayer *obj); +LINPHONE_PUBLIC void linphone_local_player_destroy(LinphonePlayer *obj); /** * @brief Check whether Matroksa format is supported by the player * @return TRUE if it is supported */ -LINPHONE_PUBLIC bool_t linphone_file_player_matroska_supported(void); +LINPHONE_PUBLIC bool_t linphone_local_player_matroska_supported(void); /** * LinphoneCallState enum represents the different state a call can reach into. diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index dbcc128c7..c1cc56e47 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5303,7 +5303,7 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *en return 0; } window_ref = env->NewGlobalRef(window); - LinphonePlayer *player = linphone_core_create_file_player((LinphoneCore *)ptr, snd_card, "MSAndroidDisplay", window_ref); + LinphonePlayer *player = linphone_core_create_local_player((LinphoneCore *)ptr, snd_card, "MSAndroidDisplay", window_ref); if(player == NULL) { ms_error("Fails to create a player"); if(window_ref) env->DeleteGlobalRef(window_ref); @@ -5318,7 +5318,7 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *en if(player->user_data) { delete (LinphonePlayerData *)player->user_data; } - jobject window_id = (jobject)ms_file_player_get_window_id((MSFilePlayer *)player->impl); + jobject window_id = (jobject)ms_media_player_get_window_id((MSMediaPlayer *)player->impl); if(window_id) env->DeleteGlobalRef(window_id); - linphone_file_player_destroy(player); + linphone_local_player_destroy(player); } diff --git a/coreapi/localplayer.c b/coreapi/localplayer.c new file mode 100644 index 000000000..9d6e1b5c3 --- /dev/null +++ b/coreapi/localplayer.c @@ -0,0 +1,96 @@ +/* +linphone +Copyright (C) 2000 - 2010 Simon MORLAT (simon.morlat@linphone.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "private.h" +#include +#include + +static int _local_player_open(LinphonePlayer *obj, const char *filename); +static int _local_player_start(LinphonePlayer *obj); +static int _local_player_pause(LinphonePlayer *obj); +static int _local_player_seek(LinphonePlayer *obj, int time_ms); +static MSPlayerState _local_player_get_state(LinphonePlayer *obj); +static int _local_player_get_duration(LinphonePlayer *obj); +static int _local_player_get_current_position(LinphonePlayer *obj); +static void _local_player_close(LinphonePlayer *obj); +static void _local_player_eof_callback(void *user_data); + +LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id) { + LinphonePlayer *obj = ms_new0(LinphonePlayer, 1); + if(snd_card == NULL) snd_card = lc->sound_conf.play_sndcard; + if(video_out == NULL) video_out = linphone_core_get_video_display_filter(lc); + obj->impl = ms_media_player_new(snd_card, video_out, window_id); + obj->open = _local_player_open; + obj->start = _local_player_start; + obj->pause = _local_player_pause; + obj->seek = _local_player_seek; + obj->get_state = _local_player_get_state; + obj->get_duration = _local_player_get_duration; + obj->get_position = _local_player_get_current_position; + obj->close = _local_player_close; + ms_media_player_set_eof_callback((MSMediaPlayer *)obj->impl, _local_player_eof_callback, obj); + return obj; +} + +void linphone_local_player_destroy(LinphonePlayer *obj) { + ms_media_player_free((MSMediaPlayer *)obj->impl); + ms_free(obj); +} + +bool_t linphone_local_player_matroska_supported(void) { + return ms_media_player_matroska_supported(); +} + +static int _local_player_open(LinphonePlayer *obj, const char *filename) { + return ms_media_player_open((MSMediaPlayer *)obj->impl, filename) ? 0 : -1; +} + +static int _local_player_start(LinphonePlayer *obj) { + return ms_media_player_start((MSMediaPlayer *)obj->impl) ? 0 : -1; +} + +static int _local_player_pause(LinphonePlayer *obj) { + ms_media_player_pause((MSMediaPlayer *)obj->impl); + return 0; +} + +static int _local_player_seek(LinphonePlayer *obj, int time_ms) { + return ms_media_player_seek((MSMediaPlayer *)obj->impl, time_ms) ? 0 : -1; +} + +static MSPlayerState _local_player_get_state(LinphonePlayer *obj) { + return ms_media_player_get_state((MSMediaPlayer *)obj->impl); +} + +static int _local_player_get_duration(LinphonePlayer *obj) { + return ms_media_player_get_duration((MSMediaPlayer *)obj->impl); +} + +static int _local_player_get_current_position(LinphonePlayer *obj) { + return ms_media_player_get_current_position((MSMediaPlayer *)obj->impl); +} + +static void _local_player_close(LinphonePlayer *obj) { + ms_media_player_close((MSMediaPlayer *)obj->impl); +} + +static void _local_player_eof_callback(void *user_data) { + LinphonePlayer *obj = (LinphonePlayer *)user_data; + obj->cb(obj, obj->user_data); +} diff --git a/mediastreamer2 b/mediastreamer2 index 2054fd609..38b219a95 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2054fd60911b585458b62d52092894a5d1b59503 +Subproject commit 38b219a95a5ecdf2ed52594c6aadcf0a78c880f5 diff --git a/tester/player_tester.c b/tester/player_tester.c index 7c37a95de..14bb6d3ab 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -59,7 +59,7 @@ static void play_file(const char *filename, bool_t unsupported_format) { CU_ASSERT_PTR_NOT_NULL(lc_manager); if(lc_manager == NULL) return; - player = linphone_core_create_file_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), _get_default_video_renderer(), NULL); + player = linphone_core_create_local_player(lc_manager->lc, ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()), _get_default_video_renderer(), NULL); CU_ASSERT_PTR_NOT_NULL(player); if(player == NULL) goto fail; @@ -79,12 +79,12 @@ static void play_file(const char *filename, bool_t unsupported_format) { linphone_player_close(player); fail: - if(player) linphone_file_player_destroy(player); + if(player) linphone_local_player_destroy(player); if(lc_manager) linphone_core_manager_destroy(lc_manager); } static void playing_test(void) { - play_file("sounds/hello_opus_h264.mkv", !linphone_file_player_matroska_supported()); + play_file("sounds/hello_opus_h264.mkv", !linphone_local_player_matroska_supported()); } test_t player_tests[] = { From 4be7d1c93102790ea74893632eb5d26ddabc4f71 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 15 Oct 2014 12:31:56 +0200 Subject: [PATCH 046/135] Fix message_storage for WP8 --- build/wp8/LibLinphone.vcxproj | 9 ++++++--- coreapi/message_storage.c | 13 ++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/build/wp8/LibLinphone.vcxproj b/build/wp8/LibLinphone.vcxproj index 7d8d4b014..f1bdecc0d 100644 --- a/build/wp8/LibLinphone.vcxproj +++ b/build/wp8/LibLinphone.vcxproj @@ -53,7 +53,7 @@ Level4 - $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;%(AdditionalIncludeDirectories) + $(ProjectDir)..\..\..\belle-sip\include;$(ProjectDir)..\..\oRTP\include;$(ProjectDir)..\..\mediastreamer2\include;$(ProjectDir)..\..\..\tunnel\include;$(ProjectDir)..\..\coreapi;$(ProjectDir)..\..\include;$(SolutionDir)$(Platform)\$(Configuration)\include;$(ProjectDir)..\..\..\zlib;$(ProjectDir)..\..\..\sqlite\;%(AdditionalIncludeDirectories) __STDC_CONSTANT_MACROS;_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;_USRDLL;WINDOW_NATIVE;_TRUE_TIME;IN_LINPHONE;USE_BELLESIP;TUNNEL_ENABLED;VIDEO_ENABLED;LINPHONE_PACKAGE_NAME="linphone";LINPHONE_VERSION="Devel";LIBLINPHONE_EXPORTS;LINPHONE_PLUGINS_DIR=".";UNICODE;_XKEYCHECK_H;HAVE_ZLIB;%(PreprocessorDefinitions) Default NotUsing @@ -74,7 +74,7 @@ - _DEBUG;%(PreprocessorDefinitions) + _DEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) true @@ -82,7 +82,7 @@ - NDEBUG;%(PreprocessorDefinitions) + NDEBUG;MSG_STORAGE_ENABLED;%(PreprocessorDefinitions) MaxSpeed true true @@ -189,6 +189,9 @@ {0565952a-ea62-46a2-8261-f5b4b490da42} + + {a45d63b9-60de-476c-8836-f8eedbe139d0} + {59500dd1-b192-4ddf-a402-8a8e3739e032} diff --git a/coreapi/message_storage.c b/coreapi/message_storage.c index c87891a34..81e3c0415 100644 --- a/coreapi/message_storage.c +++ b/coreapi/message_storage.c @@ -21,10 +21,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #ifdef MSG_STORAGE_ENABLED +#ifndef PRIu64 +#define PRIu64 "I64u" +#endif #include "sqlite3.h" -static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){ +static ORTP_INLINE LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, unsigned int storage_id){ MSList* transients = cr->transient_messages; LinphoneChatMessage* chat; while( transients ){ @@ -225,7 +228,7 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){ peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room)); local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg)); - buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i,%Q,%i);", + buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i);", local_contact, peer, msg->dir, @@ -250,7 +253,7 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){ void linphone_chat_message_store_state(LinphoneChatMessage *msg){ LinphoneCore *lc=msg->chat_room->lc; if (lc->db){ - char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (id = %i) AND utc = %i;", + char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (id = %i) AND utc = %lld;", msg->state,msg->storage_id,msg->time); linphone_sql_request(lc->db,buf); sqlite3_free(buf); @@ -459,7 +462,7 @@ static int migrate_messages_timestamp(void* data,int argc, char** argv, char** c time_t new_time = parse_time_from_db(argv[1]); if( new_time ){ /* replace 'time' by -1 and set 'utc' to the timestamp */ - char *buf = sqlite3_mprintf("UPDATE history SET utc=%i,time='-1' WHERE id=%i;", new_time, atoi(argv[0])); + char *buf = sqlite3_mprintf("UPDATE history SET utc=%lld,time='-1' WHERE id=%i;", new_time, atoi(argv[0])); if( buf) { linphone_sql_request((sqlite3*)data, buf); sqlite3_free(buf); @@ -486,7 +489,7 @@ static void linphone_migrate_timestamps(sqlite3* db){ uint64_t end; linphone_sql_request(db, "COMMIT"); end=ortp_get_cur_time_ms(); - ms_message("Migrated message timestamps to UTC in %i ms",(int)(end-begin)); + ms_message("Migrated message timestamps to UTC in %lld ms",(end-begin)); } } From 45d93b828ddaedad57d8c8d1ee267a6c497ae9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 12:32:50 +0200 Subject: [PATCH 047/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 38b219a95..04fa73768 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 38b219a95a5ecdf2ed52594c6aadcf0a78c880f5 +Subproject commit 04fa73768107968abf6ff8440e5dff20f6a4a0c9 From 2630a9b0f0b4cfccf0f7c84ac0b81eca22176023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 12:44:52 +0200 Subject: [PATCH 048/135] Fix compilation on Android --- build/android/Android.mk | 2 +- coreapi/linphonecore_jni.cc | 2 +- mediastreamer2 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/android/Android.mk b/build/android/Android.mk index 1edc91fac..e634d3fa0 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -69,7 +69,7 @@ LOCAL_SRC_FILES := \ call_log.c \ call_params.c \ player.c \ - fileplayer.c + localplayer.c ifndef LIBLINPHONE_VERSION LIBLINPHONE_VERSION = "Devel" diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index c1cc56e47..eb90870eb 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -28,7 +28,7 @@ extern "C" { #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" #include "mediastreamer2/dsptools.h" -#include "mediastreamer2/fileplayer.h" +#include "mediastreamer2/msmediaplayer.h" } #include "mediastreamer2/msjava.h" #include "private.h" diff --git a/mediastreamer2 b/mediastreamer2 index 04fa73768..83bceea35 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 04fa73768107968abf6ff8440e5dff20f6a4a0c9 +Subproject commit 83bceea35319df31971ae3dede509f02fdf7df9b From 3c955d16809f261b6e3467765d936a8b62677f0e Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Wed, 15 Oct 2014 13:35:35 +0200 Subject: [PATCH 049/135] add option sip,call_logs_use_asserted_id_instead_of_from to update call logs from value with value from P-Asserted-identity --- coreapi/callbacks.c | 28 +++++++++++++++++++++------ tester/call_tester.c | 45 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index f60e230f8..8dc5cad9e 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -234,9 +234,9 @@ static bool_t already_a_call_pending(LinphoneCore *lc){ static void call_received(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneCall *call; - const char *from,*to; char *alt_contact; - LinphoneAddress *from_addr, *to_addr; + LinphoneAddress *from_addr=NULL; + LinphoneAddress *to_addr=NULL; /*this mode is deprcated because probably useless*/ bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",FALSE); @@ -272,10 +272,26 @@ static void call_received(SalOp *h){ sal_op_release(h); return; } - from=sal_op_get_from(h); - to=sal_op_get_to(h); - from_addr=linphone_address_new(from); - to_addr=linphone_address_new(to); + /*in some situation, better to trust the network rather than the UAC*/ + if (lp_config_get_int(lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0)) { + const char * p_asserted_id = sal_custom_header_find(sal_op_get_recv_custom_header(h),"P-Asserted-Identity"); + LinphoneAddress *p_asserted_id_addr; + if (!p_asserted_id) { + ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from",h); + } else { + p_asserted_id_addr = linphone_address_new(p_asserted_id); + if (!p_asserted_id_addr) { + ms_warning("Unsupported P-Asserted-Identity header for op [%p] ",h); + } else { + ms_message("Using P-Asserted-Identity [%s] instead of from [%s] for op [%p]",p_asserted_id,sal_op_get_from(h),h); + from_addr=p_asserted_id_addr; + } + } + } + + if (!from_addr) + from_addr=linphone_address_new(sal_op_get_from(h)); + to_addr=linphone_address_new(sal_op_get_to(h)); if ((already_a_call_with_remote_address(lc,from_addr) && prevent_colliding_calls) || already_a_call_pending(lc)){ ms_warning("Receiving another call while one is ringing or initiated, refusing this one with busy message."); diff --git a/tester/call_tester.c b/tester/call_tester.c index a2feaab92..1e04fa448 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -215,8 +215,11 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr else { LinphoneAddress* callee_from=linphone_address_clone(caller_mgr->identity); linphone_address_set_port(callee_from,0); /*remove port because port is never present in from header*/ + if (linphone_call_params_get_privacy(linphone_call_get_current_params(linphone_core_get_current_call(caller_mgr->lc))) == LinphonePrivacyNone) { - CU_ASSERT_TRUE(linphone_address_weak_equal(callee_from,linphone_core_get_current_call_remote_address(callee_mgr->lc))); + /*don't check in case of p asserted id*/ + if (!lp_config_get_int(callee_mgr->lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0)) + CU_ASSERT_TRUE(linphone_address_weak_equal(callee_from,linphone_core_get_current_call_remote_address(callee_mgr->lc))); } else { CU_ASSERT_FALSE(linphone_address_weak_equal(callee_from,linphone_core_get_current_call_remote_address(callee_mgr->lc))); } @@ -3142,6 +3145,43 @@ static void call_with_custom_supported_tags(void) { } } +static void call_log_from_taken_from_p_asserted_id(void) { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCall *c1,*c2; + LinphoneCallParams *params; + const char* paulie_asserted_id ="\"Paupauche\" "; + LinphoneAddress *paulie_asserted_id_addr = linphone_address_new(paulie_asserted_id); + params=linphone_core_create_default_call_parameters(pauline->lc); + + linphone_call_params_add_custom_header(params,"P-Asserted-Identity",paulie_asserted_id); + /*fixme, should be able to add several time the same header linphone_call_params_add_custom_header(params,"P-Asserted-Identity","\"Paupauche\" ");*/ + + LpConfig *marie_lp = linphone_core_get_config(marie->lc); + lp_config_set_int(marie_lp,"sip","call_logs_use_asserted_id_instead_of_from",1); + + + CU_ASSERT_TRUE(call_with_caller_params(pauline,marie,params)); + linphone_call_params_destroy(params); + + c1=linphone_core_get_current_call(pauline->lc); + c2=linphone_core_get_current_call(marie->lc); + + CU_ASSERT_PTR_NOT_NULL(c1); + CU_ASSERT_PTR_NOT_NULL(c2); + + /*make sure remote identity is hidden*/ + CU_ASSERT_TRUE(linphone_address_weak_equal(linphone_call_get_remote_address(c2),paulie_asserted_id_addr)); + + + /*just to sleep*/ + linphone_core_terminate_all_calls(pauline->lc); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,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); +} test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, @@ -3241,7 +3281,8 @@ test_t call_tests[] = { { "Call with in-dialog UPDATE request", call_with_in_dialog_update }, { "Call with in-dialog codec change", call_with_in_dialog_codec_change }, { "Call with in-dialog codec change no sdp", call_with_in_dialog_codec_change_no_sdp }, - { "Call with custom supported tags", call_with_custom_supported_tags } + { "Call with custom supported tags", call_with_custom_supported_tags }, + { "Call log from taken from asserted id",call_log_from_taken_from_p_asserted_id} }; test_suite_t call_test_suite = { From d90dc113b2533282dc7284d45c35dd953f13f9e1 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 15 Oct 2014 13:57:34 +0200 Subject: [PATCH 050/135] fix format specifiers --- coreapi/message_storage.c | 4 ++-- tester/call_tester.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/coreapi/message_storage.c b/coreapi/message_storage.c index 81e3c0415..eb60537ca 100644 --- a/coreapi/message_storage.c +++ b/coreapi/message_storage.c @@ -237,7 +237,7 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){ msg->is_read, msg->state, msg->external_body_url, - msg->time, + (int64_t)msg->time, msg->appdata, content_id ); @@ -489,7 +489,7 @@ static void linphone_migrate_timestamps(sqlite3* db){ uint64_t end; linphone_sql_request(db, "COMMIT"); end=ortp_get_cur_time_ms(); - ms_message("Migrated message timestamps to UTC in %lld ms",(end-begin)); + ms_message("Migrated message timestamps to UTC in %lu ms",(unsigned long)(end-begin)); } } diff --git a/tester/call_tester.c b/tester/call_tester.c index 1e04fa448..072ef7002 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -3152,12 +3152,14 @@ static void call_log_from_taken_from_p_asserted_id(void) { LinphoneCallParams *params; const char* paulie_asserted_id ="\"Paupauche\" "; LinphoneAddress *paulie_asserted_id_addr = linphone_address_new(paulie_asserted_id); + LpConfig *marie_lp; + params=linphone_core_create_default_call_parameters(pauline->lc); linphone_call_params_add_custom_header(params,"P-Asserted-Identity",paulie_asserted_id); /*fixme, should be able to add several time the same header linphone_call_params_add_custom_header(params,"P-Asserted-Identity","\"Paupauche\" ");*/ - LpConfig *marie_lp = linphone_core_get_config(marie->lc); + marie_lp = linphone_core_get_config(marie->lc); lp_config_set_int(marie_lp,"sip","call_logs_use_asserted_id_instead_of_from",1); From fd75b79477517af5ea70e51808396cae019a60c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 14:34:15 +0200 Subject: [PATCH 051/135] Add a destroy function to the Linphone Player Interface --- coreapi/linphonecore.h | 7 +------ coreapi/localplayer.c | 12 +++++++----- coreapi/player.c | 14 +++++++++++++- coreapi/private.h | 3 +++ tester/player_tester.c | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 2f1036678..9b2ef15ec 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -595,6 +595,7 @@ MSPlayerState linphone_player_get_state(LinphonePlayer *obj); int linphone_player_get_duration(LinphonePlayer *obj); int linphone_player_get_current_position(LinphonePlayer *obj); void linphone_player_close(LinphonePlayer *obj); +void linphone_player_destroy(LinphonePlayer *obj); /** * @brief Create an independent media file player. @@ -607,12 +608,6 @@ void linphone_player_close(LinphonePlayer *obj); */ LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id); -/** - * @brief Destroy a local player - * @param obj File player to destroy - */ -LINPHONE_PUBLIC void linphone_local_player_destroy(LinphonePlayer *obj); - /** * @brief Check whether Matroksa format is supported by the player * @return TRUE if it is supported diff --git a/coreapi/localplayer.c b/coreapi/localplayer.c index 9d6e1b5c3..ad06e1751 100644 --- a/coreapi/localplayer.c +++ b/coreapi/localplayer.c @@ -29,6 +29,7 @@ static MSPlayerState _local_player_get_state(LinphonePlayer *obj); static int _local_player_get_duration(LinphonePlayer *obj); static int _local_player_get_current_position(LinphonePlayer *obj); static void _local_player_close(LinphonePlayer *obj); +static void _local_player_destroy(LinphonePlayer *obj); static void _local_player_eof_callback(void *user_data); LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *snd_card, const char *video_out, void *window_id) { @@ -44,15 +45,11 @@ LinphonePlayer *linphone_core_create_local_player(LinphoneCore *lc, MSSndCard *s obj->get_duration = _local_player_get_duration; obj->get_position = _local_player_get_current_position; obj->close = _local_player_close; + obj->destroy = _local_player_destroy; ms_media_player_set_eof_callback((MSMediaPlayer *)obj->impl, _local_player_eof_callback, obj); return obj; } -void linphone_local_player_destroy(LinphonePlayer *obj) { - ms_media_player_free((MSMediaPlayer *)obj->impl); - ms_free(obj); -} - bool_t linphone_local_player_matroska_supported(void) { return ms_media_player_matroska_supported(); } @@ -86,6 +83,11 @@ static int _local_player_get_current_position(LinphonePlayer *obj) { return ms_media_player_get_current_position((MSMediaPlayer *)obj->impl); } +static void _local_player_destroy(LinphonePlayer *obj) { + ms_media_player_free((MSMediaPlayer *)obj->impl); + _linphone_player_destroy(obj); +} + static void _local_player_close(LinphonePlayer *obj) { ms_media_player_close((MSMediaPlayer *)obj->impl); } diff --git a/coreapi/player.c b/coreapi/player.c index 54573ccb8..1b2a01022 100644 --- a/coreapi/player.c +++ b/coreapi/player.c @@ -97,6 +97,18 @@ void linphone_player_close(LinphonePlayer *obj){ return obj->close(obj); } +/** + * @brief Destroy a player + * @param obj The player + */ +void linphone_player_destroy(LinphonePlayer *obj) { + if(obj->destroy) obj->destroy(obj); +} + +void _linphone_player_destroy(LinphonePlayer *player) { + ms_free(player); +} + /* * Call player implementation below. @@ -169,7 +181,7 @@ static void call_player_close(LinphonePlayer *player){ } static void on_call_destroy(void *obj, belle_sip_object_t *call_being_destroyed){ - ms_free(obj); + _linphone_player_destroy(obj); } LinphonePlayer *linphone_call_build_player(LinphoneCall *call){ diff --git a/coreapi/private.h b/coreapi/private.h index 40777e207..bc0ff038c 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -923,11 +923,14 @@ struct _LinphonePlayer{ int (*get_duration)(struct _LinphonePlayer *player); int (*get_position)(struct _LinphonePlayer *player); void (*close)(struct _LinphonePlayer* player); + void (*destroy)(struct _LinphonePlayer *player); LinphonePlayerEofCallback cb; void *user_data; void *impl; }; +void _linphone_player_destroy(LinphonePlayer *player); + /***************************************************************************** * XML UTILITY FUNCTIONS * diff --git a/tester/player_tester.c b/tester/player_tester.c index 14bb6d3ab..17e692d23 100644 --- a/tester/player_tester.c +++ b/tester/player_tester.c @@ -79,7 +79,7 @@ static void play_file(const char *filename, bool_t unsupported_format) { linphone_player_close(player); fail: - if(player) linphone_local_player_destroy(player); + if(player) linphone_player_destroy(player); if(lc_manager) linphone_core_manager_destroy(lc_manager); } From 9cee579ee11482a4e4eb74e5aa50821b18b1f9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 14:39:37 +0200 Subject: [PATCH 052/135] Fix compilation on Android --- coreapi/linphonecore_jni.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index eb90870eb..4a9c7f158 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5320,5 +5320,5 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *en } jobject window_id = (jobject)ms_media_player_get_window_id((MSMediaPlayer *)player->impl); if(window_id) env->DeleteGlobalRef(window_id); - linphone_local_player_destroy(player); + linphone_player_destroy(player); } From e7b469eded20a3b798455414afe8b9a67e334a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 14:55:36 +0200 Subject: [PATCH 053/135] JNI wrapping of linphone_player_destroy() --- coreapi/linphonecore_jni.cc | 20 +++++++++---------- .../org/linphone/core/LinphoneCore.java | 6 ------ .../org/linphone/core/LinphoneCoreImpl.java | 6 ------ .../org/linphone/core/LinphonePlayerImpl.java | 6 ++++++ 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 4a9c7f158..9bd10557a 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5295,6 +5295,16 @@ extern "C" void Java_org_linphone_core_LinphonePlayerImpl_close(JNIEnv *env, job linphone_player_close(player); } +extern "C" void Java_org_linphone_core_LinphonePlayerImpl_destroy(JNIEnv *env, jobject jobj, jlong playerPtr) { + LinphonePlayer *player = (LinphonePlayer *)playerPtr; + if(player->user_data) { + delete (LinphonePlayerData *)player->user_data; + } + jobject window_id = (jobject)ms_media_player_get_window_id((MSMediaPlayer *)player->impl); + if(window_id) env->DeleteGlobalRef(window_id); + linphone_player_destroy(player); +} + extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *env, jobject jobj, jlong ptr, jobject window) { jobject window_ref = NULL; MSSndCard *snd_card = ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get()); @@ -5312,13 +5322,3 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *en return (jlong)player; } } - -extern "C" void Java_org_linphone_core_LinphoneCoreImpl_destroyPlayer(JNIEnv *env, jobject jobj, jlong playerPtr) { - LinphonePlayer *player = (LinphonePlayer *)playerPtr; - if(player->user_data) { - delete (LinphonePlayerData *)player->user_data; - } - jobject window_id = (jobject)ms_media_player_get_window_id((MSMediaPlayer *)player->impl); - if(window_id) env->DeleteGlobalRef(window_id); - linphone_player_destroy(player); -} diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index dfa2f6bca..fced284f8 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1787,10 +1787,4 @@ public interface LinphoneCore { * @return An object that implement LinphonePlayer */ public LinphonePlayer createPlayer(AndroidVideoWindowImpl window); - - /** - * Destroy a player - * @param player Player to destroy - */ - public void destroyPlayer(LinphonePlayer player); } diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 750ec1903..8ba90e937 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -1290,10 +1290,4 @@ class LinphoneCoreImpl implements LinphoneCore { return null; } } - - private native void destroyPlayer(long playerPtr); - @Override - public synchronized void destroyPlayer(LinphonePlayer player) { - - } } diff --git a/java/impl/org/linphone/core/LinphonePlayerImpl.java b/java/impl/org/linphone/core/LinphonePlayerImpl.java index 08168e093..fe486321e 100644 --- a/java/impl/org/linphone/core/LinphonePlayerImpl.java +++ b/java/impl/org/linphone/core/LinphonePlayerImpl.java @@ -61,4 +61,10 @@ public class LinphonePlayerImpl implements LinphonePlayer { public synchronized void close() { close(nativePtr); } + + private native void destroy(long nativePtr); + @Override + protected void finalize() { + destroy(nativePtr); + } } From d184192b9f6c4000f1cf780b446c97dac8697844 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 15 Oct 2014 15:08:29 +0200 Subject: [PATCH 054/135] fix linphonerc renaming for windows --- coreapi/lpconfig.c | 49 +++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index 5c556acc7..a940fb696 100644 --- a/coreapi/lpconfig.c +++ b/coreapi/lpconfig.c @@ -49,7 +49,9 @@ #include #endif - +#ifdef WIN32 +#define RENAME_REQUIRES_NONEXISTENT_NEW_PATH 1 +#endif #define lp_new0(type,n) (type*)calloc(sizeof(type),n) @@ -351,22 +353,33 @@ LpConfig *lp_config_new_with_factory(const char *config_filename, const char *fa ms_message("Using (r/w) config information from %s", config_filename); lpconfig->filename=ortp_strdup(config_filename); lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",config_filename); - lpconfig->file=fopen(config_filename,"r+"); + +#if !defined(WIN32) + { + struct stat fileStat; + if ((stat(config_filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) { + /* make existing configuration files non-group/world-accessible */ + if (chmod(config_filename, S_IRUSR | S_IWUSR) == -1) { + ms_warning("unable to correct permissions on " + "configuration file: %s", strerror(errno)); + } + } + } +#endif /*WIN32*/ + /*open with r+ to check if we can write on it later*/ + lpconfig->file=fopen(lpconfig->filename,"r+"); +#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH + if (lpconfig->file==NULL){ + lpconfig->file=fopen(lpconfig->tmpfilename,"r+"); + if (lpconfig->file){ + ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename); + } + } +#endif if (lpconfig->file!=NULL){ lp_config_parse(lpconfig,lpconfig->file); fclose(lpconfig->file); -#if !defined(WIN32) - { - struct stat fileStat; - if ((stat(config_filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) { - /* make existing configuration files non-group/world-accessible */ - if (chmod(config_filename, S_IRUSR | S_IWUSR) == -1) { - ms_warning("unable to correct permissions on " - "configuration file: %s", strerror(errno)); - } - } - } -#endif /*WIN32*/ + lpconfig->file=NULL; lpconfig->modified=0; } @@ -398,6 +411,7 @@ void lp_item_set_value(LpItem *item, const char *value){ static void _lp_config_destroy(LpConfig *lpconfig){ if (lpconfig->filename!=NULL) ortp_free(lpconfig->filename); + if (lpconfig->tmpfilename) ortp_free(lpconfig->tmpfilename); ms_list_for_each(lpconfig->sections,(void (*)(void*))lp_section_destroy); ms_list_free(lpconfig->sections); free(lpconfig); @@ -593,6 +607,13 @@ int lp_config_sync(LpConfig *lpconfig){ } ms_list_for_each2(lpconfig->sections,(void (*)(void *,void*))lp_section_write,(void *)file); fclose(file); +#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH + /* On windows, rename() does not accept that the newpath is an existing file, while it is accepted on Unix. + * As a result, we are forced to first delete the linphonerc file, and then rename.*/ + if (remove(lpconfig->filename)!=0){ + ms_error("Cannot remove %s: %s",lpconfig->filename, strerror(errno)); + } +#endif if (rename(lpconfig->tmpfilename,lpconfig->filename)!=0){ ms_error("Cannot rename %s into %s: %s",lpconfig->tmpfilename,lpconfig->filename,strerror(errno)); } From 19a4e2fbd2f77060ac2acb7a6255c46fb160a705 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 15 Oct 2014 15:39:05 +0200 Subject: [PATCH 055/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 83bceea35..f6d3d86be 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 83bceea35319df31971ae3dede509f02fdf7df9b +Subproject commit f6d3d86be0c8d66533c88bedae391c0d7ba0d7d5 From 0825195d4f126619c5c1c1449245e53a144503d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 15:59:19 +0200 Subject: [PATCH 056/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index f6d3d86be..73ac0e724 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit f6d3d86be0c8d66533c88bedae391c0d7ba0d7d5 +Subproject commit 73ac0e724ebbd16c3349d460a77c7585d6a294da From 0f523615495acc14441231d25fd4fc271e2f207c Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 15 Oct 2014 16:25:03 +0200 Subject: [PATCH 057/135] set correct name to constructor of LinphoneCoreVTable --- coreapi/TunnelManager.cc | 4 ++-- coreapi/linphonecore.c | 8 ++++---- coreapi/linphonecore.h | 12 ++++++------ tester/message_tester.c | 2 +- tester/register_tester.c | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index a0158a88a..03f6923ca 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -168,7 +168,7 @@ TunnelManager::TunnelManager(LinphoneCore* lc) : mTransportFactories.video_rtcp_func_data=this; mTransportFactories.video_rtp_func=sCreateRtpTransport; mTransportFactories.video_rtp_func_data=this; - mVTable = linphone_vtable_new(); + mVTable = linphone_core_v_table_new(); mVTable->network_reachable = networkReachableCb; linphone_core_add_listener(mCore, mVTable); } @@ -179,7 +179,7 @@ TunnelManager::~TunnelManager(){ } stopClient(); linphone_core_remove_listener(mCore, mVTable); - linphone_vtable_destroy(mVTable); + linphone_core_v_table_destroy(mVTable); } void TunnelManager::doRegistration(){ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 816f6b618..fac43f463 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1436,7 +1436,7 @@ void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata) { const char *remote_provisioning_uri = NULL; - LinphoneCoreVTable* local_vtable= linphone_vtable_new(); + LinphoneCoreVTable* local_vtable= linphone_core_v_table_new(); ms_message("Initializing LinphoneCore %s", linphone_core_get_version()); memset (lc, 0, sizeof (LinphoneCore)); lc->config=lp_config_ref(config); @@ -6180,7 +6180,7 @@ static void linphone_core_uninit(LinphoneCore *lc) if (liblinphone_serialize_logs == TRUE) { ortp_set_log_thread_id(0); } - ms_list_free_with_data(lc->vtables,(void (*)(void *))linphone_vtable_destroy); + ms_list_free_with_data(lc->vtables,(void (*)(void *))linphone_core_v_table_destroy); } static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){ @@ -6916,11 +6916,11 @@ int linphone_payload_type_get_channels(const LinphonePayloadType *pt) { return pt->channels; } -LinphoneCoreVTable *linphone_vtable_new() { +LinphoneCoreVTable *linphone_core_v_table_new() { return ms_new0(LinphoneCoreVTable,1); } -void linphone_vtable_destroy(LinphoneCoreVTable* table) { +void linphone_core_v_table_destroy(LinphoneCoreVTable* table) { ms_free(table); } #define NOTIFY_IF_EXIST(function_name) \ diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 9b2ef15ec..cd72701d7 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1731,16 +1731,16 @@ typedef struct _LinphoneCoreVTable{ } LinphoneCoreVTable; /** - * Instantiate a vtable with all argument set to NULL + * Instantiate a vtable with all arguments set to NULL * @returns newly allocated vtable */ -LINPHONE_PUBLIC LinphoneCoreVTable *linphone_vtable_new(); +LINPHONE_PUBLIC LinphoneCoreVTable *linphone_core_v_table_new(); /** - * destroy a vtable. + * Destroy a vtable. * @param vtable to be destroyed */ -LINPHONE_PUBLIC void linphone_vtable_destroy(LinphoneCoreVTable* table); +LINPHONE_PUBLIC void linphone_core_v_table_destroy(LinphoneCoreVTable* table); /** * @} @@ -1748,8 +1748,8 @@ LINPHONE_PUBLIC void linphone_vtable_destroy(LinphoneCoreVTable* table); typedef struct _LCCallbackObj { - LinphoneCoreCbFunc _func; - void * _user_data; + LinphoneCoreCbFunc _func; + void * _user_data; }LCCallbackObj; diff --git a/tester/message_tester.c b/tester/message_tester.c index a361eab1c..21ef387ae 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -226,7 +226,7 @@ static void text_message_with_credential_from_auth_cb_auth_info_requested(Linpho static void text_message_with_credential_from_auth_cb(void) { char* to; LinphoneChatRoom* chat_room; - LinphoneCoreVTable* vtable = linphone_vtable_new(); + LinphoneCoreVTable* vtable = linphone_core_v_table_new(); LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); text_message_with_credential_from_auth_cb_auth_info=linphone_auth_info_clone((LinphoneAuthInfo*)(linphone_core_get_auth_info_list(marie->lc)->data)); diff --git a/tester/register_tester.c b/tester/register_tester.c index d47ffa2df..907a10db7 100644 --- a/tester/register_tester.c +++ b/tester/register_tester.c @@ -36,7 +36,7 @@ static LinphoneCoreManager* create_lcm_with_auth(unsigned int with_auth) { LinphoneCoreManager* mgr=linphone_core_manager_new(NULL); if (with_auth) { - LinphoneCoreVTable* vtable = linphone_vtable_new(); + LinphoneCoreVTable* vtable = linphone_core_v_table_new(); vtable->auth_info_requested=auth_info_requested; linphone_core_add_listener(mgr->lc,vtable); } @@ -314,7 +314,7 @@ static void ha1_authenticated_register(){ static void authenticated_register_with_no_initial_credentials(){ LinphoneCoreManager *mgr; - LinphoneCoreVTable* vtable = linphone_vtable_new(); + LinphoneCoreVTable* vtable = linphone_core_v_table_new(); stats* counters; char route[256]; From a6c013bdb7a2b4a837763899178e44065fd0b27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 15 Oct 2014 16:42:40 +0200 Subject: [PATCH 058/135] Rename LinphoneCore.createPlayer() into LinphoneCore.createLocalPlayer() --- coreapi/linphonecore_jni.cc | 2 +- java/common/org/linphone/core/LinphoneCore.java | 2 +- java/impl/org/linphone/core/LinphoneCoreImpl.java | 6 +++--- mediastreamer2 | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 9bd10557a..2fff651f5 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5305,7 +5305,7 @@ extern "C" void Java_org_linphone_core_LinphonePlayerImpl_destroy(JNIEnv *env, j linphone_player_destroy(player); } -extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createPlayer(JNIEnv *env, jobject jobj, jlong ptr, jobject window) { +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_createLocalPlayer(JNIEnv *env, jobject jobj, jlong ptr, jobject window) { jobject window_ref = NULL; MSSndCard *snd_card = ms_snd_card_manager_get_default_playback_card(ms_snd_card_manager_get()); if(snd_card == NULL) { diff --git a/java/common/org/linphone/core/LinphoneCore.java b/java/common/org/linphone/core/LinphoneCore.java index fced284f8..af878e24f 100644 --- a/java/common/org/linphone/core/LinphoneCore.java +++ b/java/common/org/linphone/core/LinphoneCore.java @@ -1786,5 +1786,5 @@ public interface LinphoneCore { * Create a media player * @return An object that implement LinphonePlayer */ - public LinphonePlayer createPlayer(AndroidVideoWindowImpl window); + public LinphonePlayer createLocalPlayer(AndroidVideoWindowImpl window); } diff --git a/java/impl/org/linphone/core/LinphoneCoreImpl.java b/java/impl/org/linphone/core/LinphoneCoreImpl.java index 8ba90e937..6e6f5a21f 100644 --- a/java/impl/org/linphone/core/LinphoneCoreImpl.java +++ b/java/impl/org/linphone/core/LinphoneCoreImpl.java @@ -1280,10 +1280,10 @@ class LinphoneCoreImpl implements LinphoneCore { return getFileTransferServer(nativePtr); } - private native long createPlayer(long nativePtr, AndroidVideoWindowImpl window); + private native long createLocalPlayer(long nativePtr, AndroidVideoWindowImpl window); @Override - public synchronized LinphonePlayer createPlayer(AndroidVideoWindowImpl window) { - long playerPtr = createPlayer(nativePtr, window); + public synchronized LinphonePlayer createLocalPlayer(AndroidVideoWindowImpl window) { + long playerPtr = createLocalPlayer(nativePtr, window); if(playerPtr != 0) { return new LinphonePlayerImpl(playerPtr); } else { diff --git a/mediastreamer2 b/mediastreamer2 index 73ac0e724..0190b1c34 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 73ac0e724ebbd16c3349d460a77c7585d6a294da +Subproject commit 0190b1c34fead09a19bf1eac6ee72e4b408ec94a From 2ce93fe5dcfdca39346d86b4548e0dd9f8a58cff Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Thu, 16 Oct 2014 00:52:13 +0200 Subject: [PATCH 059/135] Assign payload type to AAC 16/32/48 kHz (and update ms2/ortp) --- coreapi/linphonecore.c | 9 ++++++--- mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index fac43f463..827553dd1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1482,8 +1482,8 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_core_assign_payload_type(lc,&payload_type_h264,102,"profile-level-id=42801F"); linphone_core_assign_payload_type(lc,&payload_type_vp8,103,NULL); - linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL); - linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL); + /* linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL); commented out to free 1 slot */ + /* linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL); commented out to free 1 slot */ /* due to limited space in SDP, we have to disable this h264 line which is normally no more necessary */ /* linphone_core_assign_payload_type(&payload_type_h264,-1,"packetization-mode=1;profile-level-id=428014");*/ #endif @@ -1492,7 +1492,7 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30"); linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1"); linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1"); - linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL); + /* linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL); commented out to free 1 slot */ linphone_core_assign_payload_type(lc,&payload_type_g726_16,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g726_24,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g726_32,-1,NULL); @@ -1506,8 +1506,11 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no"); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_16k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); linphone_core_assign_payload_type(lc,&payload_type_aaceld_22k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_32k,-1,"config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); linphone_core_assign_payload_type(lc,&payload_type_aaceld_44k,-1,"config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_48k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); linphone_core_assign_payload_type(lc,&payload_type_opus,-1,"useinbandfec=1; stereo=0; sprop-stereo=0"); linphone_core_assign_payload_type(lc,&payload_type_isac,-1,NULL); linphone_core_handle_static_payloads(lc); diff --git a/mediastreamer2 b/mediastreamer2 index 0190b1c34..80cd283c5 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 0190b1c34fead09a19bf1eac6ee72e4b408ec94a +Subproject commit 80cd283c5910767ca869bb6a8c29ff56214fe4af diff --git a/oRTP b/oRTP index 05e0242a9..7ded70711 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 05e0242a91391747408340dffa34f9b4e1335be4 +Subproject commit 7ded70711a9201591db13f06ab0b69ba4d89f992 From ee2a235302d7188a596e972a04285bc65797a98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 16 Oct 2014 10:39:14 +0200 Subject: [PATCH 060/135] Change the API of AndroidVideoWindowImpl class --- java/common/org/linphone/core/LinphonePlayer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/common/org/linphone/core/LinphonePlayer.java b/java/common/org/linphone/core/LinphonePlayer.java index faba92d5d..79d05dbb4 100644 --- a/java/common/org/linphone/core/LinphonePlayer.java +++ b/java/common/org/linphone/core/LinphonePlayer.java @@ -14,9 +14,9 @@ public interface LinphonePlayer { * */ public enum State { - closed, /**< No file is open */ - paused, /**< A file is open and playback is not running */ - playing; /**< A file is open and playback is running */ + closed, /*< No file is open */ + paused, /*< A file is open and playback is not running */ + playing; /*< A file is open and playback is running */ public static State fromValue(int value) { if(value == 0) { From 17e3935464a61e0d0c02e938322d7702bf2651a8 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Thu, 16 Oct 2014 11:53:22 +0200 Subject: [PATCH 061/135] Update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 80cd283c5..82d73df17 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 80cd283c5910767ca869bb6a8c29ff56214fe4af +Subproject commit 82d73df1790af2ad21dc2eac5cb424d56060dccf From a11ac06204d2fae8dc382b4cbd656e81922fc54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 16 Oct 2014 14:34:04 +0200 Subject: [PATCH 062/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 82d73df17..c64458384 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 82d73df1790af2ad21dc2eac5cb424d56060dccf +Subproject commit c644583841f98744689096305ae9dc1156043aee From 54e6f89dac0a196d561961a1837ea125e3d92fcc Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 17 Oct 2014 11:25:28 +0200 Subject: [PATCH 063/135] fix message state whose storage in db was broken --- coreapi/message_storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreapi/message_storage.c b/coreapi/message_storage.c index eb60537ca..8cbd088f2 100644 --- a/coreapi/message_storage.c +++ b/coreapi/message_storage.c @@ -253,8 +253,8 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){ void linphone_chat_message_store_state(LinphoneChatMessage *msg){ LinphoneCore *lc=msg->chat_room->lc; if (lc->db){ - char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (id = %i) AND utc = %lld;", - msg->state,msg->storage_id,msg->time); + char *buf=sqlite3_mprintf("UPDATE history SET status=%i WHERE (id = %i);", + msg->state,msg->storage_id); linphone_sql_request(lc->db,buf); sqlite3_free(buf); } From 9e55c638750368e5b87ff986d4a304ab5c17944d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 17 Oct 2014 13:38:37 +0200 Subject: [PATCH 064/135] Add getPlayer() method to LinphoneCore class --- coreapi/linphonecore_jni.cc | 4 ++++ java/common/org/linphone/core/LinphoneCall.java | 7 +++++++ java/impl/org/linphone/core/LinphoneCallImpl.java | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 2fff651f5..857ad716d 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -2445,6 +2445,10 @@ extern "C" jfloat Java_org_linphone_core_LinphoneCallImpl_getAverageQuality( JNI return (jfloat)linphone_call_get_average_quality((LinphoneCall*)ptr); } +extern "C" jlong Java_org_linphone_core_LinphoneCallImpl_getPlayer(JNIEnv *env, jobject thiz, jlong callPtr) { + return (jlong)linphone_call_get_player((LinphoneCall *)callPtr); +} + //LinphoneFriend extern "C" jlong Java_org_linphone_core_LinphoneFriendImpl_newLinphoneFriend(JNIEnv* env ,jobject thiz diff --git a/java/common/org/linphone/core/LinphoneCall.java b/java/common/org/linphone/core/LinphoneCall.java index d75f1bbdb..f6f11ebd7 100644 --- a/java/common/org/linphone/core/LinphoneCall.java +++ b/java/common/org/linphone/core/LinphoneCall.java @@ -333,4 +333,11 @@ public interface LinphoneCall { * @return an Object. */ Object getUserData(); + + /** + * Get a call player + * Call player enable to stream a media file through a call + * @return A player + */ + public LinphonePlayer getPlayer(); } diff --git a/java/impl/org/linphone/core/LinphoneCallImpl.java b/java/impl/org/linphone/core/LinphoneCallImpl.java index ee78e9d89..b2cad202c 100644 --- a/java/impl/org/linphone/core/LinphoneCallImpl.java +++ b/java/impl/org/linphone/core/LinphoneCallImpl.java @@ -245,4 +245,10 @@ class LinphoneCallImpl implements LinphoneCall { public Object getUserData() { return userData; } + + private native long getPlayer(long callPtr); + @Override + public LinphonePlayer getPlayer() { + return new LinphonePlayerImpl(getPlayer(nativePtr)); + } } From bac60c86b0e6e12ea6ca5f85bc701729f23704b5 Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Fri, 17 Oct 2014 13:49:54 +0200 Subject: [PATCH 065/135] Multipart Message: let bellesip take care of message header --- coreapi/chat.c | 10 +--------- coreapi/linphonecore.c | 7 +------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index 0c89485f3..f9322bdd0 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -36,8 +36,6 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage* msg); -#define MULTIPART_BOUNDARY "---------------------------14737809831466499882746641449" -const char *multipart_boundary=MULTIPART_BOUNDARY; static void process_io_error_upload(void *data, const belle_sip_io_error_event_t *event){ LinphoneChatMessage* msg=(LinphoneChatMessage *)data; @@ -128,7 +126,6 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co belle_http_request_t *req; belle_sip_multipart_body_handler_t *bh; char* ua; - char *content_type; char *first_part_header; belle_sip_user_body_handler_t *first_part_bh; @@ -144,19 +141,14 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co /* insert it in a multipart body handler which will manage the boundaries of multipart message */ bh=belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, (belle_sip_body_handler_t *)first_part_bh); + /* create the http request: do not include the message header at this point, it is done by bellesip when setting the multipart body handler in the message */ ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version()); - - content_type=belle_sip_strdup_printf("multipart/form-data; boundary=%s",multipart_boundary); - uri=belle_generic_uri_parse(linphone_core_get_file_transfer_server(msg->chat_room->lc)); - req=belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent",ua), - belle_sip_header_create("Content-type",content_type), NULL); ms_free(ua); - belle_sip_free(content_type); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req),BELLE_SIP_BODY_HANDLER(bh)); cbs.process_response=linphone_chat_message_process_response_from_post_file; cbs.process_io_error=process_io_error_upload; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 827553dd1..5f794718f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -276,8 +276,6 @@ static void process_auth_requested_upload_log_collection(void *data, belle_sip_a delete_log_collection_upload_file(); } -extern const char *multipart_boundary; - /** * Callback called when posting a log collection file to server (following rcs5.1 recommendation) * @@ -341,7 +339,6 @@ static void process_response_from_post_file_log_collection(void *data, const bel belle_http_request_t *req; belle_sip_multipart_body_handler_t *bh; char* ua; - char *content_type; char *first_part_header; belle_sip_user_body_handler_t *first_part_bh; @@ -359,11 +356,9 @@ static void process_response_from_post_file_log_collection(void *data, const bel /* Insert it in a multipart body handler which will manage the boundaries of multipart message */ bh = belle_sip_multipart_body_handler_new(log_collection_upload_on_progress, core, (belle_sip_body_handler_t *)first_part_bh); ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version()); - content_type = belle_sip_strdup_printf("multipart/form-data; boundary=%s", multipart_boundary); uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); - req = belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent", ua), belle_sip_header_create("Content-type", content_type), NULL); + req = belle_http_request_create("POST", uri, belle_sip_header_create("User-Agent", ua), NULL); ms_free(ua); - belle_sip_free(content_type); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh)); cbs.process_response = process_response_from_post_file_log_collection; cbs.process_io_error = process_io_error_upload_log_collection; From 4afcb2ede3f577e3f6089442a508bf804aeaf4ae Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 20 Oct 2014 11:01:31 +0200 Subject: [PATCH 066/135] Fix user data in chat message state callbacks. --- coreapi/chat.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index f9322bdd0..cbdacd7aa 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -41,14 +41,14 @@ static void process_io_error_upload(void *data, const belle_sip_io_error_event_t LinphoneChatMessage* msg=(LinphoneChatMessage *)data; ms_error("I/O Error during file upload to %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room); if (msg->cb) { - msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->chat_room->lc); + msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->cb_ud); } } static void process_auth_requested_upload(void *data, belle_sip_auth_event_t *event){ LinphoneChatMessage* msg=(LinphoneChatMessage *)data; ms_error("Error during file upload : auth requested to connect %s - msg [%p] chat room[%p]", linphone_core_get_file_transfer_server(msg->chat_room->lc), msg, msg->chat_room); if (msg->cb) { - msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->chat_room->lc); + msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->cb_ud); } } @@ -56,14 +56,14 @@ static void process_io_error_download(void *data, const belle_sip_io_error_event LinphoneChatMessage* msg=(LinphoneChatMessage *)data; ms_error("I/O Error during file download %s - msg [%p] chat room[%p]", msg->external_body_url, msg, msg->chat_room); if (msg->cb) { - msg->cb(msg, LinphoneChatMessageStateFileTransferError, msg->chat_room->lc); + msg->cb(msg, LinphoneChatMessageStateFileTransferError, msg->cb_ud); } } static void process_auth_requested_download(void *data, belle_sip_auth_event_t *event){ LinphoneChatMessage* msg=(LinphoneChatMessage *)data; ms_error("Error during file download : auth requested to get %s - msg [%p] chat room[%p]", msg->external_body_url, msg, msg->chat_room); if (msg->cb) { - msg->cb(msg, LinphoneChatMessageStateFileTransferError, msg->chat_room->lc); + msg->cb(msg, LinphoneChatMessageStateFileTransferError, msg->cb_ud); } } @@ -1135,7 +1135,7 @@ void linphone_chat_room_cancel_file_transfer(LinphoneChatMessage *msg) { /* waiting for this API, just set to NULL the reference to the request in the message and any request */ msg->http_request = NULL; if (msg->cb) { - msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->chat_room->lc); + msg->cb(msg, LinphoneChatMessageStateNotDelivered, msg->cb_ud); } } From 2f47ffacd3adb311a585d24084317de60497d705 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 20 Oct 2014 11:05:27 +0200 Subject: [PATCH 067/135] Change file transfer progression indication to have the number of bytes sent/received and the total. --- coreapi/chat.c | 2 +- coreapi/linphonecore.c | 4 ++-- coreapi/linphonecore.h | 6 +++--- coreapi/private.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index cbdacd7aa..3f00d291c 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -75,7 +75,7 @@ static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handl LinphoneChatMessage* chatMsg=(LinphoneChatMessage *)data; LinphoneCore *lc = chatMsg->chat_room->lc; /* call back given by application level */ - linphone_core_notify_file_transfer_progress_indication(lc, chatMsg, chatMsg->file_transfer_information, (size_t)(((double)offset/(double)total)*100.0)); + linphone_core_notify_file_transfer_progress_indication(lc, chatMsg, chatMsg->file_transfer_information, offset, total); return; } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 5f794718f..a6aed5269 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -6979,8 +6979,8 @@ void linphone_core_notify_file_transfer_recv(LinphoneCore *lc, LinphoneChatMessa void linphone_core_notify_file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size) { NOTIFY_IF_EXIST(file_transfer_send)(lc,message,content,buff,size); } -void linphone_core_notify_file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress) { - NOTIFY_IF_EXIST(file_transfer_progress_indication)(lc,message,content,progress); +void linphone_core_notify_file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) { + NOTIFY_IF_EXIST(file_transfer_progress_indication)(lc,message,content,offset,total); } void linphone_core_notify_is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) { NOTIFY_IF_EXIST(is_composing_received)(lc,room); diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index cd72701d7..06fcb8e1a 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1603,10 +1603,10 @@ typedef void (*LinphoneCoreFileTransferSendCb)(LinphoneCore *lc, LinphoneChatMes * @param lc #LinphoneCore object * @param message #LinphoneChatMessage message from which the body is received. * @param content #LinphoneContent incoming content information - * @param progress number of bytes sent/received from the begening of the transfer. - * + * @param offset The number of bytes sent/received since the beginning of the transfer. + * @param total The total number of bytes to be sent/received. */ -typedef void (*LinphoneCoreFileTransferProgressIndicationCb)(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress); +typedef void (*LinphoneCoreFileTransferProgressIndicationCb)(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total); /** * Is composing notification callback prototype. diff --git a/coreapi/private.h b/coreapi/private.h index bc0ff038c..bb021c27c 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1007,7 +1007,7 @@ void linphone_core_notify_text_message_received(LinphoneCore *lc, LinphoneChatRo void linphone_core_notify_message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message); void linphone_core_notify_file_transfer_recv(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size); void linphone_core_notify_file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size); -void linphone_core_notify_file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress); +void linphone_core_notify_file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total); void linphone_core_notify_is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room); void linphone_core_notify_dtmf_received(LinphoneCore* lc, LinphoneCall *call, int dtmf); /* From 4717028dedc20be119ab9cfbba9daf54956d2ad3 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 20 Oct 2014 11:09:49 +0200 Subject: [PATCH 068/135] Allow sending a chat message containing a file from its path. --- coreapi/chat.c | 35 ++++++++++++++++++++++++++++------- coreapi/linphonecore.h | 14 ++++++++++++-- coreapi/private.h | 1 + 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index 3f00d291c..61d1b3065 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -127,19 +127,23 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co belle_sip_multipart_body_handler_t *bh; char* ua; char *first_part_header; - belle_sip_user_body_handler_t *first_part_bh; + belle_sip_body_handler_t *first_part_bh; /* temporary storage for the Content-disposition header value */ first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", msg->file_transfer_information->name); /* create a user body handler to take care of the file and add the content disposition and content-type headers */ - first_part_bh=belle_sip_user_body_handler_new(msg->file_transfer_information->size,NULL,NULL,linphone_chat_message_file_transfer_on_send_body,msg); - belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, belle_sip_header_create("Content-disposition", first_part_header)); + if (msg->file_transfer_filepath != NULL) { + first_part_bh=(belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath,NULL,msg); + } else { + first_part_bh=(belle_sip_body_handler_t *)belle_sip_user_body_handler_new(msg->file_transfer_information->size,NULL,NULL,linphone_chat_message_file_transfer_on_send_body,msg); + } + belle_sip_body_handler_add_header(first_part_bh, belle_sip_header_create("Content-disposition", first_part_header)); belle_sip_free(first_part_header); - belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create(msg->file_transfer_information->type, msg->file_transfer_information->subtype)); + belle_sip_body_handler_add_header(first_part_bh, (belle_sip_header_t *)belle_sip_header_content_type_create(msg->file_transfer_information->type, msg->file_transfer_information->subtype)); /* insert it in a multipart body handler which will manage the boundaries of multipart message */ - bh=belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, (belle_sip_body_handler_t *)first_part_bh); + bh=belle_sip_multipart_body_handler_new(linphone_chat_message_file_transfer_on_progress, msg, first_part_bh); /* create the http request: do not include the message header at this point, it is done by bellesip when setting the multipart body handler in the message */ ua = ms_strdup_printf("%s/%s", linphone_core_get_user_agent_name(), linphone_core_get_user_agent_version()); @@ -166,6 +170,9 @@ static void linphone_chat_message_process_response_from_post_file(void *data, co body = belle_sip_message_get_body((belle_sip_message_t *)event->response); msg->message = ms_strdup(body); msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml"); + if (msg->cb) { + msg->cb(msg, LinphoneChatMessageStateFileTransferDone, msg->cb_ud); + } _linphone_chat_room_send_message(msg->chat_room, msg); } } @@ -1076,8 +1083,6 @@ static void linphone_chat_process_response_headers_from_get_file(void *data, con } static void linphone_chat_process_response_from_get_file(void *data, const belle_http_response_event_t *event){ - //LinphoneChatMessage* msg=(LinphoneChatMessage *)data; - /* check the answer code */ if (event->response){ int code=belle_http_response_get_status_code(event->response); @@ -1086,6 +1091,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 */ linphone_core_notify_file_transfer_recv(lc, chatMsg, chatMsg->file_transfer_information, NULL, 0); + if (chatMsg->cb) { + chatMsg->cb(chatMsg, LinphoneChatMessageStateFileTransferDone, chatMsg->cb_ud); + } } } } @@ -1286,6 +1294,7 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg) new_message->state=msg->state; new_message->storage_id=msg->storage_id; if (msg->from) new_message->from=linphone_address_clone(msg->from); + if (msg->file_transfer_filepath) new_message->file_transfer_filepath=ms_strdup(msg->file_transfer_filepath); return new_message; } @@ -1313,6 +1322,9 @@ static void _linphone_chat_message_destroy(LinphoneChatMessage* msg) { linphone_content_uninit(msg->file_transfer_information); ms_free(msg->file_transfer_information); } + if (msg->file_transfer_filepath != NULL) { + ms_free(msg->file_transfer_filepath); + } ms_message("LinphoneChatMessage [%p] destroyed.",msg); } @@ -1372,6 +1384,15 @@ LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneCha return msg; } +LinphoneChatMessage * linphone_chat_room_create_file_transfer_message_from_file(LinphoneChatRoom *cr, LinphoneContent *initial_content, const char *filepath) { + LinphoneChatMessage *msg = linphone_chat_room_create_file_transfer_message(cr, initial_content); + if (msg->file_transfer_filepath != NULL) { + ms_free(msg->file_transfer_filepath); + } + msg->file_transfer_filepath = ms_strdup(filepath); + return msg; +} + /** * @} */ diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 06fcb8e1a..8ff23a430 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1297,9 +1297,10 @@ typedef struct _LinphoneChatRoom LinphoneChatRoom; typedef enum _LinphoneChatMessageState { LinphoneChatMessageStateIdle, /**< Initial state */ LinphoneChatMessageStateInProgress, /**< Delivery in progress */ - LinphoneChatMessageStateDelivered, /**< Message succesffully delivered an acknoleged by remote end point */ + LinphoneChatMessageStateDelivered, /**< Message successfully delivered and acknowledged by remote end point */ LinphoneChatMessageStateNotDelivered, /**< Message was not delivered */ - LinphoneChatMessageStateFileTransferError /**< Message was received(and acknowledged) but cannot get file from server */ + LinphoneChatMessageStateFileTransferError, /**< Message was received(and acknowledged) but cannot get file from server */ + LinphoneChatMessageStateFileTransferDone /**< File transfer has been completed successfully. */ } LinphoneChatMessageState; /** @@ -1357,6 +1358,15 @@ LINPHONE_PUBLIC void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void */ LINPHONE_PUBLIC LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, LinphoneContent* initial_content); +/** + * Create a message with an attached file that will be read from the given filepath. + * @param[in] cr LinphoneChatRoom object + * @param[in] initial_content LinphoneContent object describing the file to be transfered. + * @param[in] filepath The path to the file to be sent. + * @return A new LinphoneChatMessage object. + */ +LINPHONE_PUBLIC LinphoneChatMessage * linphone_chat_room_create_file_transfer_message_from_file(LinphoneChatRoom *cr, LinphoneContent *initial_content, const char *filepath); + LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr); LINPHONE_PUBLIC void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg); LINPHONE_PUBLIC void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* msg,LinphoneChatMessageStateChangedCb status_cb,void* ud); diff --git a/coreapi/private.h b/coreapi/private.h index bb021c27c..f23859ddd 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -171,6 +171,7 @@ struct _LinphoneChatMessage { LinphoneContent *file_transfer_information; /**< used to store file transfer information when the message is of file transfer type */ char *content_type; /**< is used to specified the type of message to be sent, used only for file transfer message */ belle_http_request_t *http_request; /**< keep a reference to the http_request in case of file transfer in order to be able to cancel the transfer */ + char *file_transfer_filepath; }; BELLE_SIP_DECLARE_VPTR(LinphoneChatMessage); From 78acd91a54a0cca244df68475cdc158f03608322 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 20 Oct 2014 14:00:46 +0200 Subject: [PATCH 069/135] Add API to save the compressed log collection to a file. --- coreapi/linphonecore.c | 39 ++++++++++++++++++++++----------------- coreapi/linphonecore.h | 8 ++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index a6aed5269..b01430066 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -56,6 +56,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #ifdef HAVE_ZLIB +#define COMPRESSED_LOG_COLLECTION_FILENAME "linphone_log.gz" #ifdef WIN32 #include #include @@ -64,6 +65,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SET_BINARY_MODE(file) #endif #include +#else +#define COMPRESSED_LOG_COLLECTION_FILENAME "linphone_log.txt" #endif /*#define UNSTANDART_GSM_11K 1*/ @@ -254,12 +257,9 @@ void linphone_core_enable_log_collection(bool_t enable) { } static void delete_log_collection_upload_file(void) { -#ifdef HAVE_ZLIB - char *filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); -#else - char *filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); -#endif + char *filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); unlink(filename); + ms_free(filename); } static void process_io_error_upload_log_collection(void *data, const belle_sip_io_error_event_t *event) { @@ -292,17 +292,16 @@ static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, /* If we've not reach the end of file yet, fill the buffer with more data */ if (offset < core->log_collection_upload_information->size) { + char *log_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); #ifdef HAVE_ZLIB - char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.gz"); FILE *log_file = fopen(log_filename, "rb"); #else - char *log_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone_log.txt"); FILE *log_file = fopen(log_filename, "r"); #endif fseek(log_file, offset, SEEK_SET); *size = fread(buffer, 1, *size, log_file); fclose(log_file); - ortp_free(log_filename); + ms_free(log_filename); } return BELLE_SIP_CONTINUE; @@ -439,17 +438,17 @@ static int prepare_log_collection_file_to_upload(const char *filename) { int ret = 0; ortp_mutex_lock(&liblinphone_log_collection_mutex); - output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); output_file = COMPRESS_OPEN(output_filename, "a"); if (output_file == NULL) goto error; - input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); input_file = fopen(input_filename, "r"); if (input_file == NULL) goto error; ret = compress_file(input_file, output_file); if (ret < 0) goto error; fclose(input_file); - ortp_free(input_filename); - input_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + ms_free(input_filename); + input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); input_file = fopen(input_filename, "r"); if (input_file != NULL) { ret = compress_file(input_file, output_file); @@ -459,18 +458,19 @@ static int prepare_log_collection_file_to_upload(const char *filename) { error: if (input_file != NULL) fclose(input_file); if (output_file != NULL) COMPRESS_CLOSE(output_file); - if (input_filename != NULL) ortp_free(input_filename); - if (output_filename != NULL) ortp_free(output_filename); + if (input_filename != NULL) ms_free(input_filename); + if (output_filename != NULL) ms_free(output_filename); ortp_mutex_unlock(&liblinphone_log_collection_mutex); return ret; } static size_t get_size_of_file_to_upload(const char *filename) { struct stat statbuf; - char *output_filename = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + char *output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); FILE *output_file = fopen(output_filename, "rb"); fstat(fileno(output_file), &statbuf); fclose(output_file); + ms_free(output_filename); return statbuf.st_size; } @@ -487,12 +487,11 @@ void linphone_core_upload_log_collection(LinphoneCore *core) { #ifdef HAVE_ZLIB core->log_collection_upload_information->type = "application"; core->log_collection_upload_information->subtype = "gzip"; - core->log_collection_upload_information->name = "linphone_log.gz"; #else core->log_collection_upload_information->type = "text"; core->log_collection_upload_information->subtype = "plain"; - core->log_collection_upload_information->name = "linphone_log.txt"; #endif + core->log_collection_upload_information->name = COMPRESSED_LOG_COLLECTION_FILENAME; if (prepare_log_collection_file_to_upload(core->log_collection_upload_information->name) < 0) return; core->log_collection_upload_information->size = get_size_of_file_to_upload(core->log_collection_upload_information->name); uri = belle_generic_uri_parse(linphone_core_get_log_collection_upload_server_url(core)); @@ -505,6 +504,12 @@ void linphone_core_upload_log_collection(LinphoneCore *core) { } } +char * linphone_core_compress_log_collection(LinphoneCore *core) { + if (liblinphone_log_collection_enabled == FALSE) return NULL; + if (prepare_log_collection_file_to_upload(COMPRESSED_LOG_COLLECTION_FILENAME) < 0) return NULL; + return ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); +} + /** * Enable logs in supplied FILE*. * diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 8ff23a430..ef23f1315 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1814,6 +1814,14 @@ LINPHONE_PUBLIC void linphone_core_set_log_collection_upload_server_url(Linphone */ LINPHONE_PUBLIC void linphone_core_upload_log_collection(LinphoneCore *core); +/** + * Compress the log collection in a single file. + * @ingroup misc + * @param[in] core LinphoneCore object + * @return The path of the compressed log collection file (to be freed calling ms_free()). + */ +LINPHONE_PUBLIC char * linphone_core_compress_log_collection(LinphoneCore *core); + /** * Define a log handler. * From 3c4a6f7ed4111d33965735bef3679efa4cb91a03 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 20 Oct 2014 14:44:23 +0200 Subject: [PATCH 070/135] Added callback user data to start_file_download --- coreapi/chat.c | 3 ++- coreapi/linphonecore.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index 61d1b3065..d52ab4a70 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -1104,7 +1104,7 @@ static void linphone_chat_process_response_from_get_file(void *data, const belle * @param message #LinphoneChatMessage * @param status_cb LinphoneChatMessageStateChangeCb status callback invoked when file is downloaded or could not be downloaded */ -void linphone_chat_message_start_file_download(LinphoneChatMessage *message, LinphoneChatMessageStateChangedCb status_cb) { +void linphone_chat_message_start_file_download(LinphoneChatMessage *message, LinphoneChatMessageStateChangedCb status_cb, void *ud) { belle_http_request_listener_callbacks_t cbs={0}; belle_http_request_listener_t *l; belle_generic_uri_t *uri; @@ -1129,6 +1129,7 @@ void linphone_chat_message_start_file_download(LinphoneChatMessage *message, Lin belle_sip_object_data_set(BELLE_SIP_OBJECT(req),"message",(void *)message,NULL); message->http_request = req; /* keep a reference on the request to be able to cancel the download */ message->cb = status_cb; + message->cb_ud = cb_ud; message->state = LinphoneChatMessageStateInProgress; /* start the download, status is In Progress */ belle_http_provider_send_request(message->chat_room->lc->http_provider,req,l); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index ef23f1315..bac438e93 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1430,7 +1430,7 @@ LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_to_address(cons LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message); LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url); LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage* message); -LINPHONE_PUBLIC void linphone_chat_message_start_file_download(LinphoneChatMessage* message, LinphoneChatMessageStateChangedCb status_cb); +LINPHONE_PUBLIC void linphone_chat_message_start_file_download(LinphoneChatMessage* message, LinphoneChatMessageStateChangedCb status_cb, void* ud); LINPHONE_PUBLIC void linphone_chat_room_cancel_file_transfer(LinphoneChatMessage* msg); LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message); LINPHONE_PUBLIC void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data); From 7126c413a76d742cc10ce07c0a9f6e62f0a75387 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 20 Oct 2014 14:51:09 +0200 Subject: [PATCH 071/135] Fix typo --- coreapi/chat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index d52ab4a70..cc8bc895a 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -1129,7 +1129,7 @@ void linphone_chat_message_start_file_download(LinphoneChatMessage *message, Lin belle_sip_object_data_set(BELLE_SIP_OBJECT(req),"message",(void *)message,NULL); message->http_request = req; /* keep a reference on the request to be able to cancel the download */ message->cb = status_cb; - message->cb_ud = cb_ud; + message->cb_ud = ud; message->state = LinphoneChatMessageStateInProgress; /* start the download, status is In Progress */ belle_http_provider_send_request(message->chat_room->lc->http_provider,req,l); } From 965add9d6e8baef897c32fc4427f4bd4758594d7 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 20 Oct 2014 15:09:48 +0200 Subject: [PATCH 072/135] add new states LinphoneCallEarlyUpdating and LinphoneCallEarlyUpdatedByRemote to properly handle the early dialog UPDATE scenarios. fix test suite. --- coreapi/callbacks.c | 32 ++++++++++++------ coreapi/linphonecall.c | 4 +++ coreapi/linphonecore.c | 12 +++++-- coreapi/linphonecore.h | 4 ++- .../org/linphone/core/LinphoneCall.java | 9 +++++ mediastreamer2 | 2 +- oRTP | 2 +- tester/call_tester.c | 31 +++++++++-------- tester/liblinphone_tester.h | 2 ++ tester/sounds/hello8000.mkv | Bin 42162 -> 96210 bytes 10 files changed, 67 insertions(+), 31 deletions(-) diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 8dc5cad9e..bb6b5b9fb 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -411,6 +411,7 @@ static void call_accepted(SalOp *op){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); SalMediaDescription *md; + bool_t update_state=TRUE; if (call==NULL){ ms_warning("No call to accept."); @@ -433,12 +434,21 @@ static void call_accepted(SalOp *op){ if (md) /*make sure re-invite will not propose video again*/ call->params->has_video &= linphone_core_media_description_contains_video_stream(md); - if (call->state==LinphoneCallOutgoingProgress || - call->state==LinphoneCallOutgoingRinging || - call->state==LinphoneCallOutgoingEarlyMedia){ - linphone_call_set_state(call,LinphoneCallConnected,"Connected"); - if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call); + switch (call->state){ + case LinphoneCallOutgoingProgress: + case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingEarlyMedia: + linphone_call_set_state(call,LinphoneCallConnected,"Connected"); + if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call); + break; + case LinphoneCallEarlyUpdating: + linphone_call_set_state(call,call->prevstate,"Early update accepted"); + update_state=FALSE; + break; + default: + break; } + if (md && !sal_media_description_empty(md) && !linphone_core_incompatible_security(lc,md)){ linphone_call_update_remote_session_id_and_ver(call); if (sal_media_description_has_dir(md,SalStreamSendOnly) || @@ -451,7 +461,7 @@ static void call_accepted(SalOp *op){ ms_free(msg); } linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); + if (update_state) linphone_call_set_state(call,LinphoneCallPaused,"Call paused"); if (call->refer_pending) linphone_core_start_refered_call(lc,call,NULL); }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){ @@ -464,7 +474,7 @@ static void call_accepted(SalOp *op){ ms_free(msg); } linphone_core_update_streams (lc,call,md); - linphone_call_set_state(call,LinphoneCallPausedByRemote,"Call paused by remote"); + if (update_state) linphone_call_set_state(call,LinphoneCallPausedByRemote,"Call paused by remote"); }else{ if (call->state!=LinphoneCallUpdating){ if (call->state==LinphoneCallResuming){ @@ -485,8 +495,7 @@ static void call_accepted(SalOp *op){ linphone_call_fix_call_parameters(call); if (!call->current_params->in_conference) lc->current_call=call; - if (call->prevstate != LinphoneCallIncomingEarlyMedia) /*don't change state in aswer to a SIP UPDATE in early media*/ - linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); + if (update_state) linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running"); } }else{ /*send a bye*/ @@ -570,7 +579,8 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t if (rmd==NULL) call->expect_media_in_ack=TRUE; } else if (is_update){ /*SIP UPDATE case, can occur in early states*/ - _linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state)); + linphone_call_set_state(call, LinphoneCallEarlyUpdatedByRemote, "EarlyUpdatedByRemote"); + _linphone_core_accept_call_update(lc,call,NULL,call->prevstate,linphone_call_state_to_string(call->prevstate)); } } @@ -627,6 +637,8 @@ static void call_updating(SalOp *op, bool_t is_update){ case LinphoneCallRefered: case LinphoneCallError: case LinphoneCallReleased: + case LinphoneCallEarlyUpdatedByRemote: + case LinphoneCallEarlyUpdating: ms_warning("Receiving reINVITE or UPDATE while in state [%s], should not happen.",linphone_call_state_to_string(call->state)); break; } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 5e7f9e5af..152f35e2c 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -924,6 +924,10 @@ const char *linphone_call_state_to_string(LinphoneCallState cs){ return "LinphoneCallUpdating"; case LinphoneCallReleased: return "LinphoneCallReleased"; + case LinphoneCallEarlyUpdatedByRemote: + return "LinphoneCallEarlyUpdatedByRemote"; + case LinphoneCallEarlyUpdating: + return "LinphoneCallEarlyUpdating"; } return "undefined state"; } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b01430066..40723820a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3281,15 +3281,20 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){ **/ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){ int err=0; + LinphoneCallState nextstate; #if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) bool_t has_video = FALSE; #endif switch(call->state){ - case LinphoneCallIncomingEarlyMedia: case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallOutgoingRinging: + case LinphoneCallOutgoingEarlyMedia: + nextstate=LinphoneCallEarlyUpdating; + break; case LinphoneCallStreamsRunning: - /*these states are allowed for linphone_core_update_call()*/ + nextstate=LinphoneCallUpdating; break; default: ms_error("linphone_core_update_call() is not allowed in [%s] state",linphone_call_state_to_string(call->state)); @@ -3297,7 +3302,7 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho } if (params!=NULL){ - linphone_call_set_state(call,LinphoneCallUpdating,"Updating call"); + linphone_call_set_state(call,nextstate,"Updating call"); #if defined(VIDEO_ENABLED) && defined(BUILD_UPNP) has_video = call->params->has_video; @@ -6413,6 +6418,7 @@ LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *l * @param lc the LinphoneCore * @param call the call for which the parameters are to be build, or NULL in the case where the parameters are to be used for a new outgoing call. * @return a new LinphoneCallParams + * @ingroup call_control */ LinphoneCallParams *linphone_core_create_call_params(LinphoneCore *lc, LinphoneCall *call){ if (!call) return linphone_core_create_default_call_parameters(lc); diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index bac438e93..f3fbe22cb 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -638,7 +638,9 @@ typedef enum _LinphoneCallState{ LinphoneCallUpdatedByRemote, /**number_of_LinphoneCallIncomingEarlyMedia++;break; case LinphoneCallUpdating :counters->number_of_LinphoneCallUpdating++;break; case LinphoneCallReleased :counters->number_of_LinphoneCallReleased++;break; + case LinphoneCallEarlyUpdating: counters->number_of_LinphoneCallEarlyUpdating++;break; + case LinphoneCallEarlyUpdatedByRemote: counters->number_of_LinphoneCallEarlyUpdatedByRemote++;break; default: CU_FAIL("unexpected event");break; } @@ -1906,7 +1908,9 @@ static void call_with_file_player(void) { snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); - /*caller uses soundcard*/ + /*caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player*/ + linphone_core_use_files(pauline->lc,TRUE); + linphone_core_set_play_file(pauline->lc,NULL); /*callee is recording and plays file*/ linphone_core_use_files(pauline->lc,TRUE); @@ -1963,8 +1967,9 @@ static void call_with_mkv_file_player(void) { snprintf(hellowav,sizeof(hellowav), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix); snprintf(hellomkv,sizeof(hellomkv), "%s/sounds/hello8000.mkv", liblinphone_tester_file_prefix); - /*caller uses soundcard*/ - + /*caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player*/ + linphone_core_use_files(pauline->lc,TRUE); + linphone_core_set_play_file(pauline->lc,NULL); /*callee is recording and plays file*/ linphone_core_use_files(pauline->lc,TRUE); linphone_core_set_play_file(pauline->lc,hellowav); /*just to send something but we are not testing what is sent by pauline*/ @@ -1985,7 +1990,7 @@ static void call_with_mkv_file_player(void) { CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); CU_ASSERT_TRUE(ms_audio_diff(hellowav,recordpath,&similar,NULL,NULL)==0); - CU_ASSERT_TRUE(similar>0.9); + CU_ASSERT_TRUE(similar>0.6); CU_ASSERT_TRUE(similar<=1.0); ms_free(recordpath); @@ -2185,7 +2190,6 @@ static void early_media_call_with_update_base(bool_t media_change){ if (media_change) { disable_all_audio_codecs_except_one(marie->lc,"pcmu",-1); disable_all_audio_codecs_except_one(pauline->lc,"pcmu",-1); - } /* Marie calls Pauline, and after the call has rung, transitions to an early_media session @@ -2216,26 +2220,23 @@ static void early_media_call_with_update_base(bool_t media_change){ linphone_call_params_set_session_name(pauline_params,UPDATED_SESSION_NAME); linphone_core_update_call(pauline->lc, pauline_call, pauline_params); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallEarlyUpdating,1,2000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallEarlyUpdatedByRemote,1,2000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1,2000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingEarlyMedia,1,2000)); /*just to wait 2s*/ liblinphone_tester_check_rtcp(marie, pauline); - wait_for_list(lcs, &marie->stat.number_of_LinphoneCallUpdatedByRemote,100000,2000); CU_ASSERT_STRING_EQUAL( linphone_call_params_get_session_name(linphone_call_get_remote_params(marie_call)) , UPDATED_SESSION_NAME); linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); - - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingEarlyMedia,1); - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallStreamsRunning,1); - CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallConnected,1); - - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallIncomingEarlyMedia,1); - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallStreamsRunning,1); - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallConnected,1); - CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallUpdating,1); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallConnected, 1,1000)); + CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); liblinphone_tester_check_rtcp(marie, pauline); diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 9178bac8f..eda851698 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -116,6 +116,8 @@ typedef struct _stats { int number_of_LinphoneCallIncomingEarlyMedia; int number_of_LinphoneCallUpdating; int number_of_LinphoneCallReleased; + int number_of_LinphoneCallEarlyUpdatedByRemote; + int number_of_LinphoneCallEarlyUpdating; int number_of_LinphoneTransferCallOutgoingInit; int number_of_LinphoneTransferCallOutgoingProgress; diff --git a/tester/sounds/hello8000.mkv b/tester/sounds/hello8000.mkv index 42fa387e552d470186a9a4313939d74b6ca274a7..812d62563dd0a01586e16a5ddb37a49aacc91f8e 100644 GIT binary patch literal 96210 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

f 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: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

3HoM7C8s}HNiPa2J*v45 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∨=Ms3nL;^yg|P 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 Date: Mon, 20 Oct 2014 15:26:55 +0200 Subject: [PATCH 073/135] Added missing param since recent changes --- coreapi/linphonecore_jni.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 857ad716d..8b7ea4039 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -2911,7 +2911,7 @@ extern "C" void Java_org_linphone_core_LinphoneChatMessageImpl_startFileDownload jobject listener = env->NewGlobalRef(jlistener); LinphoneChatMessage * message = (LinphoneChatMessage *)ptr; message->cb_ud = listener; - linphone_chat_message_start_file_download(message, chat_room_impl_callback); + linphone_chat_message_start_file_download(message, chat_room_impl_callback, NULL); } extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setVideoWindowId(JNIEnv* env From 4fc7ef7a0bf0adfd68896b231ff17bffcc1e7ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 20 Oct 2014 16:33:50 +0200 Subject: [PATCH 074/135] Fix compilation --- coreapi/help/filetransfer.c | 6 +++--- tester/liblinphone_tester.h | 2 +- tester/message_tester.c | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/coreapi/help/filetransfer.c b/coreapi/help/filetransfer.c index 01f6f3c2a..035a05597 100644 --- a/coreapi/help/filetransfer.c +++ b/coreapi/help/filetransfer.c @@ -48,11 +48,11 @@ static void stop(int signum){ /** * function invoked to report file transfer progress. * */ -static void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress) { +static void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) { const LinphoneAddress* from_address = linphone_chat_message_get_from(message); const LinphoneAddress* to_address = linphone_chat_message_get_to(message); char *address = linphone_chat_message_is_outgoing(message)?linphone_address_as_string(to_address):linphone_address_as_string(from_address); - printf(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", (int)progress + printf(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", (int)((offset *100)/total) ,(linphone_chat_message_is_outgoing(message)?"sent":"received") , content->type , content->subtype @@ -133,7 +133,7 @@ static void message_received(LinphoneCore *lc, LinphoneChatRoom *cr, LinphoneCha const LinphoneContent *file_transfer_info = linphone_chat_message_get_file_transfer_information(msg); printf ("Do you really want to download %s (size %ld)?[Y/n]\nOk, let's go\n", file_transfer_info->name, (long int)file_transfer_info->size); - linphone_chat_message_start_file_download(msg, linphone_file_transfer_state_changed); + linphone_chat_message_start_file_download(msg, linphone_file_transfer_state_changed, NULL); } diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index eda851698..18990f313 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -226,7 +226,7 @@ void text_message_received(LinphoneCore *lc, LinphoneChatRoom *room, const Linph void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage* message); void file_transfer_received(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size); void file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size); -void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress); +void file_transfer_progress_indication(LinphoneCore *lc, 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); void new_subscription_requested(LinphoneCore *lc, LinphoneFriend *lf, const char *url); diff --git a/tester/message_tester.c b/tester/message_tester.c index 21ef387ae..ff6d16cc9 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -116,12 +116,13 @@ void file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const L /** * function invoked to report file transfer progress. * */ -void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress) { +void file_transfer_progress_indication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) { const LinphoneAddress* from_address = linphone_chat_message_get_from(message); const LinphoneAddress* to_address = linphone_chat_message_get_to(message); char *address = linphone_chat_message_is_outgoing(message)?linphone_address_as_string(to_address):linphone_address_as_string(from_address); stats* counters = get_stats(lc); - ms_message(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", (int)progress + int progress = (int)((offset * 100)/total); + ms_message(" File transfer [%d%%] %s of type [%s/%s] %s [%s] \n", progress ,(linphone_chat_message_is_outgoing(message)?"sent":"received") , content->type , content->subtype @@ -431,7 +432,7 @@ static void file_transfer_message(void) { linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,pauline->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); if (marie->stat.last_received_chat_message ) { - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, NULL); } CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageExtBodyReceived,1)); @@ -486,7 +487,7 @@ static void small_file_transfer_message(void) { linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,pauline->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); if (marie->stat.last_received_chat_message ) { - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, NULL); } CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageExtBodyReceived,1)); From 6a455bc346914ecddebabcc8ff3ec743748f42e5 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 20 Oct 2014 16:44:56 +0200 Subject: [PATCH 075/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index c5b6a6df8..d2ed7e161 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit c5b6a6df8c5ccad06e34e6d09410cf8c15d32cf7 +Subproject commit d2ed7e1615a040eb0596e80f8f5b0fcb62a2d5ad From 78c11c8f6e9807fd1342aae78532d46d20f70248 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Mon, 20 Oct 2014 22:49:40 +0200 Subject: [PATCH 076/135] compute call log duration since connected state instead of from call creation --- coreapi/call_log.c | 1 + coreapi/call_log.h | 2 +- coreapi/linphonecall.c | 9 ++++----- coreapi/linphonecore.c | 4 ++-- coreapi/private.h | 4 ++-- tester/call_tester.c | 14 ++++++++++++-- tester/rcfiles/laure_rc | 5 ++++- tester/rcfiles/marie_rc | 3 +++ tester/rcfiles/pauline_rc | 3 +++ 9 files changed, 32 insertions(+), 13 deletions(-) diff --git a/coreapi/call_log.c b/coreapi/call_log.c index d1e59578f..7ce488096 100644 --- a/coreapi/call_log.c +++ b/coreapi/call_log.c @@ -281,6 +281,7 @@ LinphoneCallLog * linphone_call_log_new(LinphoneCallDir dir, LinphoneAddress *fr cl->reporting.reports[LINPHONE_CALL_STATS_AUDIO]=linphone_reporting_new(); cl->reporting.reports[LINPHONE_CALL_STATS_VIDEO]=linphone_reporting_new(); + cl->connected_date_time=0; return cl; } diff --git a/coreapi/call_log.h b/coreapi/call_log.h index d274037d2..6a3ec8dab 100644 --- a/coreapi/call_log.h +++ b/coreapi/call_log.h @@ -79,7 +79,7 @@ LINPHONE_PUBLIC const char * linphone_call_log_get_call_id(const LinphoneCallLog LINPHONE_PUBLIC LinphoneCallDir linphone_call_log_get_dir(LinphoneCallLog *cl); /** - * Get the duration of the call. + * Get the duration of the call since connected. * @param[in] cl LinphoneCallLog object * @return The duration of the call in seconds. **/ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 152f35e2c..7608c47ba 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -564,7 +564,6 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, ms_message("New LinphoneCall [%p] initialized (LinphoneCore version: %s)",call,linphone_core_get_version()); call->state=LinphoneCallIdle; call->transfer_state = LinphoneCallIdle; - call->media_start_time=0; call->log=linphone_call_log_new(call->dir, from, to); call->camera_enabled=TRUE; call->current_params = linphone_call_params_new(); @@ -972,7 +971,7 @@ void linphone_call_set_state_base(LinphoneCall *call, LinphoneCallState cstate, } if (cstate == LinphoneCallConnected) { call->log->status=LinphoneCallSuccess; - call->media_start_time=time(NULL); + call->log->connected_date_time=time(NULL); } if (!silently) @@ -1284,8 +1283,8 @@ bool_t linphone_call_has_transfer_pending(const LinphoneCall *call){ * Returns call's duration in seconds. **/ int linphone_call_get_duration(const LinphoneCall *call){ - if (call->media_start_time==0) return 0; - return time(NULL)-call->media_start_time; + if (call->log->connected_date_time==0) return 0; + return time(NULL)-call->log->connected_date_time; } /** @@ -2952,7 +2951,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse void linphone_call_log_completed(LinphoneCall *call){ LinphoneCore *lc=call->core; - call->log->duration=time(NULL)-call->log->start_date_time; + call->log->duration=linphone_call_get_duration(call); /*store duration since connected*/ if (call->log->status==LinphoneCallMissed){ char *info; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 40723820a..69152647f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2452,8 +2452,8 @@ void linphone_core_iterate(LinphoneCore *lc){ } } if ( (lc->sip_conf.in_call_timeout > 0) - && (call->media_start_time != 0) - && ((curtime - call->media_start_time) > lc->sip_conf.in_call_timeout)) + && (call->log->connected_date_time != 0) + && ((curtime - call->log->connected_date_time) > lc->sip_conf.in_call_timeout)) { ms_message("in call timeout (%i)",lc->sip_conf.in_call_timeout); linphone_core_terminate_call(lc,call); diff --git a/coreapi/private.h b/coreapi/private.h index f23859ddd..7cef61242 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -125,12 +125,13 @@ struct _LinphoneCallLog{ LinphoneAddress *from; /**lc); lcs = ms_list_append(lcs,pauline->lc); @@ -2142,6 +2146,7 @@ static void early_media_call_with_ringing(void){ 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); CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000)); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000)); @@ -2159,20 +2164,25 @@ static void early_media_call_with_ringing(void){ linphone_core_accept_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallConnected, 1,1000)); + connected_time=time(NULL); CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallStreamsRunning, 1,1000)); CU_ASSERT_EQUAL(marie_call, linphone_core_get_current_call(marie->lc)); liblinphone_tester_check_rtcp(marie, pauline); + /*just to have a call duration !=0*/ + wait_for_list(lcs,&dummy,1,2000); linphone_core_terminate_all_calls(pauline->lc); CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,1000)); CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,1000)); - - + ended_time=time(NULL); + CU_ASSERT_TRUE (abs (linphone_call_log_get_duration(marie_call_log) - (ended_time - connected_time)) <1 ); ms_list_free(lcs); } + linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } diff --git a/tester/rcfiles/laure_rc b/tester/rcfiles/laure_rc index 54a682401..7f4d099f5 100644 --- a/tester/rcfiles/laure_rc +++ b/tester/rcfiles/laure_rc @@ -38,4 +38,7 @@ automatically_accept=0 device=StaticImage: Static picture [sound] -echocancellation=0 #to not overload cpu in case of VG \ No newline at end of file +echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general \ No newline at end of file diff --git a/tester/rcfiles/marie_rc b/tester/rcfiles/marie_rc index f4f9aa793..a1721c22a 100644 --- a/tester/rcfiles/marie_rc +++ b/tester/rcfiles/marie_rc @@ -48,3 +48,6 @@ device=StaticImage: Static picture [sound] echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general diff --git a/tester/rcfiles/pauline_rc b/tester/rcfiles/pauline_rc index 09669b72c..7322fd99a 100644 --- a/tester/rcfiles/pauline_rc +++ b/tester/rcfiles/pauline_rc @@ -45,3 +45,6 @@ device=StaticImage: Static picture [sound] echocancellation=0 #to not overload cpu in case of VG + +[net] +dns_srv_enabled=0 #no srv needed in general \ No newline at end of file From d76e97d73f6d7f6e5b5425c0e74438a024add6a0 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 08:50:56 +0200 Subject: [PATCH 077/135] Copy path string when setting log collection path. --- coreapi/linphonecore.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 69152647f..c85be1c1a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -82,7 +82,7 @@ static const char *liblinphone_version= ; static OrtpLogFunc liblinphone_log_func = NULL; static bool_t liblinphone_log_collection_enabled = FALSE; -static const char * liblinphone_log_collection_path = "."; +static char * liblinphone_log_collection_path = NULL; static ortp_mutex_t liblinphone_log_collection_mutex; static bool_t liblinphone_serialize_logs = FALSE; static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); @@ -205,8 +205,8 @@ static void linphone_core_log_collection_handler(OrtpLogLevel level, const char } msg = ortp_strdup_vprintf(fmt, args); - log_filename1 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); - log_filename2 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + log_filename1 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone1.log"); + log_filename2 = ortp_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone2.log"); ortp_mutex_lock(&liblinphone_log_collection_mutex); log_file = fopen(log_filename1, "a"); fstat(fileno(log_file), &statbuf); @@ -233,7 +233,13 @@ static void linphone_core_log_collection_handler(OrtpLogLevel level, const char } void linphone_core_set_log_collection_path(const char *path) { - liblinphone_log_collection_path = path; + if (liblinphone_log_collection_path != NULL) { + ms_free(liblinphone_log_collection_path); + liblinphone_log_collection_path = NULL; + } + if (path != NULL) { + liblinphone_log_collection_path = ms_strdup(path); + } } const char *linphone_core_get_log_collection_upload_server_url(LinphoneCore *core) { @@ -257,7 +263,7 @@ void linphone_core_enable_log_collection(bool_t enable) { } static void delete_log_collection_upload_file(void) { - char *filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); + char *filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", COMPRESSED_LOG_COLLECTION_FILENAME); unlink(filename); ms_free(filename); } @@ -292,7 +298,7 @@ static int log_collection_upload_on_send_body(belle_sip_user_body_handler_t *bh, /* If we've not reach the end of file yet, fill the buffer with more data */ if (offset < core->log_collection_upload_information->size) { - char *log_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); + char *log_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", COMPRESSED_LOG_COLLECTION_FILENAME); #ifdef HAVE_ZLIB FILE *log_file = fopen(log_filename, "rb"); #else @@ -438,17 +444,17 @@ static int prepare_log_collection_file_to_upload(const char *filename) { int ret = 0; ortp_mutex_lock(&liblinphone_log_collection_mutex); - output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", filename); output_file = COMPRESS_OPEN(output_filename, "a"); if (output_file == NULL) goto error; - input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone1.log"); + input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone1.log"); input_file = fopen(input_filename, "r"); if (input_file == NULL) goto error; ret = compress_file(input_file, output_file); if (ret < 0) goto error; fclose(input_file); ms_free(input_filename); - input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, "linphone2.log"); + input_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone2.log"); input_file = fopen(input_filename, "r"); if (input_file != NULL) { ret = compress_file(input_file, output_file); @@ -466,7 +472,7 @@ error: static size_t get_size_of_file_to_upload(const char *filename) { struct stat statbuf; - char *output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path, filename); + char *output_filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", filename); FILE *output_file = fopen(output_filename, "rb"); fstat(fileno(output_file), &statbuf); fclose(output_file); @@ -507,7 +513,7 @@ void linphone_core_upload_log_collection(LinphoneCore *core) { char * linphone_core_compress_log_collection(LinphoneCore *core) { if (liblinphone_log_collection_enabled == FALSE) return NULL; if (prepare_log_collection_file_to_upload(COMPRESSED_LOG_COLLECTION_FILENAME) < 0) return NULL; - return ms_strdup_printf("%s/%s", liblinphone_log_collection_path, COMPRESSED_LOG_COLLECTION_FILENAME); + return ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", COMPRESSED_LOG_COLLECTION_FILENAME); } /** From be7cb58b1899577f4356cb9d9159e406854bc949 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 11:02:33 +0200 Subject: [PATCH 078/135] Fix message tester. --- tester/message_tester.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tester/message_tester.c b/tester/message_tester.c index ff6d16cc9..5fb61d999 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -432,7 +432,7 @@ static void file_transfer_message(void) { linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,pauline->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); if (marie->stat.last_received_chat_message ) { - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, NULL); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, marie->lc); } CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageExtBodyReceived,1)); @@ -487,7 +487,7 @@ static void small_file_transfer_message(void) { linphone_chat_room_send_message2(chat_room,message,liblinphone_tester_chat_message_state_change,pauline->lc); CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1)); if (marie->stat.last_received_chat_message ) { - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, NULL); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, marie->lc); } CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageExtBodyReceived,1)); @@ -606,7 +606,7 @@ static void file_transfer_message_io_error_download(void) { if (marie->stat.last_received_chat_message ) { /* get last message and use it to download file */ - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, marie->lc); /* wait for file to be 50% downloaded */ CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.progress_of_LinphoneFileTransfer, 50)); /* and simulate network error */ @@ -725,7 +725,7 @@ static void file_transfer_message_download_cancelled(void) { if (marie->stat.last_received_chat_message ) { /* get last message and use it to download file */ - linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change); + linphone_chat_message_start_file_download(marie->stat.last_received_chat_message, liblinphone_tester_chat_message_state_change, marie->lc); /* wait for file to be 50% downloaded */ CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.progress_of_LinphoneFileTransfer, 50)); /* and cancel the transfer */ From 9a128da1dbba0ace64e94022af4a5af0b0f097d4 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 12:31:49 +0200 Subject: [PATCH 079/135] Handle file transfer download to a file. --- coreapi/chat.c | 36 +++++++++++++++++++++++------------- coreapi/linphonecore.h | 1 + 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/coreapi/chat.c b/coreapi/chat.c index cc8bc895a..11c63af6d 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -1075,10 +1075,17 @@ static void linphone_chat_process_response_headers_from_get_file(void *data, con body_size = message->file_transfer_information->size; } - belle_sip_message_set_body_handler( - (belle_sip_message_t*)event->response, - (belle_sip_body_handler_t*)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress,on_recv_body,NULL,message) - ); + if (message->file_transfer_filepath == NULL) { + belle_sip_message_set_body_handler( + (belle_sip_message_t*)event->response, + (belle_sip_body_handler_t*)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress,on_recv_body,NULL,message) + ); + } else { + belle_sip_message_set_body_handler( + (belle_sip_message_t *)event->response, + (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(message->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, message) + ); + } } } @@ -1362,6 +1369,18 @@ LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg) { } +/** + * Set the path to the file to read from or write to during the file transfer. + * @param[in] msg LinphoneChatMessage object + * @param[in] filepath The path to the file to use for the file transfer. + */ +void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath) { + if (msg->file_transfer_filepath != NULL) { + ms_free(msg->file_transfer_filepath); + } + msg->file_transfer_filepath = ms_strdup(filepath); +} + /** * Create a message attached to a dedicated chat room with a particular content. Use #linphone_chat_room_send_message2 to initiate the transfer @@ -1385,15 +1404,6 @@ LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneCha return msg; } -LinphoneChatMessage * linphone_chat_room_create_file_transfer_message_from_file(LinphoneChatRoom *cr, LinphoneContent *initial_content, const char *filepath) { - LinphoneChatMessage *msg = linphone_chat_room_create_file_transfer_message(cr, initial_content); - if (msg->file_transfer_filepath != NULL) { - ms_free(msg->file_transfer_filepath); - } - msg->file_transfer_filepath = ms_strdup(filepath); - return msg; -} - /** * @} */ diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index f3fbe22cb..9ed211fae 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1450,6 +1450,7 @@ LINPHONE_PUBLIC bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* me LINPHONE_PUBLIC unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message); LINPHONE_PUBLIC LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg); LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg); +LINPHONE_PUBLIC void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath); /** * @} */ From 63ec504cfc66855db8eec15ebb535a25adfd43a6 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 14:19:18 +0200 Subject: [PATCH 080/135] Add accessor to get the file transfer filepath. --- coreapi/chat.c | 9 +++++++++ coreapi/linphonecore.h | 1 + 2 files changed, 10 insertions(+) diff --git a/coreapi/chat.c b/coreapi/chat.c index 11c63af6d..45e1fc1ce 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -1381,6 +1381,15 @@ void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, msg->file_transfer_filepath = ms_strdup(filepath); } +/** + * Get the path to the file to read from or write to during the file transfer. + * @param[in] msg LinphoneChatMessage object + * @return The path to the file to use for the file transfer. + */ +const char * linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg) { + return msg->file_transfer_filepath; +} + /** * Create a message attached to a dedicated chat room with a particular content. Use #linphone_chat_room_send_message2 to initiate the transfer diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 9ed211fae..a9ca88ddf 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1451,6 +1451,7 @@ LINPHONE_PUBLIC unsigned int linphone_chat_message_get_storage_id(LinphoneChatMe LINPHONE_PUBLIC LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg); LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg); LINPHONE_PUBLIC void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath); +LINPHONE_PUBLIC const char * linphone_chat_message_get_file_transfer_filepath(LinphoneChatMessage *msg); /** * @} */ From 3cbddce46dda9c77b9d4470465bd0e34e5cc211e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 14:35:30 +0200 Subject: [PATCH 081/135] Add missing declaration of linphone_core_set_avpf_rr_interval(). --- coreapi/linphonecore.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index a9ca88ddf..85b7109a9 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3044,6 +3044,8 @@ LINPHONE_PUBLIC void linphone_core_set_avpf_mode(LinphoneCore *lc, LinphoneAVPFM LINPHONE_PUBLIC LinphoneAVPFMode linphone_core_get_avpf_mode(const LinphoneCore *lc); +LINPHONE_PUBLIC void linphone_core_set_avpf_rr_interval(LinphoneCore *lc, int interval); + LINPHONE_PUBLIC int linphone_core_get_avpf_rr_interval(const LinphoneCore *lc); #ifdef __cplusplus From 2a4b0857d84b86b604896facec548476a7802d61 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 14:55:27 +0200 Subject: [PATCH 082/135] Remove declaration of linphone_chat_room_create_file_transfer_message_from_file(). --- coreapi/linphonecore.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 85b7109a9..ec4ab8d1d 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1360,15 +1360,6 @@ LINPHONE_PUBLIC void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void */ LINPHONE_PUBLIC LinphoneChatMessage* linphone_chat_room_create_file_transfer_message(LinphoneChatRoom *cr, LinphoneContent* initial_content); -/** - * Create a message with an attached file that will be read from the given filepath. - * @param[in] cr LinphoneChatRoom object - * @param[in] initial_content LinphoneContent object describing the file to be transfered. - * @param[in] filepath The path to the file to be sent. - * @return A new LinphoneChatMessage object. - */ -LINPHONE_PUBLIC LinphoneChatMessage * linphone_chat_room_create_file_transfer_message_from_file(LinphoneChatRoom *cr, LinphoneContent *initial_content, const char *filepath); - LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_room_get_peer_address(LinphoneChatRoom *cr); LINPHONE_PUBLIC void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg); LINPHONE_PUBLIC void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage* msg,LinphoneChatMessageStateChangedCb status_cb,void* ud); From b156e392075653fda1cb475a9c319f547eebe39f Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 15:20:30 +0200 Subject: [PATCH 083/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index d2ed7e161..2564422eb 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit d2ed7e1615a040eb0596e80f8f5b0fcb62a2d5ad +Subproject commit 2564422ebf4dfe128ddaf922bfae25255a14f377 From b7a8975d4f3c6950160ef11f81ea1ec61aa26843 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Oct 2014 15:22:12 +0200 Subject: [PATCH 084/135] Add localplayer to the Windows Phone 8 build. --- build/wp8/LibLinphone.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/build/wp8/LibLinphone.vcxproj b/build/wp8/LibLinphone.vcxproj index f1bdecc0d..876c93d2a 100644 --- a/build/wp8/LibLinphone.vcxproj +++ b/build/wp8/LibLinphone.vcxproj @@ -121,6 +121,7 @@ + From f7640d9e455fa9e00d5f570c9c5ee14c5883631e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 22 Oct 2014 08:54:15 +0200 Subject: [PATCH 085/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 2564422eb..edb8e3a17 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 2564422ebf4dfe128ddaf922bfae25255a14f377 +Subproject commit edb8e3a1732e1e94b750d546b21be2c968bca0cc From 61419585b47b92d77d8a264472827f6c9bc045b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 22 Oct 2014 11:59:32 +0200 Subject: [PATCH 086/135] Test that LinphonePlayer has been instanciated while destroying --- coreapi/linphonecore_jni.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 8b7ea4039..bcaaa3de0 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -5301,6 +5301,10 @@ extern "C" void Java_org_linphone_core_LinphonePlayerImpl_close(JNIEnv *env, job extern "C" void Java_org_linphone_core_LinphonePlayerImpl_destroy(JNIEnv *env, jobject jobj, jlong playerPtr) { LinphonePlayer *player = (LinphonePlayer *)playerPtr; + if(player == NULL) { + ms_error("Cannot destroy the LinphonePlayerImpl object. Native pointer is NULL"); + return; + } if(player->user_data) { delete (LinphonePlayerData *)player->user_data; } From a742a5aa9b9f9adb4d9fc46574f0836da8de0b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 22 Oct 2014 14:07:39 +0200 Subject: [PATCH 087/135] Fix compilation on Android --- coreapi/linphonecore_jni.cc | 3 ++- mediastreamer2 | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index bcaaa3de0..42f9848b2 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -812,9 +812,10 @@ public: env->CallVoidMethod(lcData->listener, lcData->configuringStateId, lcData->core, env->CallStaticObjectMethod(lcData->configuringStateClass,lcData->configuringStateFromIntId,(jint)status), message ? env->NewStringUTF(message) : NULL); } - static void fileTransferProgressIndication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t progress) { + static void fileTransferProgressIndication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) { JNIEnv *env = 0; jobject jmsg; + size_t progress = (offset * 100) / total; jint result = jvm->AttachCurrentThread(&env,NULL); if (result != 0) { ms_error("cannot attach VM"); diff --git a/mediastreamer2 b/mediastreamer2 index edb8e3a17..d051490ff 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit edb8e3a1732e1e94b750d546b21be2c968bca0cc +Subproject commit d051490ff070aece407149baa9a477d97031c1c6 From 367109341e17360979581efa7905ebff9d6e3372 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 22 Oct 2014 17:22:49 +0200 Subject: [PATCH 088/135] Generate enums with the good values in lp_gen_wrappers. --- tools/generator.cc | 7 +++---- tools/genwrappers.cc | 8 ++++++-- tools/software-desc.hh | 6 +++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/generator.cc b/tools/generator.cc index 82f50c660..2b8ab0a39 100644 --- a/tools/generator.cc +++ b/tools/generator.cc @@ -52,7 +52,7 @@ void CplusplusGenerator::generate(Project *proj){ void CplusplusGenerator::writeEnumMember(ConstField *cf, bool isLast){ writeTabs(1); - mOutfile<getName(); + mOutfile<getName()<<"="<getValue(); if (!isLast) mOutfile<<","; if (!cf->getHelp().empty()) mOutfile<<"\t/**< "<getHelp()<<" */"; mOutfile< members=klass->getConstFields(); list::iterator it; string enum_name=getEnumName(klass); - int value=0; filename<getName())<<"/"<getHelp().empty()){ writeTabs(1); @@ -254,7 +253,7 @@ void JavascriptGenerator::writeEnum(Class *klass){ mOutfile<<"*/"<getName().substr(prefix_size,string::npos)<<" : "<getName().substr(prefix_size,string::npos)<<" : "<getValue(); if (++it!=members.end()) mOutfile<<","; mOutfile< #include #include +#include #include #include #include @@ -335,11 +336,14 @@ static void parseEnum(Project *proj, XmlNode node){ klass->setHelp(node.getChild("detaileddescription").getChild("para").getText()); list enumValues=node.getChildren("enumvalue"); list::iterator it; + int value = 0; for (it=enumValues.begin();it!=enumValues.end();++it){ - ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText()); + XmlNode initializer = (*it).getChild("initializer"); + if (initializer) value=atoi(initializer.getText().c_str()); + ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText(),value); cf->setHelp((*it).getChild("detaileddescription").getChild("para").getText()); klass->addConstField(cf); - + value++; } } diff --git a/tools/software-desc.hh b/tools/software-desc.hh index 2d454e502..575f52fb0 100644 --- a/tools/software-desc.hh +++ b/tools/software-desc.hh @@ -294,7 +294,7 @@ private: class ConstField{ public: - ConstField(Type *type, const string &name, const string &value="") : mType(type), mName(name), mValue(value){ + ConstField(Type *type, const string &name, int value) : mType(type), mName(name), mValue(value){ } void setHelp(const string & help){ mHelp=help; @@ -308,7 +308,7 @@ public: Type *getType()const{ return mType; } - const string &getValue()const{ + int getValue()const{ return mValue; } static string getCommonPrefix(list fields){ @@ -338,7 +338,7 @@ public: private: Type *mType; string mName; - string mValue; + int mValue; string mHelp; }; From 615ceb278fd6df29be0cf1d5b92d4e5c711695d2 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 22 Oct 2014 17:28:40 +0200 Subject: [PATCH 089/135] Fix compilation of lp_gen_wrappers. --- tools/genwrappers.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/genwrappers.cc b/tools/genwrappers.cc index 6f5178367..7c17a2554 100644 --- a/tools/genwrappers.cc +++ b/tools/genwrappers.cc @@ -338,8 +338,8 @@ static void parseEnum(Project *proj, XmlNode node){ list::iterator it; int value = 0; for (it=enumValues.begin();it!=enumValues.end();++it){ - XmlNode initializer = (*it).getChild("initializer"); - if (initializer) value=atoi(initializer.getText().c_str()); + string initializer = (*it).getChild("initializer").getText(); + if (initializer!="") value=atoi(initializer.c_str()); ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText(),value); cf->setHelp((*it).getChild("detaileddescription").getChild("para").getText()); klass->addConstField(cf); From 830d990771be4e9e19230f7a58422a952275974d Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 22 Oct 2014 17:47:36 +0200 Subject: [PATCH 090/135] Fix parsing of enum values in the lp_gen_wrappers. --- tools/genwrappers.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/genwrappers.cc b/tools/genwrappers.cc index 7c17a2554..5f942002f 100644 --- a/tools/genwrappers.cc +++ b/tools/genwrappers.cc @@ -339,7 +339,7 @@ static void parseEnum(Project *proj, XmlNode node){ int value = 0; for (it=enumValues.begin();it!=enumValues.end();++it){ string initializer = (*it).getChild("initializer").getText(); - if (initializer!="") value=atoi(initializer.c_str()); + if (initializer.length() > 1) value=atoi(initializer.substr(1).c_str()); ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText(),value); cf->setHelp((*it).getChild("detaileddescription").getChild("para").getText()); klass->addConstField(cf); From bc69b2796decdafb3b1e344a60ca0a088e7c3c65 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 23 Oct 2014 09:49:35 +0200 Subject: [PATCH 091/135] Improve parsing of enum values in lp_gen_wrappers to support octal and hexadecimal values. --- tools/genwrappers.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/genwrappers.cc b/tools/genwrappers.cc index 5f942002f..26ad7acc1 100644 --- a/tools/genwrappers.cc +++ b/tools/genwrappers.cc @@ -24,7 +24,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include -#include #include #include #include @@ -339,7 +338,19 @@ static void parseEnum(Project *proj, XmlNode node){ int value = 0; for (it=enumValues.begin();it!=enumValues.end();++it){ string initializer = (*it).getChild("initializer").getText(); - if (initializer.length() > 1) value=atoi(initializer.substr(1).c_str()); + if ((initializer.length() > 1) && (initializer.at(0) == '=')) { + std::stringstream ss; + if ((initializer.length() > 2) && (initializer.at(1) == '0')) { + if ((initializer.length() > 3) && (initializer.at(2) == 'x')) { + ss << std::hex << initializer.substr(3); + } else { + ss << std::oct << initializer.substr(2); + } + } else { + ss << std::dec << initializer.substr(1); + } + ss >> value; + } ConstField *cf=new ConstField(Type::getType("int"),(*it).getChild("name").getText(),value); cf->setHelp((*it).getChild("detaileddescription").getChild("para").getText()); klass->addConstField(cf); From 672b8336659c1b03cb185e39bbacc522c8636494 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Thu, 23 Oct 2014 10:53:11 +0200 Subject: [PATCH 092/135] Ignore macos bundle geenrated files --- .gitignore | 3 +++ README.macos | 22 +++++++++++----------- java/common/org/.DS_Store | Bin 6148 -> 0 bytes java/common/org/linphone/.DS_Store | Bin 6148 -> 0 bytes mediastreamer2 | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) delete mode 100644 java/common/org/.DS_Store delete mode 100644 java/common/org/linphone/.DS_Store diff --git a/.gitignore b/.gitignore index 6a94843fa..d5a842ef1 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ tools/xml2lpc_test coreapi/help/filetransfer tester/receive_file.dump tester/tmp.db +.DS_Store +Linphone.app +*.dmg diff --git a/README.macos b/README.macos index e3ffb72d5..722bdc939 100644 --- a/README.macos +++ b/README.macos @@ -27,7 +27,7 @@ You need: $ sudo port install hicolor-icon-theme The next pieces need to be compiled manually. To ensure compatibility with multiple mac os version it is recommended to do: - $ export MACOSX_DEPLOYMENT_TARGET=10.6 + $ export MACOSX_DEPLOYMENT_TARGET=10.6 $ export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" $ export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" $ export CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" @@ -41,7 +41,7 @@ The next pieces need to be compiled manually. To ensure compatibility with multi $ git clone -b linphone git://git.linphone.org/antlr3.git $ cd antlr3/runtime/C $ ./autogen.sh - $ ./configure --disable-static --prefix=/opt/local && make + $ ./configure --disable-static --prefix=/opt/local && make $ sudo make install - Install polarssl (encryption library used by belle-sip) @@ -65,7 +65,7 @@ The next pieces need to be compiled manually. To ensure compatibility with multi $ sudo port install cmake $ git clone https://github.com/wernerd/ZRTPCPP.git $ cd ZRTPCPP - $ cmake -DCORE_LIB=true -DSDES=false CMAKE_INSTALL_NAME_DIR=/usr/local/lib/ -DCMAKE_C_FLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" -DCMAKE_CXX_FLAGS="-arch i386 -arch x86_64 --stdlib=libstdc++ -std=c++11 -lstdc++ -mmacosx-version-min=10.5" -DCMAKE_C_COMPILER=`xcrun --find clang` -DCMAKE_CXX_COMPILER=`xcrun --find clang` . + $ cmake -DCORE_LIB=true -DSDES=false CMAKE_INSTALL_NAME_DIR=/usr/local/lib/ -DCMAKE_C_FLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" -DCMAKE_CXX_FLAGS="-arch i386 -arch x86_64 --stdlib=libstdc++ -std=c++11 -lstdc++ -mmacosx-version-min=10.5" -DCMAKE_C_COMPILER=`xcrun --find clang` -DCMAKE_CXX_COMPILER=`xcrun --find clang` . $ sudo make install @@ -87,22 +87,22 @@ $ sudo make install If you got the source code from git, run ./autogen.sh first. Then or otherwise, do: - + $ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make Install to /opt/local - $ sudo make install + $ sudo make install Done. If you want to generate a portable bundle, then install gtk-mac-bundler. Use git: - $ git clone https://github.com/jralls/gtk-mac-bundler.git + $ 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: - $ sudo touch touch /opt/local/lib/charset.alias + $ 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 . If you don't need plugins, remove or comment out this line from the bundler file: @@ -121,13 +121,13 @@ For a better appearance, you can install the gtk-quartz-engine (a gtk theme) tha $ git clone https://github.com/jralls/gtk-quartz-engine.git $ cd gtk-quartz-engine - $ autoreconf -i - $ ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make + $ autoreconf -i + $ ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make $ sudo make install Generate a new bundle to have it included. -libiconv hack +libiconv hack ************* The Makefile.am rules used to generate the bundle fetch a libiconv.2.dylib from a linphone download page. @@ -141,6 +141,6 @@ In case this library needs to generated, here are the commands: The resulted library can be found in /tmp/opt/local/lib - + diff --git a/java/common/org/.DS_Store b/java/common/org/.DS_Store deleted file mode 100644 index 2d8977d32c09ad774315cd4189e97f8691d52aef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ8r`;3?*9+2D*6cs4L_KLXe&y7YI@ZXaJ)wP;~dybMW_g#>`1v|D*?$L&+shaZ zmjD(E0BhnBhzLxB3Jj`di=jbBykuTYTmpkGn$3si&6*vG`t3Nsc)Dl}RF zSPoYIU*K2z|22s#DnJG9N&)S5``s2#%G%mI&T4IeAK;er12@CmDHyyQ1HBw$VdePQ clOnI!9Q!qK33NK*P6zU5z;vNef&W(E7lsNImH+?% diff --git a/java/common/org/linphone/.DS_Store b/java/common/org/linphone/.DS_Store deleted file mode 100644 index f0988f2021b965fa11efdf8712fc62f3d2a44de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~J&MCX427RIE&|)QOgYUSAU7C7a)MkS*I1iPt)Ctw2uyWN*^)X&uu{pNC7Dz1*Cu! zSdapFjITcz^h|mbDIf)wp@4rM3f)FOW6aI$5DvP7ju? z7GsFlqn#{yT}`&m-VV#*!}8ANQw+^|JFGCFSq&&i0Vyz3VAb={&;JAculax0qEre< zfj3jYhW&BB<4fh)`to{SKV{X|jZVhp3{O7+O#CR`(8IW2d_mS^>tuzdAAyiTK??k< F0>7ec60QIM diff --git a/mediastreamer2 b/mediastreamer2 index d051490ff..055690a69 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit d051490ff070aece407149baa9a477d97031c1c6 +Subproject commit 055690a69eea4758f7aa265862992aca1f2cb982 From d60890107afe62be1207e09ae6c8e98e3b09733f Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 23 Oct 2014 11:20:43 +0200 Subject: [PATCH 093/135] Add API to reset the log collection. --- coreapi/linphonecore.c | 13 +++++++++++++ coreapi/linphonecore.h | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index c85be1c1a..22e53b7a0 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -516,6 +516,19 @@ char * linphone_core_compress_log_collection(LinphoneCore *core) { return ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", COMPRESSED_LOG_COLLECTION_FILENAME); } +void linphone_core_reset_log_collection(LinphoneCore *core) { + char *filename; + ortp_mutex_lock(&liblinphone_log_collection_mutex); + delete_log_collection_upload_file(); + filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone1.log"); + unlink(filename); + ms_free(filename); + filename = ms_strdup_printf("%s/%s", liblinphone_log_collection_path ? liblinphone_log_collection_path : ".", "linphone2.log"); + unlink(filename); + ms_free(filename); + ortp_mutex_unlock(&liblinphone_log_collection_mutex); +} + /** * Enable logs in supplied FILE*. * diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index ec4ab8d1d..c35b7187a 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1817,6 +1817,13 @@ LINPHONE_PUBLIC void linphone_core_upload_log_collection(LinphoneCore *core); */ LINPHONE_PUBLIC char * linphone_core_compress_log_collection(LinphoneCore *core); +/** + * Reset the log collection by removing the log files. + * @ingroup misc + * @param[in] core LinphoneCore object + */ +LINPHONE_PUBLIC void linphone_core_reset_log_collection(LinphoneCore *core); + /** * Define a log handler. * From 891010b78af4fe2484c7d1787e2659f7dee14e45 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Thu, 23 Oct 2014 20:56:21 +0200 Subject: [PATCH 094/135] android/aac: aac needs either hardware AEC or 16kHz sample rate --- coreapi/linphonecore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 22e53b7a0..cac592107 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1031,8 +1031,10 @@ static MSList *codec_append_if_new(MSList *l, PayloadType *pt){ } #if defined(ANDROID) -static int is_aac_eld_payload(const void* a, const void* b) { - PayloadType *pt = (PayloadType*)a; +static int is_aac_eld_not_16k_payload(const void* _pt, const void* unused) { + PayloadType *pt = (PayloadType*)_pt; + if (pt->clock_rate == 16000) + return 1; return strncmp(pt->mime_type, "mpeg4-generic", strlen("mpeg4-generic")); } #endif @@ -1053,11 +1055,11 @@ static void codecs_config_read(LinphoneCore *lc) audio_codecs=add_missing_codecs(lc,SalAudio,audio_codecs); #if defined(ANDROID) - /* AAC-ELD requires hardware AEC */ + /* AAC-ELD requires hardware AEC or 16kHz sample rate */ if (lc->sound_conf.capt_sndcard && !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER)) { /* Remove AAC-ELD */ - audio_codecs = ms_list_remove_custom(audio_codecs, is_aac_eld_payload, NULL); + audio_codecs = ms_list_remove_custom(audio_codecs, is_aac_eld_not_16k_payload, NULL); ms_message("Disable AAC-ELD (needs hardware AEC)"); } #endif From 0c124c486e84ae1ebe194f410e440260e8477eba Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Fri, 24 Oct 2014 14:23:17 +0200 Subject: [PATCH 095/135] Fix logs disable when linphone_core_enable_log_collection is called with FALSE at app start --- coreapi/linphonecore.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index cac592107..03dd18c7f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -173,7 +173,7 @@ static void linphone_core_log_collection_handler(OrtpLogLevel level, const char char *log_filename2; FILE *log_file; struct timeval tp; - struct tm *lt; + struct tm *lt; time_t tt; struct stat statbuf; @@ -251,6 +251,11 @@ void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, cons } void linphone_core_enable_log_collection(bool_t enable) { + /* at first call of this function, set liblinphone_log_func to the current + * ortp log function */ + if( liblinphone_log_func == NULL ){ + liblinphone_log_func = ortp_logv_out; + } if ((enable == TRUE) && (liblinphone_log_collection_enabled == FALSE)) { liblinphone_log_collection_enabled = TRUE; ortp_mutex_init(&liblinphone_log_collection_mutex, NULL); From 367db94f2d3f96a6123330e9c21c71b670d1898a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 24 Oct 2014 16:32:59 +0200 Subject: [PATCH 096/135] Fix crash when playing a file with call player once again --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 055690a69..0941fe191 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 055690a69eea4758f7aa265862992aca1f2cb982 +Subproject commit 0941fe191ce6e80c34c33d6d66a19894e7e22cd8 From 74ac3e68da4bfb6f29854d99ba8c38cacd789640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 24 Oct 2014 16:50:41 +0200 Subject: [PATCH 097/135] Fix call_with_mkv_file_player() tester --- tester/call_tester.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tester/call_tester.c b/tester/call_tester.c index 042a4ba61..62b4d55b5 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -1955,7 +1955,9 @@ static void call_with_mkv_file_player(void) { char hellowav[256]; char *recordpath; double similar; - + + ortp_set_log_level_mask(ORTP_ERROR | ORTP_FATAL | ORTP_MESSAGE | ORTP_WARNING); + if (!is_format_supported(marie->lc,"mkv")){ ms_warning("Test skipped, no mkv support."); goto end; @@ -1968,8 +1970,8 @@ static void call_with_mkv_file_player(void) { snprintf(hellomkv,sizeof(hellomkv), "%s/sounds/hello8000.mkv", liblinphone_tester_file_prefix); /*caller uses files instead of soundcard in order to avoid mixing soundcard input with file played using call's player*/ - linphone_core_use_files(pauline->lc,TRUE); - linphone_core_set_play_file(pauline->lc,NULL); + linphone_core_use_files(marie->lc,TRUE); + linphone_core_set_play_file(marie->lc,NULL); /*callee is recording and plays file*/ linphone_core_use_files(pauline->lc,TRUE); linphone_core_set_play_file(pauline->lc,hellowav); /*just to send something but we are not testing what is sent by pauline*/ @@ -1982,8 +1984,9 @@ static void call_with_mkv_file_player(void) { if (player){ CU_ASSERT_TRUE(linphone_player_open(player,hellomkv,on_eof,marie)==0); CU_ASSERT_TRUE(linphone_player_start(player)==0); + CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&marie->stat.number_of_player_eof,1,12000)); + linphone_player_close(player); } - CU_ASSERT_TRUE(wait_for_until(pauline->lc,marie->lc,&marie->stat.number_of_player_eof,1,12000)); /*just to sleep*/ linphone_core_terminate_all_calls(marie->lc); From 4f23779ab6bed7ef0fa50fab84e7c57ba1835523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 24 Oct 2014 17:32:46 +0200 Subject: [PATCH 098/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 0941fe191..1f1dc7d66 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 0941fe191ce6e80c34c33d6d66a19894e7e22cd8 +Subproject commit 1f1dc7d66dfc8c0fe06c56bbdbebf99795e68a3a From 91d660562f50925ecdee157a740864cd119cd998 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Sat, 25 Oct 2014 15:23:34 +0200 Subject: [PATCH 099/135] fixes video quality problem at high definition and bitrate - key frame spoofing because of loss reported very fast resulting in target bitrate largely exceeded - rate control algorithm taking decision too often due to loss rate estimator counting packets only (not time) --- coreapi/linphonecore.c | 2 +- mediastreamer2 | 2 +- oRTP | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 03dd18c7f..d8ee0ef0b 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1218,7 +1218,7 @@ void linphone_core_set_adaptive_rate_algorithm(LinphoneCore *lc, const char* alg * See linphone_core_set_adaptive_rate_algorithm(). **/ const char * linphone_core_get_adaptive_rate_algorithm(const LinphoneCore *lc){ - return lp_config_get_string(lc->config, "net", "adaptive_rate_algorithm", "Simple"); + return lp_config_get_string(lc->config, "net", "adaptive_rate_algorithm", "Stateful"); } bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc){ diff --git a/mediastreamer2 b/mediastreamer2 index 1f1dc7d66..674ce09c1 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 1f1dc7d66dfc8c0fe06c56bbdbebf99795e68a3a +Subproject commit 674ce09c17014bcf62e89bbddee624f74eee88d1 diff --git a/oRTP b/oRTP index 4077e127e..f1e77dd83 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 4077e127ee731b0797a9303d55f0d61bcb947bf8 +Subproject commit f1e77dd83f697954e78e024e6c837f32fc70ba5f From ea9952ee899235e44a0d2bda761abd3c2c8e3dec Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Sat, 25 Oct 2014 15:26:33 +0200 Subject: [PATCH 100/135] revert change (commited by mistake) --- coreapi/linphonecore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index d8ee0ef0b..03dd18c7f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1218,7 +1218,7 @@ void linphone_core_set_adaptive_rate_algorithm(LinphoneCore *lc, const char* alg * See linphone_core_set_adaptive_rate_algorithm(). **/ const char * linphone_core_get_adaptive_rate_algorithm(const LinphoneCore *lc){ - return lp_config_get_string(lc->config, "net", "adaptive_rate_algorithm", "Stateful"); + return lp_config_get_string(lc->config, "net", "adaptive_rate_algorithm", "Simple"); } bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc){ From 7c3d6206505133a0ac1546edda576fa5a0dcab94 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Mon, 27 Oct 2014 11:56:24 +0100 Subject: [PATCH 101/135] Update README.macos: use Markdown and add HomeBrew installation --- README.macos | 146 ---------------------------------- README.macos.md | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 146 deletions(-) delete mode 100644 README.macos create mode 100644 README.macos.md diff --git a/README.macos b/README.macos deleted file mode 100644 index 722bdc939..000000000 --- a/README.macos +++ /dev/null @@ -1,146 +0,0 @@ -********************************** -* Compiling linphone on macos X * -********************************** - -You need: - - Xcode (download from apple or using appstore application) - - Java SE - - Macports: http://www.macports.org/ - Download and install macports using its user friendly installer. - -- In order to enable generation of bundle for multiple macos version and 32 bit processors, it is recommended to: - 1) edit /opt/local/etc/macports/macports.conf to add the following line: - macosx_deployment_target 10.6 - 2) edit /opt/local/etc/macports/variants.conf to add the following line: - +universal - -- Install build time dependencies - $ sudo port install automake autoconf libtool intltool wget cunit - -- Install some linphone dependencies with macports - $ sudo port install antlr3 speex libvpx readline sqlite3 libsoup openldap libupnp - $ sudo port install ffmpeg-devel -gpl2 - -- 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 - $ sudo port install hicolor-icon-theme - -The next pieces need to be compiled manually. To ensure compatibility with multiple mac os version it is recommended to do: - $ export MACOSX_DEPLOYMENT_TARGET=10.6 - $ export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" - $ export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" - $ 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" - -- (Optional) libvpx-1.2 has a bug on macos resulting in ugly video. It is recommended to upgrade it manually to 1.3 from source. - The libvpx build isn't able to produce dual architecture files. To workaround this, configure libvpx twice and use lipo to create a dual architecture - libvpx.a . - -- Install libantlr3c (library used by belle-sip for parsing) - $ git clone -b linphone git://git.linphone.org/antlr3.git - $ cd antlr3/runtime/C - $ ./autogen.sh - $ ./configure --disable-static --prefix=/opt/local && make - $ sudo make install - -- Install polarssl (encryption library used by belle-sip) - $ git clone git://git.linphone.org/polarssl.git -b linphone - $ cd polarssl - $ ./autogen.sh && ./configure --prefix=/opt/local && make - $ sudo make install - -- Install belle-sip (sip stack) - $ git clone git://git.linphone.org/belle-sip.git - $ cd belle-sip - $ ./autogen.sh && ./configure --prefix=/opt/local && make - $ sudo make install - -- Install srtp (optional) for call encryption - $ git clone git://git.linphone.org/srtp.git - $ cd srtp && autoconf && ./configure --prefix=/opt/local && make libsrtp.a - $ sudo make install - -- Install zrtpcpp (optional), for unbreakable call encryption - $ sudo port install cmake - $ git clone https://github.com/wernerd/ZRTPCPP.git - $ cd ZRTPCPP - $ cmake -DCORE_LIB=true -DSDES=false CMAKE_INSTALL_NAME_DIR=/usr/local/lib/ -DCMAKE_C_FLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" -DCMAKE_CXX_FLAGS="-arch i386 -arch x86_64 --stdlib=libstdc++ -std=c++11 -lstdc++ -mmacosx-version-min=10.5" -DCMAKE_C_COMPILER=`xcrun --find clang` -DCMAKE_CXX_COMPILER=`xcrun --find clang` . -$ sudo make install - - -- Install gsm codec (optional) - $ git clone git://git.linphone.org/gsm.git - $ cd gsm - $ make CCFLAGS="$CFLAGS -c -O2 -DNeedFunctionPrototypes=1" - $ sudo make install INSTALL_ROOT=/opt/local GSM_INSTALL_INC=/opt/local/include - -- Compile and install the tunnel library (optional, proprietary extension only) - - If you got the source code from git, run ./autogen.sh first. - Then or otherwise, do: - - $ ./configure --prefix=/opt/local && make && sudo make install - -- Compile linphone - - If you got the source code from git, run ./autogen.sh first. - - Then or otherwise, do: - - $ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make - - Install to /opt/local - - $ sudo make install - - Done. - -If you want to generate a portable bundle, then install gtk-mac-bundler. -Use git: - $ 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: - $ 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 . -If you don't need plugins, remove or comment out this line from the bundler file: - - ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so - - -Then run, inside linphone source tree: - Run configure as told before but with "--enable-relativeprefix" appended. - $ make - $ make bundle - -The resulting bundle is located in linphone build directory, together with a zipped version. - -For a better appearance, you can install the gtk-quartz-engine (a gtk theme) that make gtk application more similar to other mac applications (but not perfect). - - $ git clone https://github.com/jralls/gtk-quartz-engine.git - $ cd gtk-quartz-engine - $ autoreconf -i - $ ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make - $ sudo make install - -Generate a new bundle to have it included. - -libiconv hack -************* - -The Makefile.am rules used to generate the bundle fetch a libiconv.2.dylib from a linphone download page. -This library adds some additional symbols so that dependencies requiring the iconv from /usr/lib and the ones requiring from the bundle are both satisfied. -In case this library needs to generated, here are the commands: - $ wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz - $ cd libiconv-1.14 - $ patch -p1 < ../linphone/build/macos/libiconv-macos.patch - $ ./configure --prefix=/opt/local --disable-static 'CFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' 'LDFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" && make - $ make install DESTDIR=/tmp - -The resulted library can be found in /tmp/opt/local/lib - - - - diff --git a/README.macos.md b/README.macos.md new file mode 100644 index 000000000..8b3ecc0cb --- /dev/null +++ b/README.macos.md @@ -0,0 +1,202 @@ +# Compiling Linphone on MacOS X + +## Dependencies + +* Xcode (download from apple or using appstore application) +* Java SE +* [HomeBrew](http://brew.sh) or [Macports](http://www.macports.org/). + +### Multiple MacOS version support + +In order to enable generation of bundle for multiple MacOS version and 32 bit processors, it is recommended to: + +1. Edit `/opt/local/etc/macports/macports.conf` to add the following line: + + > macosx_deployment_target 10.6 + +2. Edit `/opt/local/etc/macports/variants.conf` to add the following line: + + > +universal + +### Build time dependencies + +#### Using MacPorts + +* Linphone core dependencies + ```sh + sudo port install automake autoconf libtool 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. + + ```sh + sudo port install gtk2 +quartz +no_x11 + sudo port install gtk-osx-application -python27 + sudo port install hicolor-icon-theme + ``` + + #### Using HomeBrew + + ```sh +brew install automake intltool libtool pkg-config coreutils \ +yasm nasm wget imagemagick gettext gtk+ speex ffmpeg pygtk +brew link gettext --force + +# Yet gtk-mac-integration is not available in main repository +wget https://raw.githubusercontent.com/guitorri/homebrew/794cb6f68dd92cffc60da86fc4b900bc9ce571ef/Library/Formula/gtk-mac-integration.rb +sudo mv gtk-mac-integration.rb /usr/local/Library/Formula/ +brew install gtk-mac-integration + ``` + + ### Building Linphone + +The next pieces need to be compiled manually. + +* To ensure compatibility with multiple MacOS versions it is recommended to do: + + ```sh + export MACOSX_DEPLOYMENT_TARGET=10.6 + export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" + export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" + 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) + + ```sh + git clone -b linphone git://git.linphone.org/antlr3.git + cd antlr3/runtime/C + ./autogen.sh + ./configure --disable-static --prefix=/opt/local && make + sudo make install + ``` + +* Install polarssl (encryption library used by belle-sip) + ```sh + git clone git://git.linphone.org/polarssl.git -b linphone + cd polarssl + ./autogen.sh && ./configure --prefix=/opt/local && make + sudo make install + ``` + +* Install belle-sip (sip stack) + + ```sh + git clone git://git.linphone.org/belle-sip.git + cd belle-sip + ./autogen.sh && ./configure --prefix=/opt/local && make + sudo make install + ``` + +* (Optional) Install srtp for call encryption + + ```sh + git clone git://git.linphone.org/srtp.git + cd srtp && autoconf && ./configure --prefix=/opt/local && make libsrtp.a + sudo make install + ``` + +* (Optional) Install zrtpcpp, for unbreakable call encryption + + ```sh + sudo port install cmake + git clone https://github.com/wernerd/ZRTPCPP.git + cd ZRTPCPP + cmake -DCORE_LIB=true -DSDES=false CMAKE_INSTALL_NAME_DIR=/usr/local/lib/ -DCMAKE_C_FLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" -DCMAKE_CXX_FLAGS="-arch i386 -arch x86_64 --stdlib=libstdc++ -std=c++11 -lstdc++ -mmacosx-version-min=10.5" -DCMAKE_C_COMPILER=`xcrun --find clang` -DCMAKE_CXX_COMPILER=`xcrun --find clang` . + sudo make install + ``` + +* (Optional) Install gsm codec + + ```sh + git clone git://git.linphone.org/gsm.git + cd gsm + make CCFLAGS="$CFLAGS -c -O2 -DNeedFunctionPrototypes=1" + sudo make install INSTALL_ROOT=/opt/local GSM_INSTALL_INC=/opt/local/include + ``` + +* (Optional) libvpx-1.2 has a bug on MacOS resulting in ugly video. It is recommended to upgrade it manually to 1.3 from source. +The libvpx build isn't able to produce dual architecture files. To workaround this, configure libvpx twice and use lipo to create a dual architecture `libvpx.a`. + +* (Optional, proprietary extension only) Compile and install the tunnel library + + If you got the source code from git, run `./autogen.sh` first. + Then or otherwise, do: + + `./configure --prefix=/opt/local && make && sudo make install` + +* Compile Linphone + + If you got the source code from git, run `./autogen.sh` first. + + Then or otherwise, do: + + `PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make` + +* Install on the system + + `sudo make install` + +You are done. + +### Generate portable bundle + +If you want to generate a portable bundle, then install `gtk-mac-bundler`: + + ```sh + 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: + 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`. +If you don't need plugins, remove or comment out this line from the bundler file: + +```xml + +${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so + +``` + +Then run, inside Linphone source tree configure as told before but with `--enable-relativeprefix` appended. + + `make && make bundle` + +The resulting bundle is located in Linphone build directory, together with a zipped version. + +* For a better appearance, you can install `gtk-quartz-engine` (a GTK theme) that makes GTK application more similar to other Mac applications (but not perfect). + + ```sh + git clone https://github.com/jralls/gtk-quartz-engine.git + cd gtk-quartz-engine + autoreconf -i + ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make + sudo make install + ``` + +Generate a new bundle to have it included. + +### libiconv hack + +The `Makefile.am` rules used to generate the bundle fetch a `libiconv.2.dylib` from a Linphone download page. +This library adds some additional symbols so that dependencies requiring the `iconv` from `/usr/lib` and the ones requiring from the bundle are both satisfied. +In case this library needs to generated, here are the commands: + + ```sh + wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz + cd libiconv-1.14 + patch -p1 < ../linphone/build/MacOS/libiconv-MacOS.patch + ./configure --prefix=/opt/local --disable-static 'CFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' 'LDFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" && make + make install DESTDIR=/tmp + ``` + +The resulted library can be found in `/tmp/opt/local/lib`. + + + + From cce5ea1923f42d46112226057e1b541895b12491 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 27 Oct 2014 12:21:17 +0100 Subject: [PATCH 102/135] Update ms2 for AAC-ELD --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 674ce09c1..aacb46a5d 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 674ce09c17014bcf62e89bbddee624f74eee88d1 +Subproject commit aacb46a5dc5c7c9c40844ea52a182c14c08df9fa From 1c16614c3d964290786bbab02b2179cda266806f Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Mon, 27 Oct 2014 12:26:39 +0100 Subject: [PATCH 103/135] Use which to detect intltoolize (for HomeBrew) --- autogen.sh | 9 ++------- mediastreamer2 | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/autogen.sh b/autogen.sh index cf357cd66..9cedbf164 100755 --- a/autogen.sh +++ b/autogen.sh @@ -22,6 +22,7 @@ if test -f /opt/local/bin/glibtoolize ; then else LIBTOOLIZE=libtoolize fi + if test -d /opt/local/share/aclocal ; then ACLOCAL_ARGS="-I /opt/local/share/aclocal" fi @@ -30,13 +31,7 @@ if test -d /share/aclocal ; then ACLOCAL_ARGS="$ACLOCAL_ARGS -I /share/aclocal" fi -if test -f /opt/local/bin/intltoolize ; then - #darwin - INTLTOOLIZE=/opt/local/bin/intltoolize -else - #on mingw, it is important to invoke intltoolize with an absolute path to avoid a bug - INTLTOOLIZE=/usr/bin/intltoolize -fi +INTLTOOLIZE=$(which intltoolize) echo "Generating build scripts in linphone..." set -x diff --git a/mediastreamer2 b/mediastreamer2 index aacb46a5d..5f9a266ad 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit aacb46a5dc5c7c9c40844ea52a182c14c08df9fa +Subproject commit 5f9a266adb16fc981ed06ce5d21865a38f47a935 From b682b01ffab498a41e7ac472564c66a17e01bbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 27 Oct 2014 14:34:18 +0100 Subject: [PATCH 104/135] Update mediastreamer2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 5f9a266ad..9f5f86113 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 5f9a266adb16fc981ed06ce5d21865a38f47a935 +Subproject commit 9f5f861136a9d46dbc7965fbff97b9df74c1adf2 From cd6ffc1b413e4bfdccfb0c8dec65e1470490152b Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 27 Oct 2014 14:41:59 +0100 Subject: [PATCH 105/135] restore support for gtk/x11 on mac --- gtk/videowindow.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gtk/videowindow.c b/gtk/videowindow.c index 4a119faa9..8d404adc5 100644 --- a/gtk/videowindow.c +++ b/gtk/videowindow.c @@ -100,7 +100,11 @@ unsigned long get_native_handle(GdkWindow *gdkw){ #elif defined(WIN32) return (unsigned long)GDK_WINDOW_HWND(gdkw); #elif defined(__APPLE__) - return (unsigned long)gdk_quartz_window_get_nsview(gdkw); +# ifdef HAVE_GTK_OSX /*let's assume the use of gtk-osx implies the use of gtk-quartz.*/ + return (unsigned long)gdk_quartz_window_get_nsview(gdkw); +# else + return (unsigned long)GDK_WINDOW_XID(gdkw); +# endif #endif g_warning("No way to get the native handle from gdk window"); return 0; From 9a6d848d09a80075fb878718a0e6a462ddaaf6fb Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 27 Oct 2014 14:51:39 +0100 Subject: [PATCH 106/135] better handling of gdkx dependency --- gtk/videowindow.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/gtk/videowindow.c b/gtk/videowindow.c index 8d404adc5..24dc584f7 100644 --- a/gtk/videowindow.c +++ b/gtk/videowindow.c @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphone.h" -#ifdef __linux +#ifdef GDK_WINDOWING_X11 #include #elif defined(WIN32) #include @@ -95,16 +95,12 @@ static gboolean drag_drop(GtkWidget *widget, GdkDragContext *drag_context, gint } unsigned long get_native_handle(GdkWindow *gdkw){ -#ifdef __linux +#ifdef GDK_WINDOWING_X11 return (unsigned long)GDK_WINDOW_XID(gdkw); #elif defined(WIN32) return (unsigned long)GDK_WINDOW_HWND(gdkw); #elif defined(__APPLE__) -# ifdef HAVE_GTK_OSX /*let's assume the use of gtk-osx implies the use of gtk-quartz.*/ - return (unsigned long)gdk_quartz_window_get_nsview(gdkw); -# else - return (unsigned long)GDK_WINDOW_XID(gdkw); -# endif + return (unsigned long)gdk_quartz_window_get_nsview(gdkw); #endif g_warning("No way to get the native handle from gdk window"); return 0; From 2409af763767e51f8f8bca91a7c30b8eb83d7768 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Mon, 27 Oct 2014 15:45:54 +0100 Subject: [PATCH 107/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 9f5f86113..ac63fdc65 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 9f5f861136a9d46dbc7965fbff97b9df74c1adf2 +Subproject commit ac63fdc657cf86c6aceaff6bf76e9f04179793c0 From cf3b09e35b4d293e5582f59c2facc1b4abda8e99 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 27 Oct 2014 15:51:15 +0100 Subject: [PATCH 108/135] forcely do not use some codecs under the following conditions: rate!=8000 and rate!=16000 no hardware AEC AEC required (thus software) webRTC AEC is used not opus (because opus can accept 16khz in input) --- coreapi/linphonecore.c | 19 ------------------- coreapi/misc.c | 13 ++++++++++++- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 03dd18c7f..bdf64f557 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1035,15 +1035,6 @@ static MSList *codec_append_if_new(MSList *l, PayloadType *pt){ return l; } -#if defined(ANDROID) -static int is_aac_eld_not_16k_payload(const void* _pt, const void* unused) { - PayloadType *pt = (PayloadType*)_pt; - if (pt->clock_rate == 16000) - return 1; - return strncmp(pt->mime_type, "mpeg4-generic", strlen("mpeg4-generic")); -} -#endif - static void codecs_config_read(LinphoneCore *lc) { int i; @@ -1059,16 +1050,6 @@ static void codecs_config_read(LinphoneCore *lc) } audio_codecs=add_missing_codecs(lc,SalAudio,audio_codecs); -#if defined(ANDROID) - /* AAC-ELD requires hardware AEC or 16kHz sample rate */ - if (lc->sound_conf.capt_sndcard && - !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER)) { - /* Remove AAC-ELD */ - audio_codecs = ms_list_remove_custom(audio_codecs, is_aac_eld_not_16k_payload, NULL); - ms_message("Disable AAC-ELD (needs hardware AEC)"); - } -#endif - for (i=0;get_codec(lc,"video_codec",i,&pt);i++){ if (pt){ if (!ms_filter_codec_supported(pt->mime_type)){ diff --git a/coreapi/misc.c b/coreapi/misc.c index c89111333..efca3c9f9 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -250,7 +250,18 @@ bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, cons /* return TRUE if codec can be used with bandwidth, FALSE else*/ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt){ - return linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, linphone_core_get_payload_type_bitrate(lc,pt)); + bool_t ret=linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, linphone_core_get_payload_type_bitrate(lc,pt)); + if (lc->sound_conf.capt_sndcard + && !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) + && linphone_core_echo_cancellation_enabled(lc) + && (pt->clock_rate!=16000 && pt->clock_rate!=8000) + && strcasecmp(pt->mime_type,"opus")!=0 + && ms_filter_lookup_by_name("MSWebRTCAEC")!=NULL){ + ms_warning("Payload type %s/%i cannot be used because software echo cancellation is required but is unable to operate at this rate.", + pt->mime_type,pt->clock_rate); + ret=FALSE; + } + return ret; } bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){ From 21d20b86b87123d5fb3fca327fc7c763f0af13f4 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 27 Oct 2014 16:38:23 +0100 Subject: [PATCH 109/135] Update readme for osx --- README.macos.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.macos.md b/README.macos.md index 8b3ecc0cb..06bfb2228 100644 --- a/README.macos.md +++ b/README.macos.md @@ -39,18 +39,19 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr #### Using HomeBrew - ```sh -brew install automake intltool libtool pkg-config coreutils \ -yasm nasm wget imagemagick gettext gtk+ speex ffmpeg pygtk -brew link gettext --force + ```sh + brew install automake intltool libtool pkg-config coreutils \ + yasm nasm wget imagemagick gettext gtk+ speex ffmpeg pygtk + brew link gettext --force + + -- gtk-mac-integration is not available in main repository or Brew yet. -# Yet gtk-mac-integration is not available in main repository -wget https://raw.githubusercontent.com/guitorri/homebrew/794cb6f68dd92cffc60da86fc4b900bc9ce571ef/Library/Formula/gtk-mac-integration.rb -sudo mv gtk-mac-integration.rb /usr/local/Library/Formula/ -brew install gtk-mac-integration - ``` + wget https://gist.github.com/Gui13/cdcad37faa6b8ffa0588/raw/bf2277d45e261ad48ae1344c4c97f2684974ed87/gtk-mac-integration.rb + mv gtk-mac-integration.rb /usr/local/Library/Formula/ + brew install gtk-mac-integration - ### Building Linphone + +### Building Linphone The next pieces need to be compiled manually. From 856231394b2c8c127045e75a863aadeaa6ec68e7 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Mon, 27 Oct 2014 16:42:29 +0100 Subject: [PATCH 110/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index ac63fdc65..1f20c8f1f 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit ac63fdc657cf86c6aceaff6bf76e9f04179793c0 +Subproject commit 1f20c8f1fda2d904c0a70819e434c6f2d75062f1 From 03f8b6ae22f7ecdea7c479352e6ee9ee33eb90ea Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Tue, 28 Oct 2014 10:12:19 +0100 Subject: [PATCH 111/135] Update README ZRTP --- README | 31 ++++++++++++++----------------- README.macos.md | 8 +++----- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/README b/README index e10ecc2d2..30666070a 100644 --- a/README +++ b/README @@ -11,12 +11,12 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol. - belle-sip>=1.3.0 - speex>=1.2.0 (including libspeexdsp part) - libxml2 - + + if you want the gtk/glade interface: - libgtk >=2.16.0 + if you want video support: - libvpx (VP8 codec) - - libavcodec (ffmpeg) + - libavcodec (ffmpeg) - libswscale (part of ffmpeg too) for better scaling performance - libxv (x11 video extension) - libgl1-mesa (OpenGL API -- GLX development files) @@ -31,7 +31,7 @@ This is Linphone, a free (GPL) video softphone based on the SIP protocol. + if you want uPnP support (optional): - libupnp (version 1.6 branch (not patched with 18-url-upnpstrings.patch)) - Here is the command line to get these dependencies installed for Ubuntu && Debian + Here is the command line to get these dependencies installed for Ubuntu && Debian $ sudo apt-get install libtool intltool libgtk2.0-dev libspeexdsp-dev \ libavcodec-dev libswscale-dev libx11-dev libxv-dev libgl1-mesa-dev \ @@ -43,22 +43,19 @@ libsoup2.4-dev libsqlite3-dev libupnp4-dev + Install srtp (optional) for call encryption : $ git clone git://git.linphone.org/srtp.git - $ cd srtp && autoconf && ./configure && make - $ sudo make install + $ cd srtp && autoconf && ./configure && make + $ sudo make install - + Install zrtpcpp (optional), for unbreakable call encryption - $ sudo apt-get install cmake - $ git clone https://github.com/wernerd/ZRTPCPP.git - $ cd ZRTPCPP - $ cmake -DCORE_LIB=true -DSDES=false . && make - $ sudo make install - If you get this error: "cc1plus: error: unrecognized command line option ‘-std=c++11’", edit CMakeLists.txt and replace c++11 by c++0x . + + Install zrtp (optional), for unbreakable call encryption + $ git clone git://git.linphone.org:bzrtp + $ cd bzrtp && ./autogen.sh && ./configure && make + $ sudo make install - Compile linphone $ ./autogen.sh - $ ./configure - $ make && sudo make install + $ ./configure + $ make && sudo make install $ sudo ldconfig @@ -71,10 +68,10 @@ For macOS X, see README.macos Here is a short description of the content of the source tree. -- oRTP/ is a poweful implementation of the RTP protocol. See the oRTP/README for more details. +- oRTP/ is a poweful implementation of the RTP protocol. See the oRTP/README for more details. It is used by the mediastreamer to send and receive streams to the network. -- mediastreamer2/ is one of the important part of linphone. It is a framework library for audio +- mediastreamer2/ is one of the important part of linphone. It is a framework library for audio and video processing. It contains several objects for grabing audio and video and outputing it (through rtp, to file). It contains also codec objects to compress audio and video streams. @@ -89,6 +86,6 @@ Here is a short description of the content of the source tree. * linphonec.c is the main file for the console version of linphone. * sipomatic.c / sipomatic.h contains the code for sipomatic, the test program that auto-answer to linphone calls. * shell.c (program name: linphonecsh) is a small utilities to send interactive commands to a running linphonec daemon. - + - share/ contains translation, documentation, rings and hello sound files. diff --git a/README.macos.md b/README.macos.md index 06bfb2228..e8d7b1e16 100644 --- a/README.macos.md +++ b/README.macos.md @@ -100,13 +100,11 @@ The next pieces need to be compiled manually. sudo make install ``` -* (Optional) Install zrtpcpp, for unbreakable call encryption +* (Optional) Install zrtp, for unbreakable call encryption ```sh - sudo port install cmake - git clone https://github.com/wernerd/ZRTPCPP.git - cd ZRTPCPP - cmake -DCORE_LIB=true -DSDES=false CMAKE_INSTALL_NAME_DIR=/usr/local/lib/ -DCMAKE_C_FLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" -DCMAKE_CXX_FLAGS="-arch i386 -arch x86_64 --stdlib=libstdc++ -std=c++11 -lstdc++ -mmacosx-version-min=10.5" -DCMAKE_C_COMPILER=`xcrun --find clang` -DCMAKE_CXX_COMPILER=`xcrun --find clang` . + git clone git://git.linphone.org:bzrtp + cd bzrtp && ./autogen.sh && ./configure --prefix=/opt/local && make sudo make install ``` From 445cdeb88bf70f70e261c1cc653e40c701ca7d70 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Tue, 28 Oct 2014 11:07:22 +0100 Subject: [PATCH 112/135] Fix readme renaming in makefile --- Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index ff729400c..dd66874b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,7 +51,7 @@ PACKAGE_BUNDLE_FILE=$(top_srcdir)/build/macos/$(PACKAGE).bundle EXTRA_DIST = BUGS \ README.arm \ README.mingw \ - README.macos \ + README.macos.md \ autogen.sh \ linphone.spec \ linphone.spec.in \ @@ -172,7 +172,7 @@ filelist: zip setup.exe: filelist cp $(ISS_SCRIPT) $(INSTALLDIR_WITH_PREFIX)/. cd $(INSTALLDIR_WITH_PREFIX) && \ - $(ISCC) $(ISS_SCRIPT) + $(ISCC) $(ISS_SCRIPT) mv $(INSTALLDIR_WITH_PREFIX)/Output/setup.exe $(PACKAGE)-$(VERSION)-setup.exe rm -rf $(INSTALLDIR_WITH_PREFIX)/Output rm -f $(INSTALLDIR_WITH_PREFIX)/$(PACKAGE_WIN32_FILELIST) From 5e6c3bfb9941ebf2cca340cc0fc217cbd426f490 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Tue, 28 Oct 2014 11:26:11 +0100 Subject: [PATCH 113/135] Update README.macos --- README.macos.md | 12 ++++++++++-- mediastreamer2 | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.macos.md b/README.macos.md index e8d7b1e16..48b1e425c 100644 --- a/README.macos.md +++ b/README.macos.md @@ -43,12 +43,20 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr brew install automake intltool libtool pkg-config coreutils \ yasm nasm wget imagemagick gettext gtk+ speex ffmpeg pygtk brew link gettext --force - - -- gtk-mac-integration is not available in main repository or Brew yet. + # then you have to install antlr3 from a tap. + wget https://gist.githubusercontent.com/Gui13/f5cf103f50d34c28c7be/raw/f50242f5e0c3a6d25ed7fca1462bce3a7b738971/antlr3.rb + mv antlr3.rb /usr/local/Library/Formula/ + brew install antlr3 + + brew tap marekjelen/gtk + brew install gtk+-quartz + + # gtk-mac-integration is not available in main repository or Brew yet. wget https://gist.github.com/Gui13/cdcad37faa6b8ffa0588/raw/bf2277d45e261ad48ae1344c4c97f2684974ed87/gtk-mac-integration.rb mv gtk-mac-integration.rb /usr/local/Library/Formula/ brew install gtk-mac-integration + ``` ### Building Linphone diff --git a/mediastreamer2 b/mediastreamer2 index 1f20c8f1f..5f9a266ad 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 1f20c8f1fda2d904c0a70819e434c6f2d75062f1 +Subproject commit 5f9a266adb16fc981ed06ce5d21865a38f47a935 From b4b1683616bd185ee3e5c0bf113f9ee5da4c3427 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Tue, 28 Oct 2014 11:43:18 +0100 Subject: [PATCH 114/135] Add readline linking in readme.macos which is needed for linphonec.c --- README.macos.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.macos.md b/README.macos.md index 48b1e425c..46cdcf4b6 100644 --- a/README.macos.md +++ b/README.macos.md @@ -43,6 +43,8 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr 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 # then you have to install antlr3 from a tap. wget https://gist.githubusercontent.com/Gui13/f5cf103f50d34c28c7be/raw/f50242f5e0c3a6d25ed7fca1462bce3a7b738971/antlr3.rb From e8afe1573d0a939b767519ae53dae28ba0397e88 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Tue, 28 Oct 2014 12:53:20 +0100 Subject: [PATCH 115/135] Better doc indentation --- README.macos.md | 173 ++++++++++++++++++++---------------------------- 1 file changed, 73 insertions(+), 100 deletions(-) diff --git a/README.macos.md b/README.macos.md index 46cdcf4b6..cbdd4c24c 100644 --- a/README.macos.md +++ b/README.macos.md @@ -23,43 +23,37 @@ In order to enable generation of bundle for multiple MacOS version and 32 bit pr #### Using MacPorts * Linphone core dependencies - ```sh - sudo port install automake autoconf libtool intltool wget cunit \ - antlr3 speex libvpx readline sqlite3 libsoup openldap libupnp \ - ffmpeg-devel -gpl2 - ``` + + sudo port install automake autoconf libtool 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. - ```sh - sudo port install gtk2 +quartz +no_x11 - sudo port install gtk-osx-application -python27 - sudo port install hicolor-icon-theme - ``` + sudo port install gtk2 +quartz +no_x11 + sudo port install gtk-osx-application -python27 + sudo port install hicolor-icon-theme #### Using HomeBrew - ```sh - 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 + 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 - # then you have to install antlr3 from a tap. - wget https://gist.githubusercontent.com/Gui13/f5cf103f50d34c28c7be/raw/f50242f5e0c3a6d25ed7fca1462bce3a7b738971/antlr3.rb - mv antlr3.rb /usr/local/Library/Formula/ - brew install antlr3 + # then you have to install antlr3 from a tap. + wget https://gist.githubusercontent.com/Gui13/f5cf103f50d34c28c7be/raw/f50242f5e0c3a6d25ed7fca1462bce3a7b738971/antlr3.rb + mv antlr3.rb /usr/local/Library/Formula/ + brew install antlr3 - brew tap marekjelen/gtk - brew install gtk+-quartz - - # gtk-mac-integration is not available in main repository or Brew yet. - wget https://gist.github.com/Gui13/cdcad37faa6b8ffa0588/raw/bf2277d45e261ad48ae1344c4c97f2684974ed87/gtk-mac-integration.rb - mv gtk-mac-integration.rb /usr/local/Library/Formula/ - brew install gtk-mac-integration - ``` + brew tap marekjelen/gtk + brew install gtk+-quartz + # gtk-mac-integration is not available in main repository or Brew yet. + wget https://gist.github.com/Gui13/cdcad37faa6b8ffa0588/raw/bf2277d45e261ad48ae1344c4c97f2684974ed87/gtk-mac-integration.rb + mv gtk-mac-integration.rb /usr/local/Library/Formula/ + brew install gtk-mac-integration ### Building Linphone @@ -67,65 +61,52 @@ The next pieces need to be compiled manually. * To ensure compatibility with multiple MacOS versions it is recommended to do: - ```sh - export MACOSX_DEPLOYMENT_TARGET=10.6 - export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" - export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" - 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" - ``` + export MACOSX_DEPLOYMENT_TARGET=10.6 + export CFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" + export OBJCFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" + 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) - ```sh - git clone -b linphone git://git.linphone.org/antlr3.git - cd antlr3/runtime/C - ./autogen.sh - ./configure --disable-static --prefix=/opt/local && make - sudo make install - ``` + git clone -b linphone git://git.linphone.org/antlr3.git + cd antlr3/runtime/C + ./autogen.sh + ./configure --disable-static --prefix=/opt/local && make + sudo make install * Install polarssl (encryption library used by belle-sip) - ```sh - git clone git://git.linphone.org/polarssl.git -b linphone - cd polarssl - ./autogen.sh && ./configure --prefix=/opt/local && make - sudo make install - ``` + + git clone git://git.linphone.org/polarssl.git -b linphone + cd polarssl + ./autogen.sh && ./configure --prefix=/opt/local && make + sudo make install * Install belle-sip (sip stack) - ```sh - git clone git://git.linphone.org/belle-sip.git - cd belle-sip - ./autogen.sh && ./configure --prefix=/opt/local && make - sudo make install - ``` + git clone git://git.linphone.org/belle-sip.git + cd belle-sip + ./autogen.sh && ./configure --prefix=/opt/local && make + sudo make install * (Optional) Install srtp for call encryption - ```sh - git clone git://git.linphone.org/srtp.git - cd srtp && autoconf && ./configure --prefix=/opt/local && make libsrtp.a - sudo make install - ``` + git clone git://git.linphone.org/srtp.git + cd srtp && autoconf && ./configure --prefix=/opt/local && make libsrtp.a + sudo make install * (Optional) Install zrtp, for unbreakable call encryption - ```sh - git clone git://git.linphone.org:bzrtp - cd bzrtp && ./autogen.sh && ./configure --prefix=/opt/local && make - sudo make install - ``` + git clone git://git.linphone.org:bzrtp + cd bzrtp && ./autogen.sh && ./configure --prefix=/opt/local && make + sudo make install * (Optional) Install gsm codec - ```sh - git clone git://git.linphone.org/gsm.git - cd gsm - make CCFLAGS="$CFLAGS -c -O2 -DNeedFunctionPrototypes=1" - sudo make install INSTALL_ROOT=/opt/local GSM_INSTALL_INC=/opt/local/include - ``` + git clone git://git.linphone.org/gsm.git + cd gsm + make CCFLAGS="$CFLAGS -c -O2 -DNeedFunctionPrototypes=1" + sudo make install INSTALL_ROOT=/opt/local GSM_INSTALL_INC=/opt/local/include * (Optional) libvpx-1.2 has a bug on MacOS resulting in ugly video. It is recommended to upgrade it manually to 1.3 from source. The libvpx build isn't able to produce dual architecture files. To workaround this, configure libvpx twice and use lipo to create a dual architecture `libvpx.a`. @@ -135,7 +116,7 @@ The libvpx build isn't able to produce dual architecture files. To workaround th If you got the source code from git, run `./autogen.sh` first. Then or otherwise, do: - `./configure --prefix=/opt/local && make && sudo make install` + ./configure --prefix=/opt/local && make && sudo make install * Compile Linphone @@ -143,11 +124,11 @@ The libvpx build isn't able to produce dual architecture files. To workaround th Then or otherwise, do: - `PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make` + PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --prefix=/opt/local --with-readline=/opt/local --disable-x11 --with-srtp=/opt/local --with-gsm=/opt/local --enable-zrtp --disable-strict && make * Install on the system - `sudo make install` + sudo make install You are done. @@ -155,38 +136,32 @@ You are done. If you want to generate a portable bundle, then install `gtk-mac-bundler`: - ```sh - 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: - sudo touch /opt/local/lib/charset.alias - ``` + 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: + 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`. If you don't need plugins, remove or comment out this line from the bundler file: -```xml - -${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so - -``` + + ${prefix:ms2plugins}/lib/mediastreamer/plugins/*.*.so + Then run, inside Linphone source tree configure as told before but with `--enable-relativeprefix` appended. - `make && make bundle` + make && make bundle The resulting bundle is located in Linphone build directory, together with a zipped version. * For a better appearance, you can install `gtk-quartz-engine` (a GTK theme) that makes GTK application more similar to other Mac applications (but not perfect). - ```sh - git clone https://github.com/jralls/gtk-quartz-engine.git - cd gtk-quartz-engine - autoreconf -i - ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make - sudo make install - ``` + git clone https://github.com/jralls/gtk-quartz-engine.git + cd gtk-quartz-engine + autoreconf -i + ./configure --prefix=/opt/local CFLAGS="$CFLAGS -Wno-error" && make + sudo make install Generate a new bundle to have it included. @@ -196,13 +171,11 @@ The `Makefile.am` rules used to generate the bundle fetch a `libiconv.2.dylib` f This library adds some additional symbols so that dependencies requiring the `iconv` from `/usr/lib` and the ones requiring from the bundle are both satisfied. In case this library needs to generated, here are the commands: - ```sh - wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz - cd libiconv-1.14 - patch -p1 < ../linphone/build/MacOS/libiconv-MacOS.patch - ./configure --prefix=/opt/local --disable-static 'CFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' 'LDFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" && make - make install DESTDIR=/tmp - ``` + wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz + cd libiconv-1.14 + patch -p1 < ../linphone/build/MacOS/libiconv-MacOS.patch + ./configure --prefix=/opt/local --disable-static 'CFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' 'LDFLAGS=-arch i386 -arch x86_64 -mmacosx-version-min=10.5' CXXFLAGS="-arch i386 -arch x86_64 -mmacosx-version-min=10.5" && make + make install DESTDIR=/tmp The resulted library can be found in `/tmp/opt/local/lib`. From 327aadb48cb5534067d4a0341236e1cc79f40756 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 28 Oct 2014 15:06:56 +0100 Subject: [PATCH 116/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 5f9a266ad..5efd2f201 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 5f9a266adb16fc981ed06ce5d21865a38f47a935 +Subproject commit 5efd2f20158e72b028ee16dddc08e56b812aff65 From d91b0eaa2867f57840e7bd91cdfca06a3c9f2b9e Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 28 Oct 2014 15:59:58 +0100 Subject: [PATCH 117/135] fix video payload becoming unusable by mistake --- coreapi/misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coreapi/misc.c b/coreapi/misc.c index efca3c9f9..81365f5a2 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -251,7 +251,8 @@ bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, cons /* return TRUE if codec can be used with bandwidth, FALSE else*/ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt){ bool_t ret=linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, linphone_core_get_payload_type_bitrate(lc,pt)); - if (lc->sound_conf.capt_sndcard + if ((pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED) + && lc->sound_conf.capt_sndcard && !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER) && linphone_core_echo_cancellation_enabled(lc) && (pt->clock_rate!=16000 && pt->clock_rate!=8000) From 9d42b2964ce43405ef7b157af1c9cd02b24483ee Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 28 Oct 2014 23:10:18 +0100 Subject: [PATCH 118/135] update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 5efd2f201..36d8af7fa 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 5efd2f20158e72b028ee16dddc08e56b812aff65 +Subproject commit 36d8af7faba64c9970e40bf986ae98c3aebcf9c0 From 29ce46aa1ff04b133daa3e4e6c244fef7757eb94 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 29 Oct 2014 11:26:03 +0100 Subject: [PATCH 119/135] Add API to get the preferred video size name. --- coreapi/linphonecore.c | 6 +++++- coreapi/linphonecore.h | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index bdf64f557..8e1bdcfc2 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -5577,10 +5577,14 @@ void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char * * @ingroup media_parameters **/ -MSVideoSize linphone_core_get_preferred_video_size(LinphoneCore *lc){ +MSVideoSize linphone_core_get_preferred_video_size(const LinphoneCore *lc){ return lc->video_conf.vsize; } +char * linphone_core_get_preferred_video_size_name(const LinphoneCore *lc) { + return ms_strdup(video_size_get_name(lc->video_conf.vsize)); +} + /** * Set the preferred frame rate for video. * Based on the available bandwidth constraints and network conditions, the video encoder diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index c35b7187a..99fe701ea 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -2627,7 +2627,14 @@ 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_preferred_video_size(LinphoneCore *lc); +LINPHONE_PUBLIC MSVideoSize linphone_core_get_preferred_video_size(const LinphoneCore *lc); + +/** + * Get the name of the current preferred video size for sending. + * @param[in] lc #LinphoneCore object. + * @returns A string containing the name of the current preferred video size (to be freed with ms_free()). + */ +LINPHONE_PUBLIC char * linphone_core_get_preferred_video_size_name(const LinphoneCore *lc); LINPHONE_PUBLIC void linphone_core_set_preferred_video_size_by_name(LinphoneCore *lc, const char *name); LINPHONE_PUBLIC void linphone_core_set_preferred_framerate(LinphoneCore *lc, float fps); LINPHONE_PUBLIC float linphone_core_get_preferred_framerate(LinphoneCore *lc); From 0c5309a3c875a2d07d05094358b8e67931595bfc Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Wed, 29 Oct 2014 15:38:51 +0100 Subject: [PATCH 120/135] Fix SDP overflow --- coreapi/bellesip_sal/.dirstamp | 0 coreapi/bellesip_sal/sal_op_call.c | 27 +++++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 coreapi/bellesip_sal/.dirstamp diff --git a/coreapi/bellesip_sal/.dirstamp b/coreapi/bellesip_sal/.dirstamp new file mode 100644 index 000000000..e69de29bb diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index bf352d1b4..9f99077d9 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -75,22 +75,33 @@ static void sdp_process(SalOp *h){ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) { belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; - belle_sip_error_code error = BELLE_SIP_OK; + belle_sip_error_code error = BELLE_SIP_BUFFER_OVERFLOW; size_t length = 0; - char buff[2048]; if (session_desc) { + size_t bufLen = 2048; + size_t hardlimit = 16*1024; /* 16k SDP limit seems reasonable */ + char* buff = belle_sip_malloc(bufLen); content_type = belle_sip_header_content_type_create("application","sdp"); - error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,sizeof(buff),&length); - if (error != BELLE_SIP_OK) { - ms_error("Buffer too small or sdp too big"); + + /* try to marshal the description. This could go higher than 2k so we iterate */ + while( error != BELLE_SIP_OK && bufLen <= hardlimit && buff != NULL){ +// error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); + if( error != BELLE_SIP_OK ){ + bufLen *= 2; + buff = belle_sip_realloc(buff,bufLen); + } + } + /* give up if hard limit reached */ + if (error != BELLE_SIP_OK || buff == NULL) { + ms_error("Buffer too small (%d) or not enough memory, giving up SDP", (int)bufLen); return -1; } - content_length= belle_sip_header_content_length_create(length); + content_length = belle_sip_header_content_length_create(length); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length)); - belle_sip_message_set_body(msg,buff,length); + belle_sip_message_assign_body(msg,buff,length); return 0; } else { return -1; @@ -319,7 +330,7 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ belle_sip_request_t* req; belle_sip_response_t* resp; bool_t release_call=FALSE; - + if (client_transaction) { req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(client_transaction)); From 18e619884c11bee1e648145374bf275a029c78cf Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Wed, 29 Oct 2014 15:41:03 +0100 Subject: [PATCH 121/135] Remove dirstamp --- coreapi/bellesip_sal/.dirstamp | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 coreapi/bellesip_sal/.dirstamp diff --git a/coreapi/bellesip_sal/.dirstamp b/coreapi/bellesip_sal/.dirstamp deleted file mode 100644 index e69de29bb..000000000 From b84133da775286c542f78bc06b866d82574ceb36 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Wed, 29 Oct 2014 16:54:51 +0100 Subject: [PATCH 122/135] Unbreakable space should be also detected as valid for phone numbers (used by iOS to format contacts) --- coreapi/proxy.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 13fa6e048..7e0e35c8a 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -881,8 +881,11 @@ static bool_t is_a_phone_number(const char *username){ *p==')' || *p=='(' || *p=='/' || - *p=='+') continue; - else return FALSE; + *p=='+' || + (unsigned char)*p== 0xca // non-breakable space (iOS uses it to format contacts phone number) + ) + continue; + return FALSE; } return TRUE; } From de1f471a89810ca14802f5e562fc0e707de52957 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 29 Oct 2014 22:59:39 +0100 Subject: [PATCH 123/135] Revert "Fix SDP overflow" This reverts commit 0c5309a3c875a2d07d05094358b8e67931595bfc. --- coreapi/bellesip_sal/sal_op_call.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 9f99077d9..bf352d1b4 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -75,33 +75,22 @@ static void sdp_process(SalOp *h){ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) { belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; - belle_sip_error_code error = BELLE_SIP_BUFFER_OVERFLOW; + belle_sip_error_code error = BELLE_SIP_OK; size_t length = 0; + char buff[2048]; if (session_desc) { - size_t bufLen = 2048; - size_t hardlimit = 16*1024; /* 16k SDP limit seems reasonable */ - char* buff = belle_sip_malloc(bufLen); content_type = belle_sip_header_content_type_create("application","sdp"); - - /* try to marshal the description. This could go higher than 2k so we iterate */ - while( error != BELLE_SIP_OK && bufLen <= hardlimit && buff != NULL){ -// error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); - if( error != BELLE_SIP_OK ){ - bufLen *= 2; - buff = belle_sip_realloc(buff,bufLen); - } - } - /* give up if hard limit reached */ - if (error != BELLE_SIP_OK || buff == NULL) { - ms_error("Buffer too small (%d) or not enough memory, giving up SDP", (int)bufLen); + error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,sizeof(buff),&length); + if (error != BELLE_SIP_OK) { + ms_error("Buffer too small or sdp too big"); return -1; } - content_length = belle_sip_header_content_length_create(length); + content_length= belle_sip_header_content_length_create(length); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length)); - belle_sip_message_assign_body(msg,buff,length); + belle_sip_message_set_body(msg,buff,length); return 0; } else { return -1; @@ -330,7 +319,7 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ belle_sip_request_t* req; belle_sip_response_t* resp; bool_t release_call=FALSE; - + if (client_transaction) { req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(client_transaction)); From 636976e55d9dcb9137a17a39c7596bc4f1c71a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 30 Oct 2014 11:02:20 +0100 Subject: [PATCH 124/135] Reworking TunnelManager Some booleans have been replaced by a state variable --- coreapi/TunnelManager.cc | 190 +++++++++++++++++++++------------------ coreapi/TunnelManager.hh | 15 ++-- 2 files changed, 113 insertions(+), 92 deletions(-) diff --git a/coreapi/TunnelManager.cc b/coreapi/TunnelManager.cc index 03f6923ca..77e26fb1b 100644 --- a/coreapi/TunnelManager.cc +++ b/coreapi/TunnelManager.cc @@ -97,32 +97,15 @@ RtpTransport *TunnelManager::createRtpTransport(int port){ void TunnelManager::startClient() { ms_message("TunnelManager: Starting tunnel client"); - if (mTunnelClient == NULL) { - mTunnelClient = new TunnelClient(); - mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this); - list::iterator it; - for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ - const ServerAddr &addr=*it; - mTunnelClient->addServer(addr.mAddr.c_str(), addr.mPort); - } - mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str()); + mTunnelClient = new TunnelClient(); + mTunnelClient->setCallback((TunnelClientController::StateCallback)tunnelCallback,this); + list::iterator it; + for(it=mServerAddrs.begin();it!=mServerAddrs.end();++it){ + const ServerAddr &addr=*it; + mTunnelClient->addServer(addr.mAddr.c_str(), addr.mPort); } + mTunnelClient->setHttpProxy(mHttpProxyHost.c_str(), mHttpProxyPort, mHttpUserName.c_str(), mHttpPasswd.c_str()); mTunnelClient->start(); - linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories); - if(mTunnelizeSipPackets) { - sal_enable_tunnel(mCore->sal, mTunnelClient); - } - mConnecting = true; -} - -void TunnelManager::stopClient(){ - ms_message("TunnelManager: Stopping tunnel client"); - linphone_core_set_rtp_transport_factories(mCore,NULL); - sal_disable_tunnel(mCore->sal); - if (mTunnelClient){ - delete mTunnelClient; - mTunnelClient=NULL; - } } bool TunnelManager::isConnected() const { @@ -151,9 +134,7 @@ TunnelManager::TunnelManager(LinphoneCore* lc) : mExosipTransport(NULL), #endif mMode(LinphoneTunnelModeDisable), - mAutoDetecting(false), - mConnecting(false), - mScheduledRegistration(false), + mState(disabled), mTunnelizeSipPackets(true), mTunnelClient(NULL), mHttpProxyPort(0), @@ -177,62 +158,94 @@ TunnelManager::~TunnelManager(){ for(UdpMirrorClientList::iterator udpMirror = mUdpMirrorClients.begin(); udpMirror != mUdpMirrorClients.end(); udpMirror++) { udpMirror->stop(); } - stopClient(); + if(mTunnelClient) delete mTunnelClient; linphone_core_remove_listener(mCore, mVTable); linphone_core_v_table_destroy(mVTable); } void TunnelManager::doRegistration(){ - if(mTunnelizeSipPackets) { - LinphoneProxyConfig* lProxy; - linphone_core_get_default_proxy(mCore, &lProxy); - if (lProxy) { - ms_message("TunnelManager: need to register"); - if(linphone_proxy_config_get_state(lProxy) != LinphoneRegistrationProgress) { - linphone_proxy_config_refresh_register(lProxy); - mScheduledRegistration = false; - } else { - ms_warning("TunnelManager: register difered. There is already a registration in progress"); - mScheduledRegistration = true; - } - } else { - mScheduledRegistration = false; - } + LinphoneProxyConfig* lProxy; + linphone_core_get_default_proxy(mCore, &lProxy); + if (lProxy) { + ms_message("TunnelManager: New registration"); + lProxy->commit = TRUE; + } +} + +void TunnelManager::doUnregistration() { + LinphoneProxyConfig *lProxy; + linphone_core_get_default_proxy(mCore, &lProxy); + if(lProxy) { + _linphone_proxy_config_unregister(lProxy); } } void TunnelManager::processTunnelEvent(const Event &ev){ if (ev.mData.mConnected){ - ms_message("Tunnel is connected"); - doRegistration(); + ms_message("TunnelManager: tunnel is connected"); + if(mState == connecting) { + linphone_core_set_rtp_transport_factories(mCore,&mTransportFactories); + if(mTunnelizeSipPackets) { + doUnregistration(); + sal_enable_tunnel(mCore->sal, mTunnelClient); + doRegistration(); + } + mState = ready; + } } else { - ms_error("Tunnel has been disconnected"); + ms_error("TunnelManager: tunnel has been disconnected"); } - mConnecting = false; } void TunnelManager::setMode(LinphoneTunnelMode mode) { - if(mMode != mode) { - ms_message("TunnelManager: Switching mode from %s to %s", - tunnel_mode_to_string(mMode), - tunnel_mode_to_string(mode)); - switch(mode) { - case LinphoneTunnelModeEnable: - mMode = mode; + if(mMode == mode) return; + if((mode==LinphoneTunnelModeDisable && mState==disabled) + || (mode==LinphoneTunnelModeEnable && mState==ready)) { + return; + } + ms_message("TunnelManager: switching mode from %s to %s", + tunnel_mode_to_string(mMode), + tunnel_mode_to_string(mode)); + switch(mode) { + case LinphoneTunnelModeEnable: + if(mState == disabled) { startClient(); - break; - case LinphoneTunnelModeDisable: + mState = connecting; mMode = mode; - stopClient(); - doRegistration(); - break; - case LinphoneTunnelModeAuto: - mMode = mode; - autoDetect(); - break; - default: - ms_error("TunnelManager::setMode(): invalid mode (%d)", mode); + } else { + ms_error("TunnelManager: could not change mode. Bad state"); } + break; + case LinphoneTunnelModeDisable: + if(mState == ready) { + linphone_core_set_rtp_transport_factories(mCore,NULL); + if(mTunnelizeSipPackets) { + doUnregistration(); + sal_disable_tunnel(mCore->sal); + } + delete mTunnelClient; + mTunnelClient=NULL; + if(mTunnelizeSipPackets) { + doRegistration(); + } + mState = disabled; + mMode = mode; + } else { + ms_error("TunnelManager: could not change mode. Bad state"); + } + break; + case LinphoneTunnelModeAuto: + if(mState == disabled || mState == ready) { + if(startAutoDetection()) { + mState = autodetecting; + mMode = mode; + } + } else { + ms_error("TunnelManager: could not change mode. Bad state"); + } + break; + default: + ms_error("TunnelManager::setMode(): invalid mode (%d)", mode); } } @@ -244,10 +257,6 @@ void TunnelManager::tunnelCallback(bool connected, TunnelManager *zis){ } void TunnelManager::onIterate(){ - if(mScheduledRegistration) { - ms_message("Apply difered registration"); - doRegistration(); - } mMutex.lock(); while(!mEvq.empty()){ Event ev=mEvq.front(); @@ -313,22 +322,31 @@ LinphoneTunnelMode TunnelManager::getMode() const { } void TunnelManager::processUdpMirrorEvent(const Event &ev){ + if(mState != autodetecting) return; if (ev.mData.mHaveUdp) { - ms_message("TunnelManager: auto detection test succeed"); - stopClient(); - doRegistration(); - mAutoDetecting = false; + ms_message("TunnelManager: UDP mirror test succeed"); + if(mTunnelClient) { + if(mTunnelizeSipPackets) doUnregistration(); + delete mTunnelClient; + mTunnelClient = NULL; + if(mTunnelizeSipPackets) doRegistration(); + } + mState = disabled; } else { - ms_message("TunnelManager: auto detection test failed"); + ms_message("TunnelManager: UDP mirror test failed"); mCurrentUdpMirrorClient++; if (mCurrentUdpMirrorClient !=mUdpMirrorClients.end()) { - ms_message("TunnelManager: trying another udp mirror"); + ms_message("TunnelManager: trying another UDP mirror"); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); } else { - ms_message("TunnelManager: all auto detection failed. Need ti enable tunnel"); - startClient(); - mAutoDetecting = false; + ms_message("TunnelManager: all UDP mirror tests failed"); + if(mTunnelClient==NULL) { + startClient(); + mState = connecting; + } else { + mState = ready; + } } } } @@ -349,24 +367,22 @@ void TunnelManager::sUdpMirrorClientCallback(bool isUdpAvailable, void* data) { void TunnelManager::networkReachableCb(LinphoneCore *lc, bool_t reachable) { TunnelManager *tunnel = bcTunnel(linphone_core_get_tunnel(lc)); - if(reachable && tunnel->getMode() == LinphoneTunnelModeAuto) { - tunnel->autoDetect(); + if(reachable && tunnel->getMode() == LinphoneTunnelModeAuto && tunnel->mState != connecting && tunnel->mState != autodetecting) { + tunnel->startAutoDetection(); + tunnel->mState = autodetecting; } } -void TunnelManager::autoDetect() { - if(mAutoDetecting) { - ms_error("TunnelManager: Cannot start auto detection. One auto detection is going on"); - return; - } +bool TunnelManager::startAutoDetection() { if (mUdpMirrorClients.empty()) { ms_error("TunnelManager: No UDP mirror server configured aborting auto detection"); - return; + return false; } + ms_message("TunnelManager: Starting auto-detection"); mCurrentUdpMirrorClient = mUdpMirrorClients.begin(); UdpMirrorClient &lUdpMirrorClient=*mCurrentUdpMirrorClient; lUdpMirrorClient.start(TunnelManager::sUdpMirrorClientCallback,(void*)this); - mAutoDetecting = true; + return true; } void TunnelManager::setHttpProxyAuthInfo(const char* username,const char* passwd) { diff --git a/coreapi/TunnelManager.hh b/coreapi/TunnelManager.hh index 9b1a4c3a9..58475aab3 100644 --- a/coreapi/TunnelManager.hh +++ b/coreapi/TunnelManager.hh @@ -143,6 +143,13 @@ namespace belledonnecomm { bool isConnected() const; private: + enum State { + disabled, + connecting, + ready, + autodetecting + }; + enum EventType{ UdpMirrorClientEvent, TunnelEvent, @@ -168,9 +175,9 @@ namespace belledonnecomm { private: void onIterate(); void doRegistration(); + void doUnregistration(); void startClient(); - void stopClient(); - void autoDetect(); + bool startAutoDetection(); void processTunnelEvent(const Event &ev); void processUdpMirrorEvent(const Event &ev); void postEvent(const Event &ev); @@ -178,9 +185,7 @@ namespace belledonnecomm { private: LinphoneCore* mCore; LinphoneTunnelMode mMode; - bool mAutoDetecting; - bool mConnecting; - bool mScheduledRegistration; + State mState; bool mTunnelizeSipPackets; TunnelClient* mTunnelClient; std::string mHttpUserName; From a2954ef1aab1ef1337c09aec6a5ec10ea657273b Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 14:59:08 +0100 Subject: [PATCH 125/135] Fix compilation warnings/errors when compiling for Android. --- build/android/Android.mk | 3 +++ coreapi/linphonecore_jni.cc | 10 +++++++--- mediastreamer2 | 2 +- oRTP | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/build/android/Android.mk b/build/android/Android.mk index e634d3fa0..c281b241d 100755 --- a/build/android/Android.mk +++ b/build/android/Android.mk @@ -266,5 +266,8 @@ LOCAL_MODULE_FILENAME := liblinphone-$(TARGET_ARCH_ABI) include $(BUILD_SHARED_LIBRARY) +LOCAL_CPPFLAGS=$(LOCAL_CFLAGS) +LOCAL_CFLAGS += -Wdeclaration-after-statement + $(call import-module,android/cpufeatures) diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 42f9848b2..241f130cc 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -715,7 +715,6 @@ public: static void infoReceived(LinphoneCore *lc, LinphoneCall*call, const LinphoneInfoMessage *info){ JNIEnv *env = 0; jint result = jvm->AttachCurrentThread(&env,NULL); - jobject jcall; if (result != 0) { ms_error("cannot attach VM"); return; @@ -4055,7 +4054,7 @@ JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_addHeader( * Signature: (JLjava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_linphone_core_LinphoneInfoMessageImpl_getHeader(JNIEnv *env, jobject jobj, jlong infoptr, jstring jname){ - const char *name=name=env->GetStringUTFChars(jname,NULL); + const char *name=env->GetStringUTFChars(jname,NULL); const char *ret=linphone_info_message_get_header((LinphoneInfoMessage*)infoptr,name); env->ReleaseStringUTFChars(jname,name); return ret ? env->NewStringUTF(ret) : NULL; @@ -4727,6 +4726,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceActivityImpl_setDescriptio const char *cdescription = description ? env->GetStringUTFChars(description, NULL) : NULL; linphone_presence_activity_set_description(activity, cdescription); if (cdescription) env->ReleaseStringUTFChars(description, cdescription); + return (jint)0; } /* @@ -4778,6 +4778,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_setId(JNIEnv * const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL; linphone_presence_service_set_id(service, cid); if (cid) env->ReleaseStringUTFChars(id, cid); + return (jint)0; } /* @@ -4823,6 +4824,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceServiceImpl_setContact(JNI const char *ccontact = contact ? env->GetStringUTFChars(contact, NULL) : NULL; linphone_presence_service_set_contact(service, ccontact); if (ccontact) env->ReleaseStringUTFChars(contact, ccontact); + return (jint)0; } /* @@ -4913,6 +4915,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresencePersonImpl_setId(JNIEnv *e const char *cid = id ? env->GetStringUTFChars(id, NULL) : NULL; linphone_presence_person_set_id(person, cid); if (cid) env->ReleaseStringUTFChars(id, cid); + return (jint)0; } /* @@ -5085,6 +5088,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceNoteImpl_setContent(JNIEnv const char *ccontent = content ? env->GetStringUTFChars(content, NULL) : NULL; linphone_presence_note_set_content(note, ccontent); if (ccontent) env->ReleaseStringUTFChars(content, ccontent); + return (jint)0; } /* @@ -5108,6 +5112,7 @@ JNIEXPORT jint JNICALL Java_org_linphone_core_PresenceNoteImpl_setLang(JNIEnv *e const char *clang = lang ? env->GetStringUTFChars(lang, NULL) : NULL; linphone_presence_note_set_lang(note, clang); if (clang) env->ReleaseStringUTFChars(lang, clang); + return (jint)0; } /* @@ -5266,7 +5271,6 @@ extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_open(JNIEnv *env, jobj } extern "C" jint Java_org_linphone_core_LinphonePlayerImpl_start(JNIEnv *env, jobject jobj, jlong ptr) { - LinphonePlayerData *player_data = (LinphonePlayerData *)((LinphonePlayer *)ptr)->user_data; return (jint)linphone_player_start((LinphonePlayer *)ptr); } diff --git a/mediastreamer2 b/mediastreamer2 index 36d8af7fa..8642a0971 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 36d8af7faba64c9970e40bf986ae98c3aebcf9c0 +Subproject commit 8642a0971455f5bd444e2b84c555848d9981aebc diff --git a/oRTP b/oRTP index f1e77dd83..90a19604d 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit f1e77dd83f697954e78e024e6c837f32fc70ba5f +Subproject commit 90a19604d061d7f23189feb925847e66d366898e From d58ec509e96126fdd699b7db68f9754704977180 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 15:02:14 +0100 Subject: [PATCH 126/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 8642a0971..203e7f061 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 8642a0971455f5bd444e2b84c555848d9981aebc +Subproject commit 203e7f061ab816d4bb204845995c8a208a184230 From 53af357a32e1981a26977a57d6af7626bf17382b Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Thu, 30 Oct 2014 15:38:42 +0100 Subject: [PATCH 127/135] Update ms2 for SBR in AAC --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 203e7f061..4cd6b406b 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 203e7f061ab816d4bb204845995c8a208a184230 +Subproject commit 4cd6b406b43e9c481c8bbf223735fd7f16f4d178 From 87893d9b8f144693d4504c69963a876187daa706 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Thu, 30 Oct 2014 15:39:40 +0100 Subject: [PATCH 128/135] Add SBR support for AAC when [misc] aac_use_sbr=1 is in linphonerc --- coreapi/linphonecore.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 8e1bdcfc2..e715d9e78 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1443,6 +1443,7 @@ void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata) { const char *remote_provisioning_uri = NULL; + const char *aac_ftmp162248, *aac_ftmp3244; LinphoneCoreVTable* local_vtable= linphone_core_v_table_new(); ms_message("Initializing LinphoneCore %s", linphone_core_get_version()); memset (lc, 0, sizeof (LinphoneCore)); @@ -1495,6 +1496,18 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab /* linphone_core_assign_payload_type(&payload_type_h264,-1,"packetization-mode=1;profile-level-id=428014");*/ #endif + /* For AAC, we use a config value to determine if we ought to support SBR. Since it is not offically supported + * for the mpeg4-generic mime type, setting this flag to 1 will break compatibility with other clients. */ + if( lp_config_get_int(lc->config, "misc", "aac_use_sbr", FALSE) ) { + ms_message("Using SBR for AAC"); + aac_ftmp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5 SBR-enabled=1;"; + aac_ftmp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5 SBR-enabled=1;"; + } else { + aac_ftmp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; + aac_ftmp3244 = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"; + } + + /*add all payload type for which we don't care about the number */ linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30"); linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1"); @@ -1513,11 +1526,11 @@ static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtab linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL); linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_16k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_22k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_32k,-1,"config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_44k,-1,"config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); - linphone_core_assign_payload_type(lc,&payload_type_aaceld_48k,-1,"config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5"); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_16k,-1,aac_ftmp162248); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_22k,-1,aac_ftmp162248); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_32k,-1,aac_ftmp3244); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_44k,-1,aac_ftmp3244); + linphone_core_assign_payload_type(lc,&payload_type_aaceld_48k,-1,aac_ftmp162248); linphone_core_assign_payload_type(lc,&payload_type_opus,-1,"useinbandfec=1; stereo=0; sprop-stereo=0"); linphone_core_assign_payload_type(lc,&payload_type_isac,-1,NULL); linphone_core_handle_static_payloads(lc); From 2c940567961a979dff77f1c0998784997c3fdf16 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Thu, 30 Oct 2014 15:40:22 +0100 Subject: [PATCH 129/135] Fix SDP overflow commit --- coreapi/bellesip_sal/sal_op_call.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index bf352d1b4..fbed2cf18 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -75,22 +75,33 @@ static void sdp_process(SalOp *h){ static int set_sdp(belle_sip_message_t *msg,belle_sdp_session_description_t* session_desc) { belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; - belle_sip_error_code error = BELLE_SIP_OK; + belle_sip_error_code error = BELLE_SIP_BUFFER_OVERFLOW; size_t length = 0; - char buff[2048]; if (session_desc) { + size_t bufLen = 2048; + size_t hardlimit = 16*1024; /* 16k SDP limit seems reasonable */ + char* buff = belle_sip_malloc(bufLen); content_type = belle_sip_header_content_type_create("application","sdp"); - error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,sizeof(buff),&length); - if (error != BELLE_SIP_OK) { - ms_error("Buffer too small or sdp too big"); + + /* try to marshal the description. This could go higher than 2k so we iterate */ + while( error != BELLE_SIP_OK && bufLen <= hardlimit && buff != NULL){ + error = belle_sip_object_marshal(BELLE_SIP_OBJECT(session_desc),buff,bufLen,&length); + if( error != BELLE_SIP_OK ){ + bufLen *= 2; + buff = belle_sip_realloc(buff,bufLen); + } + } + /* give up if hard limit reached */ + if (error != BELLE_SIP_OK || buff == NULL) { + ms_error("Buffer too small (%d) or not enough memory, giving up SDP", (int)bufLen); return -1; } - content_length= belle_sip_header_content_length_create(length); + content_length = belle_sip_header_content_length_create(length); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(msg,BELLE_SIP_HEADER(content_length)); - belle_sip_message_set_body(msg,buff,length); + belle_sip_message_assign_body(msg,buff,length); return 0; } else { return -1; From f5c52560499a9d39cb3c212567b1fabd933383c7 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 15:46:00 +0100 Subject: [PATCH 130/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 4cd6b406b..6d7273da2 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 4cd6b406b43e9c481c8bbf223735fd7f16f4d178 +Subproject commit 6d7273da2b8cde1d12d5b4395925b453e7e8bbcb From 7461f509f7f5266a4799fb11d292d71d662ea9b2 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 15:53:19 +0100 Subject: [PATCH 131/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 6d7273da2..3f84f2ffb 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 6d7273da2b8cde1d12d5b4395925b453e7e8bbcb +Subproject commit 3f84f2ffbb6043414bd5aa99e6320295adf458c9 From f84a588c11700913dbae7c324c3b0d9a87af3307 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Thu, 30 Oct 2014 16:19:17 +0100 Subject: [PATCH 132/135] Update ms2 --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 3f84f2ffb..d6dcb9b91 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 3f84f2ffbb6043414bd5aa99e6320295adf458c9 +Subproject commit d6dcb9b91ec7c2d3d9ef9bdcacb5bdfd5471abad From dc2ad51c2b629a21d1b6cb19f50d26371672c830 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 16:38:15 +0100 Subject: [PATCH 133/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index d6dcb9b91..7255bc7ad 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit d6dcb9b91ec7c2d3d9ef9bdcacb5bdfd5471abad +Subproject commit 7255bc7adb8bdf1e19be3b52b3d2f168c6b1b4cc From 62fd80b8555567bffb7b5a15ac25104e5bddae50 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Thu, 30 Oct 2014 16:50:39 +0100 Subject: [PATCH 134/135] Update ms2 submodule. --- mediastreamer2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediastreamer2 b/mediastreamer2 index 7255bc7ad..9dd1d9633 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit 7255bc7adb8bdf1e19be3b52b3d2f168c6b1b4cc +Subproject commit 9dd1d9633e66ea018d346cc79a6d93ff6daeddf1 From ba815377f7ee98142b370e7d091fbca4608520e7 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Fri, 31 Oct 2014 09:47:50 +0100 Subject: [PATCH 135/135] Change API of linphone_core_enable_log_collection() to be able to deactivate the previous log handler. --- coreapi/linphonecore.c | 10 +++++++--- coreapi/linphonecore.h | 10 ++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index e715d9e78..8e6316b54 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -250,16 +250,20 @@ void linphone_core_set_log_collection_upload_server_url(LinphoneCore *core, cons lp_config_set_string(core->config, "misc", "log_collection_upload_server_url", server_url); } -void linphone_core_enable_log_collection(bool_t enable) { +void linphone_core_enable_log_collection(LinphoneLogCollectionState state) { /* at first call of this function, set liblinphone_log_func to the current * ortp log function */ if( liblinphone_log_func == NULL ){ liblinphone_log_func = ortp_logv_out; } - if ((enable == TRUE) && (liblinphone_log_collection_enabled == FALSE)) { + if ((state != LinphoneLogCollectionDisabled) && (liblinphone_log_collection_enabled == FALSE)) { liblinphone_log_collection_enabled = TRUE; ortp_mutex_init(&liblinphone_log_collection_mutex, NULL); - liblinphone_log_func = ortp_logv_out; + if (state == LinphoneLogCollectionEnabledWithoutPreviousLogHandler) { + liblinphone_log_func = NULL; + } else { + liblinphone_log_func = ortp_logv_out; + } ortp_set_log_handler(linphone_core_log_collection_handler); } else { liblinphone_log_collection_enabled = FALSE; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 99fe701ea..be9e367a4 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -1780,12 +1780,18 @@ typedef void * (*LinphoneCoreWaitingCallback)(LinphoneCore *lc, void *context, L /* THE main API */ +typedef enum _LinphoneLogCollectionState { + LinphoneLogCollectionDisabled, + LinphoneLogCollectionEnabled, + LinphoneLogCollectionEnabledWithoutPreviousLogHandler +} LinphoneLogCollectionState; + /** * Enable the linphone core log collection to upload logs on a server. * @ingroup misc - * @param[in] enable Boolean value telling whether to enable log collection or not. + * @param[in] state LinphoneLogCollectionState value telling whether to enable log collection or not. */ -LINPHONE_PUBLIC void linphone_core_enable_log_collection(bool_t enable); +LINPHONE_PUBLIC void linphone_core_enable_log_collection(LinphoneLogCollectionState state); /** * Set the path where the log files will be written for log collection.