From 1450c22d01cbb5185ee8eac14657ddf3301d7e48 Mon Sep 17 00:00:00 2001 From: mannol Date: Sat, 24 Jan 2015 23:29:54 +0100 Subject: Current progress --- toxcore/logger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'toxcore/logger.c') diff --git a/toxcore/logger.c b/toxcore/logger.c index 2ef5f21a..ac81a900 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c @@ -42,7 +42,7 @@ #endif -typedef struct logger { +struct logger { FILE *log_file; LOG_LEVEL level; uint64_t start_time; /* Time when lib loaded */ @@ -55,7 +55,7 @@ typedef struct logger { /* For thread synchronisation */ pthread_mutex_t mutex[1]; -} logger; +}; logger *global = NULL; -- cgit v1.2.3 From 4fa31d14cf53dd54b182508df31b5524b1f24cb6 Mon Sep 17 00:00:00 2001 From: mannol Date: Thu, 9 Apr 2015 02:43:13 +0200 Subject: Make it possible to decode mono audio with stereo decoder --- toxav/av_test.c | 26 ++++++++++++----------- toxav/codec.c | 64 +++++++++++++++++++++++++++++++++++++------------------- toxav/codec.h | 3 +++ toxav/toxav.c | 15 ++++++++----- toxcore/logger.c | 12 +++++------ 5 files changed, 76 insertions(+), 44 deletions(-) (limited to 'toxcore/logger.c') diff --git a/toxav/av_test.c b/toxav/av_test.c index a04aff7c..007a1a10 100644 --- a/toxav/av_test.c +++ b/toxav/av_test.c @@ -141,28 +141,30 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, int32_t processed = 0, queued = 16; alGetSourcei(adout, AL_BUFFERS_PROCESSED, &processed); alGetSourcei(adout, AL_BUFFERS_QUEUED, &queued); - + if(processed) { uint32_t bufids[processed]; alSourceUnqueueBuffers(adout, processed, bufids); alDeleteBuffers(processed - 1, bufids + 1); -// bufid = bufids[0]; + bufid = bufids[0]; } -// else if(queued < 16) +// else if(queued < 16) { alGenBuffers(1, &bufid); +// } // else // return; - + alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, - pcm, sample_count * 2, sampling_rate); + pcm, sample_count * 2 * channels, sampling_rate); alSourceQueueBuffers(adout, 1, &bufid); - + int32_t state; alGetSourcei(adout, AL_SOURCE_STATE, &state); - - if(state != AL_PLAYING) + if(state != AL_PLAYING) { + printf("Here\n"); alSourcePlay(adout); + } } void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata) { @@ -260,7 +262,8 @@ void* iterate_toxav (void * data) printf("\rToxAV interval: %d ", rc); fflush(stdout); - cvWaitKey(rc); +// cvWaitKey(rc); + c_sleep(rc/2); } data_cast->sig = 1; @@ -319,7 +322,7 @@ ALCdevice* open_audio_device(const char* audio_out_dev_name) alcMakeContextCurrent(out_ctx); alGenSources((uint32_t)1, &adout); - alSourcei(adout, AL_LOOPING, AL_FALSE); + alSourcei(adout, AL_LOOPING, AL_FALSE); alSourcePlay(adout); return rc; @@ -730,7 +733,7 @@ int main (int argc, char** argv) { /* Call */ TOXAV_ERR_CALL rc; - toxav_call(AliceAV, 0, 48, 0, &rc); + toxav_call(AliceAV, 0, 8, 0, &rc); if (rc != TOXAV_ERR_CALL_OK) { printf("toxav_call failed: %d\n", rc); @@ -785,7 +788,6 @@ int main (int argc, char** argv) int64_t count = sf_read_short(af_handle, PCM, frame_size); if (count > 0) { -// t_toxav_receive_audio_frame_cb(AliceAV, 0, PCM, count, af_info.channels, af_info.samplerate, NULL); TOXAV_ERR_SEND_FRAME rc; if (toxav_send_audio_frame(AliceAV, 0, PCM, count, af_info.channels, af_info.samplerate, &rc) == false) { printf("Error sending frame of size %ld: %d\n", count, rc); diff --git a/toxav/codec.c b/toxav/codec.c index cd26d1e3..be69ee70 100644 --- a/toxav/codec.c +++ b/toxav/codec.c @@ -38,7 +38,7 @@ #include "rtp.h" #include "codec.h" -#define DEFAULT_JBUF 6 +#define DEFAULT_JBUF 3 /* Good quality encode. */ #define MAX_DECODE_TIME_US 0 @@ -342,39 +342,59 @@ void cs_do(CSession *cs) (cs->last_packet_sampling_rate * cs->last_packet_frame_duration / 1000) * cs->last_packet_channel_count, 1); } else { - /* Get values from packet and decode. - * It also checks for validity of an opus packet - */ + /* 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; - cs->last_packet_channel_count = opus_packet_get_nb_channels(msg->data); - - cs->last_packet_frame_duration = - ( opus_packet_get_samples_per_frame(msg->data, cs->last_packet_sampling_rate) * 1000 ) - / cs->last_packet_sampling_rate; - - /* TODO FIXME WARNING calculate properly according to propper channel count */ - cs->last_packet_frame_duration /= cs->last_packet_channel_count; } else { LOGGER_WARNING("Failed to load packet values!"); rtp_free_msg(NULL, msg); continue; - } + }*/ + - rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, 5760, 0); + /* 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); + 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) { - /* Play */ - LOGGER_DEBUG("Playing audio frame size: %d; channels: %d; srate: %d; duration %d", rc, - cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->last_packet_frame_duration); - cs->acb.first(cs->av, cs->friend_id, tmp, rc, - cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->acb.second); + /* Extract channels */ + int16_t left[rc/2]; + int16_t right[rc/2]; + int i = 0; + for (; i < rc/2; i ++) { + left[i] = tmp[i * 2]; + right[i] = tmp[(i * 2) + 1]; + } + + if (memcmp(left, right, sizeof(int16_t)) == 0) { + cs->last_packet_channel_count = 1; + cs->last_packet_frame_duration = (rc * 1000) / cs->last_packet_sampling_rate * cs->last_packet_channel_count; + + LOGGER_DEBUG("Playing mono audio frame size: %d; srate: %d; duration %d", rc, + cs->last_packet_sampling_rate, cs->last_packet_frame_duration); + + cs->acb.first(cs->av, cs->friend_id, right, rc / 2, + cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->acb.second); + } else { + cs->last_packet_channel_count = 2; + cs->last_packet_frame_duration = (rc * 1000) / cs->last_packet_sampling_rate * cs->last_packet_channel_count; + + LOGGER_DEBUG("Playing stereo audio frame size: %d; channels: %d; srate: %d; duration %d", rc, + cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->last_packet_frame_duration); + + cs->acb.first(cs->av, cs->friend_id, tmp, rc, + cs->last_packet_channel_count, cs->last_packet_sampling_rate, cs->acb.second); + } } LOGGED_LOCK(cs->queue_mutex); @@ -438,7 +458,7 @@ CSession *cs_new(uint32_t peer_video_frame_piece_size) */ int status; - cs->audio_decoder = opus_decoder_create(48000, 1, &status ); /* NOTE: Must be mono */ + cs->audio_decoder = opus_decoder_create(48000, 2, &status ); /* NOTE: Must be stereo */ if ( status != OPUS_OK ) { LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); @@ -482,7 +502,7 @@ CSession *cs_new(uint32_t peer_video_frame_piece_size) goto FAILURE; cs->linfts = current_time_monotonic(); - cs->lcfd = 10; + cs->lcfd = 60; /*++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* Initialize encoders with default values */ @@ -579,6 +599,8 @@ const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) 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; diff --git a/toxav/codec.h b/toxav/codec.h index 93b08cd2..4e2b995b 100644 --- a/toxav/codec.h +++ b/toxav/codec.h @@ -42,6 +42,9 @@ #define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; } +#define PACKED_AUDIO_SIZE(x) (x + 5) +#define UNPACKED_AUDIO_SIZE(x) (x - 5) + typedef struct CSession_s { /* VIDEO diff --git a/toxav/toxav.c b/toxav/toxav.c index 0f16fde2..12f8b561 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c @@ -223,7 +223,7 @@ void toxav_iterate(ToxAV* av) 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); + rc = MIN(i->cs->lcfd, rc); /* TODO handle on/off */ uint32_t fid = i->friend_id; @@ -712,9 +712,11 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc goto END; } - LOGGER_DEBUG("Sending audio frame size: %d; channels: %d; srate: %d", sample_count, channels, sampling_rate); - uint8_t dest[sample_count * channels * sizeof(int16_t)]; - int vrc = opus_encode(call->cs->audio_encoder, pcm, sample_count, dest, sizeof (dest)); + uint8_t dest[sample_count * channels + 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->cs->audio_encoder, pcm, sample_count, dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); if (vrc < 0) { LOGGER_WARNING("Failed to encode frame"); @@ -723,7 +725,10 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc goto END; } - if (rtp_send_msg(call->rtps[audio_index], dest, vrc) != 0) { + 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) { LOGGER_WARNING("Failed to send audio packet"); rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; } diff --git a/toxcore/logger.c b/toxcore/logger.c index e8aef7e0..fc6a989a 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c @@ -200,12 +200,12 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con #endif static const char *logger_format = - "%s " /* Logger id string */ - "%-16s" /* Time string of format: %m:%d %H:%M:%S */ - "%u " /* Thread id */ - "%-5s " /* Logger lever string */ - "%-20s " /* File:line string */ - "- %s" /* Output message */ + "%s " /* Logger id string */ + "%-16s" /* Time string of format: %m:%d %H:%M:%S */ + "%-12u " /* Thread id */ + "%-5s " /* Logger lever string */ + "%-20s " /* File:line string */ + "- %s" /* Output message */ WIN_CR "\n"; /* Every new print new line */ -- 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 'toxcore/logger.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