From 3fd0ee5f0873924b4881b0e33eb1c17ea877ab4a Mon Sep 17 00:00:00 2001 From: mannol Date: Tue, 21 Apr 2015 02:31:12 +0200 Subject: Final touchups --- toxav/audio.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 toxav/audio.c (limited to 'toxav/audio.c') diff --git a/toxav/audio.c b/toxav/audio.c new file mode 100644 index 00000000..f3e969e9 --- /dev/null +++ b/toxav/audio.c @@ -0,0 +1,383 @@ +/** audio.c + * + * Copyright (C) 2013-2015 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 . + * + */ + +#include + +#include "audio.h" +#include "rtp.h" + +#include "../toxcore/logger.h" + +static struct JitterBuffer *jbuf_new(uint32_t capacity); +static void jbuf_clear(struct JitterBuffer *q); +static void jbuf_free(struct JitterBuffer *q); +static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); +static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); + +OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count); +bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); + + + +ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data) +{ + ACSession *ac = calloc(sizeof(ACSession), 1); + + if (!ac) { + LOGGER_WARNING("Allocation failed! Application might misbehave!"); + return NULL; + } + + if (create_recursive_mutex(ac->queue_mutex) != 0) { + LOGGER_WARNING("Failed to create recursive mutex!"); + free(ac); + return NULL; + } + + int status; + ac->decoder = opus_decoder_create(48000, 2, &status ); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); + goto BASE_CLEANUP; + } + + if ( !(ac->j_buf = jbuf_new(3)) ) { + LOGGER_WARNING("Jitter buffer creaton failed!"); + opus_decoder_destroy(ac->decoder); + goto BASE_CLEANUP; + } + + /* Initialize encoders with default values */ + ac->encoder = create_audio_encoder(48000, 48000, 2); + if (ac->encoder == NULL) + goto DECODER_CLEANUP; + + ac->last_encoding_bitrate = 48000; + ac->last_encoding_sampling_rate = 48000; + ac->last_encoding_channel_count = 2; + + ac->last_decoding_channel_count = 2; + ac->last_decoding_sampling_rate = 48000; + ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */ + + /* These need to be set in order to properly + * do error correction with opus */ + ac->last_packet_frame_duration = 120; + ac->last_packet_sampling_rate = 48000; + + ac->av = av; + ac->friend_id = friend_id; + ac->acb.first = cb; + ac->acb.second = cb_data; + + return ac; + +DECODER_CLEANUP: + opus_decoder_destroy(ac->decoder); + jbuf_free(ac->j_buf); +BASE_CLEANUP: + pthread_mutex_destroy(ac->queue_mutex); + free(ac); + return NULL; +} +void ac_kill(ACSession* ac) +{ + if (!ac) + return; + + opus_encoder_destroy(ac->encoder); + opus_decoder_destroy(ac->decoder); + jbuf_free(ac->j_buf); + + pthread_mutex_destroy(ac->queue_mutex); + + LOGGER_DEBUG("Terminated audio handler: %p", ac); + free(ac); +} +void ac_do(ACSession* ac) +{ + if (!ac) + return; + + /* Enough space for the maximum frame size (120 ms 48 KHz audio) */ + int16_t tmp[5760]; + + RTPMessage *msg; + int rc = 0; + + pthread_mutex_lock(ac->queue_mutex); + while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) { + pthread_mutex_unlock(ac->queue_mutex); + + if (rc == 2) { + LOGGER_DEBUG("OPUS correction"); + rc = opus_decode(ac->decoder, NULL, 0, tmp, + (ac->last_packet_sampling_rate * ac->last_packet_frame_duration / 1000) / + ac->last_packet_channel_count, 1); + } else { + /* Get values from packet and decode. */ + /* NOTE: This didn't work very well + rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); + if (rc != -1) { + cs->last_packet_sampling_rate = rc; + } else { + LOGGER_WARNING("Failed to load packet values!"); + rtp_free_msg(NULL, msg); + continue; + }*/ + + + /* Pick up sampling rate from packet */ + memcpy(&ac->last_packet_sampling_rate, msg->data, 4); + ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate); + + ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4); + + /* + * NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, + * it didn't work quite well. + */ + if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) { + LOGGER_WARNING("Failed to reconfigure decoder!"); + rtp_free_msg(NULL, msg); + continue; + } + + rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); + rtp_free_msg(NULL, msg); + } + + if (rc < 0) { + LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); + } else if (ac->acb.first) { + ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate * ac->last_packet_channel_count; + + ac->acb.first(ac->av, ac->friend_id, tmp, rc * ac->last_packet_channel_count, + ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second); + } + + return; + } + pthread_mutex_unlock(ac->queue_mutex); +} +int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) +{ + if (!ac) + return; + + /* Values are checked in toxav.c */ + if (ac->last_encoding_sampling_rate != sampling_rate || ac->last_encoding_channel_count != channels) { + OpusEncoder* new_encoder = create_audio_encoder(bitrate, sampling_rate, channels); + if (new_encoder == NULL) + return -1; + + opus_encoder_destroy(ac->encoder); + ac->encoder = new_encoder; + } else if (ac->last_encoding_bitrate == bitrate) + return 0; /* Nothing changed */ + else { + int status = opus_encoder_ctl(ac->encoder, OPUS_SET_BITRATE(bitrate)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + return -1; + } + } + + ac->last_encoding_bitrate = bitrate; + ac->last_encoding_sampling_rate = sampling_rate; + ac->last_encoding_channel_count = channels; + + LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); + return 0; +} +/* called from rtp */ +void ac_queue_message(void* acp, RTPMessage *msg) +{ + if (!acp || !msg) + return; + + ACSession* ac = acp; + + pthread_mutex_lock(ac->queue_mutex); + int ret = jbuf_write(ac->j_buf, msg); + pthread_mutex_unlock(ac->queue_mutex); + + if (ret == -1) + rtp_free_msg(NULL, msg); +} + + +/* JITTER BUFFER WORK */ +struct JitterBuffer { + RTPMessage **queue; + uint32_t size; + uint32_t capacity; + uint16_t bottom; + uint16_t top; +}; + +static struct JitterBuffer *jbuf_new(uint32_t capacity) +{ + unsigned int size = 1; + + while (size <= (capacity * 4)) { + size *= 2; + } + + struct JitterBuffer *q; + + if ( !(q = calloc(sizeof(struct 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 jbuf_clear(struct 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; + } + } +} +static void jbuf_free(struct JitterBuffer *q) +{ + if (!q) return; + + jbuf_clear(q); + free(q->queue); + free(q); +} +static int jbuf_write(struct JitterBuffer *q, RTPMessage *m) +{ + uint16_t sequnum = m->header->sequnum; + + unsigned int num = sequnum % q->size; + + if ((uint32_t)(sequnum - q->bottom) > q->size) { + LOGGER_DEBUG("Clearing filled jitter buffer: %p", q); + + jbuf_clear(q); + q->bottom = sequnum - q->capacity; + q->queue[num] = m; + q->top = sequnum + 1; + return 0; + } + + if (q->queue[num]) + return -1; + + q->queue[num] = m; + + if ((sequnum - q->bottom) >= (q->top - q->bottom)) + q->top = sequnum + 1; + + return 0; +} +static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *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; +} +OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count) +{ + int status = OPUS_OK; + OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status)); + return NULL; + } + + status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bitrate)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + goto FAILURE; + } + + status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + goto FAILURE; + } + + return rc; + +FAILURE: + opus_encoder_destroy(rc); + return NULL; +} +bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels) +{ + if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) { + if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500) + return false; + + int status; + OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status)); + return false; + } + + ac->last_decoding_sampling_rate = sampling_rate; + ac->last_decoding_channel_count = channels; + ac->last_decoder_reconfiguration = current_time_monotonic(); + + opus_decoder_destroy(ac->decoder); + ac->decoder = new_dec; + + LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels); + } + + return true; +} \ No newline at end of file -- cgit v1.2.3 From 1bfd93e64a2a6d3bf9c90a9aa89abd29f3d826a7 Mon Sep 17 00:00:00 2001 From: mannol Date: Wed, 22 Apr 2015 02:09:37 +0200 Subject: Finished refactoring --- toxav/Makefile.inc | 2 - toxav/audio.c | 62 +++-- toxav/audio.h | 3 + toxav/av_test.c | 7 +- toxav/codec.c | 686 ----------------------------------------------------- toxav/codec.h | 125 ---------- toxav/msi.c | 50 ++-- toxav/rtp.c | 41 +--- toxav/rtp.h | 46 ++-- toxav/toxav.c | 371 ++++++++++++++--------------- toxav/video.c | 70 +++--- toxav/video.h | 3 + 12 files changed, 338 insertions(+), 1128 deletions(-) delete mode 100644 toxav/codec.c delete mode 100644 toxav/codec.h (limited to 'toxav/audio.c') diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index d9adb4fe..0434a3c6 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -10,8 +10,6 @@ libtoxav_la_SOURCES = ../toxav/rtp.h \ ../toxav/msi.c \ ../toxav/group.h \ ../toxav/group.c \ - ../toxav/codec.h \ - ../toxav/codec.c \ ../toxav/audio.h \ ../toxav/audio.c \ ../toxav/video.h \ diff --git a/toxav/audio.c b/toxav/audio.c index f3e969e9..dc85452a 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -83,6 +83,7 @@ ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *c * do error correction with opus */ ac->last_packet_frame_duration = 120; ac->last_packet_sampling_rate = 48000; + ac->last_packet_channel_count = 1; ac->av = av; ac->friend_id = friend_id; @@ -119,7 +120,7 @@ void ac_do(ACSession* ac) return; /* Enough space for the maximum frame size (120 ms 48 KHz audio) */ - int16_t tmp[5760]; + int16_t tmp[5760 * 2]; RTPMessage *msg; int rc = 0; @@ -130,9 +131,8 @@ void ac_do(ACSession* ac) if (rc == 2) { LOGGER_DEBUG("OPUS correction"); - rc = opus_decode(ac->decoder, NULL, 0, tmp, - (ac->last_packet_sampling_rate * ac->last_packet_frame_duration / 1000) / - ac->last_packet_channel_count, 1); + int fs = (ac->last_packet_sampling_rate * ac->last_packet_frame_duration) / 1000; + rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1); } else { /* Get values from packet and decode. */ /* NOTE: This didn't work very well @@ -152,10 +152,9 @@ void ac_do(ACSession* ac) ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4); - /* - * NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, - * it didn't work quite well. - */ + /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, + * it didn't work quite well. + */ if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) { LOGGER_WARNING("Failed to reconfigure decoder!"); rtp_free_msg(NULL, msg); @@ -169,7 +168,7 @@ void ac_do(ACSession* ac) if (rc < 0) { LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); } else if (ac->acb.first) { - ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate * ac->last_packet_channel_count; + ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate; ac->acb.first(ac->av, ac->friend_id, tmp, rc * ac->last_packet_channel_count, ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second); @@ -179,6 +178,37 @@ void ac_do(ACSession* ac) } pthread_mutex_unlock(ac->queue_mutex); } +int ac_queue_message(void* acp, struct RTPMessage_s *msg) +{ + if (!acp || !msg) + return -1; + + if ((msg->header->marker_payloadt & 0x7f) == rtp_TypeDummyAudio % 128) { + LOGGER_WARNING("Got dummy!"); + rtp_free_msg(NULL, msg); + return 0; + } + + if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) { + LOGGER_WARNING("Invalid payload type!"); + rtp_free_msg(NULL, msg); + return -1; + } + + ACSession* ac = acp; + + pthread_mutex_lock(ac->queue_mutex); + int rc = jbuf_write(ac->j_buf, msg); + pthread_mutex_unlock(ac->queue_mutex); + + if (rc == -1) { + LOGGER_WARNING("Could not queue the message!"); + rtp_free_msg(NULL, msg); + return -1; + } + + return 0; +} int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) { if (!ac) @@ -210,21 +240,7 @@ int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); return 0; } -/* called from rtp */ -void ac_queue_message(void* acp, RTPMessage *msg) -{ - if (!acp || !msg) - return; - - ACSession* ac = acp; - - pthread_mutex_lock(ac->queue_mutex); - int ret = jbuf_write(ac->j_buf, msg); - pthread_mutex_unlock(ac->queue_mutex); - if (ret == -1) - rtp_free_msg(NULL, msg); -} /* JITTER BUFFER WORK */ diff --git a/toxav/audio.h b/toxav/audio.h index 62a28cdf..2cb0d8f6 100644 --- a/toxav/audio.h +++ b/toxav/audio.h @@ -29,6 +29,8 @@ #include "../toxcore/util.h" +struct RTPMessage_s; + typedef struct ACSession_s { /* encoding */ OpusEncoder *encoder; @@ -56,5 +58,6 @@ typedef struct ACSession_s { ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data); void ac_kill(ACSession* ac); void ac_do(ACSession* ac); +int ac_queue_message(void *acp, struct RTPMessage_s *msg); int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels); #endif /* AUDIO_H */ \ No newline at end of file diff --git a/toxav/av_test.c b/toxav/av_test.c index 994f9cf6..dce63184 100644 --- a/toxav/av_test.c +++ b/toxav/av_test.c @@ -77,8 +77,8 @@ #define TEST_REJECT 0 #define TEST_CANCEL 0 #define TEST_MUTE_UNMUTE 0 -#define TEST_TRANSFER_A 1 -#define TEST_TRANSFER_V 0 +#define TEST_TRANSFER_A 0 +#define TEST_TRANSFER_V 1 typedef struct { @@ -329,6 +329,9 @@ void* iterate_toxav (void * data) toxav_iterate(data_cast->BobAV); int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); + printf("\rIteration interval: %d ", rc); + fflush(stdout); + #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 cvWaitKey(rc); #else diff --git a/toxav/codec.c b/toxav/codec.c deleted file mode 100644 index 57e43c67..00000000 --- a/toxav/codec.c +++ /dev/null @@ -1,686 +0,0 @@ -/** codec.c - * - * Copyright (C) 2013-2015 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 . - * - */ - - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include "../toxcore/logger.h" -#include "../toxcore/util.h" - -#include -#include -#include -#include -#include -#include - -#include "msi.h" -#include "rtp.h" -#include "codec.h" - -#define DEFAULT_JBUF 3 - -/* Good quality encode. */ -#define MAX_DECODE_TIME_US 0 - -#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ -#define VIDEOFRAME_HEADER_SIZE 0x2 - -/* FIXME: Might not be enough? NOTE: I think it is enough */ -#define VIDEO_DECODE_BUFFER_SIZE 20 - -#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } - -typedef ARRAY(uint8_t) Payload; - -/* JITTER BUFFER WORK */ -typedef struct JitterBuffer_s { - RTPMessage **queue; - uint32_t size; - uint32_t capacity; - uint16_t bottom; - uint16_t top; -} JitterBuffer; - -static JitterBuffer *jbuf_new(uint32_t capacity) -{ - unsigned int size = 1; - - while (size <= (capacity * 4)) { - 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 jbuf_clear(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; - } - } -} - -static void jbuf_free(JitterBuffer *q) -{ - if (!q) return; - - jbuf_clear(q); - free(q->queue); - free(q); -} - -static int jbuf_write(JitterBuffer *q, RTPMessage *m) -{ - uint16_t sequnum = m->header->sequnum; - - unsigned int num = sequnum % q->size; - - if ((uint32_t)(sequnum - q->bottom) > q->size) { - LOGGER_DEBUG("Clearing filled jitter buffer: %p", q); - - jbuf_clear(q); - q->bottom = sequnum - q->capacity; - q->queue[num] = m; - q->top = sequnum + 1; - return 0; - } - - if (q->queue[num]) - return -1; - - q->queue[num] = m; - - if ((sequnum - q->bottom) >= (q->top - q->bottom)) - q->top = sequnum + 1; - - return 0; -} - -/* success is set to 0 when there is nothing to dequeue, - * 1 when there's a good packet, - * 2 when there's a lost packet */ -static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *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; -} - -static int convert_bw_to_sampling_rate(int bw) -{ - switch(bw) - { - case OPUS_BANDWIDTH_NARROWBAND: return 8000; - case OPUS_BANDWIDTH_MEDIUMBAND: return 12000; - case OPUS_BANDWIDTH_WIDEBAND: return 16000; - case OPUS_BANDWIDTH_SUPERWIDEBAND: return 24000; - case OPUS_BANDWIDTH_FULLBAND: return 48000; - default: return -1; - } -} - -OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count) -{ - int status = OPUS_OK; - OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status)); - return NULL; - } - - status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bitrate)); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); - goto FAILURE; - } - - status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10)); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); - goto FAILURE; - } - - return rc; - -FAILURE: - opus_encoder_destroy(rc); - return NULL; -} - -bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate) -{ - assert(dest); - - 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 false; - } - - rc = vpx_codec_enc_init_ver(dest, 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 false; - } - - cfg.rc_target_bitrate = bitrate; - cfg.g_w = 800; - cfg.g_h = 600; - 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 = 48; - cfg.kf_mode = VPX_KF_AUTO; - - rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8); - - if ( rc != VPX_CODEC_OK) { - LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); - vpx_codec_destroy(dest); - } - - return true; -} - -bool reconfigure_audio_decoder(CSession* cs, int32_t sampling_rate, int8_t channels) -{ - if (sampling_rate != cs->last_decoding_sampling_rate || channels != cs->last_decoding_channel_count) { - if (current_time_monotonic() - cs->last_decoder_reconfiguration < 500) - return false; - - int status; - OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status)); - return false; - } - - cs->last_decoding_sampling_rate = sampling_rate; - cs->last_decoding_channel_count = channels; - cs->last_decoder_reconfiguration = current_time_monotonic(); - - opus_decoder_destroy(cs->audio_decoder); - cs->audio_decoder = new_dec; - - LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels); - } - - return true; -} - -/* PUBLIC */ -void cs_do(CSession *cs) -{ - /* Codec session should always be protected by call mutex so no need to check for cs validity - */ - - if (!cs) - return; - - Payload *p; - int rc; - - int success = 0; - - LOGGED_LOCK(cs->queue_mutex); - - /********************* AUDIO *********************/ - if (cs->audio_decoder) { - RTPMessage *msg; - - /* The maximum for 120 ms 48 KHz audio */ - int16_t tmp[5760]; - - while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { - LOGGED_UNLOCK(cs->queue_mutex); - - if (success == 2) { - LOGGER_DEBUG("OPUS correction"); - rc = opus_decode(cs->audio_decoder, NULL, 0, tmp, - (cs->last_packet_sampling_rate * cs->last_packet_frame_duration / 1000) / - cs->last_packet_channel_count, 1); - } else { - /* Get values from packet and decode. */ - /* NOTE: This didn't work very well - rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); - if (rc != -1) { - cs->last_packet_sampling_rate = rc; - } else { - LOGGER_WARNING("Failed to load packet values!"); - rtp_free_msg(NULL, msg); - continue; - }*/ - - - /* Pick up sampling rate from packet */ - memcpy(&cs->last_packet_sampling_rate, msg->data, 4); - cs->last_packet_sampling_rate = ntohl(cs->last_packet_sampling_rate); - - cs->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4); - - /* - * NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, - * it didn't work quite well. - */ - if (!reconfigure_audio_decoder(cs, cs->last_packet_sampling_rate, cs->last_packet_channel_count)) { - LOGGER_WARNING("Failed to reconfigure decoder!"); - rtp_free_msg(NULL, msg); - continue; - } - - rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); - rtp_free_msg(NULL, msg); - } - - if (rc < 0) { - LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); - } else if (cs->acb.first) { - cs->last_packet_frame_duration = (rc * 1000) / cs->last_packet_sampling_rate * cs->last_packet_channel_count; - - cs->acb.first(cs->av, cs->friend_id, tmp, rc * cs->last_packet_channel_count, - cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->acb.second); - - } - - LOGGED_LOCK(cs->queue_mutex); - } - } - - /********************* VIDEO *********************/ - if (cs->vbuf_raw && !rb_empty(cs->vbuf_raw)) { - /* Decode video */ - rb_read(cs->vbuf_raw, (void**)&p); - - /* Leave space for (possibly) other thread to queue more data after we read it here */ - LOGGED_UNLOCK(cs->queue_mutex); - - rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); - free(p); - - if (rc != VPX_CODEC_OK) { - LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); - } else { - vpx_codec_iter_t iter = NULL; - vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); - - /* Play decoded images */ - for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { - if (cs->vcb.first) - cs->vcb.first(cs->av, cs->friend_id, dest->d_w, dest->d_h, - (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2], - dest->stride[0], dest->stride[1], dest->stride[2], cs->vcb.second); - - vpx_img_free(dest); - } - } - - return; - } - - LOGGED_UNLOCK(cs->queue_mutex); -} -CSession *cs_new(uint32_t peer_video_frame_piece_size) -{ - CSession *cs = calloc(sizeof(CSession), 1); - - if (!cs) { - LOGGER_WARNING("Allocation failed! Application might misbehave!"); - return NULL; - } - - if (create_recursive_mutex(cs->queue_mutex) != 0) { - LOGGER_WARNING("Failed to create recursive mutex!"); - free(cs); - return NULL; - } - - /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ - /* Create decoders and set up their values - */ - - /* - * AUDIO - */ - - int status; - cs->audio_decoder = opus_decoder_create(48000, 2, &status ); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); - goto FAILURE; - } - - cs->last_decoding_channel_count = 2; - cs->last_decoding_sampling_rate = 48000; - cs->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */ - - /* These need to be set in order to properly - * do error correction with opus */ - cs->last_packet_frame_duration = 120; - cs->last_packet_sampling_rate = 48000; - - if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { - LOGGER_WARNING("Jitter buffer creaton failed!"); - opus_decoder_destroy(cs->audio_decoder); - goto FAILURE; - } - - /* - * VIDEO - */ - 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)); - goto AUDIO_DECODER_CLEANUP; - } - - if ( !(cs->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) ) { - vpx_codec_destroy(cs->v_decoder); - goto AUDIO_DECODER_CLEANUP; - } - - if ( !(cs->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) ) { - free(cs->frame_buf); - vpx_codec_destroy(cs->v_decoder); - goto AUDIO_DECODER_CLEANUP; - } - - if ( !(cs->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) ) - goto FAILURE; - - cs->linfts = current_time_monotonic(); - cs->lcfd = 60; - /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ - - /* Initialize encoders with default values */ - cs->audio_encoder = create_audio_encoder(48000, 48000, 2); - if (cs->audio_encoder == NULL) - goto VIDEO_DECODER_CLEANUP; - - cs->last_encoding_bitrate = 48000; - cs->last_encoding_sampling_rate = 48000; - cs->last_encoding_channel_count = 2; - - if (!create_video_encoder(cs->v_encoder, 500000)) { - opus_encoder_destroy(cs->audio_encoder); - goto VIDEO_DECODER_CLEANUP; - } - - cs->peer_video_frame_piece_size = peer_video_frame_piece_size; - - return cs; - -VIDEO_DECODER_CLEANUP: - rb_free(cs->vbuf_raw); - free(cs->frame_buf); - vpx_codec_destroy(cs->v_decoder); -AUDIO_DECODER_CLEANUP: - opus_decoder_destroy(cs->audio_decoder); - jbuf_free(cs->j_buf); -FAILURE: - pthread_mutex_destroy(cs->queue_mutex); - free(cs); - return NULL; -} -void cs_kill(CSession *cs) -{ - if (!cs) - return; - - /* NOTE: queue_message() will not be called since - * the callback is unregistered before cs_kill is called. - */ - - opus_encoder_destroy(cs->audio_encoder); - opus_decoder_destroy(cs->audio_decoder); - jbuf_free(cs->j_buf); - vpx_codec_destroy(cs->v_encoder); - vpx_codec_destroy(cs->v_decoder); - rb_free(cs->vbuf_raw); - free(cs->frame_buf); - free(cs->split_video_frame); - - pthread_mutex_destroy(cs->queue_mutex); - - LOGGER_DEBUG("Terminated codec state: %p", cs); - free(cs); -} -void cs_init_video_splitter_cycle(CSession* cs) -{ - cs->split_video_frame[0] = cs->frameid_out++; - cs->split_video_frame[1] = 0; -} -int cs_update_video_splitter_cycle(CSession *cs, const uint8_t *payload, uint16_t length) -{ - cs->processing_video_frame = payload; - cs->processing_video_frame_size = length; - - return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1; -} -const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) -{ - if (!cs || !size) return NULL; - - if (cs->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) { - memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, - cs->processing_video_frame, - VIDEOFRAME_PIECE_SIZE); - - cs->processing_video_frame += VIDEOFRAME_PIECE_SIZE; - cs->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE; - - *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE; - } else { - memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, - cs->processing_video_frame, - cs->processing_video_frame_size); - - *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; - } - - cs->split_video_frame[1]++; - - return cs->split_video_frame; -} -int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height) -{ - vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; - if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) - return 0; /* Nothing changed */ - - cfg.rc_target_bitrate = bitrate; - 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 cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels) -{ - /* Values are checked in toxav.c */ - - if (cs->last_encoding_sampling_rate != sampling_rate || cs->last_encoding_channel_count != channels) { - OpusEncoder* new_encoder = create_audio_encoder(bitrate, sampling_rate, channels); - if (new_encoder == NULL) - return -1; - - opus_encoder_destroy(cs->audio_encoder); - cs->audio_encoder = new_encoder; - } else if (cs->last_encoding_bitrate == bitrate) - return 0; /* Nothing changed */ - else { - int status = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(bitrate)); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); - return -1; - } - } - - cs->last_encoding_bitrate = bitrate; - cs->last_encoding_sampling_rate = sampling_rate; - cs->last_encoding_channel_count = channels; - - LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); - return 0; -} -/* Called from RTP */ -void queue_message(RTPSession *session, RTPMessage *msg) -{ - CSession *cs = session->cs; - - if (!cs) - return; - - /* Audio */ - if (session->payload_type == rtp_TypeAudio % 128) { - LOGGED_LOCK(cs->queue_mutex); - int ret = jbuf_write(cs->j_buf, msg); - LOGGED_UNLOCK(cs->queue_mutex); - - if (ret == -1) { - rtp_free_msg(NULL, msg); - } - } - /* Video */ - else { - uint8_t *packet = msg->data; - uint32_t packet_size = msg->length; - - if (packet_size < VIDEOFRAME_HEADER_SIZE) - goto end; - - uint8_t diff = packet[0] - cs->frameid_in; - - if (diff != 0) { - if (diff < 225) { /* New frame */ - /* Flush last frames' data and get ready for this frame */ - Payload *p = malloc(sizeof(Payload) + cs->frame_size); - - if (p) { - LOGGED_LOCK(cs->queue_mutex); - - if (rb_full(cs->vbuf_raw)) { - LOGGER_DEBUG("Dropped video frame"); - Payload *tp; - rb_read(cs->vbuf_raw, (void**)&tp); - free(tp); - } else { - p->size = cs->frame_size; - memcpy(p->data, cs->frame_buf, cs->frame_size); - } - - /* Calculate time took for peer to send us this frame */ - uint32_t t_lcfd = current_time_monotonic() - cs->linfts; - cs->lcfd = t_lcfd > 100 ? cs->lcfd : t_lcfd; - cs->linfts = current_time_monotonic(); - - rb_write(cs->vbuf_raw, p); - LOGGED_UNLOCK(cs->queue_mutex); - } else { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - goto end; - } - - cs->frameid_in = packet[0]; - memset(cs->frame_buf, 0, cs->frame_size); - cs->frame_size = 0; - - } else { /* Old frame; drop */ - LOGGER_DEBUG("Old packet: %u", packet[0]); - goto end; - } - } - - uint8_t piece_number = packet[1]; - - uint32_t length_before_piece = ((piece_number - 1) * cs->peer_video_frame_piece_size); - uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE); - - if (framebuf_new_length > MAX_VIDEOFRAME_SIZE) { - goto end; - } - - /* Otherwise it's part of the frame so just process */ - /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */ - - memcpy(cs->frame_buf + length_before_piece, - packet + VIDEOFRAME_HEADER_SIZE, - packet_size - VIDEOFRAME_HEADER_SIZE); - - if (framebuf_new_length > cs->frame_size) - cs->frame_size = framebuf_new_length; - -end: - rtp_free_msg(NULL, msg); - } -} diff --git a/toxav/codec.h b/toxav/codec.h deleted file mode 100644 index 497016eb..00000000 --- a/toxav/codec.h +++ /dev/null @@ -1,125 +0,0 @@ -/** codec.h - * - * Copyright (C) 2013-2015 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 . - * - */ - -#ifndef CODEC_H -#define CODEC_H - -#include "toxav.h" -#include "rtp.h" - -#include "../toxcore/util.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx()) -#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx()) - -/* Audio encoding/decoding */ -#include - -typedef struct CSession_s { - - /* VIDEO - * - * - */ - - /* video encoding */ - vpx_codec_ctx_t v_encoder[1]; - uint32_t frame_counter; - - /* video decoding */ - vpx_codec_ctx_t v_decoder[1]; - void *vbuf_raw; /* Un-decoded data */ - - /* Data handling */ - uint8_t *frame_buf; /* buffer for split video payloads */ - uint32_t frame_size; /* largest address written to in frame_buf for current input frame */ - uint8_t frameid_in, frameid_out; /* id of input and output video frame */ - uint64_t linfts; /* Last received frame time stamp */ - uint32_t lcfd; /* Last calculated frame duration for incoming video payload */ - - /* Limits */ - uint32_t peer_video_frame_piece_size; - - /* Splitting */ - uint8_t *split_video_frame; - const uint8_t *processing_video_frame; - uint16_t processing_video_frame_size; - - - - /* AUDIO - * - * - */ - - /* audio encoding */ - OpusEncoder *audio_encoder; - int32_t last_encoding_sampling_rate; - int32_t last_encoding_channel_count; - int32_t last_encoding_bitrate; - - /* audio decoding */ - OpusDecoder *audio_decoder; - int32_t last_packet_channel_count; - int32_t last_packet_sampling_rate; - int32_t last_packet_frame_duration; - int32_t last_decoding_sampling_rate; - int32_t last_decoding_channel_count; - uint64_t last_decoder_reconfiguration; - struct JitterBuffer_s *j_buf; - - - /* OTHER - * - * - */ - ToxAV *av; - int32_t friend_id; - - PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ - PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ - - pthread_mutex_t queue_mutex[1]; -} CSession; - - -void cs_do(CSession *cs); -/* Make sure to be called BEFORE corresponding rtp_new */ -CSession *cs_new(uint32_t peer_mvfpsz); -/* Make sure to be called AFTER corresponding rtp_kill */ -void cs_kill(CSession *cs); - -void cs_init_video_splitter_cycle(CSession *cs); -int cs_update_video_splitter_cycle(CSession* cs, const uint8_t* payload, uint16_t length); -const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size); - -int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height); -int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels); -#endif /* CODEC_H */ diff --git a/toxav/msi.c b/toxav/msi.c index b7926e07..0bd04c56 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -99,9 +99,9 @@ void handle_msi_packet ( Messenger *m, uint32_t friend_id, const uint8_t *data, */ void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id) { - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); session->callbacks[id] = callback; - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); } MSISession *msi_new ( Messenger *messenger ) { @@ -141,7 +141,7 @@ int msi_kill ( MSISession *session ) } m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL); - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); if (session->calls) { MSIMessage msg; @@ -154,7 +154,7 @@ int msi_kill ( MSISession *session ) } } - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); pthread_mutex_destroy(session->mutex); LOGGER_DEBUG("Terminated session: %p", session); @@ -165,17 +165,17 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ { LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); if (get_call(session, friend_id) != NULL) { LOGGER_ERROR("Already in a call"); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return -1; } (*call) = new_call ( session, friend_id ); if ( *call == NULL ) { - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return -1; } @@ -195,7 +195,7 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ (*call)->state = msi_CallRequesting; LOGGER_DEBUG("Invite sent"); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return 0; } int msi_hangup ( MSICall* call ) @@ -203,7 +203,7 @@ int msi_hangup ( MSICall* call ) LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); MSISession* session = call->session; - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); MSIMessage msg; msg_init(&msg, requ_pop); @@ -211,7 +211,7 @@ int msi_hangup ( MSICall* call ) send_message ( session->messenger, call->friend_id, &msg ); kill_call(call); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return 0; } int msi_answer ( MSICall* call, uint8_t capabilities ) @@ -219,13 +219,13 @@ int msi_answer ( MSICall* call, uint8_t capabilities ) LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); MSISession* session = call->session; - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); if ( call->state != msi_CallRequested ) { /* Though sending in invalid state will not cause anything wierd * Its better to not do it like a maniac */ LOGGER_ERROR("Call is in invalid state!"); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return -1; } @@ -243,7 +243,7 @@ int msi_answer ( MSICall* call, uint8_t capabilities ) send_message ( session->messenger, call->friend_id, &msg ); call->state = msi_CallActive; - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return 0; } @@ -252,7 +252,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities ) LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id); MSISession* session = call->session; - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); if ( call->state != msi_CallActive ) { /* Sending capabilities change can cause error on other side if @@ -263,7 +263,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities ) * like new. TODO: explain this better */ LOGGER_ERROR("Call is in invalid state!"); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return -1; } @@ -277,7 +277,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities ) send_message ( call->session->messenger, call->friend_id, &msg ); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return 0; } @@ -349,7 +349,7 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) case IDVFPSZ: CHECK_SIZE(it, size_constraint, 2); SET_UINT16(it, dest->vfpsz); - dest->vfpsz = ntohs(dest->vfpsz); + dest->vfpsz.value = ntohs(dest->vfpsz.value); if (dest->vfpsz.value > 1200) { LOGGER_ERROR("Invalid vfpsz param"); @@ -425,7 +425,7 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ) } if (msg->vfpsz.exists) { - uint16_t nb_vfpsz = htons(msg->vfpsz); + uint16_t nb_vfpsz = htons(msg->vfpsz.value); it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz, sizeof(nb_vfpsz), &size); } @@ -588,17 +588,17 @@ void on_peer_status(Messenger* m, uint32_t friend_id, uint8_t status, void* data case 0: { /* Friend is now offline */ LOGGER_DEBUG("Friend %d is now offline", friend_id); - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); MSICall* call = get_call(session, friend_id); if (call == NULL) { - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return; } invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */ kill_call(call); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); } break; @@ -766,20 +766,20 @@ void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, LOGGER_DEBUG("Successfully parsed message"); } - LOGGED_LOCK(session->mutex); + pthread_mutex_lock(session->mutex); MSICall *call = get_call(session, friend_id); if (call == NULL) { if (msg.request.value != requ_push) { send_error(m, friend_id, msi_EStrayMessage); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return; } call = new_call(session, friend_id); if (call == NULL) { send_error(m, friend_id, msi_ESystem); - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); return; } } @@ -789,5 +789,5 @@ void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, else handle_pop(call, &msg); /* always kills the call */ - LOGGED_UNLOCK(session->mutex); + pthread_mutex_unlock(session->mutex); } diff --git a/toxav/rtp.c b/toxav/rtp.c index 9ef41b35..9657da67 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -68,12 +68,6 @@ typedef struct RTCPSession_s { } RTCPSession; - -/* These are defined externally */ -void ac_queue_message(void *acp, RTPMessage *msg); -void vc_queue_message(void *vcp, RTPMessage *msg); - - RTPHeader *parse_header_in ( const uint8_t *payload, int length ); RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); RTPMessage *msg_parse ( const uint8_t *data, int length ); @@ -100,7 +94,7 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) retu->tstate = rtp_StateNormal; retu->m = messenger; - retu->dest = friend_num; + retu->friend_id = friend_num; if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) { LOGGER_WARNING("Alloc failed! Program might misbehave!"); @@ -155,7 +149,7 @@ void rtp_do(RTPSession *session) return; if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) { - send_rtcp_report(session->rtcp_session, session->m, session->dest); + send_rtcp_report(session->rtcp_session, session->m, session->friend_id); } if (rb_full(session->rtcp_session->pl_stats)) { @@ -202,15 +196,15 @@ int rtp_start_receiving(RTPSession* session) if (session == NULL) return -1; - if (m_callback_rtp_packet(session->m, session->dest, session->prefix, + if (m_callback_rtp_packet(session->m, session->friend_id, session->prefix, handle_rtp_packet, session) == -1) { LOGGER_WARNING("Failed to register rtp receive handler"); return -1; } - if (m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix, + if (m_callback_rtp_packet(session->m, session->friend_id, session->rtcp_session->prefix, handle_rtcp_packet, session->rtcp_session) == -1) { LOGGER_WARNING("Failed to register rtcp receive handler"); - m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL); + m_callback_rtp_packet(session->m, session->friend_id, session->prefix, NULL, NULL); return -1; } @@ -221,8 +215,8 @@ int rtp_stop_receiving(RTPSession* session) if (session == NULL) return -1; - m_callback_rtp_packet(session->m, session->dest, session->prefix, NULL, NULL); - m_callback_rtp_packet(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ + m_callback_rtp_packet(session->m, session->friend_id, session->prefix, NULL, NULL); + m_callback_rtp_packet(session->m, session->friend_id, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ return 0; } @@ -253,7 +247,7 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) memcpy ( it, data, length ); - if ( -1 == send_custom_lossy_packet(session->m, session->dest, parsed, parsed_len) ) { + if ( -1 == send_custom_lossy_packet(session->m, session->friend_id, parsed, parsed_len) ) { LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); return -1; } @@ -546,7 +540,6 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber) } int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) { - /* TODO on message callback */ RTPSession *session = object; RTPMessage *msg; @@ -578,20 +571,12 @@ int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data session->rtcp_session->last_received_packets ++; - /* Check if this session can handle the packet */ - if (session->payload_type != session->prefix % 128) { - LOGGER_WARNING("Friend %d sent invalid payload type!", session->dest); - rtp_free_msg(msg); - return -1; + if (session->mcb) + return session->mcb (session->cs, msg); + else { + rtp_free_msg(session, msg); + return 0; } - - /* Handle */ - if (session->payload_type == rtp_TypeAudio % 128) - ac_queue_message(session->cs, msg); - else /* It can only be video */ - vc_queue_message(session->cs, msg); - - return 0; } int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) { diff --git a/toxav/rtp.h b/toxav/rtp.h index a28ae7bc..3056b54e 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -42,11 +42,13 @@ #define MAX_RTP_SIZE 65535 /** - * Payload type identifier. Also used as rtp callback prefix. + * Payload type identifier. Also used as rtp callback prefix. (Not dummies) */ enum { rtp_TypeAudio = 192, - rtp_TypeVideo + rtp_TypeVideo, + rtp_TypeDummyAudio, + rtp_TypeDummyVideo, }; typedef enum { @@ -79,7 +81,7 @@ typedef struct { /** * Standard rtp message. */ -typedef struct { +typedef struct RTPMessage_s { RTPHeader *header; RTPExtHeader *ext_header; @@ -91,34 +93,36 @@ typedef struct { * RTP control session. */ typedef struct { - uint8_t version; - uint8_t padding; - uint8_t extension; - uint8_t cc; - uint8_t marker; - uint8_t payload_type; - uint16_t sequnum; /* Sending sequence number */ - uint16_t rsequnum; /* Receiving sequence number */ - uint32_t rtimestamp; - uint32_t ssrc; - uint32_t *csrc; + uint8_t version; + uint8_t padding; + uint8_t extension; + uint8_t cc; + uint8_t marker; + uint8_t payload_type; + uint16_t sequnum; /* Sending sequence number */ + uint16_t rsequnum; /* Receiving sequence number */ + uint32_t rtimestamp; + 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; + RTPExtHeader *ext_header; /* Msg prefix for core to know when recving */ - uint8_t prefix; - - int dest; + uint8_t prefix; + Messenger *m; + int friend_id; + RTPTransmissionState tstate; struct RTCPSession_s *rtcp_session; - struct CSession_s *cs; - Messenger *m; + + + void *cs; + int (*mcb) (void*, RTPMessage* msg); - RTPTransmissionState tstate; } RTPSession; /** diff --git a/toxav/toxav.c b/toxav/toxav.c index 6f712af9..5cb614d4 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -23,7 +23,8 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#include "msi.h" /* Includes codec.h, rtp.h and toxav.h */ +#include "msi.h" +#include "rtp.h" #include "../toxcore/Messenger.h" #include "../toxcore/logger.h" @@ -35,20 +36,17 @@ #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) -enum { - audio_index, - video_index, -}; typedef struct ToxAVCall_s { ToxAV* av; - RTPSession *rtps[2]; /* Audio is first and video is second */ - CSession *cs; - pthread_mutex_t mutex_audio_sending[1]; - pthread_mutex_t mutex_video_sending[1]; - /* Only audio or video can be decoded at the time */ - pthread_mutex_t mutex_decoding[1]; + pthread_mutex_t mutex_audio[1]; + PAIR(RTPSession *, ACSession *) audio; + + pthread_mutex_t mutex_video[1]; + PAIR(RTPSession *, VCSession *) video; + + pthread_mutex_t mutex[1]; bool active; MSICall* msi_call; @@ -57,8 +55,8 @@ typedef struct ToxAVCall_s { uint32_t audio_bit_rate; /* Sending audio bitrate */ uint32_t video_bit_rate; /* Sending video bitrate */ - uint8_t last_self_capabilities; - uint8_t last_peer_capabilities; + /** Required for monitoring */ + uint8_t previous_self_capabilities; /** Quality control */ uint64_t time_audio_good; @@ -181,7 +179,7 @@ void toxav_kill(ToxAV* av) { if (av == NULL) return; - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); msi_kill(av->msi); @@ -194,7 +192,7 @@ void toxav_kill(ToxAV* av) } } - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); pthread_mutex_destroy(av->mutex); free(av); } @@ -212,9 +210,9 @@ uint32_t toxav_iteration_interval(const ToxAV* av) void toxav_iterate(ToxAV* av) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); if (av->calls == NULL) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); return; } @@ -224,30 +222,36 @@ void toxav_iterate(ToxAV* av) ToxAVCall* i = av->calls[av->calls_head]; for (; i; i = i->next) { if (i->active) { - LOGGED_LOCK(i->mutex_decoding); - LOGGED_UNLOCK(av->mutex); + pthread_mutex_lock(i->mutex); + pthread_mutex_unlock(av->mutex); + + rtp_do(i->audio.first); + ac_do(i->audio.second); + + rtp_do(i->video.first); + vc_do(i->video.second); - cs_do(i->cs); - rtp_do(i->rtps[audio_index]); - rtp_do(i->rtps[video_index]); qc_do(i); - if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */ - rc = MIN(i->cs->last_packet_frame_duration, rc); - if (i->last_self_capabilities & msi_CapRVideo) /* Receiving video */ - rc = MIN(i->cs->lcfd, rc); /* TODO handle on/off */ + if (i->msi_call->self_capabilities & msi_CapRAudio && + i->msi_call->peer_capabilities & msi_CapSAudio) + rc = MIN(i->audio.second->last_packet_frame_duration, rc); + + if (i->msi_call->self_capabilities & msi_CapRVideo && + i->msi_call->peer_capabilities & msi_CapSVideo) + rc = MIN(i->video.second->lcfd, rc); uint32_t fid = i->friend_id; - LOGGED_UNLOCK(i->mutex_decoding); - LOGGED_LOCK(av->mutex); + pthread_mutex_unlock(i->mutex); + pthread_mutex_lock(av->mutex); /* In case this call is popped from container stop iteration */ if (call_get(av, fid) != i) break; } } - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa); av->dmsst += current_time_monotonic() - start; @@ -261,46 +265,46 @@ void toxav_iterate(ToxAV* av) bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); ToxAVCall* call = call_new(av, friend_number, error); if (call == NULL) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); return false; } call->audio_bit_rate = audio_bit_rate; call->video_bit_rate = video_bit_rate; - call->last_self_capabilities = msi_CapRAudio | msi_CapRVideo; + call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; - call->last_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; - call->last_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; + call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; + call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; - if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_self_capabilities) != 0) { + if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) { call_remove(call); if (error) *error = TOXAV_ERR_CALL_MALLOC; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); return false; } call->msi_call->av_call = call; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); return true; } void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->ccb.first = function; av->ccb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; if (m_friend_exists(av->m, friend_number) == 0) { @@ -329,17 +333,17 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui call->audio_bit_rate = audio_bit_rate; call->video_bit_rate = video_bit_rate; - call->last_self_capabilities = msi_CapRAudio | msi_CapRVideo; + call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; - call->last_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; - call->last_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; + call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; + call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; - if (msi_answer(call->msi_call, call->last_self_capabilities) != 0) + if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ END: - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); if (error) *error = rc; @@ -349,15 +353,15 @@ END: void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->scb.first = function; av->scb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; if (m_friend_exists(av->m, friend_number) == 0) { @@ -381,18 +385,18 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co /* Only act if paused and had media transfer active before */ if (call->msi_call->self_capabilities == 0 && - call->last_self_capabilities ) { + call->previous_self_capabilities ) { if (msi_change_capabilities(call->msi_call, - call->last_self_capabilities) == -1) { + call->previous_self_capabilities) == -1) { /* The only reason for this function to fail is invalid state * ( not active ) */ rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; goto END; } - rtp_start_receiving(call->rtps[audio_index]); - rtp_start_receiving(call->rtps[video_index]); + rtp_start_receiving(call->audio.first); + rtp_start_receiving(call->video.first); } } break; @@ -404,7 +408,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co /* Only act if not already paused */ if (call->msi_call->self_capabilities) { - call->last_self_capabilities = call->msi_call->self_capabilities; + call->previous_self_capabilities = call->msi_call->self_capabilities; if (msi_change_capabilities(call->msi_call, 0) == -1 ) { /* The only reason for this function to fail is invalid state @@ -413,8 +417,8 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co goto END; } - rtp_stop_receiving(call->rtps[audio_index]); - rtp_stop_receiving(call->rtps[video_index]); + rtp_stop_receiving(call->audio.first); + rtp_stop_receiving(call->video.first); } } break; @@ -442,7 +446,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co goto END; } - rtp_stop_receiving(call->rtps[audio_index]); + rtp_stop_receiving(call->audio.first); } else { /* This call was already muted so notify the friend that he can * start sending audio again @@ -455,7 +459,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co goto END; } - rtp_start_receiving(call->rtps[audio_index]); + rtp_start_receiving(call->audio.first); } } break; @@ -474,7 +478,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co goto END; } - rtp_stop_receiving(call->rtps[video_index]); + rtp_stop_receiving(call->video.first); } else { /* This call was already muted so notify the friend that he can * start sending video again @@ -487,13 +491,13 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co goto END; } - rtp_start_receiving(call->rtps[video_index]); + rtp_start_receiving(call->video.first); } } break; } END: - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); if (error) *error = rc; @@ -516,19 +520,19 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_ goto END; } - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_BIT_RATE_FRIEND_NOT_IN_CALL; goto END; } /* Decoding mutex is locked because of quality control */ - LOGGED_LOCK(call->mutex_decoding); + pthread_mutex_lock(call->mutex); call->audio_bit_rate = audio_bit_rate; - LOGGED_UNLOCK(call->mutex_decoding); - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(call->mutex); + pthread_mutex_unlock(av->mutex); END: if (error) @@ -552,19 +556,19 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_ goto END; } - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_BIT_RATE_FRIEND_NOT_IN_CALL; goto END; } /* Decoding mutex is locked because of quality control */ - LOGGED_LOCK(call->mutex_decoding); + pthread_mutex_lock(call->mutex); call->video_bit_rate = video_bit_rate; - LOGGED_UNLOCK(call->mutex_decoding); - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(call->mutex); + pthread_mutex_unlock(av->mutex); END: if (error) @@ -575,10 +579,10 @@ END: void toxav_callback_video_frame_request(ToxAV* av, toxav_video_frame_request_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->rvcb.first = function; av->rvcb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) @@ -591,25 +595,25 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u goto END; } - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; goto END; } - LOGGED_LOCK(call->mutex_video_sending); - LOGGED_UNLOCK(av->mutex); + pthread_mutex_lock(call->mutex_video); + pthread_mutex_unlock(av->mutex); if ( y == NULL || u == NULL || v == NULL ) { - LOGGED_UNLOCK(call->mutex_video_sending); + pthread_mutex_unlock(call->mutex_video); rc = TOXAV_ERR_SEND_FRAME_NULL; goto END; } - if ( cs_reconfigure_video_encoder(call->cs, call->video_bit_rate, width, height) != 0 ) { - LOGGED_UNLOCK(call->mutex_video_sending); + if ( vc_reconfigure_encoder(call->video.second, call->video_bit_rate, width, height) != 0 ) { + pthread_mutex_unlock(call->mutex_video); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } @@ -626,29 +630,29 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); - int vrc = vpx_codec_encode(call->cs->v_encoder, &img, - call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); + int vrc = vpx_codec_encode(call->video.second->v_encoder, &img, + call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US); vpx_img_free(&img); if ( vrc != VPX_CODEC_OK) { - LOGGED_UNLOCK(call->mutex_video_sending); + pthread_mutex_unlock(call->mutex_video); LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } } - ++call->cs->frame_counter; + ++call->video.second->frame_counter; { /* Split and send */ vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; - cs_init_video_splitter_cycle(call->cs); + vc_init_video_splitter_cycle(call->video.second); - while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { + while ( (pkt = vpx_codec_get_cx_data(call->video.second->v_encoder, &iter)) ) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - int parts = cs_update_video_splitter_cycle(call->cs, pkt->data.frame.buf, + int parts = vc_update_video_splitter_cycle(call->video.second, pkt->data.frame.buf, pkt->data.frame.sz); if (parts < 0) /* Should never happen though */ @@ -659,10 +663,10 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u int i; for (i = 0; i < parts; i++) { - iter = cs_iterate_split_video_frame(call->cs, &part_size); + iter = vc_iterate_split_video_frame(call->video.second, &part_size); - if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) { - LOGGED_UNLOCK(call->mutex_video_sending); + if (rtp_send_msg(call->video.first, iter, part_size) < 0) { + pthread_mutex_unlock(call->mutex_video); LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); goto END; } @@ -671,7 +675,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u } } - LOGGED_UNLOCK(call->mutex_video_sending); + pthread_mutex_unlock(call->mutex_video); END: if (error) @@ -682,10 +686,10 @@ END: void toxav_callback_audio_frame_request(ToxAV* av, toxav_audio_frame_request_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->racb.first = function; av->racb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) @@ -698,32 +702,32 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc goto END; } - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; goto END; } - LOGGED_LOCK(call->mutex_audio_sending); - LOGGED_UNLOCK(av->mutex); + pthread_mutex_lock(call->mutex_audio); + pthread_mutex_unlock(av->mutex); if ( pcm == NULL ) { - LOGGED_UNLOCK(call->mutex_audio_sending); + pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_NULL; goto END; } if ( channels > 2 ) { - LOGGED_UNLOCK(call->mutex_audio_sending); + pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } { /* Encode and send */ - if (cs_reconfigure_audio_encoder(call->cs, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { - LOGGED_UNLOCK(call->mutex_audio_sending); + if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { + pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } @@ -732,12 +736,12 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc sampling_rate = htonl(sampling_rate); memcpy(dest, &sampling_rate, sizeof(sampling_rate)); - int vrc = opus_encode(call->cs->audio_encoder, pcm, sample_count, + int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count, dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); if (vrc < 0) { LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc)); - LOGGED_UNLOCK(call->mutex_audio_sending); + pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } @@ -745,13 +749,13 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc // LOGGER_DEBUG("Sending encoded audio frame size: %d; channels: %d; srate: %d", vrc, channels, // ntohl(sampling_rate)); - if (rtp_send_msg(call->rtps[audio_index], dest, vrc + sizeof(sampling_rate)) != 0) { + if (rtp_send_msg(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) { LOGGER_WARNING("Failed to send audio packet"); rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; } } - LOGGED_UNLOCK(call->mutex_audio_sending); + pthread_mutex_unlock(call->mutex_audio); END: if (error) @@ -762,18 +766,18 @@ END: void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->vcb.first = function; av->vcb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) { - LOGGED_LOCK(av->mutex); + pthread_mutex_lock(av->mutex); av->acb.first = function; av->acb.second = user_data; - LOGGED_UNLOCK(av->mutex); + pthread_mutex_unlock(av->mutex); } @@ -785,12 +789,12 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* int callback_invite(void* toxav_inst, MSICall* call) { ToxAV* toxav = toxav_inst; - LOGGED_LOCK(toxav->mutex); + pthread_mutex_lock(toxav->mutex); ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL); if (av_call == NULL) { LOGGER_WARNING("Failed to initialize call..."); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return -1; } @@ -801,72 +805,72 @@ int callback_invite(void* toxav_inst, MSICall* call) toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return 0; } int callback_start(void* toxav_inst, MSICall* call) { ToxAV* toxav = toxav_inst; - LOGGED_LOCK(toxav->mutex); + pthread_mutex_lock(toxav->mutex); ToxAVCall* av_call = call_get(toxav, call->friend_id); if (av_call == NULL) { /* Should this ever happen? */ - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return -1; } if (!call_prepare_transmission(av_call)) { callback_error(toxav_inst, call); call_remove(av_call); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return -1; } invoke_call_state(toxav, call->friend_id, call->peer_capabilities); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return 0; } int callback_end(void* toxav_inst, MSICall* call) { ToxAV* toxav = toxav_inst; - LOGGED_LOCK(toxav->mutex); + pthread_mutex_lock(toxav->mutex); invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_END); call_kill_transmission(call->av_call); call_remove(call->av_call); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return 0; } int callback_error(void* toxav_inst, MSICall* call) { ToxAV* toxav = toxav_inst; - LOGGED_LOCK(toxav->mutex); + pthread_mutex_lock(toxav->mutex); invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR); call_kill_transmission(call->av_call); call_remove(call->av_call); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return 0; } int callback_capabilites(void* toxav_inst, MSICall* call) { ToxAV* toxav = toxav_inst; - LOGGED_LOCK(toxav->mutex); + pthread_mutex_lock(toxav->mutex); invoke_call_state(toxav, call->friend_id, call->peer_capabilities); - LOGGED_UNLOCK(toxav->mutex); + pthread_mutex_unlock(toxav->mutex); return 0; } @@ -982,10 +986,8 @@ ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) void qc_do(ToxAVCall* call) { - /* Please NOTE: The quality control is rather basic, - * advanced algorithms will be applied in the future - */ - switch(call->rtps[audio_index]->tstate) { + /* + switch(call->audio.first->tstate) { case rtp_StateBad: LOGGER_DEBUG("Suggesting lower bitrate for audio..."); call->time_audio_good = 0; @@ -1007,9 +1009,9 @@ void qc_do(ToxAVCall* call) case rtp_StateNormal: call->time_audio_good = 0; break; - } - - switch(call->rtps[video_index]->tstate) { + }*/ + /* + switch(call->video.first->tstate) { case rtp_StateBad: LOGGER_DEBUG("Suggesting lower bitrate for video..."); call->time_video_good = 0; @@ -1030,8 +1032,7 @@ void qc_do(ToxAVCall* call) case rtp_StateNormal: call->time_video_good = 0; break; - } - + }*/ } void call_remove(ToxAVCall* call) @@ -1086,61 +1087,50 @@ bool call_prepare_transmission(ToxAVCall* call) return true; } - if (create_recursive_mutex(call->mutex_audio_sending) != 0) + if (create_recursive_mutex(call->mutex_audio) != 0) return false; - if (create_recursive_mutex(call->mutex_video_sending) != 0) { + if (create_recursive_mutex(call->mutex_video) != 0) { goto AUDIO_SENDING_MUTEX_CLEANUP; } - if (create_recursive_mutex(call->mutex_decoding) != 0) { + if (create_recursive_mutex(call->mutex) != 0) { goto VIDEO_SENDING_MUTEX_CLEANUP; } - /* Creates both audio and video encoders and decoders with some default values. - * Make sure to reconfigure encoders dynamically when sending data - */ - call->cs = cs_new(call->msi_call->peer_vfpsz); - - if ( !call->cs ) { - LOGGER_ERROR("Error while starting Codec State!\n"); - goto FAILURE; - } - - call->cs->av = av; - call->cs->friend_id = call->friend_id; - - memcpy(&call->cs->acb, &av->acb, sizeof(av->acb)); - memcpy(&call->cs->vcb, &av->vcb, sizeof(av->vcb)); - { /* Prepare audio RTP */ - call->rtps[audio_index] = rtp_new(rtp_TypeAudio, av->m, call->friend_id); + { /* Prepare audio */ + call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_id); + call->audio.second = ac_new(av, call->friend_id, av->acb.first, av->acb.second); - if ( !call->rtps[audio_index] ) { - LOGGER_ERROR("Error while starting audio RTP session!\n"); + if ( !call->audio.first || !call->audio.second ) { + LOGGER_ERROR("Error while starting audio!\n"); goto FAILURE; } - call->rtps[audio_index]->cs = call->cs; + call->audio.first->cs = call->audio.second; + call->audio.first->mcb = ac_queue_message; - if (rtp_start_receiving(call->rtps[audio_index]) != 0) { + if (rtp_start_receiving(call->audio.first) != 0) { LOGGER_WARNING("Failed to enable audio receiving!"); goto FAILURE; } } - { /* Prepare video RTP */ - call->rtps[video_index] = rtp_new(rtp_TypeVideo, av->m, call->friend_id); + { /* Prepare video */ + call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_id); + call->video.second = vc_new(av, call->friend_id, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); - if ( !call->rtps[video_index] ) { - LOGGER_ERROR("Error while starting video RTP session!\n"); + if ( !call->video.first || !call->video.second ) { + LOGGER_ERROR("Error while starting video!\n"); goto FAILURE; } - call->rtps[video_index]->cs = call->cs; + call->video.first->cs = call->video.second; + call->video.first->mcb = vc_queue_message; - if (rtp_start_receiving(call->rtps[video_index]) != 0) { - LOGGER_WARNING("Failed to enable audio receiving!"); + if (rtp_start_receiving(call->video.first) != 0) { + LOGGER_WARNING("Failed to enable video receiving!"); goto FAILURE; } } @@ -1149,17 +1139,19 @@ bool call_prepare_transmission(ToxAVCall* call) return true; FAILURE: - rtp_kill(call->rtps[audio_index]); - call->rtps[audio_index] = NULL; - rtp_kill(call->rtps[video_index]); - call->rtps[video_index] = NULL; - cs_kill(call->cs); - call->cs = NULL; - pthread_mutex_destroy(call->mutex_decoding); + rtp_kill(call->audio.first); + ac_kill(call->audio.second); + call->audio.first = NULL; + call->audio.second = NULL; + rtp_kill(call->video.first); + vc_kill(call->video.second); + call->video.first = NULL; + call->video.second = NULL; + pthread_mutex_destroy(call->mutex); VIDEO_SENDING_MUTEX_CLEANUP: - pthread_mutex_destroy(call->mutex_video_sending); + pthread_mutex_destroy(call->mutex_video); AUDIO_SENDING_MUTEX_CLEANUP: - pthread_mutex_destroy(call->mutex_audio_sending); + pthread_mutex_destroy(call->mutex_audio); return false; } @@ -1170,23 +1162,24 @@ void call_kill_transmission(ToxAVCall* call) call->active = 0; - LOGGED_LOCK(call->mutex_audio_sending); - LOGGED_UNLOCK(call->mutex_audio_sending); - LOGGED_LOCK(call->mutex_video_sending); - LOGGED_UNLOCK(call->mutex_video_sending); - LOGGED_LOCK(call->mutex_decoding); - LOGGED_UNLOCK(call->mutex_decoding); - - - rtp_kill(call->rtps[audio_index]); - call->rtps[audio_index] = NULL; - rtp_kill(call->rtps[video_index]); - call->rtps[video_index] = NULL; - - cs_kill(call->cs); - call->cs = NULL; - - pthread_mutex_destroy(call->mutex_audio_sending); - pthread_mutex_destroy(call->mutex_video_sending); - pthread_mutex_destroy(call->mutex_decoding); + pthread_mutex_lock(call->mutex_audio); + pthread_mutex_unlock(call->mutex_audio); + pthread_mutex_lock(call->mutex_video); + pthread_mutex_unlock(call->mutex_video); + pthread_mutex_lock(call->mutex); + pthread_mutex_unlock(call->mutex); + + rtp_kill(call->audio.first); + ac_kill(call->audio.second); + call->audio.first = NULL; + call->audio.second = NULL; + + rtp_kill(call->video.first); + vc_kill(call->video.second); + call->video.first = NULL; + call->video.second = NULL; + + pthread_mutex_destroy(call->mutex_audio); + pthread_mutex_destroy(call->mutex_video); + pthread_mutex_destroy(call->mutex); } diff --git a/toxav/video.c b/toxav/video.c index d51cfd4a..039fc2a0 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -24,6 +24,7 @@ #include "video.h" #include "msi.h" +#include "rtp.h" #include "../toxcore/logger.h" #include "../toxcore/network.h" @@ -78,7 +79,9 @@ VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb* c vc->linfts = current_time_monotonic(); vc->lcfd = 60; - + vc->vcb.first = cb; + vc->vcb.second = cb_data; + vc->friend_id = friend_id; vc->peer_video_frame_piece_size = mvfpsz; return vc; @@ -187,35 +190,25 @@ const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size) return vc->split_video_frame; } -int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height) -{ - if (!vc) - return; - - vpx_codec_enc_cfg_t cfg = *vc->v_encoder[0].config.enc; - if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) - return 0; /* Nothing changed */ - - cfg.rc_target_bitrate = bitrate; - cfg.g_w = width; - cfg.g_h = height; - - int rc = vpx_codec_enc_config_set(vc->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; -} -/* Called from RTP */ -void vc_queue_message(void* vcp, RTPMessage *msg) +int vc_queue_message(void* vcp, struct RTPMessage_s *msg) { /* This function does the reconstruction of video packets. * See more info about video splitting in docs */ if (!vcp || !msg) - return; + return -1; + + if ((msg->header->marker_payloadt & 0x7f) == rtp_TypeDummyVideo % 128) { + LOGGER_WARNING("Got dummy!"); + rtp_free_msg(NULL, msg); + return 0; + } + + if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) { + LOGGER_WARNING("Invalid payload type!"); + rtp_free_msg(NULL, msg); + return -1; + } VCSession* vc = vcp; @@ -233,7 +226,7 @@ void vc_queue_message(void* vcp, RTPMessage *msg) Payload *p = malloc(sizeof(Payload) + vc->frame_size); if (p) { - LOGGED_LOCK(vc->queue_mutex); + pthread_mutex_lock(vc->queue_mutex); if (rb_full(vc->vbuf_raw)) { LOGGER_DEBUG("Dropped video frame"); @@ -251,7 +244,7 @@ void vc_queue_message(void* vcp, RTPMessage *msg) vc->linfts = current_time_monotonic(); rb_write(vc->vbuf_raw, p); - LOGGED_UNLOCK(vc->queue_mutex); + pthread_mutex_unlock(vc->queue_mutex); } else { LOGGER_WARNING("Allocation failed! Program might misbehave!"); goto end; @@ -288,9 +281,32 @@ void vc_queue_message(void* vcp, RTPMessage *msg) end: rtp_free_msg(NULL, msg); + return 0; +} +int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height) +{ + if (!vc) + return; + + vpx_codec_enc_cfg_t cfg = *vc->v_encoder[0].config.enc; + if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) + return 0; /* Nothing changed */ + + cfg.rc_target_bitrate = bitrate; + cfg.g_w = width; + cfg.g_h = height; + + int rc = vpx_codec_enc_config_set(vc->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; } + bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate) { assert(dest); diff --git a/toxav/video.h b/toxav/video.h index c1678ad2..ed264f36 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -36,6 +36,8 @@ #include "../toxcore/util.h" +struct RTPMessage_s; + typedef struct VCSession_s { /* encoding */ @@ -76,6 +78,7 @@ void vc_do(VCSession* vc); void vc_init_video_splitter_cycle(VCSession* vc); int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length); const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size); +int vc_queue_message(void *vcp, struct RTPMessage_s *msg); int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height); #endif /* VIDEO_H */ \ No newline at end of file -- cgit v1.2.3 From 144fc94d6987c8c6f74d8024af5a5c1738fe4678 Mon Sep 17 00:00:00 2001 From: mannol Date: Sun, 26 Apr 2015 00:31:03 +0200 Subject: Almost done --- toxav/audio.c | 94 ++++++++++++------- toxav/audio.h | 7 ++ toxav/av_test.c | 8 +- toxav/rtp.c | 276 +++++++++++++++++++++++++++++--------------------------- toxav/rtp.h | 14 ++- toxav/toxav.c | 268 ++++++++++++++++++++++++++++++++---------------------- toxav/toxav.h | 78 ++++++++-------- toxav/video.c | 32 +++---- toxav/video.h | 4 +- 9 files changed, 439 insertions(+), 342 deletions(-) (limited to 'toxav/audio.c') diff --git a/toxav/audio.c b/toxav/audio.c index dc85452a..2f068c85 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -33,10 +33,11 @@ static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count); +bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, + int32_t *old_br, int32_t *old_sr, int32_t *old_ch); bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); - ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data) { ACSession *ac = calloc(sizeof(ACSession), 1); @@ -71,10 +72,20 @@ ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *c if (ac->encoder == NULL) goto DECODER_CLEANUP; + ac->test_encoder = create_audio_encoder(48000, 48000, 2); + if (ac->test_encoder == NULL) { + opus_encoder_destroy(ac->encoder); + goto DECODER_CLEANUP; + } + ac->last_encoding_bitrate = 48000; ac->last_encoding_sampling_rate = 48000; ac->last_encoding_channel_count = 2; + ac->last_test_encoding_bitrate = 48000; + ac->last_test_encoding_sampling_rate = 48000; + ac->last_test_encoding_channel_count = 2; + ac->last_decoding_channel_count = 2; ac->last_decoding_sampling_rate = 48000; ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */ @@ -141,7 +152,7 @@ void ac_do(ACSession* ac) cs->last_packet_sampling_rate = rc; } else { LOGGER_WARNING("Failed to load packet values!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); continue; }*/ @@ -157,12 +168,12 @@ void ac_do(ACSession* ac) */ if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) { LOGGER_WARNING("Failed to reconfigure decoder!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); continue; } rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); } if (rc < 0) { @@ -183,15 +194,15 @@ int ac_queue_message(void* acp, struct RTPMessage_s *msg) if (!acp || !msg) return -1; - if ((msg->header->marker_payloadt & 0x7f) == rtp_TypeDummyAudio % 128) { + if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeAudio + 2) % 128) { LOGGER_WARNING("Got dummy!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return 0; } if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) { LOGGER_WARNING("Invalid payload type!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return -1; } @@ -203,7 +214,7 @@ int ac_queue_message(void* acp, struct RTPMessage_s *msg) if (rc == -1) { LOGGER_WARNING("Could not queue the message!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return -1; } @@ -211,35 +222,22 @@ int ac_queue_message(void* acp, struct RTPMessage_s *msg) } int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) { - if (!ac) - return; - - /* Values are checked in toxav.c */ - if (ac->last_encoding_sampling_rate != sampling_rate || ac->last_encoding_channel_count != channels) { - OpusEncoder* new_encoder = create_audio_encoder(bitrate, sampling_rate, channels); - if (new_encoder == NULL) - return -1; - - opus_encoder_destroy(ac->encoder); - ac->encoder = new_encoder; - } else if (ac->last_encoding_bitrate == bitrate) - return 0; /* Nothing changed */ - else { - int status = opus_encoder_ctl(ac->encoder, OPUS_SET_BITRATE(bitrate)); - - if ( status != OPUS_OK ) { - LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); - return -1; - } - } - - ac->last_encoding_bitrate = bitrate; - ac->last_encoding_sampling_rate = sampling_rate; - ac->last_encoding_channel_count = channels; + if (!ac || !reconfigure_audio_encoder(&ac->encoder, bitrate, sampling_rate, channels, + &ac->last_encoding_bitrate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) + return -1; LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); return 0; } +int ac_reconfigure_test_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) +{ + if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bitrate, sampling_rate, channels, + &ac->last_encoding_bitrate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) + return -1; + + LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); + return 0; +} @@ -277,7 +275,7 @@ static void jbuf_clear(struct 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]); + rtp_free_msg(q->queue[q->bottom % q->size]); q->queue[q->bottom % q->size] = NULL; } } @@ -372,6 +370,34 @@ FAILURE: opus_encoder_destroy(rc); return NULL; } +bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, + int32_t* old_br, int32_t* old_sr, int32_t* old_ch) +{ + /* Values are checked in toxav.c */ + if (*old_sr != new_sr || *old_ch != new_ch) { + OpusEncoder* new_encoder = create_audio_encoder(new_br, new_sr, new_ch); + if (new_encoder == NULL) + return false; + + opus_encoder_destroy(*e); + *e = new_encoder; + } else if (*old_br == new_br) + return true; /* Nothing changed */ + else { + int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + return false; + } + } + + *old_br = new_br; + *old_sr = new_sr; + *old_ch = new_ch; + + return true; +} bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels) { if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) { diff --git a/toxav/audio.h b/toxav/audio.h index 2cb0d8f6..a36396f1 100644 --- a/toxav/audio.h +++ b/toxav/audio.h @@ -38,6 +38,12 @@ typedef struct ACSession_s { int32_t last_encoding_channel_count; int32_t last_encoding_bitrate; + /* Testing encoder for dynamic bitrate streaming */ + OpusEncoder *test_encoder; + int32_t last_test_encoding_sampling_rate; + int32_t last_test_encoding_channel_count; + int32_t last_test_encoding_bitrate; + /* decoding */ OpusDecoder *decoder; int32_t last_packet_channel_count; @@ -60,4 +66,5 @@ void ac_kill(ACSession* ac); void ac_do(ACSession* ac); int ac_queue_message(void *acp, struct RTPMessage_s *msg); int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels); +int ac_reconfigure_test_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels); #endif /* AUDIO_H */ \ No newline at end of file diff --git a/toxav/av_test.c b/toxav/av_test.c index dce63184..11de0138 100644 --- a/toxav/av_test.c +++ b/toxav/av_test.c @@ -186,7 +186,7 @@ void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, unsigned long int i, j; for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { - uint8_t *point = (void*)img_data + 3 * ((i * width) + j); + uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j); int yx = y[(i * ystride) + j]; int ux = u[((i / 2) * ustride) + (j / 2)]; int vx = v[((i / 2) * vstride) + (j / 2)]; @@ -226,7 +226,7 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { if (length == 7 && memcmp("gentoo", data, 7) == 0) { - assert(tox_friend_add_norequest(m, public_key, NULL) != ~0); + assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); } } @@ -262,7 +262,7 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA tox_self_get_address(Alice, address); - assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != ~0); + assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); uint8_t off = 1; @@ -563,7 +563,7 @@ int main (int argc, char** argv) } \ } \ \ - iterate(bootstrap, AliceAV, BobAV); \ + iterate_tox(bootstrap, AliceAV, BobAV); \ } \ printf("Success!\n");\ } while(0) diff --git a/toxav/rtp.c b/toxav/rtp.c index 9657da67..a48eba20 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -29,6 +29,7 @@ #include "rtp.h" #include +#include #define size_32 4 #define RTCP_REPORT_INTERVAL_MS 500 @@ -37,8 +38,8 @@ #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 ADD_SETTING_MARKER(_h, _v) do { ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) +#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _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) @@ -70,17 +71,19 @@ typedef struct RTCPSession_s { RTPHeader *parse_header_in ( const uint8_t *payload, int length ); RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); -RTPMessage *msg_parse ( const uint8_t *data, int length ); uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload ); uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload ); -void build_header ( RTPSession* session, RTPHeader* header ); -void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber ); int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); +void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber ); -RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) +RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ) { + assert(mcb); + assert(cs); + assert(messenger); + RTPSession *retu = calloc(1, sizeof(RTPSession)); if ( !retu ) { @@ -107,6 +110,8 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) /* Also set payload type as prefix */ retu->prefix = payload_type; + retu->cs = cs; + retu->mcb = mcb; /* Initialize rtcp session */ if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) { @@ -120,6 +125,14 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) retu->rtcp_session->pl_stats = rb_new(4); retu->rtcp_session->rtp_session = retu; + if (-1 == rtp_start_receiving(retu)) { + LOGGER_WARNING("Failed to start rtp receiving mode"); + free(retu->rtcp_session); + free(retu->csrc); + free(retu); + return NULL; + } + return retu; } void rtp_kill ( RTPSession *session ) @@ -156,11 +169,12 @@ void rtp_do(RTPSession *session) RTCPReport* reports[4]; int i = 0; - for (; rb_read(session->rtcp_session->pl_stats, (void**) reports + i); i++); + for (; i < 4; i++) + rb_read(session->rtcp_session->pl_stats, (void**) reports + i); /* Check for timed out reports (> 6 sec) */ uint64_t now = current_time_monotonic(); - for (i = 0; i < 4 && now - reports[i]->timestamp < 6000; i ++); + for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++); for (; i < 4; i ++) { rb_write(session->rtcp_session->pl_stats, reports[i]); reports[i] = NULL; @@ -168,18 +182,18 @@ void rtp_do(RTPSession *session) if (!rb_empty(session->rtcp_session->pl_stats)) { for (i = 0; reports[i] != NULL; i ++) free(reports[i]); - return; /* As some reports are timed out, we need more... */ + return; /* As some reports are timed out, we need more */ } /* We have 4 on-time reports so we can proceed */ uint32_t quality = 100; for (i = 0; i < 4; i++) { - uint32_t idx = reports[i]->received_packets * 100 / reports[i]->expected_packets; - quality = MIN(quality, idx); + uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets; + quality = MIN(quality, current); free(reports[i]); } - if (quality <= 70) { + if (quality <= 90) { session->tstate = rtp_StateBad; LOGGER_WARNING("Stream quality: BAD"); } else if (quality >= 99) { @@ -220,7 +234,7 @@ int rtp_stop_receiving(RTPSession* session) return 0; } -int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) +int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy ) { if ( !session ) { LOGGER_WARNING("No session!"); @@ -231,12 +245,31 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) uint8_t *it; RTPHeader header[1]; - build_header(session, header); + ADD_FLAG_VERSION ( header, session->version ); + ADD_FLAG_PADDING ( header, session->padding ); + ADD_FLAG_EXTENSION ( header, session->extension ); + ADD_FLAG_CSRCC ( header, session->cc ); + ADD_SETTING_MARKER ( header, session->marker ); + + if (dummy) + ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 ); + else + ADD_SETTING_PAYLOAD ( header, session->payload_type ); + header->sequnum = session->sequnum; + header->timestamp = current_time_monotonic(); + header->ssrc = session->ssrc; + + int i; + for ( i = 0; i < session->cc; i++ ) + header->csrc[i] = session->csrc[i]; + + header->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); + uint32_t parsed_len = length + header->length + 1; + assert(parsed_len + (session->ext_header ? session->ext_header->length * size_32 : 0) > MAX_RTP_SIZE ); parsed[0] = session->prefix; - it = parse_header_out ( header, parsed + 1 ); if ( session->ext_header ) { @@ -244,8 +277,7 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) it = parse_ext_header_out ( session->ext_header, it ); } - memcpy ( it, data, length ); - + memcpy(it, data, length); if ( -1 == send_custom_lossy_packet(session->m, session->friend_id, parsed, parsed_len) ) { LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); @@ -256,18 +288,11 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; return 0; } -void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) +void rtp_free_msg ( 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 ); - } + if ( msg->ext_header ) { + free ( msg->ext_header->table ); + free ( msg->ext_header ); } free ( msg->header ); @@ -389,50 +414,6 @@ RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ) return retu; } -RTPMessage *msg_parse ( const uint8_t *data, int length ) -{ - /* TODO: data dynamic, [0] - * TODO: dummy payload type - * TODO: parse header before allocating message - */ - RTPMessage *retu = calloc(1, sizeof (RTPMessage)); - - retu->header = parse_header_in ( 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 = parse_ext_header_in ( 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; - } - - return retu; -} uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload ) { uint8_t cc = GET_FLAG_CSRCC ( header ); @@ -495,88 +476,95 @@ uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload ) return it + 4; } -void build_header ( RTPSession *session, RTPHeader *header ) -{ - ADD_FLAG_VERSION ( header, session->version ); - ADD_FLAG_PADDING ( header, session->padding ); - ADD_FLAG_EXTENSION ( header, session->extension ); - ADD_FLAG_CSRCC ( header, session->cc ); - ADD_SETTING_MARKER ( header, session->marker ); - ADD_SETTING_PAYLOAD ( header, session->payload_type ); - - header->sequnum = session->sequnum; - header->timestamp = current_time_monotonic(); /* milliseconds */ - header->ssrc = session->ssrc; - - int i; - for ( i = 0; i < session->cc; i++ ) - header->csrc[i] = session->csrc[i]; - - header->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); -} -void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber) -{ - if (session->last_expected_packets == 0) - return; - - uint8_t parsed[9]; - parsed[0] = session->prefix; - - uint32_t received_packets = htonl(session->last_received_packets); - uint32_t expected_packets = htonl(session->last_expected_packets); - - memcpy(parsed + 1, &received_packets, 4); - memcpy(parsed + 5, &expected_packets, 4); - - if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed))) - LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno)); - else { - LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets); - - session->last_received_packets = 0; - session->last_expected_packets = 0; - session->last_sent_report_ts = current_time_monotonic(); - } -} int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) { RTPSession *session = object; - RTPMessage *msg; - if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */ + if ( !session || length < 13 || length > MAX_RTP_SIZE ) { LOGGER_WARNING("No session or invalid length of received buffer!"); return -1; } + + RTPHeader* header = parse_header_in ( data, length ); - msg = msg_parse ( data + 1, length - 1 ); - - if ( !msg ) { - LOGGER_WARNING("Could not parse message!"); + if ( !header ) { + LOGGER_WARNING("Could not parse message: Header failed to extract!"); return -1; } + + RTPExtHeader* ext_header = NULL; + + uint16_t from_pos = header->length; + uint16_t msg_length = length - from_pos; + if ( GET_FLAG_EXTENSION ( header ) ) { + ext_header = parse_ext_header_in ( data + from_pos, length ); + + if ( ext_header ) { + msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * size_32 ); + from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * size_32 ); + } else { /* Error */ + LOGGER_WARNING("Could not parse message: Ext Header failed to extract!"); + free(header); + return -1; + } + } + + if (msg_length > MAX_RTP_SIZE) { + LOGGER_WARNING("Could not parse message: Invalid length!"); + free(header); + free(ext_header); + return -1; + } + /* Check if message came in late */ - if ( msg->header->sequnum > session->rsequnum || msg->header->timestamp > session->rtimestamp ) { + if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) { /* Not late */ - if (msg->header->sequnum > session->rsequnum) - session->rtcp_session->last_expected_packets += msg->header->sequnum - session->rsequnum; - else if (msg->header->sequnum < session->rsequnum) - session->rtcp_session->last_expected_packets += (msg->header->sequnum + 65535) - session->rsequnum; + if (header->sequnum > session->rsequnum) + session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum; + else if (header->sequnum < session->rsequnum) + session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum; else /* Usual case when transmission starts */ session->rtcp_session->last_expected_packets ++; - session->rsequnum = msg->header->sequnum; - session->rtimestamp = msg->header->timestamp; + session->rsequnum = header->sequnum; + session->rtimestamp = header->timestamp; } session->rtcp_session->last_received_packets ++; - if (session->mcb) - return session->mcb (session->cs, msg); - else { - rtp_free_msg(session, msg); + /* Check if the message is dummy. We don't keep dummy messages */ + if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) { + LOGGER_DEBUG("Received dummy rtp message"); + free(header); + free(ext_header); + return 0; + } + + /* Otherwise we will store the message if we have an appropriate handler */ + if (!session->mcb) { + LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header)); + free(header); + free(ext_header); return 0; } + + RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length); + + if ( !msg ) { + LOGGER_WARNING("Could not parse message: Allocation failed!"); + free(header); + free(ext_header); + return -1; + } + + msg->header = header; + msg->ext_header = ext_header; + msg->length = msg_length; + + memcpy ( msg->data, data + from_pos, msg_length ); + + return session->mcb (session->cs, msg); } int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) { @@ -605,4 +593,28 @@ int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* dat LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets); return 0; +} +void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber) +{ + if (session->last_expected_packets == 0) + return; + + uint8_t parsed[9]; + parsed[0] = session->prefix; + + uint32_t received_packets = htonl(session->last_received_packets); + uint32_t expected_packets = htonl(session->last_expected_packets); + + memcpy(parsed + 1, &received_packets, 4); + memcpy(parsed + 5, &expected_packets, 4); + + if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed))) + LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno)); + else { + LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets); + + session->last_received_packets = 0; + session->last_expected_packets = 0; + session->last_sent_report_ts = current_time_monotonic(); + } } \ No newline at end of file diff --git a/toxav/rtp.h b/toxav/rtp.h index 3056b54e..fe07c4f6 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -39,7 +39,7 @@ } while(0) #define MAX_SEQU_NUM 65535 -#define MAX_RTP_SIZE 65535 +#define MAX_RTP_SIZE 1500 /** * Payload type identifier. Also used as rtp callback prefix. (Not dummies) @@ -47,8 +47,6 @@ enum { rtp_TypeAudio = 192, rtp_TypeVideo, - rtp_TypeDummyAudio, - rtp_TypeDummyVideo, }; typedef enum { @@ -85,8 +83,8 @@ typedef struct RTPMessage_s { RTPHeader *header; RTPExtHeader *ext_header; - uint8_t data[MAX_RTP_SIZE]; uint32_t length; + uint8_t data[]; } RTPMessage; /** @@ -128,7 +126,7 @@ typedef struct { /** * Must be called before calling any other rtp function. */ -RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ); +RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ); /** * Terminate the session. @@ -141,7 +139,7 @@ void rtp_kill ( RTPSession* session ); void rtp_do(RTPSession *session); /** - * By default rtp is not in receiving state + * By default rtp is in receiving state */ int rtp_start_receiving (RTPSession *session); @@ -153,12 +151,12 @@ int rtp_stop_receiving (RTPSession *session); /** * Sends msg to RTPSession::dest */ -int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length ); +int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy ); /** * Dealloc msg. */ -void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); +void rtp_free_msg ( RTPMessage *msg ); #endif /* RTP_H */ diff --git a/toxav/toxav.c b/toxav/toxav.c index 5cb614d4..500ca9a8 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -36,6 +36,14 @@ #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) +typedef struct ToxAvBitrateAdapter_s { + bool active; + uint32_t cycle_sent; + uint32_t cycle_ratio; + uint32_t cycle_repeat; + uint64_t start_time; + uint32_t bit_rate; +} ToxAvBitrateAdapter; typedef struct ToxAVCall_s { ToxAV* av; @@ -55,7 +63,10 @@ typedef struct ToxAVCall_s { uint32_t audio_bit_rate; /* Sending audio bitrate */ uint32_t video_bit_rate; /* Sending video bitrate */ - /** Required for monitoring */ + ToxAvBitrateAdapter aba; + ToxAvBitrateAdapter vba; + + /** Required for monitoring changes in states */ uint8_t previous_self_capabilities; /** Quality control */ @@ -82,8 +93,6 @@ struct toxAV { PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ - PAIR(toxav_video_frame_request_cb *, void *) rvcb; /* Video request callback */ - PAIR(toxav_audio_frame_request_cb *, void *) racb; /* Audio request callback */ /** Decode time measures */ int32_t dmssc; /** Measure count */ @@ -103,14 +112,15 @@ int callback_capabilites(void* toxav_inst, MSICall* call); bool audio_bitrate_invalid(uint32_t bitrate); bool video_bitrate_invalid(uint32_t bitrate); void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state); +void qc_do(ToxAVCall* call); ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); -void qc_do(ToxAVCall* call); void call_remove(ToxAVCall* call); bool call_prepare_transmission(ToxAVCall* call); void call_kill_transmission(ToxAVCall* call); - - +void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate); +bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba); +void ba_update_sent_regular(ToxAvBitrateAdapter* ba); ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) { @@ -300,7 +310,7 @@ void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) av->ccb.first = function; av->ccb.second = user_data; pthread_mutex_unlock(av->mutex); -} +}/** Required for monitoring */ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) { @@ -505,7 +515,7 @@ END: return rc == TOXAV_ERR_CALL_CONTROL_OK; } -bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) +bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_BIT_RATE* error) { TOXAV_ERR_BIT_RATE rc = TOXAV_ERR_BIT_RATE_OK; ToxAVCall* call; @@ -528,9 +538,17 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_ goto END; } - /* Decoding mutex is locked because of quality control */ + if (call->audio_bit_rate == audio_bit_rate && call->aba.active && call->aba.bit_rate == audio_bit_rate) { + pthread_mutex_unlock(av->mutex); + goto END; + } + pthread_mutex_lock(call->mutex); - call->audio_bit_rate = audio_bit_rate; + if (force) { + call->audio_bit_rate = audio_bit_rate; + } else + ba_set(&call->aba, audio_bit_rate); + pthread_mutex_unlock(call->mutex); pthread_mutex_unlock(av->mutex); @@ -541,7 +559,7 @@ END: return rc == TOXAV_ERR_BIT_RATE_OK; } -bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) +bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_BIT_RATE* error) { TOXAV_ERR_BIT_RATE rc = TOXAV_ERR_BIT_RATE_OK; ToxAVCall* call; @@ -564,9 +582,17 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_ goto END; } - /* Decoding mutex is locked because of quality control */ + if (call->video_bit_rate == video_bit_rate && call->vba.active && call->vba.bit_rate == video_bit_rate) { + pthread_mutex_unlock(av->mutex); + goto END; + } + pthread_mutex_lock(call->mutex); - call->video_bit_rate = video_bit_rate; + if (force) { + call->video_bit_rate = video_bit_rate; + } else { + ba_set(&call->vba, video_bit_rate); + } pthread_mutex_unlock(call->mutex); pthread_mutex_unlock(av->mutex); @@ -577,14 +603,6 @@ END: return rc == TOXAV_ERR_BIT_RATE_OK; } -void toxav_callback_video_frame_request(ToxAV* av, toxav_video_frame_request_cb* function, void* user_data) -{ - pthread_mutex_lock(av->mutex); - av->rvcb.first = function; - av->rvcb.second = user_data; - pthread_mutex_unlock(av->mutex); -} - bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) { TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; @@ -630,7 +648,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); - int vrc = vpx_codec_encode(call->video.second->v_encoder, &img, + int vrc = vpx_codec_encode(call->video.second->encoder, &img, call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US); vpx_img_free(&img); @@ -650,7 +668,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u vc_init_video_splitter_cycle(call->video.second); - while ( (pkt = vpx_codec_get_cx_data(call->video.second->v_encoder, &iter)) ) { + while ( (pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter)) ) { if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { int parts = vc_update_video_splitter_cycle(call->video.second, pkt->data.frame.buf, pkt->data.frame.sz); @@ -665,7 +683,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u for (i = 0; i < parts; i++) { iter = vc_iterate_split_video_frame(call->video.second, &part_size); - if (rtp_send_msg(call->video.first, iter, part_size) < 0) { + if (rtp_send_data(call->video.first, iter, part_size, false) < 0) { pthread_mutex_unlock(call->mutex_video); LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); goto END; @@ -684,14 +702,6 @@ END: return rc == TOXAV_ERR_SEND_FRAME_OK; } -void toxav_callback_audio_frame_request(ToxAV* av, toxav_audio_frame_request_cb* function, void* user_data) -{ - pthread_mutex_lock(av->mutex); - av->racb.first = function; - av->racb.second = user_data; - pthread_mutex_unlock(av->mutex); -} - bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) { TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; @@ -746,13 +756,41 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc goto END; } -// LOGGER_DEBUG("Sending encoded audio frame size: %d; channels: %d; srate: %d", vrc, channels, -// ntohl(sampling_rate)); - - if (rtp_send_msg(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) { + if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), false) != 0) { LOGGER_WARNING("Failed to send audio packet"); rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; } + + + /* For bitrate measurement; send dummy packet */ + if (ba_shoud_send_dummy(&call->aba)) { + sampling_rate = ntohl(sampling_rate); + if (ac_reconfigure_test_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { + /* FIXME should the bitrate changing fail here? */ + pthread_mutex_unlock(call->mutex_audio); + rc = TOXAV_ERR_SEND_FRAME_INVALID; + goto END; + } + + sampling_rate = htonl(sampling_rate); + memcpy(dest, &sampling_rate, sizeof(sampling_rate)); + vrc = opus_encode(call->audio.second->test_encoder, pcm, sample_count, + dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); + + if (vrc < 0) { + LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc)); + pthread_mutex_unlock(call->mutex_audio); + rc = TOXAV_ERR_SEND_FRAME_INVALID; + goto END; + } + + if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), true) != 0) { + LOGGER_WARNING("Failed to send audio packet"); + rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; + } + } + + ba_update_sent_regular(&call->aba); } pthread_mutex_unlock(call->mutex_audio); @@ -894,6 +932,57 @@ void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state) av->scb.first(av, friend_number, state, av->scb.second); } +void qc_do(ToxAVCall* call) +{ + /* + switch(call->audio.first->tstate) { + case rtp_StateBad: + LOGGER_DEBUG("Suggesting lower bitrate for audio..."); + call->time_audio_good = 0; + call->last_bad_audio_bit_rate = call->audio_bit_rate; + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_DECREASE_AUDIO_BITRATE); + break; + case rtp_StateGood: + if (call->time_audio_good == 0) + call->time_audio_good = current_time_monotonic(); + else if (current_time_monotonic() - call->time_audio_good >= 30000 && + 64 > call->audio_bit_rate) + if (call->last_bad_audio_bit_rate > call->audio_bit_rate) { + if (current_time_monotonic() - call->time_audio_good >= 45000) + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE); + call->last_bad_audio_bit_rate = 0; + } else + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE); + break; + case rtp_StateNormal: + call->time_audio_good = 0; + break; + }*/ + /* + switch(call->video.first->tstate) { + case rtp_StateBad: + LOGGER_DEBUG("Suggesting lower bitrate for video..."); + call->time_video_good = 0; + call->last_bad_video_bit_rate = call->video_bit_rate; + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_DECREASE_VIDEO_BITRATE); + break; + case rtp_StateGood: + if (call->time_video_good == 0) + call->time_video_good = current_time_monotonic(); + else if (current_time_monotonic() - call->time_video_good >= 30000) + if (call->last_bad_video_bit_rate > call->video_bit_rate) { + if (current_time_monotonic() - call->time_video_good >= 45000) + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE); + call->last_bad_video_bit_rate = 0; + } else + invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE); + break; + case rtp_StateNormal: + call->time_video_good = 0; + break; + }*/ +} + ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) { /* Assumes mutex locked */ @@ -984,57 +1073,6 @@ ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) return av->calls[friend_number]; } -void qc_do(ToxAVCall* call) -{ - /* - switch(call->audio.first->tstate) { - case rtp_StateBad: - LOGGER_DEBUG("Suggesting lower bitrate for audio..."); - call->time_audio_good = 0; - call->last_bad_audio_bit_rate = call->audio_bit_rate; - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_DECREASE_AUDIO_BITRATE); - break; - case rtp_StateGood: - if (call->time_audio_good == 0) - call->time_audio_good = current_time_monotonic(); - else if (current_time_monotonic() - call->time_audio_good >= 30000 && - 64 > call->audio_bit_rate) - if (call->last_bad_audio_bit_rate > call->audio_bit_rate) { - if (current_time_monotonic() - call->time_audio_good >= 45000) - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE); - call->last_bad_audio_bit_rate = 0; - } else - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE); - break; - case rtp_StateNormal: - call->time_audio_good = 0; - break; - }*/ - /* - switch(call->video.first->tstate) { - case rtp_StateBad: - LOGGER_DEBUG("Suggesting lower bitrate for video..."); - call->time_video_good = 0; - call->last_bad_video_bit_rate = call->video_bit_rate; - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_DECREASE_VIDEO_BITRATE); - break; - case rtp_StateGood: - if (call->time_video_good == 0) - call->time_video_good = current_time_monotonic(); - else if (current_time_monotonic() - call->time_video_good >= 30000) - if (call->last_bad_video_bit_rate > call->video_bit_rate) { - if (current_time_monotonic() - call->time_video_good >= 45000) - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE); - call->last_bad_video_bit_rate = 0; - } else - invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE); - break; - case rtp_StateNormal: - call->time_video_good = 0; - break; - }*/ -} - void call_remove(ToxAVCall* call) { if (call == NULL) @@ -1098,41 +1136,24 @@ bool call_prepare_transmission(ToxAVCall* call) goto VIDEO_SENDING_MUTEX_CLEANUP; } - { /* Prepare audio */ - call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_id); call->audio.second = ac_new(av, call->friend_id, av->acb.first, av->acb.second); + call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_id, call->audio.second, ac_queue_message); if ( !call->audio.first || !call->audio.second ) { LOGGER_ERROR("Error while starting audio!\n"); goto FAILURE; } - - call->audio.first->cs = call->audio.second; - call->audio.first->mcb = ac_queue_message; - - if (rtp_start_receiving(call->audio.first) != 0) { - LOGGER_WARNING("Failed to enable audio receiving!"); - goto FAILURE; - } } { /* Prepare video */ - call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_id); call->video.second = vc_new(av, call->friend_id, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); + call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_id, call->video.second, vc_queue_message); if ( !call->video.first || !call->video.second ) { LOGGER_ERROR("Error while starting video!\n"); goto FAILURE; } - - call->video.first->cs = call->video.second; - call->video.first->mcb = vc_queue_message; - - if (rtp_start_receiving(call->video.first) != 0) { - LOGGER_WARNING("Failed to enable video receiving!"); - goto FAILURE; - } } call->active = 1; @@ -1183,3 +1204,38 @@ void call_kill_transmission(ToxAVCall* call) pthread_mutex_destroy(call->mutex_video); pthread_mutex_destroy(call->mutex); } + +void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate) +{ + ba->bit_rate = bit_rate; + ba->start_time = current_time_monotonic(); + ba->cycle_sent = 0; + ba->cycle_repeat = 3; + ba->cycle_ratio = 4; + ba->active = true; +} + +bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba) +{ + if (!ba->active || ba->cycle_ratio == 0) + return false; + + if (ba->cycle_sent % ba->cycle_ratio == 0) + return true; +} + +void ba_update_sent_regular(ToxAvBitrateAdapter* ba) +{ + if (!ba->active || ba->cycle_ratio == 0) { + ba->active = false; + return; + } + + if (ba->cycle_sent % ba->cycle_ratio == 0) { + ba->cycle_sent = 0; + + if (--ba->cycle_repeat == 0) + --ba->cycle_ratio; + } else + ++ba->cycle_sent; +} diff --git a/toxav/toxav.h b/toxav/toxav.h index bf8756eb..f2c3b2b3 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -216,22 +216,6 @@ typedef enum TOXAV_CALL_STATE { * transitions can occur for the call. */ TOXAV_CALL_STATE_END = 16, - /** - * AV core suggests you to lower bitrate for audio. - */ - TOXAV_CALL_STATE_DECREASE_AUDIO_BITRATE = 32, - /** - * AV core suggests you to lower bitrate for video. - */ - TOXAV_CALL_STATE_DECREASE_VIDEO_BITRATE = 64, - /** - * AV core suggests you to increase bitrate for audio. - */ - TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE = 128, - /** - * AV core suggests you to increase bitrate for video. - */ - TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE = 256, /** * Set by the AV core if an error occurred on the remote end. */ @@ -348,6 +332,24 @@ typedef enum TOXAV_ERR_BIT_RATE { */ TOXAV_ERR_BIT_RATE_FRIEND_NOT_IN_CALL } TOXAV_ERR_BIT_RATE; +/** + * The function type for the `audio_bitrate_control` callback. + * + * @param friend_number The friend number of the friend for which to set the + * audio bit rate. + * @param good Is the stream good enough to keep the said bitrate. Upon failed + * non forceful bit rate setup this will be set to false and 'bit_rate' + * will be set to the bit rate that failed, otherwise 'good' will be set to + * true with 'bit_rate' set to new bit rate. If the stream becomes bad, + * the 'good' wil be set to false with 'bit_rate' set to the current bit rate. + * This callback will never be called when the stream is good. + * @param bit_rate The bit rate in Kb/sec. + */ +typedef void toxav_audio_bitrate_control_cb(ToxAV *av, uint32_t friend_number, bool good, uint32_t bit_rate, void *user_data); +/** + * Set the callback for the `audio_bitrate_control` event. Pass NULL to unset. + */ +void toxav_callback_audio_bitrate_control(ToxAV *av, toxav_audio_bitrate_control_cb *function, void *user_data); /** * Set the audio bit rate to be used in subsequent audio frames. * @@ -358,7 +360,25 @@ typedef enum TOXAV_ERR_BIT_RATE { * * @see toxav_call for the valid bit rates. */ -bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE *error); +bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_BIT_RATE *error); +/** + * The function type for the `video_bitrate_control` callback. + * + * @param friend_number The friend number of the friend for which to set the + * video bit rate. + * @param good Is the stream good enough to keep the said bitrate. Upon failed + * non forceful bit rate setup this will be set to false and 'bit_rate' + * will be set to the bit rate that failed, otherwise 'good' will be set to + * true with 'bit_rate' set to new bit rate. If the stream becomes bad, + * the 'good' wil be set to false with 'bit_rate' set to the current bit rate. + * This callback will never be called when the stream is good. + * @param bit_rate The bit rate in Kb/sec. + */ +typedef void toxav_video_bitrate_control_cb(ToxAV *av, uint32_t friend_number, bool good, uint32_t bit_rate, void *user_data); +/** + * Set the callback for the `video_bitrate_control` event. Pass NULL to unset. + */ +void toxav_callback_video_bitrate_control(ToxAV *av, toxav_video_bitrate_control_cb *function, void *user_data); /** * Set the video bit rate to be used in subsequent video frames. * @@ -369,7 +389,7 @@ bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_ * * @see toxav_call for the valid bit rates. */ -bool toxav_set_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE *error); +bool toxav_set_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_BIT_RATE *error); /******************************************************************************* * * :: A/V sending @@ -408,17 +428,6 @@ typedef enum TOXAV_ERR_SEND_FRAME { */ TOXAV_ERR_SEND_FRAME_RTP_FAILED } TOXAV_ERR_SEND_FRAME; -/** - * The function type for the `video_frame_request` callback. - * - * @param friend_number The friend number of the friend for which the next video - * frame should be sent. - */ -typedef void toxav_video_frame_request_cb(ToxAV *av, uint32_t friend_number, void *user_data); -/** - * Set the callback for the `video_frame_request` event. Pass NULL to unset. - */ -void toxav_callback_video_frame_request(ToxAV *av, toxav_video_frame_request_cb *function, void *user_data); /** * Send a video frame to a friend. * @@ -440,17 +449,6 @@ bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v, TOXAV_ERR_SEND_FRAME *error); -/** - * The function type for the `audio_frame_request` callback. - * - * @param friend_number The friend number of the friend for which the next audio - * frame should be sent. - */ -typedef void toxav_audio_frame_request_cb(ToxAV *av, uint32_t friend_number, void *user_data); -/** - * Set the callback for the `audio_frame_request` event. Pass NULL to unset. - */ -void toxav_callback_audio_frame_request(ToxAV *av, toxav_audio_frame_request_cb *function, void *user_data); /** * Send an audio frame to a friend. * diff --git a/toxav/video.c b/toxav/video.c index 039fc2a0..cdc3c0ae 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -65,15 +65,15 @@ VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb* c if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto BASE_CLEANUP; - int rc = vpx_codec_dec_init_ver(vc->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, + int rc = vpx_codec_dec_init_ver(vc->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)); goto BASE_CLEANUP; } - if (!create_video_encoder(vc->v_encoder, 500000)) { - vpx_codec_destroy(vc->v_decoder); + if (!create_video_encoder(vc->encoder, 500000)) { + vpx_codec_destroy(vc->decoder); goto BASE_CLEANUP; } @@ -99,8 +99,8 @@ void vc_kill(VCSession* vc) if (!vc) return; - vpx_codec_destroy(vc->v_encoder); - vpx_codec_destroy(vc->v_decoder); + vpx_codec_destroy(vc->encoder); + vpx_codec_destroy(vc->decoder); rb_free(vc->vbuf_raw); free(vc->split_video_frame); free(vc->frame_buf); @@ -122,17 +122,17 @@ void vc_do(VCSession* vc) if (rb_read(vc->vbuf_raw, (void**)&p)) { pthread_mutex_unlock(vc->queue_mutex); - rc = vpx_codec_decode(vc->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); + rc = vpx_codec_decode(vc->decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); free(p); if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); } else { vpx_codec_iter_t iter = NULL; - vpx_image_t *dest = vpx_codec_get_frame(vc->v_decoder, &iter); + vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter); /* Play decoded images */ - for (; dest; dest = vpx_codec_get_frame(vc->v_decoder, &iter)) { + for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) { if (vc->vcb.first) vc->vcb.first(vc->av, vc->friend_id, dest->d_w, dest->d_h, (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2], @@ -157,7 +157,7 @@ void vc_init_video_splitter_cycle(VCSession* vc) int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length) { if (!vc) - return; + return 0; vc->processing_video_frame = payload; vc->processing_video_frame_size = length; @@ -198,15 +198,15 @@ int vc_queue_message(void* vcp, struct RTPMessage_s *msg) if (!vcp || !msg) return -1; - if ((msg->header->marker_payloadt & 0x7f) == rtp_TypeDummyVideo % 128) { + if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeVideo + 2) % 128) { LOGGER_WARNING("Got dummy!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return 0; } if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) { LOGGER_WARNING("Invalid payload type!"); - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return -1; } @@ -280,15 +280,15 @@ int vc_queue_message(void* vcp, struct RTPMessage_s *msg) vc->frame_size = framebuf_new_length; end: - rtp_free_msg(NULL, msg); + rtp_free_msg(msg); return 0; } int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height) { if (!vc) - return; + return -1; - vpx_codec_enc_cfg_t cfg = *vc->v_encoder[0].config.enc; + vpx_codec_enc_cfg_t cfg = *vc->encoder[0].config.enc; if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) return 0; /* Nothing changed */ @@ -296,7 +296,7 @@ int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint1 cfg.g_w = width; cfg.g_h = height; - int rc = vpx_codec_enc_config_set(vc->v_encoder, &cfg); + int rc = vpx_codec_enc_config_set(vc->encoder, &cfg); if ( rc != VPX_CODEC_OK) { LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); return -1; diff --git a/toxav/video.h b/toxav/video.h index ed264f36..9c5836a3 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -41,11 +41,11 @@ struct RTPMessage_s; typedef struct VCSession_s { /* encoding */ - vpx_codec_ctx_t v_encoder[1]; + vpx_codec_ctx_t encoder[1]; uint32_t frame_counter; /* decoding */ - vpx_codec_ctx_t v_decoder[1]; + vpx_codec_ctx_t decoder[1]; void *vbuf_raw; /* Un-decoded data */ /* Data handling */ -- cgit v1.2.3 From 9bba7a0434d0967d5dd76b8afc7783ea2edad0cf Mon Sep 17 00:00:00 2001 From: mannol Date: Wed, 29 Apr 2015 01:01:25 +0200 Subject: Done --- auto_tests/Makefile.inc | 10 +- auto_tests/toxav_basic_test.c | 875 +++++++++++++++---------------------- auto_tests/toxav_many_test.c | 459 ++++++-------------- testing/av_test.c | 744 ++++++++++++++++++++++++++++++++ toxav/Makefile.inc | 21 - toxav/audio.c | 35 +- toxav/audio.h | 33 +- toxav/av_test.c | 976 ------------------------------------------ toxav/msi.c | 124 +++--- toxav/msi.h | 24 +- toxav/rtp.c | 67 ++- toxav/rtp.h | 11 +- toxav/toxav.c | 132 +++--- toxav/toxav.h | 40 +- toxav/video.c | 25 +- toxav/video.h | 36 +- 16 files changed, 1492 insertions(+), 2120 deletions(-) create mode 100644 testing/av_test.c delete mode 100644 toxav/av_test.c (limited to 'toxav/audio.c') diff --git a/auto_tests/Makefile.inc b/auto_tests/Makefile.inc index d78a6a5a..741ca7fa 100644 --- a/auto_tests/Makefile.inc +++ b/auto_tests/Makefile.inc @@ -21,8 +21,8 @@ AUTOTEST_LDADD = \ if BUILD_AV -TESTS += toxav_basic_test toxav_many_test -check_PROGRAMS += toxav_basic_test toxav_many_test +TESTS += toxav_basic_test #toxav_many_test +check_PROGRAMS += toxav_basic_test #toxav_many_test AUTOTEST_LDADD += libtoxav.la endif @@ -90,11 +90,11 @@ toxav_basic_test_CFLAGS = $(AUTOTEST_CFLAGS) toxav_basic_test_LDADD = $(AUTOTEST_LDADD) $(AV_LIBS) -toxav_many_test_SOURCES = ../auto_tests/toxav_many_test.c +#toxav_many_test_SOURCES = ../auto_tests/toxav_many_test.c -toxav_many_test_CFLAGS = $(AUTOTEST_CFLAGS) +#toxav_many_test_CFLAGS = $(AUTOTEST_CFLAGS) -toxav_many_test_LDADD = $(AUTOTEST_LDADD) +#toxav_many_test_LDADD = $(AUTOTEST_LDADD) endif endif diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index a43b7c2f..7598c0db 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -14,6 +14,7 @@ #include #include "../toxcore/tox.h" +#include "../toxcore/util.h" #include "../toxcore/logger.h" #include "../toxcore/crypto_core.h" #include "../toxav/toxav.h" @@ -28,600 +29,406 @@ #endif +#define TEST_REGULAR_AV 1 +#define TEST_REGULAR_A 1 +#define TEST_REGULAR_V 1 +#define TEST_REJECT 1 +#define TEST_CANCEL 1 +#define TEST_MUTE_UNMUTE 1 -typedef enum _CallStatus { - none, - InCall, - Ringing, - Ended, - Rejected, - Canceled, - TimedOut -} CallStatus; +typedef struct { + bool incoming; + uint32_t state; + +} CallControl; -typedef struct _Party { - CallStatus status; - ToxAv *av; - time_t *CallStarted; - int call_index; -} Party; -typedef struct _Status { - Party Alice; - Party Bob; -} Status; - -/* My default settings */ -static ToxAvCSettings muhcaps; - -void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) -{ - if (length == 7 && memcmp("gentoo", data, 7) == 0) { - tox_friend_add_norequest(m, public_key, 0); - } -} - - -/******************************************************************************/ -void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) -{ - Status *cast = _arg; - - if (cast->Alice.av == av) { - // ... - } else if (cast->Bob.av == av) { - /* Bob always receives invite */ - cast->Bob.status = Ringing; - cast->Bob.call_index = call_index; - } -} -void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) -{ - Status *cast = _arg; - - if (cast->Alice.av == av) { - /* Alice always sends invite */ - cast->Alice.status = Ringing; - } else if (cast->Bob.av == av) { - // ... - } -} - - -void callback_call_started ( void *av, int32_t call_index, void *_arg ) -{ - Status *cast = _arg; - - if (cast->Alice.av == av) { - printf("Call started on Alices side...\n"); - cast->Alice.status = InCall; - toxav_prepare_transmission(av, call_index, 1); - } else if (cast->Bob.av == av) { - printf("Call started on Bob side...\n"); - cast->Bob.status = InCall; - toxav_prepare_transmission(av, call_index, 1); - } -} -void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) +/** + * Callbacks + */ +void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) { - Status *cast = _arg; - - if (cast->Alice.av == av) { - // ... - } else if (cast->Bob.av == av) { - printf ( "Call Canceled for Bob!\n" ); - cast->Bob.status = Canceled; - } + printf("Handling CALL callback\n"); + ((CallControl*)user_data)->incoming = true; } -void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) +void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) { - Status *cast = _arg; - - printf ( "Call rejected by Bob!\n" - "Call ended for Alice!\n" ); - - /* If Bob rejects, call is ended for alice and she sends ending */ - if (cast->Alice.av == av) { - cast->Alice.status = Rejected; - } else if (cast->Bob.av == av) { - //... ignor - } + printf("Handling CALL STATE callback: %d\n", state); + ((CallControl*)user_data)->state = state; } -void callback_call_ended ( void *av, int32_t call_index, void *_arg ) +void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, + uint16_t width, uint16_t height, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, + void *user_data) { - Status *cast = _arg; - - if (cast->Alice.av == av) { - printf ( "Call ended for Alice!\n" ); - cast->Alice.status = Ended; - } else if (cast->Bob.av == av) { - printf ( "Call ended for Bob!\n" ); - cast->Bob.status = Ended; - } + (void) av; + (void) friend_number; + (void) width; + (void) height; + (void) y; + (void) u; + (void) v; + (void) ystride; + (void) ustride; + (void) vstride; + (void) user_data; } - -void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg ) +void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, + int16_t const *pcm, + size_t sample_count, + uint8_t channels, + uint32_t sampling_rate, + void *user_data) { - ToxAvCSettings csettings; - toxav_get_peer_csettings(av, call_index, 0, &csettings); - - printf("Peer changing settings to: \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", - csettings.call_type, - csettings.video_bitrate, - csettings.max_video_height, - csettings.max_video_width, - csettings.audio_bitrate, - csettings.audio_frame_duration, - csettings.audio_sample_rate, - csettings.audio_channels - ); + (void) av; + (void) friend_number; + (void) pcm; + (void) sample_count; + (void) channels; + (void) sampling_rate; + (void) user_data; } - -void callback_self_cs_change ( void *av, int32_t call_index, void *_arg ) +void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { - ToxAvCSettings csettings; - toxav_get_peer_csettings(av, call_index, 0, &csettings); - - printf("Changed settings to: \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", - csettings.call_type, - csettings.video_bitrate, - csettings.max_video_height, - csettings.max_video_width, - csettings.audio_bitrate, - csettings.audio_frame_duration, - csettings.audio_sample_rate, - csettings.audio_channels - ); -} - -void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) -{ - Status *cast = _arg; - printf("Call timed-out!\n"); - - if (cast->Alice.av == av) { - cast->Alice.status = TimedOut; - } else if (cast->Bob.av == av) { - cast->Bob.status = TimedOut; + if (length == 7 && memcmp("gentoo", data, 7) == 0) { + assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); } } -void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data) -{} - -void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data) -{} -void register_callbacks(ToxAv *av, void *data) +/** + * Iterate helper + */ +int iterate_tox(Tox* bootstrap, Tox* Alice, Tox* Bob) { - toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); - toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); - toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); - toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); - toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); - toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); - toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); - toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data); - toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data); - toxav_register_audio_callback(av, callback_audio, NULL); - toxav_register_video_callback(av, callback_video, NULL); + tox_iterate(bootstrap); + tox_iterate(Alice); + tox_iterate(Bob); + + return MIN(tox_iteration_interval(Alice), tox_iteration_interval(Bob)); } -/*************************************************************************************************/ - -/* Alice calls bob and the call starts. - * What happens during the call is defined after. To quit the loop use: step++; - */ -#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \ -{ int step = 0, running = 1; while (running) {\ - tox_iterate(bootstrap_node); tox_iterate(Alice); tox_iterate(Bob); \ - toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \ - switch ( step ) {\ - case 0: /* Alice */ printf("Alice is calling...\n");\ - toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); step++; break;\ - case 1: /* Bob */ if (status_control.Bob.status == Ringing) { printf("Bob answers...\n");\ - cur_time = time(NULL); toxav_answer(status_control.Bob.av, status_control.Bob.call_index, &muhcaps); step++; } break; \ - case 2: /* Rtp transmission */ \ - if (status_control.Bob.status == InCall && status_control.Alice.status == InCall) - - -#define TERMINATE_SCOPE() break;\ -case 3: /* Wait for Both to have status ended */\ -if (status_control.Alice.status == Ended && status_control.Bob.status == Ended) running = 0; break; } c_sleep(20); } } printf("\n"); START_TEST(test_AV_flows) { + Tox* Alice, *Bob, *bootstrap; + ToxAV* AliceAV, *BobAV; + + CallControl AliceCC, BobCC; + + { + TOX_ERR_NEW error; + + bootstrap = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + Alice = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + Bob = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + } + + printf("Created 3 instances of Tox\n"); + printf("Preparing network...\n"); long long unsigned int cur_time = time(NULL); - Tox *bootstrap_node = tox_new(0, 0, 0, 0); - Tox *Alice = tox_new(0, 0, 0, 0); - Tox *Bob = tox_new(0, 0, 0, 0); - - ck_assert_msg(bootstrap_node || Alice || Bob, "Failed to create 3 tox instances"); - + uint32_t to_compare = 974536; - tox_callback_friend_request(Alice, accept_friend_request, &to_compare); uint8_t address[TOX_ADDRESS_SIZE]; + + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); tox_self_get_address(Alice, address); - uint32_t test = tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, 0); - - ck_assert_msg(test == 0, "Failed to add friend error code: %i", test); - + + + assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); + uint8_t off = 1; - + while (1) { - tox_iterate(bootstrap_node); - tox_iterate(Alice); - tox_iterate(Bob); - - if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice) - && tox_self_get_connection_status(Bob) - && off) { + iterate_tox(bootstrap, Alice, Bob); + + if (tox_self_get_connection_status(bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bob) && off) { printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); off = 0; } - - if (tox_friend_get_connection_status(Alice, 0, 0) && tox_friend_get_connection_status(Bob, 0, 0)) + + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) break; - + c_sleep(20); } - - printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); - - muhcaps = av_DefaultSettings; - muhcaps.max_video_height = muhcaps.max_video_width = 128; - - Status status_control = { - {none, toxav_new(Alice, 1), NULL, -1}, - {none, toxav_new(Bob, 1), NULL, -1}, - }; - - ck_assert_msg(status_control.Alice.av || status_control.Bob.av, "Failed to create 2 toxav instances"); - - - register_callbacks(status_control.Alice.av, &status_control); - register_callbacks(status_control.Bob.av, &status_control); - - const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); - int16_t sample_payload[frame_size]; - randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); - - uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; - int payload_size; - - vpx_image_t *sample_image = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 128, 128, 1); - - memcpy(sample_image->planes[VPX_PLANE_Y], sample_payload, 10); - memcpy(sample_image->planes[VPX_PLANE_U], sample_payload, 10); - memcpy(sample_image->planes[VPX_PLANE_V], sample_payload, 10); - - - /************************************************************************************************* - * Successful flows (when call starts) - */ - - /* - * Call with audio only on both sides. Alice calls Bob. - */ - - - CALL_AND_START_LOOP(TypeAudio, TypeAudio) { - /* Both send */ - payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, - 1000, sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); - - payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, - sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); - - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ - step++; /* This terminates the loop */ - toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); - toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); - - /* Call over Alice hangs up */ - toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); - } + + + { + TOXAV_ERR_NEW error; + AliceAV = toxav_new(Alice, &error); + assert(error == TOXAV_ERR_NEW_OK); + + BobAV = toxav_new(Bob, &error); + assert(error == TOXAV_ERR_NEW_OK); } - TERMINATE_SCOPE() - - - /* - * Call with audio on both sides and video on one side. Alice calls Bob. - */ - CALL_AND_START_LOOP(TypeAudio, TypeVideo) { - /* Both send */ - payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, - 1000, sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); - - payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, - sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); - -// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); - - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ - step++; /* This terminates the loop */ - toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); - toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); - - /* Call over Alice hangs up */ - toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); - } + + toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC); + toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC); + toxav_callback_receive_video_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC); + toxav_callback_receive_audio_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC); + + toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC); + toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC); + toxav_callback_receive_video_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC); + toxav_callback_receive_audio_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC); + + printf("Created 2 instances of ToxAV\n"); + printf("All set after %llu seconds!\n", time(NULL) - cur_time); + + +#define REGULAR_CALL_FLOW(A_BR, V_BR) \ + do { \ + memset(&AliceCC, 0, sizeof(CallControl)); \ + memset(&BobCC, 0, sizeof(CallControl)); \ + \ + TOXAV_ERR_CALL rc; \ + toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \ + \ + if (rc != TOXAV_ERR_CALL_OK) { \ + printf("toxav_call failed: %d\n", rc); \ + exit(1); \ + } \ + \ + \ + long long unsigned int start_time = time(NULL); \ + \ + \ + while (BobCC.state != TOXAV_CALL_STATE_END) { \ + \ + if (BobCC.incoming) { \ + TOXAV_ERR_ANSWER rc; \ + toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \ + \ + if (rc != TOXAV_ERR_ANSWER_OK) { \ + printf("toxav_answer failed: %d\n", rc); \ + exit(1); \ + } \ + BobCC.incoming = false; \ + } else { \ + /* TODO rtp */ \ + \ + if (time(NULL) - start_time == 5) { \ + \ + TOXAV_ERR_CALL_CONTROL rc; \ + toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ + \ + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ + printf("toxav_call_control failed: %d\n", rc); \ + exit(1); \ + } \ + } \ + } \ + \ + iterate_tox(bootstrap, Alice, Bob); \ + } \ + printf("Success!\n");\ + } while(0) + + if (TEST_REGULAR_AV) { + printf("\nTrying regular call (Audio and Video)...\n"); + REGULAR_CALL_FLOW(48, 4000); } - TERMINATE_SCOPE() - - - /* - * Call with audio and video on both sides. Alice calls Bob. - */ - CALL_AND_START_LOOP(TypeVideo, TypeVideo) { - /* Both send */ - - payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, - 1000, sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); - - payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, - sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); - -// toxav_send_video(status_control.Alice.av, status_control.Alice.call_index, sample_image); -// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); - - - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ - step++; /* This terminates the loop */ - toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); - toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); - - /* Call over Alice hangs up */ - toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); - } + + if (TEST_REGULAR_A) { + printf("\nTrying regular call (Audio only)...\n"); + REGULAR_CALL_FLOW(48, 0); } - TERMINATE_SCOPE() - - - uint64_t times_they_are_a_changin = time(NULL); - /* Media change */ - CALL_AND_START_LOOP(TypeAudio, TypeAudio) { - /* Both send */ - payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, - 1000, sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); + + if (TEST_REGULAR_V) { + printf("\nTrying regular call (Video only)...\n"); + REGULAR_CALL_FLOW(0, 4000); + } + +#undef REGULAR_CALL_FLOW + + if (TEST_REJECT) { /* Alice calls; Bob rejects */ + printf("\nTrying reject flow...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); + memset(&BobCC, 0, sizeof(CallControl)); + + { + TOXAV_ERR_CALL rc; + toxav_call(AliceAV, 0, 48, 0, &rc); + + if (rc != TOXAV_ERR_CALL_OK) { + printf("toxav_call failed: %d\n", rc); + exit(1); + } } - - toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); - - payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, - sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); + + while (!BobCC.incoming) + iterate_tox(bootstrap, Alice, Bob); + + /* Reject */ + { + TOXAV_ERR_CALL_CONTROL rc; + toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); + + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { + printf("toxav_call_control failed: %d\n", rc); + exit(1); + } } - - toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); - - /* Wait 2 seconds and change transmission type */ - if (time(NULL) - times_they_are_a_changin > 2) { - times_they_are_a_changin = time(NULL); - muhcaps.audio_bitrate ++; - toxav_change_settings(status_control.Alice.av, status_control.Alice.call_index, &muhcaps); + + while (AliceCC.state != TOXAV_CALL_STATE_END) + iterate_tox(bootstrap, Alice, Bob); + + printf("Success!\n"); + } + + if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */ + printf("\nTrying cancel (while ringing) flow...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); + memset(&BobCC, 0, sizeof(CallControl)); + + { + TOXAV_ERR_CALL rc; + toxav_call(AliceAV, 0, 48, 0, &rc); + + if (rc != TOXAV_ERR_CALL_OK) { + printf("toxav_call failed: %d\n", rc); + exit(1); + } } - - if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ - step++; /* This terminates the loop */ - toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); - toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); - - /* Call over Alice hangs up */ - toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); + + while (!BobCC.incoming) + iterate_tox(bootstrap, Alice, Bob); + + /* Cancel */ + { + TOXAV_ERR_CALL_CONTROL rc; + toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); + + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { + printf("toxav_call_control failed: %d\n", rc); + exit(1); + } } + + /* Alice will not receive end state */ + while (BobCC.state != TOXAV_CALL_STATE_END) + iterate_tox(bootstrap, Alice, Bob); + + printf("Success!\n"); } - TERMINATE_SCOPE() - - - /************************************************************************************************* - * Other flows - */ - - /* - * Call and reject - */ - { - int step = 0; - int running = 1; - - while (running) { - tox_iterate(bootstrap_node); - tox_iterate(Alice); - tox_iterate(Bob); - - switch ( step ) { - case 0: /* Alice */ - printf("Alice is calling...\n"); - toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); - step++; - break; - - case 1: /* Bob */ - if (status_control.Bob.status == Ringing) { - printf("Bob rejects...\n"); - toxav_reject(status_control.Bob.av, status_control.Bob.call_index, "Who likes D's anyway?"); - step++; - } - - break; - - case 2: /* Wait for Both to have status ended */ - if (status_control.Alice.status == Rejected && status_control.Bob.status == Ended) running = 0; - - break; + + if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */ + printf("\nTrying mute functionality...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); + memset(&BobCC, 0, sizeof(CallControl)); + + /* Assume sending audio and video */ + { + TOXAV_ERR_CALL rc; + toxav_call(AliceAV, 0, 48, 1000, &rc); + + if (rc != TOXAV_ERR_CALL_OK) { + printf("toxav_call failed: %d\n", rc); + exit(1); } - - c_sleep(20); } - - printf("\n"); - } - - - /* - * Call and cancel - */ - { - int step = 0; - int running = 1; - - while (running) { - tox_iterate(bootstrap_node); - tox_iterate(Alice); - tox_iterate(Bob); - - toxav_do(status_control.Alice.av); - toxav_do(status_control.Bob.av); - - - switch ( step ) { - case 0: /* Alice */ - printf("Alice is calling...\n"); - toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); - step++; - break; - - - case 1: /* Alice again */ - if (status_control.Bob.status == Ringing) { - printf("Alice cancels...\n"); - toxav_cancel(status_control.Alice.av, status_control.Alice.call_index, 0, "Who likes D's anyway?"); - step++; - } - - break; - - case 2: /* Wait for Both to have status ended */ - if (status_control.Bob.status == Canceled) running = 0; - - break; + + while (!BobCC.incoming) + iterate_tox(bootstrap, Alice, Bob); + + /* At first try all stuff while in invalid state */ + assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); + assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); + assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); + assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); + + { + TOXAV_ERR_ANSWER rc; + toxav_answer(BobAV, 0, 48, 4000, &rc); + + if (rc != TOXAV_ERR_ANSWER_OK) { + printf("toxav_answer failed: %d\n", rc); + exit(1); } - - c_sleep(20); } - - printf("\n"); - } - - /* - * Timeout - */ - { - int step = 0; - int running = 1; - - while (running) { - tox_iterate(bootstrap_node); - tox_iterate(Alice); - tox_iterate(Bob); - - toxav_do(status_control.Alice.av); - toxav_do(status_control.Bob.av); - - switch ( step ) { - case 0: - printf("Alice is calling...\n"); - toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); - step++; - break; - - case 1: - if (status_control.Alice.status == TimedOut) running = 0; - - break; + + iterate_tox(bootstrap, Alice, Bob); + + /* Pause and Resume */ + printf("Pause and Resume\n"); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state == TOXAV_CALL_STATE_PAUSED); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state & (TOXAV_CALL_STATE_SENDING_A | TOXAV_CALL_STATE_SENDING_V)); + + /* Mute/Unmute single */ + printf("Mute/Unmute single\n"); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A); + + /* Mute/Unmute both */ + printf("Mute/Unmute both\n"); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_V); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A); + assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_V); + + { + TOXAV_ERR_CALL_CONTROL rc; + toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); + + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { + printf("toxav_call_control failed: %d\n", rc); + exit(1); } - - c_sleep(20); } - - printf("\n"); + + iterate_tox(bootstrap, Alice, Bob); + assert(BobCC.state == TOXAV_CALL_STATE_END); + + printf("Success!\n"); } - - vpx_img_free(sample_image); - toxav_kill(status_control.Alice.av); - toxav_kill(status_control.Bob.av); - tox_kill(bootstrap_node); - tox_kill(Alice); + + toxav_kill(BobAV); + toxav_kill(AliceAV); tox_kill(Bob); - - printf("Calls ended!\n"); + tox_kill(Alice); + tox_kill(bootstrap); + + printf("\nTest successful!\n"); } END_TEST -/*************************************************************************************************/ - - -/*************************************************************************************************/ - -/*************************************************************************************************/ - Suite *tox_suite(void) { Suite *s = suite_create("ToxAV"); DEFTESTCASE_SLOW(AV_flows, 200); - return s; } int main(int argc, char *argv[]) @@ -637,6 +444,4 @@ int main(int argc, char *argv[]) srunner_free(test_runner); return number_failed; - -// return test_AV_flows(); } diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c index 4287118f..ef59b2b2 100644 --- a/auto_tests/toxav_many_test.c +++ b/auto_tests/toxav_many_test.c @@ -14,6 +14,7 @@ #include #include "../toxcore/tox.h" +#include "../toxcore/util.h" #include "../toxcore/logger.h" #include "../toxcore/crypto_core.h" #include "../toxav/toxav.h" @@ -26,359 +27,177 @@ #define c_sleep(x) usleep(1000*x) #endif -pthread_mutex_t muhmutex; -typedef enum _CallStatus { - none, - InCall, - Ringing, - Ended, - Rejected, - Canceled +typedef struct { + bool incoming; + uint32_t state; + +} CallControl; -} CallStatus; -typedef struct _Party { - CallStatus status; - ToxAv *av; - int id; -} Party; - -typedef struct _ACall { - pthread_t tid; - int idx; - - Party Caller; - Party Callee; -} ACall; - -typedef struct _Status { - ACall calls[3]; /* Make 3 calls for this test */ -} Status; - -Status status_control; - -void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) -{ - if (length == 7 && memcmp("gentoo", data, 7) == 0) { - tox_friend_add_norequest(m, public_key, 0); - } -} - - -/******************************************************************************/ -void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) +/** + * Callbacks + */ +void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) { - Status *cast = _arg; - cast->calls[call_index].Callee.status = Ringing; + printf("Handling CALL callback\n"); + ((CallControl*)user_data)->incoming = true; } -void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) +void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) { - Status *cast = _arg; - cast->calls[call_index].Caller.status = Ringing; + printf("Handling CALL STATE callback: %d\n", state); + ((CallControl*)user_data)->state = state; } -void callback_call_ended ( void *av, int32_t call_index, void *_arg ) +void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, + uint16_t width, uint16_t height, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, + void *user_data) { - Status *cast = _arg; - - if (av == cast->calls[call_index].Caller.av) - cast->calls[call_index].Caller.status = Ended; - else - cast->calls[call_index].Callee.status = Ended; + (void) av; + (void) friend_number; + (void) width; + (void) height; + (void) y; + (void) u; + (void) v; + (void) ystride; + (void) ustride; + (void) vstride; + (void) user_data; } -void callback_call_started ( void *av, int32_t call_index, void *_arg ) +void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, + int16_t const *pcm, + size_t sample_count, + uint8_t channels, + uint32_t sampling_rate, + void *user_data) { - Status *cast = _arg; - - if (av == cast->calls[call_index].Caller.av) - cast->calls[call_index].Caller.status = InCall; - else - cast->calls[call_index].Callee.status = InCall; + (void) av; + (void) friend_number; + (void) pcm; + (void) sample_count; + (void) channels; + (void) sampling_rate; + (void) user_data; } -void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) +void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { + if (length == 7 && memcmp("gentoo", data, 7) == 0) { + assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); + } } -void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) -{ - Status *cast = _arg; - cast->calls[call_index].Caller.status = Rejected; -} - -void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) -{ - ck_assert_msg(0, "No answer!"); -} - -void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data) -{} -void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data) -{} -void register_callbacks(ToxAv *av, void *data) +/** + * Iterate helper + */ +ToxAV* setup_av_instance(Tox* tox, CallControl *CC) { - toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); - toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); - toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); - toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); - toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); - toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); - toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); - - - toxav_register_audio_callback(av, callback_audio, NULL); - toxav_register_video_callback(av, callback_video, NULL); + TOXAV_ERR_NEW error; + + ToxAV* av = toxav_new(tox, &error); + assert(error == TOXAV_ERR_NEW_OK); + + toxav_callback_call(av, t_toxav_call_cb, CC); + toxav_callback_call_state(av, t_toxav_call_state_cb, CC); + toxav_callback_receive_video_frame(av, t_toxav_receive_video_frame_cb, CC); + toxav_callback_receive_audio_frame(av, t_toxav_receive_audio_frame_cb, CC); + + return av; } -/*************************************************************************************************/ - -int call_running[3]; - -void *in_thread_call (void *arg) +void* call_thread(ToxAV* Alice, ToxAV* Bob) { -#define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args) - - ACall *this_call = arg; - uint64_t start = 0; - int step = 0; - int call_idx; - - const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); - int16_t sample_payload[frame_size]; - randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); - - uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; - - register_callbacks(this_call->Caller.av, &status_control); - register_callbacks(this_call->Callee.av, arg); - - /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */ - pthread_mutex_lock(&muhmutex); - - while (call_running[this_call->idx]) { - - pthread_mutex_unlock(&muhmutex); - - switch ( step ) { - case 0: /* CALLER */ - toxav_call(this_call->Caller.av, &call_idx, this_call->Callee.id, &av_DefaultSettings, 10); - call_print(call_idx, "Calling ..."); - step++; - break; - - case 1: /* CALLEE */ - pthread_mutex_lock(&muhmutex); - - if (this_call->Caller.status == Ringing) { - call_print(call_idx, "Callee answers ..."); - pthread_mutex_unlock(&muhmutex); - toxav_answer(this_call->Callee.av, 0, &av_DefaultSettings); - step++; - start = time(NULL); - pthread_mutex_lock(&muhmutex); - } - - pthread_mutex_unlock(&muhmutex); - break; - - case 2: /* Rtp transmission */ - pthread_mutex_lock(&muhmutex); - - if (this_call->Caller.status == InCall) { /* I think this is okay */ - call_print(call_idx, "Sending rtp ..."); - pthread_mutex_unlock(&muhmutex); - - c_sleep(1000); /* We have race condition here */ - toxav_prepare_transmission(this_call->Callee.av, 0, 1); - toxav_prepare_transmission(this_call->Caller.av, call_idx, 1); - - int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE, - sample_payload, frame_size); - - if ( payload_size < 0 ) { - ck_assert_msg ( 0, "Failed to encode payload" ); - } - - - while (time(NULL) - start < 10) { /* 10 seconds */ - /* Both send */ - toxav_send_audio(this_call->Caller.av, call_idx, prepared_payload, payload_size); - - toxav_send_audio(this_call->Callee.av, 0, prepared_payload, payload_size); - - /* Both receive */ - int16_t storage[RTP_PAYLOAD_SIZE]; - int recved; - - c_sleep(20); - } - - step++; /* This terminates the loop */ - - pthread_mutex_lock(&muhmutex); - toxav_kill_transmission(this_call->Callee.av, 0); - toxav_kill_transmission(this_call->Caller.av, call_idx); - pthread_mutex_unlock(&muhmutex); - - /* Call over CALLER hangs up */ - toxav_hangup(this_call->Caller.av, call_idx); - call_print(call_idx, "Hanging up ..."); - - pthread_mutex_lock(&muhmutex); - } - - pthread_mutex_unlock(&muhmutex); - break; - - case 3: /* Wait for Both to have status ended */ - pthread_mutex_lock(&muhmutex); - - if (this_call->Caller.status == Ended) { - pthread_mutex_unlock(&muhmutex); - c_sleep(1000); /* race condition */ - pthread_mutex_lock(&muhmutex); - this_call->Callee.status = Ended; - call_running[this_call->idx] = 0; - } - - pthread_mutex_unlock(&muhmutex); - - break; - - } - - c_sleep(20); - - pthread_mutex_lock(&muhmutex); - } - - pthread_mutex_unlock(&muhmutex); - call_print(call_idx, "Call ended successfully!"); pthread_exit(NULL); } - - - - START_TEST(test_AV_three_calls) -// void test_AV_three_calls() { - long long unsigned int cur_time = time(NULL); - Tox *bootstrap_node = tox_new(0, 0, 0, 0); - Tox *caller = tox_new(0, 0, 0, 0); - Tox *callees[3] = { - tox_new(0, 0, 0, 0), - tox_new(0, 0, 0, 0), - tox_new(0, 0, 0, 0), - }; - - - ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node"); - + Tox* Alice, *bootstrap, *Bobs[3]; + ToxAV* AliceAV, *BobsAV[3]; + + CallControl AliceCC[3], BobsCC[3]; + int i = 0; - - for (; i < 3; i ++) { - ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances"); - } - - for ( i = 0; i < 3; i ++ ) { - uint32_t to_compare = 974536; - tox_callback_friend_request(callees[i], accept_friend_request, &to_compare); - uint8_t address[TOX_ADDRESS_SIZE]; - tox_self_get_address(callees[i], address); - - uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0); - ck_assert_msg( test == i, "Failed to add friend error code: %i", test); + { + TOX_ERR_NEW error; + + bootstrap = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + Alice = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + for (; i < 3; i ++) { + BobsAV[i] = tox_new(NULL, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + } } - + + printf("Created 5 instances of Tox\n"); + printf("Preparing network...\n"); + long long unsigned int cur_time = time(NULL); + + uint32_t to_compare = 974536; + uint8_t address[TOX_ADDRESS_SIZE]; + + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); + tox_self_get_address(Alice, address); + + + assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); + assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); + assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); + uint8_t off = 1; - + while (1) { - tox_iterate(bootstrap_node); - tox_iterate(caller); - - for (i = 0; i < 3; i ++) { - tox_iterate(callees[i]); - } - - - if (tox_self_get_connection_status(bootstrap_node) && - tox_self_get_connection_status(caller) && - tox_self_get_connection_status(callees[0]) && - tox_self_get_connection_status(callees[1]) && - tox_self_get_connection_status(callees[2]) && off) { + tox_iterate(bootstrap); + tox_iterate(Alice); + tox_iterate(Bobs[0]); + tox_iterate(Bobs[1]); + tox_iterate(Bobs[2]); + + if (tox_self_get_connection_status(bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bobs[0]) && + tox_self_get_connection_status(Bobs[1]) && + tox_self_get_connection_status(Bobs[2]) && off) { printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); off = 0; } - - - if (tox_friend_get_connection_status(caller, 0, 0) && - tox_friend_get_connection_status(caller, 1, 0) && - tox_friend_get_connection_status(caller, 2, 0) ) + + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP) break; - + c_sleep(20); } - - printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); - - ToxAv *uniqcallerav = toxav_new(caller, 3); - + + AliceAV = setup_av_instance(Alice, &AliceCC); + BobsAV[0] = setup_av_instance(Bobs[0], &BobsCC[0]); + BobsAV[1] = setup_av_instance(Bobs[1], &BobsCC[1]); + BobsAV[2] = setup_av_instance(Bobs[2], &BobsCC[2]); + + printf("Created 4 instances of ToxAV\n"); + printf("All set after %llu seconds!\n", time(NULL) - cur_time); + + + + tox_kill(bootstrap); + tox_kill(Alice); + toxav_kill(AliceAV); + for (i = 0; i < 3; i ++) { - status_control.calls[i].idx = i; - - status_control.calls[i].Caller.av = uniqcallerav; - status_control.calls[i].Caller.id = 0; - status_control.calls[i].Caller.status = none; - - status_control.calls[i].Callee.av = toxav_new(callees[i], 1); - status_control.calls[i].Callee.id = i; - status_control.calls[i].Callee.status = none; + tox_kill(Bobs[i]); + toxav_kill(BobsAV[i]); } - - pthread_mutex_init(&muhmutex, NULL); - - for ( i = 0; i < 3; i++ ) { - call_running[i] = 1; - pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]); - } - - /* Now start 3 calls and they'll run for 10 s */ - - for ( i = 0; i < 3; i++ ) - pthread_detach(status_control.calls[i].tid); - - while (call_running[0] || call_running[1] || call_running[2]) { - pthread_mutex_lock(&muhmutex); - - tox_iterate(bootstrap_node); - tox_iterate(caller); - tox_iterate(callees[0]); - tox_iterate(callees[1]); - tox_iterate(callees[2]); - - for ( i = 0; i < 3; i++ ) - toxav_do(status_control.calls[0].Caller.av); - - toxav_do(status_control.calls[0].Callee.av); - toxav_do(status_control.calls[1].Callee.av); - toxav_do(status_control.calls[2].Callee.av); - - pthread_mutex_unlock(&muhmutex); - c_sleep(20); - } - - toxav_kill(status_control.calls[0].Caller.av); - toxav_kill(status_control.calls[0].Callee.av); - toxav_kill(status_control.calls[1].Callee.av); - toxav_kill(status_control.calls[2].Callee.av); - - tox_kill(bootstrap_node); - tox_kill(caller); - - for ( i = 0; i < 3; i ++) - tox_kill(callees[i]); - + + printf("\nTest successful!\n"); } END_TEST @@ -410,8 +229,4 @@ int main(int argc, char *argv[]) srunner_free(test_runner); return number_failed; - -// test_AV_three_calls(); - -// return 0; } diff --git a/testing/av_test.c b/testing/av_test.c new file mode 100644 index 00000000..7298ed23 --- /dev/null +++ b/testing/av_test.c @@ -0,0 +1,744 @@ +/** av_test.c + * + * Copyright (C) 2013-2015 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 . + * + * Compile with (Linux only; in newly created directory toxcore/dir_name): + * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \ + * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio + */ + + +#include "../toxav/toxav.h" +#include "../toxcore/tox.h" +#include "../toxcore/util.h" +#include "../toxcore/network.h" /* current_time_monotonic() */ + +#define LOGGING +#include "../toxcore/logger.h" + +/* Playing audio data */ +#include +/* Reading audio */ +#include + +/* Reading and Displaying video data */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define c_sleep(x) usleep(1000*x) + + +#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) + +// RGB -> YUV +#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) +#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) +#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) + +// YUV -> RGB +#define C(Y) ( (Y) - 16 ) +#define D(U) ( (U) - 128 ) +#define E(V) ( (V) - 128 ) + +#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8) +#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) +#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) + + +#define TEST_TRANSFER_A 0 +#define TEST_TRANSFER_V 1 + + +typedef struct { + bool incoming; + uint32_t state; + pthread_mutex_t arb_mutex[1]; + RingBuffer* arb; /* Audio ring buffer */ + +} CallControl; + +struct toxav_thread_data { + ToxAV* AliceAV; + ToxAV* BobAV; + int32_t sig; +}; + +const char* vdout = "AV Test"; /* Video output */ +PaStream* adout = NULL; /* Audio output */ + + +typedef struct { + uint16_t size; + int16_t data[]; +} frame; + +void* pa_write_thread (void* d) +{ + /* The purpose of this thread is to make sure Pa_WriteStream will not block + * toxav_iterate thread + */ + CallControl* cc = d; + + while (Pa_IsStreamActive(adout)) { + frame* f; + pthread_mutex_lock(cc->arb_mutex); + if (rb_read(cc->arb, (void**)&f)) { + pthread_mutex_unlock(cc->arb_mutex); + Pa_WriteStream(adout, f->data, f->size); + free(f); + } else { + pthread_mutex_unlock(cc->arb_mutex); + c_sleep(10); + } + } +} + + +/** + * Callbacks + */ +void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) +{ + printf("Handling CALL callback\n"); + ((CallControl*)user_data)->incoming = true; +} +void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) +{ + printf("Handling CALL STATE callback: %d\n", state); + ((CallControl*)user_data)->state = state; +} +void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, + uint16_t width, uint16_t height, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, + void *user_data) +{ + uint16_t *img_data = malloc(height * width * 6); + + unsigned long int i, j; + for (i = 0; i < height; ++i) { + for (j = 0; j < width; ++j) { + uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j); + int yx = y[(i * ystride) + j]; + int ux = u[((i / 2) * ustride) + (j / 2)]; + int vx = v[((i / 2) * vstride) + (j / 2)]; + + point[0] = YUV2R(yx, ux, vx); + point[1] = YUV2G(yx, ux, vx); + point[2] = YUV2B(yx, ux, vx); + } + } + + + CvMat mat = cvMat(height, width, CV_8UC3, img_data); + + CvSize sz = {.height = height, .width = width}; + + IplImage* header = cvCreateImageHeader(sz, 1, 3); + IplImage* img = cvGetImage(&mat, header); + cvShowImage(vdout, img); + free(img_data); +} +void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, + int16_t const *pcm, + size_t sample_count, + uint8_t channels, + uint32_t sampling_rate, + void *user_data) +{ + CallControl* cc = user_data; + frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t)); + memcpy(f->data, pcm, sample_count * sizeof(int16_t)); + f->size = sample_count/channels; + + pthread_mutex_lock(cc->arb_mutex); + free(rb_write(cc->arb, f)); + pthread_mutex_unlock(cc->arb_mutex); +} +void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, + bool stable, uint32_t bit_rate, void *user_data) +{ + if (stable) + printf ("Set new audio bit rate to: %d\n", bit_rate); + else + printf ("The network is overly saturated with audio bit rate at: %d\n", bit_rate); +} +void t_toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, + bool stable, uint32_t bit_rate, void *user_data) +{ + if (stable) + printf ("Set new video bit rate to: %d", bit_rate); + else + printf ("The network is overly saturated with video bit rate at: %d", bit_rate); +} +void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) +{ + if (length == 7 && memcmp("gentoo", data, 7) == 0) { + assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); + } +} + + +/** + */ +void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC) +{ + Tox* Alice; + Tox* Bob; + + struct Tox_Options opts; + tox_options_default(&opts); + + opts.end_port = 0; + + { + TOX_ERR_NEW error; + + opts.start_port = 33445; + *bootstrap = tox_new(&opts, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + opts.start_port = 33455; + Alice = tox_new(&opts, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + + opts.start_port = 33465; + Bob = tox_new(&opts, NULL, 0, &error); + assert(error == TOX_ERR_NEW_OK); + } + + printf("Created 3 instances of Tox\n"); + printf("Preparing network...\n"); + long long unsigned int cur_time = time(NULL); + + uint32_t to_compare = 974536; + uint8_t address[TOX_ADDRESS_SIZE]; + + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); + tox_self_get_address(Alice, address); + + + assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); + + uint8_t off = 1; + + while (1) { + tox_iterate(*bootstrap); + tox_iterate(Alice); + tox_iterate(Bob); + + if (tox_self_get_connection_status(*bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bob) && off) { + printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); + off = 0; + } + + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) + break; + + c_sleep(20); + } + + + TOXAV_ERR_NEW rc; + *AliceAV = toxav_new(Alice, &rc); + assert(rc == TOXAV_ERR_NEW_OK); + + *BobAV = toxav_new(Bob, &rc); + assert(rc == TOXAV_ERR_NEW_OK); + + + /* Alice */ + toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); + toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); + toxav_callback_receive_video_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); + toxav_callback_receive_audio_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); + toxav_callback_audio_bit_rate_status(*AliceAV, t_toxav_audio_bit_rate_status_cb, AliceCC); + toxav_callback_video_bit_rate_status(*AliceAV, t_toxav_video_bit_rate_status_cb, AliceCC); + + /* Bob */ + toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); + toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); + toxav_callback_receive_video_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); + toxav_callback_receive_audio_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); + toxav_callback_audio_bit_rate_status(*BobAV, t_toxav_audio_bit_rate_status_cb, BobCC); + toxav_callback_video_bit_rate_status(*BobAV, t_toxav_video_bit_rate_status_cb, BobCC); + + + printf("Created 2 instances of ToxAV\n"); + printf("All set after %llu seconds!\n", time(NULL) - cur_time); +} +int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV) +{ + tox_iterate(bootstrap); + tox_iterate(toxav_get_tox(AliceAV)); + tox_iterate(toxav_get_tox(BobAV)); + + return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV))); +} +void* iterate_toxav (void * data) +{ + struct toxav_thread_data* data_cast = data; +#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 + cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE); +#endif + + while (data_cast->sig == 0) { + toxav_iterate(data_cast->AliceAV); + toxav_iterate(data_cast->BobAV); + int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); + + printf("\rIteration interval: %d ", rc); + fflush(stdout); + +#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 + cvWaitKey(rc); +#else + c_sleep(rc); +#endif + } + + data_cast->sig = 1; + +#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 + cvDestroyWindow(vdout); +#endif + + pthread_exit(NULL); +} + +int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) +{ + int32_t strides[3] = { 1280, 640, 640 }; + uint8_t* planes[3] = { + malloc(img->height * img->width), + malloc(img->height * img->width / 2), + malloc(img->height * img->width / 2), + }; + + int x_chroma_shift = 1; + int y_chroma_shift = 1; + + int x, y; + for (y = 0; y < img->height; ++y) { + for (x = 0; x < img->width; ++x) { + uint8_t r = img->imageData[(x + y * img->width) * 3 + 0]; + uint8_t g = img->imageData[(x + y * img->width) * 3 + 1]; + uint8_t b = img->imageData[(x + y * img->width) * 3 + 2]; + + planes[0][x + y * strides[0]] = RGB2Y(r, g, b); + if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) { + const int i = x / (1 << x_chroma_shift); + const int j = y / (1 << y_chroma_shift); + planes[1][i + j * strides[1]] = RGB2U(r, g, b); + planes[2][i + j * strides[2]] = RGB2V(r, g, b); + } + } + } + + + int rc = toxav_send_video_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL); + free(planes[0]); + free(planes[1]); + free(planes[2]); + return rc; +} + +int print_audio_devices() +{ + int i = 0; + for (i = 0; i < Pa_GetDeviceCount(); ++i) { + const PaDeviceInfo* info = Pa_GetDeviceInfo(i); + if (info) + printf("%d) %s\n", i, info->name); + } + + return 0; +} + +int print_help (const char* name) +{ + printf("Usage: %s -[a:v:o:dh]\n" + "-a audio input file\n" + "-b audio frame duration\n" + "-v video input file\n" + "-x video frame duration\n" + "-o output audio device index\n" + "-d print output audio devices\n" + "-h print this help\n", name); + + return 0; +} + + +int main (int argc, char** argv) +{ + freopen("/dev/zero", "w", stderr); + Pa_Initialize(); + + struct stat st; + + /* AV files for testing */ + const char* af_name = NULL; + const char* vf_name = NULL; + long audio_out_dev_idx = -1; + + int32_t audio_frame_duration = 20; + int32_t video_frame_duration = 10; + + /* Parse settings */ + CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) { + case 'a': + af_name = optarg; + goto CHECK_ARG; + case 'b':{ + char *d; + audio_frame_duration = strtol(optarg, &d, 10); + if (*d) { + printf("Invalid value for argument: 'b'"); + exit(1); + } + goto CHECK_ARG; + } + case 'v': + vf_name = optarg; + goto CHECK_ARG; + case 'x':{ + char *d; + video_frame_duration = strtol(optarg, &d, 10); + if (*d) { + printf("Invalid value for argument: 'x'"); + exit(1); + } + goto CHECK_ARG; + } + case 'o': { + char *d; + audio_out_dev_idx = strtol(optarg, &d, 10); + if (*d) { + printf("Invalid value for argument: 'o'"); + exit(1); + } + goto CHECK_ARG; + } + case 'd': + return print_audio_devices(); + case 'h': + return print_help(argv[0]); + case '?': + exit(1); + case -1:; + } + + { /* Check files */ + if (!af_name) { + printf("Required audio input file!\n"); + exit(1); + } + + if (!vf_name) { + printf("Required video input file!\n"); + exit(1); + } + + /* Check for files */ + if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) + { + printf("%s doesn't seem to be a regular file!\n", af_name); + exit(1); + } + + if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) + { + printf("%s doesn't seem to be a regular file!\n", vf_name); + exit(1); + } + } + + if (audio_out_dev_idx < 0) + audio_out_dev_idx = Pa_GetDefaultOutputDevice(); + + const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx); + if (!audio_dev) { + fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx); + return 1; + } + + printf("Using audio device: %s\n", audio_dev->name); + printf("Using audio file: %s\n", af_name); + printf("Using video file: %s\n", vf_name); + + /* START TOX NETWORK */ + + Tox *bootstrap; + ToxAV *AliceAV; + ToxAV *BobAV; + + CallControl AliceCC; + CallControl BobCC; + + initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC); + + if (TEST_TRANSFER_A) { + SNDFILE* af_handle; + SF_INFO af_info; + + printf("\nTrying audio enc/dec...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); + memset(&BobCC, 0, sizeof(CallControl)); + + pthread_mutex_init(AliceCC.arb_mutex, NULL); + pthread_mutex_init(BobCC.arb_mutex, NULL); + + AliceCC.arb = rb_new(16); + BobCC.arb = rb_new(16); + + { /* Call */ + TOXAV_ERR_CALL rc; + toxav_call(AliceAV, 0, 48, 0, &rc); + + if (rc != TOXAV_ERR_CALL_OK) { + printf("toxav_call failed: %d\n", rc); + exit(1); + } + } + + while (!BobCC.incoming) + iterate_tox(bootstrap, AliceAV, BobAV); + + { /* Answer */ + TOXAV_ERR_ANSWER rc; + toxav_answer(BobAV, 0, 48, 0, &rc); + + if (rc != TOXAV_ERR_ANSWER_OK) { + printf("toxav_answer failed: %d\n", rc); + exit(1); + } + } + + while (AliceCC.state == 0) + iterate_tox(bootstrap, AliceAV, BobAV); + + /* Open audio file */ + af_handle = sf_open(af_name, SFM_READ, &af_info); + if (af_handle == NULL) { + printf("Failed to open the file.\n"); + exit(1); + } + + int16_t PCM[5760]; + + time_t start_time = time(NULL); + time_t expected_time = af_info.frames / af_info.samplerate + 2; + + + /* Start decode thread */ + struct toxav_thread_data data = { + .AliceAV = AliceAV, + .BobAV = BobAV, + .sig = 0 + }; + + pthread_t dect; + pthread_create(&dect, NULL, iterate_toxav, &data); + pthread_detach(dect); + + int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels; + + struct PaStreamParameters output; + output.device = audio_out_dev_idx; + output.channelCount = af_info.channels; + output.sampleFormat = paInt16; + output.suggestedLatency = audio_dev->defaultHighOutputLatency; + output.hostApiSpecificStreamInfo = NULL; + + PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL); + assert(err == paNoError); + + err = Pa_StartStream(adout); + assert(err == paNoError); + + toxav_set_audio_bit_rate(AliceAV, 0, 64, false, NULL); + + /* Start write thread */ + pthread_t t; + pthread_create(&t, NULL, pa_write_thread, &BobCC); + pthread_detach(t); + + printf("Sample rate %d\n", af_info.samplerate); + while ( start_time + expected_time > time(NULL) ) { + uint64_t enc_start_time = current_time_monotonic(); + int64_t count = sf_read_short(af_handle, PCM, frame_size); + if (count > 0) { + TOXAV_ERR_SEND_FRAME rc; + if (toxav_send_audio_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { + printf("Error sending frame of size %ld: %d\n", count, rc); + } + } + iterate_tox(bootstrap, AliceAV, BobAV); + c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1)); + } + + printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time); + + Pa_StopStream(adout); + sf_close(af_handle); + + { /* Hangup */ + TOXAV_ERR_CALL_CONTROL rc; + toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); + + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { + printf("toxav_call_control failed: %d\n", rc); + exit(1); + } + } + + iterate_tox(bootstrap, AliceAV, BobAV); + assert(BobCC.state == TOXAV_CALL_STATE_END); + + /* Stop decode thread */ + data.sig = -1; + while(data.sig != 1) + pthread_yield(); + + pthread_mutex_destroy(AliceCC.arb_mutex); + pthread_mutex_destroy(BobCC.arb_mutex); + + void* f = NULL; + while(rb_read(AliceCC.arb, &f)) + free(f); + + while(rb_read(BobCC.arb, &f)) + free(f); + + printf("Success!"); + } + + if (TEST_TRANSFER_V) { + printf("\nTrying video enc/dec...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); + memset(&BobCC, 0, sizeof(CallControl)); + + { /* Call */ + TOXAV_ERR_CALL rc; + toxav_call(AliceAV, 0, 0, 3000, &rc); + + if (rc != TOXAV_ERR_CALL_OK) { + printf("toxav_call failed: %d\n", rc); + exit(1); + } + } + + while (!BobCC.incoming) + iterate_tox(bootstrap, AliceAV, BobAV); + + { /* Answer */ + TOXAV_ERR_ANSWER rc; + toxav_answer(BobAV, 0, 0, 500, &rc); + + if (rc != TOXAV_ERR_ANSWER_OK) { + printf("toxav_answer failed: %d\n", rc); + exit(1); + } + } + + iterate_tox(bootstrap, AliceAV, BobAV); + + /* Start decode thread */ + struct toxav_thread_data data = { + .AliceAV = AliceAV, + .BobAV = BobAV, + .sig = 0 + }; + + pthread_t dect; + pthread_create(&dect, NULL, iterate_toxav, &data); + pthread_detach(dect); + + CvCapture* capture = cvCreateFileCapture(vf_name); + if (!capture) { + printf("Failed to open video file: %s\n", vf_name); + exit(1); + } + + toxav_set_video_bit_rate(AliceAV, 0, 5000, false, NULL); + + time_t start_time = time(NULL); + while(start_time + 90 > time(NULL)) { + IplImage* frame = cvQueryFrame( capture ); + if (!frame) + break; + + send_opencv_img(AliceAV, 0, frame); + iterate_tox(bootstrap, AliceAV, BobAV); + c_sleep(video_frame_duration); + } + + cvReleaseCapture(&capture); + + { /* Hangup */ + TOXAV_ERR_CALL_CONTROL rc; + toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); + + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { + printf("toxav_call_control failed: %d\n", rc); + exit(1); + } + } + + iterate_tox(bootstrap, AliceAV, BobAV); + assert(BobCC.state == TOXAV_CALL_STATE_END); + + /* Stop decode thread */ + printf("Stopping decode thread\n"); + data.sig = -1; + while(data.sig != 1) + pthread_yield(); + + printf("Success!"); + } + + + Tox* Alice = toxav_get_tox(AliceAV); + Tox* Bob = toxav_get_tox(BobAV); + toxav_kill(BobAV); + toxav_kill(AliceAV); + tox_kill(Bob); + tox_kill(Alice); + tox_kill(bootstrap); + + printf("\nTest successful!\n"); + + Pa_Terminate(); + return 0; +} diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 0434a3c6..3907951e 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -37,25 +37,4 @@ libtoxav_la_LIBADD = libtoxcore.la \ $(PTHREAD_LIBS) \ $(AV_LIBS) - - -#noinst_PROGRAMS += av_test - -#av_test_SOURCES = ../toxav/av_test.c - -#av_test_CFLAGS = $(LIBSODIUM_CFLAGS) \ - $(NACL_CFLAGS) - -#av_test_LDADD = $(LIBSODIUM_LDFLAGS) \ - $(NACL_LDFLAGS) \ - libtoxav.la \ - libtoxcore.la \ - $(LIBSODIUM_LIBS) \ - $(NACL_OBJECTS) \ - -lopenal \ - -lopencv_calib3d -lopencv_contrib -lopencv_core -lopencv_features2d -lopencv_flann -lopencv_gpu -lopencv_highgui -lopencv_imgproc -lopencv_legacy -lopencv_ml -lopencv_objdetect -lopencv_ocl -lopencv_photo -lopencv_stitching -lopencv_superres -lopencv_ts -lopencv_video -lopencv_videostab \ - -lsndfile \ - $(NACL_LIBS) - - endif \ No newline at end of file diff --git a/toxav/audio.c b/toxav/audio.c index 2f068c85..c592a7da 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -31,14 +31,14 @@ static void jbuf_clear(struct JitterBuffer *q); static void jbuf_free(struct JitterBuffer *q); static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); - -OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count); +OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, int32_t *old_br, int32_t *old_sr, int32_t *old_ch); bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); -ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data) + +ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_receive_audio_frame_cb *cb, void *cb_data) { ACSession *ac = calloc(sizeof(ACSession), 1); @@ -78,11 +78,11 @@ ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *c goto DECODER_CLEANUP; } - ac->last_encoding_bitrate = 48000; + ac->last_encoding_bit_rate = 48000; ac->last_encoding_sampling_rate = 48000; ac->last_encoding_channel_count = 2; - ac->last_test_encoding_bitrate = 48000; + ac->last_test_encoding_bit_rate = 48000; ac->last_test_encoding_sampling_rate = 48000; ac->last_test_encoding_channel_count = 2; @@ -97,7 +97,7 @@ ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *c ac->last_packet_channel_count = 1; ac->av = av; - ac->friend_id = friend_id; + ac->friend_number = friend_number; ac->acb.first = cb; ac->acb.second = cb_data; @@ -181,7 +181,7 @@ void ac_do(ACSession* ac) } else if (ac->acb.first) { ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate; - ac->acb.first(ac->av, ac->friend_id, tmp, rc * ac->last_packet_channel_count, + ac->acb.first(ac->av, ac->friend_number, tmp, rc * ac->last_packet_channel_count, ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second); } @@ -220,28 +220,27 @@ int ac_queue_message(void* acp, struct RTPMessage_s *msg) return 0; } -int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) +int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) { - if (!ac || !reconfigure_audio_encoder(&ac->encoder, bitrate, sampling_rate, channels, - &ac->last_encoding_bitrate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) + if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels, + &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) return -1; - LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); + LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); return 0; } -int ac_reconfigure_test_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels) +int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) { - if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bitrate, sampling_rate, channels, - &ac->last_encoding_bitrate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) + if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bit_rate, sampling_rate, channels, + &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) return -1; - LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); + LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); return 0; } -/* JITTER BUFFER WORK */ struct JitterBuffer { RTPMessage **queue; uint32_t size; @@ -340,7 +339,7 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) *success = 0; return NULL; } -OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32_t channel_count) +OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) { int status = OPUS_OK; OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status); @@ -350,7 +349,7 @@ OpusEncoder* create_audio_encoder (int32_t bitrate, int32_t sampling_rate, int32 return NULL; } - status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bitrate)); + status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate)); if ( status != OPUS_OK ) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); diff --git a/toxav/audio.h b/toxav/audio.h index a36396f1..c003bac0 100644 --- a/toxav/audio.h +++ b/toxav/audio.h @@ -31,18 +31,21 @@ struct RTPMessage_s; +/* + * Base Audio Codec session type. + */ typedef struct ACSession_s { /* encoding */ OpusEncoder *encoder; int32_t last_encoding_sampling_rate; int32_t last_encoding_channel_count; - int32_t last_encoding_bitrate; + int32_t last_encoding_bit_rate; - /* Testing encoder for dynamic bitrate streaming */ + /* Testing encoder for dynamic bit rate streaming */ OpusEncoder *test_encoder; int32_t last_test_encoding_sampling_rate; int32_t last_test_encoding_channel_count; - int32_t last_test_encoding_bitrate; + int32_t last_test_encoding_bit_rate; /* decoding */ OpusDecoder *decoder; @@ -57,14 +60,30 @@ typedef struct ACSession_s { pthread_mutex_t queue_mutex[1]; ToxAV* av; - uint32_t friend_id; + uint32_t friend_number; PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ } ACSession; -ACSession* ac_new(ToxAV* av, uint32_t friend_id, toxav_receive_audio_frame_cb *cb, void *cb_data); +/* + * Create new Audio Codec session. + */ +ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_receive_audio_frame_cb *cb, void *cb_data); +/* + * Kill the Audio Codec session. + */ void ac_kill(ACSession* ac); +/* + * Do periodic work. Work is consisted out of decoding only. + */ void ac_do(ACSession* ac); +/* + * Queue new rtp message. + */ int ac_queue_message(void *acp, struct RTPMessage_s *msg); -int ac_reconfigure_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels); -int ac_reconfigure_test_encoder(ACSession* ac, int32_t bitrate, int32_t sampling_rate, uint8_t channels); +/* + * Set new values to the encoders. + */ +int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); +int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); + #endif /* AUDIO_H */ \ No newline at end of file diff --git a/toxav/av_test.c b/toxav/av_test.c deleted file mode 100644 index 86deebdf..00000000 --- a/toxav/av_test.c +++ /dev/null @@ -1,976 +0,0 @@ -/** av_test.c - * - * Copyright (C) 2013-2015 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 . - * - * Compile with (Linux only; in newly created directory toxcore/dir_name): - * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \ - * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio - */ - - -#include "../toxav/toxav.h" -#include "../toxcore/tox.h" -#include "../toxcore/util.h" -#include "../toxcore/network.h" /* current_time_monotonic() */ - -#define LOGGING -#include "../toxcore/logger.h" - -/* Playing audio data */ -#include -/* Reading audio */ -#include - -/* Reading and Displaying video data */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define c_sleep(x) usleep(1000*x) - - -#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) - -// RGB -> YUV -#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) -#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) -#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) - -// YUV -> RGB -#define C(Y) ( (Y) - 16 ) -#define D(U) ( (U) - 128 ) -#define E(V) ( (V) - 128 ) - -#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8) -#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) -#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) - - -/* Enable/disable tests */ -#define TEST_REGULAR_AV 0 -#define TEST_REGULAR_A 0 -#define TEST_REGULAR_V 0 -#define TEST_REJECT 0 -#define TEST_CANCEL 0 -#define TEST_MUTE_UNMUTE 0 -#define TEST_TRANSFER_A 0 -#define TEST_TRANSFER_V 1 - - -typedef struct { - bool incoming; - uint32_t state; - pthread_mutex_t arb_mutex[1]; - RingBuffer* arb; /* Audio ring buffer */ - -} CallControl; - -struct toxav_thread_data { - ToxAV* AliceAV; - ToxAV* BobAV; - int32_t sig; -}; - -const char* vdout = "AV Test"; /* Video output */ -PaStream* adout = NULL; /* Audio output */ - - -typedef struct { - uint16_t size; - int16_t data[]; -} frame; - -void* pa_write_thread (void* d) -{ - /* The purpose of this thread is to make sure Pa_WriteStream will not block - * toxav_iterate thread - */ - CallControl* cc = d; - - while (Pa_IsStreamActive(adout)) { - frame* f; - pthread_mutex_lock(cc->arb_mutex); - if (rb_read(cc->arb, (void**)&f)) { - pthread_mutex_unlock(cc->arb_mutex); - Pa_WriteStream(adout, f->data, f->size); - free(f); - } else { - pthread_mutex_unlock(cc->arb_mutex); - c_sleep(10); - } - } -} - - -/** - * Callbacks - */ -void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) -{ - printf("Handling CALL callback\n"); - ((CallControl*)user_data)->incoming = true; -} -void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) -{ - printf("Handling CALL STATE callback: %d\n", state); - ((CallControl*)user_data)->state = state; -} -void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, - uint16_t width, uint16_t height, - uint8_t const *y, uint8_t const *u, uint8_t const *v, - int32_t ystride, int32_t ustride, int32_t vstride, - void *user_data) -{ - uint16_t *img_data = malloc(height * width * 6); - - unsigned long int i, j; - for (i = 0; i < height; ++i) { - for (j = 0; j < width; ++j) { - uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j); - int yx = y[(i * ystride) + j]; - int ux = u[((i / 2) * ustride) + (j / 2)]; - int vx = v[((i / 2) * vstride) + (j / 2)]; - - point[0] = YUV2R(yx, ux, vx); - point[1] = YUV2G(yx, ux, vx); - point[2] = YUV2B(yx, ux, vx); - } - } - - - CvMat mat = cvMat(height, width, CV_8UC3, img_data); - - CvSize sz = {.height = height, .width = width}; - - IplImage* header = cvCreateImageHeader(sz, 1, 3); - IplImage* img = cvGetImage(&mat, header); - cvShowImage(vdout, img); - free(img_data); -} -void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, - int16_t const *pcm, - size_t sample_count, - uint8_t channels, - uint32_t sampling_rate, - void *user_data) -{ - CallControl* cc = user_data; - frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t)); - memcpy(f->data, pcm, sample_count * sizeof(int16_t)); - f->size = sample_count/channels; - - pthread_mutex_lock(cc->arb_mutex); - free(rb_write(cc->arb, f)); - pthread_mutex_unlock(cc->arb_mutex); -} -void t_toxav_audio_bitrate_control_cb(ToxAV *av, uint32_t friend_number, - bool good, uint32_t bit_rate, void *user_data) -{ - if (good) - printf ("Set new audio bitrate to: %d\n", bit_rate); - else - printf ("The network is overly saturated with audio bitrate at: %d\n", bit_rate); -} -void t_toxav_video_bitrate_control_cb(ToxAV *av, uint32_t friend_number, - bool good, uint32_t bit_rate, void *user_data) -{ - if (good) - printf ("Set new video bitrate to: %d", bit_rate); - else - printf ("The network is overly saturated with video bitrate at: %d", bit_rate); -} -void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) -{ - if (length == 7 && memcmp("gentoo", data, 7) == 0) { - assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); - } -} - - -/** - */ -void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC) -{ - Tox* Alice; - Tox* Bob; - - struct Tox_Options opts; - tox_options_default(&opts); - - opts.end_port = 0; - - { - TOX_ERR_NEW error; - - opts.start_port = 33445; - *bootstrap = tox_new(&opts, NULL, 0, &error); - assert(error == TOX_ERR_NEW_OK); - - opts.start_port = 33455; - Alice = tox_new(&opts, NULL, 0, &error); - assert(error == TOX_ERR_NEW_OK); - - opts.start_port = 33465; - Bob = tox_new(&opts, NULL, 0, &error); - assert(error == TOX_ERR_NEW_OK); - } - - printf("Created 3 instances of Tox\n"); - printf("Preparing network...\n"); - long long unsigned int cur_time = time(NULL); - - uint32_t to_compare = 974536; - uint8_t address[TOX_ADDRESS_SIZE]; - - tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); - tox_self_get_address(Alice, address); - - - assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); - - uint8_t off = 1; - - while (1) { - tox_iterate(*bootstrap); - tox_iterate(Alice); - tox_iterate(Bob); - - if (tox_self_get_connection_status(*bootstrap) && - tox_self_get_connection_status(Alice) && - tox_self_get_connection_status(Bob) && off) { - printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); - off = 0; - } - - if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) - break; - - c_sleep(20); - } - - - TOXAV_ERR_NEW rc; - *AliceAV = toxav_new(Alice, &rc); - assert(rc == TOXAV_ERR_NEW_OK); - - *BobAV = toxav_new(Bob, &rc); - assert(rc == TOXAV_ERR_NEW_OK); - - - /* Alice */ - toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); - toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); - toxav_callback_receive_video_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); - toxav_callback_receive_audio_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); - toxav_callback_audio_bitrate_control(*AliceAV, t_toxav_audio_bitrate_control_cb, AliceCC); - toxav_callback_video_bitrate_control(*AliceAV, t_toxav_video_bitrate_control_cb, AliceCC); - - /* Bob */ - toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); - toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); - toxav_callback_receive_video_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); - toxav_callback_receive_audio_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); - toxav_callback_audio_bitrate_control(*BobAV, t_toxav_audio_bitrate_control_cb, BobCC); - toxav_callback_video_bitrate_control(*BobAV, t_toxav_video_bitrate_control_cb, BobCC); - - - printf("Created 2 instances of ToxAV\n"); - printf("All set after %llu seconds!\n", time(NULL) - cur_time); -} -int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV) -{ - tox_iterate(bootstrap); - tox_iterate(toxav_get_tox(AliceAV)); - tox_iterate(toxav_get_tox(BobAV)); - - return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV))); -} -void* iterate_toxav (void * data) -{ - struct toxav_thread_data* data_cast = data; -#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 - cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE); -#endif - - while (data_cast->sig == 0) { - toxav_iterate(data_cast->AliceAV); - toxav_iterate(data_cast->BobAV); - int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); - - printf("\rIteration interval: %d ", rc); - fflush(stdout); - -#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 - cvWaitKey(rc); -#else - c_sleep(rc); -#endif - } - - data_cast->sig = 1; - -#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 - cvDestroyWindow(vdout); -#endif - - pthread_exit(NULL); -} - -int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) -{ - int32_t strides[3] = { 1280, 640, 640 }; - uint8_t* planes[3] = { - malloc(img->height * img->width), - malloc(img->height * img->width / 2), - malloc(img->height * img->width / 2), - }; - - int x_chroma_shift = 1; - int y_chroma_shift = 1; - - int x, y; - for (y = 0; y < img->height; ++y) { - for (x = 0; x < img->width; ++x) { - uint8_t r = img->imageData[(x + y * img->width) * 3 + 0]; - uint8_t g = img->imageData[(x + y * img->width) * 3 + 1]; - uint8_t b = img->imageData[(x + y * img->width) * 3 + 2]; - - planes[0][x + y * strides[0]] = RGB2Y(r, g, b); - if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) { - const int i = x / (1 << x_chroma_shift); - const int j = y / (1 << y_chroma_shift); - planes[1][i + j * strides[1]] = RGB2U(r, g, b); - planes[2][i + j * strides[2]] = RGB2V(r, g, b); - } - } - } - - - int rc = toxav_send_video_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL); - free(planes[0]); - free(planes[1]); - free(planes[2]); - return rc; -} - -int print_audio_devices() -{ - int i = 0; - for (i = 0; i < Pa_GetDeviceCount(); ++i) { - const PaDeviceInfo* info = Pa_GetDeviceInfo(i); - if (info) - printf("%d) %s\n", i, info->name); - } - - return 0; -} - -int print_help (const char* name) -{ - printf("Usage: %s -[a:v:o:dh]\n" - "-a audio input file\n" - "-b audio frame duration\n" - "-v video input file\n" - "-x video frame duration\n" - "-o output audio device index\n" - "-d print output audio devices\n" - "-h print this help\n", name); - - return 0; -} - - -int main (int argc, char** argv) -{ - freopen("/dev/zero", "w", stderr); - Pa_Initialize(); - - struct stat st; - - /* AV files for testing */ - const char* af_name = NULL; - const char* vf_name = NULL; - long audio_out_dev_idx = -1; - - int32_t audio_frame_duration = 20; - int32_t video_frame_duration = 10; - - /* Parse settings */ - CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) { - case 'a': - af_name = optarg; - goto CHECK_ARG; - case 'b':{ - char *d; - audio_frame_duration = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'b'"); - exit(1); - } - goto CHECK_ARG; - } - case 'v': - vf_name = optarg; - goto CHECK_ARG; - case 'x':{ - char *d; - video_frame_duration = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'x'"); - exit(1); - } - goto CHECK_ARG; - } - case 'o': { - char *d; - audio_out_dev_idx = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'o'"); - exit(1); - } - goto CHECK_ARG; - } - case 'd': - return print_audio_devices(); - case 'h': - return print_help(argv[0]); - case '?': - exit(1); - case -1:; - } - - { /* Check files */ - if (!af_name) { - printf("Required audio input file!\n"); - exit(1); - } - - if (!vf_name) { - printf("Required video input file!\n"); - exit(1); - } - - /* Check for files */ - if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) - { - printf("%s doesn't seem to be a regular file!\n", af_name); - exit(1); - } - - if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) - { - printf("%s doesn't seem to be a regular file!\n", vf_name); - exit(1); - } - } - - if (audio_out_dev_idx < 0) - audio_out_dev_idx = Pa_GetDefaultOutputDevice(); - - const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx); - if (!audio_dev) { - fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx); - return 1; - } - - printf("Using audio device: %s\n", audio_dev->name); - printf("Using audio file: %s\n", af_name); - printf("Using video file: %s\n", vf_name); - - /* START TOX NETWORK */ - - Tox *bootstrap; - ToxAV *AliceAV; - ToxAV *BobAV; - - CallControl AliceCC; - CallControl BobCC; - - initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC); - -#define REGULAR_CALL_FLOW(A_BR, V_BR) \ - do { \ - memset(&AliceCC, 0, sizeof(CallControl)); \ - memset(&BobCC, 0, sizeof(CallControl)); \ - \ - TOXAV_ERR_CALL rc; \ - toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \ - \ - if (rc != TOXAV_ERR_CALL_OK) { \ - printf("toxav_call failed: %d\n", rc); \ - exit(1); \ - } \ - \ - \ - long long unsigned int start_time = time(NULL); \ - \ - \ - while (BobCC.state != TOXAV_CALL_STATE_END) { \ - \ - if (BobCC.incoming) { \ - TOXAV_ERR_ANSWER rc; \ - toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \ - \ - if (rc != TOXAV_ERR_ANSWER_OK) { \ - printf("toxav_answer failed: %d\n", rc); \ - exit(1); \ - } \ - BobCC.incoming = false; \ - } else { \ - /* TODO rtp */ \ - \ - if (time(NULL) - start_time == 5) { \ - \ - TOXAV_ERR_CALL_CONTROL rc; \ - toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ - \ - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ - printf("toxav_call_control failed: %d\n", rc); \ - exit(1); \ - } \ - } \ - } \ - \ - iterate_tox(bootstrap, AliceAV, BobAV); \ - } \ - printf("Success!\n");\ - } while(0) - - if (TEST_REGULAR_AV) { - printf("\nTrying regular call (Audio and Video)...\n"); - REGULAR_CALL_FLOW(48, 4000); - } - - if (TEST_REGULAR_A) { - printf("\nTrying regular call (Audio only)...\n"); - REGULAR_CALL_FLOW(48, 0); - } - - if (TEST_REGULAR_V) { - printf("\nTrying regular call (Video only)...\n"); - REGULAR_CALL_FLOW(0, 4000); - } - -#undef REGULAR_CALL_FLOW - - if (TEST_REJECT) { /* Alice calls; Bob rejects */ - printf("\nTrying reject flow...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); - memset(&BobCC, 0, sizeof(CallControl)); - - { - TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 48, 0, &rc); - - if (rc != TOXAV_ERR_CALL_OK) { - printf("toxav_call failed: %d\n", rc); - exit(1); - } - } - - while (!BobCC.incoming) - iterate_tox(bootstrap, AliceAV, BobAV); - - /* Reject */ - { - TOXAV_ERR_CALL_CONTROL rc; - toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { - printf("toxav_call_control failed: %d\n", rc); - exit(1); - } - } - - while (AliceCC.state != TOXAV_CALL_STATE_END) - iterate_tox(bootstrap, AliceAV, BobAV); - - printf("Success!\n"); - } - - if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */ - printf("\nTrying cancel (while ringing) flow...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); - memset(&BobCC, 0, sizeof(CallControl)); - - { - TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 48, 0, &rc); - - if (rc != TOXAV_ERR_CALL_OK) { - printf("toxav_call failed: %d\n", rc); - exit(1); - } - } - - while (!BobCC.incoming) - iterate_tox(bootstrap, AliceAV, BobAV); - - /* Cancel */ - { - TOXAV_ERR_CALL_CONTROL rc; - toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { - printf("toxav_call_control failed: %d\n", rc); - exit(1); - } - } - - /* Alice will not receive end state */ - while (BobCC.state != TOXAV_CALL_STATE_END) - iterate_tox(bootstrap, AliceAV, BobAV); - - printf("Success!\n"); - } - - if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */ - printf("\nTrying mute functionality...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); - memset(&BobCC, 0, sizeof(CallControl)); - - /* Assume sending audio and video */ - { - TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 48, 1000, &rc); - - if (rc != TOXAV_ERR_CALL_OK) { - printf("toxav_call failed: %d\n", rc); - exit(1); - } - } - - while (!BobCC.incoming) - iterate_tox(bootstrap, AliceAV, BobAV); - - /* At first try all stuff while in invalid state */ - assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); - assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); - assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); - assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); - - { - TOXAV_ERR_ANSWER rc; - toxav_answer(BobAV, 0, 48, 4000, &rc); - - if (rc != TOXAV_ERR_ANSWER_OK) { - printf("toxav_answer failed: %d\n", rc); - exit(1); - } - } - - iterate_tox(bootstrap, AliceAV, BobAV); - - /* Pause and Resume */ - printf("Pause and Resume\n"); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state == TOXAV_CALL_STATE_PAUSED); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state & (TOXAV_CALL_STATE_SENDING_A | TOXAV_CALL_STATE_SENDING_V)); - - /* Mute/Unmute single */ - printf("Mute/Unmute single\n"); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A); - - /* Mute/Unmute both */ - printf("Mute/Unmute both\n"); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_V); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A); - assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL)); - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_V); - - { - TOXAV_ERR_CALL_CONTROL rc; - toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { - printf("toxav_call_control failed: %d\n", rc); - exit(1); - } - } - - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state == TOXAV_CALL_STATE_END); - - printf("Success!\n"); - } - - if (TEST_TRANSFER_A) { /* Audio encoding/decoding and transfer */ - SNDFILE* af_handle; - SF_INFO af_info; - - printf("\nTrying audio enc/dec...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); - memset(&BobCC, 0, sizeof(CallControl)); - - pthread_mutex_init(AliceCC.arb_mutex, NULL); - pthread_mutex_init(BobCC.arb_mutex, NULL); - - AliceCC.arb = rb_new(16); - BobCC.arb = rb_new(16); - - { /* Call */ - TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 48, 0, &rc); - - if (rc != TOXAV_ERR_CALL_OK) { - printf("toxav_call failed: %d\n", rc); - exit(1); - } - } - - while (!BobCC.incoming) - iterate_tox(bootstrap, AliceAV, BobAV); - - { /* Answer */ - TOXAV_ERR_ANSWER rc; - toxav_answer(BobAV, 0, 48, 0, &rc); - - if (rc != TOXAV_ERR_ANSWER_OK) { - printf("toxav_answer failed: %d\n", rc); - exit(1); - } - } - - while (AliceCC.state == 0) - iterate_tox(bootstrap, AliceAV, BobAV); - - /* Open audio file */ - af_handle = sf_open(af_name, SFM_READ, &af_info); - if (af_handle == NULL) { - printf("Failed to open the file.\n"); - exit(1); - } - - int16_t PCM[5760]; - - time_t start_time = time(NULL); - time_t expected_time = af_info.frames / af_info.samplerate + 2; - - - /* Start decode thread */ - struct toxav_thread_data data = { - .AliceAV = AliceAV, - .BobAV = BobAV, - .sig = 0 - }; - - pthread_t dect; - pthread_create(&dect, NULL, iterate_toxav, &data); - pthread_detach(dect); - - int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels; - - struct PaStreamParameters output; - output.device = audio_out_dev_idx; - output.channelCount = af_info.channels; - output.sampleFormat = paInt16; - output.suggestedLatency = audio_dev->defaultHighOutputLatency; - output.hostApiSpecificStreamInfo = NULL; - - PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL); - assert(err == paNoError); - - err = Pa_StartStream(adout); - assert(err == paNoError); - - toxav_set_audio_bit_rate(AliceAV, 0, 64, false, NULL); - - /* Start write thread */ - pthread_t t; - pthread_create(&t, NULL, pa_write_thread, &BobCC); - pthread_detach(t); - - printf("Sample rate %d\n", af_info.samplerate); - while ( start_time + expected_time > time(NULL) ) { - uint64_t enc_start_time = current_time_monotonic(); - int64_t count = sf_read_short(af_handle, PCM, frame_size); - if (count > 0) { - TOXAV_ERR_SEND_FRAME rc; - if (toxav_send_audio_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { - printf("Error sending frame of size %ld: %d\n", count, rc); - } - } - iterate_tox(bootstrap, AliceAV, BobAV); - c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1)); - } - - printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time); - - Pa_StopStream(adout); - sf_close(af_handle); - - { /* Hangup */ - TOXAV_ERR_CALL_CONTROL rc; - toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { - printf("toxav_call_control failed: %d\n", rc); - exit(1); - } - } - - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state == TOXAV_CALL_STATE_END); - - /* Stop decode thread */ - data.sig = -1; - while(data.sig != 1) - pthread_yield(); - - pthread_mutex_destroy(AliceCC.arb_mutex); - pthread_mutex_destroy(BobCC.arb_mutex); - - void* f = NULL; - while(rb_read(AliceCC.arb, &f)) - free(f); - - while(rb_read(BobCC.arb, &f)) - free(f); - - printf("Success!"); - } - - if (TEST_TRANSFER_V) { - printf("\nTrying video enc/dec...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); - memset(&BobCC, 0, sizeof(CallControl)); - - { /* Call */ - TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 0, 3000, &rc); - - if (rc != TOXAV_ERR_CALL_OK) { - printf("toxav_call failed: %d\n", rc); - exit(1); - } - } - - while (!BobCC.incoming) - iterate_tox(bootstrap, AliceAV, BobAV); - - { /* Answer */ - TOXAV_ERR_ANSWER rc; - toxav_answer(BobAV, 0, 0, 500, &rc); - - if (rc != TOXAV_ERR_ANSWER_OK) { - printf("toxav_answer failed: %d\n", rc); - exit(1); - } - } - - iterate_tox(bootstrap, AliceAV, BobAV); - - /* Start decode thread */ - struct toxav_thread_data data = { - .AliceAV = AliceAV, - .BobAV = BobAV, - .sig = 0 - }; - - pthread_t dect; - pthread_create(&dect, NULL, iterate_toxav, &data); - pthread_detach(dect); - - CvCapture* capture = cvCreateFileCapture(vf_name); - if (!capture) { - printf("Failed to open video file: %s\n", vf_name); - exit(1); - } - - toxav_set_video_bit_rate(AliceAV, 0, 5000, false, NULL); - - time_t start_time = time(NULL); - while(start_time + 90 > time(NULL)) { - IplImage* frame = cvQueryFrame( capture ); - if (!frame) - break; - - send_opencv_img(AliceAV, 0, frame); - iterate_tox(bootstrap, AliceAV, BobAV); - c_sleep(video_frame_duration); - } - - cvReleaseCapture(&capture); - - { /* Hangup */ - TOXAV_ERR_CALL_CONTROL rc; - toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - - if (rc != TOXAV_ERR_CALL_CONTROL_OK) { - printf("toxav_call_control failed: %d\n", rc); - exit(1); - } - } - - iterate_tox(bootstrap, AliceAV, BobAV); - assert(BobCC.state == TOXAV_CALL_STATE_END); - - /* Stop decode thread */ - printf("Stopping decode thread\n"); - data.sig = -1; - while(data.sig != 1) - pthread_yield(); - - printf("Success!"); - } - - - Tox* Alice = toxav_get_tox(AliceAV); - Tox* Bob = toxav_get_tox(BobAV); - toxav_kill(BobAV); - toxav_kill(AliceAV); - tox_kill(Bob); - tox_kill(Alice); - tox_kill(bootstrap); - - printf("\nTest successful!\n"); - - Pa_Terminate(); - return 0; -} diff --git a/toxav/msi.c b/toxav/msi.c index 0bd04c56..f8bc8451 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -75,37 +75,37 @@ typedef struct { MSIHeaderRequest request; MSIHeaderError error; MSIHeaderCapabilities capabilities; - MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */ + MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order TODO: get rid of this eventually */ } MSIMessage; void msg_init (MSIMessage *dest, MSIRequest request); int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); -int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ); -int send_error ( Messenger* m, uint32_t friend_id, MSIError error ); +int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ); +int send_error ( Messenger* m, uint32_t friend_number, MSIError error ); static int invoke_callback(MSICall* call, MSICallbackID cb); -static MSICall *get_call ( MSISession *session, uint32_t friend_id ); -MSICall *new_call ( MSISession *session, uint32_t friend_id ); +static MSICall *get_call ( MSISession *session, uint32_t friend_number ); +MSICall *new_call ( MSISession *session, uint32_t friend_number ); void kill_call ( MSICall *call ); -void on_peer_status(Messenger *m, uint32_t friend_id, uint8_t status, void *data); +void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); void handle_push ( MSICall *call, const MSIMessage *msg ); void handle_pop ( MSICall *call, const MSIMessage *msg ); -void handle_msi_packet ( Messenger *m, uint32_t friend_id, const uint8_t *data, uint16_t length, void *object ); +void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object ); /** * Public functions */ -void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id) +void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id) { pthread_mutex_lock(session->mutex); session->callbacks[id] = callback; pthread_mutex_unlock(session->mutex); } -MSISession *msi_new ( Messenger *messenger ) +MSISession *msi_new ( Messenger *m ) { - if (messenger == NULL) { + if (m == NULL) { LOGGER_ERROR("Could not init session on empty messenger!"); return NULL; } @@ -123,12 +123,12 @@ MSISession *msi_new ( Messenger *messenger ) return NULL; } - retu->messenger = messenger; + retu->messenger = m; - m_callback_msi_packet(messenger, handle_msi_packet, retu ); + m_callback_msi_packet(m, handle_msi_packet, retu ); /* This is called when remote terminates session */ - m_callback_connectionstatus_internal_av(messenger, on_peer_status, retu); + m_callback_connectionstatus_internal_av(m, on_peer_status, retu); LOGGER_DEBUG("New msi session: %p ", retu); return retu; @@ -149,7 +149,7 @@ int msi_kill ( MSISession *session ) MSICall* it = get_call(session, session->calls_head); for (; it; it = it->next) { - send_message(session->messenger, it->friend_id, &msg); + send_message(session->messenger, it->friend_number, &msg); kill_call(it); /* This will eventually free session->calls */ } } @@ -161,18 +161,18 @@ int msi_kill ( MSISession *session ) free ( session ); return 0; } -int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_t capabilities ) +int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities ) { - LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); + LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number); pthread_mutex_lock(session->mutex); - if (get_call(session, friend_id) != NULL) { + if (get_call(session, friend_number) != NULL) { LOGGER_ERROR("Already in a call"); pthread_mutex_unlock(session->mutex); return -1; } - (*call) = new_call ( session, friend_id ); + (*call) = new_call ( session, friend_number ); if ( *call == NULL ) { pthread_mutex_unlock(session->mutex); @@ -190,7 +190,7 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ msg.vfpsz.exists = true; msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - send_message ( (*call)->session->messenger, (*call)->friend_id, &msg ); + send_message ( (*call)->session->messenger, (*call)->friend_number, &msg ); (*call)->state = msi_CallRequesting; @@ -200,7 +200,7 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ } int msi_hangup ( MSICall* call ) { - LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); + LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number); MSISession* session = call->session; pthread_mutex_lock(session->mutex); @@ -208,7 +208,7 @@ int msi_hangup ( MSICall* call ) MSIMessage msg; msg_init(&msg, requ_pop); - send_message ( session->messenger, call->friend_id, &msg ); + send_message ( session->messenger, call->friend_number, &msg ); kill_call(call); pthread_mutex_unlock(session->mutex); @@ -216,7 +216,7 @@ int msi_hangup ( MSICall* call ) } int msi_answer ( MSICall* call, uint8_t capabilities ) { - LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); + LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number); MSISession* session = call->session; pthread_mutex_lock(session->mutex); @@ -240,7 +240,7 @@ int msi_answer ( MSICall* call, uint8_t capabilities ) msg.vfpsz.exists = true; msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - send_message ( session->messenger, call->friend_id, &msg ); + send_message ( session->messenger, call->friend_number, &msg ); call->state = msi_CallActive; pthread_mutex_unlock(session->mutex); @@ -249,7 +249,7 @@ int msi_answer ( MSICall* call, uint8_t capabilities ) } int msi_change_capabilities( MSICall* call, uint8_t capabilities ) { - LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id); + LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number); MSISession* session = call->session; pthread_mutex_lock(session->mutex); @@ -275,7 +275,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities ) msg.capabilities.exists = true; msg.capabilities.value = capabilities; - send_message ( call->session->messenger, call->friend_id, &msg ); + send_message ( call->session->messenger, call->friend_number, &msg ); pthread_mutex_unlock(session->mutex); return 0; @@ -394,7 +394,7 @@ uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value return dest + value_len; /* Set to next position ready to be written */ } -int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ) +int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ) { /* Parse and send message */ assert(m); @@ -438,19 +438,19 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ) *it = 0; size ++; - if ( m_msi_packet(m, friend_id, parsed, size) ) { + if ( m_msi_packet(m, friend_number, parsed, size) ) { LOGGER_DEBUG("Sent message"); return 0; } return -1; } -int send_error ( Messenger* m, uint32_t friend_id, MSIError error ) +int send_error ( Messenger* m, uint32_t friend_number, MSIError error ) { /* Send error message */ assert(m); - LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id); + LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number); MSIMessage msg; msg_init(&msg, requ_pop); @@ -458,7 +458,7 @@ int send_error ( Messenger* m, uint32_t friend_id, MSIError error ) msg.error.exists = true; msg.error.value = error; - send_message ( m, friend_id, &msg ); + send_message ( m, friend_number, &msg ); return 0; } int invoke_callback(MSICall* call, MSICallbackID cb) @@ -484,16 +484,16 @@ FAILURE: call->error = msi_EHandle; return -1; } -static MSICall *get_call ( MSISession *session, uint32_t friend_id ) +static MSICall *get_call ( MSISession *session, uint32_t friend_number ) { assert(session); - if (session->calls == NULL || session->calls_tail < friend_id) + if (session->calls == NULL || session->calls_tail < friend_number) return NULL; - return session->calls[friend_id]; + return session->calls[friend_number]; } -MSICall *new_call ( MSISession *session, uint32_t friend_id ) +MSICall *new_call ( MSISession *session, uint32_t friend_number ) { assert(session); @@ -503,20 +503,20 @@ MSICall *new_call ( MSISession *session, uint32_t friend_id ) return NULL; rc->session = session; - rc->friend_id = friend_id; + rc->friend_number = friend_number; if (session->calls == NULL) { /* Creating */ - session->calls = calloc (sizeof(MSICall*), friend_id + 1); + session->calls = calloc (sizeof(MSICall*), friend_number + 1); if (session->calls == NULL) { free(rc); return NULL; } - session->calls_tail = session->calls_head = friend_id; + session->calls_tail = session->calls_head = friend_number; - } else if (session->calls_tail < friend_id) { /* Appending */ - void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_id + 1); + } else if (session->calls_tail < friend_number) { /* Appending */ + void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_number + 1); if (tmp == NULL) { free(rc); @@ -526,22 +526,22 @@ MSICall *new_call ( MSISession *session, uint32_t friend_id ) session->calls = tmp; /* Set fields in between to null */ - int32_t i = session->calls_tail; - for (; i < friend_id; i ++) + int32_t i = session->calls_tail + 1; + for (; i < friend_number; i ++) session->calls[i] = NULL; rc->prev = session->calls[session->calls_tail]; session->calls[session->calls_tail]->next = rc; - session->calls_tail = friend_id; + session->calls_tail = friend_number; - } else if (session->calls_head > friend_id) { /* Inserting at front */ + } else if (session->calls_head > friend_number) { /* Inserting at front */ rc->next = session->calls[session->calls_head]; session->calls[session->calls_head]->prev = rc; - session->calls_head = friend_id; + session->calls_head = friend_number; } - session->calls[friend_id] = rc; + session->calls[friend_number] = rc; return rc; } void kill_call ( MSICall *call ) @@ -560,16 +560,16 @@ void kill_call ( MSICall *call ) if (prev) prev->next = next; else if (next) - session->calls_head = next->friend_id; + session->calls_head = next->friend_number; else goto CLEAR_CONTAINER; if (next) next->prev = prev; else if (prev) - session->calls_tail = prev->friend_id; + session->calls_tail = prev->friend_number; else goto CLEAR_CONTAINER; - session->calls[call->friend_id] = NULL; + session->calls[call->friend_number] = NULL; free(call); return; @@ -579,17 +579,17 @@ CLEAR_CONTAINER: free(call); session->calls = NULL; } -void on_peer_status(Messenger* m, uint32_t friend_id, uint8_t status, void* data) +void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data) { (void)m; MSISession *session = data; switch ( status ) { case 0: { /* Friend is now offline */ - LOGGER_DEBUG("Friend %d is now offline", friend_id); + LOGGER_DEBUG("Friend %d is now offline", friend_number); pthread_mutex_lock(session->mutex); - MSICall* call = get_call(session, friend_id); + MSICall* call = get_call(session, friend_number); if (call == NULL) { pthread_mutex_unlock(session->mutex); @@ -610,9 +610,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg ) { assert(call); - MSISession* session = call->session; - - LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_id); + LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number); if (!msg->capabilities.exists) { LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); @@ -670,7 +668,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg ) msg.vfpsz.exists = true; msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - send_message ( call->session->messenger, call->friend_id, &msg ); + send_message ( call->session->messenger, call->friend_number, &msg ); /* If peer changed capabilities during re-call they will * be handled accordingly during the next step @@ -708,14 +706,14 @@ void handle_push ( MSICall *call, const MSIMessage *msg ) return; FAILURE: - send_error(call->session->messenger, call->friend_id, call->error); + send_error(call->session->messenger, call->friend_number, call->error); kill_call(call); } void handle_pop ( MSICall *call, const MSIMessage *msg ) { assert(call); - LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id); + LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number); /* callback errors are ignored */ @@ -751,7 +749,7 @@ void handle_pop ( MSICall *call, const MSIMessage *msg ) kill_call ( call ); } -void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, uint16_t length, void* object ) +void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object ) { LOGGER_DEBUG("Got msi message"); @@ -760,25 +758,25 @@ void handle_msi_packet ( Messenger* m, uint32_t friend_id, const uint8_t* data, if ( msg_parse_in ( &msg, data, length ) == -1 ) { LOGGER_WARNING("Error parsing message"); - send_error(m, friend_id, msi_EInvalidMessage); + send_error(m, friend_number, msi_EInvalidMessage); return; } else { LOGGER_DEBUG("Successfully parsed message"); } pthread_mutex_lock(session->mutex); - MSICall *call = get_call(session, friend_id); + MSICall *call = get_call(session, friend_number); if (call == NULL) { if (msg.request.value != requ_push) { - send_error(m, friend_id, msi_EStrayMessage); + send_error(m, friend_number, msi_EStrayMessage); pthread_mutex_unlock(session->mutex); return; } - call = new_call(session, friend_id); + call = new_call(session, friend_number); if (call == NULL) { - send_error(m, friend_id, msi_ESystem); + send_error(m, friend_number, msi_ESystem); pthread_mutex_unlock(session->mutex); return; } diff --git a/toxav/msi.h b/toxav/msi.h index 457d3148..4836ae89 100644 --- a/toxav/msi.h +++ b/toxav/msi.h @@ -89,7 +89,7 @@ typedef struct MSICall_s { uint8_t peer_capabilities; /* Peer capabilities */ uint8_t self_capabilities; /* Self capabilities */ uint16_t peer_vfpsz; /* Video frame piece size */ - uint32_t friend_id; /* Index of this call in MSISession */ + uint32_t friend_number; /* Index of this call in MSISession */ MSIError error; /* Last error */ void* av_call; /* Pointer to av call handler */ @@ -100,12 +100,11 @@ typedef struct MSICall_s { /** - * Msi callback type. 'agent' is a pointer to ToxAv. * Expected return on success is 0, if any other number is * returned the call is considered errored and will be handled * as such which means it will be terminated without any notice. */ -typedef int ( *MSICallbackType ) ( void *agent, MSICall* call); +typedef int msi_action_cb ( void *av, MSICall* call); /** * Control session struct. Please do not modify outside msi.c @@ -119,43 +118,34 @@ typedef struct MSISession_s { void *av; Messenger *messenger; - /* The mutex controls async access from control - * thread(s) and core thread. - */ pthread_mutex_t mutex[1]; - MSICallbackType callbacks[7]; + msi_action_cb* callbacks[7]; } MSISession; /** * Start the control session. */ -MSISession *msi_new ( Messenger *messenger ); - +MSISession *msi_new ( Messenger *m ); /** * Terminate control session. NOTE: all calls will be freed */ int msi_kill ( MSISession *session ); - /** * Callback setter. */ -void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id); - +void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id); /** - * Send invite request to friend_id. + * Send invite request to friend_number. */ -int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_id, uint8_t capabilities ); - +int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities ); /** * Hangup call. NOTE: 'call' will be freed */ int msi_hangup ( MSICall* call ); - /** * Answer call request. */ int msi_answer ( MSICall* call, uint8_t capabilities ); - /** * Change capabilities of the call. */ diff --git a/toxav/rtp.c b/toxav/rtp.c index 6c603f79..2219805b 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -78,11 +78,11 @@ int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *dat void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber ); -RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ) +RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ) { assert(mcb); assert(cs); - assert(messenger); + assert(m); RTPSession *retu = calloc(1, sizeof(RTPSession)); @@ -95,8 +95,8 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num, vo retu->ssrc = random_int(); retu->payload_type = payload_type % 128; - retu->m = messenger; - retu->friend_id = friend_num; + retu->m = m; + retu->friend_number = friend_num; if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) { LOGGER_WARNING("Alloc failed! Program might misbehave!"); @@ -161,7 +161,7 @@ int rtp_do(RTPSession *session) return rtp_StateNormal; if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) { - send_rtcp_report(session->rtcp_session, session->m, session->friend_id); + send_rtcp_report(session->rtcp_session, session->m, session->friend_number); } if (rb_full(session->rtcp_session->pl_stats)) { @@ -209,15 +209,15 @@ int rtp_start_receiving(RTPSession* session) if (session == NULL) return -1; - if (m_callback_rtp_packet(session->m, session->friend_id, session->prefix, + if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix, handle_rtp_packet, session) == -1) { LOGGER_WARNING("Failed to register rtp receive handler"); return -1; } - if (m_callback_rtp_packet(session->m, session->friend_id, session->rtcp_session->prefix, + if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, handle_rtcp_packet, session->rtcp_session) == -1) { LOGGER_WARNING("Failed to register rtcp receive handler"); - m_callback_rtp_packet(session->m, session->friend_id, session->prefix, NULL, NULL); + m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL); return -1; } @@ -228,8 +228,8 @@ int rtp_stop_receiving(RTPSession* session) if (session == NULL) return -1; - m_callback_rtp_packet(session->m, session->friend_id, session->prefix, NULL, NULL); - m_callback_rtp_packet(session->m, session->friend_id, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ + m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL); + m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ return 0; } @@ -243,7 +243,8 @@ int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, b uint8_t parsed[MAX_RTP_SIZE]; uint8_t *it; - RTPHeader header[1]; + RTPHeader header[1] = {0}; + ADD_FLAG_VERSION ( header, session->version ); ADD_FLAG_PADDING ( header, session->padding ); ADD_FLAG_EXTENSION ( header, session->extension ); @@ -278,12 +279,11 @@ int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, b memcpy(it, data, length); - if ( -1 == send_custom_lossy_packet(session->m, session->friend_id, parsed, parsed_len) ) { + if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) { LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); return -1; } - /* Set sequ number */ session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; return 0; } @@ -300,7 +300,6 @@ void rtp_free_msg ( RTPMessage *msg ) - RTPHeader *parse_header_in ( const uint8_t *payload, int length ) { if ( !payload || !length ) { @@ -322,12 +321,7 @@ RTPHeader *parse_header_in ( const uint8_t *payload, int length ) 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!"); @@ -335,15 +329,10 @@ RTPHeader *parse_header_in ( const uint8_t *payload, int length ) 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 total = 12 /* Minimum header len */ + ( cc * 4 ); if ( length < total ) { - /* Deallocate */ LOGGER_WARNING("Length invalid!"); free(retu); return NULL; @@ -355,9 +344,10 @@ RTPHeader *parse_header_in ( const uint8_t *payload, int length ) memcpy(&retu->timestamp, it, sizeof(retu->timestamp)); - retu->timestamp = ntohl(retu->timestamp); it += 4; memcpy(&retu->ssrc, it, sizeof(retu->ssrc)); + + retu->timestamp = ntohl(retu->timestamp); retu->ssrc = ntohl(retu->ssrc); uint8_t x; @@ -380,34 +370,31 @@ RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ) return NULL; } - uint16_t ext_length; - memcpy(&ext_length, it, sizeof(ext_length)); - ext_length = ntohs(ext_length); + memcpy(&retu->length, it, sizeof(retu->length)); + retu->length = ntohs(retu->length); it += 2; - - - if ( length < ( ext_length * sizeof(uint32_t) ) ) { + + if ( length < ( retu->length * sizeof(uint32_t) ) ) { LOGGER_WARNING("Length invalid!"); free(retu); return NULL; } - - retu->length = ext_length; + memcpy(&retu->type, it, sizeof(retu->type)); retu->type = ntohs(retu->type); + it += 2; - - if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) { + + if ( !(retu->table = calloc(retu->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++ ) { + for ( x = 0; x < retu->length; x++ ) { it += 4; - memcpy(&(retu->table[x]), it, sizeof(retu->table[x])); + memcpy(retu->table + x, it, sizeof(*retu->table)); retu->table[x] = ntohl(retu->table[x]); } @@ -433,7 +420,6 @@ uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload ) *it = header->marker_payloadt; ++it; - timestamp = htonl(header->timestamp); memcpy(it, ×tamp, sizeof(timestamp)); it += 4; @@ -579,7 +565,6 @@ int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* dat report->received_packets = ntohl(report->received_packets); report->expected_packets = ntohl(report->expected_packets); - /* Invalid values */ if (report->expected_packets == 0 || report->received_packets > report->expected_packets) { LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets); free(report); diff --git a/toxav/rtp.h b/toxav/rtp.h index c973d262..a158d724 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -113,10 +113,9 @@ typedef struct { uint8_t prefix; Messenger *m; - int friend_id; + int friend_number; struct RTCPSession_s *rtcp_session; - void *cs; int (*mcb) (void*, RTPMessage* msg); @@ -125,33 +124,27 @@ typedef struct { /** * Must be called before calling any other rtp function. */ -RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ); - +RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ); /** * Terminate the session. */ void rtp_kill ( RTPSession* session ); - /** * Do periodical rtp work. */ int rtp_do(RTPSession *session); - /** * By default rtp is in receiving state */ int rtp_start_receiving (RTPSession *session); - /** * Pause rtp receiving mode. */ int rtp_stop_receiving (RTPSession *session); - /** * Sends msg to RTPSession::dest */ int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy ); - /** * Dealloc msg. */ diff --git a/toxav/toxav.c b/toxav/toxav.c index 8d47f5cd..25a2857c 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -58,10 +58,10 @@ typedef struct ToxAVCall_s { bool active; MSICall* msi_call; - uint32_t friend_id; + uint32_t friend_number; - uint32_t audio_bit_rate; /* Sending audio bitrate */ - uint32_t video_bit_rate; /* Sending video bitrate */ + uint32_t audio_bit_rate; /* Sending audio bit rate */ + uint32_t video_bit_rate; /* Sending video bit rate */ ToxAvBitrateAdapter aba; ToxAvBitrateAdapter vba; @@ -93,8 +93,8 @@ struct toxAV { PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ - PAIR(toxav_audio_bitrate_control_cb *, void *) abcb; /* Audio bitrate control callback */ - PAIR(toxav_video_bitrate_control_cb *, void *) vbcb; /* Video bitrate control callback */ + PAIR(toxav_audio_bit_rate_status_cb *, void *) abcb; /* Audio bit rate control callback */ + PAIR(toxav_video_bit_rate_status_cb *, void *) vbcb; /* Video bit rate control callback */ /** Decode time measures */ int32_t dmssc; /** Measure count */ @@ -111,8 +111,8 @@ int callback_end(void* toxav_inst, MSICall* call); int callback_error(void* toxav_inst, MSICall* call); int callback_capabilites(void* toxav_inst, MSICall* call); -bool audio_bitrate_invalid(uint32_t bitrate); -bool video_bitrate_invalid(uint32_t bitrate); +bool audio_bit_rate_invalid(uint32_t bit_rate); +bool video_bit_rate_invalid(uint32_t bit_rate); void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state); ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); @@ -129,12 +129,12 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) if (tox == NULL) { rc = TOXAV_ERR_NEW_NULL; - goto FAILURE; + goto END; } if (((Messenger*)tox)->msi_packet) { rc = TOXAV_ERR_NEW_MULTIPLE; - goto FAILURE; + goto END; } av = calloc (sizeof(ToxAV), 1); @@ -142,13 +142,13 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) if (av == NULL) { LOGGER_WARNING("Allocation failed!"); rc = TOXAV_ERR_NEW_MALLOC; - goto FAILURE; + goto END; } if (create_recursive_mutex(av->mutex) != 0) { LOGGER_WARNING("Mutex creation failed!"); rc = TOXAV_ERR_NEW_MALLOC; - goto FAILURE; + goto END; } av->m = (Messenger *)tox; @@ -157,7 +157,7 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) if (av->msi == NULL) { pthread_mutex_destroy(av->mutex); rc = TOXAV_ERR_NEW_MALLOC; - goto FAILURE; + goto END; } av->interval = 200; @@ -170,19 +170,16 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout); msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities); - - if (error) - *error = rc; - - return av; - -FAILURE: +END: if (error) *error = rc; - free(av); + if (rc != TOXAV_ERR_NEW_OK) { + free(av); + av = NULL; + } - return NULL; + return av; } void toxav_kill(ToxAV* av) @@ -249,14 +246,14 @@ void toxav_iterate(ToxAV* av) /* Notify app */ if (av->abcb.first) - av->abcb.first (av, i->friend_id, false, bb, av->abcb.second); + av->abcb.first (av, i->friend_number, false, bb, av->abcb.second); } else if (i->aba.active && i->aba.end_time < current_time_monotonic()) { i->audio_bit_rate = i->aba.bit_rate; - /* Notify user about the new bitrate */ + /* Notify user about the new bit rate */ if (av->abcb.first) - av->abcb.first (av, i->friend_id, true, i->aba.bit_rate, av->abcb.second); + av->abcb.first (av, i->friend_number, true, i->aba.bit_rate, av->abcb.second); /* Stop sending dummy packets */ memset(&i->aba, 0, sizeof(i->aba)); @@ -275,15 +272,15 @@ void toxav_iterate(ToxAV* av) /* Notify app */ if (av->vbcb.first) - av->vbcb.first (av, i->friend_id, false, bb, av->vbcb.second); + av->vbcb.first (av, i->friend_number, false, bb, av->vbcb.second); } else if (i->vba.active && i->vba.end_time < current_time_monotonic()) { i->video_bit_rate = i->vba.bit_rate; - /* Notify user about the new bitrate */ + /* Notify user about the new bit rate */ if (av->vbcb.first) - av->vbcb.first (av, i->friend_id, true, i->vba.bit_rate, av->vbcb.second); + av->vbcb.first (av, i->friend_number, true, i->vba.bit_rate, av->vbcb.second); /* Stop sending dummy packets */ memset(&i->vba, 0, sizeof(i->vba)); @@ -297,7 +294,7 @@ void toxav_iterate(ToxAV* av) i->msi_call->peer_capabilities & msi_CapSVideo) rc = MIN(i->video.second->lcfd, rc); - uint32_t fid = i->friend_id; + uint32_t fid = i->friend_number; pthread_mutex_unlock(i->mutex); pthread_mutex_lock(av->mutex); @@ -356,7 +353,7 @@ void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) av->ccb.first = function; av->ccb.second = user_data; pthread_mutex_unlock(av->mutex); -}/** Required for monitoring */ +} bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) { @@ -368,8 +365,8 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui goto END; } - if ((audio_bit_rate && audio_bitrate_invalid(audio_bit_rate)) - ||(video_bit_rate && video_bitrate_invalid(video_bit_rate)) + if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) + ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate)) ) { rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; goto END; @@ -561,7 +558,7 @@ END: return rc == TOXAV_ERR_CALL_CONTROL_OK; } -void toxav_callback_video_bitrate_control(ToxAV* av, toxav_video_bitrate_control_cb* function, void* user_data) +void toxav_callback_video_bit_rate_status(ToxAV* av, toxav_video_bit_rate_status_cb* function, void* user_data) { pthread_mutex_lock(av->mutex); av->vbcb.first = function; @@ -579,7 +576,7 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_ goto END; } - if (video_bitrate_invalid(video_bit_rate)) { + if (video_bit_rate_invalid(video_bit_rate)) { rc = TOXAV_ERR_BIT_RATE_INVALID; goto END; } @@ -605,7 +602,7 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_ call->video_bit_rate = video_bit_rate; if (!force && av->vbcb.first) - av->vbcb.first (av, call->friend_id, true, video_bit_rate, av->vbcb.second); + av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second); } pthread_mutex_unlock(call->mutex); @@ -618,7 +615,7 @@ END: return rc == TOXAV_ERR_BIT_RATE_OK; } -void toxav_callback_audio_bitrate_control(ToxAV* av, toxav_audio_bitrate_control_cb* function, void* user_data) +void toxav_callback_audio_bit_rate_status(ToxAV* av, toxav_audio_bit_rate_status_cb* function, void* user_data) { pthread_mutex_lock(av->mutex); av->abcb.first = function; @@ -636,7 +633,7 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_ goto END; } - if (audio_bitrate_invalid(audio_bit_rate)) { + if (audio_bit_rate_invalid(audio_bit_rate)) { rc = TOXAV_ERR_BIT_RATE_INVALID; goto END; } @@ -662,7 +659,7 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_ call->audio_bit_rate = audio_bit_rate; if (!force && av->abcb.first) - av->abcb.first (av, call->friend_id, true, audio_bit_rate, av->abcb.second); + av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second); } pthread_mutex_unlock(call->mutex); @@ -897,11 +894,11 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc } - /* For bitrate measurement; send dummy packet */ + /* For bit rate measurement; send dummy packet */ if (ba_shoud_send_dummy(&call->aba)) { sampling_rate = ntohl(sampling_rate); if (ac_reconfigure_test_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { - /* FIXME should the bitrate changing fail here? */ + /* FIXME should the bit rate changing fail here? */ pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; @@ -966,7 +963,7 @@ int callback_invite(void* toxav_inst, MSICall* call) ToxAV* toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL); + ToxAVCall* av_call = call_new(toxav, call->friend_number, NULL); if (av_call == NULL) { LOGGER_WARNING("Failed to initialize call..."); pthread_mutex_unlock(toxav->mutex); @@ -977,7 +974,7 @@ int callback_invite(void* toxav_inst, MSICall* call) av_call->msi_call = call; if (toxav->ccb.first) - toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, + toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio, call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); pthread_mutex_unlock(toxav->mutex); @@ -989,7 +986,7 @@ int callback_start(void* toxav_inst, MSICall* call) ToxAV* toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - ToxAVCall* av_call = call_get(toxav, call->friend_id); + ToxAVCall* av_call = call_get(toxav, call->friend_number); if (av_call == NULL) { /* Should this ever happen? */ @@ -1004,7 +1001,7 @@ int callback_start(void* toxav_inst, MSICall* call) return -1; } - invoke_call_state(toxav, call->friend_id, call->peer_capabilities); + invoke_call_state(toxav, call->friend_number, call->peer_capabilities); pthread_mutex_unlock(toxav->mutex); return 0; @@ -1015,7 +1012,7 @@ int callback_end(void* toxav_inst, MSICall* call) ToxAV* toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_END); + invoke_call_state(toxav, call->friend_number, TOXAV_CALL_STATE_END); call_kill_transmission(call->av_call); call_remove(call->av_call); @@ -1029,7 +1026,7 @@ int callback_error(void* toxav_inst, MSICall* call) ToxAV* toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR); + invoke_call_state(toxav, call->friend_number, TOXAV_CALL_STATE_ERROR); call_kill_transmission(call->av_call); call_remove(call->av_call); @@ -1043,21 +1040,21 @@ int callback_capabilites(void* toxav_inst, MSICall* call) ToxAV* toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - invoke_call_state(toxav, call->friend_id, call->peer_capabilities); + invoke_call_state(toxav, call->friend_number, call->peer_capabilities); pthread_mutex_unlock(toxav->mutex); return 0; } -bool audio_bitrate_invalid(uint32_t bitrate) +bool audio_bit_rate_invalid(uint32_t bit_rate) { /* Opus RFC 6716 section-2.1.1 dictates the following: - * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. + * Opus supports all bit rates from 6 kbit/s to 510 kbit/s. */ - return bitrate < 6 || bitrate > 510; + return bit_rate < 6 || bit_rate > 510; } -bool video_bitrate_invalid(uint32_t bitrate) +bool video_bit_rate_invalid(uint32_t bit_rate) { /* TODO: If anyone knows the answer to this one please fill it up */ return false; @@ -1099,7 +1096,7 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) } call->av = av; - call->friend_id = friend_number; + call->friend_number = friend_number; if (av->calls == NULL) { /* Creating */ av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); @@ -1126,7 +1123,7 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) av->calls = tmp; /* Set fields in between to null */ - int32_t i = av->calls_tail; + int32_t i = av->calls_tail + 1; for (; i < friend_number; i ++) av->calls[i] = NULL; @@ -1164,7 +1161,7 @@ void call_remove(ToxAVCall* call) if (call == NULL) return; - uint32_t friend_id = call->friend_id; + uint32_t friend_number = call->friend_number; ToxAV* av = call->av; ToxAVCall* prev = call->prev; @@ -1175,16 +1172,16 @@ void call_remove(ToxAVCall* call) if (prev) prev->next = next; else if (next) - av->calls_head = next->friend_id; + av->calls_head = next->friend_number; else goto CLEAR; if (next) next->prev = prev; else if (prev) - av->calls_tail = prev->friend_id; + av->calls_tail = prev->friend_number; else goto CLEAR; - av->calls[friend_id] = NULL; + av->calls[friend_number] = NULL; return; CLEAR: @@ -1214,17 +1211,16 @@ bool call_prepare_transmission(ToxAVCall* call) if (create_recursive_mutex(call->mutex_audio) != 0) return false; - if (create_recursive_mutex(call->mutex_video) != 0) { - goto AUDIO_SENDING_MUTEX_CLEANUP; - } + if (create_recursive_mutex(call->mutex_video) != 0) + goto FAILURE_3; + + if (create_recursive_mutex(call->mutex) != 0) + goto FAILURE_2; - if (create_recursive_mutex(call->mutex) != 0) { - goto VIDEO_SENDING_MUTEX_CLEANUP; - } { /* Prepare audio */ - call->audio.second = ac_new(av, call->friend_id, av->acb.first, av->acb.second); - call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_id, call->audio.second, ac_queue_message); + call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second); + call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->audio.second, ac_queue_message); if ( !call->audio.first || !call->audio.second ) { LOGGER_ERROR("Error while starting audio!\n"); @@ -1233,8 +1229,8 @@ bool call_prepare_transmission(ToxAVCall* call) } { /* Prepare video */ - call->video.second = vc_new(av, call->friend_id, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); - call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_id, call->video.second, vc_queue_message); + call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); + call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->video.second, vc_queue_message); if ( !call->video.first || !call->video.second ) { LOGGER_ERROR("Error while starting video!\n"); @@ -1255,9 +1251,9 @@ FAILURE: call->video.first = NULL; call->video.second = NULL; pthread_mutex_destroy(call->mutex); -VIDEO_SENDING_MUTEX_CLEANUP: +FAILURE_2: pthread_mutex_destroy(call->mutex_video); -AUDIO_SENDING_MUTEX_CLEANUP: +FAILURE_3: pthread_mutex_destroy(call->mutex_audio); return false; } diff --git a/toxav/toxav.h b/toxav/toxav.h index f2c3b2b3..b8db223e 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -333,23 +333,23 @@ typedef enum TOXAV_ERR_BIT_RATE { TOXAV_ERR_BIT_RATE_FRIEND_NOT_IN_CALL } TOXAV_ERR_BIT_RATE; /** - * The function type for the `audio_bitrate_control` callback. + * The function type for the `audio_bit_rate_status` callback. * * @param friend_number The friend number of the friend for which to set the * audio bit rate. - * @param good Is the stream good enough to keep the said bitrate. Upon failed - * non forceful bit rate setup this will be set to false and 'bit_rate' - * will be set to the bit rate that failed, otherwise 'good' will be set to - * true with 'bit_rate' set to new bit rate. If the stream becomes bad, - * the 'good' wil be set to false with 'bit_rate' set to the current bit rate. - * This callback will never be called when the stream is good. + * @param stable Is the stream stable enough to keep the bit rate. + * Upon successful, non forceful, bit rate change, this is set to + * true and 'bit_rate' is set to new bit rate. + * The stable is set to false with bit_rate set to the unstable + * bit rate when either current stream is unstable with said bit rate + * or the non forceful change failed. * @param bit_rate The bit rate in Kb/sec. */ -typedef void toxav_audio_bitrate_control_cb(ToxAV *av, uint32_t friend_number, bool good, uint32_t bit_rate, void *user_data); +typedef void toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); /** - * Set the callback for the `audio_bitrate_control` event. Pass NULL to unset. + * Set the callback for the `audio_bit_rate_status` event. Pass NULL to unset. */ -void toxav_callback_audio_bitrate_control(ToxAV *av, toxav_audio_bitrate_control_cb *function, void *user_data); +void toxav_callback_audio_bit_rate_status(ToxAV *av, toxav_audio_bit_rate_status_cb *function, void *user_data); /** * Set the audio bit rate to be used in subsequent audio frames. * @@ -362,23 +362,23 @@ void toxav_callback_audio_bitrate_control(ToxAV *av, toxav_audio_bitrate_control */ bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_BIT_RATE *error); /** - * The function type for the `video_bitrate_control` callback. + * The function type for the `video_bit_rate_status` callback. * * @param friend_number The friend number of the friend for which to set the * video bit rate. - * @param good Is the stream good enough to keep the said bitrate. Upon failed - * non forceful bit rate setup this will be set to false and 'bit_rate' - * will be set to the bit rate that failed, otherwise 'good' will be set to - * true with 'bit_rate' set to new bit rate. If the stream becomes bad, - * the 'good' wil be set to false with 'bit_rate' set to the current bit rate. - * This callback will never be called when the stream is good. + * @param stable Is the stream stable enough to keep the bit rate. + * Upon successful, non forceful, bit rate change, this is set to + * true and 'bit_rate' is set to new bit rate. + * The stable is set to false with bit_rate set to the unstable + * bit rate when either current stream is unstable with said bit rate + * or the non forceful change failed. * @param bit_rate The bit rate in Kb/sec. */ -typedef void toxav_video_bitrate_control_cb(ToxAV *av, uint32_t friend_number, bool good, uint32_t bit_rate, void *user_data); +typedef void toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); /** - * Set the callback for the `video_bitrate_control` event. Pass NULL to unset. + * Set the callback for the `video_bit_rate_status` event. Pass NULL to unset. */ -void toxav_callback_video_bitrate_control(ToxAV *av, toxav_video_bitrate_control_cb *function, void *user_data); +void toxav_callback_video_bit_rate_status(ToxAV *av, toxav_video_bit_rate_status_cb *function, void *user_data); /** * Set the video bit rate to be used in subsequent video frames. * diff --git a/toxav/video.c b/toxav/video.c index c540af3b..22ca2bee 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -35,15 +35,14 @@ #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ #define VIDEOFRAME_HEADER_SIZE 0x2 -/* FIXME: Might not be enough? NOTE: I think it is enough */ #define VIDEO_DECODE_BUFFER_SIZE 20 typedef struct { uint16_t size; uint8_t data[]; } Payload; -bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate); +bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate); -VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb* cb, void* cb_data, uint32_t mvfpsz) +VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_receive_video_frame_cb* cb, void* cb_data, uint32_t mvfpsz) { VCSession *vc = calloc(sizeof(VCSession), 1); @@ -86,7 +85,7 @@ VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb* c vc->lcfd = 60; vc->vcb.first = cb; vc->vcb.second = cb_data; - vc->friend_id = friend_id; + vc->friend_number = friend_number; vc->peer_video_frame_piece_size = mvfpsz; return vc; @@ -140,7 +139,7 @@ void vc_do(VCSession* vc) /* Play decoded images */ for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) { if (vc->vcb.first) - vc->vcb.first(vc->av, vc->friend_id, dest->d_w, dest->d_h, + vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h, (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2], dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second); @@ -289,16 +288,16 @@ end: rtp_free_msg(msg); return 0; } -int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height) +int vc_reconfigure_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height) { if (!vc) return -1; vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc; - if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) + if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) return 0; /* Nothing changed */ - cfg.rc_target_bitrate = bitrate; + cfg.rc_target_bitrate = bit_rate; cfg.g_w = width; cfg.g_h = height; @@ -310,16 +309,16 @@ int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint1 return 0; } -int vc_reconfigure_test_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height) +int vc_reconfigure_test_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height) { if (!vc) return -1; vpx_codec_enc_cfg_t cfg = *vc->test_encoder->config.enc; - if (cfg.rc_target_bitrate == bitrate && cfg.g_w == width && cfg.g_h == height) + if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) return 0; /* Nothing changed */ - cfg.rc_target_bitrate = bitrate; + cfg.rc_target_bitrate = bit_rate; cfg.g_w = width; cfg.g_h = height; @@ -334,7 +333,7 @@ int vc_reconfigure_test_encoder(VCSession* vc, int32_t bitrate, uint16_t width, -bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate) +bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate) { assert(dest); @@ -354,7 +353,7 @@ bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bitrate) return false; } - cfg.rc_target_bitrate = bitrate; + cfg.rc_target_bitrate = bit_rate; cfg.g_w = 800; cfg.g_h = 600; cfg.g_pass = VPX_RC_ONE_PASS; diff --git a/toxav/video.h b/toxav/video.h index 78003ef3..8da15578 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -38,6 +38,9 @@ struct RTPMessage_s; +/* + * Base Video Codec session type. + */ typedef struct VCSession_s { /* encoding */ @@ -65,23 +68,46 @@ typedef struct VCSession_s { const uint8_t *processing_video_frame; uint16_t processing_video_frame_size; - ToxAV *av; - int32_t friend_id; + uint32_t friend_number; PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ pthread_mutex_t queue_mutex[1]; } VCSession; -VCSession* vc_new(ToxAV* av, uint32_t friend_id, toxav_receive_video_frame_cb *cb, void *cb_data, uint32_t mvfpsz); +/* + * Create new Video Codec session. + */ +VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_receive_video_frame_cb *cb, void *cb_data, uint32_t mvfpsz); +/* + * Kill the Video Codec session. + */ void vc_kill(VCSession* vc); +/* + * Do periodic work. Work is consisted out of decoding only. + */ void vc_do(VCSession* vc); +/* + * Set new video splitting cycle. This is requirement in order to send video packets. + */ void vc_init_video_splitter_cycle(VCSession* vc); +/* + * Update the video splitter cycle with new data. + */ int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length); +/* + * Iterate over splitted cycle. + */ const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size); +/* + * Queue new rtp message. + */ int vc_queue_message(void *vcp, struct RTPMessage_s *msg); -int vc_reconfigure_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height); -int vc_reconfigure_test_encoder(VCSession* vc, int32_t bitrate, uint16_t width, uint16_t height); +/* + * Set new values to the encoders. + */ +int vc_reconfigure_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height); +int vc_reconfigure_test_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height); #endif /* VIDEO_H */ \ No newline at end of file -- cgit v1.2.3 From a3132feddb25656e33c7ce8c9bc6abc78657a01e Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 13 Jun 2015 15:00:34 +0200 Subject: Fixed sample size in callback and other stuff --- auto_tests/toxav_basic_test.c | 4 ++-- auto_tests/toxav_many_test.c | 10 ++++------ testing/av_test.c | 23 +++++++++++------------ toxav/audio.c | 4 ++-- 4 files changed, 19 insertions(+), 22 deletions(-) (limited to 'toxav/audio.c') diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index a3847640..abe5d034 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -66,8 +66,8 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, vo } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, - uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, - int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, void *user_data) { (void) av; diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c index 761a4525..438f2789 100644 --- a/auto_tests/toxav_many_test.c +++ b/auto_tests/toxav_many_test.c @@ -61,8 +61,8 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, vo } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, - uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, - int32_t ystride, int32_t ustride, int32_t vstride, int32_t stride, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, void *user_data) { (void) av; @@ -159,13 +159,11 @@ void* call_thread(void* pd) uint8_t video_y[800*600]; uint8_t video_u[800*600 / 2]; uint8_t video_v[800*600 / 2]; - uint8_t video_a[800*600]; memset(PCM, 0, sizeof(PCM)); memset(video_y, 0, sizeof(video_y)); memset(video_u, 0, sizeof(video_u)); memset(video_v, 0, sizeof(video_v)); - memset(video_a, 0, sizeof(video_a)); time_t start_time = time(NULL); while(time(NULL) - start_time < 4) { @@ -175,8 +173,8 @@ void* call_thread(void* pd) toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL); toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL); - toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, video_a, NULL); - toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, video_a, NULL); + toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL); + toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL); c_sleep(10); } diff --git a/testing/av_test.c b/testing/av_test.c index de973d91..cd9608b6 100644 --- a/testing/av_test.c +++ b/testing/av_test.c @@ -70,8 +70,8 @@ #define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) -#define TEST_TRANSFER_A 0 -#define TEST_TRANSFER_V 1 +#define TEST_TRANSFER_A 1 +#define TEST_TRANSFER_V 0 typedef struct { @@ -134,14 +134,13 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, vo } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, - uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, - int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride, + uint8_t const *y, uint8_t const *u, uint8_t const *v, + int32_t ystride, int32_t ustride, int32_t vstride, void *user_data) { ystride = abs(ystride); ustride = abs(ustride); vstride = abs(vstride); - astride = abs(astride); uint16_t *img_data = malloc(height * width * 6); @@ -177,9 +176,9 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data) { CallControl* cc = user_data; - frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t)); - memcpy(f->data, pcm, sample_count * sizeof(int16_t)); - f->size = sample_count/channels; + frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels); + memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels); + f->size = sample_count; pthread_mutex_lock(cc->arb_mutex); free(rb_write(cc->arb, f)); @@ -225,15 +224,15 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA TOX_ERR_NEW error; opts.start_port = 33445; - *bootstrap = tox_new(&opts, NULL, 0, &error); + *bootstrap = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); opts.start_port = 33455; - Alice = tox_new(&opts, NULL, 0, &error); + Alice = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); opts.start_port = 33465; - Bob = tox_new(&opts, NULL, 0, &error); + Bob = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); } @@ -369,7 +368,7 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) } - int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL, NULL); + int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL); free(planes[0]); free(planes[1]); free(planes[2]); diff --git a/toxav/audio.c b/toxav/audio.c index f6993a1d..0a6f04e3 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -181,8 +181,8 @@ void ac_do(ACSession* ac) } else if (ac->acb.first) { ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate; - ac->acb.first(ac->av, ac->friend_number, tmp, rc * ac->last_packet_channel_count, - ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second); + ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->last_packet_channel_count, + ac->last_packet_sampling_rate, ac->acb.second); } return; -- cgit v1.2.3 From 657a57b406717a3ff08233eef14f20818c137f47 Mon Sep 17 00:00:00 2001 From: mannol Date: Fri, 7 Aug 2015 02:04:04 +0200 Subject: Fixed memory leak during session cleanup and fixed naming. --- other/apidsl/toxav.in.h | 4 ++-- testing/av_test.c | 5 ----- toxav/audio.c | 1 + toxav/rtp.c | 3 ++- toxav/toxav.h | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) (limited to 'toxav/audio.c') diff --git a/other/apidsl/toxav.in.h b/other/apidsl/toxav.in.h index 22a01602..f437eeb3 100644 --- a/other/apidsl/toxav.in.h +++ b/other/apidsl/toxav.in.h @@ -490,12 +490,12 @@ namespace video { * * @param friend_number The friend number of the friend for which to set the * video bit rate. - * @param audio_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable + * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable * video sending. * @param force True if the bit rate change is forceful. * */ - bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate; + bool set(uint32_t friend_number, uint32_t video_bit_rate, bool force) with error for set_bit_rate; } } /******************************************************************************* diff --git a/testing/av_test.c b/testing/av_test.c index 20e00c12..8e048d02 100644 --- a/testing/av_test.c +++ b/testing/av_test.c @@ -91,7 +91,6 @@ struct toxav_thread_data { const char* vdout = "AV Test"; /* Video output */ PaStream* adout = NULL; /* Audio output */ - typedef struct { uint16_t size; int16_t data[]; @@ -118,7 +117,6 @@ void* pa_write_thread (void* d) } } - /** * Callbacks */ @@ -207,7 +205,6 @@ void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t } } - /** */ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC) @@ -374,7 +371,6 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) free(planes[2]); return rc; } - int print_audio_devices() { int i = 0; @@ -386,7 +382,6 @@ int print_audio_devices() return 0; } - int print_help (const char* name) { printf("Usage: %s -[a:v:o:dh]\n" diff --git a/toxav/audio.c b/toxav/audio.c index 0a6f04e3..afb9f26c 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -117,6 +117,7 @@ void ac_kill(ACSession* ac) return; opus_encoder_destroy(ac->encoder); + opus_encoder_destroy(ac->test_encoder); opus_decoder_destroy(ac->decoder); jbuf_free(ac->j_buf); diff --git a/toxav/rtp.c b/toxav/rtp.c index 148c4238..7a20877f 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -138,7 +138,7 @@ void rtp_kill ( RTPSession *session ) { if ( !session ) return; - rtp_stop_receiving (session); + rtp_stop_receiving (session); free ( session->ext_header ); free ( session->csrc ); @@ -153,6 +153,7 @@ void rtp_kill ( RTPSession *session ) LOGGER_DEBUG("Terminated RTP session: %p", session); /* And finally free session */ + free ( session->rtcp_session ); free ( session ); } int rtp_do(RTPSession *session) diff --git a/toxav/toxav.h b/toxav/toxav.h index 3c745b36..58d5503f 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -510,12 +510,12 @@ void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_sta * * @param friend_number The friend number of the friend for which to set the * video bit rate. - * @param audio_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable + * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable * video sending. * @param force True if the bit rate change is forceful. * */ -bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); +bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); /******************************************************************************* * * :: A/V sending -- cgit v1.2.3 From 6ab4308581f6b06b2a4516614b4f1e2f9b3667a9 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 8 Aug 2015 18:01:36 +0200 Subject: Apply OPUS codec fixes --- toxav/audio.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'toxav/audio.c') diff --git a/toxav/audio.c b/toxav/audio.c index afb9f26c..ff1e1782 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -343,7 +343,7 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) { int status = OPUS_OK; - OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status); + OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status); if ( status != OPUS_OK ) { LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status)); @@ -357,6 +357,26 @@ OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int3 goto FAILURE; } + /* Enable in-band forward error correction in codec */ + status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + goto FAILURE; + } + + /* Make codec resistant to up to 10% packet loss + * NOTE This could also be adjusted on the fly, rather than hard-coded, + * with feedback from the receiving client. + */ + status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10)); + + if ( status != OPUS_OK ) { + LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); + goto FAILURE; + } + + /* Set algorithm to the highest complexity, maximizing compression */ status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10)); if ( status != OPUS_OK ) { -- cgit v1.2.3 From d6fdf16520b6f242935ca95eeb739ec9a8eaa14c Mon Sep 17 00:00:00 2001 From: Eniz Vukovic Date: Sat, 10 Oct 2015 23:54:23 +0200 Subject: New Adaptive BR algorithm, cleanups and fixes --- auto_tests/toxav_basic_test.c | 30 +- auto_tests/toxav_many_test.c | 26 +- configure.ac | 14 +- other/apidsl/toxav.in.h | 128 ++--- testing/av_test.c | 74 ++- toxav/Makefile.inc | 61 +-- toxav/audio.c | 282 +++++----- toxav/audio.h | 65 +-- toxav/bwcontroler.c | 207 ++++++++ toxav/bwcontroler.h | 37 ++ toxav/group.c | 12 +- toxav/msi.c | 620 +++++++++++----------- toxav/msi.h | 35 +- toxav/rtp.c | 744 +++++++++----------------- toxav/rtp.h | 139 ++--- toxav/toxav.c | 1178 +++++++++++++++++------------------------ toxav/toxav.h | 226 ++++---- toxav/toxav_old.c | 4 +- toxav/video.c | 292 ++++------ toxav/video.h | 65 +-- toxcore/Messenger.c | 6 +- toxcore/assoc.c | 8 +- toxcore/assoc.h | 4 +- toxcore/logger.c | 18 +- toxcore/logger.h | 19 +- toxcore/network.c | 12 +- toxcore/util.c | 29 +- toxcore/util.h | 6 +- 28 files changed, 2005 insertions(+), 2336 deletions(-) create mode 100644 toxav/bwcontroler.c create mode 100644 toxav/bwcontroler.h (limited to 'toxav/audio.c') diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index 41fb6787..5821a6d4 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -2,11 +2,20 @@ #include "config.h" #endif +#ifndef HAVE_LIBCHECK +# include + +# define ck_assert(X) assert(X); +# define START_TEST(NAME) void NAME () +# define END_TEST +#else +# include "helpers.h" +#endif + #include #include #include #include -#include #include #include @@ -18,7 +27,6 @@ #include "../toxcore/crypto_core.h" #include "../toxav/toxav.h" -#include "helpers.h" #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) #define c_sleep(x) Sleep(1*x) @@ -462,19 +470,19 @@ START_TEST(test_AV_flows) printf("Call started as audio only\n"); printf("Turning on video for Alice...\n"); - ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 1000, false, NULL)); + ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V); printf("Turning off video for Alice...\n"); - ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 0, false, NULL)); + ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)); printf("Turning off audio for Alice...\n"); - ck_assert(toxav_audio_bit_rate_set(AliceAV, 0, 0, false, NULL)); + ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A)); @@ -564,7 +572,16 @@ START_TEST(test_AV_flows) } END_TEST - +#ifndef HAVE_LIBCHECK +int main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + test_AV_flows(); + return 0; +} +#else Suite *tox_suite(void) { Suite *s = suite_create("ToxAV"); @@ -589,3 +606,4 @@ int main(int argc, char *argv[]) return number_failed; } +#endif diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c index 438f2789..7dc82c6f 100644 --- a/auto_tests/toxav_many_test.c +++ b/auto_tests/toxav_many_test.c @@ -2,18 +2,25 @@ #include "config.h" #endif +#ifndef HAVE_LIBCHECK +# include + +# define ck_assert(X) assert(X); +# define START_TEST(NAME) void NAME () +# define END_TEST +#else +# include "helpers.h" +#endif + #include #include #include #include -#include #include #include #include -#include "helpers.h" - #include "../toxcore/tox.h" #include "../toxcore/util.h" #include "../toxcore/logger.h" @@ -331,8 +338,16 @@ START_TEST(test_AV_three_calls) END_TEST - - +#ifndef HAVE_LIBCHECK +int main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + test_AV_three_calls(); + return 0; +} +#else Suite *tox_suite(void) { Suite *s = suite_create("ToxAV"); @@ -362,3 +377,4 @@ int main(int argc, char *argv[]) return number_failed; } +#endif diff --git a/configure.ac b/configure.ac index 2b7f3a2e..639fc20c 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ BUILD_TESTS="yes" BUILD_AV="yes" BUILD_TESTING="yes" -LOGGING="no" +TOX_LOGGER="no" LOGGING_OUTNAM="libtoxcore.log" NCURSES_FOUND="no" @@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir], ] ) -AC_ARG_ENABLE([log], - [AC_HELP_STRING([--enable-log], [enable logging (default: auto)]) ], +AC_ARG_ENABLE([logger], + [AC_HELP_STRING([--enable-logger], [enable logging (default: auto)]) ], [ if test "x$enableval" = "xyes"; then - LOGGING="yes" + TOX_LOGGER="yes" - AC_DEFINE([LOGGING], [], [If logging enabled]) + AC_DEFINE([TOX_LOGGER], [], [If logging enabled]) AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value]) AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger]) fi @@ -99,7 +99,7 @@ AC_ARG_WITH(log-level, AC_HELP_STRING([--with-log-level=LEVEL], [Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]), [ - if test "x$LOGGING" = "xno"; then + if test "x$TOX_LOGGER" = "xno"; then AC_MSG_WARN([Logging disabled!]) else if test "x$withval" = "xTRACE"; then @@ -127,7 +127,7 @@ AC_ARG_WITH(log-path, AC_HELP_STRING([--with-log-path=DIR], [Path of logger output]), [ - if test "x$LOGGING" = "xno"; then + if test "x$TOX_LOGGER" = "xno"; then AC_MSG_WARN([Logging disabled!]) else AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger]) diff --git a/other/apidsl/toxav.in.h b/other/apidsl/toxav.in.h index f437eeb3..c272b934 100644 --- a/other/apidsl/toxav.in.h +++ b/other/apidsl/toxav.in.h @@ -56,12 +56,19 @@ extern "C" { /** \subsection threading Threading implications * * Unlike the Core API, this API is fully thread-safe. The library will ensure - * the proper synchronisation of parallel calls. + * the proper synchronization of parallel calls. * * A common way to run ToxAV (multiple or single instance) is to have a thread, * separate from tox instance thread, running a simple ${toxAV.iterate} loop, * sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration. * + * An important thing to note is that events are triggered from both tox and + * toxav thread (see above). audio and video receive frame events are triggered + * from toxav thread while all the other events are triggered from tox thread. + * + * Tox thread has priority with mutex mechanisms. Any api function can + * fail if mutexes are held by tox thread in which case they will set SYNC + * error code. */ /** @@ -231,6 +238,10 @@ bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_ra * required for the call. */ MALLOC, + /** + * Synchronization error occurred. + */ + SYNC, /** * The friend number did not designate a valid friend. */ @@ -273,6 +284,10 @@ event call { * video sending. */ bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) { + /** + * Synchronization error occurred. + */ + SYNC, /** * Failed to initialize codecs for call session. Note that codec initiation * will fail if there is no receive callback registered for either audio or @@ -347,7 +362,7 @@ event call_state { * ******************************************************************************/ enum class CALL_CONTROL { - /** + /** * Resume a previously paused call. Only valid if the pause was caused by this * client, if not, this control is ignored. Not valid before the call is accepted. */ @@ -392,6 +407,10 @@ enum class CALL_CONTROL { * @return true on success. */ bool call_control (uint32_t friend_number, CALL_CONTROL control) { + /** + * Synchronization error occurred. + */ + SYNC, /** * The friend_number passed did not designate a valid friend. */ @@ -412,38 +431,7 @@ bool call_control (uint32_t friend_number, CALL_CONTROL control) { * :: Controlling bit rates * ******************************************************************************/ -error for set_bit_rate { - /** - * The bit rate passed was not one of the supported values. - */ - INVALID, - /** - * The friend_number passed did not designate a valid friend. - */ - FRIEND_NOT_FOUND, - /** - * This client is currently not in a call with the friend. - */ - FRIEND_NOT_IN_CALL, -} -namespace audio { - namespace bit_rate { - event status { - /** - * The function type for the ${event status} callback. - * - * @param friend_number The friend number of the friend for which to set the - * audio bit rate. - * @param stable Is the stream stable enough to keep the bit rate. - * Upon successful, non forceful, bit rate change, this is set to - * true and 'bit_rate' is set to new bit rate. - * The stable is set to false with bit_rate set to the unstable - * bit rate when either current stream is unstable with said bit rate - * or the non forceful change failed. - * @param bit_rate The bit rate in Kb/sec. - */ - typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate); - } +namespace bit_rate { /** * Set the audio bit rate to be used in subsequent audio frames. If the passed * bit rate is the same as the current bit rate this function will return true @@ -452,51 +440,43 @@ namespace audio { * forcefully set and the previous non forceful request is cancelled. The active * non forceful setup will be canceled in favour of new non forceful setup. * - * @param friend_number The friend number of the friend for which to set the - * audio bit rate. + * @param friend_number The friend number. * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable - * audio sending. - * @param force True if the bit rate change is forceful. + * audio sending. Set to -1 to leave unchanged. + * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable + * video sending. Set to -1 to leave unchanged. * */ - bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate; - } -} -namespace video { - namespace bit_rate { + bool set(uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate) { + /** + * Synchronization error occurred. + */ + SYNC, + /** + * The bit rate passed was not one of the supported values. + */ + INVALID, + /** + * The friend_number passed did not designate a valid friend. + */ + FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. + */ + FRIEND_NOT_IN_CALL, + } event status { - /** - * The function type for the ${event status} callback. - * - * @param friend_number The friend number of the friend for which to set the - * video bit rate. - * @param stable Is the stream stable enough to keep the bit rate. - * Upon successful, non forceful, bit rate change, this is set to - * true and 'bit_rate' is set to new bit rate. - * The stable is set to false with bit_rate set to the unstable - * bit rate when either current stream is unstable with said bit rate - * or the non forceful change failed. - * @param bit_rate The bit rate in Kb/sec. - */ - typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate); + /** + * The function type for the ${event status} callback. The event is triggered + * when the network becomes too saturated for current bit rates at which + * point core suggests new bit rates. + * + * @param friend_number The friend number. + * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec. + * @param video_bit_rate Suggested maximum video bit rate in Kb/sec. + */ + typedef void(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate); } - /** - * Set the video bit rate to be used in subsequent video frames. If the passed - * bit rate is the same as the current bit rate this function will return true - * without calling a callback. If there is an active non forceful setup with the - * passed video bit rate and the new set request is forceful, the bit rate is - * forcefully set and the previous non forceful request is cancelled. The active - * non forceful setup will be canceled in favour of new non forceful setup. - * - * @param friend_number The friend number of the friend for which to set the - * video bit rate. - * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable - * video sending. - * @param force True if the bit rate change is forceful. - * - */ - bool set(uint32_t friend_number, uint32_t video_bit_rate, bool force) with error for set_bit_rate; - } } /******************************************************************************* * diff --git a/testing/av_test.c b/testing/av_test.c index 1c13ebad..fa6a831f 100644 --- a/testing/av_test.c +++ b/testing/av_test.c @@ -28,9 +28,6 @@ #include "../toxcore/util.h" #include "../toxcore/network.h" /* current_time_monotonic() */ -#define LOGGING -#include "../toxcore/logger.h" - /* Playing audio data */ #include /* Reading audio */ @@ -53,21 +50,21 @@ #define c_sleep(x) usleep(1000*x) -#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) +#define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X) // RGB -> YUV -#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) -#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) -#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) +#define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) +#define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) +#define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) // YUV -> RGB -#define C(Y) ( (Y) - 16 ) -#define D(U) ( (U) - 128 ) -#define E(V) ( (V) - 128 ) +#define C(Y) ((Y) - 16 ) +#define D(U) ((U) - 128 ) +#define E(V) ((V) - 128 ) -#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8) -#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) -#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) +#define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8) +#define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) +#define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8) #define TEST_TRANSFER_A 0 @@ -182,21 +179,11 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, free(rb_write(cc->arb, f)); pthread_mutex_unlock(cc->arb_mutex); } -void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, - bool stable, uint32_t bit_rate, void *user_data) -{ - if (stable) - printf ("Set new audio bit rate to: %d\n", bit_rate); - else - printf ("The network is overly saturated with audio bit rate at: %d\n", bit_rate); -} -void t_toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, - bool stable, uint32_t bit_rate, void *user_data) +void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, + uint32_t audio_bit_rate, uint32_t video_bit_rate, + void *user_data) { - if (stable) - printf ("Set new video bit rate to: %d", bit_rate); - else - printf ("The network is overly saturated with video bit rate at: %d", bit_rate); + printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate); } void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { @@ -216,6 +203,7 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA tox_options_default(&opts); opts.end_port = 0; + opts.ipv6_enabled = false; { TOX_ERR_NEW error; @@ -279,18 +267,16 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA /* Alice */ toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); + toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC); toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); - toxav_callback_video_bit_rate_status(*AliceAV, t_toxav_video_bit_rate_status_cb, AliceCC); - toxav_callback_audio_bit_rate_status(*AliceAV, t_toxav_audio_bit_rate_status_cb, AliceCC); /* Bob */ toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); + toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC); toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); - toxav_callback_video_bit_rate_status(*BobAV, t_toxav_video_bit_rate_status_cb, BobCC); - toxav_callback_audio_bit_rate_status(*BobAV, t_toxav_audio_bit_rate_status_cb, BobCC); printf("Created 2 instances of ToxAV\n"); @@ -320,6 +306,9 @@ void* iterate_toxav (void * data) fflush(stdout); #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 + if (!rc) + rc = 1; + cvWaitKey(rc); #else c_sleep(rc); @@ -340,8 +329,8 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) int32_t strides[3] = { 1280, 640, 640 }; uint8_t* planes[3] = { malloc(img->height * img->width), - malloc(img->height * img->width / 2), - malloc(img->height * img->width / 2), + malloc(img->height * img->width / 4), + malloc(img->height * img->width / 4), }; int x_chroma_shift = 1; @@ -363,9 +352,9 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) } } } - - - int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL); + + int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, + planes[0], planes[1], planes[2], NULL); free(planes[0]); free(planes[1]); free(planes[2]); @@ -396,9 +385,8 @@ int print_help (const char* name) return 0; } - int main (int argc, char** argv) -{ +{ freopen("/dev/zero", "w", stderr); Pa_Initialize(); @@ -585,7 +573,7 @@ int main (int argc, char** argv) err = Pa_StartStream(adout); assert(err == paNoError); - toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL); +// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL); /* Start write thread */ pthread_t t; @@ -593,7 +581,7 @@ int main (int argc, char** argv) pthread_detach(t); printf("Sample rate %d\n", af_info.samplerate); - while ( start_time + expected_time > time(NULL) ) { + while (start_time + expected_time > time(NULL) ) { uint64_t enc_start_time = current_time_monotonic(); int64_t count = sf_read_short(af_handle, PCM, frame_size); if (count > 0) { @@ -674,7 +662,7 @@ int main (int argc, char** argv) iterate_tox(bootstrap, AliceAV, BobAV); /* Start decode thread */ - struct toxav_thread_data data = { + struct toxav_thread_data data = { .AliceAV = AliceAV, .BobAV = BobAV, .sig = 0 @@ -694,13 +682,13 @@ int main (int argc, char** argv) time_t start_time = time(NULL); while(start_time + 90 > time(NULL)) { - IplImage* frame = cvQueryFrame( capture ); + IplImage* frame = cvQueryFrame(capture ); if (!frame) break; send_opencv_img(AliceAV, 0, frame); iterate_tox(bootstrap, AliceAV, BobAV); - c_sleep(video_frame_duration); + c_sleep(10); } cvReleaseCapture(&capture); diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc index 79275c9b..232c06de 100644 --- a/toxav/Makefile.inc +++ b/toxav/Makefile.inc @@ -1,42 +1,43 @@ if BUILD_AV -lib_LTLIBRARIES += libtoxav.la -libtoxav_la_include_HEADERS = ../toxav/toxav.h -libtoxav_la_includedir = $(includedir)/tox +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/group.h \ - ../toxav/group.c \ - ../toxav/audio.h \ - ../toxav/audio.c \ - ../toxav/video.h \ - ../toxav/video.c \ - ../toxav/toxav.h \ - ../toxav/toxav.c \ - ../toxav/toxav_old.h \ - ../toxav/toxav_old.c - + ../toxav/rtp.c \ + ../toxav/msi.h \ + ../toxav/msi.c \ + ../toxav/group.h \ + ../toxav/group.c \ + ../toxav/audio.h \ + ../toxav/audio.c \ + ../toxav/video.h \ + ../toxav/video.c \ + ../toxav/bwcontroler.h \ + ../toxav/bwcontroler.c \ + ../toxav/toxav.h \ + ../toxav/toxav.c \ + ../toxav/toxav_old.h \ + ../toxav/toxav_old.c libtoxav_la_CFLAGS = -I../toxcore \ - -I../toxav \ - $(LIBSODIUM_CFLAGS) \ - $(NACL_CFLAGS) \ - $(AV_CFLAGS) \ - $(PTHREAD_CFLAGS) + -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) + $(LIBSODIUM_LDFLAGS) \ + $(NACL_LDFLAGS) \ + $(EXTRA_LT_LDFLAGS) \ + $(WINSOCK2_LIBS) libtoxav_la_LIBADD = libtoxcore.la \ - $(LIBSODIUM_LIBS) \ - $(NACL_LIBS) \ - $(PTHREAD_LIBS) \ - $(AV_LIBS) + $(LIBSODIUM_LIBS) \ + $(NACL_LIBS) \ + $(PTHREAD_LIBS) \ + $(AV_LIBS) endif \ No newline at end of file diff --git a/toxav/audio.c b/toxav/audio.c index ff1e1782..3ba95c03 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -19,6 +19,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + #include #include "audio.h" @@ -29,80 +33,71 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity); static void jbuf_clear(struct JitterBuffer *q); static void jbuf_free(struct JitterBuffer *q); -static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); -static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); -OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); -bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, +static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m); +static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); +OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); +bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch, int32_t *old_br, int32_t *old_sr, int32_t *old_ch); -bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); +bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels); -ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data) +ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data) { ACSession *ac = calloc(sizeof(ACSession), 1); - + if (!ac) { LOGGER_WARNING("Allocation failed! Application might misbehave!"); return NULL; } - + if (create_recursive_mutex(ac->queue_mutex) != 0) { LOGGER_WARNING("Failed to create recursive mutex!"); free(ac); return NULL; } - + int status; - ac->decoder = opus_decoder_create(48000, 2, &status ); - - if ( status != OPUS_OK ) { + ac->decoder = opus_decoder_create(48000, 2, &status); + + if (status != OPUS_OK) { LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); goto BASE_CLEANUP; } - - if ( !(ac->j_buf = jbuf_new(3)) ) { + + if (!(ac->j_buf = jbuf_new(3))) { LOGGER_WARNING("Jitter buffer creaton failed!"); opus_decoder_destroy(ac->decoder); goto BASE_CLEANUP; } - + /* Initialize encoders with default values */ ac->encoder = create_audio_encoder(48000, 48000, 2); + if (ac->encoder == NULL) goto DECODER_CLEANUP; - ac->test_encoder = create_audio_encoder(48000, 48000, 2); - if (ac->test_encoder == NULL) { - opus_encoder_destroy(ac->encoder); - goto DECODER_CLEANUP; - } - - ac->last_encoding_bit_rate = 48000; - ac->last_encoding_sampling_rate = 48000; - ac->last_encoding_channel_count = 2; - - ac->last_test_encoding_bit_rate = 48000; - ac->last_test_encoding_sampling_rate = 48000; - ac->last_test_encoding_channel_count = 2; - - ac->last_decoding_channel_count = 2; - ac->last_decoding_sampling_rate = 48000; - ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */ + ac->le_bit_rate = 48000; + ac->le_sample_rate = 48000; + ac->le_channel_count = 2; + ac->ld_channel_count = 2; + ac->ld_sample_rate = 48000; + ac->ldrts = 0; /* Make it possible to reconfigure straight away */ + /* These need to be set in order to properly * do error correction with opus */ - ac->last_packet_frame_duration = 120; - ac->last_packet_sampling_rate = 48000; - ac->last_packet_channel_count = 1; - + ac->lp_frame_duration = 120; + ac->lp_sampling_rate = 48000; + ac->lp_channel_count = 1; + ac->av = av; ac->friend_number = friend_number; ac->acb.first = cb; ac->acb.second = cb_data; - + return ac; - + DECODER_CLEANUP: opus_decoder_destroy(ac->decoder); jbuf_free(ac->j_buf); @@ -111,39 +106,41 @@ BASE_CLEANUP: free(ac); return NULL; } -void ac_kill(ACSession* ac) +void ac_kill(ACSession *ac) { if (!ac) return; - + opus_encoder_destroy(ac->encoder); - opus_encoder_destroy(ac->test_encoder); opus_decoder_destroy(ac->decoder); jbuf_free(ac->j_buf); - + pthread_mutex_destroy(ac->queue_mutex); - + LOGGER_DEBUG("Terminated audio handler: %p", ac); free(ac); } -void ac_do(ACSession* ac) +void ac_iterate(ACSession *ac) { if (!ac) return; + + /* TODO fix this and jitter buffering */ - /* Enough space for the maximum frame size (120 ms 48 KHz audio) */ + /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */ int16_t tmp[5760 * 2]; - - RTPMessage *msg; + + struct RTPMessage *msg; int rc = 0; - + pthread_mutex_lock(ac->queue_mutex); + while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) { pthread_mutex_unlock(ac->queue_mutex); - + if (rc == 2) { LOGGER_DEBUG("OPUS correction"); - int fs = (ac->last_packet_sampling_rate * ac->last_packet_frame_duration) / 1000; + int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000; rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1); } else { /* Get values from packet and decode. */ @@ -156,98 +153,93 @@ void ac_do(ACSession* ac) rtp_free_msg(msg); continue; }*/ - - + + /* Pick up sampling rate from packet */ - memcpy(&ac->last_packet_sampling_rate, msg->data, 4); - ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate); - - ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4); - + memcpy(&ac->lp_sampling_rate, msg->data, 4); + ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate); + + ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4); + /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, * it didn't work quite well. */ - if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) { + if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) { LOGGER_WARNING("Failed to reconfigure decoder!"); - rtp_free_msg(msg); + free(msg); continue; } - - rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); - rtp_free_msg(msg); + + rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0); + free(msg); } - + if (rc < 0) { LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); } else if (ac->acb.first) { - ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate; - - ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->last_packet_channel_count, - ac->last_packet_sampling_rate, ac->acb.second); + ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate; + + ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count, + ac->lp_sampling_rate, ac->acb.second); } - + return; } + pthread_mutex_unlock(ac->queue_mutex); } -int ac_queue_message(void* acp, struct RTPMessage_s *msg) +int ac_queue_message(void *acp, struct RTPMessage *msg) { if (!acp || !msg) return -1; - - if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeAudio + 2) % 128) { + + if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) { LOGGER_WARNING("Got dummy!"); - rtp_free_msg(msg); + free(msg); return 0; } - - if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) { + + if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) { LOGGER_WARNING("Invalid payload type!"); - rtp_free_msg(msg); + free(msg); return -1; } - - ACSession* ac = acp; - + + ACSession *ac = acp; + pthread_mutex_lock(ac->queue_mutex); int rc = jbuf_write(ac->j_buf, msg); pthread_mutex_unlock(ac->queue_mutex); - + if (rc == -1) { LOGGER_WARNING("Could not queue the message!"); - rtp_free_msg(msg); + free(msg); return -1; } - + return 0; } -int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) +int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) { - if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels, - &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) + if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, + sampling_rate, channels, + &ac->le_bit_rate, + &ac->le_sample_rate, + &ac->le_channel_count)) return -1; - + LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); return 0; } -int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) -{ - if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bit_rate, sampling_rate, channels, - &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) - return -1; - - LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); - return 0; -} struct JitterBuffer { - RTPMessage **queue; - uint32_t size; - uint32_t capacity; - uint16_t bottom; - uint16_t top; + struct RTPMessage **queue; + uint32_t size; + uint32_t capacity; + uint16_t bottom; + uint16_t top; }; static struct JitterBuffer *jbuf_new(uint32_t capacity) @@ -260,9 +252,9 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity) struct JitterBuffer *q; - if ( !(q = calloc(sizeof(struct JitterBuffer), 1)) ) return NULL; + if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL; - if (!(q->queue = calloc(sizeof(RTPMessage *), size))) { + if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) { free(q); return NULL; } @@ -275,7 +267,7 @@ static void jbuf_clear(struct JitterBuffer *q) { for (; q->bottom != q->top; ++q->bottom) { if (q->queue[q->bottom % q->size]) { - rtp_free_msg(q->queue[q->bottom % q->size]); + free(q->queue[q->bottom % q->size]); q->queue[q->bottom % q->size] = NULL; } } @@ -288,15 +280,15 @@ static void jbuf_free(struct JitterBuffer *q) free(q->queue); free(q); } -static int jbuf_write(struct JitterBuffer *q, RTPMessage *m) +static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m) { - uint16_t sequnum = m->header->sequnum; + uint16_t sequnum = m->header.sequnum; unsigned int num = sequnum % q->size; if ((uint32_t)(sequnum - q->bottom) > q->size) { LOGGER_DEBUG("Clearing filled jitter buffer: %p", q); - + jbuf_clear(q); q->bottom = sequnum - q->capacity; q->queue[num] = m; @@ -314,7 +306,7 @@ static int jbuf_write(struct JitterBuffer *q, RTPMessage *m) return 0; } -static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) +static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) { if (q->top == q->bottom) { *success = 0; @@ -324,7 +316,7 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) unsigned int num = q->bottom % q->size; if (q->queue[num]) { - RTPMessage *ret = q->queue[num]; + struct RTPMessage *ret = q->queue[num]; q->queue[num] = NULL; ++q->bottom; *success = 1; @@ -340,73 +332,74 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) *success = 0; return NULL; } -OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) +OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) { int status = OPUS_OK; - OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status); - - if ( status != OPUS_OK ) { + OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status); + + if (status != OPUS_OK) { LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status)); return NULL; } - + status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate)); - - if ( status != OPUS_OK ) { + + if (status != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); goto FAILURE; } - + /* Enable in-band forward error correction in codec */ status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1)); - if ( status != OPUS_OK ) { + if (status != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); goto FAILURE; } - + /* Make codec resistant to up to 10% packet loss * NOTE This could also be adjusted on the fly, rather than hard-coded, * with feedback from the receiving client. */ status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10)); - if ( status != OPUS_OK ) { + if (status != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); goto FAILURE; } - + /* Set algorithm to the highest complexity, maximizing compression */ status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10)); - - if ( status != OPUS_OK ) { + + if (status != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); goto FAILURE; } - + return rc; - + FAILURE: opus_encoder_destroy(rc); return NULL; } -bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, - int32_t* old_br, int32_t* old_sr, int32_t* old_ch) +bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch, + int32_t *old_br, int32_t *old_sr, int32_t *old_ch) { /* Values are checked in toxav.c */ if (*old_sr != new_sr || *old_ch != new_ch) { - OpusEncoder* new_encoder = create_audio_encoder(new_br, new_sr, new_ch); + OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch); + if (new_encoder == NULL) return false; - + opus_encoder_destroy(*e); *e = new_encoder; } else if (*old_br == new_br) return true; /* Nothing changed */ else { int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br)); - - if ( status != OPUS_OK ) { + + if (status != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); return false; } @@ -415,31 +408,32 @@ bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, *old_br = new_br; *old_sr = new_sr; *old_ch = new_ch; - + return true; } -bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels) +bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels) { - if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) { - if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500) + if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) { + if (current_time_monotonic() - ac->ldrts < 500) return false; - + int status; - OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); - if ( status != OPUS_OK ) { + OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status); + + if (status != OPUS_OK) { LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status)); return false; } - - ac->last_decoding_sampling_rate = sampling_rate; - ac->last_decoding_channel_count = channels; - ac->last_decoder_reconfiguration = current_time_monotonic(); - + + ac->ld_sample_rate = sampling_rate; + ac->ld_channel_count = channels; + ac->ldrts = current_time_monotonic(); + opus_decoder_destroy(ac->decoder); ac->decoder = new_dec; - + LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels); } - + return true; -} \ No newline at end of file +} diff --git a/toxav/audio.h b/toxav/audio.h index 9ef10ae4..b1db7448 100644 --- a/toxav/audio.h +++ b/toxav/audio.h @@ -29,61 +29,36 @@ #include "../toxcore/util.h" -struct RTPMessage_s; +struct RTPMessage; -/* - * Base Audio Codec session type. - */ typedef struct ACSession_s { /* encoding */ OpusEncoder *encoder; - int32_t last_encoding_sampling_rate; - int32_t last_encoding_channel_count; - int32_t last_encoding_bit_rate; - - /* Testing encoder for dynamic bit rate streaming */ - OpusEncoder *test_encoder; - int32_t last_test_encoding_sampling_rate; - int32_t last_test_encoding_channel_count; - int32_t last_test_encoding_bit_rate; - + int32_t le_sample_rate; /* Last encoder sample rate */ + int32_t le_channel_count; /* Last encoder channel count */ + int32_t le_bit_rate; /* Last encoder bit rate */ + /* decoding */ OpusDecoder *decoder; - int32_t last_packet_channel_count; - int32_t last_packet_sampling_rate; - int32_t last_packet_frame_duration; - int32_t last_decoding_sampling_rate; - int32_t last_decoding_channel_count; - uint64_t last_decoder_reconfiguration; + int32_t lp_channel_count; /* Last packet channel count */ + int32_t lp_sampling_rate; /* Last packet sample rate */ + int32_t lp_frame_duration; /* Last packet frame duration */ + int32_t ld_sample_rate; /* Last decoder sample rate */ + int32_t ld_channel_count; /* Last decoder channel count */ + uint64_t ldrts; /* Last decoder reconfiguration time stamp */ void *j_buf; - + pthread_mutex_t queue_mutex[1]; - - ToxAV* av; + + ToxAV *av; uint32_t friend_number; PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */ } ACSession; -/* - * Create new Audio Codec session. - */ -ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data); -/* - * Kill the Audio Codec session. - */ -void ac_kill(ACSession* ac); -/* - * Do periodic work. Work is consisted out of decoding only. - */ -void ac_do(ACSession* ac); -/* - * Queue new rtp message. - */ -int ac_queue_message(void *acp, struct RTPMessage_s *msg); -/* - * Set new values to the encoders. - */ -int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); -int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); +ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data); +void ac_kill(ACSession *ac); +void ac_iterate(ACSession *ac); +int ac_queue_message(void *acp, struct RTPMessage *msg); +int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels); -#endif /* AUDIO_H */ \ No newline at end of file +#endif /* AUDIO_H */ diff --git a/toxav/bwcontroler.c b/toxav/bwcontroler.c new file mode 100644 index 00000000..2c468ce3 --- /dev/null +++ b/toxav/bwcontroler.c @@ -0,0 +1,207 @@ +/** bwcontroler.c + * + * Copyright (C) 2013-2015 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 . + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include "bwcontroler.h" +#include "../toxcore/logger.h" +#include "../toxcore/util.h" + +#define BWC_PACKET_ID 196 +#define BWC_SEND_INTERVAL_MS 1000 +#define BWC_REFRESH_INTERVAL_MS 10000 +#define BWC_AVG_PKT_COUNT 20 + +/** + * + */ + +struct BWControler_s { + void (*mcb) (BWControler *, uint32_t, float, void *); + void *mcb_data; + + Messenger *m; + uint32_t friend_number; + + struct { + uint32_t lru; /* Last recv update time stamp */ + uint32_t lsu; /* Last sent update time stamp */ + uint32_t lfu; /* Last refresh time stamp */ + + uint32_t lost; + uint32_t recv; + } cycle; + + struct { + uint32_t rb_s[BWC_AVG_PKT_COUNT]; + RingBuffer *rb; + } rcvpkt; /* To calculate average received packet */ +}; + +int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object); +void send_update(BWControler *bwc); + +BWControler *bwc_new(Messenger *m, uint32_t friendnumber, + void (*mcb) (BWControler *, uint32_t, float, void *), + void *udata) +{ + BWControler *retu = calloc(sizeof(struct BWControler_s), 1); + + retu->mcb = mcb; + retu->mcb_data = udata; + retu->m = m; + retu->friend_number = friendnumber; + retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic(); + retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT); + + /* Fill with zeros */ + int i = 0; + for (; i < BWC_AVG_PKT_COUNT; i ++) + rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i); + + m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu); + + return retu; +} +void bwc_kill(BWControler *bwc) +{ + if (!bwc) + return; + + m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL); + + rb_kill(bwc->rcvpkt.rb); + free(bwc); +} +void bwc_feed_avg(BWControler* bwc, uint32_t bytes) +{ + uint32_t *p; + + rb_read(bwc->rcvpkt.rb, (void**) &p); + rb_write(bwc->rcvpkt.rb, p); + + *p = bytes; +} +void bwc_add_lost(BWControler *bwc, uint32_t bytes) +{ + if (!bwc) + return; + + if (!bytes) { + uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1; + + rb_data(bwc->rcvpkt.rb, (void**) t_avg); + + int i = 0; + for (; i < BWC_AVG_PKT_COUNT; i ++) { + bytes += *(t_avg[i]); + + if (*(t_avg[i])) + c++; + } + + bytes /= c; + } + + bwc->cycle.lost += bytes; + send_update(bwc); +} +void bwc_add_recv(BWControler *bwc, uint32_t bytes) +{ + if (!bwc || !bytes) + return; + + bwc->cycle.recv += bytes; + send_update(bwc); +} + + +struct BWCMessage { + uint8_t core_type; /* Aligner for payload type which is always 196 */ + + uint32_t lost; + uint32_t recv; +} __attribute__((packed)); + +/* Check alignment */ +typedef char __fail_if_misaligned [ sizeof(struct BWCMessage) == 9 ? 1 : -1 ]; + +void send_update(BWControler *bwc) +{ + if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) { + + bwc->cycle.lost /= 10; + bwc->cycle.recv /= 10; + bwc->cycle.lfu = current_time_monotonic(); + } + else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) { + + if (bwc->cycle.lost) + { + LOGGER_DEBUG ("%p Sent update", bwc); + + struct BWCMessage msg; + msg.core_type = BWC_PACKET_ID; + msg.lost = htonl(bwc->cycle.lost); + msg.recv = htonl(bwc->cycle.recv); + + if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, (uint8_t *)&msg, sizeof(msg))) + LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(msg), strerror(errno)); + } + + bwc->cycle.lsu = current_time_monotonic(); + } +} +int on_update (BWControler *bwc, struct BWCMessage *msg) +{ + LOGGER_DEBUG ("%p Got update from peer", bwc); + + /* Peer must respect time boundary */ + if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) { + LOGGER_DEBUG("%p Rejecting extra update", bwc); + return -1; + } + + bwc->cycle.lru = current_time_monotonic(); + + msg->recv = ntohl(msg->recv); + msg->lost = ntohl(msg->lost); + + LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost); + + if (msg->lost && bwc->mcb) + bwc->mcb(bwc, bwc->friend_number, + ((float) (msg->lost) / (msg->recv + msg->lost)), + bwc->mcb_data); + + return 0; +} +int bwc_handle_data(Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object) +{ + if (length != sizeof(struct BWCMessage)) + return; + + /* NOTE the data is mutable */ + return on_update(object, (struct BWCMessage *) data); +} diff --git a/toxav/bwcontroler.h b/toxav/bwcontroler.h new file mode 100644 index 00000000..53b07d38 --- /dev/null +++ b/toxav/bwcontroler.h @@ -0,0 +1,37 @@ +/** bwcontroler.h + * + * Copyright (C) 2013-2015 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 . + * + */ + +#ifndef BWCONROLER_H +#define BWCONROLER_H +#include "../toxcore/Messenger.h" + +typedef struct BWControler_s BWControler; + +BWControler *bwc_new(Messenger *m, uint32_t friendnumber, + void (*mcb) (BWControler *, uint32_t, float, void *), + void *udata); +void bwc_kill(BWControler *bwc); + +void bwc_feed_avg(BWControler *bwc, uint32_t bytes); +void bwc_add_lost(BWControler *bwc, uint32_t bytes); +void bwc_add_recv(BWControler *bwc, uint32_t bytes); + +#endif /* BWCONROLER_H */ diff --git a/toxav/group.c b/toxav/group.c index 817ee6e6..190c2c3d 100644 --- a/toxav/group.c +++ b/toxav/group.c @@ -20,7 +20,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" -#endif +#endif /* HAVE_CONFIG_H */ #include "group.h" #include "../toxcore/util.h" @@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity) Group_JitterBuffer *q; - if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL; + if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL; if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) { free(q); @@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av) group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels, OPUS_APPLICATION_AUDIO, &rc); - if ( rc != OPUS_OK ) { + if (rc != OPUS_OK) { LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); group_av->audio_encoder = NULL; return -1; @@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av) rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate)); - if ( rc != OPUS_OK ) { + if (rc != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); opus_encoder_destroy(group_av->audio_encoder); group_av->audio_encoder = NULL; @@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av) rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10)); - if ( rc != OPUS_OK ) { + if (rc != OPUS_OK) { LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); opus_encoder_destroy(group_av->audio_encoder); group_av->audio_encoder = NULL; @@ -306,7 +306,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int g int rc; peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc); - if ( rc != OPUS_OK ) { + if (rc != OPUS_OK) { LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); free(pk); return -1; diff --git a/toxav/msi.c b/toxav/msi.c index b262e9a0..ef307bcb 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -46,12 +46,12 @@ typedef enum { IDRequest = 1, IDError, IDCapabilities, - IDVFPSZ, } MSIHeaderID; typedef enum { + requ_init, requ_push, requ_pop, } MSIRequest; @@ -64,224 +64,246 @@ typedef struct { \ } MSIHeader##header -GENERIC_HEADER ( Request, MSIRequest ); -GENERIC_HEADER ( Error, MSIError ); -GENERIC_HEADER ( Capabilities, uint8_t ); -GENERIC_HEADER ( VFPSZ, uint16_t ); +GENERIC_HEADER (Request, MSIRequest); +GENERIC_HEADER (Error, MSIError); +GENERIC_HEADER (Capabilities, uint8_t); typedef struct { MSIHeaderRequest request; MSIHeaderError error; MSIHeaderCapabilities capabilities; - MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order TODO: get rid of this eventually */ } MSIMessage; void msg_init (MSIMessage *dest, MSIRequest request); -int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); -uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); -static int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ); -int send_error ( Messenger* m, uint32_t friend_number, MSIError error ); -static int invoke_callback(MSICall* call, MSICallbackID cb); -static MSICall *get_call ( MSISession *session, uint32_t friend_number ); -MSICall *new_call ( MSISession *session, uint32_t friend_number ); -void kill_call ( MSICall *call ); +int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length); +uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length); +static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg); +int send_error (Messenger *m, uint32_t friend_number, MSIError error); +static int invoke_callback(MSICall *call, MSICallbackID cb); +static MSICall *get_call (MSISession *session, uint32_t friend_number); +MSICall *new_call (MSISession *session, uint32_t friend_number); +void kill_call (MSICall *call); void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); -void handle_push ( MSICall *call, const MSIMessage *msg ); -void handle_pop ( MSICall *call, const MSIMessage *msg ); -void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object ); +void handle_init (MSICall *call, const MSIMessage *msg); +void handle_push (MSICall *call, const MSIMessage *msg); +void handle_pop (MSICall *call, const MSIMessage *msg); +void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object); /** * Public functions */ -void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id) +void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id) { + if (!session) + return; + pthread_mutex_lock(session->mutex); session->callbacks[id] = callback; pthread_mutex_unlock(session->mutex); } -MSISession *msi_new ( Messenger *m ) +MSISession *msi_new (Messenger *m) { if (m == NULL) { LOGGER_ERROR("Could not init session on empty messenger!"); return NULL; } - - MSISession *retu = calloc ( sizeof ( MSISession ), 1 ); - + + MSISession *retu = calloc (sizeof (MSISession), 1); + if (retu == NULL) { LOGGER_ERROR("Allocation failed! Program might misbehave!"); return NULL; } - + if (create_recursive_mutex(retu->mutex) != 0) { LOGGER_ERROR("Failed to init mutex! Program might misbehave"); free(retu); return NULL; } - + retu->messenger = m; - - m_callback_msi_packet(m, handle_msi_packet, retu ); - + + m_callback_msi_packet(m, handle_msi_packet, retu); + /* This is called when remote terminates session */ m_callback_connectionstatus_internal_av(m, on_peer_status, retu); - + LOGGER_DEBUG("New msi session: %p ", retu); return retu; } -int msi_kill ( MSISession *session ) +int msi_kill (MSISession *session) { if (session == NULL) { LOGGER_ERROR("Tried to terminate non-existing session"); return -1; } - + m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL); - pthread_mutex_lock(session->mutex); + if (pthread_mutex_trylock(session->mutex) != 0) { + LOGGER_ERROR ("Failed to aquire lock on msi mutex"); + return -1; + } + if (session->calls) { MSIMessage msg; msg_init(&msg, requ_pop); - - MSICall* it = get_call(session, session->calls_head); + + MSICall *it = get_call(session, session->calls_head); + for (; it; it = it->next) { send_message(session->messenger, it->friend_number, &msg); kill_call(it); /* This will eventually free session->calls */ } } - + pthread_mutex_unlock(session->mutex); pthread_mutex_destroy(session->mutex); - + LOGGER_DEBUG("Terminated session: %p", session); - free ( session ); + free (session); return 0; } -int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities ) +int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities) { - LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number); + if (!session) + return -1; - pthread_mutex_lock(session->mutex); + LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number); + + if (pthread_mutex_trylock(session->mutex) != 0) { + LOGGER_ERROR ("Failed to aquire lock on msi mutex"); + return -1; + } + if (get_call(session, friend_number) != NULL) { LOGGER_ERROR("Already in a call"); pthread_mutex_unlock(session->mutex); return -1; } - - (*call) = new_call ( session, friend_number ); - - if ( *call == NULL ) { + + (*call) = new_call (session, friend_number); + + if (*call == NULL) { pthread_mutex_unlock(session->mutex); return -1; } - + (*call)->self_capabilities = capabilities; - + MSIMessage msg; - msg_init(&msg, requ_push); - + msg_init(&msg, requ_init); + msg.capabilities.exists = true; msg.capabilities.value = capabilities; - - msg.vfpsz.exists = true; - msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - - send_message ( (*call)->session->messenger, (*call)->friend_number, &msg ); - + + send_message ((*call)->session->messenger, (*call)->friend_number, &msg); + (*call)->state = msi_CallRequesting; - + LOGGER_DEBUG("Invite sent"); pthread_mutex_unlock(session->mutex); return 0; } -int msi_hangup ( MSICall* call ) +int msi_hangup (MSICall *call) { - LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number); + if (!call || !call->session) + return -1; - MSISession* session = call->session; - pthread_mutex_lock(session->mutex); + LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number); + + MSISession *session = call->session; - if ( call->state == msi_CallInactive ) { + if (pthread_mutex_trylock(session->mutex) != 0) { + LOGGER_ERROR ("Failed to aquire lock on msi mutex"); + return -1; + } + + if (call->state == msi_CallInactive) { LOGGER_ERROR("Call is in invalid state!"); pthread_mutex_unlock(session->mutex); return -1; } - + MSIMessage msg; msg_init(&msg, requ_pop); - - send_message ( session->messenger, call->friend_number, &msg ); - + + send_message (session->messenger, call->friend_number, &msg); + kill_call(call); pthread_mutex_unlock(session->mutex); return 0; } -int msi_answer ( MSICall* call, uint8_t capabilities ) +int msi_answer (MSICall *call, uint8_t capabilities) { + if (!call || !call->session) + return -1; + LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number); - - MSISession* session = call->session; - pthread_mutex_lock(session->mutex); - - if ( call->state != msi_CallRequested ) { - /* Though sending in invalid state will not cause anything wierd + + MSISession *session = call->session; + + if (pthread_mutex_trylock(session->mutex) != 0) { + LOGGER_ERROR ("Failed to aquire lock on msi mutex"); + return -1; + } + + if (call->state != msi_CallRequested) { + /* Though sending in invalid state will not cause anything wierd * Its better to not do it like a maniac */ LOGGER_ERROR("Call is in invalid state!"); pthread_mutex_unlock(session->mutex); return -1; } - + call->self_capabilities = capabilities; - + MSIMessage msg; msg_init(&msg, requ_push); - + msg.capabilities.exists = true; msg.capabilities.value = capabilities; - - msg.vfpsz.exists = true; - msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - - send_message ( session->messenger, call->friend_number, &msg ); - + + send_message (session->messenger, call->friend_number, &msg); + call->state = msi_CallActive; pthread_mutex_unlock(session->mutex); - + return 0; } -int msi_change_capabilities( MSICall* call, uint8_t capabilities ) +int msi_change_capabilities(MSICall *call, uint8_t capabilities) { - LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number); - - MSISession* session = call->session; - pthread_mutex_lock(session->mutex); + if (!call || !call->session) + return -1; - if ( call->state != msi_CallActive ) { - /* Sending capabilities change can cause error on other side if - * the call is not active since we don't send header 'vfpsz'. - * If we were to send 'vfpsz' while call is active it would be - * ignored. However, if call is not active peer will expect - * the said header on 'push' so that it could handle the call - * like new. TODO: explain this better - */ + LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number); + + MSISession *session = call->session; + + if (pthread_mutex_trylock(session->mutex) != 0) { + LOGGER_ERROR ("Failed to aquire lock on msi mutex"); + return -1; + } + + if (call->state != msi_CallActive) { LOGGER_ERROR("Call is in invalid state!"); pthread_mutex_unlock(session->mutex); return -1; } - + call->self_capabilities = capabilities; - + MSIMessage msg; msg_init(&msg, requ_push); - + msg.capabilities.exists = true; msg.capabilities.value = capabilities; - - send_message ( call->session->messenger, call->friend_number, &msg ); - + + send_message (call->session->messenger, call->friend_number, &msg); + pthread_mutex_unlock(session->mutex); return 0; } @@ -290,23 +312,23 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities ) /** * Private functions */ -void msg_init(MSIMessage* dest, MSIRequest request) +void msg_init(MSIMessage *dest, MSIRequest request) { memset(dest, 0, sizeof(*dest)); dest->request.exists = true; dest->request.value = request; } -int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) +int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length) { /* Parse raw data received from socket into MSIMessage struct */ - + #define CHECK_SIZE(bytes, constraint, size) \ if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \ - if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; } - + if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; } + #define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \ - if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; } - + if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; } + #define SET_UINT8(bytes, header) do { \ header.value = bytes[2]; \ header.exists = true; \ @@ -318,50 +340,39 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) header.exists = true; \ bytes += 4; \ } while(0) - - + + assert(dest); - if ( length == 0 || data[length - 1] ) { /* End byte must have value 0 */ + if (length == 0 || data[length - 1]) { /* End byte must have value 0 */ LOGGER_ERROR("Invalid end byte"); return -1; } - + memset(dest, 0, sizeof(*dest)); - + const uint8_t *it = data; int size_constraint = length; - while ( *it ) {/* until end byte is hit */ + while (*it) {/* until end byte is hit */ switch (*it) { case IDRequest: CHECK_SIZE(it, size_constraint, 1); CHECK_ENUM_HIGH(it, requ_pop); SET_UINT8(it, dest->request); break; - + case IDError: CHECK_SIZE(it, size_constraint, 1); CHECK_ENUM_HIGH(it, msi_EUndisclosed); SET_UINT8(it, dest->error); break; - + case IDCapabilities: CHECK_SIZE(it, size_constraint, 1); SET_UINT8(it, dest->capabilities); break; - - case IDVFPSZ: - CHECK_SIZE(it, size_constraint, 2); - SET_UINT16(it, dest->vfpsz); - dest->vfpsz.value = ntohs(dest->vfpsz.value); - - if (dest->vfpsz.value > 1200) { - LOGGER_ERROR("Invalid vfpsz param"); - return -1; - } - break; - + default: LOGGER_ERROR("Invalid id byte"); return -1; @@ -373,7 +384,7 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) LOGGER_ERROR("Invalid request field!"); return -1; } - + return 0; #undef CHECK_SIZE @@ -381,13 +392,13 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) #undef SET_UINT8 #undef SET_UINT16 } -uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ) +uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length) { /* Parse a single header for sending */ assert(dest); assert(value); assert(value_len); - + *dest = id; dest ++; *dest = value_len; @@ -399,208 +410,205 @@ uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value return dest + value_len; /* Set to next position ready to be written */ } -int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ) +int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg) { /* Parse and send message */ assert(m); - + uint8_t parsed [MSI_MAXMSG_SIZE]; uint8_t *it = parsed; uint16_t size = 0; - + if (msg->request.exists) { uint8_t cast = msg->request.value; - it = msg_parse_header_out(IDRequest, it, &cast, + it = msg_parse_header_out(IDRequest, it, &cast, sizeof(cast), &size); } else { LOGGER_DEBUG("Must have request field"); return -1; } - + if (msg->error.exists) { uint8_t cast = msg->error.value; - it = msg_parse_header_out(IDError, it, &cast, + it = msg_parse_header_out(IDError, it, &cast, sizeof(cast), &size); } - + if (msg->capabilities.exists) { - it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value, + it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value, sizeof(msg->capabilities.value), &size); } - - if (msg->vfpsz.exists) { - uint16_t nb_vfpsz = htons(msg->vfpsz.value); - it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz, - sizeof(nb_vfpsz), &size); - } - - if ( it == parsed ) { + + if (it == parsed) { LOGGER_WARNING("Parsing message failed; empty message"); return -1; } - + *it = 0; size ++; - - if ( m_msi_packet(m, friend_number, parsed, size) ) { + + if (m_msi_packet(m, friend_number, parsed, size)) { LOGGER_DEBUG("Sent message"); return 0; } return -1; } -int send_error ( Messenger* m, uint32_t friend_number, MSIError error ) +int send_error (Messenger *m, uint32_t friend_number, MSIError error) { /* Send error message */ assert(m); - + LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number); MSIMessage msg; msg_init(&msg, requ_pop); - + msg.error.exists = true; msg.error.value = error; - - send_message ( m, friend_number, &msg ); + + send_message (m, friend_number, &msg); return 0; } -int invoke_callback(MSICall* call, MSICallbackID cb) +int invoke_callback(MSICall *call, MSICallbackID cb) { assert(call); - - if ( call->session->callbacks[cb] ) { + + if (call->session->callbacks[cb]) { LOGGER_DEBUG("Invoking callback function: %d", cb); - if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) { + + if (call->session->callbacks[cb] (call->session->av, call) != 0) { LOGGER_WARNING("Callback state handling failed, sending error"); goto FAILURE; } - + return 0; } - + FAILURE: /* If no callback present or error happened while handling, * an error message will be sent to friend */ - + if (call->error == msi_ENone) call->error = msi_EHandle; + return -1; } -static MSICall *get_call ( MSISession *session, uint32_t friend_number ) +static MSICall *get_call (MSISession *session, uint32_t friend_number) { assert(session); - + if (session->calls == NULL || session->calls_tail < friend_number) return NULL; - + return session->calls[friend_number]; } -MSICall *new_call ( MSISession *session, uint32_t friend_number ) +MSICall *new_call (MSISession *session, uint32_t friend_number) { assert(session); - + MSICall *rc = calloc(sizeof(MSICall), 1); - + if (rc == NULL) return NULL; - + rc->session = session; rc->friend_number = friend_number; - + if (session->calls == NULL) { /* Creating */ - session->calls = calloc (sizeof(MSICall*), friend_number + 1); - + session->calls = calloc (sizeof(MSICall *), friend_number + 1); + if (session->calls == NULL) { free(rc); return NULL; } - + session->calls_tail = session->calls_head = friend_number; - + } else if (session->calls_tail < friend_number) { /* Appending */ - void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_number + 1); - + void *tmp = realloc(session->calls, sizeof(MSICall *) * friend_number + 1); + if (tmp == NULL) { free(rc); return NULL; } - + session->calls = tmp; - + /* Set fields in between to null */ uint32_t i = session->calls_tail + 1; + for (; i < friend_number; i ++) session->calls[i] = NULL; - + rc->prev = session->calls[session->calls_tail]; session->calls[session->calls_tail]->next = rc; - + session->calls_tail = friend_number; - + } else if (session->calls_head > friend_number) { /* Inserting at front */ rc->next = session->calls[session->calls_head]; session->calls[session->calls_head]->prev = rc; session->calls_head = friend_number; } - + session->calls[friend_number] = rc; return rc; } -void kill_call ( MSICall *call ) +void kill_call (MSICall *call) { /* Assume that session mutex is locked */ - if ( call == NULL ) + if (call == NULL) return; - + LOGGER_DEBUG("Killing call: %p", call); - - MSISession* session = call->session; - - MSICall* prev = call->prev; - MSICall* next = call->next; - + + MSISession *session = call->session; + + MSICall *prev = call->prev; + MSICall *next = call->next; + if (prev) prev->next = next; else if (next) session->calls_head = next->friend_number; else goto CLEAR_CONTAINER; - + if (next) next->prev = prev; else if (prev) session->calls_tail = prev->friend_number; else goto CLEAR_CONTAINER; - + session->calls[call->friend_number] = NULL; free(call); return; - + CLEAR_CONTAINER: session->calls_head = session->calls_tail = 0; free(session->calls); free(call); session->calls = NULL; } -void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data) +void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data) { (void)m; MSISession *session = data; - switch ( status ) { + switch (status) { case 0: { /* Friend is now offline */ LOGGER_DEBUG("Friend %d is now offline", friend_number); - + pthread_mutex_lock(session->mutex); - MSICall* call = get_call(session, friend_number); - + MSICall *call = get_call(session, friend_number); + if (call == NULL) { pthread_mutex_unlock(session->mutex); return; } - + invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */ kill_call(call); pthread_mutex_unlock(session->mutex); @@ -611,186 +619,208 @@ void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* break; } } -void handle_push ( MSICall *call, const MSIMessage *msg ) +void handle_init (MSICall* call, const MSIMessage* msg) { assert(call); + LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number); - LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number); - if (!msg->capabilities.exists) { - LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); + LOGGER_WARNING("Session: %p Invalid capabilities on 'init'"); call->error = msi_EInvalidMessage; goto FAILURE; } - if (call->state != msi_CallActive) { - if (!msg->vfpsz.exists) { - LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'"); - call->error = msi_EInvalidMessage; - goto FAILURE; - } - - call->peer_vfpsz = msg->vfpsz.value; - } - - - switch (call->state) { + switch (call->state) + { case msi_CallInactive: { - LOGGER_INFO("Friend is calling us"); - /* Call requested */ call->peer_capabilities = msg->capabilities.value; call->state = msi_CallRequested; - - if ( invoke_callback(call, msi_OnInvite) == -1 ) + + if (invoke_callback(call, msi_OnInvite) == -1) goto FAILURE; - - } break; + } + break; case msi_CallActive: { - if (msg->vfpsz.exists) { - /* If peer sended video frame piece size - * while the call is already active it's probable - * that he is trying to re-call us while the call - * is not terminated on our side. We can assume that - * in this case we can automatically answer the re-call. - */ - if (call->peer_vfpsz != msg->vfpsz.value) { - LOGGER_WARNING("Friend sent invalid parameters for re-call"); - call->error = msi_EInvalidParam; - invoke_callback(call, msi_OnError); - goto FAILURE; - } - - LOGGER_INFO("Friend is recalling us"); - - MSIMessage msg; - msg_init(&msg, requ_push); - - msg.capabilities.exists = true; - msg.capabilities.value = call->self_capabilities; - - msg.vfpsz.exists = true; - msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; - - send_message ( call->session->messenger, call->friend_number, &msg ); - - /* If peer changed capabilities during re-call they will - * be handled accordingly during the next step - */ - } + /* If peer sent init while the call is already + * active it's probable that he is trying to + * re-call us while the call is not terminated + * on our side. We can assume that in this case + * we can automatically answer the re-call. + */ + LOGGER_INFO("Friend is recalling us"); + + MSIMessage msg; + msg_init(&msg, requ_push); + + msg.capabilities.exists = true; + msg.capabilities.value = call->self_capabilities; + + send_message (call->session->messenger, call->friend_number, &msg); + + /* If peer changed capabilities during re-call they will + * be handled accordingly during the next step + */ + } + break; + + default: { + LOGGER_WARNING("Session: %p Invalid state on 'init'"); + call->error = msi_EInvalidState; + goto FAILURE; + } + break; + } + + return; +FAILURE: + send_error(call->session->messenger, call->friend_number, call->error); + kill_call(call); +} +void handle_push (MSICall *call, const MSIMessage *msg) +{ + assert(call); + + LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number); + + if (!msg->capabilities.exists) { + LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); + call->error = msi_EInvalidMessage; + goto FAILURE; + } + + switch (call->state) { + case msi_CallActive: { /* Only act if capabilities changed */ - if ( call->peer_capabilities != msg->capabilities.value) { + if (call->peer_capabilities != msg->capabilities.value) { LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value); - + call->peer_capabilities = msg->capabilities.value; - if ( invoke_callback(call, msi_OnCapabilities) == -1 ) + + if (invoke_callback(call, msi_OnCapabilities) == -1) goto FAILURE; } - } break; - + } + break; + case msi_CallRequesting: { LOGGER_INFO("Friend answered our call"); - + /* Call started */ call->peer_capabilities = msg->capabilities.value; call->state = msi_CallActive; - - if ( invoke_callback(call, msi_OnStart) == -1 ) + + if (invoke_callback(call, msi_OnStart) == -1) goto FAILURE; - - } break; - + + } + break; + + /* Pushes during initialization state are ignored */ + case msi_CallInactive: case msi_CallRequested: { - /* Consecutive pushes during initialization state are ignored */ - LOGGER_WARNING("Consecutive push"); - } break; + LOGGER_WARNING("Ignoring invalid push"); + } + break; } - + return; - + FAILURE: send_error(call->session->messenger, call->friend_number, call->error); kill_call(call); } -void handle_pop ( MSICall *call, const MSIMessage *msg ) +void handle_pop (MSICall *call, const MSIMessage *msg) { assert(call); - + LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number); - + /* callback errors are ignored */ - + if (msg->error.exists) { LOGGER_WARNING("Friend detected an error: %d", msg->error.value); call->error = msg->error.value; invoke_callback(call, msi_OnError); - + } else switch (call->state) { case msi_CallInactive: { LOGGER_ERROR("Handling what should be impossible case"); abort(); - } break; - + } + break; + case msi_CallActive: { /* Hangup */ LOGGER_INFO("Friend hung up on us"); invoke_callback(call, msi_OnEnd); - } break; - + } + break; + case msi_CallRequesting: { /* Reject */ LOGGER_INFO("Friend rejected our call"); invoke_callback(call, msi_OnEnd); - } break; - + } + break; + case msi_CallRequested: { /* Cancel */ LOGGER_INFO("Friend canceled call invite"); invoke_callback(call, msi_OnEnd); - } break; + } + break; } - - kill_call ( call ); + + kill_call (call); } -void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object ) +void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object) { LOGGER_DEBUG("Got msi message"); - + MSISession *session = object; MSIMessage msg; - - if ( msg_parse_in ( &msg, data, length ) == -1 ) { + + if (msg_parse_in (&msg, data, length) == -1) { LOGGER_WARNING("Error parsing message"); send_error(m, friend_number, msi_EInvalidMessage); return; } else { LOGGER_DEBUG("Successfully parsed message"); } - + pthread_mutex_lock(session->mutex); MSICall *call = get_call(session, friend_number); - + if (call == NULL) { - if (msg.request.value != requ_push) { + if (msg.request.value != requ_init) { send_error(m, friend_number, msi_EStrayMessage); pthread_mutex_unlock(session->mutex); return; } - + call = new_call(session, friend_number); + if (call == NULL) { send_error(m, friend_number, msi_ESystem); pthread_mutex_unlock(session->mutex); return; } } - - if (msg.request.value == requ_push) - handle_push(call, &msg); - else - handle_pop(call, &msg); /* always kills the call */ - + + switch (msg.request.value) { + case requ_init: + handle_init(call, &msg); + break; + case requ_push: + handle_push(call, &msg); + break; + case requ_pop: + handle_pop(call, &msg); /* always kills the call */ + break; + } + pthread_mutex_unlock(session->mutex); } diff --git a/toxav/msi.h b/toxav/msi.h index 59f32c1d..e69581d1 100644 --- a/toxav/msi.h +++ b/toxav/msi.h @@ -29,9 +29,6 @@ #include "video.h" #include "../toxcore/Messenger.h" -/** Preconfigured value for video splitting */ -#define VIDEOFRAME_PIECE_SIZE 500 - /** * Error codes. */ @@ -89,13 +86,13 @@ typedef struct MSICall_s { uint8_t peer_capabilities; /* Peer capabilities */ uint8_t self_capabilities; /* Self capabilities */ uint16_t peer_vfpsz; /* Video frame piece size */ - uint32_t friend_number; /* Index of this call in MSISession */ + uint32_t friend_number; /* Index of this call in MSISession */ MSIError error; /* Last error */ - - void* av_call; /* Pointer to av call handler */ - - struct MSICall_s* next; - struct MSICall_s* prev; + + void *av_call; /* Pointer to av call handler */ + + struct MSICall_s *next; + struct MSICall_s *prev; } MSICall; @@ -104,7 +101,7 @@ typedef struct MSICall_s { * returned the call is considered errored and will be handled * as such which means it will be terminated without any notice. */ -typedef int msi_action_cb ( void *av, MSICall* call); +typedef int msi_action_cb (void *av, MSICall *call); /** * Control session struct. Please do not modify outside msi.c @@ -114,41 +111,41 @@ typedef struct MSISession_s { MSICall **calls; uint32_t calls_tail; uint32_t calls_head; - + void *av; Messenger *messenger; pthread_mutex_t mutex[1]; - msi_action_cb* callbacks[7]; + msi_action_cb *callbacks[7]; } MSISession; /** * Start the control session. */ -MSISession *msi_new ( Messenger *m ); +MSISession *msi_new(Messenger *m); /** * Terminate control session. NOTE: all calls will be freed */ -int msi_kill ( MSISession *session ); +int msi_kill(MSISession *session); /** * Callback setter. */ -void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id); +void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id); /** * Send invite request to friend_number. */ -int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities ); +int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities); /** * Hangup call. NOTE: 'call' will be freed */ -int msi_hangup ( MSICall* call ); +int msi_hangup(MSICall *call); /** * Answer call request. */ -int msi_answer ( MSICall* call, uint8_t capabilities ); +int msi_answer(MSICall *call, uint8_t capabilities); /** * Change capabilities of the call. */ -int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); +int msi_change_capabilities(MSICall *call, uint8_t capabilities); #endif /* MSI_H */ diff --git a/toxav/rtp.c b/toxav/rtp.c index 4df2e2d5..763166cd 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -24,6 +24,7 @@ #endif /* HAVE_CONFIG_H */ #include "rtp.h" +#include "bwcontroler.h" #include "../toxcore/logger.h" #include "../toxcore/util.h" #include "../toxcore/Messenger.h" @@ -31,584 +32,361 @@ #include #include -#define RTCP_REPORT_INTERVAL_MS 500 -#define RTP_MAX_SIZE 1500 -#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 { ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0) -#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0) +int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object); -#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) - -typedef struct { - uint64_t timestamp; /* in ms */ - - uint32_t received_packets; - uint32_t expected_packets; - /* ... other stuff in the future */ -} RTCPReport; - -typedef struct RTCPSession_s { - RTPSession *rtp_session; - - uint8_t prefix; - uint64_t last_sent_report_ts; - uint32_t last_received_packets; - uint32_t last_expected_packets; - - RingBuffer* pl_stats; /* Packet loss stats over time */ -} RTCPSession; - - -RTPHeader *parse_header_in ( const uint8_t *payload, int length ); -RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); -uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload ); -uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload ); -int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); -int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ); -void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber ); - - -RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ) +RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber, + BWControler *bwc, void *cs, + int (*mcb) (void *, struct RTPMessage *)) { assert(mcb); assert(cs); assert(m); - + RTPSession *retu = calloc(1, sizeof(RTPSession)); - if ( !retu ) { + if (!retu) { LOGGER_WARNING("Alloc failed! Program might misbehave!"); return NULL; } - retu->version = RTP_VERSION; /* It's always 2 */ - retu->ssrc = random_int(); - retu->payload_type = payload_type % 128; - - retu->m = m; - retu->friend_number = friend_num; + retu->ssrc = random_int(); + retu->payload_type = payload_type; - 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 */ + retu->m = m; + retu->friend_number = friendnumber; /* Also set payload type as prefix */ - retu->prefix = payload_type; - + + retu->bwc = bwc; retu->cs = cs; retu->mcb = mcb; - - /* Initialize rtcp session */ - if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - free(retu->csrc); - free(retu); - return NULL; - } - - retu->rtcp_session->prefix = payload_type + 2; - retu->rtcp_session->pl_stats = rb_new(4); - retu->rtcp_session->rtp_session = retu; - - if (-1 == rtp_start_receiving(retu)) { + + if (-1 == rtp_allow_receiving(retu)) { LOGGER_WARNING("Failed to start rtp receiving mode"); - free(retu->rtcp_session); - free(retu->csrc); free(retu); return NULL; } - + return retu; } -void rtp_kill ( RTPSession *session ) +void rtp_kill (RTPSession *session) { - if ( !session ) return; - - rtp_stop_receiving (session); + if (!session) + return; - free ( session->ext_header ); - free ( session->csrc ); - - void* t; - while (!rb_empty(session->rtcp_session->pl_stats)) { - rb_read(session->rtcp_session->pl_stats, (void**) &t); - free(t); - } - rb_free(session->rtcp_session->pl_stats); - LOGGER_DEBUG("Terminated RTP session: %p", session); - /* And finally free session */ - free ( session->rtcp_session ); - free ( session ); -} -int rtp_do(RTPSession *session) -{ - if (!session || !session->rtcp_session) - return rtp_StateNormal; - - if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) { - send_rtcp_report(session->rtcp_session, session->m, session->friend_number); - } - - if (rb_full(session->rtcp_session->pl_stats)) { - RTCPReport* reports[4]; - - int i = 0; - for (; i < 4; i++) - rb_read(session->rtcp_session->pl_stats, (void**) reports + i); - - /* Check for timed out reports (> 6 sec) */ - uint64_t now = current_time_monotonic(); - for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++); - for (; i < 4; i ++) { - rb_write(session->rtcp_session->pl_stats, reports[i]); - reports[i] = NULL; - } - if (!rb_empty(session->rtcp_session->pl_stats)) { - for (i = 0; reports[i] != NULL; i ++) - free(reports[i]); - return rtp_StateNormal; /* As some reports are timed out, we need more */ - } - - /* We have 4 on-time reports so we can proceed */ - uint32_t quality = 100; - for (i = 0; i < 4; i++) { - uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets; - quality = MIN(quality, current); - free(reports[i]); - } - - if (quality <= 90) { - LOGGER_WARNING("Stream quality: BAD (%d)", quality); - return rtp_StateBad; - } else if (quality >= 99) { - LOGGER_DEBUG("Stream quality: GOOD (%d)", quality); - return rtp_StateGood; - } else { - LOGGER_DEBUG("Stream quality: NORMAL (%d)", quality); - } - } - return rtp_StateNormal; + rtp_stop_receiving (session); + free (session); } -int rtp_start_receiving(RTPSession* session) +int rtp_allow_receiving(RTPSession *session) { if (session == NULL) return -1; - - if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix, - handle_rtp_packet, session) == -1) { + + if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, + handle_rtp_packet, session) == -1) { LOGGER_WARNING("Failed to register rtp receive handler"); return -1; } - if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, - handle_rtcp_packet, session->rtcp_session) == -1) { - LOGGER_WARNING("Failed to register rtcp receive handler"); - m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL); - return -1; - } - + LOGGER_DEBUG("Started receiving on session: %p", session); return 0; } -int rtp_stop_receiving(RTPSession* session) +int rtp_stop_receiving(RTPSession *session) { if (session == NULL) return -1; - - m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL); - m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ - + + m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL); + LOGGER_DEBUG("Stopped receiving on session: %p", session); return 0; } -int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy ) +int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length) { - if ( !session ) { + if (!session) { LOGGER_WARNING("No session!"); return -1; } - - uint8_t parsed[RTP_MAX_SIZE]; - uint8_t *it; - RTPHeader header[1]; - memset(header, 0, sizeof(header)); - - ADD_FLAG_VERSION ( header, session->version ); - ADD_FLAG_PADDING ( header, session->padding ); - ADD_FLAG_EXTENSION ( header, session->extension ); - ADD_FLAG_CSRCC ( header, session->cc ); - ADD_SETTING_MARKER ( header, session->marker ); - - if (dummy) - ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 ); - else - ADD_SETTING_PAYLOAD ( header, session->payload_type ); + uint8_t rdata[length + sizeof(struct RTPHeader) + 1]; + memset(rdata, 0, sizeof(rdata)); - header->sequnum = session->sequnum; - header->timestamp = current_time_monotonic(); - header->ssrc = session->ssrc; + rdata[0] = session->payload_type; - int i; - for ( i = 0; i < session->cc; i++ ) - header->csrc[i] = session->csrc[i]; + struct RTPHeader *header = (struct RTPHeader *)(rdata + 1); - header->length = 12 /* Minimum header len */ + ( session->cc * 4 ); - - uint32_t parsed_len = length + header->length + 1; - assert(parsed_len + (session->ext_header ? session->ext_header->length * 4 : 0) < RTP_MAX_SIZE ); + header->ve = 2; + header->pe = 0; + header->xe = 0; + header->cc = 0; - parsed[0] = session->prefix; - it = parse_header_out ( header, parsed + 1 ); - - if ( session->ext_header ) { - parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * 4 ); - it = parse_ext_header_out ( session->ext_header, it ); - } + header->ma = 0; + header->pt = session->payload_type % 128; - memcpy(it, data, length); - - if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) { - LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); - return -1; - } - - session->sequnum ++; - return 0; -} -void rtp_free_msg ( RTPMessage *msg ) -{ - if ( msg->ext_header ) { - free ( msg->ext_header->table ); - free ( msg->ext_header ); - } - - free ( msg->header ); - free ( msg ); -} + header->sequnum = htons(session->sequnum); + header->timestamp = htonl(current_time_monotonic()); + header->ssrc = htonl(session->ssrc); + header->cpart = 0; + header->tlen = htons(length); + if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) { -RTPHeader *parse_header_in ( const uint8_t *payload, int length ) -{ - if ( !payload || !length ) { - LOGGER_WARNING("No payload to extract!"); - return NULL; - } + /** + * The lenght is lesser than the maximum allowed lenght (including header) + * Send the packet in single piece. + */ - RTPHeader *retu = calloc(1, sizeof (RTPHeader)); + memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length); - if ( !retu ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - return NULL; - } + if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata))) + LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno)); + } else { - memcpy(&retu->sequnum, payload, sizeof(retu->sequnum)); - retu->sequnum = ntohs(retu->sequnum); + /** + * The lenght is greater than the maximum allowed lenght (including header) + * Send the packet in multiple pieces. + */ - const uint8_t *it = payload + 2; + uint16_t sent = 0; + uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1); - retu->flags = *it; - ++it; - - if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) { - /* Deallocate */ - LOGGER_WARNING("Invalid version!"); - free(retu); - return NULL; - } + while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) { + memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece); - uint8_t cc = GET_FLAG_CSRCC ( retu ); - int total = 12 /* Minimum header len */ + ( cc * 4 ); + if (-1 == send_custom_lossy_packet(session->m, session->friend_number, + rdata, piece + sizeof(struct RTPHeader) + 1)) + LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", + piece + sizeof(struct RTPHeader) + 1, strerror(errno)); - if ( length < total ) { - LOGGER_WARNING("Length invalid!"); - free(retu); - return NULL; - } + sent += piece; + header->cpart = htons(sent); + } - retu->marker_payloadt = *it; - ++it; - retu->length = total; + /* Send remaining */ + piece = length - sent; + if (piece) { + memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece); - memcpy(&retu->timestamp, it, sizeof(retu->timestamp)); - it += 4; - memcpy(&retu->ssrc, it, sizeof(retu->ssrc)); - - retu->timestamp = ntohl(retu->timestamp); - retu->ssrc = ntohl(retu->ssrc); - - uint8_t x; - for ( x = 0; x < cc; x++ ) { - it += 4; - memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x])); - retu->csrc[x] = ntohl(retu->csrc[x]); + if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, + piece + sizeof(struct RTPHeader) + 1)) + LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", + piece + sizeof(struct RTPHeader) + 1, strerror(errno)); + } } - return retu; + session->sequnum ++; + return 0; } -RTPExtHeader *parse_ext_header_in ( 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; - } - - memcpy(&retu->length, it, sizeof(retu->length)); - retu->length = ntohs(retu->length); - it += 2; - - if ( length < ( retu->length * sizeof(uint32_t) ) ) { - LOGGER_WARNING("Length invalid!"); - free(retu); - return NULL; - } - - memcpy(&retu->type, it, sizeof(retu->type)); - retu->type = ntohs(retu->type); - - it += 2; - - if ( !(retu->table = calloc(retu->length, sizeof (uint32_t))) ) { - LOGGER_WARNING("Alloc failed! Program might misbehave!"); - free(retu); - return NULL; - } - uint16_t x; - for ( x = 0; x < retu->length; x++ ) { - it += 4; - memcpy(retu->table + x, it, sizeof(*retu->table)); - retu->table[x] = ntohl(retu->table[x]); - } - return retu; -} -uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload ) +bool chloss (const RTPSession *session, const struct RTPHeader *header) { - uint8_t cc = GET_FLAG_CSRCC ( header ); - uint8_t *it = payload; - uint16_t sequnum; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc; - - - /* Add sequence number first */ - sequnum = htons(header->sequnum); - memcpy(it, &sequnum, sizeof(sequnum)); - it += 2; - - *it = header->flags; - ++it; - *it = header->marker_payloadt; - ++it; - - timestamp = htonl(header->timestamp); - memcpy(it, ×tamp, sizeof(timestamp)); - it += 4; - ssrc = htonl(header->ssrc); - memcpy(it, &ssrc, sizeof(ssrc)); - - uint8_t x; - - for ( x = 0; x < cc; x++ ) { - it += 4; - csrc = htonl(header->csrc[x]); - memcpy(it, &csrc, sizeof(csrc)); + if (ntohl(header->timestamp) < session->rtimestamp) { + uint16_t hosq, lost = 0; + + hosq = ntohs(header->sequnum); + + lost = (hosq > session->rsequnum) ? + (session->rsequnum + 65535) - hosq : + session->rsequnum - hosq; + + puts ("Lost packet"); + while (lost --) + bwc_add_lost(session->bwc ,0); + + return true; } - return it + 4; + return false; } -uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload ) +struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length) { - uint8_t *it = payload; - uint16_t length; - uint16_t type; - uint32_t entry; - - length = htons(header->length); - memcpy(it, &length, sizeof(length)); - it += 2; - type = htons(header->type); - memcpy(it, &type, sizeof(type)); - it -= 2; /* Return to 0 position */ - - if ( header->table ) { - uint16_t x; - for ( x = 0; x < header->length; x++ ) { - it += 4; - entry = htonl(header->table[x]); - memcpy(it, &entry, sizeof(entry)); - } - } + assert(allocate_len >= data_length); + + struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1); - return it + 4; + msg->len = data_length - sizeof(struct RTPHeader); + memcpy(&msg->header, data, data_length); + + msg->header.sequnum = ntohs(msg->header.sequnum); + msg->header.timestamp = ntohl(msg->header.timestamp); + msg->header.ssrc = ntohl(msg->header.ssrc); + + msg->header.cpart = ntohs(msg->header.cpart); + msg->header.tlen = ntohs(msg->header.tlen); + + return msg; } -int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) +int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object) { (void) m; (void) friendnumber; - + RTPSession *session = object; - if ( !session || length < 13 || length > RTP_MAX_SIZE ) { + data ++; + length--; + + if (!session || length < sizeof (struct RTPHeader)) { LOGGER_WARNING("No session or invalid length of received buffer!"); return -1; } - - RTPHeader* header = parse_header_in ( data + 1, length ); - if ( !header ) { - LOGGER_WARNING("Could not parse message: Header failed to extract!"); + const struct RTPHeader *header = (struct RTPHeader *) data; + + if (header->pt != session->payload_type % 128) { + LOGGER_WARNING("Invalid payload type with the session"); return -1; } - - RTPExtHeader* ext_header = NULL; - - uint16_t from_pos = header->length + 1; - uint16_t msg_length = length - from_pos; - - if ( GET_FLAG_EXTENSION ( header ) ) { - ext_header = parse_ext_header_in ( data + from_pos, length ); - - if ( ext_header ) { - msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * 4 ); - from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * 4 ); - } else { /* Error */ - LOGGER_WARNING("Could not parse message: Ext Header failed to extract!"); - free(header); - return -1; - } - } - - if (msg_length > RTP_MAX_SIZE) { - LOGGER_WARNING("Could not parse message: Invalid length!"); - free(header); - free(ext_header); + + if (ntohs(header->cpart) >= ntohs(header->tlen)) { + /* Never allow this case to happen */ return -1; } + + bwc_feed_avg(session->bwc, length); - /* Check if message came in late */ - if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) { - /* Not late */ - if (header->sequnum > session->rsequnum) - session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum; - else if (header->sequnum < session->rsequnum) - session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum; - else /* Usual case when transmission starts */ - session->rtcp_session->last_expected_packets ++; + if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) { + /* The message is sent in single part */ + + /* Only allow messages which have arrived in order; + * drop late messages + */ + if (chloss(session, header)) { + return 0; + } else { + /* Message is not late; pick up the latest parameters */ + session->rsequnum = ntohs(header->sequnum); + session->rtimestamp = ntohl(header->timestamp); + } + + bwc_add_recv(session->bwc, length); - session->rsequnum = header->sequnum; - session->rtimestamp = header->timestamp; - } + /* Invoke processing of active multiparted message */ + if (session->mp) { + if (session->mcb) + session->mcb (session->cs, session->mp); + else + free(session->mp); + + session->mp = NULL; + } - session->rtcp_session->last_received_packets ++; - - /* Check if the message is dummy. We don't keep dummy messages */ - if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) { - LOGGER_DEBUG("Received dummy rtp message"); - free(header); - free(ext_header); - return 0; - } - - /* Otherwise we will store the message if we have an appropriate handler */ - if (!session->mcb) { - LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header)); - free(header); - free(ext_header); - return 0; - } - - RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length); - - if ( !msg ) { - LOGGER_WARNING("Could not parse message: Allocation failed!"); - free(header); - free(ext_header); - return -1; - } - - msg->header = header; - msg->ext_header = ext_header; - msg->length = msg_length; - - memcpy ( msg->data, data + from_pos, msg_length ); - - return session->mcb (session->cs, msg); -} -int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) -{ - (void) m; - (void) friendnumber; - - if (length < 9) - return -1; - - RTCPSession* session = object; - RTCPReport* report = malloc(sizeof(RTCPReport)); - - memcpy(&report->received_packets, data + 1, 4); - memcpy(&report->expected_packets, data + 5, 4); - - report->received_packets = ntohl(report->received_packets); - report->expected_packets = ntohl(report->expected_packets); - - if (report->expected_packets == 0 || report->received_packets > report->expected_packets) { - LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets); - free(report); - return 0; + /* The message came in the allowed time; + * process it only if handler for the session is present. + */ + + if (!session->mcb) + return 0; + + return session->mcb (session->cs, new_message(length, data, length)); + } else { + /* The message is sent in multiple parts */ + + if (session->mp) { + /* There are 2 possible situations in this case: + * 1) being that we got the part of already processing message. + * 2) being that we got the part of a new/old message. + * + * We handle them differently as we only allow a single multiparted + * processing message + */ + + if (session->mp->header.sequnum == ntohs(header->sequnum) && + session->mp->header.timestamp == ntohl(header->timestamp)) { + /* First case */ + + /* Make sure we have enough allocated memory */ + if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) || + session->mp->header.tlen <= ntohs(header->cpart)) { + /* There happened to be some corruption on the stream; + * continue wihtout this part + */ + return 0; + } + + memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader), + length - sizeof(struct RTPHeader)); + + session->mp->len += length - sizeof(struct RTPHeader); + + bwc_add_recv(session->bwc, length); + + if (session->mp->len == session->mp->header.tlen) { + /* Received a full message; now push it for the further + * processing. + */ + if (session->mcb) + session->mcb (session->cs, session->mp); + else + free(session->mp); + + session->mp = NULL; + } + } else { + /* Second case */ + + if (session->mp->header.timestamp > ntohl(header->timestamp)) + /* The received message part is from the old message; + * discard it. + */ + return 0; + + /* Measure missing parts of the old message */ + bwc_add_lost(session->bwc, + (session->mp->header.tlen - session->mp->len) + + + /* Must account sizes of rtp headers too */ + ((session->mp->header.tlen - session->mp->len) / + MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) ); + + /* Push the previous message for processing */ + if (session->mcb) + session->mcb (session->cs, session->mp); + else + free(session->mp); + + session->mp = NULL; + goto NEW_MULTIPARTED; + } + } else { + /* In this case threat the message as if it was received in order + */ + + /* This is also a point for new multiparted messages */ +NEW_MULTIPARTED: + + /* Only allow messages which have arrived in order; + * drop late messages + */ + if (chloss(session, header)) { + return 0; + } else { + /* Message is not late; pick up the latest parameters */ + session->rsequnum = ntohs(header->sequnum); + session->rtimestamp = ntohl(header->timestamp); + } + + bwc_add_recv(session->bwc, length); + + /* Again, only store message if handler is present + */ + if (session->mcb) { + session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length); + + /* Reposition data if necessary */ + if (ntohs(header->cpart)); + + memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len); + } + } } - - report->timestamp = current_time_monotonic(); - - free(rb_write(session->pl_stats, report)); - - LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets); + return 0; } -void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber) -{ - if (session->last_expected_packets == 0) - return; - - uint8_t parsed[9]; - parsed[0] = session->prefix; - - uint32_t received_packets = htonl(session->last_received_packets); - uint32_t expected_packets = htonl(session->last_expected_packets); - - memcpy(parsed + 1, &received_packets, 4); - memcpy(parsed + 5, &expected_packets, 4); - - if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed))) - LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno)); - else { - LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets); - - session->last_received_packets = 0; - session->last_expected_packets = 0; - session->last_sent_report_ts = current_time_monotonic(); - } -} diff --git a/toxav/rtp.h b/toxav/rtp.h index 9c5cf07d..fddbce3d 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h @@ -22,119 +22,88 @@ #ifndef RTP_H #define RTP_H -#define RTP_VERSION 2 - +#include "bwcontroler.h" #include "../toxcore/Messenger.h" #include "stdbool.h" /** - * Payload type identifier. Also used as rtp callback prefix. (Not dummies) + * Payload type identifier. Also used as rtp callback prefix. */ enum { rtp_TypeAudio = 192, rtp_TypeVideo, }; -enum { - rtp_StateBad = -1, - rtp_StateNormal, - rtp_StateGood, -}; +struct RTPHeader { + /* Standard RTP header */ +#ifndef WORDS_BIGENDIAN + unsigned cc: 4; /* Contributing sources count */ + unsigned xe: 1; /* Extra header */ + unsigned pe: 1; /* Padding */ + unsigned ve: 2; /* Version */ + + unsigned pt: 7; /* Payload type */ + unsigned ma: 1; /* Marker */ +#else + unsigned ve: 2; /* Version */ + unsigned pe: 1; /* Padding */ + unsigned xe: 1; /* Extra header */ + unsigned cc: 4; /* Contributing sources count */ + + unsigned ma: 1; /* Marker */ + unsigned pt: 7; /* Payload type */ +#endif + + uint16_t sequnum; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[16]; -/** - * Standard rtp header. - */ -typedef struct { - 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; -/** - * Standard rtp extension header. - */ -typedef struct { - uint16_t type; /* Extension profile */ - uint16_t length; /* Number of extensions */ - uint32_t *table; /* Extension's table */ -} RTPExtHeader; + /* Non-standard TOX-specific fields */ + uint16_t cpart;/* Data offset of the current part */ + uint16_t tlen; /* Total message lenght */ +} __attribute__ ((packed)); -/** - * Standard rtp message. - */ -typedef struct RTPMessage_s { - RTPHeader *header; - RTPExtHeader *ext_header; +/* Check alignment */ +typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ]; + +struct RTPMessage { + uint16_t len; + + struct RTPHeader header; + uint8_t data[]; +} __attribute__ ((packed)); - uint32_t length; - uint8_t data[]; -} RTPMessage; +/* Check alignment */ +typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ]; /** * RTP control session. */ typedef struct { - uint8_t version; - uint8_t padding; - uint8_t extension; - uint8_t cc; - uint8_t marker; uint8_t payload_type; - uint16_t sequnum; /* Sending sequence number */ - uint16_t rsequnum; /* Receiving sequence number */ + uint16_t sequnum; /* Sending sequence number */ + uint16_t rsequnum; /* Receiving sequence number */ uint32_t rtimestamp; 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; + struct RTPMessage *mp; /* Expected parted message */ Messenger *m; - int friend_number; - struct RTCPSession_s *rtcp_session; + uint32_t friend_number; + BWControler *bwc; void *cs; - int (*mcb) (void*, RTPMessage* msg); - + int (*mcb) (void *, struct RTPMessage *msg); } RTPSession; -/** - * Must be called before calling any other rtp function. - */ -RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) ); -/** - * Terminate the session. - */ -void rtp_kill ( RTPSession* session ); -/** - * Do periodical rtp work. - */ -int rtp_do(RTPSession *session); -/** - * By default rtp is in receiving state - */ -int rtp_start_receiving (RTPSession *session); -/** - * Pause rtp receiving mode. - */ -int rtp_stop_receiving (RTPSession *session); -/** - * Sends msg to RTPSession::dest - */ -int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy ); -/** - * Dealloc msg. - */ -void rtp_free_msg ( RTPMessage *msg ); +RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num, + BWControler *bwc, void *cs, + int (*mcb) (void *, struct RTPMessage *)); +void rtp_kill (RTPSession *session); +int rtp_allow_receiving (RTPSession *session); +int rtp_stop_receiving (RTPSession *session); +int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length); #endif /* RTP_H */ diff --git a/toxav/toxav.c b/toxav/toxav.c index 8624a6b1..4a413b66 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -1,5 +1,5 @@ /** toxav.c - * + * * Copyright (C) 2013-2015 Tox project All Rights Reserved. * * This file is part of Tox. @@ -35,92 +35,74 @@ #include #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) -#define BITRATE_CHANGE_TESTING_TIME_MS 4000 - -typedef struct ToxAvBitrateAdapter_s { - bool active; - uint64_t end_time; - uint64_t next_send; - uint64_t next_send_interval; - uint32_t bit_rate; -} ToxAvBitrateAdapter; typedef struct ToxAVCall_s { - ToxAV* av; - + ToxAV *av; + pthread_mutex_t mutex_audio[1]; PAIR(RTPSession *, ACSession *) audio; - + pthread_mutex_t mutex_video[1]; PAIR(RTPSession *, VCSession *) video; - - pthread_mutex_t mutex[1]; - + + BWControler *bwc; + bool active; - MSICall* msi_call; + MSICall *msi_call; uint32_t friend_number; - + uint32_t audio_bit_rate; /* Sending audio bit rate */ uint32_t video_bit_rate; /* Sending video bit rate */ - - ToxAvBitrateAdapter aba; - ToxAvBitrateAdapter vba; - + /** Required for monitoring changes in states */ uint8_t previous_self_capabilities; - - /** Quality control */ - uint64_t time_audio_good; - uint32_t last_bad_audio_bit_rate; - uint64_t time_video_good; - uint32_t last_bad_video_bit_rate; - + + pthread_mutex_t mutex[1]; + struct ToxAVCall_s *prev; struct ToxAVCall_s *next; } ToxAVCall; -struct ToxAV { - Messenger* m; - MSISession* msi; - +struct ToxAV_s { + Messenger *m; + MSISession *msi; + /* Two-way storage: first is array of calls and second is list of calls with head and tail */ - ToxAVCall** calls; + ToxAVCall **calls; uint32_t calls_tail; uint32_t calls_head; pthread_mutex_t mutex[1]; - - PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ + + PAIR(toxav_call_cb *, void *) ccb; /* Call callback */ PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */ PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */ - PAIR(toxav_audio_bit_rate_status_cb *, void *) abcb; /* Audio bit rate control callback */ - PAIR(toxav_video_bit_rate_status_cb *, void *) vbcb; /* Video bit rate control callback */ - + PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */ + /** Decode time measures */ int32_t dmssc; /** Measure count */ int32_t dmsst; /** Last cycle total */ int32_t dmssa; /** Average decoding time in ms */ - + uint32_t interval; /** Calculated interval */ }; +void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data); -int callback_invite(void* toxav_inst, MSICall* call); -int callback_start(void* toxav_inst, MSICall* call); -int callback_end(void* toxav_inst, MSICall* call); -int callback_error(void* toxav_inst, MSICall* call); -int callback_capabilites(void* toxav_inst, MSICall* call); +int callback_invite(void *toxav_inst, MSICall *call); +int callback_start(void *toxav_inst, MSICall *call); +int callback_end(void *toxav_inst, MSICall *call); +int callback_error(void *toxav_inst, MSICall *call); +int callback_capabilites(void *toxav_inst, MSICall *call); bool audio_bit_rate_invalid(uint32_t bit_rate); bool video_bit_rate_invalid(uint32_t bit_rate); -bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state); -ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); -ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); -ToxAVCall* call_remove(ToxAVCall* call); -bool call_prepare_transmission(ToxAVCall* call); -void call_kill_transmission(ToxAVCall* call); -void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate); -bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba); +bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state); +ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error); +ToxAVCall *call_get(ToxAV *av, uint32_t friend_number); +ToxAVCall *call_remove(ToxAVCall *call); +bool call_prepare_transmission(ToxAVCall *call); +void call_kill_transmission(ToxAVCall *call); uint32_t toxav_version_major(void) { @@ -139,79 +121,86 @@ bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch) (void)major; (void)minor; (void)patch; - + return 1; } -ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) +ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error) { TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK; ToxAV *av = NULL; - + if (tox == NULL) { rc = TOXAV_ERR_NEW_NULL; goto END; } - - if (((Messenger*)tox)->msi_packet) { + + if (((Messenger *)tox)->msi_packet) { rc = TOXAV_ERR_NEW_MULTIPLE; goto END; } - + av = calloc (sizeof(ToxAV), 1); - + if (av == NULL) { LOGGER_WARNING("Allocation failed!"); rc = TOXAV_ERR_NEW_MALLOC; goto END; } - + if (create_recursive_mutex(av->mutex) != 0) { LOGGER_WARNING("Mutex creation failed!"); rc = TOXAV_ERR_NEW_MALLOC; goto END; } - + av->m = (Messenger *)tox; av->msi = msi_new(av->m); - + if (av->msi == NULL) { pthread_mutex_destroy(av->mutex); rc = TOXAV_ERR_NEW_MALLOC; goto END; } - + av->interval = 200; av->msi->av = av; - + msi_register_callback(av->msi, callback_invite, msi_OnInvite); msi_register_callback(av->msi, callback_start, msi_OnStart); msi_register_callback(av->msi, callback_end, msi_OnEnd); msi_register_callback(av->msi, callback_error, msi_OnError); msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout); msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities); - + END: + if (error) *error = rc; - + if (rc != TOXAV_ERR_NEW_OK) { free(av); av = NULL; } - + return av; } -void toxav_kill(ToxAV* av) +void toxav_kill(ToxAV *av) { if (av == NULL) return; + pthread_mutex_lock(av->mutex); - msi_kill(av->msi); - + /* To avoid possible deadlocks */ + while (av->msi && msi_kill(av->msi) != 0) { + pthread_mutex_unlock(av->mutex); + pthread_mutex_lock(av->mutex); + } + /* Msi kill will hang up all calls so just clean these calls */ if (av->calls) { - ToxAVCall* it = call_get(av, av->calls_head); + ToxAVCall *it = call_get(av, av->calls_head); + while (it) { call_kill_transmission(it); it = call_remove(it); /* This will eventually free av->calls */ @@ -220,812 +209,614 @@ void toxav_kill(ToxAV* av) pthread_mutex_unlock(av->mutex); pthread_mutex_destroy(av->mutex); + free(av); } -Tox* toxav_get_tox(const ToxAV* av) +Tox *toxav_get_tox(const ToxAV *av) { - return (Tox*) av->m; + return (Tox *) av->m; } -uint32_t toxav_iteration_interval(const ToxAV* av) +uint32_t toxav_iteration_interval(const ToxAV *av) { /* If no call is active interval is 200 */ return av->calls ? av->interval : 200; } -void toxav_iterate(ToxAV* av) +void toxav_iterate(ToxAV *av) { pthread_mutex_lock(av->mutex); + if (av->calls == NULL) { pthread_mutex_unlock(av->mutex); return; } - + uint64_t start = current_time_monotonic(); int32_t rc = 500; - - ToxAVCall* i = av->calls[av->calls_head]; + + ToxAVCall *i = av->calls[av->calls_head]; + for (; i; i = i->next) { if (i->active) { pthread_mutex_lock(i->mutex); pthread_mutex_unlock(av->mutex); - - ac_do(i->audio.second); - if (rtp_do(i->audio.first) < 0) { - /* Bad transmission */ - - uint32_t bb = i->audio_bit_rate; - - if (i->aba.active) { - bb = i->aba.bit_rate; - /* Stop sending dummy packets */ - memset(&i->aba, 0, sizeof(i->aba)); - } - - /* Notify app */ - if (av->abcb.first) - av->abcb.first (av, i->friend_number, false, bb, av->abcb.second); - } else if (i->aba.active && i->aba.end_time < current_time_monotonic()) { - - i->audio_bit_rate = i->aba.bit_rate; - - /* Notify user about the new bit rate */ - if (av->abcb.first) - av->abcb.first (av, i->friend_number, true, i->aba.bit_rate, av->abcb.second); - - /* Stop sending dummy packets */ - memset(&i->aba, 0, sizeof(i->aba)); - } - - vc_do(i->video.second); - if (rtp_do(i->video.first) < 0) { - /* Bad transmission */ - uint32_t bb = i->video_bit_rate; - - if (i->vba.active) { - bb = i->vba.bit_rate; - /* Stop sending dummy packets */ - memset(&i->vba, 0, sizeof(i->vba)); - } - - /* Notify app */ - if (av->vbcb.first) - av->vbcb.first (av, i->friend_number, false, bb, av->vbcb.second); - - } else if (i->vba.active && i->vba.end_time < current_time_monotonic()) { - - i->video_bit_rate = i->vba.bit_rate; - - /* Notify user about the new bit rate */ - if (av->vbcb.first) - av->vbcb.first (av, i->friend_number, true, i->vba.bit_rate, av->vbcb.second); - - /* Stop sending dummy packets */ - memset(&i->vba, 0, sizeof(i->vba)); - } - - if (i->msi_call->self_capabilities & msi_CapRAudio && - i->msi_call->peer_capabilities & msi_CapSAudio) - rc = MIN(i->audio.second->last_packet_frame_duration, rc); - - if (i->msi_call->self_capabilities & msi_CapRVideo && - i->msi_call->peer_capabilities & msi_CapSVideo) + + ac_iterate(i->audio.second); + vc_iterate(i->video.second); + + if (i->msi_call->self_capabilities & msi_CapRAudio && + i->msi_call->peer_capabilities & msi_CapSAudio) + rc = MIN(i->audio.second->lp_frame_duration, rc); + + if (i->msi_call->self_capabilities & msi_CapRVideo && + i->msi_call->peer_capabilities & msi_CapSVideo) rc = MIN(i->video.second->lcfd, (uint32_t) rc); - + uint32_t fid = i->friend_number; - + pthread_mutex_unlock(i->mutex); pthread_mutex_lock(av->mutex); - + /* In case this call is popped from container stop iteration */ if (call_get(av, fid) != i) break; } } + pthread_mutex_unlock(av->mutex); - + av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa); av->dmsst += current_time_monotonic() - start; - + if (++av->dmssc == 3) { av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */; av->dmssc = 0; av->dmsst = 0; } } -bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) +bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, + TOXAV_ERR_CALL *error) { - if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) - ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate)) - ) { - if (error) - *error = TOXAV_ERR_CALL_INVALID_BIT_RATE; - return false; - } + TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; pthread_mutex_lock(av->mutex); - ToxAVCall* call = call_new(av, friend_number, error); + + if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) + || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) { + rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; + goto END; + } + + ToxAVCall *call = call_new(av, friend_number, error); + if (call == NULL) { - pthread_mutex_unlock(av->mutex); - return false; + rc = TOXAV_ERR_CALL_MALLOC; + goto END; } - + call->audio_bit_rate = audio_bit_rate; call->video_bit_rate = video_bit_rate; - + call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; - + call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; - + if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) { call_remove(call); - if (error) - *error = TOXAV_ERR_CALL_MALLOC; - pthread_mutex_unlock(av->mutex); - return false; + rc = TOXAV_ERR_CALL_SYNC; + goto END; } - + call->msi_call->av_call = call; + +END: pthread_mutex_unlock(av->mutex); - return true; + if (error) + *error = rc; + + return rc == TOXAV_ERR_CALL_OK; } -void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) +void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data) { pthread_mutex_lock(av->mutex); av->ccb.first = function; av->ccb.second = user_data; pthread_mutex_unlock(av->mutex); } -bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) +bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, + TOXAV_ERR_ANSWER *error) { pthread_mutex_lock(av->mutex); - + TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; + if (m_friend_exists(av->m, friend_number) == 0) { rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; goto END; } - + if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) - ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate)) - ) { + || (video_bit_rate && video_bit_rate_invalid(video_bit_rate)) + ) { rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE; goto END; } - - ToxAVCall* call = call_get(av, friend_number); + + ToxAVCall *call = call_get(av, friend_number); + if (call == NULL) { rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; goto END; } - + if (!call_prepare_transmission(call)) { - rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION; - goto END; - } - + rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION; + goto END; + } + call->audio_bit_rate = audio_bit_rate; call->video_bit_rate = video_bit_rate; - + call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; - + call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; - + if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) - rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ - - + rc = TOXAV_ERR_ANSWER_SYNC; + END: pthread_mutex_unlock(av->mutex); - + if (error) *error = rc; - + return rc == TOXAV_ERR_ANSWER_OK; } -void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) +void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data) { pthread_mutex_lock(av->mutex); av->scb.first = function; av->scb.second = user_data; pthread_mutex_unlock(av->mutex); } -bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) +bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error) { pthread_mutex_lock(av->mutex); TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; - + if (m_friend_exists(av->m, friend_number) == 0) { rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; goto END; } - - ToxAVCall* call = call_get(av, friend_number); + + ToxAVCall *call = call_get(av, friend_number); + if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) { rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; goto END; } - + switch (control) { case TOXAV_CALL_CONTROL_RESUME: { /* Only act if paused and had media transfer active before */ - if (call->msi_call->self_capabilities == 0 && - call->previous_self_capabilities ) { - - if (msi_change_capabilities(call->msi_call, - call->previous_self_capabilities) == -1) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + if (call->msi_call->self_capabilities == 0 && + call->previous_self_capabilities) { + + if (msi_change_capabilities(call->msi_call, + call->previous_self_capabilities) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - - rtp_start_receiving(call->audio.first); - rtp_start_receiving(call->video.first); + + rtp_allow_receiving(call->audio.first); + rtp_allow_receiving(call->video.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; - + } + break; + case TOXAV_CALL_CONTROL_PAUSE: { /* Only act if not already paused */ if (call->msi_call->self_capabilities) { call->previous_self_capabilities = call->msi_call->self_capabilities; - - if (msi_change_capabilities(call->msi_call, 0) == -1 ) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + + if (msi_change_capabilities(call->msi_call, 0) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - + rtp_stop_receiving(call->audio.first); rtp_stop_receiving(call->video.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; - + } + break; + case TOXAV_CALL_CONTROL_CANCEL: { /* Hang up */ - msi_hangup(call->msi_call); - + if (msi_hangup(call->msi_call) != 0) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; + goto END; + } + /* No mather the case, terminate the call */ call_kill_transmission(call); call_remove(call); - } break; - + } + break; + case TOXAV_CALL_CONTROL_MUTE_AUDIO: { if (call->msi_call->self_capabilities & msi_CapRAudio) { if (msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities ^ msi_CapRAudio) == -1) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + msi_call->self_capabilities ^ msi_CapRAudio) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - + rtp_stop_receiving(call->audio.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; - + } + break; + case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: { if (call->msi_call->self_capabilities ^ msi_CapRAudio) { if (msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities | msi_CapRAudio) == -1) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + msi_call->self_capabilities | msi_CapRAudio) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - - rtp_start_receiving(call->audio.first); + + rtp_allow_receiving(call->audio.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; - + } + break; + case TOXAV_CALL_CONTROL_HIDE_VIDEO: { if (call->msi_call->self_capabilities & msi_CapRVideo) { if (msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities ^ msi_CapRVideo) == -1) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + msi_call->self_capabilities ^ msi_CapRVideo) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - + rtp_stop_receiving(call->video.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; - + } + break; + case TOXAV_CALL_CONTROL_SHOW_VIDEO: { if (call->msi_call->self_capabilities ^ msi_CapRVideo) { if (msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities | msi_CapRVideo) == -1) { - /* The only reason for this function to fail is invalid state - * ( not active ) */ - rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; + msi_call->self_capabilities | msi_CapRVideo) == -1) { + rc = TOXAV_ERR_CALL_CONTROL_SYNC; goto END; } - - rtp_start_receiving(call->audio.first); + + rtp_allow_receiving(call->audio.first); } else { rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; goto END; } - } break; + } + break; } - + END: pthread_mutex_unlock(av->mutex); - + if (error) *error = rc; - + return rc == TOXAV_ERR_CALL_CONTROL_OK; } -void toxav_callback_audio_bit_rate_status(ToxAV* av, toxav_audio_bit_rate_status_cb* function, void* user_data) +bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate, + int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error) { - pthread_mutex_lock(av->mutex); - av->abcb.first = function; - av->abcb.second = user_data; - pthread_mutex_unlock(av->mutex); -} -bool toxav_audio_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error) -{ - LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate); - - TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK; - ToxAVCall* call; - + TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK; + ToxAVCall *call; + if (m_friend_exists(av->m, friend_number) == 0) { - rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND; + rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND; goto END; } - - if (audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) { - rc = TOXAV_ERR_SET_BIT_RATE_INVALID; + + if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) { + rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE; goto END; } - + + if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) { + rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE; + goto END; + } + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); + if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { pthread_mutex_unlock(av->mutex); - rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL; - goto END; - } - - if (call->audio_bit_rate == audio_bit_rate || (call->aba.active && call->aba.bit_rate == audio_bit_rate)) { - pthread_mutex_unlock(av->mutex); - goto END; - } - - /* Video sending is turned off; notify peer */ - if (audio_bit_rate == 0) { - call->audio_bit_rate = 0; - - msi_change_capabilities(call->msi_call, call->msi_call-> - self_capabilities ^ msi_CapSAudio); - pthread_mutex_unlock(av->mutex); + rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL; goto END; } - - pthread_mutex_lock(call->mutex); - - if (call->audio_bit_rate == 0) { - /* The audio has been turned off before this */ - call->audio_bit_rate = audio_bit_rate; - - msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities | msi_CapSAudio); - - if (!force && av->abcb.first) - av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second); - } else { - /* The audio was active before this */ - if (audio_bit_rate > call->audio_bit_rate && !force) - ba_set(&call->aba, audio_bit_rate); - else { - /* Cancel any previous non forceful bitrate change request */ - memset(&call->aba, 0, sizeof(call->aba)); + + if (audio_bit_rate >= 0) { + LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate); + + if (call->audio_bit_rate == audio_bit_rate) { + LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate); + } else if (audio_bit_rate == 0) { + LOGGER_DEBUG("Turned off audio sending"); + if (msi_change_capabilities(call->msi_call, call->msi_call-> + self_capabilities ^ msi_CapSAudio) != 0) { + pthread_mutex_unlock(av->mutex); + rc = TOXAV_ERR_BIT_RATE_SET_SYNC; + goto END; + } + /* Audio sending is turned off; notify peer */ + call->audio_bit_rate = 0; + } else { + pthread_mutex_lock(call->mutex); + if (call->audio_bit_rate == 0) { + LOGGER_DEBUG("Turned on audio sending"); + /* The audio has been turned off before this */ + if (msi_change_capabilities(call->msi_call, call-> + msi_call->self_capabilities | msi_CapSAudio) != 0) { + pthread_mutex_unlock(call->mutex); + pthread_mutex_unlock(av->mutex); + rc = TOXAV_ERR_BIT_RATE_SET_SYNC; + goto END; + } + } else + LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate); call->audio_bit_rate = audio_bit_rate; - - if (!force && av->abcb.first) - av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second); + pthread_mutex_unlock(call->mutex); + } + } + + if (video_bit_rate >= 0) { + LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate); + + if (call->video_bit_rate == video_bit_rate) { + LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate); + } else if (video_bit_rate == 0) { + LOGGER_DEBUG("Turned off video sending"); + /* Video sending is turned off; notify peer */ + if (msi_change_capabilities(call->msi_call, call->msi_call-> + self_capabilities ^ msi_CapSVideo) != 0) { + pthread_mutex_unlock(av->mutex); + rc = TOXAV_ERR_BIT_RATE_SET_SYNC; + goto END; + } + call->video_bit_rate = 0; + } else { + pthread_mutex_lock(call->mutex); + if (call->video_bit_rate == 0) { + LOGGER_DEBUG("Turned on video sending"); + /* The video has been turned off before this */ + if (msi_change_capabilities(call->msi_call, call-> + msi_call->self_capabilities | msi_CapSVideo) != 0) { + pthread_mutex_unlock(call->mutex); + pthread_mutex_unlock(av->mutex); + rc = TOXAV_ERR_BIT_RATE_SET_SYNC; + goto END; + } + } else + LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate); + call->video_bit_rate = video_bit_rate; + pthread_mutex_unlock(call->mutex); } } - pthread_mutex_unlock(call->mutex); pthread_mutex_unlock(av->mutex); - END: if (error) *error = rc; - - return rc == TOXAV_ERR_SET_BIT_RATE_OK; + + return rc == TOXAV_ERR_BIT_RATE_SET_OK; } -void toxav_callback_video_bit_rate_status(ToxAV* av, toxav_video_bit_rate_status_cb* function, void* user_data) +void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data) { pthread_mutex_lock(av->mutex); - av->vbcb.first = function; - av->vbcb.second = user_data; + av->bcb.first = function; + av->bcb.second = user_data; pthread_mutex_unlock(av->mutex); } -bool toxav_video_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error) -{ - LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate); - - TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK; - ToxAVCall* call; - - if (m_friend_exists(av->m, friend_number) == 0) { - rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND; - goto END; - } - - if (video_bit_rate && video_bit_rate_invalid(video_bit_rate)) { - rc = TOXAV_ERR_SET_BIT_RATE_INVALID; - goto END; - } - - pthread_mutex_lock(av->mutex); - call = call_get(av, friend_number); - if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { - pthread_mutex_unlock(av->mutex); - rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL; - goto END; - } - - if (call->video_bit_rate == video_bit_rate || (call->vba.active && call->vba.bit_rate == video_bit_rate)) { - pthread_mutex_unlock(av->mutex); - goto END; - } - - /* Video sending is turned off; notify peer */ - if (video_bit_rate == 0) { - call->video_bit_rate = 0; - - msi_change_capabilities(call->msi_call, call->msi_call-> - self_capabilities ^ msi_CapSVideo); - pthread_mutex_unlock(av->mutex); - goto END; - } - - pthread_mutex_lock(call->mutex); - - if (call->video_bit_rate == 0) { - /* The video has been turned off before this */ - call->video_bit_rate = video_bit_rate; - - msi_change_capabilities(call->msi_call, call-> - msi_call->self_capabilities | msi_CapSVideo); - - if (!force && av->vbcb.first) - av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second); - } else { - /* The video was active before this */ - if (video_bit_rate > call->video_bit_rate && !force) - ba_set(&call->vba, video_bit_rate); - else { - /* Cancel any previous non forceful bitrate change request */ - memset(&call->vba, 0, sizeof(call->vba)); - call->video_bit_rate = video_bit_rate; - - if (!force && av->vbcb.first) - av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second); - } - } - - pthread_mutex_unlock(call->mutex); - pthread_mutex_unlock(av->mutex); - -END: - if (error) - *error = rc; - - return rc == TOXAV_ERR_SET_BIT_RATE_OK; -} -bool toxav_audio_send_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) +bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count, + uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error) { TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; - ToxAVCall* call; - + ToxAVCall *call; + if (m_friend_exists(av->m, friend_number) == 0) { rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; goto END; } - + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); + if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; goto END; } - + if (call->audio_bit_rate == 0 || - !(call->msi_call->self_capabilities & msi_CapSAudio) || - !(call->msi_call->peer_capabilities & msi_CapRAudio)) { + !(call->msi_call->self_capabilities & msi_CapSAudio) || + !(call->msi_call->peer_capabilities & msi_CapRAudio)) { pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED; goto END; } - + pthread_mutex_lock(call->mutex_audio); pthread_mutex_unlock(av->mutex); - - if ( pcm == NULL ) { + + if (pcm == NULL) { pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_NULL; goto END; } - - if ( channels > 2 ) { + + if (channels > 2) { pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } - + { /* Encode and send */ if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } - + uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */ - + sampling_rate = htonl(sampling_rate); memcpy(dest, &sampling_rate, sizeof(sampling_rate)); int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count, dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); - + if (vrc < 0) { LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc)); pthread_mutex_unlock(call->mutex_audio); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } - - if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), false) != 0) { + + if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) { LOGGER_WARNING("Failed to send audio packet"); rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; } - - - /* For bit rate measurement; send dummy packet */ - if (ba_shoud_send_dummy(&call->aba)) { - sampling_rate = ntohl(sampling_rate); - if (ac_reconfigure_test_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { - /* FIXME should the bit rate changing fail here? */ - pthread_mutex_unlock(call->mutex_audio); - rc = TOXAV_ERR_SEND_FRAME_INVALID; - goto END; - } - - sampling_rate = htonl(sampling_rate); - memcpy(dest, &sampling_rate, sizeof(sampling_rate)); - vrc = opus_encode(call->audio.second->test_encoder, pcm, sample_count, - dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); - - if (vrc < 0) { - LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc)); - pthread_mutex_unlock(call->mutex_audio); - rc = TOXAV_ERR_SEND_FRAME_INVALID; - goto END; - } - - if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), true) != 0) { - LOGGER_WARNING("Failed to send audio packet"); - rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; - } - - if (call->aba.end_time == (uint64_t) ~0) - call->aba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS; - } } - - + + pthread_mutex_unlock(call->mutex_audio); - + END: if (error) *error = rc; - + return rc == TOXAV_ERR_SEND_FRAME_OK; } -bool toxav_video_send_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) +bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, + const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error) { TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; - ToxAVCall* call; - + ToxAVCall *call; + if (m_friend_exists(av->m, friend_number) == 0) { rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; goto END; } - + pthread_mutex_lock(av->mutex); call = call_get(av, friend_number); + if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; goto END; } - + if (call->video_bit_rate == 0 || - !(call->msi_call->self_capabilities & msi_CapSVideo) || - !(call->msi_call->peer_capabilities & msi_CapRVideo)) { + !(call->msi_call->self_capabilities & msi_CapSVideo) || + !(call->msi_call->peer_capabilities & msi_CapRVideo)) { pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED; goto END; } - + pthread_mutex_lock(call->mutex_video); pthread_mutex_unlock(av->mutex); - - if ( y == NULL || u == NULL || v == NULL ) { + + if (y == NULL || u == NULL || v == NULL) { pthread_mutex_unlock(call->mutex_video); rc = TOXAV_ERR_SEND_FRAME_NULL; goto END; } - - if ( vc_reconfigure_encoder(call->video.second->encoder, call->video_bit_rate * 1000, width, height) != 0 ) { + + if (vc_reconfigure_encoder(call->video.second->encoder, call->video_bit_rate * 1000, width, height) != 0) { pthread_mutex_unlock(call->mutex_video); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } - + { /* Encode */ vpx_image_t img; img.w = img.h = img.d_w = img.d_h = 0; - vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); - - /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." + vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0); + + /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." * http://fourcc.org/yuv.php#IYUV */ memcpy(img.planes[VPX_PLANE_Y], y, width * height); - memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); - memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); - - int vrc = vpx_codec_encode(call->video.second->encoder, &img, + memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2)); + memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2)); + + int vrc = vpx_codec_encode(call->video.second->encoder, &img, call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US); - + vpx_img_free(&img); - if ( vrc != VPX_CODEC_OK) { + + if (vrc != VPX_CODEC_OK) { pthread_mutex_unlock(call->mutex_video); LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); rc = TOXAV_ERR_SEND_FRAME_INVALID; goto END; } } - + ++call->video.second->frame_counter; - - { /* Split and send */ - vpx_codec_iter_t iter = NULL; - const vpx_codec_cx_pkt_t *pkt; - - vc_init_video_splitter_cycle(call->video.second); - - while ( (pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter)) ) { - if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - int parts = vc_update_video_splitter_cycle(call->video.second, pkt->data.frame.buf, - pkt->data.frame.sz); - - if (parts < 0) /* Should never happen though */ - continue; - - uint16_t part_size; - const uint8_t *iter; - - int i; - for (i = 0; i < parts; i++) { - iter = vc_iterate_split_video_frame(call->video.second, &part_size); - - if (rtp_send_data(call->video.first, iter, part_size, false) < 0) { - pthread_mutex_unlock(call->mutex_video); - LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); - rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; - goto END; - } - } - } - } - } - - if (ba_shoud_send_dummy(&call->vba)) { - if ( vc_reconfigure_encoder(call->video.second->test_encoder, call->vba.bit_rate * 1000, width, height) != 0 ) { - pthread_mutex_unlock(call->mutex_video); - rc = TOXAV_ERR_SEND_FRAME_INVALID; - goto END; - } - - /* FIXME use the same image as before */ - vpx_image_t img; - img.w = img.h = img.d_w = img.d_h = 0; - vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); - - /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." - * http://fourcc.org/yuv.php#IYUV - */ - memcpy(img.planes[VPX_PLANE_Y], y, width * height); - memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); - memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); - - int vrc = vpx_codec_encode(call->video.second->test_encoder, &img, - call->video.second->test_frame_counter, 1, 0, MAX_ENCODE_TIME_US); - - vpx_img_free(&img); - if ( vrc != VPX_CODEC_OK) { - pthread_mutex_unlock(call->mutex_video); - LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); - rc = TOXAV_ERR_SEND_FRAME_INVALID; - goto END; - } - - call->video.second->test_frame_counter++; - + + { /* Send frames */ vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; - - /* Send the encoded data as dummy packets */ - while ( (pkt = vpx_codec_get_cx_data(call->video.second->test_encoder, &iter)) ) { - if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { - - int parts = pkt->data.frame.sz / 1300; - int i; - for (i = 0; i < parts; i++) { - if (rtp_send_data(call->video.first, pkt->data.frame.buf + i * 1300, 1300, true) < 0) { - pthread_mutex_unlock(call->mutex_video); - LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); - rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; - goto END; - } - } - - if (pkt->data.frame.sz % 1300) { - if (rtp_send_data(call->video.first, pkt->data.frame.buf + parts * 1300, pkt->data.frame.sz % 1300, true) < 0) { - pthread_mutex_unlock(call->mutex_video); - LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); - rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; - goto END; - } - } + + while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) { + if (pkt->kind == VPX_CODEC_CX_FRAME_PKT && + rtp_send_data(call->video.first, pkt->data.frame.buf, pkt->data.frame.sz) < 0) { + + pthread_mutex_unlock(call->mutex_video); + LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno)); + rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; + goto END; } } - - if (call->vba.end_time == (uint64_t) ~0) - call->vba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS; } - + pthread_mutex_unlock(call->mutex_video); - + END: if (error) *error = rc; - + return rc == TOXAV_ERR_SEND_FRAME_OK; } -void toxav_callback_audio_receive_frame(ToxAV* av, toxav_audio_receive_frame_cb* function, void* user_data) +void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data) { pthread_mutex_lock(av->mutex); av->acb.first = function; av->acb.second = user_data; pthread_mutex_unlock(av->mutex); } -void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb* function, void* user_data) +void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data) { pthread_mutex_lock(av->mutex); av->vcb.first = function; @@ -1039,108 +830,144 @@ void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb* * :: Internal * ******************************************************************************/ -int callback_invite(void* toxav_inst, MSICall* call) +void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* user_data) { - ToxAV* toxav = toxav_inst; - pthread_mutex_lock(toxav->mutex); + /* Callback which is called when the internal measure mechanism reported packet loss. + * We report suggested lowered bitrate to an app. If app is sending both audio and video, + * we will report lowered bitrate for video only because in that case video probably + * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio. + * The application may choose to disable video totally if the stream is too bad. + */ + + ToxAVCall* call = user_data; + assert(call); + + LOGGER_DEBUG("Reported loss of %f%%", loss*100); - ToxAVCall* av_call = call_new(toxav, call->friend_number, NULL); + if (loss < .01f) + return; + + pthread_mutex_lock(call->av->mutex); + if (!call->av->bcb.first) { + pthread_mutex_unlock(call->av->mutex); + LOGGER_WARNING("No callback to report loss on"); + return; + } + + if (call->video_bit_rate) + (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate, + call->video_bit_rate - (call->video_bit_rate * loss), + call->av->bcb.second); + else if (call->audio_bit_rate) + (*call->av->bcb.first) (call->av, friend_number, + call->audio_bit_rate - (call->audio_bit_rate * loss), + 0, call->av->bcb.second); + + pthread_mutex_unlock(call->av->mutex); +} +int callback_invite(void *toxav_inst, MSICall *call) +{ + ToxAV *toxav = toxav_inst; + pthread_mutex_lock(toxav->mutex); + + ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL); + if (av_call == NULL) { LOGGER_WARNING("Failed to initialize call..."); pthread_mutex_unlock(toxav->mutex); return -1; } - + call->av_call = av_call; av_call->msi_call = call; - + if (toxav->ccb.first) - toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio, + toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio, call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); else { /* No handler to capture the call request, send failure */ pthread_mutex_unlock(toxav->mutex); return -1; } - + pthread_mutex_unlock(toxav->mutex); return 0; } -int callback_start(void* toxav_inst, MSICall* call) +int callback_start(void *toxav_inst, MSICall *call) { - ToxAV* toxav = toxav_inst; + ToxAV *toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - - ToxAVCall* av_call = call_get(toxav, call->friend_number); - + + ToxAVCall *av_call = call_get(toxav, call->friend_number); + if (av_call == NULL) { /* Should this ever happen? */ pthread_mutex_unlock(toxav->mutex); return -1; } - + if (!call_prepare_transmission(av_call)) { callback_error(toxav_inst, call); pthread_mutex_unlock(toxav->mutex); return -1; } - + if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) { callback_error(toxav_inst, call); pthread_mutex_unlock(toxav->mutex); return -1; } - + pthread_mutex_unlock(toxav->mutex); return 0; } -int callback_end(void* toxav_inst, MSICall* call) +int callback_end(void *toxav_inst, MSICall *call) { - ToxAV* toxav = toxav_inst; + ToxAV *toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - + invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED); - + if (call->av_call) { call_kill_transmission(call->av_call); call_remove(call->av_call); } - + pthread_mutex_unlock(toxav->mutex); return 0; } -int callback_error(void* toxav_inst, MSICall* call) +int callback_error(void *toxav_inst, MSICall *call) { - ToxAV* toxav = toxav_inst; + ToxAV *toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - + invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR); - + if (call->av_call) { call_kill_transmission(call->av_call); call_remove(call->av_call); } - + pthread_mutex_unlock(toxav->mutex); return 0; } -int callback_capabilites(void* toxav_inst, MSICall* call) +int callback_capabilites(void *toxav_inst, MSICall *call) { - ToxAV* toxav = toxav_inst; + ToxAV *toxav = toxav_inst; pthread_mutex_lock(toxav->mutex); - + if (call->peer_capabilities & msi_CapSAudio) - rtp_start_receiving(((ToxAVCall*)call->av_call)->audio.first); + rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first); else - rtp_stop_receiving(((ToxAVCall*)call->av_call)->audio.first); - + rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first); + if (call->peer_capabilities & msi_CapSVideo) - rtp_start_receiving(((ToxAVCall*)call->av_call)->video.first); + rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first); else - rtp_stop_receiving(((ToxAVCall*)call->av_call)->video.first); - + rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first); + invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities); - + pthread_mutex_unlock(toxav->mutex); return 0; } @@ -1157,201 +984,212 @@ bool video_bit_rate_invalid(uint32_t bit_rate) /* TODO: If anyone knows the answer to this one please fill it up */ return false; } -bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state) +bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state) { if (av->scb.first) av->scb.first(av, friend_number, state, av->scb.second); else return false; + return true; } -ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) +ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error) { /* Assumes mutex locked */ TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; - ToxAVCall* call = NULL; - + ToxAVCall *call = NULL; + if (m_friend_exists(av->m, friend_number) == 0) { rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; goto END; } - + if (m_get_friend_connectionstatus(av->m, friend_number) < 1) { rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; goto END; } - + if (call_get(av, friend_number) != NULL) { rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; goto END; } - - + + call = calloc(sizeof(ToxAVCall), 1); - + if (call == NULL) { rc = TOXAV_ERR_CALL_MALLOC; goto END; } - + call->av = av; call->friend_number = friend_number; - + if (av->calls == NULL) { /* Creating */ - av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); - + av->calls = calloc (sizeof(ToxAVCall *), friend_number + 1); + if (av->calls == NULL) { free(call); call = NULL; rc = TOXAV_ERR_CALL_MALLOC; goto END; } - + av->calls_tail = av->calls_head = friend_number; - + } else if (av->calls_tail < friend_number) { /* Appending */ - void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); - + void *tmp = realloc(av->calls, sizeof(ToxAVCall *) * friend_number + 1); + if (tmp == NULL) { free(call); call = NULL; rc = TOXAV_ERR_CALL_MALLOC; goto END; } - + av->calls = tmp; - + /* Set fields in between to null */ uint32_t i = av->calls_tail + 1; + for (; i < friend_number; i ++) av->calls[i] = NULL; - + call->prev = av->calls[av->calls_tail]; av->calls[av->calls_tail]->next = call; - + av->calls_tail = friend_number; - + } else if (av->calls_head > friend_number) { /* Inserting at front */ call->next = av->calls[av->calls_head]; av->calls[av->calls_head]->prev = call; av->calls_head = friend_number; } - + av->calls[friend_number] = call; - + END: + if (error) *error = rc; - + return call; } -ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) +ToxAVCall *call_get(ToxAV *av, uint32_t friend_number) { /* Assumes mutex locked */ if (av->calls == NULL || av->calls_tail < friend_number) return NULL; - + return av->calls[friend_number]; } -ToxAVCall* call_remove(ToxAVCall* call) +ToxAVCall *call_remove(ToxAVCall *call) { if (call == NULL) return NULL; - + uint32_t friend_number = call->friend_number; - ToxAV* av = call->av; - - ToxAVCall* prev = call->prev; - ToxAVCall* next = call->next; - + ToxAV *av = call->av; + + ToxAVCall *prev = call->prev; + ToxAVCall *next = call->next; + /* Set av call in msi to NULL in order to know if call if ToxAVCall is * removed from the msi call. */ call->msi_call->av_call = NULL; free(call); - + if (prev) prev->next = next; else if (next) av->calls_head = next->friend_number; else goto CLEAR; - + if (next) next->prev = prev; else if (prev) av->calls_tail = prev->friend_number; else goto CLEAR; - + av->calls[friend_number] = NULL; return next; - + CLEAR: av->calls_head = av->calls_tail = 0; free(av->calls); av->calls = NULL; - + return NULL; } -bool call_prepare_transmission(ToxAVCall* call) +bool call_prepare_transmission(ToxAVCall *call) { /* Assumes mutex locked */ - + if (call == NULL) return false; - - ToxAV* av = call->av; - + + ToxAV *av = call->av; + if (!av->acb.first && !av->vcb.first) /* It makes no sense to have CSession without callbacks */ return false; - + if (call->active) { LOGGER_WARNING("Call already active!\n"); return true; } - + if (create_recursive_mutex(call->mutex_audio) != 0) return false; - + if (create_recursive_mutex(call->mutex_video) != 0) goto FAILURE_3; - - if (create_recursive_mutex(call->mutex) != 0) + + if (create_recursive_mutex(call->mutex) != 0) goto FAILURE_2; - + + /* Prepare bwc */ + call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call); { /* Prepare audio */ call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second); + if (!call->audio.second) { LOGGER_ERROR("Failed to create audio codec session"); goto FAILURE; } - - call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->audio.second, ac_queue_message); + + call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc, + call->audio.second, ac_queue_message); + if (!call->audio.first) { LOGGER_ERROR("Failed to create audio rtp session");; goto FAILURE; } } - { /* Prepare video */ - call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); + call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second); + if (!call->video.second) { LOGGER_ERROR("Failed to create video codec session"); goto FAILURE; } - - call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->video.second, vc_queue_message); + + call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc, + call->video.second, vc_queue_message); + if (!call->video.first) { LOGGER_ERROR("Failed to create video rtp session"); goto FAILURE; } } - + call->active = 1; return true; - + FAILURE: + bwc_kill(call->bwc); rtp_kill(call->audio.first); ac_kill(call->audio.second); call->audio.first = NULL; @@ -1367,49 +1205,33 @@ FAILURE_3: pthread_mutex_destroy(call->mutex_audio); return false; } -void call_kill_transmission(ToxAVCall* call) +void call_kill_transmission(ToxAVCall *call) { if (call == NULL || call->active == 0) return; - + call->active = 0; - + pthread_mutex_lock(call->mutex_audio); pthread_mutex_unlock(call->mutex_audio); pthread_mutex_lock(call->mutex_video); pthread_mutex_unlock(call->mutex_video); pthread_mutex_lock(call->mutex); pthread_mutex_unlock(call->mutex); + + bwc_kill(call->bwc); rtp_kill(call->audio.first); ac_kill(call->audio.second); call->audio.first = NULL; call->audio.second = NULL; - + rtp_kill(call->video.first); vc_kill(call->video.second); call->video.first = NULL; call->video.second = NULL; - + pthread_mutex_destroy(call->mutex_audio); pthread_mutex_destroy(call->mutex_video); pthread_mutex_destroy(call->mutex); } -void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate) -{ - ba->bit_rate = bit_rate; - ba->next_send = current_time_monotonic(); - ba->end_time = ~0; - ba->next_send_interval = 1000; - ba->active = true; -} -bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba) -{ - if (!ba->active || ba->next_send > current_time_monotonic()) - return false; - - ba->next_send_interval *= 0.8; - ba->next_send = current_time_monotonic() + ba->next_send_interval; - - return true; -} \ No newline at end of file diff --git a/toxav/toxav.h b/toxav/toxav.h index 58d5503f..e83f4edc 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -52,12 +52,19 @@ extern "C" { /** \subsection threading Threading implications * * Unlike the Core API, this API is fully thread-safe. The library will ensure - * the proper synchronisation of parallel calls. + * the proper synchronization of parallel calls. * * A common way to run ToxAV (multiple or single instance) is to have a thread, * separate from tox instance thread, running a simple toxav_iterate loop, * sleeping for toxav_iteration_interval * milliseconds on each iteration. * + * An important thing to note is that events are triggered from both tox and + * toxav thread (see above). Audio and video receive frame events are triggered + * from toxav thread while all the other events are triggered from tox thread. + * + * Tox thread has priority with mutex mechanisms. Any api function can + * fail if mutexes are held by tox thread in which case they will set SYNC + * error code. */ /** * External Tox type. @@ -80,8 +87,10 @@ typedef struct Tox Tox; */ #ifndef TOXAV_DEFINED #define TOXAV_DEFINED -typedef struct ToxAV ToxAV; +typedef struct ToxAV_s ToxAV; #endif /* TOXAV_DEFINED */ + + /******************************************************************************* * * :: API version @@ -92,17 +101,20 @@ typedef struct ToxAV ToxAV; * incompatible way. */ #define TOXAV_VERSION_MAJOR 0u + /** * The minor version number. Incremented when functionality is added without * breaking the API or ABI. Set to 0 when the major version number is * incremented. */ #define TOXAV_VERSION_MINOR 0u + /** * The patch or revision number. Incremented when bugfixes are applied without * changing any functionality or API or ABI. */ #define TOXAV_VERSION_PATCH 0u + /** * A macro to check at preprocessing time whether the client code is compatible * with the installed version of ToxAV. @@ -112,37 +124,45 @@ typedef struct ToxAV ToxAV; (TOXAV_VERSION_MINOR > MINOR || \ (TOXAV_VERSION_MINOR == MINOR && \ TOXAV_VERSION_PATCH >= PATCH))) + /** * A macro to make compilation fail if the client code is not compatible with * the installed version of ToxAV. */ #define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \ typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1] + /** * A convenience macro to call toxav_version_is_compatible with the currently * compiling API version. */ #define TOXAV_VERSION_IS_ABI_COMPATIBLE() \ toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH) + /** * Return the major version number of the library. Can be used to display the * ToxAV library version or to check whether the client is compatible with the * dynamically linked version of ToxAV. */ uint32_t toxav_version_major(void); + /** * Return the minor version number of the library. */ uint32_t toxav_version_minor(void); + /** * Return the patch number of the library. */ uint32_t toxav_version_patch(void); + /** * Return whether the compiled library version is compatible with the passed * version numbers. */ bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); + + /******************************************************************************* * * :: Creation and destruction @@ -167,10 +187,12 @@ typedef enum TOXAV_ERR_NEW { */ TOXAV_ERR_NEW_MULTIPLE, } TOXAV_ERR_NEW; + /** * Start new A/V session. There can only be only one session per Tox instance. */ ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); + /** * Releases all resources associated with the A/V session. * @@ -179,10 +201,13 @@ ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); * called and the av pointer becomes invalid. */ void toxav_kill(ToxAV *toxAV); + /** * Returns the Tox instance the A/V object was created for. */ Tox *toxav_get_tox(const ToxAV *toxAV); + + /******************************************************************************* * * :: A/V event loop @@ -193,12 +218,15 @@ Tox *toxav_get_tox(const ToxAV *toxAV); * be. If no call is active at the moment, this function returns 200. */ uint32_t toxav_iteration_interval(const ToxAV *toxAV); + /** * Main loop for the session. This function needs to be called in intervals of * toxav_iteration_interval() milliseconds. It is best called in the separate * thread from tox_iterate. */ void toxav_iterate(ToxAV *toxAV); + + /******************************************************************************* * * :: Call setup @@ -214,6 +242,10 @@ typedef enum TOXAV_ERR_CALL { * required for the call. */ TOXAV_ERR_CALL_MALLOC, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_CALL_SYNC, /** * The friend number did not designate a valid friend. */ @@ -232,6 +264,7 @@ typedef enum TOXAV_ERR_CALL { */ TOXAV_ERR_CALL_INVALID_BIT_RATE, } TOXAV_ERR_CALL; + /** * Call a friend. This will start ringing the friend. * @@ -246,7 +279,9 @@ typedef enum TOXAV_ERR_CALL { * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable * video sending. */ -bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); +bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, + uint32_t video_bit_rate, TOXAV_ERR_CALL *error); + /** * The function type for the call callback. * @@ -254,17 +289,24 @@ bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, u * @param audio_enabled True if friend is sending audio. * @param video_enabled True if friend is sending video. */ -typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); +typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, + bool video_enabled, void *user_data); + /** * Set the callback for the `call` event. Pass NULL to unset. * */ void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data); + typedef enum TOXAV_ERR_ANSWER { /** * The function returned successfully. */ TOXAV_ERR_ANSWER_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_ANSWER_SYNC, /** * Failed to initialize codecs for call session. Note that codec initiation * will fail if there is no receive callback registered for either audio or @@ -285,6 +327,7 @@ typedef enum TOXAV_ERR_ANSWER { */ TOXAV_ERR_ANSWER_INVALID_BIT_RATE, } TOXAV_ERR_ANSWER; + /** * Accept an incoming call. * @@ -299,6 +342,8 @@ typedef enum TOXAV_ERR_ANSWER { * video sending. */ bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); + + /******************************************************************************* * * :: Call state graph @@ -336,7 +381,6 @@ enum TOXAV_FRIEND_CALL_STATE { TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32, }; - /** * The function type for the call_state callback. * @@ -347,11 +391,13 @@ enum TOXAV_FRIEND_CALL_STATE { * friend. */ typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data); + /** * Set the callback for the `call_state` event. Pass NULL to unset. * */ void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data); + /******************************************************************************* * * :: Call control @@ -393,11 +439,16 @@ typedef enum TOXAV_CALL_CONTROL { */ TOXAV_CALL_CONTROL_SHOW_VIDEO, } TOXAV_CALL_CONTROL; + typedef enum TOXAV_ERR_CALL_CONTROL { /** * The function returned successfully. */ TOXAV_ERR_CALL_CONTROL_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_CALL_CONTROL_SYNC, /** * The friend_number passed did not designate a valid friend. */ @@ -413,6 +464,7 @@ typedef enum TOXAV_ERR_CALL_CONTROL { */ TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION, } TOXAV_ERR_CALL_CONTROL; + /** * Sends a call control command to a friend. * @@ -423,48 +475,40 @@ typedef enum TOXAV_ERR_CALL_CONTROL { * @return true on success. */ bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); + + /******************************************************************************* * * :: Controlling bit rates * ******************************************************************************/ -typedef enum TOXAV_ERR_SET_BIT_RATE { +typedef enum TOXAV_ERR_BIT_RATE_SET { /** * The function returned successfully. */ - TOXAV_ERR_SET_BIT_RATE_OK, + TOXAV_ERR_BIT_RATE_SET_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_BIT_RATE_SET_SYNC, /** - * The bit rate passed was not one of the supported values. + * The audio bit rate passed was not one of the supported values. */ - TOXAV_ERR_SET_BIT_RATE_INVALID, + TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE, + /** + * The video bit rate passed was not one of the supported values. + */ + TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE, /** * The friend_number passed did not designate a valid friend. */ - TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND, + TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND, /** * This client is currently not in a call with the friend. */ - TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL, -} TOXAV_ERR_SET_BIT_RATE; -/** - * The function type for the audio_bit_rate_status callback. - * - * @param friend_number The friend number of the friend for which to set the - * audio bit rate. - * @param stable Is the stream stable enough to keep the bit rate. - * Upon successful, non forceful, bit rate change, this is set to - * true and 'bit_rate' is set to new bit rate. - * The stable is set to false with bit_rate set to the unstable - * bit rate when either current stream is unstable with said bit rate - * or the non forceful change failed. - * @param bit_rate The bit rate in Kb/sec. - */ -typedef void toxav_audio_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); -/** - * Set the callback for the `audio_bit_rate_status` event. Pass NULL to unset. - * - */ -void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_status_cb *callback, void *user_data); + TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL, +} TOXAV_ERR_BIT_RATE_SET; + /** * Set the audio bit rate to be used in subsequent audio frames. If the passed * bit rate is the same as the current bit rate this function will return true @@ -476,46 +520,33 @@ void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_sta * @param friend_number The friend number of the friend for which to set the * audio bit rate. * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable - * audio sending. - * @param force True if the bit rate change is forceful. + * audio sending. Set to -1 to leave unchanged. + * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable + * video sending. Set to -1 to leave unchanged. * */ -bool toxav_audio_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); +bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate, + int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error); + /** - * The function type for the video_bit_rate_status callback. + * The function type for the bit_rate_status callback. The event is triggered + * when the network becomes too saturated for current bit rates at which + * point core suggests new bit rates. * * @param friend_number The friend number of the friend for which to set the - * video bit rate. - * @param stable Is the stream stable enough to keep the bit rate. - * Upon successful, non forceful, bit rate change, this is set to - * true and 'bit_rate' is set to new bit rate. - * The stable is set to false with bit_rate set to the unstable - * bit rate when either current stream is unstable with said bit rate - * or the non forceful change failed. - * @param bit_rate The bit rate in Kb/sec. + * audio bit rate. + * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec. + * @param video_bit_rate Suggested maximum video bit rate in Kb/sec. */ -typedef void toxav_video_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); +typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data); + /** - * Set the callback for the `video_bit_rate_status` event. Pass NULL to unset. + * Set the callback for the `bit_rate_status` event. Pass NULL to unset. * */ -void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_status_cb *callback, void *user_data); -/** - * Set the video bit rate to be used in subsequent video frames. If the passed - * bit rate is the same as the current bit rate this function will return true - * without calling a callback. If there is an active non forceful setup with the - * passed video bit rate and the new set request is forceful, the bit rate is - * forcefully set and the previous non forceful request is cancelled. The active - * non forceful setup will be canceled in favour of new non forceful setup. - * - * @param friend_number The friend number of the friend for which to set the - * video bit rate. - * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable - * video sending. - * @param force True if the bit rate change is forceful. - * - */ -bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); +void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data); + + /******************************************************************************* * * :: A/V sending @@ -554,6 +585,7 @@ typedef enum TOXAV_ERR_SEND_FRAME { */ TOXAV_ERR_SEND_FRAME_RTP_FAILED, } TOXAV_ERR_SEND_FRAME; + /** * Send an audio frame to a friend. * @@ -574,7 +606,10 @@ typedef enum TOXAV_ERR_SEND_FRAME { * @param sampling_rate Audio sampling rate used in this frame. Valid sampling * rates are 8000, 12000, 16000, 24000, or 48000. */ -bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error); +bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, + size_t sample_count, uint8_t channels, uint32_t sampling_rate, + TOXAV_ERR_SEND_FRAME *error); + /** * Send a video frame to a friend. * @@ -590,7 +625,11 @@ bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t * @param u U (Chroma) plane data. * @param v V (Chroma) plane data. */ -bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error); +bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, + uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, + TOXAV_ERR_SEND_FRAME *error); + + /******************************************************************************* * * :: A/V receiving @@ -600,7 +639,7 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width * The function type for the audio_receive_frame callback. The callback can be * called multiple times per single iteration depending on the amount of queued * frames in the buffer. The received format is the same as in send function. - * + * * @param friend_number The friend number of the friend who sent an audio frame. * @param pcm An array of audio samples (sample_count * channels elements). * @param sample_count The number of audio samples per channel in the PCM array. @@ -608,12 +647,16 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width * @param sampling_rate Sampling rate used in this frame. * */ -typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void *user_data); +typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, + size_t sample_count, uint8_t channels, uint32_t sampling_rate, + void *user_data); + /** * Set the callback for the `audio_receive_frame` event. Pass NULL to unset. * */ void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data); + /** * The function type for the video_receive_frame callback. * @@ -635,60 +678,17 @@ void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_ * image is bottom-up hence why you MUST abs() it when * calculating plane buffer size. */ -typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); +typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, + uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, + int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); + /** * Set the callback for the `video_receive_frame` event. Pass NULL to unset. * */ void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data); -/** - * NOTE Compatibility with old toxav group calls TODO remove - */ -/* Create a new toxav group. - * - * return group number on success. - * return -1 on failure. - * - * Audio data callback format: - * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) - * - * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). - */ -int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, - unsigned int, void *), void *userdata); - -/* Join a AV group (you need to have been invited first.) - * - * returns group number on success - * returns -1 on failure. - * - * Audio data callback format (same as the one for toxav_add_av_groupchat()): - * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) - * - * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). - */ -int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, - void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); - -/* Send audio to the group chat. - * - * return 0 on success. - * return -1 on failure. - * - * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). - * - * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) - * Valid number of channels are 1 or 2. - * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. - * - * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 - */ -int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, - unsigned int sample_rate); - #ifdef __cplusplus } #endif - #endif /* TOXAV_H */ diff --git a/toxav/toxav_old.c b/toxav/toxav_old.c index 61c2f020..7d7e5e7b 100644 --- a/toxav/toxav_old.c +++ b/toxav/toxav_old.c @@ -1,5 +1,5 @@ /* toxav_old.h - * + * * Copyright (C) 2013-2015 Tox project All Rights Reserved. * * This file is part of Tox. @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU General Public License * along with Tox. If not, see . - * + * */ /** * This file contains the group chats code for the backwards compatibility. diff --git a/toxav/video.c b/toxav/video.c index 389d2e1c..919e3c81 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -19,6 +19,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + #include #include @@ -29,281 +33,166 @@ #include "../toxcore/logger.h" #include "../toxcore/network.h" -/* Good quality encode. */ -#define MAX_DECODE_TIME_US 0 - -#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ -#define VIDEOFRAME_HEADER_SIZE 0x2 - +#define MAX_DECODE_TIME_US 0 /* Good quality encode. */ #define VIDEO_DECODE_BUFFER_SIZE 20 -typedef struct { uint16_t size; uint8_t data[]; } Payload; -bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate); +bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate); - -VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data, uint32_t mvfpsz) +VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data) { VCSession *vc = calloc(sizeof(VCSession), 1); - + if (!vc) { LOGGER_WARNING("Allocation failed! Application might misbehave!"); return NULL; } - + if (create_recursive_mutex(vc->queue_mutex) != 0) { LOGGER_WARNING("Failed to create recursive mutex!"); free(vc); return NULL; } - - if ( !(vc->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) ) - goto BASE_CLEANUP; - if ( !(vc->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) ) - goto BASE_CLEANUP; - if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) ) + + if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE))) goto BASE_CLEANUP; - - int rc = vpx_codec_dec_init_ver(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, - NULL, 0, VPX_DECODER_ABI_VERSION); - if ( rc != VPX_CODEC_OK) { + + int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0); + + if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); goto BASE_CLEANUP; } - + if (!create_video_encoder(vc->encoder, 500000)) { vpx_codec_destroy(vc->decoder); goto BASE_CLEANUP; } - if (!create_video_encoder(vc->test_encoder, 500000)) { - vpx_codec_destroy(vc->encoder); - vpx_codec_destroy(vc->decoder); - goto BASE_CLEANUP; - } - + vc->linfts = current_time_monotonic(); vc->lcfd = 60; vc->vcb.first = cb; vc->vcb.second = cb_data; vc->friend_number = friend_number; - vc->peer_video_frame_piece_size = mvfpsz; vc->av = av; - + return vc; - + BASE_CLEANUP: pthread_mutex_destroy(vc->queue_mutex); - rb_free(vc->vbuf_raw); - free(vc->split_video_frame); - free(vc->frame_buf); + rb_kill(vc->vbuf_raw); free(vc); return NULL; } -void vc_kill(VCSession* vc) +void vc_kill(VCSession *vc) { if (!vc) return; - + vpx_codec_destroy(vc->encoder); - vpx_codec_destroy(vc->test_encoder); vpx_codec_destroy(vc->decoder); - rb_free(vc->vbuf_raw); - free(vc->split_video_frame); - free(vc->frame_buf); - + + void *p; + + while (rb_read(vc->vbuf_raw, (void **)&p)) + free(p); + + rb_kill(vc->vbuf_raw); + pthread_mutex_destroy(vc->queue_mutex); - + LOGGER_DEBUG("Terminated video handler: %p", vc); free(vc); } -void vc_do(VCSession* vc) +void vc_iterate(VCSession *vc) { if (!vc) return; - - Payload *p; + + struct RTPMessage *p; int rc; - + pthread_mutex_lock(vc->queue_mutex); - if (rb_read(vc->vbuf_raw, (void**)&p)) { + + if (rb_read(vc->vbuf_raw, (void **)&p)) { pthread_mutex_unlock(vc->queue_mutex); - - rc = vpx_codec_decode(vc->decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); + + rc = vpx_codec_decode(vc->decoder, p->data, p->len, NULL, MAX_DECODE_TIME_US); free(p); - - if (rc != VPX_CODEC_OK) { + + if (rc != VPX_CODEC_OK) LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); - } else { + else { vpx_codec_iter_t iter = NULL; vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter); - + /* Play decoded images */ for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) { - if (vc->vcb.first) - vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h, - (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2], + if (vc->vcb.first) + vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h, + (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2], dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second); - + vpx_img_free(dest); } } - - return; - } - pthread_mutex_unlock(vc->queue_mutex); -} -void vc_init_video_splitter_cycle(VCSession* vc) -{ - if (!vc) - return; - - vc->split_video_frame[0] = vc->frameid_out++; - vc->split_video_frame[1] = 0; -} -int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length) -{ - if (!vc) - return 0; - - vc->processing_video_frame = payload; - vc->processing_video_frame_size = length; - - return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1; -} -const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size) -{ - if (!vc || !size) - return NULL; - if (vc->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) { - memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE, - vc->processing_video_frame, - VIDEOFRAME_PIECE_SIZE); - - vc->processing_video_frame += VIDEOFRAME_PIECE_SIZE; - vc->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE; - - *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE; - } else { - memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE, - vc->processing_video_frame, - vc->processing_video_frame_size); - - *size = vc->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; + return; } - vc->split_video_frame[1]++; - - return vc->split_video_frame; + pthread_mutex_unlock(vc->queue_mutex); } -int vc_queue_message(void* vcp, struct RTPMessage_s *msg) +int vc_queue_message(void *vcp, struct RTPMessage *msg) { - /* This function does the reconstruction of video packets. + /* This function does the reconstruction of video packets. * See more info about video splitting in docs */ if (!vcp || !msg) return -1; - - if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeVideo + 2) % 128) { + + if (msg->header.pt == (rtp_TypeVideo + 2) % 128) { LOGGER_WARNING("Got dummy!"); - rtp_free_msg(msg); + free(msg); return 0; } - - if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) { + + if (msg->header.pt != rtp_TypeVideo % 128) { LOGGER_WARNING("Invalid payload type!"); - rtp_free_msg(msg); + free(msg); return -1; } - - VCSession* vc = vcp; - - uint8_t *packet = msg->data; - uint32_t packet_size = msg->length; - - if (packet_size < VIDEOFRAME_HEADER_SIZE) - goto end; - - uint8_t diff = packet[0] - vc->frameid_in; - - if (diff != 0) { - if (diff < 225) { /* New frame */ - /* Flush last frames' data and get ready for this frame */ - Payload *p = malloc(sizeof(Payload) + vc->frame_size); - - if (p) { - pthread_mutex_lock(vc->queue_mutex); - - if (rb_full(vc->vbuf_raw)) { - LOGGER_DEBUG("Dropped video frame"); - Payload *tp; - rb_read(vc->vbuf_raw, (void**)&tp); - free(tp); - } else { - p->size = vc->frame_size; - memcpy(p->data, vc->frame_buf, vc->frame_size); - } - - /* Calculate time took for peer to send us this frame */ - uint32_t t_lcfd = current_time_monotonic() - vc->linfts; - vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd; - vc->linfts = current_time_monotonic(); - - rb_write(vc->vbuf_raw, p); - pthread_mutex_unlock(vc->queue_mutex); - } else { - LOGGER_WARNING("Allocation failed! Program might misbehave!"); - goto end; - } - vc->frameid_in = packet[0]; - memset(vc->frame_buf, 0, vc->frame_size); - vc->frame_size = 0; + VCSession *vc = vcp; - } else { /* Old frame; drop */ - LOGGER_DEBUG("Old packet: %u", packet[0]); - goto end; - } + pthread_mutex_lock(vc->queue_mutex); + free(rb_write(vc->vbuf_raw, msg)); + { + /* Calculate time took for peer to send us this frame */ + uint32_t t_lcfd = current_time_monotonic() - vc->linfts; + vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd; + vc->linfts = current_time_monotonic(); } + pthread_mutex_unlock(vc->queue_mutex); - uint8_t piece_number = packet[1]; - - uint32_t length_before_piece = ((piece_number - 1) * vc->peer_video_frame_piece_size); - uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE); - - if (framebuf_new_length > MAX_VIDEOFRAME_SIZE) - goto end; - - - /* Otherwise it's part of the frame so just process */ - /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */ - - memcpy(vc->frame_buf + length_before_piece, - packet + VIDEOFRAME_HEADER_SIZE, - packet_size - VIDEOFRAME_HEADER_SIZE); - - if (framebuf_new_length > vc->frame_size) - vc->frame_size = framebuf_new_length; - -end: - rtp_free_msg(msg); return 0; } -int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height) +int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height) { if (!vccdc) return -1; - + vpx_codec_enc_cfg_t cfg = *vccdc->config.enc; + if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) return 0; /* Nothing changed */ - + cfg.rc_target_bitrate = bit_rate; cfg.g_w = width; cfg.g_h = height; - + int rc = vpx_codec_enc_config_set(vccdc, &cfg); - if ( rc != VPX_CODEC_OK) { + + if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); return -1; } @@ -312,42 +201,43 @@ int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t w } -bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate) +bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate) { assert(dest); - + 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 false; } - + cfg.rc_target_bitrate = bit_rate; - cfg.g_w = 4000; - cfg.g_h = 4000; + cfg.g_w = 800; + cfg.g_h = 600; cfg.g_pass = VPX_RC_ONE_PASS; - cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; + /* FIXME If we set error resilience the app will crash due to bug in vp8. + Perhaps vp9 has solved it?*/ +// 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 = 48; cfg.kf_mode = VPX_KF_AUTO; - - rc = vpx_codec_enc_init_ver(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, - VPX_ENCODER_ABI_VERSION); - - if ( rc != VPX_CODEC_OK) { + + rc = vpx_codec_enc_init(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); + + if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); return false; } - + rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8); - - if ( rc != VPX_CODEC_OK) { + + if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); vpx_codec_destroy(dest); } - + return true; -} \ No newline at end of file +} diff --git a/toxav/video.h b/toxav/video.h index ac165df6..1ad1f6f5 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -36,77 +36,32 @@ #include "../toxcore/util.h" -struct RTPMessage_s; +struct RTPMessage; -/* - * Base Video Codec session type. - */ typedef struct VCSession_s { - /* encoding */ vpx_codec_ctx_t encoder[1]; - vpx_codec_ctx_t test_encoder[1]; uint32_t frame_counter; - uint32_t test_frame_counter; /* decoding */ vpx_codec_ctx_t decoder[1]; - void *vbuf_raw; /* Un-decoded data */ + void *vbuf_raw; /* Un-decoded data */ - /* Data handling */ - uint8_t *frame_buf; /* buffer for split video payloads */ - uint32_t frame_size; /* largest address written to in frame_buf for current input frame */ - uint8_t frameid_in, frameid_out; /* id of input and output video frame */ uint64_t linfts; /* Last received frame time stamp */ uint32_t lcfd; /* Last calculated frame duration for incoming video payload */ - - /* Limits */ - uint32_t peer_video_frame_piece_size; - /* Splitting */ - uint8_t *split_video_frame; - const uint8_t *processing_video_frame; - uint16_t processing_video_frame_size; - ToxAV *av; uint32_t friend_number; - + PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */ - + pthread_mutex_t queue_mutex[1]; } VCSession; -/* - * Create new Video Codec session. - */ -VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data, uint32_t mvfpsz); -/* - * Kill the Video Codec session. - */ -void vc_kill(VCSession* vc); -/* - * Do periodic work. Work is consisted out of decoding only. - */ -void vc_do(VCSession* vc); -/* - * Set new video splitting cycle. This is requirement in order to send video packets. - */ -void vc_init_video_splitter_cycle(VCSession* vc); -/* - * Update the video splitter cycle with new data. - */ -int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length); -/* - * Iterate over splitted cycle. - */ -const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size); -/* - * Queue new rtp message. - */ -int vc_queue_message(void *vcp, struct RTPMessage_s *msg); -/* - * Set new values to the encoders. - */ -int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height); +VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data); +void vc_kill(VCSession *vc); +void vc_iterate(VCSession *vc); +int vc_queue_message(void *vcp, struct RTPMessage *msg); +int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height); -#endif /* VIDEO_H */ \ No newline at end of file +#endif /* VIDEO_H */ diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index 4277f16a..4cd9e1d6 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -2239,7 +2239,7 @@ static void connection_status_cb(Messenger *m) } -#ifdef LOGGING +#ifdef TOX_LOGGER #define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL static time_t lastdump = 0; static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1]; @@ -2315,7 +2315,7 @@ void do_messenger(Messenger *m) do_friends(m); connection_status_cb(m); -#ifdef LOGGING +#ifdef TOX_LOGGER if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { @@ -2414,7 +2414,7 @@ void do_messenger(Messenger *m) } } -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ } /* new messenger format for load/save, more robust and forward compatible */ diff --git a/toxcore/assoc.c b/toxcore/assoc.c index 44c4cc30..932adc76 100644 --- a/toxcore/assoc.c +++ b/toxcore/assoc.c @@ -878,9 +878,9 @@ void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id) } } -#ifdef LOGGING +#ifdef TOX_LOGGER static char *idpart2str(uint8_t *id, size_t len); -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ /* refresh buckets */ void do_Assoc(Assoc *assoc, DHT *dht) @@ -974,7 +974,7 @@ void kill_Assoc(Assoc *assoc) } } -#ifdef LOGGING +#ifdef TOX_LOGGER static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; static char *idpart2str(uint8_t *id, size_t len) @@ -1028,4 +1028,4 @@ void Assoc_status(const Assoc *assoc) } } -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ diff --git a/toxcore/assoc.h b/toxcore/assoc.h index 1b4e1ff9..65a2745d 100644 --- a/toxcore/assoc.h +++ b/toxcore/assoc.h @@ -97,8 +97,8 @@ void do_Assoc(Assoc *assoc, DHT *dht); /* destroy */ void kill_Assoc(Assoc *assoc); -#ifdef LOGGING +#ifdef TOX_LOGGER void Assoc_status(const Assoc *assoc); -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ #endif /* !__ASSOC_H__ */ diff --git a/toxcore/logger.c b/toxcore/logger.c index fc6a989a..f19f76b1 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c @@ -44,7 +44,7 @@ #endif -struct logger { +struct Logger { FILE *log_file; LOG_LEVEL level; uint64_t start_time; /* Time when lib loaded */ @@ -87,7 +87,7 @@ char *strtime(char *dest, size_t max_len) */ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) { -#ifndef LOGGING /* Disabled */ +#ifndef TOX_LOGGER /* Disabled */ return NULL; #endif @@ -96,7 +96,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) if (!retu) return NULL; - if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) { + if (pthread_mutex_init(retu->mutex, NULL) != 0) { free(retu); return NULL; } @@ -110,7 +110,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) if (!(retu->tstr = calloc(16, sizeof (char))) || !(retu->posstr = calloc(300, sizeof (char))) || - !(retu->msg = calloc(4096, sizeof (char))) ) + !(retu->msg = calloc(4096, sizeof (char)))) goto FAILURE; if (id) { @@ -147,7 +147,7 @@ FAILURE: void logger_kill(Logger *log) { -#ifndef LOGGING /* Disabled */ +#ifndef TOX_LOGGER /* Disabled */ return; #endif @@ -160,7 +160,7 @@ void logger_kill(Logger *log) free(log->posstr); free(log->msg); - if (fclose(log->log_file) != 0 ) + if (fclose(log->log_file) != 0) perror("Could not close log file"); pthread_mutex_unlock(log->mutex); @@ -177,7 +177,7 @@ void logger_kill_global(void) void logger_set_global(Logger *log) { -#ifndef LOGGING /* Disabled */ +#ifndef TOX_LOGGER /* Disabled */ return; #endif @@ -186,7 +186,7 @@ void logger_set_global(Logger *log) Logger *logger_get_global(void) { -#ifndef LOGGING /* Disabled */ +#ifndef TOX_LOGGER /* Disabled */ return NULL; #endif @@ -195,7 +195,7 @@ Logger *logger_get_global(void) void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...) { -#ifndef LOGGING /* Disabled */ +#ifndef TOX_LOGGER /* Disabled */ return; #endif diff --git a/toxcore/logger.h b/toxcore/logger.h index 0513b32c..4d3e3b54 100644 --- a/toxcore/logger.h +++ b/toxcore/logger.h @@ -43,7 +43,7 @@ typedef enum { LOG_ERROR } LOG_LEVEL; -typedef struct logger Logger; +typedef struct Logger Logger; /** * Set 'level' as the lowest printable level. If id == NULL, random number is used. @@ -66,21 +66,22 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con /* To do some checks or similar only when logging, use this */ -#ifdef LOGGING +#ifdef TOX_LOGGER # define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0) # define LOGGER_WRITE(log, level, format, ...) \ - logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__ ) + logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__) #else +/* # warning "Logging disabled" */ # define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0) # define LOGGER_WRITE(log, level, format, ...) do {} while(0) -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ /* To log with an logger */ -#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__ ) -#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__ ) -#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__ ) -#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__ ) -#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__ ) +#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__) +#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__) +#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__) +#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__) +#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__) /* To log with the global logger */ #define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__) diff --git a/toxcore/network.c b/toxcore/network.c index 22ee4202..965e65f9 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -266,7 +266,7 @@ uint64_t current_time_monotonic(void) } /* In case no logging */ -#ifndef LOGGING +#ifndef TOX_LOGGER #define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__) #else #define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0 @@ -287,7 +287,7 @@ uint64_t current_time_monotonic(void) __buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \ ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__)); -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ /* Basic network functions: * Function to send packet(data) of length length to ip_port. @@ -615,9 +615,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, } if (ip.family == AF_INET6) { -#ifdef LOGGING +#ifdef TOX_LOGGER int is_dualstack = -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ set_socket_dualstack(temp->sock); LOGGER_DEBUG( "Dual-stack socket: %s", is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" ); @@ -628,9 +628,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to, mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02; mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; mreq.ipv6mr_interface = 0; -#ifdef LOGGING +#ifdef TOX_LOGGER int res = -#endif /* LOGGING */ +#endif /* TOX_LOGGER */ setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" : diff --git a/toxcore/util.c b/toxcore/util.c index 5865a172..81fa84c6 100644 --- a/toxcore/util.c +++ b/toxcore/util.c @@ -234,14 +234,6 @@ bool rb_read(RingBuffer *b, void **p) b->start = (b->start + 1) % b->size; return true; } -void rb_clear(RingBuffer *b) -{ - while (!rb_empty(b)) { - void *p; - rb_read(b, &p); - free(p); - } -} RingBuffer *rb_new(int size) { RingBuffer *buf = calloc(sizeof(RingBuffer), 1); @@ -257,11 +249,28 @@ RingBuffer *rb_new(int size) return buf; } -void rb_free(RingBuffer *b) +void rb_kill(RingBuffer *b) { if (b) { - rb_clear(b); free(b->data); free(b); } } +uint16_t rb_size(const RingBuffer* b) +{ + if (rb_empty(b)) + return 0; + + return + b->end > b->start ? + b->end - b->start : + (b->size - b->start) + b->end; +} +uint16_t rb_data(const RingBuffer* b, void** dest) +{ + uint16_t i = 0; + for (; i < rb_size(b); i++) + dest[i] = b->data[(b->start + i) % b->size]; + + return i; +} diff --git a/toxcore/util.h b/toxcore/util.h index 7670a80f..7cf63178 100644 --- a/toxcore/util.h +++ b/toxcore/util.h @@ -64,7 +64,9 @@ bool rb_full(const RingBuffer *b); bool rb_empty(const RingBuffer *b); void* rb_write(RingBuffer* b, void* p); bool rb_read(RingBuffer* b, void** p); -void rb_clear(RingBuffer *b); RingBuffer *rb_new(int size); -void rb_free(RingBuffer *b); +void rb_kill(RingBuffer *b); +uint16_t rb_size(const RingBuffer *b); +uint16_t rb_data(const RingBuffer* b, void** dest); + #endif /* __UTIL_H__ */ -- cgit v1.2.3 From caa038287717e6fd47df420660c0854246fbc4c8 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 24 Oct 2015 16:16:42 +0200 Subject: Fix misplaced log message. --- toxav/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'toxav/audio.c') diff --git a/toxav/audio.c b/toxav/audio.c index 3ba95c03..4f9d3562 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -228,7 +228,6 @@ int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rat &ac->le_channel_count)) return -1; - LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); return 0; } @@ -409,6 +408,7 @@ bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, *old_sr = new_sr; *old_ch = new_ch; + LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", new_br, new_sr, new_ch); return true; } bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels) -- cgit v1.2.3 From 6a494e2cbdd146bb13185d8220061322661a5f5a Mon Sep 17 00:00:00 2001 From: irungentoo Date: Tue, 3 Nov 2015 13:42:05 -0500 Subject: Astyle. --- auto_tests/dht_test.c | 6 +- auto_tests/toxav_basic_test.c | 224 ++++++++--------- auto_tests/toxav_many_test.c | 169 ++++++------- testing/av_test.c | 487 +++++++++++++++++++------------------ toxav/audio.c | 10 +- toxav/bwcontroler.c | 62 ++--- toxav/msi.c | 79 +++--- toxav/rtp.c | 27 ++- toxav/toxav.c | 57 +++-- toxav/toxav.h | 541 +++++++++++++++++++++--------------------- toxav/video.c | 23 +- toxav/video.h | 2 +- toxcore/util.c | 26 +- toxcore/util.h | 6 +- toxdns/toxdns.c | 2 +- 15 files changed, 890 insertions(+), 831 deletions(-) (limited to 'toxav/audio.c') diff --git a/auto_tests/dht_test.c b/auto_tests/dht_test.c index 32985bca..8304a046 100644 --- a/auto_tests/dht_test.c +++ b/auto_tests/dht_test.c @@ -153,7 +153,8 @@ void test_addto_lists_bad(DHT *dht, { // check "bad" clients replacement int used, test1, test2, test3; - uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES]; + uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], + test_id3[crypto_box_PUBLICKEYBYTES]; uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; randombytes(public_key, sizeof(public_key)); @@ -196,7 +197,8 @@ void test_addto_lists_possible_bad(DHT *dht, { // check "possibly bad" clients replacement int used, test1, test2, test3; - uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES]; + uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], + test_id3[crypto_box_PUBLICKEYBYTES]; uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; randombytes(public_key, sizeof(public_key)); diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c index 5821a6d4..20432dca 100644 --- a/auto_tests/toxav_basic_test.c +++ b/auto_tests/toxav_basic_test.c @@ -49,12 +49,12 @@ typedef struct { bool incoming; uint32_t state; - + } CallControl; /** - * Callbacks + * Callbacks */ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) { @@ -62,17 +62,17 @@ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool (void) friend_number; (void) audio_enabled; (void) video_enabled; - + printf("Handling CALL callback\n"); - ((CallControl*)user_data)->incoming = true; + ((CallControl *)user_data)->incoming = true; } void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) { (void) av; (void) friend_number; - + printf("Handling CALL STATE callback: %d\n", state); - ((CallControl*)user_data)->state = state; + ((CallControl *)user_data)->state = state; } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, @@ -112,7 +112,7 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { (void) userdata; - + if (length == 7 && memcmp("gentoo", data, 7) == 0) { ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); } @@ -122,12 +122,12 @@ void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t /** * Iterate helper */ -int iterate_tox(Tox* bootstrap, Tox* Alice, Tox* Bob) +int iterate_tox(Tox *bootstrap, Tox *Alice, Tox *Bob) { tox_iterate(bootstrap); tox_iterate(Alice); tox_iterate(Bob); - + return MIN(tox_iteration_interval(Alice), tox_iteration_interval(Bob)); } @@ -135,80 +135,80 @@ int iterate_tox(Tox* bootstrap, Tox* Alice, Tox* Bob) START_TEST(test_AV_flows) { - Tox* Alice, *Bob, *bootstrap; - ToxAV* AliceAV, *BobAV; - + Tox *Alice, *Bob, *bootstrap; + ToxAV *AliceAV, *BobAV; + CallControl AliceCC, BobCC; - + { TOX_ERR_NEW error; - + bootstrap = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Alice = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Bob = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); } - + printf("Created 3 instances of Tox\n"); printf("Preparing network...\n"); long long unsigned int cur_time = time(NULL); - + uint32_t to_compare = 974536; uint8_t address[TOX_ADDRESS_SIZE]; - + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); tox_self_get_address(Alice, address); - - + + ck_assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); - + uint8_t off = 1; - + while (1) { iterate_tox(bootstrap, Alice, Bob); - - if (tox_self_get_connection_status(bootstrap) && - tox_self_get_connection_status(Alice) && - tox_self_get_connection_status(Bob) && off) { + + if (tox_self_get_connection_status(bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bob) && off) { printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); off = 0; } - - if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) + + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) break; - + c_sleep(20); } - - + + { TOXAV_ERR_NEW error; AliceAV = toxav_new(Alice, &error); ck_assert(error == TOXAV_ERR_NEW_OK); - + BobAV = toxav_new(Bob, &error); ck_assert(error == TOXAV_ERR_NEW_OK); } - + toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC); toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC); toxav_callback_video_receive_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC); toxav_callback_audio_receive_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC); - + toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC); toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC); toxav_callback_video_receive_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC); toxav_callback_audio_receive_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC); - + printf("Created 2 instances of ToxAV\n"); printf("All set after %llu seconds!\n", time(NULL) - cur_time); - - + + #define REGULAR_CALL_FLOW(A_BR, V_BR) \ do { \ memset(&AliceCC, 0, sizeof(CallControl)); \ @@ -256,117 +256,117 @@ START_TEST(test_AV_flows) } \ printf("Success!\n");\ } while(0) - + if (TEST_REGULAR_AV) { printf("\nTrying regular call (Audio and Video)...\n"); REGULAR_CALL_FLOW(48, 4000); } - + if (TEST_REGULAR_A) { printf("\nTrying regular call (Audio only)...\n"); REGULAR_CALL_FLOW(48, 0); } - + if (TEST_REGULAR_V) { printf("\nTrying regular call (Video only)...\n"); REGULAR_CALL_FLOW(0, 4000); } - + #undef REGULAR_CALL_FLOW - + if (TEST_REJECT) { /* Alice calls; Bob rejects */ printf("\nTrying reject flow...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + { TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, Alice, Bob); - + /* Reject */ { TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); ck_assert(0); } } - + while (AliceCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED) iterate_tox(bootstrap, Alice, Bob); - + printf("Success!\n"); } - + if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */ printf("\nTrying cancel (while ringing) flow...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + { TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, Alice, Bob); - + /* Cancel */ { TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); ck_assert(0); } } - + /* Alice will not receive end state */ while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED) iterate_tox(bootstrap, Alice, Bob); - + printf("Success!\n"); } - + if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */ printf("\nTrying mute functionality...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + /* Assume sending audio and video */ { TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 1000, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, Alice, Bob); - + /* At first try all stuff while in invalid state */ ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); @@ -374,19 +374,19 @@ START_TEST(test_AV_flows) ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL)); ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, NULL)); ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL)); - + { TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 48, 4000, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } - + iterate_tox(bootstrap, Alice, Bob); - + /* Pause and Resume */ printf("Pause and Resume\n"); ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); @@ -395,7 +395,7 @@ START_TEST(test_AV_flows) ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state & (TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V)); - + /* Mute/Unmute single */ printf("Mute/Unmute single\n"); ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL)); @@ -404,7 +404,7 @@ START_TEST(test_AV_flows) ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A); - + /* Mute/Unmute both */ printf("Mute/Unmute both\n"); ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL)); @@ -419,122 +419,122 @@ START_TEST(test_AV_flows) ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL)); iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V); - + { TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); ck_assert(0); } } - + iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); - + printf("Success!\n"); } - + if (TEST_STOP_RESUME_PAYLOAD) { /* Stop and resume audio/video payload */ printf("\nTrying stop/resume functionality...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + /* Assume sending audio and video */ { TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, Alice, Bob); - + { TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } - + iterate_tox(bootstrap, Alice, Bob); - + printf("Call started as audio only\n"); printf("Turning on video for Alice...\n"); ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL)); - + iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V); - + printf("Turning off video for Alice...\n"); ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL)); - + iterate_tox(bootstrap, Alice, Bob); ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)); - + printf("Turning off audio for Alice...\n"); ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL)); - + iterate_tox(bootstrap, Alice, Bob); ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A)); - + { TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); ck_assert(0); } } - + iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); - + printf("Success!\n"); } - + if (TEST_PAUSE_RESUME_SEND) { /* Stop and resume audio/video payload and test send options */ printf("\nTrying stop/resume functionality...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + /* Assume sending audio and video */ { TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, Alice, Bob); - + { TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } - + int16_t PCM[5670]; - + iterate_tox(bootstrap, Alice, Bob); ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); iterate_tox(bootstrap, Alice, Bob); @@ -545,29 +545,29 @@ START_TEST(test_AV_flows) ck_assert(toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, NULL)); ck_assert(toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL)); iterate_tox(bootstrap, Alice, Bob); - + { TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); ck_assert(0); } } - + iterate_tox(bootstrap, Alice, Bob); ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); - + printf("Success!\n"); } - + toxav_kill(BobAV); toxav_kill(AliceAV); tox_kill(Bob); tox_kill(Alice); tox_kill(bootstrap); - + printf("\nTest successful!\n"); } END_TEST @@ -577,7 +577,7 @@ int main(int argc, char *argv[]) { (void) argc; (void) argv; - + test_AV_flows(); return 0; } @@ -593,7 +593,7 @@ int main(int argc, char *argv[]) { (void) argc; (void) argv; - + Suite *tox = tox_suite(); SRunner *test_runner = srunner_create(tox); diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c index 9819b694..ab496024 100644 --- a/auto_tests/toxav_many_test.c +++ b/auto_tests/toxav_many_test.c @@ -42,29 +42,29 @@ typedef struct { } CallControl; typedef struct { - ToxAV* AliceAV; - ToxAV* BobAV; - CallControl* AliceCC; - CallControl* BobCC; + ToxAV *AliceAV; + ToxAV *BobAV; + CallControl *AliceCC; + CallControl *BobCC; uint32_t friend_number; } thread_data; /** - * Callbacks + * Callbacks */ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) { (void) av; (void) audio_enabled; (void) video_enabled; - + printf("Handling CALL callback\n"); - ((CallControl*)user_data)[friend_number].incoming = true; + ((CallControl *)user_data)[friend_number].incoming = true; } void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) { printf("Handling CALL STATE callback: %d %p\n", state, av); - ((CallControl*)user_data)[friend_number].state = state; + ((CallControl *)user_data)[friend_number].state = state; } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, @@ -102,6 +102,7 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) { (void) userdata; + if (length == 7 && memcmp("gentoo", data, 7) == 0) { ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0); } @@ -111,92 +112,93 @@ void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t /** * Iterate helper */ -ToxAV* setup_av_instance(Tox* tox, CallControl *CC) +ToxAV *setup_av_instance(Tox *tox, CallControl *CC) { TOXAV_ERR_NEW error; - - ToxAV* av = toxav_new(tox, &error); + + ToxAV *av = toxav_new(tox, &error); ck_assert(error == TOXAV_ERR_NEW_OK); - + toxav_callback_call(av, t_toxav_call_cb, CC); toxav_callback_call_state(av, t_toxav_call_state_cb, CC); toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC); toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC); - + return av; } -void* call_thread(void* pd) +void *call_thread(void *pd) { - ToxAV* AliceAV = ((thread_data*) pd)->AliceAV; - ToxAV* BobAV = ((thread_data*) pd)->BobAV; - CallControl* AliceCC = ((thread_data*) pd)->AliceCC; - CallControl* BobCC = ((thread_data*) pd)->BobCC; - uint32_t friend_number = ((thread_data*) pd)->friend_number; - - + ToxAV *AliceAV = ((thread_data *) pd)->AliceAV; + ToxAV *BobAV = ((thread_data *) pd)->BobAV; + CallControl *AliceCC = ((thread_data *) pd)->AliceCC; + CallControl *BobCC = ((thread_data *) pd)->BobCC; + uint32_t friend_number = ((thread_data *) pd)->friend_number; + + memset(AliceCC, 0, sizeof(CallControl)); memset(BobCC, 0, sizeof(CallControl)); - + { /* Call */ TOXAV_ERR_CALL rc; toxav_call(AliceAV, friend_number, 48, 3000, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); ck_assert(0); } } - + while (!BobCC->incoming) c_sleep(10); - + { /* Answer */ TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 8, 500, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); ck_assert(0); } } - + c_sleep(30); - + int16_t PCM[960]; - uint8_t video_y[800*600]; - uint8_t video_u[800*600 / 2]; - uint8_t video_v[800*600 / 2]; - + uint8_t video_y[800 * 600]; + uint8_t video_u[800 * 600 / 2]; + uint8_t video_v[800 * 600 / 2]; + memset(PCM, 0, sizeof(PCM)); memset(video_y, 0, sizeof(video_y)); memset(video_u, 0, sizeof(video_u)); memset(video_v, 0, sizeof(video_v)); - + time_t start_time = time(NULL); - while(time(NULL) - start_time < 4) { + + while (time(NULL) - start_time < 4) { toxav_iterate(AliceAV); toxav_iterate(BobAV); - + toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL); toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL); - + toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL); toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL); - + c_sleep(10); } - + { /* Hangup */ TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d %p %p\n", rc, AliceAV, BobAV); } } - + c_sleep(30); - + printf ("Closing thread\n"); pthread_exit(NULL); } @@ -204,111 +206,112 @@ void* call_thread(void* pd) START_TEST(test_AV_three_calls) { - Tox* Alice, *bootstrap, *Bobs[3]; - ToxAV* AliceAV, *BobsAV[3]; - + Tox *Alice, *bootstrap, *Bobs[3]; + ToxAV *AliceAV, *BobsAV[3]; + CallControl AliceCC[3], BobsCC[3]; - + { TOX_ERR_NEW error; - + bootstrap = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Alice = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Bobs[0] = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Bobs[1] = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); - + Bobs[2] = tox_new(NULL, &error); ck_assert(error == TOX_ERR_NEW_OK); } - + printf("Created 5 instances of Tox\n"); printf("Preparing network...\n"); long long unsigned int cur_time = time(NULL); - + uint32_t to_compare = 974536; uint8_t address[TOX_ADDRESS_SIZE]; - + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); tox_self_get_address(Alice, address); - - + + ck_assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); ck_assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); ck_assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); - + uint8_t off = 1; - + while (1) { tox_iterate(bootstrap); tox_iterate(Alice); tox_iterate(Bobs[0]); tox_iterate(Bobs[1]); tox_iterate(Bobs[2]); - - if (tox_self_get_connection_status(bootstrap) && - tox_self_get_connection_status(Alice) && - tox_self_get_connection_status(Bobs[0]) && - tox_self_get_connection_status(Bobs[1]) && - tox_self_get_connection_status(Bobs[2]) && off) { + + if (tox_self_get_connection_status(bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bobs[0]) && + tox_self_get_connection_status(Bobs[1]) && + tox_self_get_connection_status(Bobs[2]) && off) { printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); off = 0; } - + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP) + tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP) break; - + c_sleep(20); } - + AliceAV = setup_av_instance(Alice, AliceCC); BobsAV[0] = setup_av_instance(Bobs[0], BobsCC + 0); BobsAV[1] = setup_av_instance(Bobs[1], BobsCC + 1); BobsAV[2] = setup_av_instance(Bobs[2], BobsCC + 2); - + printf("Created 4 instances of ToxAV\n"); printf("All set after %llu seconds!\n", time(NULL) - cur_time); - + thread_data tds[3]; tds[0].AliceAV = AliceAV; tds[0].BobAV = BobsAV[0]; tds[0].AliceCC = AliceCC + 0; tds[0].BobCC = BobsCC + 0; tds[0].friend_number = 0; - + tds[1].AliceAV = AliceAV; tds[1].BobAV = BobsAV[1]; tds[1].AliceCC = AliceCC + 1; tds[1].BobCC = BobsCC + 1; tds[1].friend_number = 1; - + tds[2].AliceAV = AliceAV; tds[2].BobAV = BobsAV[2]; tds[2].AliceCC = AliceCC + 2; tds[2].BobCC = BobsCC + 2; tds[2].friend_number = 2; - + pthread_t tids[3]; (void) pthread_create(tids + 0, NULL, call_thread, tds + 0); (void) pthread_create(tids + 1, NULL, call_thread, tds + 1); (void) pthread_create(tids + 2, NULL, call_thread, tds + 2); - + (void) pthread_detach(tids[0]); (void) pthread_detach(tids[1]); (void) pthread_detach(tids[2]); - + time_t start_time = time(NULL); + while (time(NULL) - start_time < 5) { tox_iterate(Alice); tox_iterate(Bobs[0]); @@ -316,11 +319,11 @@ START_TEST(test_AV_three_calls) tox_iterate(Bobs[2]); c_sleep(20); } - + (void) pthread_join(tids[0], NULL); (void) pthread_join(tids[1], NULL); (void) pthread_join(tids[2], NULL); - + printf ("Killing all instances\n"); toxav_kill(BobsAV[0]); toxav_kill(BobsAV[1]); @@ -331,7 +334,7 @@ START_TEST(test_AV_three_calls) tox_kill(Bobs[2]); tox_kill(Alice); tox_kill(bootstrap); - + printf("\nTest successful!\n"); } END_TEST @@ -342,7 +345,7 @@ int main(int argc, char *argv[]) { (void) argc; (void) argv; - + test_AV_three_calls(); return 0; } @@ -363,7 +366,7 @@ int main(int argc, char *argv[]) { (void) argc; (void) argv; - + Suite *tox = tox_suite(); SRunner *test_runner = srunner_create(tox); diff --git a/testing/av_test.c b/testing/av_test.c index fa6a831f..4b2fe61d 100644 --- a/testing/av_test.c +++ b/testing/av_test.c @@ -16,8 +16,8 @@ * * You should have received a copy of the GNU General Public License * along with Tox. If not, see . - * - * Compile with (Linux only; in newly created directory toxcore/dir_name): + * + * Compile with (Linux only; in newly created directory toxcore/dir_name): * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \ * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio */ @@ -75,35 +75,36 @@ typedef struct { bool incoming; uint32_t state; pthread_mutex_t arb_mutex[1]; - RingBuffer* arb; /* Audio ring buffer */ - + RingBuffer *arb; /* Audio ring buffer */ + } CallControl; struct toxav_thread_data { - ToxAV* AliceAV; - ToxAV* BobAV; + ToxAV *AliceAV; + ToxAV *BobAV; int32_t sig; }; -const char* vdout = "AV Test"; /* Video output */ -PaStream* adout = NULL; /* Audio output */ +const char *vdout = "AV Test"; /* Video output */ +PaStream *adout = NULL; /* Audio output */ typedef struct { uint16_t size; int16_t data[]; } frame; -void* pa_write_thread (void* d) +void *pa_write_thread (void *d) { - /* The purpose of this thread is to make sure Pa_WriteStream will not block + /* The purpose of this thread is to make sure Pa_WriteStream will not block * toxav_iterate thread */ - CallControl* cc = d; - + CallControl *cc = d; + while (Pa_IsStreamActive(adout)) { - frame* f; + frame *f; pthread_mutex_lock(cc->arb_mutex); - if (rb_read(cc->arb, (void**)&f)) { + + if (rb_read(cc->arb, (void **)&f)) { pthread_mutex_unlock(cc->arb_mutex); Pa_WriteStream(adout, f->data, f->size); free(f); @@ -115,17 +116,17 @@ void* pa_write_thread (void* d) } /** - * Callbacks + * Callbacks */ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) { printf("Handling CALL callback\n"); - ((CallControl*)user_data)->incoming = true; + ((CallControl *)user_data)->incoming = true; } void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) { printf("Handling CALL STATE callback: %d\n", state); - ((CallControl*)user_data)->state = state; + ((CallControl *)user_data)->state = state; } void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, @@ -136,30 +137,31 @@ void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, ystride = abs(ystride); ustride = abs(ustride); vstride = abs(vstride); - + uint16_t *img_data = malloc(height * width * 6); - + unsigned long int i, j; + for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { - uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j); + uint8_t *point = (uint8_t *) img_data + 3 * ((i * width) + j); int yx = y[(i * ystride) + j]; int ux = u[((i / 2) * ustride) + (j / 2)]; int vx = v[((i / 2) * vstride) + (j / 2)]; - + point[0] = YUV2R(yx, ux, vx); point[1] = YUV2G(yx, ux, vx); point[2] = YUV2B(yx, ux, vx); } } - - + + CvMat mat = cvMat(height, width, CV_8UC3, img_data); - + CvSize sz = {.height = height, .width = width}; - - IplImage* header = cvCreateImageHeader(sz, 1, 3); - IplImage* img = cvGetImage(&mat, header); + + IplImage *header = cvCreateImageHeader(sz, 1, 3); + IplImage *img = cvGetImage(&mat, header); cvShowImage(vdout, img); free(img_data); } @@ -170,16 +172,16 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, uint32_t sampling_rate, void *user_data) { - CallControl* cc = user_data; - frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels); + CallControl *cc = user_data; + frame *f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels); memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels); f->size = sample_count; - + pthread_mutex_lock(cc->arb_mutex); free(rb_write(cc->arb, f)); pthread_mutex_unlock(cc->arb_mutex); } -void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, +void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data) { @@ -194,156 +196,159 @@ void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t /** */ -void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC) +void initialize_tox(Tox **bootstrap, ToxAV **AliceAV, CallControl *AliceCC, ToxAV **BobAV, CallControl *BobCC) { - Tox* Alice; - Tox* Bob; - + Tox *Alice; + Tox *Bob; + struct Tox_Options opts; tox_options_default(&opts); - + opts.end_port = 0; opts.ipv6_enabled = false; - + { TOX_ERR_NEW error; - + opts.start_port = 33445; *bootstrap = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); - + opts.start_port = 33455; Alice = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); - + opts.start_port = 33465; Bob = tox_new(&opts, &error); assert(error == TOX_ERR_NEW_OK); } - + printf("Created 3 instances of Tox\n"); printf("Preparing network...\n"); long long unsigned int cur_time = time(NULL); - + uint32_t to_compare = 974536; uint8_t address[TOX_ADDRESS_SIZE]; - + tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare); tox_self_get_address(Alice, address); - - + + assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0); - + uint8_t off = 1; - + while (1) { tox_iterate(*bootstrap); tox_iterate(Alice); tox_iterate(Bob); - - if (tox_self_get_connection_status(*bootstrap) && - tox_self_get_connection_status(Alice) && - tox_self_get_connection_status(Bob) && off) { + + if (tox_self_get_connection_status(*bootstrap) && + tox_self_get_connection_status(Alice) && + tox_self_get_connection_status(Bob) && off) { printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); off = 0; } - - if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && - tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) + + if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP && + tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP) break; - + c_sleep(20); } - - + + TOXAV_ERR_NEW rc; *AliceAV = toxav_new(Alice, &rc); assert(rc == TOXAV_ERR_NEW_OK); - + *BobAV = toxav_new(Bob, &rc); assert(rc == TOXAV_ERR_NEW_OK); - - + + /* Alice */ toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC); toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); - + /* Bob */ toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC); toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); - - + + printf("Created 2 instances of ToxAV\n"); printf("All set after %llu seconds!\n", time(NULL) - cur_time); } -int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV) +int iterate_tox(Tox *bootstrap, ToxAV *AliceAV, ToxAV *BobAV) { tox_iterate(bootstrap); tox_iterate(toxav_get_tox(AliceAV)); tox_iterate(toxav_get_tox(BobAV)); - + return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV))); } -void* iterate_toxav (void * data) +void *iterate_toxav (void *data) { - struct toxav_thread_data* data_cast = data; + struct toxav_thread_data *data_cast = data; #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE); #endif - + while (data_cast->sig == 0) { toxav_iterate(data_cast->AliceAV); toxav_iterate(data_cast->BobAV); int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); - + printf("\rIteration interval: %d ", rc); fflush(stdout); - + #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 + if (!rc) rc = 1; - + cvWaitKey(rc); #else c_sleep(rc); #endif } - + data_cast->sig = 1; - + #if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 cvDestroyWindow(vdout); #endif - + pthread_exit(NULL); } -int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) -{ +int send_opencv_img(ToxAV *av, uint32_t friend_number, const IplImage *img) +{ int32_t strides[3] = { 1280, 640, 640 }; - uint8_t* planes[3] = { + uint8_t *planes[3] = { malloc(img->height * img->width), malloc(img->height * img->width / 4), malloc(img->height * img->width / 4), }; - + int x_chroma_shift = 1; int y_chroma_shift = 1; - + int x, y; + for (y = 0; y < img->height; ++y) { for (x = 0; x < img->width; ++x) { uint8_t r = img->imageData[(x + y * img->width) * 3 + 0]; uint8_t g = img->imageData[(x + y * img->width) * 3 + 1]; uint8_t b = img->imageData[(x + y * img->width) * 3 + 2]; - + planes[0][x + y * strides[0]] = RGB2Y(r, g, b); + if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) { const int i = x / (1 << x_chroma_shift); const int j = y / (1 << y_chroma_shift); @@ -363,15 +368,17 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img) int print_audio_devices() { int i = 0; + for (i = 0; i < Pa_GetDeviceCount(); ++i) { - const PaDeviceInfo* info = Pa_GetDeviceInfo(i); + const PaDeviceInfo *info = Pa_GetDeviceInfo(i); + if (info) printf("%d) %s\n", i, info->name); } - + return 0; } -int print_help (const char* name) +int print_help (const char *name) { printf("Usage: %s -[a:v:o:dh]\n" "-a audio input file\n" @@ -381,351 +388,379 @@ int print_help (const char* name) "-o output audio device index\n" "-d print output audio devices\n" "-h print this help\n", name); - + return 0; } -int main (int argc, char** argv) -{ +int main (int argc, char **argv) +{ freopen("/dev/zero", "w", stderr); Pa_Initialize(); - + struct stat st; - + /* AV files for testing */ - const char* af_name = NULL; - const char* vf_name = NULL; + const char *af_name = NULL; + const char *vf_name = NULL; long audio_out_dev_idx = -1; - + int32_t audio_frame_duration = 20; int32_t video_frame_duration = 10; - + /* Parse settings */ - CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) { - case 'a': - af_name = optarg; - goto CHECK_ARG; - case 'b':{ - char *d; - audio_frame_duration = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'b'"); - exit(1); +CHECK_ARG: + + switch (getopt(argc, argv, "a:b:v:x:o:dh")) { + case 'a': + af_name = optarg; + goto CHECK_ARG; + + case 'b': { + char *d; + audio_frame_duration = strtol(optarg, &d, 10); + + if (*d) { + printf("Invalid value for argument: 'b'"); + exit(1); + } + + goto CHECK_ARG; } - goto CHECK_ARG; - } - case 'v': - vf_name = optarg; - goto CHECK_ARG; - case 'x':{ - char *d; - video_frame_duration = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'x'"); - exit(1); + + case 'v': + vf_name = optarg; + goto CHECK_ARG; + + case 'x': { + char *d; + video_frame_duration = strtol(optarg, &d, 10); + + if (*d) { + printf("Invalid value for argument: 'x'"); + exit(1); + } + + goto CHECK_ARG; } - goto CHECK_ARG; - } - case 'o': { - char *d; - audio_out_dev_idx = strtol(optarg, &d, 10); - if (*d) { - printf("Invalid value for argument: 'o'"); - exit(1); + + case 'o': { + char *d; + audio_out_dev_idx = strtol(optarg, &d, 10); + + if (*d) { + printf("Invalid value for argument: 'o'"); + exit(1); + } + + goto CHECK_ARG; } - goto CHECK_ARG; - } - case 'd': - return print_audio_devices(); - case 'h': - return print_help(argv[0]); - case '?': - exit(1); - case -1:; + + case 'd': + return print_audio_devices(); + + case 'h': + return print_help(argv[0]); + + case '?': + exit(1); + + case -1: + ; } - + { /* Check files */ if (!af_name) { printf("Required audio input file!\n"); exit(1); } - + if (!vf_name) { printf("Required video input file!\n"); exit(1); } - + /* Check for files */ - if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) - { + if (stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) { printf("%s doesn't seem to be a regular file!\n", af_name); exit(1); } - - if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) - { + + if (stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) { printf("%s doesn't seem to be a regular file!\n", vf_name); exit(1); } } - + if (audio_out_dev_idx < 0) audio_out_dev_idx = Pa_GetDefaultOutputDevice(); - - const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx); + + const PaDeviceInfo *audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx); + if (!audio_dev) { fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx); return 1; } - + printf("Using audio device: %s\n", audio_dev->name); printf("Using audio file: %s\n", af_name); printf("Using video file: %s\n", vf_name); - + /* START TOX NETWORK */ - + Tox *bootstrap; ToxAV *AliceAV; ToxAV *BobAV; - + CallControl AliceCC; CallControl BobCC; - + initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC); - + if (TEST_TRANSFER_A) { - SNDFILE* af_handle; + SNDFILE *af_handle; SF_INFO af_info; - - printf("\nTrying audio enc/dec...\n"); - - memset(&AliceCC, 0, sizeof(CallControl)); + + printf("\nTrying audio enc/dec...\n"); + + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + pthread_mutex_init(AliceCC.arb_mutex, NULL); pthread_mutex_init(BobCC.arb_mutex, NULL); - + AliceCC.arb = rb_new(16); BobCC.arb = rb_new(16); - + { /* Call */ TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); exit(1); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, AliceAV, BobAV); - - { /* Answer */ + + { /* Answer */ TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 48, 0, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); exit(1); } } - + while (AliceCC.state == 0) iterate_tox(bootstrap, AliceAV, BobAV); - + /* Open audio file */ af_handle = sf_open(af_name, SFM_READ, &af_info); + if (af_handle == NULL) { printf("Failed to open the file.\n"); exit(1); } - + int16_t PCM[5760]; - + time_t start_time = time(NULL); time_t expected_time = af_info.frames / af_info.samplerate + 2; - - + + /* Start decode thread */ struct toxav_thread_data data = { .AliceAV = AliceAV, .BobAV = BobAV, .sig = 0 }; - + pthread_t dect; pthread_create(&dect, NULL, iterate_toxav, &data); pthread_detach(dect); - + int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels; - + struct PaStreamParameters output; output.device = audio_out_dev_idx; output.channelCount = af_info.channels; output.sampleFormat = paInt16; output.suggestedLatency = audio_dev->defaultHighOutputLatency; output.hostApiSpecificStreamInfo = NULL; - + PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL); assert(err == paNoError); - + err = Pa_StartStream(adout); assert(err == paNoError); - + // toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL); - + /* Start write thread */ pthread_t t; pthread_create(&t, NULL, pa_write_thread, &BobCC); pthread_detach(t); - + printf("Sample rate %d\n", af_info.samplerate); - while (start_time + expected_time > time(NULL) ) { + + while (start_time + expected_time > time(NULL) ) { uint64_t enc_start_time = current_time_monotonic(); int64_t count = sf_read_short(af_handle, PCM, frame_size); + if (count > 0) { TOXAV_ERR_SEND_FRAME rc; - if (toxav_audio_send_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { + + if (toxav_audio_send_frame(AliceAV, 0, PCM, count / af_info.channels, af_info.channels, af_info.samplerate, + &rc) == false) { printf("Error sending frame of size %ld: %d\n", count, rc); } } + iterate_tox(bootstrap, AliceAV, BobAV); c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1)); - } - - printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time); - + } + + printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time); + Pa_StopStream(adout); sf_close(af_handle); - - { /* Hangup */ + + { /* Hangup */ TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); exit(1); } } - + iterate_tox(bootstrap, AliceAV, BobAV); assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); - + /* Stop decode thread */ data.sig = -1; - while(data.sig != 1) + + while (data.sig != 1) pthread_yield(); - + pthread_mutex_destroy(AliceCC.arb_mutex); pthread_mutex_destroy(BobCC.arb_mutex); - - void* f = NULL; - while(rb_read(AliceCC.arb, &f)) + + void *f = NULL; + + while (rb_read(AliceCC.arb, &f)) free(f); - - while(rb_read(BobCC.arb, &f)) + + while (rb_read(BobCC.arb, &f)) free(f); - - printf("Success!"); - } - - if (TEST_TRANSFER_V) { + + printf("Success!"); + } + + if (TEST_TRANSFER_V) { printf("\nTrying video enc/dec...\n"); - + memset(&AliceCC, 0, sizeof(CallControl)); memset(&BobCC, 0, sizeof(CallControl)); - + { /* Call */ TOXAV_ERR_CALL rc; toxav_call(AliceAV, 0, 0, 2000, &rc); - + if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); exit(1); } } - + while (!BobCC.incoming) iterate_tox(bootstrap, AliceAV, BobAV); - + { /* Answer */ TOXAV_ERR_ANSWER rc; toxav_answer(BobAV, 0, 0, 5000, &rc); - + if (rc != TOXAV_ERR_ANSWER_OK) { printf("toxav_answer failed: %d\n", rc); exit(1); } } - + iterate_tox(bootstrap, AliceAV, BobAV); - + /* Start decode thread */ struct toxav_thread_data data = { .AliceAV = AliceAV, .BobAV = BobAV, .sig = 0 }; - + pthread_t dect; pthread_create(&dect, NULL, iterate_toxav, &data); pthread_detach(dect); - - CvCapture* capture = cvCreateFileCapture(vf_name); + + CvCapture *capture = cvCreateFileCapture(vf_name); + if (!capture) { printf("Failed to open video file: %s\n", vf_name); exit(1); } - + // toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL); - + time_t start_time = time(NULL); - while(start_time + 90 > time(NULL)) { - IplImage* frame = cvQueryFrame(capture ); + + while (start_time + 90 > time(NULL)) { + IplImage *frame = cvQueryFrame(capture ); + if (!frame) break; - + send_opencv_img(AliceAV, 0, frame); iterate_tox(bootstrap, AliceAV, BobAV); c_sleep(10); } - + cvReleaseCapture(&capture); - + { /* Hangup */ TOXAV_ERR_CALL_CONTROL rc; toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); - + if (rc != TOXAV_ERR_CALL_CONTROL_OK) { printf("toxav_call_control failed: %d\n", rc); exit(1); } } - + iterate_tox(bootstrap, AliceAV, BobAV); assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED); - + /* Stop decode thread */ printf("Stopping decode thread\n"); data.sig = -1; - while(data.sig != 1) + + while (data.sig != 1) pthread_yield(); - + printf("Success!"); } - - - Tox* Alice = toxav_get_tox(AliceAV); - Tox* Bob = toxav_get_tox(BobAV); + + + Tox *Alice = toxav_get_tox(AliceAV); + Tox *Bob = toxav_get_tox(BobAV); toxav_kill(BobAV); toxav_kill(AliceAV); tox_kill(Bob); tox_kill(Alice); tox_kill(bootstrap); - + printf("\nTest successful!\n"); - + Pa_Terminate(); return 0; } diff --git a/toxav/audio.c b/toxav/audio.c index 4f9d3562..ad543502 100644 --- a/toxav/audio.c +++ b/toxav/audio.c @@ -76,11 +76,11 @@ ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_c if (ac->encoder == NULL) goto DECODER_CLEANUP; - + ac->le_bit_rate = 48000; ac->le_sample_rate = 48000; ac->le_channel_count = 2; - + ac->ld_channel_count = 2; ac->ld_sample_rate = 48000; ac->ldrts = 0; /* Make it possible to reconfigure straight away */ @@ -126,7 +126,7 @@ void ac_iterate(ACSession *ac) return; /* TODO fix this and jitter buffering */ - + /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */ int16_t tmp[5760 * 2]; @@ -221,9 +221,9 @@ int ac_queue_message(void *acp, struct RTPMessage *msg) } int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) { - if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, + if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels, - &ac->le_bit_rate, + &ac->le_bit_rate, &ac->le_sample_rate, &ac->le_channel_count)) return -1; diff --git a/toxav/bwcontroler.c b/toxav/bwcontroler.c index 2530e7fd..9dd15c93 100644 --- a/toxav/bwcontroler.c +++ b/toxav/bwcontroler.c @@ -34,7 +34,7 @@ #define BWC_AVG_PKT_COUNT 20 /** - * + * */ struct BWControler_s { @@ -77,10 +77,11 @@ BWControler *bwc_new(Messenger *m, uint32_t friendnumber, /* Fill with zeros */ int i = 0; + for (; i < BWC_AVG_PKT_COUNT; i ++) rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i); - - m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu); + + m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu); return retu; } @@ -88,19 +89,19 @@ void bwc_kill(BWControler *bwc) { if (!bwc) return; - + m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL); - + rb_kill(bwc->rcvpkt.rb); free(bwc); } -void bwc_feed_avg(BWControler* bwc, uint32_t bytes) +void bwc_feed_avg(BWControler *bwc, uint32_t bytes) { uint32_t *p; - - rb_read(bwc->rcvpkt.rb, (void**) &p); + + rb_read(bwc->rcvpkt.rb, (void **) &p); rb_write(bwc->rcvpkt.rb, p); - + *p = bytes; } void bwc_add_lost(BWControler *bwc, uint32_t bytes) @@ -109,21 +110,22 @@ void bwc_add_lost(BWControler *bwc, uint32_t bytes) return; if (!bytes) { - uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1; - - rb_data(bwc->rcvpkt.rb, (void**) t_avg); - + uint32_t *t_avg[BWC_AVG_PKT_COUNT], c = 1; + + rb_data(bwc->rcvpkt.rb, (void **) t_avg); + int i = 0; + for (; i < BWC_AVG_PKT_COUNT; i ++) { bytes += *(t_avg[i]); - + if (*(t_avg[i])) c++; } - + bytes /= c; } - + bwc->cycle.lost += bytes; send_update(bwc); } @@ -131,7 +133,7 @@ void bwc_add_recv(BWControler *bwc, uint32_t bytes) { if (!bwc || !bytes) return; - + bwc->cycle.recv += bytes; send_update(bwc); } @@ -145,21 +147,19 @@ struct BWCMessage { void send_update(BWControler *bwc) { if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) { - + bwc->cycle.lost /= 10; bwc->cycle.recv /= 10; bwc->cycle.lfu = current_time_monotonic(); - } - else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) { - - if (bwc->cycle.lost) - { + } else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) { + + if (bwc->cycle.lost) { LOGGER_DEBUG ("%p Sent update rcv: %u lost: %u", bwc, bwc->cycle.recv, bwc->cycle.lost); uint8_t p_msg[sizeof(struct BWCMessage) + 1]; - struct BWCMessage* b_msg = (struct BWCMessage*)(p_msg + 1); - + struct BWCMessage *b_msg = (struct BWCMessage *)(p_msg + 1); + p_msg[0] = BWC_PACKET_ID; b_msg->lost = htonl(bwc->cycle.lost); b_msg->recv = htonl(bwc->cycle.recv); @@ -167,27 +167,27 @@ void send_update(BWControler *bwc) if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, p_msg, sizeof(p_msg))) LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(p_msg), strerror(errno)); } - + bwc->cycle.lsu = current_time_monotonic(); } } int on_update (BWControler *bwc, struct BWCMessage *msg) { LOGGER_DEBUG ("%p Got update from peer", bwc); - + /* Peer must respect time boundary */ if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) { LOGGER_DEBUG("%p Rejecting extra update", bwc); return -1; } - + bwc->cycle.lru = current_time_monotonic(); msg->recv = ntohl(msg->recv); msg->lost = ntohl(msg->lost); - + LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost); - + if (msg->lost && bwc->mcb) bwc->mcb(bwc, bwc->friend_number, ((float) (msg->lost) / (msg->recv + msg->lost)), @@ -195,7 +195,7 @@ int on_update (BWControler *bwc, struct BWCMessage *msg) return 0; } -int bwc_handle_data(Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object) +int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object) { if (length - 1 != sizeof(struct BWCMessage)) return -1; diff --git a/toxav/msi.c b/toxav/msi.c index 7ad39a54..c08c0135 100644 --- a/toxav/msi.c +++ b/toxav/msi.c @@ -99,7 +99,7 @@ void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICal { if (!session) return; - + pthread_mutex_lock(session->mutex); session->callbacks[id] = callback; pthread_mutex_unlock(session->mutex); @@ -142,7 +142,7 @@ int msi_kill (MSISession *session) } m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL); - + if (pthread_mutex_trylock(session->mutex) != 0) { LOGGER_ERROR ("Failed to aquire lock on msi mutex"); return -1; @@ -171,7 +171,7 @@ int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uin { if (!session) return -1; - + LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number); if (pthread_mutex_trylock(session->mutex) != 0) { @@ -212,11 +212,11 @@ int msi_hangup (MSICall *call) { if (!call || !call->session) return -1; - + LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number); MSISession *session = call->session; - + if (pthread_mutex_trylock(session->mutex) != 0) { LOGGER_ERROR ("Failed to aquire lock on msi mutex"); return -1; @@ -278,7 +278,7 @@ int msi_change_capabilities(MSICall *call, uint8_t capabilities) { if (!call || !call->session) return -1; - + LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number); MSISession *session = call->session; @@ -619,19 +619,18 @@ void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void * break; } } -void handle_init (MSICall* call, const MSIMessage* msg) +void handle_init (MSICall *call, const MSIMessage *msg) { assert(call); LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number); - + if (!msg->capabilities.exists) { LOGGER_WARNING("Session: %p Invalid capabilities on 'init'"); call->error = msi_EInvalidMessage; goto FAILURE; } - - switch (call->state) - { + + switch (call->state) { case msi_CallInactive: { /* Call requested */ call->peer_capabilities = msg->capabilities.value; @@ -641,7 +640,7 @@ void handle_init (MSICall* call, const MSIMessage* msg) goto FAILURE; } break; - + case msi_CallActive: { /* If peer sent init while the call is already * active it's probable that he is trying to @@ -649,9 +648,9 @@ void handle_init (MSICall* call, const MSIMessage* msg) * on our side. We can assume that in this case * we can automatically answer the re-call. */ - + LOGGER_INFO("Friend is recalling us"); - + MSIMessage msg; msg_init(&msg, requ_push); @@ -665,7 +664,7 @@ void handle_init (MSICall* call, const MSIMessage* msg) */ } break; - + default: { LOGGER_WARNING("Session: %p Invalid state on 'init'"); call->error = msi_EInvalidState; @@ -673,7 +672,7 @@ void handle_init (MSICall* call, const MSIMessage* msg) } break; } - + return; FAILURE: send_error(call->session->messenger, call->friend_number, call->error); @@ -746,33 +745,33 @@ void handle_pop (MSICall *call, const MSIMessage *msg) invoke_callback(call, msi_OnError); } else switch (call->state) { - case msi_CallInactive: { - LOGGER_ERROR("Handling what should be impossible case"); - abort(); - } - break; + case msi_CallInactive: { + LOGGER_ERROR("Handling what should be impossible case"); + abort(); + } + break; - case msi_CallActive: { - /* Hangup */ - LOGGER_INFO("Friend hung up on us"); - invoke_callback(call, msi_OnEnd); - } - break; + case msi_CallActive: { + /* Hangup */ + LOGGER_INFO("Friend hung up on us"); + invoke_callback(call, msi_OnEnd); + } + break; - case msi_CallRequesting: { - /* Reject */ - LOGGER_INFO("Friend rejected our call"); - invoke_callback(call, msi_OnEnd); - } - break; + case msi_CallRequesting: { + /* Reject */ + LOGGER_INFO("Friend rejected our call"); + invoke_callback(call, msi_OnEnd); + } + break; - case msi_CallRequested: { - /* Cancel */ - LOGGER_INFO("Friend canceled call invite"); - invoke_callback(call, msi_OnEnd); + case msi_CallRequested: { + /* Cancel */ + LOGGER_INFO("Friend canceled call invite"); + invoke_callback(call, msi_OnEnd); + } + break; } - break; - } kill_call (call); } @@ -814,9 +813,11 @@ void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *dat case requ_init: handle_init(call, &msg); break; + case requ_push: handle_push(call, &msg); break; + case requ_pop: handle_pop(call, &msg); /* always kills the call */ break; diff --git a/toxav/rtp.c b/toxav/rtp.c index 763166cd..1813725a 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c @@ -189,17 +189,18 @@ bool chloss (const RTPSession *session, const struct RTPHeader *header) { if (ntohl(header->timestamp) < session->rtimestamp) { uint16_t hosq, lost = 0; - + hosq = ntohs(header->sequnum); - - lost = (hosq > session->rsequnum) ? - (session->rsequnum + 65535) - hosq : - session->rsequnum - hosq; - + + lost = (hosq > session->rsequnum) ? + (session->rsequnum + 65535) - hosq : + session->rsequnum - hosq; + puts ("Lost packet"); + while (lost --) - bwc_add_lost(session->bwc ,0); - + bwc_add_lost(session->bwc , 0); + return true; } @@ -251,7 +252,7 @@ int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, } bwc_feed_avg(session->bwc, length); - + if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) { /* The message is sent in single part */ @@ -267,7 +268,7 @@ int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, } bwc_add_recv(session->bwc, length); - + /* Invoke processing of active multiparted message */ if (session->mp) { if (session->mcb) @@ -341,11 +342,11 @@ int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, /* Measure missing parts of the old message */ bwc_add_lost(session->bwc, (session->mp->header.tlen - session->mp->len) + - + /* Must account sizes of rtp headers too */ ((session->mp->header.tlen - session->mp->len) / - MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) ); - + MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) ); + /* Push the previous message for processing */ if (session->mcb) session->mcb (session->cs, session->mp); diff --git a/toxav/toxav.c b/toxav/toxav.c index 6a17f55d..9eda3412 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -190,7 +190,7 @@ void toxav_kill(ToxAV *av) return; pthread_mutex_lock(av->mutex); - + /* To avoid possible deadlocks */ while (av->msi && msi_kill(av->msi) != 0) { pthread_mutex_unlock(av->mutex); @@ -206,10 +206,10 @@ void toxav_kill(ToxAV *av) it = call_remove(it); /* This will eventually free av->calls */ } } - + pthread_mutex_unlock(av->mutex); pthread_mutex_destroy(av->mutex); - + free(av); } Tox *toxav_get_tox(const ToxAV *av) @@ -277,11 +277,11 @@ bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint TOXAV_ERR_CALL *error) { TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; - + pthread_mutex_lock(av->mutex); - + if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) - || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) { + || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) { rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; goto END; } @@ -311,7 +311,7 @@ bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint END: pthread_mutex_unlock(av->mutex); - + if (error) *error = rc; @@ -337,7 +337,7 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui } if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) - || (video_bit_rate && video_bit_rate_invalid(video_bit_rate)) + || (video_bit_rate && video_bit_rate_invalid(video_bit_rate)) ) { rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE; goto END; @@ -441,9 +441,10 @@ bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL co case TOXAV_CALL_CONTROL_CANCEL: { /* Hang up */ pthread_mutex_lock(call->mutex); + if (msi_hangup(call->msi_call) != 0) { rc = TOXAV_ERR_CALL_CONTROL_SYNC; - pthread_mutex_unlock(call->mutex); + pthread_mutex_unlock(call->mutex); goto END; } @@ -566,18 +567,22 @@ bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rat LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate); } else if (audio_bit_rate == 0) { LOGGER_DEBUG("Turned off audio sending"); + if (msi_change_capabilities(call->msi_call, call->msi_call-> self_capabilities ^ msi_CapSAudio) != 0) { pthread_mutex_unlock(av->mutex); rc = TOXAV_ERR_BIT_RATE_SET_SYNC; goto END; } + /* Audio sending is turned off; notify peer */ call->audio_bit_rate = 0; } else { pthread_mutex_lock(call->mutex); + if (call->audio_bit_rate == 0) { LOGGER_DEBUG("Turned on audio sending"); + /* The audio has been turned off before this */ if (msi_change_capabilities(call->msi_call, call-> msi_call->self_capabilities | msi_CapSAudio) != 0) { @@ -588,6 +593,7 @@ bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rat } } else LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate); + call->audio_bit_rate = audio_bit_rate; pthread_mutex_unlock(call->mutex); } @@ -600,6 +606,7 @@ bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rat LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate); } else if (video_bit_rate == 0) { LOGGER_DEBUG("Turned off video sending"); + /* Video sending is turned off; notify peer */ if (msi_change_capabilities(call->msi_call, call->msi_call-> self_capabilities ^ msi_CapSVideo) != 0) { @@ -607,11 +614,14 @@ bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rat rc = TOXAV_ERR_BIT_RATE_SET_SYNC; goto END; } + call->video_bit_rate = 0; } else { pthread_mutex_lock(call->mutex); + if (call->video_bit_rate == 0) { LOGGER_DEBUG("Turned on video sending"); + /* The video has been turned off before this */ if (msi_change_capabilities(call->msi_call, call-> msi_call->self_capabilities | msi_CapSVideo) != 0) { @@ -622,13 +632,15 @@ bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rat } } else LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate); + call->video_bit_rate = video_bit_rate; pthread_mutex_unlock(call->mutex); } } - + pthread_mutex_unlock(av->mutex); END: + if (error) *error = rc; @@ -656,7 +668,7 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pc rc = TOXAV_ERR_SEND_FRAME_SYNC; goto END; } - + call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { @@ -719,6 +731,7 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pc pthread_mutex_unlock(call->mutex_audio); END: + if (error) *error = rc; @@ -739,7 +752,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u rc = TOXAV_ERR_SEND_FRAME_SYNC; goto END; } - + call = call_get(av, friend_number); if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { @@ -817,6 +830,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u pthread_mutex_unlock(call->mutex_video); END: + if (error) *error = rc; @@ -843,7 +857,7 @@ void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb * :: Internal * ******************************************************************************/ -void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* user_data) +void callback_bwc(BWControler *bwc, uint32_t friend_number, float loss, void *user_data) { /* Callback which is called when the internal measure mechanism reported packet loss. * We report suggested lowered bitrate to an app. If app is sending both audio and video, @@ -851,22 +865,23 @@ void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* us * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio. * The application may choose to disable video totally if the stream is too bad. */ - - ToxAVCall* call = user_data; + + ToxAVCall *call = user_data; assert(call); - LOGGER_DEBUG("Reported loss of %f%%", loss*100); - + LOGGER_DEBUG("Reported loss of %f%%", loss * 100); + if (loss < .01f) return; pthread_mutex_lock(call->av->mutex); + if (!call->av->bcb.first) { pthread_mutex_unlock(call->av->mutex); LOGGER_WARNING("No callback to report loss on"); return; } - + if (call->video_bit_rate) (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate, call->video_bit_rate - (call->video_bit_rate * loss), @@ -875,7 +890,7 @@ void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* us (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate - (call->audio_bit_rate * loss), 0, call->av->bcb.second); - + pthread_mutex_unlock(call->av->mutex); } int callback_invite(void *toxav_inst, MSICall *call) @@ -1167,7 +1182,7 @@ bool call_prepare_transmission(ToxAVCall *call) /* Prepare bwc */ call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call); - + { /* Prepare audio */ call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second); @@ -1236,7 +1251,7 @@ void call_kill_transmission(ToxAVCall *call) pthread_mutex_unlock(call->mutex); bwc_kill(call->bwc); - + rtp_kill(call->audio.first); ac_kill(call->audio.second); call->audio.first = NULL; diff --git a/toxav/toxav.h b/toxav/toxav.h index 5c5195b3..08a6d265 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h @@ -1,5 +1,5 @@ /* toxav.h - * + * * Copyright (C) 2013-2015 Tox project All Rights Reserved. * * This file is part of Tox. @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU General Public License * along with Tox. If not, see . - * + * */ #ifndef TOXAV_H @@ -31,37 +31,37 @@ extern "C" { #endif /** \page av Public audio/video API for Tox clients. - * + * * This API can handle multiple calls. Each call has its state, in very rare * occasions the library can change the state of the call without apps knowledge. - * + * */ /** \subsection events Events and callbacks * - * As in Core API, events are handled by callbacks. One callback can be - * registered per event. All events have a callback function type named + * As in Core API, events are handled by callbacks. One callback can be + * registered per event. All events have a callback function type named * `toxav_{event}_cb` and a function to register it named `toxav_callback_{event}`. - * Passing a NULL callback will result in no callback being registered for that - * event. Only one callback per event can be registered, so if a client needs - * multiple event listeners, it needs to implement the dispatch functionality - * itself. Unlike Core API, lack of some event handlers will cause the the - * library to drop calls before they are started. Hanging up call from a + * Passing a NULL callback will result in no callback being registered for that + * event. Only one callback per event can be registered, so if a client needs + * multiple event listeners, it needs to implement the dispatch functionality + * itself. Unlike Core API, lack of some event handlers will cause the the + * library to drop calls before they are started. Hanging up call from a * callback causes undefined behaviour. - * + * */ /** \subsection threading Threading implications * * Unlike the Core API, this API is fully thread-safe. The library will ensure - * the proper synchronization of parallel calls. - * + * the proper synchronization of parallel calls. + * * A common way to run ToxAV (multiple or single instance) is to have a thread, - * separate from tox instance thread, running a simple toxav_iterate loop, + * separate from tox instance thread, running a simple toxav_iterate loop, * sleeping for toxav_iteration_interval * milliseconds on each iteration. * * An important thing to note is that events are triggered from both tox and * toxav thread (see above). Audio and video receive frame events are triggered * from toxav thread while all the other events are triggered from tox thread. - * + * * Tox thread has priority with mutex mechanisms. Any api function can * fail if mutexes are held by tox thread in which case they will set SYNC * error code. @@ -81,9 +81,9 @@ typedef struct Tox Tox; * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox * instance, and Tox instance can have only one ToxAV instance. One must make * sure to close ToxAV instance prior closing Tox instance otherwise undefined - * behaviour occurs. Upon closing of ToxAV instance, all active calls will be + * behaviour occurs. Upon closing of ToxAV instance, all active calls will be * forcibly terminated without notifying peers. - * + * */ #ifndef TOXAV_DEFINED #define TOXAV_DEFINED @@ -164,28 +164,28 @@ bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch) /******************************************************************************* - * + * * :: Creation and destruction * ******************************************************************************/ typedef enum TOXAV_ERR_NEW { - /** - * The function returned successfully. - */ - TOXAV_ERR_NEW_OK, - /** - * One of the arguments to the function was NULL when it was not expected. - */ - TOXAV_ERR_NEW_NULL, - /** - * Memory allocation failure while trying to allocate structures required for - * the A/V session. - */ - TOXAV_ERR_NEW_MALLOC, - /** - * Attempted to create a second session for the same Tox instance. - */ - TOXAV_ERR_NEW_MULTIPLE, + /** + * The function returned successfully. + */ + TOXAV_ERR_NEW_OK, + /** + * One of the arguments to the function was NULL when it was not expected. + */ + TOXAV_ERR_NEW_NULL, + /** + * Memory allocation failure while trying to allocate structures required for + * the A/V session. + */ + TOXAV_ERR_NEW_MALLOC, + /** + * Attempted to create a second session for the same Tox instance. + */ + TOXAV_ERR_NEW_MULTIPLE, } TOXAV_ERR_NEW; /** @@ -209,7 +209,7 @@ Tox *toxav_get_tox(const ToxAV *toxAV); /******************************************************************************* - * + * * :: A/V event loop * ******************************************************************************/ @@ -221,48 +221,48 @@ uint32_t toxav_iteration_interval(const ToxAV *toxAV); /** * Main loop for the session. This function needs to be called in intervals of - * toxav_iteration_interval() milliseconds. It is best called in the separate + * toxav_iteration_interval() milliseconds. It is best called in the separate * thread from tox_iterate. */ void toxav_iterate(ToxAV *toxAV); /******************************************************************************* - * + * * :: Call setup * ******************************************************************************/ typedef enum TOXAV_ERR_CALL { - /** - * The function returned successfully. - */ - TOXAV_ERR_CALL_OK, - /** - * A resource allocation error occurred while trying to create the structures - * required for the call. - */ - TOXAV_ERR_CALL_MALLOC, - /** - * Synchronization error occurred. - */ - TOXAV_ERR_CALL_SYNC, - /** - * The friend number did not designate a valid friend. - */ - TOXAV_ERR_CALL_FRIEND_NOT_FOUND, - /** - * The friend was valid, but not currently connected. - */ - TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED, - /** - * Attempted to call a friend while already in an audio or video call with - * them. - */ - TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL, - /** - * Audio or video bit rate is invalid. - */ - TOXAV_ERR_CALL_INVALID_BIT_RATE, + /** + * The function returned successfully. + */ + TOXAV_ERR_CALL_OK, + /** + * A resource allocation error occurred while trying to create the structures + * required for the call. + */ + TOXAV_ERR_CALL_MALLOC, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_CALL_SYNC, + /** + * The friend number did not designate a valid friend. + */ + TOXAV_ERR_CALL_FRIEND_NOT_FOUND, + /** + * The friend was valid, but not currently connected. + */ + TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED, + /** + * Attempted to call a friend while already in an audio or video call with + * them. + */ + TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL, + /** + * Audio or video bit rate is invalid. + */ + TOXAV_ERR_CALL_INVALID_BIT_RATE, } TOXAV_ERR_CALL; /** @@ -270,7 +270,7 @@ typedef enum TOXAV_ERR_CALL { * * It is the client's responsibility to stop ringing after a certain timeout, * if such behaviour is desired. If the client does not stop ringing, the - * library will not stop until the friend is disconnected. Audio and video + * library will not stop until the friend is disconnected. Audio and video * receiving are both enabled by default. * * @param friend_number The friend number of the friend that should be called. @@ -284,7 +284,7 @@ bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, /** * The function type for the call callback. - * + * * @param friend_number The friend number from which the call is incoming. * @param audio_enabled True if friend is sending audio. * @param video_enabled True if friend is sending video. @@ -299,33 +299,33 @@ typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enab void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data); typedef enum TOXAV_ERR_ANSWER { - /** - * The function returned successfully. - */ - TOXAV_ERR_ANSWER_OK, - /** - * Synchronization error occurred. - */ - TOXAV_ERR_ANSWER_SYNC, - /** - * Failed to initialize codecs for call session. Note that codec initiation - * will fail if there is no receive callback registered for either audio or - * video. - */ - TOXAV_ERR_ANSWER_CODEC_INITIALIZATION, - /** - * The friend number did not designate a valid friend. - */ - TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND, - /** - * The friend was valid, but they are not currently trying to initiate a call. - * This is also returned if this client is already in a call with the friend. - */ - TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING, - /** - * Audio or video bit rate is invalid. - */ - TOXAV_ERR_ANSWER_INVALID_BIT_RATE, + /** + * The function returned successfully. + */ + TOXAV_ERR_ANSWER_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_ANSWER_SYNC, + /** + * Failed to initialize codecs for call session. Note that codec initiation + * will fail if there is no receive callback registered for either audio or + * video. + */ + TOXAV_ERR_ANSWER_CODEC_INITIALIZATION, + /** + * The friend number did not designate a valid friend. + */ + TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND, + /** + * The friend was valid, but they are not currently trying to initiate a call. + * This is also returned if this client is already in a call with the friend. + */ + TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING, + /** + * Audio or video bit rate is invalid. + */ + TOXAV_ERR_ANSWER_INVALID_BIT_RATE, } TOXAV_ERR_ANSWER; /** @@ -341,44 +341,45 @@ typedef enum TOXAV_ERR_ANSWER { * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable * video sending. */ -bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); +bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, + TOXAV_ERR_ANSWER *error); /******************************************************************************* - * + * * :: Call state graph * ******************************************************************************/ enum TOXAV_FRIEND_CALL_STATE { - /** - * Set by the AV core if an error occurred on the remote end or if friend - * timed out. This is the final state after which no more state - * transitions can occur for the call. This call state will never be triggered - * in combination with other call states. - */ - TOXAV_FRIEND_CALL_STATE_ERROR = 1, - /** - * The call has finished. This is the final state after which no more state - * transitions can occur for the call. This call state will never be - * triggered in combination with other call states. - */ - TOXAV_FRIEND_CALL_STATE_FINISHED = 2, - /** - * The flag that marks that friend is sending audio. - */ - TOXAV_FRIEND_CALL_STATE_SENDING_A = 4, - /** - * The flag that marks that friend is sending video. - */ - TOXAV_FRIEND_CALL_STATE_SENDING_V = 8, - /** - * The flag that marks that friend is receiving audio. - */ - TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16, - /** - * The flag that marks that friend is receiving video. - */ - TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32, + /** + * Set by the AV core if an error occurred on the remote end or if friend + * timed out. This is the final state after which no more state + * transitions can occur for the call. This call state will never be triggered + * in combination with other call states. + */ + TOXAV_FRIEND_CALL_STATE_ERROR = 1, + /** + * The call has finished. This is the final state after which no more state + * transitions can occur for the call. This call state will never be + * triggered in combination with other call states. + */ + TOXAV_FRIEND_CALL_STATE_FINISHED = 2, + /** + * The flag that marks that friend is sending audio. + */ + TOXAV_FRIEND_CALL_STATE_SENDING_A = 4, + /** + * The flag that marks that friend is sending video. + */ + TOXAV_FRIEND_CALL_STATE_SENDING_V = 8, + /** + * The flag that marks that friend is receiving audio. + */ + TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16, + /** + * The flag that marks that friend is receiving video. + */ + TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32, }; /** @@ -399,70 +400,70 @@ typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data); /******************************************************************************* - * + * * :: Call control * ******************************************************************************/ typedef enum TOXAV_CALL_CONTROL { - /** - * Resume a previously paused call. Only valid if the pause was caused by this - * client, if not, this control is ignored. Not valid before the call is accepted. - */ - TOXAV_CALL_CONTROL_RESUME, - /** - * Put a call on hold. Not valid before the call is accepted. - */ - TOXAV_CALL_CONTROL_PAUSE, - /** - * Reject a call if it was not answered, yet. Cancel a call after it was - * answered. - */ - TOXAV_CALL_CONTROL_CANCEL, - /** - * Request that the friend stops sending audio. Regardless of the friend's - * compliance, this will cause the audio_receive_frame event to stop being - * triggered on receiving an audio frame from the friend. - */ - TOXAV_CALL_CONTROL_MUTE_AUDIO, - /** - * Calling this control will notify client to start sending audio again. - */ - TOXAV_CALL_CONTROL_UNMUTE_AUDIO, - /** - * Request that the friend stops sending video. Regardless of the friend's - * compliance, this will cause the video_receive_frame event to stop being - * triggered on receiving a video frame from the friend. - */ - TOXAV_CALL_CONTROL_HIDE_VIDEO, - /** - * Calling this control will notify client to start sending video again. - */ - TOXAV_CALL_CONTROL_SHOW_VIDEO, + /** + * Resume a previously paused call. Only valid if the pause was caused by this + * client, if not, this control is ignored. Not valid before the call is accepted. + */ + TOXAV_CALL_CONTROL_RESUME, + /** + * Put a call on hold. Not valid before the call is accepted. + */ + TOXAV_CALL_CONTROL_PAUSE, + /** + * Reject a call if it was not answered, yet. Cancel a call after it was + * answered. + */ + TOXAV_CALL_CONTROL_CANCEL, + /** + * Request that the friend stops sending audio. Regardless of the friend's + * compliance, this will cause the audio_receive_frame event to stop being + * triggered on receiving an audio frame from the friend. + */ + TOXAV_CALL_CONTROL_MUTE_AUDIO, + /** + * Calling this control will notify client to start sending audio again. + */ + TOXAV_CALL_CONTROL_UNMUTE_AUDIO, + /** + * Request that the friend stops sending video. Regardless of the friend's + * compliance, this will cause the video_receive_frame event to stop being + * triggered on receiving a video frame from the friend. + */ + TOXAV_CALL_CONTROL_HIDE_VIDEO, + /** + * Calling this control will notify client to start sending video again. + */ + TOXAV_CALL_CONTROL_SHOW_VIDEO, } TOXAV_CALL_CONTROL; typedef enum TOXAV_ERR_CALL_CONTROL { - /** - * The function returned successfully. - */ - TOXAV_ERR_CALL_CONTROL_OK, - /** - * Synchronization error occurred. - */ - TOXAV_ERR_CALL_CONTROL_SYNC, - /** - * The friend_number passed did not designate a valid friend. - */ - TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, - /** - * This client is currently not in a call with the friend. Before the call is - * answered, only CANCEL is a valid control. - */ - TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL, - /** - * Happens if user tried to pause an already paused call or if trying to - * resume a call that is not paused. - */ - TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION, + /** + * The function returned successfully. + */ + TOXAV_ERR_CALL_CONTROL_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_CALL_CONTROL_SYNC, + /** + * The friend_number passed did not designate a valid friend. + */ + TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. Before the call is + * answered, only CANCEL is a valid control. + */ + TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL, + /** + * Happens if user tried to pause an already paused call or if trying to + * resume a call that is not paused. + */ + TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION, } TOXAV_ERR_CALL_CONTROL; /** @@ -474,39 +475,40 @@ typedef enum TOXAV_ERR_CALL_CONTROL { * * @return true on success. */ -bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); +bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, + TOXAV_ERR_CALL_CONTROL *error); /******************************************************************************* - * + * * :: Controlling bit rates * ******************************************************************************/ typedef enum TOXAV_ERR_BIT_RATE_SET { - /** - * The function returned successfully. - */ - TOXAV_ERR_BIT_RATE_SET_OK, - /** - * Synchronization error occurred. - */ - TOXAV_ERR_BIT_RATE_SET_SYNC, - /** - * The audio bit rate passed was not one of the supported values. - */ - TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE, - /** - * The video bit rate passed was not one of the supported values. - */ - TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE, - /** - * The friend_number passed did not designate a valid friend. - */ - TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND, - /** - * This client is currently not in a call with the friend. - */ - TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL, + /** + * The function returned successfully. + */ + TOXAV_ERR_BIT_RATE_SET_OK, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_BIT_RATE_SET_SYNC, + /** + * The audio bit rate passed was not one of the supported values. + */ + TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE, + /** + * The video bit rate passed was not one of the supported values. + */ + TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE, + /** + * The friend_number passed did not designate a valid friend. + */ + TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. + */ + TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL, } TOXAV_ERR_BIT_RATE_SET; /** @@ -518,22 +520,23 @@ typedef enum TOXAV_ERR_BIT_RATE_SET { * audio sending. Set to -1 to leave unchanged. * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable * video sending. Set to -1 to leave unchanged. - * + * */ bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error); /** * The function type for the bit_rate_status callback. The event is triggered - * when the network becomes too saturated for current bit rates at which + * when the network becomes too saturated for current bit rates at which * point core suggests new bit rates. - * + * * @param friend_number The friend number of the friend for which to set the * bit rate. * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec. * @param video_bit_rate Suggested maximum video bit rate in Kb/sec. */ -typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data); +typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, + uint32_t video_bit_rate, void *user_data); /** * Set the callback for the `bit_rate_status` event. Pass NULL to unset. @@ -543,46 +546,46 @@ void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *call /******************************************************************************* - * + * * :: A/V sending * ******************************************************************************/ typedef enum TOXAV_ERR_SEND_FRAME { - /** - * The function returned successfully. - */ - TOXAV_ERR_SEND_FRAME_OK, - /** - * In case of video, one of Y, U, or V was NULL. In case of audio, the samples - * data pointer was NULL. - */ - TOXAV_ERR_SEND_FRAME_NULL, - /** - * The friend_number passed did not designate a valid friend. - */ - TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND, - /** - * This client is currently not in a call with the friend. - */ - TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL, - /** - * Synchronization error occurred. - */ - TOXAV_ERR_SEND_FRAME_SYNC, - /** - * One of the frame parameters was invalid. E.g. the resolution may be too - * small or too large, or the audio sampling rate may be unsupported. - */ - TOXAV_ERR_SEND_FRAME_INVALID, - /** - * Either friend turned off audio or video receiving or we turned off sending - * for the said payload. - */ - TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED, - /** - * Failed to push frame through rtp interface. - */ - TOXAV_ERR_SEND_FRAME_RTP_FAILED, + /** + * The function returned successfully. + */ + TOXAV_ERR_SEND_FRAME_OK, + /** + * In case of video, one of Y, U, or V was NULL. In case of audio, the samples + * data pointer was NULL. + */ + TOXAV_ERR_SEND_FRAME_NULL, + /** + * The friend_number passed did not designate a valid friend. + */ + TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND, + /** + * This client is currently not in a call with the friend. + */ + TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL, + /** + * Synchronization error occurred. + */ + TOXAV_ERR_SEND_FRAME_SYNC, + /** + * One of the frame parameters was invalid. E.g. the resolution may be too + * small or too large, or the audio sampling rate may be unsupported. + */ + TOXAV_ERR_SEND_FRAME_INVALID, + /** + * Either friend turned off audio or video receiving or we turned off sending + * for the said payload. + */ + TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED, + /** + * Failed to push frame through rtp interface. + */ + TOXAV_ERR_SEND_FRAME_RTP_FAILED, } TOXAV_ERR_SEND_FRAME; /** @@ -605,7 +608,7 @@ typedef enum TOXAV_ERR_SEND_FRAME { * @param sampling_rate Audio sampling rate used in this frame. Valid sampling * rates are 8000, 12000, 16000, 24000, or 48000. */ -bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, +bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error); @@ -624,13 +627,13 @@ bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t * @param u U (Chroma) plane data. * @param v V (Chroma) plane data. */ -bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, - uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, +bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, + uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error); /******************************************************************************* - * + * * :: A/V receiving * ******************************************************************************/ @@ -638,7 +641,7 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width * The function type for the audio_receive_frame callback. The callback can be * called multiple times per single iteration depending on the amount of queued * frames in the buffer. The received format is the same as in send function. - * + * * @param friend_number The friend number of the friend who sent an audio frame. * @param pcm An array of audio samples (sample_count * channels elements). * @param sample_count The number of audio samples per channel in the PCM array. @@ -647,8 +650,8 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width * */ typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, - size_t sample_count, uint8_t channels, uint32_t sampling_rate, - void *user_data); + size_t sample_count, uint8_t channels, uint32_t sampling_rate, + void *user_data); /** * Set the callback for the `audio_receive_frame` event. Pass NULL to unset. @@ -662,24 +665,24 @@ void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_ * @param friend_number The friend number of the friend who sent a video frame. * @param width Width of the frame in pixels. * @param height Height of the frame in pixels. - * @param y - * @param u + * @param y + * @param u * @param v Plane data. * The size of plane data is derived from width and height where - * Y = MAX(width, abs(ystride)) * height, - * U = MAX(width/2, abs(ustride)) * (height/2) and + * Y = MAX(width, abs(ystride)) * height, + * U = MAX(width/2, abs(ustride)) * (height/2) and * V = MAX(width/2, abs(vstride)) * (height/2). * @param ystride * @param ustride * @param vstride Strides data. Strides represent padding for each plane * that may or may not be present. You must handle strides in - * your image processing code. Strides are negative if the + * your image processing code. Strides are negative if the * image is bottom-up hence why you MUST abs() it when * calculating plane buffer size. */ -typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, - uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, - int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); +typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, + uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, + int32_t ystride, int32_t ustride, int32_t vstride, void *user_data); /** * Set the callback for the `video_receive_frame` event. Pass NULL to unset. @@ -700,7 +703,7 @@ void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_ * * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). */ -int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, +int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); /* Join a AV group (you need to have been invited first.) @@ -714,7 +717,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, con * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). */ int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, - void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); + void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); /* Send audio to the group chat. * diff --git a/toxav/video.c b/toxav/video.c index acc1852b..8a832201 100644 --- a/toxav/video.c +++ b/toxav/video.c @@ -207,43 +207,40 @@ int vc_queue_message(void *vcp, struct RTPMessage *msg) return 0; } -int vc_reconfigure_encoder(VCSession* vc, uint32_t bit_rate, uint16_t width, uint16_t height) +int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height) { if (!vc) return -1; vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc; int rc; - + if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) return 0; /* Nothing changed */ - if (cfg.g_w == width && cfg.g_h == height) - { + if (cfg.g_w == width && cfg.g_h == height) { /* Only bit rate changed */ cfg.rc_target_bitrate = bit_rate; - + rc = vpx_codec_enc_config_set(vc->encoder, &cfg); - + if (rc != VPX_CODEC_OK) { LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); return -1; } - } - else - { + } else { /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support * reconfiguring encoder to use resolutions greater than initially set. */ - + LOGGER_DEBUG("Have to reinitialize vpx encoder on session %p", vc); - + cfg.rc_target_bitrate = bit_rate; cfg.g_w = width; cfg.g_h = height; vpx_codec_ctx_t new_c; - + rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); if (rc != VPX_CODEC_OK) { @@ -258,7 +255,7 @@ int vc_reconfigure_encoder(VCSession* vc, uint32_t bit_rate, uint16_t width, uin vpx_codec_destroy(&new_c); return -1; } - + vpx_codec_destroy(vc->encoder); memcpy(vc->encoder, &new_c, sizeof(new_c)); } diff --git a/toxav/video.h b/toxav/video.h index 51f34318..fb836a35 100644 --- a/toxav/video.h +++ b/toxav/video.h @@ -58,7 +58,7 @@ typedef struct VCSession_s { pthread_mutex_t queue_mutex[1]; } VCSession; -VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data); +VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data); void vc_kill(VCSession *vc); void vc_iterate(VCSession *vc); int vc_queue_message(void *vcp, struct RTPMessage *msg); diff --git a/toxcore/util.c b/toxcore/util.c index 81fa84c6..28d8721c 100644 --- a/toxcore/util.c +++ b/toxcore/util.c @@ -209,18 +209,19 @@ bool rb_empty(const RingBuffer *b) { return b->end == b->start; } -void* rb_write(RingBuffer *b, void *p) +void *rb_write(RingBuffer *b, void *p) { - void* rc = NULL; + void *rc = NULL; + if ((b->end + 1) % b->size == b->start) /* full */ rc = b->data[b->start]; - + b->data[b->end] = p; b->end = (b->end + 1) % b->size; - if (b->end == b->start) + if (b->end == b->start) b->start = (b->start + 1) % b->size; - + return rc; } bool rb_read(RingBuffer *b, void **p) @@ -229,7 +230,7 @@ bool rb_read(RingBuffer *b, void **p) *p = NULL; return false; } - + *p = b->data[b->start]; b->start = (b->start + 1) % b->size; return true; @@ -256,21 +257,22 @@ void rb_kill(RingBuffer *b) free(b); } } -uint16_t rb_size(const RingBuffer* b) -{ +uint16_t rb_size(const RingBuffer *b) +{ if (rb_empty(b)) return 0; - + return - b->end > b->start ? + b->end > b->start ? b->end - b->start : (b->size - b->start) + b->end; } -uint16_t rb_data(const RingBuffer* b, void** dest) +uint16_t rb_data(const RingBuffer *b, void **dest) { uint16_t i = 0; + for (; i < rb_size(b); i++) dest[i] = b->data[(b->start + i) % b->size]; - + return i; } diff --git a/toxcore/util.h b/toxcore/util.h index 7cf63178..bdbf7d3b 100644 --- a/toxcore/util.h +++ b/toxcore/util.h @@ -62,11 +62,11 @@ int create_recursive_mutex(pthread_mutex_t *mutex); typedef struct RingBuffer RingBuffer; bool rb_full(const RingBuffer *b); bool rb_empty(const RingBuffer *b); -void* rb_write(RingBuffer* b, void* p); -bool rb_read(RingBuffer* b, void** p); +void *rb_write(RingBuffer *b, void *p); +bool rb_read(RingBuffer *b, void **p); RingBuffer *rb_new(int size); void rb_kill(RingBuffer *b); uint16_t rb_size(const RingBuffer *b); -uint16_t rb_data(const RingBuffer* b, void** dest); +uint16_t rb_data(const RingBuffer *b, void **dest); #endif /* __UTIL_H__ */ diff --git a/toxdns/toxdns.c b/toxdns/toxdns.c index f7df5df3..1fbf4a00 100644 --- a/toxdns/toxdns.c +++ b/toxdns/toxdns.c @@ -44,7 +44,7 @@ uint8_t i = 0; \ } \ } \ } \ - + typedef struct { uint8_t temp_pk[crypto_box_PUBLICKEYBYTES]; uint8_t temp_sk[crypto_box_SECRETKEYBYTES]; -- cgit v1.2.3