summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEniz Vukovic <eniz_vukovic@hotmail.com>2015-10-10 23:54:23 +0200
committerEniz Vukovic <eniz_vukovic@hotmail.com>2015-10-10 23:54:23 +0200
commitd6fdf16520b6f242935ca95eeb739ec9a8eaa14c (patch)
tree069f3355835aa0497fe494c36554ea24d0b222f1
parentbf5e9b89d2a67c293aae503c03e193307ea7990b (diff)
New Adaptive BR algorithm, cleanups and fixes
-rw-r--r--auto_tests/toxav_basic_test.c30
-rw-r--r--auto_tests/toxav_many_test.c26
-rw-r--r--configure.ac14
-rw-r--r--other/apidsl/toxav.in.h128
-rw-r--r--testing/av_test.c74
-rw-r--r--toxav/Makefile.inc61
-rw-r--r--toxav/audio.c282
-rw-r--r--toxav/audio.h65
-rw-r--r--toxav/bwcontroler.c207
-rw-r--r--toxav/bwcontroler.h37
-rw-r--r--toxav/group.c12
-rw-r--r--toxav/msi.c620
-rw-r--r--toxav/msi.h35
-rw-r--r--toxav/rtp.c744
-rw-r--r--toxav/rtp.h139
-rw-r--r--toxav/toxav.c1178
-rw-r--r--toxav/toxav.h226
-rw-r--r--toxav/toxav_old.c4
-rw-r--r--toxav/video.c292
-rw-r--r--toxav/video.h65
-rw-r--r--toxcore/Messenger.c6
-rw-r--r--toxcore/assoc.c8
-rw-r--r--toxcore/assoc.h4
-rw-r--r--toxcore/logger.c18
-rw-r--r--toxcore/logger.h19
-rw-r--r--toxcore/network.c12
-rw-r--r--toxcore/util.c29
-rw-r--r--toxcore/util.h6
28 files changed, 2005 insertions, 2336 deletions
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 @@
2#include "config.h" 2#include "config.h"
3#endif 3#endif
4 4
5#ifndef HAVE_LIBCHECK
6# include <assert.h>
7
8# define ck_assert(X) assert(X);
9# define START_TEST(NAME) void NAME ()
10# define END_TEST
11#else
12# include "helpers.h"
13#endif
14
5#include <sys/types.h> 15#include <sys/types.h>
6#include <stdint.h> 16#include <stdint.h>
7#include <string.h> 17#include <string.h>
8#include <stdio.h> 18#include <stdio.h>
9#include <check.h>
10#include <stdlib.h> 19#include <stdlib.h>
11#include <time.h> 20#include <time.h>
12 21
@@ -18,7 +27,6 @@
18#include "../toxcore/crypto_core.h" 27#include "../toxcore/crypto_core.h"
19#include "../toxav/toxav.h" 28#include "../toxav/toxav.h"
20 29
21#include "helpers.h"
22 30
23#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) 31#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
24#define c_sleep(x) Sleep(1*x) 32#define c_sleep(x) Sleep(1*x)
@@ -462,19 +470,19 @@ START_TEST(test_AV_flows)
462 470
463 printf("Call started as audio only\n"); 471 printf("Call started as audio only\n");
464 printf("Turning on video for Alice...\n"); 472 printf("Turning on video for Alice...\n");
465 ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 1000, false, NULL)); 473 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL));
466 474
467 iterate_tox(bootstrap, Alice, Bob); 475 iterate_tox(bootstrap, Alice, Bob);
468 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V); 476 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V);
469 477
470 printf("Turning off video for Alice...\n"); 478 printf("Turning off video for Alice...\n");
471 ck_assert(toxav_video_bit_rate_set(AliceAV, 0, 0, false, NULL)); 479 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL));
472 480
473 iterate_tox(bootstrap, Alice, Bob); 481 iterate_tox(bootstrap, Alice, Bob);
474 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V)); 482 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V));
475 483
476 printf("Turning off audio for Alice...\n"); 484 printf("Turning off audio for Alice...\n");
477 ck_assert(toxav_audio_bit_rate_set(AliceAV, 0, 0, false, NULL)); 485 ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL));
478 486
479 iterate_tox(bootstrap, Alice, Bob); 487 iterate_tox(bootstrap, Alice, Bob);
480 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A)); 488 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A));
@@ -564,7 +572,16 @@ START_TEST(test_AV_flows)
564} 572}
565END_TEST 573END_TEST
566 574
567 575#ifndef HAVE_LIBCHECK
576int main(int argc, char *argv[])
577{
578 (void) argc;
579 (void) argv;
580
581 test_AV_flows();
582 return 0;
583}
584#else
568Suite *tox_suite(void) 585Suite *tox_suite(void)
569{ 586{
570 Suite *s = suite_create("ToxAV"); 587 Suite *s = suite_create("ToxAV");
@@ -589,3 +606,4 @@ int main(int argc, char *argv[])
589 606
590 return number_failed; 607 return number_failed;
591} 608}
609#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 @@
2#include "config.h" 2#include "config.h"
3#endif 3#endif
4 4
5#ifndef HAVE_LIBCHECK
6# include <assert.h>
7
8# define ck_assert(X) assert(X);
9# define START_TEST(NAME) void NAME ()
10# define END_TEST
11#else
12# include "helpers.h"
13#endif
14
5#include <sys/types.h> 15#include <sys/types.h>
6#include <stdint.h> 16#include <stdint.h>
7#include <string.h> 17#include <string.h>
8#include <stdio.h> 18#include <stdio.h>
9#include <check.h>
10#include <stdlib.h> 19#include <stdlib.h>
11#include <time.h> 20#include <time.h>
12 21
13#include <vpx/vpx_image.h> 22#include <vpx/vpx_image.h>
14 23
15#include "helpers.h"
16
17#include "../toxcore/tox.h" 24#include "../toxcore/tox.h"
18#include "../toxcore/util.h" 25#include "../toxcore/util.h"
19#include "../toxcore/logger.h" 26#include "../toxcore/logger.h"
@@ -331,8 +338,16 @@ START_TEST(test_AV_three_calls)
331END_TEST 338END_TEST
332 339
333 340
334 341#ifndef HAVE_LIBCHECK
335 342int main(int argc, char *argv[])
343{
344 (void) argc;
345 (void) argv;
346
347 test_AV_three_calls();
348 return 0;
349}
350#else
336Suite *tox_suite(void) 351Suite *tox_suite(void)
337{ 352{
338 Suite *s = suite_create("ToxAV"); 353 Suite *s = suite_create("ToxAV");
@@ -362,3 +377,4 @@ int main(int argc, char *argv[])
362 377
363 return number_failed; 378 return number_failed;
364} 379}
380#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"
33BUILD_AV="yes" 33BUILD_AV="yes"
34BUILD_TESTING="yes" 34BUILD_TESTING="yes"
35 35
36LOGGING="no" 36TOX_LOGGER="no"
37LOGGING_OUTNAM="libtoxcore.log" 37LOGGING_OUTNAM="libtoxcore.log"
38 38
39NCURSES_FOUND="no" 39NCURSES_FOUND="no"
@@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir],
82 ] 82 ]
83) 83)
84 84
85AC_ARG_ENABLE([log], 85AC_ARG_ENABLE([logger],
86 [AC_HELP_STRING([--enable-log], [enable logging (default: auto)]) ], 86 [AC_HELP_STRING([--enable-logger], [enable logging (default: auto)]) ],
87 [ 87 [
88 if test "x$enableval" = "xyes"; then 88 if test "x$enableval" = "xyes"; then
89 LOGGING="yes" 89 TOX_LOGGER="yes"
90 90
91 AC_DEFINE([LOGGING], [], [If logging enabled]) 91 AC_DEFINE([TOX_LOGGER], [], [If logging enabled])
92 AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value]) 92 AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value])
93 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger]) 93 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger])
94 fi 94 fi
@@ -99,7 +99,7 @@ AC_ARG_WITH(log-level,
99 AC_HELP_STRING([--with-log-level=LEVEL], 99 AC_HELP_STRING([--with-log-level=LEVEL],
100 [Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]), 100 [Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]),
101 [ 101 [
102 if test "x$LOGGING" = "xno"; then 102 if test "x$TOX_LOGGER" = "xno"; then
103 AC_MSG_WARN([Logging disabled!]) 103 AC_MSG_WARN([Logging disabled!])
104 else 104 else
105 if test "x$withval" = "xTRACE"; then 105 if test "x$withval" = "xTRACE"; then
@@ -127,7 +127,7 @@ AC_ARG_WITH(log-path,
127 AC_HELP_STRING([--with-log-path=DIR], 127 AC_HELP_STRING([--with-log-path=DIR],
128 [Path of logger output]), 128 [Path of logger output]),
129 [ 129 [
130 if test "x$LOGGING" = "xno"; then 130 if test "x$TOX_LOGGER" = "xno"; then
131 AC_MSG_WARN([Logging disabled!]) 131 AC_MSG_WARN([Logging disabled!])
132 else 132 else
133 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger]) 133 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" {
56/** \subsection threading Threading implications 56/** \subsection threading Threading implications
57 * 57 *
58 * Unlike the Core API, this API is fully thread-safe. The library will ensure 58 * Unlike the Core API, this API is fully thread-safe. The library will ensure
59 * the proper synchronisation of parallel calls. 59 * the proper synchronization of parallel calls.
60 * 60 *
61 * A common way to run ToxAV (multiple or single instance) is to have a thread, 61 * A common way to run ToxAV (multiple or single instance) is to have a thread,
62 * separate from tox instance thread, running a simple ${toxAV.iterate} loop, 62 * separate from tox instance thread, running a simple ${toxAV.iterate} loop,
63 * sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration. 63 * sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration.
64 * 64 *
65 * An important thing to note is that events are triggered from both tox and
66 * toxav thread (see above). audio and video receive frame events are triggered
67 * from toxav thread while all the other events are triggered from tox thread.
68 *
69 * Tox thread has priority with mutex mechanisms. Any api function can
70 * fail if mutexes are held by tox thread in which case they will set SYNC
71 * error code.
65 */ 72 */
66 73
67/** 74/**
@@ -232,6 +239,10 @@ bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_ra
232 */ 239 */
233 MALLOC, 240 MALLOC,
234 /** 241 /**
242 * Synchronization error occurred.
243 */
244 SYNC,
245 /**
235 * The friend number did not designate a valid friend. 246 * The friend number did not designate a valid friend.
236 */ 247 */
237 FRIEND_NOT_FOUND, 248 FRIEND_NOT_FOUND,
@@ -274,6 +285,10 @@ event call {
274 */ 285 */
275bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) { 286bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
276 /** 287 /**
288 * Synchronization error occurred.
289 */
290 SYNC,
291 /**
277 * Failed to initialize codecs for call session. Note that codec initiation 292 * Failed to initialize codecs for call session. Note that codec initiation
278 * will fail if there is no receive callback registered for either audio or 293 * will fail if there is no receive callback registered for either audio or
279 * video. 294 * video.
@@ -347,7 +362,7 @@ event call_state {
347 * 362 *
348 ******************************************************************************/ 363 ******************************************************************************/
349enum class CALL_CONTROL { 364enum class CALL_CONTROL {
350 /** 365 /**
351 * Resume a previously paused call. Only valid if the pause was caused by this 366 * Resume a previously paused call. Only valid if the pause was caused by this
352 * client, if not, this control is ignored. Not valid before the call is accepted. 367 * client, if not, this control is ignored. Not valid before the call is accepted.
353 */ 368 */
@@ -393,6 +408,10 @@ enum class CALL_CONTROL {
393 */ 408 */
394bool call_control (uint32_t friend_number, CALL_CONTROL control) { 409bool call_control (uint32_t friend_number, CALL_CONTROL control) {
395 /** 410 /**
411 * Synchronization error occurred.
412 */
413 SYNC,
414 /**
396 * The friend_number passed did not designate a valid friend. 415 * The friend_number passed did not designate a valid friend.
397 */ 416 */
398 FRIEND_NOT_FOUND, 417 FRIEND_NOT_FOUND,
@@ -412,38 +431,7 @@ bool call_control (uint32_t friend_number, CALL_CONTROL control) {
412 * :: Controlling bit rates 431 * :: Controlling bit rates
413 * 432 *
414 ******************************************************************************/ 433 ******************************************************************************/
415error for set_bit_rate { 434namespace bit_rate {
416 /**
417 * The bit rate passed was not one of the supported values.
418 */
419 INVALID,
420 /**
421 * The friend_number passed did not designate a valid friend.
422 */
423 FRIEND_NOT_FOUND,
424 /**
425 * This client is currently not in a call with the friend.
426 */
427 FRIEND_NOT_IN_CALL,
428}
429namespace audio {
430 namespace bit_rate {
431 event status {
432 /**
433 * The function type for the ${event status} callback.
434 *
435 * @param friend_number The friend number of the friend for which to set the
436 * audio bit rate.
437 * @param stable Is the stream stable enough to keep the bit rate.
438 * Upon successful, non forceful, bit rate change, this is set to
439 * true and 'bit_rate' is set to new bit rate.
440 * The stable is set to false with bit_rate set to the unstable
441 * bit rate when either current stream is unstable with said bit rate
442 * or the non forceful change failed.
443 * @param bit_rate The bit rate in Kb/sec.
444 */
445 typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
446 }
447 /** 435 /**
448 * Set the audio bit rate to be used in subsequent audio frames. If the passed 436 * Set the audio bit rate to be used in subsequent audio frames. If the passed
449 * bit rate is the same as the current bit rate this function will return true 437 * bit rate is the same as the current bit rate this function will return true
@@ -452,51 +440,43 @@ namespace audio {
452 * forcefully set and the previous non forceful request is cancelled. The active 440 * forcefully set and the previous non forceful request is cancelled. The active
453 * non forceful setup will be canceled in favour of new non forceful setup. 441 * non forceful setup will be canceled in favour of new non forceful setup.
454 * 442 *
455 * @param friend_number The friend number of the friend for which to set the 443 * @param friend_number The friend number.
456 * audio bit rate.
457 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable 444 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
458 * audio sending. 445 * audio sending. Set to -1 to leave unchanged.
459 * @param force True if the bit rate change is forceful. 446 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
447 * video sending. Set to -1 to leave unchanged.
460 * 448 *
461 */ 449 */
462 bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate; 450 bool set(uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate) {
463 } 451 /**
464} 452 * Synchronization error occurred.
465namespace video { 453 */
466 namespace bit_rate { 454 SYNC,
455 /**
456 * The bit rate passed was not one of the supported values.
457 */
458 INVALID,
459 /**
460 * The friend_number passed did not designate a valid friend.
461 */
462 FRIEND_NOT_FOUND,
463 /**
464 * This client is currently not in a call with the friend.
465 */
466 FRIEND_NOT_IN_CALL,
467 }
467 event status { 468 event status {
468 /** 469 /**
469 * The function type for the ${event status} callback. 470 * The function type for the ${event status} callback. The event is triggered
470 * 471 * when the network becomes too saturated for current bit rates at which
471 * @param friend_number The friend number of the friend for which to set the 472 * point core suggests new bit rates.
472 * video bit rate. 473 *
473 * @param stable Is the stream stable enough to keep the bit rate. 474 * @param friend_number The friend number.
474 * Upon successful, non forceful, bit rate change, this is set to 475 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
475 * true and 'bit_rate' is set to new bit rate. 476 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
476 * The stable is set to false with bit_rate set to the unstable 477 */
477 * bit rate when either current stream is unstable with said bit rate 478 typedef void(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate);
478 * or the non forceful change failed.
479 * @param bit_rate The bit rate in Kb/sec.
480 */
481 typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
482 } 479 }
483 /**
484 * Set the video bit rate to be used in subsequent video frames. If the passed
485 * bit rate is the same as the current bit rate this function will return true
486 * without calling a callback. If there is an active non forceful setup with the
487 * passed video bit rate and the new set request is forceful, the bit rate is
488 * forcefully set and the previous non forceful request is cancelled. The active
489 * non forceful setup will be canceled in favour of new non forceful setup.
490 *
491 * @param friend_number The friend number of the friend for which to set the
492 * video bit rate.
493 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
494 * video sending.
495 * @param force True if the bit rate change is forceful.
496 *
497 */
498 bool set(uint32_t friend_number, uint32_t video_bit_rate, bool force) with error for set_bit_rate;
499 }
500} 480}
501/******************************************************************************* 481/*******************************************************************************
502 * 482 *
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 @@
28#include "../toxcore/util.h" 28#include "../toxcore/util.h"
29#include "../toxcore/network.h" /* current_time_monotonic() */ 29#include "../toxcore/network.h" /* current_time_monotonic() */
30 30
31#define LOGGING
32#include "../toxcore/logger.h"
33
34/* Playing audio data */ 31/* Playing audio data */
35#include <portaudio.h> 32#include <portaudio.h>
36/* Reading audio */ 33/* Reading audio */
@@ -53,21 +50,21 @@
53#define c_sleep(x) usleep(1000*x) 50#define c_sleep(x) usleep(1000*x)
54 51
55 52
56#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) 53#define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X)
57 54
58// RGB -> YUV 55// RGB -> YUV
59#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) 56#define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
60#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) 57#define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
61#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) 58#define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
62 59
63// YUV -> RGB 60// YUV -> RGB
64#define C(Y) ( (Y) - 16 ) 61#define C(Y) ((Y) - 16 )
65#define D(U) ( (U) - 128 ) 62#define D(U) ((U) - 128 )
66#define E(V) ( (V) - 128 ) 63#define E(V) ((V) - 128 )
67 64
68#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8) 65#define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8)
69#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) 66#define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
70#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) 67#define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8)
71 68
72 69
73#define TEST_TRANSFER_A 0 70#define TEST_TRANSFER_A 0
@@ -182,21 +179,11 @@ void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
182 free(rb_write(cc->arb, f)); 179 free(rb_write(cc->arb, f));
183 pthread_mutex_unlock(cc->arb_mutex); 180 pthread_mutex_unlock(cc->arb_mutex);
184} 181}
185void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, 182void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
186 bool stable, uint32_t bit_rate, void *user_data) 183 uint32_t audio_bit_rate, uint32_t video_bit_rate,
187{ 184 void *user_data)
188 if (stable)
189 printf ("Set new audio bit rate to: %d\n", bit_rate);
190 else
191 printf ("The network is overly saturated with audio bit rate at: %d\n", bit_rate);
192}
193void t_toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
194 bool stable, uint32_t bit_rate, void *user_data)
195{ 185{
196 if (stable) 186 printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate);
197 printf ("Set new video bit rate to: %d", bit_rate);
198 else
199 printf ("The network is overly saturated with video bit rate at: %d", bit_rate);
200} 187}
201void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) 188void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
202{ 189{
@@ -216,6 +203,7 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
216 tox_options_default(&opts); 203 tox_options_default(&opts);
217 204
218 opts.end_port = 0; 205 opts.end_port = 0;
206 opts.ipv6_enabled = false;
219 207
220 { 208 {
221 TOX_ERR_NEW error; 209 TOX_ERR_NEW error;
@@ -279,18 +267,16 @@ void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxA
279 /* Alice */ 267 /* Alice */
280 toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC); 268 toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
281 toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC); 269 toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
270 toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC);
282 toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC); 271 toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
283 toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC); 272 toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
284 toxav_callback_video_bit_rate_status(*AliceAV, t_toxav_video_bit_rate_status_cb, AliceCC);
285 toxav_callback_audio_bit_rate_status(*AliceAV, t_toxav_audio_bit_rate_status_cb, AliceCC);
286 273
287 /* Bob */ 274 /* Bob */
288 toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC); 275 toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
289 toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC); 276 toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
277 toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC);
290 toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC); 278 toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
291 toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC); 279 toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
292 toxav_callback_video_bit_rate_status(*BobAV, t_toxav_video_bit_rate_status_cb, BobCC);
293 toxav_callback_audio_bit_rate_status(*BobAV, t_toxav_audio_bit_rate_status_cb, BobCC);
294 280
295 281
296 printf("Created 2 instances of ToxAV\n"); 282 printf("Created 2 instances of ToxAV\n");
@@ -320,6 +306,9 @@ void* iterate_toxav (void * data)
320 fflush(stdout); 306 fflush(stdout);
321 307
322#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1 308#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
309 if (!rc)
310 rc = 1;
311
323 cvWaitKey(rc); 312 cvWaitKey(rc);
324#else 313#else
325 c_sleep(rc); 314 c_sleep(rc);
@@ -340,8 +329,8 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
340 int32_t strides[3] = { 1280, 640, 640 }; 329 int32_t strides[3] = { 1280, 640, 640 };
341 uint8_t* planes[3] = { 330 uint8_t* planes[3] = {
342 malloc(img->height * img->width), 331 malloc(img->height * img->width),
343 malloc(img->height * img->width / 2), 332 malloc(img->height * img->width / 4),
344 malloc(img->height * img->width / 2), 333 malloc(img->height * img->width / 4),
345 }; 334 };
346 335
347 int x_chroma_shift = 1; 336 int x_chroma_shift = 1;
@@ -363,9 +352,9 @@ int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
363 } 352 }
364 } 353 }
365 } 354 }
366 355
367 356 int rc = toxav_video_send_frame(av, friend_number, img->width, img->height,
368 int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL); 357 planes[0], planes[1], planes[2], NULL);
369 free(planes[0]); 358 free(planes[0]);
370 free(planes[1]); 359 free(planes[1]);
371 free(planes[2]); 360 free(planes[2]);
@@ -396,9 +385,8 @@ int print_help (const char* name)
396 return 0; 385 return 0;
397} 386}
398 387
399
400int main (int argc, char** argv) 388int main (int argc, char** argv)
401{ 389{
402 freopen("/dev/zero", "w", stderr); 390 freopen("/dev/zero", "w", stderr);
403 Pa_Initialize(); 391 Pa_Initialize();
404 392
@@ -585,7 +573,7 @@ int main (int argc, char** argv)
585 err = Pa_StartStream(adout); 573 err = Pa_StartStream(adout);
586 assert(err == paNoError); 574 assert(err == paNoError);
587 575
588 toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL); 576// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
589 577
590 /* Start write thread */ 578 /* Start write thread */
591 pthread_t t; 579 pthread_t t;
@@ -593,7 +581,7 @@ int main (int argc, char** argv)
593 pthread_detach(t); 581 pthread_detach(t);
594 582
595 printf("Sample rate %d\n", af_info.samplerate); 583 printf("Sample rate %d\n", af_info.samplerate);
596 while ( start_time + expected_time > time(NULL) ) { 584 while (start_time + expected_time > time(NULL) ) {
597 uint64_t enc_start_time = current_time_monotonic(); 585 uint64_t enc_start_time = current_time_monotonic();
598 int64_t count = sf_read_short(af_handle, PCM, frame_size); 586 int64_t count = sf_read_short(af_handle, PCM, frame_size);
599 if (count > 0) { 587 if (count > 0) {
@@ -674,7 +662,7 @@ int main (int argc, char** argv)
674 iterate_tox(bootstrap, AliceAV, BobAV); 662 iterate_tox(bootstrap, AliceAV, BobAV);
675 663
676 /* Start decode thread */ 664 /* Start decode thread */
677 struct toxav_thread_data data = { 665 struct toxav_thread_data data = {
678 .AliceAV = AliceAV, 666 .AliceAV = AliceAV,
679 .BobAV = BobAV, 667 .BobAV = BobAV,
680 .sig = 0 668 .sig = 0
@@ -694,13 +682,13 @@ int main (int argc, char** argv)
694 682
695 time_t start_time = time(NULL); 683 time_t start_time = time(NULL);
696 while(start_time + 90 > time(NULL)) { 684 while(start_time + 90 > time(NULL)) {
697 IplImage* frame = cvQueryFrame( capture ); 685 IplImage* frame = cvQueryFrame(capture );
698 if (!frame) 686 if (!frame)
699 break; 687 break;
700 688
701 send_opencv_img(AliceAV, 0, frame); 689 send_opencv_img(AliceAV, 0, frame);
702 iterate_tox(bootstrap, AliceAV, BobAV); 690 iterate_tox(bootstrap, AliceAV, BobAV);
703 c_sleep(video_frame_duration); 691 c_sleep(10);
704 } 692 }
705 693
706 cvReleaseCapture(&capture); 694 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 @@
1if BUILD_AV 1if BUILD_AV
2 2
3lib_LTLIBRARIES += libtoxav.la 3lib_LTLIBRARIES += libtoxav.la
4libtoxav_la_include_HEADERS = ../toxav/toxav.h 4 libtoxav_la_include_HEADERS = ../toxav/toxav.h
5libtoxav_la_includedir = $(includedir)/tox 5 libtoxav_la_includedir = $(includedir)/tox
6 6
7libtoxav_la_SOURCES = ../toxav/rtp.h \ 7libtoxav_la_SOURCES = ../toxav/rtp.h \
8 ../toxav/rtp.c \ 8 ../toxav/rtp.c \
9 ../toxav/msi.h \ 9 ../toxav/msi.h \
10 ../toxav/msi.c \ 10 ../toxav/msi.c \
11 ../toxav/group.h \ 11 ../toxav/group.h \
12 ../toxav/group.c \ 12 ../toxav/group.c \
13 ../toxav/audio.h \ 13 ../toxav/audio.h \
14 ../toxav/audio.c \ 14 ../toxav/audio.c \
15 ../toxav/video.h \ 15 ../toxav/video.h \
16 ../toxav/video.c \ 16 ../toxav/video.c \
17 ../toxav/toxav.h \ 17 ../toxav/bwcontroler.h \
18 ../toxav/toxav.c \ 18 ../toxav/bwcontroler.c \
19 ../toxav/toxav_old.h \ 19 ../toxav/toxav.h \
20 ../toxav/toxav_old.c 20 ../toxav/toxav.c \
21 21 ../toxav/toxav_old.h \
22 ../toxav/toxav_old.c
22 23
23libtoxav_la_CFLAGS = -I../toxcore \ 24libtoxav_la_CFLAGS = -I../toxcore \
24 -I../toxav \ 25 -I../toxav \
25 $(LIBSODIUM_CFLAGS) \ 26 $(LIBSODIUM_CFLAGS) \
26 $(NACL_CFLAGS) \ 27 $(NACL_CFLAGS) \
27 $(AV_CFLAGS) \ 28 $(AV_CFLAGS) \
28 $(PTHREAD_CFLAGS) 29 $(PTHREAD_CFLAGS)
29 30
30libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ 31libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
31 $(LIBSODIUM_LDFLAGS) \ 32 $(LIBSODIUM_LDFLAGS) \
32 $(NACL_LDFLAGS) \ 33 $(NACL_LDFLAGS) \
33 $(EXTRA_LT_LDFLAGS) \ 34 $(EXTRA_LT_LDFLAGS) \
34 $(WINSOCK2_LIBS) 35 $(WINSOCK2_LIBS)
35 36
36libtoxav_la_LIBADD = libtoxcore.la \ 37libtoxav_la_LIBADD = libtoxcore.la \
37 $(LIBSODIUM_LIBS) \ 38 $(LIBSODIUM_LIBS) \
38 $(NACL_LIBS) \ 39 $(NACL_LIBS) \
39 $(PTHREAD_LIBS) \ 40 $(PTHREAD_LIBS) \
40 $(AV_LIBS) 41 $(AV_LIBS)
41 42
42endif \ No newline at end of file 43endif \ 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 @@
19 * 19 *
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
22#include <stdlib.h> 26#include <stdlib.h>
23 27
24#include "audio.h" 28#include "audio.h"
@@ -29,80 +33,71 @@
29static struct JitterBuffer *jbuf_new(uint32_t capacity); 33static struct JitterBuffer *jbuf_new(uint32_t capacity);
30static void jbuf_clear(struct JitterBuffer *q); 34static void jbuf_clear(struct JitterBuffer *q);
31static void jbuf_free(struct JitterBuffer *q); 35static void jbuf_free(struct JitterBuffer *q);
32static int jbuf_write(struct JitterBuffer *q, RTPMessage *m); 36static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m);
33static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success); 37static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
34OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count); 38OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
35bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, 39bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
36 int32_t *old_br, int32_t *old_sr, int32_t *old_ch); 40 int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
37bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels); 41bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels);
38 42
39 43
40 44
41ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data) 45ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
42{ 46{
43 ACSession *ac = calloc(sizeof(ACSession), 1); 47 ACSession *ac = calloc(sizeof(ACSession), 1);
44 48
45 if (!ac) { 49 if (!ac) {
46 LOGGER_WARNING("Allocation failed! Application might misbehave!"); 50 LOGGER_WARNING("Allocation failed! Application might misbehave!");
47 return NULL; 51 return NULL;
48 } 52 }
49 53
50 if (create_recursive_mutex(ac->queue_mutex) != 0) { 54 if (create_recursive_mutex(ac->queue_mutex) != 0) {
51 LOGGER_WARNING("Failed to create recursive mutex!"); 55 LOGGER_WARNING("Failed to create recursive mutex!");
52 free(ac); 56 free(ac);
53 return NULL; 57 return NULL;
54 } 58 }
55 59
56 int status; 60 int status;
57 ac->decoder = opus_decoder_create(48000, 2, &status ); 61 ac->decoder = opus_decoder_create(48000, 2, &status);
58 62
59 if ( status != OPUS_OK ) { 63 if (status != OPUS_OK) {
60 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); 64 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
61 goto BASE_CLEANUP; 65 goto BASE_CLEANUP;
62 } 66 }
63 67
64 if ( !(ac->j_buf = jbuf_new(3)) ) { 68 if (!(ac->j_buf = jbuf_new(3))) {
65 LOGGER_WARNING("Jitter buffer creaton failed!"); 69 LOGGER_WARNING("Jitter buffer creaton failed!");
66 opus_decoder_destroy(ac->decoder); 70 opus_decoder_destroy(ac->decoder);
67 goto BASE_CLEANUP; 71 goto BASE_CLEANUP;
68 } 72 }
69 73
70 /* Initialize encoders with default values */ 74 /* Initialize encoders with default values */
71 ac->encoder = create_audio_encoder(48000, 48000, 2); 75 ac->encoder = create_audio_encoder(48000, 48000, 2);
76
72 if (ac->encoder == NULL) 77 if (ac->encoder == NULL)
73 goto DECODER_CLEANUP; 78 goto DECODER_CLEANUP;
74 79
75 ac->test_encoder = create_audio_encoder(48000, 48000, 2); 80 ac->le_bit_rate = 48000;
76 if (ac->test_encoder == NULL) { 81 ac->le_sample_rate = 48000;
77 opus_encoder_destroy(ac->encoder); 82 ac->le_channel_count = 2;
78 goto DECODER_CLEANUP;
79 }
80
81 ac->last_encoding_bit_rate = 48000;
82 ac->last_encoding_sampling_rate = 48000;
83 ac->last_encoding_channel_count = 2;
84
85 ac->last_test_encoding_bit_rate = 48000;
86 ac->last_test_encoding_sampling_rate = 48000;
87 ac->last_test_encoding_channel_count = 2;
88
89 ac->last_decoding_channel_count = 2;
90 ac->last_decoding_sampling_rate = 48000;
91 ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */
92 83
84 ac->ld_channel_count = 2;
85 ac->ld_sample_rate = 48000;
86 ac->ldrts = 0; /* Make it possible to reconfigure straight away */
87
93 /* These need to be set in order to properly 88 /* These need to be set in order to properly
94 * do error correction with opus */ 89 * do error correction with opus */
95 ac->last_packet_frame_duration = 120; 90 ac->lp_frame_duration = 120;
96 ac->last_packet_sampling_rate = 48000; 91 ac->lp_sampling_rate = 48000;
97 ac->last_packet_channel_count = 1; 92 ac->lp_channel_count = 1;
98 93
99 ac->av = av; 94 ac->av = av;
100 ac->friend_number = friend_number; 95 ac->friend_number = friend_number;
101 ac->acb.first = cb; 96 ac->acb.first = cb;
102 ac->acb.second = cb_data; 97 ac->acb.second = cb_data;
103 98
104 return ac; 99 return ac;
105 100
106DECODER_CLEANUP: 101DECODER_CLEANUP:
107 opus_decoder_destroy(ac->decoder); 102 opus_decoder_destroy(ac->decoder);
108 jbuf_free(ac->j_buf); 103 jbuf_free(ac->j_buf);
@@ -111,39 +106,41 @@ BASE_CLEANUP:
111 free(ac); 106 free(ac);
112 return NULL; 107 return NULL;
113} 108}
114void ac_kill(ACSession* ac) 109void ac_kill(ACSession *ac)
115{ 110{
116 if (!ac) 111 if (!ac)
117 return; 112 return;
118 113
119 opus_encoder_destroy(ac->encoder); 114 opus_encoder_destroy(ac->encoder);
120 opus_encoder_destroy(ac->test_encoder);
121 opus_decoder_destroy(ac->decoder); 115 opus_decoder_destroy(ac->decoder);
122 jbuf_free(ac->j_buf); 116 jbuf_free(ac->j_buf);
123 117
124 pthread_mutex_destroy(ac->queue_mutex); 118 pthread_mutex_destroy(ac->queue_mutex);
125 119
126 LOGGER_DEBUG("Terminated audio handler: %p", ac); 120 LOGGER_DEBUG("Terminated audio handler: %p", ac);
127 free(ac); 121 free(ac);
128} 122}
129void ac_do(ACSession* ac) 123void ac_iterate(ACSession *ac)
130{ 124{
131 if (!ac) 125 if (!ac)
132 return; 126 return;
127
128 /* TODO fix this and jitter buffering */
133 129
134 /* Enough space for the maximum frame size (120 ms 48 KHz audio) */ 130 /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */
135 int16_t tmp[5760 * 2]; 131 int16_t tmp[5760 * 2];
136 132
137 RTPMessage *msg; 133 struct RTPMessage *msg;
138 int rc = 0; 134 int rc = 0;
139 135
140 pthread_mutex_lock(ac->queue_mutex); 136 pthread_mutex_lock(ac->queue_mutex);
137
141 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) { 138 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
142 pthread_mutex_unlock(ac->queue_mutex); 139 pthread_mutex_unlock(ac->queue_mutex);
143 140
144 if (rc == 2) { 141 if (rc == 2) {
145 LOGGER_DEBUG("OPUS correction"); 142 LOGGER_DEBUG("OPUS correction");
146 int fs = (ac->last_packet_sampling_rate * ac->last_packet_frame_duration) / 1000; 143 int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000;
147 rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1); 144 rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
148 } else { 145 } else {
149 /* Get values from packet and decode. */ 146 /* Get values from packet and decode. */
@@ -156,98 +153,93 @@ void ac_do(ACSession* ac)
156 rtp_free_msg(msg); 153 rtp_free_msg(msg);
157 continue; 154 continue;
158 }*/ 155 }*/
159 156
160 157
161 /* Pick up sampling rate from packet */ 158 /* Pick up sampling rate from packet */
162 memcpy(&ac->last_packet_sampling_rate, msg->data, 4); 159 memcpy(&ac->lp_sampling_rate, msg->data, 4);
163 ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate); 160 ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate);
164 161
165 ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4); 162 ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
166 163
167 /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, 164 /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
168 * it didn't work quite well. 165 * it didn't work quite well.
169 */ 166 */
170 if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) { 167 if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
171 LOGGER_WARNING("Failed to reconfigure decoder!"); 168 LOGGER_WARNING("Failed to reconfigure decoder!");
172 rtp_free_msg(msg); 169 free(msg);
173 continue; 170 continue;
174 } 171 }
175 172
176 rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); 173 rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0);
177 rtp_free_msg(msg); 174 free(msg);
178 } 175 }
179 176
180 if (rc < 0) { 177 if (rc < 0) {
181 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); 178 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
182 } else if (ac->acb.first) { 179 } else if (ac->acb.first) {
183 ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate; 180 ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate;
184 181
185 ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->last_packet_channel_count, 182 ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count,
186 ac->last_packet_sampling_rate, ac->acb.second); 183 ac->lp_sampling_rate, ac->acb.second);
187 } 184 }
188 185
189 return; 186 return;
190 } 187 }
188
191 pthread_mutex_unlock(ac->queue_mutex); 189 pthread_mutex_unlock(ac->queue_mutex);
192} 190}
193int ac_queue_message(void* acp, struct RTPMessage_s *msg) 191int ac_queue_message(void *acp, struct RTPMessage *msg)
194{ 192{
195 if (!acp || !msg) 193 if (!acp || !msg)
196 return -1; 194 return -1;
197 195
198 if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeAudio + 2) % 128) { 196 if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
199 LOGGER_WARNING("Got dummy!"); 197 LOGGER_WARNING("Got dummy!");
200 rtp_free_msg(msg); 198 free(msg);
201 return 0; 199 return 0;
202 } 200 }
203 201
204 if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) { 202 if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) {
205 LOGGER_WARNING("Invalid payload type!"); 203 LOGGER_WARNING("Invalid payload type!");
206 rtp_free_msg(msg); 204 free(msg);
207 return -1; 205 return -1;
208 } 206 }
209 207
210 ACSession* ac = acp; 208 ACSession *ac = acp;
211 209
212 pthread_mutex_lock(ac->queue_mutex); 210 pthread_mutex_lock(ac->queue_mutex);
213 int rc = jbuf_write(ac->j_buf, msg); 211 int rc = jbuf_write(ac->j_buf, msg);
214 pthread_mutex_unlock(ac->queue_mutex); 212 pthread_mutex_unlock(ac->queue_mutex);
215 213
216 if (rc == -1) { 214 if (rc == -1) {
217 LOGGER_WARNING("Could not queue the message!"); 215 LOGGER_WARNING("Could not queue the message!");
218 rtp_free_msg(msg); 216 free(msg);
219 return -1; 217 return -1;
220 } 218 }
221 219
222 return 0; 220 return 0;
223} 221}
224int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels) 222int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
225{ 223{
226 if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels, 224 if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate,
227 &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count)) 225 sampling_rate, channels,
226 &ac->le_bit_rate,
227 &ac->le_sample_rate,
228 &ac->le_channel_count))
228 return -1; 229 return -1;
229 230
230 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels); 231 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
231 return 0; 232 return 0;
232} 233}
233int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
234{
235 if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bit_rate, sampling_rate, channels,
236 &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count))
237 return -1;
238
239 LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
240 return 0;
241}
242 234
243 235
244 236
245struct JitterBuffer { 237struct JitterBuffer {
246 RTPMessage **queue; 238 struct RTPMessage **queue;
247 uint32_t size; 239 uint32_t size;
248 uint32_t capacity; 240 uint32_t capacity;
249 uint16_t bottom; 241 uint16_t bottom;
250 uint16_t top; 242 uint16_t top;
251}; 243};
252 244
253static struct JitterBuffer *jbuf_new(uint32_t capacity) 245static struct JitterBuffer *jbuf_new(uint32_t capacity)
@@ -260,9 +252,9 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity)
260 252
261 struct JitterBuffer *q; 253 struct JitterBuffer *q;
262 254
263 if ( !(q = calloc(sizeof(struct JitterBuffer), 1)) ) return NULL; 255 if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL;
264 256
265 if (!(q->queue = calloc(sizeof(RTPMessage *), size))) { 257 if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) {
266 free(q); 258 free(q);
267 return NULL; 259 return NULL;
268 } 260 }
@@ -275,7 +267,7 @@ static void jbuf_clear(struct JitterBuffer *q)
275{ 267{
276 for (; q->bottom != q->top; ++q->bottom) { 268 for (; q->bottom != q->top; ++q->bottom) {
277 if (q->queue[q->bottom % q->size]) { 269 if (q->queue[q->bottom % q->size]) {
278 rtp_free_msg(q->queue[q->bottom % q->size]); 270 free(q->queue[q->bottom % q->size]);
279 q->queue[q->bottom % q->size] = NULL; 271 q->queue[q->bottom % q->size] = NULL;
280 } 272 }
281 } 273 }
@@ -288,15 +280,15 @@ static void jbuf_free(struct JitterBuffer *q)
288 free(q->queue); 280 free(q->queue);
289 free(q); 281 free(q);
290} 282}
291static int jbuf_write(struct JitterBuffer *q, RTPMessage *m) 283static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m)
292{ 284{
293 uint16_t sequnum = m->header->sequnum; 285 uint16_t sequnum = m->header.sequnum;
294 286
295 unsigned int num = sequnum % q->size; 287 unsigned int num = sequnum % q->size;
296 288
297 if ((uint32_t)(sequnum - q->bottom) > q->size) { 289 if ((uint32_t)(sequnum - q->bottom) > q->size) {
298 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q); 290 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
299 291
300 jbuf_clear(q); 292 jbuf_clear(q);
301 q->bottom = sequnum - q->capacity; 293 q->bottom = sequnum - q->capacity;
302 q->queue[num] = m; 294 q->queue[num] = m;
@@ -314,7 +306,7 @@ static int jbuf_write(struct JitterBuffer *q, RTPMessage *m)
314 306
315 return 0; 307 return 0;
316} 308}
317static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) 309static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
318{ 310{
319 if (q->top == q->bottom) { 311 if (q->top == q->bottom) {
320 *success = 0; 312 *success = 0;
@@ -324,7 +316,7 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
324 unsigned int num = q->bottom % q->size; 316 unsigned int num = q->bottom % q->size;
325 317
326 if (q->queue[num]) { 318 if (q->queue[num]) {
327 RTPMessage *ret = q->queue[num]; 319 struct RTPMessage *ret = q->queue[num];
328 q->queue[num] = NULL; 320 q->queue[num] = NULL;
329 ++q->bottom; 321 ++q->bottom;
330 *success = 1; 322 *success = 1;
@@ -340,73 +332,74 @@ static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
340 *success = 0; 332 *success = 0;
341 return NULL; 333 return NULL;
342} 334}
343OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count) 335OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
344{ 336{
345 int status = OPUS_OK; 337 int status = OPUS_OK;
346 OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status); 338 OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
347 339
348 if ( status != OPUS_OK ) { 340 if (status != OPUS_OK) {
349 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status)); 341 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
350 return NULL; 342 return NULL;
351 } 343 }
352 344
353 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate)); 345 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
354 346
355 if ( status != OPUS_OK ) { 347 if (status != OPUS_OK) {
356 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); 348 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
357 goto FAILURE; 349 goto FAILURE;
358 } 350 }
359 351
360 /* Enable in-band forward error correction in codec */ 352 /* Enable in-band forward error correction in codec */
361 status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1)); 353 status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1));
362 354
363 if ( status != OPUS_OK ) { 355 if (status != OPUS_OK) {
364 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); 356 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
365 goto FAILURE; 357 goto FAILURE;
366 } 358 }
367 359
368 /* Make codec resistant to up to 10% packet loss 360 /* Make codec resistant to up to 10% packet loss
369 * NOTE This could also be adjusted on the fly, rather than hard-coded, 361 * NOTE This could also be adjusted on the fly, rather than hard-coded,
370 * with feedback from the receiving client. 362 * with feedback from the receiving client.
371 */ 363 */
372 status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10)); 364 status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10));
373 365
374 if ( status != OPUS_OK ) { 366 if (status != OPUS_OK) {
375 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); 367 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
376 goto FAILURE; 368 goto FAILURE;
377 } 369 }
378 370
379 /* Set algorithm to the highest complexity, maximizing compression */ 371 /* Set algorithm to the highest complexity, maximizing compression */
380 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10)); 372 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
381 373
382 if ( status != OPUS_OK ) { 374 if (status != OPUS_OK) {
383 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); 375 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
384 goto FAILURE; 376 goto FAILURE;
385 } 377 }
386 378
387 return rc; 379 return rc;
388 380
389FAILURE: 381FAILURE:
390 opus_encoder_destroy(rc); 382 opus_encoder_destroy(rc);
391 return NULL; 383 return NULL;
392} 384}
393bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch, 385bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
394 int32_t* old_br, int32_t* old_sr, int32_t* old_ch) 386 int32_t *old_br, int32_t *old_sr, int32_t *old_ch)
395{ 387{
396 /* Values are checked in toxav.c */ 388 /* Values are checked in toxav.c */
397 if (*old_sr != new_sr || *old_ch != new_ch) { 389 if (*old_sr != new_sr || *old_ch != new_ch) {
398 OpusEncoder* new_encoder = create_audio_encoder(new_br, new_sr, new_ch); 390 OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
391
399 if (new_encoder == NULL) 392 if (new_encoder == NULL)
400 return false; 393 return false;
401 394
402 opus_encoder_destroy(*e); 395 opus_encoder_destroy(*e);
403 *e = new_encoder; 396 *e = new_encoder;
404 } else if (*old_br == new_br) 397 } else if (*old_br == new_br)
405 return true; /* Nothing changed */ 398 return true; /* Nothing changed */
406 else { 399 else {
407 int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br)); 400 int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
408 401
409 if ( status != OPUS_OK ) { 402 if (status != OPUS_OK) {
410 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status)); 403 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
411 return false; 404 return false;
412 } 405 }
@@ -415,31 +408,32 @@ bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr,
415 *old_br = new_br; 408 *old_br = new_br;
416 *old_sr = new_sr; 409 *old_sr = new_sr;
417 *old_ch = new_ch; 410 *old_ch = new_ch;
418 411
419 return true; 412 return true;
420} 413}
421bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels) 414bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels)
422{ 415{
423 if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) { 416 if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) {
424 if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500) 417 if (current_time_monotonic() - ac->ldrts < 500)
425 return false; 418 return false;
426 419
427 int status; 420 int status;
428 OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); 421 OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status);
429 if ( status != OPUS_OK ) { 422
423 if (status != OPUS_OK) {
430 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status)); 424 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
431 return false; 425 return false;
432 } 426 }
433 427
434 ac->last_decoding_sampling_rate = sampling_rate; 428 ac->ld_sample_rate = sampling_rate;
435 ac->last_decoding_channel_count = channels; 429 ac->ld_channel_count = channels;
436 ac->last_decoder_reconfiguration = current_time_monotonic(); 430 ac->ldrts = current_time_monotonic();
437 431
438 opus_decoder_destroy(ac->decoder); 432 opus_decoder_destroy(ac->decoder);
439 ac->decoder = new_dec; 433 ac->decoder = new_dec;
440 434
441 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels); 435 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
442 } 436 }
443 437
444 return true; 438 return true;
445} \ No newline at end of file 439}
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 @@
29 29
30#include "../toxcore/util.h" 30#include "../toxcore/util.h"
31 31
32struct RTPMessage_s; 32struct RTPMessage;
33 33
34/*
35 * Base Audio Codec session type.
36 */
37typedef struct ACSession_s { 34typedef struct ACSession_s {
38 /* encoding */ 35 /* encoding */
39 OpusEncoder *encoder; 36 OpusEncoder *encoder;
40 int32_t last_encoding_sampling_rate; 37 int32_t le_sample_rate; /* Last encoder sample rate */
41 int32_t last_encoding_channel_count; 38 int32_t le_channel_count; /* Last encoder channel count */
42 int32_t last_encoding_bit_rate; 39 int32_t le_bit_rate; /* Last encoder bit rate */
43 40
44 /* Testing encoder for dynamic bit rate streaming */
45 OpusEncoder *test_encoder;
46 int32_t last_test_encoding_sampling_rate;
47 int32_t last_test_encoding_channel_count;
48 int32_t last_test_encoding_bit_rate;
49
50 /* decoding */ 41 /* decoding */
51 OpusDecoder *decoder; 42 OpusDecoder *decoder;
52 int32_t last_packet_channel_count; 43 int32_t lp_channel_count; /* Last packet channel count */
53 int32_t last_packet_sampling_rate; 44 int32_t lp_sampling_rate; /* Last packet sample rate */
54 int32_t last_packet_frame_duration; 45 int32_t lp_frame_duration; /* Last packet frame duration */
55 int32_t last_decoding_sampling_rate; 46 int32_t ld_sample_rate; /* Last decoder sample rate */
56 int32_t last_decoding_channel_count; 47 int32_t ld_channel_count; /* Last decoder channel count */
57 uint64_t last_decoder_reconfiguration; 48 uint64_t ldrts; /* Last decoder reconfiguration time stamp */
58 void *j_buf; 49 void *j_buf;
59 50
60 pthread_mutex_t queue_mutex[1]; 51 pthread_mutex_t queue_mutex[1];
61 52
62 ToxAV* av; 53 ToxAV *av;
63 uint32_t friend_number; 54 uint32_t friend_number;
64 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */ 55 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
65} ACSession; 56} ACSession;
66 57
67/* 58ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
68 * Create new Audio Codec session. 59void ac_kill(ACSession *ac);
69 */ 60void ac_iterate(ACSession *ac);
70ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data); 61int ac_queue_message(void *acp, struct RTPMessage *msg);
71/* 62int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
72 * Kill the Audio Codec session.
73 */
74void ac_kill(ACSession* ac);
75/*
76 * Do periodic work. Work is consisted out of decoding only.
77 */
78void ac_do(ACSession* ac);
79/*
80 * Queue new rtp message.
81 */
82int ac_queue_message(void *acp, struct RTPMessage_s *msg);
83/*
84 * Set new values to the encoders.
85 */
86int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
87int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
88 63
89#endif /* AUDIO_H */ \ No newline at end of file 64#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 @@
1/** bwcontroler.c
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <assert.h>
27#include "bwcontroler.h"
28#include "../toxcore/logger.h"
29#include "../toxcore/util.h"
30
31#define BWC_PACKET_ID 196
32#define BWC_SEND_INTERVAL_MS 1000
33#define BWC_REFRESH_INTERVAL_MS 10000
34#define BWC_AVG_PKT_COUNT 20
35
36/**
37 *
38 */
39
40struct BWControler_s {
41 void (*mcb) (BWControler *, uint32_t, float, void *);
42 void *mcb_data;
43
44 Messenger *m;
45 uint32_t friend_number;
46
47 struct {
48 uint32_t lru; /* Last recv update time stamp */
49 uint32_t lsu; /* Last sent update time stamp */
50 uint32_t lfu; /* Last refresh time stamp */
51
52 uint32_t lost;
53 uint32_t recv;
54 } cycle;
55
56 struct {
57 uint32_t rb_s[BWC_AVG_PKT_COUNT];
58 RingBuffer *rb;
59 } rcvpkt; /* To calculate average received packet */
60};
61
62int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
63void send_update(BWControler *bwc);
64
65BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
66 void (*mcb) (BWControler *, uint32_t, float, void *),
67 void *udata)
68{
69 BWControler *retu = calloc(sizeof(struct BWControler_s), 1);
70
71 retu->mcb = mcb;
72 retu->mcb_data = udata;
73 retu->m = m;
74 retu->friend_number = friendnumber;
75 retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic();
76 retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
77
78 /* Fill with zeros */
79 int i = 0;
80 for (; i < BWC_AVG_PKT_COUNT; i ++)
81 rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
82
83 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
84
85 return retu;
86}
87void bwc_kill(BWControler *bwc)
88{
89 if (!bwc)
90 return;
91
92 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
93
94 rb_kill(bwc->rcvpkt.rb);
95 free(bwc);
96}
97void bwc_feed_avg(BWControler* bwc, uint32_t bytes)
98{
99 uint32_t *p;
100
101 rb_read(bwc->rcvpkt.rb, (void**) &p);
102 rb_write(bwc->rcvpkt.rb, p);
103
104 *p = bytes;
105}
106void bwc_add_lost(BWControler *bwc, uint32_t bytes)
107{
108 if (!bwc)
109 return;
110
111 if (!bytes) {
112 uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1;
113
114 rb_data(bwc->rcvpkt.rb, (void**) t_avg);
115
116 int i = 0;
117 for (; i < BWC_AVG_PKT_COUNT; i ++) {
118 bytes += *(t_avg[i]);
119
120 if (*(t_avg[i]))
121 c++;
122 }
123
124 bytes /= c;
125 }
126
127 bwc->cycle.lost += bytes;
128 send_update(bwc);
129}
130void bwc_add_recv(BWControler *bwc, uint32_t bytes)
131{
132 if (!bwc || !bytes)
133 return;
134
135 bwc->cycle.recv += bytes;
136 send_update(bwc);
137}
138
139
140struct BWCMessage {
141 uint8_t core_type; /* Aligner for payload type which is always 196 */
142
143 uint32_t lost;
144 uint32_t recv;
145} __attribute__((packed));
146
147/* Check alignment */
148typedef char __fail_if_misaligned [ sizeof(struct BWCMessage) == 9 ? 1 : -1 ];
149
150void send_update(BWControler *bwc)
151{
152 if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
153
154 bwc->cycle.lost /= 10;
155 bwc->cycle.recv /= 10;
156 bwc->cycle.lfu = current_time_monotonic();
157 }
158 else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
159
160 if (bwc->cycle.lost)
161 {
162 LOGGER_DEBUG ("%p Sent update", bwc);
163
164 struct BWCMessage msg;
165 msg.core_type = BWC_PACKET_ID;
166 msg.lost = htonl(bwc->cycle.lost);
167 msg.recv = htonl(bwc->cycle.recv);
168
169 if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, (uint8_t *)&msg, sizeof(msg)))
170 LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(msg), strerror(errno));
171 }
172
173 bwc->cycle.lsu = current_time_monotonic();
174 }
175}
176int on_update (BWControler *bwc, struct BWCMessage *msg)
177{
178 LOGGER_DEBUG ("%p Got update from peer", bwc);
179
180 /* Peer must respect time boundary */
181 if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) {
182 LOGGER_DEBUG("%p Rejecting extra update", bwc);
183 return -1;
184 }
185
186 bwc->cycle.lru = current_time_monotonic();
187
188 msg->recv = ntohl(msg->recv);
189 msg->lost = ntohl(msg->lost);
190
191 LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost);
192
193 if (msg->lost && bwc->mcb)
194 bwc->mcb(bwc, bwc->friend_number,
195 ((float) (msg->lost) / (msg->recv + msg->lost)),
196 bwc->mcb_data);
197
198 return 0;
199}
200int bwc_handle_data(Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object)
201{
202 if (length != sizeof(struct BWCMessage))
203 return;
204
205 /* NOTE the data is mutable */
206 return on_update(object, (struct BWCMessage *) data);
207}
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 @@
1/** bwcontroler.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef BWCONROLER_H
23#define BWCONROLER_H
24#include "../toxcore/Messenger.h"
25
26typedef struct BWControler_s BWControler;
27
28BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
29 void (*mcb) (BWControler *, uint32_t, float, void *),
30 void *udata);
31void bwc_kill(BWControler *bwc);
32
33void bwc_feed_avg(BWControler *bwc, uint32_t bytes);
34void bwc_add_lost(BWControler *bwc, uint32_t bytes);
35void bwc_add_recv(BWControler *bwc, uint32_t bytes);
36
37#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 @@
20 20
21#ifdef HAVE_CONFIG_H 21#ifdef HAVE_CONFIG_H
22#include "config.h" 22#include "config.h"
23#endif 23#endif /* HAVE_CONFIG_H */
24 24
25#include "group.h" 25#include "group.h"
26#include "../toxcore/util.h" 26#include "../toxcore/util.h"
@@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity)
54 54
55 Group_JitterBuffer *q; 55 Group_JitterBuffer *q;
56 56
57 if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL; 57 if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL;
58 58
59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) { 59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) {
60 free(q); 60 free(q);
@@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av)
190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels, 190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
191 OPUS_APPLICATION_AUDIO, &rc); 191 OPUS_APPLICATION_AUDIO, &rc);
192 192
193 if ( rc != OPUS_OK ) { 193 if (rc != OPUS_OK) {
194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); 194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
195 group_av->audio_encoder = NULL; 195 group_av->audio_encoder = NULL;
196 return -1; 196 return -1;
@@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av)
198 198
199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate)); 199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
200 200
201 if ( rc != OPUS_OK ) { 201 if (rc != OPUS_OK) {
202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
203 opus_encoder_destroy(group_av->audio_encoder); 203 opus_encoder_destroy(group_av->audio_encoder);
204 group_av->audio_encoder = NULL; 204 group_av->audio_encoder = NULL;
@@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av)
207 207
208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10)); 208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
209 209
210 if ( rc != OPUS_OK ) { 210 if (rc != OPUS_OK) {
211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
212 opus_encoder_destroy(group_av->audio_encoder); 212 opus_encoder_destroy(group_av->audio_encoder);
213 group_av->audio_encoder = NULL; 213 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
306 int rc; 306 int rc;
307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc); 307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
308 308
309 if ( rc != OPUS_OK ) { 309 if (rc != OPUS_OK) {
310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); 310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
311 free(pk); 311 free(pk);
312 return -1; 312 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 {
46 IDRequest = 1, 46 IDRequest = 1,
47 IDError, 47 IDError,
48 IDCapabilities, 48 IDCapabilities,
49 IDVFPSZ,
50 49
51} MSIHeaderID; 50} MSIHeaderID;
52 51
53 52
54typedef enum { 53typedef enum {
54 requ_init,
55 requ_push, 55 requ_push,
56 requ_pop, 56 requ_pop,
57} MSIRequest; 57} MSIRequest;
@@ -64,224 +64,246 @@ typedef struct { \
64} MSIHeader##header 64} MSIHeader##header
65 65
66 66
67GENERIC_HEADER ( Request, MSIRequest ); 67GENERIC_HEADER (Request, MSIRequest);
68GENERIC_HEADER ( Error, MSIError ); 68GENERIC_HEADER (Error, MSIError);
69GENERIC_HEADER ( Capabilities, uint8_t ); 69GENERIC_HEADER (Capabilities, uint8_t);
70GENERIC_HEADER ( VFPSZ, uint16_t );
71 70
72 71
73typedef struct { 72typedef struct {
74 MSIHeaderRequest request; 73 MSIHeaderRequest request;
75 MSIHeaderError error; 74 MSIHeaderError error;
76 MSIHeaderCapabilities capabilities; 75 MSIHeaderCapabilities capabilities;
77 MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order TODO: get rid of this eventually */
78} MSIMessage; 76} MSIMessage;
79 77
80 78
81void msg_init (MSIMessage *dest, MSIRequest request); 79void msg_init (MSIMessage *dest, MSIRequest request);
82int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); 80int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length);
83uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); 81uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length);
84static int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ); 82static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg);
85int send_error ( Messenger* m, uint32_t friend_number, MSIError error ); 83int send_error (Messenger *m, uint32_t friend_number, MSIError error);
86static int invoke_callback(MSICall* call, MSICallbackID cb); 84static int invoke_callback(MSICall *call, MSICallbackID cb);
87static MSICall *get_call ( MSISession *session, uint32_t friend_number ); 85static MSICall *get_call (MSISession *session, uint32_t friend_number);
88MSICall *new_call ( MSISession *session, uint32_t friend_number ); 86MSICall *new_call (MSISession *session, uint32_t friend_number);
89void kill_call ( MSICall *call ); 87void kill_call (MSICall *call);
90void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data); 88void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
91void handle_push ( MSICall *call, const MSIMessage *msg ); 89void handle_init (MSICall *call, const MSIMessage *msg);
92void handle_pop ( MSICall *call, const MSIMessage *msg ); 90void handle_push (MSICall *call, const MSIMessage *msg);
93void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object ); 91void handle_pop (MSICall *call, const MSIMessage *msg);
92void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
94 93
95 94
96/** 95/**
97 * Public functions 96 * Public functions
98 */ 97 */
99void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id) 98void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id)
100{ 99{
100 if (!session)
101 return;
102
101 pthread_mutex_lock(session->mutex); 103 pthread_mutex_lock(session->mutex);
102 session->callbacks[id] = callback; 104 session->callbacks[id] = callback;
103 pthread_mutex_unlock(session->mutex); 105 pthread_mutex_unlock(session->mutex);
104} 106}
105MSISession *msi_new ( Messenger *m ) 107MSISession *msi_new (Messenger *m)
106{ 108{
107 if (m == NULL) { 109 if (m == NULL) {
108 LOGGER_ERROR("Could not init session on empty messenger!"); 110 LOGGER_ERROR("Could not init session on empty messenger!");
109 return NULL; 111 return NULL;
110 } 112 }
111 113
112 MSISession *retu = calloc ( sizeof ( MSISession ), 1 ); 114 MSISession *retu = calloc (sizeof (MSISession), 1);
113 115
114 if (retu == NULL) { 116 if (retu == NULL) {
115 LOGGER_ERROR("Allocation failed! Program might misbehave!"); 117 LOGGER_ERROR("Allocation failed! Program might misbehave!");
116 return NULL; 118 return NULL;
117 } 119 }
118 120
119 if (create_recursive_mutex(retu->mutex) != 0) { 121 if (create_recursive_mutex(retu->mutex) != 0) {
120 LOGGER_ERROR("Failed to init mutex! Program might misbehave"); 122 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
121 free(retu); 123 free(retu);
122 return NULL; 124 return NULL;
123 } 125 }
124 126
125 retu->messenger = m; 127 retu->messenger = m;
126 128
127 m_callback_msi_packet(m, handle_msi_packet, retu ); 129 m_callback_msi_packet(m, handle_msi_packet, retu);
128 130
129 /* This is called when remote terminates session */ 131 /* This is called when remote terminates session */
130 m_callback_connectionstatus_internal_av(m, on_peer_status, retu); 132 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
131 133
132 LOGGER_DEBUG("New msi session: %p ", retu); 134 LOGGER_DEBUG("New msi session: %p ", retu);
133 return retu; 135 return retu;
134} 136}
135int msi_kill ( MSISession *session ) 137int msi_kill (MSISession *session)
136{ 138{
137 if (session == NULL) { 139 if (session == NULL) {
138 LOGGER_ERROR("Tried to terminate non-existing session"); 140 LOGGER_ERROR("Tried to terminate non-existing session");
139 return -1; 141 return -1;
140 } 142 }
141 143
142 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL); 144 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
143 pthread_mutex_lock(session->mutex);
144 145
146 if (pthread_mutex_trylock(session->mutex) != 0) {
147 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
148 return -1;
149 }
150
145 if (session->calls) { 151 if (session->calls) {
146 MSIMessage msg; 152 MSIMessage msg;
147 msg_init(&msg, requ_pop); 153 msg_init(&msg, requ_pop);
148 154
149 MSICall* it = get_call(session, session->calls_head); 155 MSICall *it = get_call(session, session->calls_head);
156
150 for (; it; it = it->next) { 157 for (; it; it = it->next) {
151 send_message(session->messenger, it->friend_number, &msg); 158 send_message(session->messenger, it->friend_number, &msg);
152 kill_call(it); /* This will eventually free session->calls */ 159 kill_call(it); /* This will eventually free session->calls */
153 } 160 }
154 } 161 }
155 162
156 pthread_mutex_unlock(session->mutex); 163 pthread_mutex_unlock(session->mutex);
157 pthread_mutex_destroy(session->mutex); 164 pthread_mutex_destroy(session->mutex);
158 165
159 LOGGER_DEBUG("Terminated session: %p", session); 166 LOGGER_DEBUG("Terminated session: %p", session);
160 free ( session ); 167 free (session);
161 return 0; 168 return 0;
162} 169}
163int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities ) 170int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
164{ 171{
165 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number); 172 if (!session)
173 return -1;
166 174
167 pthread_mutex_lock(session->mutex); 175 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
176
177 if (pthread_mutex_trylock(session->mutex) != 0) {
178 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
179 return -1;
180 }
181
168 if (get_call(session, friend_number) != NULL) { 182 if (get_call(session, friend_number) != NULL) {
169 LOGGER_ERROR("Already in a call"); 183 LOGGER_ERROR("Already in a call");
170 pthread_mutex_unlock(session->mutex); 184 pthread_mutex_unlock(session->mutex);
171 return -1; 185 return -1;
172 } 186 }
173 187
174 (*call) = new_call ( session, friend_number ); 188 (*call) = new_call (session, friend_number);
175 189
176 if ( *call == NULL ) { 190 if (*call == NULL) {
177 pthread_mutex_unlock(session->mutex); 191 pthread_mutex_unlock(session->mutex);
178 return -1; 192 return -1;
179 } 193 }
180 194
181 (*call)->self_capabilities = capabilities; 195 (*call)->self_capabilities = capabilities;
182 196
183 MSIMessage msg; 197 MSIMessage msg;
184 msg_init(&msg, requ_push); 198 msg_init(&msg, requ_init);
185 199
186 msg.capabilities.exists = true; 200 msg.capabilities.exists = true;
187 msg.capabilities.value = capabilities; 201 msg.capabilities.value = capabilities;
188 202
189 msg.vfpsz.exists = true; 203 send_message ((*call)->session->messenger, (*call)->friend_number, &msg);
190 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; 204
191
192 send_message ( (*call)->session->messenger, (*call)->friend_number, &msg );
193
194 (*call)->state = msi_CallRequesting; 205 (*call)->state = msi_CallRequesting;
195 206
196 LOGGER_DEBUG("Invite sent"); 207 LOGGER_DEBUG("Invite sent");
197 pthread_mutex_unlock(session->mutex); 208 pthread_mutex_unlock(session->mutex);
198 return 0; 209 return 0;
199} 210}
200int msi_hangup ( MSICall* call ) 211int msi_hangup (MSICall *call)
201{ 212{
202 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number); 213 if (!call || !call->session)
214 return -1;
203 215
204 MSISession* session = call->session; 216 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number);
205 pthread_mutex_lock(session->mutex); 217
218 MSISession *session = call->session;
206 219
207 if ( call->state == msi_CallInactive ) { 220 if (pthread_mutex_trylock(session->mutex) != 0) {
221 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
222 return -1;
223 }
224
225 if (call->state == msi_CallInactive) {
208 LOGGER_ERROR("Call is in invalid state!"); 226 LOGGER_ERROR("Call is in invalid state!");
209 pthread_mutex_unlock(session->mutex); 227 pthread_mutex_unlock(session->mutex);
210 return -1; 228 return -1;
211 } 229 }
212 230
213 MSIMessage msg; 231 MSIMessage msg;
214 msg_init(&msg, requ_pop); 232 msg_init(&msg, requ_pop);
215 233
216 send_message ( session->messenger, call->friend_number, &msg ); 234 send_message (session->messenger, call->friend_number, &msg);
217 235
218 kill_call(call); 236 kill_call(call);
219 pthread_mutex_unlock(session->mutex); 237 pthread_mutex_unlock(session->mutex);
220 return 0; 238 return 0;
221} 239}
222int msi_answer ( MSICall* call, uint8_t capabilities ) 240int msi_answer (MSICall *call, uint8_t capabilities)
223{ 241{
242 if (!call || !call->session)
243 return -1;
244
224 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number); 245 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
225 246
226 MSISession* session = call->session; 247 MSISession *session = call->session;
227 pthread_mutex_lock(session->mutex); 248
228 249 if (pthread_mutex_trylock(session->mutex) != 0) {
229 if ( call->state != msi_CallRequested ) { 250 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
230 /* Though sending in invalid state will not cause anything wierd 251 return -1;
252 }
253
254 if (call->state != msi_CallRequested) {
255 /* Though sending in invalid state will not cause anything wierd
231 * Its better to not do it like a maniac */ 256 * Its better to not do it like a maniac */
232 LOGGER_ERROR("Call is in invalid state!"); 257 LOGGER_ERROR("Call is in invalid state!");
233 pthread_mutex_unlock(session->mutex); 258 pthread_mutex_unlock(session->mutex);
234 return -1; 259 return -1;
235 } 260 }
236 261
237 call->self_capabilities = capabilities; 262 call->self_capabilities = capabilities;
238 263
239 MSIMessage msg; 264 MSIMessage msg;
240 msg_init(&msg, requ_push); 265 msg_init(&msg, requ_push);
241 266
242 msg.capabilities.exists = true; 267 msg.capabilities.exists = true;
243 msg.capabilities.value = capabilities; 268 msg.capabilities.value = capabilities;
244 269
245 msg.vfpsz.exists = true; 270 send_message (session->messenger, call->friend_number, &msg);
246 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE; 271
247
248 send_message ( session->messenger, call->friend_number, &msg );
249
250 call->state = msi_CallActive; 272 call->state = msi_CallActive;
251 pthread_mutex_unlock(session->mutex); 273 pthread_mutex_unlock(session->mutex);
252 274
253 return 0; 275 return 0;
254} 276}
255int msi_change_capabilities( MSICall* call, uint8_t capabilities ) 277int msi_change_capabilities(MSICall *call, uint8_t capabilities)
256{ 278{
257 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number); 279 if (!call || !call->session)
258 280 return -1;
259 MSISession* session = call->session;
260 pthread_mutex_lock(session->mutex);
261 281
262 if ( call->state != msi_CallActive ) { 282 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
263 /* Sending capabilities change can cause error on other side if 283
264 * the call is not active since we don't send header 'vfpsz'. 284 MSISession *session = call->session;
265 * If we were to send 'vfpsz' while call is active it would be 285
266 * ignored. However, if call is not active peer will expect 286 if (pthread_mutex_trylock(session->mutex) != 0) {
267 * the said header on 'push' so that it could handle the call 287 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
268 * like new. TODO: explain this better 288 return -1;
269 */ 289 }
290
291 if (call->state != msi_CallActive) {
270 LOGGER_ERROR("Call is in invalid state!"); 292 LOGGER_ERROR("Call is in invalid state!");
271 pthread_mutex_unlock(session->mutex); 293 pthread_mutex_unlock(session->mutex);
272 return -1; 294 return -1;
273 } 295 }
274 296
275 call->self_capabilities = capabilities; 297 call->self_capabilities = capabilities;
276 298
277 MSIMessage msg; 299 MSIMessage msg;
278 msg_init(&msg, requ_push); 300 msg_init(&msg, requ_push);
279 301
280 msg.capabilities.exists = true; 302 msg.capabilities.exists = true;
281 msg.capabilities.value = capabilities; 303 msg.capabilities.value = capabilities;
282 304
283 send_message ( call->session->messenger, call->friend_number, &msg ); 305 send_message (call->session->messenger, call->friend_number, &msg);
284 306
285 pthread_mutex_unlock(session->mutex); 307 pthread_mutex_unlock(session->mutex);
286 return 0; 308 return 0;
287} 309}
@@ -290,23 +312,23 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities )
290/** 312/**
291 * Private functions 313 * Private functions
292 */ 314 */
293void msg_init(MSIMessage* dest, MSIRequest request) 315void msg_init(MSIMessage *dest, MSIRequest request)
294{ 316{
295 memset(dest, 0, sizeof(*dest)); 317 memset(dest, 0, sizeof(*dest));
296 dest->request.exists = true; 318 dest->request.exists = true;
297 dest->request.value = request; 319 dest->request.value = request;
298} 320}
299int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) 321int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length)
300{ 322{
301 /* Parse raw data received from socket into MSIMessage struct */ 323 /* Parse raw data received from socket into MSIMessage struct */
302 324
303#define CHECK_SIZE(bytes, constraint, size) \ 325#define CHECK_SIZE(bytes, constraint, size) \
304 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \ 326 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
305 if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; } 327 if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; }
306 328
307#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \ 329#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
308 if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; } 330 if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
309 331
310#define SET_UINT8(bytes, header) do { \ 332#define SET_UINT8(bytes, header) do { \
311 header.value = bytes[2]; \ 333 header.value = bytes[2]; \
312 header.exists = true; \ 334 header.exists = true; \
@@ -318,50 +340,39 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
318 header.exists = true; \ 340 header.exists = true; \
319 bytes += 4; \ 341 bytes += 4; \
320 } while(0) 342 } while(0)
321 343
322 344
323 assert(dest); 345 assert(dest);
324 346
325 if ( length == 0 || data[length - 1] ) { /* End byte must have value 0 */ 347 if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
326 LOGGER_ERROR("Invalid end byte"); 348 LOGGER_ERROR("Invalid end byte");
327 return -1; 349 return -1;
328 } 350 }
329 351
330 memset(dest, 0, sizeof(*dest)); 352 memset(dest, 0, sizeof(*dest));
331 353
332 const uint8_t *it = data; 354 const uint8_t *it = data;
333 int size_constraint = length; 355 int size_constraint = length;
334 356
335 while ( *it ) {/* until end byte is hit */ 357 while (*it) {/* until end byte is hit */
336 switch (*it) { 358 switch (*it) {
337 case IDRequest: 359 case IDRequest:
338 CHECK_SIZE(it, size_constraint, 1); 360 CHECK_SIZE(it, size_constraint, 1);
339 CHECK_ENUM_HIGH(it, requ_pop); 361 CHECK_ENUM_HIGH(it, requ_pop);
340 SET_UINT8(it, dest->request); 362 SET_UINT8(it, dest->request);
341 break; 363 break;
342 364
343 case IDError: 365 case IDError:
344 CHECK_SIZE(it, size_constraint, 1); 366 CHECK_SIZE(it, size_constraint, 1);
345 CHECK_ENUM_HIGH(it, msi_EUndisclosed); 367 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
346 SET_UINT8(it, dest->error); 368 SET_UINT8(it, dest->error);
347 break; 369 break;
348 370
349 case IDCapabilities: 371 case IDCapabilities:
350 CHECK_SIZE(it, size_constraint, 1); 372 CHECK_SIZE(it, size_constraint, 1);
351 SET_UINT8(it, dest->capabilities); 373 SET_UINT8(it, dest->capabilities);
352 break; 374 break;
353 375
354 case IDVFPSZ:
355 CHECK_SIZE(it, size_constraint, 2);
356 SET_UINT16(it, dest->vfpsz);
357 dest->vfpsz.value = ntohs(dest->vfpsz.value);
358
359 if (dest->vfpsz.value > 1200) {
360 LOGGER_ERROR("Invalid vfpsz param");
361 return -1;
362 }
363 break;
364
365 default: 376 default:
366 LOGGER_ERROR("Invalid id byte"); 377 LOGGER_ERROR("Invalid id byte");
367 return -1; 378 return -1;
@@ -373,7 +384,7 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
373 LOGGER_ERROR("Invalid request field!"); 384 LOGGER_ERROR("Invalid request field!");
374 return -1; 385 return -1;
375 } 386 }
376 387
377 return 0; 388 return 0;
378 389
379#undef CHECK_SIZE 390#undef CHECK_SIZE
@@ -381,13 +392,13 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
381#undef SET_UINT8 392#undef SET_UINT8
382#undef SET_UINT16 393#undef SET_UINT16
383} 394}
384uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ) 395uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
385{ 396{
386 /* Parse a single header for sending */ 397 /* Parse a single header for sending */
387 assert(dest); 398 assert(dest);
388 assert(value); 399 assert(value);
389 assert(value_len); 400 assert(value_len);
390 401
391 *dest = id; 402 *dest = id;
392 dest ++; 403 dest ++;
393 *dest = value_len; 404 *dest = value_len;
@@ -399,208 +410,205 @@ uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value
399 410
400 return dest + value_len; /* Set to next position ready to be written */ 411 return dest + value_len; /* Set to next position ready to be written */
401} 412}
402int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg ) 413int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg)
403{ 414{
404 /* Parse and send message */ 415 /* Parse and send message */
405 assert(m); 416 assert(m);
406 417
407 uint8_t parsed [MSI_MAXMSG_SIZE]; 418 uint8_t parsed [MSI_MAXMSG_SIZE];
408 419
409 uint8_t *it = parsed; 420 uint8_t *it = parsed;
410 uint16_t size = 0; 421 uint16_t size = 0;
411 422
412 if (msg->request.exists) { 423 if (msg->request.exists) {
413 uint8_t cast = msg->request.value; 424 uint8_t cast = msg->request.value;
414 it = msg_parse_header_out(IDRequest, it, &cast, 425 it = msg_parse_header_out(IDRequest, it, &cast,
415 sizeof(cast), &size); 426 sizeof(cast), &size);
416 } else { 427 } else {
417 LOGGER_DEBUG("Must have request field"); 428 LOGGER_DEBUG("Must have request field");
418 return -1; 429 return -1;
419 } 430 }
420 431
421 if (msg->error.exists) { 432 if (msg->error.exists) {
422 uint8_t cast = msg->error.value; 433 uint8_t cast = msg->error.value;
423 it = msg_parse_header_out(IDError, it, &cast, 434 it = msg_parse_header_out(IDError, it, &cast,
424 sizeof(cast), &size); 435 sizeof(cast), &size);
425 } 436 }
426 437
427 if (msg->capabilities.exists) { 438 if (msg->capabilities.exists) {
428 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value, 439 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
429 sizeof(msg->capabilities.value), &size); 440 sizeof(msg->capabilities.value), &size);
430 } 441 }
431 442
432 if (msg->vfpsz.exists) { 443 if (it == parsed) {
433 uint16_t nb_vfpsz = htons(msg->vfpsz.value);
434 it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz,
435 sizeof(nb_vfpsz), &size);
436 }
437
438 if ( it == parsed ) {
439 LOGGER_WARNING("Parsing message failed; empty message"); 444 LOGGER_WARNING("Parsing message failed; empty message");
440 return -1; 445 return -1;
441 } 446 }
442 447
443 *it = 0; 448 *it = 0;
444 size ++; 449 size ++;
445 450
446 if ( m_msi_packet(m, friend_number, parsed, size) ) { 451 if (m_msi_packet(m, friend_number, parsed, size)) {
447 LOGGER_DEBUG("Sent message"); 452 LOGGER_DEBUG("Sent message");
448 return 0; 453 return 0;
449 } 454 }
450 455
451 return -1; 456 return -1;
452} 457}
453int send_error ( Messenger* m, uint32_t friend_number, MSIError error ) 458int send_error (Messenger *m, uint32_t friend_number, MSIError error)
454{ 459{
455 /* Send error message */ 460 /* Send error message */
456 assert(m); 461 assert(m);
457 462
458 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number); 463 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
459 464
460 MSIMessage msg; 465 MSIMessage msg;
461 msg_init(&msg, requ_pop); 466 msg_init(&msg, requ_pop);
462 467
463 msg.error.exists = true; 468 msg.error.exists = true;
464 msg.error.value = error; 469 msg.error.value = error;
465 470
466 send_message ( m, friend_number, &msg ); 471 send_message (m, friend_number, &msg);
467 return 0; 472 return 0;
468} 473}
469int invoke_callback(MSICall* call, MSICallbackID cb) 474int invoke_callback(MSICall *call, MSICallbackID cb)
470{ 475{
471 assert(call); 476 assert(call);
472 477
473 if ( call->session->callbacks[cb] ) { 478 if (call->session->callbacks[cb]) {
474 LOGGER_DEBUG("Invoking callback function: %d", cb); 479 LOGGER_DEBUG("Invoking callback function: %d", cb);
475 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) { 480
481 if (call->session->callbacks[cb] (call->session->av, call) != 0) {
476 LOGGER_WARNING("Callback state handling failed, sending error"); 482 LOGGER_WARNING("Callback state handling failed, sending error");
477 goto FAILURE; 483 goto FAILURE;
478 } 484 }
479 485
480 return 0; 486 return 0;
481 } 487 }
482 488
483FAILURE: 489FAILURE:
484 /* If no callback present or error happened while handling, 490 /* If no callback present or error happened while handling,
485 * an error message will be sent to friend 491 * an error message will be sent to friend
486 */ 492 */
487 493
488 if (call->error == msi_ENone) 494 if (call->error == msi_ENone)
489 call->error = msi_EHandle; 495 call->error = msi_EHandle;
496
490 return -1; 497 return -1;
491} 498}
492static MSICall *get_call ( MSISession *session, uint32_t friend_number ) 499static MSICall *get_call (MSISession *session, uint32_t friend_number)
493{ 500{
494 assert(session); 501 assert(session);
495 502
496 if (session->calls == NULL || session->calls_tail < friend_number) 503 if (session->calls == NULL || session->calls_tail < friend_number)
497 return NULL; 504 return NULL;
498 505
499 return session->calls[friend_number]; 506 return session->calls[friend_number];
500} 507}
501MSICall *new_call ( MSISession *session, uint32_t friend_number ) 508MSICall *new_call (MSISession *session, uint32_t friend_number)
502{ 509{
503 assert(session); 510 assert(session);
504 511
505 MSICall *rc = calloc(sizeof(MSICall), 1); 512 MSICall *rc = calloc(sizeof(MSICall), 1);
506 513
507 if (rc == NULL) 514 if (rc == NULL)
508 return NULL; 515 return NULL;
509 516
510 rc->session = session; 517 rc->session = session;
511 rc->friend_number = friend_number; 518 rc->friend_number = friend_number;
512 519
513 if (session->calls == NULL) { /* Creating */ 520 if (session->calls == NULL) { /* Creating */
514 session->calls = calloc (sizeof(MSICall*), friend_number + 1); 521 session->calls = calloc (sizeof(MSICall *), friend_number + 1);
515 522
516 if (session->calls == NULL) { 523 if (session->calls == NULL) {
517 free(rc); 524 free(rc);
518 return NULL; 525 return NULL;
519 } 526 }
520 527
521 session->calls_tail = session->calls_head = friend_number; 528 session->calls_tail = session->calls_head = friend_number;
522 529
523 } else if (session->calls_tail < friend_number) { /* Appending */ 530 } else if (session->calls_tail < friend_number) { /* Appending */
524 void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_number + 1); 531 void *tmp = realloc(session->calls, sizeof(MSICall *) * friend_number + 1);
525 532
526 if (tmp == NULL) { 533 if (tmp == NULL) {
527 free(rc); 534 free(rc);
528 return NULL; 535 return NULL;
529 } 536 }
530 537
531 session->calls = tmp; 538 session->calls = tmp;
532 539
533 /* Set fields in between to null */ 540 /* Set fields in between to null */
534 uint32_t i = session->calls_tail + 1; 541 uint32_t i = session->calls_tail + 1;
542
535 for (; i < friend_number; i ++) 543 for (; i < friend_number; i ++)
536 session->calls[i] = NULL; 544 session->calls[i] = NULL;
537 545
538 rc->prev = session->calls[session->calls_tail]; 546 rc->prev = session->calls[session->calls_tail];
539 session->calls[session->calls_tail]->next = rc; 547 session->calls[session->calls_tail]->next = rc;
540 548
541 session->calls_tail = friend_number; 549 session->calls_tail = friend_number;
542 550
543 } else if (session->calls_head > friend_number) { /* Inserting at front */ 551 } else if (session->calls_head > friend_number) { /* Inserting at front */
544 rc->next = session->calls[session->calls_head]; 552 rc->next = session->calls[session->calls_head];
545 session->calls[session->calls_head]->prev = rc; 553 session->calls[session->calls_head]->prev = rc;
546 session->calls_head = friend_number; 554 session->calls_head = friend_number;
547 } 555 }
548 556
549 session->calls[friend_number] = rc; 557 session->calls[friend_number] = rc;
550 return rc; 558 return rc;
551} 559}
552void kill_call ( MSICall *call ) 560void kill_call (MSICall *call)
553{ 561{
554 /* Assume that session mutex is locked */ 562 /* Assume that session mutex is locked */
555 if ( call == NULL ) 563 if (call == NULL)
556 return; 564 return;
557 565
558 LOGGER_DEBUG("Killing call: %p", call); 566 LOGGER_DEBUG("Killing call: %p", call);
559 567
560 MSISession* session = call->session; 568 MSISession *session = call->session;
561 569
562 MSICall* prev = call->prev; 570 MSICall *prev = call->prev;
563 MSICall* next = call->next; 571 MSICall *next = call->next;
564 572
565 if (prev) 573 if (prev)
566 prev->next = next; 574 prev->next = next;
567 else if (next) 575 else if (next)
568 session->calls_head = next->friend_number; 576 session->calls_head = next->friend_number;
569 else goto CLEAR_CONTAINER; 577 else goto CLEAR_CONTAINER;
570 578
571 if (next) 579 if (next)
572 next->prev = prev; 580 next->prev = prev;
573 else if (prev) 581 else if (prev)
574 session->calls_tail = prev->friend_number; 582 session->calls_tail = prev->friend_number;
575 else goto CLEAR_CONTAINER; 583 else goto CLEAR_CONTAINER;
576 584
577 session->calls[call->friend_number] = NULL; 585 session->calls[call->friend_number] = NULL;
578 free(call); 586 free(call);
579 return; 587 return;
580 588
581CLEAR_CONTAINER: 589CLEAR_CONTAINER:
582 session->calls_head = session->calls_tail = 0; 590 session->calls_head = session->calls_tail = 0;
583 free(session->calls); 591 free(session->calls);
584 free(call); 592 free(call);
585 session->calls = NULL; 593 session->calls = NULL;
586} 594}
587void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data) 595void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
588{ 596{
589 (void)m; 597 (void)m;
590 MSISession *session = data; 598 MSISession *session = data;
591 599
592 switch ( status ) { 600 switch (status) {
593 case 0: { /* Friend is now offline */ 601 case 0: { /* Friend is now offline */
594 LOGGER_DEBUG("Friend %d is now offline", friend_number); 602 LOGGER_DEBUG("Friend %d is now offline", friend_number);
595 603
596 pthread_mutex_lock(session->mutex); 604 pthread_mutex_lock(session->mutex);
597 MSICall* call = get_call(session, friend_number); 605 MSICall *call = get_call(session, friend_number);
598 606
599 if (call == NULL) { 607 if (call == NULL) {
600 pthread_mutex_unlock(session->mutex); 608 pthread_mutex_unlock(session->mutex);
601 return; 609 return;
602 } 610 }
603 611
604 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */ 612 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
605 kill_call(call); 613 kill_call(call);
606 pthread_mutex_unlock(session->mutex); 614 pthread_mutex_unlock(session->mutex);
@@ -611,186 +619,208 @@ void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void*
611 break; 619 break;
612 } 620 }
613} 621}
614void handle_push ( MSICall *call, const MSIMessage *msg ) 622void handle_init (MSICall* call, const MSIMessage* msg)
615{ 623{
616 assert(call); 624 assert(call);
625 LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
617 626
618 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
619
620 if (!msg->capabilities.exists) { 627 if (!msg->capabilities.exists) {
621 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); 628 LOGGER_WARNING("Session: %p Invalid capabilities on 'init'");
622 call->error = msi_EInvalidMessage; 629 call->error = msi_EInvalidMessage;
623 goto FAILURE; 630 goto FAILURE;
624 } 631 }
625 632
626 if (call->state != msi_CallActive) { 633 switch (call->state)
627 if (!msg->vfpsz.exists) { 634 {
628 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
629 call->error = msi_EInvalidMessage;
630 goto FAILURE;
631 }
632
633 call->peer_vfpsz = msg->vfpsz.value;
634 }
635
636
637 switch (call->state) {
638 case msi_CallInactive: { 635 case msi_CallInactive: {
639 LOGGER_INFO("Friend is calling us");
640
641 /* Call requested */ 636 /* Call requested */
642 call->peer_capabilities = msg->capabilities.value; 637 call->peer_capabilities = msg->capabilities.value;
643 call->state = msi_CallRequested; 638 call->state = msi_CallRequested;
644 639
645 if ( invoke_callback(call, msi_OnInvite) == -1 ) 640 if (invoke_callback(call, msi_OnInvite) == -1)
646 goto FAILURE; 641 goto FAILURE;
647 642 }
648 } break; 643 break;
649 644
650 case msi_CallActive: { 645 case msi_CallActive: {
651 if (msg->vfpsz.exists) { 646 /* If peer sent init while the call is already
652 /* If peer sended video frame piece size 647 * active it's probable that he is trying to
653 * while the call is already active it's probable 648 * re-call us while the call is not terminated
654 * that he is trying to re-call us while the call 649 * on our side. We can assume that in this case
655 * is not terminated on our side. We can assume that 650 * we can automatically answer the re-call.
656 * in this case we can automatically answer the re-call. 651 */
657 */
658 if (call->peer_vfpsz != msg->vfpsz.value) {
659 LOGGER_WARNING("Friend sent invalid parameters for re-call");
660 call->error = msi_EInvalidParam;
661 invoke_callback(call, msi_OnError);
662 goto FAILURE;
663 }
664
665 LOGGER_INFO("Friend is recalling us");
666
667 MSIMessage msg;
668 msg_init(&msg, requ_push);
669
670 msg.capabilities.exists = true;
671 msg.capabilities.value = call->self_capabilities;
672
673 msg.vfpsz.exists = true;
674 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
675
676 send_message ( call->session->messenger, call->friend_number, &msg );
677
678 /* If peer changed capabilities during re-call they will
679 * be handled accordingly during the next step
680 */
681 }
682 652
653 LOGGER_INFO("Friend is recalling us");
654
655 MSIMessage msg;
656 msg_init(&msg, requ_push);
657
658 msg.capabilities.exists = true;
659 msg.capabilities.value = call->self_capabilities;
660
661 send_message (call->session->messenger, call->friend_number, &msg);
662
663 /* If peer changed capabilities during re-call they will
664 * be handled accordingly during the next step
665 */
666 }
667 break;
668
669 default: {
670 LOGGER_WARNING("Session: %p Invalid state on 'init'");
671 call->error = msi_EInvalidState;
672 goto FAILURE;
673 }
674 break;
675 }
676
677 return;
678FAILURE:
679 send_error(call->session->messenger, call->friend_number, call->error);
680 kill_call(call);
681}
682void handle_push (MSICall *call, const MSIMessage *msg)
683{
684 assert(call);
685
686 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
687
688 if (!msg->capabilities.exists) {
689 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
690 call->error = msi_EInvalidMessage;
691 goto FAILURE;
692 }
693
694 switch (call->state) {
695 case msi_CallActive: {
683 /* Only act if capabilities changed */ 696 /* Only act if capabilities changed */
684 if ( call->peer_capabilities != msg->capabilities.value) { 697 if (call->peer_capabilities != msg->capabilities.value) {
685 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value); 698 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
686 699
687 call->peer_capabilities = msg->capabilities.value; 700 call->peer_capabilities = msg->capabilities.value;
688 if ( invoke_callback(call, msi_OnCapabilities) == -1 ) 701
702 if (invoke_callback(call, msi_OnCapabilities) == -1)
689 goto FAILURE; 703 goto FAILURE;
690 } 704 }
691 } break; 705 }
692 706 break;
707
693 case msi_CallRequesting: { 708 case msi_CallRequesting: {
694 LOGGER_INFO("Friend answered our call"); 709 LOGGER_INFO("Friend answered our call");
695 710
696 /* Call started */ 711 /* Call started */
697 call->peer_capabilities = msg->capabilities.value; 712 call->peer_capabilities = msg->capabilities.value;
698 call->state = msi_CallActive; 713 call->state = msi_CallActive;
699 714
700 if ( invoke_callback(call, msi_OnStart) == -1 ) 715 if (invoke_callback(call, msi_OnStart) == -1)
701 goto FAILURE; 716 goto FAILURE;
702 717
703 } break; 718 }
704 719 break;
720
721 /* Pushes during initialization state are ignored */
722 case msi_CallInactive:
705 case msi_CallRequested: { 723 case msi_CallRequested: {
706 /* Consecutive pushes during initialization state are ignored */ 724 LOGGER_WARNING("Ignoring invalid push");
707 LOGGER_WARNING("Consecutive push"); 725 }
708 } break; 726 break;
709 } 727 }
710 728
711 return; 729 return;
712 730
713FAILURE: 731FAILURE:
714 send_error(call->session->messenger, call->friend_number, call->error); 732 send_error(call->session->messenger, call->friend_number, call->error);
715 kill_call(call); 733 kill_call(call);
716} 734}
717void handle_pop ( MSICall *call, const MSIMessage *msg ) 735void handle_pop (MSICall *call, const MSIMessage *msg)
718{ 736{
719 assert(call); 737 assert(call);
720 738
721 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number); 739 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
722 740
723 /* callback errors are ignored */ 741 /* callback errors are ignored */
724 742
725 if (msg->error.exists) { 743 if (msg->error.exists) {
726 LOGGER_WARNING("Friend detected an error: %d", msg->error.value); 744 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
727 call->error = msg->error.value; 745 call->error = msg->error.value;
728 invoke_callback(call, msi_OnError); 746 invoke_callback(call, msi_OnError);
729 747
730 } else switch (call->state) { 748 } else switch (call->state) {
731 case msi_CallInactive: { 749 case msi_CallInactive: {
732 LOGGER_ERROR("Handling what should be impossible case"); 750 LOGGER_ERROR("Handling what should be impossible case");
733 abort(); 751 abort();
734 } break; 752 }
735 753 break;
754
736 case msi_CallActive: { 755 case msi_CallActive: {
737 /* Hangup */ 756 /* Hangup */
738 LOGGER_INFO("Friend hung up on us"); 757 LOGGER_INFO("Friend hung up on us");
739 invoke_callback(call, msi_OnEnd); 758 invoke_callback(call, msi_OnEnd);
740 } break; 759 }
741 760 break;
761
742 case msi_CallRequesting: { 762 case msi_CallRequesting: {
743 /* Reject */ 763 /* Reject */
744 LOGGER_INFO("Friend rejected our call"); 764 LOGGER_INFO("Friend rejected our call");
745 invoke_callback(call, msi_OnEnd); 765 invoke_callback(call, msi_OnEnd);
746 } break; 766 }
747 767 break;
768
748 case msi_CallRequested: { 769 case msi_CallRequested: {
749 /* Cancel */ 770 /* Cancel */
750 LOGGER_INFO("Friend canceled call invite"); 771 LOGGER_INFO("Friend canceled call invite");
751 invoke_callback(call, msi_OnEnd); 772 invoke_callback(call, msi_OnEnd);
752 } break; 773 }
774 break;
753 } 775 }
754 776
755 kill_call ( call ); 777 kill_call (call);
756} 778}
757void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object ) 779void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
758{ 780{
759 LOGGER_DEBUG("Got msi message"); 781 LOGGER_DEBUG("Got msi message");
760 782
761 MSISession *session = object; 783 MSISession *session = object;
762 MSIMessage msg; 784 MSIMessage msg;
763 785
764 if ( msg_parse_in ( &msg, data, length ) == -1 ) { 786 if (msg_parse_in (&msg, data, length) == -1) {
765 LOGGER_WARNING("Error parsing message"); 787 LOGGER_WARNING("Error parsing message");
766 send_error(m, friend_number, msi_EInvalidMessage); 788 send_error(m, friend_number, msi_EInvalidMessage);
767 return; 789 return;
768 } else { 790 } else {
769 LOGGER_DEBUG("Successfully parsed message"); 791 LOGGER_DEBUG("Successfully parsed message");
770 } 792 }
771 793
772 pthread_mutex_lock(session->mutex); 794 pthread_mutex_lock(session->mutex);
773 MSICall *call = get_call(session, friend_number); 795 MSICall *call = get_call(session, friend_number);
774 796
775 if (call == NULL) { 797 if (call == NULL) {
776 if (msg.request.value != requ_push) { 798 if (msg.request.value != requ_init) {
777 send_error(m, friend_number, msi_EStrayMessage); 799 send_error(m, friend_number, msi_EStrayMessage);
778 pthread_mutex_unlock(session->mutex); 800 pthread_mutex_unlock(session->mutex);
779 return; 801 return;
780 } 802 }
781 803
782 call = new_call(session, friend_number); 804 call = new_call(session, friend_number);
805
783 if (call == NULL) { 806 if (call == NULL) {
784 send_error(m, friend_number, msi_ESystem); 807 send_error(m, friend_number, msi_ESystem);
785 pthread_mutex_unlock(session->mutex); 808 pthread_mutex_unlock(session->mutex);
786 return; 809 return;
787 } 810 }
788 } 811 }
789 812
790 if (msg.request.value == requ_push) 813 switch (msg.request.value) {
791 handle_push(call, &msg); 814 case requ_init:
792 else 815 handle_init(call, &msg);
793 handle_pop(call, &msg); /* always kills the call */ 816 break;
794 817 case requ_push:
818 handle_push(call, &msg);
819 break;
820 case requ_pop:
821 handle_pop(call, &msg); /* always kills the call */
822 break;
823 }
824
795 pthread_mutex_unlock(session->mutex); 825 pthread_mutex_unlock(session->mutex);
796} 826}
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 @@
29#include "video.h" 29#include "video.h"
30#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
31 31
32/** Preconfigured value for video splitting */
33#define VIDEOFRAME_PIECE_SIZE 500
34
35/** 32/**
36 * Error codes. 33 * Error codes.
37 */ 34 */
@@ -89,13 +86,13 @@ typedef struct MSICall_s {
89 uint8_t peer_capabilities; /* Peer capabilities */ 86 uint8_t peer_capabilities; /* Peer capabilities */
90 uint8_t self_capabilities; /* Self capabilities */ 87 uint8_t self_capabilities; /* Self capabilities */
91 uint16_t peer_vfpsz; /* Video frame piece size */ 88 uint16_t peer_vfpsz; /* Video frame piece size */
92 uint32_t friend_number; /* Index of this call in MSISession */ 89 uint32_t friend_number; /* Index of this call in MSISession */
93 MSIError error; /* Last error */ 90 MSIError error; /* Last error */
94 91
95 void* av_call; /* Pointer to av call handler */ 92 void *av_call; /* Pointer to av call handler */
96 93
97 struct MSICall_s* next; 94 struct MSICall_s *next;
98 struct MSICall_s* prev; 95 struct MSICall_s *prev;
99} MSICall; 96} MSICall;
100 97
101 98
@@ -104,7 +101,7 @@ typedef struct MSICall_s {
104 * returned the call is considered errored and will be handled 101 * returned the call is considered errored and will be handled
105 * as such which means it will be terminated without any notice. 102 * as such which means it will be terminated without any notice.
106 */ 103 */
107typedef int msi_action_cb ( void *av, MSICall* call); 104typedef int msi_action_cb (void *av, MSICall *call);
108 105
109/** 106/**
110 * Control session struct. Please do not modify outside msi.c 107 * Control session struct. Please do not modify outside msi.c
@@ -114,41 +111,41 @@ typedef struct MSISession_s {
114 MSICall **calls; 111 MSICall **calls;
115 uint32_t calls_tail; 112 uint32_t calls_tail;
116 uint32_t calls_head; 113 uint32_t calls_head;
117 114
118 void *av; 115 void *av;
119 Messenger *messenger; 116 Messenger *messenger;
120 117
121 pthread_mutex_t mutex[1]; 118 pthread_mutex_t mutex[1];
122 msi_action_cb* callbacks[7]; 119 msi_action_cb *callbacks[7];
123} MSISession; 120} MSISession;
124 121
125/** 122/**
126 * Start the control session. 123 * Start the control session.
127 */ 124 */
128MSISession *msi_new ( Messenger *m ); 125MSISession *msi_new(Messenger *m);
129/** 126/**
130 * Terminate control session. NOTE: all calls will be freed 127 * Terminate control session. NOTE: all calls will be freed
131 */ 128 */
132int msi_kill ( MSISession *session ); 129int msi_kill(MSISession *session);
133/** 130/**
134 * Callback setter. 131 * Callback setter.
135 */ 132 */
136void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id); 133void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id);
137/** 134/**
138 * Send invite request to friend_number. 135 * Send invite request to friend_number.
139 */ 136 */
140int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities ); 137int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
141/** 138/**
142 * Hangup call. NOTE: 'call' will be freed 139 * Hangup call. NOTE: 'call' will be freed
143 */ 140 */
144int msi_hangup ( MSICall* call ); 141int msi_hangup(MSICall *call);
145/** 142/**
146 * Answer call request. 143 * Answer call request.
147 */ 144 */
148int msi_answer ( MSICall* call, uint8_t capabilities ); 145int msi_answer(MSICall *call, uint8_t capabilities);
149/** 146/**
150 * Change capabilities of the call. 147 * Change capabilities of the call.
151 */ 148 */
152int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); 149int msi_change_capabilities(MSICall *call, uint8_t capabilities);
153 150
154#endif /* MSI_H */ 151#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 @@
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 25
26#include "rtp.h" 26#include "rtp.h"
27#include "bwcontroler.h"
27#include "../toxcore/logger.h" 28#include "../toxcore/logger.h"
28#include "../toxcore/util.h" 29#include "../toxcore/util.h"
29#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
@@ -31,584 +32,361 @@
31#include <stdlib.h> 32#include <stdlib.h>
32#include <assert.h> 33#include <assert.h>
33 34
34#define RTCP_REPORT_INTERVAL_MS 500
35#define RTP_MAX_SIZE 1500
36 35
37#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) 36int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
38#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
39#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
40#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
41#define ADD_SETTING_MARKER(_h, _v) do { ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
42#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
43 37
44#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
45#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
46#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
47#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
48#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
49#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
50 38
51 39RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
52typedef struct { 40 BWControler *bwc, void *cs,
53 uint64_t timestamp; /* in ms */ 41 int (*mcb) (void *, struct RTPMessage *))
54
55 uint32_t received_packets;
56 uint32_t expected_packets;
57 /* ... other stuff in the future */
58} RTCPReport;
59
60typedef struct RTCPSession_s {
61 RTPSession *rtp_session;
62
63 uint8_t prefix;
64 uint64_t last_sent_report_ts;
65 uint32_t last_received_packets;
66 uint32_t last_expected_packets;
67
68 RingBuffer* pl_stats; /* Packet loss stats over time */
69} RTCPSession;
70
71
72RTPHeader *parse_header_in ( const uint8_t *payload, int length );
73RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
74uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
75uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
76int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
77int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
78void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
79
80
81RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) )
82{ 42{
83 assert(mcb); 43 assert(mcb);
84 assert(cs); 44 assert(cs);
85 assert(m); 45 assert(m);
86 46
87 RTPSession *retu = calloc(1, sizeof(RTPSession)); 47 RTPSession *retu = calloc(1, sizeof(RTPSession));
88 48
89 if ( !retu ) { 49 if (!retu) {
90 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 50 LOGGER_WARNING("Alloc failed! Program might misbehave!");
91 return NULL; 51 return NULL;
92 } 52 }
93 53
94 retu->version = RTP_VERSION; /* It's always 2 */ 54 retu->ssrc = random_int();
95 retu->ssrc = random_int(); 55 retu->payload_type = payload_type;
96 retu->payload_type = payload_type % 128;
97
98 retu->m = m;
99 retu->friend_number = friend_num;
100 56
101 if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) { 57 retu->m = m;
102 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 58 retu->friend_number = friendnumber;
103 free(retu);
104 return NULL;
105 }
106
107 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
108 59
109 /* Also set payload type as prefix */ 60 /* Also set payload type as prefix */
110 retu->prefix = payload_type; 61
111 62 retu->bwc = bwc;
112 retu->cs = cs; 63 retu->cs = cs;
113 retu->mcb = mcb; 64 retu->mcb = mcb;
114 65
115 /* Initialize rtcp session */ 66 if (-1 == rtp_allow_receiving(retu)) {
116 if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
117 LOGGER_WARNING("Alloc failed! Program might misbehave!");
118 free(retu->csrc);
119 free(retu);
120 return NULL;
121 }
122
123 retu->rtcp_session->prefix = payload_type + 2;
124 retu->rtcp_session->pl_stats = rb_new(4);
125 retu->rtcp_session->rtp_session = retu;
126
127 if (-1 == rtp_start_receiving(retu)) {
128 LOGGER_WARNING("Failed to start rtp receiving mode"); 67 LOGGER_WARNING("Failed to start rtp receiving mode");
129 free(retu->rtcp_session);
130 free(retu->csrc);
131 free(retu); 68 free(retu);
132 return NULL; 69 return NULL;
133 } 70 }
134 71
135 return retu; 72 return retu;
136} 73}
137void rtp_kill ( RTPSession *session ) 74void rtp_kill (RTPSession *session)
138{ 75{
139 if ( !session ) return; 76 if (!session)
140 77 return;
141 rtp_stop_receiving (session);
142 78
143 free ( session->ext_header );
144 free ( session->csrc );
145
146 void* t;
147 while (!rb_empty(session->rtcp_session->pl_stats)) {
148 rb_read(session->rtcp_session->pl_stats, (void**) &t);
149 free(t);
150 }
151 rb_free(session->rtcp_session->pl_stats);
152
153 LOGGER_DEBUG("Terminated RTP session: %p", session); 79 LOGGER_DEBUG("Terminated RTP session: %p", session);
154 80
155 /* And finally free session */ 81 rtp_stop_receiving (session);
156 free ( session->rtcp_session ); 82 free (session);
157 free ( session );
158}
159int rtp_do(RTPSession *session)
160{
161 if (!session || !session->rtcp_session)
162 return rtp_StateNormal;
163
164 if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
165 send_rtcp_report(session->rtcp_session, session->m, session->friend_number);
166 }
167
168 if (rb_full(session->rtcp_session->pl_stats)) {
169 RTCPReport* reports[4];
170
171 int i = 0;
172 for (; i < 4; i++)
173 rb_read(session->rtcp_session->pl_stats, (void**) reports + i);
174
175 /* Check for timed out reports (> 6 sec) */
176 uint64_t now = current_time_monotonic();
177 for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++);
178 for (; i < 4; i ++) {
179 rb_write(session->rtcp_session->pl_stats, reports[i]);
180 reports[i] = NULL;
181 }
182 if (!rb_empty(session->rtcp_session->pl_stats)) {
183 for (i = 0; reports[i] != NULL; i ++)
184 free(reports[i]);
185 return rtp_StateNormal; /* As some reports are timed out, we need more */
186 }
187
188 /* We have 4 on-time reports so we can proceed */
189 uint32_t quality = 100;
190 for (i = 0; i < 4; i++) {
191 uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets;
192 quality = MIN(quality, current);
193 free(reports[i]);
194 }
195
196 if (quality <= 90) {
197 LOGGER_WARNING("Stream quality: BAD (%d)", quality);
198 return rtp_StateBad;
199 } else if (quality >= 99) {
200 LOGGER_DEBUG("Stream quality: GOOD (%d)", quality);
201 return rtp_StateGood;
202 } else {
203 LOGGER_DEBUG("Stream quality: NORMAL (%d)", quality);
204 }
205 }
206 return rtp_StateNormal;
207} 83}
208int rtp_start_receiving(RTPSession* session) 84int rtp_allow_receiving(RTPSession *session)
209{ 85{
210 if (session == NULL) 86 if (session == NULL)
211 return -1; 87 return -1;
212 88
213 if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix, 89 if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
214 handle_rtp_packet, session) == -1) { 90 handle_rtp_packet, session) == -1) {
215 LOGGER_WARNING("Failed to register rtp receive handler"); 91 LOGGER_WARNING("Failed to register rtp receive handler");
216 return -1; 92 return -1;
217 } 93 }
218 if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, 94
219 handle_rtcp_packet, session->rtcp_session) == -1) {
220 LOGGER_WARNING("Failed to register rtcp receive handler");
221 m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
222 return -1;
223 }
224
225 LOGGER_DEBUG("Started receiving on session: %p", session); 95 LOGGER_DEBUG("Started receiving on session: %p", session);
226 return 0; 96 return 0;
227} 97}
228int rtp_stop_receiving(RTPSession* session) 98int rtp_stop_receiving(RTPSession *session)
229{ 99{
230 if (session == NULL) 100 if (session == NULL)
231 return -1; 101 return -1;
232 102
233 m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL); 103 m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
234 m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */ 104
235
236 LOGGER_DEBUG("Stopped receiving on session: %p", session); 105 LOGGER_DEBUG("Stopped receiving on session: %p", session);
237 return 0; 106 return 0;
238} 107}
239int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy ) 108int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
240{ 109{
241 if ( !session ) { 110 if (!session) {
242 LOGGER_WARNING("No session!"); 111 LOGGER_WARNING("No session!");
243 return -1; 112 return -1;
244 } 113 }
245
246 uint8_t parsed[RTP_MAX_SIZE];
247 uint8_t *it;
248 114
249 RTPHeader header[1]; 115 uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
250 memset(header, 0, sizeof(header)); 116 memset(rdata, 0, sizeof(rdata));
251
252 ADD_FLAG_VERSION ( header, session->version );
253 ADD_FLAG_PADDING ( header, session->padding );
254 ADD_FLAG_EXTENSION ( header, session->extension );
255 ADD_FLAG_CSRCC ( header, session->cc );
256 ADD_SETTING_MARKER ( header, session->marker );
257
258 if (dummy)
259 ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 );
260 else
261 ADD_SETTING_PAYLOAD ( header, session->payload_type );
262 117
263 header->sequnum = session->sequnum; 118 rdata[0] = session->payload_type;
264 header->timestamp = current_time_monotonic();
265 header->ssrc = session->ssrc;
266 119
267 int i; 120 struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
268 for ( i = 0; i < session->cc; i++ )
269 header->csrc[i] = session->csrc[i];
270 121
271 header->length = 12 /* Minimum header len */ + ( session->cc * 4 ); 122 header->ve = 2;
272 123 header->pe = 0;
273 uint32_t parsed_len = length + header->length + 1; 124 header->xe = 0;
274 assert(parsed_len + (session->ext_header ? session->ext_header->length * 4 : 0) < RTP_MAX_SIZE ); 125 header->cc = 0;
275 126
276 parsed[0] = session->prefix; 127 header->ma = 0;
277 it = parse_header_out ( header, parsed + 1 ); 128 header->pt = session->payload_type % 128;
278
279 if ( session->ext_header ) {
280 parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * 4 );
281 it = parse_ext_header_out ( session->ext_header, it );
282 }
283 129
284 memcpy(it, data, length); 130 header->sequnum = htons(session->sequnum);
285 131 header->timestamp = htonl(current_time_monotonic());
286 if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) { 132 header->ssrc = htonl(session->ssrc);
287 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
288 return -1;
289 }
290
291 session->sequnum ++;
292 return 0;
293}
294void rtp_free_msg ( RTPMessage *msg )
295{
296 if ( msg->ext_header ) {
297 free ( msg->ext_header->table );
298 free ( msg->ext_header );
299 }
300
301 free ( msg->header );
302 free ( msg );
303}
304 133
134 header->cpart = 0;
135 header->tlen = htons(length);
305 136
137 if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
306 138
307RTPHeader *parse_header_in ( const uint8_t *payload, int length ) 139 /**
308{ 140 * The lenght is lesser than the maximum allowed lenght (including header)
309 if ( !payload || !length ) { 141 * Send the packet in single piece.
310 LOGGER_WARNING("No payload to extract!"); 142 */
311 return NULL;
312 }
313 143
314 RTPHeader *retu = calloc(1, sizeof (RTPHeader)); 144 memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
315 145
316 if ( !retu ) { 146 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
317 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 147 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
318 return NULL; 148 } else {
319 }
320 149
321 memcpy(&retu->sequnum, payload, sizeof(retu->sequnum)); 150 /**
322 retu->sequnum = ntohs(retu->sequnum); 151 * The lenght is greater than the maximum allowed lenght (including header)
152 * Send the packet in multiple pieces.
153 */
323 154
324 const uint8_t *it = payload + 2; 155 uint16_t sent = 0;
156 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
325 157
326 retu->flags = *it; 158 while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
327 ++it; 159 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
328
329 if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
330 /* Deallocate */
331 LOGGER_WARNING("Invalid version!");
332 free(retu);
333 return NULL;
334 }
335 160
336 uint8_t cc = GET_FLAG_CSRCC ( retu ); 161 if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
337 int total = 12 /* Minimum header len */ + ( cc * 4 ); 162 rdata, piece + sizeof(struct RTPHeader) + 1))
163 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
164 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
338 165
339 if ( length < total ) { 166 sent += piece;
340 LOGGER_WARNING("Length invalid!"); 167 header->cpart = htons(sent);
341 free(retu); 168 }
342 return NULL;
343 }
344 169
345 retu->marker_payloadt = *it; 170 /* Send remaining */
346 ++it; 171 piece = length - sent;
347 retu->length = total;
348 172
173 if (piece) {
174 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
349 175
350 memcpy(&retu->timestamp, it, sizeof(retu->timestamp)); 176 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
351 it += 4; 177 piece + sizeof(struct RTPHeader) + 1))
352 memcpy(&retu->ssrc, it, sizeof(retu->ssrc)); 178 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
353 179 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
354 retu->timestamp = ntohl(retu->timestamp); 180 }
355 retu->ssrc = ntohl(retu->ssrc);
356
357 uint8_t x;
358 for ( x = 0; x < cc; x++ ) {
359 it += 4;
360 memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
361 retu->csrc[x] = ntohl(retu->csrc[x]);
362 } 181 }
363 182
364 return retu; 183 session->sequnum ++;
184 return 0;
365} 185}
366RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
367{
368 const uint8_t *it = payload;
369
370 RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader));
371
372 if ( !retu ) {
373 LOGGER_WARNING("Alloc failed! Program might misbehave!");
374 return NULL;
375 }
376
377 memcpy(&retu->length, it, sizeof(retu->length));
378 retu->length = ntohs(retu->length);
379 it += 2;
380
381 if ( length < ( retu->length * sizeof(uint32_t) ) ) {
382 LOGGER_WARNING("Length invalid!");
383 free(retu);
384 return NULL;
385 }
386
387 memcpy(&retu->type, it, sizeof(retu->type));
388 retu->type = ntohs(retu->type);
389
390 it += 2;
391
392 if ( !(retu->table = calloc(retu->length, sizeof (uint32_t))) ) {
393 LOGGER_WARNING("Alloc failed! Program might misbehave!");
394 free(retu);
395 return NULL;
396 }
397 186
398 uint16_t x;
399 for ( x = 0; x < retu->length; x++ ) {
400 it += 4;
401 memcpy(retu->table + x, it, sizeof(*retu->table));
402 retu->table[x] = ntohl(retu->table[x]);
403 }
404 187
405 return retu; 188bool chloss (const RTPSession *session, const struct RTPHeader *header)
406}
407uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload )
408{ 189{
409 uint8_t cc = GET_FLAG_CSRCC ( header ); 190 if (ntohl(header->timestamp) < session->rtimestamp) {
410 uint8_t *it = payload; 191 uint16_t hosq, lost = 0;
411 uint16_t sequnum; 192
412 uint32_t timestamp; 193 hosq = ntohs(header->sequnum);
413 uint32_t ssrc; 194
414 uint32_t csrc; 195 lost = (hosq > session->rsequnum) ?
415 196 (session->rsequnum + 65535) - hosq :
416 197 session->rsequnum - hosq;
417 /* Add sequence number first */ 198
418 sequnum = htons(header->sequnum); 199 puts ("Lost packet");
419 memcpy(it, &sequnum, sizeof(sequnum)); 200 while (lost --)
420 it += 2; 201 bwc_add_lost(session->bwc ,0);
421 202
422 *it = header->flags; 203 return true;
423 ++it;
424 *it = header->marker_payloadt;
425 ++it;
426
427 timestamp = htonl(header->timestamp);
428 memcpy(it, &timestamp, sizeof(timestamp));
429 it += 4;
430 ssrc = htonl(header->ssrc);
431 memcpy(it, &ssrc, sizeof(ssrc));
432
433 uint8_t x;
434
435 for ( x = 0; x < cc; x++ ) {
436 it += 4;
437 csrc = htonl(header->csrc[x]);
438 memcpy(it, &csrc, sizeof(csrc));
439 } 204 }
440 205
441 return it + 4; 206 return false;
442} 207}
443uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload ) 208struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
444{ 209{
445 uint8_t *it = payload; 210 assert(allocate_len >= data_length);
446 uint16_t length; 211
447 uint16_t type; 212 struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
448 uint32_t entry;
449
450 length = htons(header->length);
451 memcpy(it, &length, sizeof(length));
452 it += 2;
453 type = htons(header->type);
454 memcpy(it, &type, sizeof(type));
455 it -= 2; /* Return to 0 position */
456
457 if ( header->table ) {
458 uint16_t x;
459 for ( x = 0; x < header->length; x++ ) {
460 it += 4;
461 entry = htonl(header->table[x]);
462 memcpy(it, &entry, sizeof(entry));
463 }
464 }
465 213
466 return it + 4; 214 msg->len = data_length - sizeof(struct RTPHeader);
215 memcpy(&msg->header, data, data_length);
216
217 msg->header.sequnum = ntohs(msg->header.sequnum);
218 msg->header.timestamp = ntohl(msg->header.timestamp);
219 msg->header.ssrc = ntohl(msg->header.ssrc);
220
221 msg->header.cpart = ntohs(msg->header.cpart);
222 msg->header.tlen = ntohs(msg->header.tlen);
223
224 return msg;
467} 225}
468int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) 226int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
469{ 227{
470 (void) m; 228 (void) m;
471 (void) friendnumber; 229 (void) friendnumber;
472 230
473 RTPSession *session = object; 231 RTPSession *session = object;
474 232
475 if ( !session || length < 13 || length > RTP_MAX_SIZE ) { 233 data ++;
234 length--;
235
236 if (!session || length < sizeof (struct RTPHeader)) {
476 LOGGER_WARNING("No session or invalid length of received buffer!"); 237 LOGGER_WARNING("No session or invalid length of received buffer!");
477 return -1; 238 return -1;
478 } 239 }
479
480 RTPHeader* header = parse_header_in ( data + 1, length );
481 240
482 if ( !header ) { 241 const struct RTPHeader *header = (struct RTPHeader *) data;
483 LOGGER_WARNING("Could not parse message: Header failed to extract!"); 242
243 if (header->pt != session->payload_type % 128) {
244 LOGGER_WARNING("Invalid payload type with the session");
484 return -1; 245 return -1;
485 } 246 }
486 247
487 RTPExtHeader* ext_header = NULL; 248 if (ntohs(header->cpart) >= ntohs(header->tlen)) {
488 249 /* Never allow this case to happen */
489 uint16_t from_pos = header->length + 1;
490 uint16_t msg_length = length - from_pos;
491
492 if ( GET_FLAG_EXTENSION ( header ) ) {
493 ext_header = parse_ext_header_in ( data + from_pos, length );
494
495 if ( ext_header ) {
496 msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * 4 );
497 from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * 4 );
498 } else { /* Error */
499 LOGGER_WARNING("Could not parse message: Ext Header failed to extract!");
500 free(header);
501 return -1;
502 }
503 }
504
505 if (msg_length > RTP_MAX_SIZE) {
506 LOGGER_WARNING("Could not parse message: Invalid length!");
507 free(header);
508 free(ext_header);
509 return -1; 250 return -1;
510 } 251 }
252
253 bwc_feed_avg(session->bwc, length);
511 254
512 /* Check if message came in late */ 255 if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
513 if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) { 256 /* The message is sent in single part */
514 /* Not late */ 257
515 if (header->sequnum > session->rsequnum) 258 /* Only allow messages which have arrived in order;
516 session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum; 259 * drop late messages
517 else if (header->sequnum < session->rsequnum) 260 */
518 session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum; 261 if (chloss(session, header)) {
519 else /* Usual case when transmission starts */ 262 return 0;
520 session->rtcp_session->last_expected_packets ++; 263 } else {
264 /* Message is not late; pick up the latest parameters */
265 session->rsequnum = ntohs(header->sequnum);
266 session->rtimestamp = ntohl(header->timestamp);
267 }
268
269 bwc_add_recv(session->bwc, length);
521 270
522 session->rsequnum = header->sequnum; 271 /* Invoke processing of active multiparted message */
523 session->rtimestamp = header->timestamp; 272 if (session->mp) {
524 } 273 if (session->mcb)
274 session->mcb (session->cs, session->mp);
275 else
276 free(session->mp);
277
278 session->mp = NULL;
279 }
525 280
526 session->rtcp_session->last_received_packets ++; 281 /* The message came in the allowed time;
527 282 * process it only if handler for the session is present.
528 /* Check if the message is dummy. We don't keep dummy messages */ 283 */
529 if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) { 284
530 LOGGER_DEBUG("Received dummy rtp message"); 285 if (!session->mcb)
531 free(header); 286 return 0;
532 free(ext_header); 287
533 return 0; 288 return session->mcb (session->cs, new_message(length, data, length));
534 } 289 } else {
535 290 /* The message is sent in multiple parts */
536 /* Otherwise we will store the message if we have an appropriate handler */ 291
537 if (!session->mcb) { 292 if (session->mp) {
538 LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header)); 293 /* There are 2 possible situations in this case:
539 free(header); 294 * 1) being that we got the part of already processing message.
540 free(ext_header); 295 * 2) being that we got the part of a new/old message.
541 return 0; 296 *
542 } 297 * We handle them differently as we only allow a single multiparted
543 298 * processing message
544 RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length); 299 */
545 300
546 if ( !msg ) { 301 if (session->mp->header.sequnum == ntohs(header->sequnum) &&
547 LOGGER_WARNING("Could not parse message: Allocation failed!"); 302 session->mp->header.timestamp == ntohl(header->timestamp)) {
548 free(header); 303 /* First case */
549 free(ext_header); 304
550 return -1; 305 /* Make sure we have enough allocated memory */
551 } 306 if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
552 307 session->mp->header.tlen <= ntohs(header->cpart)) {
553 msg->header = header; 308 /* There happened to be some corruption on the stream;
554 msg->ext_header = ext_header; 309 * continue wihtout this part
555 msg->length = msg_length; 310 */
556 311 return 0;
557 memcpy ( msg->data, data + from_pos, msg_length ); 312 }
558 313
559 return session->mcb (session->cs, msg); 314 memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
560} 315 length - sizeof(struct RTPHeader));
561int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object ) 316
562{ 317 session->mp->len += length - sizeof(struct RTPHeader);
563 (void) m; 318
564 (void) friendnumber; 319 bwc_add_recv(session->bwc, length);
565 320
566 if (length < 9) 321 if (session->mp->len == session->mp->header.tlen) {
567 return -1; 322 /* Received a full message; now push it for the further
568 323 * processing.
569 RTCPSession* session = object; 324 */
570 RTCPReport* report = malloc(sizeof(RTCPReport)); 325 if (session->mcb)
571 326 session->mcb (session->cs, session->mp);
572 memcpy(&report->received_packets, data + 1, 4); 327 else
573 memcpy(&report->expected_packets, data + 5, 4); 328 free(session->mp);
574 329
575 report->received_packets = ntohl(report->received_packets); 330 session->mp = NULL;
576 report->expected_packets = ntohl(report->expected_packets); 331 }
577 332 } else {
578 if (report->expected_packets == 0 || report->received_packets > report->expected_packets) { 333 /* Second case */
579 LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets); 334
580 free(report); 335 if (session->mp->header.timestamp > ntohl(header->timestamp))
581 return 0; 336 /* The received message part is from the old message;
337 * discard it.
338 */
339 return 0;
340
341 /* Measure missing parts of the old message */
342 bwc_add_lost(session->bwc,
343 (session->mp->header.tlen - session->mp->len) +
344
345 /* Must account sizes of rtp headers too */
346 ((session->mp->header.tlen - session->mp->len) /
347 MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
348
349 /* Push the previous message for processing */
350 if (session->mcb)
351 session->mcb (session->cs, session->mp);
352 else
353 free(session->mp);
354
355 session->mp = NULL;
356 goto NEW_MULTIPARTED;
357 }
358 } else {
359 /* In this case threat the message as if it was received in order
360 */
361
362 /* This is also a point for new multiparted messages */
363NEW_MULTIPARTED:
364
365 /* Only allow messages which have arrived in order;
366 * drop late messages
367 */
368 if (chloss(session, header)) {
369 return 0;
370 } else {
371 /* Message is not late; pick up the latest parameters */
372 session->rsequnum = ntohs(header->sequnum);
373 session->rtimestamp = ntohl(header->timestamp);
374 }
375
376 bwc_add_recv(session->bwc, length);
377
378 /* Again, only store message if handler is present
379 */
380 if (session->mcb) {
381 session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
382
383 /* Reposition data if necessary */
384 if (ntohs(header->cpart));
385
386 memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
387 }
388 }
582 } 389 }
583 390
584 report->timestamp = current_time_monotonic();
585
586 free(rb_write(session->pl_stats, report));
587
588 LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
589 return 0; 391 return 0;
590} 392}
591void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
592{
593 if (session->last_expected_packets == 0)
594 return;
595
596 uint8_t parsed[9];
597 parsed[0] = session->prefix;
598
599 uint32_t received_packets = htonl(session->last_received_packets);
600 uint32_t expected_packets = htonl(session->last_expected_packets);
601
602 memcpy(parsed + 1, &received_packets, 4);
603 memcpy(parsed + 5, &expected_packets, 4);
604
605 if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
606 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
607 else {
608 LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
609
610 session->last_received_packets = 0;
611 session->last_expected_packets = 0;
612 session->last_sent_report_ts = current_time_monotonic();
613 }
614}
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 @@
22#ifndef RTP_H 22#ifndef RTP_H
23#define RTP_H 23#define RTP_H
24 24
25#define RTP_VERSION 2 25#include "bwcontroler.h"
26
27#include "../toxcore/Messenger.h" 26#include "../toxcore/Messenger.h"
28#include "stdbool.h" 27#include "stdbool.h"
29 28
30/** 29/**
31 * Payload type identifier. Also used as rtp callback prefix. (Not dummies) 30 * Payload type identifier. Also used as rtp callback prefix.
32 */ 31 */
33enum { 32enum {
34 rtp_TypeAudio = 192, 33 rtp_TypeAudio = 192,
35 rtp_TypeVideo, 34 rtp_TypeVideo,
36}; 35};
37 36
38enum { 37struct RTPHeader {
39 rtp_StateBad = -1, 38 /* Standard RTP header */
40 rtp_StateNormal, 39#ifndef WORDS_BIGENDIAN
41 rtp_StateGood, 40 unsigned cc: 4; /* Contributing sources count */
42}; 41 unsigned xe: 1; /* Extra header */
42 unsigned pe: 1; /* Padding */
43 unsigned ve: 2; /* Version */
44
45 unsigned pt: 7; /* Payload type */
46 unsigned ma: 1; /* Marker */
47#else
48 unsigned ve: 2; /* Version */
49 unsigned pe: 1; /* Padding */
50 unsigned xe: 1; /* Extra header */
51 unsigned cc: 4; /* Contributing sources count */
52
53 unsigned ma: 1; /* Marker */
54 unsigned pt: 7; /* Payload type */
55#endif
56
57 uint16_t sequnum;
58 uint32_t timestamp;
59 uint32_t ssrc;
60 uint32_t csrc[16];
43 61
44/** 62 /* Non-standard TOX-specific fields */
45 * Standard rtp header. 63 uint16_t cpart;/* Data offset of the current part */
46 */ 64 uint16_t tlen; /* Total message lenght */
47typedef struct { 65} __attribute__ ((packed));
48 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
49 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
50 uint16_t sequnum; /* Sequence Number */
51 uint32_t timestamp; /* Timestamp */
52 uint32_t ssrc; /* SSRC */
53 uint32_t csrc[16]; /* CSRC's table */
54 uint32_t length; /* Length of the header in payload string. */
55} RTPHeader;
56/**
57 * Standard rtp extension header.
58 */
59typedef struct {
60 uint16_t type; /* Extension profile */
61 uint16_t length; /* Number of extensions */
62 uint32_t *table; /* Extension's table */
63} RTPExtHeader;
64 66
65/** 67/* Check alignment */
66 * Standard rtp message. 68typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ];
67 */ 69
68typedef struct RTPMessage_s { 70struct RTPMessage {
69 RTPHeader *header; 71 uint16_t len;
70 RTPExtHeader *ext_header; 72
73 struct RTPHeader header;
74 uint8_t data[];
75} __attribute__ ((packed));
71 76
72 uint32_t length; 77/* Check alignment */
73 uint8_t data[]; 78typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ];
74} RTPMessage;
75 79
76/** 80/**
77 * RTP control session. 81 * RTP control session.
78 */ 82 */
79typedef struct { 83typedef struct {
80 uint8_t version;
81 uint8_t padding;
82 uint8_t extension;
83 uint8_t cc;
84 uint8_t marker;
85 uint8_t payload_type; 84 uint8_t payload_type;
86 uint16_t sequnum; /* Sending sequence number */ 85 uint16_t sequnum; /* Sending sequence number */
87 uint16_t rsequnum; /* Receiving sequence number */ 86 uint16_t rsequnum; /* Receiving sequence number */
88 uint32_t rtimestamp; 87 uint32_t rtimestamp;
89 uint32_t ssrc; 88 uint32_t ssrc;
90 uint32_t *csrc;
91 89
92 /* If some additional data must be sent via message 90 struct RTPMessage *mp; /* Expected parted message */
93 * apply it here. Only by allocating this member you will be
94 * automatically placing it within a message.
95 */
96 RTPExtHeader *ext_header;
97
98 /* Msg prefix for core to know when recving */
99 uint8_t prefix;
100 91
101 Messenger *m; 92 Messenger *m;
102 int friend_number; 93 uint32_t friend_number;
103 struct RTCPSession_s *rtcp_session;
104 94
95 BWControler *bwc;
105 void *cs; 96 void *cs;
106 int (*mcb) (void*, RTPMessage* msg); 97 int (*mcb) (void *, struct RTPMessage *msg);
107
108} RTPSession; 98} RTPSession;
109 99
110/**
111 * Must be called before calling any other rtp function.
112 */
113RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) );
114/**
115 * Terminate the session.
116 */
117void rtp_kill ( RTPSession* session );
118/**
119 * Do periodical rtp work.
120 */
121int rtp_do(RTPSession *session);
122/**
123 * By default rtp is in receiving state
124 */
125int rtp_start_receiving (RTPSession *session);
126/**
127 * Pause rtp receiving mode.
128 */
129int rtp_stop_receiving (RTPSession *session);
130/**
131 * Sends msg to RTPSession::dest
132 */
133int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy );
134/**
135 * Dealloc msg.
136 */
137void rtp_free_msg ( RTPMessage *msg );
138 100
101RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num,
102 BWControler *bwc, void *cs,
103 int (*mcb) (void *, struct RTPMessage *));
104void rtp_kill (RTPSession *session);
105int rtp_allow_receiving (RTPSession *session);
106int rtp_stop_receiving (RTPSession *session);
107int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length);
139 108
140#endif /* RTP_H */ 109#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 @@
1/** toxav.c 1/** toxav.c
2 * 2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
@@ -35,92 +35,74 @@
35#include <string.h> 35#include <string.h>
36 36
37#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) 37#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
38#define BITRATE_CHANGE_TESTING_TIME_MS 4000
39
40typedef struct ToxAvBitrateAdapter_s {
41 bool active;
42 uint64_t end_time;
43 uint64_t next_send;
44 uint64_t next_send_interval;
45 uint32_t bit_rate;
46} ToxAvBitrateAdapter;
47 38
48typedef struct ToxAVCall_s { 39typedef struct ToxAVCall_s {
49 ToxAV* av; 40 ToxAV *av;
50 41
51 pthread_mutex_t mutex_audio[1]; 42 pthread_mutex_t mutex_audio[1];
52 PAIR(RTPSession *, ACSession *) audio; 43 PAIR(RTPSession *, ACSession *) audio;
53 44
54 pthread_mutex_t mutex_video[1]; 45 pthread_mutex_t mutex_video[1];
55 PAIR(RTPSession *, VCSession *) video; 46 PAIR(RTPSession *, VCSession *) video;
56 47
57 pthread_mutex_t mutex[1]; 48 BWControler *bwc;
58 49
59 bool active; 50 bool active;
60 MSICall* msi_call; 51 MSICall *msi_call;
61 uint32_t friend_number; 52 uint32_t friend_number;
62 53
63 uint32_t audio_bit_rate; /* Sending audio bit rate */ 54 uint32_t audio_bit_rate; /* Sending audio bit rate */
64 uint32_t video_bit_rate; /* Sending video bit rate */ 55 uint32_t video_bit_rate; /* Sending video bit rate */
65 56
66 ToxAvBitrateAdapter aba;
67 ToxAvBitrateAdapter vba;
68
69 /** Required for monitoring changes in states */ 57 /** Required for monitoring changes in states */
70 uint8_t previous_self_capabilities; 58 uint8_t previous_self_capabilities;
71 59
72 /** Quality control */ 60 pthread_mutex_t mutex[1];
73 uint64_t time_audio_good; 61
74 uint32_t last_bad_audio_bit_rate;
75 uint64_t time_video_good;
76 uint32_t last_bad_video_bit_rate;
77
78 struct ToxAVCall_s *prev; 62 struct ToxAVCall_s *prev;
79 struct ToxAVCall_s *next; 63 struct ToxAVCall_s *next;
80} ToxAVCall; 64} ToxAVCall;
81 65
82struct ToxAV { 66struct ToxAV_s {
83 Messenger* m; 67 Messenger *m;
84 MSISession* msi; 68 MSISession *msi;
85 69
86 /* Two-way storage: first is array of calls and second is list of calls with head and tail */ 70 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
87 ToxAVCall** calls; 71 ToxAVCall **calls;
88 uint32_t calls_tail; 72 uint32_t calls_tail;
89 uint32_t calls_head; 73 uint32_t calls_head;
90 pthread_mutex_t mutex[1]; 74 pthread_mutex_t mutex[1];
91 75
92 PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ 76 PAIR(toxav_call_cb *, void *) ccb; /* Call callback */
93 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ 77 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
94 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */ 78 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
95 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */ 79 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
96 PAIR(toxav_audio_bit_rate_status_cb *, void *) abcb; /* Audio bit rate control callback */ 80 PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */
97 PAIR(toxav_video_bit_rate_status_cb *, void *) vbcb; /* Video bit rate control callback */ 81
98
99 /** Decode time measures */ 82 /** Decode time measures */
100 int32_t dmssc; /** Measure count */ 83 int32_t dmssc; /** Measure count */
101 int32_t dmsst; /** Last cycle total */ 84 int32_t dmsst; /** Last cycle total */
102 int32_t dmssa; /** Average decoding time in ms */ 85 int32_t dmssa; /** Average decoding time in ms */
103 86
104 uint32_t interval; /** Calculated interval */ 87 uint32_t interval; /** Calculated interval */
105}; 88};
106 89
90void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data);
107 91
108int callback_invite(void* toxav_inst, MSICall* call); 92int callback_invite(void *toxav_inst, MSICall *call);
109int callback_start(void* toxav_inst, MSICall* call); 93int callback_start(void *toxav_inst, MSICall *call);
110int callback_end(void* toxav_inst, MSICall* call); 94int callback_end(void *toxav_inst, MSICall *call);
111int callback_error(void* toxav_inst, MSICall* call); 95int callback_error(void *toxav_inst, MSICall *call);
112int callback_capabilites(void* toxav_inst, MSICall* call); 96int callback_capabilites(void *toxav_inst, MSICall *call);
113 97
114bool audio_bit_rate_invalid(uint32_t bit_rate); 98bool audio_bit_rate_invalid(uint32_t bit_rate);
115bool video_bit_rate_invalid(uint32_t bit_rate); 99bool video_bit_rate_invalid(uint32_t bit_rate);
116bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state); 100bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
117ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); 101ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error);
118ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); 102ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
119ToxAVCall* call_remove(ToxAVCall* call); 103ToxAVCall *call_remove(ToxAVCall *call);
120bool call_prepare_transmission(ToxAVCall* call); 104bool call_prepare_transmission(ToxAVCall *call);
121void call_kill_transmission(ToxAVCall* call); 105void call_kill_transmission(ToxAVCall *call);
122void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate);
123bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba);
124 106
125uint32_t toxav_version_major(void) 107uint32_t toxav_version_major(void)
126{ 108{
@@ -139,79 +121,86 @@ bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
139 (void)major; 121 (void)major;
140 (void)minor; 122 (void)minor;
141 (void)patch; 123 (void)patch;
142 124
143 return 1; 125 return 1;
144} 126}
145ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) 127ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error)
146{ 128{
147 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK; 129 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK;
148 ToxAV *av = NULL; 130 ToxAV *av = NULL;
149 131
150 if (tox == NULL) { 132 if (tox == NULL) {
151 rc = TOXAV_ERR_NEW_NULL; 133 rc = TOXAV_ERR_NEW_NULL;
152 goto END; 134 goto END;
153 } 135 }
154 136
155 if (((Messenger*)tox)->msi_packet) { 137 if (((Messenger *)tox)->msi_packet) {
156 rc = TOXAV_ERR_NEW_MULTIPLE; 138 rc = TOXAV_ERR_NEW_MULTIPLE;
157 goto END; 139 goto END;
158 } 140 }
159 141
160 av = calloc (sizeof(ToxAV), 1); 142 av = calloc (sizeof(ToxAV), 1);
161 143
162 if (av == NULL) { 144 if (av == NULL) {
163 LOGGER_WARNING("Allocation failed!"); 145 LOGGER_WARNING("Allocation failed!");
164 rc = TOXAV_ERR_NEW_MALLOC; 146 rc = TOXAV_ERR_NEW_MALLOC;
165 goto END; 147 goto END;
166 } 148 }
167 149
168 if (create_recursive_mutex(av->mutex) != 0) { 150 if (create_recursive_mutex(av->mutex) != 0) {
169 LOGGER_WARNING("Mutex creation failed!"); 151 LOGGER_WARNING("Mutex creation failed!");
170 rc = TOXAV_ERR_NEW_MALLOC; 152 rc = TOXAV_ERR_NEW_MALLOC;
171 goto END; 153 goto END;
172 } 154 }
173 155
174 av->m = (Messenger *)tox; 156 av->m = (Messenger *)tox;
175 av->msi = msi_new(av->m); 157 av->msi = msi_new(av->m);
176 158
177 if (av->msi == NULL) { 159 if (av->msi == NULL) {
178 pthread_mutex_destroy(av->mutex); 160 pthread_mutex_destroy(av->mutex);
179 rc = TOXAV_ERR_NEW_MALLOC; 161 rc = TOXAV_ERR_NEW_MALLOC;
180 goto END; 162 goto END;
181 } 163 }
182 164
183 av->interval = 200; 165 av->interval = 200;
184 av->msi->av = av; 166 av->msi->av = av;
185 167
186 msi_register_callback(av->msi, callback_invite, msi_OnInvite); 168 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
187 msi_register_callback(av->msi, callback_start, msi_OnStart); 169 msi_register_callback(av->msi, callback_start, msi_OnStart);
188 msi_register_callback(av->msi, callback_end, msi_OnEnd); 170 msi_register_callback(av->msi, callback_end, msi_OnEnd);
189 msi_register_callback(av->msi, callback_error, msi_OnError); 171 msi_register_callback(av->msi, callback_error, msi_OnError);
190 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout); 172 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
191 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities); 173 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
192 174
193END: 175END:
176
194 if (error) 177 if (error)
195 *error = rc; 178 *error = rc;
196 179
197 if (rc != TOXAV_ERR_NEW_OK) { 180 if (rc != TOXAV_ERR_NEW_OK) {
198 free(av); 181 free(av);
199 av = NULL; 182 av = NULL;
200 } 183 }
201 184
202 return av; 185 return av;
203} 186}
204void toxav_kill(ToxAV* av) 187void toxav_kill(ToxAV *av)
205{ 188{
206 if (av == NULL) 189 if (av == NULL)
207 return; 190 return;
191
208 pthread_mutex_lock(av->mutex); 192 pthread_mutex_lock(av->mutex);
209 193
210 msi_kill(av->msi); 194 /* To avoid possible deadlocks */
211 195 while (av->msi && msi_kill(av->msi) != 0) {
196 pthread_mutex_unlock(av->mutex);
197 pthread_mutex_lock(av->mutex);
198 }
199
212 /* Msi kill will hang up all calls so just clean these calls */ 200 /* Msi kill will hang up all calls so just clean these calls */
213 if (av->calls) { 201 if (av->calls) {
214 ToxAVCall* it = call_get(av, av->calls_head); 202 ToxAVCall *it = call_get(av, av->calls_head);
203
215 while (it) { 204 while (it) {
216 call_kill_transmission(it); 205 call_kill_transmission(it);
217 it = call_remove(it); /* This will eventually free av->calls */ 206 it = call_remove(it); /* This will eventually free av->calls */
@@ -220,812 +209,614 @@ void toxav_kill(ToxAV* av)
220 209
221 pthread_mutex_unlock(av->mutex); 210 pthread_mutex_unlock(av->mutex);
222 pthread_mutex_destroy(av->mutex); 211 pthread_mutex_destroy(av->mutex);
212
223 free(av); 213 free(av);
224} 214}
225Tox* toxav_get_tox(const ToxAV* av) 215Tox *toxav_get_tox(const ToxAV *av)
226{ 216{
227 return (Tox*) av->m; 217 return (Tox *) av->m;
228} 218}
229uint32_t toxav_iteration_interval(const ToxAV* av) 219uint32_t toxav_iteration_interval(const ToxAV *av)
230{ 220{
231 /* If no call is active interval is 200 */ 221 /* If no call is active interval is 200 */
232 return av->calls ? av->interval : 200; 222 return av->calls ? av->interval : 200;
233} 223}
234void toxav_iterate(ToxAV* av) 224void toxav_iterate(ToxAV *av)
235{ 225{
236 pthread_mutex_lock(av->mutex); 226 pthread_mutex_lock(av->mutex);
227
237 if (av->calls == NULL) { 228 if (av->calls == NULL) {
238 pthread_mutex_unlock(av->mutex); 229 pthread_mutex_unlock(av->mutex);
239 return; 230 return;
240 } 231 }
241 232
242 uint64_t start = current_time_monotonic(); 233 uint64_t start = current_time_monotonic();
243 int32_t rc = 500; 234 int32_t rc = 500;
244 235
245 ToxAVCall* i = av->calls[av->calls_head]; 236 ToxAVCall *i = av->calls[av->calls_head];
237
246 for (; i; i = i->next) { 238 for (; i; i = i->next) {
247 if (i->active) { 239 if (i->active) {
248 pthread_mutex_lock(i->mutex); 240 pthread_mutex_lock(i->mutex);
249 pthread_mutex_unlock(av->mutex); 241 pthread_mutex_unlock(av->mutex);
250 242
251 ac_do(i->audio.second); 243 ac_iterate(i->audio.second);
252 if (rtp_do(i->audio.first) < 0) { 244 vc_iterate(i->video.second);
253 /* Bad transmission */ 245
254 246 if (i->msi_call->self_capabilities & msi_CapRAudio &&
255 uint32_t bb = i->audio_bit_rate; 247 i->msi_call->peer_capabilities & msi_CapSAudio)
256 248 rc = MIN(i->audio.second->lp_frame_duration, rc);
257 if (i->aba.active) { 249
258 bb = i->aba.bit_rate; 250 if (i->msi_call->self_capabilities & msi_CapRVideo &&
259 /* Stop sending dummy packets */ 251 i->msi_call->peer_capabilities & msi_CapSVideo)
260 memset(&i->aba, 0, sizeof(i->aba));
261 }
262
263 /* Notify app */
264 if (av->abcb.first)
265 av->abcb.first (av, i->friend_number, false, bb, av->abcb.second);
266 } else if (i->aba.active && i->aba.end_time < current_time_monotonic()) {
267
268 i->audio_bit_rate = i->aba.bit_rate;
269
270 /* Notify user about the new bit rate */
271 if (av->abcb.first)
272 av->abcb.first (av, i->friend_number, true, i->aba.bit_rate, av->abcb.second);
273
274 /* Stop sending dummy packets */
275 memset(&i->aba, 0, sizeof(i->aba));
276 }
277
278 vc_do(i->video.second);
279 if (rtp_do(i->video.first) < 0) {
280 /* Bad transmission */
281 uint32_t bb = i->video_bit_rate;
282
283 if (i->vba.active) {
284 bb = i->vba.bit_rate;
285 /* Stop sending dummy packets */
286 memset(&i->vba, 0, sizeof(i->vba));
287 }
288
289 /* Notify app */
290 if (av->vbcb.first)
291 av->vbcb.first (av, i->friend_number, false, bb, av->vbcb.second);
292
293 } else if (i->vba.active && i->vba.end_time < current_time_monotonic()) {
294
295 i->video_bit_rate = i->vba.bit_rate;
296
297 /* Notify user about the new bit rate */
298 if (av->vbcb.first)
299 av->vbcb.first (av, i->friend_number, true, i->vba.bit_rate, av->vbcb.second);
300
301 /* Stop sending dummy packets */
302 memset(&i->vba, 0, sizeof(i->vba));
303 }
304
305 if (i->msi_call->self_capabilities & msi_CapRAudio &&
306 i->msi_call->peer_capabilities & msi_CapSAudio)
307 rc = MIN(i->audio.second->last_packet_frame_duration, rc);
308
309 if (i->msi_call->self_capabilities & msi_CapRVideo &&
310 i->msi_call->peer_capabilities & msi_CapSVideo)
311 rc = MIN(i->video.second->lcfd, (uint32_t) rc); 252 rc = MIN(i->video.second->lcfd, (uint32_t) rc);
312 253
313 uint32_t fid = i->friend_number; 254 uint32_t fid = i->friend_number;
314 255
315 pthread_mutex_unlock(i->mutex); 256 pthread_mutex_unlock(i->mutex);
316 pthread_mutex_lock(av->mutex); 257 pthread_mutex_lock(av->mutex);
317 258
318 /* In case this call is popped from container stop iteration */ 259 /* In case this call is popped from container stop iteration */
319 if (call_get(av, fid) != i) 260 if (call_get(av, fid) != i)
320 break; 261 break;
321 } 262 }
322 } 263 }
264
323 pthread_mutex_unlock(av->mutex); 265 pthread_mutex_unlock(av->mutex);
324 266
325 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa); 267 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa);
326 av->dmsst += current_time_monotonic() - start; 268 av->dmsst += current_time_monotonic() - start;
327 269
328 if (++av->dmssc == 3) { 270 if (++av->dmssc == 3) {
329 av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */; 271 av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */;
330 av->dmssc = 0; 272 av->dmssc = 0;
331 av->dmsst = 0; 273 av->dmsst = 0;
332 } 274 }
333} 275}
334bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) 276bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
277 TOXAV_ERR_CALL *error)
335{ 278{
336 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) 279 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
337 ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate))
338 ) {
339 if (error)
340 *error = TOXAV_ERR_CALL_INVALID_BIT_RATE;
341 return false;
342 }
343 280
344 pthread_mutex_lock(av->mutex); 281 pthread_mutex_lock(av->mutex);
345 ToxAVCall* call = call_new(av, friend_number, error); 282
283 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) {
285 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
286 goto END;
287 }
288
289 ToxAVCall *call = call_new(av, friend_number, error);
290
346 if (call == NULL) { 291 if (call == NULL) {
347 pthread_mutex_unlock(av->mutex); 292 rc = TOXAV_ERR_CALL_MALLOC;
348 return false; 293 goto END;
349 } 294 }
350 295
351 call->audio_bit_rate = audio_bit_rate; 296 call->audio_bit_rate = audio_bit_rate;
352 call->video_bit_rate = video_bit_rate; 297 call->video_bit_rate = video_bit_rate;
353 298
354 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; 299 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
355 300
356 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 301 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
357 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 302 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
358 303
359 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) { 304 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
360 call_remove(call); 305 call_remove(call);
361 if (error) 306 rc = TOXAV_ERR_CALL_SYNC;
362 *error = TOXAV_ERR_CALL_MALLOC; 307 goto END;
363 pthread_mutex_unlock(av->mutex);
364 return false;
365 } 308 }
366 309
367 call->msi_call->av_call = call; 310 call->msi_call->av_call = call;
311
312END:
368 pthread_mutex_unlock(av->mutex); 313 pthread_mutex_unlock(av->mutex);
369 314
370 return true; 315 if (error)
316 *error = rc;
317
318 return rc == TOXAV_ERR_CALL_OK;
371} 319}
372void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) 320void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data)
373{ 321{
374 pthread_mutex_lock(av->mutex); 322 pthread_mutex_lock(av->mutex);
375 av->ccb.first = function; 323 av->ccb.first = function;
376 av->ccb.second = user_data; 324 av->ccb.second = user_data;
377 pthread_mutex_unlock(av->mutex); 325 pthread_mutex_unlock(av->mutex);
378} 326}
379bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) 327bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
328 TOXAV_ERR_ANSWER *error)
380{ 329{
381 pthread_mutex_lock(av->mutex); 330 pthread_mutex_lock(av->mutex);
382 331
383 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; 332 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
333
384 if (m_friend_exists(av->m, friend_number) == 0) { 334 if (m_friend_exists(av->m, friend_number) == 0) {
385 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; 335 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
386 goto END; 336 goto END;
387 } 337 }
388 338
389 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) 339 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
390 ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate)) 340 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))
391 ) { 341 ) {
392 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE; 342 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
393 goto END; 343 goto END;
394 } 344 }
395 345
396 ToxAVCall* call = call_get(av, friend_number); 346 ToxAVCall *call = call_get(av, friend_number);
347
397 if (call == NULL) { 348 if (call == NULL) {
398 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; 349 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
399 goto END; 350 goto END;
400 } 351 }
401 352
402 if (!call_prepare_transmission(call)) { 353 if (!call_prepare_transmission(call)) {
403 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION; 354 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
404 goto END; 355 goto END;
405 } 356 }
406 357
407 call->audio_bit_rate = audio_bit_rate; 358 call->audio_bit_rate = audio_bit_rate;
408 call->video_bit_rate = video_bit_rate; 359 call->video_bit_rate = video_bit_rate;
409 360
410 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo; 361 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
411 362
412 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 363 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
413 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 364 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
414 365
415 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) 366 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
416 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ 367 rc = TOXAV_ERR_ANSWER_SYNC;
417 368
418
419END: 369END:
420 pthread_mutex_unlock(av->mutex); 370 pthread_mutex_unlock(av->mutex);
421 371
422 if (error) 372 if (error)
423 *error = rc; 373 *error = rc;
424 374
425 return rc == TOXAV_ERR_ANSWER_OK; 375 return rc == TOXAV_ERR_ANSWER_OK;
426} 376}
427void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) 377void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data)
428{ 378{
429 pthread_mutex_lock(av->mutex); 379 pthread_mutex_lock(av->mutex);
430 av->scb.first = function; 380 av->scb.first = function;
431 av->scb.second = user_data; 381 av->scb.second = user_data;
432 pthread_mutex_unlock(av->mutex); 382 pthread_mutex_unlock(av->mutex);
433} 383}
434bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) 384bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error)
435{ 385{
436 pthread_mutex_lock(av->mutex); 386 pthread_mutex_lock(av->mutex);
437 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; 387 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
438 388
439 if (m_friend_exists(av->m, friend_number) == 0) { 389 if (m_friend_exists(av->m, friend_number) == 0) {
440 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; 390 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
441 goto END; 391 goto END;
442 } 392 }
443 393
444 ToxAVCall* call = call_get(av, friend_number); 394 ToxAVCall *call = call_get(av, friend_number);
395
445 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) { 396 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
446 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 397 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
447 goto END; 398 goto END;
448 } 399 }
449 400
450 switch (control) { 401 switch (control) {
451 case TOXAV_CALL_CONTROL_RESUME: { 402 case TOXAV_CALL_CONTROL_RESUME: {
452 /* Only act if paused and had media transfer active before */ 403 /* Only act if paused and had media transfer active before */
453 if (call->msi_call->self_capabilities == 0 && 404 if (call->msi_call->self_capabilities == 0 &&
454 call->previous_self_capabilities ) { 405 call->previous_self_capabilities) {
455 406
456 if (msi_change_capabilities(call->msi_call, 407 if (msi_change_capabilities(call->msi_call,
457 call->previous_self_capabilities) == -1) { 408 call->previous_self_capabilities) == -1) {
458 /* The only reason for this function to fail is invalid state 409 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
459 * ( not active ) */
460 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
461 goto END; 410 goto END;
462 } 411 }
463 412
464 rtp_start_receiving(call->audio.first); 413 rtp_allow_receiving(call->audio.first);
465 rtp_start_receiving(call->video.first); 414 rtp_allow_receiving(call->video.first);
466 } else { 415 } else {
467 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 416 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
468 goto END; 417 goto END;
469 } 418 }
470 } break; 419 }
471 420 break;
421
472 case TOXAV_CALL_CONTROL_PAUSE: { 422 case TOXAV_CALL_CONTROL_PAUSE: {
473 /* Only act if not already paused */ 423 /* Only act if not already paused */
474 if (call->msi_call->self_capabilities) { 424 if (call->msi_call->self_capabilities) {
475 call->previous_self_capabilities = call->msi_call->self_capabilities; 425 call->previous_self_capabilities = call->msi_call->self_capabilities;
476 426
477 if (msi_change_capabilities(call->msi_call, 0) == -1 ) { 427 if (msi_change_capabilities(call->msi_call, 0) == -1) {
478 /* The only reason for this function to fail is invalid state 428 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
479 * ( not active ) */
480 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
481 goto END; 429 goto END;
482 } 430 }
483 431
484 rtp_stop_receiving(call->audio.first); 432 rtp_stop_receiving(call->audio.first);
485 rtp_stop_receiving(call->video.first); 433 rtp_stop_receiving(call->video.first);
486 } else { 434 } else {
487 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 435 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
488 goto END; 436 goto END;
489 } 437 }
490 } break; 438 }
491 439 break;
440
492 case TOXAV_CALL_CONTROL_CANCEL: { 441 case TOXAV_CALL_CONTROL_CANCEL: {
493 /* Hang up */ 442 /* Hang up */
494 msi_hangup(call->msi_call); 443 if (msi_hangup(call->msi_call) != 0) {
495 444 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
445 goto END;
446 }
447
496 /* No mather the case, terminate the call */ 448 /* No mather the case, terminate the call */
497 call_kill_transmission(call); 449 call_kill_transmission(call);
498 call_remove(call); 450 call_remove(call);
499 } break; 451 }
500 452 break;
453
501 case TOXAV_CALL_CONTROL_MUTE_AUDIO: { 454 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
502 if (call->msi_call->self_capabilities & msi_CapRAudio) { 455 if (call->msi_call->self_capabilities & msi_CapRAudio) {
503 if (msi_change_capabilities(call->msi_call, call-> 456 if (msi_change_capabilities(call->msi_call, call->
504 msi_call->self_capabilities ^ msi_CapRAudio) == -1) { 457 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
505 /* The only reason for this function to fail is invalid state 458 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
506 * ( not active ) */
507 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
508 goto END; 459 goto END;
509 } 460 }
510 461
511 rtp_stop_receiving(call->audio.first); 462 rtp_stop_receiving(call->audio.first);
512 } else { 463 } else {
513 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 464 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
514 goto END; 465 goto END;
515 } 466 }
516 } break; 467 }
517 468 break;
469
518 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: { 470 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
519 if (call->msi_call->self_capabilities ^ msi_CapRAudio) { 471 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
520 if (msi_change_capabilities(call->msi_call, call-> 472 if (msi_change_capabilities(call->msi_call, call->
521 msi_call->self_capabilities | msi_CapRAudio) == -1) { 473 msi_call->self_capabilities | msi_CapRAudio) == -1) {
522 /* The only reason for this function to fail is invalid state 474 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
523 * ( not active ) */
524 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
525 goto END; 475 goto END;
526 } 476 }
527 477
528 rtp_start_receiving(call->audio.first); 478 rtp_allow_receiving(call->audio.first);
529 } else { 479 } else {
530 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 480 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
531 goto END; 481 goto END;
532 } 482 }
533 } break; 483 }
534 484 break;
485
535 case TOXAV_CALL_CONTROL_HIDE_VIDEO: { 486 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
536 if (call->msi_call->self_capabilities & msi_CapRVideo) { 487 if (call->msi_call->self_capabilities & msi_CapRVideo) {
537 if (msi_change_capabilities(call->msi_call, call-> 488 if (msi_change_capabilities(call->msi_call, call->
538 msi_call->self_capabilities ^ msi_CapRVideo) == -1) { 489 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
539 /* The only reason for this function to fail is invalid state 490 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
540 * ( not active ) */
541 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
542 goto END; 491 goto END;
543 } 492 }
544 493
545 rtp_stop_receiving(call->video.first); 494 rtp_stop_receiving(call->video.first);
546 } else { 495 } else {
547 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 496 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
548 goto END; 497 goto END;
549 } 498 }
550 } break; 499 }
551 500 break;
501
552 case TOXAV_CALL_CONTROL_SHOW_VIDEO: { 502 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
553 if (call->msi_call->self_capabilities ^ msi_CapRVideo) { 503 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
554 if (msi_change_capabilities(call->msi_call, call-> 504 if (msi_change_capabilities(call->msi_call, call->
555 msi_call->self_capabilities | msi_CapRVideo) == -1) { 505 msi_call->self_capabilities | msi_CapRVideo) == -1) {
556 /* The only reason for this function to fail is invalid state 506 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
557 * ( not active ) */
558 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
559 goto END; 507 goto END;
560 } 508 }
561 509
562 rtp_start_receiving(call->audio.first); 510 rtp_allow_receiving(call->audio.first);
563 } else { 511 } else {
564 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; 512 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
565 goto END; 513 goto END;
566 } 514 }
567 } break; 515 }
516 break;
568 } 517 }
569 518
570END: 519END:
571 pthread_mutex_unlock(av->mutex); 520 pthread_mutex_unlock(av->mutex);
572 521
573 if (error) 522 if (error)
574 *error = rc; 523 *error = rc;
575 524
576 return rc == TOXAV_ERR_CALL_CONTROL_OK; 525 return rc == TOXAV_ERR_CALL_CONTROL_OK;
577} 526}
578void toxav_callback_audio_bit_rate_status(ToxAV* av, toxav_audio_bit_rate_status_cb* function, void* user_data) 527bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate,
528 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error)
579{ 529{
580 pthread_mutex_lock(av->mutex); 530 TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK;
581 av->abcb.first = function; 531 ToxAVCall *call;
582 av->abcb.second = user_data; 532
583 pthread_mutex_unlock(av->mutex);
584}
585bool toxav_audio_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error)
586{
587 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
588
589 TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK;
590 ToxAVCall* call;
591
592 if (m_friend_exists(av->m, friend_number) == 0) { 533 if (m_friend_exists(av->m, friend_number) == 0) {
593 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND; 534 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
594 goto END; 535 goto END;
595 } 536 }
596 537
597 if (audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate)) { 538 if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) {
598 rc = TOXAV_ERR_SET_BIT_RATE_INVALID; 539 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE;
599 goto END; 540 goto END;
600 } 541 }
601 542
543 if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) {
544 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE;
545 goto END;
546 }
547
602 pthread_mutex_lock(av->mutex); 548 pthread_mutex_lock(av->mutex);
603 call = call_get(av, friend_number); 549 call = call_get(av, friend_number);
550
604 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { 551 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
605 pthread_mutex_unlock(av->mutex); 552 pthread_mutex_unlock(av->mutex);
606 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL; 553 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
607 goto END;
608 }
609
610 if (call->audio_bit_rate == audio_bit_rate || (call->aba.active && call->aba.bit_rate == audio_bit_rate)) {
611 pthread_mutex_unlock(av->mutex);
612 goto END;
613 }
614
615 /* Video sending is turned off; notify peer */
616 if (audio_bit_rate == 0) {
617 call->audio_bit_rate = 0;
618
619 msi_change_capabilities(call->msi_call, call->msi_call->
620 self_capabilities ^ msi_CapSAudio);
621 pthread_mutex_unlock(av->mutex);
622 goto END; 554 goto END;
623 } 555 }
624 556
625 pthread_mutex_lock(call->mutex); 557 if (audio_bit_rate >= 0) {
626 558 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
627 if (call->audio_bit_rate == 0) { 559
628 /* The audio has been turned off before this */ 560 if (call->audio_bit_rate == audio_bit_rate) {
629 call->audio_bit_rate = audio_bit_rate; 561 LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate);
630 562 } else if (audio_bit_rate == 0) {
631 msi_change_capabilities(call->msi_call, call-> 563 LOGGER_DEBUG("Turned off audio sending");
632 msi_call->self_capabilities | msi_CapSAudio); 564 if (msi_change_capabilities(call->msi_call, call->msi_call->
633 565 self_capabilities ^ msi_CapSAudio) != 0) {
634 if (!force && av->abcb.first) 566 pthread_mutex_unlock(av->mutex);
635 av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second); 567 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
636 } else { 568 goto END;
637 /* The audio was active before this */ 569 }
638 if (audio_bit_rate > call->audio_bit_rate && !force) 570 /* Audio sending is turned off; notify peer */
639 ba_set(&call->aba, audio_bit_rate); 571 call->audio_bit_rate = 0;
640 else { 572 } else {
641 /* Cancel any previous non forceful bitrate change request */ 573 pthread_mutex_lock(call->mutex);
642 memset(&call->aba, 0, sizeof(call->aba)); 574 if (call->audio_bit_rate == 0) {
575 LOGGER_DEBUG("Turned on audio sending");
576 /* The audio has been turned off before this */
577 if (msi_change_capabilities(call->msi_call, call->
578 msi_call->self_capabilities | msi_CapSAudio) != 0) {
579 pthread_mutex_unlock(call->mutex);
580 pthread_mutex_unlock(av->mutex);
581 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
582 goto END;
583 }
584 } else
585 LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate);
643 call->audio_bit_rate = audio_bit_rate; 586 call->audio_bit_rate = audio_bit_rate;
644 587 pthread_mutex_unlock(call->mutex);
645 if (!force && av->abcb.first) 588 }
646 av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second); 589 }
590
591 if (video_bit_rate >= 0) {
592 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
593
594 if (call->video_bit_rate == video_bit_rate) {
595 LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate);
596 } else if (video_bit_rate == 0) {
597 LOGGER_DEBUG("Turned off video sending");
598 /* Video sending is turned off; notify peer */
599 if (msi_change_capabilities(call->msi_call, call->msi_call->
600 self_capabilities ^ msi_CapSVideo) != 0) {
601 pthread_mutex_unlock(av->mutex);
602 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
603 goto END;
604 }
605 call->video_bit_rate = 0;
606 } else {
607 pthread_mutex_lock(call->mutex);
608 if (call->video_bit_rate == 0) {
609 LOGGER_DEBUG("Turned on video sending");
610 /* The video has been turned off before this */
611 if (msi_change_capabilities(call->msi_call, call->
612 msi_call->self_capabilities | msi_CapSVideo) != 0) {
613 pthread_mutex_unlock(call->mutex);
614 pthread_mutex_unlock(av->mutex);
615 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
616 goto END;
617 }
618 } else
619 LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate);
620 call->video_bit_rate = video_bit_rate;
621 pthread_mutex_unlock(call->mutex);
647 } 622 }
648 } 623 }
649 624
650 pthread_mutex_unlock(call->mutex);
651 pthread_mutex_unlock(av->mutex); 625 pthread_mutex_unlock(av->mutex);
652
653END: 626END:
654 if (error) 627 if (error)
655 *error = rc; 628 *error = rc;
656 629
657 return rc == TOXAV_ERR_SET_BIT_RATE_OK; 630 return rc == TOXAV_ERR_BIT_RATE_SET_OK;
658} 631}
659void toxav_callback_video_bit_rate_status(ToxAV* av, toxav_video_bit_rate_status_cb* function, void* user_data) 632void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data)
660{ 633{
661 pthread_mutex_lock(av->mutex); 634 pthread_mutex_lock(av->mutex);
662 av->vbcb.first = function; 635 av->bcb.first = function;
663 av->vbcb.second = user_data; 636 av->bcb.second = user_data;
664 pthread_mutex_unlock(av->mutex); 637 pthread_mutex_unlock(av->mutex);
665} 638}
666bool toxav_video_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error) 639bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
667{ 640 uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error)
668 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
669
670 TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK;
671 ToxAVCall* call;
672
673 if (m_friend_exists(av->m, friend_number) == 0) {
674 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND;
675 goto END;
676 }
677
678 if (video_bit_rate && video_bit_rate_invalid(video_bit_rate)) {
679 rc = TOXAV_ERR_SET_BIT_RATE_INVALID;
680 goto END;
681 }
682
683 pthread_mutex_lock(av->mutex);
684 call = call_get(av, friend_number);
685 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
686 pthread_mutex_unlock(av->mutex);
687 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL;
688 goto END;
689 }
690
691 if (call->video_bit_rate == video_bit_rate || (call->vba.active && call->vba.bit_rate == video_bit_rate)) {
692 pthread_mutex_unlock(av->mutex);
693 goto END;
694 }
695
696 /* Video sending is turned off; notify peer */
697 if (video_bit_rate == 0) {
698 call->video_bit_rate = 0;
699
700 msi_change_capabilities(call->msi_call, call->msi_call->
701 self_capabilities ^ msi_CapSVideo);
702 pthread_mutex_unlock(av->mutex);
703 goto END;
704 }
705
706 pthread_mutex_lock(call->mutex);
707
708 if (call->video_bit_rate == 0) {
709 /* The video has been turned off before this */
710 call->video_bit_rate = video_bit_rate;
711
712 msi_change_capabilities(call->msi_call, call->
713 msi_call->self_capabilities | msi_CapSVideo);
714
715 if (!force && av->vbcb.first)
716 av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second);
717 } else {
718 /* The video was active before this */
719 if (video_bit_rate > call->video_bit_rate && !force)
720 ba_set(&call->vba, video_bit_rate);
721 else {
722 /* Cancel any previous non forceful bitrate change request */
723 memset(&call->vba, 0, sizeof(call->vba));
724 call->video_bit_rate = video_bit_rate;
725
726 if (!force && av->vbcb.first)
727 av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second);
728 }
729 }
730
731 pthread_mutex_unlock(call->mutex);
732 pthread_mutex_unlock(av->mutex);
733
734END:
735 if (error)
736 *error = rc;
737
738 return rc == TOXAV_ERR_SET_BIT_RATE_OK;
739}
740bool 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)
741{ 641{
742 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; 642 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
743 ToxAVCall* call; 643 ToxAVCall *call;
744 644
745 if (m_friend_exists(av->m, friend_number) == 0) { 645 if (m_friend_exists(av->m, friend_number) == 0) {
746 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; 646 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
747 goto END; 647 goto END;
748 } 648 }
749 649
750 pthread_mutex_lock(av->mutex); 650 pthread_mutex_lock(av->mutex);
751 call = call_get(av, friend_number); 651 call = call_get(av, friend_number);
652
752 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { 653 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
753 pthread_mutex_unlock(av->mutex); 654 pthread_mutex_unlock(av->mutex);
754 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 655 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
755 goto END; 656 goto END;
756 } 657 }
757 658
758 if (call->audio_bit_rate == 0 || 659 if (call->audio_bit_rate == 0 ||
759 !(call->msi_call->self_capabilities & msi_CapSAudio) || 660 !(call->msi_call->self_capabilities & msi_CapSAudio) ||
760 !(call->msi_call->peer_capabilities & msi_CapRAudio)) { 661 !(call->msi_call->peer_capabilities & msi_CapRAudio)) {
761 pthread_mutex_unlock(av->mutex); 662 pthread_mutex_unlock(av->mutex);
762 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED; 663 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
763 goto END; 664 goto END;
764 } 665 }
765 666
766 pthread_mutex_lock(call->mutex_audio); 667 pthread_mutex_lock(call->mutex_audio);
767 pthread_mutex_unlock(av->mutex); 668 pthread_mutex_unlock(av->mutex);
768 669
769 if ( pcm == NULL ) { 670 if (pcm == NULL) {
770 pthread_mutex_unlock(call->mutex_audio); 671 pthread_mutex_unlock(call->mutex_audio);
771 rc = TOXAV_ERR_SEND_FRAME_NULL; 672 rc = TOXAV_ERR_SEND_FRAME_NULL;
772 goto END; 673 goto END;
773 } 674 }
774 675
775 if ( channels > 2 ) { 676 if (channels > 2) {
776 pthread_mutex_unlock(call->mutex_audio); 677 pthread_mutex_unlock(call->mutex_audio);
777 rc = TOXAV_ERR_SEND_FRAME_INVALID; 678 rc = TOXAV_ERR_SEND_FRAME_INVALID;
778 goto END; 679 goto END;
779 } 680 }
780 681
781 { /* Encode and send */ 682 { /* Encode and send */
782 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) { 683 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
783 pthread_mutex_unlock(call->mutex_audio); 684 pthread_mutex_unlock(call->mutex_audio);
784 rc = TOXAV_ERR_SEND_FRAME_INVALID; 685 rc = TOXAV_ERR_SEND_FRAME_INVALID;
785 goto END; 686 goto END;
786 } 687 }
787 688
788 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */ 689 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
789 690
790 sampling_rate = htonl(sampling_rate); 691 sampling_rate = htonl(sampling_rate);
791 memcpy(dest, &sampling_rate, sizeof(sampling_rate)); 692 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
792 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count, 693 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
793 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate)); 694 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
794 695
795 if (vrc < 0) { 696 if (vrc < 0) {
796 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc)); 697 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
797 pthread_mutex_unlock(call->mutex_audio); 698 pthread_mutex_unlock(call->mutex_audio);
798 rc = TOXAV_ERR_SEND_FRAME_INVALID; 699 rc = TOXAV_ERR_SEND_FRAME_INVALID;
799 goto END; 700 goto END;
800 } 701 }
801 702
802 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), false) != 0) { 703 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) {
803 LOGGER_WARNING("Failed to send audio packet"); 704 LOGGER_WARNING("Failed to send audio packet");
804 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; 705 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
805 } 706 }
806
807
808 /* For bit rate measurement; send dummy packet */
809 if (ba_shoud_send_dummy(&call->aba)) {
810 sampling_rate = ntohl(sampling_rate);
811 if (ac_reconfigure_test_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
812 /* FIXME should the bit rate changing fail here? */
813 pthread_mutex_unlock(call->mutex_audio);
814 rc = TOXAV_ERR_SEND_FRAME_INVALID;
815 goto END;
816 }
817
818 sampling_rate = htonl(sampling_rate);
819 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
820 vrc = opus_encode(call->audio.second->test_encoder, pcm, sample_count,
821 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
822
823 if (vrc < 0) {
824 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
825 pthread_mutex_unlock(call->mutex_audio);
826 rc = TOXAV_ERR_SEND_FRAME_INVALID;
827 goto END;
828 }
829
830 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), true) != 0) {
831 LOGGER_WARNING("Failed to send audio packet");
832 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
833 }
834
835 if (call->aba.end_time == (uint64_t) ~0)
836 call->aba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS;
837 }
838 } 707 }
839 708
840 709
841 pthread_mutex_unlock(call->mutex_audio); 710 pthread_mutex_unlock(call->mutex_audio);
842 711
843END: 712END:
844 if (error) 713 if (error)
845 *error = rc; 714 *error = rc;
846 715
847 return rc == TOXAV_ERR_SEND_FRAME_OK; 716 return rc == TOXAV_ERR_SEND_FRAME_OK;
848} 717}
849bool 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) 718bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
719 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
850{ 720{
851 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; 721 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
852 ToxAVCall* call; 722 ToxAVCall *call;
853 723
854 if (m_friend_exists(av->m, friend_number) == 0) { 724 if (m_friend_exists(av->m, friend_number) == 0) {
855 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; 725 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
856 goto END; 726 goto END;
857 } 727 }
858 728
859 pthread_mutex_lock(av->mutex); 729 pthread_mutex_lock(av->mutex);
860 call = call_get(av, friend_number); 730 call = call_get(av, friend_number);
731
861 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) { 732 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
862 pthread_mutex_unlock(av->mutex); 733 pthread_mutex_unlock(av->mutex);
863 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 734 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
864 goto END; 735 goto END;
865 } 736 }
866 737
867 if (call->video_bit_rate == 0 || 738 if (call->video_bit_rate == 0 ||
868 !(call->msi_call->self_capabilities & msi_CapSVideo) || 739 !(call->msi_call->self_capabilities & msi_CapSVideo) ||
869 !(call->msi_call->peer_capabilities & msi_CapRVideo)) { 740 !(call->msi_call->peer_capabilities & msi_CapRVideo)) {
870 pthread_mutex_unlock(av->mutex); 741 pthread_mutex_unlock(av->mutex);
871 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED; 742 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
872 goto END; 743 goto END;
873 } 744 }
874 745
875 pthread_mutex_lock(call->mutex_video); 746 pthread_mutex_lock(call->mutex_video);
876 pthread_mutex_unlock(av->mutex); 747 pthread_mutex_unlock(av->mutex);
877 748
878 if ( y == NULL || u == NULL || v == NULL ) { 749 if (y == NULL || u == NULL || v == NULL) {
879 pthread_mutex_unlock(call->mutex_video); 750 pthread_mutex_unlock(call->mutex_video);
880 rc = TOXAV_ERR_SEND_FRAME_NULL; 751 rc = TOXAV_ERR_SEND_FRAME_NULL;
881 goto END; 752 goto END;
882 } 753 }
883 754
884 if ( vc_reconfigure_encoder(call->video.second->encoder, call->video_bit_rate * 1000, width, height) != 0 ) { 755 if (vc_reconfigure_encoder(call->video.second->encoder, call->video_bit_rate * 1000, width, height) != 0) {
885 pthread_mutex_unlock(call->mutex_video); 756 pthread_mutex_unlock(call->mutex_video);
886 rc = TOXAV_ERR_SEND_FRAME_INVALID; 757 rc = TOXAV_ERR_SEND_FRAME_INVALID;
887 goto END; 758 goto END;
888 } 759 }
889 760
890 { /* Encode */ 761 { /* Encode */
891 vpx_image_t img; 762 vpx_image_t img;
892 img.w = img.h = img.d_w = img.d_h = 0; 763 img.w = img.h = img.d_w = img.d_h = 0;
893 vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); 764 vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
894 765
895 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." 766 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
896 * http://fourcc.org/yuv.php#IYUV 767 * http://fourcc.org/yuv.php#IYUV
897 */ 768 */
898 memcpy(img.planes[VPX_PLANE_Y], y, width * height); 769 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
899 memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); 770 memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
900 memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); 771 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
901 772
902 int vrc = vpx_codec_encode(call->video.second->encoder, &img, 773 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
903 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 774 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
904 775
905 vpx_img_free(&img); 776 vpx_img_free(&img);
906 if ( vrc != VPX_CODEC_OK) { 777
778 if (vrc != VPX_CODEC_OK) {
907 pthread_mutex_unlock(call->mutex_video); 779 pthread_mutex_unlock(call->mutex_video);
908 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); 780 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
909 rc = TOXAV_ERR_SEND_FRAME_INVALID; 781 rc = TOXAV_ERR_SEND_FRAME_INVALID;
910 goto END; 782 goto END;
911 } 783 }
912 } 784 }
913 785
914 ++call->video.second->frame_counter; 786 ++call->video.second->frame_counter;
915 787
916 { /* Split and send */ 788 { /* Send frames */
917 vpx_codec_iter_t iter = NULL;
918 const vpx_codec_cx_pkt_t *pkt;
919
920 vc_init_video_splitter_cycle(call->video.second);
921
922 while ( (pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter)) ) {
923 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
924 int parts = vc_update_video_splitter_cycle(call->video.second, pkt->data.frame.buf,
925 pkt->data.frame.sz);
926
927 if (parts < 0) /* Should never happen though */
928 continue;
929
930 uint16_t part_size;
931 const uint8_t *iter;
932
933 int i;
934 for (i = 0; i < parts; i++) {
935 iter = vc_iterate_split_video_frame(call->video.second, &part_size);
936
937 if (rtp_send_data(call->video.first, iter, part_size, false) < 0) {
938 pthread_mutex_unlock(call->mutex_video);
939 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
940 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
941 goto END;
942 }
943 }
944 }
945 }
946 }
947
948 if (ba_shoud_send_dummy(&call->vba)) {
949 if ( vc_reconfigure_encoder(call->video.second->test_encoder, call->vba.bit_rate * 1000, width, height) != 0 ) {
950 pthread_mutex_unlock(call->mutex_video);
951 rc = TOXAV_ERR_SEND_FRAME_INVALID;
952 goto END;
953 }
954
955 /* FIXME use the same image as before */
956 vpx_image_t img;
957 img.w = img.h = img.d_w = img.d_h = 0;
958 vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1);
959
960 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
961 * http://fourcc.org/yuv.php#IYUV
962 */
963 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
964 memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2));
965 memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2));
966
967 int vrc = vpx_codec_encode(call->video.second->test_encoder, &img,
968 call->video.second->test_frame_counter, 1, 0, MAX_ENCODE_TIME_US);
969
970 vpx_img_free(&img);
971 if ( vrc != VPX_CODEC_OK) {
972 pthread_mutex_unlock(call->mutex_video);
973 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
974 rc = TOXAV_ERR_SEND_FRAME_INVALID;
975 goto END;
976 }
977
978 call->video.second->test_frame_counter++;
979
980 vpx_codec_iter_t iter = NULL; 789 vpx_codec_iter_t iter = NULL;
981 const vpx_codec_cx_pkt_t *pkt; 790 const vpx_codec_cx_pkt_t *pkt;
982 791
983 /* Send the encoded data as dummy packets */ 792 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) {
984 while ( (pkt = vpx_codec_get_cx_data(call->video.second->test_encoder, &iter)) ) { 793 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT &&
985 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 794 rtp_send_data(call->video.first, pkt->data.frame.buf, pkt->data.frame.sz) < 0) {
986 795
987 int parts = pkt->data.frame.sz / 1300; 796 pthread_mutex_unlock(call->mutex_video);
988 int i; 797 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
989 for (i = 0; i < parts; i++) { 798 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
990 if (rtp_send_data(call->video.first, pkt->data.frame.buf + i * 1300, 1300, true) < 0) { 799 goto END;
991 pthread_mutex_unlock(call->mutex_video);
992 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
993 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
994 goto END;
995 }
996 }
997
998 if (pkt->data.frame.sz % 1300) {
999 if (rtp_send_data(call->video.first, pkt->data.frame.buf + parts * 1300, pkt->data.frame.sz % 1300, true) < 0) {
1000 pthread_mutex_unlock(call->mutex_video);
1001 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
1002 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
1003 goto END;
1004 }
1005 }
1006 } 800 }
1007 } 801 }
1008
1009 if (call->vba.end_time == (uint64_t) ~0)
1010 call->vba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS;
1011 } 802 }
1012 803
1013 pthread_mutex_unlock(call->mutex_video); 804 pthread_mutex_unlock(call->mutex_video);
1014 805
1015END: 806END:
1016 if (error) 807 if (error)
1017 *error = rc; 808 *error = rc;
1018 809
1019 return rc == TOXAV_ERR_SEND_FRAME_OK; 810 return rc == TOXAV_ERR_SEND_FRAME_OK;
1020} 811}
1021void toxav_callback_audio_receive_frame(ToxAV* av, toxav_audio_receive_frame_cb* function, void* user_data) 812void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data)
1022{ 813{
1023 pthread_mutex_lock(av->mutex); 814 pthread_mutex_lock(av->mutex);
1024 av->acb.first = function; 815 av->acb.first = function;
1025 av->acb.second = user_data; 816 av->acb.second = user_data;
1026 pthread_mutex_unlock(av->mutex); 817 pthread_mutex_unlock(av->mutex);
1027} 818}
1028void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb* function, void* user_data) 819void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data)
1029{ 820{
1030 pthread_mutex_lock(av->mutex); 821 pthread_mutex_lock(av->mutex);
1031 av->vcb.first = function; 822 av->vcb.first = function;
@@ -1039,108 +830,144 @@ void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb*
1039 * :: Internal 830 * :: Internal
1040 * 831 *
1041 ******************************************************************************/ 832 ******************************************************************************/
1042int callback_invite(void* toxav_inst, MSICall* call) 833void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* user_data)
1043{ 834{
1044 ToxAV* toxav = toxav_inst; 835 /* Callback which is called when the internal measure mechanism reported packet loss.
1045 pthread_mutex_lock(toxav->mutex); 836 * We report suggested lowered bitrate to an app. If app is sending both audio and video,
837 * we will report lowered bitrate for video only because in that case video probably
838 * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
839 * The application may choose to disable video totally if the stream is too bad.
840 */
841
842 ToxAVCall* call = user_data;
843 assert(call);
844
845 LOGGER_DEBUG("Reported loss of %f%%", loss*100);
1046 846
1047 ToxAVCall* av_call = call_new(toxav, call->friend_number, NULL); 847 if (loss < .01f)
848 return;
849
850 pthread_mutex_lock(call->av->mutex);
851 if (!call->av->bcb.first) {
852 pthread_mutex_unlock(call->av->mutex);
853 LOGGER_WARNING("No callback to report loss on");
854 return;
855 }
856
857 if (call->video_bit_rate)
858 (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate,
859 call->video_bit_rate - (call->video_bit_rate * loss),
860 call->av->bcb.second);
861 else if (call->audio_bit_rate)
862 (*call->av->bcb.first) (call->av, friend_number,
863 call->audio_bit_rate - (call->audio_bit_rate * loss),
864 0, call->av->bcb.second);
865
866 pthread_mutex_unlock(call->av->mutex);
867}
868int callback_invite(void *toxav_inst, MSICall *call)
869{
870 ToxAV *toxav = toxav_inst;
871 pthread_mutex_lock(toxav->mutex);
872
873 ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL);
874
1048 if (av_call == NULL) { 875 if (av_call == NULL) {
1049 LOGGER_WARNING("Failed to initialize call..."); 876 LOGGER_WARNING("Failed to initialize call...");
1050 pthread_mutex_unlock(toxav->mutex); 877 pthread_mutex_unlock(toxav->mutex);
1051 return -1; 878 return -1;
1052 } 879 }
1053 880
1054 call->av_call = av_call; 881 call->av_call = av_call;
1055 av_call->msi_call = call; 882 av_call->msi_call = call;
1056 883
1057 if (toxav->ccb.first) 884 if (toxav->ccb.first)
1058 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio, 885 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
1059 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); 886 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
1060 else { 887 else {
1061 /* No handler to capture the call request, send failure */ 888 /* No handler to capture the call request, send failure */
1062 pthread_mutex_unlock(toxav->mutex); 889 pthread_mutex_unlock(toxav->mutex);
1063 return -1; 890 return -1;
1064 } 891 }
1065 892
1066 pthread_mutex_unlock(toxav->mutex); 893 pthread_mutex_unlock(toxav->mutex);
1067 return 0; 894 return 0;
1068} 895}
1069int callback_start(void* toxav_inst, MSICall* call) 896int callback_start(void *toxav_inst, MSICall *call)
1070{ 897{
1071 ToxAV* toxav = toxav_inst; 898 ToxAV *toxav = toxav_inst;
1072 pthread_mutex_lock(toxav->mutex); 899 pthread_mutex_lock(toxav->mutex);
1073 900
1074 ToxAVCall* av_call = call_get(toxav, call->friend_number); 901 ToxAVCall *av_call = call_get(toxav, call->friend_number);
1075 902
1076 if (av_call == NULL) { 903 if (av_call == NULL) {
1077 /* Should this ever happen? */ 904 /* Should this ever happen? */
1078 pthread_mutex_unlock(toxav->mutex); 905 pthread_mutex_unlock(toxav->mutex);
1079 return -1; 906 return -1;
1080 } 907 }
1081 908
1082 if (!call_prepare_transmission(av_call)) { 909 if (!call_prepare_transmission(av_call)) {
1083 callback_error(toxav_inst, call); 910 callback_error(toxav_inst, call);
1084 pthread_mutex_unlock(toxav->mutex); 911 pthread_mutex_unlock(toxav->mutex);
1085 return -1; 912 return -1;
1086 } 913 }
1087 914
1088 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) { 915 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
1089 callback_error(toxav_inst, call); 916 callback_error(toxav_inst, call);
1090 pthread_mutex_unlock(toxav->mutex); 917 pthread_mutex_unlock(toxav->mutex);
1091 return -1; 918 return -1;
1092 } 919 }
1093 920
1094 pthread_mutex_unlock(toxav->mutex); 921 pthread_mutex_unlock(toxav->mutex);
1095 return 0; 922 return 0;
1096} 923}
1097int callback_end(void* toxav_inst, MSICall* call) 924int callback_end(void *toxav_inst, MSICall *call)
1098{ 925{
1099 ToxAV* toxav = toxav_inst; 926 ToxAV *toxav = toxav_inst;
1100 pthread_mutex_lock(toxav->mutex); 927 pthread_mutex_lock(toxav->mutex);
1101 928
1102 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED); 929 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
1103 930
1104 if (call->av_call) { 931 if (call->av_call) {
1105 call_kill_transmission(call->av_call); 932 call_kill_transmission(call->av_call);
1106 call_remove(call->av_call); 933 call_remove(call->av_call);
1107 } 934 }
1108 935
1109 pthread_mutex_unlock(toxav->mutex); 936 pthread_mutex_unlock(toxav->mutex);
1110 return 0; 937 return 0;
1111} 938}
1112int callback_error(void* toxav_inst, MSICall* call) 939int callback_error(void *toxav_inst, MSICall *call)
1113{ 940{
1114 ToxAV* toxav = toxav_inst; 941 ToxAV *toxav = toxav_inst;
1115 pthread_mutex_lock(toxav->mutex); 942 pthread_mutex_lock(toxav->mutex);
1116 943
1117 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR); 944 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR);
1118 945
1119 if (call->av_call) { 946 if (call->av_call) {
1120 call_kill_transmission(call->av_call); 947 call_kill_transmission(call->av_call);
1121 call_remove(call->av_call); 948 call_remove(call->av_call);
1122 } 949 }
1123 950
1124 pthread_mutex_unlock(toxav->mutex); 951 pthread_mutex_unlock(toxav->mutex);
1125 return 0; 952 return 0;
1126} 953}
1127int callback_capabilites(void* toxav_inst, MSICall* call) 954int callback_capabilites(void *toxav_inst, MSICall *call)
1128{ 955{
1129 ToxAV* toxav = toxav_inst; 956 ToxAV *toxav = toxav_inst;
1130 pthread_mutex_lock(toxav->mutex); 957 pthread_mutex_lock(toxav->mutex);
1131 958
1132 if (call->peer_capabilities & msi_CapSAudio) 959 if (call->peer_capabilities & msi_CapSAudio)
1133 rtp_start_receiving(((ToxAVCall*)call->av_call)->audio.first); 960 rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first);
1134 else 961 else
1135 rtp_stop_receiving(((ToxAVCall*)call->av_call)->audio.first); 962 rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first);
1136 963
1137 if (call->peer_capabilities & msi_CapSVideo) 964 if (call->peer_capabilities & msi_CapSVideo)
1138 rtp_start_receiving(((ToxAVCall*)call->av_call)->video.first); 965 rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first);
1139 else 966 else
1140 rtp_stop_receiving(((ToxAVCall*)call->av_call)->video.first); 967 rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first);
1141 968
1142 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities); 969 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
1143 970
1144 pthread_mutex_unlock(toxav->mutex); 971 pthread_mutex_unlock(toxav->mutex);
1145 return 0; 972 return 0;
1146} 973}
@@ -1157,201 +984,212 @@ bool video_bit_rate_invalid(uint32_t bit_rate)
1157 /* TODO: If anyone knows the answer to this one please fill it up */ 984 /* TODO: If anyone knows the answer to this one please fill it up */
1158 return false; 985 return false;
1159} 986}
1160bool invoke_call_state_callback(ToxAV* av, uint32_t friend_number, uint32_t state) 987bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1161{ 988{
1162 if (av->scb.first) 989 if (av->scb.first)
1163 av->scb.first(av, friend_number, state, av->scb.second); 990 av->scb.first(av, friend_number, state, av->scb.second);
1164 else 991 else
1165 return false; 992 return false;
993
1166 return true; 994 return true;
1167} 995}
1168ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) 996ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
1169{ 997{
1170 /* Assumes mutex locked */ 998 /* Assumes mutex locked */
1171 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; 999 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1172 ToxAVCall* call = NULL; 1000 ToxAVCall *call = NULL;
1173 1001
1174 if (m_friend_exists(av->m, friend_number) == 0) { 1002 if (m_friend_exists(av->m, friend_number) == 0) {
1175 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; 1003 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1176 goto END; 1004 goto END;
1177 } 1005 }
1178 1006
1179 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) { 1007 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
1180 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; 1008 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1181 goto END; 1009 goto END;
1182 } 1010 }
1183 1011
1184 if (call_get(av, friend_number) != NULL) { 1012 if (call_get(av, friend_number) != NULL) {
1185 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; 1013 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1186 goto END; 1014 goto END;
1187 } 1015 }
1188 1016
1189 1017
1190 call = calloc(sizeof(ToxAVCall), 1); 1018 call = calloc(sizeof(ToxAVCall), 1);
1191 1019
1192 if (call == NULL) { 1020 if (call == NULL) {
1193 rc = TOXAV_ERR_CALL_MALLOC; 1021 rc = TOXAV_ERR_CALL_MALLOC;
1194 goto END; 1022 goto END;
1195 } 1023 }
1196 1024
1197 call->av = av; 1025 call->av = av;
1198 call->friend_number = friend_number; 1026 call->friend_number = friend_number;
1199 1027
1200 if (av->calls == NULL) { /* Creating */ 1028 if (av->calls == NULL) { /* Creating */
1201 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); 1029 av->calls = calloc (sizeof(ToxAVCall *), friend_number + 1);
1202 1030
1203 if (av->calls == NULL) { 1031 if (av->calls == NULL) {
1204 free(call); 1032 free(call);
1205 call = NULL; 1033 call = NULL;
1206 rc = TOXAV_ERR_CALL_MALLOC; 1034 rc = TOXAV_ERR_CALL_MALLOC;
1207 goto END; 1035 goto END;
1208 } 1036 }
1209 1037
1210 av->calls_tail = av->calls_head = friend_number; 1038 av->calls_tail = av->calls_head = friend_number;
1211 1039
1212 } else if (av->calls_tail < friend_number) { /* Appending */ 1040 } else if (av->calls_tail < friend_number) { /* Appending */
1213 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); 1041 void *tmp = realloc(av->calls, sizeof(ToxAVCall *) * friend_number + 1);
1214 1042
1215 if (tmp == NULL) { 1043 if (tmp == NULL) {
1216 free(call); 1044 free(call);
1217 call = NULL; 1045 call = NULL;
1218 rc = TOXAV_ERR_CALL_MALLOC; 1046 rc = TOXAV_ERR_CALL_MALLOC;
1219 goto END; 1047 goto END;
1220 } 1048 }
1221 1049
1222 av->calls = tmp; 1050 av->calls = tmp;
1223 1051
1224 /* Set fields in between to null */ 1052 /* Set fields in between to null */
1225 uint32_t i = av->calls_tail + 1; 1053 uint32_t i = av->calls_tail + 1;
1054
1226 for (; i < friend_number; i ++) 1055 for (; i < friend_number; i ++)
1227 av->calls[i] = NULL; 1056 av->calls[i] = NULL;
1228 1057
1229 call->prev = av->calls[av->calls_tail]; 1058 call->prev = av->calls[av->calls_tail];
1230 av->calls[av->calls_tail]->next = call; 1059 av->calls[av->calls_tail]->next = call;
1231 1060
1232 av->calls_tail = friend_number; 1061 av->calls_tail = friend_number;
1233 1062
1234 } else if (av->calls_head > friend_number) { /* Inserting at front */ 1063 } else if (av->calls_head > friend_number) { /* Inserting at front */
1235 call->next = av->calls[av->calls_head]; 1064 call->next = av->calls[av->calls_head];
1236 av->calls[av->calls_head]->prev = call; 1065 av->calls[av->calls_head]->prev = call;
1237 av->calls_head = friend_number; 1066 av->calls_head = friend_number;
1238 } 1067 }
1239 1068
1240 av->calls[friend_number] = call; 1069 av->calls[friend_number] = call;
1241 1070
1242END: 1071END:
1072
1243 if (error) 1073 if (error)
1244 *error = rc; 1074 *error = rc;
1245 1075
1246 return call; 1076 return call;
1247} 1077}
1248ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) 1078ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
1249{ 1079{
1250 /* Assumes mutex locked */ 1080 /* Assumes mutex locked */
1251 if (av->calls == NULL || av->calls_tail < friend_number) 1081 if (av->calls == NULL || av->calls_tail < friend_number)
1252 return NULL; 1082 return NULL;
1253 1083
1254 return av->calls[friend_number]; 1084 return av->calls[friend_number];
1255} 1085}
1256ToxAVCall* call_remove(ToxAVCall* call) 1086ToxAVCall *call_remove(ToxAVCall *call)
1257{ 1087{
1258 if (call == NULL) 1088 if (call == NULL)
1259 return NULL; 1089 return NULL;
1260 1090
1261 uint32_t friend_number = call->friend_number; 1091 uint32_t friend_number = call->friend_number;
1262 ToxAV* av = call->av; 1092 ToxAV *av = call->av;
1263 1093
1264 ToxAVCall* prev = call->prev; 1094 ToxAVCall *prev = call->prev;
1265 ToxAVCall* next = call->next; 1095 ToxAVCall *next = call->next;
1266 1096
1267 /* Set av call in msi to NULL in order to know if call if ToxAVCall is 1097 /* Set av call in msi to NULL in order to know if call if ToxAVCall is
1268 * removed from the msi call. 1098 * removed from the msi call.
1269 */ 1099 */
1270 call->msi_call->av_call = NULL; 1100 call->msi_call->av_call = NULL;
1271 free(call); 1101 free(call);
1272 1102
1273 if (prev) 1103 if (prev)
1274 prev->next = next; 1104 prev->next = next;
1275 else if (next) 1105 else if (next)
1276 av->calls_head = next->friend_number; 1106 av->calls_head = next->friend_number;
1277 else goto CLEAR; 1107 else goto CLEAR;
1278 1108
1279 if (next) 1109 if (next)
1280 next->prev = prev; 1110 next->prev = prev;
1281 else if (prev) 1111 else if (prev)
1282 av->calls_tail = prev->friend_number; 1112 av->calls_tail = prev->friend_number;
1283 else goto CLEAR; 1113 else goto CLEAR;
1284 1114
1285 av->calls[friend_number] = NULL; 1115 av->calls[friend_number] = NULL;
1286 return next; 1116 return next;
1287 1117
1288CLEAR: 1118CLEAR:
1289 av->calls_head = av->calls_tail = 0; 1119 av->calls_head = av->calls_tail = 0;
1290 free(av->calls); 1120 free(av->calls);
1291 av->calls = NULL; 1121 av->calls = NULL;
1292 1122
1293 return NULL; 1123 return NULL;
1294} 1124}
1295bool call_prepare_transmission(ToxAVCall* call) 1125bool call_prepare_transmission(ToxAVCall *call)
1296{ 1126{
1297 /* Assumes mutex locked */ 1127 /* Assumes mutex locked */
1298 1128
1299 if (call == NULL) 1129 if (call == NULL)
1300 return false; 1130 return false;
1301 1131
1302 ToxAV* av = call->av; 1132 ToxAV *av = call->av;
1303 1133
1304 if (!av->acb.first && !av->vcb.first) 1134 if (!av->acb.first && !av->vcb.first)
1305 /* It makes no sense to have CSession without callbacks */ 1135 /* It makes no sense to have CSession without callbacks */
1306 return false; 1136 return false;
1307 1137
1308 if (call->active) { 1138 if (call->active) {
1309 LOGGER_WARNING("Call already active!\n"); 1139 LOGGER_WARNING("Call already active!\n");
1310 return true; 1140 return true;
1311 } 1141 }
1312 1142
1313 if (create_recursive_mutex(call->mutex_audio) != 0) 1143 if (create_recursive_mutex(call->mutex_audio) != 0)
1314 return false; 1144 return false;
1315 1145
1316 if (create_recursive_mutex(call->mutex_video) != 0) 1146 if (create_recursive_mutex(call->mutex_video) != 0)
1317 goto FAILURE_3; 1147 goto FAILURE_3;
1318 1148
1319 if (create_recursive_mutex(call->mutex) != 0) 1149 if (create_recursive_mutex(call->mutex) != 0)
1320 goto FAILURE_2; 1150 goto FAILURE_2;
1321 1151
1152 /* Prepare bwc */
1153 call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call);
1322 1154
1323 { /* Prepare audio */ 1155 { /* Prepare audio */
1324 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second); 1156 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1157
1325 if (!call->audio.second) { 1158 if (!call->audio.second) {
1326 LOGGER_ERROR("Failed to create audio codec session"); 1159 LOGGER_ERROR("Failed to create audio codec session");
1327 goto FAILURE; 1160 goto FAILURE;
1328 } 1161 }
1329 1162
1330 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->audio.second, ac_queue_message); 1163 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc,
1164 call->audio.second, ac_queue_message);
1165
1331 if (!call->audio.first) { 1166 if (!call->audio.first) {
1332 LOGGER_ERROR("Failed to create audio rtp session");; 1167 LOGGER_ERROR("Failed to create audio rtp session");;
1333 goto FAILURE; 1168 goto FAILURE;
1334 } 1169 }
1335 } 1170 }
1336
1337 { /* Prepare video */ 1171 { /* Prepare video */
1338 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz); 1172 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second);
1173
1339 if (!call->video.second) { 1174 if (!call->video.second) {
1340 LOGGER_ERROR("Failed to create video codec session"); 1175 LOGGER_ERROR("Failed to create video codec session");
1341 goto FAILURE; 1176 goto FAILURE;
1342 } 1177 }
1343 1178
1344 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->video.second, vc_queue_message); 1179 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc,
1180 call->video.second, vc_queue_message);
1181
1345 if (!call->video.first) { 1182 if (!call->video.first) {
1346 LOGGER_ERROR("Failed to create video rtp session"); 1183 LOGGER_ERROR("Failed to create video rtp session");
1347 goto FAILURE; 1184 goto FAILURE;
1348 } 1185 }
1349 } 1186 }
1350 1187
1351 call->active = 1; 1188 call->active = 1;
1352 return true; 1189 return true;
1353 1190
1354FAILURE: 1191FAILURE:
1192 bwc_kill(call->bwc);
1355 rtp_kill(call->audio.first); 1193 rtp_kill(call->audio.first);
1356 ac_kill(call->audio.second); 1194 ac_kill(call->audio.second);
1357 call->audio.first = NULL; 1195 call->audio.first = NULL;
@@ -1367,49 +1205,33 @@ FAILURE_3:
1367 pthread_mutex_destroy(call->mutex_audio); 1205 pthread_mutex_destroy(call->mutex_audio);
1368 return false; 1206 return false;
1369} 1207}
1370void call_kill_transmission(ToxAVCall* call) 1208void call_kill_transmission(ToxAVCall *call)
1371{ 1209{
1372 if (call == NULL || call->active == 0) 1210 if (call == NULL || call->active == 0)
1373 return; 1211 return;
1374 1212
1375 call->active = 0; 1213 call->active = 0;
1376 1214
1377 pthread_mutex_lock(call->mutex_audio); 1215 pthread_mutex_lock(call->mutex_audio);
1378 pthread_mutex_unlock(call->mutex_audio); 1216 pthread_mutex_unlock(call->mutex_audio);
1379 pthread_mutex_lock(call->mutex_video); 1217 pthread_mutex_lock(call->mutex_video);
1380 pthread_mutex_unlock(call->mutex_video); 1218 pthread_mutex_unlock(call->mutex_video);
1381 pthread_mutex_lock(call->mutex); 1219 pthread_mutex_lock(call->mutex);
1382 pthread_mutex_unlock(call->mutex); 1220 pthread_mutex_unlock(call->mutex);
1221
1222 bwc_kill(call->bwc);
1383 1223
1384 rtp_kill(call->audio.first); 1224 rtp_kill(call->audio.first);
1385 ac_kill(call->audio.second); 1225 ac_kill(call->audio.second);
1386 call->audio.first = NULL; 1226 call->audio.first = NULL;
1387 call->audio.second = NULL; 1227 call->audio.second = NULL;
1388 1228
1389 rtp_kill(call->video.first); 1229 rtp_kill(call->video.first);
1390 vc_kill(call->video.second); 1230 vc_kill(call->video.second);
1391 call->video.first = NULL; 1231 call->video.first = NULL;
1392 call->video.second = NULL; 1232 call->video.second = NULL;
1393 1233
1394 pthread_mutex_destroy(call->mutex_audio); 1234 pthread_mutex_destroy(call->mutex_audio);
1395 pthread_mutex_destroy(call->mutex_video); 1235 pthread_mutex_destroy(call->mutex_video);
1396 pthread_mutex_destroy(call->mutex); 1236 pthread_mutex_destroy(call->mutex);
1397} 1237}
1398void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate)
1399{
1400 ba->bit_rate = bit_rate;
1401 ba->next_send = current_time_monotonic();
1402 ba->end_time = ~0;
1403 ba->next_send_interval = 1000;
1404 ba->active = true;
1405}
1406bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba)
1407{
1408 if (!ba->active || ba->next_send > current_time_monotonic())
1409 return false;
1410
1411 ba->next_send_interval *= 0.8;
1412 ba->next_send = current_time_monotonic() + ba->next_send_interval;
1413
1414 return true;
1415} \ 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" {
52/** \subsection threading Threading implications 52/** \subsection threading Threading implications
53 * 53 *
54 * Unlike the Core API, this API is fully thread-safe. The library will ensure 54 * Unlike the Core API, this API is fully thread-safe. The library will ensure
55 * the proper synchronisation of parallel calls. 55 * the proper synchronization of parallel calls.
56 * 56 *
57 * A common way to run ToxAV (multiple or single instance) is to have a thread, 57 * A common way to run ToxAV (multiple or single instance) is to have a thread,
58 * separate from tox instance thread, running a simple toxav_iterate loop, 58 * separate from tox instance thread, running a simple toxav_iterate loop,
59 * sleeping for toxav_iteration_interval * milliseconds on each iteration. 59 * sleeping for toxav_iteration_interval * milliseconds on each iteration.
60 * 60 *
61 * An important thing to note is that events are triggered from both tox and
62 * toxav thread (see above). Audio and video receive frame events are triggered
63 * from toxav thread while all the other events are triggered from tox thread.
64 *
65 * Tox thread has priority with mutex mechanisms. Any api function can
66 * fail if mutexes are held by tox thread in which case they will set SYNC
67 * error code.
61 */ 68 */
62/** 69/**
63 * External Tox type. 70 * External Tox type.
@@ -80,8 +87,10 @@ typedef struct Tox Tox;
80 */ 87 */
81#ifndef TOXAV_DEFINED 88#ifndef TOXAV_DEFINED
82#define TOXAV_DEFINED 89#define TOXAV_DEFINED
83typedef struct ToxAV ToxAV; 90typedef struct ToxAV_s ToxAV;
84#endif /* TOXAV_DEFINED */ 91#endif /* TOXAV_DEFINED */
92
93
85/******************************************************************************* 94/*******************************************************************************
86 * 95 *
87 * :: API version 96 * :: API version
@@ -92,17 +101,20 @@ typedef struct ToxAV ToxAV;
92 * incompatible way. 101 * incompatible way.
93 */ 102 */
94#define TOXAV_VERSION_MAJOR 0u 103#define TOXAV_VERSION_MAJOR 0u
104
95/** 105/**
96 * The minor version number. Incremented when functionality is added without 106 * The minor version number. Incremented when functionality is added without
97 * breaking the API or ABI. Set to 0 when the major version number is 107 * breaking the API or ABI. Set to 0 when the major version number is
98 * incremented. 108 * incremented.
99 */ 109 */
100#define TOXAV_VERSION_MINOR 0u 110#define TOXAV_VERSION_MINOR 0u
111
101/** 112/**
102 * The patch or revision number. Incremented when bugfixes are applied without 113 * The patch or revision number. Incremented when bugfixes are applied without
103 * changing any functionality or API or ABI. 114 * changing any functionality or API or ABI.
104 */ 115 */
105#define TOXAV_VERSION_PATCH 0u 116#define TOXAV_VERSION_PATCH 0u
117
106/** 118/**
107 * A macro to check at preprocessing time whether the client code is compatible 119 * A macro to check at preprocessing time whether the client code is compatible
108 * with the installed version of ToxAV. 120 * with the installed version of ToxAV.
@@ -112,37 +124,45 @@ typedef struct ToxAV ToxAV;
112 (TOXAV_VERSION_MINOR > MINOR || \ 124 (TOXAV_VERSION_MINOR > MINOR || \
113 (TOXAV_VERSION_MINOR == MINOR && \ 125 (TOXAV_VERSION_MINOR == MINOR && \
114 TOXAV_VERSION_PATCH >= PATCH))) 126 TOXAV_VERSION_PATCH >= PATCH)))
127
115/** 128/**
116 * A macro to make compilation fail if the client code is not compatible with 129 * A macro to make compilation fail if the client code is not compatible with
117 * the installed version of ToxAV. 130 * the installed version of ToxAV.
118 */ 131 */
119#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \ 132#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
120 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1] 133 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
134
121/** 135/**
122 * A convenience macro to call toxav_version_is_compatible with the currently 136 * A convenience macro to call toxav_version_is_compatible with the currently
123 * compiling API version. 137 * compiling API version.
124 */ 138 */
125#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \ 139#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
126 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH) 140 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
141
127/** 142/**
128 * Return the major version number of the library. Can be used to display the 143 * Return the major version number of the library. Can be used to display the
129 * ToxAV library version or to check whether the client is compatible with the 144 * ToxAV library version or to check whether the client is compatible with the
130 * dynamically linked version of ToxAV. 145 * dynamically linked version of ToxAV.
131 */ 146 */
132uint32_t toxav_version_major(void); 147uint32_t toxav_version_major(void);
148
133/** 149/**
134 * Return the minor version number of the library. 150 * Return the minor version number of the library.
135 */ 151 */
136uint32_t toxav_version_minor(void); 152uint32_t toxav_version_minor(void);
153
137/** 154/**
138 * Return the patch number of the library. 155 * Return the patch number of the library.
139 */ 156 */
140uint32_t toxav_version_patch(void); 157uint32_t toxav_version_patch(void);
158
141/** 159/**
142 * Return whether the compiled library version is compatible with the passed 160 * Return whether the compiled library version is compatible with the passed
143 * version numbers. 161 * version numbers.
144 */ 162 */
145bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch); 163bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
164
165
146/******************************************************************************* 166/*******************************************************************************
147 * 167 *
148 * :: Creation and destruction 168 * :: Creation and destruction
@@ -167,10 +187,12 @@ typedef enum TOXAV_ERR_NEW {
167 */ 187 */
168 TOXAV_ERR_NEW_MULTIPLE, 188 TOXAV_ERR_NEW_MULTIPLE,
169} TOXAV_ERR_NEW; 189} TOXAV_ERR_NEW;
190
170/** 191/**
171 * Start new A/V session. There can only be only one session per Tox instance. 192 * Start new A/V session. There can only be only one session per Tox instance.
172 */ 193 */
173ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); 194ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
195
174/** 196/**
175 * Releases all resources associated with the A/V session. 197 * Releases all resources associated with the A/V session.
176 * 198 *
@@ -179,10 +201,13 @@ ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
179 * called and the av pointer becomes invalid. 201 * called and the av pointer becomes invalid.
180 */ 202 */
181void toxav_kill(ToxAV *toxAV); 203void toxav_kill(ToxAV *toxAV);
204
182/** 205/**
183 * Returns the Tox instance the A/V object was created for. 206 * Returns the Tox instance the A/V object was created for.
184 */ 207 */
185Tox *toxav_get_tox(const ToxAV *toxAV); 208Tox *toxav_get_tox(const ToxAV *toxAV);
209
210
186/******************************************************************************* 211/*******************************************************************************
187 * 212 *
188 * :: A/V event loop 213 * :: A/V event loop
@@ -193,12 +218,15 @@ Tox *toxav_get_tox(const ToxAV *toxAV);
193 * be. If no call is active at the moment, this function returns 200. 218 * be. If no call is active at the moment, this function returns 200.
194 */ 219 */
195uint32_t toxav_iteration_interval(const ToxAV *toxAV); 220uint32_t toxav_iteration_interval(const ToxAV *toxAV);
221
196/** 222/**
197 * Main loop for the session. This function needs to be called in intervals of 223 * Main loop for the session. This function needs to be called in intervals of
198 * toxav_iteration_interval() milliseconds. It is best called in the separate 224 * toxav_iteration_interval() milliseconds. It is best called in the separate
199 * thread from tox_iterate. 225 * thread from tox_iterate.
200 */ 226 */
201void toxav_iterate(ToxAV *toxAV); 227void toxav_iterate(ToxAV *toxAV);
228
229
202/******************************************************************************* 230/*******************************************************************************
203 * 231 *
204 * :: Call setup 232 * :: Call setup
@@ -215,6 +243,10 @@ typedef enum TOXAV_ERR_CALL {
215 */ 243 */
216 TOXAV_ERR_CALL_MALLOC, 244 TOXAV_ERR_CALL_MALLOC,
217 /** 245 /**
246 * Synchronization error occurred.
247 */
248 TOXAV_ERR_CALL_SYNC,
249 /**
218 * The friend number did not designate a valid friend. 250 * The friend number did not designate a valid friend.
219 */ 251 */
220 TOXAV_ERR_CALL_FRIEND_NOT_FOUND, 252 TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
@@ -232,6 +264,7 @@ typedef enum TOXAV_ERR_CALL {
232 */ 264 */
233 TOXAV_ERR_CALL_INVALID_BIT_RATE, 265 TOXAV_ERR_CALL_INVALID_BIT_RATE,
234} TOXAV_ERR_CALL; 266} TOXAV_ERR_CALL;
267
235/** 268/**
236 * Call a friend. This will start ringing the friend. 269 * Call a friend. This will start ringing the friend.
237 * 270 *
@@ -246,7 +279,9 @@ typedef enum TOXAV_ERR_CALL {
246 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable 279 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
247 * video sending. 280 * video sending.
248 */ 281 */
249bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); 282bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
283 uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
284
250/** 285/**
251 * The function type for the call callback. 286 * The function type for the call callback.
252 * 287 *
@@ -254,18 +289,25 @@ bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, u
254 * @param audio_enabled True if friend is sending audio. 289 * @param audio_enabled True if friend is sending audio.
255 * @param video_enabled True if friend is sending video. 290 * @param video_enabled True if friend is sending video.
256 */ 291 */
257typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); 292typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled,
293 bool video_enabled, void *user_data);
294
258/** 295/**
259 * Set the callback for the `call` event. Pass NULL to unset. 296 * Set the callback for the `call` event. Pass NULL to unset.
260 * 297 *
261 */ 298 */
262void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data); 299void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
300
263typedef enum TOXAV_ERR_ANSWER { 301typedef enum TOXAV_ERR_ANSWER {
264 /** 302 /**
265 * The function returned successfully. 303 * The function returned successfully.
266 */ 304 */
267 TOXAV_ERR_ANSWER_OK, 305 TOXAV_ERR_ANSWER_OK,
268 /** 306 /**
307 * Synchronization error occurred.
308 */
309 TOXAV_ERR_ANSWER_SYNC,
310 /**
269 * Failed to initialize codecs for call session. Note that codec initiation 311 * Failed to initialize codecs for call session. Note that codec initiation
270 * will fail if there is no receive callback registered for either audio or 312 * will fail if there is no receive callback registered for either audio or
271 * video. 313 * video.
@@ -285,6 +327,7 @@ typedef enum TOXAV_ERR_ANSWER {
285 */ 327 */
286 TOXAV_ERR_ANSWER_INVALID_BIT_RATE, 328 TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
287} TOXAV_ERR_ANSWER; 329} TOXAV_ERR_ANSWER;
330
288/** 331/**
289 * Accept an incoming call. 332 * Accept an incoming call.
290 * 333 *
@@ -299,6 +342,8 @@ typedef enum TOXAV_ERR_ANSWER {
299 * video sending. 342 * video sending.
300 */ 343 */
301bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); 344bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error);
345
346
302/******************************************************************************* 347/*******************************************************************************
303 * 348 *
304 * :: Call state graph 349 * :: Call state graph
@@ -336,7 +381,6 @@ enum TOXAV_FRIEND_CALL_STATE {
336 TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32, 381 TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
337}; 382};
338 383
339
340/** 384/**
341 * The function type for the call_state callback. 385 * The function type for the call_state callback.
342 * 386 *
@@ -347,11 +391,13 @@ enum TOXAV_FRIEND_CALL_STATE {
347 * friend. 391 * friend.
348 */ 392 */
349typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data); 393typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
394
350/** 395/**
351 * Set the callback for the `call_state` event. Pass NULL to unset. 396 * Set the callback for the `call_state` event. Pass NULL to unset.
352 * 397 *
353 */ 398 */
354void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data); 399void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
400
355/******************************************************************************* 401/*******************************************************************************
356 * 402 *
357 * :: Call control 403 * :: Call control
@@ -393,12 +439,17 @@ typedef enum TOXAV_CALL_CONTROL {
393 */ 439 */
394 TOXAV_CALL_CONTROL_SHOW_VIDEO, 440 TOXAV_CALL_CONTROL_SHOW_VIDEO,
395} TOXAV_CALL_CONTROL; 441} TOXAV_CALL_CONTROL;
442
396typedef enum TOXAV_ERR_CALL_CONTROL { 443typedef enum TOXAV_ERR_CALL_CONTROL {
397 /** 444 /**
398 * The function returned successfully. 445 * The function returned successfully.
399 */ 446 */
400 TOXAV_ERR_CALL_CONTROL_OK, 447 TOXAV_ERR_CALL_CONTROL_OK,
401 /** 448 /**
449 * Synchronization error occurred.
450 */
451 TOXAV_ERR_CALL_CONTROL_SYNC,
452 /**
402 * The friend_number passed did not designate a valid friend. 453 * The friend_number passed did not designate a valid friend.
403 */ 454 */
404 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, 455 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
@@ -413,6 +464,7 @@ typedef enum TOXAV_ERR_CALL_CONTROL {
413 */ 464 */
414 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION, 465 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
415} TOXAV_ERR_CALL_CONTROL; 466} TOXAV_ERR_CALL_CONTROL;
467
416/** 468/**
417 * Sends a call control command to a friend. 469 * Sends a call control command to a friend.
418 * 470 *
@@ -423,48 +475,40 @@ typedef enum TOXAV_ERR_CALL_CONTROL {
423 * @return true on success. 475 * @return true on success.
424 */ 476 */
425bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); 477bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error);
478
479
426/******************************************************************************* 480/*******************************************************************************
427 * 481 *
428 * :: Controlling bit rates 482 * :: Controlling bit rates
429 * 483 *
430 ******************************************************************************/ 484 ******************************************************************************/
431typedef enum TOXAV_ERR_SET_BIT_RATE { 485typedef enum TOXAV_ERR_BIT_RATE_SET {
432 /** 486 /**
433 * The function returned successfully. 487 * The function returned successfully.
434 */ 488 */
435 TOXAV_ERR_SET_BIT_RATE_OK, 489 TOXAV_ERR_BIT_RATE_SET_OK,
490 /**
491 * Synchronization error occurred.
492 */
493 TOXAV_ERR_BIT_RATE_SET_SYNC,
436 /** 494 /**
437 * The bit rate passed was not one of the supported values. 495 * The audio bit rate passed was not one of the supported values.
438 */ 496 */
439 TOXAV_ERR_SET_BIT_RATE_INVALID, 497 TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
498 /**
499 * The video bit rate passed was not one of the supported values.
500 */
501 TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
440 /** 502 /**
441 * The friend_number passed did not designate a valid friend. 503 * The friend_number passed did not designate a valid friend.
442 */ 504 */
443 TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND, 505 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
444 /** 506 /**
445 * This client is currently not in a call with the friend. 507 * This client is currently not in a call with the friend.
446 */ 508 */
447 TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL, 509 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
448} TOXAV_ERR_SET_BIT_RATE; 510} TOXAV_ERR_BIT_RATE_SET;
449/** 511
450 * The function type for the audio_bit_rate_status callback.
451 *
452 * @param friend_number The friend number of the friend for which to set the
453 * audio bit rate.
454 * @param stable Is the stream stable enough to keep the bit rate.
455 * Upon successful, non forceful, bit rate change, this is set to
456 * true and 'bit_rate' is set to new bit rate.
457 * The stable is set to false with bit_rate set to the unstable
458 * bit rate when either current stream is unstable with said bit rate
459 * or the non forceful change failed.
460 * @param bit_rate The bit rate in Kb/sec.
461 */
462typedef void toxav_audio_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data);
463/**
464 * Set the callback for the `audio_bit_rate_status` event. Pass NULL to unset.
465 *
466 */
467void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_status_cb *callback, void *user_data);
468/** 512/**
469 * Set the audio bit rate to be used in subsequent audio frames. If the passed 513 * Set the audio bit rate to be used in subsequent audio frames. If the passed
470 * bit rate is the same as the current bit rate this function will return true 514 * 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
476 * @param friend_number The friend number of the friend for which to set the 520 * @param friend_number The friend number of the friend for which to set the
477 * audio bit rate. 521 * audio bit rate.
478 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable 522 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
479 * audio sending. 523 * audio sending. Set to -1 to leave unchanged.
480 * @param force True if the bit rate change is forceful. 524 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
525 * video sending. Set to -1 to leave unchanged.
481 * 526 *
482 */ 527 */
483bool toxav_audio_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error); 528bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
529 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
530
484/** 531/**
485 * The function type for the video_bit_rate_status callback. 532 * The function type for the bit_rate_status callback. The event is triggered
533 * when the network becomes too saturated for current bit rates at which
534 * point core suggests new bit rates.
486 * 535 *
487 * @param friend_number The friend number of the friend for which to set the 536 * @param friend_number The friend number of the friend for which to set the
488 * video bit rate. 537 * audio bit rate.
489 * @param stable Is the stream stable enough to keep the bit rate. 538 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
490 * Upon successful, non forceful, bit rate change, this is set to 539 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
491 * true and 'bit_rate' is set to new bit rate.
492 * The stable is set to false with bit_rate set to the unstable
493 * bit rate when either current stream is unstable with said bit rate
494 * or the non forceful change failed.
495 * @param bit_rate The bit rate in Kb/sec.
496 */ 540 */
497typedef void toxav_video_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data); 541typedef 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);
542
498/** 543/**
499 * Set the callback for the `video_bit_rate_status` event. Pass NULL to unset. 544 * Set the callback for the `bit_rate_status` event. Pass NULL to unset.
500 * 545 *
501 */ 546 */
502void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_status_cb *callback, void *user_data); 547void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
503/** 548
504 * Set the video bit rate to be used in subsequent video frames. If the passed 549
505 * bit rate is the same as the current bit rate this function will return true
506 * without calling a callback. If there is an active non forceful setup with the
507 * passed video bit rate and the new set request is forceful, the bit rate is
508 * forcefully set and the previous non forceful request is cancelled. The active
509 * non forceful setup will be canceled in favour of new non forceful setup.
510 *
511 * @param friend_number The friend number of the friend for which to set the
512 * video bit rate.
513 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
514 * video sending.
515 * @param force True if the bit rate change is forceful.
516 *
517 */
518bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error);
519/******************************************************************************* 550/*******************************************************************************
520 * 551 *
521 * :: A/V sending 552 * :: A/V sending
@@ -554,6 +585,7 @@ typedef enum TOXAV_ERR_SEND_FRAME {
554 */ 585 */
555 TOXAV_ERR_SEND_FRAME_RTP_FAILED, 586 TOXAV_ERR_SEND_FRAME_RTP_FAILED,
556} TOXAV_ERR_SEND_FRAME; 587} TOXAV_ERR_SEND_FRAME;
588
557/** 589/**
558 * Send an audio frame to a friend. 590 * Send an audio frame to a friend.
559 * 591 *
@@ -574,7 +606,10 @@ typedef enum TOXAV_ERR_SEND_FRAME {
574 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling 606 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
575 * rates are 8000, 12000, 16000, 24000, or 48000. 607 * rates are 8000, 12000, 16000, 24000, or 48000.
576 */ 608 */
577bool 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); 609bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
610 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
611 TOXAV_ERR_SEND_FRAME *error);
612
578/** 613/**
579 * Send a video frame to a friend. 614 * Send a video frame to a friend.
580 * 615 *
@@ -590,7 +625,11 @@ bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t
590 * @param u U (Chroma) plane data. 625 * @param u U (Chroma) plane data.
591 * @param v V (Chroma) plane data. 626 * @param v V (Chroma) plane data.
592 */ 627 */
593bool 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); 628bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
629 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
630 TOXAV_ERR_SEND_FRAME *error);
631
632
594/******************************************************************************* 633/*******************************************************************************
595 * 634 *
596 * :: A/V receiving 635 * :: A/V receiving
@@ -600,7 +639,7 @@ bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width
600 * The function type for the audio_receive_frame callback. The callback can be 639 * The function type for the audio_receive_frame callback. The callback can be
601 * called multiple times per single iteration depending on the amount of queued 640 * called multiple times per single iteration depending on the amount of queued
602 * frames in the buffer. The received format is the same as in send function. 641 * frames in the buffer. The received format is the same as in send function.
603 * 642 *
604 * @param friend_number The friend number of the friend who sent an audio frame. 643 * @param friend_number The friend number of the friend who sent an audio frame.
605 * @param pcm An array of audio samples (sample_count * channels elements). 644 * @param pcm An array of audio samples (sample_count * channels elements).
606 * @param sample_count The number of audio samples per channel in the PCM array. 645 * @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
608 * @param sampling_rate Sampling rate used in this frame. 647 * @param sampling_rate Sampling rate used in this frame.
609 * 648 *
610 */ 649 */
611typedef 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); 650typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
651 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
652 void *user_data);
653
612/** 654/**
613 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset. 655 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
614 * 656 *
615 */ 657 */
616void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data); 658void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
659
617/** 660/**
618 * The function type for the video_receive_frame callback. 661 * The function type for the video_receive_frame callback.
619 * 662 *
@@ -635,60 +678,17 @@ void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_
635 * image is bottom-up hence why you MUST abs() it when 678 * image is bottom-up hence why you MUST abs() it when
636 * calculating plane buffer size. 679 * calculating plane buffer size.
637 */ 680 */
638typedef 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); 681typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
682 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
683 int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
684
639/** 685/**
640 * Set the callback for the `video_receive_frame` event. Pass NULL to unset. 686 * Set the callback for the `video_receive_frame` event. Pass NULL to unset.
641 * 687 *
642 */ 688 */
643void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data); 689void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
644 690
645/**
646 * NOTE Compatibility with old toxav group calls TODO remove
647 */
648/* Create a new toxav group.
649 *
650 * return group number on success.
651 * return -1 on failure.
652 *
653 * Audio data callback format:
654 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
655 *
656 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
657 */
658int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t,
659 unsigned int, void *), void *userdata);
660
661/* Join a AV group (you need to have been invited first.)
662 *
663 * returns group number on success
664 * returns -1 on failure.
665 *
666 * Audio data callback format (same as the one for toxav_add_av_groupchat()):
667 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
668 *
669 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
670 */
671int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
672 void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
673
674/* Send audio to the group chat.
675 *
676 * return 0 on success.
677 * return -1 on failure.
678 *
679 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
680 *
681 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
682 * Valid number of channels are 1 or 2.
683 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
684 *
685 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
686 */
687int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
688 unsigned int sample_rate);
689
690#ifdef __cplusplus 691#ifdef __cplusplus
691} 692}
692#endif 693#endif
693
694#endif /* TOXAV_H */ 694#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 @@
1/* toxav_old.h 1/* toxav_old.h
2 * 2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
@@ -16,7 +16,7 @@
16 * 16 *
17 * You should have received a copy of the GNU General Public License 17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>. 18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 * 19 *
20 */ 20 */
21/** 21/**
22 * This file contains the group chats code for the backwards compatibility. 22 * 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 @@
19 * 19 *
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
22#include <stdlib.h> 26#include <stdlib.h>
23#include <assert.h> 27#include <assert.h>
24 28
@@ -29,281 +33,166 @@
29#include "../toxcore/logger.h" 33#include "../toxcore/logger.h"
30#include "../toxcore/network.h" 34#include "../toxcore/network.h"
31 35
32/* Good quality encode. */ 36#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
33#define MAX_DECODE_TIME_US 0
34
35#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
36#define VIDEOFRAME_HEADER_SIZE 0x2
37
38#define VIDEO_DECODE_BUFFER_SIZE 20 37#define VIDEO_DECODE_BUFFER_SIZE 20
39 38
40typedef struct { uint16_t size; uint8_t data[]; } Payload;
41 39
42bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate); 40bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate);
43 41
44 42VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
45VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data, uint32_t mvfpsz)
46{ 43{
47 VCSession *vc = calloc(sizeof(VCSession), 1); 44 VCSession *vc = calloc(sizeof(VCSession), 1);
48 45
49 if (!vc) { 46 if (!vc) {
50 LOGGER_WARNING("Allocation failed! Application might misbehave!"); 47 LOGGER_WARNING("Allocation failed! Application might misbehave!");
51 return NULL; 48 return NULL;
52 } 49 }
53 50
54 if (create_recursive_mutex(vc->queue_mutex) != 0) { 51 if (create_recursive_mutex(vc->queue_mutex) != 0) {
55 LOGGER_WARNING("Failed to create recursive mutex!"); 52 LOGGER_WARNING("Failed to create recursive mutex!");
56 free(vc); 53 free(vc);
57 return NULL; 54 return NULL;
58 } 55 }
59 56
60 if ( !(vc->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) ) 57 if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)))
61 goto BASE_CLEANUP;
62 if ( !(vc->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) )
63 goto BASE_CLEANUP;
64 if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) )
65 goto BASE_CLEANUP; 58 goto BASE_CLEANUP;
66 59
67 int rc = vpx_codec_dec_init_ver(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, 60 int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0);
68 NULL, 0, VPX_DECODER_ABI_VERSION); 61
69 if ( rc != VPX_CODEC_OK) { 62 if (rc != VPX_CODEC_OK) {
70 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); 63 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
71 goto BASE_CLEANUP; 64 goto BASE_CLEANUP;
72 } 65 }
73 66
74 if (!create_video_encoder(vc->encoder, 500000)) { 67 if (!create_video_encoder(vc->encoder, 500000)) {
75 vpx_codec_destroy(vc->decoder); 68 vpx_codec_destroy(vc->decoder);
76 goto BASE_CLEANUP; 69 goto BASE_CLEANUP;
77 } 70 }
78 if (!create_video_encoder(vc->test_encoder, 500000)) { 71
79 vpx_codec_destroy(vc->encoder);
80 vpx_codec_destroy(vc->decoder);
81 goto BASE_CLEANUP;
82 }
83
84 vc->linfts = current_time_monotonic(); 72 vc->linfts = current_time_monotonic();
85 vc->lcfd = 60; 73 vc->lcfd = 60;
86 vc->vcb.first = cb; 74 vc->vcb.first = cb;
87 vc->vcb.second = cb_data; 75 vc->vcb.second = cb_data;
88 vc->friend_number = friend_number; 76 vc->friend_number = friend_number;
89 vc->peer_video_frame_piece_size = mvfpsz;
90 vc->av = av; 77 vc->av = av;
91 78
92 return vc; 79 return vc;
93 80
94BASE_CLEANUP: 81BASE_CLEANUP:
95 pthread_mutex_destroy(vc->queue_mutex); 82 pthread_mutex_destroy(vc->queue_mutex);
96 rb_free(vc->vbuf_raw); 83 rb_kill(vc->vbuf_raw);
97 free(vc->split_video_frame);
98 free(vc->frame_buf);
99 free(vc); 84 free(vc);
100 return NULL; 85 return NULL;
101} 86}
102void vc_kill(VCSession* vc) 87void vc_kill(VCSession *vc)
103{ 88{
104 if (!vc) 89 if (!vc)
105 return; 90 return;
106 91
107 vpx_codec_destroy(vc->encoder); 92 vpx_codec_destroy(vc->encoder);
108 vpx_codec_destroy(vc->test_encoder);
109 vpx_codec_destroy(vc->decoder); 93 vpx_codec_destroy(vc->decoder);
110 rb_free(vc->vbuf_raw); 94
111 free(vc->split_video_frame); 95 void *p;
112 free(vc->frame_buf); 96
113 97 while (rb_read(vc->vbuf_raw, (void **)&p))
98 free(p);
99
100 rb_kill(vc->vbuf_raw);
101
114 pthread_mutex_destroy(vc->queue_mutex); 102 pthread_mutex_destroy(vc->queue_mutex);
115 103
116 LOGGER_DEBUG("Terminated video handler: %p", vc); 104 LOGGER_DEBUG("Terminated video handler: %p", vc);
117 free(vc); 105 free(vc);
118} 106}
119void vc_do(VCSession* vc) 107void vc_iterate(VCSession *vc)
120{ 108{
121 if (!vc) 109 if (!vc)
122 return; 110 return;
123 111
124 Payload *p; 112 struct RTPMessage *p;
125 int rc; 113 int rc;
126 114
127 pthread_mutex_lock(vc->queue_mutex); 115 pthread_mutex_lock(vc->queue_mutex);
128 if (rb_read(vc->vbuf_raw, (void**)&p)) { 116
117 if (rb_read(vc->vbuf_raw, (void **)&p)) {
129 pthread_mutex_unlock(vc->queue_mutex); 118 pthread_mutex_unlock(vc->queue_mutex);
130 119
131 rc = vpx_codec_decode(vc->decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); 120 rc = vpx_codec_decode(vc->decoder, p->data, p->len, NULL, MAX_DECODE_TIME_US);
132 free(p); 121 free(p);
133 122
134 if (rc != VPX_CODEC_OK) { 123 if (rc != VPX_CODEC_OK)
135 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); 124 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
136 } else { 125 else {
137 vpx_codec_iter_t iter = NULL; 126 vpx_codec_iter_t iter = NULL;
138 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter); 127 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
139 128
140 /* Play decoded images */ 129 /* Play decoded images */
141 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) { 130 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
142 if (vc->vcb.first) 131 if (vc->vcb.first)
143 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h, 132 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
144 (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2], 133 (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
145 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second); 134 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
146 135
147 vpx_img_free(dest); 136 vpx_img_free(dest);
148 } 137 }
149 } 138 }
150
151 return;
152 }
153 pthread_mutex_unlock(vc->queue_mutex);
154}
155void vc_init_video_splitter_cycle(VCSession* vc)
156{
157 if (!vc)
158 return;
159
160 vc->split_video_frame[0] = vc->frameid_out++;
161 vc->split_video_frame[1] = 0;
162}
163int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length)
164{
165 if (!vc)
166 return 0;
167
168 vc->processing_video_frame = payload;
169 vc->processing_video_frame_size = length;
170
171 return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1;
172}
173const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size)
174{
175 if (!vc || !size)
176 return NULL;
177 139
178 if (vc->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) { 140 return;
179 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
180 vc->processing_video_frame,
181 VIDEOFRAME_PIECE_SIZE);
182
183 vc->processing_video_frame += VIDEOFRAME_PIECE_SIZE;
184 vc->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE;
185
186 *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE;
187 } else {
188 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
189 vc->processing_video_frame,
190 vc->processing_video_frame_size);
191
192 *size = vc->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
193 } 141 }
194 142
195 vc->split_video_frame[1]++; 143 pthread_mutex_unlock(vc->queue_mutex);
196
197 return vc->split_video_frame;
198} 144}
199int vc_queue_message(void* vcp, struct RTPMessage_s *msg) 145int vc_queue_message(void *vcp, struct RTPMessage *msg)
200{ 146{
201 /* This function does the reconstruction of video packets. 147 /* This function does the reconstruction of video packets.
202 * See more info about video splitting in docs 148 * See more info about video splitting in docs
203 */ 149 */
204 if (!vcp || !msg) 150 if (!vcp || !msg)
205 return -1; 151 return -1;
206 152
207 if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeVideo + 2) % 128) { 153 if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
208 LOGGER_WARNING("Got dummy!"); 154 LOGGER_WARNING("Got dummy!");
209 rtp_free_msg(msg); 155 free(msg);
210 return 0; 156 return 0;
211 } 157 }
212 158
213 if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) { 159 if (msg->header.pt != rtp_TypeVideo % 128) {
214 LOGGER_WARNING("Invalid payload type!"); 160 LOGGER_WARNING("Invalid payload type!");
215 rtp_free_msg(msg); 161 free(msg);
216 return -1; 162 return -1;
217 } 163 }
218
219 VCSession* vc = vcp;
220
221 uint8_t *packet = msg->data;
222 uint32_t packet_size = msg->length;
223
224 if (packet_size < VIDEOFRAME_HEADER_SIZE)
225 goto end;
226
227 uint8_t diff = packet[0] - vc->frameid_in;
228
229 if (diff != 0) {
230 if (diff < 225) { /* New frame */
231 /* Flush last frames' data and get ready for this frame */
232 Payload *p = malloc(sizeof(Payload) + vc->frame_size);
233
234 if (p) {
235 pthread_mutex_lock(vc->queue_mutex);
236
237 if (rb_full(vc->vbuf_raw)) {
238 LOGGER_DEBUG("Dropped video frame");
239 Payload *tp;
240 rb_read(vc->vbuf_raw, (void**)&tp);
241 free(tp);
242 } else {
243 p->size = vc->frame_size;
244 memcpy(p->data, vc->frame_buf, vc->frame_size);
245 }
246
247 /* Calculate time took for peer to send us this frame */
248 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
249 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
250 vc->linfts = current_time_monotonic();
251
252 rb_write(vc->vbuf_raw, p);
253 pthread_mutex_unlock(vc->queue_mutex);
254 } else {
255 LOGGER_WARNING("Allocation failed! Program might misbehave!");
256 goto end;
257 }
258 164
259 vc->frameid_in = packet[0]; 165 VCSession *vc = vcp;
260 memset(vc->frame_buf, 0, vc->frame_size);
261 vc->frame_size = 0;
262 166
263 } else { /* Old frame; drop */ 167 pthread_mutex_lock(vc->queue_mutex);
264 LOGGER_DEBUG("Old packet: %u", packet[0]); 168 free(rb_write(vc->vbuf_raw, msg));
265 goto end; 169 {
266 } 170 /* Calculate time took for peer to send us this frame */
171 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
172 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
173 vc->linfts = current_time_monotonic();
267 } 174 }
175 pthread_mutex_unlock(vc->queue_mutex);
268 176
269 uint8_t piece_number = packet[1];
270
271 uint32_t length_before_piece = ((piece_number - 1) * vc->peer_video_frame_piece_size);
272 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
273
274 if (framebuf_new_length > MAX_VIDEOFRAME_SIZE)
275 goto end;
276
277
278 /* Otherwise it's part of the frame so just process */
279 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
280
281 memcpy(vc->frame_buf + length_before_piece,
282 packet + VIDEOFRAME_HEADER_SIZE,
283 packet_size - VIDEOFRAME_HEADER_SIZE);
284
285 if (framebuf_new_length > vc->frame_size)
286 vc->frame_size = framebuf_new_length;
287
288end:
289 rtp_free_msg(msg);
290 return 0; 177 return 0;
291} 178}
292int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height) 179int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height)
293{ 180{
294 if (!vccdc) 181 if (!vccdc)
295 return -1; 182 return -1;
296 183
297 vpx_codec_enc_cfg_t cfg = *vccdc->config.enc; 184 vpx_codec_enc_cfg_t cfg = *vccdc->config.enc;
185
298 if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height) 186 if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height)
299 return 0; /* Nothing changed */ 187 return 0; /* Nothing changed */
300 188
301 cfg.rc_target_bitrate = bit_rate; 189 cfg.rc_target_bitrate = bit_rate;
302 cfg.g_w = width; 190 cfg.g_w = width;
303 cfg.g_h = height; 191 cfg.g_h = height;
304 192
305 int rc = vpx_codec_enc_config_set(vccdc, &cfg); 193 int rc = vpx_codec_enc_config_set(vccdc, &cfg);
306 if ( rc != VPX_CODEC_OK) { 194
195 if (rc != VPX_CODEC_OK) {
307 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); 196 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
308 return -1; 197 return -1;
309 } 198 }
@@ -312,42 +201,43 @@ int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t w
312} 201}
313 202
314 203
315bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate) 204bool create_video_encoder (vpx_codec_ctx_t *dest, int32_t bit_rate)
316{ 205{
317 assert(dest); 206 assert(dest);
318 207
319 vpx_codec_enc_cfg_t cfg; 208 vpx_codec_enc_cfg_t cfg;
320 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); 209 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
321 210
322 if (rc != VPX_CODEC_OK) { 211 if (rc != VPX_CODEC_OK) {
323 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); 212 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
324 return false; 213 return false;
325 } 214 }
326 215
327 cfg.rc_target_bitrate = bit_rate; 216 cfg.rc_target_bitrate = bit_rate;
328 cfg.g_w = 4000; 217 cfg.g_w = 800;
329 cfg.g_h = 4000; 218 cfg.g_h = 600;
330 cfg.g_pass = VPX_RC_ONE_PASS; 219 cfg.g_pass = VPX_RC_ONE_PASS;
331 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; 220 /* FIXME If we set error resilience the app will crash due to bug in vp8.
221 Perhaps vp9 has solved it?*/
222// cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
332 cfg.g_lag_in_frames = 0; 223 cfg.g_lag_in_frames = 0;
333 cfg.kf_min_dist = 0; 224 cfg.kf_min_dist = 0;
334 cfg.kf_max_dist = 48; 225 cfg.kf_max_dist = 48;
335 cfg.kf_mode = VPX_KF_AUTO; 226 cfg.kf_mode = VPX_KF_AUTO;
336 227
337 rc = vpx_codec_enc_init_ver(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, 228 rc = vpx_codec_enc_init(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
338 VPX_ENCODER_ABI_VERSION); 229
339 230 if (rc != VPX_CODEC_OK) {
340 if ( rc != VPX_CODEC_OK) {
341 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); 231 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
342 return false; 232 return false;
343 } 233 }
344 234
345 rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8); 235 rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8);
346 236
347 if ( rc != VPX_CODEC_OK) { 237 if (rc != VPX_CODEC_OK) {
348 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); 238 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
349 vpx_codec_destroy(dest); 239 vpx_codec_destroy(dest);
350 } 240 }
351 241
352 return true; 242 return true;
353} \ No newline at end of file 243}
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 @@
36 36
37#include "../toxcore/util.h" 37#include "../toxcore/util.h"
38 38
39struct RTPMessage_s; 39struct RTPMessage;
40 40
41/*
42 * Base Video Codec session type.
43 */
44typedef struct VCSession_s { 41typedef struct VCSession_s {
45
46 /* encoding */ 42 /* encoding */
47 vpx_codec_ctx_t encoder[1]; 43 vpx_codec_ctx_t encoder[1];
48 vpx_codec_ctx_t test_encoder[1];
49 uint32_t frame_counter; 44 uint32_t frame_counter;
50 uint32_t test_frame_counter;
51 45
52 /* decoding */ 46 /* decoding */
53 vpx_codec_ctx_t decoder[1]; 47 vpx_codec_ctx_t decoder[1];
54 void *vbuf_raw; /* Un-decoded data */ 48 void *vbuf_raw; /* Un-decoded data */
55 49
56 /* Data handling */
57 uint8_t *frame_buf; /* buffer for split video payloads */
58 uint32_t frame_size; /* largest address written to in frame_buf for current input frame */
59 uint8_t frameid_in, frameid_out; /* id of input and output video frame */
60 uint64_t linfts; /* Last received frame time stamp */ 50 uint64_t linfts; /* Last received frame time stamp */
61 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */ 51 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
62
63 /* Limits */
64 uint32_t peer_video_frame_piece_size;
65 52
66 /* Splitting */
67 uint8_t *split_video_frame;
68 const uint8_t *processing_video_frame;
69 uint16_t processing_video_frame_size;
70
71 ToxAV *av; 53 ToxAV *av;
72 uint32_t friend_number; 54 uint32_t friend_number;
73 55
74 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */ 56 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
75 57
76 pthread_mutex_t queue_mutex[1]; 58 pthread_mutex_t queue_mutex[1];
77} VCSession; 59} VCSession;
78 60
79/* 61VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data);
80 * Create new Video Codec session. 62void vc_kill(VCSession *vc);
81 */ 63void vc_iterate(VCSession *vc);
82VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data, uint32_t mvfpsz); 64int vc_queue_message(void *vcp, struct RTPMessage *msg);
83/* 65int vc_reconfigure_encoder(vpx_codec_ctx_t *vccdc, uint32_t bit_rate, uint16_t width, uint16_t height);
84 * Kill the Video Codec session.
85 */
86void vc_kill(VCSession* vc);
87/*
88 * Do periodic work. Work is consisted out of decoding only.
89 */
90void vc_do(VCSession* vc);
91/*
92 * Set new video splitting cycle. This is requirement in order to send video packets.
93 */
94void vc_init_video_splitter_cycle(VCSession* vc);
95/*
96 * Update the video splitter cycle with new data.
97 */
98int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length);
99/*
100 * Iterate over splitted cycle.
101 */
102const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size);
103/*
104 * Queue new rtp message.
105 */
106int vc_queue_message(void *vcp, struct RTPMessage_s *msg);
107/*
108 * Set new values to the encoders.
109 */
110int vc_reconfigure_encoder(vpx_codec_ctx_t* vccdc, uint32_t bit_rate, uint16_t width, uint16_t height);
111 66
112#endif /* VIDEO_H */ \ No newline at end of file 67#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)
2239} 2239}
2240 2240
2241 2241
2242#ifdef LOGGING 2242#ifdef TOX_LOGGER
2243#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL 2243#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
2244static time_t lastdump = 0; 2244static time_t lastdump = 0;
2245static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1]; 2245static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1];
@@ -2315,7 +2315,7 @@ void do_messenger(Messenger *m)
2315 do_friends(m); 2315 do_friends(m);
2316 connection_status_cb(m); 2316 connection_status_cb(m);
2317 2317
2318#ifdef LOGGING 2318#ifdef TOX_LOGGER
2319 2319
2320 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { 2320 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
2321 2321
@@ -2414,7 +2414,7 @@ void do_messenger(Messenger *m)
2414 } 2414 }
2415 } 2415 }
2416 2416
2417#endif /* LOGGING */ 2417#endif /* TOX_LOGGER */
2418} 2418}
2419 2419
2420/* new messenger format for load/save, more robust and forward compatible */ 2420/* 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)
878 } 878 }
879} 879}
880 880
881#ifdef LOGGING 881#ifdef TOX_LOGGER
882static char *idpart2str(uint8_t *id, size_t len); 882static char *idpart2str(uint8_t *id, size_t len);
883#endif /* LOGGING */ 883#endif /* TOX_LOGGER */
884 884
885/* refresh buckets */ 885/* refresh buckets */
886void do_Assoc(Assoc *assoc, DHT *dht) 886void do_Assoc(Assoc *assoc, DHT *dht)
@@ -974,7 +974,7 @@ void kill_Assoc(Assoc *assoc)
974 } 974 }
975} 975}
976 976
977#ifdef LOGGING 977#ifdef TOX_LOGGER
978 978
979static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; 979static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1];
980static char *idpart2str(uint8_t *id, size_t len) 980static char *idpart2str(uint8_t *id, size_t len)
@@ -1028,4 +1028,4 @@ void Assoc_status(const Assoc *assoc)
1028 } 1028 }
1029} 1029}
1030 1030
1031#endif /* LOGGING */ 1031#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);
97/* destroy */ 97/* destroy */
98void kill_Assoc(Assoc *assoc); 98void kill_Assoc(Assoc *assoc);
99 99
100#ifdef LOGGING 100#ifdef TOX_LOGGER
101void Assoc_status(const Assoc *assoc); 101void Assoc_status(const Assoc *assoc);
102#endif /* LOGGING */ 102#endif /* TOX_LOGGER */
103 103
104#endif /* !__ASSOC_H__ */ 104#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 @@
44#endif 44#endif
45 45
46 46
47struct logger { 47struct Logger {
48 FILE *log_file; 48 FILE *log_file;
49 LOG_LEVEL level; 49 LOG_LEVEL level;
50 uint64_t start_time; /* Time when lib loaded */ 50 uint64_t start_time; /* Time when lib loaded */
@@ -87,7 +87,7 @@ char *strtime(char *dest, size_t max_len)
87 */ 87 */
88Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) 88Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
89{ 89{
90#ifndef LOGGING /* Disabled */ 90#ifndef TOX_LOGGER /* Disabled */
91 return NULL; 91 return NULL;
92#endif 92#endif
93 93
@@ -96,7 +96,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
96 if (!retu) 96 if (!retu)
97 return NULL; 97 return NULL;
98 98
99 if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) { 99 if (pthread_mutex_init(retu->mutex, NULL) != 0) {
100 free(retu); 100 free(retu);
101 return NULL; 101 return NULL;
102 } 102 }
@@ -110,7 +110,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
110 110
111 if (!(retu->tstr = calloc(16, sizeof (char))) || 111 if (!(retu->tstr = calloc(16, sizeof (char))) ||
112 !(retu->posstr = calloc(300, sizeof (char))) || 112 !(retu->posstr = calloc(300, sizeof (char))) ||
113 !(retu->msg = calloc(4096, sizeof (char))) ) 113 !(retu->msg = calloc(4096, sizeof (char))))
114 goto FAILURE; 114 goto FAILURE;
115 115
116 if (id) { 116 if (id) {
@@ -147,7 +147,7 @@ FAILURE:
147 147
148void logger_kill(Logger *log) 148void logger_kill(Logger *log)
149{ 149{
150#ifndef LOGGING /* Disabled */ 150#ifndef TOX_LOGGER /* Disabled */
151 return; 151 return;
152#endif 152#endif
153 153
@@ -160,7 +160,7 @@ void logger_kill(Logger *log)
160 free(log->posstr); 160 free(log->posstr);
161 free(log->msg); 161 free(log->msg);
162 162
163 if (fclose(log->log_file) != 0 ) 163 if (fclose(log->log_file) != 0)
164 perror("Could not close log file"); 164 perror("Could not close log file");
165 165
166 pthread_mutex_unlock(log->mutex); 166 pthread_mutex_unlock(log->mutex);
@@ -177,7 +177,7 @@ void logger_kill_global(void)
177 177
178void logger_set_global(Logger *log) 178void logger_set_global(Logger *log)
179{ 179{
180#ifndef LOGGING /* Disabled */ 180#ifndef TOX_LOGGER /* Disabled */
181 return; 181 return;
182#endif 182#endif
183 183
@@ -186,7 +186,7 @@ void logger_set_global(Logger *log)
186 186
187Logger *logger_get_global(void) 187Logger *logger_get_global(void)
188{ 188{
189#ifndef LOGGING /* Disabled */ 189#ifndef TOX_LOGGER /* Disabled */
190 return NULL; 190 return NULL;
191#endif 191#endif
192 192
@@ -195,7 +195,7 @@ Logger *logger_get_global(void)
195 195
196void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...) 196void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...)
197{ 197{
198#ifndef LOGGING /* Disabled */ 198#ifndef TOX_LOGGER /* Disabled */
199 return; 199 return;
200#endif 200#endif
201 201
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 {
43 LOG_ERROR 43 LOG_ERROR
44} LOG_LEVEL; 44} LOG_LEVEL;
45 45
46typedef struct logger Logger; 46typedef struct Logger Logger;
47 47
48/** 48/**
49 * Set 'level' as the lowest printable level. If id == NULL, random number is used. 49 * 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
66 66
67 67
68/* To do some checks or similar only when logging, use this */ 68/* To do some checks or similar only when logging, use this */
69#ifdef LOGGING 69#ifdef TOX_LOGGER
70# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0) 70# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)
71# define LOGGER_WRITE(log, level, format, ...) \ 71# define LOGGER_WRITE(log, level, format, ...) \
72 logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__ ) 72 logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__)
73#else 73#else
74/* # warning "Logging disabled" */
74# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0) 75# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0)
75# define LOGGER_WRITE(log, level, format, ...) do {} while(0) 76# define LOGGER_WRITE(log, level, format, ...) do {} while(0)
76#endif /* LOGGING */ 77#endif /* TOX_LOGGER */
77 78
78/* To log with an logger */ 79/* To log with an logger */
79#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__ ) 80#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__)
80#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__ ) 81#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__)
81#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__ ) 82#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__)
82#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__ ) 83#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__)
83#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__ ) 84#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__)
84 85
85/* To log with the global logger */ 86/* To log with the global logger */
86#define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__) 87#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)
266} 266}
267 267
268/* In case no logging */ 268/* In case no logging */
269#ifndef LOGGING 269#ifndef TOX_LOGGER
270#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__) 270#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__)
271#else 271#else
272#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0 272#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0
@@ -287,7 +287,7 @@ uint64_t current_time_monotonic(void)
287 __buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \ 287 __buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \
288 ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__)); 288 ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__));
289 289
290#endif /* LOGGING */ 290#endif /* TOX_LOGGER */
291 291
292/* Basic network functions: 292/* Basic network functions:
293 * Function to send packet(data) of length length to ip_port. 293 * 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,
615 } 615 }
616 616
617 if (ip.family == AF_INET6) { 617 if (ip.family == AF_INET6) {
618#ifdef LOGGING 618#ifdef TOX_LOGGER
619 int is_dualstack = 619 int is_dualstack =
620#endif /* LOGGING */ 620#endif /* TOX_LOGGER */
621 set_socket_dualstack(temp->sock); 621 set_socket_dualstack(temp->sock);
622 LOGGER_DEBUG( "Dual-stack socket: %s", 622 LOGGER_DEBUG( "Dual-stack socket: %s",
623 is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" ); 623 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,
628 mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02; 628 mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02;
629 mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; 629 mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
630 mreq.ipv6mr_interface = 0; 630 mreq.ipv6mr_interface = 0;
631#ifdef LOGGING 631#ifdef TOX_LOGGER
632 int res = 632 int res =
633#endif /* LOGGING */ 633#endif /* TOX_LOGGER */
634 setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); 634 setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
635 635
636 LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" : 636 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)
234 b->start = (b->start + 1) % b->size; 234 b->start = (b->start + 1) % b->size;
235 return true; 235 return true;
236} 236}
237void rb_clear(RingBuffer *b)
238{
239 while (!rb_empty(b)) {
240 void *p;
241 rb_read(b, &p);
242 free(p);
243 }
244}
245RingBuffer *rb_new(int size) 237RingBuffer *rb_new(int size)
246{ 238{
247 RingBuffer *buf = calloc(sizeof(RingBuffer), 1); 239 RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
@@ -257,11 +249,28 @@ RingBuffer *rb_new(int size)
257 249
258 return buf; 250 return buf;
259} 251}
260void rb_free(RingBuffer *b) 252void rb_kill(RingBuffer *b)
261{ 253{
262 if (b) { 254 if (b) {
263 rb_clear(b);
264 free(b->data); 255 free(b->data);
265 free(b); 256 free(b);
266 } 257 }
267} 258}
259uint16_t rb_size(const RingBuffer* b)
260{
261 if (rb_empty(b))
262 return 0;
263
264 return
265 b->end > b->start ?
266 b->end - b->start :
267 (b->size - b->start) + b->end;
268}
269uint16_t rb_data(const RingBuffer* b, void** dest)
270{
271 uint16_t i = 0;
272 for (; i < rb_size(b); i++)
273 dest[i] = b->data[(b->start + i) % b->size];
274
275 return i;
276}
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);
64bool rb_empty(const RingBuffer *b); 64bool rb_empty(const RingBuffer *b);
65void* rb_write(RingBuffer* b, void* p); 65void* rb_write(RingBuffer* b, void* p);
66bool rb_read(RingBuffer* b, void** p); 66bool rb_read(RingBuffer* b, void** p);
67void rb_clear(RingBuffer *b);
68RingBuffer *rb_new(int size); 67RingBuffer *rb_new(int size);
69void rb_free(RingBuffer *b); 68void rb_kill(RingBuffer *b);
69uint16_t rb_size(const RingBuffer *b);
70uint16_t rb_data(const RingBuffer* b, void** dest);
71
70#endif /* __UTIL_H__ */ 72#endif /* __UTIL_H__ */