diff options
Diffstat (limited to 'protocols/Tox/toxcore/toxav')
-rw-r--r-- | protocols/Tox/toxcore/toxav/Makefile.inc | 36 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/codec.c | 357 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/codec.h | 116 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/msi.c | 1947 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/msi.h | 267 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/rtp.c | 600 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/rtp.h | 196 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/toxav.c | 1148 | ||||
-rw-r--r-- | protocols/Tox/toxcore/toxav/toxav.h | 389 |
9 files changed, 0 insertions, 5056 deletions
diff --git a/protocols/Tox/toxcore/toxav/Makefile.inc b/protocols/Tox/toxcore/toxav/Makefile.inc deleted file mode 100644 index de8ef8ff38..0000000000 --- a/protocols/Tox/toxcore/toxav/Makefile.inc +++ /dev/null @@ -1,36 +0,0 @@ -if BUILD_AV - -lib_LTLIBRARIES += libtoxav.la -libtoxav_la_include_HEADERS = ../toxav/toxav.h -libtoxav_la_includedir = $(includedir)/tox - -libtoxav_la_SOURCES = ../toxav/rtp.h \ - ../toxav/rtp.c \ - ../toxav/msi.h \ - ../toxav/msi.c \ - ../toxav/codec.h \ - ../toxav/codec.c \ - ../toxav/toxav.h \ - ../toxav/toxav.c - - -libtoxav_la_CFLAGS = -I../toxcore \ - -I../toxav \ - $(LIBSODIUM_CFLAGS) \ - $(NACL_CFLAGS) \ - $(AV_CFLAGS) \ - $(PTHREAD_CFLAGS) - -libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ - $(LIBSODIUM_LDFLAGS) \ - $(NACL_LDFLAGS) \ - $(EXTRA_LT_LDFLAGS) \ - $(WINSOCK2_LIBS) - -libtoxav_la_LIBADD = libtoxcore.la \ - $(LIBSODIUM_LIBS) \ - $(NACL_LIBS) \ - $(PTHREAD_LIBS) \ - $(AV_LIBS) - -endif
\ No newline at end of file diff --git a/protocols/Tox/toxcore/toxav/codec.c b/protocols/Tox/toxcore/toxav/codec.c deleted file mode 100644 index 10dc4f53ff..0000000000 --- a/protocols/Tox/toxcore/toxav/codec.c +++ /dev/null @@ -1,357 +0,0 @@ -/** codec.c - * - * Audio and video codec intitialization, encoding/decoding and playback - * - * 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 <stdio.h> -#include <stdlib.h> -#include <math.h> -#include <assert.h> - -#include "rtp.h" -#include "codec.h" - -JitterBuffer *create_queue(unsigned int capacity) -{ - unsigned int size = 1; - - while (size <= capacity) { - size *= 2; - } - - JitterBuffer *q; - - if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL; - - if (!(q->queue = calloc(sizeof(RTPMessage *), size))) { - free(q); - return NULL; - } - - q->size = size; - q->capacity = capacity; - return q; -} - -static void clear_queue(JitterBuffer *q) -{ - for (; q->bottom != q->top; ++q->bottom) { - if (q->queue[q->bottom % q->size]) { - rtp_free_msg(NULL, q->queue[q->bottom % q->size]); - q->queue[q->bottom % q->size] = NULL; - } - } -} - -void terminate_queue(JitterBuffer *q) -{ - if (!q) return; - - clear_queue(q); - free(q->queue); - free(q); -} - -void queue(JitterBuffer *q, RTPMessage *pk) -{ - uint16_t sequnum = pk->header->sequnum; - - unsigned int num = sequnum % q->size; - - if ((uint32_t)(sequnum - q->bottom) > q->size) { - clear_queue(q); - q->bottom = sequnum; - q->queue[num] = pk; - q->top = sequnum + 1; - return; - } - - if (q->queue[num]) - return; - - q->queue[num] = pk; - - if ((sequnum - q->bottom) >= (q->top - q->bottom)) - q->top = sequnum + 1; -} - -/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ -RTPMessage *dequeue(JitterBuffer *q, int *success) -{ - if (q->top == q->bottom) { - *success = 0; - return NULL; - } - - unsigned int num = q->bottom % q->size; - - if (q->queue[num]) { - RTPMessage *ret = q->queue[num]; - q->queue[num] = NULL; - ++q->bottom; - *success = 1; - return ret; - } - - if ((uint32_t)(q->top - q->bottom) > q->capacity) { - ++q->bottom; - *success = 2; - return NULL; - } - - *success = 0; - return NULL; -} - - -int init_video_decoder(CodecState *cs) -{ - int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - return 0; -} - -int init_audio_decoder(CodecState *cs, uint32_t audio_channels) -{ - int rc; - cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); - - if ( rc != OPUS_OK ) { - LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); - return -1; - } - - cs->audio_decoder_channels = audio_channels; - return 0; -} - -int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height) -{ - vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; - - if (cfg.g_w == width && cfg.g_h == height) - return 0; - - if (width * height > cs->max_width * cs->max_height) - return -1; - - LOGGER_DEBUG("New video resolution: %u %u", width, height); - cfg.g_w = width; - cfg.g_h = height; - int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - return 0; -} - -int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate) -{ - vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; - - if (cfg.rc_target_bitrate == video_bitrate) - return 0; - - LOGGER_DEBUG("New video bitrate: %u", video_bitrate); - cfg.rc_target_bitrate = video_bitrate; - int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - return 0; -} - -int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) -{ - vpx_codec_enc_cfg_t cfg; - int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); - - if (rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - cfg.rc_target_bitrate = video_bitrate; - cfg.g_w = max_width; - cfg.g_h = max_height; - cfg.g_pass = VPX_RC_ONE_PASS; - cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; - cfg.g_lag_in_frames = 0; - cfg.kf_min_dist = 0; - cfg.kf_max_dist = 300; - cfg.kf_mode = VPX_KF_AUTO; - - cs->max_width = max_width; - cs->max_height = max_height; - cs->bitrate = video_bitrate; - - rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 7); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); - return -1; - } - - return 0; -} - -int init_audio_encoder(CodecState *cs, uint32_t audio_channels) -{ - int rc = OPUS_OK; - cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc); - - if ( rc != OPUS_OK ) { - LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); - return -1; - } - - rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); - - if ( rc != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); - return -1; - } - - rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); - - if ( rc != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); - return -1; - } - - cs->audio_encoder_channels = audio_channels; - return 0; -} - - -CodecState *codec_init_session ( uint32_t audio_bitrate, - uint16_t audio_frame_duration, - uint32_t audio_sample_rate, - uint32_t encoder_audio_channels, - uint32_t decoder_audio_channels, - uint32_t audio_VAD_tolerance_ms, - uint16_t max_video_width, - uint16_t max_video_height, - uint32_t video_bitrate ) -{ - CodecState *retu = calloc(sizeof(CodecState), 1); - - if (!retu) return NULL; - - retu->audio_bitrate = audio_bitrate; - retu->audio_sample_rate = audio_sample_rate; - - /* Encoders */ - if (!max_video_width || !max_video_height) { /* Disable video */ - /*video_width = 320; - video_height = 240; */ - } else { - retu->capabilities |= ( 0 == init_video_encoder(retu, max_video_width, max_video_height, - video_bitrate) ) ? v_encoding : 0; - retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0; - } - - retu->capabilities |= ( 0 == init_audio_encoder(retu, encoder_audio_channels) ) ? a_encoding : 0; - retu->capabilities |= ( 0 == init_audio_decoder(retu, decoder_audio_channels) ) ? a_decoding : 0; - - if ( retu->capabilities == 0 ) { /* everything failed */ - free (retu); - return NULL; - } - - - retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ? - audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration; - - return retu; -} - -void codec_terminate_session ( CodecState *cs ) -{ - if (!cs) return; - - if ( cs->audio_encoder ) - opus_encoder_destroy(cs->audio_encoder); - - if ( cs->audio_decoder ) - opus_decoder_destroy(cs->audio_decoder); - - if ( cs->capabilities & v_decoding ) - vpx_codec_destroy(&cs->v_decoder); - - if ( cs->capabilities & v_encoding ) - vpx_codec_destroy(&cs->v_encoder); - - LOGGER_DEBUG("Terminated codec state: %p", cs); - free(cs); -} - -static float calculate_sum_sq (int16_t *n, uint16_t k) -{ - float result = 0; - uint16_t i = 0; - - for ( ; i < k; i ++) result += (float) (n[i] * n[i]); - - return result; -} - -int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) -{ - float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; - - if ( frame_energy > energy) { - cs->EVAD_tolerance_cr = cs->EVAD_tolerance; /* Reset counter */ - return 1; - } - - if ( cs->EVAD_tolerance_cr ) { - cs->EVAD_tolerance_cr --; - return 1; - } - - return 0; -} diff --git a/protocols/Tox/toxcore/toxav/codec.h b/protocols/Tox/toxcore/toxav/codec.h deleted file mode 100644 index db4fbea0dc..0000000000 --- a/protocols/Tox/toxcore/toxav/codec.h +++ /dev/null @@ -1,116 +0,0 @@ -/** codec.h - * - * Audio and video codec intitialization, encoding/decoding and playback - * - * 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/>. - * - */ - -#ifndef _CODEC_H_ -#define _CODEC_H_ - -#include <stdio.h> -#include <math.h> -#include <pthread.h> - -#include <vpx/vpx_decoder.h> -#include <vpx/vpx_encoder.h> -#include <vpx/vp8dx.h> -#include <vpx/vp8cx.h> -#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) -#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) - -/* Audio encoding/decoding */ -#include <opus.h> - -typedef enum _Capabilities { - none, - a_encoding = 1 << 0, - a_decoding = 1 << 1, - v_encoding = 1 << 2, - v_decoding = 1 << 3 -} Capabilities; - -extern const uint16_t min_jbuf_size; - -typedef struct _CodecState { - - /* video encoding */ - vpx_codec_ctx_t v_encoder; - uint32_t frame_counter; - - /* video decoding */ - vpx_codec_ctx_t v_decoder; - int bitrate; - int max_width; - int max_height; - - /* audio encoding */ - OpusEncoder *audio_encoder; - int audio_bitrate; - int audio_sample_rate; - int audio_encoder_channels; - - /* audio decoding */ - OpusDecoder *audio_decoder; - int audio_decoder_channels; - - uint64_t capabilities; /* supports*/ - - /* Voice activity detection */ - uint32_t EVAD_tolerance; /* In frames */ - uint32_t EVAD_tolerance_cr; -} CodecState; - - -typedef struct _JitterBuffer { - RTPMessage **queue; - uint32_t size; - uint32_t capacity; - uint16_t bottom; - uint16_t top; -} JitterBuffer; - -JitterBuffer *create_queue(unsigned int capacity); -void terminate_queue(JitterBuffer *q); -void queue(JitterBuffer *q, RTPMessage *pk); -RTPMessage *dequeue(JitterBuffer *q, int *success); - - -CodecState *codec_init_session ( uint32_t audio_bitrate, - uint16_t audio_frame_duration, - uint32_t audio_sample_rate, - uint32_t encoder_audio_channels, - uint32_t decoder_audio_channels, - uint32_t audio_VAD_tolerance_ms, - uint16_t max_video_width, - uint16_t max_video_height, - uint32_t video_bitrate ); - -void codec_terminate_session(CodecState *cs); - -/* Reconfigure video encoder - return 0 on success. - return -1 on failure. */ -int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height); -int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate); - -/* Calculate energy and return 1 if has voice, 0 if not */ -int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy); - -#endif /* _CODEC_H_ */ 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; -} diff --git a/protocols/Tox/toxcore/toxav/msi.h b/protocols/Tox/toxcore/toxav/msi.h deleted file mode 100644 index 64fa08819a..0000000000 --- a/protocols/Tox/toxcore/toxav/msi.h +++ /dev/null @@ -1,267 +0,0 @@ -/** msi.h - * - * 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/>. - * - */ - -#ifndef __TOXMSI -#define __TOXMSI - -#include <inttypes.h> -#include <pthread.h> - -#include "../toxcore/Messenger.h" - -typedef uint8_t MSICallIDType[12]; -typedef uint8_t MSIReasonStrType[255]; -typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); - - -/** - * @brief Call type identifier. Also used as rtp callback prefix. - */ -typedef enum { - type_audio = 192, - type_video -} MSICallType; - - -/** - * @brief Call state identifiers. - */ -typedef enum { - call_inviting, /* when sending call invite */ - call_starting, /* when getting call invite */ - call_active, - call_hold, - call_hanged_up - -} MSICallState; - - -/** - * @brief Encoding settings. - */ -typedef struct _MSICodecSettings { - MSICallType call_type; - - uint32_t video_bitrate; /* In kbits/s */ - uint16_t max_video_width; /* In px */ - uint16_t max_video_height; /* In px */ - - uint32_t audio_bitrate; /* In bits/s */ - uint16_t audio_frame_duration; /* In ms */ - uint32_t audio_sample_rate; /* In Hz */ - uint32_t audio_channels; -} MSICSettings; - - -/** - * @brief Callbacks ids that handle the states - */ -typedef enum { - /* Requests */ - MSI_OnInvite, - MSI_OnStart, - MSI_OnCancel, - MSI_OnReject, - MSI_OnEnd, - - /* Responses */ - MSI_OnRinging, - MSI_OnStarting, - MSI_OnEnding, - - /* Protocol */ - MSI_OnRequestTimeout, - MSI_OnPeerTimeout, - MSI_OnMediaChange -} MSICallbackID; - -/** - * @brief Callbacks container - */ -typedef struct _MSICallbackCont { - MSICallbackType function; - void *data; -} MSICallbackCont; - -/** - * @brief The call struct. - * - */ -typedef struct _MSICall { /* Call info structure */ - struct _MSISession *session; /* Session pointer */ - - MSICallState state; - - MSICSettings csettings_local; /* Local call settings */ - MSICSettings *csettings_peer; /* Peers call settings */ - - MSICallIDType id; /* Random value identifying the call */ - - int ringing_tout_ms; /* Ringing timeout in ms */ - - int request_timer_id; /* Timer id for outgoing request/action */ - int ringing_timer_id; /* Timer id for ringing timeout */ - - - pthread_mutex_t mutex; /* */ - uint32_t *peers; - uint16_t peer_count; - - int32_t call_idx; /* Index of this call in MSISession */ -} MSICall; - - -/** - * @brief Control session struct - * - */ -typedef struct _MSISession { - - /* Call handlers */ - MSICall **calls; - int32_t max_calls; - - void *agent_handler; - Messenger *messenger_handle; - - uint32_t frequ; - uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ - - pthread_mutex_t mutex; - - void *timer_handler; - MSICallbackCont callbacks[11]; /* Callbacks used by this session */ -} MSISession; - -/** - * @brief Callback setter. - * - * @param session The container. - * @param callback The callback. - * @param id The id. - * @return void - */ -void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *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 ); - - -/** - * @brief Terminate control session. - * - * @param session The session - * @return int - */ -int msi_terminate_session ( MSISession *session ); - - -/** - * @brief Send invite request to friend_id. - * - * @param session Control session. - * @param call_index Set to new 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_invite ( MSISession *session, int32_t *call_index, MSICSettings csettings, uint32_t rngsec, - uint32_t friend_id ); - - -/** - * @brief Hangup active call. - * - * @param session Control session. - * @param call_index 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 ); - - -/** - * @brief Answer active call request. - * - * @param session Control session. - * @param call_index 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 ); - - -/** - * @brief Cancel request. - * - * @param session Control session. - * @param call_index To which call is this action handled. - * @param peer To which peer. - * @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 ); - - -/** - * @brief Reject request. - * - * @param session Control session. - * @param call_index To which call is this action handled. - * @param reason Set optional reason header. Pass NULL if none. - * @return int - */ -int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); - - -/** - * @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 ); - - -/** - * @brief Terminate the current call. - * - * @param session Control session. - * @param call_index To which call is this action handled. - * @return int - */ -int msi_stopcall ( MSISession *session, int32_t call_index ); - -#endif /* __TOXMSI */ diff --git a/protocols/Tox/toxcore/toxav/rtp.c b/protocols/Tox/toxcore/toxav/rtp.c deleted file mode 100644 index de6c9c418c..0000000000 --- a/protocols/Tox/toxcore/toxav/rtp.c +++ /dev/null @@ -1,600 +0,0 @@ -/** rtp.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 "rtp.h" -#include <stdlib.h> -void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg); - -#define size_32 4 - -#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) -#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) -#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0) -#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0) -#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) -#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0) - -#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6) -#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5) -#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4) -#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f ) -#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) -#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) - -/** - * @brief Checks if message came in late. - * - * @param session Control session. - * @param msg The message. - * @return int - * @retval -1 The message came in order. - * @retval 0 The message came late. - */ -inline__ int check_late_message (RTPSession *session, RTPMessage *msg) -{ - /* - * Check Sequence number. If this new msg has lesser number then the session->rsequnum - * it shows that the message came in late. Also check timestamp to be 100% certain. - * - */ - return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; -} - - -/** - * @brief Extracts header from payload. - * - * @param payload The payload. - * @param length The size of payload. - * @return RTPHeader* Extracted header. - * @retval NULL Error occurred while extracting header. - */ -RTPHeader *extract_header ( const uint8_t *payload, int length ) -{ - if ( !payload || !length ) { - LOGGER_WARNING("No payload to extract!"); - return NULL; - } - - RTPHeader *_retu = calloc(1, sizeof (RTPHeader)); - - if ( !_retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } - - bytes_to_U16(&_retu->sequnum, payload); - - const uint8_t *_it = payload + 2; - - _retu->flags = *_it; - ++_it; - - /* This indicates if the first 2 bits are valid. - * Now it may happen that this is out of order but - * it cuts down chances of parsing some invalid value - */ - - if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ) { - /* Deallocate */ - LOGGER_WARNING("Invalid version!"); - free(_retu); - return NULL; - } - - /* - * Added a check for the size of the header little sooner so - * I don't need to parse the other stuff if it's bad - */ - uint8_t _cc = GET_FLAG_CSRCC ( _retu ); - int _length = 12 /* Minimum header len */ + ( _cc * 4 ); - - if ( length < _length ) { - /* Deallocate */ - LOGGER_WARNING("Length invalid!"); - free(_retu); - return NULL; - } - - memset(_retu->csrc, 0, 16 * sizeof (uint32_t)); - - _retu->marker_payloadt = *_it; - ++_it; - _retu->length = _length; - - - bytes_to_U32(&_retu->timestamp, _it); - _it += 4; - bytes_to_U32(&_retu->ssrc, _it); - - uint8_t _x; - - for ( _x = 0; _x < _cc; _x++ ) { - _it += 4; - bytes_to_U32(&(_retu->csrc[_x]), _it); - } - - return _retu; -} - -/** - * @brief Extracts external header from payload. Must be called AFTER extract_header()! - * - * @param payload The ITERATED payload. - * @param length The size of payload. - * @return RTPExtHeader* Extracted extension header. - * @retval NULL Error occurred while extracting extension header. - */ -RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) -{ - const uint8_t *_it = payload; - - RTPExtHeader *_retu = calloc(1, sizeof (RTPExtHeader)); - - if ( !_retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } - - uint16_t _ext_length; - bytes_to_U16(&_ext_length, _it); - _it += 2; - - - if ( length < ( _ext_length * sizeof(uint32_t) ) ) { - LOGGER_WARNING("Length invalid!"); - free(_retu); - return NULL; - } - - _retu->length = _ext_length; - bytes_to_U16(&_retu->type, _it); - _it += 2; - - if ( !(_retu->table = calloc(_ext_length, sizeof (uint32_t))) ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - free(_retu); - return NULL; - } - - uint16_t _x; - - for ( _x = 0; _x < _ext_length; _x++ ) { - _it += 4; - bytes_to_U32(&(_retu->table[_x]), _it); - } - - return _retu; -} - -/** - * @brief Adds header to payload. Make sure _payload_ has enough space. - * - * @param header The header. - * @param payload The payload. - * @return uint8_t* Iterated position. - */ -uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) -{ - uint8_t _cc = GET_FLAG_CSRCC ( header ); - - uint8_t *_it = payload; - - - /* Add sequence number first */ - U16_to_bytes(_it, header->sequnum); - _it += 2; - - *_it = header->flags; - ++_it; - *_it = header->marker_payloadt; - ++_it; - - - U32_to_bytes( _it, header->timestamp); - _it += 4; - U32_to_bytes( _it, header->ssrc); - - uint8_t _x; - - for ( _x = 0; _x < _cc; _x++ ) { - _it += 4; - U32_to_bytes( _it, header->csrc[_x]); - } - - return _it + 4; -} - -/** - * @brief Adds extension header to payload. Make sure _payload_ has enough space. - * - * @param header The header. - * @param payload The payload. - * @return uint8_t* Iterated position. - */ -uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) -{ - uint8_t *_it = payload; - - U16_to_bytes(_it, header->length); - _it += 2; - U16_to_bytes(_it, header->type); - _it -= 2; /* Return to 0 position */ - - if ( header->table ) { - uint16_t _x; - - for ( _x = 0; _x < header->length; _x++ ) { - _it += 4; - U32_to_bytes(_it, header->table[_x]); - } - } - - return _it + 4; -} - -/** - * @brief Builds header from control session values. - * - * @param session Control session. - * @return RTPHeader* Created header. - */ -RTPHeader *build_header ( RTPSession *session ) -{ - RTPHeader *_retu = calloc ( 1, sizeof (RTPHeader) ); - - if ( !_retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } - - ADD_FLAG_VERSION ( _retu, session->version ); - ADD_FLAG_PADDING ( _retu, session->padding ); - ADD_FLAG_EXTENSION ( _retu, session->extension ); - ADD_FLAG_CSRCC ( _retu, session->cc ); - ADD_SETTING_MARKER ( _retu, session->marker ); - ADD_SETTING_PAYLOAD ( _retu, session->payload_type ); - - _retu->sequnum = session->sequnum; - _retu->timestamp = current_time_monotonic(); /* milliseconds */ - _retu->ssrc = session->ssrc; - - int i; - - for ( i = 0; i < session->cc; i++ ) - _retu->csrc[i] = session->csrc[i]; - - _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); - - return _retu; -} - - -/** - * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data - * and so the length variable is set accordingly. _sequnum_ argument is - * passed by the handle_packet() since it's parsed already. - * - * @param session Control session. - * @param sequnum Sequence number that's parsed from payload in handle_packet() - * @param data Payload data. - * @param length Payload size. - * @return RTPMessage* - * @retval NULL Error occurred. - */ -RTPMessage *msg_parse ( const uint8_t *data, int length ) -{ - RTPMessage *_retu = calloc(1, sizeof (RTPMessage)); - - _retu->header = extract_header ( data, length ); /* It allocates memory and all */ - - if ( !_retu->header ) { - LOGGER_WARNING("Header failed to extract!"); - free(_retu); - return NULL; - } - - uint16_t _from_pos = _retu->header->length; - _retu->length = length - _from_pos; - - - - if ( GET_FLAG_EXTENSION ( _retu->header ) ) { - _retu->ext_header = extract_ext_header ( data + _from_pos, length ); - - if ( _retu->ext_header ) { - _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - } else { /* Error */ - LOGGER_WARNING("Ext Header failed to extract!"); - rtp_free_msg(NULL, _retu); - return NULL; - } - } else { - _retu->ext_header = NULL; - } - - if ( length - _from_pos <= MAX_RTP_SIZE ) - memcpy ( _retu->data, data + _from_pos, length - _from_pos ); - else { - LOGGER_WARNING("Invalid length!"); - rtp_free_msg(NULL, _retu); - return NULL; - } - - _retu->next = NULL; - - return _retu; -} - -/** - * @brief Callback for networking core. - * - * @param object RTPSession object. - * @param ip_port Where the message comes from. - * @param data Message data. - * @param length Message length. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_handle_packet ( void *object, const uint8_t *data, uint32_t length ) -{ - RTPSession *_session = object; - RTPMessage *_msg; - - if ( !_session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */ - LOGGER_WARNING("No session or invalid length of received buffer!"); - return -1; - } - - _msg = msg_parse ( data + 1, length - 1 ); - - if ( !_msg ) { - LOGGER_WARNING("Could not parse message!"); - return -1; - } - - /* Check if message came in late */ - if ( check_late_message(_session, _msg) < 0 ) { /* Not late */ - _session->rsequnum = _msg->header->sequnum; - _session->timestamp = _msg->header->timestamp; - } - - toxav_handle_packet(_session, _msg); - - return 0; -} - - - -/** - * @brief Stores headers and payload data in one container ( data ) - * and the length is set accordingly. Returned message is used for sending _only_. - * - * @param session The control session. - * @param data Payload data to send ( This is what you pass ). - * @param length Size of the payload data. - * @return RTPMessage* Created message. - * @retval NULL Error occurred. - */ -RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length ) -{ - if ( !session ) { - LOGGER_WARNING("No session!"); - return NULL; - } - - uint8_t *_from_pos; - RTPMessage *_retu = calloc(1, sizeof (RTPMessage)); - - if ( !_retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } - - /* Sets header values and copies the extension header in _retu */ - _retu->header = build_header ( session ); /* It allocates memory and all */ - _retu->ext_header = session->ext_header; - - - uint32_t _total_length = length + _retu->header->length + 1; - - _retu->data[0] = session->prefix; - - if ( _retu->ext_header ) { - _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 ); - - _from_pos = add_header ( _retu->header, _retu->data + 1 ); - _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 ); - } else { - _from_pos = add_header ( _retu->header, _retu->data + 1 ); - } - - /* - * Parses the extension header into the message - * Of course if any - */ - - /* Appends _data on to _retu->_data */ - memcpy ( _from_pos, data, length ); - - _retu->length = _total_length; - - _retu->next = NULL; - - return _retu; -} - - -/** - * @brief Sends data to _RTPSession::dest - * - * @param session The session. - * @param messenger Tox* object. - * @param data The payload. - * @param length Size of the payload. - * @return int - * @retval -1 On error. - * @retval 0 On success. - */ -int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) -{ - RTPMessage *msg = rtp_new_message (session, data, length); - - if ( !msg ) { - LOGGER_WARNING("No session!"); - return -1; - } - - if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { - LOGGER_WARNING("Failed to send full packet! std error: %s", strerror(errno)); - rtp_free_msg ( session, msg ); - return -1; - } - - - /* Set sequ number */ - session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; - rtp_free_msg ( session, msg ); - - return 0; -} - - -/** - * @brief Speaks for it self. - * - * @param session The control session msg belongs to. You set it as NULL when freeing recved messages. - * Otherwise set it to session the message was created from. - * @param msg The message. - * @return void - */ -void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) -{ - if ( !session ) { - if ( msg->ext_header ) { - free ( msg->ext_header->table ); - free ( msg->ext_header ); - } - } else { - if ( msg->ext_header && session->ext_header != msg->ext_header ) { - free ( msg->ext_header->table ); - free ( msg->ext_header ); - } - } - - free ( msg->header ); - free ( msg ); -} - -/** - * @brief Must be called before calling any other rtp function. It's used - * to initialize RTP control session. - * - * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType - * @param messenger Tox* object. - * @param friend_num Friend id. - * @return RTPSession* Created control session. - * @retval NULL Error occurred. - */ -RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num ) -{ - RTPSession *_retu = calloc(1, sizeof(RTPSession)); - - if ( !_retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } - - if ( -1 == custom_lossy_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu)) { - LOGGER_ERROR("Error setting custom register handler for rtp session"); - free(_retu); - return NULL; - } - - LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); - - _retu->version = RTP_VERSION; /* It's always 2 */ - _retu->padding = 0; /* If some additional data is needed about the packet */ - _retu->extension = 0; /* If extension to header is needed */ - _retu->cc = 1; /* Amount of contributors */ - _retu->csrc = NULL; /* Container */ - _retu->ssrc = random_int(); - _retu->marker = 0; - _retu->payload_type = payload_type % 128; - - _retu->dest = friend_num; - - _retu->rsequnum = _retu->sequnum = 0; - - _retu->ext_header = NULL; /* When needed allocate */ - - - if ( !(_retu->csrc = calloc(1, sizeof (uint32_t))) ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - free(_retu); - return NULL; - } - - _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */ - - /* Also set payload type as prefix */ - _retu->prefix = payload_type; - - /* - * - */ - return _retu; -} - - -/** - * @brief Terminate the session. - * - * @param session The session. - * @param messenger The messenger who owns the session - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -void rtp_terminate_session ( RTPSession *session, Messenger *messenger ) -{ - if ( !session ) return; - - custom_lossy_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL); - - free ( session->ext_header ); - free ( session->csrc ); - - LOGGER_DEBUG("Terminated RTP session: %p", session); - - /* And finally free session */ - free ( session ); - -} diff --git a/protocols/Tox/toxcore/toxav/rtp.h b/protocols/Tox/toxcore/toxav/rtp.h deleted file mode 100644 index d57c5ef720..0000000000 --- a/protocols/Tox/toxcore/toxav/rtp.h +++ /dev/null @@ -1,196 +0,0 @@ -/** rtp.h - * - * 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/>. - * - */ - -#ifndef __TOXRTP -#define __TOXRTP - -#define RTP_VERSION 2 -#include <inttypes.h> -#include <pthread.h> - -#include "../toxcore/util.h" -#include "../toxcore/network.h" -#include "../toxcore/net_crypto.h" -#include "../toxcore/Messenger.h" - -#define MAX_SEQU_NUM 65535 -#define MAX_RTP_SIZE 65535 - -/** - * @brief Standard rtp header - * - */ - -typedef struct _RTPHeader { - uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ - uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ - uint16_t sequnum; /* Sequence Number */ - uint32_t timestamp; /* Timestamp */ - uint32_t ssrc; /* SSRC */ - uint32_t csrc[16]; /* CSRC's table */ - uint32_t length; /* Length of the header in payload string. */ - -} RTPHeader; - - -/** - * @brief Standard rtp extension header. - * - */ -typedef struct _RTPExtHeader { - uint16_t type; /* Extension profile */ - uint16_t length; /* Number of extensions */ - uint32_t *table; /* Extension's table */ - -} RTPExtHeader; - - -/** - * @brief Standard rtp message. - * - */ -typedef struct _RTPMessage { - RTPHeader *header; - RTPExtHeader *ext_header; - - uint8_t data[MAX_RTP_SIZE]; - uint32_t length; - - struct _RTPMessage *next; -} RTPMessage; - - -/** - * @brief Our main session descriptor. - * It measures the session variables and controls - * the entire session. There are functions for manipulating - * the session so tend to use those instead of directly modifying - * session parameters. - * - */ -typedef struct _RTPSession { - uint8_t version; - uint8_t padding; - uint8_t extension; - uint8_t cc; - uint8_t marker; - uint8_t payload_type; - uint16_t sequnum; /* Set when sending */ - uint16_t rsequnum; /* Check when recving msg */ - uint32_t timestamp; - uint32_t ssrc; - uint32_t *csrc; - - /* If some additional data must be sent via message - * apply it here. Only by allocating this member you will be - * automatically placing it within a message. - */ - RTPExtHeader *ext_header; - - /* Msg prefix for core to know when recving */ - uint8_t prefix; - - int dest; - int32_t call_index; - struct _ToxAv *av; - -} RTPSession; - - -/** - * @brief Release all messages held by session. - * - * @param session The session. - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -int rtp_release_session_recv ( RTPSession *session ); - - -/** - * @brief Call this to change queue limit - * - * @param session The session - * @param limit new limit - * @return void - */ -void rtp_queue_adjust_limit ( RTPSession *session, uint64_t limit ); - -/** - * @brief Get's oldest message in the list. - * - * @param session Where the list is. - * @return RTPMessage* The message. You need to call rtp_msg_free() to free it. - * @retval NULL No messages in the list, or no list. - */ -RTPMessage *rtp_recv_msg ( RTPSession *session ); - - -/** - * @brief Sends msg to _RTPSession::dest - * - * @param session The session. - * @param msg The message - * @param messenger Tox* object. - * @return int - * @retval -1 On error. - * @retval 0 On success. - */ -int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); - - -/** - * @brief Speaks for it self. - * - * @param session The control session msg belongs to. It can be NULL. - * @param msg The message. - * @return void - */ -void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); - -/** - * @brief Must be called before calling any other rtp function. It's used - * to initialize RTP control session. - * - * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType - * @param messenger Tox* object. - * @param friend_num Friend id. - * @return RTPSession* Created control session. - * @retval NULL Error occurred. - */ -RTPSession *rtp_init_session ( int payload_type, Messenger *messenger, int friend_num ); - - -/** - * @brief Terminate the session. - * - * @param session The session. - * @param messenger The messenger who owns the session - * @return int - * @retval -1 Error occurred. - * @retval 0 Success. - */ -void rtp_terminate_session ( RTPSession *session, Messenger *messenger ); - - - -#endif /* __TOXRTP */ diff --git a/protocols/Tox/toxcore/toxav/toxav.c b/protocols/Tox/toxcore/toxav/toxav.c deleted file mode 100644 index cd0ec70ed7..0000000000 --- a/protocols/Tox/toxcore/toxav/toxav.c +++ /dev/null @@ -1,1148 +0,0 @@ -/** toxav.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 */ - - -#define _GNU_SOURCE /* implicit declaration warning */ - -#include "rtp.h" -#include "codec.h" -#include "msi.h" -#include "toxav.h" - -#include "../toxcore/logger.h" - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -/* Assume 24 fps*/ -#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) -#define MAX_DECODE_TIME_US 0 - -#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ -#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ -#define VIDEOFRAME_HEADER_SIZE 0x2 - - -#define inline__ inline __attribute__((always_inline)) - -/* call index invalid: true if invalid */ -#define cii(c_idx, session) (c_idx < 0 || c_idx >= session->max_calls) - - -const ToxAvCSettings av_DefaultSettings = { - TypeAudio, - - 500, - 1280, - 720, - - 64000, - 20, - 48000, - 1 -}; - -const uint32_t av_jbufdc = 3; -const uint32_t av_VADd = 40; - - -static const uint8_t audio_index = 0, video_index = 1; - -typedef struct { - uint32_t size; - uint8_t data[0]; -} DECODE_PACKET; - -#define VIDEO_DECODE_QUEUE_SIZE 2 -#define AUDIO_DECODE_QUEUE_SIZE 16 - -typedef struct _CallSpecific { - RTPSession *crtps[2]; /** Audio is first and video is second */ - CodecState *cs;/** Each call have its own encoders and decoders. - * You can, but don't have to, reuse encoders for - * multiple calls. If you choose to reuse encoders, - * make sure to also reuse encoded payload for every call. - * Decoders have to be unique for each call. FIXME: Now add refcounted encoders and - * reuse them really. - */ - JitterBuffer *j_buf; /** Jitter buffer for audio */ - - uint32_t frame_limit; /* largest address written to in frame_buf for current input frame*/ - uint8_t frame_id, frame_outid; /* id of input and output video frame */ - void *frame_buf; /* buffer for split video payloads */ - - _Bool call_active; - pthread_mutex_t mutex; - - /* used in the "decode on another thread" system */ - volatile _Bool exit, decoding; - uint8_t video_decode_read, video_decode_write, audio_decode_read, audio_decode_write; - pthread_mutex_t decode_cond_mutex; - pthread_cond_t decode_cond; - DECODE_PACKET *volatile video_decode_queue[VIDEO_DECODE_QUEUE_SIZE]; - DECODE_PACKET *volatile audio_decode_queue[AUDIO_DECODE_QUEUE_SIZE]; -} CallSpecific; - -struct _ToxAv { - Messenger *messenger; - MSISession *msi_session; /** Main msi session */ - CallSpecific *calls; /** Per-call params */ - - void (*audio_callback)(ToxAv *, int32_t, int16_t *, int, void *); - void (*video_callback)(ToxAv *, int32_t, vpx_image_t *, void *); - - void *audio_callback_userdata; - void *video_callback_userdata; - - uint32_t max_calls; -}; - -static void *toxav_decoding(void *arg); - -static MSICSettings msicsettings_cast (const ToxAvCSettings *from) -{ - MSICSettings csettings; - csettings.call_type = from->call_type; - - csettings.video_bitrate = from->video_bitrate; - csettings.max_video_width = from->max_video_width; - csettings.max_video_height = from->max_video_height; - - csettings.audio_bitrate = from->audio_bitrate; - csettings.audio_frame_duration = from->audio_frame_duration; - csettings.audio_sample_rate = from->audio_sample_rate; - csettings.audio_channels = from->audio_channels; - - return csettings; -} - -static ToxAvCSettings toxavcsettings_cast (const MSICSettings *from) -{ - ToxAvCSettings csettings; - csettings.call_type = from->call_type; - - csettings.video_bitrate = from->video_bitrate; - csettings.max_video_width = from->max_video_width; - csettings.max_video_height = from->max_video_height; - - csettings.audio_bitrate = from->audio_bitrate; - csettings.audio_frame_duration = from->audio_frame_duration; - csettings.audio_sample_rate = from->audio_sample_rate; - csettings.audio_channels = from->audio_channels; - - return csettings; -} - -/** - * @brief Start new A/V session. There can only be one session at the time. If you register more - * it will result in undefined behaviour. - * - * @param messenger The messenger handle. - * @param userdata The agent handling A/V session (i.e. phone). - * @param video_width Width of video frame. - * @param video_height Height of video frame. - * @return ToxAv* - * @retval NULL On error. - */ -ToxAv *toxav_new( Tox *messenger, int32_t max_calls) -{ - ToxAv *av = calloc ( sizeof(ToxAv), 1); - - if (av == NULL) { - LOGGER_WARNING("Allocation failed!"); - return NULL; - } - - av->messenger = (Messenger *)messenger; - av->msi_session = msi_init_session(av->messenger, max_calls); - av->msi_session->agent_handler = av; - av->calls = calloc(sizeof(CallSpecific), max_calls); - av->max_calls = max_calls; - - return av; -} - -/** - * @brief Remove A/V session. - * - * @param av Handler. - * @return void - */ -void toxav_kill ( ToxAv *av ) -{ - uint32_t i; - - for (i = 0; i < av->max_calls; i ++) { - if ( av->calls[i].crtps[audio_index] ) - rtp_terminate_session(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); - - - if ( av->calls[i].crtps[video_index] ) - rtp_terminate_session(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); - - - - if ( av->calls[i].j_buf ) terminate_queue(av->calls[i].j_buf); - - if ( av->calls[i].cs ) codec_terminate_session(av->calls[i].cs); - } - - msi_terminate_session(av->msi_session); - - free(av->calls); - free(av); -} - -/** - * @brief Register callback for call state. - * - * @param av Handler. - * @param callback The callback - * @param id One of the ToxAvCallbackID values - * @return void - */ -void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata ) -{ - msi_register_callback(av->msi_session, (MSICallbackType)callback, (MSICallbackID) id, userdata); -} - -/** - * @brief Register callback for recieving audio data - * - * @param callback The callback - * @return void - */ -void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *), - void *user_data) -{ - av->audio_callback = callback; - av->audio_callback_userdata = user_data; -} - -/** - * @brief Register callback for recieving video data - * - * @param callback The callback - * @return void - */ -void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *), - void *user_data) -{ - av->video_callback = callback; - av->video_callback_userdata = user_data; -} - -/** - * @brief Call user. Use its friend_id. - * - * @param av Handler. - * @param user The user. - * @param call_type Call type. - * @param ringing_seconds Ringing timeout. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_call (ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds ) -{ - return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user); -} - -/** - * @brief Hangup active call. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_hangup ( ToxAv *av, int32_t call_index ) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - if ( av->msi_session->calls[call_index]->state != call_active ) { - return ErrorInvalidState; - } - - return msi_hangup(av->msi_session, call_index); -} - -/** - * @brief Answer incomming call. - * - * @param av Handler. - * @param call_type Answer with... - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - if ( av->msi_session->calls[call_index]->state != call_starting ) { - return ErrorInvalidState; - } - - return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); -} - -/** - * @brief Reject incomming call. - * - * @param av Handler. - * @param reason Optional reason. Set NULL if none. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - if ( av->msi_session->calls[call_index]->state != call_starting ) { - return ErrorInvalidState; - } - - return msi_reject(av->msi_session, call_index, reason); -} - -/** - * @brief Cancel outgoing request. - * - * @param av Handler. - * @param reason Optional reason. - * @param peer_id peer friend_id - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - if ( av->msi_session->calls[call_index]->state != call_inviting ) { - return ErrorInvalidState; - } - - return msi_cancel(av->msi_session, call_index, peer_id, reason); -} - -/** - * @brief Notify peer that we are changing call type - * - * @param av Handler. - * @return int - * @param call_type Change to... - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); -} - -/** - * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_stop_call ( ToxAv *av, int32_t call_index ) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) { - return ErrorNoCall; - } - - return msi_stopcall(av->msi_session, call_index); -} - -/** - * @brief Must be call before any RTP transmission occurs. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, uint32_t jbuf_capacity, uint32_t VAD_treshold, - int support_video ) -{ - if ( !av->msi_session || cii(call_index, av->msi_session) || - !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer || - av->calls[call_index].call_active) { - LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); - return ErrorInternal; - } - - CallSpecific *call = &av->calls[call_index]; - - call->crtps[audio_index] = - rtp_init_session(type_audio, av->messenger, av->msi_session->calls[call_index]->peers[0]); - - - if ( !call->crtps[audio_index] ) { - LOGGER_ERROR("Error while starting audio RTP session!\n"); - return ErrorInternal; - } - - call->crtps[audio_index]->call_index = call_index; - call->crtps[audio_index]->av = av; - - if ( support_video ) { - call->crtps[video_index] = - rtp_init_session(type_video, av->messenger, av->msi_session->calls[call_index]->peers[0]); - - if ( !call->crtps[video_index] ) { - LOGGER_ERROR("Error while starting video RTP session!\n"); - goto error; - } - - call->crtps[video_index]->call_index = call_index; - call->crtps[video_index]->av = av; - - call->frame_limit = 0; - call->frame_id = 0; - call->frame_outid = 0; - - call->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1); - - if (!call->frame_buf) { - LOGGER_WARNING("Frame buffer allocation failed!"); - goto error; - } - - } - - if ( !(call->j_buf = create_queue(jbuf_capacity)) ) { - LOGGER_WARNING("Jitter buffer creaton failed!"); - goto error; - } - - ToxAvCSettings csettings_peer = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[0]); - ToxAvCSettings csettings_local = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_local); - LOGGER_DEBUG( - "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", - csettings_peer.call_type, - csettings_peer.video_bitrate, - csettings_peer.max_video_height, - csettings_peer.max_video_width, - csettings_peer.audio_bitrate, - csettings_peer.audio_frame_duration, - csettings_peer.audio_sample_rate, - csettings_peer.audio_channels ); - - if ( (call->cs = codec_init_session(csettings_local.audio_bitrate, - csettings_local.audio_frame_duration, - csettings_local.audio_sample_rate, - csettings_local.audio_channels, - csettings_peer.audio_channels, - VAD_treshold, - csettings_local.max_video_width, - csettings_local.max_video_height, - csettings_local.video_bitrate) )) { - - if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) goto error; - - //todo: add error checks - pthread_mutex_init(&call->decode_cond_mutex, NULL); - pthread_cond_init(&call->decode_cond, NULL); - - void **arg = malloc(2 * sizeof(void *)); - arg[0] = av; - arg[1] = call; - - pthread_t temp; - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 1 << 18); - pthread_create(&temp, &attr, toxav_decoding, arg); - pthread_attr_destroy(&attr); - - - LOGGER_WARNING("Got here"); - call->call_active = 1; - - return ErrorNone; - } - -error: - rtp_terminate_session(call->crtps[audio_index], av->messenger); - rtp_terminate_session(call->crtps[video_index], av->messenger); - free(call->frame_buf); - terminate_queue(call->j_buf); - codec_terminate_session(call->cs); - - return ErrorInternal; -} - -/** - * @brief Call this at the end of the transmission. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) -{ - if (cii(call_index, av->msi_session)) { - LOGGER_WARNING("Invalid call index: %d", call_index); - return ErrorNoCall; - } - - CallSpecific *call = &av->calls[call_index]; - - pthread_mutex_lock(&call->mutex); - - if (!call->call_active) { - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - - call->call_active = 0; - - rtp_terminate_session(call->crtps[audio_index], av->messenger); - call->crtps[audio_index] = NULL; - rtp_terminate_session(call->crtps[video_index], av->messenger); - call->crtps[video_index] = NULL; - terminate_queue(call->j_buf); - call->j_buf = NULL; - - int i; - DECODE_PACKET *p; - - call->exit = 1; - pthread_mutex_lock(&call->decode_cond_mutex); - pthread_cond_signal(&call->decode_cond); - pthread_cond_wait(&call->decode_cond, &call->decode_cond_mutex); - pthread_mutex_unlock(&call->decode_cond_mutex); - pthread_mutex_destroy(&call->decode_cond_mutex); - pthread_cond_destroy(&call->decode_cond); - - for (i = 0; i != VIDEO_DECODE_QUEUE_SIZE; i++) { - p = call->video_decode_queue[i]; - call->video_decode_queue[i] = NULL; - - if (p) { - free(p); - } - } - - for (i = 0; i != AUDIO_DECODE_QUEUE_SIZE; i++) { - p = call->audio_decode_queue[i]; - call->audio_decode_queue[i] = NULL; - - if (p) { - free(p); - } - } - - codec_terminate_session(call->cs); - call->cs = NULL; - - pthread_mutex_unlock(&call->mutex); - pthread_mutex_destroy(&call->mutex); - - memset(call, 0, sizeof(CallSpecific)); - return ErrorNone; -} - - -/** - * @brief Send RTP payload. - * - * @param av Handler. - * @param type Type of payload. - * @param payload The payload. - * @param length Size of it. - * @return int - * @retval 0 Success. - * @retval -1 Failure. - */ -static int toxav_send_rtp_payload(ToxAv *av, int32_t call_index, ToxAvCallType type, const uint8_t *payload, - unsigned int length) -{ - CallSpecific *call = &av->calls[call_index]; - - if (call->crtps[type - TypeAudio]) { - - if (type == TypeAudio) { - return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, payload, length); - } else { - if (length == 0 || length > MAX_VIDEOFRAME_SIZE) { - LOGGER_ERROR("Invalid video frame size: %u\n", length); - return ErrorInternal; - } - - /* number of pieces - 1*/ - uint8_t numparts = (length - 1) / VIDEOFRAME_PIECE_SIZE; - - uint8_t load[2 + VIDEOFRAME_PIECE_SIZE]; - load[0] = call->frame_outid++; - load[1] = 0; - - int i; - - for (i = 0; i < numparts; i++) { - memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, VIDEOFRAME_PIECE_SIZE); - payload += VIDEOFRAME_PIECE_SIZE; - - if (rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, - load, VIDEOFRAME_HEADER_SIZE + VIDEOFRAME_PIECE_SIZE) != 0) { - - return ErrorInternal; - } - - load[1]++; - } - - /* remainder = length % VIDEOFRAME_PIECE_SIZE, VIDEOFRAME_PIECE_SIZE if = 0 */ - length = ((length - 1) % VIDEOFRAME_PIECE_SIZE) + 1; - memcpy(load + VIDEOFRAME_HEADER_SIZE, payload, length); - - return rtp_send_msg(call->crtps[type - TypeAudio], av->messenger, load, VIDEOFRAME_HEADER_SIZE + length); - } - } else { - return ErrorNoRtpSession; - } -} - -/** - * @brief Encode and send video packet. - * - * @param av Handler. - * @param input The packet. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) -{ - - if (cii(call_index, av->msi_session)) { - LOGGER_WARNING("Invalid call index: %d", call_index); - return ErrorNoCall; - } - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - - if (!call->call_active) { - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - int rc = toxav_send_rtp_payload(av, call_index, TypeVideo, frame, frame_size); - pthread_mutex_unlock(&call->mutex); - - return rc; -} - -/** - * @brief Encode video frame - * - * @param av Handler - * @param dest Where to - * @param dest_max Max size - * @param input What to encode - * @return int - * @retval ToxAvError On error. - * @retval >0 On success - */ -int toxav_prepare_video_frame(ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) -{ - if (cii(call_index, av->msi_session)) { - LOGGER_WARNING("Invalid call index: %d", call_index); - return ErrorNoCall; - } - - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - if (!call->call_active) { - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - if (reconfigure_video_encoder_resolution(call->cs, input->d_w, input->d_h) != 0) { - pthread_mutex_unlock(&call->mutex); - return ErrorInternal; - } - - int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); - pthread_mutex_unlock(&call->mutex); - return ErrorInternal; - } - - ++call->cs->frame_counter; - - vpx_codec_iter_t iter = NULL; - const vpx_codec_cx_pkt_t *pkt; - int copied = 0; - - while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { - if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - if ( copied + pkt->data.frame.sz > dest_max ) { - pthread_mutex_unlock(&call->mutex); - return ErrorPacketTooLarge; - } - - memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); - copied += pkt->data.frame.sz; - } - } - - pthread_mutex_unlock(&call->mutex); - return copied; -} - -/** - * @brief Send audio frame. - * - * @param av Handler. - * @param data The audio data encoded with toxav_prepare_audio_frame(). - * @param size Its size in number of bytes. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) -{ - if (size > MAX_CRYPTO_DATA_SIZE) - return ErrorInternal; - - if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - - if (!call->call_active) { - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - int rc = toxav_send_rtp_payload(av, call_index, TypeAudio, data, size); - pthread_mutex_unlock(&call->mutex); - - return rc; -} - -/** - * @brief Encode audio frame - * - * @param av Handler - * @param dest dest - * @param dest_max Max dest size - * @param frame The frame - * @param frame_size The frame size - * @return int - * @retval ToxAvError On error. - * @retval >0 On success - */ -int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, - int frame_size) -{ - if (cii(call_index, av->msi_session) || !av->calls[call_index].call_active) { - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - CallSpecific *call = &av->calls[call_index]; - pthread_mutex_lock(&call->mutex); - - - if (!call->call_active) { - pthread_mutex_unlock(&call->mutex); - LOGGER_WARNING("Action on inactive call: %d", call_index); - return ErrorNoCall; - } - - int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); - pthread_mutex_unlock(&call->mutex); - - if (rc < 0) { - LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); - return ErrorInternal; - } - - return rc; -} - -/** - * @brief Get peer transmission type. It can either be audio or video. - * - * @param av Handler. - * @param peer The peer - * @return int - * @retval ToxAvCallType On success. - * @retval ToxAvError On error. - */ -int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) -{ - if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] - || av->msi_session->calls[call_index]->peer_count <= peer ) - return ErrorInternal; - - *dest = toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); - return ErrorNone; -} - -/** - * @brief Get id of peer participating in conversation - * - * @param av Handler - * @param peer peer index - * @return int - * @retval ToxAvError No peer id - */ -int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) -{ - if ( peer < 0 || cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] - || av->msi_session->calls[call_index]->peer_count <= peer ) - return ErrorInternal; - - return av->msi_session->calls[call_index]->peers[peer]; -} - -/** - * @brief Get id of peer participating in conversation - * - * @param av Handler - * @param peer peer index - * @return int - * @retval ToxAvError No peer id - */ -ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) -{ - if ( cii(call_index, av->msi_session) || !av->msi_session->calls[call_index] ) - return av_CallNonExistant; - - return av->msi_session->calls[call_index]->state; - -} - -/** - * @brief Is certain capability supported - * - * @param av Handler - * @return int - * @retval 1 Yes. - * @retval 0 No. - */ -inline__ int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) -{ - return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (Capabilities) capability : 0; - /* 0 is error here */ -} - -inline__ Tox *toxav_get_tox(ToxAv *av) -{ - return (Tox *)av->messenger; -} - -int toxav_has_activity(ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy) -{ - if ( !av->calls[call_index].cs ) return ErrorInvalidCodecState; - - return energy_VAD(av->calls[call_index].cs, PCM, frame_size, ref_energy); -} - - -static void decode_video(ToxAv *av, CallSpecific *call, DECODE_PACKET *p) -{ - int32_t call_index = call - av->calls; - - int rc = vpx_codec_decode(&call->cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); - - if (rc != VPX_CODEC_OK) { - LOGGER_ERROR("Error decoding video: %s\n", vpx_codec_err_to_string(rc)); - } - - vpx_codec_iter_t iter = NULL; - vpx_image_t *img; - img = vpx_codec_get_frame(&call->cs->v_decoder, &iter); - - if (img && av->video_callback) { - av->video_callback(av, call_index, img, av->video_callback_userdata); - } else { - LOGGER_WARNING("Video packet dropped due to missing callback or no image!"); - } - - free(p); -} - -static void decode_audio(ToxAv *av, CallSpecific *call, DECODE_PACKET *p) -{ - int32_t call_index = call - av->calls; - - // ToxAvCSettings csettings; - // toxav_get_peer_csettings(av, call_index, 0, &csettings); - - int frame_size = 10000; /* FIXME: not static? */ - int16_t dest[frame_size]; - - int dec_size = opus_decode(call->cs->audio_decoder, p->data, p->size, dest, frame_size, (p->size == 0)); - free(p); - - if (dec_size < 0) { - LOGGER_WARNING("Decoding error: %s", opus_strerror(dec_size)); - return; - } - - if ( av->audio_callback ) - av->audio_callback(av, call_index, dest, dec_size, av->audio_callback_userdata); - else - LOGGER_WARNING("Audio packet dropped due to missing callback!"); -} - -static void *toxav_decoding(void *arg) -{ - void **pp = arg; - ToxAv *av = pp[0]; - CallSpecific *call = pp[1]; - free(pp); - - while (1) { - DECODE_PACKET *p; - _Bool video = 0; - - pthread_mutex_lock(&call->decode_cond_mutex); - - if (call->exit) { - break; - } - - uint8_t r; - - /* first check for available packets, otherwise wait for condition*/ - r = call->audio_decode_read; - p = call->audio_decode_queue[r]; - - if (!p) { - r = call->video_decode_read; - p = call->video_decode_queue[r]; - - if (!p) { - pthread_cond_wait(&call->decode_cond, &call->decode_cond_mutex); - r = call->audio_decode_read; - p = call->audio_decode_queue[r]; - - if (!p) { - r = call->video_decode_read; - p = call->video_decode_queue[r]; - video = 1; - } - } else { - video = 1; - } - } - - if (video) { - if (p) { - call->video_decode_queue[r] = NULL; - call->video_decode_read = (r + 1) % VIDEO_DECODE_QUEUE_SIZE; - } - } else { - call->audio_decode_queue[r] = NULL; - call->audio_decode_read = (r + 1) % AUDIO_DECODE_QUEUE_SIZE; - } - - pthread_mutex_unlock(&call->decode_cond_mutex); - - if (p) { - if (video) { - decode_video(av, call, p); - } else { - decode_audio(av, call, p); - } - } - } - - call->exit = 0; - pthread_cond_signal(&call->decode_cond); - pthread_mutex_unlock(&call->decode_cond_mutex); - - return NULL; -} - -void toxav_handle_packet(RTPSession *_session, RTPMessage *_msg) -{ - ToxAv *av = _session->av; - int32_t call_index = _session->call_index; - CallSpecific *call = &av->calls[call_index]; - - if (!call->call_active) return; - - if (_session->payload_type == type_audio % 128) { - queue(call->j_buf, _msg); - - int success = 0; - - while ((_msg = dequeue(call->j_buf, &success)) || success == 2) { - DECODE_PACKET *p; - - if (success == 2) { - p = malloc(sizeof(DECODE_PACKET)); - - if (p) { - p->size = 0; - } - } else { - p = malloc(sizeof(DECODE_PACKET) + _msg->length); - - if (p) { - p->size = _msg->length; - memcpy(p->data, _msg->data, _msg->length); - } - - rtp_free_msg(NULL, _msg); - } - - if (p) { - /* do the decoding on another thread */ - pthread_mutex_lock(&call->decode_cond_mutex); - uint8_t w = call->audio_decode_write; - - if (call->audio_decode_queue[w] == NULL) { - call->audio_decode_queue[w] = p; - call->audio_decode_write = (w + 1) % AUDIO_DECODE_QUEUE_SIZE; - pthread_cond_signal(&call->decode_cond); - } else { - LOGGER_DEBUG("Dropped audio frame\n"); - free(p); - } - - pthread_mutex_unlock(&call->decode_cond_mutex); - } else { - //malloc failed - } - } - - } else { - uint8_t *packet = _msg->data; - int recved_size = _msg->length; - - if (recved_size < VIDEOFRAME_HEADER_SIZE) { - goto end; - } - - uint8_t i = packet[0] - call->frame_id; - - if (i == 0) { - /* piece of current frame */ - } else if (i > 0 && i < 128) { - /* recieved a piece of a frame ahead, flush current frame and start reading this new frame */ - DECODE_PACKET *p = malloc(sizeof(DECODE_PACKET) + call->frame_limit); - - if (p) { - p->size = call->frame_limit; - memcpy(p->data, call->frame_buf, call->frame_limit); - - /* do the decoding on another thread */ - pthread_mutex_lock(&call->decode_cond_mutex); - uint8_t w = call->video_decode_write; - - if (call->video_decode_queue[w] == NULL) { - call->video_decode_queue[w] = p; - call->video_decode_write = (w + 1) % VIDEO_DECODE_QUEUE_SIZE; - pthread_cond_signal(&call->decode_cond); - } else { - LOGGER_DEBUG("Dropped video frame\n"); - free(p); - } - - pthread_mutex_unlock(&call->decode_cond_mutex); - } else { - //malloc failed - } - - call->frame_id = packet[0]; - memset(call->frame_buf, 0, call->frame_limit); - call->frame_limit = 0; - } else { - /* old packet, dont read */ - LOGGER_DEBUG("Old packet: %u\n", i); - goto end; - } - - if (packet[1] > (MAX_VIDEOFRAME_SIZE - VIDEOFRAME_PIECE_SIZE + 1) / - VIDEOFRAME_PIECE_SIZE) { //TODO, fix this check? not sure - /* packet out of buffer range */ - goto end; - } - - LOGGER_DEBUG("Video Packet: %u %u\n", packet[0], packet[1]); - memcpy(call->frame_buf + packet[1] * VIDEOFRAME_PIECE_SIZE, packet + VIDEOFRAME_HEADER_SIZE, - recved_size - VIDEOFRAME_HEADER_SIZE); - uint32_t limit = packet[1] * VIDEOFRAME_PIECE_SIZE + recved_size - VIDEOFRAME_HEADER_SIZE; - - if (limit > call->frame_limit) { - call->frame_limit = limit; - LOGGER_DEBUG("Limit: %u\n", call->frame_limit); - } - -end: - ; - rtp_free_msg(NULL, _msg); - } -} diff --git a/protocols/Tox/toxcore/toxav/toxav.h b/protocols/Tox/toxcore/toxav/toxav.h deleted file mode 100644 index e31c7aad1f..0000000000 --- a/protocols/Tox/toxcore/toxav/toxav.h +++ /dev/null @@ -1,389 +0,0 @@ -/** toxav.h - * - * 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/>. - * - */ - - -#ifndef __TOXAV -#define __TOXAV -#include <inttypes.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* vpx_image_t */ -#include <vpx/vpx_image.h> - -typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); -typedef struct _ToxAv ToxAv; - -#ifndef __TOX_DEFINED__ -#define __TOX_DEFINED__ -typedef struct Tox Tox; -#endif - -#define RTP_PAYLOAD_SIZE 65535 - - -/** - * @brief Callbacks ids that handle the call states. - */ -typedef enum { - /* Requests */ - av_OnInvite, - av_OnStart, - av_OnCancel, - av_OnReject, - av_OnEnd, - - /* Responses */ - av_OnRinging, - av_OnStarting, - av_OnEnding, - - /* Protocol */ - av_OnRequestTimeout, - av_OnPeerTimeout, - av_OnMediaChange -} ToxAvCallbackID; - - -/** - * @brief Call type identifier. - */ -typedef enum { - TypeAudio = 192, - TypeVideo -} ToxAvCallType; - - -typedef enum { - av_CallNonExistant = -1, - av_CallInviting, /* when sending call invite */ - av_CallStarting, /* when getting call invite */ - av_CallActive, - av_CallHold, - av_CallHanged_up -} ToxAvCallState; - -/** - * @brief Error indicators. - */ -typedef enum { - ErrorNone = 0, - ErrorInternal = -1, /* Internal error */ - ErrorAlreadyInCall = -2, /* Already has an active call */ - ErrorNoCall = -3, /* Trying to perform call action while not in a call */ - ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/ - ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */ - ErrorAudioPacketLost = -6, /* Indicating packet loss */ - ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */ - ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */ - ErrorTerminatingAudioRtp = -9, /* Returned in toxav_kill_transmission() */ - ErrorTerminatingVideoRtp = -10, /* Returned in toxav_kill_transmission() */ - ErrorPacketTooLarge = -11, /* Buffer exceeds size while encoding */ - ErrorInvalidCodecState = -12, /* Codec state not initialized */ - -} ToxAvError; - - -/** - * @brief Locally supported capabilities. - */ -typedef enum { - AudioEncoding = 1 << 0, - AudioDecoding = 1 << 1, - VideoEncoding = 1 << 2, - VideoDecoding = 1 << 3 -} ToxAvCapabilities; - - -/** - * @brief Encoding settings. - */ -typedef struct _ToxAvCodecSettings { - ToxAvCallType call_type; - - uint32_t video_bitrate; /* In kbits/s */ - uint16_t max_video_width; /* In px */ - uint16_t max_video_height; /* In px */ - - uint32_t audio_bitrate; /* In bits/s */ - uint16_t audio_frame_duration; /* In ms */ - uint32_t audio_sample_rate; /* In Hz */ - uint32_t audio_channels; -} ToxAvCSettings; - -extern const ToxAvCSettings av_DefaultSettings; -extern const uint32_t av_jbufdc; /* Jitter buffer default capacity */ -extern const uint32_t av_VADd; /* VAD default treshold */ - -/** - * @brief Start new A/V session. There can only be one session at the time. If you register more - * it will result in undefined behaviour. - * - * @param messenger The messenger handle. - * @param userdata The agent handling A/V session (i.e. phone). - * @param video_width Width of video frame. - * @param video_height Height of video frame. - * @return ToxAv* - * @retval NULL On error. - */ -ToxAv *toxav_new(Tox *messenger, int32_t max_calls); - -/** - * @brief Remove A/V session. - * - * @param av Handler. - * @return void - */ -void toxav_kill(ToxAv *av); - -/** - * @brief Register callback for call state. - * - * @param av Handler. - * @param callback The callback - * @param id One of the ToxAvCallbackID values - * @return void - */ -void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback callback, ToxAvCallbackID id, void *userdata); - -/** - * @brief Register callback for recieving audio data - * - * @param av Handler. - * @param callback The callback - * @return void - */ -void toxav_register_audio_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, int16_t *, int, void *), - void *user_data); - -/** - * @brief Register callback for recieving video data - * - * @param av Handler. - * @param callback The callback - * @return void - */ -void toxav_register_video_recv_callback (ToxAv *av, void (*callback)(ToxAv *, int32_t, vpx_image_t *, void *), - void *user_data); - -/** - * @brief Call user. Use its friend_id. - * - * @param av Handler. - * @param user The user. - * @param call_type Call type. - * @param ringing_seconds Ringing timeout. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_call(ToxAv *av, int32_t *call_index, int user, const ToxAvCSettings *csettings, int ringing_seconds); - -/** - * @brief Hangup active call. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_hangup(ToxAv *av, int32_t call_index); - -/** - * @brief Answer incomming call. - * - * @param av Handler. - * @param call_type Answer with... - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); - -/** - * @brief Reject incomming call. - * - * @param av Handler. - * @param reason Optional reason. Set NULL if none. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); - -/** - * @brief Cancel outgoing request. - * - * @param av Handler. - * @param reason Optional reason. - * @param peer_id peer friend_id - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); - -/** - * @brief Notify peer that we are changing call settings - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); - -/** - * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_stop_call(ToxAv *av, int32_t call_index); - -/** - * @brief Must be call before any RTP transmission occurs. - * - * @param av Handler. - * @param support_video Is video supported ? 1 : 0 - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_prepare_transmission(ToxAv *av, int32_t call_index, uint32_t jbuf_size, uint32_t VAD_treshold, - int support_video); - -/** - * @brief Call this at the end of the transmission. - * - * @param av Handler. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_kill_transmission(ToxAv *av, int32_t call_index); - -/** - * @brief Encode and send video packet. - * - * @param av Handler. - * @param frame The encoded frame. - * @param frame_size The size of the encoded frame. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size); - -/** - * @brief Send audio frame. - * - * @param av Handler. - * @param data The audio data encoded with toxav_prepare_audio_frame(). - * @param size Its size in number of bytes. - * @return int - * @retval 0 Success. - * @retval ToxAvError On error. - */ -int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); - -/** - * @brief Encode video frame - * - * @param av Handler - * @param dest Where to - * @param dest_max Max size - * @param input What to encode - * @return int - * @retval ToxAvError On error. - * @retval >0 On success - */ -int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input ); - -/** - * @brief Encode audio frame - * - * @param av Handler - * @param dest dest - * @param dest_max Max dest size - * @param frame The frame - * @param frame_size The frame size - * @return int - * @retval ToxAvError On error. - * @retval >0 On success - */ -int toxav_prepare_audio_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, const int16_t *frame, - int frame_size); - -/** - * @brief Get peer transmission type. It can either be audio or video. - * - * @param av Handler. - * @param peer The peer - * @return int - * @retval ToxAvCallType On success. - * @retval ToxAvError On error. - */ -int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); - -/** - * @brief Get id of peer participating in conversation - * - * @param av Handler - * @param peer peer index - * @return int - * @retval ToxAvError No peer id - */ -int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); - -/** - * @brief Get current call state - * - * @param av Handler - * @param call_index What call - * @return int - * @retval ToxAvCallState State id - */ -ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); -/** - * @brief Is certain capability supported - * - * @param av Handler - * @return int - * @retval 1 Yes. - * @retval 0 No. - */ -int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); - - -Tox *toxav_get_tox(ToxAv *av); - -int toxav_has_activity ( ToxAv *av, int32_t call_index, int16_t *PCM, uint16_t frame_size, float ref_energy ); - -#ifdef __cplusplus -} -#endif - -#endif /* __TOXAV */ |