diff options
Diffstat (limited to 'protocols/Tox/toxcore/toxav/msi.c')
-rw-r--r-- | protocols/Tox/toxcore/toxav/msi.c | 1947 |
1 files changed, 0 insertions, 1947 deletions
diff --git a/protocols/Tox/toxcore/toxav/msi.c b/protocols/Tox/toxcore/toxav/msi.c deleted file mode 100644 index 91742c3579..0000000000 --- a/protocols/Tox/toxcore/toxav/msi.c +++ /dev/null @@ -1,1947 +0,0 @@ -/** msi.c - * - * Copyright (C) 2013 Tox project All Rights Reserved. - * - * This file is part of Tox. - * - * Tox 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 3 of the License, or - * (at your option) any later version. - * - * Tox 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 Tox. If not, see <http://www.gnu.org/licenses/>. - * - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "../toxcore/logger.h" -#include "../toxcore/util.h" - -#include "msi.h" - -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <stdbool.h> - -#define MSI_MAXMSG_SIZE 256 - -/* Define default timeout for a request. - * There is no behavior specified by the msi on what will - * client do on timeout, but to call timeout callback. - */ -#define m_deftout 10000 /* in milliseconds */ - -/** - * Protocol: - * - * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| - */ - -typedef uint8_t MSIRawCSettingsType[23]; - -typedef enum { - IDRequest = 1, - IDResponse, - IDReason, - IDCallId, - IDCSettings, - -} MSIHeaderID; - -typedef enum { - TypeRequest, - TypeResponse, - -} MSIMessageType; - -typedef enum { - invite, - start, - cancel, - reject, - end, - -} MSIRequest; - -typedef enum { - ringing, - starting, - ending, - error - -} MSIResponse; - - -#define GENERIC_HEADER(header, val_type) \ -typedef struct _MSIHeader##header { \ -val_type value; \ -_Bool exists; \ -} MSIHeader##header; - - -GENERIC_HEADER ( Request, MSIRequest ) -GENERIC_HEADER ( Response, MSIResponse ) -GENERIC_HEADER ( CallId, MSICallIDType ) -GENERIC_HEADER ( Reason, MSIReasonStrType ) -GENERIC_HEADER ( CSettings, MSIRawCSettingsType ) - - -/** - * @brief This is the message structure. It contains all of the headers and - * destination/source of the message stored in friend_id. - * - */ -typedef struct _MSIMessage { - - MSIHeaderRequest request; - MSIHeaderResponse response; - MSIHeaderReason reason; - MSIHeaderCallId callid; - MSIHeaderCSettings csettings; - - int friend_id; - -} MSIMessage; - - -inline__ void invoke_callback(MSISession *session, int32_t call_index, MSICallbackID id) -{ - if ( session->callbacks[id].function ) { - LOGGER_DEBUG("Invoking callback function: %d", id); - session->callbacks[id].function ( session->agent_handler, call_index, session->callbacks[id].data ); - } -} - -/** - * @brief Parse raw 'data' received from socket into MSIMessage struct. - * Every message has to have end value of 'end_byte' or _undefined_ behavior - * occures. The best practice is to check the end of the message at the handle_packet. - * - * @param msg Container. - * @param data The data. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) -{ - -#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } -#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } -#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; } - - if ( msg == NULL ) { - LOGGER_ERROR("Could not parse message: no storage!"); - return -1; - } - - if ( data[length - 1] ) { /* End byte must have value 0 */ - LOGGER_ERROR("Invalid end byte"); - return -1; - } - - const uint8_t *it = data; - int size_constraint = length; - - while ( *it ) {/* until end byte is hit */ - switch (*it) { - case IDRequest: - FAIL_CONSTRAINT(size_constraint, 3); - FAIL_SIZE(it[1], 1); -// FAIL_LIMITS(it[2], invite, end); - FAIL_LIMITS(it[2], end); - msg->request.value = it[2]; - it += 3; - msg->request.exists = 1; - break; - - case IDResponse: - FAIL_CONSTRAINT(size_constraint, 3); - FAIL_SIZE(it[1], 1); -// FAIL_LIMITS(it[2], ringing, error); - FAIL_LIMITS(it[2], error); - msg->response.value = it[2]; - it += 3; - msg->response.exists = 1; - break; - - case IDCallId: - FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2); - FAIL_SIZE(it[1], sizeof(MSICallIDType)); - memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType)); - it += sizeof(MSICallIDType) + 2; - msg->callid.exists = 1; - break; - - case IDReason: - FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2); - FAIL_SIZE(it[1], sizeof(MSIReasonStrType)); - memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType)); - it += sizeof(MSIReasonStrType) + 2; - msg->reason.exists = 1; - break; - - case IDCSettings: - FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2); - FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType)); - memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType)); - it += sizeof(MSIRawCSettingsType) + 2; - msg->csettings.exists = 1; - break; - - default: - LOGGER_ERROR("Invalid id byte"); - return -1; - break; - } - } - - return 0; -} - -/** - * @brief Create the message. - * - * @param type Request or response. - * @param type_id Type of request/response. - * @return MSIMessage* Created message. - * @retval NULL Error occurred. - */ -MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) -{ - MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); - - if ( retu == NULL ) { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - return NULL; - } - - if ( type == TypeRequest ) { - retu->request.exists = 1; - retu->request.value = type_value; - - } else { - retu->response.exists = 1; - retu->response.value = type_value; - } - - return retu; -} - - -/** - * @brief Parse data from handle_packet. - * - * @param data The data. - * @return MSIMessage* Parsed message. - * @retval NULL Error occurred. - */ -MSIMessage *parse_recv ( const uint8_t *data, uint16_t length ) -{ - if ( data == NULL ) { - LOGGER_WARNING("Tried to parse empty message!"); - return NULL; - } - - MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); - - if ( retu == NULL ) { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - return NULL; - } - - if ( parse_raw_data ( retu, data, length ) == -1 ) { - - free ( retu ); - return NULL; - } - - return retu; -} - - -/** - * @brief Speaks for it self. - * - * @param dest Container. - * @param header_field Field. - * @param header_value Field value. - * @param value_len Length of field value. - * @param length Pointer to container length. - * @return uint8_t* Iterated container. - */ -uint8_t *format_output ( uint8_t *dest, MSIHeaderID id, const void *value, uint8_t value_len, uint16_t *length ) -{ - if ( dest == NULL ) { - LOGGER_ERROR("No destination space!"); - return NULL; - } - - if (value == NULL || value_len == 0) { - LOGGER_ERROR("Empty header value"); - return NULL; - } - - *dest = id; - dest ++; - *dest = value_len; - dest ++; - - memcpy(dest, value, value_len); - - *length += (2 + value_len); - - return dest + value_len; /* Set to next position ready to be written */ -} - - -/** - * @brief Parse MSIMessage to send. - * - * @param msg The message. - * @param dest Destination. - * @return uint16_t Its final size. - */ -uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) -{ - if (msg == NULL) { - LOGGER_ERROR("No message!"); - return 0; - } - - if (dest == NULL ) { - LOGGER_ERROR("No destination!"); - return 0; - } - - uint8_t *it = dest; - uint16_t size = 0; - - if (msg->request.exists) { - uint8_t cast = msg->request.value; - it = format_output(it, IDRequest, &cast, 1, &size); - } - - if (msg->response.exists) { - uint8_t cast = msg->response.value; - it = format_output(it, IDResponse, &cast, 1, &size); - } - - if (msg->callid.exists) { - it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); - } - - if (msg->reason.exists) { - it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size); - } - - if (msg->csettings.exists) { - it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size); - } - - *it = 0; - size ++; - - return size; -} - -void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value ) -{ - if ( !msg ) return; - - msg->reason.exists = 1; - memcpy(msg->reason.value, value, sizeof(MSIReasonStrType)); -} - -void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value ) -{ - if ( !msg ) return; - - msg->callid.exists = 1; - memcpy(msg->callid.value, value, sizeof(MSICallIDType)); -} - -void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value ) -{ - if ( !msg ) return; - - msg->csettings.exists = 1; - - msg->csettings.value[0] = value->call_type; - uint8_t *iter = msg->csettings.value + 1; - - /* Video bitrate */ - uint32_t lval = htonl(value->video_bitrate); - memcpy(iter, &lval, 4); - iter += 4; - - /* Video max width */ - uint16_t sval = htons(value->max_video_width); - memcpy(iter, &sval, 2); - iter += 2; - - /* Video max height */ - sval = htons(value->max_video_height); - memcpy(iter, &sval, 2); - iter += 2; - - /* Audio bitrate */ - lval = htonl(value->audio_bitrate); - memcpy(iter, &lval, 4); - iter += 4; - - /* Audio frame duration */ - sval = htons(value->audio_frame_duration); - memcpy(iter, &sval, 2); - iter += 2; - - /* Audio sample rate */ - lval = htonl(value->audio_sample_rate); - memcpy(iter, &lval, 4); - iter += 4; - - /* Audio channels */ - lval = htonl(value->audio_channels); - memcpy(iter, &lval, 4); -} - -void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest ) -{ - if ( !msg || !dest || !msg->csettings.exists ) return; - - dest->call_type = msg->csettings.value[0]; - uint8_t *iter = msg->csettings.value + 1; - - memcpy(&dest->video_bitrate, iter, 4); - iter += 4; - dest->video_bitrate = ntohl(dest->video_bitrate); - - memcpy(&dest->max_video_width, iter, 2); - iter += 2; - dest->max_video_width = ntohs(dest->max_video_width); - - memcpy(&dest->max_video_height, iter, 2); - iter += 2; - dest->max_video_height = ntohs(dest->max_video_height); - - memcpy(&dest->audio_bitrate, iter, 4); - iter += 4; - dest->audio_bitrate = ntohl(dest->audio_bitrate); - - memcpy(&dest->audio_frame_duration, iter, 2); - iter += 2; - dest->audio_frame_duration = ntohs(dest->audio_frame_duration); - - memcpy(&dest->audio_sample_rate, iter, 4); - iter += 4; - dest->audio_sample_rate = ntohl(dest->audio_sample_rate); - - memcpy(&dest->audio_channels, iter, 4); - dest->audio_channels = ntohl(dest->audio_channels); -} - -typedef struct _Timer { - void *(*func)(void *); - void *func_arg1; - int func_arg2; - uint64_t timeout; - int idx; - -} Timer; - -typedef struct _TimerHandler { - Timer **timers; - pthread_mutex_t mutex; - - uint32_t max_capacity; - uint32_t size; - uint64_t resolution; - - _Bool running; - -} TimerHandler; - -struct timer_function_args { - void *arg1; - int arg2; -}; - -/** - * @brief Allocate timer in array - * - * @param timers_container Handler - * @param func Function to be executed - * @param arg Its args - * @param timeout Timeout in ms - * @return int - */ -static int timer_alloc ( TimerHandler *timers_container, void *(func)(void *), void *arg1, int arg2, uint32_t timeout) -{ - static int timer_id; - pthread_mutex_lock(&timers_container->mutex); - - uint32_t i = 0; - - for (; i < timers_container->max_capacity && timers_container->timers[i]; i ++); - - if (i == timers_container->max_capacity) { - LOGGER_WARNING("Maximum capacity reached!"); - pthread_mutex_unlock(&timers_container->mutex); - return -1; - } - - Timer *timer = timers_container->timers[i] = calloc(sizeof(Timer), 1); - - if (timer == NULL) { - LOGGER_ERROR("Failed to allocate timer!"); - pthread_mutex_unlock(&timers_container->mutex); - return -1; - } - - timers_container->size ++; - - timer->func = func; - timer->func_arg1 = arg1; - timer->func_arg2 = arg2; - timer->timeout = timeout + current_time_monotonic(); /* In ms */ - ++timer_id; - timer->idx = timer_id; - - /* reorder */ - if (i) { - int64_t j = i - 1; - - for (; j >= 0 && timeout < timers_container->timers[j]->timeout; j--) { - Timer *tmp = timers_container->timers[j]; - timers_container->timers[j] = timer; - timers_container->timers[j + 1] = tmp; - } - } - - pthread_mutex_unlock(&timers_container->mutex); - - LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timers_container->size); - return timer->idx; -} - -/** - * @brief Remove timer from array - * - * @param timers_container handler - * @param idx timer id - * @param lock_mutex (does the mutex need to be locked) - * @return int - */ -static int timer_release ( TimerHandler *timers_container, int idx , int lock_mutex) -{ - if (lock_mutex) - pthread_mutex_lock(&timers_container->mutex); - - Timer **timed_events = timers_container->timers; - - size_t i; - int rc = -1; - - for (i = 0; i < timers_container->max_capacity; ++i) { - if (timed_events[i] && timed_events[i]->idx == idx) { - rc = i; - break; - } - } - - if (rc == -1) { - LOGGER_WARNING("No event with id: %d", idx); - - if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); - - return -1; - } - - free(timed_events[rc]); - - timed_events[rc] = NULL; - - i = rc + 1; - - for (; i < timers_container->max_capacity && timed_events[i]; i ++) { - timed_events[i - 1] = timed_events[i]; - timed_events[i] = NULL; - } - - timers_container->size--; - - LOGGER_DEBUG("Popped id: %d, current size: %ull ", idx, timers_container->size); - - if (lock_mutex) pthread_mutex_unlock(&timers_container->mutex); - - return 0; -} - -/** - * @brief Main poll for timer execution - * - * @param arg ... - * @return void* - */ -static void *timer_poll( void *arg ) -{ - TimerHandler *handler = arg; - - while ( handler->running ) { - - pthread_mutex_lock(&handler->mutex); - - if ( handler->running ) { - - uint64_t time = current_time_monotonic(); - - while ( handler->timers[0] && handler->timers[0]->timeout < time ) { - pthread_t tid; - - struct timer_function_args *args = malloc(sizeof(struct timer_function_args)); - args->arg1 = handler->timers[0]->func_arg1; - args->arg2 = handler->timers[0]->func_arg2; - - if ( 0 != pthread_create(&tid, NULL, handler->timers[0]->func, args) || - 0 != pthread_detach(tid) ) { - LOGGER_ERROR("Failed to execute timer at: %d!", handler->timers[0]->timeout); - free(args); - } else { - LOGGER_DEBUG("Exectued timer assigned at: %d", handler->timers[0]->timeout); - } - - timer_release(handler, handler->timers[0]->idx, 0); - } - - } - - pthread_mutex_unlock(&handler->mutex); - - usleep(handler->resolution); - } - - pthread_exit(NULL); -} - -/** - * @brief Start timer poll and return handler - * - * @param max_capacity capacity - * @param resolution ... - * @return TimerHandler* - */ -static TimerHandler *timer_init_session (int max_capacity, int resolution) -{ - TimerHandler *handler = calloc(1, sizeof(TimerHandler)); - - if (handler == NULL) { - LOGGER_ERROR("Failed to allocate memory, program might misbehave!"); - return NULL; - } - - handler->timers = calloc(max_capacity, sizeof(Timer *)); - - if (handler->timers == NULL) { - LOGGER_ERROR("Failed to allocate %d timed events!", max_capacity); - free(handler); - return NULL; - } - - handler->max_capacity = max_capacity; - handler->running = 1; - handler->resolution = resolution; - - pthread_mutex_init(&handler->mutex, NULL); - - - pthread_t _tid; - - if ( 0 != pthread_create(&_tid, NULL, timer_poll, handler) || 0 != pthread_detach(_tid) ) { - LOGGER_ERROR("Failed to start timer poll thread!"); - free(handler->timers); - free(handler); - return NULL; - } - - return handler; -} - -/** - * @brief Terminate timer session - * - * @param handler The timer handler - * @return void - */ -static void timer_terminate_session(TimerHandler *handler) -{ - pthread_mutex_lock(&handler->mutex); - - handler->running = 0; - - pthread_mutex_unlock(&handler->mutex); - - size_t i = 0; - - for (; i < handler->max_capacity; i ++) - free(handler->timers[i]); - - free(handler->timers); - - pthread_mutex_destroy( &handler->mutex ); -} - -/** - * @brief Generate _random_ alphanumerical string. - * - * @param str Destination. - * @param size Size of string. - * @return void - */ -static void t_randomstr ( uint8_t *str, uint32_t size ) -{ - if (str == NULL) { - LOGGER_DEBUG("Empty destination!"); - return; - } - - static const uint8_t _bytes[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - uint32_t _it = 0; - - for ( ; _it < size; _it++ ) { - str[_it] = _bytes[ random_int() % 61 ]; - } -} - - -typedef enum { - error_none, - error_deadcall, /* has call id but it's from old call */ - error_id_mismatch, /* non-existing call */ - - error_no_callid, /* not having call id */ - error_no_call, /* no call in session */ - error_no_crypto_key, /* no crypto key */ - - error_busy - -} MSICallError; /* Error codes */ - - -/** - * @brief Stringify error code. - * - * @param error_code The code. - * @return const uint8_t* The string. - */ -static inline__ const uint8_t *stringify_error ( MSICallError error_code ) -{ - static const uint8_t *strings[] = { - ( uint8_t *) "", - ( uint8_t *) "Using dead call", - ( uint8_t *) "Call id not set to any call", - ( uint8_t *) "Call id not available", - ( uint8_t *) "No active call in session", - ( uint8_t *) "No Crypto-key set", - ( uint8_t *) "Callee busy" - }; - - return strings[error_code]; -} - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param msg The message. - * @param to Where to. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) -{ - msi_msg_set_callid ( msg, call->id ); - - uint8_t msg_string_final [MSI_MAXMSG_SIZE]; - uint16_t length = parse_send ( msg, msg_string_final ); - - if (!length) { - LOGGER_WARNING("Parsing message failed; nothing sent!"); - return -1; - } - - if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { - LOGGER_DEBUG("Sent message"); - return 0; - } - - return -1; -} - -inline__ int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to ) -{ - MSIMessage *msg = msi_new_message ( TypeResponse, response ); - int ret = send_message ( session, call, msg, to ); - free ( msg ); - return ret; -} - -/** - * @brief Determine 'bigger' call id - * - * @param first duh - * @param second duh - * @return int - * @retval 0 it's first - * @retval 1 it's second - */ -static int call_id_bigger( const uint8_t *first, const uint8_t *second) -{ - return (memcmp(first, second, sizeof(MSICallIDType)) < 0); -} - - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param msg The message. - * @param peer_id The peer. - * @return -1, 0 - */ -static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) -{ - if ( msg->csettings.exists ) { - msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]); - - LOGGER_DEBUG("Peer: %d \n" - "Type: %u \n" - "Video bitrate: %u \n" - "Video height: %u \n" - "Video width: %u \n" - "Audio bitrate: %u \n" - "Audio framedur: %u \n" - "Audio sample rate: %u \n" - "Audio channels: %u \n", peer_id, - call->csettings_peer[peer_id].call_type, - call->csettings_peer[peer_id].video_bitrate, - call->csettings_peer[peer_id].max_video_height, - call->csettings_peer[peer_id].max_video_width, - call->csettings_peer[peer_id].audio_bitrate, - call->csettings_peer[peer_id].audio_frame_duration, - call->csettings_peer[peer_id].audio_sample_rate, - call->csettings_peer[peer_id].audio_channels ); - - return 0; - } - - LOGGER_WARNING("No csettings header!"); - return -1; -} - -static int terminate_call ( MSISession *session, MSICall *call ); - -static void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p) -{ - (void)messenger; - MSISession *session = session_p; - - switch ( status ) { - case 0: { /* Went offline */ - int32_t j = 0; - - for ( ; j < session->max_calls; j ++ ) { - - if ( !session->calls[j] ) continue; - - uint16_t i = 0; - - for ( ; i < session->calls[j]->peer_count; i ++ ) - if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) { - invoke_callback(session, j, MSI_OnPeerTimeout); - terminate_call(session, session->calls[j]); - LOGGER_DEBUG("Remote: %d timed out!", friend_num); - return; /* TODO: On group calls change behaviour */ - } - } - } - break; - - default: - break; - } -} - -static MSICall *find_call ( MSISession *session, uint8_t *call_id ) -{ - if ( call_id == NULL ) return NULL; - - int32_t i = 0; - - for (; i < session->max_calls; i ++ ) - if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) { - return session->calls[i]; - } - - return NULL; -} - -/** - * @brief Sends error response to peer. - * - * @param session The session. - * @param errid The id. - * @param to Where to? - * @return int - * @retval -1/0 It's usually always success. - */ -static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to ) -{ - if (!call) { - LOGGER_WARNING("Cannot handle error on 'null' call"); - return -1; - } - - LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); - - MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); - - msi_msg_set_reason ( msg_error, stringify_error(errid) ); - send_message ( session, call, msg_error, to ); - free ( msg_error ); - - return 0; -} - - - -/** - * @brief Add peer to peer list. - * - * @param call What call. - * @param peer_id Its id. - * @return void - */ -static void add_peer( MSICall *call, int peer_id ) -{ - uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : - realloc( call->peers, sizeof(uint32_t) * call->peer_count); - - if (!peers) { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - return; - } - - call->peer_count ++; - call->peers = peers; - call->peers[call->peer_count - 1] = peer_id; - - LOGGER_DEBUG("Added peer: %d", peer_id); -} - - -/** - * @brief Speaks for it self. - * - * @param session Control session. - * @param peers Amount of peers. (Currently it only supports 1) - * @param ringing_timeout Ringing timeout. - * @return MSICall* The created call. - */ -static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout ) -{ - - if (peers == 0) { - LOGGER_ERROR("No peers!"); - return NULL; - } - - int32_t call_idx = 0; - - for (; call_idx < session->max_calls; call_idx ++) { - if ( !session->calls[call_idx] ) { - - if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - return NULL; - } - - break; - } - } - - if ( call_idx == session->max_calls ) { - LOGGER_WARNING("Reached maximum amount of calls!"); - return NULL; - } - - - MSICall *call = session->calls[call_idx]; - - call->call_idx = call_idx; - - if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - free(call); - return NULL; - } - - call->session = session; - - call->request_timer_id = 0; - call->ringing_timer_id = 0; - - call->ringing_tout_ms = ringing_timeout; - - pthread_mutex_init ( &call->mutex, NULL ); - - LOGGER_DEBUG("Started new call with index: %u", call_idx); - return call; -} - - -/** - * @brief Terminate the call. - * - * @param session Control session. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -static int terminate_call ( MSISession *session, MSICall *call ) -{ - if ( !call ) { - LOGGER_WARNING("Tried to terminate non-existing call!"); - return -1; - } - - LOGGER_DEBUG("Terminated call id: %d", call->call_idx); - /* Check event loop and cancel timed events if there are any - * NOTE: This has to be done before possibly - * locking the mutex the second time - */ - timer_release ( session->timer_handler, call->request_timer_id, 1); - timer_release ( session->timer_handler, call->ringing_timer_id, 1); - - /* Get a handle */ - pthread_mutex_lock ( &call->mutex ); - - session->calls[call->call_idx] = NULL; - - free ( call->csettings_peer ); - free ( call->peers); - - /* Release handle */ - pthread_mutex_unlock ( &call->mutex ); - - pthread_mutex_destroy ( &call->mutex ); - - free ( call ); - - return 0; -} - - -/** - * @brief Function called at request timeout. If not called in thread it might cause trouble - * - * @param arg Control session - * @return void* - */ -static void *handle_timeout ( void *arg ) -{ - /* TODO: Cancel might not arrive there; set up - * timers on these cancels and terminate call on - * their timeout - */ - struct timer_function_args *args = arg; - int call_index = args->arg2; - MSISession *session = args->arg1; - MSICall *call = session->calls[call_index]; - - if (call) { - LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); - - invoke_callback(session, call_index, MSI_OnRequestTimeout); - } - - if ( call && call->session ) { - - /* TODO: Cancel all? */ - /* uint16_t _it = 0; - * for ( ; _it < _session->call->peer_count; _it++ ) */ - msi_cancel ( call->session, call->call_idx, call->peers [0], "Request timed out" ); - /*terminate_call(call->session, call);*/ - } - - free(arg); - pthread_exit(NULL); -} - - -/********** Request handlers **********/ -static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); - - pthread_mutex_lock(&session->mutex); - - if (!msg->csettings.exists) {/**/ - LOGGER_WARNING("Peer sent invalid codec settings!"); - send_error ( session, call, error_no_callid, msg->friend_id ); - pthread_mutex_unlock(&session->mutex); - return 0; - } - - if ( call ) { - if ( call->peers[0] == (uint32_t)msg->friend_id ) { - if (call->state == call_inviting) { - /* The glare case. A calls B when at the same time - * B calls A. Who has advantage is set bey calculating - * 'bigger' Call id and then that call id is being used in - * future. User with 'bigger' Call id has the advantage - * as in he will wait the response from the other. - */ - LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]); - - if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */ - - /* Terminate call; peer will timeout(call) if call initialization fails */ - terminate_call(session, call); - - call = init_call ( session, 1, 0 ); - - if ( !call ) { - pthread_mutex_unlock(&session->mutex); - LOGGER_ERROR("Starting call"); - return 0; - } - - } else { - pthread_mutex_unlock(&session->mutex); - return 0; /* Wait for ringing from peer */ - } - } else if (call->state == call_active) { - /* Request for media change; call callback and send starting response */ - if (flush_peer_csettings(call, msg, 0) != 0) { /**/ - LOGGER_WARNING("Peer sent invalid csetting!"); - send_error ( session, call, error_no_callid, msg->friend_id ); - pthread_mutex_unlock(&session->mutex); - return 0; - } - - LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == type_audio ? "audio" : "video"); - send_reponse(session, call, starting, msg->friend_id); - pthread_mutex_unlock(&session->mutex); - invoke_callback(session, call->call_idx, MSI_OnMediaChange); - return 1; - } - } else { - send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ - terminate_call(session, call); - pthread_mutex_unlock(&session->mutex); - return 0; - } - } else { - call = init_call ( session, 1, 0 ); - - if ( !call ) { - pthread_mutex_unlock(&session->mutex); - LOGGER_ERROR("Starting call"); - return 0; - } - } - - if ( !msg->callid.exists ) { - send_error ( session, call, error_no_callid, msg->friend_id ); - terminate_call(session, call); - pthread_mutex_unlock(&session->mutex); - return 0; - } - - memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); - call->state = call_starting; - - add_peer( call, msg->friend_id); - - flush_peer_csettings ( call, msg, 0 ); - - send_reponse(session, call, ringing, msg->friend_id); - - pthread_mutex_unlock(&session->mutex); - - invoke_callback(session, call->call_idx, MSI_OnInvite); - - return 1; -} - -static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - (void)msg; - - LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); - - pthread_mutex_lock(&session->mutex); - - call->state = call_active; - - pthread_mutex_unlock(&session->mutex); - - invoke_callback(session, call->call_idx, MSI_OnStart); - return 1; -} - -static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); - - invoke_callback(session, call->call_idx, MSI_OnReject); - - pthread_mutex_lock(&session->mutex); - - send_reponse(session, call, ending, msg->friend_id); - terminate_call(session, call); - - pthread_mutex_unlock(&session->mutex); - - return 1; -} - -static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - (void)msg; - - LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); - - invoke_callback(session, call->call_idx, MSI_OnCancel); - - pthread_mutex_lock(&session->mutex); - - terminate_call ( session, call ); - - pthread_mutex_unlock(&session->mutex); - - return 1; -} - -static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); - - invoke_callback(session, call->call_idx, MSI_OnEnd); - pthread_mutex_lock(&session->mutex); - - send_reponse(session, call, ending, msg->friend_id); - terminate_call ( session, call ); - - pthread_mutex_unlock(&session->mutex); - - - return 1; -} - -/********** Response handlers **********/ -static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - (void)msg; - - pthread_mutex_lock(&session->mutex); - - if ( call->ringing_timer_id ) { - LOGGER_WARNING("Call already ringing"); - pthread_mutex_unlock(&session->mutex); - return 0; - } - - LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx ); - - call->ringing_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, - call->ringing_tout_ms ); - - pthread_mutex_unlock(&session->mutex); - - invoke_callback(session, call->call_idx, MSI_OnRinging); - return 1; -} -static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call"); - return 0; - } - - pthread_mutex_lock(&session->mutex); - - if ( call->state == call_active ) { /* Change media */ - - LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); - pthread_mutex_unlock(&session->mutex); - - invoke_callback(session, call->call_idx, MSI_OnMediaChange); - - } else if ( call->state == call_inviting ) { - LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); - - call->state = call_active; - - MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); - send_message ( session, call, msg_start, msg->friend_id ); - free ( msg_start ); - - - flush_peer_csettings ( call, msg, 0 ); - - /* This is here in case of glare */ - timer_release ( session->timer_handler, call->ringing_timer_id, 1 ); - - pthread_mutex_unlock(&session->mutex); - - invoke_callback(session, call->call_idx, MSI_OnStarting); - } else { - LOGGER_ERROR("Invalid call state"); - terminate_call(session, call ); - pthread_mutex_unlock(&session->mutex); - return 0; - } - - return 1; -} -static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - if ( !call ) { - LOGGER_WARNING("Session: %p Handling 'start' on no call"); - return 0; - } - - (void)msg; - - LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); - - invoke_callback(session, call->call_idx, MSI_OnEnding); - - /* Terminate call */ - pthread_mutex_lock(&session->mutex); - terminate_call ( session, call ); - pthread_mutex_unlock(&session->mutex); - - return 1; -} -static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) -{ - - if ( !call ) { - LOGGER_WARNING("Handling 'error' on non-existing call!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); - - invoke_callback(session, call->call_idx, MSI_OnEnding); - - pthread_mutex_lock(&session->mutex); - - /* Handle error accordingly */ - if ( msg->reason.exists ) { - /* TODO */ - } - - terminate_call ( session, call ); - - pthread_mutex_unlock(&session->mutex); - - return 1; -} - - -/** - * @brief BASIC call flow: - * - * ALICE BOB - * | invite --> | - * | | - * | <-- ringing | - * | | - * | <-- starting | - * | | - * | start --> | - * | | - * | <-- MEDIA TRANS --> | - * | | - * | end --> | - * | | - * | <-- ending | - * - * Alice calls Bob by sending invite packet. - * Bob recvs the packet and sends an ringing packet; - * which notifies Alice that her invite is acknowledged. - * Ringing screen shown on both sides. - * Bob accepts the invite for a call by sending starting packet. - * Alice recvs the starting packet and sends the started packet to - * inform Bob that she recved the starting packet. - * Now the media transmission is established ( i.e. RTP transmission ). - * Alice hangs up and sends end packet. - * Bob recves the end packet and sends ending packet - * as the acknowledgement that the call is ending. - * - * - */ -static void msi_handle_packet ( Messenger *messenger, int source, const uint8_t *data, uint16_t length, void *object ) -{ - LOGGER_DEBUG("Got msi message"); - /* Unused */ - (void)messenger; - - MSISession *session = object; - MSIMessage *msg; - - if ( !length ) { - LOGGER_WARNING("Lenght param negative"); - return; - } - - msg = parse_recv ( data, length ); - - if ( !msg ) { - LOGGER_WARNING("Error parsing message"); - return; - } else { - LOGGER_DEBUG("Successfully parsed message"); - } - - msg->friend_id = source; - - - /* Find what call */ - MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; - - /* Now handle message */ - - if ( msg->request.exists ) { /* Handle request */ - - switch (msg->request.value) { - case invite: - handle_recv_invite ( session, call, msg ); - break; - - case start: - handle_recv_start ( session, call, msg ); - break; - - case cancel: - handle_recv_cancel ( session, call, msg ); - break; - - case reject: - handle_recv_reject ( session, call, msg ); - break; - - case end: - handle_recv_end ( session, call, msg ); - break; - } - - } else if ( msg->response.exists ) { /* Handle response */ - - /* Got response so cancel timer */ - if ( call ) timer_release ( session->timer_handler, call->request_timer_id, 1 ); - - switch (msg->response.value) { - case ringing: - handle_recv_ringing ( session, call, msg ); - break; - - case starting: - handle_recv_starting ( session, call, msg ); - break; - - case ending: - handle_recv_ending ( session, call, msg ); - break; - - case error: - handle_recv_error ( session, call, msg ); - break; - } - - } else { - LOGGER_WARNING("Invalid message: no resp nor requ headers"); - } - - free ( msg ); -} - - -/** - * @brief Callback setter. - * - * @param callback The callback. - * @param id The id. - * @return void - */ -void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata ) -{ - session->callbacks[id].function = callback; - session->callbacks[id].data = userdata; -} - - -/** - * @brief Start the control session. - * - * @param messenger Tox* object. - * @param max_calls Amount of calls possible - * @return MSISession* The created session. - * @retval NULL Error occurred. - */ -MSISession *msi_init_session ( Messenger *messenger, int32_t max_calls ) -{ - if (messenger == NULL) { - LOGGER_ERROR("Could not init session on empty messenger!"); - return NULL; - } - - TimerHandler *handler = timer_init_session(max_calls * 10, 10000); - - if ( !max_calls || !handler ) { - LOGGER_WARNING("Invalid max call treshold or timer handler initialization failed!"); - return NULL; - } - - MSISession *retu = calloc ( sizeof ( MSISession ), 1 ); - - if (retu == NULL) { - LOGGER_ERROR("Allocation failed! Program might misbehave!"); - return NULL; - } - - retu->messenger_handle = messenger; - retu->agent_handler = NULL; - retu->timer_handler = handler; - - if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) { - LOGGER_ERROR("Allocation failed! Program might misbehave!"); - free(retu); - return NULL; - } - - retu->max_calls = max_calls; - - retu->frequ = 10000; /* default value? */ - retu->call_timeout = 30000; /* default value? */ - - - m_callback_msi_packet(messenger, msi_handle_packet, retu ); - - /* This is called when remote terminates session */ - m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); - - pthread_mutex_init(&retu->mutex, NULL); - - LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls); - return retu; -} - - -/** - * @brief Terminate control session. - * - * @param session The session - * @return int - */ -int msi_terminate_session ( MSISession *session ) -{ - if (session == NULL) { - LOGGER_ERROR("Tried to terminate non-existing session"); - return -1; - } - - pthread_mutex_lock(&session->mutex); - m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); - pthread_mutex_unlock(&session->mutex); - - int _status = 0; - - /* If have calls, cancel them */ - int32_t idx = 0; - - for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) { - /* Cancel all? */ - uint16_t _it = 0; - /*for ( ; _it < session->calls[idx]->peer_count; _it++ ) - * FIXME: will not work on multiple peers, must cancel call for all peers - */ - msi_cancel ( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" ); - } - - timer_terminate_session(session->timer_handler); - - pthread_mutex_destroy(&session->mutex); - - LOGGER_DEBUG("Terminated session: %p", session); - free ( session ); - return _status; -} - - -/** - * @brief Send invite request to friend_id. - * - * @param session Control session. - * @param call_type Type of the call. Audio or Video(both audio and video) - * @param rngsec Ringing timeout. - * @param friend_id The friend. - * @return int - */ -int msi_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec, uint32_t friend_id ) -{ - pthread_mutex_lock(&session->mutex); - - LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); - - - int i = 0; - - for (; i < session->max_calls; i ++) - if (session->calls[i] && session->calls[i]->peers[0] == friend_id) { - LOGGER_ERROR("Already in a call with friend %d", friend_id); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - - MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ - - if ( !call ) { - pthread_mutex_unlock(&session->mutex); - LOGGER_ERROR("Cannot handle more calls"); - return -1; - } - - *call_index = call->call_idx; - - t_randomstr ( call->id, sizeof(call->id) ); - - add_peer ( call, friend_id ); - - call->csettings_local = csettings; - - MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); - - msi_msg_set_csettings(msg_invite, &csettings); - send_message ( session, call, msg_invite, friend_id ); - free( msg_invite ); - - call->state = call_inviting; - - call->request_timer_id = timer_alloc ( session->timer_handler, handle_timeout, session, call->call_idx, m_deftout ); - - LOGGER_DEBUG("Invite sent"); - - pthread_mutex_unlock(&session->mutex); - - return 0; -} - - -/** - * @brief Hangup active call. - * - * @param session Control session. - * @param call_id To which call is this action handled. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int msi_hangup ( MSISession *session, int32_t call_index ) -{ - pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - LOGGER_ERROR("Invalid call index!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - if ( session->calls[call_index]->state != call_active ) { - LOGGER_ERROR("No call with such index or call is not active!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); - - /* hangup for each peer */ - int it = 0; - - for ( ; it < session->calls[call_index]->peer_count; it ++ ) - send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] ); - - session->calls[call_index]->state = call_hanged_up; - - free ( msg_end ); - - session->calls[call_index]->request_timer_id = - timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); - - pthread_mutex_unlock(&session->mutex); - return 0; -} - - -/** - * @brief Answer active call request. - * - * @param session Control session. - * @param call_id To which call is this action handled. - * @param call_type Answer with Audio or Video(both). - * @return int - */ -int msi_answer ( MSISession *session, int32_t call_index, MSICSettings csettings ) -{ - pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - LOGGER_ERROR("Invalid call index!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); - - session->calls[call_index]->csettings_local = csettings; - - msi_msg_set_csettings(msg_starting, &csettings); - - send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); - free ( msg_starting ); - - session->calls[call_index]->state = call_active; - - pthread_mutex_unlock(&session->mutex); - return 0; -} - - -/** - * @brief Cancel request. - * - * @param session Control session. - * @param call_id To which call is this action handled. - * @param reason Set optional reason header. Pass NULL if none. - * @return int - */ -int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) -{ - pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - LOGGER_ERROR("Invalid call index!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); - - /* FIXME */ -#if 0 - - if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { - MSIReasonStrType reason_cast; - memset(reason_cast, '\0', sizeof(MSIReasonStrType)); - memcpy(reason_cast, reason, strlen(reason)); - msi_msg_set_reason(msg_cancel, reason_cast); - } - -#else - (void)reason; - -#endif - - send_message ( session, session->calls[call_index], msg_cancel, peer ); - free ( msg_cancel ); - - terminate_call ( session, session->calls[call_index] ); - pthread_mutex_unlock(&session->mutex); - - return 0; -} - - -/** - * @brief Reject request. - * - * @param session Control session. - * @param call_id To which call is this action handled. - * @return int - */ -int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) -{ - pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - LOGGER_ERROR("Invalid call index!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject ); - - /* FIXME */ -#if 0 - - if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { - MSIReasonStrType reason_cast; - memset(reason_cast, '\0', sizeof(MSIReasonStrType)); - memcpy(reason_cast, reason, strlen(reason)); - msi_msg_set_reason(msg_reject, reason_cast); - } - -#else - (void)reason; - -#endif - - send_message ( session, session->calls[call_index], msg_reject, - session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); - free ( msg_reject ); - - session->calls[call_index]->state = call_hanged_up; - session->calls[call_index]->request_timer_id = - timer_alloc ( session->timer_handler, handle_timeout, session, call_index, m_deftout ); - - pthread_mutex_unlock(&session->mutex); - return 0; -} - - -/** - * @brief Send invite request to friend_id. - * - * @param session Control session. - * @param call_index Call index. - * @param call_type Type of the call. Audio or Video(both audio and video) - * @param rngsec Ringing timeout. - * @param friend_id The friend. - * @return int - */ -int msi_change_csettings(MSISession *session, int32_t call_index, MSICSettings csettings) -{ - pthread_mutex_lock(&session->mutex); - - LOGGER_DEBUG("Changing media on call: %d", call_index); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - LOGGER_ERROR("Invalid call index!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSICall *call = session->calls[call_index]; - - if ( call->state != call_active ) { - LOGGER_ERROR("Call is not active!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - MSICSettings *local = &call->csettings_local; - - if ( - local->call_type == csettings.call_type && - local->video_bitrate == csettings.video_bitrate && - local->max_video_width == csettings.max_video_width && - local->max_video_height == csettings.max_video_height && - local->audio_bitrate == csettings.audio_bitrate && - local->audio_frame_duration == csettings.audio_frame_duration && - local->audio_sample_rate == csettings.audio_sample_rate && - local->audio_channels == csettings.audio_channels ) { - LOGGER_ERROR("Call is already set accordingly!"); - pthread_mutex_unlock(&session->mutex); - return -1; - } - - *local = csettings; - - MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); - - msi_msg_set_csettings ( msg_invite, local ); - send_message ( session, call, msg_invite, call->peers[0] ); - free ( msg_invite ); - - LOGGER_DEBUG("Request for media change sent"); - - pthread_mutex_unlock(&session->mutex); - - return 0; -} - - -/** - * @brief Terminate the current call. - * - * @param session Control session. - * @param call_id To which call is this action handled. - * @return int - */ -int msi_stopcall ( MSISession *session, int32_t call_index ) -{ - pthread_mutex_lock(&session->mutex); - LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); - - if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { - pthread_mutex_unlock(&session->mutex); - return -1; - } - - /* just terminate it */ - - terminate_call ( session, session->calls[call_index] ); - - pthread_mutex_unlock(&session->mutex); - return 0; -} |